parent
699abca893
commit
8801c8f122
@ -0,0 +1,22 @@ |
|||||||
|
#ifndef _BASIC_DSPUTILS_H_ |
||||||
|
#define _BASIC_DSPUTILS_H_ |
||||||
|
|
||||||
|
#include <arm_math.h> |
||||||
|
|
||||||
|
static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix); |
||||||
|
static inline void mix_pwr(float32_t mix, float32_t *wetMix, float32_t *dryMix) |
||||||
|
{ |
||||||
|
// Calculate mix parameters
|
||||||
|
// A cheap mostly energy constant crossfade from SignalSmith Blog
|
||||||
|
// https://signalsmith-audio.co.uk/writing/2021/cheap-energy-crossfade/
|
||||||
|
float32_t x2 = 1.0f - mix; |
||||||
|
float32_t A = mix*x2; |
||||||
|
float32_t B = A * (1.0f + 1.4186f * A); |
||||||
|
float32_t C = B + mix; |
||||||
|
float32_t D = B + x2; |
||||||
|
|
||||||
|
*wetMix = C * C; |
||||||
|
*dryMix = D * D; |
||||||
|
} |
||||||
|
|
||||||
|
#endif // _BASIC_DSPUTILS_H_
|
@ -0,0 +1,63 @@ |
|||||||
|
/*
|
||||||
|
* AudioEffectGain_F32 |
||||||
|
* |
||||||
|
* Created: Chip Audette, November 2016 |
||||||
|
* Purpose; Apply digital gain to the audio data. Assumes floating-point data. |
||||||
|
* |
||||||
|
* This processes a single stream fo audio data (ie, it is mono) |
||||||
|
* |
||||||
|
* MIT License. use at your own risk. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _AudioEffectGainStereo_F32_h |
||||||
|
#define _AudioEffectGainStereo_F32_h |
||||||
|
|
||||||
|
#include <arm_math.h> //ARM DSP extensions. for speed! |
||||||
|
#include <AudioStream_F32.h> |
||||||
|
|
||||||
|
class AudioEffectGainStereo_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
// constructor
|
||||||
|
AudioEffectGainStereo_F32(void) : AudioStream_F32(2, inputQueueArray_f32){}; |
||||||
|
AudioEffectGainStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray_f32){}; |
||||||
|
|
||||||
|
void update(void) |
||||||
|
{ |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockL) |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
if (blockR) |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
arm_scale_f32(blockL->data, gain, blockL->data, blockL->length); // use ARM DSP for speed!
|
||||||
|
arm_scale_f32(blockR->data, gain, blockR->data, blockR->length); |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
||||||
|
|
||||||
|
// methods to set parameters of this module
|
||||||
|
void setGain(float g) { gain = g; } |
||||||
|
void setGain_dB(float gain_dB) |
||||||
|
{ |
||||||
|
float gain = pow(10.0, gain_dB / 20.0); |
||||||
|
setGain(gain); |
||||||
|
} |
||||||
|
|
||||||
|
// methods to return information about this module
|
||||||
|
float getGain(void) { return gain; } |
||||||
|
float getGain_dB(void) { return 20.0 * log10(gain); } |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; // memory pointer for the input to this module
|
||||||
|
float gain = 1.0f; // default value
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,174 @@ |
|||||||
|
/*
|
||||||
|
* AudioEffectNoiseGate_F32 |
||||||
|
* |
||||||
|
* Created: Max Huster, Feb 2021 |
||||||
|
* Purpose: This module mutes the Audio completly, when it's below a given threshold. |
||||||
|
* |
||||||
|
* This processes a single stream fo audio data (ie, it is mono) |
||||||
|
* |
||||||
|
* MIT License. use at your own risk. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _AudioEffectNoiseGateStereo_F32_h |
||||||
|
#define _AudioEffectNoiseGateStereo_F32_h |
||||||
|
|
||||||
|
#include <arm_math.h> //ARM DSP extensions. for speed! |
||||||
|
#include <AudioStream_F32.h> |
||||||
|
|
||||||
|
class AudioEffectNoiseGateStereo_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
// constructor
|
||||||
|
AudioEffectNoiseGateStereo_F32(void) : AudioStream_F32(4, inputQueueArray_f32){}; |
||||||
|
AudioEffectNoiseGateStereo_F32(const AudioSettings_F32 &settings) : AudioStream_F32(4, inputQueueArray_f32){}; |
||||||
|
|
||||||
|
void update(void) |
||||||
|
{ |
||||||
|
audio_block_f32_t *blockL, *blockR, *blockSideChL, *blockSideChR, *blockSideCh, *blockGain; |
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
blockSideChL = AudioStream_F32::receiveReadOnly_f32(2); // side chain inputL
|
||||||
|
blockSideChR = AudioStream_F32::receiveReadOnly_f32(3); // side chain inputR
|
||||||
|
blockSideCh = AudioStream_F32::allocate_f32(); // allocate new block for summed L+R
|
||||||
|
blockGain = AudioStream_F32::allocate_f32(); // create a new audio block for the gain
|
||||||
|
|
||||||
|
|
||||||
|
if (!blockL || !blockR || !blockSideChL || !blockSideChR || !blockSideCh || !blockGain)
|
||||||
|
{ |
||||||
|
if (blockSideChL) AudioStream_F32::release(blockSideChL); |
||||||
|
if (blockSideChR) AudioStream_F32::release(blockSideChR); |
||||||
|
if (blockSideCh) AudioStream_F32::release(blockSideCh); |
||||||
|
if (blockGain) AudioStream_F32::release(blockGain);
|
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return;
|
||||||
|
}
|
||||||
|
// sum L + R
|
||||||
|
arm_add_f32(blockSideChL->data, blockSideChR->data, blockSideCh->data, blockSideCh->length); |
||||||
|
arm_scale_f32(blockSideCh->data, 0.5f, blockSideCh->data, blockSideCh->length); // divide by 2
|
||||||
|
|
||||||
|
// calculate the desired gain
|
||||||
|
calcGain(blockSideCh, blockGain); |
||||||
|
// smooth the "blocky" gain block
|
||||||
|
calcSmoothedGain(blockGain); |
||||||
|
|
||||||
|
// multiply it to the input singal
|
||||||
|
arm_mult_f32(blockGain->data, blockL->data, blockL->data, blockL->length); |
||||||
|
arm_mult_f32(blockGain->data, blockR->data, blockR->data, blockR->length); |
||||||
|
// release gainBlock
|
||||||
|
AudioStream_F32::release(blockGain); |
||||||
|
AudioStream_F32::release(blockSideCh); |
||||||
|
AudioStream_F32::release(blockSideChL); |
||||||
|
AudioStream_F32::release(blockSideChR); |
||||||
|
|
||||||
|
// transmit the block and be done
|
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
||||||
|
|
||||||
|
void setThreshold(float dbfs) |
||||||
|
{ |
||||||
|
// convert dbFS to linear value to comapre against later
|
||||||
|
linearThreshold = pow10f(dbfs / 20.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void setOpeningTime(float timeInSeconds) |
||||||
|
{ |
||||||
|
openingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE)); |
||||||
|
} |
||||||
|
|
||||||
|
void setClosingTime(float timeInSeconds) |
||||||
|
{ |
||||||
|
closingTimeConst = expf(-1.0f / (timeInSeconds * AUDIO_SAMPLE_RATE)); |
||||||
|
} |
||||||
|
|
||||||
|
void setHoldTime(float timeInSeconds) |
||||||
|
{ |
||||||
|
holdTimeNumSamples = timeInSeconds * AUDIO_SAMPLE_RATE; |
||||||
|
} |
||||||
|
|
||||||
|
bool infoIsOpen() |
||||||
|
{ |
||||||
|
return _isOpenDisplay; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
float32_t linearThreshold; |
||||||
|
float32_t prev_gain_dB = 0; |
||||||
|
float32_t openingTimeConst, closingTimeConst; |
||||||
|
float lastGainBlockValue = 0; |
||||||
|
int32_t counter, holdTimeNumSamples = 0; |
||||||
|
audio_block_f32_t *inputQueueArray_f32[4]; |
||||||
|
bool falling = false; |
||||||
|
|
||||||
|
bool _isOpen = false; |
||||||
|
bool _isOpenDisplay = false; |
||||||
|
bool _extSideChain = true; |
||||||
|
|
||||||
|
void calcGain(audio_block_f32_t *input, audio_block_f32_t *gainBlock) |
||||||
|
{ |
||||||
|
_isOpen = false; |
||||||
|
for (int i = 0; i < input->length; i++) |
||||||
|
{ |
||||||
|
// take absolute value and compare it to the set threshold
|
||||||
|
bool isAboveThres = abs(input->data[i]) > linearThreshold; |
||||||
|
_isOpen |= isAboveThres; |
||||||
|
// if above the threshold set volume to 1 otherwise to 0, we did not account for holdtime
|
||||||
|
gainBlock->data[i] = isAboveThres ? 1 : 0; |
||||||
|
|
||||||
|
// if we are falling and are above the threshold, the level is not falling
|
||||||
|
if (falling & isAboveThres) |
||||||
|
{ |
||||||
|
falling = false; |
||||||
|
} |
||||||
|
// if we have a falling signal
|
||||||
|
if (falling || lastGainBlockValue > gainBlock->data[i]) |
||||||
|
{ |
||||||
|
// check whether the hold time is not reached
|
||||||
|
if (counter < holdTimeNumSamples) |
||||||
|
{ |
||||||
|
// signal is (still) falling
|
||||||
|
falling = true; |
||||||
|
counter++; |
||||||
|
gainBlock->data[i] = 1.0f; |
||||||
|
} |
||||||
|
// otherwise the signal is already muted due to the line: "gainBlock->data[i] = isAboveThres ? 1 : 0;"
|
||||||
|
} |
||||||
|
// note the last gain value, so we can compare it if the signal is falling in the next sample
|
||||||
|
lastGainBlockValue = gainBlock->data[i]; |
||||||
|
} |
||||||
|
// note the display value
|
||||||
|
_isOpenDisplay = _isOpen; |
||||||
|
}; |
||||||
|
|
||||||
|
// this method applies the "opening" and "closing" constants to smooth the
|
||||||
|
// target gain level through time.
|
||||||
|
void calcSmoothedGain(audio_block_f32_t *gain_block) |
||||||
|
{ |
||||||
|
float32_t gain; |
||||||
|
float32_t one_minus_opening_const = 1.0f - openingTimeConst; |
||||||
|
float32_t one_minus_closing_const = 1.0f - closingTimeConst; |
||||||
|
for (int i = 0; i < gain_block->length; i++) |
||||||
|
{ |
||||||
|
gain = gain_block->data[i]; |
||||||
|
|
||||||
|
// smooth the gain using the opening or closing constants
|
||||||
|
if (gain > prev_gain_dB) |
||||||
|
{ // are we in the opening phase?
|
||||||
|
gain_block->data[i] = openingTimeConst * prev_gain_dB + one_minus_opening_const * gain; |
||||||
|
} |
||||||
|
else |
||||||
|
{ // or, we're in the closing phase
|
||||||
|
gain_block->data[i] = closingTimeConst * prev_gain_dB + one_minus_closing_const * gain; |
||||||
|
} |
||||||
|
|
||||||
|
// save value for the next time through this loop
|
||||||
|
prev_gain_dB = gain_block->data[i]; |
||||||
|
} |
||||||
|
return; // the output here is gain_block
|
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,311 @@ |
|||||||
|
#include "effect_reverbsc_F32.h" |
||||||
|
|
||||||
|
|
||||||
|
#define REVERBSC_DLYBUF_SIZE 98936 |
||||||
|
#define DELAYPOS_SHIFT 28 |
||||||
|
#define DELAYPOS_SCALE 0x10000000 |
||||||
|
#define DELAYPOS_MASK 0x0FFFFFFF |
||||||
|
#ifndef M_PI |
||||||
|
#define M_PI 3.14159265358979323846 /* pi */ |
||||||
|
#endif |
||||||
|
|
||||||
|
/* kReverbParams[n][0] = delay time (in seconds) */ |
||||||
|
/* kReverbParams[n][1] = random variation in delay time (in seconds) */ |
||||||
|
/* kReverbParams[n][2] = random variation frequency (in 1/sec) */ |
||||||
|
/* kReverbParams[n][3] = random seed (0 - 32767) */ |
||||||
|
|
||||||
|
static const float32_t kReverbParams[8][4] = |
||||||
|
{ |
||||||
|
{(2473.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0010f, 3.100f, 1966.0f}, |
||||||
|
{(2767.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0011f, 3.500f, 29491.0f}, |
||||||
|
{(3217.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0017f, 1.110f, 22937.0f}, |
||||||
|
{(3557.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0006f, 3.973f, 9830.0f}, |
||||||
|
{(3907.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0010f, 2.341f, 20643.0f}, |
||||||
|
{(4127.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0011f, 1.897f, 22937.0f}, |
||||||
|
{(2143.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0017f, 0.891f, 29491.0f}, |
||||||
|
{(1933.0f / AUDIO_SAMPLE_RATE_EXACT), 0.0006f, 3.221f, 14417.0f} |
||||||
|
}; |
||||||
|
static int DelayLineMaxSamples(float32_t sr, float32_t i_pitch_mod, int n); |
||||||
|
static int DelayLineBytesAlloc(float32_t sr, float32_t i_pitch_mod, int n); |
||||||
|
static const float32_t kOutputGain = 0.35f; |
||||||
|
static const float32_t kJpScale = 0.25f; |
||||||
|
|
||||||
|
AudioEffectReverbSc_F32::AudioEffectReverbSc_F32(bool use_psram) : AudioStream_F32(2, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
sample_rate_ = AUDIO_SAMPLE_RATE_EXACT; |
||||||
|
feedback_ = 0.7f; |
||||||
|
lpfreq_ = 10000; |
||||||
|
i_pitch_mod_ = 1; |
||||||
|
damp_fact_ = 0.195847f; // ~16kHz
|
||||||
|
|
||||||
|
int i, n_bytes = 0; |
||||||
|
n_bytes = 0; |
||||||
|
if (use_psram) aux_ = (float32_t *) extmem_malloc(REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); |
||||||
|
else
|
||||||
|
{ |
||||||
|
aux_ = (float32_t *) malloc(REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); |
||||||
|
flags.memsetup_done = 1;
|
||||||
|
} |
||||||
|
if (!aux_) return; |
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) |
||||||
|
{ |
||||||
|
if (n_bytes > REVERBSC_DLYBUF_SIZE) |
||||||
|
return; |
||||||
|
delay_lines_[i].buf = (aux_) + n_bytes; |
||||||
|
InitDelayLine(&delay_lines_[i], i); |
||||||
|
n_bytes += DelayLineBytesAlloc(AUDIO_SAMPLE_RATE_EXACT, 1, i); |
||||||
|
} |
||||||
|
mix(0.5f); |
||||||
|
flags.bypass = 0; |
||||||
|
flags.freeze = 0; |
||||||
|
initialised = true; |
||||||
|
} |
||||||
|
|
||||||
|
static int DelayLineMaxSamples(float32_t sr, float32_t i_pitch_mod, int n) |
||||||
|
{ |
||||||
|
float32_t max_del; |
||||||
|
|
||||||
|
max_del = kReverbParams[n][0]; |
||||||
|
max_del += (kReverbParams[n][1] * (float32_t)i_pitch_mod * 1.125); |
||||||
|
return (int)(max_del * sr + 16.5); |
||||||
|
} |
||||||
|
|
||||||
|
static int DelayLineBytesAlloc(float32_t sr, float32_t i_pitch_mod, int n) |
||||||
|
{ |
||||||
|
int n_bytes = 0; |
||||||
|
|
||||||
|
n_bytes += (DelayLineMaxSamples(sr, i_pitch_mod, n) * (int)sizeof(float32_t)); |
||||||
|
return n_bytes; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectReverbSc_F32::NextRandomLineseg(ReverbScDl_t *lp, int n) |
||||||
|
{ |
||||||
|
float32_t prv_del, nxt_del, phs_inc_val; |
||||||
|
|
||||||
|
/* update random seed */ |
||||||
|
if (lp->seed_val < 0) |
||||||
|
lp->seed_val += 0x10000; |
||||||
|
lp->seed_val = (lp->seed_val * 15625 + 1) & 0xFFFF; |
||||||
|
if (lp->seed_val >= 0x8000) |
||||||
|
lp->seed_val -= 0x10000; |
||||||
|
/* length of next segment in samples */ |
||||||
|
lp->rand_line_cnt = (int)((sample_rate_ / kReverbParams[n][2]) + 0.5f); |
||||||
|
prv_del = (float32_t)lp->write_pos; |
||||||
|
prv_del -= ((float32_t)lp->read_pos + ((float32_t)lp->read_pos_frac / (float32_t)DELAYPOS_SCALE)); |
||||||
|
while (prv_del < 0.0) |
||||||
|
prv_del += lp->buffer_size; |
||||||
|
prv_del = prv_del / sample_rate_; /* previous delay time in seconds */ |
||||||
|
nxt_del = (float32_t)lp->seed_val * kReverbParams[n][1] / 32768.0f; |
||||||
|
/* next delay time in seconds */ |
||||||
|
nxt_del = kReverbParams[n][0] + (nxt_del * (float32_t)i_pitch_mod_); |
||||||
|
/* calculate phase increment per sample */ |
||||||
|
phs_inc_val = (prv_del - nxt_del) / (float32_t)lp->rand_line_cnt; |
||||||
|
phs_inc_val = phs_inc_val * sample_rate_ + 1.0; |
||||||
|
lp->read_pos_frac_inc = (int)(phs_inc_val * DELAYPOS_SCALE + 0.5f); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectReverbSc_F32::InitDelayLine(ReverbScDl_t *lp, int n) |
||||||
|
{ |
||||||
|
float32_t read_pos; |
||||||
|
|
||||||
|
/* calculate length of delay line */ |
||||||
|
lp->buffer_size = DelayLineMaxSamples(sample_rate_, 1, n); |
||||||
|
lp->dummy = 0; |
||||||
|
lp->write_pos = 0; |
||||||
|
/* set random seed */ |
||||||
|
lp->seed_val = (int)(kReverbParams[n][3] + 0.5f); |
||||||
|
/* set initial delay time */ |
||||||
|
read_pos = (float32_t)lp->seed_val * kReverbParams[n][1] / 32768.0f; |
||||||
|
read_pos = kReverbParams[n][0] + (read_pos * (float32_t)i_pitch_mod_); |
||||||
|
read_pos = (float32_t)lp->buffer_size - (read_pos * sample_rate_); |
||||||
|
lp->read_pos = (int)read_pos; |
||||||
|
read_pos = (read_pos - (float32_t)lp->read_pos) * (float32_t)DELAYPOS_SCALE; |
||||||
|
lp->read_pos_frac = (int)(read_pos + 0.5); |
||||||
|
/* initialise first random line segment */ |
||||||
|
NextRandomLineseg(lp, n); |
||||||
|
/* clear delay line to zero */ |
||||||
|
lp->filter_state = 0.0f; |
||||||
|
for (int i = 0; i < lp->buffer_size; i++) |
||||||
|
{ |
||||||
|
lp->buf[i] = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectReverbSc_F32::update() |
||||||
|
{ |
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
if (!initialised) return; |
||||||
|
if ( !flags.memsetup_done) |
||||||
|
{ |
||||||
|
memset(aux_, 0, REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); |
||||||
|
arm_dcache_flush_delete(aux_, REVERBSC_DLYBUF_SIZE*sizeof(float32_t)); |
||||||
|
flags.memsetup_done = 1; |
||||||
|
return; |
||||||
|
} |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
int16_t i; |
||||||
|
float32_t a_in_l, a_in_r, a_out_l, a_out_r, dryL, dryR; |
||||||
|
float32_t vm1, v0, v1, v2, am1, a0, a1, a2, frac; |
||||||
|
ReverbScDl_t *lp; |
||||||
|
int read_pos; |
||||||
|
uint32_t n; |
||||||
|
int buffer_size; /* Local copy */ |
||||||
|
float32_t damp_fact = damp_fact_; |
||||||
|
|
||||||
|
if (flags.bypass) |
||||||
|
{ |
||||||
|
if (dry_gain > 0.0f) // if dry/wet mixer is used
|
||||||
|
{ |
||||||
|
blockL = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveReadOnly_f32(1); |
||||||
|
if (!blockL || !blockR)
|
||||||
|
{ |
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
||||||
|
blockL = AudioStream_F32::allocate_f32(); |
||||||
|
if (!blockL) return; |
||||||
|
arm_fill_f32(0.0f, blockL->data, blockL->length); |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockL, 1); |
||||||
|
AudioStream_F32::release(blockL);
|
||||||
|
return; |
||||||
|
} |
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockL) |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
if (blockR) |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (i = 0; i < blockL->length; i++) |
||||||
|
{ |
||||||
|
/* calculate "resultant junction pressure" and mix to input signals */ |
||||||
|
a_in_l = a_out_l = a_out_r = 0.0f; |
||||||
|
dryL = blockL->data[i] * input_gain; |
||||||
|
dryR = blockR->data[i] * input_gain; |
||||||
|
|
||||||
|
for (n = 0; n < 8; n++) |
||||||
|
{ |
||||||
|
a_in_l += delay_lines_[n].filter_state; |
||||||
|
} |
||||||
|
a_in_l *= kJpScale; |
||||||
|
a_in_r = a_in_l + dryR; |
||||||
|
a_in_l = a_in_l + dryL; |
||||||
|
|
||||||
|
/* loop through all delay lines */ |
||||||
|
for (n = 0; n < 8; n++) |
||||||
|
{ |
||||||
|
lp = &delay_lines_[n]; |
||||||
|
buffer_size = lp->buffer_size; |
||||||
|
|
||||||
|
/* send input signal and feedback to delay line */ |
||||||
|
lp->buf[lp->write_pos] = (float32_t)((n & 1 ? a_in_r : a_in_l) - lp->filter_state); |
||||||
|
if (++lp->write_pos >= buffer_size) lp->write_pos -= buffer_size; |
||||||
|
|
||||||
|
/* read from delay line with cubic interpolation */ |
||||||
|
if (lp->read_pos_frac >= DELAYPOS_SCALE) |
||||||
|
{ |
||||||
|
lp->read_pos += (lp->read_pos_frac >> DELAYPOS_SHIFT); |
||||||
|
lp->read_pos_frac &= DELAYPOS_MASK; |
||||||
|
} |
||||||
|
if (lp->read_pos >= buffer_size) |
||||||
|
lp->read_pos -= buffer_size; |
||||||
|
read_pos = lp->read_pos; |
||||||
|
frac = (float32_t)lp->read_pos_frac * (1.0f / (float32_t)DELAYPOS_SCALE); |
||||||
|
|
||||||
|
/* calculate interpolation coefficients */ |
||||||
|
a2 = frac * frac; |
||||||
|
a2 *= (1.0f / 6.0f); |
||||||
|
a1 = frac; |
||||||
|
a1 += 1.0f; |
||||||
|
a1 *= 0.5f; |
||||||
|
am1 = a1 - 1.0f; |
||||||
|
a0 = 3.0f * a2; |
||||||
|
a1 -= a0; |
||||||
|
am1 -= a2; |
||||||
|
a0 -= frac; |
||||||
|
|
||||||
|
/* read four samples for interpolation */ |
||||||
|
if (read_pos > 0 && read_pos < (buffer_size - 2)) |
||||||
|
{ |
||||||
|
vm1 = (float32_t)(lp->buf[read_pos - 1]); |
||||||
|
v0 = (float32_t)(lp->buf[read_pos]); |
||||||
|
v1 = (float32_t)(lp->buf[read_pos + 1]); |
||||||
|
v2 = (float32_t)(lp->buf[read_pos + 2]); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/* at buffer wrap-around, need to check index */ |
||||||
|
if (--read_pos < 0) read_pos += buffer_size; |
||||||
|
vm1 = (float32_t)lp->buf[read_pos]; |
||||||
|
if (++read_pos >= buffer_size) read_pos -= buffer_size; |
||||||
|
v0 = (float32_t)lp->buf[read_pos]; |
||||||
|
if (++read_pos >= buffer_size) read_pos -= buffer_size; |
||||||
|
v1 = (float32_t)lp->buf[read_pos]; |
||||||
|
if (++read_pos >= buffer_size) read_pos -= buffer_size; |
||||||
|
v2 = (float32_t)lp->buf[read_pos]; |
||||||
|
} |
||||||
|
v0 = (am1 * vm1 + a0 * v0 + a1 * v1 + a2 * v2) * frac + v0; |
||||||
|
|
||||||
|
/* update buffer read position */ |
||||||
|
lp->read_pos_frac += lp->read_pos_frac_inc; |
||||||
|
|
||||||
|
// apply filter
|
||||||
|
v0 = (lp->filter_state - v0) * damp_fact + v0; |
||||||
|
|
||||||
|
/* mix to output */ |
||||||
|
if (n & 1) a_out_r += v0; |
||||||
|
else a_out_l += v0; |
||||||
|
|
||||||
|
v0 *= (float32_t)feedback_; // apply feedback
|
||||||
|
lp->filter_state = v0; // save filter - this will make the reverb volume constant
|
||||||
|
|
||||||
|
/* start next random line segment if current one has reached endpoint */ |
||||||
|
if (--(lp->rand_line_cnt) <= 0) |
||||||
|
{ |
||||||
|
NextRandomLineseg(lp, n); |
||||||
|
} |
||||||
|
} |
||||||
|
blockL->data[i] = a_out_l * wet_gain + blockL->data[i] * dry_gain; |
||||||
|
blockR->data[i] = a_out_r * wet_gain + blockR->data[i] * dry_gain; |
||||||
|
} // end block processing
|
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR);
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectReverbSc_F32::freeze(bool state) |
||||||
|
{ |
||||||
|
flags.freeze = state; |
||||||
|
if (state) |
||||||
|
{ |
||||||
|
feedback_tmp = feedback_; // store the settings
|
||||||
|
damp_fact_tmp = damp_fact_; |
||||||
|
input_gain_tmp = input_gain; |
||||||
|
__disable_irq(); |
||||||
|
feedback_ = 1.0f; // infinite reverb
|
||||||
|
input_gain = freeze_ingain; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
feedback_ = feedback_tmp; |
||||||
|
damp_fact_ = damp_fact_tmp; |
||||||
|
input_gain = input_gain_tmp; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,149 @@ |
|||||||
|
/*
|
||||||
|
* ReverbSC |
||||||
|
* 8 delay line stereo FDN reverb, with feedback matrix based upon physical modeling
|
||||||
|
* scattering junction of 8 lossless waveguides of equal characteristic impedance.
|
||||||
|
* Based on Csound orchestra version by Sean Costello. |
||||||
|
*
|
||||||
|
* Original Author(s): Sean Costello, Istvan Varga |
||||||
|
* Year: 1999, 2005 |
||||||
|
* Ported to soundpipe by: Paul Batchelor |
||||||
|
*
|
||||||
|
* Ported to Teensy4 and OpenAudio_ArduinoLibrary:
|
||||||
|
* 01.2024 Piotr Zapart www.hexefx.com
|
||||||
|
*
|
||||||
|
* Fixes, changes: |
||||||
|
* - In the original code the reverb level is affected by the feedback control, fixed |
||||||
|
* - Optional
|
||||||
|
*
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _EFFECT_REVERBSC_F32_H_ |
||||||
|
#define _EFFECT_REVERBSC_F32_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "AudioStream.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
#include "basic_DSPutils.h" |
||||||
|
|
||||||
|
class AudioEffectReverbSc_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectReverbSc_F32(bool use_psram = false); |
||||||
|
~AudioEffectReverbSc_F32(){}; |
||||||
|
virtual void update(); |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
int write_pos; /**< write position */ |
||||||
|
int buffer_size; /**< buffer size */ |
||||||
|
int read_pos; /**< read position */ |
||||||
|
int read_pos_frac; /**< fractional component of read pos */ |
||||||
|
int read_pos_frac_inc; /**< increment for fractional */ |
||||||
|
int dummy; /**< dummy var */ |
||||||
|
int seed_val; /**< randseed */ |
||||||
|
int rand_line_cnt; /**< number of random lines */ |
||||||
|
float32_t filter_state; /**< state of filter */ |
||||||
|
float32_t *buf; /**< buffer ptr */ |
||||||
|
} ReverbScDl_t; |
||||||
|
|
||||||
|
inline void feedback(const float32_t &fb)
|
||||||
|
{ |
||||||
|
if (flags.freeze) return; |
||||||
|
float32_t inGain; |
||||||
|
float32_t feedb = 2.0f * fb - fb*fb; |
||||||
|
feedb = map(feedb, 0.0f, 1.0f, 0.1f, feedb_max); |
||||||
|
feedback_tmp = feedb; |
||||||
|
inGain = map(feedb, 0.1f, feedb_max, 0.5f, 0.2f); |
||||||
|
__disable_irq(); |
||||||
|
input_gain = inGain; |
||||||
|
feedback_ = feedb; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
inline void lowpass(float32_t val) |
||||||
|
{ |
||||||
|
if (flags.freeze) return; |
||||||
|
val = constrain(val, 0.0f, 0.96f); |
||||||
|
val = val*val*val; |
||||||
|
if (damp_fact_ != val) |
||||||
|
{ |
||||||
|
damp_fact_tmp = val; |
||||||
|
__disable_irq(); |
||||||
|
damp_fact_ = val; |
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
} |
||||||
|
|
||||||
|
void mix(float32_t mix) |
||||||
|
{ |
||||||
|
mix = constrain(mix, 0.0f, 1.0f); |
||||||
|
float dry, wet; |
||||||
|
mix_pwr(mix, &wet, &dry); |
||||||
|
|
||||||
|
__disable_irq(); |
||||||
|
wet_gain = wet; |
||||||
|
dry_gain = dry; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void wet_level(float32_t wet) |
||||||
|
{ |
||||||
|
wet_gain = constrain(wet, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void dry_level(float32_t dry) |
||||||
|
{ |
||||||
|
dry_gain = constrain(dry, 0.0f, 1.0f); |
||||||
|
}
|
||||||
|
void freeze(bool state); |
||||||
|
bool freeze_tgl() {flags.freeze ^= 1; freeze(flags.freeze); return flags.freeze;} |
||||||
|
bool freeze_get() {return flags.freeze;} |
||||||
|
|
||||||
|
bool bypass_get(void) {return flags.bypass;} |
||||||
|
void bypass_set(bool state)
|
||||||
|
{ |
||||||
|
flags.bypass = state; |
||||||
|
if (state) freeze(false); // disable freeze in bypass mode
|
||||||
|
} |
||||||
|
bool bypass_tgl(void)
|
||||||
|
{ |
||||||
|
flags.bypass ^= 1;
|
||||||
|
if (flags.bypass) freeze(false); // disable freeze in bypass mode
|
||||||
|
return flags.bypass; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t getBfAddr() |
||||||
|
{ |
||||||
|
float32_t *addr = aux_; |
||||||
|
return (uint32_t)addr; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
struct flags_t |
||||||
|
{ |
||||||
|
unsigned bypass: 1; |
||||||
|
unsigned freeze: 1; |
||||||
|
unsigned memsetup_done: 1; |
||||||
|
}flags = {0, 0, 0}; |
||||||
|
|
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; |
||||||
|
void NextRandomLineseg(ReverbScDl_t *lp, int n); |
||||||
|
void InitDelayLine(ReverbScDl_t *lp, int n); |
||||||
|
float32_t feedback_, feedback_tmp; |
||||||
|
float32_t lpfreq_; |
||||||
|
float32_t i_pitch_mod_; |
||||||
|
float32_t sample_rate_; |
||||||
|
float32_t damp_fact_, damp_fact_tmp; |
||||||
|
bool initialised = false; |
||||||
|
ReverbScDl_t delay_lines_[8]; |
||||||
|
float32_t *aux_; // main delay line storage buffer, placed either in RAM2 or PSRAM
|
||||||
|
float32_t dry_gain = 0.5f; |
||||||
|
float32_t wet_gain = 0.5f; |
||||||
|
|
||||||
|
float32_t input_gain = 0.5f; |
||||||
|
float32_t input_gain_tmp = 0.5f; |
||||||
|
float32_t freeze_ingain = 0.05f; |
||||||
|
static constexpr float32_t feedb_max = 0.99f; |
||||||
|
}; |
||||||
|
#endif // _EFFECT_REVERBSC_H_
|
@ -0,0 +1,408 @@ |
|||||||
|
/* Stereo spring reverb for Teensy 4
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2024 by Piotr Zapart |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "effect_springreverb_F32.h" |
||||||
|
|
||||||
|
#define INP_ALLP_COEFF (0.6f) |
||||||
|
#define CHIRP_ALLP_COEFF (-0.6f) |
||||||
|
|
||||||
|
#define TREBLE_LOSS_FREQ (0.55f) |
||||||
|
#define BASS_LOSS_FREQ (0.36f) |
||||||
|
|
||||||
|
AudioEffectSpringReverb_F32::AudioEffectSpringReverb_F32() : AudioStream_F32(2, inputQueueArray) |
||||||
|
{ |
||||||
|
input_attn = 0.5f; |
||||||
|
rv_time_k = 0.8f; |
||||||
|
in_allp_k = INP_ALLP_COEFF; |
||||||
|
bool memOK = true; |
||||||
|
if(!sp_lp_allp1a.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp1b.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp1c.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp1d.init(&in_allp_k)) memOK = false; |
||||||
|
|
||||||
|
if(!sp_lp_allp2a.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp2b.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp2c.init(&in_allp_k)) memOK = false; |
||||||
|
if(!sp_lp_allp2d.init(&in_allp_k)) memOK = false;
|
||||||
|
if(!lp_dly1.init()) memOK = false; |
||||||
|
if(!lp_dly2.init()) memOK = false; |
||||||
|
// chirp allpass chain
|
||||||
|
sp_chrp_alp1_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); |
||||||
|
sp_chrp_alp2_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); |
||||||
|
sp_chrp_alp3_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); |
||||||
|
sp_chrp_alp4_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); |
||||||
|
if (!sp_chrp_alp1_buf) memOK = false; |
||||||
|
if (!sp_chrp_alp2_buf) memOK = false; |
||||||
|
if (!sp_chrp_alp3_buf) memOK = false; |
||||||
|
if (!sp_chrp_alp4_buf) memOK = false; |
||||||
|
|
||||||
|
memset(sp_chrp_alp1_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp2_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp3_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp4_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); |
||||||
|
|
||||||
|
in_BassCut_k = 0.0f; |
||||||
|
in_TrebleCut_k = 0.95f; |
||||||
|
lp_BassCut_k = 0.0f; |
||||||
|
lp_TrebleCut_k = 1.0f; |
||||||
|
flt_in.init(BASS_LOSS_FREQ, &in_BassCut_k, TREBLE_LOSS_FREQ, &in_TrebleCut_k); |
||||||
|
flt_lp1.init(BASS_LOSS_FREQ, &lp_BassCut_k, TREBLE_LOSS_FREQ, &lp_TrebleCut_k); |
||||||
|
flt_lp2.init(BASS_LOSS_FREQ, &lp_BassCut_k, TREBLE_LOSS_FREQ, &lp_TrebleCut_k); |
||||||
|
mix(0.5f); |
||||||
|
cleanup_done = true; |
||||||
|
if (memOK) initialized = true; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectSpringReverb_F32::update() |
||||||
|
{
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
int i, j; |
||||||
|
float32_t inL, inR, dryL, dryR; |
||||||
|
float32_t acc; |
||||||
|
float32_t lp_out1, lp_out2, mono_in, dry_in; |
||||||
|
float32_t rv_time; |
||||||
|
uint32_t allp_idx; |
||||||
|
uint32_t offset; |
||||||
|
float lfo_fr;
|
||||||
|
if (!initialized) return; |
||||||
|
if (bp) |
||||||
|
{ |
||||||
|
if (!cleanup_done) |
||||||
|
{ |
||||||
|
sp_lp_allp1a.reset(); |
||||||
|
sp_lp_allp1b.reset(); |
||||||
|
sp_lp_allp1c.reset(); |
||||||
|
sp_lp_allp1d.reset(); |
||||||
|
sp_lp_allp2a.reset(); |
||||||
|
sp_lp_allp2b.reset(); |
||||||
|
sp_lp_allp2c.reset(); |
||||||
|
sp_lp_allp2d.reset(); |
||||||
|
lp_dly1.reset(); |
||||||
|
lp_dly2.reset(); |
||||||
|
memset(sp_chrp_alp1_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp2_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp3_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); |
||||||
|
memset(sp_chrp_alp4_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float));
|
||||||
|
cleanup_done = true; |
||||||
|
} |
||||||
|
|
||||||
|
if (dry_gain > 0.0f) // if dry/wet mixer is used
|
||||||
|
{ |
||||||
|
blockL = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveReadOnly_f32(1); |
||||||
|
if (!blockL || !blockR)
|
||||||
|
{ |
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
||||||
|
blockL = AudioStream_F32::allocate_f32(); |
||||||
|
if (!blockL) return; |
||||||
|
arm_fill_f32(0.0f, blockL->data, blockL->length); |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockL, 1); |
||||||
|
AudioStream_F32::release(blockL);
|
||||||
|
return; |
||||||
|
} |
||||||
|
cleanup_done = false; |
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockL) |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
if (blockR) |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
rv_time = rv_time_k; |
||||||
|
for (i=0; i < blockL->length; i++)
|
||||||
|
{
|
||||||
|
lfo.update(); |
||||||
|
dryL = blockL->data[i]; |
||||||
|
dryR = blockR->data[i]; |
||||||
|
dry_in = (dryL + dryR) * input_attn; |
||||||
|
|
||||||
|
mono_in = flt_in.process(dry_in)* (1.0f + in_BassCut_k*-1.5f); // add highpass gain compaensation?
|
||||||
|
acc = lp_dly1.getTap(0) * rv_time; // get DLY1 output
|
||||||
|
lp_out1 = flt_lp1.process(acc); // filter it
|
||||||
|
|
||||||
|
acc = sp_lp_allp1a.process(lp_out1);
|
||||||
|
acc = sp_lp_allp1b.process(acc); |
||||||
|
acc = sp_lp_allp1c.process(acc); |
||||||
|
acc = sp_lp_allp1d.process(acc); |
||||||
|
acc = lp_dly2.process(acc + mono_in) * rv_time; |
||||||
|
lp_out2 = flt_lp2.process(acc); |
||||||
|
|
||||||
|
acc = sp_lp_allp2a.process(lp_out2);
|
||||||
|
acc = sp_lp_allp2b.process(acc); |
||||||
|
acc = sp_lp_allp2c.process(acc); |
||||||
|
acc = sp_lp_allp2d.process(acc); |
||||||
|
|
||||||
|
lp_dly1.write_toOffset(acc + mono_in, 0); |
||||||
|
lp_dly1.updateIndex(); |
||||||
|
|
||||||
|
inL = inR = (lp_out1 + lp_out2); |
||||||
|
|
||||||
|
j = 0; |
||||||
|
while(j < SPRVB_CHIRP_AMNT) |
||||||
|
{ |
||||||
|
// 1st 4 are left channel
|
||||||
|
allp_idx = j*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inL * chrp_allp_k[0]; |
||||||
|
sp_chrp_alp1_buf[allp_idx] = inL - chrp_allp_k[0] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp1_idx[j] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+1)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+1]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inL * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inL - chrp_allp_k[1] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp1_idx[j+1] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+1] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+2)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+2]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inL * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inL - chrp_allp_k[2] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp1_idx[j+2] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+2] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+3)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+3]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inL * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inL - chrp_allp_k[3] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp1_idx[j+3] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+3] = 0;
|
||||||
|
// channel R
|
||||||
|
allp_idx = (j+4)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+4]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inR * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inR - chrp_allp_k[3] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp1_idx[j+4] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+4] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+5)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+5]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inR * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inR - chrp_allp_k[2] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp1_idx[j+5] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+5] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+6)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+6]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inR * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inR - chrp_allp_k[1] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp1_idx[j+6] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+6] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+7)*SPRVB_CHIRP1_LEN + chrp_alp1_idx[j+7]; |
||||||
|
acc = sp_chrp_alp1_buf[allp_idx] + inR * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp1_buf[allp_idx] = inR - chrp_allp_k[0] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp1_idx[j+7] >= SPRVB_CHIRP1_LEN) chrp_alp1_idx[j+7] = 0;
|
||||||
|
|
||||||
|
j = j + 8; |
||||||
|
} |
||||||
|
j = 0; |
||||||
|
while(j < SPRVB_CHIRP_AMNT) |
||||||
|
{ // channel L
|
||||||
|
allp_idx = j*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inL * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inL - chrp_allp_k[0] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp2_idx[j] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+1)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+1]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inL * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inL - chrp_allp_k[1] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp2_idx[j+1] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+1] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+2)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+2]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inL * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inL - chrp_allp_k[2] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp2_idx[j+2] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+2] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+3)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+3]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inL * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inL - chrp_allp_k[3] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp2_idx[j+3] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+3] = 0;
|
||||||
|
// channel R
|
||||||
|
allp_idx = (j+4)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+4]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inR * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inR - chrp_allp_k[3] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp2_idx[j+4] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+4] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+5)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+5]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inR * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inR - chrp_allp_k[2] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp2_idx[j+5] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+5] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+6)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+6]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inR * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inR - chrp_allp_k[1] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp2_idx[j+6] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+6] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+7)*SPRVB_CHIRP2_LEN + chrp_alp2_idx[j+7]; |
||||||
|
acc = sp_chrp_alp2_buf[allp_idx] + inR * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp2_buf[allp_idx] = inR - chrp_allp_k[0] * acc; |
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp2_idx[j+7] >= SPRVB_CHIRP2_LEN) chrp_alp2_idx[j+7] = 0;
|
||||||
|
j = j + 8; |
||||||
|
} |
||||||
|
j = 0; |
||||||
|
while(j < SPRVB_CHIRP_AMNT) |
||||||
|
{ // channel L
|
||||||
|
allp_idx = j*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inL * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inL - chrp_allp_k[0] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp3_idx[j] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+1)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+1]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inL * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inL - chrp_allp_k[1] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp3_idx[j+1] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+1] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+2)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+2]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inL * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inL - chrp_allp_k[2] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp3_idx[j+2] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+2] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+3)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+3]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inL * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inL - chrp_allp_k[3] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp3_idx[j+3] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+3] = 0;
|
||||||
|
// channel R
|
||||||
|
allp_idx = (j+4)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+4]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inR * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inR - chrp_allp_k[3] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp3_idx[j+4] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+4] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+5)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+5]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inR * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inR - chrp_allp_k[2] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp3_idx[j+5] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+5] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+6)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+6]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inR * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inR - chrp_allp_k[1] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp3_idx[j+6] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+6] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+7)*SPRVB_CHIRP3_LEN + chrp_alp3_idx[j+7]; |
||||||
|
acc = sp_chrp_alp3_buf[allp_idx] + inR * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp3_buf[allp_idx] = inR - chrp_allp_k[0] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp3_idx[j+7] >= SPRVB_CHIRP3_LEN) chrp_alp3_idx[j+7] = 0;
|
||||||
|
j = j + 8; |
||||||
|
} |
||||||
|
j = 0; |
||||||
|
while(j < SPRVB_CHIRP_AMNT) |
||||||
|
{ // channel L
|
||||||
|
allp_idx = j*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inL * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inL - chrp_allp_k[0] * acc; |
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp4_idx[j] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+1)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+1]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inL * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inL - chrp_allp_k[1] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp4_idx[j+1] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+1] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+2)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+2]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inL * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inL - chrp_allp_k[1] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp4_idx[j+2] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+2] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+3)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+3]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inL * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inL - chrp_allp_k[3] * acc;
|
||||||
|
inL = acc; |
||||||
|
if (++chrp_alp4_idx[j+3] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+3] = 0;
|
||||||
|
// channel R
|
||||||
|
allp_idx = (j+4)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+4]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inR * chrp_allp_k[3];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inR - chrp_allp_k[3] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp4_idx[j+4] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+4] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+5)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+5]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inR * chrp_allp_k[2];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inR - chrp_allp_k[2] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp4_idx[j+5] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+5] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+6)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+6]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inR * chrp_allp_k[1];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inR - chrp_allp_k[1] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp4_idx[j+6] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+6] = 0;
|
||||||
|
|
||||||
|
allp_idx = (j+7)*SPRVB_CHIRP4_LEN + chrp_alp4_idx[j+7]; |
||||||
|
acc = sp_chrp_alp4_buf[allp_idx] + inR * chrp_allp_k[0];
|
||||||
|
sp_chrp_alp4_buf[allp_idx] = inR - chrp_allp_k[0] * acc;
|
||||||
|
inR = acc; |
||||||
|
if (++chrp_alp4_idx[j+7] >= SPRVB_CHIRP4_LEN) chrp_alp4_idx[j+7] = 0;
|
||||||
|
j = j + 8; |
||||||
|
} |
||||||
|
|
||||||
|
// modulate the allpass filters
|
||||||
|
lfo.get(BASIC_LFO_PHASE_0, &offset, &lfo_fr);
|
||||||
|
acc = sp_lp_allp1d.getTap(offset+1, lfo_fr); |
||||||
|
sp_lp_allp1d.write_toOffset(acc, (lfo_ampl<<1)+1); |
||||||
|
lfo.get(BASIC_LFO_PHASE_90, &offset, &lfo_fr);
|
||||||
|
acc = sp_lp_allp2d.getTap(offset+1, lfo_fr); |
||||||
|
sp_lp_allp2d.write_toOffset(acc, (lfo_ampl<<1)+1); |
||||||
|
|
||||||
|
blockL->data[i] = inL * wet_gain + dryL * dry_gain;
|
||||||
|
blockR->data[i] = inR * wet_gain + dryR * dry_gain; |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,181 @@ |
|||||||
|
/* Stereo spring reverb for Teensy 4
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2024 by Piotr Zapart |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
#ifndef _EFFECT_SPRINGREVERB_F32_H |
||||||
|
#define _EFFECT_SPRINGREVERB_F32_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "AudioStream.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
#include "basic_components.h" |
||||||
|
|
||||||
|
// Chirp allpass params
|
||||||
|
#define SPRVB_CHIRP_AMNT 16 //must be mult of 8
|
||||||
|
#define SPRVB_CHIRP1_LEN 3 |
||||||
|
#define SPRVB_CHIRP2_LEN 5 |
||||||
|
#define SPRVB_CHIRP3_LEN 6 |
||||||
|
#define SPRVB_CHIRP4_LEN 7 |
||||||
|
|
||||||
|
#define SPRVB_ALLP1A_LEN (224) |
||||||
|
#define SPRVB_ALLP1B_LEN (420) |
||||||
|
#define SPRVB_ALLP1C_LEN (856) |
||||||
|
#define SPRVB_ALLP1D_LEN (1089) |
||||||
|
|
||||||
|
#define SPRVB_ALLP2A_LEN (156) |
||||||
|
#define SPRVB_ALLP2B_LEN (478) |
||||||
|
#define SPRVB_ALLP2C_LEN (956) |
||||||
|
#define SPRVB_ALLP2D_LEN (1289) |
||||||
|
|
||||||
|
#define SPRVB_DLY1_LEN (1945) |
||||||
|
#define SPRVB_DLY2_LEN (1363) |
||||||
|
|
||||||
|
class AudioEffectSpringReverb_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectSpringReverb_F32(); |
||||||
|
~AudioEffectSpringReverb_F32(){}; |
||||||
|
|
||||||
|
virtual void update(); |
||||||
|
|
||||||
|
void time(float n) |
||||||
|
{ |
||||||
|
n = constrain(n, 0.0f, 1.0f); |
||||||
|
n = map (n, 0.0f, 1.0f, 0.7f, rv_time_k_max); |
||||||
|
float32_t attn = map(n, 0.0f, rv_time_k_max, 0.5f, 0.2f); |
||||||
|
__disable_irq(); |
||||||
|
rv_time_k = n; |
||||||
|
input_attn = attn; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void treble_cut(float n) |
||||||
|
{ |
||||||
|
n = 1.0f - constrain(n, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
lp_TrebleCut_k = n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void bass_cut(float n) |
||||||
|
{ |
||||||
|
n = constrain(n, 0.0f, 1.0f); |
||||||
|
n = 2.0f * n - (n*n); |
||||||
|
__disable_irq(); |
||||||
|
in_BassCut_k = -n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void mix(float m) |
||||||
|
{ |
||||||
|
float32_t dry, wet; |
||||||
|
m = constrain(m, 0.0f, 1.0f); |
||||||
|
mix_pwr(m, &wet, &dry); |
||||||
|
__disable_irq(); |
||||||
|
wet_gain = wet; |
||||||
|
dry_gain = dry; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void wet_level(float wet) |
||||||
|
{ |
||||||
|
wet = constrain(wet, 0.0f, 6.0f); |
||||||
|
__disable_irq(); |
||||||
|
wet_gain = wet; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void dry_level(float dry) |
||||||
|
{ |
||||||
|
dry = constrain(dry, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
dry_gain = dry; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
float32_t get_size(void) {return rv_time_k;} |
||||||
|
bool bypass_get(void) {return bp;} |
||||||
|
void bypass_set(bool state) {bp = state;} |
||||||
|
bool bypass_tgl(void)
|
||||||
|
{ |
||||||
|
bp ^= 1;
|
||||||
|
return bp; |
||||||
|
}
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[2]; |
||||||
|
|
||||||
|
float32_t input_attn;
|
||||||
|
float32_t wet_gain; |
||||||
|
float32_t dry_gain; |
||||||
|
float32_t in_allp_k; // input allpass coeff (default 0.6)
|
||||||
|
float32_t chrp_allp_k[4] = {-0.7f, -0.65f, -0.6f, -0.5f}; |
||||||
|
|
||||||
|
bool bp = false; |
||||||
|
bool cleanup_done = false; |
||||||
|
uint16_t chrp_alp1_idx[SPRVB_CHIRP_AMNT] = {0}; |
||||||
|
uint16_t chrp_alp2_idx[SPRVB_CHIRP_AMNT] = {0}; |
||||||
|
uint16_t chrp_alp3_idx[SPRVB_CHIRP_AMNT] = {0}; |
||||||
|
uint16_t chrp_alp4_idx[SPRVB_CHIRP_AMNT] = {0}; |
||||||
|
|
||||||
|
static constexpr float32_t rv_time_k_max = 0.97f; |
||||||
|
float32_t rv_time_k; |
||||||
|
|
||||||
|
AudioFilterAllpass<SPRVB_ALLP1A_LEN> sp_lp_allp1a; |
||||||
|
AudioFilterAllpass<SPRVB_ALLP1B_LEN> sp_lp_allp1b; |
||||||
|
AudioFilterAllpass<SPRVB_ALLP1C_LEN> sp_lp_allp1c; |
||||||
|
AudioFilterAllpass<SPRVB_ALLP1D_LEN> sp_lp_allp1d; |
||||||
|
|
||||||
|
AudioFilterAllpass<SPRVB_ALLP2A_LEN> sp_lp_allp2a; |
||||||
|
AudioFilterAllpass<SPRVB_ALLP2B_LEN> sp_lp_allp2b;
|
||||||
|
AudioFilterAllpass<SPRVB_ALLP2D_LEN> sp_lp_allp2c; |
||||||
|
AudioFilterAllpass<SPRVB_ALLP2D_LEN> sp_lp_allp2d;
|
||||||
|
|
||||||
|
float32_t *sp_chrp_alp1_buf; |
||||||
|
float32_t *sp_chrp_alp2_buf; |
||||||
|
float32_t *sp_chrp_alp3_buf; |
||||||
|
float32_t *sp_chrp_alp4_buf; |
||||||
|
|
||||||
|
AudioBasicDelay<SPRVB_DLY1_LEN> lp_dly1; |
||||||
|
AudioBasicDelay<SPRVB_DLY2_LEN> lp_dly2; |
||||||
|
|
||||||
|
float32_t in_TrebleCut_k; |
||||||
|
float32_t in_BassCut_k; |
||||||
|
float32_t lp_TrebleCut_k; |
||||||
|
float32_t lp_BassCut_k; |
||||||
|
|
||||||
|
AudioFilterShelvingLPHP flt_in; |
||||||
|
AudioFilterShelvingLPHP flt_lp1; |
||||||
|
AudioFilterShelvingLPHP flt_lp2; |
||||||
|
|
||||||
|
static const uint8_t lfo_ampl = 10; |
||||||
|
AudioBasicLfo lfo = AudioBasicLfo(1.35f, lfo_ampl); |
||||||
|
|
||||||
|
bool initialized = false; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,153 @@ |
|||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// 3 Band EQ :)
|
||||||
|
//
|
||||||
|
// EQ.C - Main Source file for 3 band EQ
|
||||||
|
// (c) Neil C / Etanza Systems / 2K6
|
||||||
|
// Shouts / Loves / Moans = etanza at lycos dot co dot uk
|
||||||
|
//
|
||||||
|
// This work is hereby placed in the public domain for all purposes, including
|
||||||
|
// use in commercial applications.
|
||||||
|
// The author assumes NO RESPONSIBILITY for any problems caused by the use of
|
||||||
|
// this software.
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
// NOTES :
|
||||||
|
// - Original filter code by Paul Kellet (musicdsp.pdf)
|
||||||
|
// - Uses 4 first order filters in series, should give 24dB per octave
|
||||||
|
// - Now with P4 Denormal fix :)
|
||||||
|
//----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifndef _FILTER_3BANDEQ_H_ |
||||||
|
#define _FILTER_3BANDEQ_H_ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
#include "mathDSP_F32.h" |
||||||
|
#include "basic_components.h" |
||||||
|
|
||||||
|
|
||||||
|
class AudioFilterEqualizer3band_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioFilterEqualizer3band_F32(void) : AudioStream_F32(1, inputQueueArray) |
||||||
|
{ |
||||||
|
setBands(500.0f, 3000.0f); |
||||||
|
} |
||||||
|
void setBands(float32_t bassF, float32_t trebleF) |
||||||
|
{ |
||||||
|
trebleF = 2.0f * sinf(M_PI * (trebleF / AUDIO_SAMPLE_RATE_EXACT)); |
||||||
|
bassF = 2.0f * sinf(M_PI * (bassF / AUDIO_SAMPLE_RATE_EXACT)); |
||||||
|
|
||||||
|
__disable_irq(); |
||||||
|
lowpass_f = bassF; |
||||||
|
hipass_f = trebleF;
|
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void treble(float32_t t) |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
treble_g = t; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void mid(float32_t m) |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
mid_g = m; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void bass(float32_t b) |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
bass_g = b; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void set(float32_t t, float32_t m, float32_t b) |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
treble_g = t; |
||||||
|
mid_g = m; |
||||||
|
bass_g = b; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void update() |
||||||
|
{ |
||||||
|
audio_block_f32_t *block; |
||||||
|
int i; |
||||||
|
block = AudioStream_F32::receiveWritable_f32(); |
||||||
|
if (!block) |
||||||
|
return; |
||||||
|
if (bp) // bypass mode
|
||||||
|
{ |
||||||
|
AudioStream_F32::transmit(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (i = 0; i < block->length; i++) |
||||||
|
{ |
||||||
|
float32_t lpOut, midOut, hpOut; // Low / Mid / High - Sample Values
|
||||||
|
float32_t sample = block->data[i]; // give some headroom
|
||||||
|
// Filter #1 (lowpass)
|
||||||
|
f1p0 += (lowpass_f * (sample - f1p0)) + vsa; |
||||||
|
f1p1 += (lowpass_f * (f1p0 - f1p1)); |
||||||
|
f1p2 += (lowpass_f * (f1p1 - f1p2)); |
||||||
|
f1p3 += (lowpass_f * (f1p2 - f1p3)); |
||||||
|
lpOut = f1p3; |
||||||
|
|
||||||
|
// // Filter #2 (highpass)
|
||||||
|
f2p0 += (hipass_f * (sample - f2p0)) + vsa; |
||||||
|
f2p1 += (hipass_f * (f2p0 - f2p1)); |
||||||
|
f2p2 += (hipass_f * (f2p1 - f2p2)); |
||||||
|
f2p3 += (hipass_f * (f2p2 - f2p3)); |
||||||
|
hpOut = sdm3 - f2p3; |
||||||
|
|
||||||
|
midOut = sdm3 - (lpOut + hpOut); |
||||||
|
// // Scale, Combine and store
|
||||||
|
lpOut *= bass_g; |
||||||
|
midOut *= mid_g; |
||||||
|
hpOut *= treble_g; |
||||||
|
|
||||||
|
// // Shuffle history buffer
|
||||||
|
sdm3 = sdm2; |
||||||
|
sdm2 = sdm1; |
||||||
|
sdm1 = sample; |
||||||
|
|
||||||
|
block->data[i] = (lpOut + midOut + hpOut); |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(block); |
||||||
|
AudioStream_F32::release(block); |
||||||
|
} |
||||||
|
void bypass_set(bool s) { bp = s; } |
||||||
|
bool bypass_tgl() |
||||||
|
{ |
||||||
|
bp ^= 1; |
||||||
|
return bp; |
||||||
|
} |
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
bool bp = false; |
||||||
|
|
||||||
|
float32_t f1p0 = 0.0f; |
||||||
|
float32_t f1p1 = 0.0f; |
||||||
|
float32_t f1p2 = 0.0f; |
||||||
|
float32_t f1p3 = 0.0f; |
||||||
|
float32_t f2p0 = 0.0f; |
||||||
|
float32_t f2p1 = 0.0f; |
||||||
|
float32_t f2p2 = 0.0f; |
||||||
|
float32_t f2p3 = 0.0f; |
||||||
|
|
||||||
|
float32_t sdm1 = 0.0f;
|
||||||
|
float32_t sdm2 = 0.0f; // 2
|
||||||
|
float32_t sdm3 = 0.0f; // 3
|
||||||
|
|
||||||
|
static constexpr float32_t vsa = (1.0 / 4294967295.0); // Very small amount (Denormal Fix)
|
||||||
|
float32_t lpreg; |
||||||
|
float32_t hpreg; |
||||||
|
float32_t lowpass_f; |
||||||
|
float32_t bass_g = 1.0f; |
||||||
|
float32_t hipass_f; |
||||||
|
float32_t treble_g = 1.0f; |
||||||
|
float32_t mid_g = 1.0f; |
||||||
|
|
||||||
|
}; |
||||||
|
#endif |
Loading…
Reference in new issue