diff --git a/MicroDexed.ino b/MicroDexed.ino index 31098f4..f223500 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -35,41 +35,87 @@ #include "midi_devices.hpp" #include "dexed.h" #include "dexed_sysex.h" +#include "effect_modulated_delay.h" #include "PluginFx.h" -AudioPlayQueue queue1; +AudioPlayQueue dexed1; AudioAnalyzePeak peak1; -AudioEffectDelay delay1; -AudioMixer4 mixer1; -AudioMixer4 mixer2; +AudioEffectDelay delay_r; +AudioEffectDelay delay_l; +AudioEffectFreeverb freeverb_r; +AudioEffectFreeverb freeverb_l; +AudioEffectModulatedDelay modchorus_r; +AudioEffectModulatedDelay modchorus_l; +#if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT +AudioFilterBiquad modchorus_filter_r; +AudioFilterBiquad modchorus_filter_l; +#endif +AudioSynthWaveform modulator; +AudioAmplifier inverter; +AudioMixer4 chorus_mixer_r; +AudioMixer4 chorus_mixer_l; +AudioMixer4 delay_mixer_r; +AudioMixer4 delay_mixer_l; +AudioMixer4 delay_fb_mixer_r; +AudioMixer4 delay_fb_mixer_l; +AudioMixer4 reverb_mixer_r; +AudioMixer4 reverb_mixer_l; AudioAmplifier volume_r; AudioAmplifier volume_l; AudioOutputUSB usb1; -AudioConnection patchCord0(queue1, peak1); -AudioConnection patchCord1(queue1, 0, mixer1, 0); -AudioConnection patchCord2(queue1, 0, mixer2, 0); -AudioConnection patchCord3(delay1, 0, mixer1, 1); -AudioConnection patchCord4(delay1, 0, mixer2, 2); -AudioConnection patchCord5(mixer1, delay1); -AudioConnection patchCord6(mixer1, 0, mixer2, 1); -AudioConnection patchCord7(mixer2, volume_r); -AudioConnection patchCord8(mixer2, volume_l); -AudioConnection patchCord9(mixer2, 0, usb1, 0); -AudioConnection patchCord10(mixer2, 0, usb1, 1); +AudioConnection patchCord0(dexed1, peak1); +AudioConnection patchCord1(dexed1, 0, chorus_mixer_r, 0); +AudioConnection patchCord2(dexed1, 0, chorus_mixer_l, 0); +AudioConnection patchCord3(dexed1, 0, delay_mixer_r, 0); +AudioConnection patchCord4(dexed1, 0, delay_mixer_l, 0); +AudioConnection patchCord5(dexed1, 0, delay_fb_mixer_r, 0); +AudioConnection patchCord6(dexed1, 0, delay_fb_mixer_l, 0); +AudioConnection patchCord7(dexed1, 0, reverb_mixer_r, 0); +AudioConnection patchCord8(dexed1, 0, reverb_mixer_l, 0); +AudioConnection patchCord9(dexed1, 0, modchorus_r, 0); +AudioConnection patchCord10(dexed1, 0, modchorus_l, 0); +AudioConnection patchCord11(modulator, 0, modchorus_r, 1); +AudioConnection patchCord12(modulator, inverter); +AudioConnection patchCord13(inverter, 0, modchorus_l, 1); +#if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT +AudioConnection patchCord14(modchorus_r, modchorus_filter_r); +AudioConnection patchCord15(modchorus_l, modchorus_filter_l); +AudioConnection patchCord16(modchorus_filter_r, 0, chorus_mixer_r, 1); +AudioConnection patchCord17(modchorus_filter_l, 0, chorus_mixer_l, 1); +#else +AudioConnection patchCord14(modchorus_r, 0, chorus_mixer_r, 1); +AudioConnection patchCord15(modchorus_l, 0, chorus_mixer_l, 1); +#endif +AudioConnection patchCord18(chorus_mixer_r, 0, delay_mixer_r, 1); +AudioConnection patchCord19(chorus_mixer_l, 0, delay_mixer_l, 1); +AudioConnection patchCord20(delay_fb_mixer_r, 0, delay_mixer_r, 1); +AudioConnection patchCord21(delay_fb_mixer_l, 0, delay_mixer_l, 1); +AudioConnection patchCord22(delay_mixer_r, 0, reverb_mixer_r, 1); +AudioConnection patchCord23(delay_mixer_l, 0, reverb_mixer_l, 1); +AudioConnection patchCord24(delay_r, 0, delay_mixer_r, 2); +AudioConnection patchCord25(delay_l, 0, delay_mixer_l, 2); +AudioConnection patchCord26(delay_fb_mixer_r, delay_r); +AudioConnection patchCord27(delay_fb_mixer_l, delay_l); +AudioConnection patchCord28(freeverb_r, 0, reverb_mixer_r, 1); +AudioConnection patchCord29(freeverb_l, 0, reverb_mixer_l, 1); +AudioConnection patchCord30(reverb_mixer_r, volume_r); +AudioConnection patchCord31(reverb_mixer_l, volume_l); +AudioConnection patchCord32(volume_r, 0, usb1, 0); +AudioConnection patchCord33(volume_l, 0, usb1, 1); #if defined(TEENSY_AUDIO_BOARD) AudioOutputI2S i2s1; -AudioConnection patchCord11(volume_r, 0, i2s1, 0); -AudioConnection patchCord12(volume_l, 0, i2s1, 1); +AudioConnection patchCord34(volume_r, 0, i2s1, 0); +AudioConnection patchCord35(volume_l, 0, i2s1, 1); AudioControlSGTL5000 sgtl5000_1; #elif defined(TGA_AUDIO_BOARD) AudioOutputI2S i2s1; -AudioConnection patchCord11(volume_r, 0, i2s1, 0); -AudioConnection patchCord12(volume_l, 0, i2s1, 1); +AudioConnection patchCord34(volume_r, 0, i2s1, 0); +AudioConnection patchCord35(volume_l, 0, i2s1, 1); AudioControlWM8731master wm8731_1; #else AudioOutputPT8211 pt8211_1; -AudioConnection patchCord11(volume_r, 0, pt8211_1, 0); -AudioConnection patchCord12(volume_l, 0, pt8211_1, 1); +AudioConnection patchCord34(volume_r, 0, pt8211_1, 0); +AudioConnection patchCord35(volume_l, 0, pt8211_1, 1); #endif Dexed* dexed = new Dexed(SAMPLE_RATE); @@ -106,6 +152,10 @@ value_change_t soften_volume = {0.0, 0}; value_change_t soften_filter_res = {0.0, 0}; value_change_t soften_filter_cut = {0.0, 0}; +// Allocate the delay lines for left and right channels +short l_delayline[MOD_DELAY_SAMPLE_BUFFER]; +short r_delayline[MOD_DELAY_SAMPLE_BUFFER]; + #ifdef ENABLE_LCD_UI /*********************************************************************** LCDMenuLib2 @@ -247,13 +297,63 @@ void setup() } // Init effects - ////delay1.delay(0, mapfloat(effect_delay_feedback, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); - // mixer1 is the feedback-adding mixer, mixer2 the whole delay (with/without feedback) mixer - mixer1.gain(0, 1.0); // original signal - ////mixer1.gain(1, mapfloat(effect_delay_feedback, 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); // amount of feedback - ////mixer2.gain(0, 1.0 - mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // original signal - ////mixer2.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delayed signal (including feedback) - ////mixer2.gain(2, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // only delayed signal (without feedback) + if (!modchorus_r.begin(r_delayline, MOD_DELAY_SAMPLE_BUFFER)) { + Serial.println(F("AudioEffectModulatedDelay - right channel begin failed")); + while (1); + } + if (!modchorus_l.begin(l_delayline, MOD_DELAY_SAMPLE_BUFFER)) { + Serial.println(F("AudioEffectModulatedDelay - left channel begin failed")); + while (1); + } +#ifdef DEBUG + Serial.print(F("MOD_DELAY_SAMPLE_BUFFER=")); + Serial.print(MOD_DELAY_SAMPLE_BUFFER, DEC); + Serial.println(F(" samples")); +#endif + modulator.begin(MOD_WAVEFORM); + modulator.phase(0); + modulator.amplitude(0.5); + modulator.offset(0.0); +#if MOD_FILTER_OUTPUT == MOD_BUTTERWORTH_FILTER_OUTPUT + // Butterworth filter, 12 db/octave + modchorus_filter_r.setLowpass(0, MOD_FILTER_CUTOFF_HZ, 0.707); + modchorus_filter_l.setLowpass(0, MOD_FILTER_CUTOFF_HZ, 0.707); +#elif MOD_FILTER_OUTPUT == MOD_LINKWITZ_RILEY_FILTER_OUTPUT + // Linkwitz-Riley filter, 48 dB/octave + modchorus_filter_r.setLowpass(0, MOD_FILTER_CUTOFF_HZ, 0.54); + modchorus_filter_r.setLowpass(1, MOD_FILTER_CUTOFF_HZ, 1.3); + modchorus_filter_r.setLowpass(2, MOD_FILTER_CUTOFF_HZ, 0.54); + modchorus_filter_r.setLowpass(3, MOD_FILTER_CUTOFF_HZ, 1.3); + modchorus_filter_l.setLowpass(0, MOD_FILTER_CUTOFF_HZ, 0.54); + modchorus_filter_l.setLowpass(1, MOD_FILTER_CUTOFF_HZ, 1.3); + modchorus_filter_l.setLowpass(2, MOD_FILTER_CUTOFF_HZ, 0.54); + modchorus_filter_l.setLowpass(3, MOD_FILTER_CUTOFF_HZ, 1.3); +#endif + + chorus_mixer_r.gain(0, 1.0); + chorus_mixer_l.gain(0, 1.0); + chorus_mixer_r.gain(1, 0.0); + chorus_mixer_l.gain(1, 0.0); + + delay_r.delay(0, mapfloat(effect_delay_feedback, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); + delay_l.delay(0, mapfloat(effect_delay_feedback, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); + // delay_fb_mixer_r is the feedback-adding mixer, delay_mixer_r the whole delay (with/without feedback) mixer + delay_fb_mixer_r.gain(0, 1.0); // original signal + delay_fb_mixer_l.gain(0, 1.0); // original signal + delay_fb_mixer_r.gain(1, mapfloat(effect_delay_feedback, 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); // amount of feedback + delay_fb_mixer_l.gain(1, mapfloat(effect_delay_feedback, 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); // amount of feedback + delay_mixer_r.gain(0, 1.0 - mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // original signal + delay_mixer_l.gain(0, 1.0 - mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // original signal + delay_mixer_r.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delayed signal (including feedback) + delay_mixer_l.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delayed signal (including feedback) + delay_mixer_r.gain(2, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // only delayed signal (without feedback) + delay_mixer_l.gain(2, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // only delayed signal (without feedback) + + reverb_mixer_r.gain(0, 1.0); + reverb_mixer_l.gain(0, 1.0); + reverb_mixer_r.gain(1, 0.0); + reverb_mixer_l.gain(1, 0.0); + dexed->fx.Gain = 1.0; dexed->fx.Reso = 1.0 - float(effect_filter_resonance) / ENC_FILTER_RES_STEPS; dexed->fx.Cutoff = 1.0 - float(effect_filter_cutoff) / ENC_FILTER_CUT_STEPS; @@ -299,11 +399,11 @@ void loop() while (42 == 42) { // Main sound calculation - if (queue1.available() && fill_audio_buffer > audio_block_time_us - 10) + if (dexed1.available() && fill_audio_buffer > audio_block_time_us - 10) { fill_audio_buffer = 0; - audio_buffer = queue1.getBuffer(); + audio_buffer = dexed1.getBuffer(); elapsedMicros t1; dexed->getSamples(AUDIO_BLOCK_SAMPLES, audio_buffer); @@ -320,7 +420,7 @@ void loop() for (uint8_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) audio_buffer[i] *= configuration.vol; #endif - queue1.playBuffer(); + dexed1.playBuffer(); } // EEPROM update handling @@ -488,15 +588,15 @@ void handleControlChange(byte inChannel, byte inCtrl, byte inValue) break; case 105: // CC 105: delay time effect_delay_time = map(inValue, 0, 127, 0, ENC_DELAY_TIME_STEPS); - ////delay1.delay(0, mapfloat(effect_delay_time, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); + ////delay_r.delay(0, mapfloat(effect_delay_time, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); break; case 106: // CC 106: delay feedback effect_delay_feedback = map(inValue, 0, 127, 0, ENC_DELAY_FB_STEPS); - ////mixer1.gain(1, mapfloat(float(effect_delay_feedback), 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); + ////delay_mixer_r.gain(1, mapfloat(float(effect_delay_feedback), 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); break; case 107: // CC 107: delay volume effect_delay_volume = map(inValue, 0, 127, 0, ENC_DELAY_VOLUME_STEPS); - ////mixer2.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delay tap1 signal (with added feedback) + ////delay_mixer_l.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delay tap1 signal (with added feedback) break; case 120: dexed->panic(); @@ -820,7 +920,7 @@ void set_volume(float v, float p) { configuration.vol = v; configuration.pan = p; - + dexed->fx.Gain = v; uint16_t tmp = v * 1023.0 + 0.5; @@ -845,7 +945,7 @@ void set_volume(float v, float p) // http://files.csound-tutorial.net/floss_manual/Release03/Cs_FM_03_ScrapBook/b-panning-and-spatialization.html volume_r.gain(tmp3 * sinf(tmp2 * PI / 2)); volume_l.gain(tmp3 * cosf(tmp2 * PI / 2)); - + if (configuration.mono == 2) volume_l.gain(0.0); else if (configuration.mono == 3) diff --git a/UI.hpp b/UI.hpp index ceae1d0..11cbef7 100644 --- a/UI.hpp +++ b/UI.hpp @@ -331,7 +331,7 @@ void encoder_right_up(void) } break; case MENU_VOICE_SOUND: - if (configuration.voice < MAX_VOICES) + if (configuration.voice < MAX_VOICES-1) configuration.voice++; else { diff --git a/config.h b/config.h index 34b772f..a655988 100644 --- a/config.h +++ b/config.h @@ -45,7 +45,7 @@ //************************************************************************************************* // MIDI -#define MIDI_DEVICE_DIN Serial1 +//#define MIDI_DEVICE_DIN Serial1 #define MIDI_DEVICE_USB 1 #define MIDI_DEVICE_USB_HOST 1 #define MIDI_DEVICE_NUMBER 0 @@ -71,6 +71,11 @@ // EFFECTS #define FILTER_MAX_FREQ 10000 +// CHORUS parameters +#define MOD_DELAY_SAMPLE_BUFFER int32_t(TIME_MS2SAMPLES(20.0)) // 20.0 ms delay buffer. +#define MOD_WAVEFORM WAVEFORM_TRIANGLE // WAVEFORM_SINE WAVEFORM_TRIANGLE WAVEFORM_SAWTOOTH WAVEFORM_SAWTOOTH_REVERSE +#define MOD_FILTER_OUTPUT MOD_LINKWITZ_RILEY_FILTER_OUTPUT // MOD_LINKWITZ_RILEY_FILTER_OUTPUT MOD_BUTTERWORTH_FILTER_OUTPUT MOD_NO_FILTER_OUTPUT +#define MOD_FILTER_CUTOFF_HZ 3000 //************************************************************************************************* //* AUDIO SETTINGS @@ -112,7 +117,6 @@ //************************************************************************************************* //* DEBUG OUTPUT SETTINGS //************************************************************************************************* - #define DEBUG 1 #define SERIAL_SPEED 38400 #define SHOW_XRUN 1 @@ -196,6 +200,14 @@ #define USE_TEENSY_DSP 1 #define SUM_UP_AS_INT 1 +/* HELPER MACROS */ +#define TIME_MS2SAMPLES(x) floor(uint32_t(x) * AUDIO_SAMPLE_RATE / 1000) +#define SAMPLES2TIME_MS(x) float(uint32_t(x) * 1000 / AUDIO_SAMPLE_RATE) +// Modulated delay options +#define MOD_NO_FILTER_OUTPUT 0 +#define MOD_BUTTERWORTH_FILTER_OUTPUT 1 +#define MOD_LINKWITZ_RILEY_FILTER_OUTPUT 2 + // struct for holding the current configuration struct config_t { uint32_t checksum; @@ -212,4 +224,10 @@ struct value_change_t { float diff; uint16_t steps; }; + +inline float mapfloat(float val, float in_min, float in_max, float out_min, float out_max) +{ + return (val - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + #endif // CONFIG_H_INCLUDED diff --git a/effect_modulated_delay.cpp b/effect_modulated_delay.cpp new file mode 100644 index 0000000..d9a1f99 --- /dev/null +++ b/effect_modulated_delay.cpp @@ -0,0 +1,134 @@ +/* Audio Library for Teensy 3.X + Copyright (c) 2014, Pete (El Supremo) + Copyright (c) 2019, Holger Wirtz + + 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 +#include +#include "arm_math.h" +#include "effect_modulated_delay.h" +#include "config.h" + +extern config_t configuration; + +/******************************************************************/ + +// Based on; A u d i o E f f e c t D e l a y +// Written by Pete (El Supremo) Jan 2014 +// 140529 - change to handle mono stream - change modify() to voices() +// 140219 - correct storage class (not static) +// 190527 - added modulation input (by Holger Wirtz) + +boolean AudioEffectModulatedDelay::begin(short *delayline, uint16_t d_length) +{ +#if 0 + Serial.print(F("AudioEffectModulatedDelay.begin(modulated-delay line length = ")); + Serial.print(d_length); + Serial.println(F(")")); +#endif + + _delayline = NULL; + _delay_length = 0; + _cb_index = 0; + _delay_offset = 0; + + if (delayline == NULL) + return (false); + if (d_length < 10) + return (false); + + _delayline = delayline; + _delay_length = d_length; + memset(_delayline, 0, _delay_length * sizeof(int16_t)); + _delay_offset = _delay_length >> 1 ; + + return (true); +} + +uint16_t AudioEffectModulatedDelay::get_delay_length(void) +{ + return (_delay_length); +} + +void AudioEffectModulatedDelay::update(void) +{ + audio_block_t *block; + audio_block_t *modulation; + + if (_delayline == NULL) + return; + + block = receiveWritable(0); + modulation = receiveReadOnly(1); + + if (block && modulation) + { + int16_t *bp; + int16_t cb_mod_index_neighbor; + float *mp; + float mod_index; + float mod_number; + float mod_fraction; + float modulation_f32[AUDIO_BLOCK_SAMPLES]; + + bp = block->data; + arm_q15_to_float(modulation->data, modulation_f32, AUDIO_BLOCK_SAMPLES); + mp = modulation_f32; + + for (uint16_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) + { + // write data into circular buffer (delayline) + if (_cb_index >= _delay_length) + _cb_index = 0; + _delayline[_cb_index] = *bp; + + // calculate the modulation-index as a floating point number for interpolation + mod_index = *mp * _delay_offset; + mod_fraction = modff(mod_index, &mod_number); // split float of mod_index into integer (= mod_number) and fraction part + + // calculate modulation index into circular buffer + cb_mod_index = _cb_index - (_delay_offset + mod_number); + if (cb_mod_index < 0) // check for negative offsets and correct them + cb_mod_index += _delay_length; + + if (cb_mod_index == _delay_length - 1) + cb_mod_index_neighbor = 0; + 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)); + + // push the pointers forward + bp++; // next audio data + mp++; // next modulation data + _cb_index++; // next circular buffer index + } + } + + if (modulation) + release(modulation); + + if (block) + { + transmit(block, 0); + release(block); + } +} diff --git a/effect_modulated_delay.h b/effect_modulated_delay.h new file mode 100644 index 0000000..85ed2a8 --- /dev/null +++ b/effect_modulated_delay.h @@ -0,0 +1,58 @@ +/* Audio Library for Teensy 3.X + Copyright (c) 2014, Pete (El Supremo) + Copyright (c) 2019, Holger Wirtz + + 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_modulated_chorus_h_ +#define effect_modulated_chorus_h_ + +#include "Arduino.h" +#include "AudioStream.h" +#include "config.h" + +/*************************************************************************/ +// A u d i o E f f e c t M o d u l a t e d D e l a y +// Written by Pete (El Supremo) Jan 2014 +// 140219 - correct storage class (not static) +// 190527 - added modulation input handling (Aug 2019 by Holger Wirtz) + +class AudioEffectModulatedDelay : + public AudioStream +{ + public: + AudioEffectModulatedDelay(void): + AudioStream(2, inputQueueArray) + { } + + boolean begin(short *delayline, uint16_t delay_length); + virtual void update(void); + virtual uint16_t get_delay_length(void); + + private: + audio_block_t *inputQueueArray[2]; + int16_t *_delayline; // pointer for the circular buffer + uint16_t _cb_index; // current write pointer of the circular buffer + uint16_t _delay_length; // calculated number of samples of the delay + int16_t cb_mod_index; // current read pointer with modulation for the circular buffer + uint16_t _delay_offset; +}; + +#endif