Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/sof-rebuild-processing-comp-blobs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ cd "$SOF_WORKSPACE"/sof/src/audio/eq_iir/tune; $OCTAVE sof_example_iir_bandsplit
cd "$SOF_WORKSPACE"/sof/src/audio/eq_iir/tune; $OCTAVE sof_example_spk_eq.m
cd "$SOF_WORKSPACE"/sof/src/audio/multiband_drc/tune; $OCTAVE sof_example_multiband_drc.m
cd "$SOF_WORKSPACE"/sof/src/audio/tdfb/tune; ./sof_example_all.sh
cd "$SOF_WORKSPACE"/sof/src/audio/selector/tune; $OCTAVE ./sof_selector_blobs.m
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add micsel test run to scripts/host-testbench.sh to get it tested regularly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also can we add to nocodec topology ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be currently useful as the only way to validate in a real device more complex channels changes. With HDA and SDW we are limited with DAI side to 2ch.

cd "$SOF_WORKSPACE"/sof/tools/tune/mfcc; $OCTAVE setup_mfcc.m
84 changes: 79 additions & 5 deletions src/audio/selector/selector_generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <sof/audio/buffer.h>
#include <sof/audio/component.h>
#include <sof/audio/format.h>
#include <sof/audio/selector.h>
#include <sof/common.h>
#include <ipc/stream.h>
Expand Down Expand Up @@ -191,7 +192,7 @@ static void process_frame_s16le(int16_t dst[], int dst_channels,
accum += (int32_t)src[j] * (int32_t)coeffs_config->coeffs[i][j];

/* shift out 10 LSbits with rounding to get 16-bit result */
dst[i] = (int16_t)((accum + (1 << 9)) >> 10);
dst[i] = sat_int16((accum + (1 << 9)) >> 10);
}
}

Expand Down Expand Up @@ -240,7 +241,80 @@ static void sel_s16le(struct processing_module *mod, struct input_stream_buffer
}
#endif /* CONFIG_FORMAT_S16LE */

#if CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE
#if CONFIG_FORMAT_S24LE
/**
* \brief Mixing routine for 24-bit, m channel input x n channel output single frame.
* \param[out] dst Sink buffer.
* \param[in] dst_channels Number of sink channels.
* \param[in] src Source data.
* \param[in] src_channels Number of source channels.
* \param[in] coeffs_config IPC4 micsel config with Q10 coefficients.
*/
static void process_frame_s24le(int32_t dst[], int dst_channels,
int32_t src[], int src_channels,
struct ipc4_selector_coeffs_config *coeffs_config)
{
int64_t accum;
int i, j;

for (i = 0; i < dst_channels; i++) {
accum = 0;
for (j = 0; j < src_channels; j++)
accum += (int64_t)src[j] * (int64_t)coeffs_config->coeffs[i][j];

/* accum is Q1.23 * Q6.10 --> Q7.33, shift right by 10 and
* saturate to get Q1.23.
*/
dst[i] = sat_int24((accum + (1 << 9)) >> 10);
}
}

/**
* \brief Channel selection for 24-bit, m channel input x n channel output data format.
* \param[in] mod Selector base module device.
* \param[in,out] bsource Source buffer.
* \param[in,out] bsink Sink buffer.
* \param[in] frames Number of frames to process.
*/
static void sel_s24le(struct processing_module *mod, struct input_stream_buffer *bsource,
struct output_stream_buffer *bsink, uint32_t frames)
{
struct comp_data *cd = module_get_private_data(mod);
struct audio_stream *source = bsource->data;
struct audio_stream *sink = bsink->data;
int32_t *src = audio_stream_get_rptr(source);
int32_t *dest = audio_stream_get_wptr(sink);
int nmax;
int i;
int n;
int processed = 0;
int source_frame_bytes = audio_stream_frame_bytes(source);
int sink_frame_bytes = audio_stream_frame_bytes(sink);
int n_chan_source = MIN(SEL_SOURCE_CHANNELS_MAX, audio_stream_get_channels(source));
int n_chan_sink = MIN(SEL_SINK_CHANNELS_MAX, audio_stream_get_channels(sink));

while (processed < frames) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like a simple for (processed = 0; processed < frames; processed += n)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like that syntax for this usage, n varies every iteration. Packs two lines but same instructions but with ambiguity for random code reader of when the increment is evaluated. I copied this from similar s32 function. Also this will change when the component is converted to source sink API.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The for loop is easier to vectorize for the CC as we can increment by a constant, not an issue for this PR, but long term we need to factor this in.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least we can remove the divisions from hot code in next improvements with source sink API. The convert to the new API would be in 2H tasks I think.

n = frames - processed;
nmax = audio_stream_bytes_without_wrap(source, src) / source_frame_bytes;
n = MIN(n, nmax);
nmax = audio_stream_bytes_without_wrap(sink, dest) / sink_frame_bytes;
n = MIN(n, nmax);
for (i = 0; i < n; i++) {
process_frame_s24le(dest, n_chan_sink, src, n_chan_source,
&cd->coeffs_config);
src += audio_stream_get_channels(source);
dest += audio_stream_get_channels(sink);
}
src = audio_stream_wrap(source, src);
dest = audio_stream_wrap(sink, dest);
processed += n;
}

module_update_buffer_position(bsource, bsink, frames);
}
#endif /* CONFIG_FORMAT_S24LE */

#if CONFIG_FORMAT_S32LE
/**
* \brief Mixing routine for 32-bit, m channel input x n channel output single frame.
* \param[out] dst Sink buffer.
Expand All @@ -262,7 +336,7 @@ static void process_frame_s32le(int32_t dst[], int dst_channels,
accum += (int64_t)src[j] * (int64_t)coeffs_config->coeffs[i][j];

/* shift out 10 LSbits with rounding to get 32-bit result */
dst[i] = (int32_t)((accum + (1 << 9)) >> 10);
dst[i] = sat_int32((accum + (1 << 9)) >> 10);
}
}

Expand Down Expand Up @@ -309,7 +383,7 @@ static void sel_s32le(struct processing_module *mod, struct input_stream_buffer

module_update_buffer_position(bsource, bsink, frames);
}
#endif /* CONFIG_FORMAT_S24LE || CONFIG_FORMAT_S32LE */
#endif /* CONFIG_FORMAT_S32LE */
#endif

const struct comp_func_map func_table[] = {
Expand All @@ -334,7 +408,7 @@ const struct comp_func_map func_table[] = {
{SOF_IPC_FRAME_S16_LE, 0, sel_s16le},
#endif
#if CONFIG_FORMAT_S24LE
{SOF_IPC_FRAME_S24_4LE, 0, sel_s32le},
{SOF_IPC_FRAME_S24_4LE, 0, sel_s24le},
#endif
#if CONFIG_FORMAT_S32LE
{SOF_IPC_FRAME_S32_LE, 0, sel_s32le},
Expand Down
174 changes: 174 additions & 0 deletions src/audio/selector/tune/sof_selector_blobs.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
% Export configuration blobs for Selector

% SPDX-License-Identifier: BSD-3-Clause
%
% Copyright (c) 2025, Intel Corporation.

function sof_selector_blobs()

% See ITU-R BS.775-4 for mix coefficient values
sof_selector_paths(true);

% Matrix for 1:1 pass-through
sel.rsvd0 = 0;
sel.rsvd1 = 0;
sel.coeffs = diag(ones(8, 1));
write_blob(sel, "passthrough");

% Stereo to mono downmix
sel.coeffs = zeros(8,8);
sel.coeffs(1, 1) = 0.7071;
sel.coeffs(1, 2) = 0.7071;
write_blob(sel, "downmix_stereo_to_mono");

% 5.1 to stereo downmix
fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6;
m = zeros(8,8);
m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000;
m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071;
sel.coeffs = m;
write_blob(sel, "downmix_51_to_stereo");
sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left
sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right
write_blob(sel, "downmix_51_to_stereo_with_lfe");

% 5.1 to mono downmix
fl = 1; fr = 2; fc = 3; lfe = 4; sl = 5; sr = 6;
m = zeros(8,8);
m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000;
sel.coeffs = m;
write_blob(sel, "downmix_51_to_mono");
sel.coeffs(1, lfe) = 10^(+10/20);
write_blob(sel, "downmix_51_to_mono_with_lfe");

% 7.1 to 5.1 downmix
fl8 = 1; fr8 = 2; fc8 = 3; lfe8 = 4; bl8 = 5; br8 = 6; sl8 = 7; sr8 = 8;
fl6 = 1; fr6 = 2; fc6 = 3; lfe6 = 4; sl6 = 5; sr6 = 6;
m = zeros(8,8);
m(fl6, fl8) = 1;
m(fr6, fr8) = 1;
m(fc6, fc8) = 1;
m(sl6, sl8) = 1;
m(sr6, sr8) = 1;
m(sl6, bl8) = 1;
m(sr6, br8) = 1;
m(lfe6, lfe8) = 1;
sel.coeffs = m;
write_blob(sel, "downmix_71_to_51");

% 7.1 to 5.1 downmix
fl = 1; fr = 2; fc = 3; lfe = 4; bl = 5; br = 6; sl = 7; sr = 8;
m = zeros(8,8);
m(1, fl) = 1.0000; m(1, fr) = 0.0000; m(1, fc) = 0.7071; m(1, sl) = 0.7071; m(1, sr) = 0.0000; m(1, bl) = 0.7071; m(1, br) = 0.0000;
m(2, fl) = 0.0000; m(2, fr) = 1.0000; m(2, fc) = 0.7071; m(2, sl) = 0.0000; m(2, sr) = 0.7071; m(2, bl) = 0.0000; m(2, br) = 0.7071;
sel.coeffs = m;
write_blob(sel, "downmix_71_to_stereo");
sel.coeffs(1, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to left
sel.coeffs(2, lfe) = 10^(+4/20); % +10 dB, attenuate by -6 dB to right
write_blob(sel, "downmix_71_to_stereo_with_lfe");

% 7.1 to mono downmix
fl = 1; fc = 3; fr = 2; sr = 8; br = 6; bl = 5; sl = 7; lfe = 4;
m = zeros(8,8);
m(1, fl) = 0.7071; m(1, fr) = 0.7071; m(1, fc) = 1.0000; m(1, sl) = 0.5000; m(1, sr) = 0.5000; m(1, bl) = 0.5000; m(1, br) = 0.5000;
sel.coeffs = m;
write_blob(sel, "downmix_71_to_mono");
m(1, lfe) = 10^(+19/20); % +10 dB
write_blob(sel, "downmix_71_to_mono_with_lfe");

% mono to stereo upmix
sel.coeffs = zeros(8,8);
sel.coeffs(1, 1) = 10^(-3/20);
sel.coeffs(2, 1) = 10^(-3/20);
write_blob(sel, "upmix_mono_to_stereo");

% mono to 5.1 / 7.1 upmix
fc = 3
sel.coeffs = zeros(8,8);
sel.coeffs(fc, 1) = 1;
write_blob(sel, "upmix_mono_to_51");
write_blob(sel, "upmix_mono_to_71");

% stereo to 5.1 / 7.1 upmix
fl = 1; fr = 2;
sel.coeffs = zeros(8,8);
sel.coeffs(fl, 1) = 1;
sel.coeffs(fr, 2) = 1;
write_blob(sel, "upmix_stereo_to_51");
write_blob(sel, "upmix_stereo_to_71");

sof_selector_paths(false);
end

function write_blob(sel, blobname)
str_config = "selector_config";
str_exported = "Exported with script sof_selector_blobs.m";
str_howto = "cd tools/tune/selector; octave sof_selector_blobs.m"
sof_tools = '../../../../tools';
sof_tplg = fullfile(sof_tools, 'topology');
sof_tplg_selector = fullfile(sof_tplg, 'topology2/include/components/micsel');

sel

sum_coefs = sum(sel.coeffs, 2)'
max_sum_coef = max(sum_coefs)
if max_sum_coef > 1
scale = 1 / max_sum_coef;
else
scale = 1;
end

scale
sel.coeffs = scale .* sel.coeffs';

blob8 = sof_selector_build_blob(sel);
tplg2_fn = sprintf("%s/%s.conf", sof_tplg_selector, blobname);
sof_check_create_dir(tplg2_fn);
sof_tplg2_write(tplg2_fn, blob8, str_config, str_exported, str_howto);
end

function sof_selector_paths(enable)

common = '../../../../tools/tune/common';
if enable
addpath(common);
else
rmpath(common);
end
end

function blob8 = sof_selector_build_blob(sel)

s = size(sel.coeffs);
blob_type = 0;
blob_param_id = 0; % IPC4_SELECTOR_COEFFS_CONFIG_ID
data_length = s(1) * s(2) + 2;
data_size = 2 * data_length; % int16_t matrix
coeffs_vec = reshape(sel.coeffs, 1, []); % convert to row vector
coeffs_q10 = int16(round(coeffs_vec .* 1024)); % Q6.10
ipc_ver = 4;
[abi_bytes, abi_size] = sof_get_abi(data_size, ipc_ver, blob_type, blob_param_id);
blob_size = data_size + abi_size;
blob8 = uint8(zeros(1, blob_size));
blob8(1:abi_size) = abi_bytes;
j = abi_size + 1;

% header
blob8(j:j+1) = int16_to_byte(int16(sel.rsvd0));
j = j + 2;
blob8(j:j+1) = int16_to_byte(int16(sel.rsvd1));
j = j + 2;

% coeffs matrix
for i = 1:(s(1) * s(2))
blob8(j:j+1) = int16_to_byte(coeffs_q10(i));
j = j + 2;
end
end

function bytes = int16_to_byte(word)
sh = [0 -8];
bytes = uint8(zeros(1,2));
bytes(1) = bitand(bitshift(word, sh(1)), 255);
bytes(2) = bitand(bitshift(word, sh(2)), 255);
end
1 change: 1 addition & 0 deletions tools/rimage/config/tgl-h.toml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ count = 27
instance_count = "8"
domain_types = "0"
load_type = "0"
init_config = "1"
module_type = "0xC"
auto_start = "0"
sched_caps = [1, 0x00008000]
Expand Down
1 change: 1 addition & 0 deletions tools/rimage/config/tgl.toml
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ count = 27
instance_count = "8"
domain_types = "0"
load_type = "0"
init_config = "1"
module_type = "0xC"
auto_start = "0"
sched_caps = [1, 0x00008000]
Expand Down
25 changes: 25 additions & 0 deletions tools/topology/topology2/cavs-benchmark-hda.conf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<include/components/asrc.conf>
<include/components/tdfb.conf>
<include/components/template_comp.conf>
<include/components/micsel.conf>

Define {
ANALOG_PLAYBACK_PCM 'Analog Playback'
Expand Down Expand Up @@ -39,12 +40,16 @@ Object.PCM.pcm [
name $ANALOG_PLAYBACK_PCM
formats 'S32_LE,S24_LE,S16_LE'
rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000"
channels_min 1
channels_max 8
}
Object.PCM.pcm_caps.2 {
direction "capture"
name $ANALOG_CAPTURE_PCM
formats 'S32_LE,S24_LE,S16_LE'
rates "8000,11025,16000,22050,32000,44100,48000,64000,88200,96000,176400,192000"
channels_min 1
channels_max 8
}
direction duplex
}
Expand Down Expand Up @@ -527,6 +532,26 @@ IncludeByKey.BENCH_CONFIG {
<include/bench/igo_nr_s32.conf>
}

#
# Micsel component
#

"^micsel16$" {
<include/bench/micsel_s16.conf>
}

"^micsel24$" {
<include/bench/micsel_s24.conf>
}

"^micsel32$" {
<include/bench/micsel_s32.conf>
}

"^micsel_multich32$" {
<include/bench/micsel_multich_s32.conf>
}

#
# RTNR component
#
Expand Down
Loading
Loading