diff --git a/apps/encoder.c b/apps/encoder.c index b00f4f75fdc85f3dc72e6ad1a0a9568e70e2a60d..9c3ca51b14a3515c63c4e45d920ae00437605ca4 100644 --- a/apps/encoder.c +++ b/apps/encoder.c @@ -38,6 +38,7 @@ #include "ism_file_reader.h" #include "jbm_file_reader.h" #include "masa_file_reader.h" +#include "rotation_file_reader.h" #ifdef DEBUGGING #include "debug.h" #endif @@ -146,6 +147,7 @@ typedef struct #endif bool pca; bool ism_extended_metadata; + char *headRotationFilePath; } EncArguments; @@ -190,6 +192,7 @@ int main( MasaFileReader *masaReader = NULL; IsmFileReader *ismReaders[IVAS_MAX_NUM_OBJECTS] = { NULL, NULL, NULL, NULL }; int16_t *pcmBuf = NULL; + RotFileReader *headRotReader = NULL; #ifdef DEBUGGING FILE *f_forcedModeProfile = NULL; #ifdef DEBUG_SBA @@ -339,6 +342,17 @@ int main( } } + if ( ( arg.headRotationFilePath != NULL ) && ( !isEmptyString( arg.headRotationFilePath ) ) ) + { + convert_backslash( arg.headRotationFilePath ); + + if ( RotationFileReader_open( arg.headRotationFilePath, &headRotReader ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error opening file: %s\n", arg.headRotationFilePath ); + exit( -1 ); + } + } + /*------------------------------------------------------------------------------------------* * Handle Channel-aware mode *------------------------------------------------------------------------------------------*/ @@ -756,6 +770,25 @@ int main( } } + /* Read from head rotation trajectory file if specified */ + if ( headRotReader != NULL ) + { + IVAS_QUATERNION headRot; + IVAS_VECTOR3 Pos; + + if ( ( error = HeadRotationFileReading( headRotReader, &headRot, &Pos ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error in Head Rotation File Reading: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + + if ( ( error = IVAS_ENC_SetHeadRotation( hIvasEnc, headRot, Pos ) ) != IVAS_ERR_OK ) + { + fprintf( stderr, "Error setting Head Rotation: %s\n", ivas_error_to_string( error ) ); + exit( -1 ); + } + } + /* *** Encode one frame *** */ if ( ( error = IVAS_ENC_EncodeFrameToSerial( hIvasEnc, pcmBuf, pcmBufSize, bitStream, &numBits ) ) != IVAS_ERR_OK ) { @@ -840,6 +873,8 @@ cleanup: fclose( f_bitrateProfile ); } + RotationFileReader_close( &headRotReader ); + IVAS_ENC_Close( &hIvasEnc ); #ifdef WMOPS @@ -895,6 +930,7 @@ static void initArgStruct( EncArguments *arg ) arg->mimeOutput = false; arg->ism_extended_metadata = false; arg->complexityLevel = IVAS_ENC_COMPLEXITY_LEVEL_THREE; + arg->headRotationFilePath = NULL; #ifdef DEBUGGING arg->forcedMode = IVAS_ENC_FORCE_UNFORCED; @@ -1695,6 +1731,13 @@ static bool parseCmdlIVAS_enc( arg->pca = 1; i++; } + else if ( strcmp( argv_to_upper, "-TRAJECTORY_FILE" ) == 0 ) + { + fprintf( stderr, "Error: Head Roration supplied\n\n" ); + i++; + arg->headRotationFilePath = argv[i]; + i++; + } /*-----------------------------------------------------------------* * Option not recognized @@ -1913,6 +1956,7 @@ static void usage_enc( void ) #endif fprintf( stdout, "-q : Quiet mode, no frame counters\n" ); fprintf( stdout, " default is deactivated\n" ); + fprintf( stdout, "-trajectory_file F : Head rotation trajectory file for simulation of head tracking (only for SBA inputs)\n" ); fprintf( stdout, "\n" ); return; diff --git a/lib_enc/ivas_enc.c b/lib_enc/ivas_enc.c index 5ddcf9a506727f7d37ba32b00a4c56c0dd393e91..f1df0cdad3cbeeca54e7912d6d8f992f62a488e4 100644 --- a/lib_enc/ivas_enc.c +++ b/lib_enc/ivas_enc.c @@ -31,6 +31,7 @@ *******************************************************************************************************/ #include +#include #include "options.h" #include "cnst.h" #include "ivas_cnst.h" @@ -43,6 +44,347 @@ #endif #include "wmc_auto.h" +typedef struct +{ + int16_t numSamplesPerChannel; + int16_t numChannels; +} IVAS_ENC_AudioBufferConfig; + +typedef struct +{ + IVAS_ENC_AudioBufferConfig config; + float *data; +} IVAS_ENC_AudioBuffer; + +static float *getSmplPtr( + IVAS_ENC_AudioBuffer buffer, + const uint32_t chnlIdx, + const uint32_t smplIdx ) +{ + return buffer.data + chnlIdx * buffer.config.numSamplesPerChannel + smplIdx; +} + +/*------------------------------------------------------------------------- + * Helper functions used by SHrotmatgen, + * an implementation of the algorithm in + * Ivanic, J. & Ruedenberg, K., J. Phys. Chem. 100, 6342 (1996) + *------------------------------------------------------------------------*/ + +static float SHrot_p( + const int16_t i, + const int16_t l, + const int16_t a, + const int16_t b, + float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], + float R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] ) +{ + float ri1 = 0.0f, rim1 = 0.0f, ri0 = 0.0f, p = 0.0f, R_lm1_1 = 0.0f, R_lm1_2 = 0.0f; + + ri1 = SHrotmat[i + 1 + 1][1 + 1 + 1]; + rim1 = SHrotmat[i + 1 + 1][-1 + 1 + 1]; + ri0 = SHrotmat[i + 1 + 1][0 + 1 + 1]; + + if ( b == -l ) + { + R_lm1_1 = R_lm1[a + l - 1][0]; + R_lm1_2 = R_lm1[a + l - 1][2 * l - 2]; + p = ri1 * R_lm1_1 + rim1 * R_lm1_2; + } + else + { + if ( b == l ) + { + R_lm1_1 = R_lm1[a + l - 1][2 * l - 2]; + R_lm1_2 = R_lm1[a + l - 1][0]; + p = ri1 * R_lm1_1 - rim1 * R_lm1_2; + } + else + { + R_lm1_1 = R_lm1[a + l - 1][b + l - 1]; + p = ri0 * R_lm1_1; + } + } + + return p; +} + +static float SHrot_u( + const int16_t l, + const int16_t m, + const int16_t n, + float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], + float R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] ) +{ + return SHrot_p( 0, l, m, n, SHrotmat, R_lm1 ); +} + +static float SHrot_v( + const int16_t l, + const int16_t m, + const int16_t n, + float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], + float R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] ) +{ + + float result, d, p0, p1; + + if ( m == 0 ) + { + p0 = SHrot_p( 1, l, 1, n, SHrotmat, R_lm1 ); + p1 = SHrot_p( -1, l, -1, n, SHrotmat, R_lm1 ); + result = p0 + p1; + } + else + { + if ( m > 0 ) + { + d = ( m == 1 ) ? 1.0f : 0.0f; + p0 = SHrot_p( 1, l, m - 1, n, SHrotmat, R_lm1 ); + p1 = SHrot_p( -1, l, -m + 1, n, SHrotmat, R_lm1 ); + result = p0 * sqrtf( 1.0f + d ) - p1 * ( 1.0f - d ); + } + else + { + d = ( m == -1 ) ? 1.0f : 0.0f; + p0 = SHrot_p( 1, l, m + 1, n, SHrotmat, R_lm1 ); + p1 = SHrot_p( -1, l, -m - 1, n, SHrotmat, R_lm1 ); + result = p0 * ( 1.0f - d ) + p1 * sqrtf( 1.0f + d ); + } + } + + return result; +} + +static float SHrot_w( + const int16_t l, + const int16_t m, + const int16_t n, + float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], + float R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM] ) +{ + float result, p0, p1; + + if ( m == 0 ) + { +#ifdef SPLIT_REND_WITH_HEAD_ROT + assert( 0 && "ERROR should not be called\n" ); +#else + printf( "ERROR should not be called\n" ); +#endif + return 0.0f; + } + else + { + if ( m > 0 ) + { + p0 = SHrot_p( 1, l, m + 1, n, SHrotmat, R_lm1 ); + p1 = SHrot_p( -1, l, -m - 1, n, SHrotmat, R_lm1 ); + result = p0 + p1; + } + else + { + p0 = SHrot_p( 1, l, m - 1, n, SHrotmat, R_lm1 ); + p1 = SHrot_p( -1, l, -m + 1, n, SHrotmat, R_lm1 ); + result = p0 - p1; + } + } + + return result; +} + +static void SHrotmatgen( + float SHrotmat[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM], /* o : rotation matrix in SHD */ + float Rmat[3][3], /* i : real-space rotation matrix */ + const int16_t order /* i : ambisonics order */ +) +{ + int16_t d = 0; + int16_t band_idx = 0; + int16_t i, j; + int16_t l, m, n; + int16_t absm; + float sqdenom = 0.0f, sql2mm2 = 0.0f, sqdabsm = 0.0f, sqlabsm = 0.0f; + float u = 0.0f, v = 0.0f, w = 0.0f; + float R_lm1[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; + float R_l[HEADROT_SHMAT_DIM][HEADROT_SHMAT_DIM]; + + SHrotmat[0][0] = 1.0f; + + SHrotmat[1][1] = Rmat[1][1]; + SHrotmat[1][2] = Rmat[1][2]; + SHrotmat[1][3] = Rmat[1][0]; + + SHrotmat[2][1] = Rmat[2][1]; + SHrotmat[2][2] = Rmat[2][2]; + SHrotmat[2][3] = Rmat[2][0]; + + SHrotmat[3][1] = Rmat[0][1]; + SHrotmat[3][2] = Rmat[0][2]; + SHrotmat[3][3] = Rmat[0][0]; + + for ( i = 0; i < 2 * 1 + 1; i++ ) + { + for ( j = 0; j < 2 * 1 + 1; j++ ) + { + R_lm1[i][j] = SHrotmat[i + 1][j + 1]; + } + } + + band_idx = 4; + for ( l = 2; l <= order; l++ ) + { + set_zero( &R_l[0][0], HEADROT_SHMAT_DIM2 ); + + for ( m = -l; m <= l; m++ ) + { + d = ( m == 0 ) ? 1 : 0; + absm = (int16_t) abs( m ); + sql2mm2 = sqrtf( (float) ( l * l - m * m ) ); + sqdabsm = sqrtf( (float) ( ( 1 + d ) * ( l + absm - 1 ) * ( l + absm ) ) ); + sqlabsm = sqrtf( (float) ( ( l - absm - 1 ) * ( l - absm ) ) ); + + for ( n = -l; n <= l; n++ ) + { + if ( abs( n ) == l ) + { + sqdenom = sqrtf( (float) ( ( 2 * l ) * ( 2 * l - 1 ) ) ); + } + else + { + sqdenom = sqrtf( (float) ( l * l - n * n ) ); + } + + u = sql2mm2 / sqdenom; + v = sqdabsm / sqdenom * ( 1 - 2 * d ) * 0.5f; + w = sqlabsm / sqdenom * ( 1 - d ) * ( -0.5f ); + + if ( u != 0 ) + { + u = u * SHrot_u( l, m, n, SHrotmat, R_lm1 ); + } + if ( v != 0 ) + { + v = v * SHrot_v( l, m, n, SHrotmat, R_lm1 ); + } + if ( w != 0 ) + { + w = w * SHrot_w( l, m, n, SHrotmat, R_lm1 ); + } + R_l[m + l][n + l] = u + v + w; + } + } + + for ( i = 0; i < 2 * l + 1; i++ ) + { + for ( j = 0; j < 2 * l + 1; j++ ) + { + SHrotmat[band_idx + i][band_idx + j] = R_l[i][j]; + } + } + + for ( i = 0; i < 2 * l + 1; i++ ) + { + for ( j = 0; j < 2 * l + 1; j++ ) + { + R_lm1[i][j] = R_l[i][j]; + } + } + + band_idx += 2 * l + 1; + } + + return; +} + +static void rotateFrameSba( + IVAS_ENC_AudioBuffer inAudio, /* i : Input Audio buffer */ + int16_t sba_order, /* i : Input Audio config */ + const ENC_HeadRotData *headRotData, /* i : Head rotation data */ + enc_rotation_gains gains_prev, /* i/o: Previous frame rotation gains */ + IVAS_ENC_AudioBuffer outAudio /* o : Output Audio buffer */ +) +{ + int16_t i, l, n, m; + int16_t m1, m2; + int16_t shd_rot_max_order; + const float *crossfade; + + float *writePtr; + enc_rotation_matrix Rmat; + float tmpRot[2 * HEADROT_ORDER + 1]; + enc_rotation_gains gains; + int16_t idx; + float val, cf, oneminuscf; + + push_wmops( "rotateFrameSba" ); + + crossfade = headRotData->crossfade; + + shd_rot_max_order = sba_order; + + /* initialize rotation matrices with zeros */ + for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) + { + set_zero( gains[i], HEADROT_SHMAT_DIM ); + } + + for ( i = 0; i < 3; i++ ) + { + /* Set to identity */ + set_zero( Rmat[i], 3 ); + Rmat[i][i] = 1.0f; + } + + /* calculate ambisonics rotation matrices for the previous and current frames */ + SHrotmatgen( gains, Rmat, shd_rot_max_order ); + + for ( i = 0; i < inAudio.config.numSamplesPerChannel; i++ ) + { + idx = i; + cf = crossfade[i]; + oneminuscf = 1 - cf; + /* 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++ ) + { + val = inAudio.data[m * inAudio.config.numSamplesPerChannel + idx]; + /* crossfade with previous rotation gains */ + tmpRot[n - m1] += ( cf * gains[n][m] * val + oneminuscf * gains_prev[n][m] * val ); + } + } + /* write back the result */ + for ( n = m1; n < m2; n++ ) + { + writePtr = getSmplPtr( outAudio, n, idx ); + ( *writePtr ) = tmpRot[n - m1]; + } + m1 = m2; + m2 += 2 * ( l + 1 ) + 1; + } + } + + /* move SHrotmat to SHrotmat_prev */ + for ( i = 0; i < HEADROT_SHMAT_DIM; i++ ) + { + mvr2r( gains[i], gains_prev[i], HEADROT_SHMAT_DIM ); + } + + pop_wmops(); + + return; +} + /*-------------------------------------------------------------------* * ivas_enc() * @@ -238,6 +580,38 @@ ivas_error ivas_enc( } else if ( ivas_format == SBA_FORMAT ) { + /* Rotate the SBA Scene */ + if ( st_ivas->headRotData.headRotEnabled ) + { + int16_t ch; + IVAS_ENC_AudioBuffer data_in; + data_in.config.numChannels = nchan_inp; + data_in.config.numSamplesPerChannel = n_samples_chan; + data_in.data = malloc( nchan_inp * n_samples_chan * sizeof( float ) ); + + /* Copy data to audio buffer (needs to be cleaned up) */ + for ( ch = 0; ch < nchan_inp; ++ch ) + { + mvr2r( data_f[ch], &data_in.data[ch * n_samples_chan], n_samples_chan ); + } + + IVAS_ENC_AudioBuffer data_out; + data_out.config.numChannels = nchan_inp; + data_out.config.numSamplesPerChannel = n_samples_chan; + data_out.data = malloc( nchan_inp * n_samples_chan * sizeof( float ) ); + + rotateFrameSba( data_in, st_ivas->hEncoderConfig->sba_order, &st_ivas->headRotData, &st_ivas->headRotData.rot_gains_prev[0], data_out ); + + /* Copy data to audio buffer (needs to be cleaned up) */ + for ( ch = 0; ch < nchan_inp; ++ch ) + { + mvr2r( &data_out.data[ch * n_samples_chan], data_f[ch], n_samples_chan ); + } + + free( data_in.data ); + free( data_out.data ); + } + if ( ( error = ivas_spar_enc( st_ivas, data_f, input_frame, nb_bits_metadata, hMetaData ) ) != IVAS_ERR_OK ) { return error; diff --git a/lib_enc/ivas_init_enc.c b/lib_enc/ivas_init_enc.c index 0371378d73e0d2e5e621732fc6128260989115c3..d818f53eef632ffbe67344861bb22cc0d024b3a8 100644 --- a/lib_enc/ivas_init_enc.c +++ b/lib_enc/ivas_init_enc.c @@ -930,6 +930,15 @@ ivas_error ivas_init_encoder( set_f( st_ivas->mem_hp20_in[i], 0.0f, L_HP20_MEM ); } + /*-----------------------------------------------------------------* + * Initialise Head Rotation Data + *-----------------------------------------------------------------*/ + st_ivas->headRotData.headRotEnabled = 0; + for ( i = 0; i < MAX_INPUT_CHANNELS; ++i ) + { + set_f( st_ivas->headRotData.rot_gains_prev[i], 0.0f, MAX_INPUT_CHANNELS ); + } + return error; } diff --git a/lib_enc/ivas_stat_enc.h b/lib_enc/ivas_stat_enc.h index 3962ac055f232389b780a957c227cd5a3bd203be..f5800cf821aff2905178b0dca5d668fcc3205b7b 100644 --- a/lib_enc/ivas_stat_enc.h +++ b/lib_enc/ivas_stat_enc.h @@ -39,6 +39,7 @@ #include "ivas_cnst.h" #include "stat_enc.h" #include "ivas_stat_com.h" +#include "common_api_types.h" /*----------------------------------------------------------------------------------* * DFT Stereo encoder structures @@ -1211,6 +1212,18 @@ typedef struct encoder_config_structure } ENCODER_CONFIG, *ENCODER_CONFIG_HANDLE; +typedef float enc_rotation_gains[MAX_INPUT_CHANNELS][MAX_INPUT_CHANNELS]; +typedef float enc_rotation_matrix[3][3]; + +typedef struct +{ + int8_t headRotEnabled; + IVAS_QUATERNION headPositions; + IVAS_VECTOR3 Pos; + float crossfade[L_FRAME48k]; + enc_rotation_gains rot_gains_prev; + +} ENC_HeadRotData; /*----------------------------------------------------------------------------------* * @@ -1265,6 +1278,8 @@ typedef struct /* Stereo downmix for EVS module */ STEREO_DMX_EVS_ENC_HANDLE hStereoDmxEVS; /* Stereo downmix for EVS encoder handle */ + ENC_HeadRotData headRotData; + } Encoder_Struct; /* clang-format on */ diff --git a/lib_enc/lib_enc.c b/lib_enc/lib_enc.c index 4a4be817345a9cedb37036c02ebdc5a9f0b66992..b667e31448db661eca28ae1d7bd739a6390834ac 100644 --- a/lib_enc/lib_enc.c +++ b/lib_enc/lib_enc.c @@ -468,6 +468,50 @@ ivas_error IVAS_ENC_FeedObjectMetadata( return error; } +/*-------------------------------------------------------------------* + * IVAS_ENC_SetHeadRotation() + * + * + *-------------------------------------------------------------------*/ + +ivas_error IVAS_ENC_SetHeadRotation( + IVAS_ENC_HANDLE hIvasEnc, /* i/o: Renderer handle */ + const IVAS_QUATERNION headRot, /* i : head orientations for next rendering call */ + const IVAS_VECTOR3 Pos /* i : listener positions for next rendering call */ +) +{ + IVAS_QUATERNION rotQuat; + + /* Validate function arguments */ + if ( hIvasEnc == NULL ) + { + return IVAS_ERR_UNEXPECTED_NULL_POINTER; + } + + if ( ( hIvasEnc->st_ivas->hEncoderConfig->ivas_format != SBA_FORMAT ) ) + { + /* Head rotation can be set only with binaural output */ + return IVAS_ERR_INVALID_INPUT_FORMAT; + } + + hIvasEnc->st_ivas->headRotData.headRotEnabled = 1; + + /* check for Euler angle signaling */ + if ( headRot.w == -3.0f ) + { + Euler2Quat( deg2rad( headRot.x ), deg2rad( headRot.y ), deg2rad( headRot.z ), &rotQuat ); + } + else + { + rotQuat = headRot; + } + + hIvasEnc->st_ivas->headRotData.headPositions = rotQuat; + + hIvasEnc->st_ivas->headRotData.Pos = Pos; + + return IVAS_ERR_OK; +} /*---------------------------------------------------------------------* * IVAS_ENC_ConfigureForAmbisonics() diff --git a/lib_enc/lib_enc.h b/lib_enc/lib_enc.h index 2f40c1ab1beda5b883618dbd755b2a68076989f0..abd13c70f06c692ead7f2d3318f138fc88b5c25c 100644 --- a/lib_enc/lib_enc.h +++ b/lib_enc/lib_enc.h @@ -280,6 +280,12 @@ ivas_error IVAS_ENC_FeedObjectMetadata( const IVAS_ISM_METADATA metadata /* i : object metadata handle for current frame */ ); +ivas_error IVAS_ENC_SetHeadRotation( + IVAS_ENC_HANDLE hIvasEnc, /* i/o: Renderer handle */ + const IVAS_QUATERNION headRot, /* i : head orientations for next rendering call */ + const IVAS_VECTOR3 Pos /* i : listener positions for next rendering call */ +); + /*! r: error code */ ivas_error IVAS_ENC_FeedMasaMetadata( IVAS_ENC_HANDLE hIvasEnc, /* i/o: IVAS encoder handle */ diff --git a/lib_rend/lib_rend.c b/lib_rend/lib_rend.c index 1ade3c85f43d471ce4a6d5820a588983a94bc7b5..ed5344dfc98d2bbfe98b03072117d80281d83f1a 100644 --- a/lib_rend/lib_rend.c +++ b/lib_rend/lib_rend.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "wmc_auto.h" @@ -4646,7 +4647,7 @@ ivas_error IVAS_REND_SetHeadRotation( return IVAS_ERR_UNEXPECTED_NULL_POINTER; } - if ( getAudioConfigType( hIvasRend->outputConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL ) + if ( ( getAudioConfigType( hIvasRend->outputConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL ) && ( getAudioConfigType( hIvasRend->outputConfig ) != IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS ) ) { /* Head rotation can be set only with binaural output */ return IVAS_ERR_INVALID_OUTPUT_FORMAT; @@ -6674,23 +6675,61 @@ static void renderSbaToMc( } -static void renderSbaToSba( - const input_sba *sbaInput, +static ivas_error renderSbaToSba( + input_sba *sbaInput, IVAS_REND_AudioBuffer outAudio ) { int16_t i; IVAS_REND_AudioBuffer inAudio; + ivas_error error; + IVAS_REND_AudioBuffer tmpRotBuffer; + const COMBINED_ORIENTATION_HANDLE *hCombinedOrientationData; + int8_t combinedOrientationEnabled; + int16_t subframe_idx; push_wmops( "renderSbaToSba" ); inAudio = sbaInput->base.inputBuffer; + hCombinedOrientationData = sbaInput->base.ctx.pCombinedOrientationData; + combinedOrientationEnabled = 0; + if ( hCombinedOrientationData != NULL ) + { + for ( subframe_idx = 0; subframe_idx < ( *hCombinedOrientationData )->num_subframes; subframe_idx++ ) + { + if ( ( *hCombinedOrientationData )->enableCombinedOrientation[subframe_idx] != 0 ) + { + combinedOrientationEnabled = 1; + break; + } + } + } + + /* apply rotation */ + if ( combinedOrientationEnabled ) + { + tmpRotBuffer = inAudio; + tmpRotBuffer.data = 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 ); + + if ( ( error = rotateFrameSba( sbaInput->base.inputBuffer, sbaInput->base.inConfig, sbaInput->base.ctx.pHeadRotData, + sbaInput->base.ctx.pCombinedOrientationData, sbaInput->rot_gains_prev[0], tmpRotBuffer ) ) != IVAS_ERR_OK ) + { + return error; + } + + memcpy( inAudio.data, tmpRotBuffer.data, tmpRotBuffer.config.numSamplesPerChannel * tmpRotBuffer.config.numChannels * sizeof( float ) ); + free( tmpRotBuffer.data ); + } + for ( i = 0; i < inAudio.config.numChannels; ++i ) { renderBufferChannel( inAudio, i, sbaInput->hoaDecMtx[i], outAudio ); } pop_wmops(); - return; + return IVAS_ERR_OK; } #ifdef SPLIT_REND_WITH_HEAD_ROT @@ -7114,7 +7153,7 @@ static ivas_error renderInputSba( renderSbaToMc( sbaInput, outAudio ); break; case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS: - renderSbaToSba( sbaInput, outAudio ); + error = renderSbaToSba( sbaInput, outAudio ); break; case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL: switch ( outConfig )