|
|
|
@ -44,9 +44,6 @@ struct pollhlp { |
|
|
|
|
|
|
|
typedef struct ALSAVoiceOut { |
|
|
|
HWVoiceOut hw; |
|
|
|
int wpos; |
|
|
|
int pending; |
|
|
|
void *pcm_buf; |
|
|
|
snd_pcm_t *handle; |
|
|
|
struct pollhlp pollhlp; |
|
|
|
Audiodev *dev; |
|
|
|
@ -55,7 +52,6 @@ typedef struct ALSAVoiceOut { |
|
|
|
typedef struct ALSAVoiceIn { |
|
|
|
HWVoiceIn hw; |
|
|
|
snd_pcm_t *handle; |
|
|
|
void *pcm_buf; |
|
|
|
struct pollhlp pollhlp; |
|
|
|
Audiodev *dev; |
|
|
|
} ALSAVoiceIn; |
|
|
|
@ -602,102 +598,64 @@ static int alsa_open(bool in, struct alsa_params_req *req, |
|
|
|
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.shift; |
|
|
|
|
|
|
|
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.shift; |
|
|
|
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) |
|
|
|
@ -706,9 +664,6 @@ 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, |
|
|
|
@ -737,14 +692,6 @@ 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; |
|
|
|
@ -839,14 +786,6 @@ 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; |
|
|
|
@ -858,129 +797,48 @@ static void alsa_fini_in (HWVoiceIn *hw) |
|
|
|
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.shift); |
|
|
|
|
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
trace_alsa_xrun_in(); |
|
|
|
continue; |
|
|
|
|
|
|
|
hw->conv (dst, src, nread); |
|
|
|
case -EAGAIN: |
|
|
|
return pos; |
|
|
|
|
|
|
|
src = advance (src, nread << hwshift); |
|
|
|
dst += nread; |
|
|
|
|
|
|
|
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.shift; |
|
|
|
len -= nread << hw->info.shift; |
|
|
|
} |
|
|
|
|
|
|
|
exit: |
|
|
|
hw->wpos = (hw->wpos + read_samples) % hw->samples; |
|
|
|
return read_samples; |
|
|
|
return pos; |
|
|
|
} |
|
|
|
|
|
|
|
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
|
|
|
@ -1065,12 +923,12 @@ static void alsa_audio_fini (void *opaque) |
|
|
|
static struct audio_pcm_ops alsa_pcm_ops = { |
|
|
|
.init_out = alsa_init_out, |
|
|
|
.fini_out = alsa_fini_out, |
|
|
|
.run_out = alsa_run_out, |
|
|
|
.write = alsa_write, |
|
|
|
.ctl_out = alsa_ctl_out, |
|
|
|
|
|
|
|
.init_in = alsa_init_in, |
|
|
|
.fini_in = alsa_fini_in, |
|
|
|
.run_in = alsa_run_in, |
|
|
|
.read = alsa_read, |
|
|
|
.ctl_in = alsa_ctl_in, |
|
|
|
}; |
|
|
|
|
|
|
|
|