delay stereo, basic comp updates

pull/2/head
pio 9 months ago
parent 2b6ebae664
commit 6eef3f9107
  1. 7
      README.md
  2. 27
      keywords.txt
  3. 30
      src/basic_delay.h
  4. 24
      src/basic_lfo.h
  5. 204
      src/effect_delaystereo.cpp
  6. 292
      src/effect_delaystereo.h
  7. 11
      src/effect_platereverb_F32.cpp
  8. 12
      src/effect_platereverb_F32.h
  9. 3
      src/effect_reverbsc_F32.cpp
  10. 25
      src/effect_springreverb_F32.cpp
  11. 4
      src/effect_springreverb_F32.h
  12. 56
      src/filter_ir_cabsim_F32.cpp
  13. 28
      src/filter_ir_cabsim_F32.h
  14. 2
      src/hexefx_audio_F32.h

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

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

@ -19,7 +19,7 @@
*
* @tparam N delay length in samples (float)
*/
template <int N>
//template <int N>
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<int32_t>(delay);
float delay_fractional = delay - static_cast<float>(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;
};

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

@ -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);
}

@ -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 <Arduino.h>
#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_

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

@ -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_BUF_LEN> lp_dly1;
AudioBasicDelay<LP_DLY2_BUF_LEN> lp_dly2;
AudioBasicDelay<LP_DLY3_BUF_LEN> lp_dly3;
AudioBasicDelay<LP_DLY4_BUF_LEN> 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

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

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

@ -160,8 +160,8 @@ private:
float32_t *sp_chrp_alp3_buf;
float32_t *sp_chrp_alp4_buf;
AudioBasicDelay<SPRVB_DLY1_LEN> lp_dly1;
AudioBasicDelay<SPRVB_DLY2_LEN> lp_dly2;
AudioBasicDelay lp_dly1;
AudioBasicDelay lp_dly2;
float32_t in_TrebleCut_k;
float32_t in_BassCut_k;

@ -21,29 +21,20 @@
* If not, see <https://www.gnu.org/licenses/>."
*/
#include "filter_ir_cabsim_F32.h"
#include <arm_const_structs.h>
#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!

@ -32,9 +32,11 @@
#include "basic_allpass.h"
#include "basic_delay.h"
#include "basic_shelvFilter.h"
#include <arm_const_structs.h>
#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,6 +91,14 @@ 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;
@ -99,11 +107,15 @@ private:
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_l> 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;

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

Loading…
Cancel
Save