parent
445bb54e78
commit
11ce922409
@ -0,0 +1,221 @@ |
|||||||
|
/**
|
||||||
|
Example project for the Stereo Plate reverb audio component |
||||||
|
(c) 31.12.2020 by Piotr Zapart www-hexefx.com |
||||||
|
|
||||||
|
Attention!!! Works with Teensy 4.x only! |
||||||
|
|
||||||
|
The audio path follows a typical scheme used in mixing consoles |
||||||
|
where the reverb is put into aux path. |
||||||
|
Each source (like i2s or PlaySDWav etc) has a Reverb Send Level control |
||||||
|
The Stereo reverb output is mixed then with the dry signals using the output mixers. |
||||||
|
|
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "effect_platervbstereo.h" |
||||||
|
|
||||||
|
#define I2S_REVERB_SEND_CH 0 |
||||||
|
#define SDWAV_REVERB_SEND_CH 1 |
||||||
|
#define REVERB_MIX_CH 1 |
||||||
|
#define I2S_MIX_CH 0 |
||||||
|
|
||||||
|
AudioPlaySdWav playSdWav; |
||||||
|
AudioInputI2S i2s_in; |
||||||
|
AudioMixer4 reverb_send_L; |
||||||
|
AudioMixer4 reverb_send_R; |
||||||
|
AudioEffectPlateReverb reverb; |
||||||
|
AudioMixer4 mixer_out_L; |
||||||
|
AudioMixer4 mixer_out_R; |
||||||
|
AudioOutputI2S i2s_out; |
||||||
|
|
||||||
|
AudioConnection patchCord1(playSdWav, 0, reverb_send_L, 1); // wav player L reverb send
|
||||||
|
AudioConnection patchCord2(playSdWav, 0, mixer_out_L, 2); // wav player L into output mixer
|
||||||
|
AudioConnection patchCord3(playSdWav, 1, reverb_send_R, 1); // wav player R reverb send
|
||||||
|
AudioConnection patchCord4(playSdWav, 1, mixer_out_R, 2); // wav player R into output mixer
|
||||||
|
|
||||||
|
AudioConnection patchCord5(i2s_in, 0, mixer_out_L, 0); // i2s out L into output mixer
|
||||||
|
AudioConnection patchCord6(i2s_in, 1, mixer_out_R, 0); // i2s out R into output mixer
|
||||||
|
|
||||||
|
AudioConnection patchCord7(i2s_in, 0, reverb_send_L, 0); // i2s out reverb send L
|
||||||
|
AudioConnection patchCord8(i2s_in, 1, reverb_send_R, 0); // i2s out reverb send R
|
||||||
|
|
||||||
|
AudioConnection patchCord9(reverb_send_L, 0, reverb, 0); // reverb inputs
|
||||||
|
AudioConnection patchCord10(reverb_send_R, 0, reverb, 1); |
||||||
|
|
||||||
|
AudioConnection patchCord11(reverb, 0, mixer_out_L, 1); // reverb out into output mixer
|
||||||
|
AudioConnection patchCord12(reverb, 1, mixer_out_R, 1); |
||||||
|
|
||||||
|
AudioConnection patchCord13(mixer_out_L, 0, i2s_out, 0); // output mixers -> codec DAC
|
||||||
|
AudioConnection patchCord14(mixer_out_R, 0, i2s_out, 1); |
||||||
|
|
||||||
|
AudioControlSGTL5000 codec; |
||||||
|
|
||||||
|
|
||||||
|
uint32_t timeLast = 0, timeNow = 0; |
||||||
|
|
||||||
|
void flexRamInfo(void); |
||||||
|
void i2s_set_rev_send(float32_t lvl); |
||||||
|
void reverb_set_volume(float32_t lvl); |
||||||
|
void wav_set_rev_send(float32_t lvl); |
||||||
|
|
||||||
|
void setup() |
||||||
|
{ |
||||||
|
Serial.begin(115200); |
||||||
|
//while(!Serial);
|
||||||
|
delay(1000); |
||||||
|
Serial.println("--------------------------"); |
||||||
|
Serial.println("T40_GFX - stereo plate reverb"); |
||||||
|
#ifdef REVERB_USE_DMAMEM |
||||||
|
Serial.println("DMAMEM is used for reverb buffers"); |
||||||
|
#endif |
||||||
|
AudioMemory(12); |
||||||
|
codec.enable(); |
||||||
|
codec.volume(0.0); // headphones not used
|
||||||
|
codec.inputSelect(AUDIO_INPUT_LINEIN); |
||||||
|
codec.lineInLevel(2); |
||||||
|
codec.lineOutLevel(31); |
||||||
|
flexRamInfo(); |
||||||
|
|
||||||
|
i2s_set_rev_send(0.7); |
||||||
|
reverb_set_volume(0.6); |
||||||
|
|
||||||
|
reverb.size(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(0.2); // 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
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() |
||||||
|
{ |
||||||
|
timeNow = millis(); |
||||||
|
if (timeNow - timeLast > 1000) |
||||||
|
{ |
||||||
|
Serial.print("Reverb CPU load = "); |
||||||
|
Serial.println(reverb.processorUsageMax()); |
||||||
|
timeLast = timeNow; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void flexRamInfo(void) |
||||||
|
{ // credit to FrankB, KurtE and defragster !
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) |
||||||
|
int itcm = 0; |
||||||
|
int dtcm = 0; |
||||||
|
int ocram = 0; |
||||||
|
Serial.print("FlexRAM-Banks: ["); |
||||||
|
for (int i = 15; i >= 0; i--) |
||||||
|
{ |
||||||
|
switch ((IOMUXC_GPR_GPR17 >> (i * 2)) & 0b11) |
||||||
|
{ |
||||||
|
case 0b00: |
||||||
|
Serial.print("."); |
||||||
|
break; |
||||||
|
case 0b01: |
||||||
|
Serial.print("O"); |
||||||
|
ocram++; |
||||||
|
break; |
||||||
|
case 0b10: |
||||||
|
Serial.print("D"); |
||||||
|
dtcm++; |
||||||
|
break; |
||||||
|
case 0b11: |
||||||
|
Serial.print("I"); |
||||||
|
itcm++; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
Serial.print("] ITCM: "); |
||||||
|
Serial.print(itcm * 32); |
||||||
|
Serial.print(" KB, DTCM: "); |
||||||
|
Serial.print(dtcm * 32); |
||||||
|
Serial.print(" KB, OCRAM: "); |
||||||
|
Serial.print(ocram * 32); |
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
Serial.print("(+512)"); |
||||||
|
#endif |
||||||
|
Serial.println(" KB"); |
||||||
|
extern unsigned long _stext; |
||||||
|
extern unsigned long _etext; |
||||||
|
extern unsigned long _sdata; |
||||||
|
extern unsigned long _ebss; |
||||||
|
extern unsigned long _flashimagelen; |
||||||
|
extern unsigned long _heap_start; |
||||||
|
|
||||||
|
Serial.println("MEM (static usage):"); |
||||||
|
Serial.println("RAM1:"); |
||||||
|
|
||||||
|
Serial.print("ITCM = FASTRUN: "); |
||||||
|
Serial.print((unsigned)&_etext - (unsigned)&_stext); |
||||||
|
Serial.print(" "); |
||||||
|
Serial.print((float)((unsigned)&_etext - (unsigned)&_stext) / ((float)itcm * 32768.0) * 100.0); |
||||||
|
Serial.print("% of "); |
||||||
|
Serial.print(itcm * 32); |
||||||
|
Serial.print("kb "); |
||||||
|
Serial.print(" ("); |
||||||
|
Serial.print(itcm * 32768 - ((unsigned)&_etext - (unsigned)&_stext)); |
||||||
|
Serial.println(" Bytes free)"); |
||||||
|
|
||||||
|
Serial.print("DTCM = Variables: "); |
||||||
|
Serial.print((unsigned)&_ebss - (unsigned)&_sdata); |
||||||
|
Serial.print(" "); |
||||||
|
Serial.print((float)((unsigned)&_ebss - (unsigned)&_sdata) / ((float)dtcm * 32768.0) * 100.0); |
||||||
|
Serial.print("% of "); |
||||||
|
Serial.print(dtcm * 32); |
||||||
|
Serial.print("kb "); |
||||||
|
Serial.print(" ("); |
||||||
|
Serial.print(dtcm * 32768 - ((unsigned)&_ebss - (unsigned)&_sdata)); |
||||||
|
Serial.println(" Bytes free)"); |
||||||
|
|
||||||
|
Serial.println("RAM2:"); |
||||||
|
Serial.print("OCRAM = DMAMEM: "); |
||||||
|
Serial.print((unsigned)&_heap_start - 0x20200000); |
||||||
|
Serial.print(" "); |
||||||
|
Serial.print((float)((unsigned)&_heap_start - 0x20200000) / ((float)512 * 1024.0) * 100.0); |
||||||
|
Serial.print("% of "); |
||||||
|
Serial.print(512); |
||||||
|
Serial.print("kb"); |
||||||
|
Serial.print(" ("); |
||||||
|
Serial.print(512 * 1024 - ((unsigned)&_heap_start - 0x20200000)); |
||||||
|
Serial.println(" Bytes free)"); |
||||||
|
|
||||||
|
Serial.print("FLASH: "); |
||||||
|
Serial.print((unsigned)&_flashimagelen); |
||||||
|
Serial.print(" "); |
||||||
|
Serial.print(((unsigned)&_flashimagelen) / (2048.0 * 1024.0) * 100.0); |
||||||
|
Serial.print("% of "); |
||||||
|
Serial.print(2048); |
||||||
|
Serial.print("kb"); |
||||||
|
Serial.print(" ("); |
||||||
|
Serial.print(2048 * 1024 - ((unsigned)&_flashimagelen)); |
||||||
|
Serial.println(" Bytes free)"); |
||||||
|
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void i2s_set_rev_send(float32_t lvl) |
||||||
|
{ |
||||||
|
lvl = constrain(lvl, 0.0, 1.0); |
||||||
|
reverb_send_L.gain(I2S_REVERB_SEND_CH, lvl); |
||||||
|
reverb_send_R.gain(I2S_REVERB_SEND_CH, lvl); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void reverb_set_volume(float32_t lvl) |
||||||
|
{ |
||||||
|
lvl = constrain(lvl, 0.0, 1.0); |
||||||
|
mixer_out_L.gain(REVERB_MIX_CH, lvl); |
||||||
|
mixer_out_R.gain(REVERB_MIX_CH, lvl); |
||||||
|
} |
||||||
|
|
||||||
|
void wav_set_rev_send(float32_t lvl) |
||||||
|
{ |
||||||
|
lvl = constrain(lvl, 0.0, 1.0); |
||||||
|
reverb_send_L.gain(SDWAV_REVERB_SEND_CH, lvl); |
||||||
|
reverb_send_R.gain(SDWAV_REVERB_SEND_CH, lvl);
|
||||||
|
} |
After Width: | Height: | Size: 26 KiB |
@ -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[2303]; // loop allpass buffers
|
||||||
|
float32_t DMAMEM lp_allp2_buf[2905]; |
||||||
|
float32_t DMAMEM lp_allp3_buf[3175]; |
||||||
|
float32_t DMAMEM lp_allp4_buf[2398]; |
||||||
|
|
||||||
|
float32_t DMAMEM lp_dly1_buf[3423]; |
||||||
|
float32_t DMAMEM lp_dly2_buf[4589]; |
||||||
|
float32_t DMAMEM lp_dly3_buf[4365]; |
||||||
|
float32_t DMAMEM lp_dly4_buf[3698]; |
||||||
|
#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, 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[2303]; // loop allpass buffers
|
||||||
|
float32_t lp_allp2_buf[2905]; |
||||||
|
float32_t lp_allp3_buf[3175]; |
||||||
|
float32_t lp_allp4_buf[2398]; |
||||||
|
#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[3423]; |
||||||
|
float32_t lp_dly2_buf[4589]; |
||||||
|
float32_t lp_dly3_buf[4365]; |
||||||
|
float32_t lp_dly4_buf[3698]; |
||||||
|
#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
|
@ -1,2 +1,50 @@ |
|||||||
# t40fx |
# t40fx |
||||||
Teensy4.0 Audio Lib Components |
Teensy4.0 Audio Lib Components: |
||||||
|
|
||||||
|
## Stereo Plate Reverb |
||||||
|
--- |
||||||
|
Fully stereo in/out reverb component for the standard 16bit Audio library. |
||||||
|
|
||||||
|
### Connections: |
||||||
|
Reverb requires stereo in and out connenctions. |
||||||
|
### API: |
||||||
|
|
||||||
|
```void size(float32_t n);``` |
||||||
|
sets the reverb time. Parameter range: 0.0 to 1.0. |
||||||
|
Example: |
||||||
|
```reverb.size(1.0); // set the reverb time to maximum``` |
||||||
|
|
||||||
|
|
||||||
|
```void lowpass(float32_t n);``` |
||||||
|
sets the reverb master lowpass filter. Parameter range: 0.0 to 1.0. |
||||||
|
Example: |
||||||
|
```reverb.lowpass(0.7); // darken the reverb sound``` |
||||||
|
|
||||||
|
|
||||||
|
```void hidamp(float32_t n);``` |
||||||
|
sets the treble loss. Parameter range: 0.0 to 1.0. |
||||||
|
Example: |
||||||
|
```reverb.hidamp(1.0); // max hi band dampening results in darker sound ``` |
||||||
|
|
||||||
|
|
||||||
|
```void lodamp(float32_t n);``` |
||||||
|
sets the bass cut. Parameter range: 0.0 to 1.0. |
||||||
|
Example: |
||||||
|
```reverb.lodamp(0.5); // cut more bass in the reverb tail to make the sound brighter ``` |
||||||
|
|
||||||
|
Audio connections used in the exmaple project: |
||||||
|
![alt text][pic1] |
||||||
|
|
||||||
|
### Additional config: |
||||||
|
|
||||||
|
by default the reverb places it's buffers into OCRAM/DMAMEM region. |
||||||
|
Comment out the |
||||||
|
```#define REVERB_USE_DMAMEM``` |
||||||
|
line in the ```effect_platervbstereo.h``` file to place the variables into the DCTM ram region. |
||||||
|
___ |
||||||
|
|
||||||
|
Copyright 12.2020 by Piotr Zapart |
||||||
|
www.hexefx.com |
||||||
|
|
||||||
|
|
||||||
|
[pic1]: effect_platervbstereo/StereoPlateReverb.png "Stereo plate reverb connections" |
Loading…
Reference in new issue