Skip to content

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31911.196
# Visual Studio 15
VisualStudioVersion = 15.0.28307.1500
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ivas_crend_unit_test", "ivas_crend_unit_test.vcxproj", "{32354377-ACA7-40F9-9A0E-87FC956F0B78}"
EndProject
......@@ -13,32 +13,51 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_debug", "..\..\..\..\..
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "..\..\..\..\..\Workspace_msvc\lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_rend", "..\..\..\..\..\Workspace_msvc\lib_rend.vcxproj", "{718DE063-A18B-BB72-9150-62B892E6FFA6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
Unittests|Win32 = Unittests|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Debug|Win32.ActiveCfg = Debug|Win32
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Debug|Win32.Build.0 = Debug|Win32
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Release|Win32.ActiveCfg = Release|Win32
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Release|Win32.Build.0 = Release|Win32
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Unittests|Win32.ActiveCfg = Release|Win32
{32354377-ACA7-40F9-9A0E-87FC956F0B78}.Unittests|Win32.Build.0 = Release|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.ActiveCfg = Debug|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.Build.0 = Debug|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.ActiveCfg = Release|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.Build.0 = Release|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Unittests|Win32.ActiveCfg = Unittests|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Unittests|Win32.Build.0 = Unittests|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.ActiveCfg = Debug|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.Build.0 = Debug|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.ActiveCfg = Release|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.Build.0 = Release|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Unittests|Win32.ActiveCfg = Unittests|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Unittests|Win32.Build.0 = Unittests|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.ActiveCfg = Debug|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.Build.0 = Debug|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.ActiveCfg = Release|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.Build.0 = Release|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Unittests|Win32.ActiveCfg = Unittests|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Unittests|Win32.Build.0 = Unittests|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.ActiveCfg = Debug|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.Build.0 = Debug|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.ActiveCfg = Release|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.Build.0 = Release|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Unittests|Win32.ActiveCfg = Unittests|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Unittests|Win32.Build.0 = Unittests|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.ActiveCfg = Debug|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Debug|Win32.Build.0 = Debug|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.ActiveCfg = Release|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Release|Win32.Build.0 = Release|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.ActiveCfg = Unittests|Win32
{718DE063-A18B-BB72-9150-62B892E6FFA6}.Unittests|Win32.Build.0 = Unittests|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......
......@@ -17,6 +17,9 @@
<ProjectReference Include="..\..\..\..\..\Workspace_msvc\lib_dec.vcxproj">
<Project>{e822ddaf-0f5f-4cd0-a694-38ae69de74d3}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\Workspace_msvc\lib_rend.vcxproj">
<Project>{718DE063-A18B-BB72-9150-62B892E6FFA6}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\Workspace_msvc\lib_util.vcxproj">
<Project>{2fa8f384-0775-f3b7-f8c3-85209222fc70}</Project>
</ProjectReference>
......@@ -97,7 +100,7 @@
<FavorSizeOrSpeed>Neither</FavorSizeOrSpeed>
<OmitFramePointers>false</OmitFramePointers>
<EnableFiberSafeOptimizations>false</EnableFiberSafeOptimizations>
<AdditionalIncludeDirectories>..\..\..\..\..\lib_util;..\..\..\..\..\lib_dec;..\..\..\..\..\lib_com;..\..\..\..\..\lib_enc;..\..\..\..\..\lib_debug;..\..\..\..\..\lib_util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\..\..\..\lib_util;..\..\..\..\..\lib_dec;..\..\..\..\..\lib_rend;..\..\..\..\..\lib_com;..\..\..\..\..\lib_enc;..\..\..\..\..\lib_debug;..\..\..\..\..\lib_util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>UNIT_TEST_CREND_TD_BINAURAL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<StringPooling>true</StringPooling>
<ExceptionHandling />
......@@ -143,7 +146,7 @@
</Midl>
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>..\..\..\..\..\lib_util;..\..\..\..\..\lib_dec;..\..\..\..\..\lib_com;..\..\..\..\..\lib_enc;..\..\..\..\..\lib_debug;..\..\..\..\..\lib_util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\..\..\..\lib_util;..\..\..\..\..\lib_dec;..\..\..\..\..\lib_rend;..\..\..\..\..\lib_com;..\..\..\..\..\lib_enc;..\..\..\..\..\lib_debug;..\..\..\..\..\lib_util;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling />
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
......
......@@ -64,7 +64,6 @@
#include "render_config_reader.h"
static ivas_result_t ivas_dec_default_io_params( ivas_dec_io_params_t *pIO_params )
{
memset( pIO_params, 0, sizeof( ivas_dec_io_params_t ) );
......@@ -149,6 +148,8 @@ AUDIO_CONFIG ivas_crend_map_out_fmt(
return AUDIO_CONFIG_7_1;
case MULT_CH_5_1_2:
return AUDIO_CONFIG_5_1_2;
case MULT_CH_5_1_4:
return AUDIO_CONFIG_5_1_4;
case MULT_CH_7_1_4:
return AUDIO_CONFIG_7_1_4;
default:
......@@ -166,6 +167,7 @@ const char *ivas_crend_map_in_fmt(
case MULT_CH_5_1:
case MULT_CH_7_1:
case MULT_CH_5_1_2:
case MULT_CH_5_1_4:
case MULT_CH_7_1_4:
return IVAS_IN_FMT_COMBINED;
case FOA_4:
......@@ -213,6 +215,9 @@ int16_t ivas_get_num_channels(
case MULT_CH_5_1_2:
num_channels = 8;
break;
case MULT_CH_5_1_4:
num_channels = 10;
break;
case MULT_CH_7_1_4:
num_channels = 12;
break;
......@@ -279,9 +284,9 @@ static void ivas_crend_unit_test_usage( void )
fprintf( stdout, "\nOptional arguments:\n" );
fprintf( stdout, "---------------------\n" );
fprintf( stdout, "-ifmt <Input_Format> : Input format index\n" );
fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 7.1.4, 8 - HOA2, 9 - HOA3)\n" );
fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 5.1.4, 8 - 7.1.4, 9 - HOA2, 10 - HOA3)\n" );
fprintf( stdout, "-ofmt <Output_Format> : Output format index\n" );
fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 7.1.4, 8 - HOA2, 9 - HOA3)\n" );
fprintf( stdout, " (0 - MONO, 1 - STEREO, 2 - BINAURAL, 3 - FOA, 4 - 5.1, 5 - 7.1, 6 - 5.1.2, 7 - 5.1.4, 8 - 7.1.4, 9 - HOA2, 10 - HOA3)\n" );
fprintf( stdout, "-i <Input_Path/File> : Input path/file\n" );
fprintf( stdout, "-o <Output_File> : Output file\n" );
fprintf( stdout, "-r <Reference_Path/File> : Reference path/file\n" );
......@@ -446,7 +451,7 @@ void ivas_open_files_crend( ivas_crend_io_params_t *pIo_params )
#ifdef USE_PCM_OUT
if ( ( pIo_params->fIn[0] = fopen( pIo_params->in_path, "rb" ) ) == NULL )
#else
if ( AudioFileReader_open( &pIo_params->fIn[0] , pIo_params->in_path, pIo_params->sample_rate ) != IVAS_ERR_OK )
if ( AudioFileReader_open( &pIo_params->fIn[0], pIo_params->in_path, &pIo_params->sample_rate ) != IVAS_ERR_OK )
#endif
{
fprintf( stderr, "Error: Input audio file %s could not be opened\n\n", pIo_params->in_path );
......@@ -458,7 +463,7 @@ void ivas_open_files_crend( ivas_crend_io_params_t *pIo_params )
#ifdef USE_PCM_OUT
if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( ( pIo_params->fRef = fopen( pIo_params->ref_path, "rb" ) ) == NULL ) )
#else
if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( AudioFileReader_open( &pIo_params->fRef, pIo_params->ref_path, pIo_params->sample_rate ) != IVAS_ERR_OK ) )
if ( ( strlen( pIo_params->ref_path ) > 0 ) && ( AudioFileReader_open( &pIo_params->fRef, pIo_params->ref_path, &pIo_params->sample_rate ) != IVAS_ERR_OK ) )
#endif
{
fprintf( stderr, "Error: Reference audio file %s could not be opened\n\n", pIo_params->ref_path );
......@@ -561,7 +566,7 @@ ivas_result_t ivas_crend_parse_io_params( int argc, char **argv, ivas_crend_io_p
else if ( strcmp( to_upper( argv[i] ), "-IFMT" ) == 0 )
{
pIo_params->in_fmt = atoi( argv[++i] );
if ( ( pIo_params->in_fmt != MONO_1 ) && ( pIo_params->in_fmt != STEREO_2 ) && ( pIo_params->in_fmt != BIN_2 ) && ( pIo_params->in_fmt != FOA_4 ) && ( pIo_params->in_fmt != HOA_9 ) && ( pIo_params->in_fmt != HOA_16 ) && ( pIo_params->in_fmt != MULT_CH_5_1 ) && ( pIo_params->in_fmt != MULT_CH_7_1 ) && ( pIo_params->in_fmt != MULT_CH_5_1_2 ) && ( pIo_params->in_fmt != MULT_CH_7_1_4 ) && ( pIo_params->in_fmt != OBA ) )
if ( ( pIo_params->in_fmt != MONO_1 ) && ( pIo_params->in_fmt != STEREO_2 ) && ( pIo_params->in_fmt != BIN_2 ) && ( pIo_params->in_fmt != FOA_4 ) && ( pIo_params->in_fmt != HOA_9 ) && ( pIo_params->in_fmt != HOA_16 ) && ( pIo_params->in_fmt != MULT_CH_5_1 ) && ( pIo_params->in_fmt != MULT_CH_7_1 ) && ( pIo_params->in_fmt != MULT_CH_5_1_2 ) && ( pIo_params->in_fmt != MULT_CH_5_1_4 ) && ( pIo_params->in_fmt != MULT_CH_7_1_4 ) && ( pIo_params->in_fmt != OBA ) )
{
fprintf( stderr, "Error: Invalid input format\n\n" );
ivas_crend_unit_test_usage();
......@@ -575,7 +580,7 @@ ivas_result_t ivas_crend_parse_io_params( int argc, char **argv, ivas_crend_io_p
else if ( strcmp( to_upper( argv[i] ), "-OFMT" ) == 0 )
{
pIo_params->out_fmt = atoi( argv[++i] );
if ( ( pIo_params->out_fmt != MONO_1 ) && ( pIo_params->out_fmt != STEREO_2 ) && ( pIo_params->out_fmt != BIN_2 ) && ( pIo_params->out_fmt != FOA_4 ) && ( pIo_params->out_fmt != HOA_9 ) && ( pIo_params->out_fmt != HOA_16 ) && ( pIo_params->out_fmt != MULT_CH_5_1 ) && ( pIo_params->out_fmt != MULT_CH_7_1 ) && ( pIo_params->out_fmt != MULT_CH_5_1_2 ) && ( pIo_params->out_fmt != MULT_CH_7_1_4 ) )
if ( ( pIo_params->out_fmt != MONO_1 ) && ( pIo_params->out_fmt != STEREO_2 ) && ( pIo_params->out_fmt != BIN_2 ) && ( pIo_params->out_fmt != FOA_4 ) && ( pIo_params->out_fmt != HOA_9 ) && ( pIo_params->out_fmt != HOA_16 ) && ( pIo_params->out_fmt != MULT_CH_5_1 ) && ( pIo_params->out_fmt != MULT_CH_7_1 ) && ( pIo_params->out_fmt != MULT_CH_5_1_2 ) && ( pIo_params->out_fmt != MULT_CH_5_1_4 ) && ( pIo_params->out_fmt != MULT_CH_7_1_4 ) )
{
fprintf( stderr, "Error: Invalid output format\n\n" );
ivas_crend_unit_test_usage();
......@@ -875,7 +880,7 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params
#endif
{
ppPcm_in[i][j] = (float) tmp;
ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC );
// ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC );
samples_read += 1;
}
else
......@@ -913,7 +918,7 @@ static ivas_result_t ivas_wrapper_get_in_buf( ivas_crend_io_params_t *pIo_params
#endif
{
ppPcm_in[i][j] = (float) tmp;
ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC );
// ppPcm_in[i][j] *= ( 1.0 / PCM16_TO_FLT_FAC );
samples_read += 1;
}
else
......@@ -999,6 +1004,7 @@ static void ivas_copy_io_params_to_dec_io_params( ivas_crend_io_params_t *pIo_pa
case MULT_CH_5_1:
case MULT_CH_7_1:
case MULT_CH_5_1_2:
case MULT_CH_5_1_4:
case MULT_CH_7_1_4:
pDec_io_params->lfe_ch_idx = IVAS_DEFAULT_LFE_CH_IDX;
break;
......@@ -1203,13 +1209,12 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
{
if ( ( error = ivas_headTrack_open( &( st_ivas.hHeadTrackData ) ) ) != IVAS_ERR_OK )
{
return error;
return IVAS_FAILED;
}
if ( ( error = HeadRotationFileReader_open( pIo_params->csv_path, &headRotReader ) ) != IVAS_ERR_OK )
{
return error;
return IVAS_FAILED;
}
}
st_ivas.nchan_transport = audioCfg2channels( st_ivas.transport_config );
......@@ -1322,7 +1327,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
{
if ( ( st_ivas.hLFE->lfe_delay_buf = (float *) count_malloc( st_ivas.hLFE->lfe_addl_delay * sizeof( float ) ) ) == NULL )
{
return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for LFE additional delay buffer\n" ) );
fprintf( stderr, "Can not allocate memory for LFE additional delay buffer\n" );
return IVAS_FAILED;
}
set_zero( (float *) st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay );
}
......@@ -1340,7 +1346,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
{
if ( ( ppDelay_lines[i] = (float *) count_malloc( delay_lp * sizeof( float ) ) ) == NULL )
{
return ( IVAS_ERROR( IVAS_ERR_FAILED_ALLOC, "Can not allocate memory for LFE additional delay buffer\n" ) );
fprintf( stderr, "Can not allocate memory for LFE additional delay buffer\n" );
return IVAS_FAILED;
}
set_zero( (float *) ppDelay_lines[i], delay_lp );
}
......@@ -1398,36 +1405,24 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
return IVAS_IO_ERROR;
}
if ( pIo_params->lfe_lp_enable )
{
int32_t idx = 0;
for ( i = 0; i < in_ch; i++ )
{
if ( i != lfe_ch_idx )
/* ADD delay to make overall max(block_offset, 11.5)*/
if ( st_ivas.hLFE->lfe_addl_delay > 0 )
{
delay_signal( ppPcm_in[idx], frame_len, ppDelay_lines[idx], delay_lp );
idx++;
}
delay_signal( ppPcm_in[lfe_ch_idx], frame_len, st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay );
}
ivas_filter_process( &st_ivas.hLFE->filter_state, ppPcm_in[lfe_ch_idx], frame_len );
}
if ( pIo_params->test == FASTCONV_BIN_TEST )
{
ivas_binaural_cldfb( &st_ivas, ppPcm_in );
for ( i = 0; i < out_ch; i++ )
{
mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len );
}
}
else if ( pIo_params->test == TD_BIN_TEST )
{
ObjRenderIVASFrame( &st_ivas, ppPcm_in, frame_len );
for ( i = 0; i < out_ch; i++ )
{
mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len );
}
}
else if ( pIo_params->test == PARAM_BIN_TEST )
{
......@@ -1482,12 +1477,6 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
cldfbSynthesis( &outSlotRePr, &outSlotImPr, &( ppPcm_in[ch][slot_idx * maxBand] ), maxBand, st_ivas.cldfbSynDec[ch] );
}
}
for ( i = 0; i < out_ch; i++ )
{
mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len );
}
}
else
{
......@@ -1503,29 +1492,23 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
if ( mixer == NULL )
{
if ( st_ivas.hLFE )
{
/* if ( st_ivas.hLFE->filter_state.order > 0 )
if ( pIo_params->lfe_lp_enable )
{
ivas_filter_process( &st_ivas.hLFE->filter_state, ppPcm_in[lfe_ch_idx], frame_len );
}*/
/* ADD delay to make overall max(block_offset, 11.5)*/
if ( st_ivas.hLFE->lfe_addl_delay > 0 )
for ( i = 0; i < out_ch; i++ )
{
delay_signal( ppPcm_in[lfe_ch_idx], frame_len, st_ivas.hLFE->lfe_delay_buf, st_ivas.hLFE->lfe_addl_delay );
delay_signal( ppPcm_in[i], frame_len, ppDelay_lines[i], delay_lp );
}
}
ivas_binaural_add_LFE( &st_ivas, frame_len, ppPcm_in );
}
for ( i = 0; i < out_ch; i++ )
{
mvr2r( ppPcm_in[i], ppPcm_out[i], frame_len );
}
if ( pIo_params->limiter_enable )
{
ivas_limiter_dec( st_ivas.hLimiter, ppPcm_out, out_ch, frame_len, FALSE );
ivas_limiter_dec( st_ivas.hLimiter, ppPcm_out, out_ch, frame_len, 0 );
}
}
......@@ -1540,7 +1523,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
}
for ( i = 0; i < out_ch; i++ )
{
float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC );
// float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC );
float temp;
#ifdef _FIND_MAX_
valMaxLoc = ( ppPcm_out[i][j] > valMaxLoc ) ? ppPcm_out[i][j] : ( ppPcm_out[i][j] < -valMaxLoc ) ? -ppPcm_out[i][j]
: valMaxLoc;
......@@ -1549,11 +1533,10 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
valMax = valMaxLoc;
valEner += ppPcm_out[i][j] * ppPcm_out[i][j];
#endif
pcm[i] = ( temp > MAX16B ) ? MAX16B : ( temp < MIN16B_FLT ) ? MIN16B
: (short) temp;
clip = max( clip, fabs( ppPcm_out[i][j] ) );
temp = ( ppPcm_out[i][j] > MAX16B_FLT ) ? MAX16B_FLT : ( ppPcm_out[i][j] < MIN16B_FLT ) ? MIN16B_FLT
: ppPcm_out[i][j];
pcm[i] = (short) roundf( temp );
clip = max( clip, fabsf( ppPcm_out[i][j] ) );
}
if ( write_flag == 1 )
{
......@@ -1566,14 +1549,13 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
if ( write_flag == 0 )
skipped_samples++;
}
if ( clip > 1.0f )
if ( clip > MAX16B_FLT )
{
fprintf( stdout, "IVAS Common Renderer Clipped: max gain = %f\n", clip );
}
fprintf( stdout, "Processed frame: %ld\r", (long) frame_count );
frame_count++;
}
int16_t pcm[MAX_OUTPUT_CHANNELS] = { 0 };
......@@ -1603,6 +1585,8 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
ivas_td_binaural_close( &st_ivas.hBinRendererTd );
if ( st_ivas.hLimiter != NULL )
ivas_limiter_close( &st_ivas.hLimiter );
if ( st_ivas.hEFAPdata != NULL )
efap_free_data( &st_ivas.hEFAPdata );
if ( pIo_params->lfe_lp_enable )
{
......@@ -1611,7 +1595,7 @@ ivas_result_t ivas_common_mixer_renderer( ivas_crend_io_params_t *pIo_params, fl
count_free( ppDelay_lines[i] );
}
}
if ( st_ivas.hRenderConfig->roomAcoustics.late_reverb_on )
if ( st_ivas.hRenderConfig != NULL )
{
ivas_render_config_close( &st_ivas.hRenderConfig );
}
......@@ -1769,7 +1753,8 @@ ivas_result_t ivas_object_mixer_renderer( ivas_crend_io_params_t *pIo_params, in
for ( i = 0; i < out_ch; i++ )
{
float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC );
// float temp = roundf( ppPcm_out[i][j] * PCM16_TO_FLT_FAC );
float temp = roundf( ppPcm_out[i][j] );
#ifdef _FIND_MAX_
valMaxLoc = ( ppPcm_out[i][j] > valMaxLoc ) ? ppPcm_out[i][j] : ( ppPcm_out[i][j] < -valMaxLoc ) ? -ppPcm_out[i][j]
......
......@@ -83,12 +83,15 @@
#define IVAS_IN_FMT_510 "510"
#define IVAS_IN_FMT_710 "710"
#define IVAS_IN_FMT_512 "512"
#define IVAS_IN_FMT_514 "514"
#define IVAS_IN_FMT_714 "714"
#define IVAS_IN_FMT_FOA "HOA1S"
#define IVAS_IN_FMT_HOA_2 "HOA2S"
#define IVAS_IN_FMT_HOA_3 "HOA3S"
#define IVAS_IN_FMT_HOA_4 "HOA4S"
#define IVAS_MAX_NUM_CH 16
/*------------------------------------------------------------------------------------------*
* Structure definitions
*------------------------------------------------------------------------------------------*/
......
......@@ -90,7 +90,7 @@ fi
# strip switches, to remove the macros
if coan_exists; then
coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/lib_{com,dec,enc,util,debug}/*.[hc]
coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/lib_{com,dec,enc,rend,util,debug}/*.[hc]
coan source --replace --no-transients -E -K --file $ifdef_list $targetdir/apps/*.[hc]
else
./strip_defines_cppp.sh $targetdir $ifdef_list
......@@ -103,6 +103,7 @@ find $targetdir -name "*.[ch]" -exec sed -i.bak -e "s/\(0x[0-9a-fA-F]*\)UL/\(\(u
${emulator} ./wmc_tool.exe $targetdir/lib_enc/*.c /ic /op > /dev/null
${emulator} ./wmc_tool.exe $targetdir/lib_com/*.c /ic /op > /dev/null
${emulator} ./wmc_tool.exe $targetdir/lib_dec/*.c /ic /op > /dev/null
${emulator} ./wmc_tool.exe $targetdir/lib_rend/*.c /ic /op > /dev/null
# automatically enable #define WMOPS in options.h
sed -i.bak -e "s/\/\*\s*\(#define\s*WMOPS\)\s*\*\//\1/g" $targetdir/lib_com/options.h
......
# GNU Makefile
# Paths
SRC_LIBCOM = ../../lib_com
SRC_LIBDEBUG = ../../lib_debug
SRC_LIBDEC = ../../lib_dec
SRC_LIBENC = ../../lib_enc
SRC_LIBUTIL = ../../lib_util
SRC_APP = .
BUILD = build
OBJDIR = obj
SRC_DIRS = $(sort -u $(SRC_LIBCOM) $(SRC_LIBDEBUG) $(SRC_LIBDEC) $(SRC_LIBENC) $(SRC_LIBUTIL) $(SRC_APP))
# Name of CLI binaries
CLI_PRD ?= IVAS_prerenderer
LIB_LIBCOM ?= libivascom.a
LIB_LIBDEBUG ?= libivasdebug.a
LIB_LIBDEC ?= libivasdec.a
LIB_LIBENC ?= libivasenc.a
LIB_LIBUTIL ?= libivasutil.a
# Default tool settings
CC ?= gcc
RM ?= rm -f
AR ?= ar
# Detect system
UNAME_S := $(shell uname -s)
# Switches for cross-platform builds (i.e. build 32 bit code on 64 bit platforms)
ifneq "$(TARGET_PLATFORM)" ""
ifeq ("$(TARGET_PLATFORM)", "$(findstring $(TARGET_PLATFORM), i386 i586 i686)")
CFLAGS += -m32
LDFLAGS += -m32
endif
ifeq ("$(TARGET_PLATFORM)", "$(findstring $(TARGET_PLATFORM), x86_64)")
CFLAGS += -m64
LDFLAGS += -m64
endif
endif
ifndef VERBOSE
QUIET_CC = @echo ' ' Compiling $<;
QUIET_LINK= @echo ' ' Linking $@;
QUIET_AR = @echo ' ' Archiving $@;
QUIET = @
endif
# C compiler flags
CFLAGS += -std=c99 -pedantic -Wcast-qual -Wall -W -Wextra -Wno-long-long \
-Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes \
-Werror-implicit-function-declaration -Wno-unused-parameter \
-Wno-unused-function -Wno-implicit-fallthrough
# libs to link
LDLIBS += -lm
# Clang sanitizer compiler options
CCCLANG = clang
ifeq "$(CLANG)" "1"
CC = $(CCCLANG)
CFLAGS += -fsanitize=memory
LDFLAGS += -fsanitize=memory
endif
ifeq "$(CLANG)" "2"
CC = $(CCCLANG)
CFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
endif
ifeq "$(CLANG)" "3"
CC = $(CCCLANG)
CFLAGS += -fsanitize=undefined
LDFLAGS += -fsanitize=undefined
endif
ifeq "$(RELEASE)" "1"
CFLAGS += -DRELEASE
OPTIM ?= 2
endif
ifneq "$(DEBUG)" "0"
CFLAGS += -g3
LDFLAGS += -g3
endif
ifeq "$(GCOV)" "1"
CFLAGS += -fprofile-arcs -ftest-coverage
LDFLAGS += -fprofile-arcs -ftest-coverage
endif
ifeq "$(STRIP)" "1"
CFLAGS += -fdata-sections -ffunction-sections
ifneq ($(UNAME_S),Darwin)
LDFLAGS += -Wl,-gc-sections -static
else
LDFLAGS += -Wl,-dead_strip
endif
endif
OPTIM ?= 0
CFLAGS += -O$(OPTIM)
CFLAGS += $(foreach DIR,$(SRC_DIRS),-I$(DIR))
# Source file search paths
VPATH = $(SRC_DIRS)
###############################################################################
SRCS_LIBCOM = $(foreach DIR,$(SRC_LIBCOM),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
SRCS_LIBDEBUG = $(foreach DIR,$(SRC_LIBDEBUG),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
SRCS_LIBDEC = $(foreach DIR,$(SRC_LIBDEC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
SRCS_LIBENC = $(foreach DIR,$(SRC_LIBENC),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
SRCS_LIBUTIL = $(foreach DIR,$(SRC_LIBUTIL),$(patsubst $(DIR)/%,%,$(wildcard $(DIR)/*.c)))
OBJS_LIBCOM = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.o))
OBJS_LIBDEBUG = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEBUG:.c=.o))
OBJS_LIBDEC = $(addprefix $(OBJDIR)/,$(SRCS_LIBDEC:.c=.o))
OBJS_LIBENC = $(addprefix $(OBJDIR)/,$(SRCS_LIBENC:.c=.o))
OBJS_LIBUTIL = $(addprefix $(OBJDIR)/,$(SRCS_LIBUTIL:.c=.o))
OBJS_CLI_APPPRD = $(OBJDIR)/prerenderer.o
DEPS = $(addprefix $(OBJDIR)/,$(SRCS_LIBCOM:.c=.P) $(SRCS_LIBDEBUG:.c=.P) $(SRCS_LIBDEC:.c=.P) \
$(SRCS_LIBENC:.c=.P) $(SRCS_LIBUTIL:.c=.P))
###############################################################################
.PHONY: all clean clean_all
all: $(CLI_PRD)
$(OBJDIR):
$(QUIET)mkdir -p $(OBJDIR)
$(LIB_LIBCOM): $(OBJS_LIBCOM)
$(QUIET_AR)$(AR) rcs $@ $^
$(LIB_LIBDEBUG): $(OBJS_LIBDEBUG)
$(QUIET_AR)$(AR) rcs $@ $^
$(LIB_LIBDEC): $(OBJS_LIBDEC)
$(QUIET_AR)$(AR) rcs $@ $^
$(LIB_LIBENC): $(OBJS_LIBENC)
$(QUIET_AR)$(AR) rcs $@ $^
$(LIB_LIBUTIL): $(OBJS_LIBUTIL)
$(QUIET_AR)$(AR) rcs $@ $^
$(CLI_PRD): $(LIB_LIBENC) $(LIB_LIBDEC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBUTIL) $(OBJS_CLI_APPPRD)
$(QUIET_LINK)$(CC) $(LDFLAGS) $(OBJS_CLI_APPPRD) -L. -livasutil -livasenc -livasdec -livascom -livasdebug $(LDLIBS) -o $(CLI_PRD)
libs: $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL)
clean:
$(QUIET)$(RM) $(OBJS_LIBENC) $(OBJS_LIBDEC) $(OBJS_CLI_APPPRD) $(DEPS)
$(QUIET)$(RM) $(DEPS:.P=.d)
$(QUIET)test ! -d $(OBJDIR) || rm -rf $(OBJDIR)
clean_all: clean
$(QUIET)$(RM) $(CLI_PRD) $(LIB_LIBENC) $(LIB_LIBDEBUG) $(LIB_LIBCOM) $(LIB_LIBDEC) $(LIB_LIBUTIL)
$(OBJDIR)/%.o : %.c | $(OBJDIR)
$(QUIET_CC)$(CC) $(CFLAGS) -c -MD -o $@ $<
@cp $(OBJDIR)/$*.d $(OBJDIR)/$*.P; \
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < $(OBJDIR)/$*.d >> $(OBJDIR)/$*.P; \
$(RM) $(OBJDIR)/$*.d
-include $(DEPS)
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "prerenderer", "prerenderer.vcxproj", "{12B4C8A5-1E06-4E30-B443-D1F916F52B47}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_util", "..\..\..\Workspace_msvc\lib_util.vcxproj", "{2FA8F384-0775-F3B7-F8C3-85209222FC70}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_debug", "..\..\..\Workspace_msvc\lib_debug.vcxproj", "{54509728-928B-44D9-A118-A6F92F08B34F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_com", "..\..\..\Workspace_msvc\lib_com.vcxproj", "{39EC200D-7795-4FF8-B214-B24EDA5526AE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lib_dec", "..\..\..\Workspace_msvc\lib_dec.vcxproj", "{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.ActiveCfg = Debug|Win32
{12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Debug|Win32.Build.0 = Debug|Win32
{12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.ActiveCfg = Release|Win32
{12B4C8A5-1E06-4E30-B443-D1F916F52B47}.Release|Win32.Build.0 = Release|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.ActiveCfg = Debug|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Debug|Win32.Build.0 = Debug|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.ActiveCfg = Release|Win32
{2FA8F384-0775-F3B7-F8C3-85209222FC70}.Release|Win32.Build.0 = Release|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.ActiveCfg = Debug|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Debug|Win32.Build.0 = Debug|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.ActiveCfg = Release|Win32
{54509728-928B-44D9-A118-A6F92F08B34F}.Release|Win32.Build.0 = Release|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.ActiveCfg = Debug|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Debug|Win32.Build.0 = Debug|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.ActiveCfg = Release|Win32
{39EC200D-7795-4FF8-B214-B24EDA5526AE}.Release|Win32.Build.0 = Release|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.ActiveCfg = Debug|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Debug|Win32.Build.0 = Debug|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.ActiveCfg = Release|Win32
{E822DDAF-0F5F-4CD0-A694-38AE69DE74D3}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {06AC6878-76C6-4079-956D-B3FF3DB2C9A5}
EndGlobalSection
EndGlobal
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\..\lib_dec\ivas_allrad_dec.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_ls_custom_dec.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_efap.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_dirac_output_synthesis_dec.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_vbap.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_rom_dec.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_dec\ivas_limiter.c">
<Filter>dec_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\..\..\lib_enc\ivas_ism_metadata_enc.c">
<Filter>enc_ivas_c</Filter>
</ClCompile>
<ClCompile Include="..\prerenderer.c" />
<ClCompile Include="..\..\..\lib_util\ivas_prerenderer.c" />
<ClCompile Include="..\..\..\lib_util\ivas_rom_prerenderer.c" />
</ItemGroup>
<ItemGroup>
<Filter Include="prndr_c">
<UniqueIdentifier>{4fc737f1-c7a5-4376-a066-2a32d752a2ff}</UniqueIdentifier>
</Filter>
<Filter Include="dec_ivas_c">
<UniqueIdentifier>{93995380-89bd-4b04-88eb-625fbe52ebfb}</UniqueIdentifier>
</Filter>
<Filter Include="enc_ivas_c">
<UniqueIdentifier>{46364e80-1212-3600-1d53-58d3725f5bdc}</UniqueIdentifier>
</Filter>
<Filter Include="prndr_h">
<UniqueIdentifier>{67da6ab6-f800-4c08-8b7a-83bb121aad01}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\lib_util\ivas_prerenderer.h" />
<ClInclude Include="..\..\..\lib_util\ivas_rom_prerenderer.h" />
</ItemGroup>
</Project>
\ No newline at end of file
/******************************************************************************************************
(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 "audio_file_reader.h"
#include "audio_file_writer.h"
#include "common_api_types.h"
#include "ivas_prerenderer.h"
#include "ism_file_reader.h"
#include "ivas_stat_dec.h"
#include "prot.h"
#ifdef WMOPS
#include "wmops.h"
#endif
#ifdef RAM_COUNTING_TOOL
#include "mem_count.h"
#endif
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifndef count_malloc
#ifdef RAM_COUNTING_TOOL
#define count_malloc( n1 ) MALLOC_FCT_CALL( n1 )
#define count_calloc( n1, n2 ) CALLOC_FCT_CALL( n1, n2 )
#define count_free( ptr ) FREE_FCT_CALL( ptr )
#else
#define count_malloc( n1 ) malloc( n1 )
#define count_calloc( n1, n2 ) calloc( n1, n2 )
#define count_free( ptr ) free( ptr )
#endif
#endif
#ifndef min
#define min( x, y ) ( ( x ) < ( y ) ? ( x ) : ( y ) )
#endif
#ifndef max
#define max( x, y ) ( ( x ) > ( y ) ? ( x ) : ( y ) )
#endif
#define PRERENDERER_MAX_METADATA_LENGTH 8192
#define PRERENDERER_MAX_METADATA_LINE_LENGTH 1024
#define PRERENDERER_MAX_CMDLN_ARG_LENGTH FILENAME_MAX
#if !defined( DEBUGGING ) && !defined( WMOPS )
static
#endif
int32_t frame = 0;
#ifdef _WIN32
#define SEP_FOLDER '\\'
#else
#define SEP_FOLDER '/'
#endif
#ifdef WMOPS
/* void print_stack_call_tree( void );
int Const_Data_Size_ivas_prerenderer_mai( void ); // Not a typo
int Const_Data_Size_ivas_rom_prerenderer( void );
extern int16_t *ptr_base_stack;
extern int16_t *ptr_max_stack;
extern int32_t wc_frame;
extern char location_max_stack[256]; */
/* clang-format off */
/*------------------------------------------------------------------------------------------*
* Function to print complexity & memory estimates
*------------------------------------------------------------------------------------------*/
/* void print_mem_prerenderer(size_t SRAM_size)
{
fprintf( stdout, "\n\n --- Prerenderer cmdln demo memory usage --- \n\n" );
fprintf( stdout, "PROM size (prerenderer): %d words (or instructions)\n", PROM_Size_prerenderer );
fprintf( stdout, "Stack size: %d words in %s() in frame #%d\n", ( ptr_base_stack - ptr_max_stack ) * sizeof( int16_t ) / sizeof( float ), location_max_stack, wc_frame );
fprintf( stdout, "Table ROM size(prerenderer_main): %d words\n", (Const_Data_Size_ivas_prerenderer_mai() ) / sizeof( float ) );
fprintf( stdout, "Table ROM size(prerenderer): %d words\n", (Const_Data_Size_ivas_rom_prerenderer() ) / sizeof( float ) );
#ifdef RAM_COUNTING_TOOL
fprintf( stdout, "Static RAM size: %d words\n\n", SRAM_size );
#endif
print_stack_call_tree();
fprintf( stdout, "Note: this is an optimistic estimate of the memory consumption assuming\n" );
fprintf( stdout, " that each variable (short, long or float) in the codec requires\n" );
fprintf( stdout, " 32 bits of memory and may therefore be represented by 1 word.\n" );
fprintf( stdout, " The following formula is used: sizeof('memory array')/sizeof(float)\n\n" );
} */
/* clang-format on */
#endif
static const char *optInputFile = "--inputFile";
static const char *optInputFileShort = "-if";
static const char *optInputAmbisonics = "--inputAmbisonics";
static const char *optInputAmbisonicsShort = "-if-sba";
static const char *optInputMultichannel = "--inputMultichannel";
static const char *optInputMultichannelShort = "-if-mc";
static const char *optOutputFile = "--outputFile";
static const char *optOutputFileShort = "-of";
static const char *optInputAudio = "--inputAudio";
static const char *optInputAudioShort = "-ia";
static const char *optFrameSize = "--frameSize";
static const char *optFrameSizeShort = "-fr";
static const char *optOutputConfig = "--outputConfig";
static const char *optOutputConfigShort = "-oc";
static const char *optSampleRate = "--sampleRate";
static const char *optSampleRateShort = "-fs";
static const char *optNeverDropLfe = "--neverDropLfe";
static const char *optNeverDropLfeShort = "-ndl";
typedef struct Prndr_IsmPositionProvider
{
uint32_t frameCounter;
uint32_t numObjects;
IsmFileReader *ismReaders[PRERENDERER_MAX_ISM_INPUTS];
uint32_t numPositions[PRERENDERER_MAX_ISM_INPUTS];
Prndr_AudioObjectPosition *positions[PRERENDERER_MAX_ISM_INPUTS]; /* size: [PRERENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */
uint16_t *positionDurations[PRERENDERER_MAX_ISM_INPUTS]; /* size: [PRERENDERER_MAX_ISM_INPUTS][numPositions[object_index]] */
uint32_t currentPositionIdxs[PRERENDERER_MAX_ISM_INPUTS]; /* Index of current position as listed in the metadata file */
uint16_t durationCounters[PRERENDERER_MAX_ISM_INPUTS]; /* Number of frames spent at current position */
} Prndr_IsmPositionProvider;
typedef enum Prndr_ForcedInputFormat
{
prndr_forced_input_format_none = 0,
prndr_forced_input_format_sba,
prndr_forced_input_format_mc
} Prndr_ForcedInputFormat;
typedef struct Prndr_CmdlnArgs
{
char inputFilePath[FILENAME_MAX];
char outputFilePath[FILENAME_MAX];
int16_t frameSize;
int32_t sampleRate;
Prndr_OutputConfig outConfig;
Prndr_ForcedInputFormat forcedInputFormat;
char forcedAudioInputFile[FILENAME_MAX];
int8_t neverDropLfe; /* flag */
} Prndr_CmdlnArgs;
static int8_t getAmbisonicsFromNumChannels(
uint32_t numChannels,
Prndr_Ambisonics *ambisonics );
static int8_t getSpeakerLayoutFromNumChannels(
uint32_t numChannels,
Prndr_SpeakerLayout *layout );
static int8_t setInConfigFromSbaNumChannels(
int16_t numChannels,
Prndr_InputConfig *inConfig );
static int8_t setInConfigFromMcNumChannels(
int16_t numChannels,
Prndr_InputConfig *inConfig );
static Prndr_Ambisonics ambisonicsOrderToEnum(
int32_t order );
static Prndr_SpeakerLayout speakerLayoutCicpToEnum(
int32_t cicpIndex );
static int8_t parseOutConfig(
char *configString,
Prndr_OutputConfig *outConfig );
static void printUsage(
void );
static void parseConfigFile(
char *path,
char *audioFilePath,
Prndr_InputConfig *inConfig,
Prndr_IsmPositionProvider *positionProvider );
static Prndr_CmdlnArgs parseCmdlnArgs(
int32_t argc,
char **argv );
static Prndr_IsmPositionProvider *Prndr_IsmPositionProvider_open(
void );
static void Prndr_IsmPositionProvider_getNextFrame(
Prndr_IsmPositionProvider *positionProvider,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer );
static void Prndr_IsmPositionProvider_close(
Prndr_IsmPositionProvider *positionProvider );
static void readFromShorthandMetadata(
Prndr_IsmPositionProvider *positionProvider,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer,
uint32_t objIdx );
void getMetadataFromFileReader(
IsmFileReader *ismReader,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer,
uint32_t objIdx );
static void splitConfigFile(
const char *mdfFilePath,
char *metadataString,
uint32_t *metadataStringLength,
char *wavFileName,
uint32_t *wavFileNameLength );
static char *readNextMetadataChunk(
char *line,
const char *delimiter );
static void parseUint8(
char *line,
uint8_t *ret );
static int8_t parseUint32(
char *line,
uint32_t *ret );
static void parseObjectPosition(
char *line,
Prndr_AudioObjectPosition *position,
uint16_t *positionDuration );
static void parseIsm(
char *line,
char *inDir,
Prndr_InputConfig *inConfig,
Prndr_IsmPositionProvider *positionProvider,
int32_t idx );
static void parseSba(
char *line,
Prndr_InputConfig *inConfig,
int32_t idx );
static void parseMc(
char *line,
Prndr_InputConfig *inConfig,
int32_t idx );
static void parseMetadata(
char *metadataString,
char *inDir,
Prndr_InputConfig *inConfig,
Prndr_IsmPositionProvider *positionProvider );
static void convert_backslash(
char *str );
static void remove_cr(
char *str );
/* ============================================================================ */
int32_t main( int32_t argc, char **argv )
{
Prndr_CmdlnArgs args;
Prndr_Prerenderer *prerenderer;
Prndr_IsmPositionProvider *positionProvider;
Prndr_InputConfig inConfig;
char audioFilePath[FILENAME_MAX];
AudioFileReader *audioReader = NULL;
int16_t numInChannels;
AudioFileWriter *audioWriter;
int32_t inBufferSize;
int32_t outBufferSize;
int16_t *inpInt16Buffer;
float *inFloatBuffer;
int16_t *outInt16Buffer;
float *outFloatBuffer;
Prndr_AudioBuffer inPrndrBuffer;
Prndr_AudioBuffer outPrndrBuffer;
int16_t numSamplesRead;
int16_t i;
ivas_error error = IVAS_ERR_OK;
#ifdef WMOPS
size_t SRAM_size;
#endif
#ifdef WMOPS
reset_wmops();
reset_stack();
#endif
#ifdef RAM_COUNTING_TOOL
mem_count_init( 0, USE_32BITS );
#endif
args = parseCmdlnArgs( argc, argv );
/* === Open === */
prerenderer = Prndr_Prerenderer_open();
positionProvider = Prndr_IsmPositionProvider_open();
convert_backslash( args.inputFilePath );
convert_backslash( args.outputFilePath );
convert_backslash( args.forcedAudioInputFile );
/* === Parse === */
if ( args.forcedInputFormat == prndr_forced_input_format_none )
{
/* Only parse config file if input config is not forced at cmdln */
parseConfigFile( args.inputFilePath, audioFilePath, &inConfig, positionProvider );
}
else
{
/* If input config is forced, input file path is the input audio file, not config file */
strncpy( audioFilePath, args.inputFilePath, FILENAME_MAX );
/* Initialize inConfig - this will be overwritten when applying forced parameters,
* but not initializing here causes a compiler warning on msvc */
inConfig.numAmbisonicsBuses = 0;
inConfig.numMultiChannelBuses = 0;
inConfig.numAudioObjects = 0;
}
/* === Apply forced parameters === */
if ( strlen( args.forcedAudioInputFile ) != 0 )
{
strncpy( audioFilePath, args.forcedAudioInputFile, FILENAME_MAX );
}
AudioFileReader_open( &audioReader, audioFilePath, args.sampleRate );
if ( !audioReader )
{
fprintf( stderr, "Error opening file: %s\n", audioFilePath );
exit( -1 );
}
if ( args.forcedInputFormat != prndr_forced_input_format_none )
{
numInChannels = AudioFileReader_getNumChannels( audioReader );
if ( numInChannels == 0 )
{
fprintf( stderr, "File does not contain number of channels metadata, probably a raw file: %s\n", audioFilePath );
exit( -1 );
}
if ( args.forcedInputFormat == prndr_forced_input_format_sba )
{
if ( setInConfigFromSbaNumChannels( numInChannels, &inConfig ) != 0 )
{
fprintf( stderr, "File cannot be used with forced sba input: %s\n", audioFilePath );
exit( -1 );
}
}
else if ( args.forcedInputFormat == prndr_forced_input_format_mc )
{
if ( setInConfigFromMcNumChannels( numInChannels, &inConfig ) != 0 )
{
fprintf( stderr, "File cannot be used with forced mc input: %s\n", audioFilePath );
exit( -1 );
}
}
}
/* === Configure === */
if ( ( error = Prndr_Prerenderer_configure( prerenderer, inConfig, args.outConfig, args.frameSize, args.sampleRate ) ) != IVAS_ERR_OK )
{
exit( -1 );
}
if ( args.neverDropLfe )
{
Prndr_Prerenderer_setNeverDropLfe( prerenderer, 1 );
}
if ( Prndr_Prerenderer_getInChannels( prerenderer ) != AudioFileReader_getNumChannels( audioReader ) )
{
fprintf( stderr, "Number of channels in input file does not match that defined in the config file\n" );
exit( -1 );
}
/* === Process === */
AudioFileWriter_open( &audioWriter, args.outputFilePath, args.sampleRate, Prndr_Prerenderer_getOutChannels( prerenderer ) );
if ( audioWriter == NULL )
{
exit( -1 );
}
inBufferSize = args.frameSize * Prndr_Prerenderer_getInChannels( prerenderer );
outBufferSize = args.frameSize * Prndr_Prerenderer_getOutChannels( prerenderer );
inpInt16Buffer = count_calloc( inBufferSize, sizeof( int16_t ) );
inFloatBuffer = count_calloc( inBufferSize, sizeof( float ) );
outInt16Buffer = count_calloc( outBufferSize, sizeof( int16_t ) );
outFloatBuffer = count_calloc( outBufferSize, sizeof( float ) );
inPrndrBuffer.config.sampleRate = args.sampleRate;
inPrndrBuffer.config.bufferSize = args.frameSize;
inPrndrBuffer.config.numChannels = Prndr_Prerenderer_getInChannels( prerenderer );
inPrndrBuffer.data = inFloatBuffer;
outPrndrBuffer.config.sampleRate = args.sampleRate;
outPrndrBuffer.config.bufferSize = args.frameSize;
outPrndrBuffer.config.numChannels = Prndr_Prerenderer_getOutChannels( prerenderer );
outPrndrBuffer.data = outFloatBuffer;
#ifdef WMOPS
reset_wmops();
#endif
while ( 1 )
{
int32_t chnl, smpl;
int32_t num_in_channels;
num_in_channels = inPrndrBuffer.config.numChannels;
i = 0;
/* Read the input data */
if ( ( error = AudioFileReader_read( audioReader, inpInt16Buffer, inBufferSize, &numSamplesRead ) ) != IVAS_ERR_OK )
{
fprintf( stderr, "\nError reading from file %s\n", audioFilePath );
break;
}
if ( numSamplesRead == 0 )
{
/* end of input data */
break;
}
/* Convert from int to float and from interleaved to packed */
for ( smpl = 0; smpl < args.frameSize; ++smpl )
{
for ( chnl = 0; chnl < num_in_channels; ++chnl )
{
if ( i < numSamplesRead )
{
inFloatBuffer[chnl * args.frameSize + smpl] = (float) inpInt16Buffer[i] / INT16_MAX;
}
else
{
inFloatBuffer[chnl * args.frameSize + smpl] = 0.f;
}
++i;
}
}
Prndr_AudioObjectMetadataBuffer mtdBuffer;
Prndr_IsmPositionProvider_getNextFrame( positionProvider, &mtdBuffer );
Prndr_Prerenderer_render( prerenderer, inPrndrBuffer, mtdBuffer, outPrndrBuffer );
int32_t num_out_channels;
num_out_channels = outPrndrBuffer.config.numChannels;
i = 0;
/* Convert from float to int and from packed to interleaved */
for ( smpl = 0; smpl < args.frameSize; ++smpl )
{
for ( chnl = 0; chnl < num_out_channels; ++chnl )
{
outInt16Buffer[i] = (int16_t)(outFloatBuffer[chnl * args.frameSize + smpl] * INT16_MAX);
++i;
}
}
if ( AudioFileWriter_write( audioWriter, outInt16Buffer, outBufferSize ) != 0 )
{
fprintf( stderr, "Error writing audio file %s\n", args.outputFilePath );
exit( -1 );
}
frame++;
fprintf( stdout, "%-8d\b\b\b\b\b\b\b\b", frame );
#ifdef WMOPS
update_wmops();
#endif
}
fprintf( stdout, "\n" );
/* === Close === */
count_free( inpInt16Buffer );
count_free( inFloatBuffer );
count_free( outInt16Buffer );
count_free( outFloatBuffer );
AudioFileReader_close( &audioReader );
AudioFileWriter_close( &audioWriter );
Prndr_Prerenderer_close( prerenderer );
Prndr_IsmPositionProvider_close( positionProvider );
#ifdef RAM_COUNTING_TOOL
#ifdef WMOPS
SRAM_size =
#endif
mem_count_summary( USE_DEFAULT );
#endif
#ifdef WMOPS
print_wmops();
/* print_mem_prerenderer( SRAM_size ); */
#endif
return 0;
}
static int8_t getAmbisonicsFromNumChannels( uint32_t numChannels,
Prndr_Ambisonics *ambisonics )
{
switch ( numChannels )
{
case 1:
*ambisonics = prndr_ambisonics_mono;
break;
case 4:
*ambisonics = prndr_ambisonics_foa;
break;
case 9:
*ambisonics = prndr_ambisonics_soa;
break;
case 16:
*ambisonics = prndr_ambisonics_toa;
break;
default:
fprintf( stderr, "Unable to infer ambisonics order from number of channels: %d\n", numChannels );
return -1;
}
return 0;
}
static int8_t getSpeakerLayoutFromNumChannels( uint32_t numChannels,
Prndr_SpeakerLayout *layout )
{
switch ( numChannels )
{
case 1:
*layout = prndr_speaker_layout_mono;
break;
case 2:
*layout = prndr_speaker_layout_stereo;
break;
case 6:
*layout = prndr_speaker_layout_5_1;
break;
case 8:
*layout = prndr_speaker_layout_7_1;
break;
case 10:
*layout = prndr_speaker_layout_5_1_4;
break;
case 12:
*layout = prndr_speaker_layout_7_1_4;
break;
default:
fprintf( stderr, "Unable to infer speaker layout from number of channels: %d\n", numChannels );
return -1;
}
return 0;
}
static int8_t setInConfigFromSbaNumChannels( int16_t numChannels, Prndr_InputConfig *inConfig )
{
inConfig->numAmbisonicsBuses = 1;
inConfig->numAudioObjects = 0;
inConfig->numMultiChannelBuses = 0;
inConfig->ambisonicsBuses[0].inputChannelIndex = 0;
return getAmbisonicsFromNumChannels( numChannels, &inConfig->ambisonicsBuses[0].ambisonicsConfig );
}
static int8_t setInConfigFromMcNumChannels( int16_t numChannels, Prndr_InputConfig *inConfig )
{
inConfig->numAmbisonicsBuses = 0;
inConfig->numAudioObjects = 0;
inConfig->numMultiChannelBuses = 1;
inConfig->multiChannelBuses[0].inputChannelIndex = 0;
return getSpeakerLayoutFromNumChannels( numChannels, &inConfig->multiChannelBuses[0].speakerLayout );
}
static Prndr_Ambisonics ambisonicsOrderToEnum( int32_t order )
{
switch ( order )
{
case 0:
return prndr_ambisonics_mono;
case 1:
return prndr_ambisonics_foa;
case 2:
return prndr_ambisonics_soa;
case 3:
return prndr_ambisonics_toa;
}
return prndr_ambisonics_none;
}
static Prndr_SpeakerLayout speakerLayoutCicpToEnum( int32_t cicpIndex )
{
switch ( cicpIndex )
{
case 0:
return prndr_speaker_layout_custom;
case 1:
return prndr_speaker_layout_mono;
case 2:
return prndr_speaker_layout_stereo;
case 6:
return prndr_speaker_layout_5_1;
case 16:
return prndr_speaker_layout_5_1_4;
case 12:
return prndr_speaker_layout_7_1;
case 19:
return prndr_speaker_layout_7_1_4;
}
return prndr_speaker_layout_none;
}
static int8_t parseOutConfig( char *configString, Prndr_OutputConfig *outConfig )
{
char outType[5];
int32_t num;
int8_t success; /* flag */
outType[4] = '\0';
strncpy( outType, configString, 4 );
success = 1;
if ( ( outType[0] == 's' || outType[0] == 'S' ) &&
( outType[1] == 'b' || outType[1] == 'B' ) &&
( outType[2] == 'a' || outType[2] == 'A' ) )
{
num = strtol( &configString[3], NULL, 10 );
/* If num is 0, ensure it's because user requested it and not because of strtol error */
if ( num == 0 && configString[3] != 0 && configString[4] != '\0' )
{
success = 0;
}
else
{
outConfig->ambisonics = ambisonicsOrderToEnum( num );
outConfig->outSetupCustom = NULL;
}
}
else if ( ( outType[0] == 'c' || outType[0] == 'C' ) &&
( outType[1] == 'i' || outType[1] == 'I' ) &&
( outType[2] == 'c' || outType[2] == 'C' ) &&
( outType[3] == 'p' || outType[3] == 'P' ) )
{
num = strtol( &configString[4], NULL, 10 );
outConfig->speakerLayout = speakerLayoutCicpToEnum( num );
outConfig->outSetupCustom = NULL;
}
else
{
int16_t i, is_planar;
LsCustomFileReader *hLsCustomReader = NULL;
IVAS_CUSTOM_LS_DATA hLsCustomData;
/* Default to interpreting as a custom loudspeaker layout file */
outConfig->speakerLayout = speakerLayoutCicpToEnum( 0 );
CustomLsReader_open( configString, &hLsCustomReader );
success = ( CustomLsFileReading( hLsCustomReader, &hLsCustomData ) == LS_CUSTOM_FILEREADER_NO_ERROR );
outConfig->outSetupCustom->num_spk = hLsCustomData.num_spk;
mvr2r( hLsCustomData.azimuth, outConfig->outSetupCustom->ls_azimuth, hLsCustomData.num_spk );
mvr2r( hLsCustomData.elevation, outConfig->outSetupCustom->ls_elevation, hLsCustomData.num_spk );
/* Set planar flag */
is_planar = 1;
for ( i = 0; i < hLsCustomData.num_spk; i++ )
{
if ( is_planar && outConfig->outSetupCustom->ls_elevation[i] != 0.0f )
{
is_planar = 0;
}
}
outConfig->outSetupCustom->is_planar_setup = is_planar;
/* Loudspeaker LFE */
outConfig->outSetupCustom->num_lfe = hLsCustomData.num_lfe;
mvs2s( hLsCustomData.lfe_idx, outConfig->outSetupCustom->lfe_idx, hLsCustomData.num_lfe );
CustomLsReader_close( &hLsCustomReader );
}
return success ? 0 : -1;
}
static void printUsage( void )
{
printf( "\n" );
printf( "Usage: ./IVAS_prerenderer [options]\n" );
printf( "\n" );
printf( "Valid options:\n" );
printf( "%s %s Path to the input file (txt config file).\n", optInputFileShort, optInputFile );
printf( "%s %s Path to the ambisonics audio file. Use this optionally instead of the config file for single-item input configurations.\n", optInputAmbisonicsShort, optInputAmbisonics );
printf( "%s %s Path to the multichannel audio file. Use this optionally instead of the config file for single-item input configurations.\n", optInputMultichannelShort, optInputMultichannel );
printf( "%s %s Path to the output file.\n", optOutputFileShort, optOutputFile );
printf( "%s %s Path to the input audio (overrides audio path from txt config file or -if-sba and -if-mc options) - for debugging/testing purposes.\n", optInputAudioShort, optInputAudio );
printf( "%s %s Size of processing frame (in ms). Default value is 20ms.\n", optFrameSizeShort, optFrameSize );
printf( "%s %s Input sampling rate in kHz.\n", optSampleRateShort, optSampleRate );
printf( "%s %s Output configuration. For ambisonics of order n, select SBAn (e.g. SBA1 for order 1).\n", optOutputConfigShort, optOutputConfig );
printf( " For multichannel, select CICP followed by CICP index (e.g. CICP6, CICP19).\n" );
printf( " Alternatively, can be a custom loudspeaker layout file.\n" );
printf( "%s %s [flag] If set, prerenderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE.\n", optNeverDropLfeShort, optNeverDropLfe );
}
static int8_t checkOpt( uint32_t argc, char **argv, uint32_t idx, uint32_t numFollowingTokens )
{
uint32_t i;
if ( idx + numFollowingTokens >= argc )
{
fprintf( stderr, "Error: option %s requires %d following tokens\n", argv[idx], numFollowingTokens );
return 0;
}
for ( i = 1; i < numFollowingTokens; ++i )
{
if ( strlen( argv[idx + i] ) > PRERENDERER_MAX_CMDLN_ARG_LENGTH )
{
fprintf( stderr, "Too many characters in token: %s\n", argv[idx + i] );
return 0;
}
}
return 1;
}
static Prndr_CmdlnArgs parseCmdlnArgs( int32_t argc, char **argv )
{
Prndr_CmdlnArgs parsedArgs;
int8_t success; /* flag */
int32_t i;
uint32_t numFollowingTokens;
success = 1;
parsedArgs.inputFilePath[0] = '\0';
parsedArgs.outputFilePath[0] = '\0';
parsedArgs.frameSize = -1;
parsedArgs.sampleRate = -1;
parsedArgs.outConfig.ambisonics = prndr_ambisonics_none;
parsedArgs.outConfig.speakerLayout = prndr_speaker_layout_none;
parsedArgs.forcedInputFormat = prndr_forced_input_format_none;
parsedArgs.forcedAudioInputFile[0] = '\0';
parsedArgs.neverDropLfe = false;
i = 1;
while ( i < argc )
{
numFollowingTokens = 1; /* Default number of expected tokens after an option */
if ( strcmp( argv[i], optInputFileShort ) == 0 || strcmp( argv[i], optInputFile ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( strlen( parsedArgs.inputFilePath ) == 0 )
{
strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX );
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optInputFile, optInputFileShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optInputAmbisonicsShort ) == 0 || strcmp( argv[i], optInputAmbisonics ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( strlen( parsedArgs.inputFilePath ) == 0 )
{
strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX );
parsedArgs.forcedInputFormat = prndr_forced_input_format_sba;
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optInputAmbisonics, optInputAmbisonicsShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optInputMultichannelShort ) == 0 || strcmp( argv[i], optInputMultichannel ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( strlen( parsedArgs.inputFilePath ) == 0 )
{
strncpy( parsedArgs.inputFilePath, argv[i + 1], FILENAME_MAX );
parsedArgs.forcedInputFormat = prndr_forced_input_format_mc;
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optInputMultichannel, optInputMultichannelShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optOutputFileShort ) == 0 || strcmp( argv[i], optOutputFile ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( strlen( parsedArgs.outputFilePath ) == 0 )
{
strncpy( parsedArgs.outputFilePath, argv[i + 1], FILENAME_MAX );
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optOutputFile, optOutputFileShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optInputAudioShort ) == 0 || strcmp( argv[i], optInputAudio ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( strlen( parsedArgs.forcedAudioInputFile ) == 0 )
{
strncpy( parsedArgs.forcedAudioInputFile, argv[i + 1], FILENAME_MAX );
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optInputAudio, optInputAudioShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optFrameSizeShort ) == 0 || strcmp( argv[i], optFrameSize ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( parsedArgs.frameSize <= 0 )
{
/* Save value in ms for now - later converted to samples once sample rate is known */
parsedArgs.frameSize = strtol( argv[i + 1], NULL, 10 );
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optFrameSize, optFrameSizeShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optOutputConfigShort ) == 0 || strcmp( argv[i], optOutputConfig ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( parsedArgs.outConfig.ambisonics == prndr_ambisonics_none && parsedArgs.outConfig.speakerLayout == prndr_speaker_layout_none )
{
if ( parseOutConfig( argv[i + 1], &parsedArgs.outConfig ) != 0 )
{
fprintf( stderr, "Unknown output or bad config: %s\n", argv[i + 1] );
success = 0;
}
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optOutputConfig, optOutputConfigShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optSampleRateShort ) == 0 || strcmp( argv[i], optSampleRate ) == 0 )
{
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
if ( parsedArgs.sampleRate <= 0 )
{
parsedArgs.sampleRate = ( int32_t )( strtof( argv[i + 1], NULL ) * 1000 );
}
else
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optSampleRate, optSampleRateShort );
success = 0;
}
}
}
else if ( strcmp( argv[i], optNeverDropLfeShort ) == 0 || strcmp( argv[i], optNeverDropLfe ) == 0 )
{
numFollowingTokens = 0;
if ( ( success = checkOpt( argc, argv, i, numFollowingTokens ) ) == 1 )
{
parsedArgs.neverDropLfe = true;
}
}
else
{
fprintf( stderr, "Unknown option: %s\n", argv[i] );
success = 0;
}
i += 1 + numFollowingTokens;
}
/* Check if any of required args is missing */
if ( strlen( parsedArgs.inputFilePath ) == 0 )
{
fprintf( stderr, "Required option missing or invalid value: %s (%s) or %s (%s) or %s (%s)\n",
optInputFile,
optInputFileShort,
optInputAmbisonics,
optInputAmbisonicsShort,
optInputMultichannel,
optInputMultichannelShort );
success = 0;
}
if ( strlen( parsedArgs.outputFilePath ) == 0 )
{
fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optOutputFile, optOutputFileShort );
success = 0;
}
if ( parsedArgs.outConfig.ambisonics == prndr_ambisonics_none && parsedArgs.outConfig.speakerLayout == prndr_speaker_layout_none )
{
fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optOutputConfig, optOutputConfigShort );
success = 0;
}
if ( parsedArgs.sampleRate <= 0 )
{
fprintf( stderr, "Required option missing or invalid value: %s (%s)\n", optSampleRate, optSampleRateShort );
success = 0;
}
/* Set default values if not specified */
if ( parsedArgs.frameSize <= 0 )
{
parsedArgs.frameSize = parsedArgs.sampleRate / 50;
}
else
{
/* Convert from ms to samples */
parsedArgs.frameSize = parsedArgs.sampleRate * parsedArgs.frameSize / 1000;
}
if ( !success )
{
printUsage();
exit( -1 );
}
return parsedArgs;
}
Prndr_IsmPositionProvider *Prndr_IsmPositionProvider_open( void )
{
Prndr_IsmPositionProvider *ipp;
uint32_t i;
ipp = (Prndr_IsmPositionProvider *) count_malloc( sizeof( Prndr_IsmPositionProvider ) );
ipp->frameCounter = 0;
ipp->numObjects = 0;
for ( i = 0; i < PRERENDERER_MAX_ISM_INPUTS; ++i )
{
ipp->ismReaders[i] = NULL;
ipp->positions[i] = NULL;
ipp->positionDurations[i] = NULL;
ipp->currentPositionIdxs[i] = 0;
ipp->durationCounters[i] = 0;
}
return ipp;
}
void getMetadataFromFileReader(
IsmFileReader *ismReader,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer,
uint32_t objIdx )
{
IVAS_ISM_METADATA ismMetadata;
ivas_error error;
if ( ( error = IsmFileReader_readNextFrame( ismReader, &ismMetadata ) ) != IVAS_ERR_OK )
{
fprintf( stderr, "\nError (%s) while reading ism metadata from: %s\n\n", ivas_error_to_string( error ), IsmFileReader_getFilePath( ismReader ) );
exit( -1 );
}
objectMetadataBuffer->positions[objIdx].azimuth = ismMetadata.azimuth;
objectMetadataBuffer->positions[objIdx].elevation = ismMetadata.elevation;
}
void readFromShorthandMetadata( Prndr_IsmPositionProvider *positionProvider,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer,
uint32_t objIdx )
{
uint32_t preUpdatePositionIdx;
uint32_t postUpdatePositionIdx;
preUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx];
if ( positionProvider->durationCounters[objIdx] == positionProvider->positionDurations[objIdx][preUpdatePositionIdx] )
{
positionProvider->durationCounters[objIdx] = 0;
positionProvider->currentPositionIdxs[objIdx] = ( positionProvider->currentPositionIdxs[objIdx] + 1 ) % positionProvider->numPositions[objIdx];
}
++positionProvider->durationCounters[objIdx];
postUpdatePositionIdx = positionProvider->currentPositionIdxs[objIdx];
objectMetadataBuffer->positions[objIdx] = positionProvider->positions[objIdx][postUpdatePositionIdx];
}
void Prndr_IsmPositionProvider_getNextFrame(
Prndr_IsmPositionProvider *positionProvider,
Prndr_AudioObjectMetadataBuffer *objectMetadataBuffer )
{
uint32_t objIdx;
int16_t aziShifted;
objectMetadataBuffer->numObjects = positionProvider->numObjects;
for ( objIdx = 0; objIdx < positionProvider->numObjects; ++objIdx )
{
if ( positionProvider->ismReaders[objIdx] != NULL )
{
getMetadataFromFileReader( positionProvider->ismReaders[objIdx], objectMetadataBuffer, objIdx );
}
else
{
readFromShorthandMetadata( positionProvider, objectMetadataBuffer, objIdx );
}
/* Wrap azimuth to lie within (-180, 180] range */
aziShifted = objectMetadataBuffer->positions[objIdx].azimuth + 180;
objectMetadataBuffer->positions[objIdx].azimuth = aziShifted <= 0 || aziShifted % 360 == 0 ? ( aziShifted % 360 ) + 180 : ( aziShifted % 360 ) - 180;
/* Clamp elevation to lie within [-90, 90] range (can't be wrapped easily) */
objectMetadataBuffer->positions[objIdx].elevation = min( max( objectMetadataBuffer->positions[objIdx].elevation, -90 ), 90 );
}
++positionProvider->frameCounter;
}
void Prndr_IsmPositionProvider_close( Prndr_IsmPositionProvider *positionProvider )
{
uint32_t i;
if ( positionProvider == NULL )
{
assert( !"Can't close Prndr_IsmPositionProvider - pointer is NULL" );
}
for ( i = 0; i < PRERENDERER_MAX_ISM_INPUTS; ++i )
{
if ( positionProvider->ismReaders[i] != NULL )
{
IsmFileReader_close( &positionProvider->ismReaders[i] );
}
if ( positionProvider->positions[i] != NULL )
{
count_free( positionProvider->positions[i] );
}
if ( positionProvider->positionDurations[i] != NULL )
{
count_free( positionProvider->positionDurations[i] );
}
}
count_free( positionProvider );
}
static void splitConfigFile( const char *mdfFilePath,
char *metadataString,
uint32_t *metadataStringLength,
char *wavFileName,
uint32_t *wavFileNameLength )
{
FILE *file;
uint32_t bufferlength;
char wavLine[FILENAME_MAX];
uint32_t currentPositionIdxs;
uint32_t mdlength;
memset( metadataString, 0, *metadataStringLength );
memset( wavFileName, 0, (int16_t) *wavFileNameLength );
file = fopen( mdfFilePath, "rb" );
if ( !file )
{
fprintf( stderr, "Couldn't open metadata file %s\n", mdfFilePath );
exit( -1 );
}
fseek( file, 0, SEEK_END );
bufferlength = ftell( file );
fseek( file, 0, SEEK_SET );
if ( fgets( wavLine, (int) *wavFileNameLength, file ) == NULL )
{
fprintf( stderr, "Error reading metadata\n" );
exit( -1 );
}
currentPositionIdxs = ftell( file );
if ( *wavFileNameLength < currentPositionIdxs )
{
assert( !"Couldn't read wavFileName, string buffer too small" );
}
if ( !sscanf( wavLine, "%s", wavFileName ) )
{
fprintf( stderr, "Error reading metadata\n" );
exit( -1 );
}
*wavFileNameLength = strlen( wavFileName );
mdlength = bufferlength - currentPositionIdxs;
/* "+1" for null termination */
if ( *metadataStringLength + 1 < mdlength )
{
assert( !"Couldn't read metadata string, string buffer too small" );
}
fread( metadataString, 1, mdlength, file );
metadataString[mdlength] = '\0';
*metadataStringLength = mdlength + 1;
fclose( file );
}
/* r: pointer to character following last found delimiter */
static char *readNextMetadataChunkFrom( char *start_char, char *line, const char *delimiter )
{
char *token;
/* start_char can be NULL - it's used to continue parsing with strtok */
assert( line != NULL && delimiter != NULL && "unexpected NULL ptr given to readNextMetadataChunkFrom()" );
token = strtok( start_char, delimiter );
/* End of string reached */
if ( token == NULL )
{
/* Clear `line` from previous contents and return NULL */
line[0] = '\0';
return NULL;
}
strcpy( line, token );
return token + strlen( token ) + 1;
}
/* r: pointer to character following last found delimiter */
static char *readNextMetadataChunk( char *line, const char *delimiter )
{
return readNextMetadataChunkFrom( NULL, line, delimiter );
}
static void parseUint8( char *line, uint8_t *ret )
{
char *ptr;
ptr = NULL;
*ret = strtol( line, &ptr, 10 );
if ( *ptr != '\0' )
{
fprintf( stderr, "Cannot parse string \"%s\" as an integer value\n", line );
exit( -1 );
}
}
static int8_t parseUint32( char *line, uint32_t *ret )
{
char *ptr;
ptr = " ";
*ret = strtol( line, &ptr, 10 );
if ( *ptr != '\0' )
{
return -1;
}
return 0;
}
static void parseOptionalInputValues(
char *line,
float *gain_dB )
{
char *parse_pos;
char *key;
char *value;
char *endptr;
endptr = NULL;
/* Set default values, in case some values are not specified */
*gain_dB = 0.f;
/* Save parsing position - will have to be passed to strtok to resume parsing after using strtok with non-NULL value below */
parse_pos = readNextMetadataChunk( line, "\n" );
/* Look for optional metadata until end of string or next input identifier is found */
while ( parse_pos != NULL && strcmp( line, "MC" ) != 0 && strcmp( line, "SBA" ) != 0 && strcmp( line, "ISM" ) != 0 )
{
key = strtok( line, ":" );
value = strtok( NULL, "\n" );
if ( strcmp( key, "gain_dB" ) == 0 )
{
*gain_dB = (float) strtod( value, &endptr );
if ( *endptr != '\0' )
{
fprintf( stderr, "Cannot parse string string \"%s\" as a float value\n", value );
exit( -1 );
}
}
else
{
fprintf( stderr, "Unsupported optional key: %s\n", key );
exit( -1 );
}
parse_pos = readNextMetadataChunkFrom( parse_pos, line, "\n" );
}
}
static void parseObjectPosition( char *line,
Prndr_AudioObjectPosition *position,
uint16_t *positionDuration )
{
char *endptr;
readNextMetadataChunk( line, "," );
*positionDuration = strtol( line, &endptr, 10 );
if ( *endptr != '\0' )
{
fprintf( stderr, "Error reading metadata\n" );
exit( -1 );
}
readNextMetadataChunk( line, "," );
position->azimuth = strtol( line, &endptr, 10 );
if ( *endptr != '\0' )
{
fprintf( stderr, "Error reading metadata\n" );
exit( -1 );
}
readNextMetadataChunk( line, "\n" );
position->elevation = strtol( line, &endptr, 10 );
if ( *endptr != '\0' )
{
fprintf( stderr, "Error reading metadata\n" );
exit( -1 );
}
}
static void parseIsm(
char *line,
char *inDir,
Prndr_InputConfig *inConfig,
Prndr_IsmPositionProvider *positionProvider,
int32_t idx )
{
uint32_t numberOfObjectPositionsToRead;
uint32_t i;
readNextMetadataChunk( line, "\n" );
parseUint8( line, &inConfig->audioObjects[idx].inputChannelIndex );
--inConfig->audioObjects[idx].inputChannelIndex; /* Convert from 1-indexing */
readNextMetadataChunk( line, "\n" );
/* Try to interpret line as number of positions to read */
if ( parseUint32( line, &numberOfObjectPositionsToRead ) == 0 )
{
positionProvider->numPositions[idx] = numberOfObjectPositionsToRead;
positionProvider->positions[idx] = count_calloc( numberOfObjectPositionsToRead, sizeof( Prndr_AudioObjectPosition ) );
positionProvider->positionDurations[idx] = count_calloc( numberOfObjectPositionsToRead, sizeof( uint16_t ) );
for ( i = 0; i < numberOfObjectPositionsToRead; ++i )
{
parseObjectPosition( line, &positionProvider->positions[idx][i], &positionProvider->positionDurations[idx][i] );
}
}
else /* If not a number, it is a relative path from main metadata file to a metadata file */
{
char fullpath[FILENAME_MAX];
*fullpath = '\0';
strncat( fullpath, inDir, strlen( inDir ) );
strncat( fullpath, line, sizeof( fullpath ) - strlen( fullpath ) - 1 );
if ( ( positionProvider->ismReaders[idx] = IsmFileReader_open( fullpath ) ) == NULL )
{
fprintf( stderr, "Error: ISM input metadata file %s could not be opened\n", line );
exit( -1 );
}
}
/* Read optional values */
parseOptionalInputValues( line, &inConfig->audioObjects[idx].gain_dB );
}
static void parseSba( char *line,
Prndr_InputConfig *inConfig,
int32_t idx )
{
uint8_t ambiOrder;
readNextMetadataChunk( line, "\n" );
parseUint8( line, &inConfig->ambisonicsBuses[idx].inputChannelIndex );
--inConfig->ambisonicsBuses[idx].inputChannelIndex; /* Convert from 1-indexing */
readNextMetadataChunk( line, "\n" );
parseUint8( line, &ambiOrder );
inConfig->ambisonicsBuses[idx].ambisonicsConfig = ambisonicsOrderToEnum( ambiOrder );
/* Read optional values */
parseOptionalInputValues( line, &inConfig->ambisonicsBuses[idx].gain_dB );
}
static void parseMc( char *line,
Prndr_InputConfig *inConfig,
int32_t idx )
{
uint8_t cicpIndex;
readNextMetadataChunk( line, "\n" );
parseUint8( line, &inConfig->multiChannelBuses[idx].inputChannelIndex );
--inConfig->multiChannelBuses[idx].inputChannelIndex; /* Convert from 1-indexing */
readNextMetadataChunk( line, "\n" );
parseUint8( line, &cicpIndex );
inConfig->multiChannelBuses[idx].speakerLayout = speakerLayoutCicpToEnum( cicpIndex );
/* Read optional values */
parseOptionalInputValues( line, &inConfig->multiChannelBuses[idx].gain_dB );
}
static void parseMetadata(
char *metadataString,
char *inDir,
Prndr_InputConfig *inConfig,
Prndr_IsmPositionProvider *positionProvider )
{
char line[PRERENDERER_MAX_METADATA_LINE_LENGTH];
char *delimiter;
char *token;
uint8_t totalNumberOfAudioObjects;
uint8_t counterChannelAudioObjects;
uint8_t counterAmbisonicsAudioObjects;
uint8_t counterMonoAudioObjects;
uint8_t num_parsed_inputs;
delimiter = "\n";
token = strtok( metadataString, delimiter );
if ( token == NULL )
{
fprintf( stderr, "Unexpected metadata format\n" );
exit( -1 );
}
if ( !sscanf( token, "%s", line ) )
{
fprintf( stderr, "Unexpected metadata format\n" );
exit( -1 );
}
parseUint8( line, &totalNumberOfAudioObjects );
if ( totalNumberOfAudioObjects <= 0 )
{
fprintf( stderr, "Invalid metadata: number of inputs should be > 0\n" );
exit( -1 );
}
num_parsed_inputs = 0;
counterChannelAudioObjects = 0;
counterAmbisonicsAudioObjects = 0;
counterMonoAudioObjects = 0;
readNextMetadataChunk( line, delimiter );
while ( num_parsed_inputs < totalNumberOfAudioObjects )
{
/* `line` will already contain the identifier ("MC", "SBA" or "ISM") after previous iteration */
if ( strcmp( line, "MC" ) == 0 )
{
++counterChannelAudioObjects;
if ( counterChannelAudioObjects > PRERENDERER_MAX_MC_INPUTS )
{
fprintf( stderr, "Metadata exceeds the supported number of MC inputs\n" );
exit( -1 );
}
parseMc( line, inConfig, counterChannelAudioObjects - 1 );
}
else if ( strcmp( line, "SBA" ) == 0 )
{
++counterAmbisonicsAudioObjects;
if ( counterAmbisonicsAudioObjects > PRERENDERER_MAX_SBA_INPUTS )
{
fprintf( stderr, "Metadata exceeds the supported number of SBA inputs\n" );
exit( -1 );
}
parseSba( line, inConfig, counterAmbisonicsAudioObjects - 1 );
}
else if ( strcmp( line, "ISM" ) == 0 )
{
++counterMonoAudioObjects;
if ( counterMonoAudioObjects > PRERENDERER_MAX_ISM_INPUTS )
{
fprintf( stderr, "Metadata exceeds the supported number of ISM inputs\n" );
exit( -1 );
}
parseIsm( line, inDir, inConfig, positionProvider, counterMonoAudioObjects - 1 );
}
else if ( line[0] == '\0' )
{
fprintf( stderr, "Metadata string too short - expected %d inputs, found %d.\n", totalNumberOfAudioObjects, num_parsed_inputs );
exit( -1 );
}
else
{
fprintf( stderr, "Unexpected metadata identifier\n" );
exit( -1 );
}
++num_parsed_inputs;
}
inConfig->numAudioObjects = counterMonoAudioObjects;
inConfig->numAmbisonicsBuses = counterAmbisonicsAudioObjects;
inConfig->numMultiChannelBuses = counterChannelAudioObjects;
positionProvider->numObjects = counterMonoAudioObjects;
/* check for trailing text */
token = strtok( NULL, delimiter );
if ( token != NULL && sscanf( token, "%s", line ) )
{
fprintf( stderr, "Trailing text in metadata file\n" );
exit( -1 );
}
}
void parseConfigFile( char *path, char *audioFilePath, Prndr_InputConfig *inConfig, Prndr_IsmPositionProvider *positionProvider )
{
uint32_t inAudioFilePathLen;
char inAudioFilePath[FILENAME_MAX];
uint32_t mtdStrLen;
char mtdStr[PRERENDERER_MAX_METADATA_LENGTH];
char inDir[FILENAME_MAX];
char *lastSlash = NULL;
inAudioFilePathLen = FILENAME_MAX;
mtdStrLen = PRERENDERER_MAX_METADATA_LENGTH;
splitConfigFile( path,
mtdStr,
&mtdStrLen,
inAudioFilePath,
&inAudioFilePathLen );
remove_cr( mtdStr );
convert_backslash( inAudioFilePath );
/* Trim config file path to get path to the dir containing it */
lastSlash = strrchr( path, SEP_FOLDER );
*inDir = '\0';
if ( lastSlash != NULL )
{
strncat( inDir, path, ( lastSlash - path + 1 ) );
}
/* Append audio file path (relative to config file location)
* to config file location path to get full absolute path */
strcpy( audioFilePath, inDir );
strncat( audioFilePath, inAudioFilePath, inAudioFilePathLen );
parseMetadata( mtdStr, inDir, inConfig, positionProvider );
}
static void convert_backslash( char *str )
{
int i, len;
/* check that all backslashes are correct on the given platform */
len = strlen( str );
for ( i = 0; i < len; i++ )
{
#ifdef _WIN32
if ( str[i] == '/' )
{
str[i] = '\\';
}
#else
if ( str[i] == '\\' )
{
str[i] = '/';
}
#endif
}
return;
}
static void remove_cr( char *str )
{
char *pos;
/* remove all \r characters from the string */
pos = strchr( str, '\r' );
while ( pos != NULL )
{
strcpy( pos, pos + 1 );
pos = strchr( pos, '\r' );
}
return;
}
0000,-030.00,+00.00,01.00,000.00,1.00
0001,-030.00,+00.00,01.00,000.00,1.00
0002,-030.00,+00.00,01.00,000.00,1.00
0003,-030.00,+00.00,01.00,000.00,1.00
0004,-030.00,+00.00,01.00,000.00,1.00
0005,-030.00,+00.00,01.00,000.00,1.00
0006,-030.00,+00.00,01.00,000.00,1.00
0007,-030.00,+00.00,01.00,000.00,1.00
0008,-030.00,+00.00,01.00,000.00,1.00
0009,-030.00,+00.00,01.00,000.00,1.00
0010,-030.00,+00.00,01.00,000.00,1.00
0011,-030.00,+00.00,01.00,000.00,1.00
0012,-030.00,+00.00,01.00,000.00,1.00
0013,-030.00,+00.00,01.00,000.00,1.00
0014,-030.00,+00.00,01.00,000.00,1.00
0015,-030.00,+00.00,01.00,000.00,1.00
0016,-030.00,+00.00,01.00,000.00,1.00
0017,-030.00,+00.00,01.00,000.00,1.00
0018,-030.00,+00.00,01.00,000.00,1.00
0019,-030.00,+00.00,01.00,000.00,1.00
0020,-030.00,+00.00,01.00,000.00,1.00
0021,-030.00,+00.00,01.00,000.00,1.00
0022,-030.00,+00.00,01.00,000.00,1.00
0023,-030.00,+00.00,01.00,000.00,1.00
0024,-030.00,+00.00,01.00,000.00,1.00
0025,-030.00,+00.00,01.00,000.00,1.00
0026,-030.00,+00.00,01.00,000.00,1.00
0027,-030.00,+00.00,01.00,000.00,1.00
0028,-030.00,+00.00,01.00,000.00,1.00
0029,-030.00,+00.00,01.00,000.00,1.00
0030,-030.00,+00.00,01.00,000.00,1.00
0031,-030.00,+00.00,01.00,000.00,1.00
0032,-030.00,+00.00,01.00,000.00,1.00
0033,-030.00,+00.00,01.00,000.00,1.00
0034,-030.00,+00.00,01.00,000.00,1.00
0035,-030.00,+00.00,01.00,000.00,1.00
0036,-030.00,+00.00,01.00,000.00,1.00
0037,-030.00,+00.00,01.00,000.00,1.00
0038,-030.00,+00.00,01.00,000.00,1.00
0039,-030.00,+00.00,01.00,000.00,1.00
0040,-030.00,+00.00,01.00,000.00,1.00
0041,-030.00,+00.00,01.00,000.00,1.00
0042,-030.00,+00.00,01.00,000.00,1.00
0043,-030.00,+00.00,01.00,000.00,1.00
0044,-030.00,+00.00,01.00,000.00,1.00
0045,-030.00,+00.00,01.00,000.00,1.00
0046,-030.00,+00.00,01.00,000.00,1.00
0047,-030.00,+00.00,01.00,000.00,1.00
0048,-030.00,+00.00,01.00,000.00,1.00
0049,-030.00,+00.00,01.00,000.00,1.00
0000,+000.00,+00.00,01.00,000.00,1.00
0001,+000.00,+00.00,01.00,000.00,1.00
0002,+000.00,+00.00,01.00,000.00,1.00
0003,+000.00,+00.00,01.00,000.00,1.00
0004,+000.00,+00.00,01.00,000.00,1.00
0005,+000.00,+00.00,01.00,000.00,1.00
0006,+000.00,+00.00,01.00,000.00,1.00
0007,+000.00,+00.00,01.00,000.00,1.00
0008,+000.00,+00.00,01.00,000.00,1.00
0009,+000.00,+00.00,01.00,000.00,1.00
0010,+000.00,+00.00,01.00,000.00,1.00
0011,+000.00,+00.00,01.00,000.00,1.00
0012,+000.00,+00.00,01.00,000.00,1.00
0013,+000.00,+00.00,01.00,000.00,1.00
0014,+000.00,+00.00,01.00,000.00,1.00
0015,+000.00,+00.00,01.00,000.00,1.00
0016,+000.00,+00.00,01.00,000.00,1.00
0017,+000.00,+00.00,01.00,000.00,1.00
0018,+000.00,+00.00,01.00,000.00,1.00
0019,+000.00,+00.00,01.00,000.00,1.00
0020,+000.00,+00.00,01.00,000.00,1.00
0021,+000.00,+00.00,01.00,000.00,1.00
0022,+000.00,+00.00,01.00,000.00,1.00
0023,+000.00,+00.00,01.00,000.00,1.00
0024,+000.00,+00.00,01.00,000.00,1.00
0025,+000.00,+00.00,01.00,000.00,1.00
0026,+000.00,+00.00,01.00,000.00,1.00
0027,+000.00,+00.00,01.00,000.00,1.00
0028,+000.00,+00.00,01.00,000.00,1.00
0029,+000.00,+00.00,01.00,000.00,1.00
0030,+000.00,+00.00,01.00,000.00,1.00
0031,+000.00,+00.00,01.00,000.00,1.00
0032,+000.00,+00.00,01.00,000.00,1.00
0033,+000.00,+00.00,01.00,000.00,1.00
0034,+000.00,+00.00,01.00,000.00,1.00
0035,+000.00,+00.00,01.00,000.00,1.00
0036,+000.00,+00.00,01.00,000.00,1.00
0037,+000.00,+00.00,01.00,000.00,1.00
0038,+000.00,+00.00,01.00,000.00,1.00
0039,+000.00,+00.00,01.00,000.00,1.00
0040,+000.00,+00.00,01.00,000.00,1.00
0041,+000.00,+00.00,01.00,000.00,1.00
0042,+000.00,+00.00,01.00,000.00,1.00
0043,+000.00,+00.00,01.00,000.00,1.00
0044,+000.00,+00.00,01.00,000.00,1.00
0045,+000.00,+00.00,01.00,000.00,1.00
0046,+000.00,+00.00,01.00,000.00,1.00
0047,+000.00,+00.00,01.00,000.00,1.00
0048,+000.00,+00.00,01.00,000.00,1.00
0049,+000.00,+00.00,01.00,000.00,1.00
0000,+030.00,+00.00,01.00,000.00,1.00
0001,+030.00,+00.00,01.00,000.00,1.00
0002,+030.00,+00.00,01.00,000.00,1.00
0003,+030.00,+00.00,01.00,000.00,1.00
0004,+030.00,+00.00,01.00,000.00,1.00
0005,+030.00,+00.00,01.00,000.00,1.00
0006,+030.00,+00.00,01.00,000.00,1.00
0007,+030.00,+00.00,01.00,000.00,1.00
0008,+030.00,+00.00,01.00,000.00,1.00
0009,+030.00,+00.00,01.00,000.00,1.00
0010,+030.00,+00.00,01.00,000.00,1.00
0011,+030.00,+00.00,01.00,000.00,1.00
0012,+030.00,+00.00,01.00,000.00,1.00
0013,+030.00,+00.00,01.00,000.00,1.00
0014,+030.00,+00.00,01.00,000.00,1.00
0015,+030.00,+00.00,01.00,000.00,1.00
0016,+030.00,+00.00,01.00,000.00,1.00
0017,+030.00,+00.00,01.00,000.00,1.00
0018,+030.00,+00.00,01.00,000.00,1.00
0019,+030.00,+00.00,01.00,000.00,1.00
0020,+030.00,+00.00,01.00,000.00,1.00
0021,+030.00,+00.00,01.00,000.00,1.00
0022,+030.00,+00.00,01.00,000.00,1.00
0023,+030.00,+00.00,01.00,000.00,1.00
0024,+030.00,+00.00,01.00,000.00,1.00
0025,+030.00,+00.00,01.00,000.00,1.00
0026,+030.00,+00.00,01.00,000.00,1.00
0027,+030.00,+00.00,01.00,000.00,1.00
0028,+030.00,+00.00,01.00,000.00,1.00
0029,+030.00,+00.00,01.00,000.00,1.00
0030,+030.00,+00.00,01.00,000.00,1.00
0031,+030.00,+00.00,01.00,000.00,1.00
0032,+030.00,+00.00,01.00,000.00,1.00
0033,+030.00,+00.00,01.00,000.00,1.00
0034,+030.00,+00.00,01.00,000.00,1.00
0035,+030.00,+00.00,01.00,000.00,1.00
0036,+030.00,+00.00,01.00,000.00,1.00
0037,+030.00,+00.00,01.00,000.00,1.00
0038,+030.00,+00.00,01.00,000.00,1.00
0039,+030.00,+00.00,01.00,000.00,1.00
0040,+030.00,+00.00,01.00,000.00,1.00
0041,+030.00,+00.00,01.00,000.00,1.00
0042,+030.00,+00.00,01.00,000.00,1.00
0043,+030.00,+00.00,01.00,000.00,1.00
0044,+030.00,+00.00,01.00,000.00,1.00
0045,+030.00,+00.00,01.00,000.00,1.00
0046,+030.00,+00.00,01.00,000.00,1.00
0047,+030.00,+00.00,01.00,000.00,1.00
0048,+030.00,+00.00,01.00,000.00,1.00
0049,+030.00,+00.00,01.00,000.00,1.00
0000,+000.00,+00.00,01.00,000.00,1.00
0001,+007.00,+00.00,01.00,000.00,1.00
0002,+014.00,+00.00,01.00,000.00,1.00
0003,+022.00,+00.00,01.00,000.00,1.00
0004,+029.00,+00.00,01.00,000.00,1.00
0005,+036.00,+00.00,01.00,000.00,1.00
0006,+043.00,+00.00,01.00,000.00,1.00
0007,+050.00,+00.00,01.00,000.00,1.00
0008,+058.00,+00.00,01.00,000.00,1.00
0009,+065.00,+00.00,01.00,000.00,1.00
0010,+072.00,+00.00,01.00,000.00,1.00
0011,+079.00,+00.00,01.00,000.00,1.00
0012,+086.00,+00.00,01.00,000.00,1.00
0013,+094.00,+00.00,01.00,000.00,1.00
0014,+101.00,+00.00,01.00,000.00,1.00
0015,+108.00,+00.00,01.00,000.00,1.00
0016,+115.00,+00.00,01.00,000.00,1.00
0017,+122.00,+00.00,01.00,000.00,1.00
0018,+130.00,+00.00,01.00,000.00,1.00
0019,+137.00,+00.00,01.00,000.00,1.00
0020,+144.00,+00.00,01.00,000.00,1.00
0021,+151.00,+00.00,01.00,000.00,1.00
0022,+158.00,+00.00,01.00,000.00,1.00
0023,+166.00,+00.00,01.00,000.00,1.00
0024,+173.00,+00.00,01.00,000.00,1.00
0025,+180.00,+00.00,01.00,000.00,1.00
0026,+187.00,+00.00,01.00,000.00,1.00
0027,+194.00,+00.00,01.00,000.00,1.00
0028,+202.00,+00.00,01.00,000.00,1.00
0029,+209.00,+00.00,01.00,000.00,1.00
0030,+216.00,+00.00,01.00,000.00,1.00
0031,+223.00,+00.00,01.00,000.00,1.00
0032,+230.00,+00.00,01.00,000.00,1.00
0033,+238.00,+00.00,01.00,000.00,1.00
0034,+245.00,+00.00,01.00,000.00,1.00
0035,+252.00,+00.00,01.00,000.00,1.00
0036,+259.00,+00.00,01.00,000.00,1.00
0037,+266.00,+00.00,01.00,000.00,1.00
0038,+274.00,+00.00,01.00,000.00,1.00
0039,+281.00,+00.00,01.00,000.00,1.00
0040,+288.00,+00.00,01.00,000.00,1.00
0041,+295.00,+00.00,01.00,000.00,1.00
0042,+302.00,+00.00,01.00,000.00,1.00
0043,+310.00,+00.00,01.00,000.00,1.00
0044,+317.00,+00.00,01.00,000.00,1.00
0045,+324.00,+00.00,01.00,000.00,1.00
0046,+331.00,+00.00,01.00,000.00,1.00
0047,+338.00,+00.00,01.00,000.00,1.00
0048,+346.00,+00.00,01.00,000.00,1.00
0049,+353.00,+00.00,01.00,000.00,1.00
......@@ -28,6 +28,7 @@
import argparse
import os
from enum import Enum
from itertools import combinations
from typing import Optional, Tuple, Union
......@@ -75,6 +76,12 @@ def wrap_angles(
return azi, ele
class EfapDmxType(Enum):
NONE = 0
AMPLITUDE = 1
INTENSITY = 2
class EfapVertex:
"""
Vertex data structure for EFAP
......@@ -89,9 +96,17 @@ class EfapVertex:
Elevation of vertex
is_ghost : bool
Whether the vertex is a ghost, default is False
dmx_type : EfapDmxType
Downmix type for ghost vertices
"""
def __init__(self, azi: float, ele: float, is_ghost: Optional[bool] = False):
def __init__(
self,
azi: float,
ele: float,
is_ghost: Optional[bool] = False,
dmx_type: Optional[EfapDmxType] = EfapDmxType.INTENSITY,
):
self.azi, self.ele = wrap_angles(azi, ele)
self.pos = np.array(
[
......@@ -108,6 +123,7 @@ class EfapVertex:
) # vertices on the median plane have lowest index
self.is_ghost = is_ghost
self.dmx_type = dmx_type
def __str__(self):
str_ = f"a{self.azi}e{self.ele}"
......@@ -132,13 +148,15 @@ class EFAP:
Azimuth positions of the loudspeaker array
elevations : npndarray
Elevation postions of the loudspeaker array
intensity_panning : bool
Whether intensity panning is enabled or not
Examples
--------
>>> from EFAP import EFAP
>>> panner = EFAP([30, -30, 0, 110, -110], [0, 0, 0, 0, 0]])
>>> panner.pan(15, 45, intensity_panning=False)
>>> panner = EFAP([30, -30, 0, 110, -110], [0, 0, 0, 0, 0], False)
>>> panner.pan(15, 45)
array([0.66742381, 0.19069252, 0.66742381, 0.19069252, 0.19069252])
"""
......@@ -152,7 +170,10 @@ class EFAP:
_EFAP_THRESH_TRI = 1e-10 # tolerance for a point to be inside a triangle
def __init__(
self, azimuths: Union[list, np.ndarray], elevations: Union[list, np.ndarray]
self,
azimuths: Union[list, np.ndarray],
elevations: Union[list, np.ndarray],
intensity_panning: Optional[bool] = False,
):
# validation
azimuths = np.array(azimuths)
......@@ -164,6 +185,9 @@ class EFAP:
if azimuths.shape != elevations.shape:
raise ValueError("Mismatch between loudspeaker azimuths and elevations")
# set EFIP flag
self.intensity_panning = intensity_panning
# initialise vertices and add ghost loudspeakers if needed
self.verts = np.array(
[EfapVertex(azi, ele) for azi, ele in zip(azimuths, elevations)]
......@@ -198,11 +222,28 @@ class EFAP:
"""
ele = [v.ele for v in self.verts]
dmx_type = EfapDmxType.INTENSITY
# add ghost loudspeakers at the poles if necessary
if max(ele) < self._EFAP_POLAR_ELE:
self.verts = np.append(self.verts, EfapVertex(0, 90, True))
if self.intensity_panning:
if max(ele) > self._EFAP_THRESH_MID_LAYER:
dmx_type = EfapDmxType.NONE
else:
dmx_type = EfapDmxType.AMPLITUDE
self.verts = np.append(self.verts, EfapVertex(0, 90, True, dmx_type))
if min(ele) > -self._EFAP_POLAR_ELE:
self.verts = np.append(self.verts, EfapVertex(0, -90, True))
if self.intensity_panning:
if min(ele) < -self._EFAP_THRESH_MID_LAYER:
dmx_type = EfapDmxType.NONE
else:
dmx_type = EfapDmxType.AMPLITUDE
self.verts = np.append(self.verts, EfapVertex(0, -90, True, dmx_type))
# check for large gaps in the middle horizontal layer
mid_spkrs = [
......@@ -390,10 +431,21 @@ class EFAP:
vec = M @ vec
M2[:, i] = vec
# energy distribution for real LS and amplitude distribution for ghost LS
self.dmx_mat = M2[:-n_ghost, :]
# amplitude downmix for real loudspeakers
self.dmx_mat[:, :-n_ghost] = np.sqrt(self.dmx_mat[:, :-n_ghost])
# distribute ghosts according to downmix type
for i, v in enumerate(self.verts):
if v.is_ghost:
if v.dmx_type == EfapDmxType.NONE:
self.dmx_mat[:, i] = 0
elif v.dmx_type == EfapDmxType.AMPLITUDE:
pass
else:
self.dmx_mat[:, i] = np.sqrt(self.dmx_mat[:, i])
def _tri2poly(self) -> None:
"""
Merge hull triangles into polygons if they are coplanar
......@@ -496,8 +548,11 @@ class EFAP:
)
if mod:
if not np.isnan(A[0]):
A[0] %= mod
if not np.isnan(B[0]):
B[0] %= mod
if not np.isnan(C[0]):
C[0] %= mod
if self._in_triangle(P, A, B, C):
......@@ -560,14 +615,16 @@ class EFAP:
return True, None
# if the azimuth difference is large, perform the 2D check again with azimuths wrapped to (-360, 0] and [0, 360)
# RuntimeWarning due to NaNs can be safely ignored, _in_triangle() accounts for them
if np.nanmax(azi) - np.nanmin(azi) > 180:
for tri in combinations(poly, 3):
A = np.array(self._get_azi_ele(tri[0]))
B = np.array(self._get_azi_ele(tri[1]))
C = np.array(self._get_azi_ele(tri[2]))
if not np.isnan(A[0]):
A[0] %= 360
if not np.isnan(B[0]):
B[0] %= 360
if not np.isnan(C[0]):
C[0] %= 360
if self._in_triangle(P, A, B, C):
return True, 360
......@@ -576,8 +633,11 @@ class EFAP:
A = np.array(self._get_azi_ele(tri[0]))
B = np.array(self._get_azi_ele(tri[1]))
C = np.array(self._get_azi_ele(tri[2]))
if not np.isnan(A[0]):
A[0] %= -360
if not np.isnan(B[0]):
B[0] %= -360
if not np.isnan(C[0]):
C[0] %= -360
if self._in_triangle(P, A, B, C):
return True, -360
......@@ -711,9 +771,7 @@ class EFAP:
return surface
def _compute_gains_point(
self, azimuth: float, elevation: float, intensity_panning: bool = False
) -> np.ndarray:
def _compute_gains_point(self, azimuth: float, elevation: float) -> np.ndarray:
"""
Compute gains for the requested panning position
......@@ -724,8 +782,6 @@ class EFAP:
Azimuth of requested panning position
elevation : float
Elevation of requested panning position
intensity_panning : bool
Flag whether to use intensity panning (Default is False == amplitude panning)
Returns
-------
......@@ -736,18 +792,40 @@ class EFAP:
raise ValueError(f"Angles cannot be NaNs : ({azimuth}, {elevation})")
azimuth, elevation = wrap_angles(azimuth, elevation)
point_pos = [
np.cos(np.deg2rad(azimuth)) * np.cos(np.deg2rad(elevation)),
np.sin(np.deg2rad(azimuth)) * np.cos(np.deg2rad(elevation)),
np.sin(np.deg2rad(elevation)),
]
# find the polygon corresponding to the given point
found_poly = None
mod = None
# filter the polygon list with a quick 2d check
found_polys = []
for poly in self.polys:
in_poly, mod = self._in_polygon(azimuth, elevation, poly)
if in_poly:
found_poly = poly
break
else:
found_polys.append((poly, mod))
if not found_polys:
raise AssertionError("Unexpected error during panning")
# find a visible polygon with the smallest distance
dist = []
for poly, mod in found_polys:
surface = self.verts[poly]
d = self._point_plane_dist(
surface[0].pos,
surface[1].pos,
surface[2].pos,
point_pos,
)
if d >= 0:
dist.append(d)
else:
dist.append(np.inf)
found_poly, mod = found_polys[np.argmin(dist)]
# compute gains for the polygon vertices
poly_gain = self._pan_EFAP_poly(azimuth, elevation, found_poly, mod)
......@@ -757,7 +835,7 @@ class EFAP:
gains = gains @ self.dmx_mat.T
gains = gains / np.linalg.norm(gains)
if intensity_panning:
if self.intensity_panning:
gains = np.sqrt(gains / np.sum(gains))
return gains
......@@ -788,11 +866,11 @@ class EFAP:
azimuths = np.array(azimuths)
elevations = np.array(elevations)
if azimuths.size == 1 and elevations.size == 1:
return self._compute_gains_point(azimuths, elevations, intensity_panning)
return self._compute_gains_point(azimuths, elevations)
elif np.squeeze(azimuths).ndim == 1 and np.squeeze(elevations).ndim == 1:
gains = []
for a, e in zip(azimuths, elevations):
gains.append(self._compute_gains_point(a, e, intensity_panning))
gains.append(self._compute_gains_point(a, e))
return np.vstack(gains)
else:
raise ValueError(
......@@ -816,8 +894,8 @@ def main(args):
speaker_positions = np.loadtxt(
os.path.abspath(args.input), delimiter=",", max_rows=2
)
panner = EFAP(speaker_positions[0, :], speaker_positions[1, :])
print(panner.pan(args.azimuth, args.elevation, args.efip))
panner = EFAP(speaker_positions[0, :], speaker_positions[1, :], args.efip)
print(panner.pan(args.azimuth, args.elevation))
if __name__ == "__main__":
......
......@@ -84,7 +84,7 @@ def main():
"--outformat",
type=str,
metavar="OUTFORMAT",
help="Output format (default = %(default)s, same as input format)",
help="Output format (default = %(default)s, same as input format). Can be a custom loudspeaker layout file.",
default=None,
)
parser.add_argument(
......@@ -116,13 +116,6 @@ def main():
help="list of input metadata files (only relevant for ISM and MASA input)",
default=None,
)
parser.add_argument(
"-y",
"--layoutfile",
help="File describing a custom LS layout",
type=str,
default=None,
)
parser.add_argument(
"-fc",
"--outfc",
......@@ -249,7 +242,6 @@ def main():
in_fs=args.infs,
in_nchans=args.inchan,
in_meta_files=args.metadata,
in_ls_layout_file=args.layoutfile,
out_format=args.outformat,
out_fs=args.outfs,
out_fc=args.outfc,
......@@ -277,7 +269,6 @@ def main():
in_format=args.outformat,
in_fs=args.outfs,
in_meta_files=args.metadata,
in_ls_layout_file=args.layoutfile,
out_format="BINAURAL",
output_loudness=args.normalize,
loudness_tool=args.loudness_tool,
......
......@@ -286,7 +286,7 @@ def compare(ref: np.ndarray, test: np.ndarray, fs: int) -> dict:
def getdelay(x: np.ndarray, y: np.ndarray) -> int:
"""Get the delay between two audios signals
"""Get the delay between two audio signals
Parameters
----------
......
......@@ -39,12 +39,10 @@ import numpy as np
import scipy.interpolate as interp
import scipy.io as sio
import scipy.signal as sig
from pyaudio3dtools.rotation import rotateHOA, rotateISM, rotateMC
from pyaudio3dtools import audioarray, spatialaudioformat
from pyaudio3dtools import audioarray, spatialaudioformat, spatialaudioconvert
from pyaudio3dtools.constants import *
from pyaudio3dtools.rotateHOA import rotateHOA
from pyaudio3dtools.rotateISM import rotateISM
from pyaudio3dtools.rotateMC import rotateMC
main_logger = logging.getLogger("__main__")
logger = main_logger.getChild(__name__)
......@@ -53,6 +51,10 @@ logger.setLevel(logging.DEBUG)
"""" Helper functions """
def NS2SA(fs, x):
return int(int(fs / 100) * ((x) / 100) / 100000)
def read_hrirs_from_mat(
hrirs_path: str = "/HRIRs_mat/ORANGE_HRIR_53_48000_combined.mat",
) -> np.ndarray:
......@@ -87,17 +89,17 @@ def read_hrirs_from_mat(
def get_IR(
in_format: spatialaudioformat.Format,
out_format: spatialaudioformat.Format,
in_spfmt: spatialaudioformat.Format,
out_spfmt: spatialaudioformat.Format,
dataset: str,
) -> Tuple[np.ndarray, np.ndarray]:
) -> Tuple[np.ndarray, np.ndarray, float]:
"""get_IR
Parameters
----------
in_format: spatialaudioformat
in_spfmt: spatialaudioformat
input spatial audio format
out_format: spatialaudioformat
out_spfmt: spatialaudioformat
output spatial audio format
dataset: str
name of the HRIRs or BRIRs dataset
......@@ -111,7 +113,7 @@ def get_IR(
"""
# override for BRIRs, currently only one option
if out_format.name == "BINAURAL_ROOM":
if out_spfmt.name == "BINAURAL_ROOM":
dataset = "mozart_iis"
# dataset file prefix
......@@ -125,41 +127,43 @@ def get_IR(
raise ValueError(f"Unsupported dataset '{dataset}' for HRIRs")
# dataset file suffix
if in_format.name.startswith("ISM") or in_format.name.startswith("CUSTOM_LS"):
if in_spfmt.name.startswith("ISM") or in_spfmt.altname.startswith("CUSTOM_LS"):
suffix = "full.mat"
elif in_format.isloudspeaker and in_format.nchannels > 1:
elif in_spfmt.isloudspeaker and in_spfmt.nchannels > 1:
suffix = "combined.mat"
elif in_format.ambi_order > 0 or in_format.name.upper() == "MONO":
elif in_spfmt.ambi_order > 0 or in_spfmt.name.upper() == "MONO":
suffix = "SBA3.mat"
else:
raise ValueError(
f"Unsupported format '{in_format.name}' for dataset '{dataset}' for HRIRs"
f"Unsupported format '{in_spfmt.name}' for dataset '{dataset}' for HRIRs"
)
IR, SourcePosition = read_hrirs_from_mat("_".join([prefix, suffix]))
if in_format.name.startswith("MONO"):
latency_smp = float(np.min(np.argmax(np.sum(np.abs(IR), axis=(1)), axis=(0))))
if in_spfmt.name.startswith("MONO"):
IR = IR[:, :, :1] # use omni/W from SBA
elif in_format.name.startswith("STEREO"):
elif in_spfmt.name.startswith("STEREO"):
IR = IR[:, :, :2] # use L and R channels
elif in_format.isloudspeaker and not in_format.name.startswith("CUSTOM_LS"):
elif in_spfmt.isloudspeaker and not in_spfmt.altname.startswith("CUSTOM_LS"):
# extract positions from the combined file
tmpformat = spatialaudioformat.Format("COMBINED")
tmp_spfmt = spatialaudioformat.Format("COMBINED")
IR_tmp = IR.copy()
IR = np.zeros([IR_tmp.shape[0], IR_tmp.shape[1], in_format.nchannels])
IR = np.zeros([IR_tmp.shape[0], IR_tmp.shape[1], in_spfmt.nchannels])
ir_index = 0
for i in range(tmpformat.nchannels):
for j in range(in_format.nchannels):
for i in range(tmp_spfmt.nchannels):
for j in range(in_spfmt.nchannels):
if (
tmpformat.ls_azi[i] == in_format.ls_azi[j]
and tmpformat.ls_ele[i] == in_format.ls_ele[j]
tmp_spfmt.ls_azi[i] == in_spfmt.ls_azi[j]
and tmp_spfmt.ls_ele[i] == in_spfmt.ls_ele[j]
):
if j != in_format.lfe_index[0]:
if j != in_spfmt.lfe_index[0]:
IR[:, :, ir_index] = IR_tmp[:, :, i]
ir_index += 1
return IR, SourcePosition
return IR, SourcePosition, latency_smp
def FindFilter(SourcePosition: np.ndarray, azi: float, ele: float) -> int:
......@@ -256,7 +260,7 @@ def binaural_fftconv_framewise(
SourcePosition: np.ndarray,
azi: np.ndarray = None,
ele: np.ndarray = None,
frame_len: int = (IVAS_FRAME_LEN_MS // 4) * 48000,
frame_len: int = (IVAS_FRAME_LEN_MS // 4) * 48,
interp_method="linear",
verbose=False,
) -> np.ndarray:
......@@ -290,7 +294,7 @@ def binaural_fftconv_framewise(
"""
sig_len = x.shape[0]
frame_len = (IVAS_FRAME_LEN_MS // 4) * 48000
frame_len = (IVAS_FRAME_LEN_MS // 4) * 48
N_frames = int(sig_len / frame_len)
N_HRIR_taps = IR.shape[2]
......@@ -306,8 +310,8 @@ def binaural_fftconv_framewise(
[np.repeat(ele, N_frames // len(ele)), ele[: N_frames % len(ele)]]
)
iGs = np.zeros([N_frames], dtype=int)
mGs = np.zeros([N_frames], dtype=int)
iGs = np.zeros([N_frames + 1], dtype=int)
mGs = np.zeros([N_frames + 1], dtype=int)
# store trajectory as a sequence of indices of source positions
# on the HRTF database in a compressed format such that, for
......@@ -396,8 +400,9 @@ def binaural_fftconv_framewise(
y = np.zeros([sig_len + T_rev, 2])
y0 = np.zeros([N_rev, sig_len + T_rev, 2])
b = np.linspace(0.0, 1.0, frame_len, endpoint=False)
a = 1.0 - b
fade_in = np.arange(frame_len) / (frame_len - 1)
fade_in = fade_in[:, np.newaxis]
fade_out = 1.0 - fade_in
for i_ear in [0, 1]:
......@@ -419,8 +424,8 @@ def binaural_fftconv_framewise(
i2 = (i_frame + 1) * frame_len
i2p = i1 + T_rev
a = np.linspace(0.0, 1.0, T_rev, endpoint=False)
b = 1.0 - a
fade_out = np.linspace(0.0, 1.0, T_rev, endpoint=False)
fade_in = 1.0 - fade_out
for j_frame in [0, 1]:
G_n_m = G[min(j_frame + i_frame, N_frames - 1), :]
......@@ -428,7 +433,10 @@ def binaural_fftconv_framewise(
np.squeeze(x[i1:i2]), G_n_m
)
y[i1:i2p, i_ear] += a * y0[0, i1:i2p, i_ear] + b * y0[1, i1:i2p, i_ear]
y[i1:i2p, i_ear] = (
np.squeeze(fade_out) * y0[0, i1:i2p, i_ear]
+ np.squeeze(fade_in) * y0[1, i1:i2p, i_ear]
)
t1 = timeit.default_timer()
fps = (i_frame + 1) / (t1 - t0)
......@@ -464,7 +472,10 @@ def binaural_fftconv_framewise(
y0[0, j1:j2p, i_ear] += sig.oaconvolve(np.squeeze(x[j1:j2]), G0)
y0[1, j1:j2p, i_ear] += sig.oaconvolve(np.squeeze(x[j1:j2]), G1)
y[i1:i2, i_ear] = a * y0[0, i1:i2, i_ear] + b * y0[1, i1:i2, i_ear]
y[i1:i2, i_ear] = (
np.squeeze(fade_out) * y0[0, i1:i2, i_ear]
+ np.squeeze(fade_in) * y0[1, i1:i2, i_ear]
)
t1 = timeit.default_timer()
fps = (i_frame + 1) / (t1 - t0)
......@@ -489,8 +500,12 @@ def binaural_render_LFE(
fs: int = 48000,
lfe_index: list = [3],
LFE_gain: float = 10 ** (5.5 / 20),
hrir_latency_smp: float = 0,
latency_smp: int = 0,
) -> np.ndarray:
"""
Extract LFE from the given input and render
it binaurally, accounting for delay of the
"""
lfe = x[:, lfe_index].copy()
......@@ -498,6 +513,9 @@ def binaural_render_LFE(
if lfe.shape[1] > 1:
lfe = np.sum(lfe, axis=1)
# TODO tmu - disabled temporarily here, disabled in C
lfe_delay_ns = 0
"""
# 120 Hz low-pass filtering for LFE using IVAS filter coefficients
if fs == 48000:
lfe = sig.sosfilt(IVAS_LPF_4_BUTTER_48K_SOS, lfe, axis=0)
......@@ -505,11 +523,13 @@ def binaural_render_LFE(
raise NotImplementedError("Only 48 kHz supported at the moment!")
# 3.5ms LP filter delay from IVAS ROM
filter_delay = int(3.5 * fs / 1000)
lfe_delay_ns = 0.0035 * 1e9
lfe_delay_smp = round(lfe_delay_ns * fs / 1e9)
# delay adjustment
lfe = np.roll(lfe, filter_delay, axis=0)
lfe[:filter_delay, :] = 0
# Delay LFE by the same amount as the HRTF delay
lfe = np.roll(lfe, round(latency_smp), axis=0)
lfe[0 : round(latency_smp), :] = 0
"""
# apply gain
lfe *= LFE_gain
......@@ -517,7 +537,7 @@ def binaural_render_LFE(
# duplicate for each binaural channel
lfe = np.hstack([lfe, lfe])
return lfe
return lfe, lfe_delay_ns
""" Format specific wrapper functions """
......@@ -526,41 +546,46 @@ def binaural_render_LFE(
def render_custom_ls_binaural(
x: np.ndarray,
fs: int,
in_format: spatialaudioformat.Format,
in_spfmt: spatialaudioformat.Format,
out_spfmt: spatialaudioformat.Format,
IR: np.ndarray,
SourcePosition: np.ndarray,
trajectory: np.ndarray,
) -> np.ndarray:
if in_format.name != "CUSTOM_LS":
raise ValueError(
f"Unsupported format {in_format.name} for CUSTOM_LS binaural rendering!"
)
ls_azi_all = in_format.ls_azi
ls_ele_all = in_format.ls_ele
lfe_index_all = in_format.lfe_index
ls_azi_all = in_spfmt.ls_azi
ls_ele_all = in_spfmt.ls_ele
lfe_index_all = in_spfmt.lfe_index
logger.info(" Processing channels on custom LS layout")
logger.info("azi: {}".format(ls_azi_all))
logger.info("ele: {}".format(ls_ele_all))
logger.info("lfe_index: {}".format(lfe_index_all))
y = audioarray.resample(x, fs, 48000)
azis = ", ".join([f"{a:7.2f}" for a in ls_azi_all])
eles = ", ".join([f"{e:7.2f}" for e in ls_ele_all])
logger.info(f" azi: {azis}")
logger.info(f" ele: {eles}")
logger.info(f" lfe_index: {lfe_index_all}")
if out_spfmt.name == "BINAURAL_ROOM":
tmp_spfmt = spatialaudioformat.Format("7_1_4")
x = spatialaudioconvert.convert_mc(x, in_spfmt, tmp_spfmt)
ls_azi_all = tmp_spfmt.ls_azi
ls_ele_all = tmp_spfmt.ls_ele
lfe_index_all = tmp_spfmt.lfe_index
logger.info(f" {in_spfmt.name} -> {tmp_spfmt.name} -> {out_spfmt.name}")
frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000)
sig_len = y.shape[0]
sig_len = x.shape[0]
N_frames = int(sig_len / frame_len)
i_ls = 0
y_all = np.zeros([sig_len, 2])
for i_chan in range(y.shape[1]):
y = np.zeros([sig_len, 2])
for i_chan in range(x.shape[1]):
# skip LFE
if i_chan in lfe_index_all:
continue
# skip silent (or very low volume) channels
if np.allclose(y[:, i_chan], 0.0, atol=32.0):
if np.allclose(x[:, i_chan], 0.0, atol=32.0):
continue
ls_azi = np.repeat(ls_azi_all[i_ls], N_frames)
......@@ -568,8 +593,8 @@ def render_custom_ls_binaural(
azi, ele = rotateISM(ls_azi, ls_ele, trajectory=trajectory)
y_all += binaural_fftconv_framewise(
y[:, i_chan],
y += binaural_fftconv_framewise(
x[:, i_chan],
IR,
SourcePosition,
frame_len=frame_len,
......@@ -579,24 +604,20 @@ def render_custom_ls_binaural(
)
i_ls += 1
y = audioarray.resample(y_all, 48000, fs)
return y_all
return y
def render_ism_binaural(
x: np.ndarray,
fs: int,
in_format: spatialaudioformat.Format,
IR: np.ndarray,
SourcePosition: np.ndarray,
trajectory: np.ndarray,
in_pos: np.ndarray,
) -> np.ndarray:
y = audioarray.resample(x, fs, 48000)
frame_len = (IVAS_FRAME_LEN_MS // 4) * (fs // 1000)
sig_len = y.shape[0]
sig_len = x.shape[0]
N_frames = int(sig_len / frame_len)
# get ISM metadata and repeat it nsubframe times
......@@ -606,17 +627,16 @@ def render_ism_binaural(
[pos["azimuth"], pos["elevation"]] for _ in range(pos["use_for_frames"])
)
pos_data = np.array(pos_data)
pos_data = np.tile(pos_data, (4, 1))
pos_data = np.repeat(pos_data, 4, axis=0)
# extract positions only according to the audio duration
pos_data = pos_data[:N_frames, :]
y_all = np.zeros([sig_len, 2])
azi, ele = rotateISM(pos_data[:, 0], pos_data[:, 1], trajectory=trajectory)
y_all += binaural_fftconv_framewise(
y,
y = np.zeros([sig_len, 2])
y += binaural_fftconv_framewise(
x,
IR,
SourcePosition,
frame_len=frame_len,
......@@ -625,9 +645,40 @@ def render_ism_binaural(
verbose=False,
)
y = audioarray.resample(y_all, 48000, fs)
return y
def render_masa_binaural(
x: np.ndarray,
fs: int,
in_spfmt: spatialaudioformat.Format,
IR: np.ndarray,
SourcePosition: np.ndarray,
trajectory: np.ndarray,
):
y = x[:, :2]
# TODO
return y
return y_all
def render_ambi_ls_binaural(
x: np.ndarray,
fs: int,
in_spfmt: spatialaudioformat.Format,
IR: np.ndarray,
trajectory: np.ndarray,
) -> np.ndarray:
y = x[:]
if trajectory is not None:
if in_spfmt.ambi_order > 0:
y = rotateHOA(y, trajectory)
if in_spfmt.isloudspeaker:
y = rotateMC(y, trajectory, in_spfmt)
y = binaural_fftconv(y, IR, in_spfmt.nchannels, in_spfmt.lfe_index)
return y
""" Wrapper function for generic binaural rendering """
......@@ -635,14 +686,13 @@ def render_ism_binaural(
def binaural_rendering(
x: np.ndarray,
in_format: spatialaudioformat.Format,
out_format: spatialaudioformat.Format,
in_spfmt: spatialaudioformat.Format,
out_spfmt: spatialaudioformat.Format,
dataset: str = "orange53",
fs: int = 48000,
trajectory: str = None,
include_LFE: bool = False,
LFE_gain: float = 10 ** (5.5 / 20),
in_ls_layout_file: str = None,
in_pos: dict = None,
):
"""Binaural rendering
......@@ -651,7 +701,7 @@ def binaural_rendering(
----------
x: np array
input multi-channel array
in_format_name: str
in_spfmt_name: str
name of input spatial format
dataset: str
name of the HRIRs or BRIRs dataset
......@@ -672,57 +722,61 @@ def binaural_rendering(
" performing rotation along trajectory from file {}".format(trajectory)
)
# resample to 48 kHz
y = audioarray.resample(x, fs, 48000)
delay_total_ns = 0
# get IR corresponding to the input and output formats
IR, SourcePosition = get_IR(in_format, out_format, dataset)
IR, SourcePosition, latency_smp = get_IR(in_spfmt, out_spfmt, dataset)
delay_total_ns += latency_smp / float(fs) * 1e9
# prepare LFE signal to be added to output
if include_LFE and in_spfmt.isloudspeaker and in_spfmt.lfe_index:
lfe, lfe_delay_ns = binaural_render_LFE(
x, 48000, in_spfmt.lfe_index, LFE_gain, latency_smp
)
delay_total_ns += lfe_delay_ns
if in_format.name.startswith("CUSTOM_LS"):
return render_custom_ls_binaural(
x, fs, in_format, IR, SourcePosition, trajectory
# get binauralized signal based on format
if in_spfmt.altname.startswith("CUSTOM_LS"):
y = render_custom_ls_binaural(
x, fs, in_spfmt, out_spfmt, IR, SourcePosition, trajectory
)
elif in_format.name.startswith("ISM"):
elif in_spfmt.name.startswith("ISM"):
if not in_pos:
raise ValueError("ISM metadata empty!")
return render_ism_binaural(
y = render_ism_binaural(
x,
fs,
in_format,
IR,
SourcePosition,
trajectory,
in_pos,
)
elif in_format.name.startswith("MASA"):
pass # TODO
# return render_masa_binaural()
elif in_format.ambi_order > 0 or in_format.isloudspeaker:
y = audioarray.resample(x, fs, 48000)
if trajectory is not None:
if in_format.ambi_order > 0:
y = rotateHOA(y, trajectory)
if in_format.isloudspeaker:
y = rotateMC(y, trajectory, in_format)
if include_LFE:
lfe = binaural_render_LFE(
y, 48000, in_format.lfe_index, LFE_gain, latency_smp
elif in_spfmt.name.startswith("MASA"):
y = render_masa_binaural(x, fs, in_spfmt, IR, SourcePosition, trajectory)
elif in_spfmt.ambi_order > 0 or in_spfmt.isloudspeaker:
y = render_ambi_ls_binaural(x, fs, in_spfmt, IR, trajectory)
else:
raise NotImplementedError(
f"{in_spfmt.name} -> {out_spfmt.name}: format conversion not implemented"
)
y = binaural_fftconv(y, IR, in_format.nchannels, in_format.lfe_index)
if include_LFE:
# add LFE signal to output
if include_LFE and in_spfmt.isloudspeaker and in_spfmt.lfe_index:
# delay the binauralized signal by the LFE delay
lfe_delay_smp = NS2SA(fs, int(lfe_delay_ns))
y = np.roll(y, lfe_delay_smp, axis=0)
y[0:lfe_delay_smp, :] = 0
y += lfe
# HRTF delay compensation
latency_smp = np.argmax(np.sum(np.abs(IR), axis=(1, 2)))
y = np.roll(y, latency_smp, axis=0)
if latency_smp > 0:
y[:latency_smp, :] = 0
# delay compensation
delay_total_smp = NS2SA(fs, delay_total_ns)
y = np.roll(y, -delay_total_smp, axis=0)
if delay_total_smp > 0:
y[-delay_total_smp:, :] = 0
# resample back to original rate
y = audioarray.resample(y, 48000, fs)
return y
else:
raise NotImplementedError(
f"{in_format.name} -> {out_format.name}: format conversion not implemented"
)
......@@ -35,16 +35,16 @@ IVAS_CICPX_TO_MONO = np.array(
[
1,
1,
np.sqrt(0.5),
np.sqrt(0.5),
0.79999995,
0.79999995,
0.79999995,
0.79999995,
0.849999964,
0.849999964,
0.849999964,
0.849999964,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
]
]
).T
......@@ -198,8 +198,10 @@ IVAS_MC_CONVERSION = {
},
"5_1_4": {
# downmix
"MONO": IVAS_CICPX_TO_MONO[:10, :],
"STEREO": IVAS_CICPX_TO_STEREO[:10, :],
"MONO": np.vstack([IVAS_CICPX_TO_MONO[:6, :], IVAS_CICPX_TO_MONO[-4:, :]]),
"STEREO": np.vstack(
[IVAS_CICPX_TO_STEREO[:6, :], IVAS_CICPX_TO_STEREO[-4:, :]]
),
"5_1": IVAS_CICP16_TO_6,
"7_1": IVAS_CICP16_TO_12,
"5_1_2": IVAS_CICP16_TO_14,
......
......@@ -65,15 +65,21 @@ def get_hoa_mtx(
mtx_hoa_dec[1, 0] = 0.5
mtx_hoa_dec[1, 1] = -0.5
elif spkrlayout.isloudspeaker:
Y_td = getRSH(T_DESIGN_11_AZI, T_DESIGN_11_ELE, ambi_order, norm="ortho")
# TODO getRSH in IVAS casts to int16_t , decide on final behaviour for scripts
Y_td = getRSH(
T_DESIGN_11_AZI.astype(np.int16),
T_DESIGN_11_ELE.astype(np.int16),
ambi_order,
norm="ortho",
)
Y_td *= np.sqrt(4 * np.pi)
n_ls_woLFE = spkrlayout.nchannels - len(spkrlayout.lfe_index)
ls_azi_woLFE = np.delete(spkrlayout.ls_azi, spkrlayout.lfe_index).astype(float)
ls_ele_woLFE = np.delete(spkrlayout.ls_ele, spkrlayout.lfe_index).astype(float)
panner = EFAP(ls_azi_woLFE, ls_ele_woLFE)
G_td = panner.pan(T_DESIGN_11_AZI, T_DESIGN_11_ELE, intensity_panning)
panner = EFAP(ls_azi_woLFE, ls_ele_woLFE, intensity_panning)
G_td = panner.pan(T_DESIGN_11_AZI, T_DESIGN_11_ELE)
mtx_hoa_dec = (G_td.T @ Y_td.T) / T_DESIGN_11_AZI.size
......
......@@ -30,34 +30,83 @@
the United Nations Convention on Contracts on the International Sales of Goods.
"""
import os
import shutil
import subprocess as sp
from tempfile import TemporaryDirectory
import numpy as np
from pyaudio3dtools.quaternions.functions import Quat2RotMat, rotateAziEle
from pyaudio3dtools.audiofile import readfile, writefile
from pyaudio3dtools.spatialaudioformat import Format
def render_masa(
in_sig: str,
in_meta: str,
in_spfmt: Format,
out_spfmt: Format,
) -> np.ndarray:
"""Python wrapper for masaRenderer binaray
Parameters
----------
in_sig: np.ndarray
Input signal with MASA transport channels
in_meta: str
Input MASA metadata file
in_spfmt: Format
Input spatial audio format
out_spfmt: Format
Output spatial audio format
def rotateISM(
azi: np.ndarray,
ele: np.ndarray,
trajectory: str = None,
) -> tuple:
Returns
-------
out_sig: np.ndarray
Rendered signal
fs : int
Sampling frequency (always 48 kHz for masaRenderer)
"""
if shutil.which("masaRenderer") is None:
raise FileNotFoundError("The masaRenderer binary was not found in path!")
if trajectory is None:
return azi, ele
with TemporaryDirectory() as tmp_dir:
MASA_RENDERER_CMD = [
"masaRenderer",
"", # outputMode -LS51, -LS714 or BINAURAL
"", # input PCM
in_meta[0],
"", # output PCM
]
trj_data = np.genfromtxt(trajectory, delimiter=",")
trj_frames = trj_data.shape[0]
cmd = MASA_RENDERER_CMD[:]
if out_spfmt.name.startswith("BINAURAL"):
cmd[1] = "-BINAURAL"
out_nchan = 2
elif out_spfmt.name == "5_1":
cmd[1] = "-LS51"
out_nchan = 6
else:
cmd[1] = "-LS714"
out_nchan = 12
N_frames = azi.shape[0]
if ele.shape[0] != azi.shape[0]:
raise ValueError("Inconsistent input in azi and ele")
tmp_in = os.path.join(tmp_dir, "tmp_masa_in.pcm")
tmp_out = os.path.join(tmp_dir, "tmp_masa_out.pcm")
azi_rot = np.zeros([N_frames])
ele_rot = np.zeros([N_frames])
cmd[2] = tmp_in
cmd[4] = tmp_out
for i_frame in range(N_frames):
q = trj_data[i_frame % trj_frames, 1:]
azi_rot[i_frame], ele_rot[i_frame] = rotateAziEle(
azi[i_frame], ele[i_frame], Quat2RotMat(q)
writefile(tmp_in, in_sig, 48000)
try:
result = sp.run(cmd, check=True, capture_output=True, text=True)
except sp.CalledProcessError as e:
raise SystemError(
f"Command returned non-zero exit status ({e.returncode}): {' '.join(e.cmd)}\n{e.stderr}\n{e.stdout}"
)
return azi_rot, ele_rot
out_sig, _ = readfile(tmp_out, out_nchan, 48000)
return out_sig