967 lines
30 KiB
C++
Executable file
967 lines
30 KiB
C++
Executable file
#include <stdio.h>
|
|
|
|
#include "aSfxDsp.hpp"
|
|
#include "3dl2.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h> // for memmove and FMOD_memcpy
|
|
|
|
#include "../../../src/fmod_memory.h"
|
|
#include "../../../src/fmod_types.h"
|
|
|
|
#define ADD_ANTI_DENORMALS_TO_INPUT
|
|
|
|
#define SfxDataMove(source, dest, bytes) memmove(dest, source, (unsigned)bytes)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int ASfxDsp::init(float rate)
|
|
{
|
|
int delayLine;
|
|
int error = 0;
|
|
|
|
mEarlyLateDelay = NULL;
|
|
mEarlyDelay = NULL;
|
|
|
|
ZeroWritePointers();
|
|
|
|
mEarlyLateSec[0] = 0.040f;
|
|
mEarlyLateSamples[0] = (int)(mEarlyLateSec[0] * rate);
|
|
SetLate_EarlyLateDelayTaps(0.060f, kEarlyLateNextLengthSec, kEarlyLateDelayRatio, rate);
|
|
|
|
|
|
for (delayLine=0; delayLine<kNumEarlyLateDelayTaps; delayLine++)
|
|
{
|
|
mOldEarlyLateSamples[delayLine] = mEarlyLateSamples[delayLine];
|
|
}
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
mLateMask[delayLine] = 0;
|
|
}
|
|
|
|
SetEarlyDelay(kEarlyFirstDelaySec, kEarlyDelayRatio, rate);
|
|
SetAllpassDelays(rate);
|
|
SetLateDelays(LOWEST_DELAY_LINE_LENGTH_SEC, DELAY_LINE_RATIO, LOWEST_DELAY_LINE_LENGTH_B_SEC, DELAY_LINE_RATIO_B, rate);
|
|
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
mOldLateDelayLenSamples[delayLine] = mLateDelayLenSamples[delayLine];
|
|
mLateChanged[delayLine] = false;
|
|
mDecayHFChanged[delayLine] = false;
|
|
}
|
|
|
|
if (error = AllocateLateDelays(kNumLateReverbDelays, mLateDelayLenSec, rate)) // assignment, not comparison!
|
|
{
|
|
return error;
|
|
}
|
|
|
|
if (error = AllocateEarlyLateDelay(mEarlyLateSec, rate))
|
|
{
|
|
return error;
|
|
}
|
|
|
|
if (error = AllocateAllpassDelays(kNumAllpassDelays, mAllpassDelayLenSec, rate))
|
|
{
|
|
return error;
|
|
}
|
|
|
|
if (error = AllocateEarlyDelay(mEarlyDelayLenSec[kNumEarlyDelayTaps-1], rate))
|
|
{
|
|
return error;
|
|
}
|
|
|
|
mNumAllocatedInBuffSamples = 0;
|
|
|
|
mInBuff = mInBuffMemory = 0;
|
|
mMatrix = (float *)FMOD_ALIGNPOINTER(mMatrixMem, 16);
|
|
mzDecayHF = (float *)FMOD_ALIGNPOINTER(mzDecayHFMem, 16);
|
|
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
mOldFeedback[delayLine] = mPrevFeedback[delayLine] = mLatchFeedback[delayLine] = mFeedback[delayLine] = 0.32f;
|
|
mLatchDecayHF[delayLine] = mPrevDecayHF[delayLine] = mOldDecayHF[delayLine] = mDecayHF[delayLine] = 0.40f;
|
|
mLatchOneMinusDecayHF[delayLine] = 1.0f - mDecayHF[delayLine];
|
|
}
|
|
|
|
mOldHadamard = mPrevHadamard = mLatchHadamard = mHadamard = 1.0f;
|
|
mOldDiffusionScale = mPrevDiffusionScale = mLatchDiffusionScale = mDiffusionScale = 1.0f;
|
|
mOldRoomHF = mPrevRoomHF = mLatchRoomHF = mRoomHF = 0.98f;
|
|
mRoomHFChanged = false;
|
|
|
|
//FMOD_memset(&mOldRoomLF, 0, sizeof(struct coeff2ndorder));
|
|
//FMOD_memset(&mPrevRoomLF, 0, sizeof(struct coeff2ndorder));
|
|
//FMOD_memset(&mLatchRoomLF, 0, sizeof(struct coeff2ndorder));
|
|
FMOD_memset(&mRoomLFcoeffs, 0, sizeof(struct coeff2ndorder));
|
|
// mOldRoomLF.a0 = mPrevRoomLF.a0 = mLatchRoomLF.a0 =
|
|
mRoomLFcoeffs.a0 = 1.0f;
|
|
mRoomLF = 0.0f;
|
|
//mRoomLFChanged = false;
|
|
|
|
mDiffusionScale = 0.25f; // 0.3535; // cos^4(PI/4)
|
|
mAllpassGain = 0.63f; // ?
|
|
mPrevLRgain = mOldLRgain = mLatchLRgain = mLRgain = 0.0f; // ?
|
|
mLRgainChanged = false;
|
|
mPrevERgain = mOldERgain = mLatchERgain = mERgain = 0.0f; // ?
|
|
mERgainChanged = false;
|
|
ClearReverbInternalBuffers();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ASfxDsp::close()
|
|
{
|
|
if (mInBuffMemory)
|
|
{
|
|
FMOD_Memory_Free(mInBuffMemory);
|
|
mInBuffMemory = mInBuff = 0;
|
|
}
|
|
|
|
DeallocateLateDelays();
|
|
DeallocateEarlyLateDelay();
|
|
DeallocateAllpassDelays();
|
|
DeallocateEarlyDelay();
|
|
}
|
|
|
|
|
|
int ASfxDsp::AllocateLateDelays(int numDelays, float *delaySec, float sampleRate)
|
|
{
|
|
int delaySamples;
|
|
|
|
DeallocateLateDelays(); // *** But if it hasn't changed, we may want to skip all this... ***
|
|
|
|
for (int delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
delaySamples = (int)(delaySec[delayLine] * sampleRate) + 1;
|
|
|
|
delaySamples = NextPowerOf2(delaySamples);
|
|
|
|
// Allocate delay
|
|
|
|
if ((mLateDelays[delayLine] = (float*) FMOD_Memory_Calloc(delaySamples * sizeof(float))) == NULL)
|
|
{
|
|
return REVERB_LATE_ALLOCATION_ERR;
|
|
}
|
|
|
|
mLateDelaySamplesAllocated[delayLine] = delaySamples;
|
|
mLateMask[delayLine] = delaySamples - 1;
|
|
mLateWritePointer[delayLine] = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ASfxDsp::AllocateEarlyLateDelay(float *delaySec, float sampleRate)
|
|
{
|
|
// Allocate it to the max Ref & Rev delays, so we don't have to reallocate it if the delays change.
|
|
float maxDelaySec = I3DL2LISTENER_MAXREFLECTIONSDELAY + I3DL2LISTENER_MAXREVERBDELAY
|
|
+ delaySec[kNumEarlyLateDelayTaps-1] - delaySec[kNumEarly_EarlyLateDelayTaps];
|
|
|
|
int delaySamples = (int)(maxDelaySec * sampleRate) + 1;
|
|
|
|
delaySamples = NextPowerOf2(delaySamples);
|
|
|
|
DeallocateEarlyLateDelay(); // *** But if it hasn't changed, we may want to skip all this... ***
|
|
|
|
// Allocate delay
|
|
|
|
if ((mEarlyLateDelay = (float*) FMOD_Memory_Calloc(delaySamples * sizeof(float))) == NULL)
|
|
{
|
|
return REVERB_EARLYLATE_ALLOCATION_ERR;
|
|
}
|
|
|
|
mEarlyLateSamplesAllocated = delaySamples;
|
|
mEarlyLateMask = delaySamples - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ASfxDsp::AllocateAllpassDelays(int numDelays, float *delaySec, float sampleRate)
|
|
{
|
|
int delaySamples;
|
|
|
|
DeallocateAllpassDelays(); // *** But if it hasn't changed, we may want to skip all this... ***
|
|
|
|
for (int delayLine=0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
delaySamples = (int)(delaySec[delayLine] * sampleRate) + 1;
|
|
delaySamples = NextPowerOf2(delaySamples);
|
|
|
|
// Allocate delay
|
|
|
|
if ((mAllpassDelays[delayLine] = (float*) FMOD_Memory_Calloc(delaySamples * sizeof(float))) == NULL)
|
|
{
|
|
return REVERB_ALLPASS_ALLOCATION_ERR;
|
|
}
|
|
|
|
mAllpassSamplesAllocated[delayLine] = delaySamples;
|
|
mAllpassMask[delayLine] = delaySamples - 1;
|
|
mAllpassWritePointer[delayLine] = 0; // Do this here?
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ASfxDsp::AllocateEarlyDelay(float delaySec, float sampleRate)
|
|
{
|
|
int delaySamples = (int)(delaySec * sampleRate) + 1;
|
|
|
|
delaySamples = NextPowerOf2(delaySamples);
|
|
|
|
DeallocateEarlyDelay(); // *** But if it hasn't changed, we may want to skip all this... ***
|
|
|
|
// Allocate delay
|
|
|
|
if ((mEarlyDelay = (float*) FMOD_Memory_Calloc(delaySamples * sizeof(float))) == NULL)
|
|
{
|
|
return REVERB_EARLY_ALLOCATION_ERR;
|
|
}
|
|
|
|
mEarlyDelaySamplesAllocated = delaySamples;
|
|
mEarlyMask = delaySamples - 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ASfxDsp::NextPowerOf2(int delaySamples)
|
|
{
|
|
return 1 << ((int)(FMOD_LOG((float)delaySamples) / FMOD_LOG(2.0f)) + 1);
|
|
}
|
|
|
|
|
|
void ASfxDsp::DeallocateLateDelays()
|
|
{
|
|
for (int delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
if (mLateDelays[delayLine])
|
|
{
|
|
FMOD_Memory_Free(mLateDelays[delayLine]);
|
|
}
|
|
|
|
mLateDelays[delayLine] = NULL;
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::DeallocateAllpassDelays()
|
|
{
|
|
for (int delayLine=0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
if (mAllpassDelays[delayLine])
|
|
{
|
|
FMOD_Memory_Free(mAllpassDelays[delayLine]);
|
|
}
|
|
|
|
mAllpassDelays[delayLine] = NULL;
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::DeallocateEarlyDelay()
|
|
{
|
|
if (mEarlyDelay)
|
|
{
|
|
FMOD_Memory_Free(mEarlyDelay);
|
|
}
|
|
|
|
mEarlyDelay = NULL;
|
|
}
|
|
|
|
void ASfxDsp::DeallocateEarlyLateDelay()
|
|
{
|
|
if (mEarlyLateDelay)
|
|
{
|
|
FMOD_Memory_Free(mEarlyLateDelay);
|
|
}
|
|
|
|
mEarlyLateDelay = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//#pragma export(UpdateBufferSize)
|
|
int ASfxDsp::UpdateBufferSize(int newMaxBlockSize)
|
|
{
|
|
// nothing to do if the maximum number of samples per processing block hasn't changed
|
|
if (newMaxBlockSize == mNumAllocatedInBuffSamples)
|
|
{
|
|
return 0; // buffer size wasn't changed
|
|
}
|
|
|
|
// update with the new value
|
|
mNumAllocatedInBuffSamples = newMaxBlockSize;
|
|
|
|
// (re)allocate audio buffers in mInBuff
|
|
if (mInBuffMemory != NULL)
|
|
{
|
|
FMOD_Memory_Free(mInBuffMemory);
|
|
}
|
|
|
|
mInBuffMemory = (float*) FMOD_Memory_Alloc((newMaxBlockSize * sizeof(float)) + 16);
|
|
if (!mInBuffMemory)
|
|
{
|
|
return REVERB_INBUFF_ALLOCATION_ERR;
|
|
}
|
|
mInBuff = (float *)FMOD_ALIGNPOINTER(mInBuffMemory, 16);
|
|
|
|
return 0; // buffer size changed
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//#pragma export (ASfxDsp::ClearBuffers)
|
|
void ASfxDsp::ClearBuffers()
|
|
{
|
|
ClearInBuff();
|
|
ClearReverbInternalBuffers();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ASfxDsp::ClearInBuff()
|
|
{
|
|
if (mInBuff != NULL)
|
|
{
|
|
// CHANGE THIS TO A BLOCK MOVE
|
|
for (int i=0; i < mNumAllocatedInBuffSamples; i++)
|
|
{
|
|
mInBuff[i] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void ASfxDsp::ClearReverbInternalBuffers()
|
|
{
|
|
int delayLine;
|
|
|
|
if (mLateDelays)
|
|
{
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
if (mLateDelays[delayLine])
|
|
{
|
|
// *** CHANGE THESE TO BLOCK MOVES FOR BETTER EFFICIENCY ***
|
|
for (int samp=0; samp<mLateDelaySamplesAllocated[delayLine]; samp++)
|
|
{
|
|
mLateDelays[delayLine][samp] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear earlyLate delay line.
|
|
// Replace this with a block move.
|
|
int samp;
|
|
for (samp=0; samp<mEarlyLateSamplesAllocated; samp++)
|
|
{
|
|
mEarlyLateDelay[samp] = 0.0f;
|
|
}
|
|
|
|
// Clear early delay line.
|
|
// Replace this with a block move.
|
|
for (samp=0; samp<mEarlyDelaySamplesAllocated; samp++)
|
|
{
|
|
mEarlyDelay[samp] = 0.0f;
|
|
}
|
|
|
|
// Clear allpass delays
|
|
for (delayLine=0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
for (int samp=0; samp<mAllpassSamplesAllocated[delayLine]; samp++)
|
|
{
|
|
mAllpassDelays[delayLine][samp] = 0.0f;
|
|
}
|
|
}
|
|
|
|
// Zero the internal variables
|
|
FMOD_memset(mzDecayHF, 0, kNumLateReverbDelays * sizeof(float));
|
|
FMOD_memset(mMatrix, 0, kNumMatrixStages * kNumLateReverbDelays * sizeof(float));
|
|
mzRoomHF0 = 0.0f;
|
|
mzRoomHF1 = 0.0f;
|
|
mzRoomLF0 = 0.0f;
|
|
mzRoomLF1 = 0.0f;
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ DoDSPProcessing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
void ASfxDsp::DoDSPProcessing (float * inAudio, float * outAudio, int channels, unsigned int l_sampframes,
|
|
float rate, float drymix, unsigned short speakermask)
|
|
{
|
|
unsigned int i;
|
|
int ch;
|
|
float eruOut[kNumEarlyDelayTaps + 1];
|
|
float val, oldVal;
|
|
int delayLine, delayTap;
|
|
unsigned int sampframes = (unsigned int)l_sampframes;
|
|
unsigned int sampframesmi; // sampframes - i
|
|
float invSampframes = 1.0f / (float)sampframes;
|
|
|
|
#ifdef ADD_ANTI_DENORMALS_TO_INPUT
|
|
#define VERY_SMALL_BUT_NOT_DENORMAL_VALUE (1.0e-15f)
|
|
// mix very quiet noise (-300 dB) into the input signal to hopefully avoid any denormal values
|
|
for (ch=0; ch < channels; ch++)
|
|
{
|
|
for (i=0; i < sampframes; i += 2)
|
|
{
|
|
outAudio[ch+channels*i] = inAudio[ch+channels*i] + VERY_SMALL_BUT_NOT_DENORMAL_VALUE;
|
|
}
|
|
for (i=1; i < sampframes; i += 2)
|
|
{
|
|
outAudio[ch+channels*i] = inAudio[ch+channels*i] - VERY_SMALL_BUT_NOT_DENORMAL_VALUE;
|
|
}
|
|
}
|
|
#undef VERY_SMALL_BUT_NOT_DENORMAL_VALUE
|
|
#endif
|
|
|
|
// Copy input into our input buffer. This may be faster than doing it inside the big loop.
|
|
BlockProcessInput(sampframes, channels, outAudio, rate);
|
|
|
|
// Adjust the EarlyLate read pointers in case Ref or Rev delays have changed
|
|
for (delayTap=0; delayTap<kNumEarlyLateDelayTaps; delayTap++)
|
|
{
|
|
mEarlyLateReadPointer[delayTap] = (mEarlyLateSamples[delayTap] + mEarlyLateWritePointer - 1) & mEarlyLateMask;
|
|
|
|
if (mEarlyLateSamples[delayTap] == mOldEarlyLateSamples[delayTap])
|
|
{
|
|
mEarlyLateChanged[delayTap] = false;
|
|
}
|
|
else // If the delay has changed, we need to do two reads and crossfade them.
|
|
{
|
|
mEarlyLateChanged[delayTap] = true;
|
|
mOldEarlyLateReadPointer[delayTap] = (mOldEarlyLateSamples[delayTap] + mEarlyLateWritePointer - 1) & mEarlyLateMask;
|
|
mOldEarlyLateSamples[delayTap] = mEarlyLateSamples[delayTap]; // For next time...
|
|
}
|
|
}
|
|
// Deal with interpolations and crossfades
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
// Adjust the Late read pointers in case late delay sizes have changed
|
|
mLateReadPointer[delayLine] = (mLateDelayLenSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
mLateReadPointerB[delayLine] = (mLateDelayLenBSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
if (mLateDelayLenSamples[delayLine] == mOldLateDelayLenSamples[delayLine])
|
|
{
|
|
mLateChanged[delayLine] = false;
|
|
}
|
|
else // If the late delays changed, we need to do two reads and crossfade them.
|
|
{
|
|
mLateChanged[delayLine] = true;
|
|
mOldLateReadPointer[delayLine] = (mOldLateDelayLenSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
mOldLateDelayLenSamples[delayLine] = mLateDelayLenSamples[delayLine]; // For next time...
|
|
}
|
|
|
|
mLatchFeedback[delayLine] = mFeedback[delayLine]; // Latch it in case it changes in the interim.
|
|
|
|
if (mLatchFeedback[delayLine] == mOldFeedback[delayLine])
|
|
{
|
|
mPrevFeedback[delayLine] = mLatchFeedback[delayLine]; // Use it for our crossfade (in case mFeedback hasn't changed)
|
|
}
|
|
else
|
|
{
|
|
if (!mLateChanged[delayLine]) // if we haven't made these changes above
|
|
{
|
|
mLateChanged[delayLine] = true;
|
|
mOldLateReadPointer[delayLine] = (mOldLateDelayLenSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
// We do this inside the 'if', because mOldLateDelayLenSamples gets changed above,
|
|
// which would change mOldLateReadPointer back to mLateReadPointer.
|
|
}
|
|
|
|
mPrevFeedback[delayLine] = mOldFeedback[delayLine]; // For our crossfade
|
|
mOldFeedback[delayLine] = mLatchFeedback[delayLine]; // For next time...
|
|
}
|
|
|
|
mLatchDecayHF[delayLine] = mDecayHF[delayLine]; // Latch it for our interpolation.
|
|
mLatchOneMinusDecayHF[delayLine] = 1.0f - mLatchDecayHF[delayLine];
|
|
|
|
if (mLatchDecayHF[delayLine] == mOldDecayHF[delayLine])
|
|
{
|
|
mDecayHFChanged[delayLine] = false;
|
|
}
|
|
else
|
|
{
|
|
mDecayHFChanged[delayLine] = true;
|
|
mPrevDecayHF[delayLine] = mOldDecayHF[delayLine]; // For our interpolation
|
|
mOldDecayHF[delayLine] = mLatchDecayHF[delayLine]; // For next time...
|
|
}
|
|
}
|
|
|
|
mLatchLRgain = mLRgain;
|
|
|
|
if (mLatchLRgain == mOldLRgain)
|
|
{
|
|
mLRgainChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mLRgainChanged = true;
|
|
mPrevLRgain = mOldLRgain; // For our interpolation
|
|
mOldLRgain = mLatchLRgain; // For next time
|
|
}
|
|
|
|
mLatchERgain = mERgain;
|
|
|
|
if (mLatchERgain == mOldERgain)
|
|
{
|
|
mERgainChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mERgainChanged = true;
|
|
mPrevERgain = mOldERgain; // For our interpolation
|
|
mOldERgain = mLatchERgain; // For next time
|
|
}
|
|
|
|
mLatchRoomHF = mRoomHF;
|
|
|
|
if (mLatchRoomHF == mOldRoomHF)
|
|
{
|
|
mRoomHFChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mRoomHFChanged = true;
|
|
mPrevRoomHF = mOldRoomHF; // For our interpolation
|
|
mOldRoomHF = mLatchRoomHF; // For next time
|
|
}
|
|
|
|
mLatchHadamard = mHadamard;
|
|
|
|
if (mLatchHadamard == mOldHadamard)
|
|
{
|
|
mHadamardChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mHadamardChanged = true;
|
|
mPrevHadamard = mOldHadamard; // For our interpolation
|
|
mOldHadamard = mLatchHadamard; // For next time
|
|
}
|
|
|
|
mLatchDiffusionScale = mDiffusionScale;
|
|
|
|
if (mLatchDiffusionScale == mOldDiffusionScale)
|
|
{
|
|
mDiffusionScaleChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mDiffusionScaleChanged = true;
|
|
mPrevDiffusionScale = mOldDiffusionScale; // For our interpolation
|
|
mOldDiffusionScale = mLatchDiffusionScale; // For next time
|
|
}
|
|
|
|
/*
|
|
mLatchRoomLF = mRoomLF;
|
|
|
|
if (mLatchRoomLF == mOldRoomLF)
|
|
{
|
|
mRoomLFChanged = false;
|
|
}
|
|
else
|
|
{
|
|
mRoomLFChanged = true;
|
|
mPrevRoomLF = mOldRoomLF; // For our interpolation
|
|
mOldRoomLF = mLatchRoomLF; // For next time
|
|
}
|
|
*/
|
|
|
|
// Now start the Big DSP Loop...
|
|
for (i=0; i<sampframes; i++) // for each sample
|
|
{
|
|
sampframesmi = sampframes - i;
|
|
val = (float)mInBuff[i];
|
|
|
|
// Room HF low-pass filters
|
|
float roomHF, hadamard, diffusionscale;
|
|
|
|
if (mRoomHFChanged)
|
|
{
|
|
roomHF = ((mPrevRoomHF * sampframesmi) + (mLatchRoomHF * i)) * invSampframes;
|
|
}
|
|
else
|
|
{
|
|
roomHF = mLatchRoomHF;
|
|
}
|
|
|
|
if (mHadamardChanged)
|
|
{
|
|
hadamard = ((mPrevHadamard * sampframesmi) + (mLatchHadamard * i)) * invSampframes;
|
|
}
|
|
else
|
|
{
|
|
hadamard = mLatchHadamard;
|
|
}
|
|
|
|
if (mDiffusionScaleChanged)
|
|
{
|
|
diffusionscale = ((mPrevDiffusionScale * sampframesmi) + (mLatchDiffusionScale * i)) * invSampframes;
|
|
}
|
|
else
|
|
{
|
|
diffusionscale = mLatchDiffusionScale;
|
|
}
|
|
|
|
|
|
/*
|
|
Room LF - 2nd order high pass filter, don't interpolate yet
|
|
|
|
if(mRoomLFChanged)
|
|
{
|
|
roomLF = ((mPrevRoomLF * sampframesmi) + (mLatchRoomLF * i)) * invSampframes;
|
|
}
|
|
else
|
|
{
|
|
roomLF = mLatchRoomLF;
|
|
}
|
|
*/
|
|
|
|
float oneMinusRoomHF = 1.0f - roomHF;
|
|
mzRoomHF0 = val*roomHF + mzRoomHF0*oneMinusRoomHF;
|
|
mzRoomHF1 = mzRoomHF0*roomHF + mzRoomHF1*oneMinusRoomHF;
|
|
|
|
float fout;
|
|
if(mRoomLF != 0.0f)
|
|
{
|
|
fout = mzRoomHF1 * mRoomLFcoeffs.a0 + mzRoomLF0;
|
|
mzRoomLF0 = mzRoomHF1 * mRoomLFcoeffs.a1 + mzRoomLF1 + fout * mRoomLFcoeffs.b1;
|
|
mzRoomLF1 = mzRoomHF1 * mRoomLFcoeffs.a2 + fout * mRoomLFcoeffs.b2;
|
|
}
|
|
else
|
|
{
|
|
fout = mzRoomHF1;
|
|
}
|
|
|
|
// Insert Delay here
|
|
mEarlyLateWritePointer = --mEarlyLateWritePointer & mEarlyLateMask;
|
|
mEarlyLateDelay[mEarlyLateWritePointer] = fout;
|
|
|
|
// ================== Early Reflections ==================
|
|
|
|
mEarlyLateReadPointer[0] = --mEarlyLateReadPointer[0] & mEarlyLateMask;
|
|
float input, output, ERgain;
|
|
|
|
if (mERgainChanged)
|
|
{
|
|
ERgain = ((mPrevERgain * sampframesmi) + (mLatchERgain * i)) * invSampframes;
|
|
}
|
|
else
|
|
{
|
|
ERgain = mLatchERgain;
|
|
}
|
|
|
|
input = mEarlyLateDelay[mEarlyLateReadPointer[0]] * ERgain;
|
|
|
|
if (mEarlyLateChanged[0]) // Crossfade the old and new reads
|
|
{
|
|
mOldEarlyLateReadPointer[0] = --mOldEarlyLateReadPointer[0] & mEarlyLateMask;
|
|
float oldInput = mEarlyLateDelay[mOldEarlyLateReadPointer[0]] * ERgain;
|
|
input = ((oldInput * sampframesmi) + (input * i)) * invSampframes;
|
|
}
|
|
|
|
// 2 Allpasses in series
|
|
|
|
for (delayLine=0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
mAllpassWritePointer[delayLine] = --mAllpassWritePointer[delayLine] & mAllpassMask[delayLine];
|
|
|
|
mAllpassReadPointer[delayLine] = --mAllpassReadPointer[delayLine] & mAllpassMask[delayLine];
|
|
output = mAllpassDelays[delayLine][mAllpassReadPointer[delayLine]]
|
|
- input * mAllpassGain; // feedforward loop
|
|
|
|
mAllpassDelays[delayLine][mAllpassWritePointer[delayLine]] =
|
|
input + output * mAllpassGain; // feedback loop
|
|
input = output;
|
|
}
|
|
|
|
// Early Delay FIR
|
|
|
|
eruOut[0] = output;
|
|
|
|
mEarlyWritePointer = --mEarlyWritePointer & mEarlyMask;
|
|
mEarlyDelay[mEarlyWritePointer] = output;
|
|
|
|
for (delayTap=0; delayTap<kNumEarlyDelayTaps; delayTap++)
|
|
{
|
|
mEarlyReadPointer[delayTap] = --mEarlyReadPointer[delayTap] & mEarlyMask;
|
|
eruOut[delayTap+1] = mEarlyDelay[mEarlyReadPointer[delayTap]];
|
|
}
|
|
|
|
|
|
// ================== Late Reverb ====================
|
|
|
|
// This assumes mNumLateReverbDelays = kNumLate_EarlyLateDelayTaps.
|
|
for (delayLine=0; delayLine<mNumLateReverbDelays; delayLine++)
|
|
{
|
|
delayTap = kNumEarly_EarlyLateDelayTaps + delayLine;
|
|
|
|
mEarlyLateReadPointer[delayTap] = --mEarlyLateReadPointer[delayTap] & mEarlyLateMask;
|
|
val = mEarlyLateDelay[mEarlyLateReadPointer[delayTap]];
|
|
|
|
if (mEarlyLateChanged[delayTap]) // Crossfade the old and new reads
|
|
{
|
|
mOldEarlyLateReadPointer[delayTap] = --mOldEarlyLateReadPointer[delayTap] & mEarlyLateMask;
|
|
oldVal = mEarlyLateDelay[mOldEarlyLateReadPointer[delayTap]];
|
|
val = ((oldVal * sampframesmi) + (val * i)) * invSampframes;
|
|
}
|
|
|
|
if (mLRgainChanged)
|
|
{
|
|
float LRgain = ((mPrevLRgain * sampframesmi) + (mLatchLRgain * i)) * invSampframes;
|
|
val *= LRgain;
|
|
}
|
|
else
|
|
{
|
|
val *= mLatchLRgain;
|
|
}
|
|
|
|
mLateReadPointer[delayLine] = --mLateReadPointer[delayLine] & mLateMask[delayLine];
|
|
if (mLateChanged[delayLine]) // Crossfade
|
|
{
|
|
mOldLateReadPointer[delayLine] = --mOldLateReadPointer[delayLine] & mLateMask[delayLine];
|
|
oldVal = mPrevFeedback[delayLine] * mLateDelays[delayLine][mOldLateReadPointer[delayLine]];
|
|
float thisVal = mLatchFeedback[delayLine] * mLateDelays[delayLine][mLateReadPointer[delayLine]];
|
|
val += (((oldVal * sampframesmi) + (thisVal * i)) * invSampframes);
|
|
}
|
|
else
|
|
{
|
|
val += (mLatchFeedback[delayLine] * mLateDelays[delayLine][mLateReadPointer[delayLine]]);
|
|
}
|
|
|
|
// Gain to compensate for one matrix gain, based on the diffusion.
|
|
// val *= mDiffusionScale;
|
|
val *= diffusionscale;
|
|
|
|
// Low-pass filter for HF Decay ratio
|
|
if (mDecayHFChanged[delayLine])
|
|
{
|
|
// Interpolate decayHF
|
|
float decayHF = ((mPrevDecayHF[delayLine] * sampframesmi) + (mLatchDecayHF[delayLine] * i)) * invSampframes;
|
|
float oneMinusDecayHF = 1.0f - decayHF;
|
|
mzDecayHF[delayLine] = val * decayHF + mzDecayHF[delayLine] * oneMinusDecayHF;
|
|
}
|
|
else
|
|
{
|
|
mzDecayHF[delayLine] = val * mLatchDecayHF[delayLine] + mzDecayHF[delayLine] * mLatchOneMinusDecayHF[delayLine];
|
|
}
|
|
|
|
mLateWritePointer[delayLine] = --mLateWritePointer[delayLine] & mLateMask[delayLine];
|
|
}
|
|
|
|
|
|
mMatrix[0 * kNumLateReverbDelays + 0] = mzDecayHF[0] + mzDecayHF[1] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 1] = mzDecayHF[1] - mzDecayHF[0] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 2] = mzDecayHF[2] + mzDecayHF[3] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 3] = mzDecayHF[3] - mzDecayHF[2] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 4] = mzDecayHF[4] + mzDecayHF[5] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 5] = mzDecayHF[5] - mzDecayHF[4] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 6] = mzDecayHF[6] + mzDecayHF[7] * hadamard;
|
|
mMatrix[0 * kNumLateReverbDelays + 7] = mzDecayHF[7] - mzDecayHF[6] * hadamard;
|
|
|
|
mMatrix[1 * kNumLateReverbDelays + 0] = mMatrix[0 * kNumLateReverbDelays + 0] + mMatrix[0 * kNumLateReverbDelays + 2] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 1] = mMatrix[0 * kNumLateReverbDelays + 1] + mMatrix[0 * kNumLateReverbDelays + 3] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 2] = mMatrix[0 * kNumLateReverbDelays + 2] - mMatrix[0 * kNumLateReverbDelays + 0] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 3] = mMatrix[0 * kNumLateReverbDelays + 3] - mMatrix[0 * kNumLateReverbDelays + 1] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 4] = mMatrix[0 * kNumLateReverbDelays + 4] + mMatrix[0 * kNumLateReverbDelays + 6] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 5] = mMatrix[0 * kNumLateReverbDelays + 5] + mMatrix[0 * kNumLateReverbDelays + 7] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 6] = mMatrix[0 * kNumLateReverbDelays + 6] - mMatrix[0 * kNumLateReverbDelays + 4] * hadamard;
|
|
mMatrix[1 * kNumLateReverbDelays + 7] = mMatrix[0 * kNumLateReverbDelays + 7] - mMatrix[0 * kNumLateReverbDelays + 5] * hadamard;
|
|
|
|
mMatrix[2 * kNumLateReverbDelays + 0] = mMatrix[1 * kNumLateReverbDelays + 0] + mMatrix[1 * kNumLateReverbDelays + 4] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 1] = mMatrix[1 * kNumLateReverbDelays + 1] + mMatrix[1 * kNumLateReverbDelays + 5] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 2] = mMatrix[1 * kNumLateReverbDelays + 2] + mMatrix[1 * kNumLateReverbDelays + 6] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 3] = mMatrix[1 * kNumLateReverbDelays + 3] + mMatrix[1 * kNumLateReverbDelays + 7] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 4] = mMatrix[1 * kNumLateReverbDelays + 4] - mMatrix[1 * kNumLateReverbDelays + 0] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 5] = mMatrix[1 * kNumLateReverbDelays + 5] - mMatrix[1 * kNumLateReverbDelays + 1] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 6] = mMatrix[1 * kNumLateReverbDelays + 6] - mMatrix[1 * kNumLateReverbDelays + 2] * hadamard;
|
|
mMatrix[2 * kNumLateReverbDelays + 7] = mMatrix[1 * kNumLateReverbDelays + 7] - mMatrix[1 * kNumLateReverbDelays + 3] * hadamard;
|
|
|
|
// Write to the lateDelays
|
|
mLateDelays[0][mLateWritePointer[0]] = mMatrix[2 * kNumLateReverbDelays + 1];
|
|
mLateDelays[1][mLateWritePointer[1]] = mMatrix[2 * kNumLateReverbDelays + 2];
|
|
mLateDelays[2][mLateWritePointer[2]] = mMatrix[2 * kNumLateReverbDelays + 3];
|
|
mLateDelays[3][mLateWritePointer[3]] = mMatrix[2 * kNumLateReverbDelays + 4];
|
|
mLateDelays[4][mLateWritePointer[4]] = mMatrix[2 * kNumLateReverbDelays + 5];
|
|
mLateDelays[5][mLateWritePointer[5]] = mMatrix[2 * kNumLateReverbDelays + 6];
|
|
mLateDelays[6][mLateWritePointer[6]] = mMatrix[2 * kNumLateReverbDelays + 7];
|
|
mLateDelays[7][mLateWritePointer[7]] = mMatrix[2 * kNumLateReverbDelays + 0];
|
|
|
|
for (ch=0; ch<channels; ch++) // for each channel
|
|
{
|
|
if (!((1 << ch) & speakermask))
|
|
{
|
|
outAudio[i*channels+ch] = inAudio[i*channels+ch];
|
|
}
|
|
else
|
|
{
|
|
float lruSum = 0.0f;
|
|
float eruSum = 0.0f;
|
|
int nextDelay = ch;
|
|
|
|
for (int delay=0; delay<(kNumLateReverbDelays/channels); delay++)
|
|
{
|
|
mLateReadPointerB[nextDelay] = --mLateReadPointerB[nextDelay] & mLateMask[nextDelay];
|
|
lruSum += mLateDelays[nextDelay][mLateReadPointerB[nextDelay]];
|
|
|
|
eruSum += eruOut[nextDelay];
|
|
nextDelay += channels;
|
|
}
|
|
|
|
if (drymix == 0.0f)
|
|
{
|
|
outAudio[i*channels+ch] = eruSum + lruSum;
|
|
}
|
|
else
|
|
{
|
|
outAudio[i*channels+ch] = (inAudio[i*channels+ch] * drymix) + eruSum + lruSum;
|
|
}
|
|
}
|
|
|
|
} // end for each channel
|
|
} // end for each sample
|
|
|
|
} // end DoDSPProcessing
|
|
|
|
|
|
// Copy input into our input buffer. This may be faster than doing it inside the big loop (?).
|
|
void ASfxDsp::BlockProcessInput(unsigned int sampframes, int channels, float *inAudio, float rate)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (channels == 1) // mono
|
|
{
|
|
SfxDataMove(inAudio, mInBuff, sampframes*sizeof(float));
|
|
}
|
|
else if (channels == 6)
|
|
{
|
|
float *inptr = inAudio;
|
|
float *outptr = mInBuff;
|
|
|
|
for (i = 0; i < sampframes / 4; i++) // for each sample
|
|
{
|
|
outptr[0] = inptr[0] + inptr[1] + inptr[2] + inptr[3] + inptr[4] + inptr[5];
|
|
outptr[1] = inptr[6] + inptr[7] + inptr[8] + inptr[9] + inptr[10] + inptr[11];
|
|
outptr[2] = inptr[12] + inptr[13] + inptr[14] + inptr[15] + inptr[16] + inptr[17];
|
|
outptr[3] = inptr[18] + inptr[19] + inptr[20] + inptr[21] + inptr[22] + inptr[23];
|
|
inptr += 24;
|
|
outptr += 4;
|
|
}
|
|
}
|
|
else if (channels == 8)
|
|
{
|
|
float *inptr = inAudio;
|
|
float *outptr = mInBuff;
|
|
|
|
for (i = 0; i < sampframes / 4; i++) // for each sample
|
|
{
|
|
outptr[0] = inptr[0] + inptr[1] + inptr[2] + inptr[3] + inptr[4] + inptr[5] + inptr[6] + inptr[7];
|
|
outptr[1] = inptr[8] + inptr[9] + inptr[10] + inptr[11] + inptr[12] + inptr[13] + inptr[14] + inptr[15];
|
|
outptr[2] = inptr[16] + inptr[17] + inptr[18] + inptr[19] + inptr[20] + inptr[21] + inptr[22] + inptr[23];
|
|
outptr[3] = inptr[24] + inptr[25] + inptr[26] + inptr[27] + inptr[28] + inptr[29] + inptr[30] + inptr[31];
|
|
inptr += 32;
|
|
outptr += 4;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int ch;
|
|
|
|
for (i=0; i<sampframes; i++) // for each sample
|
|
{
|
|
mInBuff[i] = 0.0f;
|
|
for (ch=0; ch<channels; ++ch) // for each channel
|
|
{
|
|
mInBuff[i] += inAudio[i*channels+ch]; // *** Would it be more efficient to reverse the loops? ***
|
|
}
|
|
// mInBuff[i] /= (float)channels; // Don't average them!
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void ASfxDsp::SetLate_EarlyLateDelayTaps(float refPlusRevDelaySec, float nextLengthSec, float delayRatio, float rate)
|
|
{
|
|
mEarlyLateSec[kNumEarly_EarlyLateDelayTaps] = refPlusRevDelaySec; // Tap 1 = Ref+Rev delay
|
|
int earlyLateSamples = (int)(refPlusRevDelaySec * rate);
|
|
if (earlyLateSamples == 0)
|
|
{
|
|
earlyLateSamples++; // Avoid long wrap-around delay.
|
|
}
|
|
mEarlyLateSamples[kNumEarly_EarlyLateDelayTaps] = earlyLateSamples;
|
|
|
|
// Set the remaining EarlyLate delay taps (2 - 8)
|
|
for (int delayTap=kNumEarly_EarlyLateDelayTaps+1; delayTap<kNumEarlyLateDelayTaps; delayTap++)
|
|
{
|
|
mEarlyLateSec[delayTap] = refPlusRevDelaySec + nextLengthSec;
|
|
mEarlyLateSamples[delayTap] = (int)(mEarlyLateSec[delayTap] * rate);
|
|
nextLengthSec *= delayRatio;
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::SetAllpassDelays(float rate)
|
|
{
|
|
mAllpassDelayLenSec[0] = kAllpassDelayLenSec0;
|
|
mAllpassDelayLenSec[1] = kAllpassDelayLenSec1;
|
|
|
|
for (int delayLine = 0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
mAllpassDelayLenSamples[delayLine] = (int)(mAllpassDelayLenSec[delayLine] * rate);
|
|
mAllpassReadPointer[delayLine] = mAllpassDelayLenSamples[delayLine] - 1;
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::SetEarlyDelay(float nextLengthSec, float delayRatio, float rate)
|
|
{
|
|
// Set the early delay taps (0 - 6)
|
|
for (int delayLine=0; delayLine<kNumEarlyDelayTaps; delayLine++)
|
|
{
|
|
mEarlyDelayLenSec[delayLine] = nextLengthSec;
|
|
mEarlyDelayLenSamples[delayLine] = (int)(nextLengthSec * rate);
|
|
nextLengthSec *= delayRatio;
|
|
|
|
mEarlyReadPointer[delayLine] = mEarlyDelayLenSamples[delayLine] - 1;
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::SetLateDelays(float nextLengthSec, float delayRatio, float nextLengthBSec, float delayRatioB, float rate)
|
|
{
|
|
float nextLengthSamples;
|
|
|
|
float nextLengthBSamples = nextLengthBSec * rate;
|
|
|
|
for (int delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
// Set main delay line lengths
|
|
mLateDelayLenSec[delayLine] = nextLengthSec;
|
|
nextLengthSamples = nextLengthSec * rate;
|
|
mLateDelayLenSamples[delayLine] = (int)(nextLengthSamples + 0.5);
|
|
nextLengthSec *= delayRatio;
|
|
mLateReadPointer[delayLine] = (mLateDelayLenSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
|
|
// Set delay line lengths for B reads
|
|
mLateDelayLenBSamples[delayLine] = (int)(nextLengthBSamples + 0.5); // Short delays to keep outputs from cancelling.
|
|
nextLengthBSamples *= delayRatioB;
|
|
mLateReadPointerB[delayLine] = (mLateDelayLenBSamples[delayLine] + mLateWritePointer[delayLine] - 1)
|
|
& mLateMask[delayLine];
|
|
}
|
|
}
|
|
|
|
void ASfxDsp::ZeroWritePointers()
|
|
{
|
|
int delayLine;
|
|
|
|
mEarlyWritePointer = 0;
|
|
mEarlyLateWritePointer = 0;
|
|
|
|
for (delayLine=0; delayLine<kNumAllpassDelays; delayLine++)
|
|
{
|
|
mAllpassWritePointer[delayLine] = 0;
|
|
}
|
|
|
|
for (delayLine=0; delayLine<kNumLateReverbDelays; delayLine++)
|
|
{
|
|
mLateWritePointer[delayLine] = 0;
|
|
}
|
|
}
|