typedef struct ALSAVoiceOut {
HWVoiceOut hw;
- int wpos;
- int pending;
- void *pcm_buf;
snd_pcm_t *handle;
struct pollhlp pollhlp;
Audiodev *dev;
typedef struct ALSAVoiceIn {
HWVoiceIn hw;
snd_pcm_t *handle;
- void *pcm_buf;
struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceIn;
return SND_PCM_FORMAT_U32_LE;
}
+ case AUDIO_FORMAT_F32:
+ if (endianness) {
+ return SND_PCM_FORMAT_FLOAT_BE;
+ } else {
+ return SND_PCM_FORMAT_FLOAT_LE;
+ }
+
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
*fmt = AUDIO_FORMAT_U32;
break;
+ case SND_PCM_FORMAT_FLOAT_LE:
+ *endianness = 0;
+ *fmt = AUDIO_FORMAT_F32;
+ break;
+
+ case SND_PCM_FORMAT_FLOAT_BE:
+ *endianness = 1;
+ *fmt = AUDIO_FORMAT_F32;
+ break;
+
default:
dolog ("Unrecognized audio format %d\n", alsafmt);
return -1;
goto err;
}
- if (nchannels != 1 && nchannels != 2) {
- alsa_logerr2 (err, typ,
- "Can not handle obtained number of channels %d\n",
- nchannels);
- goto err;
- }
-
if (apdo->buffer_length) {
int dir = 0;
unsigned int btime = apdo->buffer_length;
return -1;
}
-static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
+static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
{
- snd_pcm_sframes_t avail;
-
- avail = snd_pcm_avail_update (handle);
- if (avail < 0) {
- if (avail == -EPIPE) {
- if (!alsa_recover (handle)) {
- avail = snd_pcm_avail_update (handle);
- }
- }
-
- if (avail < 0) {
- alsa_logerr (avail,
- "Could not obtain number of available frames\n");
- return -1;
- }
- }
+ ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+ size_t pos = 0;
+ size_t len_frames = len / hw->info.bytes_per_frame;
+
+ while (len_frames) {
+ char *src = advance(buf, pos);
+ snd_pcm_sframes_t written;
+
+ written = snd_pcm_writei(alsa->handle, src, len_frames);
+
+ if (written <= 0) {
+ switch (written) {
+ case 0:
+ trace_alsa_wrote_zero(len_frames);
+ return pos;
+
+ case -EPIPE:
+ if (alsa_recover(alsa->handle)) {
+ alsa_logerr(written, "Failed to write %zu frames\n",
+ len_frames);
+ return pos;
+ }
+ trace_alsa_xrun_out();
+ continue;
+
+ case -ESTRPIPE:
+ /*
+ * stream is suspended and waiting for an application
+ * recovery
+ */
+ if (alsa_resume(alsa->handle)) {
+ alsa_logerr(written, "Failed to write %zu frames\n",
+ len_frames);
+ return pos;
+ }
+ trace_alsa_resume_out();
+ continue;
- return avail;
-}
+ case -EAGAIN:
+ return pos;
-static void alsa_write_pending (ALSAVoiceOut *alsa)
-{
- HWVoiceOut *hw = &alsa->hw;
-
- while (alsa->pending) {
- int left_till_end_samples = hw->samples - alsa->wpos;
- int len = MIN (alsa->pending, left_till_end_samples);
- char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
-
- while (len) {
- snd_pcm_sframes_t written;
-
- written = snd_pcm_writei (alsa->handle, src, len);
-
- if (written <= 0) {
- switch (written) {
- case 0:
- trace_alsa_wrote_zero(len);
- return;
-
- case -EPIPE:
- if (alsa_recover (alsa->handle)) {
- alsa_logerr (written, "Failed to write %d frames\n",
- len);
- return;
- }
- trace_alsa_xrun_out();
- continue;
-
- case -ESTRPIPE:
- /* stream is suspended and waiting for an
- application recovery */
- if (alsa_resume (alsa->handle)) {
- alsa_logerr (written, "Failed to write %d frames\n",
- len);
- return;
- }
- trace_alsa_resume_out();
- continue;
-
- case -EAGAIN:
- return;
-
- default:
- alsa_logerr (written, "Failed to write %d frames from %p\n",
- len, src);
- return;
- }
+ default:
+ alsa_logerr(written, "Failed to write %zu frames from %p\n",
+ len, src);
+ return pos;
}
-
- alsa->wpos = (alsa->wpos + written) % hw->samples;
- alsa->pending -= written;
- len -= written;
}
- }
-}
-
-static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
-{
- ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
- size_t decr;
- snd_pcm_sframes_t avail;
- avail = alsa_get_avail (alsa->handle);
- if (avail < 0) {
- dolog ("Could not get number of available playback frames\n");
- return 0;
+ pos += written * hw->info.bytes_per_frame;
+ if (written < len_frames) {
+ break;
+ }
+ len_frames -= written;
}
- decr = MIN (live, avail);
- decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
- alsa->pending += decr;
- alsa_write_pending (alsa);
- return decr;
+ return pos;
}
static void alsa_fini_out (HWVoiceOut *hw)
ldebug ("alsa_fini\n");
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
-
- g_free(alsa->pcm_buf);
- alsa->pcm_buf = NULL;
}
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
- alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
- if (!alsa->pcm_buf) {
- dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
- hw->samples, 1 << hw->info.shift);
- alsa_anal_close1 (&handle);
- return -1;
- }
-
alsa->pollhlp.s = hw->s;
alsa->handle = handle;
alsa->dev = dev;
return 0;
}
-static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static void alsa_enable_out(HWVoiceOut *hw, bool enable)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
- switch (cmd) {
- case VOICE_ENABLE:
- {
- bool poll_mode = apdo->try_poll;
+ if (enable) {
+ bool poll_mode = apdo->try_poll;
- ldebug ("enabling voice\n");
- if (poll_mode && alsa_poll_out (hw)) {
- poll_mode = 0;
- }
- hw->poll_mode = poll_mode;
- return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
+ ldebug("enabling voice\n");
+ if (poll_mode && alsa_poll_out(hw)) {
+ poll_mode = 0;
}
-
- case VOICE_DISABLE:
- ldebug ("disabling voice\n");
+ hw->poll_mode = poll_mode;
+ alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
+ } else {
+ ldebug("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
- alsa_fini_poll (&alsa->pollhlp);
+ alsa_fini_poll(&alsa->pollhlp);
}
- return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
+ alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
}
-
- return -1;
}
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
- alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
- if (!alsa->pcm_buf) {
- dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
- hw->samples, 1 << hw->info.shift);
- alsa_anal_close1 (&handle);
- return -1;
- }
-
alsa->pollhlp.s = hw->s;
alsa->handle = handle;
alsa->dev = dev;
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
-
- g_free(alsa->pcm_buf);
- alsa->pcm_buf = NULL;
}
-static size_t alsa_run_in(HWVoiceIn *hw)
+static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
- int hwshift = hw->info.shift;
- int i;
- size_t live = audio_pcm_hw_get_live_in (hw);
- size_t dead = hw->samples - live;
- size_t decr;
- struct {
- size_t add;
- size_t len;
- } bufs[2] = {
- { .add = hw->wpos, .len = 0 },
- { .add = 0, .len = 0 }
- };
- snd_pcm_sframes_t avail;
- snd_pcm_uframes_t read_samples = 0;
-
- if (!dead) {
- return 0;
- }
+ size_t pos = 0;
- avail = alsa_get_avail (alsa->handle);
- if (avail < 0) {
- dolog ("Could not get number of captured frames\n");
- return 0;
- }
-
- if (!avail) {
- snd_pcm_state_t state;
-
- state = snd_pcm_state (alsa->handle);
- switch (state) {
- case SND_PCM_STATE_PREPARED:
- avail = hw->samples;
- break;
- case SND_PCM_STATE_SUSPENDED:
- /* stream is suspended and waiting for an application recovery */
- if (alsa_resume (alsa->handle)) {
- dolog ("Failed to resume suspended input stream\n");
- return 0;
- }
- trace_alsa_resume_in();
- break;
- default:
- trace_alsa_no_frames(state);
- return 0;
- }
- }
+ while (len) {
+ void *dst = advance(buf, pos);
+ snd_pcm_sframes_t nread;
- decr = MIN(dead, avail);
- if (!decr) {
- return 0;
- }
+ nread = snd_pcm_readi(
+ alsa->handle, dst, len / hw->info.bytes_per_frame);
- if (hw->wpos + decr > hw->samples) {
- bufs[0].len = (hw->samples - hw->wpos);
- bufs[1].len = (decr - (hw->samples - hw->wpos));
- }
- else {
- bufs[0].len = decr;
- }
+ if (nread <= 0) {
+ switch (nread) {
+ case 0:
+ trace_alsa_read_zero(len);
+ return pos;
- for (i = 0; i < 2; ++i) {
- void *src;
- struct st_sample *dst;
- snd_pcm_sframes_t nread;
- snd_pcm_uframes_t len;
-
- len = bufs[i].len;
-
- src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
- dst = hw->conv_buf + bufs[i].add;
-
- while (len) {
- nread = snd_pcm_readi (alsa->handle, src, len);
-
- if (nread <= 0) {
- switch (nread) {
- case 0:
- trace_alsa_read_zero(len);
- goto exit;
-
- case -EPIPE:
- if (alsa_recover (alsa->handle)) {
- alsa_logerr (nread, "Failed to read %ld frames\n", len);
- goto exit;
- }
- trace_alsa_xrun_in();
- continue;
-
- case -EAGAIN:
- goto exit;
-
- default:
- alsa_logerr (
- nread,
- "Failed to read %ld frames from %p\n",
- len,
- src
- );
- goto exit;
+ case -EPIPE:
+ if (alsa_recover(alsa->handle)) {
+ alsa_logerr(nread, "Failed to read %zu frames\n", len);
+ return pos;
}
- }
-
- hw->conv (dst, src, nread);
+ trace_alsa_xrun_in();
+ continue;
- src = advance (src, nread << hwshift);
- dst += nread;
+ case -EAGAIN:
+ return pos;
- read_samples += nread;
- len -= nread;
+ default:
+ alsa_logerr(nread, "Failed to read %zu frames to %p\n",
+ len, dst);
+ return pos;
+ }
}
+
+ pos += nread * hw->info.bytes_per_frame;
+ len -= nread * hw->info.bytes_per_frame;
}
- exit:
- hw->wpos = (hw->wpos + read_samples) % hw->samples;
- return read_samples;
+ return pos;
}
-static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static void alsa_enable_in(HWVoiceIn *hw, bool enable)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
- switch (cmd) {
- case VOICE_ENABLE:
- {
- bool poll_mode = apdo->try_poll;
+ if (enable) {
+ bool poll_mode = apdo->try_poll;
- ldebug ("enabling voice\n");
- if (poll_mode && alsa_poll_in (hw)) {
- poll_mode = 0;
- }
- hw->poll_mode = poll_mode;
-
- return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
+ ldebug("enabling voice\n");
+ if (poll_mode && alsa_poll_in(hw)) {
+ poll_mode = 0;
}
+ hw->poll_mode = poll_mode;
- case VOICE_DISABLE:
+ alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
+ } else {
ldebug ("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
- alsa_fini_poll (&alsa->pollhlp);
+ alsa_fini_poll(&alsa->pollhlp);
}
- return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
+ alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
}
-
- return -1;
}
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
- .run_out = alsa_run_out,
- .ctl_out = alsa_ctl_out,
+ .write = alsa_write,
+ .run_buffer_out = audio_generic_run_buffer_out,
+ .enable_out = alsa_enable_out,
.init_in = alsa_init_in,
.fini_in = alsa_fini_in,
- .run_in = alsa_run_in,
- .ctl_in = alsa_ctl_in,
+ .read = alsa_read,
+ .enable_in = alsa_enable_in,
};
static struct audio_driver alsa_audio_driver = {