|
|
|
/*
|
|
|
|
* 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/upgraded to Teensy4 and OpenAudio_ArduinoLibrary:
|
|
|
|
* 01.2024 Piotr Zapart www.hexefx.com
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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"
|
|
|
|
|
|
|
|
#define REVERBSC_DLYBUF_SIZE 98936
|
|
|
|
|
|
|
|
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_set = 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 = constrain(wet, 0.0f, 1.0f);
|
|
|
|
__disable_irq();
|
|
|
|
wet_gain = wet;
|
|
|
|
__enable_irq();
|
|
|
|
}
|
|
|
|
|
|
|
|
void dry_level(float32_t dry)
|
|
|
|
{
|
|
|
|
dry = constrain(dry, 0.0f, 1.0f);
|
|
|
|
__disable_irq();
|
|
|
|
dry_gain = dry;
|
|
|
|
__enable_irq();
|
|
|
|
}
|
|
|
|
void freeze(bool state);
|
|
|
|
bool freeze_tgl() {freeze(flags.freeze^1); return flags.freeze;}
|
|
|
|
bool freeze_get() {return flags.freeze;}
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
BYPASS_MODE_PASS, // pass the input signal to the output
|
|
|
|
BYPASS_MODE_OFF, // mute the output
|
|
|
|
BYPASS_MODE_TRAILS // mutes the input only
|
|
|
|
}bypass_mode_t;
|
|
|
|
void bypass_setMode(bypass_mode_t m)
|
|
|
|
{
|
|
|
|
if (m <= BYPASS_MODE_TRAILS)
|
|
|
|
{
|
|
|
|
__disable_irq();
|
|
|
|
bp_mode = m;
|
|
|
|
__enable_irq();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bypass_mode_t bypass_geMode() {return bp_mode;}
|
|
|
|
bool bypass_get(void) {return flags.bypass;}
|
|
|
|
void bypass_set(bool state)
|
|
|
|
{
|
|
|
|
if (flags.mem_fail) return;
|
|
|
|
flags.bypass = state;
|
|
|
|
if (state)
|
|
|
|
{
|
|
|
|
if (bp_mode == BYPASS_MODE_TRAILS) input_gain_set = 0.0f;
|
|
|
|
freeze(false); // disable freeze in bypass mode
|
|
|
|
__disable_irq();
|
|
|
|
memCleanupStart = 0;
|
|
|
|
memCleanupEnd = memCleanupStep;
|
|
|
|
__enable_irq();
|
|
|
|
}
|
|
|
|
else input_gain_set = input_gain_tmp;
|
|
|
|
}
|
|
|
|
bool bypass_tgl(void)
|
|
|
|
{
|
|
|
|
bypass_set(flags.bypass^1);
|
|
|
|
return flags.bypass;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
struct flags_t
|
|
|
|
{
|
|
|
|
unsigned bypass: 1;
|
|
|
|
unsigned freeze: 1;
|
|
|
|
unsigned cleanup_done: 1;
|
|
|
|
unsigned memsetup_done: 1;
|
|
|
|
unsigned mem_fail: 1;
|
|
|
|
}flags = {0, 0, 0};
|
|
|
|
bypass_mode_t bp_mode;
|
|
|
|
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
|
|
|
|
const uint32_t aux_size_bytes = REVERBSC_DLYBUF_SIZE*sizeof(float32_t);
|
|
|
|
float32_t dry_gain = 0.5f;
|
|
|
|
float32_t wet_gain = 0.5f;
|
|
|
|
|
|
|
|
float32_t input_gain_set = 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;
|
|
|
|
|
|
|
|
bool memCleanup(void);
|
|
|
|
const uint32_t memCleanupStep = 512;
|
|
|
|
uint32_t memCleanupStart = 0;
|
|
|
|
uint32_t memCleanupEnd = memCleanupStep;
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
#endif // _EFFECT_REVERBSC_H_
|