mirror of https://github.com/AxioDL/amuse.git
Implement reverb effects
This commit is contained in:
parent
1d726ed179
commit
3d3869e30d
|
@ -13,8 +13,7 @@ set(SOURCES
|
|||
lib/Voice.cpp
|
||||
lib/Submix.cpp
|
||||
lib/EffectBase.cpp
|
||||
lib/EffectReverbHi.cpp
|
||||
lib/EffectReverbStd.cpp
|
||||
lib/EffectReverb.cpp
|
||||
lib/EffectChorus.cpp
|
||||
lib/EffectDelay.cpp
|
||||
lib/SurroundProfiles.cpp
|
||||
|
@ -39,8 +38,7 @@ set(HEADERS
|
|||
include/amuse/IBackendVoice.hpp
|
||||
include/amuse/IBackendVoiceAllocator.hpp
|
||||
include/amuse/EffectBase.hpp
|
||||
include/amuse/EffectReverbHi.hpp
|
||||
include/amuse/EffectReverbStd.hpp
|
||||
include/amuse/EffectReverb.hpp
|
||||
include/amuse/EffectChorus.hpp
|
||||
include/amuse/EffectDelay.hpp
|
||||
include/amuse/SurroundProfiles.hpp
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
#ifndef __AMUSE_EFFECTREVERB_HPP__
|
||||
#define __AMUSE_EFFECTREVERB_HPP__
|
||||
|
||||
#include "EffectBase.hpp"
|
||||
#include "amuse/Common.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/** Delay state for one 'tap' of the reverb effect */
|
||||
struct ReverbDelayLine
|
||||
{
|
||||
int32_t x0_inPoint = 0;
|
||||
int32_t x4_outPoint = 0;
|
||||
int32_t x8_length = 0;
|
||||
std::unique_ptr<float[]> xc_inputs;
|
||||
float x10_lastInput = 0.f;
|
||||
|
||||
void allocate(int32_t delay);
|
||||
void setdelay(int32_t delay);
|
||||
};
|
||||
|
||||
/** Reverb effect with configurable reflection filtering */
|
||||
template <typename T, size_t AP, size_t C>
|
||||
class EffectReverb : public EffectBase<T>
|
||||
{
|
||||
protected:
|
||||
ReverbDelayLine x0_x0_AP[8][AP] = {}; /**< All-pass delay lines */
|
||||
ReverbDelayLine x78_xb4_C[8][C] = {}; /**< Comb delay lines */
|
||||
float xf0_x168_allPassCoef = 0.f; /**< All-pass mix coefficient */
|
||||
float xf4_x16c_combCoef[8][C] = {}; /**< Comb mix coefficients */
|
||||
float x10c_x190_lpLastout[8] = {}; /**< Last low-pass results */
|
||||
float x118_x19c_level = 0.f; /**< Internal wet/dry mix factor */
|
||||
float x11c_x1a0_damping = 0.f; /**< Low-pass damping */
|
||||
int32_t x120_x1a4_preDelayTime = 0; /**< Sample count of pre-delay */
|
||||
std::unique_ptr<float[]> x124_x1ac_preDelayLine[8]; /**< Dedicated pre-delay buffers */
|
||||
float* x130_x1b8_preDelayPtr[8] = {}; /**< Current pre-delay pointers */
|
||||
|
||||
float x140_x1c8_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
||||
float x144_x1cc_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||
float x148_x1d0_time; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||
float x14c_x1d4_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||
float x150_x1d8_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||
|
||||
double m_sampleRate; /**< copy of sample rate */
|
||||
bool m_dirty = true; /**< needs update of internal parameter data */
|
||||
void _update();
|
||||
public:
|
||||
EffectReverb(float coloration, float mix, float time,
|
||||
float damping, float preDelay, double sampleRate);
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
|
||||
void setColoration(float coloration)
|
||||
{
|
||||
x140_x1c8_coloration = clamp(0.f, coloration, 1.f);
|
||||
m_dirty = true;
|
||||
}
|
||||
void setMix(float mix)
|
||||
{
|
||||
x144_x1cc_mix = clamp(0.f, mix, 1.f);
|
||||
m_dirty = true;
|
||||
}
|
||||
void setTime(float time)
|
||||
{
|
||||
x148_x1d0_time = clamp(0.01f, time, 10.f);
|
||||
m_dirty = true;
|
||||
}
|
||||
void setDamping(float damping)
|
||||
{
|
||||
x14c_x1d4_damping = clamp(0.f, damping, 1.f);
|
||||
m_dirty = true;
|
||||
}
|
||||
void setPreDelay(float preDelay)
|
||||
{
|
||||
x150_x1d8_preDelay = clamp(0.f, preDelay, 0.1f);
|
||||
m_dirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
/** Standard-quality 2-tap reverb */
|
||||
template <typename T>
|
||||
class EffectReverbStd : public EffectReverb<T, 2, 2>
|
||||
{
|
||||
public:
|
||||
EffectReverbStd(float coloration, float mix, float time,
|
||||
float damping, float preDelay, double sampleRate);
|
||||
};
|
||||
|
||||
/** High-quality 3-tap reverb */
|
||||
template <typename T>
|
||||
class EffectReverbHi : public EffectReverb<T, 2, 3>
|
||||
{
|
||||
ReverbDelayLine x78_LP[8] = {}; /**< Per-channel low-pass delay-lines */
|
||||
float x1a8_internalCrosstalk;
|
||||
float x1dc_crosstalk; /**< [0.0, 1.0] factor defining how much reflections are allowed to bleed to other channels */
|
||||
void _update();
|
||||
void _handleReverb(T* audio, int chanIdx, int chanCount, int sampleCount);
|
||||
void _doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount);
|
||||
public:
|
||||
EffectReverbHi(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk, double sampleRate);
|
||||
void applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap);
|
||||
|
||||
void setCrosstalk(float crosstalk)
|
||||
{
|
||||
x1dc_crosstalk = clamp(0.f, crosstalk, 1.f);
|
||||
EffectReverb<T, 2, 3>::m_dirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTREVERB_HPP__
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef __AMUSE_EFFECTREVERBHI_HPP__
|
||||
#define __AMUSE_EFFECTREVERBHI_HPP__
|
||||
|
||||
#include "EffectBase.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/** Reverb effect with configurable reflection filtering and channel-crosstalk */
|
||||
template <typename T>
|
||||
class EffectReverbHi : public EffectBase<T>
|
||||
{
|
||||
float m_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
||||
float m_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||
float m_time; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||
float m_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||
float m_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||
float m_crosstalk; /**< [0.0, 100.0] factor defining how much reflections are allowed to bleed to other channels */
|
||||
public:
|
||||
EffectReverbHi(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk);
|
||||
void applyEffect(int16_t* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
void applyEffect(int32_t* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
void applyEffect(float* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTREVERBHI_HPP__
|
|
@ -1,31 +0,0 @@
|
|||
#ifndef __AMUSE_EFFECTREVERBSTD_HPP__
|
||||
#define __AMUSE_EFFECTREVERBSTD_HPP__
|
||||
|
||||
#include "EffectBase.hpp"
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/** Reverb effect with configurable reflection filtering */
|
||||
template <typename T>
|
||||
class EffectReverbStd : public EffectBase<T>
|
||||
{
|
||||
float m_coloration; /**< [0.0, 1.0] influences filter coefficients to define surface characteristics of a room */
|
||||
float m_mix; /**< [0.0, 1.0] dry/wet mix factor of reverb effect */
|
||||
float m_time; /**< [0.01, 10.0] time in seconds for reflection decay */
|
||||
float m_damping; /**< [0.0, 1.0] damping factor influencing low-pass filter of reflections */
|
||||
float m_preDelay; /**< [0.0, 0.1] time in seconds before initial reflection heard */
|
||||
public:
|
||||
EffectReverbStd(float coloration, float mix, float time,
|
||||
float damping, float preDelay);
|
||||
void applyEffect(int16_t* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
void applyEffect(int32_t* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
void applyEffect(float* audio, size_t frameCount,
|
||||
const ChannelMap& chanMap, double sampleRate);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_EFFECTREVERBSTD_HPP__
|
|
@ -8,8 +8,7 @@
|
|||
#include "AudioGroupSampleDirectory.hpp"
|
||||
#include "EffectChorus.hpp"
|
||||
#include "EffectDelay.hpp"
|
||||
#include "EffectReverbStd.hpp"
|
||||
#include "EffectReverbHi.hpp"
|
||||
#include "EffectReverb.hpp"
|
||||
#include "Emitter.hpp"
|
||||
#include "Engine.hpp"
|
||||
#include "Envelope.hpp"
|
||||
|
|
|
@ -14,9 +14,6 @@ static inline int16_t DSPSampClamp(int32_t val)
|
|||
return val;
|
||||
}
|
||||
|
||||
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample);
|
||||
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample);
|
||||
|
@ -27,6 +24,14 @@ unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
|||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample);
|
||||
|
||||
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample);
|
||||
|
||||
unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,414 @@
|
|||
#include "amuse/EffectReverb.hpp"
|
||||
#include "amuse/IBackendVoice.hpp"
|
||||
#include <cmath>
|
||||
#include <string.h>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
||||
/* Comb-filter delays */
|
||||
static const size_t CTapDelays[] =
|
||||
{
|
||||
1789,
|
||||
1999,
|
||||
2333
|
||||
};
|
||||
|
||||
/* All-pass-filter delays */
|
||||
static const size_t APTapDelays[] =
|
||||
{
|
||||
433,
|
||||
149
|
||||
};
|
||||
|
||||
/* Per-channel low-pass delays (Hi-quality reverb only) */
|
||||
static const size_t LPTapDelays[] =
|
||||
{
|
||||
47,
|
||||
73,
|
||||
67,
|
||||
57,
|
||||
43,
|
||||
57,
|
||||
83,
|
||||
73
|
||||
};
|
||||
|
||||
void ReverbDelayLine::allocate(int32_t delay)
|
||||
{
|
||||
delay += 2;
|
||||
x8_length = delay * sizeof(float);
|
||||
xc_inputs.reset(new float[delay]);
|
||||
memset(xc_inputs.get(), 0, x8_length);
|
||||
x10_lastInput = 0.f;
|
||||
setdelay(delay / 2);
|
||||
x0_inPoint = 0;
|
||||
x4_outPoint = 0;
|
||||
}
|
||||
|
||||
void ReverbDelayLine::setdelay(int32_t delay)
|
||||
{
|
||||
x4_outPoint = x0_inPoint - delay * sizeof(float);
|
||||
while (x4_outPoint < 0)
|
||||
x4_outPoint += x8_length;
|
||||
}
|
||||
|
||||
template <typename T, size_t AP, size_t C>
|
||||
EffectReverb<T, AP, C>::EffectReverb(float coloration, float mix, float time,
|
||||
float damping, float preDelay, double sampleRate)
|
||||
: x140_x1c8_coloration(clamp(0.f, coloration, 1.f)),
|
||||
x144_x1cc_mix(clamp(0.f, mix, 1.f)),
|
||||
x148_x1d0_time(clamp(0.01f, time, 10.f)),
|
||||
x14c_x1d4_damping(clamp(0.f, damping, 1.f)),
|
||||
x150_x1d8_preDelay(clamp(0.f, preDelay, 0.1f)),
|
||||
m_sampleRate(sampleRate)
|
||||
{}
|
||||
|
||||
template <typename T, size_t AP, size_t C>
|
||||
void EffectReverb<T, AP, C>::_update()
|
||||
{
|
||||
float timeSamples = x148_x1d0_time * m_sampleRate;
|
||||
for (int c=0 ; c<8 ; ++c)
|
||||
{
|
||||
for (int t=0 ; t<C ; ++t)
|
||||
{
|
||||
ReverbDelayLine& combLine = x78_xb4_C[c][t];
|
||||
size_t tapDelay = CTapDelays[t] * m_sampleRate / 32000.0;
|
||||
combLine.allocate(tapDelay);
|
||||
combLine.setdelay(tapDelay);
|
||||
xf4_x16c_combCoef[c][t] = std::pow(10.f, tapDelay * -3 / timeSamples);
|
||||
}
|
||||
|
||||
for (int t=0 ; t<AP ; ++t)
|
||||
{
|
||||
ReverbDelayLine& allPassLine = x0_x0_AP[c][t];
|
||||
size_t tapDelay = APTapDelays[t] * m_sampleRate / 32000.0;
|
||||
allPassLine.allocate(tapDelay);
|
||||
allPassLine.setdelay(tapDelay);
|
||||
}
|
||||
}
|
||||
|
||||
xf0_x168_allPassCoef = x140_x1c8_coloration;
|
||||
x118_x19c_level = x144_x1cc_mix;
|
||||
x11c_x1a0_damping = x14c_x1d4_damping;
|
||||
|
||||
if (x11c_x1a0_damping < 0.05f)
|
||||
x11c_x1a0_damping = 0.05f;
|
||||
|
||||
x11c_x1a0_damping = 1.f - (x11c_x1a0_damping * 0.8f + 0.05);
|
||||
|
||||
if (x150_x1d8_preDelay != 0.f)
|
||||
{
|
||||
x120_x1a4_preDelayTime = m_sampleRate * x150_x1d8_preDelay;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
{
|
||||
x124_x1ac_preDelayLine[i].reset(new float[x120_x1a4_preDelayTime]);
|
||||
memset(x124_x1ac_preDelayLine[i].get(), 0, x120_x1a4_preDelayTime * sizeof(float));
|
||||
x130_x1b8_preDelayPtr[i] = x124_x1ac_preDelayLine[i].get();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
x120_x1a4_preDelayTime = 0;
|
||||
for (int i=0 ; i<8 ; ++i)
|
||||
{
|
||||
x124_x1ac_preDelayLine[i] = nullptr;
|
||||
x130_x1b8_preDelayPtr[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
template <typename T, size_t AP, size_t C>
|
||||
void EffectReverb<T, AP, C>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
||||
{
|
||||
if (m_dirty)
|
||||
_update();
|
||||
|
||||
float dampWet = x118_x19c_level * 0.6f;
|
||||
float dampDry = 0.6f - dampWet;
|
||||
|
||||
for (size_t f=0 ; f<frameCount ;)
|
||||
{
|
||||
for (int c=0 ; c<chanMap.m_channelCount ; ++c)
|
||||
{
|
||||
float* combCoefs = xf4_x16c_combCoef[c];
|
||||
float& lpLastOut = x10c_x190_lpLastout[c];
|
||||
float* preDelayLine = x124_x1ac_preDelayLine[c].get();
|
||||
float* preDelayPtr = x130_x1b8_preDelayPtr[c];
|
||||
float* lastPreDelaySamp = &preDelayLine[x120_x1a4_preDelayTime - 1];
|
||||
|
||||
ReverbDelayLine* linesC = x78_xb4_C[c];
|
||||
ReverbDelayLine* linesAP = x0_x0_AP[c];
|
||||
|
||||
float sample = audio[c];
|
||||
for (int s=1 ; s<160 && f<frameCount ; ++s, ++f)
|
||||
{
|
||||
/* Pre-delay stage */
|
||||
float sample2 = sample;
|
||||
if (x120_x1a4_preDelayTime != 0)
|
||||
{
|
||||
sample2 = *preDelayPtr;
|
||||
*preDelayPtr = sample;
|
||||
preDelayPtr += 4;
|
||||
if (preDelayPtr == lastPreDelaySamp)
|
||||
preDelayPtr = preDelayLine;
|
||||
}
|
||||
|
||||
/* Comb filter stage */
|
||||
linesC[0].xc_inputs[linesC[0].x0_inPoint] = combCoefs[0] * linesC[0].x10_lastInput + sample2;
|
||||
linesC[0].x0_inPoint += 4;
|
||||
|
||||
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
||||
linesC[1].x0_inPoint += 4;
|
||||
|
||||
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
||||
linesC[0].x4_outPoint += 4;
|
||||
|
||||
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
||||
linesC[1].x4_outPoint += 4;
|
||||
|
||||
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
||||
linesC[0].x0_inPoint = 0;
|
||||
|
||||
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
||||
linesC[1].x0_inPoint = 0;
|
||||
|
||||
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
||||
linesC[0].x4_outPoint = 0;
|
||||
|
||||
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
||||
linesC[1].x4_outPoint = 0;
|
||||
|
||||
/* All-pass filter stage */
|
||||
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
||||
xf0_x168_allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
|
||||
float lowPass = -(xf0_x168_allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] -
|
||||
linesAP[0].x10_lastInput);
|
||||
linesAP[0].x0_inPoint += 4;
|
||||
|
||||
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
||||
linesAP[0].x4_outPoint += 4;
|
||||
|
||||
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
||||
linesAP[0].x0_inPoint = 0;
|
||||
|
||||
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
||||
linesAP[0].x4_outPoint = 0;
|
||||
|
||||
lpLastOut = x11c_x1a0_damping * lpLastOut + lowPass * 0.3f;
|
||||
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] = xf0_x168_allPassCoef * linesAP[1].x10_lastInput + lpLastOut;
|
||||
float allPass = -(xf0_x168_allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] -
|
||||
linesAP[1].x10_lastInput);
|
||||
linesAP[1].x0_inPoint += 4;
|
||||
|
||||
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
||||
linesAP[1].x4_outPoint += 4;
|
||||
|
||||
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
||||
linesAP[1].x0_inPoint = 0;
|
||||
|
||||
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
||||
linesAP[1].x4_outPoint = 0;
|
||||
|
||||
/* Mix out */
|
||||
audio[(s-1) * chanMap.m_channelCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||
sample = audio[s * chanMap.m_channelCount + c];
|
||||
}
|
||||
x130_x1b8_preDelayPtr[c] = preDelayPtr;
|
||||
}
|
||||
audio += chanMap.m_channelCount * 160;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EffectReverbStd<T>::EffectReverbStd(float coloration, float mix, float time,
|
||||
float damping, float preDelay, double sampleRate)
|
||||
: EffectReverb<T, 2, 2>(coloration, mix, time, damping, preDelay, sampleRate)
|
||||
{
|
||||
EffectReverb<T, 2, 2>::_update();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EffectReverbHi<T>::_update()
|
||||
{
|
||||
EffectReverb<T, 2, 3>::_update();
|
||||
|
||||
for (int c=0 ; c<8 ; ++c)
|
||||
{
|
||||
ReverbDelayLine& hpLine = x78_LP[c];
|
||||
size_t tapDelay = LPTapDelays[c] * EffectReverb<T, 2, 3>::m_sampleRate / 32000.0;
|
||||
hpLine.allocate(tapDelay);
|
||||
hpLine.setdelay(tapDelay);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EffectReverbHi<T>::_handleReverb(T* audio, int c, int chanCount, int sampleCount)
|
||||
{
|
||||
float dampWet = EffectReverb<T, 2, 3>::x118_x19c_level * 0.6f;
|
||||
float dampDry = 0.6f - dampWet;
|
||||
|
||||
float* combCoefs = EffectReverb<T, 2, 3>::xf4_x16c_combCoef[c];
|
||||
float& lpLastOut = EffectReverb<T, 2, 3>::x10c_x190_lpLastout[c];
|
||||
float* preDelayLine = EffectReverb<T, 2, 3>::x124_x1ac_preDelayLine[c].get();
|
||||
float* preDelayPtr = EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c];
|
||||
float* lastPreDelaySamp = &preDelayLine[EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime - 1];
|
||||
|
||||
ReverbDelayLine* linesC = EffectReverb<T, 2, 3>::x78_xb4_C[c];
|
||||
ReverbDelayLine* linesAP = EffectReverb<T, 2, 3>::x0_x0_AP[c];
|
||||
ReverbDelayLine& lineHP = x78_LP[c];
|
||||
|
||||
float allPassCoef = EffectReverb<T, 2, 3>::xf0_x168_allPassCoef;
|
||||
float damping = EffectReverb<T, 2, 3>::x11c_x1a0_damping;
|
||||
int32_t preDelayTime = EffectReverb<T, 2, 3>::x120_x1a4_preDelayTime;
|
||||
|
||||
float sample = audio[c];
|
||||
for (int s=1 ; s<sampleCount ; ++s)
|
||||
{
|
||||
/* Pre-delay stage */
|
||||
float sample2 = sample;
|
||||
if (preDelayTime != 0)
|
||||
{
|
||||
sample2 = *preDelayPtr;
|
||||
*preDelayPtr = sample;
|
||||
preDelayPtr += 4;
|
||||
if (preDelayPtr == lastPreDelaySamp)
|
||||
preDelayPtr = preDelayLine;
|
||||
}
|
||||
|
||||
/* Comb filter stage */
|
||||
linesC[0].xc_inputs[linesC[0].x0_inPoint] = combCoefs[0] * linesC[0].x10_lastInput + sample2;
|
||||
linesC[0].x0_inPoint += 4;
|
||||
|
||||
linesC[1].xc_inputs[linesC[1].x0_inPoint] = combCoefs[1] * linesC[1].x10_lastInput + sample2;
|
||||
linesC[1].x0_inPoint += 4;
|
||||
|
||||
linesC[0].x10_lastInput = linesC[0].xc_inputs[linesC[0].x4_outPoint];
|
||||
linesC[0].x4_outPoint += 4;
|
||||
|
||||
linesC[1].x10_lastInput = linesC[1].xc_inputs[linesC[1].x4_outPoint];
|
||||
linesC[1].x4_outPoint += 4;
|
||||
|
||||
if (linesC[0].x0_inPoint == linesC[0].x8_length)
|
||||
linesC[0].x0_inPoint = 0;
|
||||
|
||||
if (linesC[1].x0_inPoint == linesC[1].x8_length)
|
||||
linesC[1].x0_inPoint = 0;
|
||||
|
||||
if (linesC[0].x4_outPoint == linesC[0].x8_length)
|
||||
linesC[0].x4_outPoint = 0;
|
||||
|
||||
if (linesC[1].x4_outPoint == linesC[1].x8_length)
|
||||
linesC[1].x4_outPoint = 0;
|
||||
|
||||
/* All-pass filter stage */
|
||||
linesAP[0].xc_inputs[linesAP[0].x0_inPoint] =
|
||||
allPassCoef * linesAP[0].x10_lastInput + linesC[0].x10_lastInput + linesC[1].x10_lastInput;
|
||||
|
||||
linesAP[1].xc_inputs[linesAP[1].x0_inPoint] =
|
||||
allPassCoef * linesAP[1].x10_lastInput -
|
||||
(allPassCoef * linesAP[0].xc_inputs[linesAP[0].x0_inPoint] - linesAP[0].x10_lastInput);
|
||||
|
||||
float lowPass = -(allPassCoef * linesAP[1].xc_inputs[linesAP[1].x0_inPoint] - linesAP[1].x10_lastInput);
|
||||
linesAP[0].x0_inPoint += 4;
|
||||
linesAP[1].x0_inPoint += 4;
|
||||
|
||||
if (linesAP[0].x0_inPoint == linesAP[0].x8_length)
|
||||
linesAP[0].x0_inPoint = 0;
|
||||
|
||||
if (linesAP[1].x0_inPoint == linesAP[1].x8_length)
|
||||
linesAP[1].x0_inPoint = 0;
|
||||
|
||||
linesAP[0].x10_lastInput = linesAP[0].xc_inputs[linesAP[0].x4_outPoint];
|
||||
linesAP[0].x4_outPoint += 4;
|
||||
|
||||
linesAP[1].x10_lastInput = linesAP[1].xc_inputs[linesAP[1].x4_outPoint];
|
||||
linesAP[1].x4_outPoint += 4;
|
||||
|
||||
if (linesAP[0].x4_outPoint == linesAP[0].x8_length)
|
||||
linesAP[0].x4_outPoint = 0;
|
||||
|
||||
if (linesAP[1].x4_outPoint == linesAP[1].x8_length)
|
||||
linesAP[1].x4_outPoint = 0;
|
||||
|
||||
lpLastOut = damping * lpLastOut + lowPass * 0.3f;
|
||||
lineHP.xc_inputs[lineHP.x0_inPoint] = allPassCoef * lineHP.x10_lastInput + lpLastOut;
|
||||
float allPass = -(allPassCoef * lineHP.xc_inputs[lineHP.x0_inPoint] - lineHP.x10_lastInput);
|
||||
lineHP.x0_inPoint += 4;
|
||||
|
||||
lineHP.x10_lastInput = lineHP.xc_inputs[lineHP.x4_outPoint];
|
||||
lineHP.x4_outPoint += 4;
|
||||
|
||||
if (lineHP.x0_inPoint == lineHP.x8_length)
|
||||
lineHP.x0_inPoint = 0;
|
||||
|
||||
if (lineHP.x4_outPoint == lineHP.x8_length)
|
||||
lineHP.x4_outPoint = 0;
|
||||
|
||||
/* Mix out */
|
||||
audio[(s-1) * chanCount + c] = ClampFull<T>(dampWet * allPass + dampDry * sample);
|
||||
sample = audio[s * chanCount + c];
|
||||
}
|
||||
EffectReverb<T, 2, 3>::x130_x1b8_preDelayPtr[c] = preDelayPtr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EffectReverbHi<T>::_doCrosstalk(T* audio, float wet, float dry, int chanCount, int sampleCount)
|
||||
{
|
||||
for (int i=0 ; i<sampleCount ; ++i)
|
||||
{
|
||||
T* base = &audio[chanCount*i];
|
||||
float allWet = 0;
|
||||
for (int c=0 ; c<chanCount ; ++c)
|
||||
{
|
||||
allWet += base[c] * wet;
|
||||
base[c] *= dry;
|
||||
}
|
||||
for (int c=0 ; c<chanCount ; ++c)
|
||||
base[c] = ClampFull<T>(base[c] + allWet);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EffectReverbHi<T>::applyEffect(T* audio, size_t frameCount, const ChannelMap& chanMap)
|
||||
{
|
||||
if (EffectReverb<T, 2, 3>::m_dirty)
|
||||
_update();
|
||||
|
||||
for (size_t f=0 ; f<frameCount ; f+=160)
|
||||
{
|
||||
size_t blockSamples = std::min(size_t(160), frameCount - f);
|
||||
for (int i=0 ; i<chanMap.m_channelCount ; ++i)
|
||||
{
|
||||
if (i == 0 && x1a8_internalCrosstalk != 0.f)
|
||||
{
|
||||
float crossWet = x1a8_internalCrosstalk * 0.5;
|
||||
_doCrosstalk(audio, crossWet, 1.f - crossWet, chanMap.m_channelCount, blockSamples);
|
||||
}
|
||||
_handleReverb(audio, i, chanMap.m_channelCount, blockSamples);
|
||||
}
|
||||
audio += chanMap.m_channelCount * 160;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
EffectReverbHi<T>::EffectReverbHi(float coloration, float mix, float time,
|
||||
float damping, float preDelay, float crosstalk,
|
||||
double sampleRate)
|
||||
: EffectReverb<T, 2, 3>(coloration, mix, time, damping, preDelay, sampleRate),
|
||||
x1dc_crosstalk(clamp(0.f, crosstalk, 1.f))
|
||||
{
|
||||
_update();
|
||||
}
|
||||
|
||||
template class EffectReverbStd<int16_t>;
|
||||
template class EffectReverbStd<int32_t>;
|
||||
template class EffectReverbStd<float>;
|
||||
|
||||
template class EffectReverbHi<int16_t>;
|
||||
template class EffectReverbHi<int32_t>;
|
||||
template class EffectReverbHi<float>;
|
||||
|
||||
}
|
|
@ -366,6 +366,21 @@ void Voice::startSample(int16_t sampId, int32_t offset)
|
|||
m_lastSamplePos = m_curSample->first.m_loopLengthSamples ?
|
||||
(m_curSample->first.m_loopStartSample + m_curSample->first.m_loopLengthSamples) :
|
||||
m_curSample->first.m_numSamples;
|
||||
_checkSamplePos();
|
||||
|
||||
/* Seek DSPADPCM state if needed */
|
||||
if (m_curSamplePos)
|
||||
{
|
||||
uint32_t block = m_curSamplePos / 14;
|
||||
uint32_t rem = m_curSamplePos % 14;
|
||||
for (uint32_t b = 0 ; b < block ; ++b)
|
||||
DSPDecompressFrameStateOnly(m_curSampleData + 8 * b, m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, 14);
|
||||
|
||||
if (rem)
|
||||
DSPDecompressFrameStateOnly(m_curSampleData + 8 * block, m_curSample->second.m_coefs,
|
||||
&m_prev1, &m_prev2, rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
93
lib/dsp.c
93
lib/dsp.c
|
@ -2,36 +2,6 @@
|
|||
|
||||
static const int32_t NibbleToInt[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
|
||||
|
||||
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample)
|
||||
{
|
||||
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||
int32_t factor1 = coefs[cIdx][0];
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
sampleData <<= 11;
|
||||
sampleData += 1024;
|
||||
sampleData +=
|
||||
factor1 * ((int32_t)(*prev1)) +
|
||||
factor2 * ((int32_t)(*prev2));
|
||||
sampleData >>= 11;
|
||||
sampleData = DSPSampClamp(sampleData);
|
||||
*out++ = sampleData;
|
||||
*prev2 = *prev1;
|
||||
*prev1 = sampleData;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned DSPDecompressFrame(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample)
|
||||
|
@ -73,7 +43,7 @@ unsigned DSPDecompressFrameStereoStride(int16_t* out, const uint8_t* in,
|
|||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int sampleData = (s&1)?
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
|
@ -103,7 +73,7 @@ unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
|||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int sampleData = (s&1)?
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
|
@ -122,3 +92,62 @@ unsigned DSPDecompressFrameStereoDupe(int16_t* out, const uint8_t* in,
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned DSPDecompressFrameRanged(int16_t* out, const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample)
|
||||
{
|
||||
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||
int32_t factor1 = coefs[cIdx][0];
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
sampleData <<= 11;
|
||||
sampleData += 1024;
|
||||
sampleData +=
|
||||
factor1 * ((int32_t)(*prev1)) +
|
||||
factor2 * ((int32_t)(*prev2));
|
||||
sampleData >>= 11;
|
||||
sampleData = DSPSampClamp(sampleData);
|
||||
*out++ = sampleData;
|
||||
*prev2 = *prev1;
|
||||
*prev1 = sampleData;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample)
|
||||
{
|
||||
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||
int32_t factor1 = coefs[cIdx][0];
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (int s=0 ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
sampleData <<= 11;
|
||||
sampleData += 1024;
|
||||
sampleData +=
|
||||
factor1 * ((int32_t)(*prev1)) +
|
||||
factor2 * ((int32_t)(*prev2));
|
||||
sampleData >>= 11;
|
||||
sampleData = DSPSampClamp(sampleData);
|
||||
*prev2 = *prev1;
|
||||
*prev1 = sampleData;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue