From 6eef3f91079b5a3397743d2ef759ad1a9f88d1ed Mon Sep 17 00:00:00 2001 From: pio Date: Wed, 7 Feb 2024 15:33:10 +0100 Subject: [PATCH] delay stereo, basic comp updates --- README.md | 7 + keywords.txt | 27 +++ src/basic_delay.h | 30 ++-- src/basic_lfo.h | 24 ++- src/effect_delaystereo.cpp | 204 ++++++++++++++++++++++ src/effect_delaystereo.h | 292 ++++++++++++++++++++++++++++++++ src/effect_platereverb_F32.cpp | 11 +- src/effect_platereverb_F32.h | 12 +- src/effect_reverbsc_F32.cpp | 3 +- src/effect_reverbsc_F32.h | 4 +- src/effect_springreverb_F32.cpp | 27 ++- src/effect_springreverb_F32.h | 4 +- src/filter_ir_cabsim_F32.cpp | 56 +++--- src/filter_ir_cabsim_F32.h | 32 ++-- src/hexefx_audio_F32.h | 2 +- 15 files changed, 644 insertions(+), 91 deletions(-) create mode 100644 src/effect_delaystereo.cpp create mode 100644 src/effect_delaystereo.h diff --git a/README.md b/README.md index eb4215f..1d5cc97 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ Stereo spring reverb emulation. 8 delay line stereo FDN reverb, based on work by Sean Costello. Optional PSRAM use for the delay buffers. +**AudioEffectDelayStereo_F32** +Versatile stereo ping-pong delay with modulation. + **AudioEffectNoiseGateStereo_F32** Stereo noise gate with external SideChain input. @@ -37,6 +40,10 @@ Stereo guitar/bass cabinet emulator using low latency uniformly partitioned conv **AudioFilterEqualizer3band_F32** Simple 3 band (Treble, Mid, Bass) equalizer. +**AudioEffectGainStereo_F32** +Stereo gain control (volume etc.) + +** ## I/O **AudioInputI2S2_F32** **AudioOutputI2S2_F32** diff --git a/keywords.txt b/keywords.txt index f9995c2..9c4ace6 100644 --- a/keywords.txt +++ b/keywords.txt @@ -82,6 +82,33 @@ setBass KEYWORD2 setMid KEYWORD2 setTreble KEYWORD2 +AudioEffectNoiseGateStereo_F32 KEYWORD1 + +AudioFilterEqualizer3band_F32 KEYWORD1 +setBands KEYWORD2 +treble KEYWORD2 +mid KEYWORD2 +bass KEYWORD2 +set KEYWORD2 + +AudioEffectSpringReverb_F32 KEYWORD1 +time KEYWORD2 +treble_cut KEYWORD2 +bass_cut KEYWORD2 + +AudioEffectDelayStereo_F32 KEYWORD1 +delay KEYWORD2 +feedback KEYWORD2 +inertia +mod_rateHz KEYWORD2 +mod_rate KEYWORD2 +mod_depth KEYWORD2 +tap_tempo KEYWORD2 + +AudioEffectReverbSc_F32 KEYWORD1 +lowpass KEYWORD2 + + memcpyInterleave_f32 KEYWORD2 memcpyDeinterleave_f32 KEYWORD2 diff --git a/src/basic_delay.h b/src/basic_delay.h index 5db405e..5d264d2 100644 --- a/src/basic_delay.h +++ b/src/basic_delay.h @@ -19,7 +19,7 @@ * * @tparam N delay length in samples (float) */ -template +//template class AudioBasicDelay { public: @@ -27,9 +27,12 @@ public: { if(bf) free(bf); } - bool init() + bool init(uint32_t size_samples, bool psram=false) { - bf = (float *)malloc(N*sizeof(float)); // allocate buffer + if(bf) free(bf); + size = size_samples; + if (psram) bf = (float *)extmem_malloc(size * sizeof(float)); // allocate buffer + else bf = (float *)malloc(size * sizeof(float)); // allocate buffer if (!bf) return false; idx = 0; reset(); @@ -37,7 +40,7 @@ public: } void reset() { - memset(bf, 0, N*sizeof(float)); + memset(bf, 0, size * sizeof(float)); } /** * @brief get the tap from the delay buffer @@ -49,10 +52,10 @@ public: { int32_t read_idx, read_idx_next; read_idx = idx - offset; - if (read_idx < 0) read_idx += N; + if (read_idx < 0) read_idx += size; if (frac == 0.0f) return bf[read_idx]; read_idx_next = read_idx - 1; - if (read_idx_next < 0) read_idx_next += N; + if (read_idx_next < 0) read_idx_next += size; return (bf[read_idx]*(1.0f-frac) + bf[read_idx_next]*frac); } @@ -61,11 +64,11 @@ public: int32_t delay_integral = static_cast(delay); float delay_fractional = delay - static_cast(delay_integral); - int32_t t = (idx + delay_integral + N); - const float xm1 = bf[(t - 1) % N]; - const float x0 = bf[(t) % N]; - const float x1 = bf[(t + 1) % N]; - const float x2 = bf[(t + 2) % N]; + int32_t t = (idx + delay_integral + size); + const float xm1 = bf[(t - 1) % size]; + const float x0 = bf[(t) % size]; + const float x1 = bf[(t + 1) % size]; + const float x2 = bf[(t + 2) % size]; const float c = (x1 - xm1) * 0.5f; const float v = x0 - x1; const float w = c + v; @@ -92,14 +95,15 @@ public: { int32_t write_idx; write_idx = idx - offset; - if (write_idx < 0) write_idx += N; + if (write_idx < 0) write_idx += size; bf[write_idx] = newSample; } inline void updateIndex() { - if (++idx >= N) idx = 0; + if (++idx >= size) idx = 0; } private: + int32_t size; float *bf; int32_t idx; }; diff --git a/src/basic_lfo.h b/src/basic_lfo.h index 6c5d233..c0d3aa0 100644 --- a/src/basic_lfo.h +++ b/src/basic_lfo.h @@ -5,7 +5,10 @@ #include "AudioStream_F32.h" #define BASIC_LFO_PHASE_0 (0) +#define BASIC_LFO_PHASE_45 (32) +#define BASIC_LFO_PHASE_60 (43) #define BASIC_LFO_PHASE_90 (64) +#define BASIC_LFO_PHASE_120 (85) #define BASIC_LFO_PHASE_180 (128) extern "C" { @@ -22,7 +25,13 @@ public: AudioBasicLfo(float rateHz, uint32_t ampl) { acc = 0; - divider = (0x7FFF + (ampl>>1)) / ampl; + if (!ampl) + { + state = false; + divider = 0x8000; + } + else divider = (0x7FFF + (ampl>>1)) / ampl; + state = true; adder = (uint32_t)(rateHz * rate_mult); } @@ -39,6 +48,12 @@ public: */ inline void get(uint8_t phase8bit, uint32_t *intOffset, float *fractOffset) { + if (!state) // lfo off + { + *intOffset = 0; + *fractOffset = 0.0f; + return; + } uint32_t idx; uint32_t y0, y1; uint64_t y; @@ -59,9 +74,16 @@ public: } inline void setDepth(uint32_t ampl) { + if (!ampl) + { + state = false; + return; // do not update the divider + } + state = true; divider = (0x7FFF + (ampl>>1)) / ampl; } private: + bool state = true; uint32_t acc; uint32_t adder; int32_t divider = 1; diff --git a/src/effect_delaystereo.cpp b/src/effect_delaystereo.cpp new file mode 100644 index 0000000..a107c95 --- /dev/null +++ b/src/effect_delaystereo.cpp @@ -0,0 +1,204 @@ +/* Stereo Ping Pong delay for Teensy 4 + * + * Author: Piotr Zapart + * www.hexefx.com + * + * Copyright (c) 2024 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. + */ +#include "effect_delaystereo.h" + +#define TREBLE_LOSS_FREQ (0.20f) +#define BASS_LOSS_FREQ (0.05f) +#define BASS_FREQ (0.15f) + + + +AudioEffectDelayStereo_F32::AudioEffectDelayStereo_F32(uint32_t dly_range_ms, bool use_psram) : AudioStream_F32(2, inputQueueArray) +{ + psram_mode = use_psram; + bool memOk = true; + dly_length = ((float32_t)(dly_range_ms+500)/1000.0f) * AUDIO_SAMPLE_RATE_EXACT; + if (!dly0a.init(dly_length, use_psram)) memOk = false; + if (!dly0b.init(dly_length, use_psram)) memOk = false; + if (!dly1a.init(dly_length, use_psram)) memOk = false; + if (!dly1b.init(dly_length, use_psram)) memOk = false; + flt0L.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k); + flt1L.init(BASS_LOSS_FREQ, &bass_k, TREBLE_LOSS_FREQ, &treble_k); + flt0R.init(BASS_LOSS_FREQ, &bassCut_k, TREBLE_LOSS_FREQ, &trebleCut_k); + flt1R.init(BASS_LOSS_FREQ, &bass_k, TREBLE_LOSS_FREQ, &treble_k); + mix(0.5f); + feedback(0.5f); + cleanup_done = true; + if (memOk) initialized = true; +} + +void AudioEffectDelayStereo_F32::update() +{ + if (!initialized) return; + if (!memsetup_done) + { + dly0a.reset(); + dly0b.reset(); + dly1a.reset(); + dly1b.reset(); + memsetup_done = true; + return; + } + + audio_block_f32_t *blockL, *blockR; + int i; + float32_t acc1, acc2, outL, outR, mod_fr[4]; + uint32_t mod_int; + static float32_t dly_time_flt = 0.0f; + + if (bp) + { + if (!cleanup_done) + { + dly0a.reset(); + dly0b.reset(); + dly1a.reset(); + dly1b.reset(); + cleanup_done = true; + tap_active = false; // reset tap tempo + tap_counter = 0; + } + if (dry_gain > 0.0f) // if dry/wet mixer is used + { + blockL = AudioStream_F32::receiveReadOnly_f32(0); + blockR = AudioStream_F32::receiveReadOnly_f32(1); + if (!blockL || !blockR) + { + if (blockL) AudioStream_F32::release(blockL); + if (blockR) AudioStream_F32::release(blockR); + return; + } + AudioStream_F32::transmit(blockL, 0); + AudioStream_F32::transmit(blockR, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); + return; + } + blockL = AudioStream_F32::allocate_f32(); + if (!blockL) return; + memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t)); + AudioStream_F32::transmit(blockL, 0); + AudioStream_F32::transmit(blockL, 1); + AudioStream_F32::release(blockL); + return; + } + cleanup_done = false; + blockL = AudioStream_F32::receiveWritable_f32(0); + blockR = AudioStream_F32::receiveWritable_f32(1); + if (!blockL || !blockR) + { + if (blockL) + AudioStream_F32::release(blockL); + if (blockR) + AudioStream_F32::release(blockR); + return; + } + for (i=0; i < blockL->length; i++) + { + // tap tempo + if (tap_active) + { + if (++tap_counter > tap_counter_max) + { + tap_active = false; + tap_counter = 0; + } + } + + if (dly_time < dly_time_set) + { + dly_time += dly_time_step; + if (dly_time > dly_time_set) dly_time = dly_time_set; + } + if (dly_time > dly_time_set) + { + dly_time -= dly_time_step; + if (dly_time < dly_time_set) dly_time = dly_time_set; + } + // lowpass the dely time + acc1 = dly_time - dly_time_flt; + dly_time_flt += acc1 * 0.1f; + dly_time = dly_time_flt; + + lfo.update(); + + lfo.get(BASIC_LFO_PHASE_0, &mod_int, &mod_fr[0]); + mod_fr[0] = (float32_t)mod_int + mod_fr[0]; + acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[0]); + if (acc2 < 0.0f) mod_fr[0] += acc2; + + lfo.get(BASIC_LFO_PHASE_60, &mod_int, &mod_fr[1]); + mod_fr[1] = (float32_t)mod_int + mod_fr[1]; + acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[1]); + if (acc2 < 0.0f) mod_fr[1] += acc2; + + lfo.get(BASIC_LFO_PHASE_120, &mod_int, &mod_fr[2]); + mod_fr[2] = (float32_t)mod_int + mod_fr[2]; + acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[2]); + if (acc2 < 0.0f) mod_fr[2] += acc2; + + lfo.get(BASIC_LFO_PHASE_180, &mod_int, &mod_fr[3]); + mod_fr[3] = (float32_t)mod_int + mod_fr[3]; + acc2 = (float32_t)dly_length - 1.0f - (dly_time + mod_fr[3]); + if (acc2 < 0.0f) mod_fr[3] += acc2; + + float32_t idx = dly_time + mod_fr[0]; + if (idx > maxV) maxV = idx; + if (idx < minV) minV = idx; + + acc1 = dly0b.getTapHermite(dly_time+mod_fr[0]); + outR = acc1 * 0.6f; + acc1 = flt0R.process(acc1) * feedb; + acc1 += blockR->data[i] * input_attn; + acc1 = flt1R.process(acc1); + acc2 = dly0a.getTapHermite(dly_time+mod_fr[1]); + dly0b.write_toOffset(acc2, 0); + outL = acc2 * 0.6f; + dly0a.write_toOffset(acc1, 0); + + acc1 = dly1b.getTapHermite(dly_time+mod_fr[2]); + outR += acc1 * 0.6f; + acc1 = flt0L.process(acc1) * feedb; + acc1 += blockL->data[i] * input_attn; + acc1 = flt1L.process(acc1); + acc2 = dly1a.getTapHermite(dly_time+mod_fr[3]); + dly1b.write_toOffset(acc2, 0); + outL += acc2 * 0.6f; + dly1a.write_toOffset(acc1, 0); + + dly0a.updateIndex(); + dly0b.updateIndex(); + dly1a.updateIndex(); + dly1b.updateIndex(); + blockL->data[i] = outL * wet_gain + blockL->data[i] * dry_gain; + blockR->data[i] = outR * wet_gain + blockR->data[i] * dry_gain; + + } + AudioStream_F32::transmit(blockL, 0); + AudioStream_F32::transmit(blockR, 1); + AudioStream_F32::release(blockL); + AudioStream_F32::release(blockR); +} \ No newline at end of file diff --git a/src/effect_delaystereo.h b/src/effect_delaystereo.h new file mode 100644 index 0000000..b3dc131 --- /dev/null +++ b/src/effect_delaystereo.h @@ -0,0 +1,292 @@ +/* Stereo Ping Pong delay for Teensy 4 + * + * Author: Piotr Zapart + * www.hexefx.com + * + * Copyright (c) 2024 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. + */ +#ifndef _EFFECT_DELAYSTEREO_H_ +#define _EFFECT_DELAYSTEREO_H_ + +#include +#include "Audio.h" +#include "AudioStream.h" +#include "AudioStream_F32.h" +#include "arm_math.h" +#include "basic_components.h" + +class AudioEffectDelayStereo_F32 : public AudioStream_F32 +{ +public: + AudioEffectDelayStereo_F32(uint32_t dly_range_ms=400, bool use_psram=false); + ~AudioEffectDelayStereo_F32(){}; + virtual void update(); + /** + * @brief delay time + * + * @param t scaled to 0.0f-1.0f range + */ + void time(float t) + { + t = constrain(t, 0.0f, 1.0f); + t = t * t; + t = map(t, 0.0f, 1.0f, (float32_t)(dly_length-dly_time_min), 0.0f); + __disable_irq(); + dly_time_set = t; + __enable_irq(); + } + /** + * @brief delay time set in samples + * + * @param samples range is from 0 to delay buffer lengts - minimum delay + */ + void delay(uint32_t samples) + { + samples = constrain(samples, 0u, dly_length-dly_time_min); + samples = dly_length-dly_time_min - samples; + __disable_irq(); + dly_time_set = samples; + __enable_irq(); + } + /** + * @brief Amount of repeats + * + * @param n 0.0f-1.0f range + */ + void feedback(float n) + { + float32_t fb, attn; + n = constrain(n, 0.0f, 1.0f); + fb = map(n, 0.0f, 1.0f, 0.0f, feedb_max) * hp_feedb_limit; + attn = map(n*n*n, 0.0f, 1.0f, 1.0f, 0.4f); + __disable_irq(); + feedb = fb; + input_attn = attn; + __enable_irq(); + } + /** + * @brief How fast the delay time is updated + * emulates analog tape machines + * + * @param n 0.0f-1.0f range, 0 - fastest update + */ + void inertia(float n) + { + n = constrain(n, 0.0f, 1.0f); + n = 2.0f * n - (n*n); + n = map (n, 0.0f, 1.0f, 10.0f, 0.3f); + __disable_irq(); + dly_time_step = n; + __enable_irq(); + } + /** + * @brief Output treble control + * + * @param n 0.0f-1.0f range + */ + void treble(float n) + { + n = constrain(n, 0.0f, 1.0f); + __disable_irq(); + treble_k = n; + __enable_irq(); + } + /** + * @brief Treble loss control (darkens the repeats) + * + * @param n 0.0f-1.0f range + */ + void treble_cut(float n) + { + n = 1.0f - constrain(n, 0.0f, 1.0f); + __disable_irq(); + trebleCut_k = n; + __enable_irq(); + } + /** + * @brief Output bass control + * + * @param n 0.0f-1.0f range + */ + void bass(float n) + { + n = constrain(n, 0.0f, 1.0f); + n = 1.0f - 2.0f*n + (n*n); + __disable_irq(); + bass_k = -n; + __enable_irq(); + } + /** + * @brief Bass loss (repeats will loose low end) + * + * @param n 0.0f-1.0f range + */ + void bass_cut(float n) + { + n = constrain(n, 0.0f, 1.0f); + n = 2.0f * n - (n*n); + __disable_irq(); + bassCut_k = -n; + __enable_irq(); + } + /** + * @brief dry/wet mixer + * 0 = dry only, 1=wet only + * + * @param m 0.0f-1-0f range + */ + void mix(float m) + { + float32_t dry, wet; + m = constrain(m, 0.0f, 1.0f); + mix_pwr(m, &wet, &dry); + __disable_irq(); + wet_gain = wet; + dry_gain = dry; + __enable_irq(); + } + /** + * @brief Modulation frequency in Hz + * + * @param f range 0.0f - 16.0f + */ + void mod_rateHz(float32_t f) + { + f = constrain(f, 0.0f, 16.0f); + __disable_irq(); + lfo.setRate(f); + __enable_irq(); + } + /** + * @brief modulation frequency scaled to 0.0f-1.0f range + * + * @param r rate + */ + void mod_rate(float32_t r) + { + r = constrain(r*r*r, 0.0f, 1.0f); + r = map(r, 0.0f, 1.0f, 0.0f, lfo_fmax); + __disable_irq(); + lfo.setRate(r); + __enable_irq(); + } + /** + * @brief Modulation depth + * + * @param d 0.0f-1-0f range + */ + void mod_depth(float32_t d) + { + d = constrain(d, 0.0f, 1.0f); + d = map(d, 0.0f, 1.0f, 0.0f, lfo_ampl_max); + __disable_irq(); + lfo.setDepth(d); + __enable_irq(); + } + + bool bypass_get(void) {return bp;} + void bypass_set(bool state) {bp = state;} + bool bypass_tgl(void) + { + bp ^= 1; + return bp; + } + + uint32_t tap_tempo(bool avg=true) + { + int32_t delta; + uint32_t tempo_ticks = 0; + if (!tap_active) + { + tap_counter = 0; + tap_active = true; + } + else + { + __disable_irq(); + tap_counter_new = tap_counter; + tap_counter = 0; + __enable_irq(); + delta = tap_counter_new - tap_counter_last; + if (abs(delta) > tap_counter_deltamax || !avg) // new tempo? + { + tempo_ticks = tap_counter_new; + } + else + { + tempo_ticks = (tap_counter_new>>1) + (tap_counter_last>>1); + } + while (tempo_ticks > dly_length - dly_time_min) + { + tempo_ticks >>= 1; + } + tap_counter_last = tempo_ticks; + delay(tempo_ticks); + } + return tempo_ticks; + } + +private: + audio_block_f32_t *inputQueueArray[2]; + + uint32_t dly_length; + AudioBasicDelay dly0a; + AudioBasicDelay dly0b; + AudioBasicDelay dly1a; + AudioBasicDelay dly1b; + + AudioFilterShelvingLPHP flt0L; + AudioFilterShelvingLPHP flt1L; + AudioFilterShelvingLPHP flt0R; + AudioFilterShelvingLPHP flt1R; + + static constexpr float32_t lfo_fmax = 16.0f; + static constexpr float32_t lfo_ampl_max = 127.0f; + float32_t lfo_ampl = 0.0f; + AudioBasicLfo lfo = AudioBasicLfo(0.0f, lfo_ampl); + bool psram_mode; + bool memsetup_done = false; + bool bp = false; + bool cleanup_done = false; + + static constexpr float32_t feedb_max = 0.96f; + float32_t feedb = 0; + float32_t hp_feedb_limit = 1.0f; + float32_t wet_gain; + float32_t dry_gain; + float32_t input_attn = 1.0f; + float32_t trebleCut_k = 1.0f; + float32_t bassCut_k = 0.0f; + float32_t treble_k = 1.0f; + float32_t bass_k = 0.0f; + float32_t dly_time, dly_time_set; + float32_t dly_time_step = 10.0f; + static const uint32_t dly_time_min = 128; + bool initialized = false; + + bool tap_active = false; + uint32_t tap_counter = 0; + uint32_t tap_counter_last=0, tap_counter_new=0; + static const uint32_t tap_counter_max = 3000*AUDIO_SAMPLE_RATE; // 3 sec + static const int32_t tap_counter_deltamax = 0.3f*AUDIO_SAMPLE_RATE_EXACT; +}; + +#endif // _EFFECT_DELAYSTEREO_H_ diff --git a/src/effect_platereverb_F32.cpp b/src/effect_platereverb_F32.cpp index 071e142..efae55c 100644 --- a/src/effect_platereverb_F32.cpp +++ b/src/effect_platereverb_F32.cpp @@ -74,10 +74,10 @@ bool AudioEffectPlateReverb_F32::begin() lp_allp_out = 0.0f; - if(!lp_dly1.init()) return false; - if(!lp_dly2.init()) return false; - if(!lp_dly3.init()) return false; - if(!lp_dly4.init()) return false; + if(!lp_dly1.init(LP_DLY1_BUF_LEN)) return false; + if(!lp_dly2.init(LP_DLY2_BUF_LEN)) return false; + if(!lp_dly3.init(LP_DLY3_BUF_LEN)) return false; + if(!lp_dly4.init(LP_DLY4_BUF_LEN)) return false; lp_hidamp_k = 1.0f; lp_lodamp_k = 0.0f; @@ -164,10 +164,11 @@ void AudioEffectPlateReverb_F32::update() AudioStream_F32::transmit(blockR, 1); AudioStream_F32::release(blockL); AudioStream_F32::release(blockR); + return; } blockL = AudioStream_F32::allocate_f32(); if (!blockL) return; - arm_fill_f32(0.0f, blockL->data, blockL->length); + memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t)); AudioStream_F32::transmit(blockL, 0); AudioStream_F32::transmit(blockL, 1); AudioStream_F32::release(blockL); diff --git a/src/effect_platereverb_F32.h b/src/effect_platereverb_F32.h index 51eba6c..6ff9c2a 100644 --- a/src/effect_platereverb_F32.h +++ b/src/effect_platereverb_F32.h @@ -1,4 +1,4 @@ -/* Stereo plate reverb for ESP32 +/* Stereo plate reverb for Teensy 4 * * Author: Piotr Zapart * www.hexefx.com @@ -265,7 +265,7 @@ private: { unsigned bypass: 1; unsigned freeze: 1; - unsigned shimmer: 1; // maybe will be added at some point + unsigned shimmer: 1; unsigned cleanup_done: 1; }flags; audio_block_f32_t *inputQueueArray_f32[2]; @@ -331,10 +331,10 @@ private: float loop_allp_k; // loop allpass coeff (default 0.6) float lp_allp_out; - AudioBasicDelay lp_dly1; - AudioBasicDelay lp_dly2; - AudioBasicDelay lp_dly3; - AudioBasicDelay lp_dly4; + AudioBasicDelay lp_dly1; + AudioBasicDelay lp_dly2; + AudioBasicDelay lp_dly3; + AudioBasicDelay lp_dly4; float lp_hidamp_k, lp_hidamp_k_tmp; // loop high band damping coeff float lp_lodamp_k, lp_lodamp_k_tmp; // loop low band damping coeff diff --git a/src/effect_reverbsc_F32.cpp b/src/effect_reverbsc_F32.cpp index 943f509..e97e94a 100644 --- a/src/effect_reverbsc_F32.cpp +++ b/src/effect_reverbsc_F32.cpp @@ -169,10 +169,11 @@ void AudioEffectReverbSc_F32::update() AudioStream_F32::transmit(blockR, 1); AudioStream_F32::release(blockL); AudioStream_F32::release(blockR); + return; } blockL = AudioStream_F32::allocate_f32(); if (!blockL) return; - arm_fill_f32(0.0f, blockL->data, blockL->length); + memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t)); AudioStream_F32::transmit(blockL, 0); AudioStream_F32::transmit(blockL, 1); AudioStream_F32::release(blockL); diff --git a/src/effect_reverbsc_F32.h b/src/effect_reverbsc_F32.h index 4935832..e683b88 100644 --- a/src/effect_reverbsc_F32.h +++ b/src/effect_reverbsc_F32.h @@ -19,7 +19,7 @@ #ifndef _EFFECT_REVERBSC_F32_H_ #define _EFFECT_REVERBSC_F32_H_ - + #include #include "Audio.h" #include "AudioStream.h" @@ -137,7 +137,7 @@ private: float32_t damp_fact_, damp_fact_tmp; bool initialised = false; ReverbScDl_t delay_lines_[8]; - float32_t *aux_; // main delay line storage buffer, placed either in RAM2 or PSRAM + float32_t *aux_; // main delay line storage buffer, placed either in RAM2 or PSRAM float32_t dry_gain = 0.5f; float32_t wet_gain = 0.5f; diff --git a/src/effect_springreverb_F32.cpp b/src/effect_springreverb_F32.cpp index aadd33d..edb81d6 100644 --- a/src/effect_springreverb_F32.cpp +++ b/src/effect_springreverb_F32.cpp @@ -52,8 +52,8 @@ AudioEffectSpringReverb_F32::AudioEffectSpringReverb_F32() : AudioStream_F32(2, if(!sp_lp_allp2b.init(&in_allp_k)) memOK = false; if(!sp_lp_allp2c.init(&in_allp_k)) memOK = false; if(!sp_lp_allp2d.init(&in_allp_k)) memOK = false; - if(!lp_dly1.init()) memOK = false; - if(!lp_dly2.init()) memOK = false; + if(!lp_dly1.init(SPRVB_DLY1_LEN)) memOK = false; + if(!lp_dly2.init(SPRVB_DLY2_LEN)) memOK = false; // chirp allpass chain sp_chrp_alp1_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); sp_chrp_alp2_buf = (float *)malloc(SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); @@ -63,12 +63,10 @@ AudioEffectSpringReverb_F32::AudioEffectSpringReverb_F32() : AudioStream_F32(2, if (!sp_chrp_alp2_buf) memOK = false; if (!sp_chrp_alp3_buf) memOK = false; if (!sp_chrp_alp4_buf) memOK = false; - - memset(sp_chrp_alp1_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); - memset(sp_chrp_alp2_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); - memset(sp_chrp_alp3_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); - memset(sp_chrp_alp4_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); - + memset(&sp_chrp_alp1_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); + memset(&sp_chrp_alp2_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); + memset(&sp_chrp_alp3_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); + memset(&sp_chrp_alp4_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); in_BassCut_k = 0.0f; in_TrebleCut_k = 0.95f; lp_BassCut_k = 0.0f; @@ -108,10 +106,10 @@ void AudioEffectSpringReverb_F32::update() sp_lp_allp2d.reset(); lp_dly1.reset(); lp_dly2.reset(); - memset(sp_chrp_alp1_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); - memset(sp_chrp_alp2_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); - memset(sp_chrp_alp3_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); - memset(sp_chrp_alp4_buf, 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); + memset(&sp_chrp_alp1_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP1_LEN*sizeof(float)); + memset(&sp_chrp_alp2_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP2_LEN*sizeof(float)); + memset(&sp_chrp_alp3_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP3_LEN*sizeof(float)); + memset(&sp_chrp_alp4_buf[0], 0, SPRVB_CHIRP_AMNT*SPRVB_CHIRP4_LEN*sizeof(float)); cleanup_done = true; } @@ -129,10 +127,11 @@ void AudioEffectSpringReverb_F32::update() AudioStream_F32::transmit(blockR, 1); AudioStream_F32::release(blockL); AudioStream_F32::release(blockR); + return; } blockL = AudioStream_F32::allocate_f32(); if (!blockL) return; - arm_fill_f32(0.0f, blockL->data, blockL->length); + memset(&blockL->data[0], 0, blockL->length*sizeof(float32_t)); AudioStream_F32::transmit(blockL, 0); AudioStream_F32::transmit(blockL, 1); AudioStream_F32::release(blockL); @@ -405,4 +404,4 @@ void AudioEffectSpringReverb_F32::update() AudioStream_F32::release(blockL); AudioStream_F32::release(blockR); #endif -} \ No newline at end of file +} diff --git a/src/effect_springreverb_F32.h b/src/effect_springreverb_F32.h index 6c81366..d292532 100644 --- a/src/effect_springreverb_F32.h +++ b/src/effect_springreverb_F32.h @@ -160,8 +160,8 @@ private: float32_t *sp_chrp_alp3_buf; float32_t *sp_chrp_alp4_buf; - AudioBasicDelay lp_dly1; - AudioBasicDelay lp_dly2; + AudioBasicDelay lp_dly1; + AudioBasicDelay lp_dly2; float32_t in_TrebleCut_k; float32_t in_BassCut_k; diff --git a/src/filter_ir_cabsim_F32.cpp b/src/filter_ir_cabsim_F32.cpp index fa5798d..d737cf0 100644 --- a/src/filter_ir_cabsim_F32.cpp +++ b/src/filter_ir_cabsim_F32.cpp @@ -21,29 +21,20 @@ * If not, see ." */ #include "filter_ir_cabsim_F32.h" -#include #include "HxFx_memcpy.h" -float32_t DMAMEM maskgen[IR_FFT_LENGTH * 2]; -float32_t DMAMEM fmask[IR_NFORMAX][IR_FFT_LENGTH * 2]; // -float32_t DMAMEM fftin[IR_FFT_LENGTH * 2]; -float32_t DMAMEM accum[IR_FFT_LENGTH * 2]; - -float32_t DMAMEM last_sample_buffer_L[IR_BUFFER_SIZE * IR_N_B]; -float32_t DMAMEM last_sample_buffer_R[IR_BUFFER_SIZE * IR_N_B]; -float32_t DMAMEM fftout[IR_NFORMAX][IR_FFT_LENGTH * 2]; -float32_t DMAMEM ac2[512]; - -const static arm_cfft_instance_f32 *S; -// complex iFFT with the new library CMSIS V4.5 -const static arm_cfft_instance_f32 *iS; -// FFT instance for direct calculation of the filter mask -// from the impulse response of the FIR - the coefficients -const static arm_cfft_instance_f32 *maskS; - AudioFilterIRCabsim_F32::AudioFilterIRCabsim_F32() : AudioStream_F32(2, inputQueueArray_f32) { - if (!delay.init()) return; + if (!delay.init(delay_l)) return; + last_sample_buffer_L = (float32_t*)malloc(IR_BUFFER_SIZE * IR_N_B * sizeof(float32_t)); + last_sample_buffer_R = (float32_t*)malloc(IR_BUFFER_SIZE * IR_N_B * sizeof(float32_t)); + maskgen = (float32_t*)malloc(IR_FFT_LENGTH * 2 * sizeof(float32_t)); + fftout = (float32_t*)malloc(IR_NFORMAX * IR_FFT_LENGTH * 2 * sizeof(float32_t)); + if (!last_sample_buffer_L || !last_sample_buffer_R || !maskgen || !fftout) return; + + memset(maskgen, 0, IR_FFT_LENGTH * 2 * sizeof(float32_t)); + memset(fftout, 0, IR_NFORMAX * IR_FFT_LENGTH * 2 * sizeof(float32_t)); + arm_fir_init_f32(&FIR_preL, nfir, (float32_t *)FIRk_preL, &FIRstate[0][0], (uint32_t)block_size); arm_fir_init_f32(&FIR_preR, nfir, (float32_t *)FIRk_preR, &FIRstate[1][0], (uint32_t)block_size); arm_fir_init_f32(&FIR_postL, nfir, (float32_t *)FIRk_postL, &FIRstate[2][0], (uint32_t)block_size); @@ -69,17 +60,15 @@ void AudioFilterIRCabsim_F32::update() if (!ir_loaded) // ir not loaded yet or bypass mode { // bypass clean signal - // TODO: Add bypass signal gain stage to match the processed signal volume AudioStream_F32::transmit(blockL, 0); AudioStream_F32::release(blockL); AudioStream_F32::transmit(blockR, 1); AudioStream_F32::release(blockR); return; } - if (first_block) // fill real & imaginaries with zeros for the first BLOCKSIZE samples { - arm_fill_f32(0.0f, fftin, blockL->length * 4); + memset(&fftin[0], 0, blockL->length*sizeof(float32_t)*4); first_block = 0; } else @@ -119,7 +108,7 @@ void AudioFilterIRCabsim_F32::update() // do a complex MAC (multiply/accumulate) arm_cmplx_mult_cmplx_f32(ptr1, ptr2, ac2, 256); // This is the complex multiply for (int q = 0; q < 512; q = q + 8) - { // this is the accumulate + { accum[q] += ac2[q]; accum[q + 1] += ac2[q + 1]; accum[q + 2] += ac2[q + 2]; @@ -130,10 +119,7 @@ void AudioFilterIRCabsim_F32::update() accum[q + 7] += ac2[q + 7]; } k--; - if (k < 0) - { - k = nfor - 1; - } + if (k < 0) k = nfor - 1; k512 = k * 512; j512 += 512; } // end np loop @@ -153,10 +139,9 @@ void AudioFilterIRCabsim_F32::update() arm_fir_f32(&FIR_postL, blockL->data, blockL->data, blockL->length); arm_fir_f32(&FIR_postR, blockR->data, blockR->data, blockR->length); - arm_scale_f32(blockR->data, -doubler_gain, blockR->data, blockR->length); - arm_scale_f32(blockL->data, doubler_gain, blockL->data, blockL->length); + arm_scale_f32(blockR->data, -doubler_gainR, blockR->data, blockR->length); + arm_scale_f32(blockL->data, doubler_gainL, blockL->data, blockL->length); } - AudioStream_F32::transmit(blockL, 0); AudioStream_F32::release(blockL); AudioStream_F32::transmit(blockR, 1); @@ -173,7 +158,6 @@ void AudioFilterIRCabsim_F32::ir_register(const float32_t *irPtr, uint8_t positi void AudioFilterIRCabsim_F32::ir_load(uint8_t idx) { - const float32_t *newIrPtr = NULL; uint32_t nc = 0; @@ -193,14 +177,12 @@ void AudioFilterIRCabsim_F32::ir_load(uint8_t idx) nc = newIrPtr[0]; nfor = nc / IR_BUFFER_SIZE; if (nfor > nforMax) nfor = nforMax; + ir_length_ms = (1000.0f * nfor * (float32_t)AUDIO_BLOCK_SAMPLES) / AUDIO_SAMPLE_RATE_EXACT; ptr_fmask = &fmask[0][0]; - ptr_fftout = &fftout[0][0]; + ptr_fftout = &fftout[0]; memset(ptr_fftout, 0, nfor*512*4); // clear fftout array memset(fftin, 0, 512 * 4); // clear fftin array - S = &arm_cfft_sR_f32_len256; - iS = &arm_cfft_sR_f32_len256; - maskS = &arm_cfft_sR_f32_len256; init_partitioned_filter_masks(newIrPtr); delay.reset(); @@ -210,9 +192,11 @@ void AudioFilterIRCabsim_F32::ir_load(uint8_t idx) void AudioFilterIRCabsim_F32::init_partitioned_filter_masks(const float32_t *irPtr) { + const static arm_cfft_instance_f32 *maskS; + maskS = &arm_cfft_sR_f32_len256; for (uint32_t j = 0; j < nfor; j++) { - arm_fill_f32(0.0f, maskgen, IR_BUFFER_SIZE * 4); + memset(&maskgen[0], 0, IR_BUFFER_SIZE * 4 *sizeof(float32_t)); for (unsigned i = 0; i < IR_BUFFER_SIZE; i++) { maskgen[i * 2 + IR_BUFFER_SIZE * 2] = irPtr[2 + i + j * IR_BUFFER_SIZE] * irPtr[1]; // added gain! diff --git a/src/filter_ir_cabsim_F32.h b/src/filter_ir_cabsim_F32.h index 1e20bf9..3a66955 100644 --- a/src/filter_ir_cabsim_F32.h +++ b/src/filter_ir_cabsim_F32.h @@ -32,9 +32,11 @@ #include "basic_allpass.h" #include "basic_delay.h" #include "basic_shelvFilter.h" +#include + #define IR_BUFFER_SIZE 128 -#define IR_NFORMAX (8192 / IR_BUFFER_SIZE) +#define IR_NFORMAX (2048 / IR_BUFFER_SIZE) #define IR_FFT_LENGTH (2 * IR_BUFFER_SIZE) #define IR_N_B (1) #define IR_MAX_REG_NUM 11 // max number of registered IRs @@ -47,11 +49,9 @@ public: void ir_register(const float32_t *irPtr, uint8_t position); void ir_load(uint8_t idx); uint8_t ir_get(void) {return ir_idx;} - float ir_get_len_ms(void) + float32_t ir_get_len_ms(void) { - float32_t slen = 0.0f; - if (irPtrTable[ir_idx]) slen = irPtrTable[ir_idx][0]; - return (slen / AUDIO_SAMPLE_RATE_EXACT)*1000.0f; + return ir_length_ms; } void doubler_set(bool s) { @@ -91,19 +91,31 @@ private: int buffidx = 0; int k = 0; + float32_t fmask[IR_NFORMAX][IR_FFT_LENGTH * 2]; + float32_t ac2[512]; + float32_t accum[IR_FFT_LENGTH * 2]; + float32_t fftin[IR_FFT_LENGTH * 2]; + float32_t* last_sample_buffer_R; + float32_t* last_sample_buffer_L; + float32_t* maskgen; + float32_t* fftout; - float32_t *ptr_fftout; - float32_t *ptr_fmask; + float32_t* ptr_fftout; + float32_t* ptr_fmask; float32_t* ptr1; float32_t* ptr2; int k512; int j512; + + const arm_cfft_instance_f32 *S = &arm_cfft_sR_f32_len256; + const arm_cfft_instance_f32 *iS = &arm_cfft_sR_f32_len256; uint32_t N_BLOCKS = IR_N_B; static const uint32_t delay_l = AUDIO_SAMPLE_RATE * 0.01277f; //15ms delay - AudioBasicDelay delay; + AudioBasicDelay delay; + float32_t ir_length_ms = 0.0f; // default IR table, use NULL for bypass const float32_t *irPtrTable[IR_MAX_REG_NUM] = { @@ -112,9 +124,9 @@ private: void init_partitioned_filter_masks(const float32_t *irPtr); bool initialized = false; - // stereo doubler - static constexpr float32_t doubler_gain = 0.65f; + static constexpr float32_t doubler_gainL = 0.55f; + static constexpr float32_t doubler_gainR = 0.65f; bool doubleTrack = false; static const uint8_t nfir = 30; // fir taps arm_fir_instance_f32 FIR_preL, FIR_preR, FIR_postL, FIR_postR; diff --git a/src/hexefx_audio_F32.h b/src/hexefx_audio_F32.h index a3da7c9..b9de132 100644 --- a/src/hexefx_audio_F32.h +++ b/src/hexefx_audio_F32.h @@ -17,6 +17,6 @@ #include "effect_infphaser_F32.h" #include "effect_phaserStereo_F32.h" #include "effect_noiseGateStereo_F32.h" - +#include "effect_delaystereo.h" #endif // _HEXEFX_AUDIO_H