Skip to content
......@@ -44,7 +44,7 @@ typedef struct AudioFileReader AudioFileReader;
ivas_error AudioFileReader_open(
AudioFileReader **audioReader, /* o : AudioFileReader handle */
const char *fileName, /* i : path to wav/raw pcm file */
uint32_t expSampleRate /* i : expected sample rate */
int32_t *sampleRate /* o : sample rate of wav file, unused with pcm */
);
/*! r: number of read samples */
......
/******************************************************************************************************
(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 "cmdln_parser.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_SUPPORTED_OPTS ( 1024 )
#define MAX_OPTION_LENGTH ( 1024 )
typedef CmdLnParser_Option OptionProps;
typedef struct
{
OptionProps props;
int8_t hasBeenParsed;
} Option;
static int16_t validateNoDuplicateIds( const OptionProps *props, int32_t numOpts )
{
for ( int32_t i = 0; i < numOpts; ++i )
{
for ( int32_t j = i + 1; j < numOpts; ++j )
{
if ( props[i].id == props[j].id )
{
fprintf( stderr, "[dev] Duplicate ID == %d between options %s and %s\n", props[i].id, props[i].match, props[j].match );
return -1;
}
}
}
return 0;
}
static int16_t validateOptionProps( OptionProps props )
{
/* Check required properties */
if ( props.match == NULL )
{
/* TODO(sgi): Don't print out usage after this - props.match is used there */
fprintf( stderr, "[dev] Option with ID == %d - missing required property \"match\"\n", props.id );
return -1;
}
if ( props.id == 0 )
{
fprintf( stderr, "[dev] Invalid ID for option %s. ID == %d is reserved.\n", props.match, props.id );
return -1;
}
return 0;
}
/* Validate given OptionProps and use them to initialize array of Options */
static int16_t initOpts( const OptionProps *options, int32_t numOpts, Option *opts )
{
for ( int32_t i = 0; i < numOpts; ++i )
{
if ( validateOptionProps( options[i] ) != 0 )
{
return -1;
}
Option tmp = {
.hasBeenParsed = 0
};
tmp.props = options[i]; /* Cannot assign in aggregate initializer above - causes Visual Studio warning */
opts[i] = tmp;
}
/* Check for duplicate IDs */
if ( validateNoDuplicateIds( options, numOpts ) != 0 )
{
return -1;
}
return 0;
}
static int8_t stringLooksLikeOption( const char *str )
{
if ( str[0] == '-' )
{
return 1;
}
return 0;
}
static const char *stringToOptionName( const char *str )
{
while ( *str == '-' )
{
++str;
}
return str;
}
static int8_t optionMatchesString( Option opt, const char *str )
{
if ( !stringLooksLikeOption( str ) )
{
return 0;
}
const char *optionName = stringToOptionName( str );
if ( strncmp( optionName, opt.props.match, MAX_OPTION_LENGTH ) == 0 || strncmp( optionName, opt.props.matchShort, MAX_OPTION_LENGTH ) == 0 )
{
return 1;
}
return 0;
}
static int16_t parseOpts( int32_t argc,
char **argv,
Option *opts,
int32_t numOpts,
void *pOutputStruct,
CmdLnParser_FnPtr_ParseOption parseOption )
{
Option *currOpt = NULL;
int32_t currOptIdx = 1;
Option *nextOpt = NULL;
int32_t nextOptIdx = 0;
int16_t numValues = 0;
/* Go through all given argv */
for ( int32_t argIdx = 1; argIdx < argc; ++argIdx )
{
/* For current argument from argv go through all options and try to match */
for ( int32_t optIdx = 0; optIdx < numOpts; ++optIdx )
{
Option *optToMatch = &opts[optIdx];
if ( optionMatchesString( *optToMatch, argv[argIdx] ) )
{
nextOpt = optToMatch;
nextOptIdx = argIdx;
/* Check if already parsed */
if ( optToMatch->hasBeenParsed )
{
fprintf( stderr, "Duplicate option: %s (%s)\n", optToMatch->props.match, optToMatch->props.matchShort );
return -1;
}
break;
}
}
/* If no option matched, it is either a value belonging to current option or an invalid option */
if ( nextOpt == NULL )
{
/* Invalid option */
if ( stringLooksLikeOption( argv[argIdx] ) )
{
fprintf( stderr, "Unknown option `%s`\n", stringToOptionName( argv[argIdx] ) );
return -1;
}
/* Otherwise, value following current option.
* Exception: at the beginning of parsing (when current option is NULL) no values are allowed, throw error*/
if ( currOpt != NULL )
{
++numValues;
}
else
{
fprintf( stderr, "Unexpected token `%s`\n", argv[argIdx] );
return -1;
}
}
/* If current argument is a recognized option or no more arguments left, parse current option into output struct*/
if ( nextOpt != NULL )
{
if ( currOpt != NULL )
{
parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct );
currOpt->hasBeenParsed = 1;
}
currOpt = nextOpt;
currOptIdx = nextOptIdx;
nextOpt = NULL;
nextOptIdx = 0;
numValues = 0;
}
}
/* Parse last option */
if ( currOpt != NULL )
{
parseOption( currOpt->props.id, &argv[currOptIdx + 1], numValues, pOutputStruct );
currOpt->hasBeenParsed = 1;
}
return 0;
}
static const char *getBasename( const char *path )
{
/* Find last forward slash in path */
const char *namePtr = strrchr( path, '/' );
if ( namePtr != NULL )
{
return namePtr + 1;
}
/* If not found, try to find last backslash in path */
namePtr = strrchr( path, '\\' );
if ( namePtr != NULL )
{
return namePtr + 1;
}
/* If also not found, return full path, which implictly should be the basename */
return path;
}
static int32_t totalOptionNameLength( const OptionProps opt )
{
return strlen( opt.match ) + strlen( opt.matchShort );
}
static void printWhitespace( int32_t n )
{
for ( int32_t i = 0; i < n; ++i )
{
fprintf( stderr, " " );
}
}
static void printOptDescriptionAligned( const char *descPtr, int32_t descriptionColumnIdx )
{
if ( descPtr == NULL )
{
fprintf( stderr, "\n" );
return;
}
while ( 1 )
{
if ( *descPtr == '\0' )
{
fprintf( stderr, "\n" );
break;
}
fprintf( stderr, "%c", *descPtr );
if ( *descPtr == '\n' )
{
printWhitespace( descriptionColumnIdx );
}
++descPtr;
}
}
static void printUsage(
const char *argv0,
const OptionProps *optionProps,
int32_t numOptions )
{
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: %s [options]\n", getBasename( argv0 ) );
fprintf( stderr, "\n" );
fprintf( stderr, "Valid options:\n" );
/* Find option with longest name, used for pretty formatting */
int32_t maxOptNameLength = 0;
for ( int32_t i = 0; i < numOptions; ++i )
{
const int32_t optNameLength = totalOptionNameLength( optionProps[i] );
if ( maxOptNameLength < optNameLength )
{
maxOptNameLength = optNameLength;
}
}
const int32_t preDescriptionWhitespace = 8;
const int32_t leftColumnAdditionalChars = 7;
for ( int32_t i = 0; i < numOptions; ++i )
{
OptionProps opt = optionProps[i];
const int32_t optNameLength = totalOptionNameLength( optionProps[i] );
/* TODO(sgi): make matchShort optional */
fprintf( stderr, " --%s, -%s", opt.match, opt.matchShort );
printWhitespace( maxOptNameLength - optNameLength + preDescriptionWhitespace );
printOptDescriptionAligned( opt.description, maxOptNameLength + preDescriptionWhitespace + leftColumnAdditionalChars );
}
}
int16_t CmdLnParser_parseArgs( int32_t argc,
char **argv,
const OptionProps *optionProps,
int32_t numOptions,
void *pOutputStruct,
CmdLnParser_FnPtr_ParseOption parseOption )
{
assert( numOptions <= MAX_SUPPORTED_OPTS );
/* Prepare option array */
Option opts[MAX_SUPPORTED_OPTS];
if ( initOpts( optionProps, numOptions, opts ) != 0 )
{
goto fail;
}
/* Iterate over argv and parse */
if ( parseOpts( argc, argv, opts, numOptions, pOutputStruct, parseOption ) != 0 )
{
goto fail;
}
return 0;
fail:
printUsage( argv[0], optionProps, numOptions );
return -1;
}
void CmdLnParser_printUsage( char *executableName,
const CmdLnParser_Option *options,
int32_t numOptions )
{
printUsage( executableName, options, numOptions );
}
......@@ -30,24 +30,38 @@
*******************************************************************************************************/
#ifndef IVAS_ROM_PRERENDERER_H
#define IVAS_ROM_PRERENDERER_H
#ifndef CMDLN_PARSER_H
#define CMDLN_PARSER_H
#include <stdint.h>
#include <stdbool.h>
#include "options.h"
/*----------------------------------------------------------------------------------*
* Prerenderer SBA & MC enc/dec matrices
*----------------------------------------------------------------------------------*/
extern const float hoa_dec_mtx_CICP1[16];
extern const float ls_azimuth_CICP1[1];
extern const float ls_elevation_CICP1[1];
extern const uint32_t ls_LFE_last_idx_CICP1[1];
extern const uint32_t ls_LFE_last_idx_CICP2[2];
extern const uint32_t ls_LFE_last_idx_CICP6[6];
extern const uint32_t ls_LFE_last_idx_CICP12[8];
extern const uint32_t ls_LFE_last_idx_CICP16[10];
extern const uint32_t ls_LFE_last_idx_CICP19[12];
#endif /* IVAS_ROM_PRERENDERER_H */
typedef struct
{
int32_t id;
const char *match;
const char *matchShort;
const char *description;
} CmdLnParser_Option;
/* Function for parsing option values into an output struct, to be implemented by the user */
typedef void ( *CmdLnParser_FnPtr_ParseOption )( int32_t optionId, /* i : option ID of matched option */
char **optionValues, /* i : array of string values following the matched option in argv */
int16_t numOptionValues, /* i : number of string values following the matched option in argv */
void *pOutputStruct /* o : struct to store parsed values */
);
int16_t CmdLnParser_parseArgs( int32_t argc,
char **argv,
const CmdLnParser_Option *options,
int32_t numOptions,
void *pOutputStruct,
CmdLnParser_FnPtr_ParseOption parseOption );
void CmdLnParser_printUsage( char *executableName,
const CmdLnParser_Option *options,
int32_t numOptions );
#endif /* CMDLN_PARSER_H */
......@@ -46,28 +46,6 @@ struct HeadRotFileReader
};
/*-----------------------------------------------------------------------*
* HeadRotationFrameCheck()
*
* Check if the read frame number corresponds to the decoder frame
*-----------------------------------------------------------------------*/
static ivas_error HeadRotationFrameCheck(
HeadRotFileReader *headRotReader, /* i/o: HeadRotFileReader handle */
const int32_t frame_dec )
{
if ( headRotReader->fileRewind == false )
{
if ( headRotReader->frameCounter / IVAS_MAX_PARAM_SPATIAL_SUBFRAMES != frame_dec + 1 )
{
return IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH;
}
}
return IVAS_ERR_OK;
}
/*-----------------------------------------------------------------------*
* HeadRotationFileReader_open()
*
......@@ -121,12 +99,11 @@ ivas_error HeadRotationFileReading(
)
{
uint16_t i;
int32_t time_stamp = 0;
float w, x, y, z;
for ( i = 0; i < IVAS_MAX_PARAM_SPATIAL_SUBFRAMES; i++ )
{
if ( 5 != fscanf( headRotReader->trajFile, "%d,%f,%f,%f,%f", &time_stamp, &w, &x, &y, &z ) )
if ( 4 != fscanf( headRotReader->trajFile, "%f,%f,%f,%f", &w, &x, &y, &z ) )
{
if ( feof( headRotReader->trajFile ) )
{
......@@ -138,12 +115,6 @@ ivas_error HeadRotationFileReading(
}
if ( headRotReader->fileRewind == false && headRotReader->frameCounter != time_stamp )
{
return IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH;
}
( headRotReader->frameCounter )++;
Quaternions[i].w = w;
......@@ -152,19 +123,6 @@ ivas_error HeadRotationFileReading(
Quaternions[i].z = z;
}
if ( headRotReader->fileRewind == false )
{
if ( ( time_stamp + 1 ) % IVAS_MAX_PARAM_SPATIAL_SUBFRAMES != 0 )
{
return IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH;
}
}
if ( HeadRotationFrameCheck( headRotReader, frame_dec ) != IVAS_ERR_OK )
{
return IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH;
}
return IVAS_ERR_OK;
}
......
......@@ -43,7 +43,6 @@
struct IsmFileReader
{
FILE *file;
int32_t frameCounter;
char *file_path;
};
......@@ -75,7 +74,6 @@ IsmFileReader *IsmFileReader_open(
self = calloc( sizeof( IsmFileReader ), 1 );
self->file = file;
self->frameCounter = 0;
self->file_path = calloc( sizeof( char ), strlen( filePath ) + 1 );
strcpy( self->file_path, filePath );
......@@ -98,7 +96,6 @@ ivas_error IsmFileReader_readNextFrame(
float meta_prm[NUM_ISM_METADATA_PER_LINE];
char *char_ptr;
int16_t i;
int32_t time_stamp;
FILE *file;
if ( ismMetadata == NULL || self->file == NULL )
......@@ -114,14 +111,8 @@ ivas_error IsmFileReader_readNextFrame(
}
char_ptr = strtok( char_buff, "," );
time_stamp = (int32_t) atoi( char_ptr );
if ( time_stamp != self->frameCounter )
{
return IVAS_ERR_FILE_READER_TIMESTAMP_MISMATCH;
}
i = 0;
meta_prm[i++] = (float) atof( char_ptr );
while ( ( char_ptr = strtok( NULL, "," ) ) != NULL && i < NUM_ISM_METADATA_PER_LINE )
{
meta_prm[i++] = (float) atof( char_ptr );
......@@ -139,7 +130,6 @@ ivas_error IsmFileReader_readNextFrame(
ismMetadata->spread = meta_prm[3];
ismMetadata->gainFactor = meta_prm[4];
++self->frameCounter;
return IVAS_ERR_OK;
}
......
......@@ -43,7 +43,6 @@
struct IsmFileWriter
{
FILE *file;
int32_t frameCounter;
char *file_path;
};
......@@ -86,7 +85,6 @@ ivas_error IsmFileWriter_open(
self = calloc( sizeof( IsmFileWriter ), 1 );
self->file = file;
self->frameCounter = 0;
self->file_path = calloc( sizeof( char ), strlen( filePath ) + 1 );
strcpy( self->file_path, filePath );
......@@ -118,7 +116,7 @@ ivas_error IsmFileWriter_writeFrame(
file = ismWriter->file;
/* IVAS_fmToDo: work in progress; currently position_azimuth, position_elevation, position_radius, spread, gain_factor */
sprintf( char_buff, "%04d,%+07.2f,%+06.2f,%05.2f,%06.2f,%04.2f\n", ismWriter->frameCounter, ismMetadata.azimuth, ismMetadata.elevation, ismMetadata.radius, ismMetadata.spread, ismMetadata.gainFactor );
sprintf( char_buff, "%+07.2f,%+06.2f,%05.2f,%06.2f,%04.2f\n", ismMetadata.azimuth, ismMetadata.elevation, ismMetadata.radius, ismMetadata.spread, ismMetadata.gainFactor );
if ( file )
{
......@@ -129,8 +127,6 @@ ivas_error IsmFileWriter_writeFrame(
return IVAS_ERR_FAILED_FILE_WRITE;
}
++ismWriter->frameCounter;
return IVAS_ERR_OK;
}
......
/******************************************************************************************************
(C) 2022 IVAS codec Public Collaboration with portions copyright Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository. All Rights Reserved.
This software is protected by copyright law and by international treaties.
The IVAS codec Public Collaboration consisting of Dolby International AB, Ericsson AB,
Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V., Huawei Technologies Co. LTD.,
Koninklijke Philips N.V., Nippon Telegraph and Telephone Corporation, Nokia Technologies Oy, Orange,
Panasonic Holdings Corporation, Qualcomm Technologies, Inc., VoiceAge Corporation, and other
contributors to this repository retain full ownership rights in their respective contributions in
the software. This notice grants no license of any kind, including but not limited to patent
license, nor is any license granted by implication, estoppel or otherwise.
Contributors are required to enter into the IVAS codec Public Collaboration agreement before making
contributions.
This software is provided "AS IS", without any express or implied warranties. The software is in the
development stage. It is intended exclusively for experts who have experience with such software and
solely for the purpose of inspection. All implied warranties of non-infringement, merchantability
and fitness for a particular purpose are hereby disclaimed and excluded.
Any dispute, controversy or claim arising under or in relation to providing this software shall be
submitted to and settled by the final, binding jurisdiction of the courts of Munich, Germany in
accordance with the laws of the Federal Republic of Germany excluding its conflict of law rules and
the United Nations Convention on Contracts on the International Sales of Goods.
*******************************************************************************************************/
#include "options.h"
#include "ivas_prerenderer.h"
#include "ivas_prot.h"
#include "ivas_rom_com.h"
#include "ivas_rom_dec.h"
#include "ivas_rom_prerenderer.h"
#include "prot.h"
#include "wmops.h"
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define LIMITER_THRESHOLD 0.9988493699f /* -0.01 dBFS */
struct Prndr_Prerenderer
{
uint32_t sampleRate;
int8_t isConfigured; /* flag */
int8_t firstFrame; /* flag */
/* I/O */
Prndr_InputConfig inConfig;
Prndr_OutputConfig outConfig;
/* =========== Panning =========== */
EFAP_HANDLE efapRenderer;
Prndr_ObjPanInfo *objPanInfo; /* size: [numInObjects] */
float ***speakerPanGains; /* size: [numInMc][numSpeakers][numOutChannels] */
float *tmpGainBuffer; /* size: [numOutChannels] */
float *noLfePanBuffer; /* size: [numOutChannels] */
float *crossfade; /* size: [frameSize] */
/* =============================== */
/* Helpers */
int16_t numOutChannels; /* Total number of output channels */
int16_t numInChannels; /* Total number of input channels */
int16_t numInChannelsObj; /* Total number of input channels of object inputs */
int16_t numInChannelsAmbi; /* Total number of input channels of ambisonics inputs */
int16_t numInChannelsMc; /* Total number of input channels of multichannel inputs */
/* For each channel of MC inputs mcPassThrough contains the corresponding
* output channel index if a passthrough is possible, otherwise contains -1 */
int32_t *mcPassthrough; /* size: [numInChannelsMc] */
/* =========== LFE Handling =========== */
/* Do not drop LFE when rendering to a layout that does not have
* an LFE channel - render LFE into other channels*/
int8_t neverDropLfe; /* flag */
float *lfePanGains;
/* =========== limiter handle =========== */
IVAS_LIMITER_HANDLE hLimiter;
/* Ambisonics decoding matrix */
float *ambi_dec_mtx;
};
/*---------------------------------------------------------------------*
* Prototypes
*---------------------------------------------------------------------*/
/* clang-off */
static void renderAmbiToAmbi(
const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio );
static void renderChannelsToAmbi(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio );
static void renderObjectsToAmbi(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const Prndr_AudioObjectMetadataBuffer metadataBuffer,
Prndr_AudioBuffer outAudio );
static void renderAmbiToChannels(
const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio );
static void renderChannelsToChannels(
const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio );
static void renderObjectsToChannels(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const Prndr_AudioObjectMetadataBuffer metadataBuffer,
Prndr_AudioBuffer outAudio );
static void renderSingleObjectToAmbi(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */
Prndr_ObjPanInfo *prevPanInfo,
const Prndr_AudioObjectPosition curFrmPos,
const float gain_lin,
Prndr_AudioBuffer outAudio );
static void renderSingleObjectToChannels(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */
Prndr_ObjPanInfo *prevPanInfo,
const Prndr_AudioObjectPosition curFrmPos,
const float gain_lin,
Prndr_AudioBuffer outAudio );
/* Multiply a single channel by a vector of gains and add result to corresponding output channels */
static void applyChannelGainsAndAddToOutput(
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx, /* Index of the item within input audio buffer */
const float *const gainsCurrent, /* Vector of gains for current frame, corresponding to output channels */
const float *const gainsPrev, /* Vector of previously applied gains, used for interpolation. Set to NULL for no interpolation */
const float gain_lin, /* Additional linear gain to be applied when mixing with output buffer */
const float *const crossfade,
Prndr_AudioBuffer outAudio );
static void prepareMcPanGains(
Prndr_Prerenderer *const st );
static void prepareLfeHandling(
Prndr_Prerenderer *const st );
static void prepareMcPassthrough(
Prndr_Prerenderer *const st );
static void passthroughChannel(
const Prndr_AudioBuffer inAudio,
const uint32_t srcChnlIdx,
const uint32_t dstChnlIdx,
const float gain_lin,
Prndr_AudioBuffer outAudio );
static void getSpeakerGains(
const Prndr_Prerenderer *const st,
const float azi,
const float ele,
float *const spkGains );
static int16_t Prndr_getNumChannelsAmbisonics(
Prndr_Ambisonics ambisonics );
static int16_t Prndr_getAmbisonicsOrder(
Prndr_Ambisonics ambisonics );
static int16_t Prndr_getNumChannelsInSpeakerLayout(
Prndr_SpeakerLayout layout );
static int16_t Prndr_getNumNonLfeChannelsInSpeakerLayout(
Prndr_SpeakerLayout layout );
static const float *Prndr_getSpeakerAzimuths(
Prndr_SpeakerLayout layout );
static const float *Prndr_getSpeakerElevations(
Prndr_SpeakerLayout layout );
static const uint32_t *Prndr_getReorderedChannelIndices(
Prndr_SpeakerLayout layout );
static ivas_error Prndr_getHoaRenderMtx(
const Prndr_OutputConfig outConfig,
float **decMtx,
uint32_t ambiOrder );
static void Prndr_getHoaDecVecForAmbiChnl(
uint32_t ambiChnnlIdx,
const Prndr_OutputConfig outConfig,
const float *decMtx,
float *decCoeffs );
static void ivas_limiter_prerenderer(
IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */
float *output, /* i/o: I/O buffer */
const int16_t output_frame, /* i : number of samples per channel in the buffer */
const float threshold /* i : signal amplitude above which limiting starts to be applied */
);
static float dBToLin( const float gain_dB );
/* clang-on */
/* ========================================================================== */
Prndr_Prerenderer *Prndr_Prerenderer_open()
{
Prndr_Prerenderer *st;
st = (Prndr_Prerenderer *) count_malloc( sizeof( Prndr_Prerenderer ) );
st->isConfigured = 0;
st->efapRenderer = NULL;
st->objPanInfo = NULL;
st->speakerPanGains = NULL;
st->tmpGainBuffer = NULL;
st->noLfePanBuffer = NULL;
st->crossfade = NULL;
st->mcPassthrough = NULL;
st->neverDropLfe = 0;
st->lfePanGains = NULL;
st->hLimiter = NULL;
st->ambi_dec_mtx = NULL;
return st;
}
ivas_error Prndr_Prerenderer_configure( Prndr_Prerenderer *const st,
const Prndr_InputConfig inConfig,
const Prndr_OutputConfig outConfig,
int32_t frameSize,
uint32_t sampleRate )
{
uint32_t i;
int32_t j;
ivas_error error;
error = IVAS_ERR_OK;
/* ============================= Error checks ============================= */
assert( st != NULL && "Can't configure prerenderer - pointer is NULL" );
assert( !st->isConfigured && "Re-configuring a prerenderer is not supported" );
assert( !( outConfig.ambisonics != prndr_ambisonics_none && outConfig.speakerLayout != prndr_speaker_layout_none ) && "Multiple outputs not supported" );
assert( !( outConfig.ambisonics == prndr_ambisonics_none && outConfig.speakerLayout == prndr_speaker_layout_none ) && "At least one output must be selected" );
assert( !( inConfig.numAudioObjects == 0 && inConfig.numMultiChannelBuses == 0 && inConfig.numAmbisonicsBuses == 0 ) && "At least one input must be active" );
/* ========================== Store useful values ========================= */
st->sampleRate = sampleRate;
st->isConfigured = 1;
st->firstFrame = 1;
st->inConfig = inConfig;
st->outConfig = outConfig;
/* Save total number of channels in ambisonics inputs */
st->numInChannelsAmbi = 0;
for ( i = 0; i < inConfig.numAmbisonicsBuses; ++i )
{
st->numInChannelsAmbi += Prndr_getNumChannelsAmbisonics( inConfig.ambisonicsBuses[i].ambisonicsConfig );
}
/* Save total number of channels in MC input */
st->numInChannelsMc = 0;
for ( i = 0; i < inConfig.numMultiChannelBuses; ++i )
{
st->numInChannelsMc += Prndr_getNumChannelsInSpeakerLayout( inConfig.multiChannelBuses[i].speakerLayout );
}
/* Save total number of channels of audio object inputs */
st->numInChannelsObj = st->inConfig.numAudioObjects;
/* Save total number of input channels */
st->numInChannels = st->numInChannelsObj + st->numInChannelsAmbi + st->numInChannelsMc;
if ( st->outConfig.ambisonics != prndr_ambisonics_none )
{
/* Save number of output channels */
st->numOutChannels = Prndr_getNumChannelsAmbisonics( st->outConfig.ambisonics );
}
/* ============================ Prepare panning and ambisonics =========================== */
if ( st->outConfig.speakerLayout != prndr_speaker_layout_none )
{
if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom )
{
/* Save number of output channels */
st->numOutChannels = st->outConfig.outSetupCustom->num_spk + st->outConfig.outSetupCustom->num_lfe;
/* Open and initialize EFAP struct */
if ( ( error = efap_init_data( &st->efapRenderer, st->outConfig.outSetupCustom->ls_azimuth, st->outConfig.outSetupCustom->ls_elevation, st->outConfig.outSetupCustom->num_spk, EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
return error;
}
}
else
{
/* Save number of output channels */
st->numOutChannels = Prndr_getNumChannelsInSpeakerLayout( st->outConfig.speakerLayout );
/* Open and initialize EFAP struct */
if ( ( error = efap_init_data( &st->efapRenderer, Prndr_getSpeakerAzimuths( st->outConfig.speakerLayout ), Prndr_getSpeakerElevations( st->outConfig.speakerLayout ), Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout ), EFAP_MODE_EFAP ) ) != IVAS_ERR_OK )
{
return error;
}
}
assert( st->efapRenderer != NULL && "Could not init EFAP" );
/* Compute Ambisonics to loudspeaker decoding matrix */
if ( ( error = Prndr_getHoaRenderMtx( st->outConfig, &st->ambi_dec_mtx, 3 ) ) != IVAS_ERR_OK )
{
return error;
}
}
/* Allocate temporary pan/enc buffer to avoid allocations during rendering */
st->tmpGainBuffer = count_calloc( st->numOutChannels, sizeof( float ) );
/* Allocate temporary buffer for panning gains with lfe omitted */
if ( st->outConfig.speakerLayout != prndr_speaker_layout_none )
{
if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom )
{
st->noLfePanBuffer = count_calloc( st->outConfig.outSetupCustom->num_spk, sizeof( float ) );
}
else
{
st->noLfePanBuffer = count_calloc( Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout ), sizeof( float ) );
}
}
/* Create lookup tables for panning/encoding speaker signals */
if ( st->inConfig.numMultiChannelBuses != 0 )
{
prepareMcPanGains( st );
prepareLfeHandling( st );
}
/* Allocate structs for interpolation of object pan/enc gains between frames */
if ( st->inConfig.numAudioObjects != 0 )
{
st->objPanInfo = count_calloc( st->inConfig.numAudioObjects, sizeof( Prndr_ObjPanInfo ) );
for ( i = 0; i < st->inConfig.numAudioObjects; ++i )
{
st->objPanInfo[i].panGains = count_calloc( st->numOutChannels, sizeof( float ) );
}
}
/* =========================== Prepare crossfades ========================= */
st->crossfade = count_calloc( frameSize, sizeof( float ) );
for ( j = 0; j < frameSize; ++j )
{
st->crossfade[j] = (float) j / ( frameSize - 1 );
}
/* ========================= Prepare optimizations ======================== */
/* Make note of possible processing shortcuts in cases where input and output
* config is the same or similar. This only needs to be done for MC I/O, since
* Ambisonics I/O can always be passed through and objects can never be passed
* through */
if ( st->inConfig.numMultiChannelBuses > 0 && st->outConfig.speakerLayout != prndr_speaker_layout_none )
{
prepareMcPassthrough( st );
}
/* ============================ Configure limiter =========================== */
st->hLimiter = ivas_limiter_open( st->numOutChannels, st->sampleRate );
return error;
}
void Prndr_Prerenderer_render(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const Prndr_AudioObjectMetadataBuffer metadataBuffer,
Prndr_AudioBuffer outAudio )
{
#ifdef WMOPS
wmops_sub_start( "Prndr_Prerenderer_render" );
#endif
/* ============================= Error checks ============================= */
assert( st != NULL && "Can't render - prerenderer pointer is NULL" );
assert( st->isConfigured && "Can't render - prerenderer pointer is not configured" );
assert( inAudio.config.sampleRate == outAudio.config.sampleRate && "Input and output sample rate must be the same" );
assert( inAudio.config.bufferSize == outAudio.config.bufferSize && "Input and output frame size must be the same" );
assert( inAudio.config.numChannels == st->numInChannels && "Number of input channels does not match between prerenderer and input config" );
assert( outAudio.config.numChannels == st->numOutChannels && "Number of input channels does not match between prerenderer and input config" );
assert( inAudio.config.sampleRate != 0 && "Invalid sample rate" );
assert( inAudio.config.bufferSize != 0 && "Invalid frame size" );
assert( inAudio.data != NULL && "Can't render - input buffer is empty" );
/* ========================== Actual processing =========================== */
/* Clear output buffer */
set_zero( outAudio.data, outAudio.config.numChannels * outAudio.config.bufferSize );
/* Render target format: Ambisonics */
if ( st->outConfig.ambisonics != prndr_ambisonics_none )
{
if ( st->inConfig.numAmbisonicsBuses != 0 )
{
renderAmbiToAmbi( st, inAudio, outAudio );
}
if ( st->inConfig.numMultiChannelBuses != 0 )
{
renderChannelsToAmbi( st, inAudio, outAudio );
}
if ( st->inConfig.numAudioObjects != 0 )
{
renderObjectsToAmbi( st, inAudio, metadataBuffer, outAudio );
}
} /* Render target format: multichannel */
else if ( st->outConfig.speakerLayout != prndr_speaker_layout_none )
{
if ( st->inConfig.numAmbisonicsBuses != 0 )
{
renderAmbiToChannels( st, inAudio, outAudio );
}
if ( st->inConfig.numMultiChannelBuses != 0 )
{
renderChannelsToChannels( st, inAudio, outAudio );
}
if ( st->inConfig.numAudioObjects != 0 )
{
renderObjectsToChannels( st, inAudio, metadataBuffer, outAudio );
}
}
/* Apply limiting in place */
ivas_limiter_prerenderer(
st->hLimiter,
outAudio.data,
outAudio.config.bufferSize,
LIMITER_THRESHOLD );
if ( st->firstFrame )
{
st->firstFrame = 0;
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
void Prndr_Prerenderer_setNeverDropLfe(
Prndr_Prerenderer *st,
int8_t neverDropLfe )
{
st->neverDropLfe = neverDropLfe;
}
int16_t Prndr_Prerenderer_getInChannels(
Prndr_Prerenderer *st )
{
assert( st != NULL && "Can't get number of input channels - prerenderer pointer is NULL" );
if ( st )
{
return st->numInChannels;
}
return 0;
}
int16_t Prndr_Prerenderer_getOutChannels(
Prndr_Prerenderer *st )
{
assert( st != NULL && "Can't get number of output channels - prerenderer pointer is NULL" );
if ( st )
{
return st->numOutChannels;
}
return 0;
}
void Prndr_Prerenderer_close( Prndr_Prerenderer *st )
{
uint32_t i;
uint32_t j;
uint32_t numChannels;
assert( st != NULL && "Can't close prerenderer - pointer is NULL" );
if ( st->efapRenderer != NULL )
{
efap_free_data( &st->efapRenderer );
}
if ( st->objPanInfo != NULL )
{
for ( i = 0; i < st->inConfig.numAudioObjects; ++i )
{
if ( st->objPanInfo[i].panGains != NULL )
{
count_free( st->objPanInfo[i].panGains );
}
}
count_free( st->objPanInfo );
}
if ( st->speakerPanGains != NULL )
{
for ( i = 0; i < st->inConfig.numMultiChannelBuses; ++i )
{
numChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[i].speakerLayout );
for ( j = 0; j < numChannels; ++j )
{
count_free( st->speakerPanGains[i][j] );
}
count_free( st->speakerPanGains[i] );
}
count_free( st->speakerPanGains );
}
if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom )
{
count_free( st->outConfig.outSetupCustom );
st->outConfig.outSetupCustom = NULL;
}
if ( st->tmpGainBuffer != NULL )
{
count_free( st->tmpGainBuffer );
}
if ( st->noLfePanBuffer != NULL )
{
count_free( st->noLfePanBuffer );
}
if ( st->crossfade != NULL )
{
count_free( st->crossfade );
}
if ( st->mcPassthrough != NULL )
{
count_free( st->mcPassthrough );
}
if ( st->lfePanGains )
{
count_free( st->lfePanGains );
}
if ( st->ambi_dec_mtx != NULL )
{
count_free( st->ambi_dec_mtx );
}
ivas_limiter_close( &st->hLimiter );
count_free( st );
}
/* ============================= Local functions ============================ */
static float *get_smpl_ptr( Prndr_AudioBuffer buffer, uint32_t chnlIdx, uint32_t smplIdx )
{
return buffer.data + chnlIdx * buffer.config.bufferSize + smplIdx;
}
static void renderAmbiToAmbi( const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio )
{
float *inSmpl;
float *outSmpl;
int16_t lastChannelIdx;
int16_t smplIdx;
int16_t chnlIdx;
uint32_t inAmbiChannelIdx;
uint32_t ambiIdx;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderAmbiToAmbi" );
#endif
/* Iterate over given Ambisonics inputs */
for ( ambiIdx = 0; ambiIdx < st->inConfig.numAmbisonicsBuses; ++ambiIdx )
{
inAmbiChannelIdx = st->inConfig.ambisonicsBuses[ambiIdx].inputChannelIndex;
/* Find out how many channels to process */
lastChannelIdx = min( Prndr_getNumChannelsAmbisonics( st->inConfig.ambisonicsBuses[ambiIdx].ambisonicsConfig ), st->numOutChannels ) - 1;
gain_lin = dBToLin( st->inConfig.ambisonicsBuses[ambiIdx].gain_dB );
/* Passthrough channels */
for ( chnlIdx = 0; chnlIdx <= lastChannelIdx; ++chnlIdx )
{
inSmpl = get_smpl_ptr( inAudio, chnlIdx + inAmbiChannelIdx, 0 );
outSmpl = get_smpl_ptr( outAudio, chnlIdx, 0 );
for ( smplIdx = 0; smplIdx < inAudio.config.bufferSize; ++smplIdx )
{
*outSmpl += *inSmpl * gain_lin;
++inSmpl;
++outSmpl;
}
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderChannelsToAmbi( Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio )
{
const uint32_t *lfeLastIdxs;
uint32_t inMcChannelIdx;
uint32_t numNonLfeInChannels;
uint32_t numInChannels;
uint32_t mcIdx;
uint32_t inChIdx;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderChannelsToAmbi" );
#endif
/* Iterate over given MC inputs */
for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx )
{
/* Get channel idx of current MC input within the multitrack buffer */
inMcChannelIdx = st->inConfig.multiChannelBuses[mcIdx].inputChannelIndex;
/* Number of input speakers */
numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
lfeLastIdxs = Prndr_getReorderedChannelIndices( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
gain_lin = dBToLin( st->inConfig.multiChannelBuses[mcIdx].gain_dB );
/* Iterate over channels */
for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx )
{
applyChannelGainsAndAddToOutput( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->speakerPanGains[mcIdx][inChIdx],
NULL,
gain_lin,
st->crossfade,
outAudio );
}
if ( st->neverDropLfe )
{
/* Render LFE channels into the scene */
for ( ; inChIdx < numInChannels; ++inChIdx )
{
applyChannelGainsAndAddToOutput( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->lfePanGains,
NULL,
gain_lin,
st->crossfade,
outAudio );
}
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderObjectsToAmbi(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const Prndr_AudioObjectMetadataBuffer metadataBuffer,
Prndr_AudioBuffer outAudio )
{
const Prndr_AudioObject *curObj;
uint32_t objIdx;
Prndr_AudioObjectPosition pos;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderObjectsToAmbi" );
#endif
assert( st->inConfig.numAudioObjects == metadataBuffer.numObjects && "Metadata provided for a different number of objects than found in input" );
/* Iterate over given audio objects */
for ( objIdx = 0; objIdx < st->inConfig.numAudioObjects; ++objIdx )
{
/* Get pointer to current object and its metadata */
curObj = &st->inConfig.audioObjects[objIdx];
pos = metadataBuffer.positions[objIdx];
gain_lin = dBToLin( st->inConfig.audioObjects[objIdx].gain_dB );
/* Render to ambisonics */
renderSingleObjectToAmbi(
st,
inAudio,
curObj->inputChannelIndex,
&st->objPanInfo[objIdx],
pos,
gain_lin,
outAudio );
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderAmbiToChannels( const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio )
{
uint32_t inAmbiChannelIdx;
uint32_t ambiIdx;
uint32_t numInAmbiChnls;
uint32_t ambiChnIdx;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderAmbiToChannels" );
#endif
/* Iterate over all given ambisonics inputs */
for ( ambiIdx = 0; ambiIdx < st->inConfig.numAmbisonicsBuses; ++ambiIdx )
{
inAmbiChannelIdx = st->inConfig.ambisonicsBuses[ambiIdx].inputChannelIndex;
/* Number of input channels */
numInAmbiChnls = Prndr_getNumChannelsAmbisonics( st->inConfig.ambisonicsBuses[ambiIdx].ambisonicsConfig );
gain_lin = dBToLin( st->inConfig.multiChannelBuses[ambiIdx].gain_dB );
/* Render each ambisonics channel */
for ( ambiChnIdx = 0; ambiChnIdx < numInAmbiChnls; ++ambiChnIdx )
{
/* Write decoding gains to temp buffer */
Prndr_getHoaDecVecForAmbiChnl( ambiChnIdx, st->outConfig, st->ambi_dec_mtx, st->tmpGainBuffer );
/* Apply decoding gains and add to output */
applyChannelGainsAndAddToOutput( inAudio,
inAmbiChannelIdx + ambiChnIdx,
st->tmpGainBuffer,
NULL,
gain_lin,
st->crossfade,
outAudio );
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderChannelsToChannels(
const Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
Prndr_AudioBuffer outAudio )
{
const uint32_t *lfeLastIdxs;
uint32_t inMcChannelIdx;
uint32_t numNonLfeInChannels;
uint32_t numInChannels;
uint32_t passThroughIdx;
uint32_t mcIdx;
uint32_t inChIdx;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderChannelsToChannels" );
#endif
passThroughIdx = 0;
/* Iterate over given MC inputs */
for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx )
{
/* Get channel idx of current MC input within the multitrack buffer */
inMcChannelIdx = st->inConfig.multiChannelBuses[mcIdx].inputChannelIndex;
/* Number of non-LFE input channels */
numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
/* Number of all input channels */
numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
lfeLastIdxs = Prndr_getReorderedChannelIndices( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
gain_lin = dBToLin( st->inConfig.multiChannelBuses[mcIdx].gain_dB );
/* Iterate over non-LFE channels */
for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx )
{
if ( st->mcPassthrough[passThroughIdx] == -1 )
{
applyChannelGainsAndAddToOutput( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->speakerPanGains[mcIdx][inChIdx],
NULL,
gain_lin,
st->crossfade,
outAudio );
}
else
{
passthroughChannel( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->mcPassthrough[passThroughIdx],
gain_lin,
outAudio );
}
++passThroughIdx;
}
/* Iterate over LFE channels */
for ( ; inChIdx < numInChannels; ++inChIdx )
{
/* Pass through if possible */
if ( st->mcPassthrough[passThroughIdx] != -1 )
{
passthroughChannel( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->mcPassthrough[passThroughIdx],
gain_lin,
outAudio );
}
else
{
if ( st->neverDropLfe )
{
applyChannelGainsAndAddToOutput( inAudio,
inMcChannelIdx + lfeLastIdxs[inChIdx],
st->lfePanGains,
NULL,
gain_lin,
st->crossfade,
outAudio );
}
}
++passThroughIdx;
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderObjectsToChannels(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const Prndr_AudioObjectMetadataBuffer metadataBuffer,
Prndr_AudioBuffer outAudio )
{
const Prndr_AudioObject *curObj;
uint32_t objIdx;
Prndr_AudioObjectPosition pos;
float gain_lin;
#ifdef WMOPS
wmops_sub_start( "renderObjectsToChannels" );
#endif
assert( st->inConfig.numAudioObjects == metadataBuffer.numObjects && "Metadata provided for a different number of objects than found in input" );
/* Iterate over given audio objects */
for ( objIdx = 0; objIdx < st->inConfig.numAudioObjects; ++objIdx )
{
/* Get pointer to current object and its metadata */
curObj = &st->inConfig.audioObjects[objIdx];
pos = metadataBuffer.positions[objIdx];
gain_lin = dBToLin( st->inConfig.audioObjects[objIdx].gain_dB );
/* Render to MC */
renderSingleObjectToChannels(
st,
inAudio,
curObj->inputChannelIndex,
&st->objPanInfo[objIdx],
pos,
gain_lin,
outAudio );
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderSingleObjectToAmbi(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx,
Prndr_ObjPanInfo *prevPanInfo,
const Prndr_AudioObjectPosition curFrmPos,
const float gain_lin,
Prndr_AudioBuffer outAudio )
{
float *swapPtr;
#ifdef WMOPS
wmops_sub_start( "renderSingleObjectToAmbi" );
#endif
/* Update panning gains if position changed */
if ( prevPanInfo->position.azimuth != curFrmPos.azimuth ||
prevPanInfo->position.elevation != curFrmPos.elevation ||
st->firstFrame )
{
/* Write current panning gains to tmpBuffer */
ivas_dirac_dec_get_response( curFrmPos.azimuth,
curFrmPos.elevation,
st->tmpGainBuffer,
Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) );
prevPanInfo->position.azimuth = curFrmPos.azimuth;
prevPanInfo->position.elevation = curFrmPos.elevation;
applyChannelGainsAndAddToOutput( inAudio,
itemChnlIdx,
st->tmpGainBuffer,
st->firstFrame ? NULL : prevPanInfo->panGains,
gain_lin,
st->crossfade,
outAudio );
/* Save current gains as most recently applied gains */
swapPtr = prevPanInfo->panGains;
prevPanInfo->panGains = st->tmpGainBuffer;
st->tmpGainBuffer = swapPtr;
}
/* Otherwise use most recent gains and no interpolation */
else
{
applyChannelGainsAndAddToOutput( inAudio,
itemChnlIdx,
prevPanInfo->panGains,
NULL,
gain_lin,
st->crossfade,
outAudio );
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void renderSingleObjectToChannels(
Prndr_Prerenderer *const st,
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx,
Prndr_ObjPanInfo *prevPanInfo,
const Prndr_AudioObjectPosition curFrmPos,
const float gain_lin,
Prndr_AudioBuffer outAudio )
{
float *swapPtr;
#ifdef WMOPS
wmops_sub_start( "renderSingleObjectToChannels" );
#endif
/* Update panning gains if position changed */
if ( prevPanInfo->position.azimuth != curFrmPos.azimuth ||
prevPanInfo->position.elevation != curFrmPos.elevation ||
st->firstFrame )
{
/* Write current panning gains to tmpBuffer */
getSpeakerGains( st, curFrmPos.azimuth, curFrmPos.elevation, st->tmpGainBuffer );
prevPanInfo->position.azimuth = curFrmPos.azimuth;
prevPanInfo->position.elevation = curFrmPos.elevation;
applyChannelGainsAndAddToOutput( inAudio,
itemChnlIdx,
st->tmpGainBuffer,
st->firstFrame ? NULL : prevPanInfo->panGains,
gain_lin,
st->crossfade,
outAudio );
/* Save current gains as most recently applied gains */
swapPtr = prevPanInfo->panGains;
prevPanInfo->panGains = st->tmpGainBuffer;
st->tmpGainBuffer = swapPtr;
}
/* Otherwise use most recent gains and no interpolation */
else
{
applyChannelGainsAndAddToOutput( inAudio,
itemChnlIdx,
prevPanInfo->panGains,
NULL,
gain_lin,
st->crossfade,
outAudio );
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void applyChannelGainsAndAddToOutput(
const Prndr_AudioBuffer inAudio,
const uint32_t itemChnlIdx,
const float *const gainsCurrent,
const float *const gainsPrev,
const float gain_lin,
const float *const crossfade,
Prndr_AudioBuffer outAudio )
{
float *inSmpl;
float *outSmpl;
const float *fadeIn;
const float *fadeOut;
const float *lastInSmpl;
int16_t outChnlIdx;
float currentGain;
float previousGain;
#ifdef WMOPS
wmops_sub_start( "applyChannelGainsAndAddToOutput" );
#endif
/* Pointer to behind last input sample */
lastInSmpl = get_smpl_ptr( inAudio, itemChnlIdx, inAudio.config.bufferSize );
for ( outChnlIdx = 0; outChnlIdx < outAudio.config.numChannels; ++outChnlIdx )
{
#ifdef WMOPS
wmops_sub_start( "applyChannelGainsAndAddToOutput_chnl_loop" );
#endif
currentGain = gainsCurrent[outChnlIdx] * gain_lin;
previousGain = gainsPrev == NULL ? 0.f : gainsPrev[outChnlIdx] * gain_lin;
/* Process current output channel only if applying non-zero gains */
if ( fabsf( currentGain ) > EPSILON || ( gainsPrev != NULL && fabsf( previousGain ) > EPSILON ) )
{
/* Reset crossfade pointers */
fadeIn = crossfade;
fadeOut = &crossfade[inAudio.config.bufferSize - 1];
/* Reset input pointer to the beginning of input channel */
inSmpl = get_smpl_ptr( inAudio, itemChnlIdx, 0 );
/* Set output pointer to first output channel sample */
outSmpl = get_smpl_ptr( outAudio, outChnlIdx, 0 );
if ( gainsPrev == NULL || fabsf( previousGain - currentGain ) <= EPSILON )
{
#ifdef WMOPS
wmops_sub_start( "applyChannelGainsAndAddToOutput_smpl_loop_no_intrpl" );
#endif
/* If no interpolation from previous frame, apply current gain */
do
{
*outSmpl += currentGain * ( *inSmpl );
++outSmpl;
++inSmpl;
} while ( inSmpl != lastInSmpl );
#ifdef WMOPS
wmops_sub_end();
#endif
}
else
{
#ifdef WMOPS
wmops_sub_start( "applyChannelGainsAndAddToOutput_smpl_loop_intrpl" );
#endif
/* Otherwise use weighted average between previous and current gain */
do
{
*outSmpl += ( ( *fadeIn ) * currentGain + ( *fadeOut ) * previousGain ) * ( *inSmpl );
++outSmpl;
++inSmpl;
++fadeIn;
--fadeOut;
} while ( inSmpl != lastInSmpl );
#ifdef WMOPS
wmops_sub_end();
#endif
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void prepareLfeHandling(
Prndr_Prerenderer *const st )
{
/* uint32_t i; */
#ifdef WMOPS
wmops_sub_start( "prepareLfeHandling" );
#endif
st->lfePanGains = count_calloc( st->numOutChannels, sizeof( float ) );
if ( st->outConfig.ambisonics != prndr_ambisonics_none )
{
/* Pan LFE to south pole (experimental) */
ivas_dirac_dec_get_response( 0,
-90,
st->lfePanGains,
Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) );
/* TODO(sgi): Apply 10 dB gain? Almost surely will clip */
/* for (i=0; i< st->numOutChannels; ++i) {
st->lfePanGains[i] *= 3.1622776602f;
} */
}
else
{
set_zero( st->lfePanGains, st->numOutChannels );
/* Pan LFE to L and R with 4dB gain each (== 10dB - 6dB) */
if ( st->numOutChannels > 1 )
{
st->lfePanGains[0] = 1.5848931925f;
st->lfePanGains[1] = 1.5848931925f;
}
else
{
/* Put LFE in center channel, do not add 10dB gain to avoid clipping */
st->lfePanGains[1] = 1.f;
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void prepareMcPanGains( Prndr_Prerenderer *const st )
{
uint32_t mcIdx;
int32_t spkIdx;
int32_t numChannels;
const float *spkAzi;
const float *spkEle;
#ifdef WMOPS
wmops_sub_start( "prepareMcPanGains" );
#endif
st->speakerPanGains = count_calloc( st->inConfig.numMultiChannelBuses, sizeof( float ** ) );
for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx )
{
numChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
st->speakerPanGains[mcIdx] = count_calloc( numChannels, sizeof( float * ) );
spkAzi = Prndr_getSpeakerAzimuths( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
spkEle = Prndr_getSpeakerElevations( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
if ( st->outConfig.ambisonics != prndr_ambisonics_none )
{
for ( spkIdx = 0; spkIdx < numChannels; ++spkIdx )
{
st->speakerPanGains[mcIdx][spkIdx] = count_calloc( st->numOutChannels, sizeof( float ) );
ivas_dirac_dec_get_response( (int16_t) spkAzi[spkIdx],
(int16_t) spkEle[spkIdx],
st->speakerPanGains[mcIdx][spkIdx],
Prndr_getAmbisonicsOrder( st->outConfig.ambisonics ) );
}
}
else if ( st->outConfig.speakerLayout != prndr_speaker_layout_none )
{
for ( spkIdx = 0; spkIdx < numChannels; ++spkIdx )
{
st->speakerPanGains[mcIdx][spkIdx] = count_calloc( st->numOutChannels, sizeof( float ) );
getSpeakerGains( st, spkAzi[spkIdx], spkEle[spkIdx], st->speakerPanGains[mcIdx][spkIdx] );
}
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void prepareMcPassthrough( Prndr_Prerenderer *const st )
{
/* Output config */
const float *outSpkAzi;
const float *outSpkEle;
const uint32_t *lfeLastIdxs;
uint32_t numNonLfeOutChannels;
uint32_t numOutChannels;
uint32_t lfeLastIdx_lsCustom[MAX_OUTPUT_CHANNELS];
/* Input config */
const float *inSpkAzi;
const float *inSpkEle;
uint32_t numInChannels;
uint32_t numNonLfeInChannels;
/* Input channel index */
uint32_t passThroughIdx;
uint32_t mcIdx;
uint32_t inChIdx;
uint32_t outChIdx;
#ifdef WMOPS
wmops_sub_start( "prepareMcPassthrough" );
#endif
st->mcPassthrough = count_calloc( st->numInChannelsMc, sizeof( int32_t ) );
if ( st->outConfig.speakerLayout == prndr_speaker_layout_custom )
{
/* Number of non-LFE output channels */
numNonLfeOutChannels = st->outConfig.outSetupCustom->num_spk;
/* Number of output channels */
numOutChannels = st->outConfig.outSetupCustom->num_spk + st->outConfig.outSetupCustom->num_lfe;
/* Output speaker coordinates */
outSpkAzi = st->outConfig.outSetupCustom->ls_azimuth;
outSpkEle = st->outConfig.outSetupCustom->ls_elevation;
/* num_spk + num_lfe must be <= MAX_OUTPUT_CHANNELS for custom loudspeaker layouts */
if ( st->outConfig.outSetupCustom->num_lfe > 0 )
{
lfeLastIdx_lsCustom[numNonLfeOutChannels] = st->outConfig.outSetupCustom->lfe_idx[0];
}
for ( outChIdx = 0; outChIdx < numNonLfeOutChannels; ++outChIdx )
{
( ( st->outConfig.outSetupCustom->num_lfe > 0 ) && ( (int16_t) outChIdx >= st->outConfig.outSetupCustom->lfe_idx[0] ) ) ? ( lfeLastIdx_lsCustom[outChIdx] = outChIdx + 1 ) : ( lfeLastIdx_lsCustom[outChIdx] = outChIdx );
}
lfeLastIdxs = &lfeLastIdx_lsCustom[0];
}
else
{
/* Number of non-LFE output channels */
numNonLfeOutChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->outConfig.speakerLayout );
/* Number of output channels */
numOutChannels = Prndr_getNumChannelsInSpeakerLayout( st->outConfig.speakerLayout );
/* Output speaker coordinates */
outSpkAzi = Prndr_getSpeakerAzimuths( st->outConfig.speakerLayout );
outSpkEle = Prndr_getSpeakerElevations( st->outConfig.speakerLayout );
lfeLastIdxs = Prndr_getReorderedChannelIndices( st->outConfig.speakerLayout );
}
passThroughIdx = 0;
for ( mcIdx = 0; mcIdx < st->inConfig.numMultiChannelBuses; ++mcIdx )
{
/* Number of non-LFE input channels for current MC input */
numNonLfeInChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
/* Number of input channels for current MC input */
numInChannels = Prndr_getNumChannelsInSpeakerLayout( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
/* Input speaker coordinates */
inSpkAzi = Prndr_getSpeakerAzimuths( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
inSpkEle = Prndr_getSpeakerElevations( st->inConfig.multiChannelBuses[mcIdx].speakerLayout );
/* Check if passthrough possible for, save I/O mapping */
for ( inChIdx = 0; inChIdx < numNonLfeInChannels; ++inChIdx )
{
st->mcPassthrough[passThroughIdx] = -1;
for ( outChIdx = 0; outChIdx < numNonLfeOutChannels; ++outChIdx )
{
if ( inSpkAzi[inChIdx] == outSpkAzi[outChIdx] &&
inSpkEle[inChIdx] == outSpkEle[outChIdx] )
{
st->mcPassthrough[passThroughIdx] = lfeLastIdxs[outChIdx];
break;
}
}
++passThroughIdx;
}
/* Setup LFE passthrough, save I/O mapping */
outChIdx = numNonLfeOutChannels;
for ( ; inChIdx < numInChannels; ++inChIdx )
{
if ( outChIdx < numOutChannels )
{
st->mcPassthrough[passThroughIdx] = lfeLastIdxs[outChIdx];
}
else
{
st->mcPassthrough[passThroughIdx] = -1;
}
++outChIdx;
++passThroughIdx;
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void passthroughChannel( const Prndr_AudioBuffer inAudio,
const uint32_t srcChnlIdx,
const uint32_t dstChnlIdx,
const float gain_lin,
Prndr_AudioBuffer outAudio )
{
float *inSmpl;
float *outSmpl;
int16_t smplIdx;
#ifdef WMOPS
wmops_sub_start( "passthroughChannel" );
#endif
inSmpl = get_smpl_ptr( inAudio, srcChnlIdx, 0 );
outSmpl = get_smpl_ptr( outAudio, dstChnlIdx, 0 );
for ( smplIdx = 0; smplIdx < inAudio.config.bufferSize; ++smplIdx )
{
*outSmpl += *inSmpl * gain_lin;
++outSmpl;
++inSmpl;
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
static void getSpeakerGains( const Prndr_Prerenderer *const st,
const float azi,
const float ele,
float *const spkGains )
{
const uint32_t *lfeLastIdxs;
int16_t numNonLfeOutChannels;
int16_t noLfeIdx;
Prndr_SpeakerLayout speakerLayout;
#ifdef WMOPS
wmops_sub_start( "getSpeakerGains" );
#endif
/* EFAP returns an array of gains only for non-LFE speakers */
efap_determine_gains( st->efapRenderer, st->noLfePanBuffer, azi, ele, EFAP_MODE_EFAP );
speakerLayout = st->outConfig.speakerLayout;
if ( speakerLayout == prndr_speaker_layout_custom )
{
uint32_t lfeIdx;
numNonLfeOutChannels = st->outConfig.outSetupCustom->num_spk;
/* Clear speaker gains - not all elements will be overwritten below */
set_zero( spkGains, numNonLfeOutChannels + st->outConfig.outSetupCustom->num_lfe );
/* Copy to gain array where LFE channel(s) are included */
for ( lfeIdx = 0, noLfeIdx = 0; noLfeIdx < numNonLfeOutChannels; lfeIdx++, noLfeIdx++ )
{
if ( noLfeIdx == st->outConfig.outSetupCustom->lfe_idx[0] )
{
lfeIdx++;
}
spkGains[lfeIdx] = st->noLfePanBuffer[noLfeIdx];
}
}
else
{
numNonLfeOutChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( speakerLayout );
lfeLastIdxs = Prndr_getReorderedChannelIndices( speakerLayout );
/* Clear speaker gains - not all elements will be overwritten below */
set_zero( spkGains, Prndr_getNumChannelsInSpeakerLayout( speakerLayout ) );
/* Copy to gain array where LFE channel(s) are included */
for ( noLfeIdx = 0; noLfeIdx < numNonLfeOutChannels; ++noLfeIdx )
{
spkGains[lfeLastIdxs[noLfeIdx]] = st->noLfePanBuffer[noLfeIdx];
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
int16_t Prndr_getNumChannelsAmbisonics( Prndr_Ambisonics ambisonics )
{
switch ( ambisonics )
{
case prndr_ambisonics_none:
return 0;
case prndr_ambisonics_mono:
return 1;
case prndr_ambisonics_foa:
return 4;
case prndr_ambisonics_soa:
return 9;
case prndr_ambisonics_toa:
return 16;
default:
assert( !"Invalid ambisonics config" );
}
return 0;
}
static int16_t Prndr_getAmbisonicsOrder( Prndr_Ambisonics ambisonics )
{
assert( ambisonics != prndr_ambisonics_none && "Invalid ambisonics config" );
return ambisonics; /* Enum values map to ambisonics order */
}
int16_t Prndr_getNumChannelsInSpeakerLayout( Prndr_SpeakerLayout layout )
{
switch ( layout )
{
case prndr_speaker_layout_none:
return 0;
case prndr_speaker_layout_mono:
return 1;
case prndr_speaker_layout_stereo:
return 2;
case prndr_speaker_layout_5_1:
return 6;
case prndr_speaker_layout_7_1:
return 8;
case prndr_speaker_layout_5_1_4:
return 10;
case prndr_speaker_layout_7_1_4:
return 12;
default:
assert( !"Invalid speaker layout" );
}
return 0;
}
int16_t Prndr_getNumNonLfeChannelsInSpeakerLayout( Prndr_SpeakerLayout layout )
{
switch ( layout )
{
case prndr_speaker_layout_mono:
return 1;
case prndr_speaker_layout_stereo:
return 2;
case prndr_speaker_layout_5_1:
return 5;
case prndr_speaker_layout_7_1:
return 7;
case prndr_speaker_layout_5_1_4:
return 9;
case prndr_speaker_layout_7_1_4:
return 11;
default:
assert( !"Invalid speaker layout" );
}
return 0;
}
const float *Prndr_getSpeakerAzimuths( Prndr_SpeakerLayout layout )
{
switch ( layout )
{
case prndr_speaker_layout_mono:
return ls_azimuth_CICP1;
case prndr_speaker_layout_stereo:
return ls_azimuth_CICP2;
case prndr_speaker_layout_5_1:
return ls_azimuth_CICP6;
case prndr_speaker_layout_7_1:
return ls_azimuth_CICP12;
case prndr_speaker_layout_5_1_4:
return ls_azimuth_CICP16;
case prndr_speaker_layout_7_1_4:
return ls_azimuth_CICP19;
default:
assert( !"Invalid speaker layout" );
}
return NULL;
}
const float *Prndr_getSpeakerElevations( Prndr_SpeakerLayout layout )
{
switch ( layout )
{
case prndr_speaker_layout_mono:
return ls_elevation_CICP1;
case prndr_speaker_layout_stereo:
return ls_elevation_CICP2;
case prndr_speaker_layout_5_1:
return ls_elevation_CICP6;
case prndr_speaker_layout_7_1:
return ls_elevation_CICP12;
case prndr_speaker_layout_5_1_4:
return ls_elevation_CICP16;
case prndr_speaker_layout_7_1_4:
return ls_elevation_CICP19;
default:
assert( !"Invalid speaker layout" );
}
return NULL;
}
const uint32_t *Prndr_getReorderedChannelIndices( Prndr_SpeakerLayout layout )
{
switch ( layout )
{
case prndr_speaker_layout_mono:
return ls_LFE_last_idx_CICP1;
case prndr_speaker_layout_stereo:
return ls_LFE_last_idx_CICP2;
case prndr_speaker_layout_5_1:
return ls_LFE_last_idx_CICP6;
case prndr_speaker_layout_7_1:
return ls_LFE_last_idx_CICP12;
case prndr_speaker_layout_5_1_4:
return ls_LFE_last_idx_CICP16;
case prndr_speaker_layout_7_1_4:
return ls_LFE_last_idx_CICP19;
default:
assert( !"Invalid speaker layout" );
}
return NULL;
}
static ivas_error Prndr_getHoaRenderMtx(
const Prndr_OutputConfig outConfig,
float **decMtx,
uint32_t ambiOrder )
{
IVAS_OUTPUT_SETUP hOutSetup;
ivas_error error;
error = IVAS_ERR_OK;
#ifdef WMOPS
wmops_sub_start( "Prndr_getHoaRenderMtx" );
#endif
switch ( outConfig.speakerLayout )
{
case prndr_speaker_layout_mono:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_MONO );
hOutSetup.ls_azimuth = ls_azimuth_CICP1;
hOutSetup.ls_elevation = ls_elevation_CICP1;
break;
case prndr_speaker_layout_stereo:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_STEREO );
break;
case prndr_speaker_layout_5_1:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_5_1 );
break;
case prndr_speaker_layout_7_1:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_7_1 );
break;
case prndr_speaker_layout_5_1_4:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_5_1_4 );
break;
case prndr_speaker_layout_7_1_4:
ivas_output_init( &hOutSetup, AUDIO_CONFIG_7_1_4 );
break;
case prndr_speaker_layout_custom:
ivas_ls_custom_setup( &hOutSetup, outConfig.outSetupCustom );
break;
default:
assert( !"Invalid speaker config" );
return IVAS_ERR_WRONG_PARAMS;
}
if ( ( error = ivas_sba_get_hoa_dec_matrix( hOutSetup, decMtx, (int16_t) ambiOrder ) ) != IVAS_ERR_OK )
{
return error;
}
#ifdef WMOPS
wmops_sub_end();
#endif
return error;
}
void Prndr_getHoaDecVecForAmbiChnl(
uint32_t ambiChnnlIdx,
const Prndr_OutputConfig outConfig,
const float *decMtx,
float *decCoeffs )
{
const uint32_t *lfeLastIdxs;
int16_t numNonLfeChannels;
int16_t nonLfeChIdx;
#ifdef WMOPS
wmops_sub_start( "Prndr_getHoaDecVecForAmbiChnl" );
#endif
if ( outConfig.speakerLayout == prndr_speaker_layout_custom )
{
uint32_t lfeIdx;
numNonLfeChannels = outConfig.outSetupCustom->num_spk;
/* Clear speaker gains - not all elements will be overwritten below */
set_zero( decCoeffs, numNonLfeChannels + outConfig.outSetupCustom->num_lfe );
/* Copy to gain array where LFE channel(s) are included */
for ( lfeIdx = 0, nonLfeChIdx = 0; nonLfeChIdx < numNonLfeChannels; lfeIdx++, nonLfeChIdx++ )
{
if ( nonLfeChIdx == outConfig.outSetupCustom->lfe_idx[0] )
{
lfeIdx++;
}
decCoeffs[lfeIdx] = decMtx[16 * nonLfeChIdx + ambiChnnlIdx];
}
}
else
{
numNonLfeChannels = Prndr_getNumNonLfeChannelsInSpeakerLayout( outConfig.speakerLayout );
lfeLastIdxs = Prndr_getReorderedChannelIndices( outConfig.speakerLayout );
/* Clear decoding coefficients - not all elements will be overwritten below */
set_zero( decCoeffs, Prndr_getNumChannelsInSpeakerLayout( outConfig.speakerLayout ) );
for ( nonLfeChIdx = 0; nonLfeChIdx < numNonLfeChannels; ++nonLfeChIdx )
{
decCoeffs[lfeLastIdxs[nonLfeChIdx]] = decMtx[16 * nonLfeChIdx + ambiChnnlIdx];
}
}
#ifdef WMOPS
wmops_sub_end();
#endif
}
/*-------------------------------------------------------------------*
* ivas_limiter_prerenderer()
*
* In-place saturation control for multichannel buffers with adaptive release time
*-------------------------------------------------------------------*/
static void ivas_limiter_prerenderer(
IVAS_LIMITER_HANDLE hLimiter, /* i/o: limiter struct handle */
float *output, /* i/o: I/O buffer */
const int16_t output_frame, /* i : number of samples per channel in the buffer */
const float threshold /* i : signal amplitude above which limiting starts to be applied */
)
{
int16_t c;
float **channels;
int16_t num_channels;
/* return early if given bad parameters */
if ( hLimiter == NULL || output == NULL || output_frame <= 0 )
{
return;
}
channels = hLimiter->channel_ptrs;
num_channels = hLimiter->num_channels;
for ( c = 0; c < num_channels; ++c )
{
channels[c] = output + c * output_frame;
}
limiter_process( hLimiter, output_frame, threshold, 0, NULL );
return;
}
static float dBToLin( const float gain_dB )
{
return powf( 10.f, gain_dB / 20.f );
}
......@@ -52,7 +52,7 @@ struct LsCustomFileReader
*-----------------------------------------------------------------------*/
ivas_error CustomLsReader_open(
char *LsFilePath, /* i : LS custom layout file name */
const char *LsFilePath, /* i : LS custom layout file name */
LsCustomFileReader **hLsCustomReader /* o : HeadRotFileReader handle */
)
{
......@@ -314,7 +314,7 @@ LS_CUSTOM_FILEREADER_ERROR CustomLsFileReading(
}
}
/* parse LFE indicies; skip if blank line */
/* parse LFE indices; skip if blank line */
if ( ( fgets( line, 200, hLsCustomReader->file ) != NULL ) && ( strcmp( line, "\n" ) != 0 ) && ( strcmp( line, "\r\n" ) != 0 ) )
{
for ( tok = strtok( line, "," ); tok && *tok; tok = strtok( NULL, ",\n" ) )
......
......@@ -65,7 +65,7 @@ typedef enum _LS_CUSTOM_FILEREADER_ERROR
*-----------------------------------------------------------------------*/
ivas_error CustomLsReader_open(
char *LsFilePath, /* i : LS custom layout file name */
const char *LsFilePath, /* i : LS custom layout file name */
LsCustomFileReader **hLsCustomReader /* o : HeadRotFileReader handle */
);
......
......@@ -419,7 +419,7 @@ ivas_error RenderConfigReader_read(
while ( sscanf( pParams + params_idx, "%64[^=]=%[^;];", item, pValue ) == 2 )
{
hRenderConfig->room_acoustics.override = true;
params_idx += strlen( item ) + strlen( pValue ) + 2;
params_idx += (int32_t) ( strlen( item ) + strlen( pValue ) + 2 );
#ifdef DEBUGGING
fprintf( stderr, " PARAM: %s -> %s\n", item, pValue );
#endif
......@@ -506,7 +506,7 @@ ivas_error RenderConfigReader_read(
pValue = (char *) calloc( strlen( pParams ), sizeof( char ) );
while ( sscanf( pParams + params_idx, "%64[^=]=%[^;];", item, pValue ) == 2 )
{
params_idx += strlen( item ) + strlen( pValue ) + 2;
params_idx += (int32_t) ( strlen( item ) + strlen( pValue ) + 2 );
fprintf( stderr, " PARAM: %s -> %s\n", item, pValue );
if ( strcmp( item, "RENDERER" ) == 0 )
{
......
# pytest.ini
# note: per convention, this file is placed in the root directory of the repository
[pytest]
addopts = -ra --tb=short --basetemp=./tmp -v
# TODO remove ignore after tests are harmonized
addopts = -ra --tb=short --basetemp=./tmp -n auto -v --ignore=tests/renderer
# Write captured system-out log messages to JUnit report.
junit_logging = system-out
# Do not capture log information for passing tests to JUnit report.
junit_log_passing_tests = False
junit_duration_report = call
junit_family = xunit1
log_file_level = DEBUG
log_format = %(asctime)s %(levelname)s %(message)s
......
......@@ -62,7 +62,7 @@ EOM
}
cl-format-check-version() {
${CLANG_FORMAT} --version | awk '{print $3}'
${CLANG_FORMAT} --version | sed 's/.*version \([1-9]*\.[0-9]*\).*/\1/'
}
cl-format-apply() {
......@@ -229,7 +229,7 @@ else
rm "$NUMFAILSTMPFILE"
if [[ $NUMFAILS -gt 0 ]]; then
echo "Total fails: $NUMFAILS"
# exit $NUMFAILS ## uncomment if script should have num fails as return code
exit $NUMFAILS ## uncomment if script should have num fails as return code
fi
fi
......
{
"afspPath": "not_needed",
"utilPath": "/tools",
"inpaths": {
"MONO": "/usr/local/ltv/ltv48_MONO.wav",
"STEREO": "/usr/local/ltv/ltv48_STEREO.wav",
"FOA": "/usr/local/ltv/ltv48_FOA.wav",
"HOA2": "/usr/local/ltv/ltv48_HOA2.wav",
"HOA3": "/usr/local/ltv/ltv48_HOA3.wav",
"SBA": "/usr/local/ltv/ltv48_HOA3.wav",
"MASA1TC1DIR": "/usr/local/testv/test_MASA_1dir1TC.wav",
"MASA1TC2DIR": "/usr/local/testv/test_MASA_2dir1TC.wav",
"MASA2TC1DIR": "/usr/local/testv/test_MASA_1dir2TC.wav",
"MASA2TC2DIR": "/usr/local/testv/test_MASA_2dir2TC.wav",
"5_1": "/usr/local/ltv/ltv48_MC51.wav",
"5_1_2": "/usr/local/ltv/ltv48_MC512.wav",
"5_1_4": "/usr/local/ltv/ltv48_MC514.wav",
"7_1": "/usr/local/ltv/ltv48_MC71.wav",
"7_1_4": "/usr/local/ltv/ltv48_MC714.wav",
"ISM1": "/usr/local/ltv/ltv48_1ISM.wav",
"ISM2": "/usr/local/ltv/ltv48_2ISM.wav",
"ISM3": "/usr/local/ltv/ltv48_3ISM.wav",
"ISM4": "/usr/local/ltv/ltv48_4ISM.wav"
}
}
......@@ -276,6 +276,10 @@
../IVAS_cod -ism 1 testv/stvISM1.csv 13200 48 testv/stv1ISM48s.pcm bit
../IVAS_dec MONO 48 bit testv/stv1ISM48s.pcm_13200_48-48_MONO.tst
// 1 ISm with metadata at 13.2 kbps, 48 kHz in, 48 kHz out, DTX on, BINAURAL out, random FEC at 5%
../IVAS_cod -dtx -ism 1 testv/stvISM1.csv 13200 48 testv/stv48n.pcm bit
../IVAS_dec -fec 5 BINAURAL 48 bit testv/stv48n.pcm_13200_48-48_DTX_FEC5_BINAURAL.tst
// 2 ISm with metadata at 16.4 kbps, 48 kHz in, 48 kHz out, STEREO out
../IVAS_cod -ism 2 testv/stvISM1.csv testv/stvISM2.csv 16400 48 testv/stv2ISM48s.pcm bit
../IVAS_dec STEREO 48 bit testv/stv2ISM48s.pcm_16400_48-48_STEREO.tst
......@@ -288,6 +292,10 @@
../IVAS_cod -ism 3 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv 24400 48 testv/stv3ISM48s.pcm bit
../IVAS_dec -fec 5 MONO 48 bit testv/stv3ISM48s.pcm_24400_48-48_MONO_FEC5.tst
// 1 ISm with metadata at 32 kbps, 32 kHz in, 32 kHz out, DTX on, MONO out
../IVAS_cod -dtx -ism 1 testv/stvISM1.csv 32000 32 testv/stv32n.pcm bit
../IVAS_dec MONO 32 bit testv/stv32n.pcm_32000_32-32_DTX_MONO.tst
// 4 ISm with metadata at 32 kbps, 48 kHz in, 48 kHz out, FOA out
../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv 32000 48 testv/stv4ISM48s.pcm bit
../IVAS_dec FOA 48 bit testv/stv4ISM48s.pcm_32000_48-48_FOA.tst
......@@ -392,6 +400,14 @@
../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv 256000 48 testv/stv4ISM48s.pcm bit
../IVAS_dec -t testv/headrot_case03_3000_q.csv BINAURAL 48 bit testv/stv4ISM48s.pcm_256000_48-48_TDHR.tst
// 1 ISm with metadata bitrate switching from 13.2 kbps to 128 kbps, 32 kHz in, 32 kHz out, mono out, DTX on
../IVAS_cod -dtx -ism 1 testv/stvISM1.csv ../scripts/switchPaths/sw_13k2_to_128k_10fr.bin 32 testv/stv32c.pcm bit
../IVAS_dec MONO 32 bit testv/stv32c.pcm_brate_sw_32-32_mono_dtx.tst
// 4 ISm with metadata bitrate switching from 24.4 kbps to 256 kbps, 48 kHz in, 48 kHz out, BINAURAL out
../IVAS_cod -ism 4 testv/stvISM1.csv testv/stvISM2.csv testv/stvISM3.csv testv/stvISM4.csv ../scripts/switchPaths/sw_24k4_256k.bin 48 testv/stv4ISM48s.pcm bit
../IVAS_dec BINAURAL 48 bit testv/stv4ISM48s.pcm_brate_sw_48-48_BINAURAL.tst
// SBA at 13.2 kbps, 32kHz in, 32kHz out, HOA3 out
../IVAS_cod -sba 3 13200 32 testv/stv3OA32c.pcm bit
......@@ -569,6 +585,21 @@
../IVAS_cod -sba 3 512000 48 testv/stv3OA48c.pcm bit
../IVAS_dec binaural 48 bit testv/stv3OA48c.pcm_SBA_512000_48-48_binaural.tst
// SBA FOA bitrate switching from 13.2 kbps to 192 kbps, 48kHz in, 48kHz out, BINAURAL out
../IVAS_cod -sba 1 ../scripts/switchPaths/sw_13k2_192k_50fr.bin 48 testv/stvFOA48c.pcm bit
../IVAS_dec BINAURAL 48 bit testv/stvFOA48c.pcm_sw_48-48_BINAURAL.tst
// SBA 2OA bitrate switching from 16.4 kbps to 512 kbps, 48kHz in, 48kHz out, FOA out
../IVAS_cod -sba 2 ../scripts/switchPaths/sw_16k4_512k_50fr.bin 48 testv/stv2OA48c.pcm bit
../IVAS_dec FOA 48 bit testv/stv2OA48c.pcm_sw_48-48_FOA.tst
// SBA 3OA bitrate switching from 13.2 kbps to 512 kbps, 48kHz in, 48kHz out, HOA3 out
../IVAS_cod -sba 3 ../scripts/switchPaths/sw_13k2_512k.bin 48 testv/stv3OA48c.pcm bit
../IVAS_dec HOA3 48 bit testv/stv3OA48c.pcm_sw_48-48_HOA3.tst
// SBA planar 3OA bitrate switching from 24.4 kbps to 256 kbps, 48kHz in, 48kHz out, 7_1_4 out
../IVAS_cod -sba -3 ../scripts/switchPaths/sw_24k4_256k.bin 48 testv/stv3OA48c.pcm bit
../IVAS_dec 7_1_4 48 bit testv/stv3OA48c.pcm_sw_48-48_7_1_4.tst
// MASA 1dir 1TC at 13.2 kbps, 48kHz in, 48kHz out, BINAURAL out
../IVAS_cod -masa 1 testv/stv_IVASMASA_1dir1TC.met 13200 48 testv/stv_IVASMASA_1dir1TC.pcm bit
......
......@@ -32,7 +32,7 @@
options=$1
OBJDIR=obj
EXECUTABLES="../IVAS_cod ../IVAS_dec ./prerenderer/IVAS_prerenderer"
EXECUTABLES="../IVAS_cod ../IVAS_dec ../IVAS_rend"
evaluateTables=1
evaluateFunctions=1
......@@ -68,7 +68,6 @@ fi
# build
if [ $compile -ne 0 ]; then
make -C .. DEBUG=0 STRIP=1 clean all 1>&2
make -C prerenderer DEBUG=0 STRIP=1 clean all 1>&2
fi
if [ $evaluateFunctions != 0 ]; then
......
#!/usr/bin/env python3
"""
(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.
"""
"""
Script to run the pytest tests.
Step 1: Set the stage for the pytest run.
Step 2: Run pytest.
"""
import os
import sys
import argparse
import subprocess
import platform
from pathlib import Path
sys.path.append('scripts/ivas_pytests/tests/')
from cut_pcm import cut_samples
BIN_EXT = ".exe" if platform.system() == "Windows" else ""
HERE = Path(__file__).parent.resolve()
DEFAULT_ENCODER_DUT = str(HERE.joinpath(f"../../IVAS_cod{BIN_EXT}").resolve())
DEFAULT_DECODER_DUT = str(HERE.joinpath(f"../../IVAS_dec{BIN_EXT}").resolve())
DEFAULT_ENCODER_REF = str(HERE.joinpath(f"../../IVAS_cod_ref{BIN_EXT}").resolve())
DEFAULT_DECODER_REF = str(HERE.joinpath(f"../../IVAS_dec_ref{BIN_EXT}").resolve())
CREND_UNITTEST_REF = str(HERE.joinpath(f"tests/unit_tests/crend/IVAS_crend_unit_test_ref{BIN_EXT}").resolve())
TEST_VECTOR_DIR = str(HERE.joinpath("../testv").resolve())
REFERENCE_DIR = str(HERE.joinpath("ref").resolve())
DUT_BASE_DIR = str(HERE.joinpath("dut").resolve())
def build_enc_and_dec(src_dir):
"""
Build the encoder and decoder binaries.
"""
if platform.system() == "Windows":
olddir = os.getcwd()
os.chdir(src_dir)
os.chdir("Workspace_msvc")
command = ["MSBuild.exe", "Workspace_msvc.sln", "/t:Clean", "/p:configuration=Release", "/p:Platform=Win32"]
subprocess.run(command, check=True)
command = ["MSBuild.exe", "Workspace_msvc.sln", "/property:configuration=Release", "/p:Platform=Win32"]
subprocess.run(command, check=True)
os.chdir(olddir)
else:
command = ["make", "-C", src_dir, "clean"]
subprocess.run(command, check=True)
command = ["make", "-C", src_dir]
subprocess.run(command, check=True)
def build_crend_unittest(src_dir):
"""
Build the crend unit test binary.
"""
crend_dir = f"{src_dir}/scripts/ivas_pytests/tests/unit_tests/crend"
if platform.system() == "Windows":
olddir = os.getcwd()
os.chdir(crend_dir)
# command = ["MSBuild.exe", "ivas_crend_unit_test.sln", "/t:Clean", "/p:configuration=Release", "/p:Platform=Win32"]
# subprocess.run(command, check=True)
command = ["MSBuild.exe", "ivas_crend_unit_test.sln", "/property:configuration=Release", "/p:Platform=Win32"]
subprocess.run(command, check=True)
os.chdir(olddir)
else:
# command = ["make", "-C", src_dir, "clean"]
# subprocess.run(command, check=True)
command = ["make", "-C", src_dir, "IVAS_crend_unit_test"]
subprocess.run(command, check=True)
def build_dut_binaries():
"""
Build the DUT binaries.
"""
print("Building the DUT binaries")
dut_src_dir = str(HERE.joinpath("../..").resolve())
build_enc_and_dec(dut_src_dir)
build_crend_unittest(dut_src_dir)
def create_short_testvectors():
"""
Create short (5sec) testvectors.
"""
print("Creating short (5sec) testvectors")
num_channels = "4" # currently only FOA
cut_from = "0.0"
cut_len = "5.0"
for fs in ['48', '32', '16']:
in_file = f"{TEST_VECTOR_DIR}/stvFOA{fs}c.pcm"
cut_gain = "1.0"
cut_file = f"{TEST_VECTOR_DIR}/stvFOA{fs}c_cut.pcm"
cut_samples(in_file, cut_file, num_channels, fs + "000", cut_from, cut_len, cut_gain)
cut_gain = "16.0"
cut_file = f"{TEST_VECTOR_DIR}/stvFOA{fs}c_cut_{cut_gain}.pcm"
cut_samples(in_file, cut_file, num_channels, fs + "000", cut_from, cut_len, cut_gain)
def main(argv):
# check for python >= 3.7
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
sys.exit("This script is written for Python >= 3.7. Found: " + platform.python_version())
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument(
"--create_only",
action="store_true",
default=False,
help="Create references when needed, but don't run the tests"
)
parser.add_argument(
"--numprocesses",
action="store",
default="auto",
help="Number of processes to use in pytest (default: auto)",
)
parser.add_argument("--encref", help=f"REF encoder binary (default:{DEFAULT_ENCODER_REF})")
parser.add_argument("--decref", help=f"REF decoder binary (default:{DEFAULT_DECODER_REF})")
parser.add_argument("--encdut", help=f"DUT encoder binary (default:{DEFAULT_ENCODER_DUT})")
parser.add_argument("--decdut", help=f"DUT decoder binary (default:{DEFAULT_DECODER_DUT})")
args = parser.parse_args(argv[1:])
# check for DUT binaries
if args.encdut:
encdut_path = os.path.realpath(args.encdut)
if not os.path.exists(encdut_path):
sys.exit(f"DUT encoder binary {encdut_path} does not exist.")
else:
encdut_path = DEFAULT_ENCODER_DUT
if args.decdut:
decdut_path = os.path.realpath(args.decdut)
if not os.path.exists(decdut_path):
sys.exit(f"DUT encoder binary {decdut_path} does not exist.")
else:
decdut_path = DEFAULT_DECODER_DUT
if not os.path.exists(encdut_path) or not os.path.exists(decdut_path):
build_dut_binaries()
if not os.path.exists(REFERENCE_DIR):
# check for REF binaries
if args.encref:
encref_path = os.path.realpath(args.encref)
if not os.path.exists(encref_path):
sys.exit(f"REF encoder binary {encref_path} does not exist.")
else:
encref_path = DEFAULT_ENCODER_REF
if args.decref:
decref_path = os.path.realpath(args.decref)
if not os.path.exists(decref_path):
sys.exit(f"REF encoder binary {decref_path} does not exist.")
else:
decref_path = DEFAULT_DECODER_REF
if not os.path.exists(encref_path) or not os.path.exists(decref_path):
sys.exit("Reference binaries do not exist.")
# check for test vectors
if not os.path.exists(TEST_VECTOR_DIR):
sys.exit(f"Test vector directory {TEST_VECTOR_DIR} does not exist.")
# check for references
if os.path.exists(REFERENCE_DIR):
print(f"Using existing references directory {REFERENCE_DIR}")
else:
# create references
print(f"Creating references within the references directory {REFERENCE_DIR}")
create_short_testvectors()
if platform.system() == "Windows":
base_cmd = ["pytest"]
else:
base_cmd = ["python3", "-m", "pytest"]
base_cmd += [
"scripts/ivas_pytests/tests",
"-n",
args.numprocesses,
"--update_ref",
"1",
"-v",
"--data_system_tests_path",
TEST_VECTOR_DIR,
"--reference_path",
REFERENCE_DIR,
"--dut_base_path",
DUT_BASE_DIR,
"--ref_encoder_path",
encref_path,
"--ref_decoder_path",
decref_path,
"--dut_encoder_path",
encdut_path,
"--dut_decoder_path",
decdut_path,
]
# work-around in unit tests via environment variable
# TESTVECTOR_PATH_REL_GROUPB: to specify the test vector directory relative to ivas_pytests folder
# TESTVECTOR_PATH_REL_TRUNK: to specify the test vector directory relative to trunk
my_env = os.environ.copy()
my_env["TESTVECTOR_PATH_REL_GROUPB"] = "testv/"
my_env["TESTVECTOR_PATH_REL_TRUNK"] = "/scripts/ivas_pytests/testv/" # leading "/" is important
my_env["CREND_UNIT_TEST_BIN"] = CREND_UNITTEST_REF
print("pytest command line to be executed from project root folder:")
print(" ".join(base_cmd + ["-m", "create_ref"]))
subprocess.run(base_cmd + ["-m", "create_ref"], check=False, env=my_env)
print("pytest command line to be executed from project root folder:")
print(" ".join(base_cmd + ["-m", "create_ref_part2"]))
subprocess.run(base_cmd + ["-m", "create_ref_part2"], check=False, env=my_env)
if args.create_only:
return
# run pytest
if platform.system() == "Windows":
cmd = ["pytest"]
else:
cmd = ["python3", "-m", "pytest"]
cmd += [
"scripts/ivas_pytests/tests",
"-n",
args.numprocesses,
"-v",
"--data_system_tests_path",
TEST_VECTOR_DIR,
"--reference_path",
REFERENCE_DIR,
"--dut_base_path",
DUT_BASE_DIR,
"--dut_encoder_path",
encdut_path,
"--dut_decoder_path",
decdut_path,
"--junit-xml=report-junit.xml",
]
# print pytest commandline
print("pytest command line to be executed from project root folder:")
print(" ".join(cmd))
result = subprocess.run(cmd, check=False)
return result.returncode
if __name__ == "__main__":
sys.exit(main(sys.argv))
pytest==5.3.5
pytest-xdist==1.31.0
scipy==1.5.2
numpy==1.19.2
......@@ -46,9 +46,12 @@
#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_MAX_NUM_CH 16
#define IVAS_MAX_PATH_LEN ( 2000 )
typedef enum ivas_in_out_fmt_struct_t
......@@ -60,6 +63,7 @@ typedef enum ivas_in_out_fmt_struct_t
MULT_CH_5_1,
MULT_CH_7_1,
MULT_CH_5_1_2,
MULT_CH_5_1_4,
MULT_CH_7_1_4,
HOA_9,
HOA_16,
......
......@@ -200,14 +200,14 @@ static ivas_result_t ivas_crend_reverb_test( ivas_crend_io_params_t *pIo_params
#else
AudioFileWriter_close( &pIo_params->fOut );
AudioFileReader *fOut;
AudioFileReader_open( &fOut , pIo_params->out_path, pIo_params->sample_rate);
AudioFileReader_open( &fOut , pIo_params->out_path, &pIo_params->sample_rate);
int16_t numRead;
/* Compare */
if ( pIo_params->fRef )
{
AudioFileReader_close( &pIo_params->fRef );
AudioFileReader *fRef;
AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate );
if ( test != FAIL )
{
......@@ -337,8 +337,8 @@ static ivas_result_t ivas_crend_proximity_test( ivas_crend_io_params_t *pIo_para
AudioFileReader_close( &pIo_params->fRef );
AudioFileWriter_close( &pIo_params->fOut );
AudioFileReader *fRef, *fOut;
AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate );
AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate );
while ( 1 )
{
acc_0f = 0.0f;
......@@ -416,6 +416,10 @@ static ivas_result_t ivas_crend_binaural_test( ivas_crend_io_params_t *pIo_param
{
test_case = "CREND_512_TO_BIN";
}
else if ( pIo_params->in_fmt == MULT_CH_5_1_4 )
{
test_case = "CREND_514_TO_BIN";
}
else if ( pIo_params->in_fmt == MULT_CH_7_1_4 )
{
test_case = "CREND_714_TO_BIN";
......@@ -496,8 +500,8 @@ static ivas_result_t ivas_crend_binaural_test( ivas_crend_io_params_t *pIo_param
AudioFileReader_close( &pIo_params->fRef );
AudioFileWriter_close( &pIo_params->fOut );
AudioFileReader *fRef, *fOut;
AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate );
AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate );
skip_samples = (int32_t) ( pIo_params->latency_s * pIo_params->sample_rate );
/* skip intial samples based on latency */
......@@ -626,8 +630,8 @@ static ivas_result_t ivas_crend_no_diegetic_test( ivas_crend_io_params_t *pIo_pa
AudioFileReader_close( &pIo_params->fRef );
AudioFileWriter_close( &pIo_params->fOut );
AudioFileReader *fRef, *fOut;
AudioFileReader_open( &fOut, pIo_params->out_path, pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, pIo_params->sample_rate );
AudioFileReader_open( &fOut, pIo_params->out_path, &pIo_params->sample_rate );
AudioFileReader_open( &fRef, pIo_params->ref_path, &pIo_params->sample_rate );
if ( fRef )
{
......