diff --git a/config.h b/config.h index c067c6c..dd3383f 100644 --- a/config.h +++ b/config.h @@ -56,9 +56,8 @@ #define REDUCE_LOUDNESS 0 #define USE_XFADE_DATA 1 // CHORUS parameters -#define INTERPOLATION_WINDOW_SIZE 7 // use only odd numbers!!! -#define INTERPOLATE_MODE 11 -#define FILTER_IIR_ORDER_3 1 +#define INTERPOLATION_WINDOW_SIZE 5 // use only odd numbers!!! +//#define INTERPOLATE_MODE 11 #define CHORUS_WAVEFORM WAVEFORM_TRIANGLE // WAVEFORM_SINE WAVEFORM_TRIANGLE WAVEFORM_SAWTOOTH WAVEFORM_SAWTOOTH_REVERSE #define CHORUS_DELAY_LENGTH_SAMPLES (15*AUDIO_BLOCK_SAMPLES) // one AUDIO_BLOCK_SAMPLES = 2.902ms; you need doubled length, e.g. delay point is 20ms, so you need up to 40ms delay! diff --git a/effect_modulated_delay.cpp b/effect_modulated_delay.cpp index 46d8b85..578d77f 100644 --- a/effect_modulated_delay.cpp +++ b/effect_modulated_delay.cpp @@ -58,6 +58,12 @@ boolean AudioEffectModulatedDelay::begin(short *delayline, int d_length) _delayline = delayline; _delay_length = _max_delay_length = d_length; + // init filter + filter.numStages = 1; + filter.pState = filter_state; + filter.pCoeffs = filter_coeffs; + calcModFilterCoeff(5000.0, 0.0, 5.0); + return (true); } @@ -66,31 +72,33 @@ void AudioEffectModulatedDelay::update(void) audio_block_t *block; audio_block_t *modulation; - int16_t *bp; - int16_t *mp; - float mod_idx; - if (_delayline == NULL) return; block = receiveWritable(0); modulation = receiveReadOnly(1); -#ifdef INTERPOLATE_MODE - int8_t j; - float x[INTERPOLATION_WINDOW_SIZE]; - float y[INTERPOLATION_WINDOW_SIZE]; - Spline s(x, y, INTERPOLATION_WINDOW_SIZE, INTERPOLATE_MODE); -#endif - - bp = block->data; - mp = modulation->data; - if (block && modulation) { + int16_t *bp; + float *mp; + float mod_idx; float mod_number; float mod_fraction; +#ifdef INTERPOLATE_MODE + int8_t j; + float x[INTERPOLATION_WINDOW_SIZE]; + float y[INTERPOLATION_WINDOW_SIZE]; + Spline s(x, y, INTERPOLATION_WINDOW_SIZE, INTERPOLATE_MODE); +#endif + + // (Filter implementation: https://web.fhnw.ch/technik/projekte/eit/Fruehling2016/MuelZum/html/parametric__equalizer__example_8c_source.html) + arm_q15_to_float(modulation->data, modulation_f32, AUDIO_BLOCK_SAMPLES); + arm_biquad_cascade_df1_f32(&filter, modulation_f32, modulation_f32, AUDIO_BLOCK_SAMPLES); + bp = block->data; + mp = modulation_f32; + for (uint16_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { // write data into circular buffer @@ -100,56 +108,7 @@ void AudioEffectModulatedDelay::update(void) // Calculate modulation index as a float, for interpolation later. // The index is located around the half of the delay length multiplied by the current amount of the modulator - /************************************************************************************************************** - Use an IIR filter on the modulation for avoiding aliasing (http://www-users.cs.york.ac.uk/~fisher/mkfilter/) */ -#if defined(FILTER_IIR_ORDER_1) - /* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher - Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 1 -a 1.1333499558e-01 0.0000000000e+00 -l - Parameters: filtertype = Butterworth - passtype = Lowpass - ripple = - order = 1 - samplerate = 44117.64706 - corner1 = 5000 - corner2 = - adzero = - logmin = - **************************************************************************************************************/ -#define NZEROS 1 -#define NPOLES 1 -#define GAIN 3.688918967e+00 - static float xv[NZEROS + 1], yv[NPOLES + 1]; - xv[0] = xv[1]; - xv[1] = (float(*mp) / SHRT_MAX) / GAIN; - yv[0] = yv[1]; - yv[1] = (xv[0] + xv[1]) + (0.4578357460 * yv[0]); - //new_value = yv[1]; - mod_idx = yv[1] * float(_delay_length >> 1); -#elif defined(FILTER_IIR_ORDER_3) - /* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher - Command line: /www/usr/fisher/helpers/mkfilter -Bu -Lp -o 3 -a 1.1333333333e-01 0.0000000000e+00 -l - raw alpha1 = 0.1133333333 - raw alpha2 = 0.1133333333 - warped alpha1 = 0.1183783855 - warped alpha2 = 0.1183783855 - gain at dc : mag = 4.028005941e+01 phase = 0.0000000000 pi - gain at centre: mag = 2.848230315e+01 phase = -0.7500000000 pi - gain at hf : mag = 0.000000000e+00 - **************************************************************************************************************/ -#define NZEROS 3 -#define NPOLES 3 -#define GAIN 4.028005941e+01 - static float xv[NZEROS + 1], yv[NPOLES + 1]; - xv[0] = xv[1]; xv[1] = xv[2]; xv[2] = xv[3]; - xv[3] = (float(*mp) / SHRT_MAX) / GAIN; - yv[0] = yv[1]; yv[1] = yv[2]; yv[2] = yv[3]; - yv[3] = (xv[0] + xv[3]) + 3 * (xv[1] + xv[2]) - + ( 0.2323461955 * yv[0]) + ( -1.0299524229 * yv[1]) - + ( 1.5989967885 * yv[2]); - //new_value = yv[3]; - mod_idx = yv[3] * float(_delay_length >> 1); - /************************************************************************/ -#endif + mod_idx = *mp * float(_delay_length >> 1); mod_fraction = modff(mod_idx, &mod_number); #ifdef INTERPOLATE_MODE @@ -173,13 +132,13 @@ void AudioEffectModulatedDelay::update(void) if (c_mod_idx < 0) { - value1 = _delayline[_delay_length + c_mod_idx]; - value2 = _delayline[_delay_length + c_mod_idx - 1]; + value1 = _delayline[_delay_length + c_mod_idx - 1]; + value2 = _delayline[_delay_length + c_mod_idx]; } else { - value1 = _delayline[c_mod_idx]; - value2 = _delayline[c_mod_idx - 1]; + value1 = _delayline[c_mod_idx - 1]; + value2 = _delayline[c_mod_idx]; } *bp = mod_fraction * value1 + (1.0 - mod_fraction) * value2; #endif @@ -190,17 +149,47 @@ void AudioEffectModulatedDelay::update(void) } } + if (modulation) + release(modulation); + if (block) { transmit(block, 0); release(block); } - - if (modulation) - release(modulation); } void AudioEffectModulatedDelay::setDelay(float milliseconds) { _delay_length = min(AUDIO_SAMPLE_RATE * milliseconds / 500, _max_delay_length); } + +void AudioEffectModulatedDelay::calcModFilterCoeff(float32_t cFrq, float32_t gain, float32_t width) +{ + /* Calculate intermediate values */ + float32_t A = sqrt(pow(10, gain / 20.0f)); + float32_t w0 = 2.0f * PI * cFrq / ((float32_t)AUDIO_SAMPLE_RATE_EXACT); + float32_t cosw0 = cos(w0); + float32_t sinw0 = sin(w0); + float32_t alpha = sinw0 / (2.0f * width); + + /* Calculate coefficients */ + float32_t b0 = 1.0f + alpha * A; + float32_t b1 = -2.0f * cosw0; + float32_t b2 = 1.0f - alpha * A; + float32_t a0 = 1.0f + alpha / A; + float32_t a1 = -2.0f * cosw0; + float32_t a2 = 1.0f - alpha / A; + + /* Normalize so a0 = 1 */ + filter_coeffs[0] = b0 / a0; + filter_coeffs[1] = b1 / a0; + filter_coeffs[2] = b2 / a0; + filter_coeffs[3] = -a1 / a0; + filter_coeffs[4] = -a2 / a0; +} + +void AudioEffectModulatedDelay::setModFilter(float cFrq, float gain, float width) +{ + calcModFilterCoeff(cFrq, gain, width); +} diff --git a/effect_modulated_delay.h b/effect_modulated_delay.h index 5c395e4..450a14a 100644 --- a/effect_modulated_delay.h +++ b/effect_modulated_delay.h @@ -34,9 +34,6 @@ // 140219 - correct storage class (not static) // 190527 - added modulation input handling (by Holger Wirtz) - -#define MODF(n,i,f) ((i) = (int)(n), (f) = (n) - (double)(i)) - class AudioEffectModulatedDelay : public AudioStream { @@ -48,13 +45,21 @@ class AudioEffectModulatedDelay : boolean begin(short *delayline, int delay_length); virtual void update(void); virtual void setDelay(float milliseconds); + virtual void setModFilter(float cFrq, float gain, float width); private: + virtual void calcModFilterCoeff(float32_t cFrq, float32_t gain, float32_t width); + audio_block_t *inputQueueArray[2]; int16_t *_delayline; uint16_t _circ_idx; uint16_t _max_delay_length; uint16_t _delay_length; + // filter data + arm_biquad_casd_df1_inst_f32 filter; + float32_t modulation_f32[AUDIO_BLOCK_SAMPLES]; + float32_t filter_coeffs[5]; + float32_t filter_state[4]; }; #endif