#define AUDIO_CAP "alsa"
#include "audio_int.h"
+#define DEBUG_ALSA 0
+
struct pollhlp {
snd_pcm_t *handle;
struct pollfd *pfds;
snd_pcm_uframes_t samples;
};
-static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
+static void G_GNUC_PRINTF (2, 3) alsa_logerr (int err, const char *fmt, ...)
{
va_list ap;
AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
}
-static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
+static void G_GNUC_PRINTF (3, 4) alsa_logerr2 (
int err,
const char *typ,
const char *fmt,
return -1;
}
- pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds));
- if (!pfds) {
- dolog ("Could not initialize poll mode\n");
- return -1;
- }
+ pfds = g_new0(struct pollfd, count);
err = snd_pcm_poll_descriptors (handle, pfds, count);
if (err < 0) {
case AUDIO_FORMAT_S16:
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
- }
- else {
+ } else {
return SND_PCM_FORMAT_S16_LE;
}
case AUDIO_FORMAT_U16:
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
- }
- else {
+ } else {
return SND_PCM_FORMAT_U16_LE;
}
case AUDIO_FORMAT_S32:
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
- }
- else {
+ } else {
return SND_PCM_FORMAT_S32_LE;
}
case AUDIO_FORMAT_U32:
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
- }
- else {
+ } else {
return SND_PCM_FORMAT_U32_LE;
}
snd_pcm_hw_params_t *hw_params;
int err;
unsigned int freq, nchannels;
- const char *pcm_name = apdo->has_dev ? apdo->dev : "default";
+ const char *pcm_name = apdo->dev ?: "default";
snd_pcm_uframes_t obt_buffer_size;
const char *typ = in ? "ADC" : "DAC";
snd_pcm_format_t obtfmt;
*handlep = handle;
- if (obtfmt != req->fmt ||
- obt->nchannels != req->nchannels ||
- obt->freq != req->freq) {
+ if (DEBUG_ALSA || obtfmt != req->fmt ||
+ obt->nchannels != req->nchannels || obt->freq != req->freq) {
dolog ("Audio parameters for %s\n", typ);
alsa_dump_info(req, obt, obtfmt, apdo);
}
-#ifdef DEBUG
- alsa_dump_info(req, obt, obtfmt, pdo);
-#endif
return 0;
err:
return -1;
}
+static size_t alsa_buffer_get_free(HWVoiceOut *hw)
+{
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *)hw;
+ snd_pcm_sframes_t avail;
+ size_t alsa_free, generic_free, generic_in_use;
+
+ avail = snd_pcm_avail_update(alsa->handle);
+ if (avail < 0) {
+ if (avail == -EPIPE) {
+ if (!alsa_recover(alsa->handle)) {
+ avail = snd_pcm_avail_update(alsa->handle);
+ }
+ }
+ if (avail < 0) {
+ alsa_logerr(avail,
+ "Could not obtain number of available frames\n");
+ avail = 0;
+ }
+ }
+
+ alsa_free = avail * hw->info.bytes_per_frame;
+ generic_free = audio_generic_buffer_get_free(hw);
+ generic_in_use = hw->samples * hw->info.bytes_per_frame - generic_free;
+ if (generic_in_use) {
+ /*
+ * This code can only be reached in the unlikely case that
+ * snd_pcm_avail_update() returned a larger number of frames
+ * than snd_pcm_writei() could write. Make sure that all
+ * remaining bytes in the generic buffer can be written.
+ */
+ alsa_free = alsa_free > generic_in_use ? alsa_free - generic_in_use : 0;
+ }
+
+ return alsa_free;
+}
+
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
alsa_logerr (err, "Could not stop %s\n", typ);
return -1;
}
- }
- else {
+ } else {
err = snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
switch (nread) {
case 0:
trace_alsa_read_zero(len);
- return pos;;
+ return pos;
case -EPIPE:
if (alsa_recover(alsa->handle)) {
default:
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
len, dst);
- return pos;;
+ return pos;
}
}
}
}
-static void *alsa_audio_init(Audiodev *dev)
+static void *alsa_audio_init(Audiodev *dev, Error **errp)
{
AudiodevAlsaOptions *aopts;
assert(dev->driver == AUDIODEV_DRIVER_ALSA);
alsa_init_per_direction(aopts->in);
alsa_init_per_direction(aopts->out);
- /*
- * need to define them, as otherwise alsa produces no sound
- * doesn't set has_* so alsa_open can identify it wasn't set by the user
- */
+ /* don't set has_* so alsa_open can identify it wasn't set by the user */
if (!dev->u.alsa.out->has_period_length) {
- /* 1024 frames assuming 44100Hz */
- dev->u.alsa.out->period_length = 1024 * 1000000 / 44100;
+ /* 256 frames assuming 44100Hz */
+ dev->u.alsa.out->period_length = 5805;
}
if (!dev->u.alsa.out->has_buffer_length) {
/* 4096 frames assuming 44100Hz */
- dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100;
+ dev->u.alsa.out->buffer_length = 92880;
}
- /*
- * OptsVisitor sets unspecified optional fields to zero, but do not depend
- * on it...
- */
if (!dev->u.alsa.in->has_period_length) {
- dev->u.alsa.in->period_length = 0;
+ /* 256 frames assuming 44100Hz */
+ dev->u.alsa.in->period_length = 5805;
}
if (!dev->u.alsa.in->has_buffer_length) {
- dev->u.alsa.in->buffer_length = 0;
+ /* 4096 frames assuming 44100Hz */
+ dev->u.alsa.in->buffer_length = 92880;
}
return dev;
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
.write = alsa_write,
+ .buffer_get_free = alsa_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = alsa_enable_out,
.init_in = alsa_init_in,
.fini_in = alsa_fini_in,
.read = alsa_read,
+ .run_buffer_in = audio_generic_run_buffer_in,
.enable_in = alsa_enable_in,
};
.init = alsa_audio_init,
.fini = alsa_audio_fini,
.pcm_ops = &alsa_pcm_ops,
- .can_be_default = 1,
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
.voice_size_out = sizeof (ALSAVoiceOut),