diff --git a/MicroMDAEPiano.ino b/MicroMDAEPiano.ino index 35e3ac2..379c709 100644 --- a/MicroMDAEPiano.ino +++ b/MicroMDAEPiano.ino @@ -30,6 +30,7 @@ #include "EEPROMAnything.h" #include #include "mdaEPiano.h" +#include "effect_modulated_chorus.h" #ifdef USE_XFADE_DATA #include "mdaEPianoDataXfade.h" #else @@ -53,30 +54,39 @@ AudioMixer4 mixer_r; AudioMixer4 mixer_l; AudioAmplifier volume_r; AudioAmplifier volume_l; +AudioModulatedEffectChorus modchorus_r; +AudioModulatedEffectChorus modchorus_l; +AudioSynthWaveformSine mod_sine1; AudioConnection patchCord0(queue_r, peak_r); AudioConnection patchCord1(queue_l, peak_l); -AudioConnection patchCord4(queue_r, freeverb_r); -AudioConnection patchCord5(queue_l, freeverb_l); -AudioConnection patchCord6(queue_r, 0, mixer_r, 0); -AudioConnection patchCord7(queue_l, 0, mixer_l, 0); -AudioConnection patchCord8(freeverb_r, 0, mixer_r, 1); -AudioConnection patchCord9(freeverb_l, 0, mixer_l, 1); -AudioConnection patchCord10(mixer_r, volume_r); -AudioConnection patchCord11(mixer_l, volume_l); +AudioConnection patchCord2(queue_r, freeverb_r); +AudioConnection patchCord3(queue_l, freeverb_l); +AudioConnection patchCord4(queue_r, 0, modchorus_r, 0); +AudioConnection patchCord5(queue_l, 0, modchorus_l, 0); +AudioConnection patchCord6(mod_sine1, 0, modchorus_r, 1); +AudioConnection patchCord7(mod_sine1, 0, modchorus_l, 1); +AudioConnection patchCord8(queue_r, 0, mixer_r, 0); +AudioConnection patchCord9(queue_l, 0, mixer_l, 0); +AudioConnection patchCord10(modchorus_r, 0, mixer_r, 2); +AudioConnection patchCord11(modchorus_l, 0, mixer_l, 2); +AudioConnection patchCord12(freeverb_r, 0, mixer_r, 1); +AudioConnection patchCord13(freeverb_l, 0, mixer_l, 1); +AudioConnection patchCord14(mixer_r, volume_r); +AudioConnection patchCord15(mixer_l, volume_l); #if defined(TEENSY_AUDIO_BOARD) AudioOutputI2S i2s1; -AudioConnection patchCord12(volume_r, 0, i2s1, 0); -AudioConnection patchCord13(volume_l, 0, i2s1, 1); +AudioConnection patchCord16(volume_r, 0, i2s1, 0); +AudioConnection patchCord17(volume_l, 0, i2s1, 1); AudioControlSGTL5000 sgtl5000_1; #elif defined(TGA_AUDIO_BOARD) AudioOutputI2S i2s1; -AudioConnection patchCord12(volume_r, 0, i2s1, 1); -AudioConnection patchCord13(volume_l, 0, i2s1, 0); +AudioConnection patchCord16(volume_r, 0, i2s1, 1); +AudioConnection patchCord17(volume_l, 0, i2s1, 0); AudioControlWM8731master wm8731_1; #else AudioOutputPT8211 pt8211_1; -AudioConnection patchCord12(volume_r, 0, pt8211_1, 1); -AudioConnection patchCord13(volume_l, 0, pt8211_1, 0); +AudioConnection patchCord16(volume_r, 0, pt8211_1, 1); +AudioConnection patchCord17(volume_l, 0, pt8211_1, 0); #endif // Objects @@ -132,7 +142,6 @@ config_t configuration = { 0 // pan }; -float _loudness = mapfloat(float(ENC_LOUDNESS_DEFAULT), ENC_LOUDNESS_MIN, ENC_LOUDNESS_MAX, 0.0, 1.0); uint8_t master_volume = ENC_MASTER_VOLUME_DEFAULT; int8_t pan = ENC_MASTER_PAN_DEFAULT; uint8_t eeprom_config_update_flag = 0; @@ -143,6 +152,12 @@ elapsedMillis eeprom_master_volume_update_timer; elapsedMillis cpu_mem_millis; #endif +// Number of samples in each delay line +#define CHORUS_DELAY_LENGTH (16*AUDIO_BLOCK_SAMPLES) +// Allocate the delay lines for left and right channels +short l_delayline[CHORUS_DELAY_LENGTH]; +short r_delayline[CHORUS_DELAY_LENGTH]; + //************************************************************************************************* //* SETUP FUNCTION //************************************************************************************************* @@ -213,6 +228,19 @@ void setup() Serial.print(audio_block_time_us); Serial.println(F("ms)")); + if (!modchorus_r.begin(r_delayline, CHORUS_DELAY_LENGTH, NUM_CHORUS)) { + Serial.println(F("AudioModulatedEffectChorus - right channel begin failed")); + while (1); + } + if (!modchorus_l.begin(l_delayline, CHORUS_DELAY_LENGTH, NUM_CHORUS)) { + Serial.println(F("AudioModulatedEffectChorus - left channel begin failed")); + while (1); + } + + mod_sine1.amplitude(1.0); + mod_sine1.frequency(5.0); + mod_sine1.phase(0); + AudioInterrupts(); Serial.println(F("")); @@ -451,7 +479,7 @@ bool checkMidiChannel(byte inChannel) void set_master_volume(uint8_t value) { - configuration.pan = 0; // BAD HACK! + //configuration.pan = 0; // BAD HACK! uint16_t tmp = map(value, ENC_MASTER_VOLUME_MIN, ENC_MASTER_VOLUME_MAX, 0, 0x3ff); float tmp2 = mapfloat(configuration.pan, ENC_MASTER_PAN_MIN, ENC_MASTER_PAN_MAX, 0.0, 1.0); float tmp3 = (float)(tmp * (tmp + 2)) / (float)(1 << 20); @@ -477,6 +505,9 @@ void set_master_volume(uint8_t value) eeprom_master_volume_update_flag = true; eeprom_master_volume_update_timer = 0; + + if (menu_system.get_currentScreen() == &master_volume_screen) + menu_system.update(); } /****************************************************************************** diff --git a/UI.hpp b/UI.hpp index 3232ec5..bafc386 100644 --- a/UI.hpp +++ b/UI.hpp @@ -125,7 +125,6 @@ extern AudioMixer4 mixer_l; extern AudioAmplifier volume_r; extern AudioAmplifier volume_l; extern mdaEPiano* ep; -extern float _loudness; extern config_t configuration; extern uint8_t sound; extern uint8_t master_volume; @@ -2074,7 +2073,7 @@ void set_loudness(uint8_t value) Serial.print(F("Set LOUDNESS ")); Serial.println(value); #endif - _loudness = mapfloat(float(value), ENC_LOUDNESS_MIN, ENC_LOUDNESS_MAX, 0.0, 1.0); + ep->setLoudness(mapfloat(float(value), ENC_LOUDNESS_MIN, ENC_LOUDNESS_MAX, 0.0, 1.0)); //volume_r.gain(tmp); //volume_l.gain(tmp); configuration.loudness = value; diff --git a/config.h b/config.h index 4d8bd77..75d1dad 100644 --- a/config.h +++ b/config.h @@ -61,6 +61,7 @@ #define SAMPLE_RATE 44100 #define REDUCE_LOUDNESS 0 #define USE_XFADE_DATA 1 +#define NUM_CHORUS 2 //************************************************************************************************* //* DEBUG OUTPUT SETTINGS diff --git a/effect_modulated_chorus.cpp b/effect_modulated_chorus.cpp new file mode 100644 index 0000000..4f3c887 --- /dev/null +++ b/effect_modulated_chorus.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 "effect_modulated_chorus.h" + +/******************************************************************/ + +// Based on; A u d i o E f f e c t C h o r u s +// Written by Pete (El Supremo) Jan 2014 +// 140529 - change to handle mono stream - change modify() to voices() +// 140219 - correct storage class (not static) +// 190527 - adding modulation input (by Holger Wirtz) + +boolean AudioModulatedEffectChorus::begin(short *delayline, int d_length, int n_chorus) +{ +#if 0 + Serial.print("AudioModulatedEffectChorus.begin(Chorus delay line length = "); + Serial.print(d_length); + Serial.print(", n_chorus = "); + Serial.print(n_chorus); + Serial.println(")"); +#endif + + l_delayline = NULL; + delay_length = 0; + l_circ_idx = 0; + + if (delayline == NULL) { + return (false); + } + if (d_length < 10) { + return (false); + } + if (n_chorus < 1) { + return (false); + } + + l_delayline = delayline; + delay_length = d_length / 2; + num_chorus = n_chorus; + + return (true); +} + +void AudioModulatedEffectChorus::voices(int n_chorus) +{ + num_chorus = n_chorus; +} + +//int last_idx = 0; +void AudioModulatedEffectChorus::update(void) +{ + audio_block_t *block; + audio_block_t *modulation; + short *bp; + int sum; + int c_idx; + + if (l_delayline == NULL)return; + + // do passthru + // It stores the unmodified data in the delay line so that + // it isn't as likely to click + if (num_chorus <= 1) { + // Just passthrough + block = receiveWritable(0); + if (block) { + bp = block->data; + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + l_circ_idx++; + if (l_circ_idx >= delay_length) { + l_circ_idx = 0; + } + l_delayline[l_circ_idx] = *bp++; + } + transmit(block, 0); + release(block); + } + return; + } + + // L E F T C H A N N E L + + block = receiveWritable(0); + modulation = receiveReadOnly(1); + + if (block) { + bp = block->data; + uint32_t tmp = delay_length / (num_chorus - 1) - 1; + for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { + l_circ_idx++; + if (l_circ_idx >= delay_length) { + l_circ_idx = 0; + } + l_delayline[l_circ_idx] = *bp; + sum = 0; + c_idx = l_circ_idx; + for (int k = 0; k < num_chorus; k++) { + sum += l_delayline[c_idx]; + if (num_chorus > 1)c_idx -= tmp; + if (c_idx < 0) { + c_idx += delay_length; + } + } + *bp++ = sum / num_chorus; + } + + // transmit the block + transmit(block, 0); + release(block); + release(modulation); + } +} diff --git a/effect_modulated_chorus.h b/effect_modulated_chorus.h new file mode 100644 index 0000000..a9f2acb --- /dev/null +++ b/effect_modulated_chorus.h @@ -0,0 +1,60 @@ +/* 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" + +/******************************************************************/ + +// A u d i o E f f e c t C h o r u s +// Written by Pete (El Supremo) Jan 2014 +// 140219 - correct storage class (not static) +// 190527 - adding modulation input (by Holger Wirtz) + +#define CHORUS_DELAY_PASSTHRU -1 + +class AudioModulatedEffectChorus : + public AudioStream +{ + public: + AudioModulatedEffectChorus(void): + AudioStream(2, inputQueueArray), num_chorus(2) + { } + + boolean begin(short *delayline, int delay_length, int n_chorus); + virtual void update(void); + void voices(int n_chorus); + + private: + audio_block_t *inputQueueArray[2]; + + short *l_delayline; + short l_circ_idx; + int num_chorus; + int delay_length; +}; + +#endif diff --git a/mdaEPiano.cpp b/mdaEPiano.cpp index ad2a339..f419207 100644 --- a/mdaEPiano.cpp +++ b/mdaEPiano.cpp @@ -29,7 +29,7 @@ #include #include -extern float _loudness; +extern void set_master_volume(uint8_t value); extern uint8_t master_volume; extern config_t configuration; @@ -61,6 +61,7 @@ mdaEPiano::mdaEPiano() // mdaEPiano::mdaEPiano(audioMasterCallback audioMaster) setTune(0.500f); setDetune(0.146f); setOverdrive(0.000f); + setLoudness(0.64616f); waves = (short*)epianoDataXfade; @@ -249,9 +250,14 @@ void mdaEPiano::setOverdrive(float value) setParameter(MDA_EP_OVERDRIVE, value); } +void mdaEPiano::setLoudness(float value) +{ + volume = value * 0.32258; // 0.00002 * 127^2 +} + void mdaEPiano::setParameter(int32_t index, float value) { - programs[ 0].param[index] = value; + programs[0].param[index] = value; update(); } @@ -314,8 +320,8 @@ void mdaEPiano::process(int16_t* outputs_r, int16_t* outputs_l) l = 1.0; else if (l < -1.0) l = -1.0; - outputs_l[frame] = static_cast(l * _loudness * 0x7fff) >> REDUCE_LOUDNESS; - outputs_r[frame] = static_cast(r * _loudness * 0x7fff) >> REDUCE_LOUDNESS; + outputs_l[frame] = static_cast(l * 0x7fff) >> REDUCE_LOUDNESS; + outputs_r[frame] = static_cast(r * 0x7fff) >> REDUCE_LOUDNESS; } if (fabs(tl) < 1.0e-10) tl = 0.0f; //anti-denormal @@ -419,7 +425,7 @@ bool mdaEPiano::processMidiController(uint8_t data1, uint8_t data2) break; case 0x07: //volume - master_volume = map(data2, 0, 127, ENC_MASTER_VOLUME_MIN, ENC_MASTER_VOLUME_MAX); + set_master_volume(map(data2, 0, 127, ENC_MASTER_VOLUME_MIN, ENC_MASTER_VOLUME_MAX)); break; case 0x40: //sustain pedal diff --git a/mdaEPiano.h b/mdaEPiano.h index d98ec74..f4f988b 100644 --- a/mdaEPiano.h +++ b/mdaEPiano.h @@ -112,6 +112,7 @@ class mdaEPiano void setTune(float value); void setDetune(float value); void setOverdrive(float value); + void setLoudness(float value); int32_t getActiveVoices(void); private: @@ -135,7 +136,7 @@ class mdaEPiano float treb, tfrq, tl, tr; float tune, fine, random, stretch, overdrive; float muff, muffvel, sizevel, velsens, modwhl; - const float volume = 0.00002f * 127; + float volume; float vol; //uint8_t curProgram; };