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