-
Notifications
You must be signed in to change notification settings - Fork 350
Audio: Selector: Add support for multiple up/down-mix profiles #10613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -572,9 +572,9 @@ static void build_config(struct comp_data *cd, struct module_config *cfg) | |
| cd->config.out_channels_count = out_fmt->channels_count; | ||
|
|
||
| /* Build default coefficient array (unity Q10 on diagonal, i.e. pass-through mode) */ | ||
| memset(&cd->coeffs_config, 0, sizeof(cd->coeffs_config)); | ||
| memset(cd->coeffs_config, 0, sizeof(*cd->coeffs_config)); | ||
| for (i = 0; i < MIN(SEL_SOURCE_CHANNELS_MAX, SEL_SINK_CHANNELS_MAX); i++) | ||
| cd->coeffs_config.coeffs[i][i] = 1 << 10; | ||
| cd->coeffs_config->coeffs[i][i] = SEL_COEF_ONE_Q10; | ||
| } | ||
|
|
||
| static int selector_init(struct processing_module *mod) | ||
|
|
@@ -620,6 +620,18 @@ static int selector_init(struct processing_module *mod) | |
| cd->sel_ipc4_cfg.init_payload_fmt = payload_fmt; | ||
| md->private = cd; | ||
|
|
||
| /* Allocate space for max number of configurations. */ | ||
| cd->multi_coeffs_config_size = | ||
| SEL_MAX_NUM_CONFIGS * sizeof(struct ipc4_selector_coeffs_config); | ||
| cd->multi_coeffs_config = mod_zalloc(mod, cd->multi_coeffs_config_size); | ||
| if (!cd->multi_coeffs_config) { | ||
| mod_free(mod, cd); | ||
| return -ENOMEM; | ||
| } | ||
|
|
||
| /* Default configuration is set to first configuration */ | ||
| cd->coeffs_config = &cd->multi_coeffs_config[0]; | ||
|
|
||
| if (payload_fmt == IPC4_SEL_INIT_PAYLOAD_BASE_WITH_EXT) { | ||
| size_t size = sizeof(struct sof_selector_ipc4_pin_config); | ||
|
|
||
|
|
@@ -733,6 +745,7 @@ static int selector_free(struct processing_module *mod) | |
|
|
||
| comp_dbg(mod->dev, "entry"); | ||
|
|
||
| mod_free(mod, cd->multi_coeffs_config); | ||
| mod_free(mod, cd); | ||
|
|
||
| return 0; | ||
|
|
@@ -769,12 +782,26 @@ static int selector_set_config(struct processing_module *mod, uint32_t config_id | |
| size_t response_size) | ||
| { | ||
| struct comp_data *cd = module_get_private_data(mod); | ||
| int n; | ||
|
|
||
| if (config_id == IPC4_SELECTOR_COEFFS_CONFIG_ID) { | ||
| if (data_offset_size != sizeof(cd->coeffs_config)) | ||
| if (data_offset_size > cd->multi_coeffs_config_size) { | ||
| comp_err(mod->dev, "The configuration blob is too large"); | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| memcpy_s(&cd->coeffs_config, sizeof(cd->coeffs_config), fragment, data_offset_size); | ||
| /* The size must be N times the coefficient vectors size of one channels | ||
| * up/down mix profile. | ||
| */ | ||
| n = data_offset_size / sizeof(struct ipc4_selector_coeffs_config); | ||
| if (data_offset_size != n * sizeof(struct ipc4_selector_coeffs_config)) { | ||
| comp_err(mod->dev, "Invalid configuration size."); | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| memcpy_s(cd->multi_coeffs_config, cd->multi_coeffs_config_size, | ||
| fragment, data_offset_size); | ||
| cd->num_configs = n; | ||
| return 0; | ||
| } | ||
|
|
||
|
|
@@ -799,18 +826,107 @@ static int selector_process(struct processing_module *mod, | |
| struct output_stream_buffer *output_buffers, | ||
| int num_output_buffers) | ||
| { | ||
| struct audio_stream *source; | ||
| struct audio_stream *sink; | ||
| struct comp_data *cd = module_get_private_data(mod); | ||
| uint32_t avail_frames = input_buffers[0].size; | ||
| uint32_t samples; | ||
|
|
||
| comp_dbg(mod->dev, "entry"); | ||
|
|
||
| if (cd->passthrough) { | ||
| source = input_buffers->data; | ||
| sink = output_buffers->data; | ||
| samples = avail_frames * audio_stream_get_channels(source); | ||
| audio_stream_copy(source, 0, sink, 0, samples); | ||
| module_update_buffer_position(input_buffers, output_buffers, avail_frames); | ||
| return 0; | ||
| } | ||
|
|
||
| if (avail_frames) | ||
| /* copy selected channels from in to out */ | ||
| cd->sel_func(mod, input_buffers, output_buffers, avail_frames); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * \brief Get mix coefficients set from configuration blob with multiple coefficients sets. | ||
| * Also activate more efficient pass-through copy mode if the coefficients indicate 1:1 | ||
| * copy from source to sink. | ||
| * \param[in,out] mod Selector base module device. | ||
| * \param[in] source_channels Number of channels in source. | ||
| * \param[in] sink_channels Number of channels in sink. | ||
| * | ||
| * \return Error code. | ||
| */ | ||
| static int selector_find_coefficients(struct processing_module *mod, int source_channels, | ||
| int sink_channels) | ||
| { | ||
| struct comp_data *cd = module_get_private_data(mod); | ||
| struct comp_dev *dev = mod->dev; | ||
| int i, j; | ||
| int16_t coef; | ||
| bool found; | ||
|
|
||
| /* The first config for coefficients always exists. It originates from configuration | ||
| * blob for a single mix profile or from default values (1.0) from set_selector_params(). | ||
| */ | ||
| cd->coeffs_config = cd->multi_coeffs_config; | ||
|
|
||
| if (cd->num_configs > 1) { | ||
| /* A pass-through blob with two or more configurations is for the max | ||
| * channels (8) amount. | ||
| */ | ||
| if (source_channels == sink_channels) { | ||
| source_channels = SEL_SOURCE_CHANNELS_MAX; | ||
| sink_channels = SEL_SINK_CHANNELS_MAX; | ||
| } | ||
|
Comment on lines
+877
to
+884
|
||
|
|
||
| found = false; | ||
| for (i = 0; i < cd->num_configs; i++) { | ||
| if (cd->multi_coeffs_config[i].source_channels_count == source_channels && | ||
| cd->multi_coeffs_config[i].sink_channels_count == sink_channels) { | ||
| cd->coeffs_config = &cd->multi_coeffs_config[i]; | ||
| found = true; | ||
singalsu marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!found) { | ||
| comp_err(dev, "No mix coefficients found for %d to %d channels.", | ||
| source_channels, sink_channels); | ||
| return -EINVAL; | ||
| } | ||
| } | ||
|
|
||
| /* The pass-through copy function can be used if coefficients are a unit matrix for | ||
| * 1:1 stream copy. | ||
| */ | ||
| if (source_channels == sink_channels) { | ||
| cd->passthrough = true; | ||
| for (i = 0; i < sink_channels; i++) { | ||
| for (j = 0; j < source_channels; j++) { | ||
| coef = cd->coeffs_config->coeffs[i][j]; | ||
| if ((i == j && coef != SEL_COEF_ONE_Q10) || (i != j && coef != 0)) { | ||
| cd->passthrough = false; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } else { | ||
| cd->passthrough = false; | ||
| } | ||
|
|
||
| if (cd->passthrough) | ||
| comp_info(dev, "Passthrough mode."); | ||
| else | ||
| comp_info(dev, "Using coefficients for %d to %d channels.", | ||
| source_channels, sink_channels); | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| /** | ||
| * \brief Prepares selector component for processing. | ||
| * \param[in,out] mod Selector base module device. | ||
|
|
@@ -825,6 +941,8 @@ static int selector_prepare(struct processing_module *mod, | |
| struct comp_dev *dev = mod->dev; | ||
| struct comp_buffer *sinkb, *sourceb; | ||
| size_t sink_size; | ||
| unsigned int source_channels; | ||
| unsigned int sink_channels; | ||
| int ret; | ||
|
|
||
| comp_dbg(dev, "entry"); | ||
|
|
@@ -855,9 +973,9 @@ static int selector_prepare(struct processing_module *mod, | |
| * proper number of channels [1] for selector to actually | ||
| * reduce channel count between source and sink | ||
| */ | ||
| comp_info(dev, "source sink channel = %u %u", | ||
| audio_stream_get_channels(&sourceb->stream), | ||
| audio_stream_get_channels(&sinkb->stream)); | ||
| source_channels = audio_stream_get_channels(&sourceb->stream); | ||
| sink_channels = audio_stream_get_channels(&sinkb->stream); | ||
| comp_dbg(dev, "source sink channel = %u %u", source_channels, sink_channels); | ||
|
|
||
| sink_size = audio_stream_get_size(&sinkb->stream); | ||
|
|
||
|
|
@@ -891,7 +1009,7 @@ static int selector_prepare(struct processing_module *mod, | |
| return -EINVAL; | ||
| } | ||
|
|
||
| return 0; | ||
| return selector_find_coefficients(mod, source_channels, sink_channels); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
memcpy_s()return value is ignored here, and there is no check thatfragment_sizeactually coversdata_offset_size. Iffragment_size < data_offset_size(or overlap is detected),memcpy_s()will fail and the selector will continue using old coefficients without an error. Checkfragment_size/posand handle a non-zeromemcpy_s()return (log + return -EINVAL).