Line data Source code
1 : /******************************************************************************************************
2 :
3 : (C) 2022-2025 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
4 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
5 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
6 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
7 : contributors to this repository. All Rights Reserved.
8 :
9 : This software is protected by copyright law and by international treaties.
10 : The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
11 : Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
12 : Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
13 : Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
14 : contributors to this repository retain full ownership rights in their respective contributions in
15 : the software. This notice grants no license of any kind, including but not limited to patent
16 : license, nor is any license granted by implication, estoppel or otherwise.
17 :
18 : Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
19 : contributions.
20 :
21 : This software is provided "AS IS", without any express or implied warranties. The software is in the
22 : development stage. It is intended exclusively for experts who have experience with such software and
23 : solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
24 : and fitness for a particular purpose are hereby disclaimed and excluded.
25 :
26 : Any dispute, controversy or claim arising under or in relation to providing this software shall be
27 : submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
28 : accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
29 : the United Nations Convention on Contracts on the International Sales of Goods.
30 :
31 : *******************************************************************************************************/
32 :
33 : #include <math.h>
34 : #include "options.h"
35 : #include "lib_rend.h"
36 : #include "ivas_prot_rend.h"
37 : #include "ivas_prot.h"
38 : #include "ivas_cnst.h"
39 : #include "prot.h"
40 : #include "wmc_auto.h"
41 :
42 :
43 : /*---------------------------------------------------------------------*
44 : * Local function prototypes
45 : *---------------------------------------------------------------------*/
46 :
47 : static void copy_masa_meta_tile( MASA_DECODER_EXT_OUT_META_HANDLE outMeta, MASA_DECODER_EXT_OUT_META_HANDLE inMeta, const uint8_t sf, const uint8_t band );
48 :
49 : static void full_stream_merge( MASA_DECODER_EXT_OUT_META_HANDLE outMeta, MASA_DECODER_EXT_OUT_META_HANDLE inMeta1, float inEne1[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS], MASA_DECODER_EXT_OUT_META_HANDLE inMeta2, float inEne2[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS] );
50 :
51 : static void diffuse_meta_merge_1x1( MASA_DECODER_EXT_OUT_META_HANDLE outMeta, MASA_DECODER_EXT_OUT_META_HANDLE inMeta, float inEne[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS], MASA_DECODER_EXT_OUT_META_HANDLE inMetaISM, float inEneISM[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS] );
52 :
53 :
54 : /*---------------------------------------------------------------------*
55 : * copy_masa_meta_tile()
56 : *
57 : *
58 : *---------------------------------------------------------------------*/
59 :
60 86400 : void copy_masa_meta_tile(
61 : MASA_DECODER_EXT_OUT_META_HANDLE outMeta, /* o : metadata to be written */
62 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta, /* i : input metadata */
63 : const uint8_t sf, /* i : sub-frame index */
64 : const uint8_t band /* i : band index */
65 : )
66 : {
67 86400 : outMeta->directionIndex[0][sf][band] = inMeta->directionIndex[0][sf][band];
68 86400 : outMeta->directToTotalRatio[0][sf][band] = inMeta->directToTotalRatio[0][sf][band];
69 86400 : outMeta->spreadCoherence[0][sf][band] = inMeta->spreadCoherence[0][sf][band];
70 :
71 86400 : outMeta->surroundCoherence[sf][band] = inMeta->surroundCoherence[sf][band];
72 86400 : outMeta->diffuseToTotalRatio[sf][band] = inMeta->diffuseToTotalRatio[sf][band];
73 :
74 86400 : if ( inMeta->descriptiveMeta.numberOfDirections == 1 )
75 : {
76 32720 : outMeta->directionIndex[1][sf][band] = inMeta->directionIndex[1][sf][band];
77 32720 : outMeta->directToTotalRatio[1][sf][band] = inMeta->directToTotalRatio[1][sf][band];
78 32720 : outMeta->spreadCoherence[1][sf][band] = inMeta->spreadCoherence[1][sf][band];
79 : }
80 : else
81 : {
82 : /* make sure the output has zeroed data in the second direction */
83 53680 : outMeta->directionIndex[1][sf][band] = SPH_IDX_FRONT;
84 53680 : outMeta->directToTotalRatio[1][sf][band] = 0u;
85 53680 : outMeta->spreadCoherence[1][sf][band] = 0u;
86 : }
87 :
88 86400 : return;
89 : }
90 :
91 :
92 : /*---------------------------------------------------------------------*
93 : * copy_masa_descriptive_meta()
94 : *
95 : *
96 : *---------------------------------------------------------------------*/
97 :
98 300 : void copy_masa_descriptive_meta(
99 : MASA_DECRIPTIVE_META *outMeta, /* o : metadata to be written */
100 : MASA_DECRIPTIVE_META *inMeta /* i : input metadata */
101 : )
102 : {
103 : uint8_t char_idx;
104 2700 : for ( char_idx = 0; char_idx < 8; char_idx++ )
105 : {
106 2400 : outMeta->formatDescriptor[char_idx] = inMeta->formatDescriptor[char_idx];
107 : }
108 300 : outMeta->numberOfDirections = inMeta->numberOfDirections;
109 300 : outMeta->numberOfChannels = inMeta->numberOfChannels;
110 300 : outMeta->sourceFormat = inMeta->sourceFormat;
111 300 : outMeta->transportDefinition = inMeta->transportDefinition;
112 300 : outMeta->channelAngle = inMeta->channelAngle;
113 300 : outMeta->channelDistance = inMeta->channelDistance;
114 300 : outMeta->channelLayout = inMeta->channelLayout;
115 :
116 300 : return;
117 : }
118 :
119 :
120 : /*---------------------------------------------------------------------*
121 : * diffuse_meta_merge_1x1()
122 : *
123 : *
124 : *---------------------------------------------------------------------*/
125 :
126 108 : void diffuse_meta_merge_1x1(
127 : MASA_DECODER_EXT_OUT_META_HANDLE outMeta, /* o : Merged metadata output */
128 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta, /* i : Input metadata 1 */
129 : float inEne[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS], /* i/o: TF-energy of input 1. energy after merge */
130 : MASA_DECODER_EXT_OUT_META_HANDLE inMetaISM, /* i : Input metadata 2 */
131 : float inEneISM[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS] /* i : TF-energy of input 2 */
132 : )
133 : {
134 : int8_t sf, band;
135 :
136 540 : for ( sf = 0; sf < MAX_PARAM_SPATIAL_SUBFRAMES; sf++ )
137 : {
138 10800 : for ( band = 0; band < MASA_FREQUENCY_BANDS; band++ )
139 : {
140 : float energyTimesRatio, energyTimesRatioISM, total_diff_nrg, dir_nrg_ratio, total_nrg;
141 : float dir_ratio_ism;
142 :
143 10368 : energyTimesRatio = (float) ( inMeta->directToTotalRatio[0][sf][band] ) / UINT8_MAX * inEne[sf][band];
144 :
145 10368 : total_nrg = inEne[sf][band] + inEneISM[sf][band];
146 :
147 : /* target is original MASA diffuseness */
148 10368 : total_diff_nrg = (float) ( inMeta->diffuseToTotalRatio[sf][band] ) / UINT8_MAX * inEne[sf][band];
149 : /* criterion is mean of ISM ratio and new ratio */
150 10368 : dir_ratio_ism = (float) ( inMetaISM->directToTotalRatio[0][sf][band] ) / UINT8_MAX;
151 :
152 10368 : energyTimesRatioISM = ( dir_ratio_ism + ( 1.0f - total_diff_nrg / ( EPSILON + total_nrg ) ) ) / 2.0f * inEneISM[sf][band];
153 :
154 10368 : if ( energyTimesRatioISM > energyTimesRatio )
155 : {
156 : float new_dir_ratio, new_diff_ratio;
157 2394 : outMeta->directionIndex[0][sf][band] = inMetaISM->directionIndex[0][sf][band];
158 2394 : outMeta->directToTotalRatio[0][sf][band] = inMetaISM->directToTotalRatio[0][sf][band];
159 2394 : outMeta->spreadCoherence[0][sf][band] = inMetaISM->spreadCoherence[0][sf][band];
160 :
161 2394 : outMeta->surroundCoherence[sf][band] = inMetaISM->surroundCoherence[sf][band];
162 :
163 2394 : dir_nrg_ratio = 1.0f - total_diff_nrg / ( EPSILON + total_nrg ); /* new dir ratio */
164 2394 : new_dir_ratio = min( dir_nrg_ratio, dir_ratio_ism ); /* clip with original ISM dir */
165 2394 : outMeta->directToTotalRatio[0][sf][band] = (uint8_t) floorf( new_dir_ratio * UINT8_MAX );
166 2394 : new_diff_ratio = 1.0f - new_dir_ratio;
167 2394 : outMeta->diffuseToTotalRatio[sf][band] = (uint8_t) floorf( new_diff_ratio * UINT8_MAX );
168 : }
169 : else
170 : {
171 : /* use the plain original meta for this tile */
172 7974 : outMeta->directionIndex[0][sf][band] = inMeta->directionIndex[0][sf][band];
173 7974 : outMeta->directToTotalRatio[0][sf][band] = inMeta->directToTotalRatio[0][sf][band];
174 7974 : outMeta->spreadCoherence[0][sf][band] = inMeta->spreadCoherence[0][sf][band];
175 :
176 7974 : outMeta->surroundCoherence[sf][band] = inMeta->surroundCoherence[sf][band];
177 7974 : outMeta->diffuseToTotalRatio[sf][band] = inMeta->diffuseToTotalRatio[sf][band];
178 : }
179 10368 : outMeta->directionIndex[1][sf][band] = SPH_IDX_FRONT;
180 10368 : outMeta->directToTotalRatio[1][sf][band] = 0u;
181 10368 : outMeta->spreadCoherence[1][sf][band] = 0u;
182 :
183 10368 : inEne[sf][band] += inEneISM[sf][band]; /* Update energy for subsequent mergings */
184 : }
185 : }
186 :
187 : /* Set descriptive meta for mixed format */
188 108 : outMeta->descriptiveMeta.sourceFormat = 0u;
189 108 : outMeta->descriptiveMeta.transportDefinition = 0u;
190 108 : outMeta->descriptiveMeta.channelAngle = 0u;
191 108 : outMeta->descriptiveMeta.channelDistance = 0u;
192 108 : outMeta->descriptiveMeta.channelLayout = 0u;
193 108 : outMeta->descriptiveMeta.numberOfDirections = 0u;
194 : /* Number of transports should be set outside. */
195 :
196 108 : return;
197 : }
198 :
199 :
200 : /*---------------------------------------------------------------------*
201 : * full_stream_merge()
202 : *
203 : *
204 : *---------------------------------------------------------------------*/
205 :
206 900 : void full_stream_merge(
207 : MASA_DECODER_EXT_OUT_META_HANDLE outMeta, /* o : Merged metadata output */
208 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta1, /* i : Input metadata 1 */
209 : float inEne1[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS], /* i/o: TF-energy of input 1. after merge, contains the energy of the merged signal */
210 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta2, /* i : Input metadata 2 */
211 : float inEne2[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS] /* i : TF-energy of input 2 */
212 : )
213 : {
214 : float dir_nrg_1, dir_nrg_2;
215 : uint8_t n_dirs_1, n_dirs_2;
216 : uint8_t sf, band;
217 :
218 : /* full stream select based on total direct energy */
219 900 : n_dirs_1 = inMeta1->descriptiveMeta.numberOfDirections + 1u; /* to 1-based */
220 900 : n_dirs_2 = inMeta2->descriptiveMeta.numberOfDirections + 1u;
221 :
222 4500 : for ( sf = 0; sf < MAX_PARAM_SPATIAL_SUBFRAMES; sf++ )
223 : {
224 90000 : for ( band = 0; band < MASA_FREQUENCY_BANDS; band++ )
225 : {
226 86400 : dir_nrg_1 = (float) ( inMeta1->directToTotalRatio[0][sf][band] ) / UINT8_MAX * inEne1[sf][band];
227 86400 : dir_nrg_2 = (float) ( inMeta2->directToTotalRatio[0][sf][band] ) / UINT8_MAX * inEne2[sf][band];
228 :
229 86400 : if ( n_dirs_1 == 2 )
230 : {
231 28800 : dir_nrg_1 += (float) ( inMeta1->directToTotalRatio[1][sf][band] ) / UINT8_MAX * inEne1[sf][band];
232 : }
233 :
234 86400 : if ( n_dirs_2 == 2 )
235 : {
236 28800 : dir_nrg_2 += (float) ( inMeta2->directToTotalRatio[1][sf][band] ) / UINT8_MAX * inEne2[sf][band];
237 : }
238 :
239 86400 : if ( dir_nrg_1 > dir_nrg_2 )
240 : {
241 53536 : copy_masa_meta_tile( outMeta, inMeta1, sf, band );
242 : }
243 : else
244 : {
245 32864 : copy_masa_meta_tile( outMeta, inMeta2, sf, band );
246 : }
247 :
248 86400 : inEne1[sf][band] += inEne2[sf][band]; /* Update energy for subsequent mergings */
249 : }
250 : }
251 :
252 : /* Set descriptive meta for mixed format */
253 900 : outMeta->descriptiveMeta.sourceFormat = 0u;
254 900 : outMeta->descriptiveMeta.transportDefinition = 0u;
255 900 : outMeta->descriptiveMeta.channelAngle = 0u;
256 900 : outMeta->descriptiveMeta.channelDistance = 0u;
257 900 : outMeta->descriptiveMeta.channelLayout = 0u;
258 900 : if ( n_dirs_1 == 2 || n_dirs_2 == 2 )
259 : {
260 600 : outMeta->descriptiveMeta.numberOfDirections = 1u;
261 : }
262 : else
263 : {
264 300 : outMeta->descriptiveMeta.numberOfDirections = 0u;
265 : }
266 : /* Number of transports should be set outside. */
267 :
268 900 : return;
269 : }
270 :
271 :
272 : /*---------------------------------------------------------------------*
273 : * ivas_prerend_merge_masa_metadata()
274 : *
275 : *
276 : *---------------------------------------------------------------------*/
277 :
278 1008 : void ivas_prerend_merge_masa_metadata(
279 : MASA_DECODER_EXT_OUT_META_HANDLE outMeta, /* o : Merged metadata output */
280 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta1, /* i : Input metadata 1 */
281 : IVAS_REND_AudioConfigType inType1, /* i : Type of input 1 */
282 : float inEne1[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS], /* i/o: TF-energy of input 1. after merge, contains the energy of the merged signal */
283 : MASA_DECODER_EXT_OUT_META_HANDLE inMeta2, /* i : Input metadata 2 */
284 : IVAS_REND_AudioConfigType inType2, /* i : Type of input 2 */
285 : float inEne2[MAX_PARAM_SPATIAL_SUBFRAMES][MASA_FREQUENCY_BANDS] /* i : TF-energy of input 2. may be altered */
286 : )
287 : {
288 : /* mixing ISMs with non-ISM use different merge */
289 1008 : if ( inType1 == IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED && inType2 != IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED && ( inMeta1->descriptiveMeta.numberOfDirections == 0u && inMeta2->descriptiveMeta.numberOfDirections == 0u ) )
290 0 : {
291 : /* meta_1 is ISM and both are 1dir */
292 : int8_t sf;
293 :
294 0 : diffuse_meta_merge_1x1( outMeta, inMeta2, inEne2, inMeta1, inEne1 ); /* post-merge energy is now in inEne2 and needs to be copied to inEne1 */
295 :
296 0 : for ( sf = 0; sf < MAX_PARAM_SPATIAL_SUBFRAMES; sf++ )
297 : {
298 0 : mvr2r( inEne2[sf], inEne1[sf], MASA_FREQUENCY_BANDS );
299 : }
300 : }
301 1008 : else if ( inType2 == IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED && inType1 != IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED && ( inMeta1->descriptiveMeta.numberOfDirections == 0u && inMeta2->descriptiveMeta.numberOfDirections == 0u ) )
302 : {
303 : /* meta_2 is ISM and both are 1dir */
304 108 : diffuse_meta_merge_1x1( outMeta, inMeta1, inEne1, inMeta2, inEne2 );
305 : }
306 : else
307 : {
308 900 : full_stream_merge( outMeta, inMeta1, inEne1, inMeta2, inEne2 );
309 : }
310 :
311 1008 : return;
312 : }
313 :
314 :
315 : /*---------------------------------------------------------------------*
316 : * masaPrerendOpen()
317 : *
318 : *
319 : *---------------------------------------------------------------------*/
320 :
321 2 : ivas_error masaPrerendOpen(
322 : MASA_PREREND_HANDLE *hMasaPrerendPtr, /* o : handle to the opened prerenderer */
323 : int16_t numTransports, /* i : number of transport channels */
324 : int32_t input_Fs /* i : signal sampling rate */
325 : )
326 : {
327 : MASA_PREREND_HANDLE hMasaPrerend;
328 : int16_t i;
329 : ivas_error error;
330 :
331 2 : error = IVAS_ERR_OK;
332 :
333 2 : hMasaPrerend = (MASA_PREREND_HANDLE) malloc( sizeof( MASA_PREREND_DATA ) );
334 2 : if ( hMasaPrerend == NULL )
335 : {
336 0 : return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for MASA prerenderer\n" ) );
337 : }
338 :
339 2 : hMasaPrerend->num_Cldfb_instances = numTransports;
340 6 : for ( i = 0; i < hMasaPrerend->num_Cldfb_instances; i++ )
341 : {
342 4 : if ( ( error = openCldfb( &( hMasaPrerend->cldfbAnaEnc[i] ), CLDFB_ANALYSIS, input_Fs, CLDFB_PROTOTYPE_5_00MS ) ) != IVAS_ERR_OK )
343 : {
344 0 : return error;
345 : }
346 : }
347 2 : for ( ; i < MASA_MAX_TRANSPORT_CHANNELS; i++ )
348 : {
349 0 : hMasaPrerend->cldfbAnaEnc[i] = NULL;
350 : }
351 :
352 2 : if ( ( hMasaPrerend->hMasaOut = (MASA_DECODER_EXT_OUT_META_HANDLE) malloc( sizeof( MASA_DECODER_EXT_OUT_META ) ) ) == NULL )
353 : {
354 0 : return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for MASA prerenderer\n" ) );
355 : }
356 :
357 2 : if ( ( hMasaPrerend->sph_grid16 = (SPHERICAL_GRID_DATA *) malloc( sizeof( SPHERICAL_GRID_DATA ) ) ) == NULL )
358 : {
359 0 : return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for MASA prerenderer\n" ) );
360 : }
361 2 : generate_gridEq( hMasaPrerend->sph_grid16 );
362 :
363 2 : if ( error == IVAS_ERR_OK )
364 : {
365 2 : *hMasaPrerendPtr = hMasaPrerend;
366 : }
367 :
368 2 : return error;
369 : }
370 :
371 :
372 : /*---------------------------------------------------------------------*
373 : * masaPrerendClose()
374 : *
375 : *
376 : *---------------------------------------------------------------------*/
377 :
378 1316 : void masaPrerendClose(
379 : MASA_PREREND_HANDLE *hMasaPrerendPtr /* i/o: prerenderer handle to be closed */
380 : )
381 : {
382 : int16_t i;
383 :
384 1316 : if ( hMasaPrerendPtr == NULL || *hMasaPrerendPtr == NULL )
385 : {
386 1314 : return;
387 : }
388 :
389 6 : for ( i = 0; i < ( *hMasaPrerendPtr )->num_Cldfb_instances; i++ )
390 : {
391 4 : deleteCldfb( &( ( *hMasaPrerendPtr )->cldfbAnaEnc[i] ) );
392 : }
393 :
394 2 : free( ( *hMasaPrerendPtr )->hMasaOut );
395 2 : ( *hMasaPrerendPtr )->hMasaOut = NULL;
396 2 : free( ( *hMasaPrerendPtr )->sph_grid16 );
397 2 : ( *hMasaPrerendPtr )->sph_grid16 = NULL;
398 :
399 2 : free( ( *hMasaPrerendPtr ) );
400 2 : ( *hMasaPrerendPtr ) = NULL;
401 :
402 2 : return;
403 : }
|