Skip to content
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
#include "debug.h" #include "debug.h"
#endif #endif
#include "math.h" #include "math.h"
#include "ivas_rom_dec.h" #include "ivas_rom_rend.h"
#include <assert.h> #include <assert.h>
#include "wmops.h" #include "wmops.h"
...@@ -156,7 +156,9 @@ static ivas_error compute_feedback_matrix( ...@@ -156,7 +156,9 @@ static ivas_error compute_feedback_matrix(
{ {
return IVAS_ERR_INTERNAL; /* n must be 4, 6, 8 or 16, else ERROR */ return IVAS_ERR_INTERNAL; /* n must be 4, 6, 8 or 16, else ERROR */
} }
u = 1.0f / sqrtf( n );
u = inv_sqrt( n );
if ( n == 4 ) if ( n == 4 )
{ {
u = -u; u = -u;
...@@ -795,10 +797,10 @@ static void set_reverb_acoustic_data( ...@@ -795,10 +797,10 @@ static void set_reverb_acoustic_data(
{ {
int16_t nr_out_ch, hrtf_idx, offset, iter_idx, bin_idx; int16_t nr_out_ch, hrtf_idx, offset, iter_idx, bin_idx;
float ln_1e6_inverted, delay_diff, exp_argument; float ln_1e6_inverted, delay_diff, exp_argument;
float *pHrtf_set_l_re[IVAS_MAX_NUM_CH]; float *pHrtf_set_l_re[MAX_INTERN_CHANNELS];
float *pHrtf_set_l_im[IVAS_MAX_NUM_CH]; float *pHrtf_set_l_im[MAX_INTERN_CHANNELS];
float *pHrtf_set_r_re[IVAS_MAX_NUM_CH]; float *pHrtf_set_r_re[MAX_INTERN_CHANNELS];
float *pHrtf_set_r_im[IVAS_MAX_NUM_CH]; float *pHrtf_set_r_im[MAX_INTERN_CHANNELS];
/* use crend hrtf filters */ /* use crend hrtf filters */
if ( hHrtf != NULL ) if ( hHrtf != NULL )
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#include "prot.h" #include "prot.h"
#include "ivas_prot.h" #include "ivas_prot.h"
#include "ivas_rom_binauralRenderer.h" #include "ivas_rom_binauralRenderer.h"
#include "ivas_rom_dec.h" #include "ivas_rom_rend.h"
#include <math.h> #include <math.h>
#ifdef DEBUGGING #ifdef DEBUGGING
#include "debug.h" #include "debug.h"
......
...@@ -44033,19 +44033,20 @@ const float rightBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]= ...@@ -44033,19 +44033,20 @@ const float rightBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]=
} }
}; };
   
const float fastconvReverberationTimes[60] = const float fastconvReverberationTimes[CLDFB_NO_CHANNELS_MAX] =
{ {
0.429201f, 0.205110f, 0.202338f, 0.208383f, 0.215664f, 0.236545f, 0.230598f, 0.228400f, 0.227467f, 0.218956f, 0.226083f, 0.220702f, 0.221501f, 0.223471f, 0.223705f, 0.227063f, 0.227899f, 0.223071f, 0.220000f, 0.218583f, 0.220417f, 0.218250f, 0.213250f, 0.210333f, 0.207417f, 0.198750f, 0.196250f, 0.194917f, 0.190333f, 0.184500f, 0.180333f, 0.176167f, 0.176500f, 0.177583f, 0.183583f, 0.195917f, 0.203250f, 0.208417f, 0.214667f, 0.220000f, 0.222917f, 0.230417f, 0.233928f, 0.233647f, 0.236333f, 0.237428f, 0.241629f, 0.241118f, 0.238847f, 0.242384f, 0.246292f, 0.245948f, 0.246100f, 0.245396f, 0.243951f, 0.244123f, 0.239270f, 0.241474f, 0.234824f, 0.253040f, 0.429201f, 0.205110f, 0.202338f, 0.208383f, 0.215664f, 0.236545f, 0.230598f, 0.228400f, 0.227467f, 0.218956f, 0.226083f, 0.220702f, 0.221501f, 0.223471f, 0.223705f, 0.227063f, 0.227899f, 0.223071f, 0.220000f, 0.218583f, 0.220417f, 0.218250f, 0.213250f, 0.210333f, 0.207417f, 0.198750f, 0.196250f, 0.194917f, 0.190333f, 0.184500f, 0.180333f, 0.176167f, 0.176500f, 0.177583f, 0.183583f, 0.195917f, 0.203250f, 0.208417f, 0.214667f, 0.220000f, 0.222917f, 0.230417f, 0.233928f, 0.233647f, 0.236333f, 0.237428f, 0.241629f, 0.241118f, 0.238847f, 0.242384f, 0.246292f, 0.245948f, 0.246100f, 0.245396f, 0.243951f, 0.244123f, 0.239270f, 0.241474f, 0.234824f, 0.253040f,
}; };
   
   
const float fastconvReverberationEneCorrections[60] = const float fastconvReverberationEneCorrections[CLDFB_NO_CHANNELS_MAX] =
{ {
0.000584f, 0.000210f, 0.000233f, 0.000212f, 0.000257f, 0.001518f, 0.001154f, 0.001097f, 0.001265f, 0.001298f, 0.002320f, 0.002432f, 0.002686f, 0.002702f, 0.002632f, 0.002564f, 0.002732f, 0.002727f, 0.002609f, 0.002524f, 0.003417f, 0.001783f, 0.000987f, 0.000699f, 0.000606f, 0.000536f, 0.000511f, 0.000569f, 0.000600f, 0.000543f, 0.001257f, 0.001209f, 0.000957f, 0.000601f, 0.000274f, 0.000106f, 0.000072f, 0.000051f, 0.000040f, 0.000030f, 0.000024f, 0.000018f, 0.000014f, 0.000013f, 0.000012f, 0.000011f, 0.000009f, 0.000009f, 0.000008f, 0.000008f, 0.000007f, 0.000006f, 0.000005f, 0.000003f, 0.000002f, 0.000002f, 0.000001f, 0.000001f, 0.000000f, 0.000000f, 0.000584f, 0.000210f, 0.000233f, 0.000212f, 0.000257f, 0.001518f, 0.001154f, 0.001097f, 0.001265f, 0.001298f, 0.002320f, 0.002432f, 0.002686f, 0.002702f, 0.002632f, 0.002564f, 0.002732f, 0.002727f, 0.002609f, 0.002524f, 0.003417f, 0.001783f, 0.000987f, 0.000699f, 0.000606f, 0.000536f, 0.000511f, 0.000569f, 0.000600f, 0.000543f, 0.001257f, 0.001209f, 0.000957f, 0.000601f, 0.000274f, 0.000106f, 0.000072f, 0.000051f, 0.000040f, 0.000030f, 0.000024f, 0.000018f, 0.000014f, 0.000013f, 0.000012f, 0.000011f, 0.000009f, 0.000009f, 0.000008f, 0.000008f, 0.000007f, 0.000006f, 0.000005f, 0.000003f, 0.000002f, 0.000002f, 0.000001f, 0.000001f, 0.000000f, 0.000000f,
}; };
   
   
const float parametricReverberationTimes[CLDFB_NO_CHANNELS_MAX] = { const float parametricReverberationTimes[CLDFB_NO_CHANNELS_MAX] =
{
0.345494f, 0.319454f, 0.332961f, 0.360923f, 0.374299f, 0.370777f, 0.358625f, 0.348103f, 0.343109f, 0.331351f, 0.345494f, 0.319454f, 0.332961f, 0.360923f, 0.374299f, 0.370777f, 0.358625f, 0.348103f, 0.343109f, 0.331351f,
0.316502f, 0.304975f, 0.294855f, 0.287549f, 0.279920f, 0.270277f, 0.264042f, 0.256404f, 0.249899f, 0.242040f, 0.316502f, 0.304975f, 0.294855f, 0.287549f, 0.279920f, 0.270277f, 0.264042f, 0.256404f, 0.249899f, 0.242040f,
0.235074f, 0.229647f, 0.223730f, 0.218795f, 0.212599f, 0.207689f, 0.202082f, 0.198094f, 0.193907f, 0.185908f, 0.235074f, 0.229647f, 0.223730f, 0.218795f, 0.212599f, 0.207689f, 0.202082f, 0.198094f, 0.193907f, 0.185908f,
...@@ -44055,7 +44056,8 @@ const float parametricReverberationTimes[CLDFB_NO_CHANNELS_MAX] = { ...@@ -44055,7 +44056,8 @@ const float parametricReverberationTimes[CLDFB_NO_CHANNELS_MAX] = {
}; };
   
   
const float parametricReverberationEneCorrections[CLDFB_NO_CHANNELS_MAX] = { const float parametricReverberationEneCorrections[CLDFB_NO_CHANNELS_MAX] =
{
0.191981f, 0.143739f, 0.113528f, 0.093836f, 0.090147f, 0.079961f, 0.066594f, 0.072700f, 0.076491f, 0.082065f, 0.191981f, 0.143739f, 0.113528f, 0.093836f, 0.090147f, 0.079961f, 0.066594f, 0.072700f, 0.076491f, 0.082065f,
0.085265f, 0.093864f, 0.101901f, 0.113728f, 0.117646f, 0.113494f, 0.126125f, 0.126304f, 0.123928f, 0.116067f, 0.085265f, 0.093864f, 0.101901f, 0.113728f, 0.117646f, 0.113494f, 0.126125f, 0.126304f, 0.123928f, 0.116067f,
0.098528f, 0.051482f, 0.029950f, 0.025223f, 0.021143f, 0.019358f, 0.016707f, 0.016227f, 0.018416f, 0.018419f, 0.098528f, 0.051482f, 0.029950f, 0.025223f, 0.021143f, 0.019358f, 0.016707f, 0.016227f, 0.018416f, 0.018419f,
...@@ -44065,7 +44067,8 @@ const float parametricReverberationEneCorrections[CLDFB_NO_CHANNELS_MAX] = { ...@@ -44065,7 +44067,8 @@ const float parametricReverberationEneCorrections[CLDFB_NO_CHANNELS_MAX] = {
}; };
   
   
const float parametricEarlyPartEneCorrection[CLDFB_NO_CHANNELS_MAX] = { const float parametricEarlyPartEneCorrection[CLDFB_NO_CHANNELS_MAX] =
{
0.595541f, 0.684107f, 1.041399f, 0.880102f, 0.726599f, 0.529105f, 0.456758f, 0.383174f, 0.331734f, 0.330565f, 0.595541f, 0.684107f, 1.041399f, 0.880102f, 0.726599f, 0.529105f, 0.456758f, 0.383174f, 0.331734f, 0.330565f,
0.436469f, 0.592478f, 0.767344f, 1.110786f, 1.548546f, 1.946089f, 2.550005f, 3.382855f, 4.235532f, 4.688064f, 0.436469f, 0.592478f, 0.767344f, 1.110786f, 1.548546f, 1.946089f, 2.550005f, 3.382855f, 4.235532f, 4.688064f,
3.328668f, 1.312207f, 0.376543f, 0.176443f, 0.149840f, 0.130307f, 0.137089f, 0.292711f, 0.580265f, 0.733105f, 3.328668f, 1.312207f, 0.376543f, 0.176443f, 0.149840f, 0.130307f, 0.137089f, 0.292711f, 0.580265f, 0.733105f,
...@@ -44,26 +44,27 @@ ...@@ -44,26 +44,27 @@
/* Binaural rendering data set based on HRIRs */ /* Binaural rendering data set based on HRIRs */
extern const float FASTCONV_HRIR_latency_s; extern const float FASTCONV_HRIR_latency_s;
extern const float leftHRIRReal_HOA3[BINAURAL_CONVBANDS][16][7]; extern float leftHRIRReal_HOA3[BINAURAL_CONVBANDS][16][7];
extern const float leftHRIRImag_HOA3[BINAURAL_CONVBANDS][16][7]; extern float leftHRIRImag_HOA3[BINAURAL_CONVBANDS][16][7];
extern const float rightHRIRReal_HOA3[BINAURAL_CONVBANDS][16][7]; extern float rightHRIRReal_HOA3[BINAURAL_CONVBANDS][16][7];
extern const float rightHRIRImag_HOA3[BINAURAL_CONVBANDS][16][7]; extern float rightHRIRImag_HOA3[BINAURAL_CONVBANDS][16][7];
extern const float leftHRIRReal[BINAURAL_CONVBANDS][15][7]; extern float leftHRIRReal[BINAURAL_CONVBANDS][15][7];
extern const float leftHRIRImag[BINAURAL_CONVBANDS][15][7]; extern float leftHRIRImag[BINAURAL_CONVBANDS][15][7];
extern const float rightHRIRReal[BINAURAL_CONVBANDS][15][7]; extern float rightHRIRReal[BINAURAL_CONVBANDS][15][7];
extern const float rightHRIRImag[BINAURAL_CONVBANDS][15][7]; extern float rightHRIRImag[BINAURAL_CONVBANDS][15][7];
extern float FASTCONV_HOA3_latency_s;
extern float hrtfShCoeffsRe[BINAURAL_CHANNELS][HRTF_SH_CHANNELS][HRTF_NUM_BINS];
extern float hrtfShCoeffsIm[BINAURAL_CHANNELS][HRTF_SH_CHANNELS][HRTF_NUM_BINS];
extern const float FASTCONV_HOA3_latency_s;
extern const float hrtfShCoeffsRe[BINAURAL_CHANNELS][HRTF_SH_CHANNELS][HRTF_NUM_BINS];
extern const float hrtfShCoeffsIm[BINAURAL_CHANNELS][HRTF_SH_CHANNELS][HRTF_NUM_BINS];
/* Binaural rendering data set based on BRIRs */ /* Binaural rendering data set based on BRIRs */
extern const float FASTCONV_BRIR_latency_s; extern const float FASTCONV_BRIR_latency_s;
extern const float leftBRIRReal[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]; extern float leftBRIRReal[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX];
extern const float leftBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]; extern float leftBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX];
extern const float rightBRIRReal[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]; extern float rightBRIRReal[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX];
extern const float rightBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX]; extern float rightBRIRImag[BINAURAL_CONVBANDS][15][BINAURAL_NTAPS_MAX];
/* Reverberation parameters based on BRIRs for fastconv */ /* Reverberation parameters based on BRIRs for fastconv */
extern float fastconvReverberationTimes[CLDFB_NO_CHANNELS_MAX]; extern float fastconvReverberationTimes[CLDFB_NO_CHANNELS_MAX];
......
/******************************************************************************************************
(C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository. All Rights Reserved.
This software is protected by copyright law and by international treaties.
The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository retain full ownership rights in their respective contributions in
the software. This notice grants no license of any kind, including but not limited to patent
license, nor is any license granted by implication, estoppel or otherwise.
Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
contributions.
This software is provided "AS IS", without any express or implied warranties. The software is in the
development stage. It is intended exclusively for experts who have experience with such software and
solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
and fitness for a particular purpose are hereby disclaimed and excluded.
Any dispute, controversy or claim arising under or in relation to providing this software shall be
submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
the United Nations Convention on Contracts on the International Sales of Goods.
*******************************************************************************************************/
#include "options.h"
#include <stddef.h>
#ifdef DEBUGGING
#include "debug.h"
#endif
#include "ivas_rom_rend.h"
/* clang-format off */
/*----------------------------------------------------------------------------------*
* FASTCONV and PARAMETRIC binaural renderer ROM tables
*----------------------------------------------------------------------------------*/
const float dmxmtx[BINAURAL_CHANNELS][11] =
{
{ 1.0f, 0.0f, 0.70709997f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f },
{ 0.0f, 1.0f, 0.70709997f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f },
};
/*
* 0 = 30,0
* 1 = -30,0
* 2 = 0,0
* 3 = 135,0
* 4 = -135,0
* 5 = 110,0
* 6 = -110,0
* 7 = 90,0
* 8 = -90,0
* 9 = 30,35
* 10 = -30,35
* 11 = 110,35
* 12 = -110,35
* 13 = 135, 35
* 14 = -135, 35
*/
const int16_t channelIndex_CICP6[5] = { 0, 1, 2, 5, 6 };
const int16_t channelIndex_CICP12[7] = { 0, 1, 2, 5, 6, 3, 4 };
const int16_t channelIndex_CICP14[7] = { 0, 1, 2, 5, 6, 9, 10 };
const int16_t channelIndex_CICP16[9] = { 0, 1, 2, 5, 6, 9, 10, 11, 12 };
const int16_t channelIndex_CICP19[11] = { 0, 1, 2, 3, 4, 7, 8, 9, 10, 13, 14 };
/*----------------------------------------------------------------------------------*
* TD ISm binaural renderer ROM tables
*----------------------------------------------------------------------------------*/
/* The maximum target times set to 100 msec. */
const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS] =
{
1600, 3200, 4800 /* Corresponds to 16kHz, 32kHz, 48kHz */
};
/* The maximum lengths of the blocks internally in the effect. Corresponds to 6 msec. This means also that */
/* if the length of the input block is just above 6 msec, the block will be divided into two 3 msec blocks. */
const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS] =
{
96, 192, 288 /* Corresponds to 16kHz, 32kHz, 48kHz */
};
const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS] =
{
111, 222, 333 /* Corresponds to 16kHz, 32kHz, 48kHz */
};
const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS] =
{
0.0925f, 0.1850f, 0.2775f /* Corresponds to 16kHz, 32kHz, 48kHz, e.g. ( ( 2 * MaxITD ) / ( 0.05 * 48000 ) ) */
};
const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS] =
{
13, 12, 11
};
const float SincTable[321] =
{
1.00000000f, 0.99957629f, 0.99830587f, 0.99619078f, 0.99323448f, 0.98944177f, 0.98481881f, 0.97937311f,
0.97311350f, 0.96605012f, 0.95819441f, 0.94955907f, 0.94015803f, 0.93000645f, 0.91912066f, 0.90751815f,
0.89521750f, 0.88223838f, 0.86860150f, 0.85432856f, 0.83944219f, 0.82396595f, 0.80792425f, 0.79134231f,
0.77424608f, 0.75666226f, 0.73861817f, 0.72014174f, 0.70126144f, 0.68200624f, 0.66240553f, 0.64248906f,
0.62228691f, 0.60182943f, 0.58114713f, 0.56027070f, 0.53923087f, 0.51805843f, 0.49678411f, 0.47543856f,
0.45405225f, 0.43265547f, 0.41127824f, 0.38995024f, 0.36870081f, 0.34755883f, 0.32655271f, 0.30571035f,
0.28505905f, 0.26462549f, 0.24443569f, 0.22451493f, 0.20488776f, 0.18557791f, 0.16660829f, 0.14800093f,
0.12977695f, 0.11195656f, 0.09455895f, 0.07760236f, 0.06110400f, 0.04508003f, 0.02954554f, 0.01451456f,
0.00000000f, -0.01398631f, -0.02743368f, -0.04033255f, -0.05267447f, -0.06445214f, -0.07565940f, -0.08629121f,
-0.09634367f, -0.10581400f, -0.11470052f, -0.12300268f, -0.13072098f, -0.13785702f, -0.14441345f, -0.15039394f,
-0.15580318f, -0.16064685f, -0.16493160f, -0.16866498f, -0.17185547f, -0.17451243f, -0.17664604f, -0.17826729f,
-0.17938796f, -0.18002054f, -0.18017822f, -0.17987486f, -0.17912493f, -0.17794347f, -0.17634608f, -0.17434883f,
-0.17196824f, -0.16922125f, -0.16612516f, -0.16269761f, -0.15895648f, -0.15491992f, -0.15060625f, -0.14603396f,
-0.14122162f, -0.13618787f, -0.13095139f, -0.12553081f, -0.11994473f, -0.11421163f, -0.10834984f, -0.10237755f,
-0.09631271f, -0.09017300f, -0.08397586f, -0.07773838f, -0.07147731f, -0.06520902f, -0.05894946f, -0.05271415f,
-0.04651815f, -0.04037601f, -0.03430179f, -0.02830902f, -0.02241063f, -0.01661904f, -0.01094605f, -0.00540284f,
-0.00000000f, 0.00525251f, 0.01034538f, 0.01526993f, 0.02001814f, 0.02458266f, 0.02895676f, 0.03313441f,
0.03711021f, 0.04087943f, 0.04443799f, 0.04778246f, 0.05091003f, 0.05381856f, 0.05650650f, 0.05897292f,
0.06121749f, 0.06324047f, 0.06504268f, 0.06662549f, 0.06799083f, 0.06914112f, 0.07007930f, 0.07080878f,
0.07133343f, 0.07165755f, 0.07178588f, 0.07172352f, 0.07147595f, 0.07104902f, 0.07044886f, 0.06968193f,
0.06875494f, 0.06767485f, 0.06644886f, 0.06508435f, 0.06358888f, 0.06197015f, 0.06023599f, 0.05839432f,
0.05645314f, 0.05442051f, 0.05230450f, 0.05011320f, 0.04785466f, 0.04553692f, 0.04316793f, 0.04075558f,
0.03830765f, 0.03583181f, 0.03333557f, 0.03082630f, 0.02831121f, 0.02579730f, 0.02329137f, 0.02080003f,
0.01832963f, 0.01588629f, 0.01347589f, 0.01110403f, 0.00877607f, 0.00649705f, 0.00427175f, 0.00210467f,
0.00000000f, -0.00203837f, -0.00400686f, -0.00590216f, -0.00772131f, -0.00946162f, -0.01112072f, -0.01269654f,
-0.01418731f, -0.01559156f, -0.01690810f, -0.01813605f, -0.01927478f, -0.02032396f, -0.02128352f, -0.02215366f,
-0.02293482f, -0.02362769f, -0.02423318f, -0.02475245f, -0.02518686f, -0.02553797f, -0.02580754f, -0.02599752f,
-0.02611000f, -0.02614728f, -0.02611175f, -0.02600597f, -0.02583262f, -0.02559449f, -0.02529446f, -0.02493550f,
-0.02452066f, -0.02405306f, -0.02353586f, -0.02297226f, -0.02236549f, -0.02171881f, -0.02103547f, -0.02031874f,
-0.01957185f, -0.01879802f, -0.01800043f, -0.01718225f, -0.01634655f, -0.01549638f, -0.01463471f, -0.01376443f,
-0.01288838f, -0.01200928f, -0.01112977f, -0.01025241f, -0.00937962f, -0.00851376f, -0.00765705f, -0.00681160f,
-0.00597942f, -0.00516238f, -0.00436225f, -0.00358068f, -0.00281917f, -0.00207914f, -0.00136185f, -0.00066846f,
-0.00000000f, 0.00064260f, 0.00125856f, 0.00184718f, 0.00240790f, 0.00294026f, 0.00344390f, 0.00391857f,
0.00436413f, 0.00478051f, 0.00516776f, 0.00552600f, 0.00585544f, 0.00615637f, 0.00642915f, 0.00667420f,
0.00689203f, 0.00708318f, 0.00724827f, 0.00738795f, 0.00750293f, 0.00759395f, 0.00766178f, 0.00770723f,
0.00773114f, 0.00773435f, 0.00771774f, 0.00768218f, 0.00762857f, 0.00755779f, 0.00747075f, 0.00736831f,
0.00725138f, 0.00712082f, 0.00697748f, 0.00682221f, 0.00665584f, 0.00647916f, 0.00629295f, 0.00609797f,
0.00589494f, 0.00568458f, 0.00546754f, 0.00524448f, 0.00501600f, 0.00478270f, 0.00454511f, 0.00430377f,
0.00405916f, 0.00381176f, 0.00356198f, 0.00331023f, 0.00305690f, 0.00280234f, 0.00254687f, 0.00229079f,
0.00203440f, 0.00177795f, 0.00152168f, 0.00126584f, 0.00101062f, 0.00075625f, 0.00050289f, 0.00025075f,
0.00000000f
};
const float orange53_left_avg_power[257] = {
0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f,
0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f,
0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f,
0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f,
0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f,
0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f,
0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f,
0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f,
0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f,
0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f,
0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f,
0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f,
0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f,
0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f,
0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f,
0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f,
0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f,
0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f,
0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f,
0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f,
0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f,
0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f,
0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f,
0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f,
0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f,
0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f,
0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f,
0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f,
0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f
};
const float orange53_right_avg_power[257] = {
0.999231100f, 0.992580175f, 0.969233215f, 0.925614893f, 0.871408045f, 0.826101780f, 0.803222895f, 0.800087631f, 0.802672029f,
0.801490188f, 0.796555817f, 0.790879488f, 0.784882724f, 0.777585745f, 0.769326210f, 0.761789441f, 0.756145239f, 0.752754092f,
0.751703024f, 0.752594173f, 0.754317880f, 0.755515277f, 0.754378498f, 0.748860359f, 0.738919020f, 0.727488697f, 0.718792558f,
0.714865267f, 0.713446736f, 0.711076498f, 0.706021905f, 0.697553098f, 0.684623063f, 0.667031527f, 0.647006035f, 0.627680719f,
0.609939933f, 0.592472672f, 0.574803054f, 0.558499217f, 0.544599831f, 0.532128096f, 0.520152628f, 0.509682238f, 0.501904130f,
0.496162385f, 0.491121918f, 0.486813396f, 0.483951330f, 0.482198298f, 0.480713189f, 0.479654074f, 0.479590476f, 0.479965866f,
0.479589254f, 0.478181243f, 0.476334095f, 0.474199444f, 0.471616089f, 0.469089746f, 0.467486322f, 0.466943622f, 0.467153549f,
0.468381166f, 0.470996737f, 0.474416614f, 0.477639019f, 0.480612457f, 0.483910263f, 0.487287015f, 0.489909321f, 0.491668850f,
0.493155539f, 0.494319856f, 0.494512051f, 0.493615031f, 0.492155492f, 0.490116775f, 0.486886710f, 0.482303619f, 0.476902038f,
0.470775038f, 0.463377595f, 0.454571068f, 0.445130944f, 0.435581058f, 0.425568998f, 0.414717495f, 0.403531373f, 0.392556936f,
0.381436378f, 0.369506508f, 0.357099295f, 0.345049500f, 0.333368897f, 0.321326375f, 0.308959186f, 0.297232091f, 0.286592871f,
0.276453108f, 0.266589880f, 0.257950366f, 0.251341701f, 0.246435612f, 0.242861211f, 0.241405189f, 0.242839754f, 0.246688128f,
0.252115428f, 0.259297341f, 0.268399984f, 0.278481483f, 0.288520366f, 0.298599035f, 0.308846802f, 0.318350822f, 0.326248646f,
0.332813978f, 0.338464528f, 0.342543274f, 0.344278336f, 0.344031811f, 0.342641503f, 0.339995682f, 0.335437506f, 0.329174429f,
0.322237372f, 0.315035462f, 0.306967229f, 0.297821850f, 0.288482070f, 0.279766560f, 0.271234214f, 0.262228251f, 0.253214896f,
0.245183259f, 0.237939596f, 0.230546176f, 0.223051578f, 0.216552779f, 0.211263061f, 0.206180066f, 0.200917527f, 0.196485907f,
0.193453044f, 0.190857053f, 0.187853232f, 0.185171053f, 0.183685005f, 0.182665780f, 0.180928215f, 0.178784713f, 0.177342966f,
0.176323384f, 0.174430951f, 0.171496049f, 0.168740034f, 0.166518897f, 0.163711995f, 0.159658119f, 0.155442193f, 0.152056932f,
0.148795277f, 0.144545168f, 0.139905334f, 0.136263832f, 0.133493021f, 0.130194828f, 0.126240104f, 0.123071767f, 0.121281922f,
0.119557180f, 0.117016964f, 0.114773229f, 0.114072219f, 0.114103459f, 0.113414355f, 0.112460621f, 0.112842396f, 0.114564091f,
0.115944758f, 0.116569765f, 0.117913686f, 0.120910525f, 0.124211200f, 0.126575813f, 0.128826424f, 0.132578567f, 0.137430578f,
0.141675219f, 0.144987956f, 0.148879051f, 0.154273912f, 0.159992099f, 0.164641231f, 0.168560207f, 0.173201621f, 0.178906262f,
0.184429348f, 0.188756809f, 0.192309171f, 0.196154252f, 0.200732291f, 0.205381230f, 0.209404662f, 0.212832779f, 0.216197237f,
0.220162451f, 0.225029215f, 0.230637416f, 0.236752108f, 0.243243530f, 0.249900997f, 0.256293535f, 0.261716694f, 0.265186161f,
0.265652657f, 0.262010813f, 0.253508776f, 0.243198514f, 0.244490802f, 0.255167097f, 0.258825988f, 0.257396817f, 0.256197631f,
0.256865948f, 0.258354962f, 0.259370565f, 0.259730458f, 0.259894609f, 0.260285556f, 0.260970831f, 0.261650831f, 0.262020200f,
0.262095064f, 0.262225062f, 0.262741268f, 0.263585031f, 0.264350951f, 0.264654577f, 0.264539272f, 0.264409125f, 0.264633715f,
0.265172601f, 0.265621960f, 0.265678704f, 0.265469313f, 0.265454412f, 0.265907466f, 0.266625792f, 0.267101586f, 0.266997635f,
0.266522497f, 0.266185820f, 0.266298562f, 0.266692907f, 0.266907692f
};
const float orange53_coherence[257] = {
0.929530263f, 0.921171963f, 0.900268972f, 0.876067519f, 0.855227590f, 0.837884128f, 0.823401272f, 0.818804145f, 0.835025251f,
0.871971071f, 0.911253273f, 0.929330528f, 0.921199203f, 0.900894165f, 0.882577479f, 0.867001534f, 0.849280477f, 0.832460761f,
0.824062645f, 0.823441386f, 0.820908070f, 0.811902404f, 0.802339375f, 0.798648477f, 0.797345281f, 0.791158736f, 0.779512227f,
0.768243194f, 0.760565042f, 0.754912853f, 0.751044095f, 0.752276063f, 0.759258866f, 0.766927004f, 0.769716740f, 0.767338514f,
0.763358235f, 0.759508014f, 0.755201221f, 0.750362694f, 0.746060252f, 0.742611766f, 0.739434779f, 0.736354828f, 0.733443379f,
0.730109870f, 0.726028502f, 0.722365141f, 0.720153689f, 0.718220115f, 0.714793265f, 0.710619092f, 0.708084404f, 0.707218647f,
0.705624878f, 0.702472746f, 0.700073540f, 0.699947894f, 0.700519860f, 0.699934483f, 0.699344158f, 0.700895131f, 0.704551995f,
0.708814025f, 0.713567019f, 0.719995975f, 0.728467822f, 0.738399088f, 0.749545693f, 0.761859894f, 0.774593413f, 0.787218869f,
0.800481200f, 0.814727187f, 0.828367889f, 0.839860320f, 0.850490928f, 0.862034321f, 0.873037636f, 0.880097568f, 0.883217216f,
0.885473788f, 0.887664974f, 0.886511028f, 0.880120754f, 0.871120989f, 0.862524390f, 0.853262126f, 0.840783834f, 0.825854301f,
0.811407208f, 0.798167706f, 0.784307659f, 0.769172490f, 0.754072189f, 0.739893615f, 0.726129174f, 0.712544501f, 0.699519753f,
0.686980069f, 0.674778104f, 0.663931608f, 0.655511260f, 0.648816824f, 0.642671287f, 0.638217211f, 0.637585819f, 0.640332758f,
0.643755615f, 0.647433281f, 0.653589368f, 0.662824631f, 0.672268033f, 0.680022597f, 0.687623680f, 0.696763635f, 0.705829978f,
0.712574661f, 0.717432320f, 0.721986175f, 0.725707173f, 0.727064371f, 0.726255059f, 0.724350274f, 0.720927835f, 0.715189219f,
0.708206475f, 0.701428175f, 0.693923056f, 0.684313059f, 0.674107075f, 0.666009307f, 0.659245491f, 0.650998116f, 0.641600072f,
0.634524226f, 0.630267978f, 0.625348687f, 0.618164837f, 0.611785769f, 0.608430445f, 0.605561733f, 0.600407422f, 0.594782710f,
0.591767371f, 0.590365708f, 0.587845862f, 0.584915996f, 0.584355533f, 0.585834682f, 0.586913347f, 0.587935925f, 0.591403484f,
0.596784472f, 0.601111054f, 0.604539037f, 0.610374093f, 0.618451059f, 0.624519289f, 0.627448440f, 0.631859899f, 0.639748096f,
0.646256745f, 0.647378445f, 0.647664309f, 0.652599990f, 0.659044445f, 0.659743190f, 0.656243205f, 0.656651020f, 0.662200928f,
0.664544880f, 0.660030127f, 0.656303048f, 0.659881413f, 0.664978266f, 0.662953973f, 0.657274961f, 0.658065319f, 0.665406108f,
0.668446958f, 0.663809955f, 0.661349833f, 0.668595374f, 0.677367866f, 0.677208483f, 0.672289610f, 0.675831020f, 0.688208520f,
0.695776582f, 0.691749871f, 0.687812865f, 0.696674168f, 0.711764693f, 0.716045380f, 0.706839681f, 0.701565385f, 0.711955190f,
0.726487696f, 0.723370016f, 0.700417101f, 0.677427649f, 0.670733511f, 0.671355724f, 0.654210806f, 0.608316183f, 0.549225986f,
0.504217446f, 0.484227657f, 0.475346446f, 0.452598959f, 0.399407327f, 0.319485664f, 0.229244962f, 0.146649837f, 0.083417825f,
0.041744832f, 0.018142883f, 0.006854009f, 0.002511850f, 0.001177550f, 0.000840970f, 0.000701097f, 0.000571384f, 0.000458581f,
0.000376965f, 0.000320562f, 0.000278847f, 0.000245546f, 0.000218281f, 0.000195632f, 0.000176647f, 0.000160827f, 0.000147978f,
0.000137649f, 0.000129066f, 0.000121431f, 0.000114406f, 0.000108067f, 0.000102595f, 0.000097917f, 0.000093750f, 0.000089854f,
0.000086255f, 0.000083183f, 0.000080804f, 0.000079026f, 0.000077552f, 0.000076117f, 0.000074693f, 0.000073431f, 0.000072456f,
0.000071701f, 0.000071002f, 0.000070286f, 0.000069692f, 0.000069457f
};
/*----------------------------------------------------------------------------------*
* t-design and SN3D normalization table
*----------------------------------------------------------------------------------*/
/* SN3D norm */
const float norm_sn3d_hoa3[16] =
{
1.f, 1.7320508f, 1.7320508f, 1.7320508f, 2.2360680f, 2.2360680f, 2.2360680f, 2.2360680f,
2.2360680f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f, 2.6457512f
};
/* Order 11 t-design */
const uint16_t t_design_11_size = 70;
const float t_design_11_azimuth[70] =
{
1.329273e+02f, -8.393495e+01f, 8.474100e+00f, -1.133408e+02f, -1.032659e+02f, -3.323704e+01f, 2.185643e+01f, -1.565395e+02f,
-6.426475e+01f, 1.657795e+02f, -2.520283e+01f, -9.700380e+01f, 2.785464e+01f, 1.532142e+02f, -1.550616e+02f, -1.184214e+01f,
8.053873e+01f, -4.205616e+01f, -3.122333e+01f, 3.883790e+01f, 9.376069e+01f, -8.475602e+01f, 7.755368e+00f, -1.222769e+02f,
4.680127e+01f, -2.476863e+01f, 9.989047e+01f, -1.347840e+02f, -8.308802e+01f, 6.012817e+01f, 1.526447e+02f, 2.975767e+01f,
4.077932e+01f, 1.101839e+02f, 1.656521e+02f, -1.299266e+01f, 7.973599e+01f, -5.052453e+01f, 1.189239e+02f, 4.722029e+01f,
1.719253e+02f, -6.251458e+01f, -1.111567e+01f, 1.320180e+02f, -1.353555e+02f, 1.023709e+02f, 1.127393e+02f, -1.783050e+02f,
-1.223199e+02f, 5.907635e+01f, 1.517042e+02f, 2.137634e+01f, -1.690055e+02f, 1.189808e+02f, -1.160893e+02f, 9.647679e+00f,
6.089332e+01f, -1.560215e+02f, -6.346030e+01f, 1.749298e+02f, -1.752888e+02f, -1.059519e+02f, -5.019283e+01f, 1.313583e+02f,
-1.362968e+02f, 9.356446e+01f, -9.708401e+01f, -1.691583e+02f, -4.413238e+01f, 8.147954e+01f
};
const float t_design_11_elevation[70] =
{
7.692547e+00f, -2.373007e+01f, 2.351276e+01f, 7.042259e+01f, -9.896944e+00f, -7.075133e+01f, -2.646185e+01f, 4.777649e+01f,
-7.720470e+00f, 4.453436e+01f, 2.638979e+01f, -4.465789e+01f, 9.767035e+00f, -4.770533e+01f, 7.453029e+00f, -2.359012e+01f,
2.371945e+01f, 7.043827e+01f, -9.835416e+00f, -7.049808e+01f, -2.629492e+01f, 4.761480e+01f, -7.517185e+00f, 4.428623e+01f,
2.664426e+01f, -4.456937e+01f, 9.912719e+00f, -4.795996e+01f, 7.296799e+00f, -2.334460e+01f, 2.364153e+01f, 7.068431e+01f,
-9.581404e+00f, -7.039345e+01f, -2.642582e+01f, 4.775107e+01f, -7.308536e+00f, 4.426328e+01f, 2.671406e+01f, -4.431497e+01f,
9.758997e+00f, -4.803619e+01f, 7.439651e+00f, -2.333261e+01f, 2.338690e+01f, 7.082191e+01f, -9.485964e+00f, -7.058019e+01f,
-2.667403e+01f, 4.799784e+01f, -7.382762e+00f, 4.449706e+01f, 2.650250e+01f, -4.424619e+01f, 9.518451e+00f, -4.782814e+01f,
7.684274e+00f, -2.357068e+01f, 2.330745e+01f, 7.065865e+01f, -9.680889e+00f, -7.080268e+01f, -2.669635e+01f, 4.801363e+01f,
-7.637348e+00f, 4.466512e+01f, 2.630235e+01f, -4.445764e+01f, 9.523415e+00f, -4.762422e+01f
};
/*----------------------------------------------------------------------*
* Reverberator ROM tables
*-----------------------------------------------------------------------*/
const float ivas_reverb_default_fc[IVAS_REVERB_DEFAULT_N_BANDS] =
{
20.0f, 25.0f, 31.5f, 40.0f,
50.0f, 63.0f, 80.0f, 100.0f,
125.0f, 160.0f, 200.0f, 250.0f,
315.0f, 400.0f, 500.0f, 630.0f,
800.0f, 1000.0f, 1250.0f, 1600.0f,
2000.0f, 2500.0f, 3150.0f, 4000.0f,
5000.0f, 6300.0f, 8000.0f, 10000.0f,
12500.0f, 16000.0f, 20000.0f
};
const float ivas_reverb_default_RT60[IVAS_REVERB_DEFAULT_N_BANDS] =
{
1.3622f, 1.4486f, 1.3168f, 1.5787f,
1.4766f, 1.3954f, 1.2889f, 1.3462f,
1.0759f, 1.0401f, 1.097f, 1.085f,
1.091f, 1.0404f, 1.0499f, 1.0699f,
1.1028f, 1.1714f, 1.1027f, 1.0666f,
1.055f, 1.0553f, 1.0521f, 1.0569f,
1.0421f, 0.97822f, 0.80487f, 0.75944f,
0.71945f, 0.61682f, 0.60031f
};
const float ivas_reverb_default_DSR[IVAS_REVERB_DEFAULT_N_BANDS] =
{
1.8811e-08f, 2.1428e-08f, 1.3972e-08f, 1.51e-08f,
1.287e-08f, 1.8747e-08f, 2.413e-08f, 3.9927e-08f,
8.9719e-08f, 1.902e-07f, 3.702e-07f, 6.1341e-07f,
7.1432e-07f, 6.5331e-07f, 4.6094e-07f, 5.4683e-07f,
7.0134e-07f, 6.856e-07f, 7.114e-07f, 6.9604e-07f,
5.2939e-07f, 5.699e-07f, 6.1773e-07f, 5.7488e-07f,
4.7748e-07f, 2.7213e-07f, 1.3681e-07f, 1.0941e-07f,
6.2001e-08f, 2.8483e-08f, 2.6267e-08f
};
/*----------------------------------------------------------------------------------*
* Renderer SBA & MC enc/dec matrices
*----------------------------------------------------------------------------------*/
/* CICP1 - Mono */
const float ls_azimuth_CICP1[1] = { 0.0f };
const float ls_elevation_CICP1[1] = { 0.0f };
const uint32_t ls_LFE_last_idx_CICP1[1] = { 0 };
/* CICP2 - Stereo */
const uint32_t ls_LFE_last_idx_CICP2[2] = { 0, 1 };
/* CICP6 - 5.1 */
const uint32_t ls_LFE_last_idx_CICP6[6] = { 0, 1, 2, 4, 5, 3 };
/* CICP12 - 7.1 */
const uint32_t ls_LFE_last_idx_CICP12[8] = { 0, 1, 2, 4, 5, 6, 7, 3 };
/* CICP14 - 5.1.2 */
const uint32_t ls_LFE_last_idx_CICP14[8] = { 0, 1, 2, 4, 5, 6, 7, 3 };
/* CICP16 - 5.1.4 */
const uint32_t ls_LFE_last_idx_CICP16[10] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 3 };
/* CICP19 - 7.1.4 */
const uint32_t ls_LFE_last_idx_CICP19[12] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 3 };
/*----------------------------------------------------------------------------------*
* LS Renderer ROM tables
*----------------------------------------------------------------------------------*/
/* All matrices are stored with dimensions nchan_in x nchan_out */
/* Downmix matrices */
const float ls_conversion_cicpX_mono[12][1] =
{
#ifdef EXT_RENDERER
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
{1.00000000f},
#else
{1.00000000f},
{1.00000000f},
{0.70710677f},
{0.70710677f},
{0.79999995f},
{0.79999995f},
{0.79999995f},
{0.79999995f},
{0.849999964f},
{0.849999964f},
{0.849999964f},
{0.849999964f}
#endif
};
const float ls_conversion_cicpX_stereo[12][2] =
{
{1.00000000f, 0.00000000f},
{0.00000000f, 1.00000000f},
{0.70710677f, 0.70710677f},
{0.70710677f, 0.70710677f},
{0.79999995f, 0.00000000f},
{0.00000000f, 0.79999995f},
{0.79999995f, 0.00000000f},
{0.00000000f, 0.79999995f},
{0.849999964f, 0.000000000f},
{0.000000000f, 0.849999964f},
{0.849999964f, 0.000000000f},
{0.000000000f, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 6.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{7, 1.000000000f},
{14, 1.000000000f},
{21, 1.000000000f},
{28, 1.000000000f},
{35, 1.000000000f},
{40, 1.000000000f},
{47, 1.000000000f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 6.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{7, 1.000000000f},
{14, 1.000000000f},
{21, 1.000000000f},
{28, 1.000000000f},
{35, 1.000000000f},
{36, 0.849999964f},
{43, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{9, 1.000000000f},
{18, 1.000000000f},
{27, 1.000000000f},
{36, 1.000000000f},
{45, 1.000000000f},
{48, 0.849999964f},
{57, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{10, 6.0f},
#else
/* First row indicates the number of non-zero elements */
{10, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{7, 1.000000000f},
{14, 1.000000000f},
{21, 1.000000000f},
{28, 1.000000000f},
{35, 1.000000000f},
{36, 0.849999964f},
{43, 0.849999964f},
{52, 0.849999964f},
{59, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp12[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{10, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{10, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{9, 1.000000000f},
{18, 1.000000000f},
{27, 1.000000000f},
{36, 1.000000000f},
{45, 1.000000000f},
{48, 0.849999964f},
{57, 0.849999964f},
{68, 0.849999964f},
{77, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp14[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{10, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{10, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{9, 1.000000000f},
{18, 1.000000000f},
{27, 1.000000000f},
{36, 1.000000000f},
{45, 1.000000000f},
{54, 1.000000000f},
{63, 1.000000000f},
{68, 0.849999964f},
{77, 0.849999964f},
};
const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp6[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{14, 6.0f},
#else
/* First row indicates the number of non-zero elements */
{14, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{7, 1.000000000f},
{14, 1.000000000f},
{21, 1.000000000f},
{28, 1.000000000f},
{35, 1.000000000f},
{36, 0.367322683f},
{40, 0.930093586f},
{43, 0.367322683f},
{47, 0.930093586f},
{48, 0.849999964f},
{55, 0.849999964f},
{64, 0.849999964f},
{71, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp12[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{14, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{14, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{9, 1.000000000f},
{18, 1.000000000f},
{27, 1.000000000f},
{38, 1.000000000f},
{47, 1.000000000f},
{48, 0.367322683f},
{52, 0.930093586f},
{57, 0.367322683f},
{61, 0.930093586f},
{64, 0.849999964f},
{73, 0.849999964f},
{84, 0.849999964f},
{93, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp14[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{14, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{14, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{9, 1.000000000f},
{18, 1.000000000f},
{27, 1.000000000f},
{36, 1.000000000f},
{45, 1.000000000f},
{48, 0.367322683f},
{52, 0.930093586f},
{57, 0.367322683f},
{61, 0.930093586f},
{70, 1.000000000f},
{79, 1.000000000f},
{84, 0.849999964f},
{93, 0.849999964f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp16[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{14, 10.0f},
#else
/* First row indicates the number of non-zero elements */
{14, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.000000000f},
{11, 1.000000000f},
{22, 1.000000000f},
{33, 1.000000000f},
{44, 1.000000000f},
{55, 1.000000000f},
{60, 0.367322683f},
{64, 0.930093586f},
{71, 0.367322683f},
{75, 0.930093586f},
{86, 1.000000000f},
{97, 1.000000000f},
{108, 1.000000000f},
{119, 1.000000000f}
};
/* Upmix matrices */
const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp14[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 8.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.0f},
{9, 1.0f},
{18, 1.0f},
{27, 1.0f},
{36, 1.0f},
{45, 1.0f},
{52, 1.0f},
{61, 1.0f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp16[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 10.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.0f},
{11, 1.0f},
{22, 1.0f},
{33, 1.0f},
{44, 1.0f},
{55, 1.0f},
{64, 1.0f},
{75, 1.0f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp19[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 12.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.0f},
{13, 1.0f},
{26, 1.0f},
{39, 1.0f},
{54, 1.0f},
{67, 1.0f},
{76, 1.0f},
{89, 1.0f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp19[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{8, 12.0f},
#else
/* First row indicates the number of non-zero elements */
{8, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.0f},
{13, 1.0f},
{26, 1.0f},
{39, 1.0f},
{52, 1.0f},
{65, 1.0f},
{80, 1.0f},
{93, 1.0f}
};
const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp19[] =
{
#ifdef EXT_RENDERER
/* First row indicates the number of non-zero elements and the number of matrix columns */
{10, 12.0f},
#else
/* First row indicates the number of non-zero elements */
{10, 0.0f},
#endif
/* Index of non-zero element, value of non-zero element*/
{0, 1.0f},
{13, 1.0f},
{26, 1.0f},
{39, 1.0f},
{52, 1.0f},
{65, 1.0f},
{80, 1.0f},
{93, 1.0f},
{106, 1.0f},
{119, 1.0f}
};
/*
* Mapping table of input config : output config with corresponding matrix
* NULL indicates a 1:1 mapping of existing input channels to output channels ( used for upmix )
*/
const LS_CONVERSION_MAPPING ls_conversion_mapping[LS_SETUP_CONVERSION_NUM_MAPPINGS] =
{
/* Dowmix mappings - NULL is a special case for MONO / STEREO downmix */
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_MONO, NULL},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_MONO, NULL},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_MONO, NULL},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_MONO, NULL},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_MONO, NULL},
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_STEREO, NULL},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_STEREO, NULL},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_STEREO, NULL},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_STEREO, NULL},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_STEREO, NULL},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1, ls_conversion_cicp12_cicp6},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1, ls_conversion_cicp14_cicp6},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1, ls_conversion_cicp14_cicp12},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp16_cicp6},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp16_cicp12},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp16_cicp14},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1, ls_conversion_cicp19_cicp6},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_7_1, ls_conversion_cicp19_cicp12},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_2, ls_conversion_cicp19_cicp14},
{AUDIO_CONFIG_7_1_4, AUDIO_CONFIG_5_1_4, ls_conversion_cicp19_cicp16},
/* Upmix mappings - NULL implies a 1:1 upmix */
{AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1, NULL},
{AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1, NULL},
{AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_2, NULL},
{AUDIO_CONFIG_STEREO, AUDIO_CONFIG_5_1_4, NULL},
{AUDIO_CONFIG_STEREO, AUDIO_CONFIG_7_1_4, NULL},
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1, NULL},
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_2, NULL},
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_5_1_4, NULL},
{AUDIO_CONFIG_5_1, AUDIO_CONFIG_7_1_4, NULL},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_2, ls_conversion_cicp12_cicp14},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_5_1_4, ls_conversion_cicp12_cicp16},
{AUDIO_CONFIG_7_1, AUDIO_CONFIG_7_1_4, ls_conversion_cicp12_cicp19},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_5_1_4, NULL},
{AUDIO_CONFIG_5_1_2, AUDIO_CONFIG_7_1_4, ls_conversion_cicp14_cicp19},
{AUDIO_CONFIG_5_1_4, AUDIO_CONFIG_7_1_4, ls_conversion_cicp16_cicp19},
};
/* clang-format on */
...@@ -30,161 +30,112 @@ ...@@ -30,161 +30,112 @@
*******************************************************************************************************/ *******************************************************************************************************/
#ifndef IVAS_PRERENDERER_H #ifndef IVAS_ROM_REND_H
#define IVAS_PRERENDERER_H #define IVAS_ROM_REND_H
#include "options.h"
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #ifdef DEBUGGING
#include "debug.h"
#endif
#include "ivas_cnst.h"
#include "ivas_stat_rend.h"
/*----------------------------------------------------------------------------------*
* FASTCONV and PARAMETRIC binaural renderer ROM tables
*----------------------------------------------------------------------------------*/
extern const float dmxmtx[BINAURAL_CHANNELS][11];
extern const int16_t channelIndex_CICP6[5];
extern const int16_t channelIndex_CICP12[7];
extern const int16_t channelIndex_CICP14[7];
extern const int16_t channelIndex_CICP16[9];
extern const int16_t channelIndex_CICP19[11];
/*----------------------------------------------------------------------------------*
* TD ISM Object renderer
*----------------------------------------------------------------------------------*/
extern const int16_t TDREND_SRC_REND_MaxTargetTimes[IVAS_NUM_SUPPORTED_FS];
extern const int16_t TDREND_SRC_REND_MaxBlockLengths[IVAS_NUM_SUPPORTED_FS];
extern const int16_t TDREND_MaxITD[IVAS_NUM_SUPPORTED_FS];
extern const float TDREND_MaxITD_Incr[IVAS_NUM_SUPPORTED_FS];
extern const int16_t HRTF_MODEL_N_CPTS_VAR[HRTF_MODEL_N_SECTIONS];
extern const float SincTable[321];
extern const float orange53_left_avg_power[257];
extern const float orange53_right_avg_power[257];
extern const float orange53_coherence[257];
/*----------------------------------------------------------------------------------*
* t-design and SN3D normalization table
*----------------------------------------------------------------------------------*/
/* SN3D norm */
extern const float norm_sn3d_hoa3[16];
/* Order 11 t-design */
extern const uint16_t t_design_11_size;
extern const float t_design_11_azimuth[70];
extern const float t_design_11_elevation[70];
/*----------------------------------------------------------------------*
* Reverberator ROM tables
*-----------------------------------------------------------------------*/
extern const float ivas_reverb_default_fc[];
extern const float ivas_reverb_default_RT60[];
extern const float ivas_reverb_default_DSR[];
/*----------------------------------------------------------------------------------*
* Renderer SBA & MC enc/dec matrices
*----------------------------------------------------------------------------------*/
extern const float hoa_dec_mtx_CICP1[16];
extern const float ls_azimuth_CICP1[1];
extern const float ls_elevation_CICP1[1];
extern const uint32_t ls_LFE_last_idx_CICP1[1];
extern const uint32_t ls_LFE_last_idx_CICP2[2];
extern const uint32_t ls_LFE_last_idx_CICP6[6];
extern const uint32_t ls_LFE_last_idx_CICP12[8];
extern const uint32_t ls_LFE_last_idx_CICP14[8];
extern const uint32_t ls_LFE_last_idx_CICP16[10];
extern const uint32_t ls_LFE_last_idx_CICP19[12];
#include "options.h"
#include "common_api_types.h"
#include "ls_custom_file_reader.h"
#include "ivas_error.h"
#define PRERENDERER_MAX_ISM_INPUTS 16
#define PRERENDERER_MAX_MC_INPUTS 2
#define PRERENDERER_MAX_SBA_INPUTS 2
typedef enum Prndr_Ambisonics
{
prndr_ambisonics_none = -1,
prndr_ambisonics_mono = 0,
prndr_ambisonics_foa = 1,
prndr_ambisonics_soa = 2,
prndr_ambisonics_toa = 3
} Prndr_Ambisonics; /* Numerical value corresponds to Ambisonics order */
typedef enum Prndr_SpeakerLayout
{
prndr_speaker_layout_none = -1,
prndr_speaker_layout_custom = 0,
prndr_speaker_layout_mono = 1,
prndr_speaker_layout_stereo = 2,
prndr_speaker_layout_5_1 = 6,
prndr_speaker_layout_5_1_4 = 16,
prndr_speaker_layout_7_1 = 12,
prndr_speaker_layout_7_1_4 = 19
} Prndr_SpeakerLayout; /* Numerical value corresponds to CICP index */
typedef struct Prndr_AudioObjectPosition
{
int16_t azimuth;
int16_t elevation;
} Prndr_AudioObjectPosition;
typedef struct Prndr_AudioObjectMetadataBuffer
{
Prndr_AudioObjectPosition positions[PRERENDERER_MAX_ISM_INPUTS];
uint32_t numObjects;
} Prndr_AudioObjectMetadataBuffer;
typedef struct Prndr_AudioObject
{
uint8_t inputChannelIndex;
float gain_dB;
} Prndr_AudioObject;
typedef struct Prndr_ObjPanInfo
{
Prndr_AudioObjectPosition position;
float *panGains;
} Prndr_ObjPanInfo;
typedef struct Prndr_AmbisonicsBus
{
Prndr_Ambisonics ambisonicsConfig;
uint8_t inputChannelIndex;
float gain_dB;
} Prndr_AmbisonicsBus;
typedef struct Prndr_MultiChannelBus
{
Prndr_SpeakerLayout speakerLayout;
uint8_t inputChannelIndex;
float gain_dB;
} Prndr_MultiChannelBus;
typedef struct Prndr_AudioBufferConfig
{
int32_t sampleRate;
int16_t bufferSize;
int16_t numChannels;
} Prndr_AudioBufferConfig;
typedef struct Prndr_AudioBuffer
{
Prndr_AudioBufferConfig config;
float *data;
} Prndr_AudioBuffer;
typedef struct Prndr_InputConfig
{
Prndr_AudioObject audioObjects[PRERENDERER_MAX_ISM_INPUTS];
uint8_t numAudioObjects;
Prndr_MultiChannelBus multiChannelBuses[PRERENDERER_MAX_MC_INPUTS];
uint8_t numMultiChannelBuses;
Prndr_AmbisonicsBus ambisonicsBuses[PRERENDERER_MAX_SBA_INPUTS];
uint8_t numAmbisonicsBuses;
} Prndr_InputConfig;
typedef struct Prndr_OutputConfig
{
Prndr_SpeakerLayout speakerLayout;
Prndr_Ambisonics ambisonics;
IVAS_LSSETUP_CUSTOM_HANDLE outSetupCustom;
} Prndr_OutputConfig;
typedef struct Prndr_Prerenderer Prndr_Prerenderer;
/* clang-format off */
/*----------------------------------------------------------------------------------* /*----------------------------------------------------------------------------------*
* Prerenderer prototypes * LS Configuration Converter ROM tables
*----------------------------------------------------------------------------------*/ *----------------------------------------------------------------------------------*/
/*! Creates a prerenderer state. /* Downmix matrices */
* r: pointer to opened prerenderer */ extern const float ls_conversion_cicpX_mono[12][1];
Prndr_Prerenderer *Prndr_Prerenderer_open( extern const float ls_conversion_cicpX_stereo[12][2];
void extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp6[];
); extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp6[];
#ifdef FIX_I54_LS_CONVERSION
/*! Configures the prerenderer - needs to be called after Prndr_Prerenderer_open(). */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp12[];
ivas_error Prndr_Prerenderer_configure( #endif
Prndr_Prerenderer *const st, /* i : Prerenderer state */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp6[];
const Prndr_InputConfig inConfig, /* i : Input configuration */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp14[];
const Prndr_OutputConfig outConfig, /* i : Output configuration */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp6[];
int32_t frameSize, /* i : Processing frame size in samples */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp12[];
uint32_t sampleRate /* i : Processing sampling rate */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp14[];
); extern const LS_CONVERSION_MATRIX ls_conversion_cicp19_cicp16[];
/*! Renders one frame of audio samples */ /* Upmix matrices */
void Prndr_Prerenderer_render( extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp14[];
Prndr_Prerenderer *const st, /* i : Prerenderer state */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp16[];
const Prndr_AudioBuffer inAudio, /* i : Buffer with pointer to input samples and associated info */ #ifdef FIX_I54_LS_CONVERSION
const Prndr_AudioObjectMetadataBuffer metadataBuffer, /* i : Buffer with object metadata for current frame */ extern const LS_CONVERSION_MATRIX ls_conversion_cicp12_cicp19[];
Prndr_AudioBuffer outAudio /* o : Buffer with pointer to output samples and associated info */ #endif
); extern const LS_CONVERSION_MATRIX ls_conversion_cicp14_cicp19[];
extern const LS_CONVERSION_MATRIX ls_conversion_cicp16_cicp19[];
/*! Enable/disable experimental LFE handling */
void Prndr_Prerenderer_setNeverDropLfe( /* Mapping table of input config : output config with corresponding matrix */
Prndr_Prerenderer *st, /* i : Prerenderer state */ extern const LS_CONVERSION_MAPPING ls_conversion_mapping[];
int8_t neverDropLfe /* i : If 0, LFE channel will be dropped when rendering to configs w/o LFE.
If 1, tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE. */ #endif /* IVAS_ROM_REND_H */
);
/*! Get number of input channels based on InputConfig */
int16_t Prndr_Prerenderer_getInChannels(
Prndr_Prerenderer *st /* i : Prerenderer state */
);
/*! Get number of output channels based on OutputConfig */
int16_t Prndr_Prerenderer_getOutChannels(
Prndr_Prerenderer *st /* i : Prerenderer state */
);
/*! Destructs the prerenderer state and frees memory */
void Prndr_Prerenderer_close(
Prndr_Prerenderer *st /* i : Prerenderer state */
);
/* clang-format on */
#endif /* IVAS_PRERENDERER_H */
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#endif #endif
#include "wmops.h" #include "wmops.h"
#ifndef EXT_RENDERER
/*-----------------------------------------------------------------------* /*-----------------------------------------------------------------------*
* Local Constants * Local Constants
...@@ -61,6 +62,7 @@ ...@@ -61,6 +62,7 @@
static void SHrotmatgen( float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], float Rmat[3][3], const int16_t order ); static void SHrotmatgen( float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], float Rmat[3][3], const int16_t order );
#endif
/*-----------------------------------------------------------------------* /*-----------------------------------------------------------------------*
* ivas_headTrack_open() * ivas_headTrack_open()
...@@ -104,7 +106,11 @@ ivas_error ivas_headTrack_open( ...@@ -104,7 +106,11 @@ ivas_error ivas_headTrack_open(
*---------------------------------------------------------------------------------*/ *---------------------------------------------------------------------------------*/
void QuatToRotMat( void QuatToRotMat(
#ifdef EXT_RENDERER
const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */
#else
const Quaternion quat, /* i : quaternion describing the rotation */ const Quaternion quat, /* i : quaternion describing the rotation */
#endif
float Rmat[3][3] /* o : real-space rotation matrix for this rotation */ float Rmat[3][3] /* o : real-space rotation matrix for this rotation */
) )
{ {
...@@ -173,7 +179,11 @@ void QuatToRotMat( ...@@ -173,7 +179,11 @@ void QuatToRotMat(
*------------------------------------------------------------------------*/ *------------------------------------------------------------------------*/
void Quat2Euler( void Quat2Euler(
#ifdef EXT_RENDERER
const IVAS_QUATERNION quat, /* i : quaternion describing the rotation */
#else
const Quaternion quat, /* i : quaternion describing the rotation */ const Quaternion quat, /* i : quaternion describing the rotation */
#endif
float *yaw, /* o : yaw */ float *yaw, /* o : yaw */
float *pitch, /* o : pitch */ float *pitch, /* o : pitch */
float *roll /* o : roll */ float *roll /* o : roll */
...@@ -302,7 +312,9 @@ void rotateAziEle_DirAC( ...@@ -302,7 +312,9 @@ void rotateAziEle_DirAC(
void rotateFrame_shd( void rotateFrame_shd(
HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */
float output[][L_FRAME48k], /* i/o: unrotated HOA3 signal buffer in TD */ float output[][L_FRAME48k], /* i/o: unrotated HOA3 signal buffer in TD */
const int32_t output_fs, /* i : output sampling frequency */ #ifndef EXT_RENDERER
const int32_t output_Fs, /* i : output sampling frequency */
#endif
const int16_t subframe_len, /* i : subframe length per channel */ const int16_t subframe_len, /* i : subframe length per channel */
const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */
const int16_t subframe_idx /* i : subframe index */ const int16_t subframe_idx /* i : subframe index */
...@@ -310,23 +322,39 @@ void rotateFrame_shd( ...@@ -310,23 +322,39 @@ void rotateFrame_shd(
{ {
int16_t i, l, n, m; int16_t i, l, n, m;
int16_t m1, m2; int16_t m1, m2;
#ifdef EXT_RENDERER
int16_t shd_rot_max_order;
#else
int16_t shd_rot_max_order, fade_len_smp; int16_t shd_rot_max_order, fade_len_smp;
#endif
float tmp; float tmp;
float tmpRot[2 * HEADROT_ORDER + 1]; float tmpRot[2 * HEADROT_ORDER + 1];
float SHrotmat_prev[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; float SHrotmat_prev[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM];
#ifdef EXT_RENDERER
float cross_fade[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES];
#else
float cross_fade[IVAS_FB_1MS_48K_SAMP]; float cross_fade[IVAS_FB_1MS_48K_SAMP];
#endif
shd_rot_max_order = hTransSetup.ambisonics_order; shd_rot_max_order = hTransSetup.ambisonics_order;
#ifdef EXT_RENDERER
tmp = 1.0f / ( subframe_len - 1 );
for ( i = 0; i < subframe_len; i++ )
{
cross_fade[i] = i * tmp;
}
#else
/* 1ms linear crossfade */ /* 1ms linear crossfade */
fade_len_smp = NS2SA( output_fs, 1000000 ); fade_len_smp = NS2SA( output_Fs, 1000000 );
tmp = 1.0f / fade_len_smp; tmp = 1.0f / fade_len_smp;
for ( i = 0; i < fade_len_smp; i++ ) for ( i = 0; i < fade_len_smp; i++ )
{ {
cross_fade[i] = ( i + 1 ) * tmp; cross_fade[i] = ( i + 1 ) * tmp;
} }
#endif
/* initialize rotation matrices with zeros */ /* initialize rotation matrices with zeros */
for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) for ( i = 0; i < HEADROT_SHMAT_DIM; i++ )
...@@ -360,14 +388,18 @@ void rotateFrame_shd( ...@@ -360,14 +388,18 @@ void rotateFrame_shd(
for ( m = m1; m < m2; m++ ) for ( m = m1; m < m2; m++ )
{ {
/* crossfade with previous rotation gains */ /* crossfade with previous rotation gains */
#ifndef EXT_RENDERER
if ( i < fade_len_smp ) if ( i < fade_len_smp )
{ {
#endif
tmpRot[n - m1] += cross_fade[i] * SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i] + ( 1 - cross_fade[i] ) * SHrotmat_prev[n][m] * output[m][subframe_idx * subframe_len + i]; tmpRot[n - m1] += cross_fade[i] * SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i] + ( 1 - cross_fade[i] ) * SHrotmat_prev[n][m] * output[m][subframe_idx * subframe_len + i];
#ifndef EXT_RENDERER
} }
else else
{ {
tmpRot[n - m1] += SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i]; tmpRot[n - m1] += SHrotmat[n][m] * output[m][subframe_idx * subframe_len + i];
} }
#endif
} }
} }
/* write back the result */ /* write back the result */
...@@ -415,7 +447,9 @@ void rotateFrame_shd( ...@@ -415,7 +447,9 @@ void rotateFrame_shd(
void rotateFrame_sd( void rotateFrame_sd(
HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */ HEAD_TRACK_DATA_HANDLE hHeadTrackData, /* i : head track handle */
float output[][L_FRAME48k], /* i/o: unrotated SD signal buffer in TD */ float output[][L_FRAME48k], /* i/o: unrotated SD signal buffer in TD */
#ifndef EXT_RENDERER
const int32_t output_Fs, /* i : output sampling frequency */ const int32_t output_Fs, /* i : output sampling frequency */
#endif
const int16_t subframe_len, /* i : subframe length per channel */ const int16_t subframe_len, /* i : subframe length per channel */
const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */ const IVAS_OUTPUT_SETUP hTransSetup, /* i : format for rotation */
const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */ const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */
...@@ -426,20 +460,33 @@ void rotateFrame_sd( ...@@ -426,20 +460,33 @@ void rotateFrame_sd(
int16_t nchan, index_lfe; int16_t nchan, index_lfe;
int16_t ch_in, ch_in_woLFE, ch_out, ch_out_woLFE; int16_t ch_in, ch_in_woLFE, ch_out, ch_out_woLFE;
int16_t azimuth, elevation; int16_t azimuth, elevation;
#ifndef EXT_RENDERER
int16_t fade_len_smp; int16_t fade_len_smp;
#endif
float tmp; float tmp;
float tmp_gains[MAX_CICP_CHANNELS - 1]; float tmp_gains[MAX_CICP_CHANNELS - 1];
float gains[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS]; float gains[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS];
float gains_prev[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS]; float gains_prev[MAX_CICP_CHANNELS][MAX_CICP_CHANNELS];
float output_tmp[MAX_CICP_CHANNELS][L_FRAME48k]; float output_tmp[MAX_CICP_CHANNELS][L_FRAME48k];
#ifdef EXT_RENDERER
float cross_fade[L_FRAME48k / MAX_PARAM_SPATIAL_SUBFRAMES];
#else
float cross_fade[IVAS_FB_1MS_48K_SAMP]; float cross_fade[IVAS_FB_1MS_48K_SAMP];
#endif
wmops_sub_start( "rotateFrame_sd" ); wmops_sub_start( "rotateFrame_sd" );
nchan = hTransSetup.nchan_out_woLFE + hTransSetup.num_lfe; nchan = hTransSetup.nchan_out_woLFE + hTransSetup.num_lfe;
index_lfe = hTransSetup.index_lfe[0]; index_lfe = hTransSetup.index_lfe[0];
#ifdef EXT_RENDERER
tmp = 1.0f / ( subframe_len - 1 );
for ( i = 0; i < subframe_len; i++ )
{
cross_fade[i] = i * tmp;
}
#else
/* 1ms linear crossfade */ /* 1ms linear crossfade */
fade_len_smp = NS2SA( output_Fs, 1000000 ); fade_len_smp = NS2SA( output_Fs, 1000000 );
tmp = 1.0f / fade_len_smp; tmp = 1.0f / fade_len_smp;
...@@ -447,6 +494,7 @@ void rotateFrame_sd( ...@@ -447,6 +494,7 @@ void rotateFrame_sd(
{ {
cross_fade[i] = ( i + 1 ) * tmp; cross_fade[i] = ( i + 1 ) * tmp;
} }
#endif
/* Get next quaternion and calculate rotation matrix */ /* Get next quaternion and calculate rotation matrix */
QuatToRotMat( hHeadTrackData->Quaternions[hHeadTrackData->num_quaternions++], hHeadTrackData->Rmat ); QuatToRotMat( hHeadTrackData->Quaternions[hHeadTrackData->num_quaternions++], hHeadTrackData->Rmat );
...@@ -473,7 +521,11 @@ void rotateFrame_sd( ...@@ -473,7 +521,11 @@ void rotateFrame_sd(
/* gains for previous subframe rotation */ /* gains for previous subframe rotation */
rotateAziEle( hTransSetup.ls_azimuth[ch_in_woLFE], hTransSetup.ls_elevation[ch_in_woLFE], &azimuth, &elevation, hHeadTrackData->Rmat_prev, hTransSetup.is_planar_setup ); rotateAziEle( hTransSetup.ls_azimuth[ch_in_woLFE], hTransSetup.ls_elevation[ch_in_woLFE], &azimuth, &elevation, hHeadTrackData->Rmat_prev, hTransSetup.is_planar_setup );
#ifdef EXT_RENDERER
if ( hEFAPdata != NULL && ( hTransSetup.ls_azimuth[ch_in_woLFE] != azimuth || hTransSetup.ls_elevation[ch_in_woLFE] != elevation ) )
#else
if ( hEFAPdata != NULL && ( hTransSetup.ls_azimuth[ch_in_woLFE] != azimuth && hTransSetup.ls_elevation[ch_in_woLFE] != elevation ) ) if ( hEFAPdata != NULL && ( hTransSetup.ls_azimuth[ch_in_woLFE] != azimuth && hTransSetup.ls_elevation[ch_in_woLFE] != elevation ) )
#endif
{ {
efap_determine_gains( hEFAPdata, tmp_gains, azimuth, elevation, EFAP_MODE_EFAP ); efap_determine_gains( hEFAPdata, tmp_gains, azimuth, elevation, EFAP_MODE_EFAP );
for ( ch_out = 0; ch_out < nchan; ch_out++ ) for ( ch_out = 0; ch_out < nchan; ch_out++ )
...@@ -520,14 +572,20 @@ void rotateFrame_sd( ...@@ -520,14 +572,20 @@ void rotateFrame_sd(
for ( ch_in = 0; ch_in < nchan; ch_in++ ) for ( ch_in = 0; ch_in < nchan; ch_in++ )
{ {
/* crossfade with previous rotation gains */ /* crossfade with previous rotation gains */
#ifdef EXT_RENDERER
for ( i = subframe_idx * subframe_len, j = 0; j < subframe_len; i++, j++ )
#else
for ( i = subframe_idx * subframe_len, j = 0; j < fade_len_smp; i++, j++ ) for ( i = subframe_idx * subframe_len, j = 0; j < fade_len_smp; i++, j++ )
#endif
{ {
output_tmp[ch_out][i] += ( cross_fade[j] ) * gains[ch_in][ch_out] * output[ch_in][i] + ( 1 - cross_fade[j] ) * gains_prev[ch_in][ch_out] * output[ch_in][i]; output_tmp[ch_out][i] += ( cross_fade[j] ) * gains[ch_in][ch_out] * output[ch_in][i] + ( 1 - cross_fade[j] ) * gains_prev[ch_in][ch_out] * output[ch_in][i];
} }
#ifndef EXT_RENDERER
for ( ; i < ( subframe_idx + 1 ) * subframe_len; i++ ) for ( ; i < ( subframe_idx + 1 ) * subframe_len; i++ )
{ {
output_tmp[ch_out][i] += gains[ch_in][ch_out] * output[ch_in][i]; output_tmp[ch_out][i] += gains[ch_in][ch_out] * output[ch_in][i];
} }
#endif
} }
} }
...@@ -879,7 +937,11 @@ static float SHrot_w( ...@@ -879,7 +937,11 @@ static float SHrot_w(
} }
} }
#ifdef EXT_RENDERER
void SHrotmatgen(
#else
static void SHrotmatgen( static void SHrotmatgen(
#endif
float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o : rotation matrix in SHD */ float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o : rotation matrix in SHD */
float Rmat[3][3], /* i : real-space rotation matrix */ float Rmat[3][3], /* i : real-space rotation matrix */
const int16_t order /* i : ambisonics order */ const int16_t order /* i : ambisonics order */
......
...@@ -47,7 +47,293 @@ ...@@ -47,7 +47,293 @@
* Local function prototypes * Local function prototypes
*-----------------------------------------------------------------------*/ *-----------------------------------------------------------------------*/
#ifndef EXT_RENDERER
static void ivas_sba_mtx_mult( float output_f[][L_FRAME48k], const int16_t output_frame, const int16_t nchan_in, const IVAS_OUTPUT_SETUP output_setup, const float *mtx_hoa_decoder ); static void ivas_sba_mtx_mult( float output_f[][L_FRAME48k], const int16_t output_frame, const int16_t nchan_in, const IVAS_OUTPUT_SETUP output_setup, const float *mtx_hoa_decoder );
#endif
#ifdef DEBUG_MODE_DIRAC
static void debug_mode_dirac( float output[MAX_OUTPUT_CHANNELS][L_FRAME48k], const int16_t nchan_transport, const int16_t output_frame );
#endif
/*-------------------------------------------------------------------------*
* ivas_mc2sba()
*
* MC signals transformed into SBA in TD domain
*-------------------------------------------------------------------------*/
void ivas_mc2sba(
IVAS_OUTPUT_SETUP hIntSetup, /* i : Format of decoder output */
float buffer_td[][L_FRAME48k], /* i/o: MC signals (on input) and the HOA3 (on output) */
const int16_t output_frame, /* i : output frame length per channel */
const int16_t sba_order, /* i : Ambisonic (SBA) order */
const float gain_lfe /* i : gain for LFE, 0 = ignore LFE */
)
{
int16_t i, j, k;
int16_t idx_lfe, idx_in;
float buffer_tmp[16][L_FRAME48k];
float gains[16];
int16_t azimuth, elevation;
int16_t sba_num_chans;
assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" );
/* Init*/
sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 );
for ( j = 0; j < sba_num_chans; j++ )
{
set_zero( buffer_tmp[j], output_frame );
}
/* HOA encoding*/
idx_lfe = 0;
idx_in = 0;
for ( i = 0; i < hIntSetup.nchan_out_woLFE + hIntSetup.num_lfe; i++ )
{
if ( ( hIntSetup.num_lfe > 0 ) && ( i == hIntSetup.index_lfe[idx_lfe] ) )
{
if ( gain_lfe > 0.f )
{
/* Add LFE to omni W with gain*/
for ( k = 0; k < output_frame; k++ )
{
buffer_tmp[0][k] += gain_lfe * buffer_td[i][k];
}
}
if ( idx_lfe < ( hIntSetup.num_lfe - 1 ) )
{
idx_lfe++;
}
}
else
{
azimuth = (int16_t) ( hIntSetup.ls_azimuth[idx_in] );
elevation = (int16_t) ( hIntSetup.ls_elevation[idx_in] );
idx_in++;
/* get HOA response for direction (ACN/SN3D)*/
ivas_dirac_dec_get_response(
azimuth,
elevation,
gains,
sba_order );
for ( j = 0; j < sba_num_chans; j++ )
{
for ( k = 0; k < output_frame; k++ )
{
buffer_tmp[j][k] += gains[j] * buffer_td[i][k];
}
}
}
}
for ( j = 0; j < sba_num_chans; j++ )
{
mvr2r( buffer_tmp[j], buffer_td[j], output_frame );
}
return;
}
/*-------------------------------------------------------------------------*
* ivas_sba2MC_cldfb()
*
* SBA signals transformed into MC in CLDFB domain
*-------------------------------------------------------------------------*/
void ivas_sba2mc_cldfb(
IVAS_OUTPUT_SETUP hInSetup, /* i : Format of input layout */
float RealBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb real part */
float ImagBuffer[][MAX_PARAM_SPATIAL_SUBFRAMES][CLDFB_NO_CHANNELS_MAX], /* i/o: cldfb imag part */
const int16_t nb_channels_out, /* i : nb of output channels */
const int16_t nb_bands, /* i : nb of CLDFB bands to process */
const float *hoa_dec_mtx /* i : HOA decoding mtx */
)
{
int16_t iBlock, iBand, n, m;
float realOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX], imagOut[16][MAX_PARAM_SPATIAL_SUBFRAMES * CLDFB_NO_CHANNELS_MAX];
float g;
float *p_real, *p_imag, *p_realOut, *p_imagOut;
int16_t nb_channels_in;
wmops_sub_start( "ivas_sba2mc_cldfb" );
nb_channels_in = hInSetup.nchan_out_woLFE;
assert( ( nb_channels_in == 16 ) && ( nb_channels_out == 11 ) && "ivas_sba2mc_cldfb; only HOA3 to CICP19 is for now supported!" );
for ( n = 0; n < nb_channels_out; n++ )
{
set_zero( realOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands );
set_zero( imagOut[n], MAX_PARAM_SPATIAL_SUBFRAMES * nb_bands );
for ( m = 0; m < nb_channels_in; m++ )
{
g = hoa_dec_mtx[SBA_NHARM_HOA3 * n + m];
p_realOut = realOut[n];
p_imagOut = imagOut[n];
for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ )
{
p_real = RealBuffer[m][iBlock];
p_imag = ImagBuffer[m][iBlock];
for ( iBand = 0; iBand < nb_bands; iBand++ )
{
*p_realOut = *p_realOut + g * *( p_real++ );
*p_imagOut = *p_imagOut + g * *( p_imag++ );
p_realOut++;
p_imagOut++;
}
}
}
}
for ( n = 0; n < nb_channels_out; n++ )
{
p_realOut = realOut[n];
p_imagOut = imagOut[n];
for ( iBlock = 0; iBlock < MAX_PARAM_SPATIAL_SUBFRAMES; iBlock++ )
{
p_real = RealBuffer[n][iBlock];
p_imag = ImagBuffer[n][iBlock];
for ( iBand = 0; iBand < nb_bands; iBand++ )
{
*( p_real++ ) = *p_realOut++;
*( p_imag++ ) = *p_imagOut++;
}
}
}
wmops_sub_end();
return;
}
/*-------------------------------------------------------------------*
* ivas_sba_remapTCs()
*
* Get TCs from Ambisonics signal in ACN
*-------------------------------------------------------------------*/
int16_t ivas_sba_remapTCs(
float sba_data[][L_FRAME48k], /* i/o: SBA signals */
Decoder_Struct *st_ivas, /* i/o: decoder struct */
const int16_t output_frame /* i : frame length */
)
{
int16_t nchan_remapped;
#ifdef DEBUG_MODE_DIRAC
debug_mode_dirac( sba_data, st_ivas->nchan_transport, output_frame );
#endif
nchan_remapped = st_ivas->nchan_transport;
if ( ( st_ivas->sba_mode != SBA_MODE_SPAR && st_ivas->sba_planar && nchan_remapped >= 3 ) ||
( ( st_ivas->sba_mode == SBA_MODE_SPAR ) && nchan_remapped == 3 ) )
{
nchan_remapped++;
if ( st_ivas->sba_mode != SBA_MODE_SPAR )
{
assert( ( ( st_ivas->nchan_transport == 3 ) || ( st_ivas->nchan_transport == 5 ) || ( st_ivas->nchan_transport == 7 ) ) && "Number of channels must be odd for SBA planar!" );
}
if ( nchan_remapped == 4 )
{
/*For planar A-format channel 2 and 3 are identical -> Z=0*/
mvr2r( sba_data[2], sba_data[3], output_frame );
}
}
if ( st_ivas->nchan_transport >= 3 )
{
int16_t i = 0;
float temp;
/*convert WYXZ downmix to WYZX*/
for ( i = 0; i < output_frame; i++ )
{
temp = sba_data[2][i];
sba_data[2][i] = sba_data[3][i];
sba_data[3][i] = temp;
if ( st_ivas->nchan_transport == 3 )
{
sba_data[2][i] = 0;
}
}
}
if ( st_ivas->sba_mode != SBA_MODE_SPAR )
{
ivas_sba_zero_vert_comp( sba_data, st_ivas->sba_analysis_order, st_ivas->sba_planar, output_frame );
}
return ( nchan_remapped );
}
/*-------------------------------------------------------------------------*
* ivas_ism2sba()
*
* ISM transformed into SBA in TD domain.
*-------------------------------------------------------------------------*/
void ivas_ism2sba(
float buffer_td[][L_FRAME48k], /* i/o: TD signal buffers */
ISM_RENDERER_HANDLE hIsmRendererData, /* i/o: renderer data */
const ISM_METADATA_HANDLE hIsmMetaData[], /* i : object metadata */
const int16_t num_objects, /* i : number of objects */
const int16_t output_frame, /* i : output frame length per channel */
const int16_t sba_order /* i : Ambisonic (SBA) order */
)
{
int16_t i, j, k;
float buffer_tmp[16][L_FRAME48k];
float gains[16];
float g1, g2;
int16_t azimuth, elevation;
int16_t sba_num_chans;
assert( ( sba_order <= 3 ) && "Only order up to 3 is supported!" );
assert( hIsmRendererData != NULL && "hIsmRendererData not allocated!" );
/* Init*/
sba_num_chans = ( sba_order + 1 ) * ( sba_order + 1 );
for ( j = 0; j < sba_num_chans; j++ )
{
set_zero( buffer_tmp[j], output_frame );
}
for ( i = 0; i < num_objects; i++ )
{
azimuth = (int16_t) ( hIsmMetaData[i]->azimuth + 0.5f );
elevation = (int16_t) ( hIsmMetaData[i]->elevation + 0.5f );
/*get HOA gets for direction (ACN/SN3D)*/
ivas_dirac_dec_get_response( azimuth, elevation, gains, sba_order );
for ( j = 0; j < sba_num_chans; j++ )
{
g2 = 0.f;
for ( k = 0; k < output_frame; k++ )
{
g2 += 1.f / output_frame;
g1 = 1.0f - g2;
buffer_tmp[j][k] += ( g2 * gains[j] + g1 * hIsmRendererData->prev_gains[i][j] ) * buffer_td[i][k];
}
hIsmRendererData->prev_gains[i][j] = gains[j];
}
}
for ( j = 0; j < sba_num_chans; j++ )
{
mvr2r( buffer_tmp[j], buffer_td[j], output_frame );
}
return;
}
/*-------------------------------------------------------------------* /*-------------------------------------------------------------------*
...@@ -142,7 +428,11 @@ ivas_error ivas_sba_linear_renderer( ...@@ -142,7 +428,11 @@ ivas_error ivas_sba_linear_renderer(
* HOA decoding with LFE insertion * HOA decoding with LFE insertion
*-------------------------------------------------------------------*/ *-------------------------------------------------------------------*/
#ifdef EXT_RENDERER
void ivas_sba_mtx_mult(
#else
static void ivas_sba_mtx_mult( static void ivas_sba_mtx_mult(
#endif
float output_f[][L_FRAME48k], /* i/o: synthesized core-coder transport channels/DirAC output */ float output_f[][L_FRAME48k], /* i/o: synthesized core-coder transport channels/DirAC output */
const int16_t output_frame, /* i : output frame length per channel */ const int16_t output_frame, /* i : output frame length per channel */
const int16_t nchan_in, /* i : Number of ambisonic channels */ const int16_t nchan_in, /* i : Number of ambisonic channels */
...@@ -205,49 +495,16 @@ static void ivas_sba_mtx_mult( ...@@ -205,49 +495,16 @@ static void ivas_sba_mtx_mult(
void ivas_sba_upmixer_renderer( void ivas_sba_upmixer_renderer(
Decoder_Struct *st_ivas, /* i/o: IVAS decoder struct */ Decoder_Struct *st_ivas, /* i/o: IVAS decoder struct */
float output[][L_FRAME48k], /* i/o: transport/output audio channels */ float output[][L_FRAME48k], /* i/o: transport/output audio channels */
#ifndef SPAR_SCALING_HARMONIZATION
const int16_t nchan_remapped, /* i : num channels after remapping of TCs */
#endif
const int16_t output_frame /* i : output frame length */ const int16_t output_frame /* i : output frame length */
) )
{ {
#ifdef SPAR_SCALING_HARMONIZATION int16_t i, nchan_internal;
int16_t i;
#else
int16_t i, ch, nchan_out;
#endif
float temp; float temp;
int16_t nchan_internal;
wmops_sub_start( "ivas_sba_upmixer_renderer" ); wmops_sub_start( "ivas_sba_upmixer_renderer" );
nchan_internal = ivas_sba_get_nchan_metadata( st_ivas->sba_analysis_order ); nchan_internal = ivas_sba_get_nchan_metadata( st_ivas->sba_analysis_order );
#ifndef SPAR_SCALING_HARMONIZATION
nchan_out = st_ivas->hDecoderConfig->nchan_out;
#endif
#ifndef SPAR_SCALING_HARMONIZATION
for ( ch = 0; ch < nchan_remapped; ch++ )
{
for ( i = 0; i < output_frame; i++ )
{
temp = output[ch][i];
temp = floorf( temp + 0.5f );
if ( temp > MAX16B_FLT )
{
temp = MAX16B_FLT;
}
else if ( temp < ( -1.0f * PCM16_TO_FLT_FAC ) )
{
temp = ( -1.0f * PCM16_TO_FLT_FAC );
}
temp *= ( 1.0f / PCM16_TO_FLT_FAC );
output[ch][i] = temp;
}
}
#endif
if ( st_ivas->nchan_transport >= 3 ) if ( st_ivas->nchan_transport >= 3 )
{ {
/*convert WYZX downmix to WYXZ*/ /*convert WYZX downmix to WYXZ*/
...@@ -267,17 +524,6 @@ void ivas_sba_upmixer_renderer( ...@@ -267,17 +524,6 @@ void ivas_sba_upmixer_renderer(
ivas_sba_linear_renderer( output, output_frame, st_ivas->hIntSetup.nchan_out_woLFE, st_ivas->hDecoderConfig->output_config, st_ivas->hOutSetup, st_ivas->hoa_dec_mtx ); ivas_sba_linear_renderer( output, output_frame, st_ivas->hIntSetup.nchan_out_woLFE, st_ivas->hDecoderConfig->output_config, st_ivas->hOutSetup, st_ivas->hoa_dec_mtx );
} }
#ifndef SPAR_SCALING_HARMONIZATION
for ( ch = 0; ch < nchan_out; ch++ )
{
for ( i = 0; i < output_frame; i++ )
{
output[ch][i] = output[ch][i] * PCM16_TO_FLT_FAC;
}
}
#endif
wmops_sub_end(); wmops_sub_end();
return; return;
......
...@@ -30,33 +30,73 @@ ...@@ -30,33 +30,73 @@
*******************************************************************************************************/ *******************************************************************************************************/
#include "ivas_rom_prerenderer.h" #ifndef IVAS_STAT_REND_H
#include "wmops.h" #define IVAS_STAT_REND_H
/* clang-format off */ #include <stdint.h>
#include "ivas_cnst.h"
#define MAX_SPEAKERS 12 /* Max number of speakers (including LFE) in a channel-based config */
/*----------------------------------------------------------------------------------* /*----------------------------------------------------------------------------------*
* Prerenderer SBA & MC enc/dec matrices * Loudspeaker Configuration Conversion structure
*----------------------------------------------------------------------------------*/ *----------------------------------------------------------------------------------*/
/* CICP1 - Mono */ typedef struct ivas_LS_setupconversion_struct
const float ls_azimuth_CICP1[1] = { 0.0f }; {
const float ls_elevation_CICP1[1] = { 0.0f }; float *dmxMtx[MAX_OUTPUT_CHANNELS];
const uint32_t ls_LFE_last_idx_CICP1[1] = { 0 }; float *targetEnergyPrev[MAX_OUTPUT_CHANNELS];
float *dmxEnergyPrev[MAX_OUTPUT_CHANNELS];
int16_t sfbOffset[MAX_SFB + 2];
int16_t sfbCnt;
} LSSETUP_CONVERSION_STRUCT, *LSSETUP_CONVERSION_HANDLE;
/* CICP2 - Stereo */ typedef struct ivas_LS_setupconversion_matrix
const uint32_t ls_LFE_last_idx_CICP2[2] = { 0, 1 }; {
int16_t index;
float value;
} LS_CONVERSION_MATRIX;
/* CICP6 - 5.1 */ typedef struct ivas_LS_setupconversion_mapping
const uint32_t ls_LFE_last_idx_CICP6[6] = { 0, 1, 2, 4, 5, 3 }; {
AUDIO_CONFIG input_config;
AUDIO_CONFIG output_config;
const LS_CONVERSION_MATRIX *conversion_matrix;
} LS_CONVERSION_MAPPING;
/* CICP12 - 7.1 */ typedef struct ivas_mono_downmix_renderer_struct
const uint32_t ls_LFE_last_idx_CICP12[8] = { 0, 1, 2, 4, 5, 6, 7, 3 }; {
float inputEnergy[CLDFB_NO_CHANNELS_MAX];
float protoEnergy[CLDFB_NO_CHANNELS_MAX];
/* CICP16 - 5.1.4 */ } MONO_DOWNMIX_RENDERER_STRUCT, *MONO_DOWNMIX_RENDERER_HANDLE;
const uint32_t ls_LFE_last_idx_CICP16[10] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 3 };
/* CICP19 - 7.1.4 */
const uint32_t ls_LFE_last_idx_CICP19[12] = { 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 3 };
/* clang-format on */ /*----------------------------------------------------------------------------------*
* Custom Loudspeaker configuration structure
*----------------------------------------------------------------------------------*/
typedef struct ivas_LS_setup_custom
{
int16_t is_planar_setup; /* flag to indicate if setup is planar or not */
int16_t num_spk; /* number of custom loudspeakers */
float ls_azimuth[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker azimuths */
float ls_elevation[MAX_OUTPUT_CHANNELS]; /* custom loudspeaker elevations */
int16_t num_lfe; /* number of LFE channels */
int16_t lfe_idx[MAX_OUTPUT_CHANNELS]; /* index for LFE channel insertion */
int16_t separate_ch_found; /* flag to indicate if a center channel was found */
float separate_ch_gains[MAX_OUTPUT_CHANNELS]; /* gains to pan McMASA separateChannel in case no center channel is present */
} LSSETUP_CUSTOM_STRUCT, *LSSETUP_CUSTOM_HANDLE;
/* Channel types in a channel-based config */
typedef enum
{
CHANNEL_TYPE_UNUSED = 0,
CHANNEL_TYPE_SPEAKER,
CHANNEL_TYPE_LFE
} ChannelType;
#endif /* IVAS_STAT_REND_H */
/******************************************************************************************************
(C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository. All Rights Reserved.
This software is protected by copyright law and by international treaties.
The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository retain full ownership rights in their respective contributions in
the software. This notice grants no license of any kind, including but not limited to patent
license, nor is any license granted by implication, estoppel or otherwise.
Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
contributions.
This software is provided "AS IS", without any express or implied warranties. The software is in the
development stage. It is intended exclusively for experts who have experience with such software and
solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
and fitness for a particular purpose are hereby disclaimed and excluded.
Any dispute, controversy or claim arising under or in relation to providing this software shall be
submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
the United Nations Convention on Contracts on the International Sales of Goods.
*******************************************************************************************************/
#include "options.h"
#include "ivas_cnst.h"
#include "lib_rend.h"
#include "ivas_prot.h"
#include "ivas_rom_com.h"
#include "ivas_rom_dec.h"
#include "ivas_rom_rend.h"
#include "ivas_lib_rend_internal.h"
#include "prot.h"
#include "wmops.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef EXT_RENDERER
/* Maximum buffer length (per channel) in samples.
* Keep this separate from L_FRAME48k in case we want to support different size later */
#define MAX_BUFFER_LENGTH_PER_CHANNEL ( L_FRAME48k )
/* Maximum buffer length (total) in samples. */
#define MAX_BUFFER_LENGTH ( MAX_BUFFER_LENGTH_PER_CHANNEL * MAX_INPUT_CHANNELS )
/* Frame size required when rendering to binaural */
#define BINAURAL_RENDERING_FRAME_SIZE_MS 20
typedef float pan_vector[MAX_OUTPUT_CHANNELS];
typedef float pan_matrix[MAX_INPUT_CHANNELS][MAX_OUTPUT_CHANNELS];
typedef float rotation_gains[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS];
typedef float rotation_matrix[3][3];
/* EFAP wrapper to simplify writing panning gains to a vector that includes LFE channels */
typedef struct
{
EFAP_HANDLE hEfap;
IVAS_REND_AudioConfig speakerConfig;
const LSSETUP_CUSTOM_STRUCT *pCustomLsSetup; /* Pointer to main custom LS struct from renderer handle - doesn't need freeing */
} EFAP_WRAPPER;
/* Lightweight helper struct that gathers all information required for rendering
* any config to any other config. Used to simplify signatures of rendering functions.
*
* This struct should store ONLY CONST POINTERS to data existing elsewhere.
* Storing pointers instead of data itself ensures that no additional updates
* are required when any of these are changed in the renderer. Making the pointers
* const ensures that this data is only read, but not modified by the rendering functions. */
typedef struct
{
const int32_t *pOutSampleRate;
const IVAS_REND_AudioConfig *pOutConfig;
const LSSETUP_CUSTOM_STRUCT *pCustomLsOut;
const EFAP_WRAPPER *pEfapOutWrapper;
const IVAS_REND_HeadRotData *pHeadRotData;
/* TODO @Philips : would this be a better place to store the render config data?
* bearing in mind we could have multiple inputs to the renderer, we might neeed to accomodate
* multiple rendering configurations unless one global one can be used. If this is not relevant,
* feel free to remove this TODO.
*/
} rendering_context;
/* Common base for input structs */
typedef struct
{
IVAS_REND_AudioConfig inConfig;
IVAS_REND_InputId id;
IVAS_REND_AudioBuffer inputBuffer;
float bufferData[MAX_BUFFER_LENGTH];
float gain; /* Linear, not in dB */
rendering_context ctx;
int32_t numNewSamplesPerChannel; /* Used to keep track how much new audio was fed before rendering current frame */
} input_base;
typedef struct
{
input_base base;
IVAS_REND_AudioObjectPosition currentPos;
IVAS_REND_AudioObjectPosition previousPos;
TDREND_WRAPPER tdRendWrapper;
CREND_WRAPPER crendWrapper;
rotation_matrix rot_mat_prev;
} input_ism;
typedef struct
{
input_base base;
/* Full panning matrix. 1st index is input channel, 2nd index is output channel.
All LFE channels should be included, both for inputs and outputs */
pan_matrix panGains;
LSSETUP_CUSTOM_STRUCT customLsInput;
EFAP_WRAPPER efapInWrapper;
TDREND_WRAPPER tdRendWrapper;
CREND_WRAPPER crendWrapper;
rotation_gains rot_gains_prev;
IVAS_REND_LfeRouting lfeRouting;
} input_mc;
typedef struct
{
input_base base;
pan_matrix hoaDecMtx;
CREND_WRAPPER crendWrapper;
rotation_gains rot_gains_prev;
} input_sba;
struct IVAS_REND
{
int32_t sampleRateOut;
IVAS_LIMITER_HANDLE hLimiter;
#ifdef DEBUGGING
int32_t numClipping; /* Counter of clipped output samples */
#endif
input_ism inputsIsm[RENDERER_MAX_ISM_INPUTS];
input_mc inputsMc[RENDERER_MAX_MC_INPUTS];
input_sba inputsSba[RENDERER_MAX_SBA_INPUTS];
/* TODO @Philips - inputConfig should not be stored here, but read from e.g. input_mc->input_base.inConfig, please remove this */
IVAS_REND_AudioConfig inputConfig;
IVAS_REND_AudioConfig outputConfig;
EFAP_WRAPPER efapOutWrapper;
IVAS_LSSETUP_CUSTOM_STRUCT customLsOut;
IVAS_REND_HeadRotData headRotData;
int8_t rendererConfigEnabled;
RENDER_CONFIG_DATA *hRendererConfig; /* Renderer config pointer */
};
static IVAS_QUATERNION quaternionInit(
void )
{
IVAS_QUATERNION q;
q.w = 1.0f;
q.x = q.y = q.z = 0.0f;
return q;
}
static float *getSmplPtr(
IVAS_REND_AudioBuffer buffer,
uint32_t chnlIdx,
uint32_t smplIdx )
{
return buffer.data + chnlIdx * buffer.config.numSamplesPerChannel + smplIdx;
}
static void copyBufferTo2dArray(
const IVAS_REND_AudioBuffer buffer,
float array[MAX_OUTPUT_CHANNELS][L_FRAME48k] )
{
uint32_t smplIdx;
uint32_t chnlIdx;
const float *readPtr;
readPtr = buffer.data;
for ( chnlIdx = 0; chnlIdx < (uint32_t) buffer.config.numChannels; ++chnlIdx )
{
for ( smplIdx = 0; smplIdx < (uint32_t) buffer.config.numSamplesPerChannel; ++smplIdx )
{
array[chnlIdx][smplIdx] = *readPtr++;
}
}
return;
}
static void accumulate2dArrayToBuffer(
float array[MAX_OUTPUT_CHANNELS][L_FRAME48k],
IVAS_REND_AudioBuffer *buffer )
{
int16_t smplIdx, chnlIdx;
float *writePtr;
writePtr = buffer->data;
for ( chnlIdx = 0; chnlIdx < buffer->config.numChannels; ++chnlIdx )
{
for ( smplIdx = 0; smplIdx < buffer->config.numSamplesPerChannel; ++smplIdx )
{
*writePtr++ += array[chnlIdx][smplIdx];
}
}
return;
}
/*-------------------------------------------------------------------*
* limitRendererOutput()
*
* In-place saturation control for multichannel buffers with adaptive release time
*
*
*-------------------------------------------------------------------*/
/*! r: number of clipped output samples */
static int32_t limitRendererOutput(
IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */
float *output, /* i/o: I/O buffer */
const int16_t output_frame, /* i : number of samples per channel in the buffer */
const float threshold /* i : signal amplitude above which limiting starts to be applied */
)
{
int16_t i;
float **channels;
int16_t num_channels;
int32_t numClipping = 0;
/* return early if given bad parameters */
if ( hLimiter == NULL || output == NULL || output_frame <= 0 )
{
return 0;
}
channels = hLimiter->channel_ptrs;
num_channels = hLimiter->num_channels;
for ( i = 0; i < num_channels; ++i )
{
channels[i] = output + i * output_frame;
}
limiter_process( hLimiter, output_frame, threshold, 0, NULL );
/* Apply clipping to buffer in case the limiter let through some samples > 1.0f */
for ( i = 0; i < output_frame * num_channels; ++i )
{
#ifdef DEBUGGING
if ( output[i] < INT16_MIN || output[i] > INT16_MAX )
{
++numClipping;
}
#endif
output[i] = min( max( INT16_MIN, output[i] ), INT16_MAX );
}
return numClipping;
}
static AUDIO_CONFIG rendAudioConfigToIvasAudioConfig( // VE2AT: similar is defined again at line 397, why?
IVAS_REND_AudioConfig rendConfig )
{
switch ( rendConfig )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
return AUDIO_CONFIG_MONO;
case IVAS_REND_AUDIO_CONFIG_STEREO:
return AUDIO_CONFIG_STEREO;
case IVAS_REND_AUDIO_CONFIG_5_1:
return AUDIO_CONFIG_5_1;
case IVAS_REND_AUDIO_CONFIG_7_1:
return AUDIO_CONFIG_7_1;
case IVAS_REND_AUDIO_CONFIG_5_1_2:
return AUDIO_CONFIG_5_1_2;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
return AUDIO_CONFIG_5_1_4;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
return AUDIO_CONFIG_7_1_4;
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
return AUDIO_CONFIG_LS_CUSTOM;
case IVAS_REND_AUDIO_CONFIG_FOA:
return AUDIO_CONFIG_FOA;
case IVAS_REND_AUDIO_CONFIG_HOA2:
return AUDIO_CONFIG_HOA2;
case IVAS_REND_AUDIO_CONFIG_HOA3:
return AUDIO_CONFIG_HOA3;
case IVAS_REND_AUDIO_CONFIG_OBJECT:
return AUDIO_CONFIG_OBA;
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
return AUDIO_CONFIG_BINAURAL;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
return AUDIO_CONFIG_BINAURAL_ROOM;
case IVAS_REND_AUDIO_CONFIG_MASA1:
return AUDIO_CONFIG_MASA1;
case IVAS_REND_AUDIO_CONFIG_MASA2:
return AUDIO_CONFIG_MASA2;
default:
break;
}
return AUDIO_CONFIG_INVALID;
}
static ivas_error validateOutputAudioConfig(
IVAS_REND_AudioConfig outConfig )
{
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
case IVAS_REND_AUDIO_CONFIG_STEREO:
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
case IVAS_REND_AUDIO_CONFIG_FOA:
case IVAS_REND_AUDIO_CONFIG_HOA2:
case IVAS_REND_AUDIO_CONFIG_HOA3:
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
return IVAS_ERR_OK;
default:
break;
}
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
IVAS_REND_AudioConfigType getAudioConfigType(
IVAS_REND_AudioConfig config )
{
/* By definition, config type is the second byte (from LSB) of IVAS_REND_AudioConfig enum. */
return ( config & 0xFF00 ) >> 8; // VE2AT: MSVC returns warning C4244: 'return': conversion from 'int' to 'IVAS_REND_InputId', possible loss of data
}
static ivas_error validateOutputSampleRate(
const int32_t sampleRate,
const IVAS_REND_AudioConfig outConfig )
{
if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL )
{
/* If no binaural rendering, any sampling rate is supported */
return IVAS_ERR_OK;
}
/* Otherwise rendering to binaural, support the same set as IVAS decoder */
switch ( sampleRate )
{
case 8000:
case 16000:
case 32000:
case 48000:
return IVAS_ERR_OK;
}
return IVAS_ERR_INVALID_SAMPLING_RATE;
}
ivas_error getAudioConfigNumChannels(
const IVAS_REND_AudioConfig config,
int16_t *numChannels )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
case IVAS_REND_AUDIO_CONFIG_OBJECT:
*numChannels = 1;
break;
case IVAS_REND_AUDIO_CONFIG_STEREO:
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
*numChannels = 2;
break;
case IVAS_REND_AUDIO_CONFIG_FOA:
*numChannels = 4;
break;
case IVAS_REND_AUDIO_CONFIG_5_1:
*numChannels = 6;
break;
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
*numChannels = 8;
break;
case IVAS_REND_AUDIO_CONFIG_HOA2:
*numChannels = 9;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
*numChannels = 10;
break;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
*numChannels = 12;
break;
case IVAS_REND_AUDIO_CONFIG_HOA3:
*numChannels = 16;
break;
default:
return IVAS_ERR_NUM_CHANNELS_UNKNOWN;
}
return IVAS_ERR_OK;
}
AUDIO_CONFIG getIvasAudioConfigFromRendAudioConfig(
IVAS_REND_AudioConfig config )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
return AUDIO_CONFIG_MONO;
case IVAS_REND_AUDIO_CONFIG_STEREO:
return AUDIO_CONFIG_STEREO;
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
return AUDIO_CONFIG_BINAURAL;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
return AUDIO_CONFIG_BINAURAL_ROOM;
case IVAS_REND_AUDIO_CONFIG_5_1:
return AUDIO_CONFIG_5_1;
case IVAS_REND_AUDIO_CONFIG_7_1:
return AUDIO_CONFIG_7_1;
case IVAS_REND_AUDIO_CONFIG_5_1_2:
return AUDIO_CONFIG_5_1_2;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
return AUDIO_CONFIG_5_1_4;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
return AUDIO_CONFIG_7_1_4;
case IVAS_REND_AUDIO_CONFIG_FOA:
return AUDIO_CONFIG_FOA;
case IVAS_REND_AUDIO_CONFIG_HOA2:
return AUDIO_CONFIG_HOA2;
case IVAS_REND_AUDIO_CONFIG_HOA3:
return AUDIO_CONFIG_HOA3;
default:
return AUDIO_CONFIG_INVALID;
}
}
static ivas_error initLimiter(
IVAS_LIMITER_HANDLE *phLimiter,
const int16_t numChannels,
const int32_t sampleRate )
{
/* If re-initializing with unchanged values, return early */
if ( *phLimiter != NULL &&
( *phLimiter )->num_channels == numChannels &&
( *phLimiter )->sampling_rate == sampleRate )
{
return IVAS_ERR_OK;
}
/* Support re-init: close if already allocated */
if ( *phLimiter != NULL )
{
ivas_limiter_close( phLimiter );
}
*phLimiter = ivas_limiter_open( (int16_t) numChannels, sampleRate );
if ( *phLimiter == NULL )
{
return IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Failed to open limiter" );
}
return IVAS_ERR_OK;
}
static LSSETUP_CUSTOM_STRUCT defaultCustomLs(
void )
{
LSSETUP_CUSTOM_STRUCT ls;
/* Set mono by default. This simplifies initialization,
since output config is never in an undefined state. */
ls.is_planar_setup = 1;
ls.num_spk = 1;
set_zero( ls.ls_azimuth, MAX_OUTPUT_CHANNELS );
set_zero( ls.ls_elevation, MAX_OUTPUT_CHANNELS );
ls.num_lfe = 0;
set_s( ls.lfe_idx, 0, MAX_OUTPUT_CHANNELS );
ls.separate_ch_found = 0;
set_f( ls.separate_ch_gains, 0, MAX_OUTPUT_CHANNELS );
return ls;
}
static ivas_error getSpeakerAzimuths(
IVAS_REND_AudioConfig config,
const float **azimuths )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
*azimuths = ls_azimuth_CICP1;
break;
case IVAS_REND_AUDIO_CONFIG_STEREO:
*azimuths = ls_azimuth_CICP2;
break;
case IVAS_REND_AUDIO_CONFIG_5_1:
*azimuths = ls_azimuth_CICP6;
break;
case IVAS_REND_AUDIO_CONFIG_7_1:
*azimuths = ls_azimuth_CICP12;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_2:
*azimuths = ls_azimuth_CICP14;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
*azimuths = ls_azimuth_CICP16;
break;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
*azimuths = ls_azimuth_CICP19;
break;
default:
return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" );
}
return IVAS_ERR_OK;
}
static ivas_error getSpeakerElevations(
IVAS_REND_AudioConfig config,
const float **elevations )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
*elevations = ls_elevation_CICP1;
break;
case IVAS_REND_AUDIO_CONFIG_STEREO:
*elevations = ls_elevation_CICP2;
break;
case IVAS_REND_AUDIO_CONFIG_5_1:
*elevations = ls_elevation_CICP6;
break;
case IVAS_REND_AUDIO_CONFIG_7_1:
*elevations = ls_elevation_CICP12;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_2:
*elevations = ls_elevation_CICP14;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
*elevations = ls_elevation_CICP16;
break;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
*elevations = ls_elevation_CICP19;
break;
default:
return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" );
}
return IVAS_ERR_OK;
}
static ivas_error getAmbisonicsOrder(
IVAS_REND_AudioConfig config,
int16_t *order )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_FOA:
*order = 1;
break;
case IVAS_REND_AUDIO_CONFIG_HOA2:
*order = 2;
break;
case IVAS_REND_AUDIO_CONFIG_HOA3:
*order = 3;
break;
default:
return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unsupported audio config" );
}
return IVAS_ERR_OK;
}
static ivas_error getNumNonLfeChannelsInSpeakerLayout(
IVAS_REND_AudioConfig config,
int16_t *numNonLfeChannels )
{
switch ( config )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
*numNonLfeChannels = 1;
break;
case IVAS_REND_AUDIO_CONFIG_STEREO:
*numNonLfeChannels = 2;
break;
case IVAS_REND_AUDIO_CONFIG_5_1:
*numNonLfeChannels = 5;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_7_1:
*numNonLfeChannels = 7;
break;
case IVAS_REND_AUDIO_CONFIG_5_1_4:
*numNonLfeChannels = 9;
break;
case IVAS_REND_AUDIO_CONFIG_7_1_4:
*numNonLfeChannels = 11;
break;
default:
return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Unexpected audio config" );
}
return IVAS_ERR_OK;
}
static ivas_error getMcConfigValues(
IVAS_REND_AudioConfig inConfig,
LSSETUP_CUSTOM_STRUCT inCustomLs,
const float **azimuth,
const float **elevation,
int16_t *lfe_idx,
int16_t *is_planar )
{
int16_t i;
*lfe_idx = -1;
*is_planar = 1;
switch ( inConfig )
{
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
*azimuth = (const float *) &inCustomLs.ls_azimuth;
*elevation = (const float *) &inCustomLs.ls_elevation;
if ( inCustomLs.num_lfe > 0 )
{
*lfe_idx = inCustomLs.lfe_idx[0];
}
for ( i = 0; i < inCustomLs.num_spk; i++ )
{
if ( inCustomLs.ls_elevation[i] != 0 )
{
*is_planar = 0;
break;
}
}
break;
case IVAS_REND_AUDIO_CONFIG_MONO:
case IVAS_REND_AUDIO_CONFIG_STEREO:
getSpeakerAzimuths( inConfig, azimuth );
getSpeakerElevations( inConfig, elevation );
break;
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
getSpeakerAzimuths( inConfig, azimuth );
getSpeakerElevations( inConfig, elevation );
*lfe_idx = LFE_CHANNEL;
*is_planar = ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) ? 1 : 0;
break;
default:
assert( !"Invalid speaker config" );
return IVAS_ERR_WRONG_PARAMS;
}
return IVAS_ERR_OK;
}
static ivas_error initEfap(
EFAP_WRAPPER *pEfapWrapper,
IVAS_REND_AudioConfig outConfig,
const LSSETUP_CUSTOM_STRUCT *pCustomLsOut )
{
ivas_error error;
const float *azimuths;
const float *elevations;
int16_t numNonLfeChannels;
if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM )
{
pEfapWrapper->speakerConfig = IVAS_REND_AUDIO_CONFIG_7_1_4;
}
else
{
pEfapWrapper->speakerConfig = outConfig;
}
pEfapWrapper->pCustomLsSetup = pCustomLsOut;
/* If re-initializing, free existing EFAP handle. */
if ( pEfapWrapper->hEfap != NULL )
{
efap_free_data( &pEfapWrapper->hEfap );
}
/* Only initialize EFAP handle if output config is channel-based */
if ( getAudioConfigType( pEfapWrapper->speakerConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED )
{
pEfapWrapper->hEfap = NULL;
return IVAS_ERR_OK;
}
if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
if ( ( error = efap_init_data( &pEfapWrapper->hEfap, pCustomLsOut->ls_azimuth, pCustomLsOut->ls_elevation, pCustomLsOut->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
return error;
}
}
else
{
if ( ( error = getSpeakerAzimuths( pEfapWrapper->speakerConfig, &azimuths ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getSpeakerElevations( pEfapWrapper->speakerConfig, &elevations ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getNumNonLfeChannelsInSpeakerLayout( pEfapWrapper->speakerConfig, &numNonLfeChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = efap_init_data( &pEfapWrapper->hEfap, azimuths, elevations, numNonLfeChannels, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
static ivas_error getEfapGains(
EFAP_WRAPPER efapWrapper,
const float azi,
const float ele,
pan_vector panGains )
{
pan_vector tmpPanGains; /* tmp pan gain buffer without LFE channels */
float *readPtr;
int16_t i;
int16_t lfeCount;
int16_t numChannels;
ivas_error error;
/* EFAP returns an array of gains only for non-LFE speakers */
efap_determine_gains( efapWrapper.hEfap, tmpPanGains, azi, ele, EFAP_MODE_EFAP );
/* Now copy to buffer that includes LFE channels */
if ( efapWrapper.speakerConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
numChannels = efapWrapper.pCustomLsSetup->num_spk + efapWrapper.pCustomLsSetup->num_lfe;
readPtr = tmpPanGains;
for ( i = 0, lfeCount = 0; i < numChannels; ++i )
{
if ( lfeCount < efapWrapper.pCustomLsSetup->num_lfe && i == efapWrapper.pCustomLsSetup->lfe_idx[lfeCount] )
{
panGains[i] = 0.0f;
++lfeCount;
}
else
{
panGains[i] = *readPtr;
++readPtr;
}
}
}
else
{
if ( ( error = getAudioConfigNumChannels( efapWrapper.speakerConfig, &numChannels ) ) != IVAS_ERR_OK )
{
return error;
}
readPtr = tmpPanGains;
for ( i = 0; i < numChannels; ++i )
{
if ( i == LFE_CHANNEL )
{
panGains[i] = 0.0f;
}
else
{
panGains[i] = *readPtr;
++readPtr;
}
}
}
return IVAS_ERR_OK;
}
static void initHeadRotation(
IVAS_REND_HANDLE hIvasRend )
{
int16_t i, crossfade_len;
float tmp;
/* Head rotation is enabled by default */
hIvasRend->headRotData.headRotEnabled = 1;
/* Initialize 5ms crossfade */
crossfade_len = L_FRAME48k / RENDERER_HEAD_POSITIONS_PER_FRAME;
tmp = 1.f / ( crossfade_len - 1 );
for ( i = 0; i < crossfade_len; i++ )
{
hIvasRend->headRotData.crossfade[i] = i * tmp;
}
/* Initialize with unit quaternions */
for ( i = 0; i < RENDERER_HEAD_POSITIONS_PER_FRAME; ++i )
{
hIvasRend->headRotData.headPositions[i] = quaternionInit();
}
return;
}
static void initRotMatrix(
rotation_matrix rot_mat )
{
int16_t i;
/* Initialize rotation matrices */
for ( i = 0; i < 3; i++ )
{
set_zero( rot_mat[i], 3 );
rot_mat[i][i] = 1.f;
}
return;
}
static void initRotGains(
rotation_gains rot_gains )
{
int16_t i;
/* Set gains to passthrough */
for ( i = 0; i < MAX_INPUT_CHANNELS; i++ )
{
set_zero( rot_gains[i], MAX_INPUT_CHANNELS );
rot_gains[i][i] = 1.f;
}
return;
}
static void initRendInputBase(
input_base *inputBase,
const IVAS_REND_AudioConfig inConfig,
const IVAS_REND_InputId id,
const rendering_context rendCtx )
{
inputBase->inConfig = inConfig;
inputBase->id = id;
inputBase->gain = 1.0f;
inputBase->ctx = rendCtx;
inputBase->numNewSamplesPerChannel = 0;
inputBase->inputBuffer.config.numSamplesPerChannel = 0;
inputBase->inputBuffer.config.numChannels = 0;
inputBase->inputBuffer.data = inputBase->bufferData;
set_zero( inputBase->bufferData, MAX_BUFFER_LENGTH );
return;
}
static IVAS_REND_AudioObjectPosition defaultObjectPosition(
void )
{
IVAS_REND_AudioObjectPosition pos;
pos.azimuth = 0.0f;
pos.elevation = 0.0f;
return pos;
}
static rendering_context getRendCtx(
IVAS_REND_HANDLE hIvasRend )
{
rendering_context ctx;
/* Note: when refactoring this, always take the ADDRESS of a member of the
* renderer struct, so that the context stores a POINTER to the member, even
* if the member is a pointer or handle itself. */
ctx.pOutConfig = &hIvasRend->outputConfig;
ctx.pOutSampleRate = &hIvasRend->sampleRateOut;
ctx.pCustomLsOut = &hIvasRend->customLsOut;
ctx.pEfapOutWrapper = &hIvasRend->efapOutWrapper;
ctx.pHeadRotData = &hIvasRend->headRotData;
return ctx;
}
static TDREND_WRAPPER defaultTdRendWrapper(
void )
{
TDREND_WRAPPER w;
w.binaural_latency_ns = 0;
w.hBinRendererTd = NULL;
w.hHrtfTD = NULL;
return w;
}
static CREND_WRAPPER defaultCrendWrapper(
void )
{
CREND_WRAPPER w;
w.hCrend = NULL;
w.hHrtfCrend = NULL;
w.binaural_latency_ns = 0;
return w;
}
static ivas_error setRendInputActiveIsm(
void *input,
const IVAS_REND_AudioConfig inConfig,
const IVAS_REND_InputId id,
RENDER_CONFIG_DATA *hRendCfg )
{
ivas_error error;
rendering_context rendCtx;
IVAS_REND_AudioConfig outConfig;
input_ism *inputIsm;
inputIsm = (input_ism *) input;
rendCtx = inputIsm->base.ctx;
outConfig = *rendCtx.pOutConfig;
initRendInputBase( &inputIsm->base, inConfig, id, rendCtx );
inputIsm->currentPos = defaultObjectPosition();
inputIsm->previousPos = defaultObjectPosition();
inputIsm->crendWrapper = defaultCrendWrapper();
inputIsm->tdRendWrapper = defaultTdRendWrapper();
initRotMatrix( inputIsm->rot_mat_prev );
error = IVAS_ERR_OK;
if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL )
{
error = ivas_rend_TDObjRendOpen( &inputIsm->tdRendWrapper, inConfig, NULL, *rendCtx.pOutSampleRate );
}
else if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM )
{
error = ivas_rend_openCrend( &inputIsm->crendWrapper,
IVAS_REND_AUDIO_CONFIG_7_1_4,
outConfig,
hRendCfg,
*rendCtx.pOutSampleRate );
}
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static void clearInputIsm(
input_ism *inputIsm )
{
rendering_context rendCtx;
rendCtx = inputIsm->base.ctx;
initRendInputBase( &inputIsm->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx );
/* Free input's internal handles */
if ( inputIsm->crendWrapper.hCrend != NULL )
{
ivas_rend_closeCrend( &inputIsm->crendWrapper );
}
if ( inputIsm->tdRendWrapper.hBinRendererTd != NULL )
{
ivas_td_binaural_close( &inputIsm->tdRendWrapper.hBinRendererTd );
inputIsm->tdRendWrapper.hHrtfTD = NULL;
}
}
static void copyLsConversionMatrixToPanMatrix(
const LS_CONVERSION_MATRIX *lsConvMatrix,
pan_matrix panMatrix )
{
int16_t i;
int16_t inCh, outCh;
int16_t numNonZeroGains;
int16_t numColumns;
/* Index 0 is special and describes the following values */
numNonZeroGains = lsConvMatrix[0].index;
numColumns = (int16_t) lsConvMatrix[0].value;
for ( i = 1; i < numNonZeroGains + 1; ++i )
{
inCh = lsConvMatrix[i].index / numColumns;
outCh = lsConvMatrix[i].index % numColumns;
panMatrix[inCh][outCh] = lsConvMatrix[i].value;
}
return;
}
static void setZeroPanMatrix(
pan_matrix panMatrix )
{
int16_t i;
for ( i = 0; i < MAX_INPUT_CHANNELS; ++i )
{
set_zero( panMatrix[i], MAX_OUTPUT_CHANNELS );
}
return;
}
/* Note: this only sets non-zero elements, call setZeroPanMatrix() to init first. */
static void fillIdentityPanMatrix(
pan_matrix panMatrix )
{
int16_t i;
for ( i = 0; i < min( MAX_INPUT_CHANNELS, MAX_OUTPUT_CHANNELS ); ++i )
{
panMatrix[i][i] = 1.0f;
}
}
static ivas_error initMcPanGainsWithIdentMatrix(
input_mc *inputMc )
{
fillIdentityPanMatrix( inputMc->panGains );
return IVAS_ERR_OK;
}
static ivas_error initMcPanGainsWithConversionMapping(
input_mc *inputMc,
const IVAS_REND_AudioConfig outConfig )
{
AUDIO_CONFIG ivasConfigIn, ivasConfigOut;
int16_t i;
ivasConfigIn = rendAudioConfigToIvasAudioConfig( inputMc->base.inConfig );
ivasConfigOut = rendAudioConfigToIvasAudioConfig( outConfig );
/* Find conversion mapping for current I/O config pair.
* Stay with default panning matrix if conversion_matrix is NULL */
for ( i = 0; i < LS_SETUP_CONVERSION_NUM_MAPPINGS; ++i )
{
if ( ls_conversion_mapping[i].input_config == ivasConfigIn &&
ls_conversion_mapping[i].output_config == ivasConfigOut )
{
/* Mapping found with valid matrix - copy */
if ( ls_conversion_mapping[i].conversion_matrix != NULL )
{
copyLsConversionMatrixToPanMatrix( ls_conversion_mapping[i].conversion_matrix, inputMc->panGains );
}
/* Mapping found with NULL matrix - use identity matrix */
else
{
fillIdentityPanMatrix( inputMc->panGains );
}
return IVAS_ERR_OK;
}
}
return IVAS_ERROR( IVAS_ERR_INTERNAL_FATAL, "Missing multichannel conversion mapping" );
}
static ivas_error initMcPanGainsWithEfap( input_mc *inputMc, IVAS_REND_AudioConfig outConfig )
{
int16_t i;
int16_t numNonLfeInChannels;
int16_t inLfeChIdx, outChIdx;
const float *spkAzi, *spkEle;
ivas_error error;
if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
if ( ( error = getNumNonLfeChannelsInSpeakerLayout( inputMc->base.inConfig, &numNonLfeInChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getSpeakerAzimuths( inputMc->base.inConfig, &spkAzi ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getSpeakerElevations( inputMc->base.inConfig, &spkEle ) ) != IVAS_ERR_OK )
{
return error;
}
inLfeChIdx = LFE_CHANNEL;
}
else
{
numNonLfeInChannels = inputMc->customLsInput.num_spk;
spkAzi = inputMc->customLsInput.ls_azimuth;
spkEle = inputMc->customLsInput.ls_elevation;
inLfeChIdx = -1;
if ( inputMc->customLsInput.num_lfe > 0 )
{
inLfeChIdx = inputMc->customLsInput.lfe_idx[0];
}
}
for ( i = 0, outChIdx = 0; i < numNonLfeInChannels; ++i, ++outChIdx )
{
if ( i == inLfeChIdx )
{
++outChIdx;
}
if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper, spkAzi[i], spkEle[i], inputMc->panGains[outChIdx] ) ) != IVAS_ERR_OK )
{
return error;
}
}
if ( outConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM && inLfeChIdx >= 0 )
{
inputMc->panGains[inLfeChIdx][LFE_CHANNEL] = 1;
}
else if ( inputMc->base.ctx.pCustomLsOut->num_lfe > 0 && inLfeChIdx >= 0 )
{
inputMc->panGains[inLfeChIdx][inputMc->base.ctx.pCustomLsOut->lfe_idx[0]] = 1;
}
return IVAS_ERR_OK;
}
static ivas_error getRendInputNumChannels(
const void *rendInput,
int16_t *numInChannels )
{
/* Using a void pointer for this function to be reusable for any input type (input_ism, input_mc, input_sba).
Assumptions:
- input_base is always the first member in the input struct
*/
ivas_error error;
const input_base *pInputBase;
const input_mc *pInputMc;
pInputBase = (const input_base *) rendInput;
if ( pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
pInputMc = (const input_mc *) rendInput;
*numInChannels = pInputMc->customLsInput.num_spk + pInputMc->customLsInput.num_lfe;
}
else
{
if ( ( error = getAudioConfigNumChannels( pInputBase->inConfig, numInChannels ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
static ivas_error initMcPanGainsWithMonoOut(
input_mc *inputMc )
{
int16_t i;
int16_t numInChannels;
ivas_error error;
if ( ( error = getRendInputNumChannels( inputMc, &numInChannels ) ) != IVAS_ERR_OK )
{
return error;
}
for ( i = 0; i < numInChannels; ++i )
{
/* It's OK to also set gain 1 for LFE input channels here.
* Correct LFE handling will be applied within updateMcPanGains() */
inputMc->panGains[i][0] = 1.f;
}
return IVAS_ERR_OK;
}
static ivas_error initMcPanGainsWithStereoLookup(
input_mc *inputMc )
{
int16_t readIdx;
int16_t writeIdx;
bool skipSideSpeakers;
int16_t numInChannels;
ivas_error error;
/* Special case - MONO input.
* Use gains for center CICP speaker and return early. */
if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_MONO )
{
inputMc->panGains[0][0] = ls_conversion_cicpX_stereo[2][0];
inputMc->panGains[0][1] = ls_conversion_cicpX_stereo[2][1];
return IVAS_ERR_OK;
}
/* ls_conversion_cicpX_stereo contains gains for side speakers.
* These should be skipped with 5.1+X inputs. */
skipSideSpeakers = false;
if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_5_1_2 || inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_5_1_4 )
{
skipSideSpeakers = true;
}
if ( ( error = getRendInputNumChannels( inputMc, &numInChannels ) ) != IVAS_ERR_OK )
{
return error;
}
for ( readIdx = 0, writeIdx = 0; writeIdx < numInChannels; ++readIdx, ++writeIdx )
{
if ( skipSideSpeakers && readIdx == 4 )
{
/* Skip gains for side speakers in lookup table */
readIdx += 2;
}
inputMc->panGains[writeIdx][0] = ls_conversion_cicpX_stereo[readIdx][0];
inputMc->panGains[writeIdx][1] = ls_conversion_cicpX_stereo[readIdx][1];
}
return IVAS_ERR_OK;
}
/* Returns 1 (true) if configs A and B are equal, otherwise returns 0 (false).
* If both configs are custom LS layouts, layout details are compared to determine equality. */
static bool configsAreEqual(
IVAS_REND_AudioConfig configA,
LSSETUP_CUSTOM_STRUCT customLsA,
IVAS_REND_AudioConfig configB,
LSSETUP_CUSTOM_STRUCT customLsB )
{
int16_t i;
/* Both input and output are custom LS - compare structs */
if ( configA == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM && configB == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
if ( customLsA.num_spk != customLsB.num_spk )
{
return false;
}
if ( customLsA.num_lfe != customLsB.num_lfe )
{
return false;
}
if ( customLsA.is_planar_setup != customLsB.is_planar_setup )
{
return false;
}
for ( i = 0; i < customLsA.num_spk; ++i )
{
/* Compare to nearest degree (hence the int cast) */
if ( (int16_t) customLsA.ls_azimuth[i] != (int16_t) customLsB.ls_azimuth[i] ||
(int16_t) customLsA.ls_elevation[i] != (int16_t) customLsB.ls_elevation[i] )
{
return false;
}
}
for ( i = 0; i < customLsA.num_lfe; ++i )
{
if ( customLsA.lfe_idx[i] != customLsB.lfe_idx[i] )
{
return false;
}
}
return true;
}
/* Otherwise it's enough to compare config enums */
return configA == configB;
}
static ivas_error updateMcPanGainsForMcOut(
input_mc *inputMc,
const IVAS_REND_AudioConfig outConfig )
{
ivas_error error;
/* "if" conditions below realize the following mapping:
If in == out, use identity matrix, otherwise follow the table:
+-----------+----------+---------------+-----------+--------------------+
| in\out | MONO | STEREO | custom LS | other |
+-----------+----------+---------------+-----------+--------------------+
| MONO | mono out | EFAP | EFAP | EFAP |
| custom LS | mono out | EFAP | EFAP | EFAP |
| other | mono out | stereo lookup | EFAP | conversion mapping |
+-----------+----------+---------------+-----------+--------------------+
*/
if ( configsAreEqual( inputMc->base.inConfig, inputMc->customLsInput, outConfig, *inputMc->base.ctx.pCustomLsOut ) )
{
error = initMcPanGainsWithIdentMatrix( inputMc );
}
else if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ||
inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_MONO ||
inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
error = initMcPanGainsWithEfap( inputMc, outConfig );
}
else if ( outConfig == IVAS_REND_AUDIO_CONFIG_MONO )
{
error = initMcPanGainsWithMonoOut( inputMc );
}
else if ( outConfig == IVAS_REND_AUDIO_CONFIG_STEREO )
{
error = initMcPanGainsWithStereoLookup( inputMc );
}
else /* default */
{
error = initMcPanGainsWithConversionMapping( inputMc, outConfig );
}
return error;
}
static ivas_error updateMcPanGainsForAmbiOut(
input_mc *inputMc,
const IVAS_REND_AudioConfig outConfig )
{
int16_t ch_in, ch_out, lfeIdx;
int16_t numNonLfeInChannels, outAmbiOrder;
const float *spkAzi, *spkEle;
ivas_error error;
if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK )
{
return error;
}
if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
if ( ( error = getNumNonLfeChannelsInSpeakerLayout( inputMc->base.inConfig, &numNonLfeInChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getSpeakerAzimuths( inputMc->base.inConfig, &spkAzi ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getSpeakerElevations( inputMc->base.inConfig, &spkEle ) ) != IVAS_ERR_OK )
{
return error;
}
for ( ch_in = 0, ch_out = 0; ch_in < numNonLfeInChannels; ++ch_in, ++ch_out )
{
if ( ch_in == LFE_CHANNEL )
{
++ch_out;
}
ivas_dirac_dec_get_response( (int16_t) spkAzi[ch_in], (int16_t) spkEle[ch_in], inputMc->panGains[ch_out], outAmbiOrder );
}
}
else
{
numNonLfeInChannels = inputMc->customLsInput.num_spk;
spkAzi = inputMc->customLsInput.ls_azimuth;
spkEle = inputMc->customLsInput.ls_elevation;
for ( ch_in = 0, ch_out = 0; ch_in < numNonLfeInChannels; ++ch_in, ++ch_out )
{
for ( lfeIdx = 0; lfeIdx < inputMc->customLsInput.num_lfe; ++lfeIdx )
{
if ( inputMc->customLsInput.lfe_idx[lfeIdx] == ch_in )
{
++ch_out;
break;
}
}
ivas_dirac_dec_get_response( (int16_t) spkAzi[ch_in], (int16_t) spkEle[ch_in], inputMc->panGains[ch_out], outAmbiOrder );
}
}
return IVAS_ERR_OK;
}
static ivas_error updateMcPanGains(
input_mc *inputMc,
const IVAS_REND_AudioConfig outConfig )
{
int16_t i;
ivas_error error;
/* Reset to all zeros - some functions below only write non-zero elements. */
setZeroPanMatrix( inputMc->panGains );
error = IVAS_ERR_OK;
switch ( getAudioConfigType( outConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
error = updateMcPanGainsForMcOut( inputMc, outConfig );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
error = updateMcPanGainsForAmbiOut( inputMc, outConfig );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
break; /* Do nothing */
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
/* Prepare rendering to intermediate format */
error = updateMcPanGainsForMcOut( inputMc, IVAS_REND_AUDIO_CONFIG_7_1_4 );
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
/* Check error here to keep switch statement more compact */
if ( error != IVAS_ERR_OK )
{
return error;
}
/* Copy LFE routing to pan gains array */
if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
for ( i = 0; i < inputMc->customLsInput.num_lfe; ++i )
{
mvr2r( inputMc->lfeRouting.lfeOutputGains[i], inputMc->panGains[inputMc->customLsInput.lfe_idx[i]], IVAS_MAX_OUTPUT_CHANNELS );
}
}
else
{
/* For code simplicity, always copy LFE gains. If config has no LFE, gains will be zero anyway. */
mvr2r( inputMc->lfeRouting.lfeOutputGains[0], inputMc->panGains[LFE_CHANNEL], IVAS_MAX_OUTPUT_CHANNELS );
}
return IVAS_ERR_OK;
}
#ifndef FIX_I81
/* Fixes initialization issues in TD renderer. Fix to be merged with branch.
See issue: https://forge.3gpp.org/rep/ivas-codec-pc/ivas-codec/-/issues/81 */
static void tmpFixBuggyTdBinRendInit( BINAURAL_TD_OBJECT_RENDERER_HANDLE hBinRendererTd )
{
int16_t i, j;
for ( i = 0; i < hBinRendererTd->NumOfSrcs; ++i )
{
for ( j = 0; j < SPAT_BIN_MAX_INPUT_CHANNELS; ++j )
{
hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].LeftFilter_p = NULL;
hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].LeftFilterIncr_p = NULL;
hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].RightFilter_p = NULL;
hBinRendererTd->Sources[i]->SrcRend_p->SfxSpatBin_p[j].RightFilterIncr_p = NULL;
}
}
}
#endif
static ivas_error initMcBinauralRendering(
input_mc *inputMc,
const IVAS_REND_AudioConfig inConfig,
const IVAS_REND_AudioConfig outConfig,
RENDER_CONFIG_DATA *hRendCfg )
{
ivas_error error;
int32_t outSampleRate;
/* check if re-initialization */
if ( inputMc->tdRendWrapper.hBinRendererTd != NULL )
{
ivas_td_binaural_close( &inputMc->tdRendWrapper.hBinRendererTd );
inputMc->tdRendWrapper.hHrtfTD = NULL;
}
if ( inputMc->crendWrapper.hCrend != NULL )
{
ivas_rend_closeCrend( &inputMc->crendWrapper );
}
outSampleRate = *inputMc->base.ctx.pOutSampleRate;
/* TODO tmu : needs review allocate both renderers; needed if headrotation is toggled so the renderer can be switched */
// bool initTDRend;
// initTDRend = false;
// if ( outConfig != IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM )
// {
// if ( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
// {
// initTDRend = true;
// }
// else if ( ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) &&
// ( inputMc->base.ctx.pHeadRotData->headRotEnabled ) )
// {
// initTDRend = true;
// }
// }
// if ( initTDRend )
{
if ( ( error = ivas_rend_TDObjRendOpen( &inputMc->tdRendWrapper, inConfig, &inputMc->customLsInput, outSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
#ifndef FIX_I81
tmpFixBuggyTdBinRendInit( inputMc->tdRendWrapper.hBinRendererTd );
#endif
}
{
if ( ( error = ivas_rend_openCrend( &inputMc->crendWrapper,
( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) ? IVAS_REND_AUDIO_CONFIG_7_1_4 : inConfig,
outConfig,
hRendCfg,
outSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
}
return error;
}
static IVAS_REND_LfeRouting defaultLfeRouting(
IVAS_REND_AudioConfig inConfig,
LSSETUP_CUSTOM_STRUCT customLsIn,
IVAS_REND_AudioConfig outConfig,
LSSETUP_CUSTOM_STRUCT customLsOut )
{
int32_t i;
IVAS_REND_LfeRouting routing;
/* Set all output gains to zero, then route each input LFE consecutively to the next available output LFE. */
for ( i = 0; i < IVAS_MAX_INPUT_LFE_CHANNELS; ++i )
{
set_zero( routing.lfeOutputGains[i], IVAS_MAX_OUTPUT_CHANNELS );
}
switch ( inConfig )
{
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
routing.numLfeChannels = 1;
break;
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
routing.numLfeChannels = customLsIn.num_lfe;
break;
default:
routing.numLfeChannels = 0;
}
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
routing.lfeOutputGains[0][LFE_CHANNEL] = 1.0f;
break;
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
for ( i = 0; i < routing.numLfeChannels && i < customLsOut.num_lfe; ++i )
{
routing.lfeOutputGains[i][customLsOut.lfe_idx[i]] = 1.0f;
}
break;
default:
/* Do nothing */
break;
}
return routing;
}
static ivas_error setRendInputActiveMc(
void *input,
const IVAS_REND_AudioConfig inConfig,
const IVAS_REND_InputId id,
RENDER_CONFIG_DATA *hRendCfg )
{
ivas_error error;
rendering_context rendCtx;
IVAS_REND_AudioConfig outConfig;
input_mc *inputMc;
inputMc = (input_mc *) input;
rendCtx = inputMc->base.ctx;
outConfig = *rendCtx.pOutConfig;
initRendInputBase( &inputMc->base, inConfig, id, rendCtx );
setZeroPanMatrix( inputMc->panGains );
inputMc->customLsInput = defaultCustomLs();
inputMc->tdRendWrapper = defaultTdRendWrapper();
inputMc->crendWrapper = defaultCrendWrapper();
initRotGains( inputMc->rot_gains_prev );
inputMc->lfeRouting = defaultLfeRouting( inConfig, inputMc->customLsInput, outConfig, *inputMc->base.ctx.pCustomLsOut );
if ( outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL || outConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM )
{
if ( ( error = initMcBinauralRendering( inputMc, inConfig, outConfig, hRendCfg ) ) != IVAS_ERR_OK )
{
return error;
}
}
if ( ( error = updateMcPanGains( inputMc, outConfig ) ) != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static void clearInputMc(
input_mc *inputMc )
{
rendering_context rendCtx;
rendCtx = inputMc->base.ctx;
initRendInputBase( &inputMc->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx );
/* Free input's internal handles */
if ( inputMc->efapInWrapper.hEfap != NULL )
{
efap_free_data( &inputMc->efapInWrapper.hEfap );
}
if ( inputMc->crendWrapper.hCrend != NULL )
{
ivas_rend_closeCrend( &inputMc->crendWrapper );
}
if ( inputMc->tdRendWrapper.hBinRendererTd != NULL )
{
ivas_td_binaural_close( &inputMc->tdRendWrapper.hBinRendererTd );
inputMc->tdRendWrapper.hHrtfTD = NULL;
}
return;
}
static ivas_error initSbaPanGainsForMcOut(
input_sba *inputSba,
const IVAS_REND_AudioConfig outConfig,
const LSSETUP_CUSTOM_STRUCT *outSetupCustom )
{
int16_t ambiOrderIn;
int16_t chInIdx, chOutIdx;
float *tmpDecMtx, *readPtr;
IVAS_OUTPUT_SETUP hOutSetup;
ivas_error error;
if ( ( error = getAmbisonicsOrder( inputSba->base.inConfig, &ambiOrderIn ) ) != IVAS_ERR_OK )
{
return error;
}
if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED )
{
assert( !"Invalid configuration" );
return IVAS_ERR_WRONG_PARAMS;
}
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_MONO:
hOutSetup.ls_azimuth = ls_azimuth_CICP1;
hOutSetup.ls_elevation = ls_elevation_CICP1;
ivas_output_init( &hOutSetup, rendAudioConfigToIvasAudioConfig( outConfig ) );
break;
case IVAS_REND_AUDIO_CONFIG_STEREO:
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
ivas_output_init( &hOutSetup, rendAudioConfigToIvasAudioConfig( outConfig ) );
break;
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
ivas_ls_custom_setup( &hOutSetup, outSetupCustom );
break;
default:
assert( !"Invalid speaker config" );
return IVAS_ERR_WRONG_PARAMS;
}
/* obtain and copy over HOA decoding matrix */
tmpDecMtx = NULL;
if ( ( error = ivas_sba_get_hoa_dec_matrix( hOutSetup, &tmpDecMtx, ambiOrderIn ) ) != IVAS_ERR_OK )
{
return error;
}
readPtr = &tmpDecMtx[0];
for ( chOutIdx = 0; chOutIdx < hOutSetup.nchan_out_woLFE + hOutSetup.num_lfe; ++chOutIdx )
{
for ( chInIdx = 0; chInIdx < SBA_NHARM_HOA3; ++chInIdx )
{
if ( hOutSetup.num_lfe > 0 && chOutIdx == hOutSetup.index_lfe[0] )
{
continue; /* nothing to be rendered to LFE */
}
inputSba->hoaDecMtx[chInIdx][chOutIdx] = *readPtr++;
}
}
count_free( tmpDecMtx );
return IVAS_ERR_OK;
}
static ivas_error initSbaPanGainsForSbaOut(
input_sba *inputSba,
const IVAS_REND_AudioConfig outConfig )
{
ivas_error error;
error = IVAS_ERR_OK;
if ( getAudioConfigType( outConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS )
{
assert( !"Invalid configuration" );
return IVAS_ERR_WRONG_PARAMS;
}
fillIdentityPanMatrix( inputSba->hoaDecMtx );
return error;
}
static ivas_error updateSbaPanGains(
input_sba *inputSba,
const IVAS_REND_AudioConfig outConfig,
RENDER_CONFIG_DATA *hRendCfg )
{
ivas_error error;
IVAS_REND_AudioConfig inConfig;
rendering_context rendCtx;
/* Reset to all zeros - some functions below only write non-zero elements. */
setZeroPanMatrix( inputSba->hoaDecMtx );
inConfig = inputSba->base.inConfig;
rendCtx = inputSba->base.ctx;
switch ( getAudioConfigType( outConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
error = initSbaPanGainsForMcOut( inputSba, outConfig, inputSba->base.ctx.pCustomLsOut );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
error = initSbaPanGainsForSbaOut( inputSba, outConfig );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
error = ivas_rend_openCrend( &inputSba->crendWrapper, inConfig, outConfig, hRendCfg, *rendCtx.pOutSampleRate );
break;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
if ( ( error = initSbaPanGainsForMcOut( inputSba, IVAS_REND_AUDIO_CONFIG_7_1_4, NULL ) ) != IVAS_ERR_OK )
{
return error;
}
error = ivas_rend_openCrend( &inputSba->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, hRendCfg, *rendCtx.pOutSampleRate );
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
/* Check error here to keep switch statement more compact */
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static ivas_error setRendInputActiveSba(
void *input,
const IVAS_REND_AudioConfig inConfig,
const IVAS_REND_InputId id,
RENDER_CONFIG_DATA *hRendCfg )
{
ivas_error error;
rendering_context rendCtx;
IVAS_REND_AudioConfig outConfig;
input_sba *inputSba;
inputSba = (input_sba *) input;
rendCtx = inputSba->base.ctx;
outConfig = *rendCtx.pOutConfig;
initRendInputBase( &inputSba->base, inConfig, id, rendCtx );
setZeroPanMatrix( inputSba->hoaDecMtx );
inputSba->crendWrapper = defaultCrendWrapper();
initRotGains( inputSba->rot_gains_prev );
if ( ( error = updateSbaPanGains( inputSba, outConfig, hRendCfg ) ) != IVAS_ERR_OK )
{
return error;
}
return error;
}
static void clearInputSba(
input_sba *inputSba )
{
rendering_context rendCtx;
rendCtx = inputSba->base.ctx;
initRendInputBase( &inputSba->base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, rendCtx );
/* Free input's internal handles */
if ( inputSba->crendWrapper.hCrend != NULL )
{
ivas_rend_closeCrend( &inputSba->crendWrapper );
}
return;
}
ivas_error IVAS_REND_Open(
IVAS_REND_HANDLE *phIvasRend,
const int32_t outputSampleRate,
const IVAS_REND_AudioConfig outConfig )
{
int16_t i;
IVAS_REND_HANDLE hIvasRend;
ivas_error error;
int16_t numOutChannels;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( phIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = validateOutputAudioConfig( outConfig ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = validateOutputSampleRate( outputSampleRate, outConfig ) ) != IVAS_ERR_OK )
{
return error;
}
*phIvasRend = (IVAS_REND_HANDLE) count_malloc( sizeof( struct IVAS_REND ) );
if ( *phIvasRend == NULL )
{
return IVAS_ERR_FAILED_ALLOC;
}
hIvasRend = *phIvasRend;
hIvasRend->sampleRateOut = outputSampleRate;
hIvasRend->outputConfig = outConfig;
hIvasRend->customLsOut = defaultCustomLs();
hIvasRend->hLimiter = NULL;
hIvasRend->efapOutWrapper.hEfap = NULL;
hIvasRend->efapOutWrapper.pCustomLsSetup = NULL;
#ifdef DEBUGGING
hIvasRend->numClipping = 0;
#endif
/* Initialize limiter */
if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK )
{
return error;
}
initLimiter( &hIvasRend->hLimiter, numOutChannels, outputSampleRate );
/* Initialize headrotation data */
initHeadRotation( hIvasRend );
/* Initialize EFAP */
initEfap( &hIvasRend->efapOutWrapper, outConfig, &hIvasRend->customLsOut );
/* Initialize inputs */
for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i )
{
initRendInputBase( &hIvasRend->inputsIsm[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) );
hIvasRend->inputsIsm[i].crendWrapper.hCrend = NULL;
hIvasRend->inputsIsm[i].tdRendWrapper.hBinRendererTd = NULL;
}
for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i )
{
initRendInputBase( &hIvasRend->inputsMc[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) );
hIvasRend->inputsMc[i].efapInWrapper.hEfap = NULL;
hIvasRend->inputsMc[i].crendWrapper.hCrend = NULL;
hIvasRend->inputsMc[i].tdRendWrapper.hBinRendererTd = NULL;
}
for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i )
{
initRendInputBase( &hIvasRend->inputsSba[i].base, IVAS_REND_AUDIO_CONFIG_UNKNOWN, 0, getRendCtx( hIvasRend ) );
hIvasRend->inputsSba[i].crendWrapper.hCrend = NULL;
}
return IVAS_ERR_OK;
}
static LSSETUP_CUSTOM_STRUCT makeCustomLsSetup(
const IVAS_CUSTOM_LS_DATA rendCustomLsLayout )
{
int16_t i;
LSSETUP_CUSTOM_STRUCT customLs;
/* Copy layout description */
customLs.num_spk = rendCustomLsLayout.num_spk;
mvr2r( rendCustomLsLayout.azimuth, customLs.ls_azimuth, rendCustomLsLayout.num_spk );
mvr2r( rendCustomLsLayout.elevation, customLs.ls_elevation, rendCustomLsLayout.num_spk );
customLs.is_planar_setup = 1;
for ( i = 0; i < rendCustomLsLayout.num_spk; ++i )
{
if ( fabsf( rendCustomLsLayout.elevation[i] ) > EPSILON )
{
customLs.is_planar_setup = 0;
break;
}
}
customLs.num_lfe = rendCustomLsLayout.num_lfe;
mvs2s( rendCustomLsLayout.lfe_idx, customLs.lfe_idx, rendCustomLsLayout.num_lfe );
return customLs;
}
static ivas_error validateCustomLsLayout(
const IVAS_CUSTOM_LS_DATA layout )
{
int16_t i;
/* Negative number of speakers or LFEs makes no sense */
if ( layout.num_spk < 0 || layout.num_lfe < 0 )
{
return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT;
}
/* There must be at least one speaker or LFE in the layout */
if ( layout.num_spk + layout.num_lfe <= 0 )
{
return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT;
}
/* LFE indices must be positive */
for ( i = 0; i < layout.num_lfe; ++i )
{
if ( layout.lfe_idx[i] < 0 )
{
return IVAS_ERR_INVALID_CUSTOM_LS_LAYOUT;
}
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout(
IVAS_REND_HANDLE hIvasRend,
const IVAS_CUSTOM_LS_DATA layout )
{
int16_t i, numOutChannels;
ivas_error error;
input_mc *inputMc;
input_sba *inputSba;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( hIvasRend->outputConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
/* Specifying details of custom speaker layout only makes sense if output config is set to custom speaker layout */
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
if ( ( error = validateCustomLsLayout( layout ) ) != IVAS_ERR_OK )
{
return error;
}
hIvasRend->customLsOut = makeCustomLsSetup( layout );
/* Re-initialize limiter - number of output channels may have changed */
if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK )
{
return error;
}
initLimiter( &hIvasRend->hLimiter, numOutChannels, hIvasRend->sampleRateOut );
/* Re-initialize EFAP - output layout has changed or has been fully defined for the first time */
initEfap( &hIvasRend->efapOutWrapper, hIvasRend->outputConfig, &hIvasRend->customLsOut );
/* Re-initialize panning gains for each active MC input, This includes re-initializing
* LFE handling for the new output layout, which means custom LFE handling is overwritten,
* if previously set for any MC input. */
for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i )
{
inputMc = &hIvasRend->inputsMc[i];
if ( inputMc->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
/* Input inactive, skip. */
continue;
}
inputMc->lfeRouting = defaultLfeRouting( inputMc->base.inConfig, inputMc->customLsInput, hIvasRend->outputConfig, *inputMc->base.ctx.pCustomLsOut );
if ( ( error = updateMcPanGains( inputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK )
{
return error;
}
}
/* Re-initialize panning gains for each active SBA input */
for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i )
{
inputSba = &hIvasRend->inputsSba[i];
if ( inputSba->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
/* Input inactive, skip. */
continue;
}
if ( ( error = updateSbaPanGains( inputSba, hIvasRend->outputConfig, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_NumOutChannels(
IVAS_REND_CONST_HANDLE hIvasRend,
int16_t *numOutChannels )
{
ivas_error error;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || numOutChannels == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
/* Handle special cases where additional info is needed from the renderer, otherwise use getAudioConfigNumChannels() */
switch ( hIvasRend->outputConfig )
{
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
*numOutChannels = hIvasRend->customLsOut.num_spk + hIvasRend->customLsOut.num_lfe;
error = IVAS_ERR_OK;
break;
default:
error = getAudioConfigNumChannels( hIvasRend->outputConfig, numOutChannels );
break;
}
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static IVAS_REND_InputId makeInputId(
IVAS_REND_AudioConfig config,
const int32_t inputIndex )
{
/* Put config type in second byte (from LSB), put index + 1 in first byte
*
* Index is incremented here so that a valid ID can never be 0. */
return (IVAS_REND_InputId) ( ( getAudioConfigType( config ) << 8 ) | ( inputIndex + 1 ) );
}
static ivas_error getInputById(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_InputId inputId,
void **ppInput )
{
int32_t inputIndex;
IVAS_REND_AudioConfigType configType;
input_base *pInputBase;
/* Reverse makeInputId() */
inputIndex = ( inputId & 0xFF ) - 1;
configType = ( inputId & 0xFF00 ) >> 8;
/* Validate values derived from input ID */
if ( inputIndex < 0 )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
switch ( configType )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED:
if ( inputIndex > RENDERER_MAX_ISM_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsIsm[inputIndex].base;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
if ( inputIndex > RENDERER_MAX_MC_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsMc[inputIndex].base;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
if ( inputIndex > RENDERER_MAX_SBA_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsSba[inputIndex].base;
break;
default:
return IVAS_ERR_INVALID_INPUT_ID;
}
/* Ensure input ID matches and that input is active */
if ( pInputBase->id != inputId || pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
/* Validation done, set value via output parameter */
*ppInput = pInputBase;
return IVAS_ERR_OK;
}
/* Unfortunately code duplication here is the only way to avoid warnings about const casting */
static ivas_error getConstInputById(
IVAS_REND_CONST_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const void **ppInput )
{
int32_t inputIndex;
IVAS_REND_AudioConfigType configType;
const input_base *pInputBase;
/* Reverse makeInputId() */
inputIndex = ( inputId & 0xFF ) - 1;
configType = ( inputId & 0xFF00 ) >> 8;
/* Validate values derived from input ID */
if ( inputIndex < 0 )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
switch ( configType )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED:
if ( inputIndex > RENDERER_MAX_ISM_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsIsm[inputIndex].base;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
if ( inputIndex > RENDERER_MAX_MC_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsMc[inputIndex].base;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
if ( inputIndex > RENDERER_MAX_SBA_INPUTS )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
pInputBase = &hIvasRend->inputsSba[inputIndex].base;
break;
default:
return IVAS_ERR_INVALID_INPUT_ID;
}
/* Ensure input ID matches and that input is active */
if ( pInputBase->id != inputId || pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
return IVAS_ERR_INVALID_INPUT_ID;
}
/* Validation done, set value via output parameter */
*ppInput = pInputBase;
return IVAS_ERR_OK;
}
static ivas_error findFreeInputSlot(
const void *inputs,
int32_t inputStructSize,
int32_t maxInputs,
int32_t *inputIndex )
{
/* Using a void pointer and a separately provided size is a hack for this function
to be reusable for arrays of any input type (input_ism, input_mc, input_sba).
Assumptions:
- input_base is always the first member in the input struct
- provided size is correct
*/
int32_t i;
bool canAddInput;
const uint8_t *pByte;
const input_base *pInputBase;
canAddInput = false;
/* Find first unused input in array */
for ( i = 0, pByte = inputs; i < maxInputs; ++i, pByte += inputStructSize )
{
pInputBase = (const input_base *) pByte;
if ( pInputBase->inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
*inputIndex = i;
canAddInput = true;
break;
}
}
if ( !canAddInput )
{
return IVAS_ERR_TOO_MANY_INPUTS;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_AddInput(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_AudioConfig inConfig,
IVAS_REND_InputId *inputId )
{
ivas_error error;
int32_t maxNumInputsOfType;
void *inputsArray;
int32_t inputStructSize;
ivas_error ( *activateInput )( void *, IVAS_REND_AudioConfig, IVAS_REND_InputId, RENDER_CONFIG_DATA * );
int32_t inputIndex;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || inputId == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
switch ( getAudioConfigType( inConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED:
maxNumInputsOfType = RENDERER_MAX_ISM_INPUTS;
inputsArray = hIvasRend->inputsIsm;
inputStructSize = sizeof( *hIvasRend->inputsIsm );
activateInput = setRendInputActiveIsm;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
maxNumInputsOfType = RENDERER_MAX_MC_INPUTS;
inputsArray = hIvasRend->inputsMc;
inputStructSize = sizeof( *hIvasRend->inputsMc );
activateInput = setRendInputActiveMc;
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
maxNumInputsOfType = RENDERER_MAX_SBA_INPUTS;
inputsArray = hIvasRend->inputsSba;
inputStructSize = sizeof( *hIvasRend->inputsSba );
activateInput = setRendInputActiveSba;
break;
default:
return IVAS_ERR_INVALID_INPUT_FORMAT;
}
/* Find first free input in array corresponding to input type */
if ( ( error = findFreeInputSlot( inputsArray, inputStructSize, maxNumInputsOfType, &inputIndex ) ) != IVAS_ERR_OK )
{
return error;
}
*inputId = makeInputId( inConfig, inputIndex );
if ( ( error = activateInput( (uint8_t *) inputsArray + inputStructSize * inputIndex, inConfig, *inputId, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_ConfigureCustomInputLoudspeakerLayout(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const IVAS_CUSTOM_LS_DATA layout )
{
input_mc *inputMc;
ivas_error error;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = validateCustomLsLayout( layout ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputMc ) ) != IVAS_ERR_OK )
{
return error;
}
if ( inputMc->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
/* Specifying details of custom speaker layout only makes sense if input config is set to custom speaker layout */
return IVAS_ERR_INVALID_INPUT_FORMAT;
}
/* Re-initialize panning gains for the MC input, This includes re-initializing LFE handling
* for the new input layout, which means custom LFE handling is overwritten, if previously
* set for the MC input. */
inputMc->customLsInput = makeCustomLsSetup( layout );
inputMc->lfeRouting = defaultLfeRouting( inputMc->base.inConfig, inputMc->customLsInput, hIvasRend->outputConfig, *inputMc->base.ctx.pCustomLsOut );
initEfap( &inputMc->efapInWrapper, inputMc->base.inConfig, &inputMc->customLsInput );
if ( hIvasRend->outputConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL || hIvasRend->outputConfig == IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM )
{
if ( ( error = initMcBinauralRendering( inputMc, inputMc->base.inConfig, hIvasRend->outputConfig, hIvasRend->hRendererConfig ) ) != IVAS_ERR_OK )
{
return error;
}
}
if ( ( error = updateMcPanGains( inputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_SetInputGain(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const float gain /* linear gain, not in dB */
)
{
input_base *inputBase;
ivas_error error;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK )
{
return error;
}
inputBase->gain = gain;
return IVAS_ERR_OK;
}
static int32_t getNumLfeChannels(
input_mc *inputMc )
{
switch ( inputMc->base.inConfig )
{
case IVAS_REND_AUDIO_CONFIG_5_1:
case IVAS_REND_AUDIO_CONFIG_7_1:
case IVAS_REND_AUDIO_CONFIG_5_1_2:
case IVAS_REND_AUDIO_CONFIG_5_1_4:
case IVAS_REND_AUDIO_CONFIG_7_1_4:
return 1;
case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
return inputMc->customLsInput.num_lfe;
default:
break;
}
return 0;
}
ivas_error IVAS_REND_SetInputLfeRouting(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const IVAS_REND_LfeRouting lfeRouting )
{
input_base *pInputBase;
input_mc *pInputMc;
ivas_error error;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &pInputBase ) ) != IVAS_ERR_OK )
{
return error;
}
if ( getAudioConfigType( pInputBase->inConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED )
{
/* Custom LFE routing only makes sense with channel-based input */
return IVAS_ERR_INVALID_INPUT_FORMAT;
}
pInputMc = (input_mc *) pInputBase;
if ( getNumLfeChannels( pInputMc ) != lfeRouting.numLfeChannels )
{
return IVAS_ERR_WRONG_NUM_CHANNELS;
}
pInputMc->lfeRouting = lfeRouting;
if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_RemoveInput(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId )
{
ivas_error error;
input_base *inputBase;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK )
{
return error;
}
switch ( getAudioConfigType( inputBase->inConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED:
clearInputIsm( (input_ism *) inputBase );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
clearInputMc( (input_mc *) inputBase );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
clearInputSba( (input_sba *) inputBase );
break;
default:
return IVAS_ERR_INVALID_INPUT_FORMAT;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_GetInputNumChannels(
IVAS_REND_CONST_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
int16_t *numChannels )
{
ivas_error error;
const input_base *pInput;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || numChannels == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = getConstInputById( hIvasRend, inputId, (const void **) &pInput ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getRendInputNumChannels( pInput, numChannels ) ) != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_GetDelay(
IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer state */
int16_t *nSamples, /* o : Renderer delay in samples */
int32_t *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */
)
{
/* TODO tmu : this function only returns the maximum delay across all inputs
* Ideally each input has its own delay buffer and everything is aligned (binaural and LFE filtering delays are nonuniform)
*/
int16_t i;
int32_t latency_ns;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || nSamples == NULL || timeScale == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
*timeScale = hIvasRend->sampleRateOut;
*nSamples = 0;
/* Compute the maximum delay across all inputs */
for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; i++ )
{
if ( hIvasRend->inputsIsm[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
latency_ns = max( hIvasRend->inputsIsm[i].crendWrapper.binaural_latency_ns,
hIvasRend->inputsIsm[i].tdRendWrapper.binaural_latency_ns );
*nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) );
}
}
for ( i = 0; i < RENDERER_MAX_MC_INPUTS; i++ )
{
if ( hIvasRend->inputsMc[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
latency_ns = max( hIvasRend->inputsMc[i].crendWrapper.binaural_latency_ns,
hIvasRend->inputsMc[i].tdRendWrapper.binaural_latency_ns );
*nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) );
}
}
for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; i++ )
{
if ( hIvasRend->inputsSba[i].base.inConfig != IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
latency_ns = hIvasRend->inputsSba[i].crendWrapper.binaural_latency_ns;
*nSamples = max( *nSamples, NS2SA( *timeScale, latency_ns ) );
}
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_FeedInputAudio(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const IVAS_REND_ReadOnlyAudioBuffer inputAudio )
{
ivas_error error;
input_base *inputBase;
int16_t numInputChannels;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || inputAudio.data == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( inputAudio.config.numSamplesPerChannel <= 0 || MAX_BUFFER_LENGTH_PER_CHANNEL < inputAudio.config.numSamplesPerChannel )
{
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
if ( inputAudio.config.numChannels <= 0 || MAX_INPUT_CHANNELS < inputAudio.config.numChannels )
{
return IVAS_ERR_WRONG_NUM_CHANNELS;
}
if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL &&
inputAudio.config.numSamplesPerChannel * 1000 != BINAURAL_RENDERING_FRAME_SIZE_MS * hIvasRend->sampleRateOut )
{
/* Binaural rendering requires specific frame size */
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getRendInputNumChannels( inputBase, &numInputChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( numInputChannels != inputAudio.config.numChannels )
{
return IVAS_ERR_WRONG_NUM_CHANNELS;
}
inputBase->inputBuffer.config = inputAudio.config;
mvr2r( inputAudio.data, inputBase->inputBuffer.data, inputAudio.config.numSamplesPerChannel * inputAudio.config.numChannels );
inputBase->numNewSamplesPerChannel = inputAudio.config.numSamplesPerChannel;
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_FeedInputObjectMetadata(
IVAS_REND_HANDLE hIvasRend,
const IVAS_REND_InputId inputId,
const IVAS_REND_AudioObjectPosition objectPosition )
{
input_base *inputBase;
input_ism *inputIsm;
ivas_error error;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( ( error = getInputById( hIvasRend, inputId, (void **) &inputBase ) ) != IVAS_ERR_OK )
{
return error;
}
if ( inputBase->inConfig != IVAS_REND_AUDIO_CONFIG_OBJECT )
{
/* Object metadata should only be fed for object inputs */
return IVAS_ERR_METADATA_NOT_EXPECTED;
}
inputIsm = (input_ism *) inputBase;
inputIsm->previousPos = inputIsm->currentPos;
inputIsm->currentPos = objectPosition;
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_InitConfig( IVAS_REND_HANDLE st,
bool rendererConfigEnabled )
{
ivas_error error;
if ( rendererConfigEnabled )
{
st->rendererConfigEnabled = 1;
}
else
{
st->rendererConfigEnabled = 0;
}
if ( ( error = ivas_render_config_open( &( st->hRendererConfig ) ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ivas_render_config_init_from_rom( &st->hRendererConfig, st->rendererConfigEnabled ) != IVAS_ERR_OK )
{
return IVAS_ERR_INTERNAL_FATAL;
}
return IVAS_ERR_OK;
}
int16_t IVAS_REND_GetRenderConfig(
IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */
const IVAS_RENDER_CONFIG_HANDLE hRCout /* o : Render configuration handle */
)
{
RENDER_CONFIG_HANDLE hRCin;
if ( hIvasRend == NULL || hIvasRend->hRendererConfig == NULL || hRCout == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
hRCin = hIvasRend->hRendererConfig;
#ifdef DEBUGGING
switch ( hRCin->renderer_type_override )
{
case RENDER_TYPE_OVERRIDE_CREND:
hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_CREND;
break;
case RENDER_TYPE_OVERRIDE_FASTCONV:
hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_FASTCONV;
break;
default:
hRCout->renderer_type_override = IVAS_RENDER_TYPE_OVERRIDE_NONE;
break;
}
#endif
hRCout->room_acoustics.override = hRCin->roomAcoustics.override;
hRCout->room_acoustics.use_brir = hRCin->roomAcoustics.use_brir;
hRCout->room_acoustics.late_reverb_on = hRCin->roomAcoustics.late_reverb_on;
hRCout->room_acoustics.nBands = hRCin->roomAcoustics.nBands;
hRCout->room_acoustics.acousticPreDelay = hRCin->roomAcoustics.acousticPreDelay;
hRCout->room_acoustics.inputPreDelay = hRCin->roomAcoustics.inputPreDelay;
mvr2r( hRCin->roomAcoustics.pFc_input, hRCout->room_acoustics.pFc_input, CLDFB_NO_CHANNELS_MAX );
mvr2r( hRCin->roomAcoustics.pAcoustic_rt60, hRCout->room_acoustics.pAcoustic_rt60, CLDFB_NO_CHANNELS_MAX );
mvr2r( hRCin->roomAcoustics.pAcoustic_dsr, hRCout->room_acoustics.pAcoustic_dsr, CLDFB_NO_CHANNELS_MAX );
return IVAS_ERR_OK;
}
int16_t IVAS_REND_FeedRenderConfig(
IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */
const IVAS_RENDER_CONFIG_DATA renderConfig /* i : Render configuration struct */
)
{
RENDER_CONFIG_HANDLE hRenderConfig;
if ( hIvasRend == NULL || hIvasRend->hRendererConfig == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
hRenderConfig = hIvasRend->hRendererConfig;
#ifdef DEBUGGING
hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_NONE;
if ( renderConfig.renderer_type_override == IVAS_RENDER_TYPE_OVERRIDE_FASTCONV )
{
hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_FASTCONV;
}
if ( renderConfig.renderer_type_override == IVAS_RENDER_TYPE_OVERRIDE_CREND )
{
hRenderConfig->renderer_type_override = RENDER_TYPE_OVERRIDE_CREND;
}
#endif
hRenderConfig->roomAcoustics.override = renderConfig.room_acoustics.override;
hRenderConfig->roomAcoustics.use_brir = renderConfig.room_acoustics.use_brir;
hRenderConfig->roomAcoustics.late_reverb_on = renderConfig.room_acoustics.late_reverb_on;
hRenderConfig->roomAcoustics.nBands = renderConfig.room_acoustics.nBands;
hRenderConfig->roomAcoustics.acousticPreDelay = renderConfig.room_acoustics.acousticPreDelay;
hRenderConfig->roomAcoustics.inputPreDelay = renderConfig.room_acoustics.inputPreDelay;
mvr2r( renderConfig.room_acoustics.pFc_input, hRenderConfig->roomAcoustics.pFc_input, CLDFB_NO_CHANNELS_MAX );
mvr2r( renderConfig.room_acoustics.pAcoustic_rt60, hRenderConfig->roomAcoustics.pAcoustic_rt60, CLDFB_NO_CHANNELS_MAX );
mvr2r( renderConfig.room_acoustics.pAcoustic_dsr, hRenderConfig->roomAcoustics.pAcoustic_dsr, CLDFB_NO_CHANNELS_MAX );
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_SetHeadRotation(
IVAS_REND_HANDLE hIvasRend,
const IVAS_QUATERNION headRot[RENDERER_HEAD_POSITIONS_PER_FRAME] )
{
int16_t i;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( getAudioConfigType( hIvasRend->outputConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL )
{
/* Head rotation can be set only with binaural output */
return IVAS_ERR_METADATA_NOT_EXPECTED;
}
if ( headRot == NULL )
{
hIvasRend->headRotData.headRotEnabled = 0;
}
else
{
hIvasRend->headRotData.headRotEnabled = 1;
for ( i = 0; i < RENDERER_HEAD_POSITIONS_PER_FRAME; ++i )
{
hIvasRend->headRotData.headPositions[i] = headRot[i];
}
}
return IVAS_ERR_OK;
}
/* Take one channel from input buffer and copy it to each channel
in output buffer, with different gain applied per output channel.
This function takes 2 gain vectors - one for the beginning and one
for the end of the buffer. Gain values are lineraly interpolated
for all samples in between. */
static void renderBufferChannelLerp(
const IVAS_REND_AudioBuffer inAudio,
const int32_t inChannelIdx,
const float *const gainsCurrent,
const float *const gainsPrev,
IVAS_REND_AudioBuffer outAudio )
{
const float *inSmpl;
float *outSmpl;
float fadeIn;
float fadeOut;
int32_t i;
const float *lastInSmpl;
int16_t outChnlIdx;
float currentGain;
float previousGain;
/* Pointer to behind last input sample */
lastInSmpl = getSmplPtr( inAudio, inChannelIdx, inAudio.config.numSamplesPerChannel );
for ( outChnlIdx = 0; outChnlIdx < outAudio.config.numChannels; ++outChnlIdx )
{
currentGain = gainsCurrent[outChnlIdx];
previousGain = gainsPrev == NULL ? 0.f : gainsPrev[outChnlIdx];
/* Process current output channel only if applying non-zero gains */
if ( fabsf( currentGain ) > EPSILON || ( gainsPrev != NULL && fabsf( previousGain ) > EPSILON ) )
{
/* Reset input pointer to the beginning of input channel */
inSmpl = getSmplPtr( inAudio, inChannelIdx, 0 );
/* Set output pointer to first output channel sample */
outSmpl = getSmplPtr( outAudio, outChnlIdx, 0 );
if ( gainsPrev == NULL || fabsf( previousGain - currentGain ) <= EPSILON )
{
/* If no interpolation from previous frame, apply current gain */
do
{
*outSmpl += currentGain * ( *inSmpl );
++outSmpl;
++inSmpl;
} while ( inSmpl != lastInSmpl );
}
else
{
i = 0;
/* Otherwise use weighted average between previous and current gain */
do
{
/* TODO(sgi): This is calculated for each output channel - can be optimised */
fadeIn = (float) i / ( outAudio.config.numSamplesPerChannel - 1 );
fadeOut = 1.0f - fadeIn;
*outSmpl += ( fadeIn * currentGain + fadeOut * previousGain ) * ( *inSmpl );
++outSmpl;
++inSmpl;
++i;
} while ( inSmpl != lastInSmpl );
}
}
}
}
/* Take one channel from input buffer and copy it to each channel
in output buffer, with different gain applied per output channel */
static void renderBufferChannel(
const IVAS_REND_AudioBuffer inAudio,
const int32_t inChannelIdx,
const float *const outputGains,
IVAS_REND_AudioBuffer outAudio )
{
renderBufferChannelLerp( inAudio, inChannelIdx, outputGains, NULL, outAudio );
return;
}
static ivas_error rotateFrameMc(
IVAS_REND_AudioBuffer inAudio, /* i : Input Audio buffer */
IVAS_REND_AudioConfig inConfig, /* i : Input Audio config */
LSSETUP_CUSTOM_STRUCT inCustomLs, /* i : Input Custom LS setup */
const IVAS_REND_HeadRotData *headRotData, /* i : Head rotation data */
rotation_gains gains_prev, /* i/o: Previous frame rotation gains */
const EFAP_HANDLE hEFAPdata, /* i : EFAP structure */
IVAS_REND_AudioBuffer outAudio /* o : Output Audio buffer */
)
{
int16_t i;
int16_t subframe_idx, subframe_len;
int16_t azimuth, elevation;
int16_t is_planar_setup, lfe_idx;
int16_t nchan;
int16_t ch_in, ch_out;
int16_t ch_in_woLFE, ch_out_woLFE;
float *readPtr, *writePtr;
const float *ls_azimuth, *ls_elevation;
rotation_matrix Rmat;
rotation_gains gains;
float tmp_gains[MAX_INPUT_CHANNELS];
wmops_sub_start( "rotateFrameMc" );
if ( inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
getAudioConfigNumChannels( inConfig, &nchan );
}
else
{
nchan = inCustomLs.num_spk + inCustomLs.num_lfe;
}
getMcConfigValues( inConfig, inCustomLs, &ls_azimuth, &ls_elevation, &lfe_idx, &is_planar_setup );
/* initialize gains to passthrough */
for ( ch_in = 0; ch_in < nchan; ch_in++ )
{
set_zero( gains[ch_in], (int16_t) nchan );
gains[ch_in][ch_in] = 1.f;
}
/* subframe loop */
subframe_len = inAudio.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME;
for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ )
{
/* Get next quaternion and calculate rotation matrix */
QuatToRotMat( headRotData->headPositions[subframe_idx], Rmat );
for ( ch_in = 0; ch_in < nchan; ch_in++ )
{
/* skip LFE */
if ( ch_in == lfe_idx )
{
continue;
}
/* input channel index without LFE */
ch_in_woLFE = ( ( lfe_idx > 0 ) && ( ch_in >= lfe_idx ) ) ? ch_in - 1 : ch_in;
/* gains for current subframe rotation */
rotateAziEle( ls_azimuth[ch_in_woLFE], ls_elevation[ch_in_woLFE], &azimuth, &elevation, Rmat, is_planar_setup );
if ( hEFAPdata != NULL && ( ls_azimuth[ch_in_woLFE] != azimuth || ls_elevation[ch_in_woLFE] != elevation ) )
{
efap_determine_gains( hEFAPdata, tmp_gains, azimuth, elevation, EFAP_MODE_EFAP );
for ( ch_out = 0; ch_out < nchan; ch_out++ )
{
/* skip LFE */
if ( ch_out == lfe_idx )
{
continue;
}
/* output channel index without LFE */
ch_out_woLFE = ( ( lfe_idx > 0 ) && ( ch_out >= lfe_idx ) ) ? ch_out - 1 : ch_out;
gains[ch_in][ch_out] = tmp_gains[ch_out_woLFE];
}
}
}
/* apply panning gains by mtx multiplication */
for ( ch_out = 0; ch_out < nchan; ch_out++ )
{
for ( ch_in = 0; ch_in < nchan; ch_in++ )
{
writePtr = getSmplPtr( outAudio, ch_out, subframe_idx * subframe_len );
readPtr = getSmplPtr( inAudio, ch_in, subframe_idx * subframe_len );
/* crossfade with previous rotation gains */
for ( i = 0; i < subframe_len; i++ )
{
*writePtr++ +=
( *readPtr ) * ( ( 1 - headRotData->crossfade[i] ) * gains_prev[ch_in][ch_out] ) +
( *readPtr ) * ( headRotData->crossfade[i] * gains[ch_in][ch_out] );
readPtr++;
}
}
}
/* move gains to gains_prev */
for ( i = 0; i < nchan; i++ )
{
mvr2r( gains[i], gains_prev[i], (int16_t) nchan );
}
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error rotateFrameSba(
IVAS_REND_AudioBuffer inAudio, /* i : Input Audio buffer */
IVAS_REND_AudioConfig inConfig, /* i : Input Audio config */
const IVAS_REND_HeadRotData *headRotData, /* i : Head rotation data */
rotation_gains gains_prev, /* i/o: Previous frame rotation gains */
IVAS_REND_AudioBuffer outAudio /* o : Output Audio buffer */
)
{
int16_t i, l, n, m;
int16_t m1, m2;
int16_t shd_rot_max_order;
int16_t subframe_idx, subframe_len;
float *readPtr, *writePtr;
rotation_matrix Rmat;
float tmpRot[2 * HEADROT_ORDER + 1];
rotation_gains gains;
wmops_sub_start( "rotateFrameSba" );
getAmbisonicsOrder( inConfig, &shd_rot_max_order );
/* subframe loop */
subframe_len = inAudio.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME;
for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ )
{
/* initialize rotation matrices with zeros */
for ( i = 0; i < HEADROT_SHMAT_DIM; i++ )
{
set_zero( gains[i], HEADROT_SHMAT_DIM );
}
/* Get next quaternion and calculate rotation matrix */
QuatToRotMat( headRotData->headPositions[subframe_idx], Rmat );
/* calculate ambisonics rotation matrices for the previous and current frames */
SHrotmatgen( gains, Rmat, shd_rot_max_order );
for ( i = 0; i < subframe_len; i++ )
{
/* As the rotation matrix becomes block diagonal in a SH basis, we can*/
/* apply each angular-momentum block individually to save complexity. */
/* loop over l blocks */
m1 = 1;
m2 = 4;
for ( l = 1; l <= shd_rot_max_order; l++ )
{
/* compute mtx-vector product for this l */
for ( n = m1; n < m2; n++ )
{
tmpRot[n - m1] = 0.f;
for ( m = m1; m < m2; m++ )
{
readPtr = getSmplPtr( inAudio, m, subframe_idx * subframe_len + i );
/* crossfade with previous rotation gains */
tmpRot[n - m1] += headRotData->crossfade[i] * gains[n][m] * ( *readPtr ) +
( 1 - headRotData->crossfade[i] ) * gains_prev[n][m] * ( *readPtr );
}
}
}
/* write back the result */
for ( n = m1; n < m2; n++ )
{
writePtr = getSmplPtr( outAudio, n, subframe_idx * subframe_len + i );
( *writePtr ) = tmpRot[n - m1];
}
m1 = m2;
m2 += 2 * ( l + 1 ) + 1;
}
/*unoptimized code for reference (full matrix multiplication)*/
// for ( n = 0; n < nchan; n++ )
// {
// tmpRot[n] = 0.f;
//
// for ( m = 0; m < nchan; m++ )
// {
// tmpRot[n] += SHrotmat[n][m] * output[m][i];
// }
// }
// for ( n = 0; n < nchan; n++ )
// {
// output[n][i] = tmpRot[n];
// }
/* move SHrotmat to SHrotmat_prev */
for ( i = 0; i < HEADROT_SHMAT_DIM; i++ )
{
mvr2r( gains[i], gains_prev[i], HEADROT_SHMAT_DIM );
}
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderIsmToBinaural(
const input_ism *ismInput,
IVAS_REND_AudioBuffer outAudio )
{
float tmpTDRendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
ivas_error error;
wmops_sub_start( "renderIsmToBinaural" );
copyBufferTo2dArray( ismInput->base.inputBuffer, tmpTDRendBuffer );
/* TODO tmu : missing: interpolation between positions, 5ms rendering */
if ( ( error = ivas_rend_TDObjRenderFrame( &ismInput->tdRendWrapper,
ismInput->base.inConfig,
NULL,
ismInput->base.ctx.pHeadRotData,
&ismInput->currentPos,
outAudio.config.numSamplesPerChannel,
*ismInput->base.ctx.pOutSampleRate,
tmpTDRendBuffer ) ) != IVAS_ERR_OK )
{
return error;
}
accumulate2dArrayToBuffer( tmpTDRendBuffer, &outAudio );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderIsmToBinauralRoom(
input_ism *ismInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
int16_t azi_rot, ele_rot;
int16_t subframe_idx, subframe_len;
int16_t tmp;
rotation_matrix Rmat;
float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
IVAS_QUATERNION quat;
ivas_error error;
pan_vector currentPanGains;
pan_vector previousPanGains;
IVAS_REND_AudioBuffer tmpMcBuffer;
IVAS_REND_AudioObjectPosition rotatedPos;
const IVAS_REND_HeadRotData *headRotData;
wmops_sub_start( "renderIsmToBinauralRoom" );
headRotData = ismInput->base.ctx.pHeadRotData;
rotatedPos = defaultObjectPosition();
if ( headRotData->headRotEnabled )
{
subframe_len = ismInput->base.inputBuffer.config.numSamplesPerChannel / RENDERER_HEAD_POSITIONS_PER_FRAME;
// for ( subframe_idx = 0; subframe_idx < RENDERER_HEAD_POSITIONS_PER_FRAME; subframe_idx++ )
for ( subframe_idx = 0; subframe_idx < 1; subframe_idx++ )
{
quat.w = headRotData->headPositions[subframe_idx].w;
quat.x = headRotData->headPositions[subframe_idx].x;
quat.y = headRotData->headPositions[subframe_idx].y;
quat.z = headRotData->headPositions[subframe_idx].z;
QuatToRotMat( quat, Rmat );
}
(void) subframe_len; // avoid warning
}
/* TODO tmu : missing: interpolation between positions, 5ms rendering */
/* TODO(sgi): Possible optimization: less processing needed if position didn't change */
/* TODO tmu2sgi: needs a lot of cleanup, we could also add rot_gains_prev to ismInput and use that */
/* previous position gains */
if ( headRotData->headRotEnabled )
{
rotateAziEle( ismInput->previousPos.azimuth, ismInput->previousPos.elevation, &azi_rot, &ele_rot, ismInput->rot_mat_prev, 0 );
rotatedPos.azimuth = (float) azi_rot;
rotatedPos.elevation = (float) ele_rot;
}
if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper,
( headRotData->headRotEnabled ) ? rotatedPos.azimuth : ismInput->previousPos.azimuth,
( headRotData->headRotEnabled ) ? rotatedPos.elevation : ismInput->previousPos.elevation,
previousPanGains ) ) != IVAS_ERR_OK )
{
return error;
}
/* current position gains */
if ( headRotData->headRotEnabled )
{
rotateAziEle( ismInput->currentPos.azimuth, ismInput->currentPos.elevation, &azi_rot, &ele_rot, Rmat, 0 );
rotatedPos.azimuth = (float) azi_rot;
rotatedPos.elevation = (float) ele_rot;
}
if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper,
( headRotData->headRotEnabled ) ? rotatedPos.azimuth : ismInput->currentPos.azimuth,
( headRotData->headRotEnabled ) ? rotatedPos.elevation : ismInput->currentPos.elevation,
currentPanGains ) ) != IVAS_ERR_OK )
{
return error;
}
for ( i = 0; i < 3; i++ )
{
mvr2r( Rmat[i], ismInput->rot_mat_prev[i], 3 );
}
/* intermediate rendering to 7_1_4 */
tmpMcBuffer = ismInput->base.inputBuffer;
getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp );
tmpMcBuffer.config.numChannels = tmp;
tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels );
renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, tmpMcBuffer );
copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer );
ivas_rend_crendProcess( &ismInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM, tmpCrendBuffer, *ismInput->base.ctx.pOutSampleRate );
accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio );
count_free( tmpMcBuffer.data );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderIsmToMc(
const input_ism *ismInput,
const IVAS_REND_AudioBuffer outAudio )
{
pan_vector currentPanGains;
pan_vector previousPanGains;
ivas_error error;
wmops_sub_start( "renderIsmToMc" );
/* TODO(sgi): Possible optimization: less processing needed if position didn't change */
if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, ismInput->currentPos.azimuth, ismInput->currentPos.elevation, currentPanGains ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getEfapGains( *ismInput->base.ctx.pEfapOutWrapper, ismInput->previousPos.azimuth, ismInput->previousPos.elevation, previousPanGains ) ) != IVAS_ERR_OK )
{
return error;
}
/* Assume num channels in audio buffer to be 1.
* This should have been validated in IVAS_REND_FeedInputAudio() */
renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, outAudio );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderIsmToSba(
const input_ism *ismInput,
const IVAS_REND_AudioConfig outConfig,
const IVAS_REND_AudioBuffer outAudio )
{
int16_t ambiOrderOut;
int16_t numOutChannels;
pan_vector currentPanGains;
pan_vector previousPanGains;
ivas_error error;
error = IVAS_ERR_OK;
wmops_sub_start( "renderIsmToSba" );
if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = getAmbisonicsOrder( outConfig, &ambiOrderOut ) ) != IVAS_ERR_OK )
{
return error;
}
ivas_dirac_dec_get_response( (int16_t) ismInput->previousPos.azimuth, (int16_t) ismInput->previousPos.elevation, previousPanGains, ambiOrderOut );
if ( ( ismInput->currentPos.azimuth == ismInput->previousPos.azimuth ) &&
( ismInput->currentPos.elevation == ismInput->previousPos.elevation ) )
{
mvr2r( previousPanGains, currentPanGains, MAX_OUTPUT_CHANNELS );
}
else
{
ivas_dirac_dec_get_response( (int16_t) ismInput->currentPos.azimuth, (int16_t) ismInput->currentPos.elevation, currentPanGains, ambiOrderOut );
}
/* Assume num channels in audio buffer to be 1.
* This should have been validated in IVAS_REND_FeedInputAudio() */
renderBufferChannelLerp( ismInput->base.inputBuffer, 0, currentPanGains, previousPanGains, outAudio );
wmops_sub_end();
return error;
}
static ivas_error renderInputIsm(
input_ism *ismInput,
const IVAS_REND_AudioConfig outConfig,
const IVAS_REND_AudioBuffer outAudio )
{
ivas_error error;
IVAS_REND_AudioBuffer inAudio;
inAudio = ismInput->base.inputBuffer;
if ( ismInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel )
{
/* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
ismInput->base.numNewSamplesPerChannel = 0;
/* Apply input gain to new audio */
v_multc( inAudio.data, ismInput->base.gain, inAudio.data, inAudio.config.numSamplesPerChannel * inAudio.config.numChannels );
switch ( getAudioConfigType( outConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
error = renderIsmToMc( ismInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
error = renderIsmToSba( ismInput, outConfig, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
error = renderIsmToBinaural( ismInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
error = renderIsmToBinauralRoom( ismInput, outAudio );
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
/* Check error here to keep switch statement more compact */
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static ivas_error renderActiveInputsIsm(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
input_ism *pCurrentInput;
ivas_error error;
for ( i = 0, pCurrentInput = hIvasRend->inputsIsm; i < RENDERER_MAX_ISM_INPUTS; ++i, ++pCurrentInput )
{
if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
/* Skip inactive inputs */
continue;
}
if ( ( error = renderInputIsm( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
static ivas_error renderLfeToBinaural(
const input_mc *mcInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
int16_t lfe_idx;
float gain;
float *readPtr, *writePtr;
assert( ( outAudio.config.numChannels == 2 ) && "Must be binaural output" );
wmops_sub_start( "renderLfeToBinaural" );
gain = GAIN_LFE;
if ( mcInput->base.inConfig != IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
lfe_idx = LFE_CHANNEL;
}
else if ( mcInput->customLsInput.num_lfe > 0 )
{
lfe_idx = mcInput->customLsInput.lfe_idx[0];
}
else
{
/* no LFE to render */
return IVAS_ERR_OK;
}
/* Copy LFE to left and right ears */
readPtr = getSmplPtr( mcInput->base.inputBuffer, lfe_idx, 0 );
writePtr = getSmplPtr( outAudio, 0, 0 );
for ( i = 0; i < mcInput->base.inputBuffer.config.numSamplesPerChannel; i++ )
{
*writePtr++ += gain * ( *readPtr++ );
}
readPtr = getSmplPtr( mcInput->base.inputBuffer, lfe_idx, 0 );
writePtr = getSmplPtr( outAudio, 1, 0 );
for ( i = 0; i < mcInput->base.inputBuffer.config.numSamplesPerChannel; i++ )
{
*writePtr++ += gain * ( *readPtr++ );
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderMcToBinaural(
input_mc *mcInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
int8_t headRotEnabled;
float tmpRendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
IVAS_REND_AudioConfig inConfig;
ivas_error error;
IVAS_REND_AudioBuffer tmpRotBuffer;
wmops_sub_start( "renderMcToBinaural" );
headRotEnabled = mcInput->base.ctx.pHeadRotData->headRotEnabled;
inConfig = mcInput->base.inConfig;
if ( ( inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM ) ||
( headRotEnabled && ( inConfig == IVAS_REND_AUDIO_CONFIG_5_1 || inConfig == IVAS_REND_AUDIO_CONFIG_7_1 ) ) )
{
copyBufferTo2dArray( mcInput->base.inputBuffer, tmpRendBuffer );
if ( ( error = ivas_rend_TDObjRenderFrame( &mcInput->tdRendWrapper,
mcInput->base.inConfig,
&mcInput->customLsInput,
mcInput->base.ctx.pHeadRotData,
NULL,
mcInput->base.inputBuffer.config.numSamplesPerChannel,
*mcInput->base.ctx.pOutSampleRate,
tmpRendBuffer ) ) != IVAS_ERR_OK )
{
return error;
}
}
else
{
/* apply rotation */
if ( headRotEnabled )
{
tmpRotBuffer = mcInput->base.inputBuffer;
tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels );
rotateFrameMc( mcInput->base.inputBuffer,
mcInput->base.inConfig,
mcInput->customLsInput,
mcInput->base.ctx.pHeadRotData,
mcInput->rot_gains_prev,
mcInput->efapInWrapper.hEfap,
tmpRotBuffer );
copyBufferTo2dArray( tmpRotBuffer, tmpRendBuffer );
count_free( tmpRotBuffer.data );
}
else
{
copyBufferTo2dArray( mcInput->base.inputBuffer, tmpRendBuffer );
}
/* call CREND */
if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, mcInput->base.inConfig, outConfig, tmpRendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
}
accumulate2dArrayToBuffer( tmpRendBuffer, &outAudio );
/* TODO tmu : needs delay compensation */
renderLfeToBinaural( mcInput, outAudio );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderMcToBinauralRoom(
input_mc *mcInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
ivas_error error;
IVAS_REND_AudioBuffer tmpRotBuffer;
wmops_sub_start( "renderMcToBinauralRoom" );
/* apply rotation */
if ( mcInput->base.ctx.pHeadRotData->headRotEnabled )
{
tmpRotBuffer = mcInput->base.inputBuffer;
tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels );
rotateFrameMc( mcInput->base.inputBuffer,
mcInput->base.inConfig,
mcInput->customLsInput,
mcInput->base.ctx.pHeadRotData,
mcInput->rot_gains_prev,
mcInput->efapInWrapper.hEfap,
tmpRotBuffer );
copyBufferTo2dArray( tmpRotBuffer, tmpCrendBuffer );
count_free( tmpRotBuffer.data );
}
else
{
copyBufferTo2dArray( mcInput->base.inputBuffer, tmpCrendBuffer );
}
/* call CREND */
if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, mcInput->base.inConfig, outConfig, tmpCrendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio );
/* TODO tmu : needs delay compensation */
renderLfeToBinaural( mcInput, outAudio );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderMcCustomLsToBinauralRoom(
input_mc *mcInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
int8_t headRotEnabled;
int16_t i;
int16_t tmp;
float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
ivas_error error;
IVAS_REND_AudioBuffer tmpRotBuffer;
IVAS_REND_AudioBuffer tmpMcBuffer;
IVAS_REND_AudioBuffer *tmpBufPtr;
wmops_sub_start( "renderMcCustomLsToBinauralRoom" );
tmpRotBuffer = outAudio; /* avoid compilation warning */
headRotEnabled = mcInput->base.ctx.pHeadRotData->headRotEnabled;
/* apply rotation */
if ( headRotEnabled )
{
tmpRotBuffer = mcInput->base.inputBuffer;
tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels );
rotateFrameMc( mcInput->base.inputBuffer,
mcInput->base.inConfig,
mcInput->customLsInput,
mcInput->base.ctx.pHeadRotData,
mcInput->rot_gains_prev,
mcInput->efapInWrapper.hEfap,
tmpRotBuffer );
}
/* intermediate conversion to 7_1_4 */
tmpMcBuffer = mcInput->base.inputBuffer;
getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp );
tmpMcBuffer.config.numChannels = (int16_t) tmp;
tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels );
tmpBufPtr = ( headRotEnabled ) ? &tmpRotBuffer : &mcInput->base.inputBuffer;
for ( i = 0; i < mcInput->base.inputBuffer.config.numChannels; i++ )
{
renderBufferChannel( *tmpBufPtr, i, mcInput->panGains[i], tmpMcBuffer );
}
copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer );
/* call CREND */
if ( ( error = ivas_rend_crendProcess( &mcInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, tmpCrendBuffer, *mcInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio );
/* TODO tmu : needs delay compensation */
renderLfeToBinaural( mcInput, outAudio );
if ( headRotEnabled )
{
count_free( tmpRotBuffer.data );
}
count_free( tmpMcBuffer.data );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderMcToMc(
const input_mc *mcInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
IVAS_REND_AudioBuffer inAudio;
wmops_sub_start( "renderMcToMc" );
inAudio = mcInput->base.inputBuffer;
for ( i = 0; i < inAudio.config.numChannels; ++i )
{
renderBufferChannel( inAudio, i, mcInput->panGains[i], outAudio );
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderMcToSba(
const input_mc *mcInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
IVAS_REND_AudioBuffer inAudio;
wmops_sub_start( "renderMcToSba" );
inAudio = mcInput->base.inputBuffer;
for ( i = 0; i < inAudio.config.numChannels; ++i )
{
renderBufferChannel( inAudio, i, mcInput->panGains[i], outAudio );
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderInputMc(
input_mc *mcInput,
IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
ivas_error error;
IVAS_REND_AudioBuffer inAudio;
inAudio = mcInput->base.inputBuffer;
if ( mcInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel )
{
/* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
mcInput->base.numNewSamplesPerChannel = 0;
/* Apply input gain to new audio */
v_multc( inAudio.data,
mcInput->base.gain,
inAudio.data,
inAudio.config.numSamplesPerChannel * inAudio.config.numChannels );
switch ( getAudioConfigType( outConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
error = renderMcToMc( mcInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
error = renderMcToSba( mcInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
error = renderMcToBinaural( mcInput, outConfig, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
if ( mcInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
{
error = renderMcCustomLsToBinauralRoom( mcInput, outConfig, outAudio );
}
else
{
error = renderMcToBinauralRoom( mcInput, outConfig, outAudio );
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
/* Check error here to keep switch statement more compact */
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static ivas_error renderActiveInputsMc(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
input_mc *pCurrentInput;
ivas_error error;
for ( i = 0, pCurrentInput = hIvasRend->inputsMc; i < RENDERER_MAX_MC_INPUTS; ++i, ++pCurrentInput )
{
if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
/* Skip inactive inputs */
continue;
}
if ( ( error = renderInputMc( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
static ivas_error renderSbaToMc(
const input_sba *sbaInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
IVAS_REND_AudioBuffer inAudio;
wmops_sub_start( "renderSbaToMc" );
inAudio = sbaInput->base.inputBuffer;
for ( i = 0; i < inAudio.config.numChannels; ++i )
{
renderBufferChannel( inAudio, i, sbaInput->hoaDecMtx[i], outAudio );
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderSbaToSba(
const input_sba *sbaInput,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
IVAS_REND_AudioBuffer inAudio;
wmops_sub_start( "renderSbaToSba" );
inAudio = sbaInput->base.inputBuffer;
for ( i = 0; i < inAudio.config.numChannels; ++i )
{
renderBufferChannel( inAudio, i, sbaInput->hoaDecMtx[i], outAudio );
}
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderSbaToBinaural(
input_sba *sbaInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
ivas_error error;
IVAS_REND_AudioBuffer tmpRotBuffer;
wmops_sub_start( "renderSbaToBinaural" );
/* apply rotation */
if ( sbaInput->base.ctx.pHeadRotData->headRotEnabled )
{
tmpRotBuffer = sbaInput->base.inputBuffer;
tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) );
/* copy input for in-place rotation */
mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data,
tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel );
rotateFrameSba( sbaInput->base.inputBuffer,
sbaInput->base.inConfig,
sbaInput->base.ctx.pHeadRotData,
sbaInput->rot_gains_prev,
tmpRotBuffer );
copyBufferTo2dArray( tmpRotBuffer, tmpCrendBuffer );
count_free( tmpRotBuffer.data );
}
else
{
copyBufferTo2dArray( sbaInput->base.inputBuffer, tmpCrendBuffer );
}
/* call CREND */
if ( ( error = ivas_rend_crendProcess( &sbaInput->crendWrapper, sbaInput->base.inConfig, outConfig, tmpCrendBuffer, *sbaInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderSbaToBinauralRoom(
input_sba *sbaInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
int8_t headRotEnabled;
int16_t i;
int16_t tmp;
float tmpCrendBuffer[MAX_OUTPUT_CHANNELS][L_FRAME48k];
ivas_error error;
IVAS_REND_AudioBuffer tmpRotBuffer;
IVAS_REND_AudioBuffer tmpMcBuffer;
IVAS_REND_AudioBuffer *tmpBufPtr;
tmpRotBuffer = outAudio; /* avoid compilation warning */
wmops_sub_start( "renderSbaToBinauralRoom" );
headRotEnabled = sbaInput->base.ctx.pHeadRotData->headRotEnabled;
/* apply rotation */
if ( headRotEnabled )
{
tmpRotBuffer = sbaInput->base.inputBuffer;
tmpRotBuffer.data = count_malloc( tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) );
/* copy input for in-place rotation */
mvr2r( sbaInput->base.inputBuffer.data, tmpRotBuffer.data, tmpRotBuffer.config.numChannels * tmpRotBuffer.config.numSamplesPerChannel );
rotateFrameSba( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, sbaInput->rot_gains_prev, tmpRotBuffer );
}
/* intermediate rendering to 7_1_4 */
tmpMcBuffer = sbaInput->base.inputBuffer;
getAudioConfigNumChannels( IVAS_REND_AUDIO_CONFIG_7_1_4, &tmp );
tmpMcBuffer.config.numChannels = (int16_t) tmp;
tmpMcBuffer.data = count_malloc( tmpMcBuffer.config.numSamplesPerChannel * tmpMcBuffer.config.numChannels * sizeof( float ) );
set_zero( tmpMcBuffer.data, tmpMcBuffer.config.numChannels * tmpMcBuffer.config.numSamplesPerChannel );
tmpBufPtr = ( headRotEnabled ) ? &tmpRotBuffer : &sbaInput->base.inputBuffer;
for ( i = 0; i < sbaInput->base.inputBuffer.config.numChannels; i++ )
{
renderBufferChannel( *tmpBufPtr, i, sbaInput->hoaDecMtx[i], tmpMcBuffer );
}
copyBufferTo2dArray( tmpMcBuffer, tmpCrendBuffer );
/* call CREND */
if ( ( error = ivas_rend_crendProcess( &sbaInput->crendWrapper, IVAS_REND_AUDIO_CONFIG_7_1_4, outConfig, tmpCrendBuffer, *sbaInput->base.ctx.pOutSampleRate ) ) != IVAS_ERR_OK )
{
return error;
}
accumulate2dArrayToBuffer( tmpCrendBuffer, &outAudio );
if ( headRotEnabled )
{
count_free( tmpRotBuffer.data );
}
count_free( tmpMcBuffer.data );
wmops_sub_end();
return IVAS_ERR_OK;
}
static ivas_error renderInputSba(
input_sba *sbaInput,
const IVAS_REND_AudioConfig outConfig,
IVAS_REND_AudioBuffer outAudio )
{
ivas_error error;
IVAS_REND_AudioBuffer inAudio;
inAudio = sbaInput->base.inputBuffer;
if ( sbaInput->base.numNewSamplesPerChannel != outAudio.config.numSamplesPerChannel )
{
/* Mismatch between the number of input samples vs number of requested output samples - currently not allowed */
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
sbaInput->base.numNewSamplesPerChannel = 0;
/* Apply input gain to new audio */
v_multc( inAudio.data,
sbaInput->base.gain,
inAudio.data,
inAudio.config.numSamplesPerChannel * inAudio.config.numChannels );
switch ( getAudioConfigType( outConfig ) )
{
case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
error = renderSbaToMc( sbaInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
error = renderSbaToSba( sbaInput, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
switch ( outConfig )
{
case IVAS_REND_AUDIO_CONFIG_BINAURAL:
error = renderSbaToBinaural( sbaInput, outConfig, outAudio );
break;
case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
error = renderSbaToBinauralRoom( sbaInput, outConfig, outAudio );
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
break;
default:
return IVAS_ERR_INVALID_OUTPUT_FORMAT;
}
/* Check error here to keep switch statement more compact */
if ( error != IVAS_ERR_OK )
{
return error;
}
return IVAS_ERR_OK;
}
static ivas_error renderActiveInputsSba(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_AudioBuffer outAudio )
{
int16_t i;
input_sba *pCurrentInput;
ivas_error error;
for ( i = 0, pCurrentInput = hIvasRend->inputsSba; i < RENDERER_MAX_SBA_INPUTS; ++i, ++pCurrentInput )
{
if ( pCurrentInput->base.inConfig == IVAS_REND_AUDIO_CONFIG_UNKNOWN )
{
/* Skip inactive inputs */
continue;
}
if ( ( error = renderInputSba( pCurrentInput, hIvasRend->outputConfig, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
}
return IVAS_ERR_OK;
}
ivas_error IVAS_REND_GetSamples(
IVAS_REND_HANDLE hIvasRend,
IVAS_REND_AudioBuffer outAudio )
{
ivas_error error;
int16_t numOutChannels;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( hIvasRend == NULL || outAudio.data == NULL )
{
return IVAS_ERR_UNEXPECTED_NULL_POINTER;
}
if ( outAudio.config.numSamplesPerChannel <= 0 || MAX_BUFFER_LENGTH_PER_CHANNEL < outAudio.config.numSamplesPerChannel )
{
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
if ( outAudio.config.numChannels <= 0 || MAX_OUTPUT_CHANNELS < outAudio.config.numChannels )
{
return IVAS_ERR_WRONG_NUM_CHANNELS;
}
if ( getAudioConfigType( hIvasRend->outputConfig ) == IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL &&
outAudio.config.numSamplesPerChannel * 1000 != BINAURAL_RENDERING_FRAME_SIZE_MS * hIvasRend->sampleRateOut )
{
/* Binaural rendering requires specific frame size */
return IVAS_ERR_INVALID_BUFFER_SIZE;
}
if ( ( error = IVAS_REND_NumOutChannels( hIvasRend, &numOutChannels ) ) != IVAS_ERR_OK )
{
return error;
}
if ( numOutChannels != outAudio.config.numChannels )
{
return IVAS_ERR_WRONG_NUM_CHANNELS;
}
/* Clear output buffer */
set_zero( outAudio.data, outAudio.config.numChannels * outAudio.config.numSamplesPerChannel );
if ( ( error = renderActiveInputsIsm( hIvasRend, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = renderActiveInputsMc( hIvasRend, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
if ( ( error = renderActiveInputsSba( hIvasRend, outAudio ) ) != IVAS_ERR_OK )
{
return error;
}
#ifdef DEBUGGING
hIvasRend->numClipping +=
#endif
limitRendererOutput( hIvasRend->hLimiter, outAudio.data, outAudio.config.numSamplesPerChannel, IVAS_LIMITER_THRESHOLD );
return IVAS_ERR_OK;
}
void IVAS_REND_Close(
IVAS_REND_HANDLE *phIvasRend )
{
uint16_t i;
IVAS_REND_HANDLE hIvasRend;
/*-----------------------------------------------------------------*
* Validate function arguments
*-----------------------------------------------------------------*/
if ( phIvasRend == NULL || *phIvasRend == NULL )
{
return;
}
hIvasRend = *phIvasRend;
if ( hIvasRend->efapOutWrapper.hEfap != NULL )
{
efap_free_data( &hIvasRend->efapOutWrapper.hEfap );
}
/* clear inputs */
for ( i = 0; i < RENDERER_MAX_ISM_INPUTS; ++i )
{
clearInputIsm( &hIvasRend->inputsIsm[i] );
}
for ( i = 0; i < RENDERER_MAX_MC_INPUTS; ++i )
{
clearInputMc( &hIvasRend->inputsMc[i] );
}
for ( i = 0; i < RENDERER_MAX_SBA_INPUTS; ++i )
{
clearInputSba( &hIvasRend->inputsSba[i] );
}
/* clear Config. Renderer */
ivas_render_config_close( &( hIvasRend->hRendererConfig ) );
ivas_limiter_close( &hIvasRend->hLimiter );
count_free( hIvasRend );
*phIvasRend = NULL;
return;
}
#ifdef DEBUGGING
int32_t IVAS_REND_GetNoCLipping(
IVAS_REND_CONST_HANDLE hIvasRend )
{
return hIvasRend->numClipping;
}
int32_t IVAS_REND_GetCntFramesLimited(
IVAS_REND_CONST_HANDLE hIvasRend )
{
if ( hIvasRend->hLimiter == NULL )
{
return 0;
}
return hIvasRend->hLimiter->cnt_frames_limited;
}
#endif
#endif
/******************************************************************************************************
(C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository. All Rights Reserved.
This software is protected by copyright law and by international treaties.
The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository retain full ownership rights in their respective contributions in
the software. This notice grants no license of any kind, including but not limited to patent
license, nor is any license granted by implication, estoppel or otherwise.
Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
contributions.
This software is provided "AS IS", without any express or implied warranties. The software is in the
development stage. It is intended exclusively for experts who have experience with such software and
solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
and fitness for a particular purpose are hereby disclaimed and excluded.
Any dispute, controversy or claim arising under or in relation to providing this software shall be
submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
the United Nations Convention on Contracts on the International Sales of Goods.
*******************************************************************************************************/
#ifndef LIB_REND_H
#define LIB_REND_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "options.h"
#include "common_api_types.h"
#include "ivas_error.h"
#ifdef EXT_RENDERER
#define RENDERER_MAX_ISM_INPUTS 4
#define RENDERER_MAX_MC_INPUTS 1
#define RENDERER_MAX_SBA_INPUTS 1
#define RENDERER_MAX_MASA_INPUTS 1
#define RENDERER_HEAD_POSITIONS_PER_FRAME 4
typedef struct
{
int16_t numSamplesPerChannel;
int16_t numChannels;
} IVAS_REND_AudioBufferConfig;
typedef struct
{
IVAS_REND_AudioBufferConfig config;
float *data;
} IVAS_REND_AudioBuffer;
typedef struct
{
IVAS_REND_AudioBufferConfig config;
const float *data;
} IVAS_REND_ReadOnlyAudioBuffer;
typedef struct
{
float azimuth;
float elevation;
} IVAS_REND_AudioObjectPosition;
typedef struct IVAS_REND *IVAS_REND_HANDLE;
typedef struct IVAS_REND const *IVAS_REND_CONST_HANDLE;
typedef enum
{
IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED = 0,
IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS,
IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED,
IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL,
IVAS_REND_AUDIO_CONFIG_TYPE_MASA,
IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN,
} IVAS_REND_AudioConfigType;
/* TODO(sgi): Harmonize with AUDIO_CONFIG */
/*
Note: numerical values carry specific information here.
MSB LSB
--------------------------------------------------------------------------------
... unused (assumed all 0) ... | config type (1 byte) | config variant (1 byte) |
--------------------------------------------------------------------------------
Where "config type" is the general type from the following list:
- unknown
- channel-based
- ambisonics
- object-based
- binaural
- MASA
Config variants are concrete configs of each type.
*/
typedef enum
{
IVAS_REND_AUDIO_CONFIG_MONO = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 0,
IVAS_REND_AUDIO_CONFIG_STEREO = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 1,
IVAS_REND_AUDIO_CONFIG_5_1 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 2,
IVAS_REND_AUDIO_CONFIG_7_1 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 3,
IVAS_REND_AUDIO_CONFIG_5_1_2 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 4,
IVAS_REND_AUDIO_CONFIG_5_1_4 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 5,
IVAS_REND_AUDIO_CONFIG_7_1_4 = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 6,
IVAS_REND_AUDIO_CONFIG_LS_CUSTOM = IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED << 8 | 255,
IVAS_REND_AUDIO_CONFIG_FOA = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 0,
IVAS_REND_AUDIO_CONFIG_HOA2 = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 1,
IVAS_REND_AUDIO_CONFIG_HOA3 = IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS << 8 | 2,
IVAS_REND_AUDIO_CONFIG_OBJECT = IVAS_REND_AUDIO_CONFIG_TYPE_OBJECT_BASED << 8 | 0,
IVAS_REND_AUDIO_CONFIG_BINAURAL = IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL << 8 | 0,
IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM = IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL << 8 | 1,
IVAS_REND_AUDIO_CONFIG_MASA1 = IVAS_REND_AUDIO_CONFIG_TYPE_MASA << 8 | 0,
IVAS_REND_AUDIO_CONFIG_MASA2 = IVAS_REND_AUDIO_CONFIG_TYPE_MASA << 8 | 1,
IVAS_REND_AUDIO_CONFIG_UNKNOWN = IVAS_REND_AUDIO_CONFIG_TYPE_UNKNOWN << 8 | 0,
} IVAS_REND_AudioConfig;
typedef uint16_t IVAS_REND_InputId;
typedef struct
{
int16_t numLfeChannels;
float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS];
} IVAS_REND_LfeRouting;
/* clang-format off */
/*----------------------------------------------------------------------------------*
* Function prototypes
*----------------------------------------------------------------------------------*/
/* Functions to be called before rendering */
ivas_error IVAS_REND_Open(
IVAS_REND_HANDLE *phIvasRend, /* i/o: Pointer to renderer handle */
const int32_t outputSampleRate, /* i : output sampling rate */
const IVAS_REND_AudioConfig outConfig /* i : output audio config */
);
/* Note: this will reset custom LFE routings set for any MC input */
ivas_error IVAS_REND_ConfigureCustomOutputLoudspeakerLayout(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_CUSTOM_LS_DATA layout /* i : custom loudspeaker layout for renderer output */
);
/* Support for custom HRTFs will be added in the future. */
/* Note: this affects output delay */
ivas_error IVAS_REND_SetCustomHrtf(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
void* TODO
);
/* Functions to be called before/during rendering */
ivas_error IVAS_REND_NumOutChannels(
IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */
int16_t *numOutChannels /* o : number of output channels */
);
ivas_error IVAS_REND_AddInput(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_AudioConfig inConfig, /* i : audio config for a new input */
IVAS_REND_InputId *inputId /* o : ID of the new input */
);
/* Note: this will reset any custom LFE routing set for the input */
ivas_error IVAS_REND_ConfigureCustomInputLoudspeakerLayout(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
const IVAS_CUSTOM_LS_DATA layout /* i : custom loudspeaker layout for input */
);
ivas_error IVAS_REND_SetInputGain(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
const float gain /* i : linear gain (not in dB) */
);
ivas_error IVAS_REND_SetInputLfeRouting(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
const IVAS_REND_LfeRouting lfeRouting /* i : custom LFE routing struct */
);
ivas_error IVAS_REND_RemoveInput(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId /* i : ID of the input */
);
ivas_error IVAS_REND_GetInputNumChannels(
IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
int16_t *numChannels /* o : number of channels of the input */
);
ivas_error IVAS_REND_GetDelay(
IVAS_REND_CONST_HANDLE hIvasRend, /* i : Renderer handle */
int16_t *nSamples, /* o : Renderer delay in samples */
int32_t *timeScale /* o : Time scale of the delay, equal to renderer output sampling rate */
);
/* Functions to be called during rendering */
ivas_error IVAS_REND_FeedInputAudio(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
const IVAS_REND_ReadOnlyAudioBuffer inputAudio /* i : buffer with input audio */
);
ivas_error IVAS_REND_FeedInputObjectMetadata(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
const IVAS_REND_AudioObjectPosition objectPosition /* i : object position struct */
);
/* Support for MASA input will be added in the future. */
ivas_error IVAS_REND_FeedInputMasaMetadata(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_REND_InputId inputId, /* i : ID of the input */
void* TODO
);
ivas_error IVAS_REND_InitConfig(
IVAS_REND_HANDLE st, /* i/o: Renderer handle */
bool rendererConfigEnabled /* i : flag indicating if a renderer configuration file was supplied */
);
int16_t IVAS_REND_GetRenderConfig(
IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */
const IVAS_RENDER_CONFIG_HANDLE hRCout /* o : Render configuration handle */
);
int16_t IVAS_REND_FeedRenderConfig(
IVAS_REND_HANDLE hIvasRend, /* i/o: IVAS decoder handle */
const IVAS_RENDER_CONFIG_DATA renderConfig /* i : Render configuration struct */
);
ivas_error IVAS_REND_SetHeadRotation(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
const IVAS_QUATERNION headRot[RENDERER_HEAD_POSITIONS_PER_FRAME] /* i : head positions for next rendering call */
);
ivas_error IVAS_REND_GetSamples(
IVAS_REND_HANDLE hIvasRend, /* i/o: Renderer handle */
IVAS_REND_AudioBuffer outAudio /* i/o: buffer for output audio */
);
/* Functions to be called after rendering */
void IVAS_REND_Close(
IVAS_REND_HANDLE* phIvasRend /* i/o: Pointer to renderer handle */
);
#ifdef DEBUGGING
int32_t IVAS_REND_GetNoCLipping(
IVAS_REND_CONST_HANDLE hIvasRend /* i : Renderer handle */
);
int32_t IVAS_REND_GetCntFramesLimited(
IVAS_REND_CONST_HANDLE hIvasRend /* i : Renderer handle */
);
#endif
/* clang-format on */
#endif /* LIB_REND_H */
#endif
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "audio_file_reader.h" #include "audio_file_reader.h"
#include "tinywavein_c.h" #include "tinywavein_c.h"
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "wmops.h" #include "wmops.h"
...@@ -54,12 +55,13 @@ static int8_t AudioFileReader_open_raw( ...@@ -54,12 +55,13 @@ static int8_t AudioFileReader_open_raw(
static int8_t AudioFileReader_open_wav( static int8_t AudioFileReader_open_wav(
AudioFileReader *self, AudioFileReader *self,
const char *fileName, const char *fileName,
uint32_t expSampleRate ) int32_t *sampleRate )
{ {
uint32_t sampleRate, samplesInFile; uint32_t sampleRate_, samplesInFile;
int16_t bps; int16_t bps;
self->wavFile = OpenWav( fileName, &sampleRate, &self->numChannels, &samplesInFile, &bps ); self->wavFile = OpenWav( fileName, &sampleRate_, &self->numChannels, &samplesInFile, &bps );
*sampleRate = sampleRate_;
if ( !self->wavFile ) if ( !self->wavFile )
{ {
...@@ -67,12 +69,6 @@ static int8_t AudioFileReader_open_wav( ...@@ -67,12 +69,6 @@ static int8_t AudioFileReader_open_wav(
return -1; return -1;
} }
if ( sampleRate != expSampleRate )
{
fprintf( stderr, "Input wav file has unexpected samplerate (should be %d): %s\n", expSampleRate, fileName );
return -1;
}
return 0; return 0;
} }
...@@ -81,7 +77,7 @@ static int8_t AudioFileReader_open_wav( ...@@ -81,7 +77,7 @@ static int8_t AudioFileReader_open_wav(
ivas_error AudioFileReader_open( ivas_error AudioFileReader_open(
AudioFileReader **audioReader, /* o : AudioFileReader handle */ AudioFileReader **audioReader, /* o : AudioFileReader handle */
const char *fileName, /* i : path to wav/raw pcm file */ const char *fileName, /* i : path to wav/raw pcm file */
uint32_t expSampleRate /* i : expected sample rate */ int32_t *sampleRate /* o : sample rate of wav file, unused with pcm */
) )
{ {
AudioFileReader *self; AudioFileReader *self;
...@@ -108,7 +104,7 @@ ivas_error AudioFileReader_open( ...@@ -108,7 +104,7 @@ ivas_error AudioFileReader_open(
if ( fileNameLen > wavSuffixLen && strncmp( fileName + fileNameLen - wavSuffixLen, wavSuffix, wavSuffixLen ) == 0 ) if ( fileNameLen > wavSuffixLen && strncmp( fileName + fileNameLen - wavSuffixLen, wavSuffix, wavSuffixLen ) == 0 )
{ {
retCode = AudioFileReader_open_wav( self, fileName, expSampleRate ); retCode = AudioFileReader_open_wav( self, fileName, sampleRate );
} }
else else
{ {
......