/*----------------------------------------------------------------------------
*
* 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 || value<EAS_REVERB_WET_MIN)
return EAS_ERROR_INVALID_PARAMETER;
p->m_nWet = (EAS_I16)value;
break;
case EAS_PARAM_REVERB_DRY:
if(value>EAS_REVERB_DRY_MAX || value<EAS_REVERB_DRY_MIN)
return EAS_ERROR_INVALID_PARAMETER;
p->m_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;
}