Replaced FreeVerb with patched version of PlateReverb. Thanks @positionhigh!!!

pull/37/head
Holger Wirtz 3 years ago
parent fecebf1b4a
commit 6c4a11c91e
  1. 55
      MicroDexed.ino
  2. 18
      UI.hpp
  3. 4
      config.h
  4. 484
      effect_platervbstereo.cpp
  5. 211
      effect_platervbstereo.h

@ -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

@ -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 <LCDMenuLib2.h>
@ -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 *********

@ -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

@ -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 <Arduino.h>
#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<<LFO_AMPL_BITS) + 1) // lfo amplitude
#define LFO_READ_OFFSET (LFO_AMPL>>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<<LFO_FRAC_BITS)-1) // mask for the above
#define LFO1_FREQ_HZ (1.37) // LFO1 frequency in Hz
#define LFO2_FREQ_HZ (1.52) // LFO2 frequency in Hz
#define RV_MASTER_LOWPASS_F (0.6) // master lowpass scaled frequency coeff.
extern "C" {
extern const int16_t AudioWaveformSine[257];
}
#ifdef REVERB_USE_DMAMEM
float32_t DMAMEM input_blockL[AUDIO_BLOCK_SAMPLES];
float32_t DMAMEM input_blockR[AUDIO_BLOCK_SAMPLES];
float32_t DMAMEM in_allp1_bufL[224]; // input allpass buffers
float32_t DMAMEM in_allp2_bufL[420];
float32_t DMAMEM in_allp3_bufL[856];
float32_t DMAMEM in_allp4_bufL[1089];
float32_t DMAMEM in_allp1_bufR[156]; // input allpass buffers
float32_t DMAMEM in_allp2_bufR[520];
float32_t DMAMEM in_allp3_bufR[956];
float32_t DMAMEM in_allp4_bufR[1289];
float32_t DMAMEM lp_allp1_buf[1303]; // loop allpass buffers
float32_t DMAMEM lp_allp2_buf[905];
float32_t DMAMEM lp_allp3_buf[1175];
float32_t DMAMEM lp_allp4_buf[1398];
float32_t DMAMEM lp_dly1_buf[1423];
float32_t DMAMEM lp_dly2_buf[1589];
float32_t DMAMEM lp_dly3_buf[1365];
float32_t DMAMEM lp_dly4_buf[1698];
#endif
AudioEffectPlateReverb::AudioEffectPlateReverb() : AudioStream(2, inputQueueArray)
{
input_attn = 0.5;
in_allp_k = INP_ALLP_COEFF;
memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL));
memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL));
memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL));
memset(in_allp4_bufL, 0, sizeof(in_allp4_bufL));
in_allp1_idxL = 0;
in_allp2_idxL = 0;
in_allp3_idxL = 0;
in_allp4_idxL = 0;
memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR));
memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR));
memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR));
memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR));
in_allp1_idxR = 0;
in_allp2_idxR = 0;
in_allp3_idxR = 0;
in_allp4_idxR = 0;
in_allp_out_R = 0;
memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf));
memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf));
memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf));
memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf));
lp_allp1_idx = 0;
lp_allp2_idx = 0;
lp_allp3_idx = 0;
lp_allp4_idx = 0;
loop_allp_k = LOOP_ALLOP_COEFF;
lp_allp_out = 0;
memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf));
memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf));
memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf));
memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf));
lp_dly1_idx = 0;
lp_dly2_idx = 0;
lp_dly3_idx = 0;
lp_dly4_idx = 0;
lp_hidamp_k = 1.0;
lp_lodamp_k = 0.0;
lp_lowpass_f = HI_LOSS_FREQ;
lp_hipass_f = LO_LOSS_FREQ;
lpf1 = 0;
lpf2 = 0;
lpf3 = 0;
lpf4 = 0;
hpf1 = 0;
hpf2 = 0;
hpf3 = 0;
hpf4 = 0;
master_lowpass_f = RV_MASTER_LOWPASS_F;
master_lowpass_l = 0;
master_lowpass_r = 0;
lfo1_phase_acc = 0;
lfo1_adder = (UINT32_MAX + 1)/(AUDIO_SAMPLE_RATE_EXACT * LFO1_FREQ_HZ);
lfo2_phase_acc = 0;
lfo2_adder = (UINT32_MAX + 1)/(AUDIO_SAMPLE_RATE_EXACT * LFO2_FREQ_HZ);
}
#define sat16(n, rshift) signed_saturate_rshift((n), 16, (rshift))
// TODO: move this to one of the data files, use in output_adat.cpp, output_tdm.cpp, etc
static const audio_block_t zeroblock = {
0, 0, 0, {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#if AUDIO_BLOCK_SAMPLES > 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
}

@ -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 <Arduino.h>
#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
Loading…
Cancel
Save