diff --git a/src/audio/buffers/comp_buffer.c b/src/audio/buffers/comp_buffer.c index 9aa5c0f0ab10..cfbbc5b8a6b5 100644 --- a/src/audio/buffers/comp_buffer.c +++ b/src/audio/buffers/comp_buffer.c @@ -100,6 +100,13 @@ static int comp_buffer_commit_buffer(struct sof_sink *sink, size_t commit_size) return 0; } +static int comp_buffer_sink_get_state(struct sof_sink *sink) +{ + struct comp_buffer *buffer = comp_buffer_get_from_sink(sink); + + return comp_get_state(comp_buffer_get_sink_component(buffer)); +} + static int comp_buffer_set_ipc_params(struct sof_audio_buffer *audio_buffer, struct sof_ipc_stream_params *params, bool force_update) @@ -189,6 +196,7 @@ APP_TASK_DATA static const struct sink_ops comp_buffer_sink_ops = { .on_audio_format_set = audio_buffer_sink_on_audio_format_set, .set_alignment_constants = audio_buffer_sink_set_alignment_constants, .get_lft = audio_buffer_sink_get_lft, + .get_state = comp_buffer_sink_get_state, }; static const struct audio_buffer_ops audio_buffer_ops = { diff --git a/src/audio/mux/mux.c b/src/audio/mux/mux.c index 1476779e7324..04fee306e760 100644 --- a/src/audio/mux/mux.c +++ b/src/audio/mux/mux.c @@ -203,8 +203,8 @@ static struct mux_look_up *get_lookup_table(struct comp_dev *dev, struct comp_da } static void demux_prepare_active_look_up(struct comp_data *cd, - struct audio_stream *sink, - const struct audio_stream *source, + struct sof_sink *sink, + struct sof_source *source, struct mux_look_up *look_up) { int elem; @@ -212,8 +212,8 @@ static void demux_prepare_active_look_up(struct comp_data *cd, /* init pointers */ for (elem = 0; elem < look_up->num_elems; elem++) { - if (look_up->copy_elem[elem].in_ch >= audio_stream_get_channels(source) || - look_up->copy_elem[elem].out_ch >= audio_stream_get_channels(sink)) + if (look_up->copy_elem[elem].in_ch >= source_get_channels(source) || + look_up->copy_elem[elem].out_ch >= sink_get_channels(sink)) continue; cd->active_lookup.copy_elem[active_elem] = look_up->copy_elem[elem]; @@ -225,56 +225,78 @@ static void demux_prepare_active_look_up(struct comp_data *cd, /* process and copy stream data from source to sink buffers */ static int demux_process(struct processing_module *mod, - struct input_stream_buffer *input_buffers, int num_input_buffers, - struct output_stream_buffer *output_buffers, int num_output_buffers) + struct sof_source **sources, int num_of_sources, + struct sof_sink **sinks, int num_of_sinks) { struct comp_data *cd = module_get_private_data(mod); struct comp_dev *dev = mod->dev; - struct comp_buffer *sink; - struct audio_stream *sinks_stream[MUX_MAX_STREAMS] = { NULL }; - struct mux_look_up *look_ups[MUX_MAX_STREAMS] = { NULL }; - int frames; - int sink_bytes; - int source_bytes; + struct sof_source *source = sources[0]; + const void *source_data; + const void *source_start; + size_t source_size; + size_t source_bytes; + uint32_t frames; + int ret; int i; comp_dbg(dev, "entry"); - /* align sink streams with their respective configurations */ - comp_dev_for_each_consumer(dev, sink) { - if (comp_buffer_get_sink_state(sink) == dev->state) { - i = get_stream_index(dev, cd, buffer_pipeline_id(sink)); - /* return if index wrong */ - if (i < 0) { - return i; - } + /* if there are no sinks active, then there is nothing to do */ + if (num_of_sinks == 0) + return 0; - look_ups[i] = get_lookup_table(dev, cd, buffer_pipeline_id(sink)); - sinks_stream[i] = &sink->stream; - } + /* the same number of frames is distributed to every sink, so it is + * limited by both the source availability and every active sink's free + * space + */ + frames = source_get_data_frames_available(source); + for (i = 0; i < num_of_sinks; i++) { + if (sink_get_state(sinks[i]) != dev->state) + continue; + + frames = MIN(frames, sink_get_free_frames(sinks[i])); } - /* if there are no sinks active, then sinks[] is also empty */ - if (num_output_buffers == 0) + if (!frames) return 0; - frames = input_buffers[0].size; - source_bytes = frames * audio_stream_frame_bytes(mod->input_buffers[0].data); - sink_bytes = frames * audio_stream_frame_bytes(mod->output_buffers[0].data); + /* the source is read-only and shared by all sinks, so it is obtained + * once here and released once after all sinks have been served + */ + source_bytes = frames * source_get_frame_bytes(source); + ret = source_get_data(source, source_bytes, &source_data, &source_start, + &source_size); + if (ret) + return ret; /* produce output, one sink at a time */ - for (i = 0; i < num_output_buffers; i++) { - if (sinks_stream[i]) { - demux_prepare_active_look_up(cd, sinks_stream[i], - input_buffers[0].data, look_ups[i]); - cd->demux(dev, sinks_stream[i], input_buffers[0].data, - frames, &cd->active_lookup); + for (i = 0; i < num_of_sinks; i++) { + struct sof_sink *sink = sinks[i]; + uint32_t pipeline_id = sink_get_pipeline_id(sink); + struct mux_look_up *look_up; + + /* skip sinks that are not in the same state as the component */ + if (sink_get_state(sink) != dev->state) + continue; + + /* return if configuration for this pipeline is missing */ + if (get_stream_index(dev, cd, pipeline_id) < 0) { + source_release_data(source, 0); + return -EINVAL; + } + + look_up = get_lookup_table(dev, cd, pipeline_id); + demux_prepare_active_look_up(cd, sink, source, look_up); + ret = cd->demux(dev, sink, source, source_data, source_start, + source_size, frames, &cd->active_lookup); + if (ret) { + source_release_data(source, 0); + return ret; } - mod->output_buffers[i].size = sink_bytes; } - /* Update consumed */ - mod->input_buffers[0].consumed = source_bytes; + /* consume the processed data from the source */ + source_release_data(source, source_bytes); return 0; } @@ -454,7 +476,7 @@ static const struct module_interface demux_interface = { .set_configuration = mux_set_config, .get_configuration = mux_get_config, .prepare = mux_prepare, - .process_audio_stream = demux_process, + .process = demux_process, .trigger = demux_trigger, .reset = mux_reset, .free = mux_free, diff --git a/src/audio/mux/mux.h b/src/audio/mux/mux.h index 0fb11d8300b5..7ade2ba1983d 100644 --- a/src/audio/mux/mux.h +++ b/src/audio/mux/mux.h @@ -66,9 +66,10 @@ struct mux_stream_data { uint8_t reserved2[3]; // padding to ensure proper alignment of following instances } __attribute__((packed, aligned(4))); -typedef void(*demux_func)(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *look_up); +typedef int(*demux_func)(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *look_up); typedef void(*mux_func)(struct comp_dev *dev, struct audio_stream *sink, const struct audio_stream **sources, uint32_t frames, struct mux_look_up *look_up); diff --git a/src/audio/mux/mux_generic.c b/src/audio/mux/mux_generic.c index 20d21afdffb1..823f7ffaf224 100644 --- a/src/audio/mux/mux_generic.c +++ b/src/audio/mux/mux_generic.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,48 +38,8 @@ static void mux_check_for_wrap(struct audio_stream *sink, } } -static void demux_check_for_wrap(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t elem; - - /* check sources and destinations for wrap */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].dest = - audio_stream_wrap(sink, lookup->copy_elem[elem].dest); - lookup->copy_elem[elem].src = - audio_stream_wrap(source, lookup->copy_elem[elem].src); - } -} - #if CONFIG_FORMAT_S16LE -static uint32_t demux_calc_frames_without_wrap_s16(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t frames; - uint32_t min_frames; - void *ptr; - - /* for demux we process each source buffer separately - dest/src for - * each copy_elem refers to the same sink/source buffer, so min_frames - * calculation based only on lookup table first element is sufficient. - */ - ptr = (int16_t *)lookup->copy_elem[0].dest - - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); - - ptr = (int16_t *)lookup->copy_elem[0].src - - lookup->copy_elem[0].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); - - min_frames = (frames < min_frames) ? frames : min_frames; - - return min_frames; -} - static uint32_t mux_calc_frames_without_wrap_s16(struct audio_stream *sink, const struct audio_stream **sources, struct mux_look_up *lookup) @@ -131,57 +92,57 @@ static void mux_init_look_up_pointers_s16(struct audio_stream *sink, } } -static void demux_init_look_up_pointers_s16(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) +static int demux_s16le(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *lookup) { + const int16_t *x_start = source_start; + const int16_t *x_end = x_start + (source_size >> 1); + int16_t *y, *y_start, *y_end; + int y_size; + int source_channels = source_get_channels(source); + int sink_channels = sink_get_channels(sink); + int bytes = frames * sink_get_frame_bytes(sink); uint32_t elem; - - /* init pointers */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].src = (int16_t *)audio_stream_get_rptr(source) + - lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); - - lookup->copy_elem[elem].dest = (int16_t *)audio_stream_get_wptr(sink) + - lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); - } -} - -/** - * Source stream are routed to sinks with regard to look up table based on - * routing bitmasks from mux_stream_data structures array. Each sink channel - * has it's own lookup[].copy_elem describing source and sink fragment of - * memory featured in copying. - * - * @param[in] dev Component device - * @param[in,out] sink Destination buffer. - * @param[in,out] sources Array of source buffers. - * @param[in] frames Number of frames to process. - * @param[in] lookup mux look up table. - */ -static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *lookup) -{ uint32_t i; - int16_t *src; - int16_t *dst; - uint32_t elem; - uint32_t frames_without_wrap; + int ret; comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) - return; + return 0; - demux_init_look_up_pointers_s16(sink, source, lookup); + /* obtain the sink circular buffer for this output stream */ + ret = sink_get_buffer_s16(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; - while (frames) { - frames_without_wrap = - demux_calc_frames_without_wrap_s16(sink, source, lookup); + y_end = y_start + y_size; + + /* init pointers based on the freshly obtained buffers */ + for (elem = 0; elem < lookup->num_elems; elem++) { + lookup->copy_elem[elem].src = (int16_t *)source_data + + lookup->copy_elem[elem].in_ch; + lookup->copy_elem[elem].src_inc = source_channels; + lookup->copy_elem[elem].dest = y + lookup->copy_elem[elem].out_ch; + lookup->copy_elem[elem].dest_inc = sink_channels; + } + + while (frames) { + int16_t *src = (int16_t *)lookup->copy_elem[0].src - + lookup->copy_elem[0].in_ch; + int16_t *dst = (int16_t *)lookup->copy_elem[0].dest - + lookup->copy_elem[0].out_ch; + uint32_t source_frames_without_wrap = + circ_buf_frames_without_wrap(src, x_end, sizeof(*src), source_channels); + uint32_t sink_frames_without_wrap = + circ_buf_frames_without_wrap(dst, y_end, sizeof(*dst), sink_channels); + uint32_t frames_without_wrap; + + frames_without_wrap = MIN(source_frames_without_wrap, + sink_frames_without_wrap); frames_without_wrap = MIN(frames, frames_without_wrap); for (i = 0; i < frames_without_wrap; i++) { @@ -196,10 +157,24 @@ static void demux_s16le(struct comp_dev *dev, struct audio_stream *sink, } } - demux_check_for_wrap(sink, source, lookup); + /* check sources and destinations for wrap */ + for (elem = 0; elem < lookup->num_elems; elem++) { + src = (int16_t *)lookup->copy_elem[elem].src; + dst = (int16_t *)lookup->copy_elem[elem].dest; + if (src >= x_end) + src -= (source_size >> 1); + if (dst >= y_end) + dst -= y_size; + lookup->copy_elem[elem].src = src; + lookup->copy_elem[elem].dest = dst; + } frames -= frames_without_wrap; } + + sink_commit_buffer(sink, bytes); + + return 0; } /** @@ -287,29 +262,6 @@ static uint32_t mux_calc_frames_without_wrap_s32(struct audio_stream *sink, return min_frames; } -static uint32_t demux_calc_frames_without_wrap_s32(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t frames; - uint32_t min_frames; - void *ptr; - - /* for demux we process each source buffer separately - dest/src for - * each copy_elem refers to the same sink/source buffer, so min_frames - * calculation based only on lookup table first element is sufficient. - */ - ptr = (int32_t *)lookup->copy_elem[0].dest - lookup->copy_elem[0].out_ch; - min_frames = audio_stream_frames_without_wrap(sink, ptr); - - ptr = (int32_t *)lookup->copy_elem[0].src - lookup->copy_elem[0].in_ch; - frames = audio_stream_frames_without_wrap(source, ptr); - - min_frames = (frames < min_frames) ? frames : min_frames; - - return min_frames; -} - static void mux_init_look_up_pointers_s32(struct audio_stream *sink, const struct audio_stream **sources, struct mux_look_up *lookup) @@ -331,57 +283,73 @@ static void mux_init_look_up_pointers_s32(struct audio_stream *sink, } } -static void demux_init_look_up_pointers_s32(struct audio_stream *sink, - const struct audio_stream *source, - struct mux_look_up *lookup) -{ - uint32_t elem; - - /* init pointers */ - for (elem = 0; elem < lookup->num_elems; elem++) { - lookup->copy_elem[elem].src = (int32_t *)audio_stream_get_rptr(source) + - lookup->copy_elem[elem].in_ch; - lookup->copy_elem[elem].src_inc = audio_stream_get_channels(source); - - lookup->copy_elem[elem].dest = (int32_t *)audio_stream_get_wptr(sink) + - lookup->copy_elem[elem].out_ch; - lookup->copy_elem[elem].dest_inc = audio_stream_get_channels(sink); - } -} - /** - * Source stream are routed to sinks with regard to look up table based on + * Source stream is routed to sinks with regard to look up table based on * routing bitmasks from mux_stream_data structures array. Each sink channel * has it's own lookup[].copy_elem describing source and sink fragment of * memory featured in copying. * * @param[in] dev Component device - * @param[in,out] sink Destination buffer. - * @param[in,out] sources Array of source buffers. + * @param[in,out] sink Destination sink (sof_sink handle). + * @param[in] source Source handle, used for channel count metadata only. + * @param[in] source_data Read pointer into the source circular buffer. + * @param[in] source_start Start address of the source circular buffer. + * @param[in] source_size Size of the source circular buffer in bytes. * @param[in] frames Number of frames to process. * @param[in] lookup mux look up table. + * @return 0 on success, negative error code otherwise. */ -static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, - const struct audio_stream *source, uint32_t frames, - struct mux_look_up *lookup) +static int demux_s32le(struct comp_dev *dev, struct sof_sink *sink, + struct sof_source *source, const void *source_data, + const void *source_start, size_t source_size, + uint32_t frames, struct mux_look_up *lookup) { - uint32_t i; - int32_t *src; - int32_t *dst; + const int32_t *x_start = source_start; + const int32_t *x_end = x_start + (source_size >> 2); + int32_t *y, *y_start, *y_end; + int y_size; + int source_channels = source_get_channels(source); + int sink_channels = sink_get_channels(sink); + int bytes = frames * sink_get_frame_bytes(sink); uint32_t elem; - uint32_t frames_without_wrap; + uint32_t i; + int ret; comp_dbg(dev, "entry"); if (!lookup || !lookup->num_elems) - return; + return 0; - demux_init_look_up_pointers_s32(sink, source, lookup); + /* obtain the sink circular buffer for this output stream */ + ret = sink_get_buffer_s32(sink, bytes, &y, &y_start, &y_size); + if (ret) + return ret; - while (frames) { - frames_without_wrap = - demux_calc_frames_without_wrap_s32(sink, source, lookup); + y_end = y_start + y_size; + + /* init pointers based on the freshly obtained buffers */ + for (elem = 0; elem < lookup->num_elems; elem++) { + lookup->copy_elem[elem].src = (int32_t *)source_data + + lookup->copy_elem[elem].in_ch; + lookup->copy_elem[elem].src_inc = source_channels; + + lookup->copy_elem[elem].dest = y + lookup->copy_elem[elem].out_ch; + lookup->copy_elem[elem].dest_inc = sink_channels; + } + while (frames) { + int32_t *src = (int32_t *)lookup->copy_elem[0].src - + lookup->copy_elem[0].in_ch; + int32_t *dst = (int32_t *)lookup->copy_elem[0].dest - + lookup->copy_elem[0].out_ch; + uint32_t source_frames_without_wrap = + circ_buf_frames_without_wrap(src, x_end, sizeof(*src), source_channels); + uint32_t sink_frames_without_wrap = + circ_buf_frames_without_wrap(dst, y_end, sizeof(*dst), sink_channels); + uint32_t frames_without_wrap; + + frames_without_wrap = MIN(source_frames_without_wrap, + sink_frames_without_wrap); frames_without_wrap = MIN(frames, frames_without_wrap); for (i = 0; i < frames_without_wrap; i++) { @@ -396,10 +364,24 @@ static void demux_s32le(struct comp_dev *dev, struct audio_stream *sink, } } - demux_check_for_wrap(sink, source, lookup); + /* check sources and destinations for wrap */ + for (elem = 0; elem < lookup->num_elems; elem++) { + src = (int32_t *)lookup->copy_elem[elem].src; + dst = (int32_t *)lookup->copy_elem[elem].dest; + if (src >= x_end) + src -= (source_size >> 2); + if (dst >= y_end) + dst -= y_size; + lookup->copy_elem[elem].src = src; + lookup->copy_elem[elem].dest = dst; + } frames -= frames_without_wrap; } + + sink_commit_buffer(sink, bytes); + + return 0; } /** diff --git a/src/include/module/audio/audio_stream.h b/src/include/module/audio/audio_stream.h index e032ef322f85..f4f79718f565 100644 --- a/src/include/module/audio/audio_stream.h +++ b/src/include/module/audio/audio_stream.h @@ -74,4 +74,22 @@ struct sof_audio_stream_params { enum sof_audio_buffer_state state; /**< audio stream state */ }; +/** + * @brief Number of whole frames available before a circular buffer wraps. + * + * Computes (end - began) / channels expressed in frames. The pointer distance + * is evaluated in bytes so the helper works for any sample width. + * + * @param began current position in the circular buffer + * @param end one past the last sample of the circular buffer + * @param sample_bytes size of a single sample in bytes (2 for s16, 4 for s32) + * @param channels number of channels, i.e. samples per frame + * @return number of frames that can be processed without wrapping + */ +static inline int circ_buf_frames_without_wrap(const void *began, const void *end, + int sample_bytes, int channels) +{ + return ((const char *)end - (const char *)began) / sample_bytes / channels; +} + #endif /* __MODULE_AUDIO_AUDIO_STREAM_H__ */ diff --git a/src/include/module/audio/sink_api.h b/src/include/module/audio/sink_api.h index 920087d6f8b3..b4284a23025a 100644 --- a/src/include/module/audio/sink_api.h +++ b/src/include/module/audio/sink_api.h @@ -83,6 +83,13 @@ struct sink_ops { */ uint32_t (*get_lft)(struct sof_sink *sink); + /** + * OPTIONAL: get the state of the component consuming data from this sink + * + * see comment of sink_get_state() + */ + int (*get_state)(struct sof_sink *sink); + /** * OPTIONAL: Notification to the sink implementation about changes in audio format * @@ -307,6 +314,25 @@ static inline uint32_t sink_get_pipeline_id(struct sof_sink *sink) return sink->audio_stream_params->pipeline_id; } +/** + * @brief get the state of the component consuming data from this sink + * + * This allows a data producer to query the state of its downstream consumer + * without reaching into the underlying buffer implementation. + * + * @param sink a sink to be checked + * + * @return state of the consuming component, or COMP_STATE_NOT_EXIST (0) if there + * is no connected consumer or the sink implementation does not track it + */ +static inline int sink_get_state(struct sof_sink *sink) +{ + if (!sink->ops->get_state) + return 0; /* COMP_STATE_NOT_EXIST */ + + return sink->ops->get_state(sink); +} + /** * @brief hook to be called when a module connects to the API *