1111
1212% SPDX-License-Identifier: BSD-3-Clause
1313%
14- % Copyright (c) 2025, Intel Corporation.
14+ % Copyright (c) 2025-2026 , Intel Corporation.
1515
1616function sof_selector_blobs()
1717
1818 % See ITU-R BS.775-4 for mix coefficient values
1919 sof_selector_paths(true );
2020
21+ % Values of enum ipc4_channel_config
22+ IPC4_CHANNEL_CONFIG_MONO = 0 ;
23+ IPC4_CHANNEL_CONFIG_STEREO = 1 ;
24+ IPC4_CHANNEL_CONFIG_QUATRO = 5 ;
25+ IPC4_CHANNEL_CONFIG_5_POINT_1 = 8 ;
26+ IPC4_CHANNEL_CONFIG_7_POINT_1 = 12 ;
27+
2128 % Matrix for 1:1 pass-through
22- sel.rsvd0 = 0 ;
23- sel.rsvd1 = 0 ;
29+ sel.ch_count = [8 8 ]; % Number of channels
30+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_7_POINT_1 ];
31+ sel.ch_out = 8 ;
2432 sel.coeffs = diag(ones(8 , 1 ));
25- write_blob(sel , " passthrough" );
33+ passthrough_pack8 = write_blob(sel , " passthrough" );
2634
2735 % Stereo to mono downmix
36+ sel.ch_count = [2 1 ];
37+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_MONO ];
2838 sel.coeffs = zeros(8 ,8 );
2939 sel .coeffs(1 , 1 ) = 0.7071 ;
3040 sel .coeffs(1 , 2 ) = 0.7071 ;
31- write_blob(sel , " downmix_stereo_to_mono" );
41+ stereo_to_mono_pack8 = write_blob(sel , " downmix_stereo_to_mono" );
3242
3343 % 5.1 to stereo downmix
44+ sel.ch_count = [6 2 ];
45+ sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_STEREO ];
3446 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; sl = 5 ; sr = 6 ;
3547 m = zeros(8 ,8 );
3648 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 ;
3749 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 ;
3850 sel.coeffs = m ;
39- write_blob(sel , " downmix_51_to_stereo" );
4051 sel .coeffs(1 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to left
4152 sel .coeffs(2 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to right
42- write_blob(sel , " downmix_51_to_stereo_with_lfe" );
53+ sixch_to_stereo_pack8 = write_blob(sel , " downmix_51_to_stereo_with_lfe" );
4354
4455 % 5.1 to mono downmix
56+ sel.ch_count = [6 1 ];
57+ sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_MONO ];
4558 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; sl = 5 ; sr = 6 ;
4659 m = zeros(8 ,8 );
4760 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 ;
4861 sel.coeffs = m ;
49- write_blob(sel , " downmix_51_to_mono" );
5062 sel .coeffs(1 , lfe ) = 10 ^(+10 / 20 );
51- write_blob(sel , " downmix_51_to_mono_with_lfe" );
63+ sixch_to_mono_pack8 = write_blob(sel , " downmix_51_to_mono_with_lfe" );
5264
5365 % 7.1 to 5.1 downmix
66+ sel.ch_count = [8 6 ];
67+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_5_POINT_1 ];
5468 fl8 = 1 ; fr8 = 2 ; fc8 = 3 ; lfe8 = 4 ; bl8 = 5 ; br8 = 6 ; sl8 = 7 ; sr8 = 8 ;
5569 fl6 = 1 ; fr6 = 2 ; fc6 = 3 ; lfe6 = 4 ; sl6 = 5 ; sr6 = 6 ;
5670 m = zeros(8 ,8 );
@@ -63,50 +77,69 @@ function sof_selector_blobs()
6377 m(sr6 , br8 ) = 1 ;
6478 m(lfe6 , lfe8 ) = 1 ;
6579 sel.coeffs = m ;
66- write_blob(sel , " downmix_71_to_51" );
80+ eightch_to_sixch_pack8 = write_blob(sel , " downmix_71_to_51" );
6781
68- % 7.1 to 5.1 downmix
82+ % 7.1 to stereo downmix
83+ sel.ch_count = [8 2 ];
84+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_STEREO ];
6985 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; bl = 5 ; br = 6 ; sl = 7 ; sr = 8 ;
7086 m = zeros(8 ,8 );
7187 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 ;
7288 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 ;
7389 sel.coeffs = m ;
74- write_blob(sel , " downmix_71_to_stereo" );
7590 sel .coeffs(1 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to left
7691 sel .coeffs(2 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to right
77- write_blob(sel , " downmix_71_to_stereo_with_lfe" );
92+ eightch_to_stereo_pack8 = write_blob(sel , " downmix_71_to_stereo_with_lfe" );
7893
7994 % 7.1 to mono downmix
95+ sel.ch_count = [8 1 ];
96+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_MONO ];
8097 fl = 1 ; fc = 3 ; fr = 2 ; sr = 8 ; br = 6 ; bl = 5 ; sl = 7 ; lfe = 4 ;
8198 m = zeros(8 ,8 );
8299 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 ;
83100 sel.coeffs = m ;
84- write_blob(sel , " downmix_71_to_mono" );
85101 m(1 , lfe ) = 10 ^(+19 / 20 ); % +10 dB
86- write_blob(sel , " downmix_71_to_mono_with_lfe" );
102+ eightch_to_mono_pack8 = write_blob(sel , " downmix_71_to_mono_with_lfe" );
87103
88104 % mono to stereo upmix
105+ sel.ch_count = [1 2 ];
106+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_STEREO ];
89107 sel.coeffs = zeros(8 ,8 );
90108 sel .coeffs(1 , 1 ) = 10 ^(-3 / 20 );
91109 sel .coeffs(2 , 1 ) = 10 ^(-3 / 20 );
92- write_blob(sel , " upmix_mono_to_stereo" );
110+ mono_to_stereo_pack8 = write_blob(sel , " upmix_mono_to_stereo" );
93111
94112 % mono to 5.1 / 7.1 upmix
95- fc = 3
113+ sel.ch_count = [1 6 ];
114+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_5_POINT_1 ];
115+ fc = 3 ;
96116 sel.coeffs = zeros(8 ,8 );
97117 sel .coeffs(fc , 1 ) = 1 ;
98- write_blob(sel , " upmix_mono_to_51" );
99- write_blob(sel , " upmix_mono_to_71" );
118+ mono_to_sixch_pack8 = write_blob(sel , " upmix_mono_to_51" );
119+ sel.ch_count = [1 8 ];
120+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_7_POINT_1 ];
121+ mono_to_eightch_pack8 = write_blob(sel , " upmix_mono_to_71" );
100122
101123 % stereo to 5.1 / 7.1 upmix
124+ sel.ch_count = [2 6 ];
125+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_5_POINT_1 ];
102126 fl = 1 ; fr = 2 ;
103127 sel.coeffs = zeros(8 ,8 );
104128 sel .coeffs(fl , 1 ) = 1 ;
105129 sel .coeffs(fr , 2 ) = 1 ;
106- write_blob(sel , " upmix_stereo_to_51" );
107- write_blob(sel , " upmix_stereo_to_71" );
130+ stereo_to_sixch_pack8 = write_blob(sel , " upmix_stereo_to_51" );
131+ sel.ch_count = [2 8 ];
132+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_7_POINT_1 ];
133+ stereo_to_eightch_pack8 = write_blob(sel , " upmix_stereo_to_71" );
134+
135+ % For blob format with multiple up/down-mix profiles, intended
136+ % for playback decoder offload conversions.
137+ multi_pack8 = [passthrough_pack8 mono_to_stereo_pack8 sixch_to_stereo_pack8 eightch_to_stereo_pack8 ];
138+ write_8bit_packed(multi_pack8 , ' stereo_endpoint_playback_updownmix' );
108139
109140 % Stereo to L,L,R,R
141+ sel.ch_count = [2 4 ];
142+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
110143 sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
111144 1 0 0 0 0 0 0 0 ; ...
112145 0 1 0 0 0 0 0 0 ; ...
@@ -118,6 +151,8 @@ function sof_selector_blobs()
118151 write_blob(sel , " xover_selector_lr_to_llrr" );
119152
120153 % Stereo to R,R,L,L
154+ sel.ch_count = [2 4 ];
155+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
121156 sel.coeffs = [ 0 1 0 0 0 0 0 0 ; ...
122157 0 1 0 0 0 0 0 0 ; ...
123158 1 0 0 0 0 0 0 0 ; ...
@@ -129,6 +164,8 @@ function sof_selector_blobs()
129164 write_blob(sel , " xover_selector_lr_to_rrll" );
130165
131166 % Stereo to L,R,L,R
167+ sel.ch_count = [2 4 ];
168+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
132169 sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
133170 0 1 0 0 0 0 0 0 ; ...
134171 1 0 0 0 0 0 0 0 ; ...
@@ -142,32 +179,56 @@ function sof_selector_blobs()
142179 sof_selector_paths(false );
143180end
144181
145- function write_blob(sel , blobname )
182+ function pack8 = write_blob(sel , blobname )
183+ pack8 = pack_selector_config(sel );
184+ pack8_copy = pack8 ;
185+ pack8_copy(1 : 4 ) = uint8([0 0 0 0 ]);
186+ write_8bit_packed(pack8_copy , blobname );
187+ end
188+
189+ function write_8bit_packed(pack8 , blobname )
190+ blob8 = sof_selector_build_blob(pack8 );
146191 str_config = " selector_config" ;
147192 str_exported = " Exported with script sof_selector_blobs.m" ;
148- str_howto = " cd tools/tune/selector; octave sof_selector_blobs.m"
193+ str_howto = " cd tools/tune/selector; octave sof_selector_blobs.m" ;
149194 sof_tools = ' ../../../../tools' ;
150195 sof_tplg = fullfile(sof_tools , ' topology' );
151196 sof_tplg_selector = fullfile(sof_tplg , ' topology2/include/components/micsel' );
197+ tplg2_fn = sprintf(" %s/%s.conf" , sof_tplg_selector , blobname );
198+ sof_check_create_dir(tplg2_fn );
199+ sof_tplg2_write(tplg2_fn , blob8 , str_config , str_exported , str_howto );
200+ end
152201
153- sel
202+ function pack8 = pack_selector_config( sel )
154203
155- sum_coefs = sum(sel .coeffs , 2 )'
156- max_sum_coef = max(sum_coefs )
204+ sum_coefs = sum(sel .coeffs , 2 )' ;
205+ max_sum_coef = max(sum_coefs );
157206 if max_sum_coef > 1
158207 scale = 1 / max_sum_coef ;
159208 else
160209 scale = 1 ;
161210 end
162211
163- scale
164212 sel.coeffs = scale .* sel .coeffs ' ;
213+ coeffs_vec = reshape(sel .coeffs , 1 , []); % convert to row vector
214+ coeffs_q10 = int16(round(coeffs_vec .* 1024 )); % Q6.10
215+ pack8 = uint8(2 * length(coeffs_q10 ) + 4 );
216+ rsvd1 = 0 ;
165217
166- blob8 = sof_selector_build_blob(sel );
167- tplg2_fn = sprintf(" %s/%s.conf" , sof_tplg_selector , blobname );
168- sof_check_create_dir(tplg2_fn );
169- sof_tplg2_write(tplg2_fn , blob8 , str_config , str_exported , str_howto );
170- end
218+ % header
219+ j = 1 ;
220+ pack8(j : j + 1 ) = uint8(sel .ch_count );
221+ j = j + 2 ;
222+ pack8(j : j + 1 ) = uint8(sel .ch_config );
223+ j = j + 2 ;
224+
225+ % coeffs matrix
226+ for i = 1 : length(coeffs_q10 )
227+ pack8(j : j + 1 ) = int16_to_byte(coeffs_q10(i ));
228+ j = j + 2 ;
229+ end
230+
231+ end
171232
172233function sof_selector_paths(enable )
173234
@@ -179,33 +240,19 @@ function sof_selector_paths(enable)
179240 end
180241end
181242
182- function blob8 = sof_selector_build_blob(sel )
243+ function blob8 = sof_selector_build_blob(pack8 )
183244
184- s = size(sel .coeffs );
185245 blob_type = 0 ;
186246 blob_param_id = 0 ; % IPC4_SELECTOR_COEFFS_CONFIG_ID
187- data_length = s(1 ) * s(2 ) + 2 ;
188- data_size = 2 * data_length ; % int16_t matrix
189- coeffs_vec = reshape(sel .coeffs , 1 , []); % convert to row vector
190- coeffs_q10 = int16(round(coeffs_vec .* 1024 )); % Q6.10
247+ data_size = length(pack8 );
191248 ipc_ver = 4 ;
192249 [abi_bytes , abi_size ] = sof_get_abi(data_size , ipc_ver , blob_type , blob_param_id );
193250 blob_size = data_size + abi_size ;
194251 blob8 = uint8(zeros(1 , blob_size ));
195252 blob8(1 : abi_size ) = abi_bytes ;
196253 j = abi_size + 1 ;
197254
198- % header
199- blob8(j : j + 1 ) = int16_to_byte(int16(sel .rsvd0 ));
200- j = j + 2 ;
201- blob8(j : j + 1 ) = int16_to_byte(int16(sel .rsvd1 ));
202- j = j + 2 ;
203-
204- % coeffs matrix
205- for i = 1 : (s(1 ) * s(2 ))
206- blob8(j : j + 1 ) = int16_to_byte(coeffs_q10(i ));
207- j = j + 2 ;
208- end
255+ blob8(j : j + data_size - 1 ) = pack8 ;
209256end
210257
211258function bytes = int16_to_byte(word )
0 commit comments