/* * Copyright (C) 2004-2010 NXP Software * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /****************************************************************************************/ /* */ /* Includes */ /* */ /****************************************************************************************/ #include "LVREV_Private.h" #include "Filter.h" /****************************************************************************************/ /* */ /* FUNCTION: LVREV_ApplyNewSettings */ /* */ /* DESCRIPTION: */ /* Applies the new control parameters */ /* */ /* PARAMETERS: */ /* pPrivate Pointer to the instance private parameters */ /* */ /* RETURNS: */ /* LVREV_Success Succeeded */ /* LVREV_NULLADDRESS When pPrivate is NULL */ /* */ /* NOTES: */ /* */ /****************************************************************************************/ LVREV_ReturnStatus_en LVREV_ApplyNewSettings (LVREV_Instance_st *pPrivate) { LVM_Mode_en OperatingMode; LVM_INT32 NumberOfDelayLines; /* Check for NULL pointer */ if(pPrivate == LVM_NULL) { return LVREV_NULLADDRESS; } OperatingMode = pPrivate->NewParams.OperatingMode; if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4) { NumberOfDelayLines = 4; } else if(pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_2) { NumberOfDelayLines = 2; } else { NumberOfDelayLines = 1; } /* * Update the high pass filter coefficients */ if((pPrivate->NewParams.HPF != pPrivate->CurrentParams.HPF) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_FLOAT Omega; FO_FLOAT_Coefs_t Coeffs; Omega = LVM_GetOmega(pPrivate->NewParams.HPF, pPrivate->NewParams.SampleRate); LVM_FO_HPF(Omega, &Coeffs); FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->HPCoefs, &pPrivate->pFastData->HPTaps, &Coeffs); LoadConst_Float(0, (LVM_FLOAT *)&pPrivate->pFastData->HPTaps, sizeof(Biquad_1I_Order1_FLOAT_Taps_t) / sizeof(LVM_FLOAT)); } /* * Update the low pass filter coefficients */ if((pPrivate->NewParams.LPF != pPrivate->CurrentParams.LPF) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_FLOAT Omega; FO_FLOAT_Coefs_t Coeffs; Coeffs.A0 = 1; Coeffs.A1 = 0; Coeffs.B1 = 0; if(pPrivate->NewParams.LPF <= (LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1)) { Omega = LVM_GetOmega(pPrivate->NewParams.LPF, pPrivate->NewParams.SampleRate); /* * Do not apply filter if w =2*pi*fc/fs >= 2.9 */ if(Omega <= (LVM_FLOAT)LVREV_2_9_INQ29) { LVM_FO_LPF(Omega, &Coeffs); } } FO_1I_D32F32Cll_TRC_WRA_01_Init( &pPrivate->pFastCoef->LPCoefs, &pPrivate->pFastData->LPTaps, &Coeffs); LoadConst_Float(0, (LVM_FLOAT *)&pPrivate->pFastData->LPTaps, sizeof(Biquad_1I_Order1_FLOAT_Taps_t) / sizeof(LVM_FLOAT)); } /* * Calculate the room size parameter */ if( pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) { /* Room size range is 10ms to 200ms * 0% -- 10ms * 50% -- 65ms * 100% -- 120ms */ pPrivate->RoomSizeInms = 10 + (((pPrivate->NewParams.RoomSize*11) + 5) / 10); } /* * Update the T delay number of samples and the all pass delay number of samples */ if( (pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_UINT32 Temp; LVM_INT32 APDelaySize; LVM_INT32 Fs = LVM_GetFsFromTable(pPrivate->NewParams.SampleRate); LVM_UINT32 DelayLengthSamples = (LVM_UINT32)(Fs * pPrivate->RoomSizeInms); LVM_INT16 i; LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4, \ LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4}; LVM_INT16 MaxT_Delay[] = {LVREV_MAX_T0_DELAY, LVREV_MAX_T1_DELAY, \ LVREV_MAX_T2_DELAY, LVREV_MAX_T3_DELAY}; LVM_INT16 MaxAP_Delay[] = {LVREV_MAX_AP0_DELAY, LVREV_MAX_AP1_DELAY, \ LVREV_MAX_AP2_DELAY, LVREV_MAX_AP3_DELAY}; /* * For each delay line */ for (i = 0; i < NumberOfDelayLines; i++) { if (i != 0) { LVM_FLOAT Temp1; /* to avoid QAC warning on type conversion */ Temp1=(LVM_FLOAT)DelayLengthSamples; Temp = (LVM_UINT32)(Temp1 * ScaleTable[i]); } else { Temp = DelayLengthSamples; } APDelaySize = Temp / 1500; /* * Set the fixed delay */ Temp = (MaxT_Delay[i] - MaxAP_Delay[i]) * Fs / 192000; pPrivate->Delay_AP[i] = pPrivate->T[i] - Temp; /* * Set the tap selection */ if (pPrivate->AB_Selection) { /* Smooth from tap A to tap B */ pPrivate->pOffsetB[i] = &pPrivate->pDelay_T[i][pPrivate->T[i] - \ Temp - APDelaySize]; pPrivate->B_DelaySize[i] = APDelaySize; pPrivate->Mixer_APTaps[i].Target1 = 0; pPrivate->Mixer_APTaps[i].Target2 = 1.0f; } else { /* Smooth from tap B to tap A */ pPrivate->pOffsetA[i] = &pPrivate->pDelay_T[i][pPrivate->T[i] - \ Temp - APDelaySize]; pPrivate->A_DelaySize[i] = APDelaySize; pPrivate->Mixer_APTaps[i].Target2 = 0; pPrivate->Mixer_APTaps[i].Target1 = 1.0f; } /* * Set the maximum block size to the smallest delay size */ pPrivate->MaxBlkLen = Temp; if (pPrivate->MaxBlkLen > pPrivate->A_DelaySize[i]) { pPrivate->MaxBlkLen = pPrivate->A_DelaySize[i]; } if (pPrivate->MaxBlkLen > pPrivate->B_DelaySize[i]) { pPrivate->MaxBlkLen = pPrivate->B_DelaySize[i]; } } if (pPrivate->AB_Selection) { pPrivate->AB_Selection = 0; } else { pPrivate->AB_Selection = 1; } /* * Limit the maximum block length */ /* Just as a precausion, but no problem if we remove this line */ pPrivate->MaxBlkLen = pPrivate->MaxBlkLen - 2; if(pPrivate->MaxBlkLen > pPrivate->InstanceParams.MaxBlockSize) { pPrivate->MaxBlkLen = (LVM_INT32)pPrivate->InstanceParams.MaxBlockSize; } } /* * Update the low pass filter coefficient */ if( (pPrivate->NewParams.Damping != pPrivate->CurrentParams.Damping) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_INT32 Temp; LVM_FLOAT Omega; FO_FLOAT_Coefs_t Coeffs; LVM_INT16 i; LVM_INT16 Damping = (LVM_INT16)((pPrivate->NewParams.Damping * 100) + 1000); LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_0_on_4, LVREV_T_3_Power_1_on_4, LVREV_T_3_Power_2_on_4, LVREV_T_3_Power_3_on_4}; /* * For each filter */ for (i = 0; i < NumberOfDelayLines; i++) { if (i != 0) { Temp = (LVM_INT32)(ScaleTable[i] * Damping); } else { Temp = Damping; } if(Temp <= (LVM_INT32)(LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1)) { Omega = LVM_GetOmega(Temp, pPrivate->NewParams.SampleRate); LVM_FO_LPF(Omega, &Coeffs); } else { Coeffs.A0 = 1; Coeffs.A1 = 0; Coeffs.B1 = 0; } FO_1I_D32F32Cll_TRC_WRA_01_Init(&pPrivate->pFastCoef->RevLPCoefs[i], &pPrivate->pFastData->RevLPTaps[i], &Coeffs); } } /* * Update All-pass filter mixer time constants */ if( (pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->NewParams.Density != pPrivate->CurrentParams.Density)) { LVM_INT16 i; LVM_FLOAT Alpha; LVM_FLOAT AlphaTap; Alpha = LVM_Mixer_TimeConstant(LVREV_ALLPASS_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1); AlphaTap = LVM_Mixer_TimeConstant(LVREV_ALLPASS_TAP_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1); for (i = 0; i < 4; i++) { pPrivate->Mixer_APTaps[i].Alpha1 = AlphaTap; pPrivate->Mixer_APTaps[i].Alpha2 = AlphaTap; pPrivate->Mixer_SGFeedback[i].Alpha = Alpha; pPrivate->Mixer_SGFeedforward[i].Alpha = Alpha; } } /* * Update the feed back gain */ if( (pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) || (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->NewParams.T60 != pPrivate->CurrentParams.T60) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_FLOAT G[4]; /* Feedback gain (Q7.24) */ if(pPrivate->NewParams.T60 == 0) { G[3] = 0; G[2] = 0; G[1] = 0; G[0] = 0; } else { LVM_FLOAT Temp1; LVM_FLOAT Temp2; LVM_INT16 i; LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4, LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4}; /* * For each delay line */ for (i = 0; i < NumberOfDelayLines; i++) { Temp1 = (3 * pPrivate->RoomSizeInms * ScaleTable[i]) / pPrivate->NewParams.T60; if(Temp1 >= (4)) { G[i] = 0; } else if((Temp1 >= (2))) { Temp2 = LVM_Power10(-(Temp1 / 2.0f)); Temp1 = LVM_Power10(-(Temp1 / 2.0f)); Temp1 = Temp1 * Temp2; } else { Temp1 = LVM_Power10(-(Temp1)); } if (NumberOfDelayLines == 1) { G[i] = Temp1; } else { LVM_FLOAT TempG; TempG = Temp1 * ONE_OVER_SQRT_TWO; G[i]=TempG; } } } /* Set up the feedback mixers for four delay lines */ pPrivate->FeedbackMixer[0].Target=G[0]; pPrivate->FeedbackMixer[1].Target=G[1]; pPrivate->FeedbackMixer[2].Target=G[2]; pPrivate->FeedbackMixer[3].Target=G[3]; } /* * Calculate the gain correction */ if((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) || (pPrivate->NewParams.Level != pPrivate->CurrentParams.Level) || (pPrivate->NewParams.T60 != pPrivate->CurrentParams.T60) ) { LVM_INT32 Index=0; LVM_FLOAT Index_FLOAT; LVM_INT32 i=0; LVM_FLOAT Gain=0; LVM_INT32 RoomSize=0; LVM_FLOAT T60; LVM_FLOAT Coefs[5]; if(pPrivate->NewParams.RoomSize == 0) { RoomSize = 1; } else { RoomSize = (LVM_INT32)pPrivate->NewParams.RoomSize; } if(pPrivate->NewParams.T60 < 100) { T60 = 100 * LVREV_T60_SCALE; } else { T60 = pPrivate->NewParams.T60 * LVREV_T60_SCALE; } /* Find the nearest room size in table */ for(i = 0; i < 24; i++) { if(RoomSize <= LVREV_GainPolyTable[i][0]) { Index = i; break; } } if(RoomSize == LVREV_GainPolyTable[Index][0]) { /* Take table values if the room size is in table */ for(i = 1; i < 5; i++) { Coefs[i-1] = LVREV_GainPolyTable[Index][i]; } Coefs[4] = 0; Gain = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */ } else { /* Interpolate the gain between nearest room sizes */ LVM_FLOAT Gain1,Gain2; LVM_INT32 Tot_Dist,Dist; Tot_Dist = (LVM_UINT32)LVREV_GainPolyTable[Index][0] - \ (LVM_UINT32)LVREV_GainPolyTable[Index-1][0]; Dist = RoomSize - (LVM_UINT32)LVREV_GainPolyTable[Index - 1][0]; /* Get gain for first */ for(i = 1; i < 5; i++) { Coefs[i-1] = LVREV_GainPolyTable[Index-1][i]; } Coefs[4] = 0; Gain1 = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */ /* Get gain for second */ for(i = 1; i < 5; i++) { Coefs[i-1] = LVREV_GainPolyTable[Index][i]; } Coefs[4] = 0; Gain2 = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */ /* Linear Interpolate the gain */ Gain = Gain1 + (((Gain2 - Gain1) * Dist) / (Tot_Dist)); } /* * Get the inverse of gain: Q.15 * Gain is mostly above one except few cases, take only gains above 1 */ if(Gain < 1) { pPrivate->Gain = 1; } else { pPrivate->Gain = 1 / Gain; } Index_FLOAT = 100.0f / (LVM_FLOAT)(100 + pPrivate->NewParams.Level); pPrivate->Gain = pPrivate->Gain * Index_FLOAT; pPrivate->GainMixer.Target = (pPrivate->Gain*Index_FLOAT) / 2; } /* * Update the all pass comb filter coefficient */ if( (pPrivate->NewParams.Density != pPrivate->CurrentParams.Density) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_INT16 i; LVM_FLOAT b = (LVM_FLOAT)pPrivate->NewParams.Density * LVREV_B_8_on_1000; for (i = 0; i < 4; i++) { pPrivate->Mixer_SGFeedback[i].Target = b; pPrivate->Mixer_SGFeedforward[i].Target = b; } } /* * Update the bypass mixer time constant */ if((pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) || (pPrivate->bFirstControl == LVM_TRUE)) { LVM_UINT16 NumChannels = 1; /* Assume MONO format */ LVM_FLOAT Alpha; Alpha = LVM_Mixer_TimeConstant(LVREV_FEEDBACKMIXER_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), NumChannels); pPrivate->FeedbackMixer[0].Alpha = Alpha; pPrivate->FeedbackMixer[1].Alpha = Alpha; pPrivate->FeedbackMixer[2].Alpha = Alpha; pPrivate->FeedbackMixer[3].Alpha = Alpha; NumChannels = 2; /* Always stereo output */ pPrivate->BypassMixer.Alpha1 = LVM_Mixer_TimeConstant(LVREV_BYPASSMIXER_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), NumChannels); pPrivate->BypassMixer.Alpha2 = pPrivate->BypassMixer.Alpha1; pPrivate->GainMixer.Alpha = pPrivate->BypassMixer.Alpha1; } /* * Update the bypass mixer targets */ if( (pPrivate->NewParams.Level != pPrivate->CurrentParams.Level) && (pPrivate->NewParams.OperatingMode == LVM_MODE_ON)) { pPrivate->BypassMixer.Target2 = (LVM_FLOAT)(pPrivate->NewParams.Level ) / 100.0f; pPrivate->BypassMixer.Target1 = 0x00000000; if ((pPrivate->NewParams.Level == 0) && (pPrivate->bFirstControl == LVM_FALSE)) { pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE; } if (pPrivate->NewParams.Level != 0) { pPrivate->bDisableReverb = LVM_FALSE; } } if(pPrivate->NewParams.OperatingMode != pPrivate->CurrentParams.OperatingMode) { if(pPrivate->NewParams.OperatingMode == LVM_MODE_ON) { pPrivate->BypassMixer.Target2 = (LVM_FLOAT)(pPrivate->NewParams.Level ) / 100.0f; pPrivate->BypassMixer.Target1 = 0x00000000; pPrivate->BypassMixer.CallbackSet2 = LVM_FALSE; OperatingMode = LVM_MODE_ON; if (pPrivate->NewParams.Level == 0) { pPrivate->bDisableReverb = LVM_TRUE; } else { pPrivate->bDisableReverb = LVM_FALSE; } } else if (pPrivate->bFirstControl == LVM_FALSE) { pPrivate->BypassMixer.Target2 = 0x00000000; pPrivate->BypassMixer.Target1 = 0x00000000; pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE; pPrivate->GainMixer.Target = 0.03125f; OperatingMode = LVM_MODE_ON; } else { OperatingMode = LVM_MODE_OFF; } } /* If it is the first call to ApplyNew settings force the current to the target \ to begin immediate playback of the effect */ if(pPrivate->bFirstControl == LVM_TRUE) { pPrivate->BypassMixer.Current1 = pPrivate->BypassMixer.Target1; pPrivate->BypassMixer.Current2 = pPrivate->BypassMixer.Target2; } /* * Copy the new parameters */ pPrivate->CurrentParams = pPrivate->NewParams; pPrivate->CurrentParams.OperatingMode = OperatingMode; /* * Update flag */ if(pPrivate->bFirstControl == LVM_TRUE) { pPrivate->bFirstControl = LVM_FALSE; } return LVREV_SUCCESS; } /****************************************************************************************/ /* */ /* FUNCTION: BypassMixer_Callback */ /* */ /* DESCRIPTION: */ /* Controls the On to Off operating mode transition */ /* */ /* PARAMETERS: */ /* pPrivate Pointer to the instance private parameters */ /* */ /* RETURNS: */ /* LVREV_Success Succeeded */ /* LVREV_NULLADDRESS When pPrivate is NULL */ /* */ /* NOTES: */ /* */ /****************************************************************************************/ LVM_INT32 BypassMixer_Callback (void *pCallbackData, void *pGeneralPurpose, LVM_INT16 GeneralPurpose ) { LVREV_Instance_st *pLVREV_Private = (LVREV_Instance_st *)pCallbackData; /* * Avoid build warnings */ (void)pGeneralPurpose; (void)GeneralPurpose; /* * Turn off */ pLVREV_Private->CurrentParams.OperatingMode = LVM_MODE_OFF; pLVREV_Private->bDisableReverb = LVM_TRUE; LVREV_ClearAudioBuffers((LVREV_Handle_t)pCallbackData); return 0; } /* End of file */