ao_alsa: support non-interleaved audio

ALSA supports non-interleaved audio natively using a separate API
function for writing audio. (Though you have to tell it about this on
initialization.) ALSA doesn't have separate sample formats for this,
so just pretend to negotiate the interleaved format, and assume that
all non-interleaved formats have an interleaved companion format.
This commit is contained in:
wm4 2013-11-10 23:39:08 +01:00
parent fedb9229d5
commit dab6eaaa5e
1 changed files with 23 additions and 25 deletions

View File

@ -52,12 +52,11 @@
struct priv { struct priv {
snd_pcm_t *alsa; snd_pcm_t *alsa;
snd_pcm_format_t alsa_fmt; snd_pcm_format_t alsa_fmt;
size_t bytes_per_sample;
int can_pause; int can_pause;
snd_pcm_sframes_t prepause_frames; snd_pcm_sframes_t prepause_frames;
float delay_before_pause; float delay_before_pause;
int buffersize; int buffersize; // in frames
int outburst; int outburst; // in frames
int cfg_block; int cfg_block;
char *cfg_device; char *cfg_device;
@ -251,6 +250,7 @@ static const int mp_to_alsa_format[][2] = {
static int find_alsa_format(int af_format) static int find_alsa_format(int af_format)
{ {
af_format = af_fmt_from_planar(af_format);
for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) { for (int n = 0; mp_to_alsa_format[n][0] != AF_FORMAT_UNKNOWN; n++) {
if (mp_to_alsa_format[n][0] == af_format) if (mp_to_alsa_format[n][0] == af_format)
return mp_to_alsa_format[n][1]; return mp_to_alsa_format[n][1];
@ -432,12 +432,6 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams); err = snd_pcm_hw_params_any(p->alsa, alsa_hwparams);
CHECK_ALSA_ERROR("Unable to get initial parameters"); CHECK_ALSA_ERROR("Unable to get initial parameters");
err = snd_pcm_hw_params_set_access
(p->alsa, alsa_hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
CHECK_ALSA_ERROR("Unable to set access type");
ao->format = af_fmt_from_planar(ao->format);
p->alsa_fmt = find_alsa_format(ao->format); p->alsa_fmt = find_alsa_format(ao->format);
if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) { if (p->alsa_fmt == SND_PCM_FORMAT_UNKNOWN) {
p->alsa_fmt = SND_PCM_FORMAT_S16; p->alsa_fmt = SND_PCM_FORMAT_S16;
@ -460,6 +454,12 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt); err = snd_pcm_hw_params_set_format(p->alsa, alsa_hwparams, p->alsa_fmt);
CHECK_ALSA_ERROR("Unable to set format"); CHECK_ALSA_ERROR("Unable to set format");
snd_pcm_access_t access = af_fmt_is_planar(ao->format)
? SND_PCM_ACCESS_RW_NONINTERLEAVED
: SND_PCM_ACCESS_RW_INTERLEAVED;
err = snd_pcm_hw_params_set_access(p->alsa, alsa_hwparams, access);
CHECK_ALSA_ERROR("Unable to set access type");
int num_channels = ao->channels.num; int num_channels = ao->channels.num;
err = snd_pcm_hw_params_set_channels_near err = snd_pcm_hw_params_set_channels_near
(p->alsa, alsa_hwparams, &num_channels); (p->alsa, alsa_hwparams, &num_channels);
@ -480,9 +480,6 @@ static int init(struct ao *ao)
(p->alsa, alsa_hwparams, &ao->samplerate, NULL); (p->alsa, alsa_hwparams, &ao->samplerate, NULL);
CHECK_ALSA_ERROR("Unable to set samplerate-2"); CHECK_ALSA_ERROR("Unable to set samplerate-2");
p->bytes_per_sample = af_fmt2bits(ao->format) / 8;
p->bytes_per_sample *= ao->channels.num;
err = snd_pcm_hw_params_set_buffer_time_near err = snd_pcm_hw_params_set_buffer_time_near
(p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL); (p->alsa, alsa_hwparams, &(unsigned int){BUFFER_TIME}, NULL);
CHECK_ALSA_ERROR("Unable to set buffer time near"); CHECK_ALSA_ERROR("Unable to set buffer time near");
@ -501,14 +498,14 @@ static int init(struct ao *ao)
err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize); err = snd_pcm_hw_params_get_buffer_size(alsa_hwparams, &bufsize);
CHECK_ALSA_ERROR("Unable to get buffersize"); CHECK_ALSA_ERROR("Unable to get buffersize");
p->buffersize = bufsize * p->bytes_per_sample; p->buffersize = bufsize;
MP_VERBOSE(ao, "got buffersize=%i\n", p->buffersize); MP_VERBOSE(ao, "got buffersize=%i samples\n", p->buffersize);
err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL); err = snd_pcm_hw_params_get_period_size(alsa_hwparams, &chunk_size, NULL);
CHECK_ALSA_ERROR("Unable to get period size"); CHECK_ALSA_ERROR("Unable to get period size");
MP_VERBOSE(ao, "got period size %li\n", chunk_size); MP_VERBOSE(ao, "got period size %li\n", chunk_size);
p->outburst = chunk_size * p->bytes_per_sample; p->outburst = chunk_size;
/* setting software parameters */ /* setting software parameters */
err = snd_pcm_sw_params_current(p->alsa, alsa_swparams); err = snd_pcm_sw_params_current(p->alsa, alsa_swparams);
@ -539,8 +536,8 @@ static int init(struct ao *ao)
p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams); p->can_pause = snd_pcm_hw_params_can_pause(alsa_hwparams);
MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bpf/%d bytes buffer/%s\n", MP_VERBOSE(ao, "opened: %d Hz/%d channels/%d bps/%d samples buffer/%s\n",
ao->samplerate, ao->channels.num, (int)p->bytes_per_sample, ao->samplerate, ao->channels.num, af_fmt2bits(ao->format),
p->buffersize, snd_pcm_format_description(p->alsa_fmt)); p->buffersize, snd_pcm_format_description(p->alsa_fmt));
return 0; return 0;
@ -639,23 +636,24 @@ alsa_error: ;
static int play(struct ao *ao, void **data, int samples, int flags) static int play(struct ao *ao, void **data, int samples, int flags)
{ {
struct priv *p = ao->priv; struct priv *p = ao->priv;
int num_frames;
snd_pcm_sframes_t res = 0; snd_pcm_sframes_t res = 0;
int len = samples * p->bytes_per_sample;
if (!(flags & AOPLAY_FINAL_CHUNK)) if (!(flags & AOPLAY_FINAL_CHUNK))
len = len / p->outburst * p->outburst; samples = samples / p->outburst * p->outburst;
num_frames = len / p->bytes_per_sample;
if (!p->alsa) { if (!p->alsa) {
MP_ERR(ao, "Device configuration error."); MP_ERR(ao, "Device configuration error.");
return -1; return -1;
} }
if (num_frames == 0) if (samples == 0)
return 0; return 0;
do { do {
res = snd_pcm_writei(p->alsa, data[0], num_frames); if (af_fmt_is_planar(ao->format)) {
res = snd_pcm_writen(p->alsa, data, samples);
} else {
res = snd_pcm_writei(p->alsa, data[0], samples);
}
if (res == -EINTR) { if (res == -EINTR) {
/* nothing to do */ /* nothing to do */
@ -692,10 +690,10 @@ static int get_space(struct ao *ao)
err = snd_pcm_status(p->alsa, status); err = snd_pcm_status(p->alsa, status);
CHECK_ALSA_ERROR("cannot get pcm status"); CHECK_ALSA_ERROR("cannot get pcm status");
unsigned space = snd_pcm_status_get_avail(status) * p->bytes_per_sample; unsigned space = snd_pcm_status_get_avail(status);
if (space > p->buffersize) // Buffer underrun? if (space > p->buffersize) // Buffer underrun?
space = p->buffersize; space = p->buffersize;
return space / p->bytes_per_sample; return space;
alsa_error: alsa_error:
return 0; return 0;