From 41ad26f3a0122280d0b728956579584461dab11b Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Mon, 2 Sep 2019 10:43:04 +0200 Subject: [PATCH] Save the current state. --- DCfilter.h | 89 ++++++++++++++++++++++++++++++++++++++ MicroMDAEPiano.ino | 26 ++++++----- DCBlock.h => OnePoleLP.h | 20 ++++----- config.h | 2 +- effect_modulated_delay.cpp | 55 ++++++++++------------- effect_modulated_delay.h | 7 ++- 6 files changed, 142 insertions(+), 57 deletions(-) create mode 100644 DCfilter.h rename DCBlock.h => OnePoleLP.h (72%) diff --git a/DCfilter.h b/DCfilter.h new file mode 100644 index 0000000..2b2dde7 --- /dev/null +++ b/DCfilter.h @@ -0,0 +1,89 @@ +/* + * DCfilter.h + * + * Copyright 2012 Tim Barrass. + * + * This file is part of Mozzi. + * + * Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. + * + */ + +#ifndef DCFILTER_H +#define DCFILTER_H + +/* +tb2010 adapted from: +robert bristow-johnson, DSP Trick: Fixed-Point DC Blocking Filter with Noise-Shaping +http://www.dspguru.com/book/export/html/126 + +y[n] = x[n] - x[n-1] + a * y[n-1] + +Where y[n] is the output at the current time n, and x[n] is the input at the current time n. + +also, see DC Blocker Algorithms, http://www.ingelec.uns.edu.ar/pds2803/materiales/articulos/04472252.pdf + */ + +/** +A DC-blocking filter useful for highlighting changes in control signals. +The output of the filter settles to 0 if the incoming signal stays constant. If the input changes, the +filter output swings to track the change and eventually settles back to 0. +*/ +class DCfilter +{ +public: +/** +Instantiate a DC-blocking filter. +@param pole sets the responsiveness of the filter, +how long it takes to settle to 0 if the input signal levels out at a constant value. +*/ + DCfilter(float pole):acc(0),prev_x(0),prev_y(0) + { + A = (int)(32768.0*(1.0 - pole)); + } + +/* almost original + // timing: 20us + int next(int x) + { + setPin13High(); + acc -= prev_x; + prev_x = (long)x<<15; + acc += prev_x; + acc -= A*prev_y; + prev_y = acc>>15; // quantization happens here + int filtered = (int)prev_y; + // acc has y[n] in upper 17 bits and -e[n] in lower 15 bits + setPin13Low(); + return filtered; + } + */ + + /** + Filter the incoming value and return the result. + @param x the value to filter + @return filtered signal + */ + // timing :8us + inline + int next(int x) + { + acc += ((long)(x-prev_x)<<16)>>1; + prev_x = x; + acc -= (long)A*prev_y; // acc has y[n] in upper 17 bits and -e[n] in lower 15 bits + prev_y = (acc>>16)<<1; // faster than >>15 but loses bit 0 + if (acc & 32784) prev_y += 1; // adds 1 if it was in the 0 bit position lost in the shifts above + return prev_y; + } + +private: + long acc; + int prev_x, prev_y,A; +}; + +/** +@example 05.Control_Filters/DCFilter/DCFilter.ino +This example demonstrates the DCFilter class. +*/ + +#endif // #ifndef DCFILTER_H diff --git a/MicroMDAEPiano.ino b/MicroMDAEPiano.ino index 610be67..02518f0 100644 --- a/MicroMDAEPiano.ino +++ b/MicroMDAEPiano.ino @@ -57,8 +57,8 @@ AudioAmplifier inverter; AudioEffectModulatedDelay modchorus_r; AudioEffectModulatedDelay modchorus_l; AudioSynthWaveform modulator; -AudioFilterStateVariable chorus_filter_r; -AudioFilterStateVariable chorus_filter_l; +//AudioFilterStateVariable chorus_filter_r; +//AudioFilterStateVariable chorus_filter_l; AudioConnection patchCord0(queue_r, peak_r); AudioConnection patchCord1(queue_l, peak_l); AudioConnection patchCord2(queue_r, freeverb_r); @@ -70,10 +70,12 @@ AudioConnection patchCord9(modulator, inverter); AudioConnection patchCord10(inverter, 0, modchorus_l, 1); AudioConnection patchCord11(queue_r, 0, mixer_r, 0); AudioConnection patchCord12(queue_l, 0, mixer_l, 0); -AudioConnection patchCord13(modchorus_r, chorus_filter_r); -AudioConnection patchCord14(modchorus_l, chorus_filter_l); -AudioConnection patchCord15(chorus_filter_r, 0, mixer_r, 2); -AudioConnection patchCord16(chorus_filter_l, 0, mixer_l, 2); +//AudioConnection patchCord13(modchorus_r, chorus_filter_r); +//AudioConnection patchCord14(modchorus_l, chorus_filter_l); +//AudioConnection patchCord15(chorus_filter_r, 0, mixer_r, 2); +//AudioConnection patchCord16(chorus_filter_l, 0, mixer_l, 2); +AudioConnection patchCord15(modchorus_r, 0, mixer_r, 2); +AudioConnection patchCord16(modchorus_l, 0, mixer_l, 2); AudioConnection patchCord17(freeverb_r, 0, mixer_r, 1); AudioConnection patchCord18(freeverb_l, 0, mixer_l, 1); AudioConnection patchCord19(mixer_r, volume_r); @@ -238,12 +240,12 @@ void setup() modulator.amplitude(1.0); modulator.offset(0.0); inverter.gain(-1.0); // change phase for second modulated delay (faked stereo mode) - chorus_filter_r.frequency(3500); - chorus_filter_r.resonance(0.7); - chorus_filter_r.octaveControl(1); - chorus_filter_l.frequency(3500); - chorus_filter_l.resonance(0.7); - chorus_filter_l.octaveControl(1); + //chorus_filter_r.frequency(3500); + //chorus_filter_r.resonance(0.7); + //chorus_filter_r.octaveControl(1); + //chorus_filter_l.frequency(3500); + //chorus_filter_l.resonance(0.7); + //chorus_filter_l.octaveControl(1); modchorus_r.offset(15.0); modchorus_l.offset(15.0); diff --git a/DCBlock.h b/OnePoleLP.h similarity index 72% rename from DCBlock.h rename to OnePoleLP.h index 0e286c6..c0ea171 100644 --- a/DCBlock.h +++ b/OnePoleLP.h @@ -1,4 +1,3 @@ -// From "git clone --recursive https://github.com/SpotlightKid/ykchorus.git" /* ============================================================================== This file is part of Tal-NoiseMaker by Patrick Kunz. @@ -22,24 +21,23 @@ ============================================================================== */ -#if !defined(__DCBlock_h) -#define __DCBlock_h +#if !defined(__OnePoleLP_h) +#define __OnePoleLP_h -class DCBlock { +class OnePoleLP { public: float inputs, outputs, lastOutput; - DCBlock() { + OnePoleLP() { lastOutput = inputs = outputs = 0.0f; } - ~DCBlock() {} + ~OnePoleLP() {} - inline void tick(float *sample, float cutoff) { - outputs = *sample - inputs + (0.999f - cutoff * 0.4f) * outputs; - inputs = *sample; - lastOutput = outputs; - *sample = lastOutput; + void tick(float *sample, float cutoff) { + float p = (cutoff * 0.98f) * (cutoff * 0.98f) * (cutoff * 0.98f) * (cutoff * 0.98f); + outputs = (1.0f - p) * (*sample) + p * outputs; + *sample = outputs; } }; diff --git a/config.h b/config.h index cfed6ec..a801768 100644 --- a/config.h +++ b/config.h @@ -56,7 +56,7 @@ #define USE_XFADE_DATA 1 // CHORUS parameters #define CHORUS_DELAY_LENGTH_SAMPLES (15*AUDIO_BLOCK_SAMPLES) // one AUDIO_BLOCK_SAMPLES = 2.902ms -#define CHORUS_WAVEFORM_MOD WAVEFORM_TRIANGLE // WAVEFORM_SINE WAVEFORM_TRIANGLE WAVEFORM_SAWTOOTH WAVEFORM_SAWTOOTH_REVERSE +#define CHORUS_WAVEFORM_MOD WAVEFORM_SINE // WAVEFORM_SINE WAVEFORM_TRIANGLE WAVEFORM_SAWTOOTH WAVEFORM_SAWTOOTH_REVERSE //************************************************************************************************* //* DEBUG OUTPUT SETTINGS //************************************************************************************************* diff --git a/effect_modulated_delay.cpp b/effect_modulated_delay.cpp index 0187451..4dd3c83 100644 --- a/effect_modulated_delay.cpp +++ b/effect_modulated_delay.cpp @@ -26,7 +26,8 @@ #include "arm_math.h" #include "effect_modulated_delay.h" #include "config.h" -#include "DCBlock.h" +#include "DCfilter.h" +#include "OnePoleLP.h" #include "limits.h" extern config_t configuration; @@ -51,6 +52,7 @@ boolean AudioEffectModulatedDelay::begin(short *delayline, int d_length) _delay_length = 0; _delay_offset = 0.0; _cb_index = 0; + z1 = 0; if (delayline == NULL) { return (false); @@ -65,7 +67,8 @@ boolean AudioEffectModulatedDelay::begin(short *delayline, int d_length) set_modulator_filter_coeffs(); modulator_filter_data = {1, modulator_filter_state, modulator_filter_coeffs}; - dcBlock = new DCBlock(); + lp = new OnePoleLP(); + dcFilter = new DCfilter(0.9); return (true); } @@ -77,12 +80,12 @@ void AudioEffectModulatedDelay::set_modulator_filter_coeffs(void) // Example: https://web.fhnw.ch/technik/projekte/eit/Fruehling2016/MuelZum/html/parametric_equalizer_example_8c-example.html // Coeeficients calculated with https://arachnoid.com/BiQuadDesigner/index.html - // SR = 44110, Fc = 400 Hz; Q=0.707 - modulator_filter_coeffs[0] = 7.80321719e-4; // b0 - modulator_filter_coeffs[1] = 0.00156064; // b1 - modulator_filter_coeffs[2] = 7.80321719e-4; // b2 - modulator_filter_coeffs[3] = 1.91943335; // -a1 - modulator_filter_coeffs[4] = -0.92255463; // -a2 + // SR = 44110, Fc = 20 Hz; Q=0.707 + modulator_filter_coeffs[0] = 5.06973332e-7; // b0 + modulator_filter_coeffs[1] = 1.01394666e-6; // b1 + modulator_filter_coeffs[2] = modulator_filter_coeffs[0]; // b2 + modulator_filter_coeffs[3] = 1.99798478; // -a1 + modulator_filter_coeffs[4] = -0.99798681; // -a2 } void AudioEffectModulatedDelay::update(void) @@ -124,33 +127,23 @@ void AudioEffectModulatedDelay::update(void) // calculate modulation index into circular buffer cb_mod_index = (_cb_index - (_delay_offset + mod_number)); - /* if (cb_mod_index >= _delay_length) - cb_mod_index -= _delay_length; */ if (cb_mod_index < 0) // check for negative offsets and correct them cb_mod_index += _delay_length; - if (*mp < 0.0) - { - if (cb_mod_index == 0) - cb_mod_index_neighbor = _delay_length; - else - cb_mod_index_neighbor = cb_mod_index - 1; - *bp = round(float(_delayline[cb_mod_index]) * mod_fraction + float(_delayline[cb_mod_index_neighbor]) * (1.0 - mod_fraction)); - } + if (cb_mod_index == 0) + cb_mod_index_neighbor = _delay_length; else - { - if (cb_mod_index == _delay_length) - cb_mod_index_neighbor = 0; - else - cb_mod_index_neighbor = cb_mod_index + 1; - *bp = round(float(_delayline[cb_mod_index_neighbor]) * mod_fraction + float(_delayline[cb_mod_index]) * (1.0 - mod_fraction)); - } - - // simple Test for DC filter - float bp_f=*bp/SHRT_MAX; - dcBlock->tick(&bp_f, 0.01f); - *bp=bp_f*SHRT_MAX; - + cb_mod_index_neighbor = cb_mod_index - 1; + + //*bp = round(float(_delayline[cb_mod_index]) * mod_fraction + float(_delayline[cb_mod_index_neighbor]) * (1.0 - mod_fraction)); + *bp = (round(float(_delayline[cb_mod_index]) * mod_fraction + float(_delayline[cb_mod_index_neighbor]) * (1.0 - mod_fraction))+z1)/2; + z1 = *bp; + + float bp_f = *bp / float(SHRT_MAX); + lp->tick(&bp_f, 0.95f); + *bp = round(bp_f * SHRT_MAX); + *bp = dcFilter->next(*bp); + // push the pointers forward bp++; // next audio data mp++; // next modulation data diff --git a/effect_modulated_delay.h b/effect_modulated_delay.h index 6688e45..48f8edd 100644 --- a/effect_modulated_delay.h +++ b/effect_modulated_delay.h @@ -27,7 +27,8 @@ #include "Arduino.h" #include "AudioStream.h" #include "config.h" -#include "DCBlock.h" +#include "DCfilter.h" +#include "OnePoleLP.h" #define MODULATION_MAX_FACTOR 0.75 @@ -60,7 +61,9 @@ class AudioEffectModulatedDelay : arm_biquad_casd_df1_inst_f32 modulator_filter_data; float32_t modulator_filter_state[4]; float32_t modulator_filter_coeffs[5]; - DCBlock *dcBlock; + int16_t z1; + OnePoleLP *lp; + DCfilter *dcFilter; }; #endif