1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
/* QSA Output.
*
* Copyright (C) 2016 Reece H. Dunn
* Copyright (C) 2016 Kaj-Michael Lang
*
* This file is part of pcaudiolib.
*
* pcaudiolib is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* pcaudiolib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pcaudiolib. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "audio_priv.h"
#if defined(HAVE_SYS_ASOUNDLIB_H) && defined(HAVE_SYS_ASOUND_H)
#include <sys/asound.h>
#include <sys/asoundlib.h>
#include <string.h>
#include <errno.h>
struct qsa_object
{
struct audio_object vtable;
snd_pcm_t *handle;
uint8_t sample_size;
char *device;
};
#define to_qsa_object(object) container_of(object, struct qsa_object, vtable)
int
qsa_object_open(struct audio_object *object,
enum audio_object_format format,
uint32_t rate,
uint8_t channels)
{
struct qsa_object *self = to_qsa_object(object);
if (self->handle)
return -EEXIST;
int pcm_format;
#define FORMAT(srcfmt, dstfmt, size) case srcfmt: pcm_format = dstfmt; self->sample_size = size; break;
switch (format)
{
FORMAT(AUDIO_OBJECT_FORMAT_U8, SND_PCM_SFMT_U8, 1)
FORMAT(AUDIO_OBJECT_FORMAT_S8, SND_PCM_SFMT_S8, 1)
FORMAT(AUDIO_OBJECT_FORMAT_S16LE, SND_PCM_SFMT_S16_LE, 2)
default: return -EINVAL;
}
#undef FORMAT
snd_pcm_info_t pi;
snd_pcm_channel_info_t pci;
snd_pcm_channel_params_t pp;
snd_pcm_channel_setup_t setup;
int err = 0;
if (self->device) {
if ((err = snd_pcm_open_name(&self->handle, self->device, SND_PCM_OPEN_PLAYBACK)) < 0)
goto error;
} else {
if ((err = snd_pcm_open_preferred(&self->handle, NULL, NULL, SND_PCM_OPEN_PLAYBACK)) < 0)
goto error;
}
memset (&pi, 0, sizeof (pi));
if ((err = snd_pcm_info (self->handle, &pi)) < 0)
goto error;
memset (&pci, 0, sizeof (pci));
pci.channel = SND_PCM_CHANNEL_PLAYBACK;
if ((err = snd_pcm_plugin_info (self->handle, &pci)) < 0)
goto error;
memset (&pp, 0, sizeof (pp));
pp.mode = SND_PCM_MODE_BLOCK;
pp.channel = SND_PCM_CHANNEL_PLAYBACK;
pp.start_mode = SND_PCM_START_FULL;
pp.stop_mode = SND_PCM_STOP_STOP;
pp.buf.block.frag_size = pci.max_fragment_size;
pp.buf.block.frags_max = 4; // XXX: What should this be?
pp.buf.block.frags_min = 1;
pp.format.interleave = 1;
pp.format.rate = rate;
pp.format.voices = channels;
pp.format.format = pcm_format;
if ((err = snd_pcm_plugin_params (self->handle, &pp)) < 0)
goto error;
if ((err = snd_pcm_plugin_prepare (self->handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
goto error;
return 0;
error:
if (self->handle) {
snd_pcm_close(self->handle);
self->handle = NULL;
}
return err;
}
void
qsa_object_close(struct audio_object *object)
{
struct qsa_object *self = to_qsa_object(object);
if (self->handle) {
snd_pcm_close(self->handle);
self->handle = NULL;
}
}
void
qsa_object_destroy(struct audio_object *object)
{
struct qsa_object *self = to_qsa_object(object);
free(self->device);
free(self);
}
int
qsa_object_drain(struct audio_object *object)
{
struct qsa_object *self = to_qsa_object(object);
return snd_pcm_plugin_playback_drain(self->handle);
}
int
qsa_object_flush(struct audio_object *object)
{
struct qsa_object *self = to_qsa_object(object);
return snd_pcm_plugin_flush(self->handle, SND_PCM_CHANNEL_PLAYBACK);
}
int
qsa_object_write(struct audio_object *object,
const void *data,
size_t bytes)
{
struct qsa_object *self = to_qsa_object(object);
snd_pcm_channel_status_t status;
size_t written = snd_pcm_plugin_write(self->handle, data, bytes);
if (written < bytes) {
int err;
memset (&status, 0, sizeof (status));
status.channel = SND_PCM_CHANNEL_PLAYBACK;
err=snd_pcm_plugin_status (self->handle, &status);
if (err < 0) {
return err;
}
if (status.status == SND_PCM_STATUS_READY || status.status == SND_PCM_STATUS_UNDERRUN) {
err = snd_pcm_plugin_prepare(self->handle, SND_PCM_CHANNEL_PLAYBACK);
// Try again if we can
if (err==0 && written==0)
return snd_pcm_plugin_write(self->handle, data, bytes);
}
return err;
}
return 0;
}
const char *
qsa_object_strerror(struct audio_object *object,
int error)
{
return snd_strerror(error);
}
struct audio_object *
create_qsa_object(const char *device,
const char *application_name,
const char *description)
{
struct qsa_object *self = malloc(sizeof(struct qsa_object));
if (!self)
return NULL;
self->handle = NULL;
self->sample_size = 0;
self->device = device ? strdup(device) : NULL;
self->vtable.open = qsa_object_open;
self->vtable.close = qsa_object_close;
self->vtable.destroy = qsa_object_destroy;
self->vtable.write = qsa_object_write;
self->vtable.drain = qsa_object_drain;
self->vtable.flush = qsa_object_flush;
self->vtable.strerror = qsa_object_strerror;
return &self->vtable;
}
#else
struct audio_object *
create_qsa_object(const char *device,
const char *application_name,
const char *description)
{
return NULL;
}
#endif
|