Commit fc9b20c8 authored by Archit Tamarapu's avatar Archit Tamarapu
Browse files

initial version of configurable LFE handling for renderer

parent 407ad654
Loading
Loading
Loading
Loading
Loading
+113 −0
Original line number Diff line number Diff line
@@ -132,6 +132,11 @@ typedef struct
    bool quietModeEnabled;
    bool sceneDescriptionInput;
    float inputGainGlobal; /* Linear gain (not in dB) */
#ifdef REND_CFG_LFE
    float lfeConfigGain; /* Linear gain (not in dB) */
    float lfeConfigAzimuth;
    float lfeConfigElevation;
#endif
} CmdlnArgs;

typedef enum
@@ -222,12 +227,19 @@ static const CmdLnParser_Option cliOptions[] = {
        .description = "Head orientation tracking type: 'ref' or 'avg' (only for BINAURAL and BINAURAL_ROOM) (todo: check implementation)",
    },
    {
#ifdef REND_CFG_LFE
        .id = CmdLnOptionId_customLfeRouting,
        .match = "lfe_config",
        .matchShort = "lfe",
        .description = "LFE Routing configuration. Comma-delimited triplet of [gain, azimuth, elevation] where gain is linear (like --gain, -g) and azimuth, elevation are in degrees.\nIf specified, overrides routing of input LFE to output LFE for outputs that have the LFE channel available.",
#else
        /* TODO(sgi): Replace with more configurable input, e.g. ask for a list of triplets: (gain, azimuth, elevation) to place LFE signal */
        /* rename to "lfeHandling" */
        .id = CmdLnOptionId_customLfeRouting,
        .match = "neverDropLfe",
        .matchShort = "ndl",
        .description = "[flag] If set, renderer tries to render LFE into other channels in an optimal way when rendering to configs w/o LFE",
#endif
    },
    {
        .id = CmdLnOptionId_noDelayCmp,
@@ -679,7 +691,15 @@ int main(
            }
        }

#ifdef REND_CFG_LFE
        if ( ( error = IVAS_REND_SetInputLfeRouting( hIvasRend, mcIds[i], args.lfeConfigGain, args.lfeConfigAzimuth, args.lfeConfigElevation ) ) != IVAS_ERR_OK )
        {
            fprintf( stderr, "Error: %s\n", ivas_error_to_string( error ) );
            exit( -1 );
        }
#else
        /* TODO(sgi): Test custom LFE routing here */
#endif
    }

    for ( i = 0; i < args.inConfig.numAudioObjects; ++i )
@@ -1175,12 +1195,18 @@ static bool parseOutConfig(
    return true;
}

#ifdef REND_CFG_LFE
static bool parseDiegeticPan(
#else
static int8_t parseDiegeticPan(
#endif
    char *value,
    float *noDiegeticPan )
{
#ifndef REND_CFG_LFE
    int8_t success;
    success = 1;
#endif
    to_upper( value );

    if ( ( strcmp( value, "CENTER" ) == 0 ) || ( strchr( value, 'C' ) != NULL ) )
@@ -1202,18 +1228,32 @@ static int8_t parseDiegeticPan(
        if ( *noDiegeticPan > 1.0f || *noDiegeticPan < -1.0f )
        {
            fprintf( stderr, "Error: Incorrect value for panning option argument specified!\n\n" );
#ifdef REND_CFG_LFE
            return false;
#else
            success = 0;
#endif
        }
    }
#ifdef REND_CFG_LFE
    return false;
#else
    return success ? 0 : -1;
#endif
}

#ifdef REND_CFG_LFE
static bool parseOrientationTracking(
#else
static int8_t parseOrientationTracking(
#endif
    char *value,
    int8_t *tracking_type )
{
#ifndef REND_CFG_LFE
    int8_t success;
    success = 1;
#endif

    to_upper( value );

@@ -1228,10 +1268,18 @@ static int8_t parseOrientationTracking(
    else
    {
        fprintf( stderr, "Error: Invalid orientation tracking type %s \n\n", value );
#ifdef REND_CFG_LFE
        return false;
#else
        success = 0;
#endif
    }

#ifdef REND_CFG_LFE
    return true;
#else
    return success ? 0 : -1;
#endif
}

static IVAS_REND_AudioConfig parseAudioConfig(
@@ -1313,6 +1361,48 @@ static IVAS_REND_AudioConfig parseAudioConfig(
    return IVAS_REND_AUDIO_CONFIG_UNKNOWN;
}

#ifdef REND_CFG_LFE
static bool parseLfeConfig(
    const char *value,
    float *lfeGain,
    float *lfeAzimuth,
    float *lfeElevation )
{
    uint8_t nvalues;
    const char *tok;
    float values[3];
    char config_string[RENDERER_MAX_METADATA_LINE_LENGTH];

    strncpy( config_string, value, RENDERER_MAX_METADATA_LINE_LENGTH );
    nvalues = 0;

    for ( tok = strtok( config_string, "," ); tok && *tok; tok = strtok( NULL, ",\n" ) )
    {
        while ( *tok == ' ' )
        {
            tok++;
        }

        if ( *tok == '\0' )
        {
            continue;
        }
        values[nvalues] = (float) atof( tok );
        nvalues++;
    }

    if ( nvalues != 3 )
    {
        return false;
    }

    *lfeGain = values[0];
    *lfeAzimuth = values[1];
    *lfeElevation = values[2];
    return true;
}
#endif

static const CmdLnParser_Option *findOptionById(
    const int32_t id )
{
@@ -1410,6 +1500,11 @@ static CmdlnArgs defaultArgs(
    args.sceneDescriptionInput = false;
    args.inputGainGlobal = 1.0f;

#ifdef REND_CFG_LFE
    args.lfeConfigGain = 1.0f;
    args.lfeConfigAzimuth = 0;
    args.lfeConfigElevation = 0;
#endif
    return args;
}

@@ -1480,7 +1575,11 @@ static void parseOption(
            break;
        case CmdLnOptionId_noDiegeticPan:
            assert( numOptionValues == 1 );
#ifdef REND_CFG_LFE
            if ( !parseDiegeticPan( optionValues[0], &args->noDiegeticPan ) )
#else
            if ( parseDiegeticPan( optionValues[0], &args->noDiegeticPan ) != 0 )
#endif
            {
                fprintf( stderr, "Unknown option for diegetic panning: %s\n", optionValues[0] );
                exit( -1 );
@@ -1488,14 +1587,28 @@ static void parseOption(
            break;
        case CmdLnOptionId_orientationTracking:
            assert( numOptionValues == 1 );
#ifdef REND_CFG_LFE
            if ( !parseOrientationTracking( optionValues[0], &args->orientationTracking ) )
#else
            if ( parseOrientationTracking( optionValues[0], &args->orientationTracking ) != 0 )
#endif
            {
                fprintf( stderr, "Unknown option for orientation tracking: %s\n", optionValues[0] );
                exit( -1 );
            }
            break;
        case CmdLnOptionId_customLfeRouting:
#ifdef REND_CFG_LFE
            assert( numOptionValues == 1 );
            if ( !parseLfeConfig( optionValues[0], &args->lfeConfigGain, &args->lfeConfigAzimuth, &args->lfeConfigElevation ) )
            {
                fprintf( stderr, "Unknown or invalid option for LFE configuration: %s\n", optionValues[0] );
                exit( -1 );
            }
            break;
#else
            assert( 0 && "Not yet implemented in CLI" );
#endif
            break;
        case CmdLnOptionId_noDelayCmp:
            assert( numOptionValues == 0 );
+1 −0
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@
#define FIX_ANGLE_WRAPPING								/* Issue 244: Problems with angle wrapping*/
#define FIX_245_RANGE_CODER_VOIP_MSAN                   /* Issue 245: fix use-of-uninitialized-value in range coder in VoIP mode */
#define FIX_272_COV                                     /* Issue 272: Cleanup for code coverage related to calls to ivas_binaural_cldfb() */
#define REND_CFG_LFE                                    /* Issue 110: Configurable LFE handling for external renderer */
#define FIX_235                                         /* Issue 235: Deallocation of HR filter memory separately for lib_rend (ROM) and lib_util (from file) */


+140 −21
Original line number Diff line number Diff line
@@ -606,6 +606,26 @@ static ivas_error getAmbisonicsOrder(
    return IVAS_ERR_OK;
}

static int32_t getNumLfeChannels(
    input_mc *inputMc )
{
    switch ( inputMc->base.inConfig )
    {
        case IVAS_REND_AUDIO_CONFIG_5_1:
        case IVAS_REND_AUDIO_CONFIG_7_1:
        case IVAS_REND_AUDIO_CONFIG_5_1_2:
        case IVAS_REND_AUDIO_CONFIG_5_1_4:
        case IVAS_REND_AUDIO_CONFIG_7_1_4:
            return 1;
        case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
            return inputMc->customLsInput.num_lfe;
        default:
            break;
    }

    return 0;
}

static ivas_error getNumNonLfeChannelsInSpeakerLayout(
    IVAS_REND_AudioConfig config,
    int16_t *numNonLfeChannels )
@@ -1449,6 +1469,97 @@ static ivas_error updateMcPanGainsForAmbiOut(
    return IVAS_ERR_OK;
}

#ifdef REND_CFG_LFE
static ivas_error updateLfePanGainsForMcOut(
    input_mc *inputMc,
    const IVAS_REND_AudioConfig outConfig )
{
    int16_t i, numLfeIn, numOutChannels;
    ivas_error error;
    error = IVAS_ERR_OK;

    /* If panning is not required, simply return */
    if ( !inputMc->lfeRouting.pan_lfe )
    {
        return error;
    }

    numLfeIn = getNumLfeChannels( inputMc );

    if ( outConfig == IVAS_REND_AUDIO_CONFIG_LS_CUSTOM )
    {
        numOutChannels = inputMc->base.ctx.pCustomLsOut->num_spk + inputMc->base.ctx.pCustomLsOut->num_lfe;
    }
    else
    {
        if ( ( error = getAudioConfigNumChannels( outConfig, &numOutChannels ) ) != IVAS_ERR_OK )
        {
            return error;
        }
    }

    for ( i = 0; i < numLfeIn; i++ )
    {
        /* panning gains */
        if ( ( error = getEfapGains( *inputMc->base.ctx.pEfapOutWrapper,
                                     inputMc->lfeRouting.lfeOutputAzimuth,
                                     inputMc->lfeRouting.lfeOutputElevation,
                                     inputMc->lfeRouting.lfeOutputGains[i] ) ) != IVAS_ERR_OK )
        {
            return error;
        }

        /* linear input gain */
        v_multc( inputMc->lfeRouting.lfeOutputGains[i],
                 inputMc->lfeRouting.lfeInputGain,
                 inputMc->lfeRouting.lfeOutputGains[i],
                 numOutChannels );
    }

    return error;
}

static ivas_error updateLfePanGainsForAmbiOut(
    input_mc *inputMc,
    const IVAS_REND_AudioConfig outConfig )
{
    int16_t i;
    int16_t numLfeIn, outAmbiOrder;
    ivas_error error;
    error = IVAS_ERR_OK;

    /* If panning is not required, simply return */
    if ( !inputMc->lfeRouting.pan_lfe )
    {
        return error;
    }

    if ( ( error = getAmbisonicsOrder( outConfig, &outAmbiOrder ) ) != IVAS_ERR_OK )
    {
        return error;
    }

    numLfeIn = getNumLfeChannels( inputMc );

    for ( i = 0; i < numLfeIn; i++ )
    {
        /* panning gains */
        ivas_dirac_dec_get_response( inputMc->lfeRouting.lfeOutputAzimuth,
                                     inputMc->lfeRouting.lfeOutputElevation,
                                     inputMc->lfeRouting.lfeOutputGains[i],
                                     outAmbiOrder );

        /* linear input gain */
        v_multc( inputMc->lfeRouting.lfeOutputGains[i],
                 inputMc->lfeRouting.lfeInputGain,
                 inputMc->lfeRouting.lfeOutputGains[i],
                 IVAS_MAX_OUTPUT_CHANNELS );
    }

    return error;
}
#endif

static ivas_error updateMcPanGains(
    input_mc *inputMc,
    const IVAS_REND_AudioConfig outConfig )
@@ -1464,9 +1575,15 @@ static ivas_error updateMcPanGains(
    {
        case IVAS_REND_AUDIO_CONFIG_TYPE_CHANNEL_BASED:
            error = updateMcPanGainsForMcOut( inputMc, outConfig );
#ifdef REND_CFG_LFE
            error = updateLfePanGainsForMcOut( inputMc, outConfig );
#endif
            break;
        case IVAS_REND_AUDIO_CONFIG_TYPE_AMBISONICS:
            error = updateMcPanGainsForAmbiOut( inputMc, outConfig );
#ifdef REND_CFG_LFE
            error = updateLfePanGainsForAmbiOut( inputMc, outConfig );
#endif
            break;
        case IVAS_REND_AUDIO_CONFIG_TYPE_BINAURAL:
            switch ( outConfig )
@@ -1476,6 +1593,9 @@ static ivas_error updateMcPanGains(
                case IVAS_REND_AUDIO_CONFIG_BINAURAL_ROOM:
                    /* Prepare rendering to intermediate format */
                    error = updateMcPanGainsForMcOut( inputMc, IVAS_REND_AUDIO_CONFIG_7_1_4 );
#ifdef REND_CFG_LFE
                    error = updateLfePanGainsForMcOut( inputMc, outConfig );
#endif
                    break;
                default:
                    return IVAS_ERR_INVALID_OUTPUT_FORMAT;
@@ -1594,6 +1714,11 @@ static IVAS_REND_LfeRouting defaultLfeRouting(
        set_zero( routing.lfeOutputGains[i], IVAS_MAX_OUTPUT_CHANNELS );
    }

#ifdef REND_CFG_LFE
    routing.pan_lfe = false;
    routing.lfeInputGain = 1.0f;
#endif

    switch ( inConfig )
    {
        case IVAS_REND_AUDIO_CONFIG_5_1:
@@ -2884,30 +3009,17 @@ ivas_error IVAS_REND_SetInputGain(
    return IVAS_ERR_OK;
}

static int32_t getNumLfeChannels(
    input_mc *inputMc )
{
    switch ( inputMc->base.inConfig )
    {
        case IVAS_REND_AUDIO_CONFIG_5_1:
        case IVAS_REND_AUDIO_CONFIG_7_1:
        case IVAS_REND_AUDIO_CONFIG_5_1_2:
        case IVAS_REND_AUDIO_CONFIG_5_1_4:
        case IVAS_REND_AUDIO_CONFIG_7_1_4:
            return 1;
        case IVAS_REND_AUDIO_CONFIG_LS_CUSTOM:
            return inputMc->customLsInput.num_lfe;
        default:
            break;
    }

    return 0;
}

ivas_error IVAS_REND_SetInputLfeRouting(
    IVAS_REND_HANDLE hIvasRend,
    const IVAS_REND_InputId inputId,
    const IVAS_REND_LfeRouting lfeRouting )
#ifdef REND_CFG_LFE
    const float inputGain,
    const float outputAzimuth,
    const float outputElevation
#else
    const IVAS_REND_LfeRouting lfeRouting
#endif
)
{
    input_base *pInputBase;
    input_mc *pInputMc;
@@ -2932,12 +3044,19 @@ ivas_error IVAS_REND_SetInputLfeRouting(
    }
    pInputMc = (input_mc *) pInputBase;

#ifdef REND_CFG_LFE
    pInputMc->lfeRouting.pan_lfe = true;
    pInputMc->lfeRouting.lfeInputGain = inputGain;
    pInputMc->lfeRouting.lfeOutputAzimuth = outputAzimuth;
    pInputMc->lfeRouting.lfeOutputElevation = outputElevation;
#else
    if ( getNumLfeChannels( pInputMc ) != lfeRouting.numLfeChannels )
    {
        return IVAS_ERR_WRONG_NUM_CHANNELS;
    }

    pInputMc->lfeRouting = lfeRouting;
#endif
    if ( ( error = updateMcPanGains( pInputMc, hIvasRend->outputConfig ) ) != IVAS_ERR_OK )
    {
        return error;
+12 −0
Original line number Diff line number Diff line
@@ -136,6 +136,12 @@ typedef uint16_t IVAS_REND_InputId;
typedef struct
{
    int16_t numLfeChannels;
#ifdef REND_CFG_LFE
    bool pan_lfe;
    float lfeInputGain;
    float lfeOutputAzimuth;
    float lfeOutputElevation;
#endif
    float lfeOutputGains[IVAS_MAX_INPUT_LFE_CHANNELS][IVAS_MAX_OUTPUT_CHANNELS];
} IVAS_REND_LfeRouting;

@@ -194,7 +200,13 @@ ivas_error IVAS_REND_SetInputGain(
ivas_error IVAS_REND_SetInputLfeRouting(
    IVAS_REND_HANDLE hIvasRend,                     /* i/o: Renderer handle                                     */
    const IVAS_REND_InputId inputId,                /* i  : ID of the input                                     */
#ifdef REND_CFG_LFE
    const float inputGain,                          /* i  : Input gain to be applied to the LFE channel(s)      */
    const float outputAzimuth,                      /* i  : Output azimuth position                             */
    const float outputElevation                     /* i  : Output elevation position                           */
#else
    const IVAS_REND_LfeRouting lfeRouting           /* i  : custom LFE routing struct                           */
#endif
);

ivas_error IVAS_REND_RemoveInput(