From 6c4a11c91e0088859b53c7954f8986113d2836b1 Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Wed, 17 Feb 2021 11:17:49 +0100 Subject: [PATCH] Replaced FreeVerb with patched version of PlateReverb. Thanks @positionhigh!!! --- MicroDexed.ino | 55 +++-- UI.hpp | 18 +- config.h | 4 +- effect_platervbstereo.cpp | 484 ++++++++++++++++++++++++++++++++++++++ effect_platervbstereo.h | 211 +++++++++++++++++ 5 files changed, 741 insertions(+), 31 deletions(-) create mode 100644 effect_platervbstereo.cpp create mode 100644 effect_platervbstereo.h diff --git a/MicroDexed.ino b/MicroDexed.ino index 78ba625..c568080 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -35,7 +35,8 @@ #include "effect_modulated_delay.h" #include "effect_stereo_mono.h" #include "effect_mono_stereo.h" -#include "effect_freeverbf.h" +//#include "effect_freeverbf.h" +#include "effect_platervbstereo.h" #include "PluginFx.h" #include "UI.hpp" #include "source_microdexed.h" @@ -60,12 +61,13 @@ AudioAnalyzePeak microdexed_peak; #if defined(USE_FX) AudioMixer4 reverb_mixer_r; AudioMixer4 reverb_mixer_l; -AudioEffectFreeverb freeverb_r; -AudioEffectFreeverb freeverb_l; -#if defined(REVERB_ANTIALIAS_FRQ) -AudioFilterBiquad freeverb_antialias_r; -AudioFilterBiquad freeverb_antialias_l; -#endif +//AudioEffectFreeverb freeverb_r; +//AudioEffectFreeverb freeverb_l; +//#if defined(REVERB_ANTIALIAS_FRQ) +//AudioFilterBiquad freeverb_antialias_r; +//AudioFilterBiquad freeverb_antialias_l; +//#endif +AudioEffectPlateReverb reverb; #endif AudioMixer4 master_mixer_r; AudioMixer4 master_mixer_l; @@ -85,17 +87,21 @@ AudioMixer4 audio_thru_mixer_l; // Audio chain tail #if defined(USE_FX) -AudioConnection patchCord0(reverb_mixer_r, freeverb_r); -AudioConnection patchCord1(reverb_mixer_l, freeverb_l); -#if defined(REVERB_ANTIALIAS_FRQ) -AudioConnection patchCord2(freeverb_r, freeverb_antialias_r); -AudioConnection patchCord3(freeverb_l, freeverb_antialias_l); -AudioConnection patchCord4(freeverb_antialias_r, 0, master_mixer_r, 3); -AudioConnection patchCord5(freeverb_antialias_l, 0, master_mixer_l, 3); -#else -AudioConnection patchCord2(freeverb_r, 0, master_mixer_r, 3); -AudioConnection patchCord3(freeverb_l, 0, master_mixer_l, 3); -#endif +//AudioConnection patchCord0(reverb_mixer_r, freeverb_r); +//AudioConnection patchCord1(reverb_mixer_l, freeverb_l); +AudioConnection patchCord0(reverb_mixer_r, 0, reverb, 0); +AudioConnection patchCord1(reverb_mixer_l, 0, reverb, 1); +AudioConnection patchCord2(reverb, 0, master_mixer_r, 3); +AudioConnection patchCord3(reverb, 0, master_mixer_l, 3); +//#if defined(REVERB_ANTIALIAS_FRQ) +//AudioConnection patchCord2(freeverb_r, freeverb_antialias_r); +//AudioConnection patchCord3(freeverb_l, freeverb_antialias_l); +//AudioConnection patchCord4(freeverb_antialias_r, 0, master_mixer_r, 3); +//AudioConnection patchCord5(freeverb_antialias_l, 0, master_mixer_l, 3); +//#else +//AudioConnection patchCord2(freeverb_r, 0, master_mixer_r, 3); +//AudioConnection patchCord3(freeverb_l, 0, master_mixer_l, 3); +//#endif #endif #if defined(ANTIALIAS_FRQ) AudioConnection patchCord6(master_mixer_r, antialias_r); @@ -1877,10 +1883,15 @@ void set_fx_params(void) } // REVERB - freeverb_r.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); - freeverb_r.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); - freeverb_l.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); - freeverb_l.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + //freeverb_r.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); + //freeverb_r.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + //freeverb_l.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); + //freeverb_l.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + reverb.size(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); // max reverb length + reverb.lowpass(0.3); // sets the reverb master lowpass filter + reverb.lodamp(0.1); // amount of low end loss in the reverb tail + reverb.hidamp(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); // amount of treble loss in the reverb tail + reverb.diffusion(1.0); // 1.0 is the detault setting, lower it to create more "echoey" reverb master_mixer_r.gain(3, pseudo_log_curve(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, 1.0))); master_mixer_l.gain(3, pseudo_log_curve(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, 1.0))); #endif diff --git a/UI.hpp b/UI.hpp index 02623cd..a8add44 100644 --- a/UI.hpp +++ b/UI.hpp @@ -30,7 +30,8 @@ #include "Disp_Plus.h" #include "effect_modulated_delay.h" #include "effect_stereo_mono.h" -#include "effect_freeverbf.h" +//#include "effect_freeverbf.h" +#include "effect_platervbstereo.h" #include "dexed.h" #include @@ -81,8 +82,9 @@ extern AudioAnalyzePeak microdexed_peak; #if defined(USE_FX) extern AudioMixer4 reverb_mixer_r; extern AudioMixer4 reverb_mixer_l; -extern AudioEffectFreeverb freeverb_r; -extern AudioEffectFreeverb freeverb_l; +//extern AudioEffectFreeverb freeverb_r; +//extern AudioEffectFreeverb freeverb_l; +extern AudioEffectPlateReverb reverb; #endif extern AudioMixer4 master_mixer_r; extern AudioMixer4 master_mixer_l; @@ -910,8 +912,9 @@ void UI_func_reverb_roomsize(uint8_t param) } lcd_display_bar_int("Reverb Room", configuration.fx.reverb_roomsize, 1.0, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 3, false, false, false); - freeverb_r.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); - freeverb_l.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); + //freeverb_r.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); + //freeverb_l.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); + reverb.size(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0)); } if (LCDML.FUNC_close()) // ****** STABLE END ********* @@ -944,8 +947,9 @@ void UI_func_reverb_damping(uint8_t param) lcd_display_bar_int("Reverb Damp.", configuration.fx.reverb_damping, 1.0, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 3, false, false, false); - freeverb_l.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); - freeverb_r.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + //freeverb_l.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + //freeverb_r.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); + reverb.hidamp(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0)); } if (LCDML.FUNC_close()) // ****** STABLE END ********* diff --git a/config.h b/config.h index fb087eb..93fb936 100644 --- a/config.h +++ b/config.h @@ -58,7 +58,7 @@ // sed -i.orig 's/^#define USB_MIDI_SYSEX_MAX 290/#define USB_MIDI_SYSEX_MAX 4104/' /usr/local/arduino-teensy/hardware/teensy/avr/cores/teensy4/usb_midi.h //#define USB_MIDI_SYSEX_MAX 4104 -#define VERSION "1.0.13" +#define VERSION "1.0.14" //************************************************************************************************* //* DEVICE SETTINGS @@ -105,7 +105,7 @@ #define DEXED_ENGINE DEXED_ENGINE_MODERN // DEXED_ENGINE_MARKI // DEXED_ENGINE_OPL // Number of Dexed instances -#define NUM_DEXED 1 // 1 or 2 - nothing else! +#define NUM_DEXED 2 // 1 or 2 - nothing else! // FX-CHAIN ENABLE/DISABLE #define USE_FX 1 // CHORUS parameters diff --git a/effect_platervbstereo.cpp b/effect_platervbstereo.cpp new file mode 100644 index 0000000..baa2435 --- /dev/null +++ b/effect_platervbstereo.cpp @@ -0,0 +1,484 @@ +/* Stereo plate reverb for Teensy 4 + * + * Author: Piotr Zapart + * www.hexefx.com + * + * Copyright (c) 2020 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 +#include "effect_platervbstereo.h" +#include "utility/dspinst.h" +#include "synth_waveform.h" + +#define INP_ALLP_COEFF (0.65) +#define LOOP_ALLOP_COEFF (0.65) + +#define HI_LOSS_FREQ (0.3) +#define HI_LOSS_FREQ_MAX (0.08) +#define LO_LOSS_FREQ (0.06) + +#define LFO_AMPL_BITS (5) // 2^LFO_AMPL_BITS will be the LFO amplitude +#define LFO_AMPL ((1<>1) // read offset = half the amplitude +#define LFO_FRAC_BITS (16 - LFO_AMPL_BITS) // fractional part used for linear interpolation +#define LFO_FRAC_MASK ((1< 16 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 32 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 48 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 64 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 80 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 96 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +#if AUDIO_BLOCK_SAMPLES > 112 +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif +} }; + +void AudioEffectPlateReverb::update() +{ + const audio_block_t *blockL, *blockR; + +#if defined(__ARM_ARCH_7EM__) + audio_block_t *outblockL; + audio_block_t *outblockR; + int i; + float32_t input, acc, temp1, temp2; + uint16_t temp16; + float32_t rv_time; + + // for LFOs: + int16_t lfo1_out_sin, lfo1_out_cos, lfo2_out_sin, lfo2_out_cos; + int32_t y0, y1; + int64_t y; + uint32_t idx; + + blockL = receiveReadOnly(0); + blockR = receiveReadOnly(1); + outblockL = allocate(); + outblockR = allocate(); + if (!outblockL || !outblockR) { + if (outblockL) release(outblockL); + if (outblockR) release(outblockR); + if (blockL) release((audio_block_t *)blockL); + if (blockR) release((audio_block_t *)blockR); + return; + } + + if (!blockL) blockL = &zeroblock; + if (!blockR) blockR = &zeroblock; + // convert data to float32 + arm_q15_to_float((q15_t *)blockL->data, input_blockL, AUDIO_BLOCK_SAMPLES); + arm_q15_to_float((q15_t *)blockR->data, input_blockR, AUDIO_BLOCK_SAMPLES); + + rv_time = rv_time_k; + + for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) + { + // do the LFOs + lfo1_phase_acc += lfo1_adder; + idx = lfo1_phase_acc >> 24; // 8bit lookup table address + y0 = AudioWaveformSine[idx]; + y1 = AudioWaveformSine[idx+1]; + idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part + y = (int64_t)y0 * (0x00FFFFFF - idx); + y += (int64_t)y1 * idx; + lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output + idx = ((lfo1_phase_acc >> 24)+64) & 0xFF; + y0 = AudioWaveformSine[idx]; + y1 = AudioWaveformSine[idx + 1]; + y = (int64_t)y0 * (0x00FFFFFF - idx); + y += (int64_t)y1 * idx; + lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output + + lfo2_phase_acc += lfo2_adder; + idx = lfo2_phase_acc >> 24; // 8bit lookup table address + y0 = AudioWaveformSine[idx]; + y1 = AudioWaveformSine[idx+1]; + idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part + y = (int64_t)y0 * (0x00FFFFFF - idx); + y += (int64_t)y1 * idx; + lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit, + idx = ((lfo2_phase_acc >> 24)+64) & 0xFF; + y0 = AudioWaveformSine[idx]; + y1 = AudioWaveformSine[idx + 1]; + y = (int64_t)y0 * (0x00FFFFFF - idx); + y += (int64_t)y1 * idx; + lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output + + input = input_blockL[i] * input_attn; + // chained input allpasses, channel L + acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k; + in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc; + input = acc; + if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0; + + acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k; + in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc; + input = acc; + if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0; + + acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k; + in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc; + input = acc; + if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0; + + acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k; + in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc; + in_allp_out_L = acc; + if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0; + + input = input_blockR[i] * input_attn; + + // chained input allpasses, channel R + acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k; + in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc; + input = acc; + if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0; + + acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k; + in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc; + input = acc; + if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0; + + acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k; + in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc; + input = acc; + if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0; + + acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k; + in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc; + in_allp_out_R = acc; + if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0; + + // input allpases done, start loop allpases + input = lp_allp_out + in_allp_out_R; + acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output + lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc; + input = acc; + if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t)) lp_allp1_idx = 0; + + acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay + lp_dly1_buf[lp_dly1_idx] = input; // write new sample + input = acc; + if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index + + // hi/lo shelving filter + temp1 = input - lpf1; + lpf1 += temp1 * lp_lowpass_f; + temp2 = input - lpf1; + temp1 = lpf1 - hpf1; + hpf1 += temp1 * lp_hipass_f; + acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k; + acc = acc * rv_time * rv_time_scaler; // scale by the reveb time + + input = acc + in_allp_out_L; + + acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k; + lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc; + input = acc; + if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0; + acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay + lp_dly2_buf[lp_dly2_idx] = input; // write new sample + input = acc; + if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index + // hi/lo shelving filter + temp1 = input - lpf2; + lpf2 += temp1 * lp_lowpass_f; + temp2 = input - lpf2; + temp1 = lpf2 - hpf2; + hpf2 += temp1 * lp_hipass_f; + acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k; + acc = acc * rv_time * rv_time_scaler; + + input = acc + in_allp_out_R; + + acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k; + lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc; + input = acc; + if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0; + acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay + lp_dly3_buf[lp_dly3_idx] = input; // write new sample + input = acc; + if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index + // hi/lo shelving filter + temp1 = input - lpf3; + lpf3 += temp1 * lp_lowpass_f; + temp2 = input - lpf3; + temp1 = lpf3 - hpf3; + hpf3 += temp1 * lp_hipass_f; + acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k; + acc = acc * rv_time * rv_time_scaler; + + input = acc + in_allp_out_L; + + acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k; + lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc; + input = acc; + if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0; + acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay + lp_dly4_buf[lp_dly4_idx] = input; // write new sample + input = acc; + if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index + // hi/lo shelving filter + temp1 = input - lpf4; + lpf4 += temp1 * lp_lowpass_f; + temp2 = input - lpf4; + temp1 = lpf4 - hpf4; + hpf4 += temp1 * lp_hipass_f; + acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k; + acc = acc * rv_time * rv_time_scaler; + + lp_allp_out = acc; + + // channel L: +#ifdef TAP1_MODULATED + temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); + temp1 = lp_dly1_buf[temp16++]; // sample now + if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly1_buf[temp16]; // sample next + input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc = (temp1*(1.0-input) + temp2*input)* 0.8; +#else + temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); + acc = lp_dly1_buf[temp16]* 0.8; +#endif + + +#ifdef TAP2_MODULATED + temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); + temp1 = lp_dly2_buf[temp16++]; + if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly2_buf[temp16]; + input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.7; +#else + temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); + acc += (temp1*(1.0-input) + temp2*input)* 0.6; +#endif + + temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); + temp1 = lp_dly3_buf[temp16++]; + if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly3_buf[temp16]; + input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.6; + + temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); + temp1 = lp_dly4_buf[temp16++]; + if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly4_buf[temp16]; + input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.5; + + // Master lowpass filter + temp1 = acc - master_lowpass_l; + master_lowpass_l += temp1 * master_lowpass_f; + + outblockL->data[i] =(int16_t)(master_lowpass_l * 32767.0); //sat16(output * 30, 0); + + // Channel R + #ifdef TAP1_MODULATED + temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); + temp1 = lp_dly1_buf[temp16++]; // sample now + if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly1_buf[temp16]; // sample next + input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + + acc = (temp1*(1.0-input) + temp2*input)* 0.8; + #else + temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); + acc = lp_dly1_buf[temp16] * 0.8; + #endif +#ifdef TAP2_MODULATED + temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); + temp1 = lp_dly2_buf[temp16++]; + if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly2_buf[temp16]; + input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.7; +#else + temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); + acc += (temp1*(1.0-input) + temp2*input)* 0.7; +#endif + temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); + temp1 = lp_dly3_buf[temp16++]; + if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly3_buf[temp16]; + input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.6; + + temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); + temp1 = lp_dly4_buf[temp16++]; + if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; + temp2 = lp_dly4_buf[temp16]; + input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k + acc += (temp1*(1.0-input) + temp2*input)* 0.5; + + // Master lowpass filter + temp1 = acc - master_lowpass_r; + master_lowpass_r += temp1 * master_lowpass_f; + outblockR->data[i] =(int16_t)(master_lowpass_r * 32767.0); + + } + transmit(outblockL, 0); + transmit(outblockR, 1); + release(outblockL); + release(outblockR); + if (blockL != &zeroblock) release((audio_block_t *)blockL); + if (blockR != &zeroblock) release((audio_block_t *)blockR); + +#elif defined(KINETISL) + blockL = receiveReadOnly(0); + if (blockL) release(blockL); + blockR = receiveReadOnly(1); + if (blockR) release(blockR); +#endif +} diff --git a/effect_platervbstereo.h b/effect_platervbstereo.h new file mode 100644 index 0000000..9a0a170 --- /dev/null +++ b/effect_platervbstereo.h @@ -0,0 +1,211 @@ +/* Stereo plate reverb for Teensy 4 + * + * Author: Piotr Zapart + * www.hexefx.com + * + * Copyright (c) 2020 by Piotr Zapart + * + * 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 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. + */ + +/*** + * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip + * + * Allpass + modulated delay line based lush plate reverb + * + * Input parameters are float in range 0.0 to 1.0: + * + * size - reverb time + * hidamp - hi frequency loss in the reverb tail + * lodamp - low frequency loss in the reverb tail + * lowpass - output/master lowpass filter, useful for darkening the reverb sound + * diffusion - lower settings will make the reverb tail more "echoey", optimal value 0.65 + * + */ + + +#ifndef _EFFECT_PLATERVBSTEREO_H +#define _EFFECT_PLATERVBSTEREO_H + +#include +#include "Audio.h" +#include "AudioStream.h" +#include "arm_math.h" + + +// if uncommented will place all the buffers in the DMAMEM section ofd the memory +// works with single instance of the reverb only +#define REVERB_USE_DMAMEM + +/*** + * Loop delay modulation: comment/uncomment to switch sin/cos + * modulation for the 1st or 2nd tap, 3rd tap is always modulated + * more modulation means more chorus type sounding reverb tail + */ +//#define TAP1_MODULATED +#define TAP2_MODULATED + +class AudioEffectPlateReverb : public AudioStream +{ +public: + AudioEffectPlateReverb(); + virtual void update(); + + void size(float n) + { + n = constrain(n, 0.0, 1.0); + n = map (n, 0.0, 1.0, 0.2, rv_time_k_max); + float32_t attn = 0.5 * map(n, 0.0, rv_time_k_max, 0.5, 1.0); + AudioNoInterrupts(); + rv_time_k = n; + input_attn = attn; + AudioInterrupts(); + } + + void hidamp(float n) + { + n = constrain(n, 0.0, 1.0); + AudioNoInterrupts(); + lp_hidamp_k = 1.0 - n; + AudioInterrupts(); + } + + void lodamp(float n) + { + n = constrain(n, 0.0, 1.0); + AudioNoInterrupts(); + lp_lodamp_k = -n; + rv_time_scaler = 1.0 - n * 0.12; // limit the max reverb time, otherwise it will clip + AudioInterrupts(); + } + + void lowpass(float n) + { + n = constrain(n, 0.0, 1.0); + n = map(n*n*n, 0.0, 1.0, 0.05, 1.0); + master_lowpass_f = n; + } + + void diffusion(float n) + { + n = constrain(n, 0.0, 1.0); + n = map(n, 0.0, 1.0, 0.005, 0.65); + AudioNoInterrupts(); + in_allp_k = n; + loop_allp_k = n; + AudioInterrupts(); + } + + float32_t get_size(void) {return rv_time_k;} +private: + audio_block_t *inputQueueArray[2]; +#ifndef REVERB_USE_DMAMEM + float32_t input_blockL[AUDIO_BLOCK_SAMPLES]; + float32_t input_blockR[AUDIO_BLOCK_SAMPLES]; +#endif + float32_t input_attn; + + float32_t in_allp_k; // input allpass coeff (default 0.6) +#ifndef REVERB_USE_DMAMEM + float32_t in_allp1_bufL[224]; // input allpass buffers + float32_t in_allp2_bufL[420]; + float32_t in_allp3_bufL[856]; + float32_t in_allp4_bufL[1089]; +#endif + uint16_t in_allp1_idxL; + uint16_t in_allp2_idxL; + uint16_t in_allp3_idxL; + uint16_t in_allp4_idxL; + float32_t in_allp_out_L; // L allpass chain output +#ifndef REVERB_USE_DMAMEM + float32_t in_allp1_bufR[156]; // input allpass buffers + float32_t in_allp2_bufR[520]; + float32_t in_allp3_bufR[956]; + float32_t in_allp4_bufR[1289]; +#endif + uint16_t in_allp1_idxR; + uint16_t in_allp2_idxR; + uint16_t in_allp3_idxR; + uint16_t in_allp4_idxR; + float32_t in_allp_out_R; // R allpass chain output +#ifndef REVERB_USE_DMAMEM + float32_t lp_allp1_buf[1303]; // loop allpass buffers + float32_t lp_allp2_buf[1905]; + float32_t lp_allp3_buf[1175]; + float32_t lp_allp4_buf[1398]; +#endif + uint16_t lp_allp1_idx; + uint16_t lp_allp2_idx; + uint16_t lp_allp3_idx; + uint16_t lp_allp4_idx; + float32_t loop_allp_k; // loop allpass coeff (default 0.6) + float32_t lp_allp_out; +#ifndef REVERB_USE_DMAMEM + float32_t lp_dly1_buf[1423]; + float32_t lp_dly2_buf[1589]; + float32_t lp_dly3_buf[1365]; + float32_t lp_dly4_buf[1698]; +#endif + uint16_t lp_dly1_idx; + uint16_t lp_dly2_idx; + uint16_t lp_dly3_idx; + uint16_t lp_dly4_idx; + + const uint16_t lp_dly1_offset_L = 201; + const uint16_t lp_dly2_offset_L = 145; + const uint16_t lp_dly3_offset_L = 1897; + const uint16_t lp_dly4_offset_L = 280; + + const uint16_t lp_dly1_offset_R = 1897; + const uint16_t lp_dly2_offset_R = 1245; + const uint16_t lp_dly3_offset_R = 487; + const uint16_t lp_dly4_offset_R = 780; + + float32_t lp_hidamp_k; // loop high band damping coeff + float32_t lp_lodamp_k; // loop low baand damping coeff + + float32_t lpf1; // lowpass filters + float32_t lpf2; + float32_t lpf3; + float32_t lpf4; + + float32_t hpf1; // highpass filters + float32_t hpf2; + float32_t hpf3; + float32_t hpf4; + + float32_t lp_lowpass_f; // loop lowpass scaled frequency + float32_t lp_hipass_f; // loop highpass scaled frequency + + float32_t master_lowpass_f; + float32_t master_lowpass_l; + float32_t master_lowpass_r; + + const float32_t rv_time_k_max = 0.95; + float32_t rv_time_k; // reverb time coeff + float32_t rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping + + uint32_t lfo1_phase_acc; // LFO 1 + uint32_t lfo1_adder; + + uint32_t lfo2_phase_acc; // LFO 2 + uint32_t lfo2_adder; +}; + +#endif // _EFFECT_PLATEREV_H