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 ] ;
2431 sel.coeffs = diag(ones(8 , 1 ));
25- write_blob(sel , " passthrough" );
32+ passthrough_pack8 = write_blob(sel , " passthrough" );
2633
2734 % Stereo to mono downmix
35+ sel.ch_count = [2 1 ];
36+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_MONO ];
2837 sel.coeffs = zeros(8 ,8 );
2938 sel .coeffs(1 , 1 ) = 0.7071 ;
3039 sel .coeffs(1 , 2 ) = 0.7071 ;
31- write_blob(sel , " downmix_stereo_to_mono" );
40+ stereo_to_mono_pack8 = write_blob(sel , " downmix_stereo_to_mono" );
3241
3342 % 5.1 to stereo downmix
43+ sel.ch_count = [6 2 ];
44+ sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_STEREO ];
3445 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; sl = 5 ; sr = 6 ;
3546 m = zeros(8 ,8 );
3647 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 ;
3748 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 ;
3849 sel.coeffs = m ;
39- write_blob(sel , " downmix_51_to_stereo" );
4050 sel .coeffs(1 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to left
4151 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" );
52+ sixch_to_stereo_pack8 = write_blob(sel , " downmix_51_to_stereo_with_lfe" );
4353
4454 % 5.1 to mono downmix
55+ sel.ch_count = [6 1 ];
56+ sel.ch_config = [IPC4_CHANNEL_CONFIG_5_POINT_1 IPC4_CHANNEL_CONFIG_MONO ];
4557 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; sl = 5 ; sr = 6 ;
4658 m = zeros(8 ,8 );
4759 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 ;
4860 sel.coeffs = m ;
49- write_blob(sel , " downmix_51_to_mono" );
5061 sel .coeffs(1 , lfe ) = 10 ^(+10 / 20 );
51- write_blob(sel , " downmix_51_to_mono_with_lfe" );
62+ sixch_to_mono_pack8 = write_blob(sel , " downmix_51_to_mono_with_lfe" );
5263
5364 % 7.1 to 5.1 downmix
65+ sel.ch_count = [8 6 ];
66+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_5_POINT_1 ];
5467 fl8 = 1 ; fr8 = 2 ; fc8 = 3 ; lfe8 = 4 ; bl8 = 5 ; br8 = 6 ; sl8 = 7 ; sr8 = 8 ;
5568 fl6 = 1 ; fr6 = 2 ; fc6 = 3 ; lfe6 = 4 ; sl6 = 5 ; sr6 = 6 ;
5669 m = zeros(8 ,8 );
@@ -63,50 +76,69 @@ function sof_selector_blobs()
6376 m(sr6 , br8 ) = 1 ;
6477 m(lfe6 , lfe8 ) = 1 ;
6578 sel.coeffs = m ;
66- write_blob(sel , " downmix_71_to_51" );
79+ eightch_to_sixch_pack8 = write_blob(sel , " downmix_71_to_51" );
6780
68- % 7.1 to 5.1 downmix
81+ % 7.1 to stereo downmix
82+ sel.ch_count = [8 2 ];
83+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_STEREO ];
6984 fl = 1 ; fr = 2 ; fc = 3 ; lfe = 4 ; bl = 5 ; br = 6 ; sl = 7 ; sr = 8 ;
7085 m = zeros(8 ,8 );
7186 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 ;
7287 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 ;
7388 sel.coeffs = m ;
74- write_blob(sel , " downmix_71_to_stereo" );
7589 sel .coeffs(1 , lfe ) = 10 ^(+4 / 20 ); % +10 dB, attenuate by -6 dB to left
7690 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" );
91+ eightch_to_stereo_pack8 = write_blob(sel , " downmix_71_to_stereo_with_lfe" );
7892
7993 % 7.1 to mono downmix
94+ sel.ch_count = [8 1 ];
95+ sel.ch_config = [IPC4_CHANNEL_CONFIG_7_POINT_1 IPC4_CHANNEL_CONFIG_MONO ];
8096 fl = 1 ; fc = 3 ; fr = 2 ; sr = 8 ; br = 6 ; bl = 5 ; sl = 7 ; lfe = 4 ;
8197 m = zeros(8 ,8 );
8298 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 ;
8399 sel.coeffs = m ;
84- write_blob(sel , " downmix_71_to_mono" );
85100 m(1 , lfe ) = 10 ^(+19 / 20 ); % +10 dB
86- write_blob(sel , " downmix_71_to_mono_with_lfe" );
101+ eightch_to_mono_pack8 = write_blob(sel , " downmix_71_to_mono_with_lfe" );
87102
88103 % mono to stereo upmix
104+ sel.ch_count = [1 2 ];
105+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_STEREO ];
89106 sel.coeffs = zeros(8 ,8 );
90107 sel .coeffs(1 , 1 ) = 10 ^(-3 / 20 );
91108 sel .coeffs(2 , 1 ) = 10 ^(-3 / 20 );
92- write_blob(sel , " upmix_mono_to_stereo" );
109+ mono_to_stereo_pack8 = write_blob(sel , " upmix_mono_to_stereo" );
93110
94111 % mono to 5.1 / 7.1 upmix
95- fc = 3
112+ sel.ch_count = [1 6 ];
113+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_5_POINT_1 ];
114+ fc = 3 ;
96115 sel.coeffs = zeros(8 ,8 );
97116 sel .coeffs(fc , 1 ) = 1 ;
98- write_blob(sel , " upmix_mono_to_51" );
99- write_blob(sel , " upmix_mono_to_71" );
117+ mono_to_sixch_pack8 = write_blob(sel , " upmix_mono_to_51" );
118+ sel.ch_count = [1 8 ];
119+ sel.ch_config = [IPC4_CHANNEL_CONFIG_MONO IPC4_CHANNEL_CONFIG_7_POINT_1 ];
120+ mono_to_eightch_pack8 = write_blob(sel , " upmix_mono_to_71" );
100121
101122 % stereo to 5.1 / 7.1 upmix
123+ sel.ch_count = [2 6 ];
124+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_5_POINT_1 ];
102125 fl = 1 ; fr = 2 ;
103126 sel.coeffs = zeros(8 ,8 );
104127 sel .coeffs(fl , 1 ) = 1 ;
105128 sel .coeffs(fr , 2 ) = 1 ;
106- write_blob(sel , " upmix_stereo_to_51" );
107- write_blob(sel , " upmix_stereo_to_71" );
129+ stereo_to_sixch_pack8 = write_blob(sel , " upmix_stereo_to_51" );
130+ sel.ch_count = [2 8 ];
131+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_7_POINT_1 ];
132+ stereo_to_eightch_pack8 = write_blob(sel , " upmix_stereo_to_71" );
133+
134+ % For blob format with multiple up/down-mix profiles, intended
135+ % for playback decoder offload conversions.
136+ multi_pack8 = [passthrough_pack8 mono_to_stereo_pack8 sixch_to_stereo_pack8 eightch_to_stereo_pack8 ];
137+ write_8bit_packed(multi_pack8 , ' stereo_endpoint_playback_updownmix' );
108138
109139 % Stereo to L,L,R,R
140+ sel.ch_count = [2 4 ];
141+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
110142 sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
111143 1 0 0 0 0 0 0 0 ; ...
112144 0 1 0 0 0 0 0 0 ; ...
@@ -118,6 +150,8 @@ function sof_selector_blobs()
118150 write_blob(sel , " xover_selector_lr_to_llrr" );
119151
120152 % Stereo to R,R,L,L
153+ sel.ch_count = [2 4 ];
154+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
121155 sel.coeffs = [ 0 1 0 0 0 0 0 0 ; ...
122156 0 1 0 0 0 0 0 0 ; ...
123157 1 0 0 0 0 0 0 0 ; ...
@@ -129,6 +163,8 @@ function sof_selector_blobs()
129163 write_blob(sel , " xover_selector_lr_to_rrll" );
130164
131165 % Stereo to L,R,L,R
166+ sel.ch_count = [2 4 ];
167+ sel.ch_config = [IPC4_CHANNEL_CONFIG_STEREO IPC4_CHANNEL_CONFIG_QUATRO ];
132168 sel.coeffs = [ 1 0 0 0 0 0 0 0 ; ...
133169 0 1 0 0 0 0 0 0 ; ...
134170 1 0 0 0 0 0 0 0 ; ...
@@ -142,32 +178,55 @@ function sof_selector_blobs()
142178 sof_selector_paths(false );
143179end
144180
145- function write_blob(sel , blobname )
181+ function pack8 = write_blob(sel , blobname )
182+ pack8 = pack_selector_config(sel );
183+ pack8_copy = pack8 ;
184+ pack8_copy(1 : 4 ) = uint8([0 0 0 0 ]);
185+ write_8bit_packed(pack8_copy , blobname );
186+ end
187+
188+ function write_8bit_packed(pack8 , blobname )
189+ blob8 = sof_selector_build_blob(pack8 );
146190 str_config = " selector_config" ;
147191 str_exported = " Exported with script sof_selector_blobs.m" ;
148- str_howto = " cd tools/tune/selector; octave sof_selector_blobs.m"
192+ str_howto = " cd tools/tune/selector; octave sof_selector_blobs.m" ;
149193 sof_tools = ' ../../../../tools' ;
150194 sof_tplg = fullfile(sof_tools , ' topology' );
151195 sof_tplg_selector = fullfile(sof_tplg , ' topology2/include/components/micsel' );
196+ tplg2_fn = sprintf(" %s/%s.conf" , sof_tplg_selector , blobname );
197+ sof_check_create_dir(tplg2_fn );
198+ sof_tplg2_write(tplg2_fn , blob8 , str_config , str_exported , str_howto );
199+ end
152200
153- sel
201+ function pack8 = pack_selector_config( sel )
154202
155- sum_coefs = sum(sel .coeffs , 2 )'
156- max_sum_coef = max(sum_coefs )
203+ sum_coefs = sum(sel .coeffs , 2 )' ;
204+ max_sum_coef = max(sum_coefs );
157205 if max_sum_coef > 1
158206 scale = 1 / max_sum_coef ;
159207 else
160208 scale = 1 ;
161209 end
162210
163- scale
164211 sel.coeffs = scale .* sel .coeffs ' ;
212+ coeffs_vec = reshape(sel .coeffs , 1 , []); % convert to row vector
213+ coeffs_q10 = int16(round(coeffs_vec .* 1024 )); % Q6.10
214+ pack8 = uint8(zeros(1 , 2 * length(coeffs_q10 ) + 4 ));
165215
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
216+ % header
217+ j = 1 ;
218+ pack8(j : j + 1 ) = uint8(sel .ch_count );
219+ j = j + 2 ;
220+ pack8(j : j + 1 ) = uint8(sel .ch_config );
221+ j = j + 2 ;
222+
223+ % coeffs matrix
224+ for i = 1 : length(coeffs_q10 )
225+ pack8(j : j + 1 ) = int16_to_byte(coeffs_q10(i ));
226+ j = j + 2 ;
227+ end
228+
229+ end
171230
172231function sof_selector_paths(enable )
173232
@@ -179,33 +238,19 @@ function sof_selector_paths(enable)
179238 end
180239end
181240
182- function blob8 = sof_selector_build_blob(sel )
241+ function blob8 = sof_selector_build_blob(pack8 )
183242
184- s = size(sel .coeffs );
185243 blob_type = 0 ;
186244 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
245+ data_size = length(pack8 );
191246 ipc_ver = 4 ;
192247 [abi_bytes , abi_size ] = sof_get_abi(data_size , ipc_ver , blob_type , blob_param_id );
193248 blob_size = data_size + abi_size ;
194249 blob8 = uint8(zeros(1 , blob_size ));
195250 blob8(1 : abi_size ) = abi_bytes ;
196251 j = abi_size + 1 ;
197252
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
253+ blob8(j : j + data_size - 1 ) = pack8 ;
209254end
210255
211256function bytes = int16_to_byte(word )
0 commit comments