/*---------------------------------------------------------------------------- * * File: * eas_reverb.c * * Contents and purpose: * Contains the implementation of the Reverb effect. * * * Copyright Sonic Network Inc. 2006 * 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. * *---------------------------------------------------------------------------- * Revision Control: * $Revision: 510 $ * $Date: 2006-12-19 01:47:33 -0800 (Tue, 19 Dec 2006) $ *---------------------------------------------------------------------------- */ /*------------------------------------ * includes *------------------------------------ */ #include "eas_data.h" #include "eas_effects.h" #include "eas_math.h" #include "eas_reverbdata.h" #include "eas_reverb.h" #include "eas_config.h" #include "eas_host.h" #include "eas_report.h" /* prototypes for effects interface */ static EAS_RESULT ReverbInit (EAS_DATA_HANDLE pEASData, EAS_VOID_PTR *pInstData); static void ReverbProcess (EAS_VOID_PTR pInstData, EAS_PCM *pSrc, EAS_PCM *pDst, EAS_I32 numSamples); static EAS_RESULT ReverbShutdown (EAS_DATA_HANDLE pEASData, EAS_VOID_PTR pInstData); static EAS_RESULT ReverbGetParam (EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue); static EAS_RESULT ReverbSetParam (EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value); /* common effects interface for configuration module */ const S_EFFECTS_INTERFACE EAS_Reverb = { ReverbInit, ReverbProcess, ReverbShutdown, ReverbGetParam, ReverbSetParam }; /*---------------------------------------------------------------------------- * InitializeReverb() *---------------------------------------------------------------------------- * Purpose: * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbInit(EAS_DATA_HANDLE pEASData, EAS_VOID_PTR *pInstData) { EAS_I32 i; EAS_U16 nOffset; EAS_INT temp; S_REVERB_OBJECT *pReverbData; S_REVERB_PRESET *pPreset; /* check Configuration Module for data allocation */ if (pEASData->staticMemoryModel) pReverbData = EAS_CMEnumFXData(EAS_MODULE_REVERB); /* allocate dynamic memory */ else pReverbData = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_REVERB_OBJECT)); if (pReverbData == NULL) { { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate Reverb memory\n"); */ } return EAS_ERROR_MALLOC_FAILED; } /* clear the structure */ EAS_HWMemSet(pReverbData, 0, sizeof(S_REVERB_OBJECT)); ReverbReadInPresets(pReverbData); pReverbData->m_nMinSamplesToAdd = REVERB_UPDATE_PERIOD_IN_SAMPLES; pReverbData->m_nRevOutFbkR = 0; pReverbData->m_nRevOutFbkL = 0; pReverbData->m_sAp0.m_zApIn = AP0_IN; pReverbData->m_sAp0.m_zApOut = AP0_IN + DEFAULT_AP0_LENGTH; pReverbData->m_sAp0.m_nApGain = DEFAULT_AP0_GAIN; pReverbData->m_zD0In = DELAY0_IN; pReverbData->m_sAp1.m_zApIn = AP1_IN; pReverbData->m_sAp1.m_zApOut = AP1_IN + DEFAULT_AP1_LENGTH; pReverbData->m_sAp1.m_nApGain = DEFAULT_AP1_GAIN; pReverbData->m_zD1In = DELAY1_IN; pReverbData->m_zLpf0 = 0; pReverbData->m_zLpf1 = 0; pReverbData->m_nLpfFwd = 8837; pReverbData->m_nLpfFbk = 6494; pReverbData->m_nSin = 0; pReverbData->m_nCos = 0; pReverbData->m_nSinIncrement = 0; pReverbData->m_nCosIncrement = 0; // set xfade parameters pReverbData->m_nXfadeInterval = (EAS_U16)REVERB_XFADE_PERIOD_IN_SAMPLES; pReverbData->m_nXfadeCounter = pReverbData->m_nXfadeInterval + 1; // force update on first iteration pReverbData->m_nPhase = -32768; pReverbData->m_nPhaseIncrement = REVERB_XFADE_PHASE_INCREMENT; pReverbData->m_nNoise = (EAS_I16)0xABCD; pReverbData->m_nMaxExcursion = 0x007F; // set delay tap lengths nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD1Cross = DELAY1_OUT - pReverbData->m_nMaxExcursion + nOffset; nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD0Cross = DELAY1_OUT - pReverbData->m_nMaxExcursion - nOffset; nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD0Self = DELAY0_OUT - pReverbData->m_nMaxExcursion - nOffset; nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD1Self = DELAY1_OUT - pReverbData->m_nMaxExcursion + nOffset; // for debugging purposes, allow noise generator pReverbData->m_bUseNoise = EAS_FALSE; // for debugging purposes, allow bypass pReverbData->m_bBypass = EAS_TRUE; //EAS_FALSE; pReverbData->m_nNextRoom = 1; pReverbData->m_nCurrentRoom = pReverbData->m_nNextRoom + 1; // force update on first iteration pReverbData->m_nWet = REVERB_DEFAULT_WET; pReverbData->m_nDry = REVERB_DEFAULT_DRY; // set base index into circular buffer pReverbData->m_nBaseIndex = 0; // set the early reflections, L pReverbData->m_sEarlyL.m_nLpfFbk = 4915; pReverbData->m_sEarlyL.m_nLpfFwd = 27852; pReverbData->m_sEarlyL.m_zLpf = 0; for (i=0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { pReverbData->m_sEarlyL.m_nGain[i] = 0; pReverbData->m_sEarlyL.m_zDelay[i] = 0; } // set the early reflections, R pReverbData->m_sEarlyR.m_nLpfFbk = 4915; pReverbData->m_sEarlyR.m_nLpfFwd = 27852; pReverbData->m_sEarlyR.m_zLpf = 0; for (i=0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { pReverbData->m_sEarlyR.m_nGain[i] = 0; pReverbData->m_sEarlyR.m_zDelay[i] = 0; } // clear the reverb delay line for (i=0; i < REVERB_BUFFER_SIZE_IN_SAMPLES; i++) { pReverbData->m_nDelayLine[i] = 0; } //////////////////////////////// ///code from the EAS DEMO Reverb //now copy from the new preset into the reverb pPreset = &pReverbData->m_sPreset.m_sPreset[pReverbData->m_nNextRoom]; pReverbData->m_nLpfFbk = pPreset->m_nLpfFbk; pReverbData->m_nLpfFwd = pPreset->m_nLpfFwd; pReverbData->m_nEarly = pPreset->m_nEarly; pReverbData->m_nWet = pPreset->m_nWet; pReverbData->m_nDry = pPreset->m_nDry; pReverbData->m_nMaxExcursion = pPreset->m_nMaxExcursion; //stored as time based, convert to sample based temp = pPreset->m_nXfadeInterval; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_nXfadeInterval = (EAS_U16) temp; //gsReverbObject.m_nXfadeInterval = pPreset->m_nXfadeInterval; pReverbData->m_sAp0.m_nApGain = pPreset->m_nAp0_ApGain; //stored as time based, convert to absolute sample value temp = pPreset->m_nAp0_ApOut; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_sAp0.m_zApOut = (EAS_U16) (pReverbData->m_sAp0.m_zApIn + temp); //gsReverbObject.m_sAp0.m_zApOut = pPreset->m_nAp0_ApOut; pReverbData->m_sAp1.m_nApGain = pPreset->m_nAp1_ApGain; //stored as time based, convert to absolute sample value temp = pPreset->m_nAp1_ApOut; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_sAp1.m_zApOut = (EAS_U16) (pReverbData->m_sAp1.m_zApIn + temp); //gsReverbObject.m_sAp1.m_zApOut = pPreset->m_nAp1_ApOut; ///code from the EAS DEMO Reverb //////////////////////////////// *pInstData = pReverbData; return EAS_SUCCESS; } /* end InitializeReverb */ /*---------------------------------------------------------------------------- * ReverbProcess() *---------------------------------------------------------------------------- * Purpose: * Reverberate the requested number of samples (block based processing) * * Inputs: * pInputBuffer - src buffer * pOutputBuffer - dst buffer * nNumSamplesToAdd - number of samples to write to buffer * * Outputs: * number of samples actually written to buffer * * Side Effects: * - samples are added to the presently free buffer * *---------------------------------------------------------------------------- */ static void ReverbProcess(EAS_VOID_PTR pInstData, EAS_PCM *pSrc, EAS_PCM *pDst, EAS_I32 numSamples) { S_REVERB_OBJECT *pReverbData; pReverbData = (S_REVERB_OBJECT*) pInstData; //if bypassed or the preset forces the signal to be completely dry if (pReverbData->m_bBypass || (pReverbData->m_nWet == 0 && pReverbData->m_nDry == 32767)) { if (pSrc != pDst) EAS_HWMemCpy(pSrc, pDst, numSamples * NUM_OUTPUT_CHANNELS * (EAS_I32) sizeof(EAS_PCM)); return; } if (pReverbData->m_nNextRoom != pReverbData->m_nCurrentRoom) { ReverbUpdateRoom(pReverbData); } ReverbUpdateXfade(pReverbData, numSamples); Reverb(pReverbData, numSamples, pDst, pSrc); /* check if update counter needs to be reset */ if (pReverbData->m_nUpdateCounter >= REVERB_MODULO_UPDATE_PERIOD_IN_SAMPLES) { /* update interval has elapsed, so reset counter */ pReverbData->m_nUpdateCounter = 0; } /* end if m_nUpdateCounter >= update interval */ /* increment update counter */ pReverbData->m_nUpdateCounter += (EAS_I16)numSamples; } /* end ComputeReverb */ /*---------------------------------------------------------------------------- * ReverbUpdateXfade *---------------------------------------------------------------------------- * Purpose: * Update the xfade parameters as required * * Inputs: * nNumSamplesToAdd - number of samples to write to buffer * * Outputs: * * * Side Effects: * - xfade parameters will be changed * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbUpdateXfade(S_REVERB_OBJECT *pReverbData, EAS_INT nNumSamplesToAdd) { EAS_U16 nOffset; EAS_I16 tempCos; EAS_I16 tempSin; if (pReverbData->m_nXfadeCounter >= pReverbData->m_nXfadeInterval) { /* update interval has elapsed, so reset counter */ pReverbData->m_nXfadeCounter = 0; // Pin the sin,cos values to min / max values to ensure that the // modulated taps' coefs are zero (thus no clicks) if (pReverbData->m_nPhaseIncrement > 0) { // if phase increment > 0, then sin -> 1, cos -> 0 pReverbData->m_nSin = 32767; pReverbData->m_nCos = 0; // reset the phase to match the sin, cos values pReverbData->m_nPhase = 32767; // modulate the cross taps because their tap coefs are zero nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD1Cross = DELAY1_OUT - pReverbData->m_nMaxExcursion + nOffset; nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD0Cross = DELAY0_OUT - pReverbData->m_nMaxExcursion - nOffset; } else { // if phase increment < 0, then sin -> 0, cos -> 1 pReverbData->m_nSin = 0; pReverbData->m_nCos = 32767; // reset the phase to match the sin, cos values pReverbData->m_nPhase = -32768; // modulate the self taps because their tap coefs are zero nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD0Self = DELAY0_OUT - pReverbData->m_nMaxExcursion - nOffset; nOffset = ReverbCalculateNoise( pReverbData->m_nMaxExcursion, &pReverbData->m_nNoise ); pReverbData->m_zD1Self = DELAY1_OUT - pReverbData->m_nMaxExcursion + nOffset; } // end if-else (pReverbData->m_nPhaseIncrement > 0) // Reverse the direction of the sin,cos so that the // tap whose coef was previously increasing now decreases // and vice versa pReverbData->m_nPhaseIncrement = -pReverbData->m_nPhaseIncrement; } // end if counter >= update interval //compute what phase will be next time pReverbData->m_nPhase += pReverbData->m_nPhaseIncrement; //calculate what the new sin and cos need to reach by the next update ReverbCalculateSinCos(pReverbData->m_nPhase, &tempSin, &tempCos); //calculate the per-sample increment required to get there by the next update /*lint -e{702} shift for performance */ pReverbData->m_nSinIncrement = (tempSin - pReverbData->m_nSin) >> REVERB_UPDATE_PERIOD_IN_BITS; /*lint -e{702} shift for performance */ pReverbData->m_nCosIncrement = (tempCos - pReverbData->m_nCos) >> REVERB_UPDATE_PERIOD_IN_BITS; /* increment update counter */ pReverbData->m_nXfadeCounter += (EAS_U16) nNumSamplesToAdd; return EAS_SUCCESS; } /* end ReverbUpdateXfade */ /*---------------------------------------------------------------------------- * ReverbCalculateNoise *---------------------------------------------------------------------------- * Purpose: * Calculate a noise sample and limit its value * * Inputs: * nMaxExcursion - noise value is limited to this value * pnNoise - return new noise sample in this (not limited) * * Outputs: * new limited noise value * * Side Effects: * - *pnNoise noise value is updated * *---------------------------------------------------------------------------- */ static EAS_U16 ReverbCalculateNoise(EAS_U16 nMaxExcursion, EAS_I16 *pnNoise) { // calculate new noise value *pnNoise = (EAS_I16) (*pnNoise * 5 + 1); #if 0 // 1xxx, test *pnNoise = 0; #endif // 1xxx, test // return the limited noise value return (nMaxExcursion & (*pnNoise)); } /* end ReverbCalculateNoise */ /*---------------------------------------------------------------------------- * ReverbCalculateSinCos *---------------------------------------------------------------------------- * Purpose: * Calculate a new sin and cosine value based on the given phase * * Inputs: * nPhase - phase angle * pnSin - input old value, output new value * pnCos - input old value, output new value * * Outputs: * * Side Effects: * - *pnSin, *pnCos are updated * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbCalculateSinCos(EAS_I16 nPhase, EAS_I16 *pnSin, EAS_I16 *pnCos) { EAS_I32 nTemp; EAS_I32 nNetAngle; // -1 <= nPhase < 1 // However, for the calculation, we need a value // that ranges from -1/2 to +1/2, so divide the phase by 2 /*lint -e{702} shift for performance */ nNetAngle = nPhase >> 1; /* Implement the following sin(x) = (2-4*c)*x^2 + c + x cos(x) = (2-4*c)*x^2 + c - x where c = 1/sqrt(2) using the a0 + x*(a1 + x*a2) approach */ /* limit the input "angle" to be between -0.5 and +0.5 */ if (nNetAngle > EG1_HALF) { nNetAngle = EG1_HALF; } else if (nNetAngle < EG1_MINUS_HALF) { nNetAngle = EG1_MINUS_HALF; } /* calculate sin */ nTemp = EG1_ONE + MULT_EG1_EG1(REVERB_PAN_G2, nNetAngle); nTemp = REVERB_PAN_G0 + MULT_EG1_EG1(nTemp, nNetAngle); *pnSin = (EAS_I16) SATURATE_EG1(nTemp); /* calculate cos */ nTemp = -EG1_ONE + MULT_EG1_EG1(REVERB_PAN_G2, nNetAngle); nTemp = REVERB_PAN_G0 + MULT_EG1_EG1(nTemp, nNetAngle); *pnCos = (EAS_I16) SATURATE_EG1(nTemp); return EAS_SUCCESS; } /* end ReverbCalculateSinCos */ /*---------------------------------------------------------------------------- * Reverb *---------------------------------------------------------------------------- * Purpose: * apply reverb to the given signal * * Inputs: * nNu * pnSin - input old value, output new value * pnCos - input old value, output new value * * Outputs: * number of samples actually reverberated * * Side Effects: * *---------------------------------------------------------------------------- */ static EAS_RESULT Reverb(S_REVERB_OBJECT *pReverbData, EAS_INT nNumSamplesToAdd, EAS_PCM *pOutputBuffer, EAS_PCM *pInputBuffer) { EAS_I32 i; EAS_I32 nDelayOut; EAS_U16 nBase; EAS_U32 nAddr; EAS_I32 nTemp1; EAS_I32 nTemp2; EAS_I32 nApIn; EAS_I32 nApOut; EAS_I32 j; EAS_I32 nEarlyOut; EAS_I32 tempValue; // get the base address nBase = pReverbData->m_nBaseIndex; for (i=0; i < nNumSamplesToAdd; i++) { // ********** Left Allpass - start // left input = (left dry/4) + right feedback from previous period /*lint -e{702} use shift for performance */ nApIn = ((*pInputBuffer++)>>2) + pReverbData->m_nRevOutFbkR; // nApIn = *pInputBuffer++; // 1xxx test and debug ap // fetch allpass delay line out //nAddr = CIRCULAR(nBase, psAp0->m_zApOut, REVERB_BUFFER_MASK); nAddr = CIRCULAR(nBase, pReverbData->m_sAp0.m_zApOut, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate allpass feedforward; subtract the feedforward result nTemp1 = MULT_EG1_EG1(nApIn, pReverbData->m_sAp0.m_nApGain); nApOut = SATURATE(nDelayOut - nTemp1); // allpass output // calculate allpass feedback; add the feedback result nTemp1 = MULT_EG1_EG1(nApOut, pReverbData->m_sAp0.m_nApGain); nTemp1 = SATURATE(nApIn + nTemp1); // inject into allpass delay nAddr = CIRCULAR(nBase, pReverbData->m_sAp0.m_zApIn, REVERB_BUFFER_MASK); pReverbData->m_nDelayLine[nAddr] = (EAS_PCM) nTemp1; // inject allpass output into delay line nAddr = CIRCULAR(nBase, pReverbData->m_zD0In, REVERB_BUFFER_MASK); pReverbData->m_nDelayLine[nAddr] = (EAS_PCM) nApOut; // ********** Left Allpass - end // ********** Right Allpass - start // right input = (right dry/4) + left feedback from previous period /*lint -e{702} use shift for performance */ nApIn = ((*pInputBuffer++)>>2) + pReverbData->m_nRevOutFbkL; // nApIn = *pInputBuffer++; // 1xxx test and debug ap // fetch allpass delay line out nAddr = CIRCULAR(nBase, pReverbData->m_sAp1.m_zApOut, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate allpass feedforward; subtract the feedforward result nTemp1 = MULT_EG1_EG1(nApIn, pReverbData->m_sAp1.m_nApGain); nApOut = SATURATE(nDelayOut - nTemp1); // allpass output // calculate allpass feedback; add the feedback result nTemp1 = MULT_EG1_EG1(nApOut, pReverbData->m_sAp1.m_nApGain); nTemp1 = SATURATE(nApIn + nTemp1); // inject into allpass delay nAddr = CIRCULAR(nBase, pReverbData->m_sAp1.m_zApIn, REVERB_BUFFER_MASK); pReverbData->m_nDelayLine[nAddr] = (EAS_PCM) nTemp1; // inject allpass output into delay line nAddr = CIRCULAR(nBase, pReverbData->m_zD1In, REVERB_BUFFER_MASK); pReverbData->m_nDelayLine[nAddr] = (EAS_PCM) nApOut; // ********** Right Allpass - end // ********** D0 output - start // fetch delay line self out nAddr = CIRCULAR(nBase, pReverbData->m_zD0Self, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate delay line self out nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nSin); // fetch delay line cross out nAddr = CIRCULAR(nBase, pReverbData->m_zD1Cross, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate delay line self out nTemp2 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nCos); // calculate unfiltered delay out nDelayOut = SATURATE(nTemp1 + nTemp2); // calculate lowpass filter (mixer scale factor included in LPF feedforward) nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nLpfFwd); nTemp2 = MULT_EG1_EG1(pReverbData->m_zLpf0, pReverbData->m_nLpfFbk); // calculate filtered delay out and simultaneously update LPF state variable // filtered delay output is stored in m_zLpf0 pReverbData->m_zLpf0 = (EAS_PCM) SATURATE(nTemp1 + nTemp2); // ********** D0 output - end // ********** D1 output - start // fetch delay line self out nAddr = CIRCULAR(nBase, pReverbData->m_zD1Self, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate delay line self out nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nSin); // fetch delay line cross out nAddr = CIRCULAR(nBase, pReverbData->m_zD0Cross, REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate delay line self out nTemp2 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nCos); // calculate unfiltered delay out nDelayOut = SATURATE(nTemp1 + nTemp2); // calculate lowpass filter (mixer scale factor included in LPF feedforward) nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_nLpfFwd); nTemp2 = MULT_EG1_EG1(pReverbData->m_zLpf1, pReverbData->m_nLpfFbk); // calculate filtered delay out and simultaneously update LPF state variable // filtered delay output is stored in m_zLpf1 pReverbData->m_zLpf1 = (EAS_PCM)SATURATE(nTemp1 + nTemp2); // ********** D1 output - end // ********** mixer and feedback - start // sum is fedback to right input (R + L) pReverbData->m_nRevOutFbkL = (EAS_PCM)SATURATE((EAS_I32)pReverbData->m_zLpf1 + (EAS_I32)pReverbData->m_zLpf0); // difference is feedback to left input (R - L) /*lint -e{685} lint complains that it can't saturate negative */ pReverbData->m_nRevOutFbkR = (EAS_PCM)SATURATE((EAS_I32)pReverbData->m_zLpf1 - (EAS_I32)pReverbData->m_zLpf0); // ********** mixer and feedback - end // ********** start early reflection generator, left //psEarly = &(pReverbData->m_sEarlyL); nEarlyOut = 0; for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) { // fetch delay line out //nAddr = CIRCULAR(nBase, psEarly->m_zDelay[j], REVERB_BUFFER_MASK); nAddr = CIRCULAR(nBase, pReverbData->m_sEarlyL.m_zDelay[j], REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate reflection //nTemp1 = MULT_EG1_EG1(nDelayOut, psEarly->m_nGain[j]); nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_sEarlyL.m_nGain[j]); nEarlyOut = SATURATE(nEarlyOut + nTemp1); } // end for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) // apply lowpass to early reflections //nTemp1 = MULT_EG1_EG1(nEarlyOut, psEarly->m_nLpfFwd); nTemp1 = MULT_EG1_EG1(nEarlyOut, pReverbData->m_sEarlyL.m_nLpfFwd); //nTemp2 = MULT_EG1_EG1(psEarly->m_zLpf, psEarly->m_nLpfFbk); nTemp2 = MULT_EG1_EG1(pReverbData->m_sEarlyL.m_zLpf, pReverbData->m_sEarlyL.m_nLpfFbk); // calculate filtered out and simultaneously update LPF state variable // filtered output is stored in m_zLpf1 //psEarly->m_zLpf = SATURATE(nTemp1 + nTemp2); pReverbData->m_sEarlyL.m_zLpf = (EAS_PCM) SATURATE(nTemp1 + nTemp2); // combine filtered early and late reflections for output //*pOutputBuffer++ = inL; //tempValue = SATURATE(psEarly->m_zLpf + pReverbData->m_nRevOutFbkL); tempValue = SATURATE((EAS_I32)pReverbData->m_sEarlyL.m_zLpf + (EAS_I32)pReverbData->m_nRevOutFbkL); //scale reverb output by wet level /*lint -e{701} use shift for performance */ tempValue = MULT_EG1_EG1(tempValue, (pReverbData->m_nWet<<1)); //sum with output buffer tempValue += *pOutputBuffer; *pOutputBuffer++ = (EAS_PCM)SATURATE(tempValue); // ********** end early reflection generator, left // ********** start early reflection generator, right //psEarly = &(pReverbData->m_sEarlyR); nEarlyOut = 0; for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) { // fetch delay line out nAddr = CIRCULAR(nBase, pReverbData->m_sEarlyR.m_zDelay[j], REVERB_BUFFER_MASK); nDelayOut = pReverbData->m_nDelayLine[nAddr]; // calculate reflection nTemp1 = MULT_EG1_EG1(nDelayOut, pReverbData->m_sEarlyR.m_nGain[j]); nEarlyOut = SATURATE(nEarlyOut + nTemp1); } // end for (j=0; j < REVERB_MAX_NUM_REFLECTIONS; j++) // apply lowpass to early reflections nTemp1 = MULT_EG1_EG1(nEarlyOut, pReverbData->m_sEarlyR.m_nLpfFwd); nTemp2 = MULT_EG1_EG1(pReverbData->m_sEarlyR.m_zLpf, pReverbData->m_sEarlyR.m_nLpfFbk); // calculate filtered out and simultaneously update LPF state variable // filtered output is stored in m_zLpf1 pReverbData->m_sEarlyR.m_zLpf = (EAS_PCM)SATURATE(nTemp1 + nTemp2); // combine filtered early and late reflections for output //*pOutputBuffer++ = inR; tempValue = SATURATE((EAS_I32)pReverbData->m_sEarlyR.m_zLpf + (EAS_I32)pReverbData->m_nRevOutFbkR); //scale reverb output by wet level /*lint -e{701} use shift for performance */ tempValue = MULT_EG1_EG1(tempValue, (pReverbData->m_nWet << 1)); //sum with output buffer tempValue = tempValue + *pOutputBuffer; *pOutputBuffer++ = (EAS_PCM)SATURATE(tempValue); // ********** end early reflection generator, right // decrement base addr for next sample period nBase--; pReverbData->m_nSin += pReverbData->m_nSinIncrement; pReverbData->m_nCos += pReverbData->m_nCosIncrement; } // end for (i=0; i < nNumSamplesToAdd; i++) // store the most up to date version pReverbData->m_nBaseIndex = nBase; return EAS_SUCCESS; } /* end Reverb */ /*---------------------------------------------------------------------------- * ReverbShutdown() *---------------------------------------------------------------------------- * Purpose: * Initializes the Reverb effect. * * Inputs: * pInstData - handle to instance data * * Outputs: * * * Side Effects: * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbShutdown (EAS_DATA_HANDLE pEASData, EAS_VOID_PTR pInstData) { /* check Configuration Module for static memory allocation */ if (!pEASData->staticMemoryModel) EAS_HWFree(pEASData->hwInstData, pInstData); return EAS_SUCCESS; } /* end ReverbShutdown */ /*---------------------------------------------------------------------------- * ReverbGetParam() *---------------------------------------------------------------------------- * Purpose: * Get a Reverb parameter * * Inputs: * pInstData - handle to instance data * param - parameter index * *pValue - pointer to variable to hold retrieved value * * Outputs: * * * Side Effects: * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbGetParam (EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 *pValue) { S_REVERB_OBJECT *p; p = (S_REVERB_OBJECT*) pInstData; switch (param) { case EAS_PARAM_REVERB_BYPASS: *pValue = (EAS_I32) p->m_bBypass; break; case EAS_PARAM_REVERB_PRESET: *pValue = (EAS_I8) p->m_nCurrentRoom; break; case EAS_PARAM_REVERB_WET: *pValue = p->m_nWet; break; case EAS_PARAM_REVERB_DRY: *pValue = p->m_nDry; break; default: return EAS_ERROR_INVALID_PARAMETER; } return EAS_SUCCESS; } /* end ReverbGetParam */ /*---------------------------------------------------------------------------- * ReverbSetParam() *---------------------------------------------------------------------------- * Purpose: * Set a Reverb parameter * * Inputs: * pInstData - handle to instance data * param - parameter index * *pValue - new paramter value * * Outputs: * * * Side Effects: * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbSetParam (EAS_VOID_PTR pInstData, EAS_I32 param, EAS_I32 value) { S_REVERB_OBJECT *p; p = (S_REVERB_OBJECT*) pInstData; switch (param) { case EAS_PARAM_REVERB_BYPASS: p->m_bBypass = (EAS_BOOL) value; break; case EAS_PARAM_REVERB_PRESET: if(value!=EAS_PARAM_REVERB_LARGE_HALL && value!=EAS_PARAM_REVERB_HALL && value!=EAS_PARAM_REVERB_CHAMBER && value!=EAS_PARAM_REVERB_ROOM) return EAS_ERROR_INVALID_PARAMETER; p->m_nNextRoom = (EAS_I16)value; break; case EAS_PARAM_REVERB_WET: if(value>EAS_REVERB_WET_MAX || valuem_nWet = (EAS_I16)value; break; case EAS_PARAM_REVERB_DRY: if(value>EAS_REVERB_DRY_MAX || valuem_nDry = (EAS_I16)value; break; default: return EAS_ERROR_INVALID_PARAMETER; } return EAS_SUCCESS; } /* end ReverbSetParam */ /*---------------------------------------------------------------------------- * ReverbUpdateRoom *---------------------------------------------------------------------------- * Purpose: * Update the room's preset parameters as required * * Inputs: * * Outputs: * * * Side Effects: * - reverb paramters (fbk, fwd, etc) will be changed * - m_nCurrentRoom := m_nNextRoom *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbUpdateRoom(S_REVERB_OBJECT *pReverbData) { EAS_INT temp; S_REVERB_PRESET *pPreset = &pReverbData->m_sPreset.m_sPreset[pReverbData->m_nNextRoom]; pReverbData->m_nLpfFwd = pPreset->m_nLpfFwd; pReverbData->m_nLpfFbk = pPreset->m_nLpfFbk; pReverbData->m_nEarly = pPreset->m_nEarly; pReverbData->m_nWet = pPreset->m_nWet; pReverbData->m_nDry = pPreset->m_nDry; pReverbData->m_nMaxExcursion = pPreset->m_nMaxExcursion; //stored as time based, convert to sample based temp = pPreset->m_nXfadeInterval; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_nXfadeInterval = (EAS_U16) temp; //gpsReverbObject->m_nXfadeInterval = pPreset->m_nXfadeInterval; pReverbData->m_sAp0.m_nApGain = pPreset->m_nAp0_ApGain; //stored as time based, convert to absolute sample value temp = pPreset->m_nAp0_ApOut; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_sAp0.m_zApOut = (EAS_U16) (pReverbData->m_sAp0.m_zApIn + temp); //gpsReverbObject->m_sAp0.m_zApOut = pPreset->m_nAp0_ApOut; pReverbData->m_sAp1.m_nApGain = pPreset->m_nAp1_ApGain; //stored as time based, convert to absolute sample value temp = pPreset->m_nAp1_ApOut; /*lint -e{702} shift for performance */ temp = (temp * _OUTPUT_SAMPLE_RATE) >> 16; pReverbData->m_sAp1.m_zApOut = (EAS_U16) (pReverbData->m_sAp1.m_zApIn + temp); //gpsReverbObject->m_sAp1.m_zApOut = pPreset->m_nAp1_ApOut; pReverbData->m_nCurrentRoom = pReverbData->m_nNextRoom; return EAS_SUCCESS; } /* end ReverbUpdateRoom */ /*---------------------------------------------------------------------------- * ReverbReadInPresets() *---------------------------------------------------------------------------- * Purpose: sets global reverb preset bank to defaults * * Inputs: * * Outputs: * *---------------------------------------------------------------------------- */ static EAS_RESULT ReverbReadInPresets(S_REVERB_OBJECT *pReverbData) { int preset = 0; int defaultPreset = 0; //now init any remaining presets to defaults for (defaultPreset = preset; defaultPreset < REVERB_MAX_ROOM_TYPE; defaultPreset++) { S_REVERB_PRESET *pPreset = &pReverbData->m_sPreset.m_sPreset[defaultPreset]; if (defaultPreset == 0 || defaultPreset > REVERB_MAX_ROOM_TYPE-1) { pPreset->m_nLpfFbk = 8307; pPreset->m_nLpfFwd = 14768; pPreset->m_nEarly = 0; pPreset->m_nWet = 27690; pPreset->m_nDry = 32767; pPreset->m_nEarlyL_LpfFbk = 3692; pPreset->m_nEarlyL_LpfFwd = 29075; pPreset->m_nEarlyL_Delay0 = 922; pPreset->m_nEarlyL_Gain0 = 22152; pPreset->m_nEarlyL_Delay1 = 1462; pPreset->m_nEarlyL_Gain1 = 17537; pPreset->m_nEarlyL_Delay2 = 0; pPreset->m_nEarlyL_Gain2 = 14768; pPreset->m_nEarlyL_Delay3 = 1221; pPreset->m_nEarlyL_Gain3 = 14307; pPreset->m_nEarlyL_Delay4 = 0; pPreset->m_nEarlyL_Gain4 = 13384; pPreset->m_nEarlyR_Delay0 = 502; pPreset->m_nEarlyR_Gain0 = 20306; pPreset->m_nEarlyR_Delay1 = 1762; pPreset->m_nEarlyR_Gain1 = 17537; pPreset->m_nEarlyR_Delay2 = 0; pPreset->m_nEarlyR_Gain2 = 14768; pPreset->m_nEarlyR_Delay3 = 0; pPreset->m_nEarlyR_Gain3 = 16153; pPreset->m_nEarlyR_Delay4 = 0; pPreset->m_nEarlyR_Gain4 = 13384; pPreset->m_nMaxExcursion = 127; pPreset->m_nXfadeInterval = 6388; pPreset->m_nAp0_ApGain = 15691; pPreset->m_nAp0_ApOut = 711; pPreset->m_nAp1_ApGain = 17999; pPreset->m_nAp1_ApOut = 1113; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; pPreset->m_rfu7 = 0; pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; } else if (defaultPreset == 1) { pPreset->m_nLpfFbk = 6461; pPreset->m_nLpfFwd = 14307; pPreset->m_nEarly = 0; pPreset->m_nWet = 27690; pPreset->m_nDry = 32767; pPreset->m_nEarlyL_LpfFbk = 3692; pPreset->m_nEarlyL_LpfFwd = 29075; pPreset->m_nEarlyL_Delay0 = 922; pPreset->m_nEarlyL_Gain0 = 22152; pPreset->m_nEarlyL_Delay1 = 1462; pPreset->m_nEarlyL_Gain1 = 17537; pPreset->m_nEarlyL_Delay2 = 0; pPreset->m_nEarlyL_Gain2 = 14768; pPreset->m_nEarlyL_Delay3 = 1221; pPreset->m_nEarlyL_Gain3 = 14307; pPreset->m_nEarlyL_Delay4 = 0; pPreset->m_nEarlyL_Gain4 = 13384; pPreset->m_nEarlyR_Delay0 = 502; pPreset->m_nEarlyR_Gain0 = 20306; pPreset->m_nEarlyR_Delay1 = 1762; pPreset->m_nEarlyR_Gain1 = 17537; pPreset->m_nEarlyR_Delay2 = 0; pPreset->m_nEarlyR_Gain2 = 14768; pPreset->m_nEarlyR_Delay3 = 0; pPreset->m_nEarlyR_Gain3 = 16153; pPreset->m_nEarlyR_Delay4 = 0; pPreset->m_nEarlyR_Gain4 = 13384; pPreset->m_nMaxExcursion = 127; pPreset->m_nXfadeInterval = 6391; pPreset->m_nAp0_ApGain = 15230; pPreset->m_nAp0_ApOut = 708; pPreset->m_nAp1_ApGain = 9692; pPreset->m_nAp1_ApOut = 1113; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; pPreset->m_rfu7 = 0; pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; } else if (defaultPreset == 2) { pPreset->m_nLpfFbk = 5077; pPreset->m_nLpfFwd = 12922; pPreset->m_nEarly = 0; pPreset->m_nWet = 24460; pPreset->m_nDry = 32767; pPreset->m_nEarlyL_LpfFbk = 3692; pPreset->m_nEarlyL_LpfFwd = 29075; pPreset->m_nEarlyL_Delay0 = 922; pPreset->m_nEarlyL_Gain0 = 22152; pPreset->m_nEarlyL_Delay1 = 1462; pPreset->m_nEarlyL_Gain1 = 17537; pPreset->m_nEarlyL_Delay2 = 0; pPreset->m_nEarlyL_Gain2 = 14768; pPreset->m_nEarlyL_Delay3 = 1221; pPreset->m_nEarlyL_Gain3 = 14307; pPreset->m_nEarlyL_Delay4 = 0; pPreset->m_nEarlyL_Gain4 = 13384; pPreset->m_nEarlyR_Delay0 = 502; pPreset->m_nEarlyR_Gain0 = 20306; pPreset->m_nEarlyR_Delay1 = 1762; pPreset->m_nEarlyR_Gain1 = 17537; pPreset->m_nEarlyR_Delay2 = 0; pPreset->m_nEarlyR_Gain2 = 14768; pPreset->m_nEarlyR_Delay3 = 0; pPreset->m_nEarlyR_Gain3 = 16153; pPreset->m_nEarlyR_Delay4 = 0; pPreset->m_nEarlyR_Gain4 = 13384; pPreset->m_nMaxExcursion = 127; pPreset->m_nXfadeInterval = 6449; pPreset->m_nAp0_ApGain = 15691; pPreset->m_nAp0_ApOut = 774; pPreset->m_nAp1_ApGain = 15691; pPreset->m_nAp1_ApOut = 1113; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; pPreset->m_rfu7 = 0; pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; } else if (defaultPreset == 3) { pPreset->m_nLpfFbk = 5077; pPreset->m_nLpfFwd = 11076; pPreset->m_nEarly = 0; pPreset->m_nWet = 23075; pPreset->m_nDry = 32767; pPreset->m_nEarlyL_LpfFbk = 3692; pPreset->m_nEarlyL_LpfFwd = 29075; pPreset->m_nEarlyL_Delay0 = 922; pPreset->m_nEarlyL_Gain0 = 22152; pPreset->m_nEarlyL_Delay1 = 1462; pPreset->m_nEarlyL_Gain1 = 17537; pPreset->m_nEarlyL_Delay2 = 0; pPreset->m_nEarlyL_Gain2 = 14768; pPreset->m_nEarlyL_Delay3 = 1221; pPreset->m_nEarlyL_Gain3 = 14307; pPreset->m_nEarlyL_Delay4 = 0; pPreset->m_nEarlyL_Gain4 = 13384; pPreset->m_nEarlyR_Delay0 = 502; pPreset->m_nEarlyR_Gain0 = 20306; pPreset->m_nEarlyR_Delay1 = 1762; pPreset->m_nEarlyR_Gain1 = 17537; pPreset->m_nEarlyR_Delay2 = 0; pPreset->m_nEarlyR_Gain2 = 14768; pPreset->m_nEarlyR_Delay3 = 0; pPreset->m_nEarlyR_Gain3 = 16153; pPreset->m_nEarlyR_Delay4 = 0; pPreset->m_nEarlyR_Gain4 = 13384; pPreset->m_nMaxExcursion = 127; pPreset->m_nXfadeInterval = 6470; //6483; pPreset->m_nAp0_ApGain = 14768; pPreset->m_nAp0_ApOut = 792; pPreset->m_nAp1_ApGain = 15783; pPreset->m_nAp1_ApOut = 1113; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; pPreset->m_rfu7 = 0; pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; } } return EAS_SUCCESS; }