alsa: support planar formats

This commit is contained in:
Wim Taymans 2020-07-01 11:42:10 +02:00
parent 6e03b4005d
commit f65d71dd5b
4 changed files with 57 additions and 38 deletions

View file

@ -383,7 +383,7 @@ impl_node_port_enum_params(void *object, int seq,
param = spa_pod_builder_add_object(&b, param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, id, SPA_TYPE_OBJECT_ParamBuffers, id,
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(this->blocks),
SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
this->props.max_latency * this->frame_size, this->props.max_latency * this->frame_size,
this->props.min_latency * this->frame_size, this->props.min_latency * this->frame_size,

View file

@ -384,7 +384,7 @@ impl_node_port_enum_params(void *object, int seq,
param = spa_pod_builder_add_object(&b, param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, id, SPA_TYPE_OBJECT_ParamBuffers, id,
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS),
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(this->blocks),
SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
this->props.max_latency * this->frame_size, this->props.max_latency * this->frame_size,
this->props.min_latency * this->frame_size, this->props.min_latency * this->frame_size,

View file

@ -114,12 +114,13 @@ static const struct format_info format_info[] = {
{ SPA_AUDIO_FORMAT_F64_BE, SPA_AUDIO_FORMAT_F64P, SND_PCM_FORMAT_FLOAT64_BE}, { SPA_AUDIO_FORMAT_F64_BE, SPA_AUDIO_FORMAT_F64P, SND_PCM_FORMAT_FLOAT64_BE},
}; };
static snd_pcm_format_t spa_format_to_alsa(uint32_t format) static snd_pcm_format_t spa_format_to_alsa(uint32_t format, bool *planar)
{ {
size_t i; size_t i;
for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) { for (i = 0; i < SPA_N_ELEMENTS(format_info); i++) {
if (format_info[i].spa_format == format) *planar = format_info[i].spa_pformat == format;
if (format_info[i].spa_format == format || *planar)
return format_info[i].format; return format_info[i].format;
} }
return SND_PCM_FORMAT_UNKNOWN; return SND_PCM_FORMAT_UNKNOWN;
@ -406,7 +407,7 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
struct spa_audio_info_raw *info = &fmt->info.raw; struct spa_audio_info_raw *info = &fmt->info.raw;
snd_pcm_t *hndl; snd_pcm_t *hndl;
unsigned int periods; unsigned int periods;
bool match = true; bool match = true, planar;
if ((err = spa_alsa_open(state)) < 0) if ((err = spa_alsa_open(state)) < 0)
return err; return err;
@ -416,24 +417,29 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
snd_pcm_hw_params_alloca(&params); snd_pcm_hw_params_alloca(&params);
/* choose all parameters */ /* choose all parameters */
CHECK(snd_pcm_hw_params_any(hndl, params), "Broken configuration for playback: no configurations available"); CHECK(snd_pcm_hw_params_any(hndl, params), "Broken configuration for playback: no configurations available");
/* set hardware resampling */ /* set hardware resampling, no resample */
CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample"); CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample");
/* set the interleaved read/write format */
CHECK(snd_pcm_hw_params_set_access(hndl, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access"); /* get format info */
format = spa_format_to_alsa(info->format, &planar);
if (format == SND_PCM_FORMAT_UNKNOWN) {
spa_log_warn(state->log, NAME" %p: unknown format %u", state, info->format);
return -EINVAL;
}
/* set the interleaved/planar read/write format */
CHECK(snd_pcm_hw_params_set_access(hndl, params,
planar ? SND_PCM_ACCESS_MMAP_NONINTERLEAVED
: SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access");
/* disable ALSA wakeups, we use a timer */ /* disable ALSA wakeups, we use a timer */
if (snd_pcm_hw_params_can_disable_period_wakeup(params)) if (snd_pcm_hw_params_can_disable_period_wakeup(params))
CHECK(snd_pcm_hw_params_set_period_wakeup(hndl, params, 0), "set_period_wakeup"); CHECK(snd_pcm_hw_params_set_period_wakeup(hndl, params, 0), "set_period_wakeup");
/* set the sample format */ /* set the sample format */
format = spa_format_to_alsa(info->format); spa_log_debug(state->log, NAME" %p: Stream parameters are %iHz, %s %s, %i channels",
if (format == SND_PCM_FORMAT_UNKNOWN) { state, info->rate, snd_pcm_format_name(format),
spa_log_warn(state->log, NAME" %p: unknown format %u", state, info->format); planar ? "planar" : "interleaved", info->channels);
return -EINVAL;
}
spa_log_debug(state->log, NAME" %p: Stream parameters are %iHz, %s, %i channels",
state, info->rate, snd_pcm_format_name(format), info->channels);
CHECK(snd_pcm_hw_params_set_format(hndl, params, format), "set_format"); CHECK(snd_pcm_hw_params_set_format(hndl, params, format), "set_format");
/* set the count of channels */ /* set the count of channels */
@ -463,7 +469,12 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
state->format = format; state->format = format;
state->channels = info->channels; state->channels = info->channels;
state->rate = info->rate; state->rate = info->rate;
state->frame_size = info->channels * (snd_pcm_format_physical_width(format) / 8); state->frame_size = snd_pcm_format_physical_width(format) / 8;
state->blocks = 1;
if (planar)
state->blocks *= info->channels;
else
state->frame_size *= info->channels;
dir = 0; dir = 0;
period_size = 1024; period_size = 1024;
@ -473,12 +484,13 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_
state->period_frames = period_size; state->period_frames = period_size;
periods = state->buffer_frames / state->period_frames; periods = state->buffer_frames / state->period_frames;
spa_log_info(state->log, NAME" %s (%s): format:%s rate:%d channels:%d " spa_log_info(state->log, NAME" %s (%s): format:%s %s rate:%d channels:%d "
"buffer frames %lu, period frames %lu, periods %u, frame_size %zd", "buffer frames %lu, period frames %lu, periods %u, frame_size %zd",
state->props.device, state->props.device,
state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback", state->stream == SND_PCM_STREAM_CAPTURE ? "capture" : "playback",
snd_pcm_format_name(state->format), state->rate, state->channels, snd_pcm_format_name(state->format), planar ? "planar" : "interleaved",
state->buffer_frames, state->period_frames, periods, state->frame_size); state->rate, state->channels, state->buffer_frames, state->period_frames,
periods, state->frame_size);
/* write the parameters to device */ /* write the parameters to device */
CHECK(snd_pcm_hw_params(hndl, params), "set_hw_params"); CHECK(snd_pcm_hw_params(hndl, params), "set_hw_params");
@ -783,14 +795,11 @@ again:
size_t n_bytes, n_frames; size_t n_bytes, n_frames;
struct buffer *b; struct buffer *b;
struct spa_data *d; struct spa_data *d;
uint32_t index, offs, avail, size, maxsize, l0, l1; uint32_t i, index, offs, avail, size, maxsize, l0, l1;
b = spa_list_first(&state->ready, struct buffer, link); b = spa_list_first(&state->ready, struct buffer, link);
d = b->buf->datas; d = b->buf->datas;
dst = SPA_MEMBER(my_areas[0].addr, off * state->frame_size, uint8_t);
src = d[0].data;
size = d[0].chunk->size; size = d[0].chunk->size;
maxsize = d[0].maxsize; maxsize = d[0].maxsize;
@ -805,10 +814,14 @@ again:
l0 = SPA_MIN(n_bytes, maxsize - offs); l0 = SPA_MIN(n_bytes, maxsize - offs);
l1 = n_bytes - l0; l1 = n_bytes - l0;
spa_memcpy(dst, src + offs, l0); for (i = 0; i < b->buf->n_datas; i++) {
if (SPA_UNLIKELY(l1 > 0)) dst = SPA_MEMBER(my_areas[i].addr, off * state->frame_size, uint8_t);
spa_memcpy(dst + l0, src, l1); src = d[i].data;
spa_memcpy(dst, src + offs, l0);
if (SPA_UNLIKELY(l1 > 0))
spa_memcpy(dst + l0, src, l1);
}
state->ready_offset += n_bytes; state->ready_offset += n_bytes;
if (state->ready_offset >= size) { if (state->ready_offset >= size) {
@ -892,7 +905,7 @@ push_frames(struct state *state,
size_t n_bytes, left; size_t n_bytes, left;
struct buffer *b; struct buffer *b;
struct spa_data *d; struct spa_data *d;
uint32_t avail, l0, l1; uint32_t i, avail, l0, l1;
b = spa_list_first(&state->free, struct buffer, link); b = spa_list_first(&state->free, struct buffer, link);
spa_list_remove(&b->link); spa_list_remove(&b->link);
@ -914,18 +927,23 @@ push_frames(struct state *state,
l0 = SPA_MIN(n_bytes, left * state->frame_size); l0 = SPA_MIN(n_bytes, left * state->frame_size);
l1 = n_bytes - l0; l1 = n_bytes - l0;
src = SPA_MEMBER(my_areas[0].addr, offset * state->frame_size, uint8_t); for (i = 0; i < b->buf->n_datas; i++) {
spa_memcpy(d[0].data, src, l0); src = SPA_MEMBER(my_areas[i].addr, offset * state->frame_size, uint8_t);
if (l1 > 0) spa_memcpy(d[i].data, src, l0);
spa_memcpy(SPA_MEMBER(d[0].data, l0, void), my_areas[0].addr, l1); if (l1 > 0)
spa_memcpy(SPA_MEMBER(d[i].data, l0, void), my_areas[i].addr, l1);
d[i].chunk->offset = 0;
d[i].chunk->size = n_bytes;
d[i].chunk->stride = state->frame_size;
}
} else { } else {
memset(d[0].data, 0, n_bytes); for (i = 0; i < b->buf->n_datas; i++) {
memset(d[i].data, 0, n_bytes);
d[i].chunk->offset = 0;
d[i].chunk->size = n_bytes;
d[i].chunk->stride = state->frame_size;
}
} }
d[0].chunk->offset = 0;
d[0].chunk->size = n_bytes;
d[0].chunk->stride = state->frame_size;
spa_list_append(&state->ready, &b->link); spa_list_append(&state->ready, &b->link);
} }
return total_frames - keep; return total_frames - keep;

View file

@ -107,6 +107,7 @@ struct state {
int rate; int rate;
int channels; int channels;
size_t frame_size; size_t frame_size;
int blocks;
int rate_denom; int rate_denom;
uint32_t delay; uint32_t delay;
uint32_t read_size; uint32_t read_size;