diff --git a/MicroDexed.ino b/MicroDexed.ino index 40ec269..80b17da 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -30,89 +30,93 @@ #include #include #include -#include "UI.hpp" #include "EEPROMAnything.h" #include "midi_devices.hpp" #include "dexed.h" #include "dexed_sysex.h" #include "effect_modulated_delay.h" +#include "effect_stereo_mono.h" #include "PluginFx.h" #include "SoftenValue.hpp" +#include "UI.hpp" AudioPlayQueue queue1; AudioAnalyzePeak peak1; AudioEffectDelay delay1; -#ifdef USE_REVERB -AudioEffectFreeverbStereo freeverbs1; -#endif AudioEffectModulatedDelay modchorus; -#if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT -AudioFilterBiquad modchorus_filter; -#endif AudioSynthWaveform modulator; -AudioMixer4 chorus_mixer; -AudioMixer4 delay_mixer; AudioMixer4 delay_fb_mixer; -AudioMixer4 reverb_mixer_r; -AudioMixer4 reverb_mixer_l; +AudioMixer4 master_mixer_r; +AudioMixer4 master_mixer_l; AudioAmplifier volume_r; AudioAmplifier volume_l; #if defined(AUDIO_DEVICE_USB) AudioOutputUSB usb1; #endif +AudioEffectStereoMono stereomono1; AudioConnection patchCord0(queue1, peak1); -AudioConnection patchCord1(queue1, 0, chorus_mixer, 0); -AudioConnection patchCord3(queue1, 0, delay_mixer, 0); -AudioConnection patchCord4(queue1, 0, delay_fb_mixer, 0); -#ifdef USE_REVERB -AudioConnection patchCord5(queue1, 0, reverb_mixer_r, 0); -AudioConnection patchCord6(queue1, 0, reverb_mixer_l, 0); -#endif -AudioConnection patchCord7(queue1, 0, modchorus, 0); -AudioConnection patchCord8(modulator, 0, modchorus, 1); +AudioConnection patchCord1(queue1, 0, delay_fb_mixer, 0); +AudioConnection patchCord2(queue1, 0, modchorus, 0); +AudioConnection patchCord3(queue1, 0 , master_mixer_r, 0); +AudioConnection patchCord4(queue1, 0 , master_mixer_l, 0); +AudioConnection patchCord5(modulator, 0, modchorus, 1); #if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT -AudioConnection patchCord9(modchorus, modchorus_filter); -AudioConnection patchCord10(modchorus_filter, 0, chorus_mixer, 1); -#else -AudioConnection patchCord9(modchorus, 0, chorus_mixer, 1); -#endif -AudioConnection patchCord11(chorus_mixer, 0, delay_mixer, 1); -AudioConnection patchCord12(delay_fb_mixer, 0, delay_mixer, 1); -AudioConnection patchCord13(delay_mixer, 0, reverb_mixer_r, 0); -AudioConnection patchCord14(delay_mixer, 0, reverb_mixer_l, 0); -AudioConnection patchCord15(delay1, 0, delay_mixer, 2); -AudioConnection patchCord16(delay_fb_mixer, delay1); -#ifdef USE_REVERB -AudioConnection patchCord17(delay_mixer, 0, freeverbs1, 0); -AudioConnection patchCord18(delay_mixer, 0, freeverbs1, 1); -AudioConnection patchCord19(freeverbs1, 0, reverb_mixer_r, 1); -AudioConnection patchCord20(freeverbs1, 1, reverb_mixer_l, 1); -AudioConnection patchCord21(reverb_mixer_r, volume_r); -AudioConnection patchCord22(reverb_mixer_l, volume_l); +AudioFilterBiquad modchorus_filter; +AudioConnection patchCord6(modchorus, modchorus_filter); +AudioConnection patchCord7(modchorus_filter, 0, master_mixer_r, 3); +AudioConnection patchCord8(modchorus_filter, 0, master_mixer_l, 3); #else -AudioConnection patchCord17(delay_mixer, volume_r); -AudioConnection patchCord18(delay_mixer, volume_l); +AudioConnection patchCord9(modchorus, 0, master_mixer_r, 3); +AudioConnection patchCord10(modchorus, 0, master_mixer_l, 3); #endif -#if defined(AUDIO_DEVICE_USB) -AudioConnection patchCord23(volume_r, 0, usb1, 0); -AudioConnection patchCord24(volume_l, 0, usb1, 1); +#if defined(USE_REVERB) +AudioEffectFreeverbStereo freeverbs1; +AudioConnection patchCord11(queue1, 0, freeverbs1, 0); +AudioConnection patchCord12(queue1, 0, freeverbs1, 1); +AudioConnection patchCord13(freeverbs1, 0, master_mixer_r, 1); +AudioConnection patchCord14(freeverbs1, 1, master_mixer_l, 1); #endif -#if defined(TEENSY_AUDIO_BOARD) || defined (I2S_AUDIO_ONLY) -AudioOutputI2S i2s1; -AudioConnection patchCord25(volume_r, 0, i2s1, 0); -AudioConnection patchCord26(volume_l, 0, i2s1, 1); +AudioConnection patchCord15(delay_fb_mixer, delay1); +AudioConnection patchCord16(delay1, 0, delay_fb_mixer, 1); +AudioConnection patchCord17(delay1, 0, master_mixer_r, 2); +AudioConnection patchCord18(delay1, 0, master_mixer_l, 2); +AudioConnection patchCord19(master_mixer_r, volume_r); +AudioConnection patchCord20(master_mixer_l, volume_l); + +AudioConnection patchCord21(volume_r, 0, stereomono1, 0); +AudioConnection patchCord22(volume_l, 0, stereomono1, 1); +#ifdef AUDIO_DEVICE_USB +AudioConnection patchCord23(stereomono1, 0, usb1, 0); +AudioConnection patchCord24(stereomono1, 1, usb1, 1); #endif #if defined(TEENSY_AUDIO_BOARD) +AudioOutputI2S i2s1; +AudioConnection patchCord25(stereomono1, 0, i2s1, 0); +AudioConnection patchCord26(stereomono1, 0, i2s1, 1); AudioControlSGTL5000 sgtl5000_1; +#elif defined (I2S_AUDIO_ONLY) +AudioOutputI2S i2s1; +AudioConnection patchCord27(stereomono1, 0, i2s1, 0); +AudioConnection patchCord28(stereomono1, 0, i2s1, 1); #elif defined(TGA_AUDIO_BOARD) AudioOutputI2S i2s1; -AudioConnection patchCord27(volume_r, 0, i2s1, 0); -AudioConnection patchCord28(volume_l, 0, i2s1, 1); +AudioConnection patchCord29(stereomono1, 0, i2s1, 0); +AudioConnection patchCord30(stereomono1, 1, i2s1, 1); AudioControlWM8731master wm8731_1; -#elif !defined(I2S_AUDIO_ONLY) +#elif defined(PT8211_AUDIO) AudioOutputPT8211 pt8211_1; -AudioConnection patchCord29(volume_r, 0, pt8211_1, 0); -AudioConnection patchCord30(volume_l, 0, pt8211_1, 1); +AudioConnection patchCord31(stereomono1, 0, pt8211_1, 0); +AudioConnection patchCord32(stereomono1, 1, pt8211_1, 1); +#elif defined(TEENSY_DAC_SYMMETRIC) +AudioOutputAnalogStereo dacOut; +AudioMixer4 invMixer; +AudioConnection patchCord33(stereomono1, 0, dacOut , 0); +AudioConnection patchCord34(stereomono1, 1, invMixer, 0); +AudioConnection patchCord35(invMixer, 0, dacOut , 1); +#else +AudioOutputAnalogStereo dacOut; +AudioConnection patchCord36(stereomono1, 0, dacOut, 0); +AudioConnection patchCord37(stereomono1, 1, dacOut, 1); #endif Dexed* MicroDexed[NUM_DEXED]; @@ -132,26 +136,35 @@ uint8_t midi_timing_counter = 0; // 24 per qarter elapsedMillis midi_timing_timestep; uint16_t midi_timing_quarter = 0; elapsedMillis long_button_pressed; -//SoftenValue effect_filter_volume[NUM_DEXED](SOFTEN_VALUE_CHANGE_STEPS); -//SoftenValue soften_filter_res[NUM_DEXED](SOFTEN_VALUE_CHANGE_STEPS); -//SoftenValue soften_filter_cut[NUM_DEXED](SOFTEN_VALUE_CHANGE_STEPS); -uint8_t effect_filter_cutoff = 0; -uint8_t effect_filter_resonance = 0; -uint8_t effect_delay_time = 0; -uint8_t effect_delay_feedback = 0; -uint8_t effect_delay_volume = 0; -bool effect_delay_sync = 0; +SoftenValue soften_volume; +SoftenValue soften_filter_res[NUM_DEXED]; +SoftenValue soften_filter_cut[NUM_DEXED]; elapsedMicros fill_audio_buffer; elapsedMillis control_rate; uint8_t active_voices = 0; #ifdef SHOW_CPU_LOAD_MSEC elapsedMillis cpu_mem_millis; #endif -config_t configuration = {0xffff, 0, 0, VOLUME, 0.5f, 0, DEFAULT_MIDI_CHANNEL}; +config_t configuration = { + 0xffff, + SYSEXBANK_DEFAULT, + SYSEXSOUND_DEFAULT, + VOLUME_DEFAULT, + PANORAMA_DEFAULT, // pan + MONO_DEFAULT, // mono + DEFAULT_MIDI_CHANNEL, + REVERB_ROOMSIZE_DEFAULT, + REVERB_DAMPING_DEFAULT, + REVERB_LEVEL_DEFAULT, + CHORUS_FREQUENCY_DEFAULT, + CHORUS_WAVEFORM_DEFAULT, + CHORUS_DEPTH_DEFAULT, + CHORUS_LEVEL_DEFAULT, + FILTER_CUTOFF_DEFAULT, + FILTER_RESONANCE_DEFAULT, + LOUDNESS_DEFAULT +}; bool eeprom_update_flag = false; -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 delayline[MOD_DELAY_SAMPLE_BUFFER]; @@ -211,7 +224,7 @@ void setup() AudioNoInterrupts(); AudioMemory(AUDIO_MEM); -#ifdef TEENSY_AUDIO_BOARD +#if defined(TEENSY_AUDIO_BOARD) sgtl5000_1.enable(); sgtl5000_1.dacVolumeRamp(); //sgtl5000_1.dacVolumeRampLinear(); @@ -246,8 +259,13 @@ void setup() Serial.println(F("TGA board enabled.")); #elif defined(I2S_AUDIO_ONLY) Serial.println(F("I2S enabled.")); -#else +#elif defined(PT8211_AUDIO) Serial.println(F("PT8211 enabled.")); +#elif defined(TEENSY_DAC_SYMMETRIC) + invMixer.gain(0, -1.f); + Serial.println(F("Internal DAC using symmetric outputs enabled.")); +#else + Serial.println(F("Internal DAC enabled.")); #endif // start SD card @@ -309,6 +327,21 @@ void setup() Serial.print(MOD_DELAY_SAMPLE_BUFFER, DEC); Serial.println(F(" samples")); #endif + master_mixer_r.gain(0, 1.0); + master_mixer_l.gain(0, 1.0); + + freeverbs1.roomsize(configuration.reverb_roomsize / 100.0); + freeverbs1.damping(configuration.reverb_damping / 100.0); + master_mixer_r.gain(1, 0.0); + master_mixer_l.gain(1, 0.0); + + delay1.delay(0, 0.0); + // delay_fb_mixer is the feedback-adding mixer + delay_fb_mixer.gain(0, 1.0 - configuration.delay_feedback / 100.0); // original signal + delay_fb_mixer.gain(1, configuration.delay_feedback / 100.0); // amount of feedback + master_mixer_r.gain(2, 0.0); + master_mixer_l.gain(2, 0.0); + modulator.begin(MOD_WAVEFORM); modulator.phase(0); modulator.amplitude(0.5); @@ -327,32 +360,21 @@ void setup() modchorus_filter.setLowpass(2, MOD_FILTER_CUTOFF_HZ, 0.54); modchorus_filter.setLowpass(3, MOD_FILTER_CUTOFF_HZ, 1.3); #endif - - chorus_mixer.gain(0, 1.0); - chorus_mixer.gain(1, 0.0); - - delay1.delay(0, mapfloat(effect_delay_feedback, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); - // delay_fb_mixer is the feedback-adding mixer, delay_mixer_r the whole delay (with/without feedback) mixer - delay_fb_mixer.gain(0, 1.0); // original signal - delay_fb_mixer.gain(1, mapfloat(effect_delay_feedback, 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); // amount of feedback - delay_mixer.gain(0, 1.0 - mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // original signal - delay_mixer.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delayed signal (including feedback) - delay_mixer.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); + master_mixer_r.gain(3, 0.0); + master_mixer_l.gain(3, 0.0); for (uint8_t i = 0; i < NUM_DEXED; i++) { + soften_filter_res[i].init(1.0, 0.0, 1.0); + soften_filter_cut[i].init(1.0, 0.0, 1.0); MicroDexed[i]->fx.Gain = 1.0; - MicroDexed[i]->fx.Reso = 1.0 - float(effect_filter_resonance) / ENC_FILTER_RES_STEPS; - MicroDexed[i]->fx.Cutoff = 1.0 - float(effect_filter_cutoff) / ENC_FILTER_CUT_STEPS; + MicroDexed[i]->fx.Reso = 1.0; + MicroDexed[i]->fx.Cutoff = 1.0; } // set initial volume and pan (read from EEPROM) set_volume(configuration.vol, configuration.pan); + soften_volume.init(configuration.vol, VOLUME_MIN, VOLUME_MAX); #if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) // Initialize processor and memory measurements @@ -412,10 +434,6 @@ void loop() if (peak1.read() > 0.99) peak++; } -#ifndef TEENSY_AUDIO_BOARD - for (uint8_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) - audio_buffer[i] *= configuration.vol; -#endif queue1.playBuffer(); } @@ -445,62 +463,56 @@ void loop() #endif control_rate = 0; - // Shutdown unused voices + // check for value changes and unused voices + soften_volume.tick(); + for (uint8_t i = 0; i < NUM_DEXED; i++) { active_voices = MicroDexed[i]->getNumNotesPlaying(); - } - // check for value changes - if (soften_volume.steps > 0) - { - // soften volume value - soften_volume.steps--; - set_volume(configuration.vol + soften_volume.diff, configuration.pan); -#ifdef DEBUG - Serial.print(F("Volume: ")); - Serial.print(configuration.vol, 5); - Serial.print(F(" Volume step: ")); - Serial.print(soften_volume.steps); - Serial.print(F(" Volume diff: ")); - Serial.println(soften_volume.diff, 5); -#endif - } - if (soften_filter_res.steps > 0) - { - // soften filter resonance value - soften_filter_res.steps--; - for (uint8_t i = 0; i < NUM_DEXED; i++) + soften_filter_res[i].tick(); + soften_filter_cut[i].tick(); + + if (soften_filter_res[i].running()) { - MicroDexed[i]->fx.Reso = MicroDexed[i]->fx.Reso + soften_filter_res.diff; + // soften filter resonance value + MicroDexed[i]->fx.Reso = soften_filter_res[i].value(); #ifdef DEBUG Serial.print(F("Filter-Resonance: ")); Serial.print(MicroDexed[i]->fx.Reso, 5); Serial.print(F(" Filter-Resonance step: ")); - Serial.print(soften_filter_res.steps); + Serial.print(soften_filter_res[i].steps()); Serial.print(F(" Filter-Resonance diff: ")); - Serial.println(soften_filter_res.diff, 5); + Serial.println(soften_filter_res[i].diff(), 5); #endif } - } - if (soften_filter_cut.steps > 0) - { // soften filter cutoff value - soften_filter_cut.steps--; - for (uint8_t i = 0; i < NUM_DEXED; i++) + if (soften_filter_cut[i].running()) { - MicroDexed[i]->fx.Cutoff = MicroDexed[i]->fx.Cutoff + soften_filter_cut.diff; + MicroDexed[i]->fx.Cutoff = soften_filter_cut[i].value(); #ifdef DEBUG Serial.print(F("Filter-Cutoff: ")); Serial.print(MicroDexed[i]->fx.Cutoff, 5); Serial.print(F(" Filter-Cutoff step: ")); - Serial.print(soften_filter_cut.steps); + Serial.print(soften_filter_cut[i].steps()); Serial.print(F(" Filter-Cutoff diff: ")); - Serial.println(soften_filter_cut.diff, 5); + Serial.println(soften_filter_cut[i].diff(), 5); #endif } } + if (soften_volume.running()) + { + set_volume(soften_volume.value(), configuration.pan); +#ifdef DEBUG + Serial.print(F("Volume: ")); + Serial.print(configuration.vol, DEC); + Serial.print(F(" step: ")); + Serial.print(soften_volume.steps()); + Serial.print(F(" diff: ")); + Serial.println(soften_volume.diff(), 5); +#endif + } } #if defined (DEBUG) && defined (SHOW_CPU_LOAD_MSEC) @@ -551,73 +563,107 @@ void handleControlChange(byte inChannel, byte inCtrl, byte inValue) } break; case 1: - MicroDexed[0]->controllers.modwheel_cc = inValue; - MicroDexed[0]->controllers.refresh(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->controllers.modwheel_cc = inValue; + MicroDexed[i]->controllers.refresh(); + } break; case 2: - MicroDexed[0]->controllers.breath_cc = inValue; - MicroDexed[0]->controllers.refresh(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->controllers.breath_cc = inValue; + MicroDexed[i]->controllers.refresh(); + } break; case 4: - MicroDexed[0]->controllers.foot_cc = inValue; - MicroDexed[0]->controllers.refresh(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->controllers.foot_cc = inValue; + MicroDexed[i]->controllers.refresh(); + } break; case 7: // Volume - configuration.vol = float(inValue) / 0x7f; - set_volume(configuration.vol, configuration.pan); + configuration.vol = map(inValue, 0, 0x7f, VOLUME_MIN, VOLUME_MAX); + soften_volume.update(configuration.vol, SOFTEN_VALUE_CHANGE_STEPS); break; case 10: // Pan - configuration.pan = float(inValue) / 128; + configuration.pan = map(inValue, 0, 0x7f, PANORAMA_MIN, PANORAMA_MAX); set_volume(configuration.vol, configuration.pan); break; case 32: // BankSelect LSB configuration.bank = inValue; break; case 64: - MicroDexed[0]->setSustain(inValue > 63); - if (!MicroDexed[0]->getSustain()) { - for (uint8_t note = 0; note < MicroDexed[0]->getMaxNotes(); note++) { - if (MicroDexed[0]->voices[note].sustained && !MicroDexed[0]->voices[note].keydown) { - MicroDexed[0]->voices[note].dx7_note->keyup(); - MicroDexed[0]->voices[note].sustained = false; + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->setSustain(inValue > 63); + if (!MicroDexed[i]->getSustain()) { + for (uint8_t note = 0; note < MicroDexed[0]->getMaxNotes(); note++) { + if (MicroDexed[i]->voices[note].sustained && !MicroDexed[i]->voices[note].keydown) { + MicroDexed[i]->voices[note].dx7_note->keyup(); + MicroDexed[i]->voices[note].sustained = false; + } } } } break; case 103: // CC 103: filter resonance - effect_filter_resonance = map(inValue, 0, 127, 0, ENC_FILTER_RES_STEPS); - MicroDexed[0]->fx.Reso = 1.0 - float(effect_filter_resonance) / ENC_FILTER_RES_STEPS; + configuration.filter_resonance = map(inValue, 0, 0x7f, FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->fx.Reso = configuration.filter_resonance / 100.0; + } break; case 104: // CC 104: filter cutoff - effect_filter_cutoff = map(inValue, 0, 127, 0, ENC_FILTER_CUT_STEPS); - MicroDexed[0]->fx.Cutoff = 1.0 - float(effect_filter_cutoff) / ENC_FILTER_CUT_STEPS; + configuration.filter_cutoff = map(inValue, 0, 0x7f, FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->fx.Cutoff = configuration.filter_cutoff / 100.0; + } break; case 105: // CC 105: delay time - effect_delay_time = map(inValue, 0, 127, 0, ENC_DELAY_TIME_STEPS); - ////delay.delay(0, mapfloat(effect_delay_time, 0, ENC_DELAY_TIME_STEPS, 0.0, DELAY_MAX_TIME)); - break; + configuration.delay_time = map(inValue, 0, 0x7f, DELAY_TIME_MIN, DELAY_TIME_MAX); + delay1.delay(0, configuration.delay_time * 10); case 106: // CC 106: delay feedback - effect_delay_feedback = map(inValue, 0, 127, 0, ENC_DELAY_FB_STEPS); - ////delay_mixer_r.gain(1, mapfloat(float(effect_delay_feedback), 0, ENC_DELAY_FB_STEPS, 0.0, 1.0)); + configuration.delay_feedback = map(inValue, 0, 0x7f, DELAY_FEEDBACK_MIN , DELAY_FEEDBACK_MAX); + delay_fb_mixer.gain(1, configuration.delay_feedback / 100.0 ); // amount of feedback + delay_fb_mixer.gain(0, 1.0 - configuration.delay_feedback / 100.0); // original signalbreak; break; case 107: // CC 107: delay volume - effect_delay_volume = map(inValue, 0, 127, 0, ENC_DELAY_VOLUME_STEPS); - ////delay_mixer_l.gain(1, mapfloat(effect_delay_volume, 0, ENC_DELAY_VOLUME_STEPS, 0.0, 1.0)); // delay tap1 signal (with added feedback) + configuration.delay_level = map(inValue, 0, 0x7f, DELAY_LEVEL_MIN, DELAY_LEVEL_MAX); + master_mixer_r.gain(2, configuration.delay_level / 100.0); + master_mixer_l.gain(2, configuration.delay_level / 100.0); break; case 120: - MicroDexed[0]->panic(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->panic(); + } break; case 121: - MicroDexed[0]->resetControllers(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->resetControllers(); + } break; case 123: - MicroDexed[0]->notesOff(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->notesOff(); + } break; case 126: - MicroDexed[0]->setMonoMode(true); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->setMonoMode(true); + } break; case 127: - MicroDexed[0]->setMonoMode(false); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->setMonoMode(false); + } break; } } @@ -625,13 +671,19 @@ void handleControlChange(byte inChannel, byte inCtrl, byte inValue) void handleAfterTouch(byte inChannel, byte inPressure) { - MicroDexed[0]->controllers.aftertouch_cc = inPressure; - MicroDexed[0]->controllers.refresh(); + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->controllers.aftertouch_cc = inPressure; + MicroDexed[i]->controllers.refresh(); + } } void handlePitchBend(byte inChannel, int inPitch) { - MicroDexed[0]->controllers.values_[kControllerPitch] = inPitch + 0x2000; // -8192 to +8191 --> 0 to 16383 + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->controllers.values_[kControllerPitch] = inPitch + 0x2000; // -8192 to +8191 --> 0 to 16383 + } } void handleProgramChange(byte inChannel, byte inProgram) @@ -928,19 +980,23 @@ bool checkMidiChannel(byte inChannel) VOLUME HELPER ******************************************************************************/ -void set_volume(float v, float p) +void set_volume(uint8_t v, int8_t p) { configuration.vol = v; configuration.pan = p; - MicroDexed[0]->fx.Gain = v; - - uint16_t tmp = v * 1023.0 + 0.5; - float tmp2 = configuration.pan; + uint16_t tmp = v / 100.0 * 1023.0 + 0.5; + float tmp2 = mapfloat(configuration.pan, PANORAMA_MIN, PANORAMA_MAX, 0.0, 1.0); float tmp3 = (float)(tmp * (tmp + 2)) / (float)(1 << 20); -#ifdef SHOW_DEBUG + + // float v = (float)(a * (a + 2))/(float)(1 << 20); // (pseudo-) logarithmic curve for volume control + // 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)); + +#ifdef DEBUG Serial.print(F("Setting volume: VOL=")); - Serial.print(value, DEC); + Serial.print(configuration.vol, DEC); Serial.print(F("[")); Serial.print(tmp3, 3); Serial.print(F("] PAN=")); @@ -953,15 +1009,21 @@ void set_volume(float v, float p) Serial.println(tmp3 * cosf(tmp2 * PI / 2), 3); #endif - // float v = (float)(a * (a + 2))/(float)(1 << 20); // (pseudo-) logarithmic curve for volume control - // 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_r.gain(1.0); volume_l.gain(0.0); + } else if (configuration.mono == 3) + { volume_r.gain(0.0); + volume_l.gain(1.0); + } + else + { + volume_r.gain(1.0); + volume_l.gain(1.0); + } } // https://www.dr-lex.be/info-stuff/volumecontrols.html#table1 @@ -1005,10 +1067,8 @@ void initial_values_from_eeprom(void) Serial.println(); #endif - if (configuration.vol > 1.0) - configuration.vol = 1.0; - else if (configuration.vol < 0.0) - configuration.vol = 0.0; + if (configuration.vol > 100) + configuration.vol = 100; } void eeprom_write(void) diff --git a/SoftenValue.hpp b/SoftenValue.hpp index 7b52230..0ba11aa 100644 --- a/SoftenValue.hpp +++ b/SoftenValue.hpp @@ -24,19 +24,26 @@ */ #ifndef SOFTEN_VALUE_H_INCLUDED +#define SOFTEN_VALUE_H_INCLUDED 1 template class SoftenValue { public: - SoftenValue(uint16_t steps = 10); - SoftenValue(T from, T to, uint16_t steps = 10) + init(T value, T minimum, T maximum) + { + _from = value; + _to = value; + _steps = 0; + _diff = 0.0; + _min = minimum; + _max = maximum; + } + + void update(T to, uint16_t steps) { - _from = from; _to = to; _steps = steps; - _diff = (from - to) / _steps; - _calculate(); } @@ -58,6 +65,12 @@ class SoftenValue { _from -= _diff; _steps--; +#ifdef DEBUG + Serial.print(F("Steps: ")); + Serial.print(_steps, DEC); + Serial.print(F(" Diff=")); + Serial.println(_diff, 5); +#endif } } @@ -74,11 +87,21 @@ class SoftenValue return (_steps); } + T diff(void) + { + return (_diff); + } + T value(void) { if (_steps == 0) return (_to); + if (_from < _min) + _from = _min; + else if (_from > _max) + _from = _max; + if (std::is_same::value) return (_from); else @@ -90,6 +113,8 @@ class SoftenValue float _to; uint16_t _steps; float _diff; + T _min; + T _max; void _calculate(void) { @@ -100,6 +125,17 @@ class SoftenValue } else _diff = (_from - _to) / _steps; + +#ifdef DEBUG + Serial.print(F("Update SoftenValue: from=")); + Serial.print(_from, 5); + Serial.print(F(" to=")); + Serial.print(_to, 5); + Serial.print(F(" diff=")); + Serial.print(_diff, 5); + Serial.print(F(" steps=")); + Serial.println(_steps, DEC); +#endif } }; diff --git a/UI.hpp b/UI.hpp index 22bc6fb..5dfe52e 100644 --- a/UI.hpp +++ b/UI.hpp @@ -54,9 +54,22 @@ extern bool load_sysex(uint8_t b, uint8_t v); #ifdef DISPLAY_LCD_SPI extern void change_disp_sd(bool d); #endif -extern value_change_t soften_volume; -extern value_change_t soften_filter_res; -extern value_change_t soften_filter_cut; +extern SoftenValue soften_volume; +extern SoftenValue soften_filter_res[NUM_DEXED]; +extern SoftenValue soften_filter_cut[NUM_DEXED]; +extern AudioEffectDelay delay1; +#ifdef USE_REVERB +extern AudioEffectFreeverbStereo freeverbs1; +#endif +extern AudioEffectModulatedDelay modchorus; +extern AudioSynthWaveform modulator; +extern AudioMixer4 delay_fb_mixer; +extern AudioMixer4 master_mixer_r; +extern AudioMixer4 master_mixer_l; +extern AudioAmplifier volume_r; +extern AudioAmplifier volume_l; +extern Dexed* MicroDexed[NUM_DEXED]; +extern void set_volume(uint8_t v, int8_t p); /*********************************************************************** GLOBAL @@ -111,6 +124,7 @@ void UI_func_reverb_roomsize(uint8_t param); void UI_func_reverb_damping(uint8_t param); void UI_func_reverb_level(uint8_t param); void UI_func_chorus_frequency(uint8_t param); +void UI_func_chorus_waveform(uint8_t param); void UI_func_chorus_depth(uint8_t param); void UI_func_chorus_level(uint8_t param); void UI_func_delay_time(uint8_t param); @@ -129,6 +143,8 @@ void UI_func_voice_selection(uint8_t param); void UI_func_volume(uint8_t param); void UI_func_back(uint8_t param); void UI_func_goToRootMenu(uint8_t param); +void lcd_display_int(int16_t var, uint8_t size, bool zeros, bool brackets, bool sign); +void lcd_display_float(float var, uint8_t size_number, uint8_t size_fraction, bool zeros, bool brackets, bool sign); // normal menu @@ -144,25 +160,26 @@ LCDML_add(4, LCDML_0_2_1, 2, "Damping", UI_func_reverb_damping); LCDML_add(5, LCDML_0_2_1, 3, "Level", UI_func_reverb_level); LCDML_add(6, LCDML_0_2, 2, "Chorus", NULL); LCDML_add(7, LCDML_0_2_2, 1, "Frequency", UI_func_chorus_frequency); -LCDML_add(8, LCDML_0_2_2, 2, "Depth", UI_func_chorus_depth); -LCDML_add(9, LCDML_0_2_2, 3, "Level", UI_func_chorus_level); -LCDML_add(10, LCDML_0_2, 3, "Delay", NULL); -LCDML_add(11, LCDML_0_2_3, 1, "Time", UI_func_delay_time); -LCDML_add(12, LCDML_0_2_3, 2, "Feedback", UI_func_delay_feedback); -LCDML_add(13, LCDML_0_2_3, 3, "Level", UI_func_delay_level); -LCDML_add(14, LCDML_0_2, 4, "Filter", NULL); -LCDML_add(15, LCDML_0_2_4, 1, "Cutoff", UI_func_filter_cutoff); -LCDML_add(16, LCDML_0_2_4, 2, "Resonance", UI_func_filter_resonance); -LCDML_add(17, LCDML_0, 3, "Store", NULL); -LCDML_add(18, LCDML_0, 4, "System", NULL); -LCDML_add(19, LCDML_0_4, 1, "MIDI Channel", UI_func_midi_channel); -LCDML_add(20, LCDML_0_4, 2, "Loudness", UI_func_loudness); -LCDML_add(21, LCDML_0_4, 3, "Panorama", UI_func_panorama); -LCDML_add(22, LCDML_0_4, 4, "Stereo/Mono", UI_func_stereo_mono); -LCDML_add(23, LCDML_0_4, 5, "Polyphony", UI_func_polyphony); -LCDML_add(24, LCDML_0_4, 6, "Engine", UI_func_engine); -LCDML_add(25, LCDML_0, 5, "Info", UI_func_information); -#define _LCDML_DISP_cnt 25 +LCDML_add(8, LCDML_0_2_2, 2, "Waveform", UI_func_chorus_waveform); +LCDML_add(9, LCDML_0_2_2, 3, "Depth", UI_func_chorus_depth); +LCDML_add(10, LCDML_0_2_2, 4, "Level", UI_func_chorus_level); +LCDML_add(11, LCDML_0_2, 3, "Delay", NULL); +LCDML_add(12, LCDML_0_2_3, 1, "Time", UI_func_delay_time); +LCDML_add(13, LCDML_0_2_3, 2, "Feedback", UI_func_delay_feedback); +LCDML_add(14, LCDML_0_2_3, 3, "Level", UI_func_delay_level); +LCDML_add(15, LCDML_0_2, 4, "Filter", NULL); +LCDML_add(16, LCDML_0_2_4, 1, "Cutoff", UI_func_filter_cutoff); +LCDML_add(17, LCDML_0_2_4, 2, "Resonance", UI_func_filter_resonance); +LCDML_add(18, LCDML_0, 3, "Store", NULL); +LCDML_add(19, LCDML_0, 4, "System", NULL); +LCDML_add(20, LCDML_0_4, 1, "MIDI Channel", UI_func_midi_channel); +LCDML_add(21, LCDML_0_4, 2, "Loudness", UI_func_loudness); +LCDML_add(22, LCDML_0_4, 3, "Panorama", UI_func_panorama); +LCDML_add(23, LCDML_0_4, 4, "Stereo/Mono", UI_func_stereo_mono); +LCDML_add(24, LCDML_0_4, 5, "Polyphony", UI_func_polyphony); +LCDML_add(25, LCDML_0_4, 6, "Engine", UI_func_engine); +LCDML_add(26, LCDML_0, 5, "Info", UI_func_information); +#define _LCDML_DISP_cnt 26 // create menu LCDML_createMenu(_LCDML_DISP_cnt); @@ -417,7 +434,7 @@ void encoder_right_up(void) } break; case MENU_VOICE_SOUND: - if (configuration.voice < MAX_VOICES-1) + if (configuration.voice < MAX_VOICES - 1) configuration.voice++; else { @@ -540,12 +557,11 @@ void encoder_left_up(void) #ifdef DEBUG Serial.println(F("Volume +")); #endif - if (configuration.vol < 0.96) - { - soften_volume.diff = 0.05 / SOFTEN_VALUE_CHANGE_STEPS; - soften_volume.steps = SOFTEN_VALUE_CHANGE_STEPS; - eeprom_write(); - } + if (configuration.vol < VOLUME_MAX) + soften_volume.update(soften_volume.value() + (VOLUME_MAX - VOLUME_MIN) / VOLUME_ENC_STEPS, SOFTEN_VALUE_CHANGE_STEPS); + else + soften_volume.update(VOLUME_MAX, SOFTEN_VALUE_CHANGE_STEPS); + eeprom_write(); UI_func_volume(0); } @@ -554,12 +570,11 @@ void encoder_left_down(void) #ifdef DEBUG Serial.println(F("Volume -")); #endif - if (configuration.vol > 0.04) - { - soften_volume.diff = -0.05 / SOFTEN_VALUE_CHANGE_STEPS; - soften_volume.steps = SOFTEN_VALUE_CHANGE_STEPS; - eeprom_write(); - } + if (configuration.vol > VOLUME_MIN) + soften_volume.update(soften_volume.value() - (VOLUME_MAX - VOLUME_MIN) / VOLUME_ENC_STEPS, SOFTEN_VALUE_CHANGE_STEPS); + else + soften_volume.update(VOLUME_MIN, SOFTEN_VALUE_CHANGE_STEPS); + eeprom_write(); UI_func_volume(0); } @@ -736,9 +751,9 @@ void UI_func_sound(uint8_t param) { // setup function lcd.setCursor(0, 0); - lcd.print(F("Filter Res.")); + lcd.print(F("Sound")); lcd.setCursor(0, 1); - lcd.print(F("")); + lcd.print(F("not implemented")); } if (LCDML.FUNC_loop()) // ****** LOOP ********* @@ -757,77 +772,704 @@ void UI_func_sound(uint8_t param) void UI_func_reverb_roomsize(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Reverb Roomsize")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.reverb_roomsize < REVERB_ROOMSIZE_MAX) + { + configuration.reverb_roomsize++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.reverb_roomsize > REVERB_ROOMSIZE_MIN) + { + configuration.reverb_roomsize--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.reverb_roomsize, 3, true, true, false); + + freeverbs1.roomsize(configuration.reverb_roomsize / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_reverb_damping(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Reverb Damping")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.reverb_damping < REVERB_DAMPING_MAX) + { + configuration.reverb_damping++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.reverb_damping > REVERB_DAMPING_MIN) + { + configuration.reverb_damping--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.reverb_damping, 3, true, true, false); + + freeverbs1.damping(configuration.reverb_damping / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_reverb_level(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Reverb Level")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.reverb_level < REVERB_LEVEL_MAX) + { + configuration.reverb_level++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.reverb_level > REVERB_LEVEL_MIN) + { + configuration.reverb_level--; + + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.reverb_level, 3, true, true, false); + + master_mixer_r.gain(1, configuration.reverb_level / 100.0); + master_mixer_l.gain(1, configuration.reverb_level / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_chorus_frequency(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Chorus Frequency")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.chorus_frequency < CHORUS_FREQUENCY_MAX) + { + configuration.chorus_frequency++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.chorus_frequency > CHORUS_FREQUENCY_MIN) + { + configuration.chorus_frequency--; + } + } + lcd.setCursor(0, 1); + lcd_display_float(configuration.chorus_frequency / 10.0, 2, 1, false, true, false); + lcd.print(" Hz"); + + modulator.frequency(configuration.chorus_frequency / 10.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } +} + +void UI_func_chorus_waveform(uint8_t param) +{ + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Chorus Waveform")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.chorus_waveform < CHORUS_WAVEFORM_MAX) + { + configuration.chorus_waveform++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.chorus_waveform > CHORUS_WAVEFORM_MIN) + { + configuration.chorus_waveform--; + } + } + lcd.setCursor(0, 1); + switch (configuration.chorus_waveform) + { + case 0: + modulator.begin(WAVEFORM_TRIANGLE); + lcd.print("[TRIANGLE]"); + break; + case 1: + modulator.begin(WAVEFORM_SINE); + lcd.print("[SINE ]"); + break; + default: + modulator.begin(WAVEFORM_TRIANGLE); + lcd.print("[TRIANGLE]"); + break; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_chorus_depth(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Chorus Depth")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.chorus_depth < CHORUS_DEPTH_MAX) + { + configuration.chorus_depth++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.chorus_depth > CHORUS_DEPTH_MIN) + { + configuration.chorus_depth--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.chorus_depth, 3, true, true, false); + + modulator.amplitude(configuration.chorus_depth / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_chorus_level(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Chorus Level")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.chorus_level < CHORUS_LEVEL_MAX) + { + configuration.chorus_level++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.chorus_level > CHORUS_LEVEL_MIN) + { + configuration.chorus_level--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.chorus_level, 3, true, true, false); + + + master_mixer_r.gain(3, configuration.chorus_level / 100.0); + master_mixer_l.gain(3, configuration.chorus_level / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_delay_time(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Delay Time")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.delay_time < DELAY_TIME_MAX) + { + configuration.delay_time += 10; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.delay_time > DELAY_TIME_MIN) + { + configuration.delay_time -= 10; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.delay_time * 10, 3, true, true, false); + + delay1.delay(0, configuration.delay_time * 10); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_delay_feedback(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Delay Feedback")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.delay_feedback < DELAY_FEEDBACK_MAX) + { + configuration.delay_feedback++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.delay_feedback > DELAY_FEEDBACK_MIN) + { + configuration.delay_feedback--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.delay_feedback, 3, true, true, false); + + delay_fb_mixer.gain(1, configuration.delay_feedback / 100.0 ); // amount of feedback + delay_fb_mixer.gain(0, 1.0 - configuration.delay_feedback / 100.0); // original signal + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_delay_level(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Delay Level")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.delay_level < DELAY_LEVEL_MAX) + { + configuration.delay_level++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.delay_level > DELAY_LEVEL_MIN) + { + configuration.delay_level--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.delay_level, 3, true, true, false); + + master_mixer_r.gain(2, configuration.delay_level / 100.0); + master_mixer_l.gain(2, configuration.delay_level / 100.0); + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_filter_cutoff(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Filter Cut-Off")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.filter_cutoff < FILTER_CUTOFF_MAX) + { + configuration.filter_cutoff++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.filter_cutoff > FILTER_CUTOFF_MIN) + { + configuration.filter_cutoff--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.filter_cutoff, 3, true, true, false); + + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->fx.Cutoff = configuration.filter_cutoff / 100.0; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_filter_resonance(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Filter Resonance")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.filter_resonance < FILTER_RESONANCE_MAX) + { + configuration.filter_resonance++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.filter_resonance > FILTER_RESONANCE_MIN) + { + configuration.filter_resonance--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.filter_resonance, 3, true, true, false); + + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->fx.Reso = configuration.filter_resonance / 100.0; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_midi_channel(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("MIDI Channel")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.midi_channel < MIDI_CHANNEL_MAX) + { + configuration.midi_channel++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.midi_channel > MIDI_CHANNEL_MIN) + { + configuration.midi_channel--; + } + } + lcd.setCursor(0, 1); + + if (configuration.midi_channel == 0) + { + lcd.print(F("[OMNI]")); + } + else + { + lcd_display_int(configuration.midi_channel, 4, false, true, false); + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_loudness(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Loudness")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.loudness < LOUDNESS_MAX) + { + configuration.loudness++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.loudness > LOUDNESS_MIN) + { + configuration.loudness--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.loudness, 3, true, true, false); + + for (uint8_t i = 0; i < NUM_DEXED; i++) + { + MicroDexed[i]->fx.Gain = configuration.loudness / 100.0; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_panorama(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Panorama")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.pan < PANORAMA_MAX) + { + configuration.pan++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.loudness > PANORAMA_MIN) + { + configuration.pan--; + } + } + lcd.setCursor(0, 1); + lcd_display_int(configuration.pan, 2, false, true, true); + } + + set_volume(configuration.vol, configuration.pan); + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_stereo_mono(uint8_t param) { - ; + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Stereo/Mono")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown()) + { + if (configuration.mono < MONO_MAX) + { + configuration.mono++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.mono > MONO_MIN) + { + configuration.mono--; + } + } + lcd.setCursor(0, 1); + switch (configuration.mono) + { + case 0: + lcd.print(F("[MONO ]")); + break; + case 1: + lcd.print(F("[STEREO]")); + break; + case 2: + lcd.print(F("[MONO-R]")); + break; + case 3: + lcd.print(F("[MONO-L]")); + break; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + } } void UI_func_polyphony(uint8_t param) @@ -920,11 +1562,11 @@ void UI_func_volume(uint8_t param) // update LCD content LCDML.DISP_clear(); lcd.show(0, 0, 8, "Volume: "); - lcd.show(0, 9, 3, configuration.vol * 100.0 + 0.5); + lcd.show(0, 9, 3, configuration.vol); lcd.setCursor(1, 1); for (uint8_t i = 0; i < LCD_cols; i++) { - if (i < int((LCD_cols - 2) * configuration.vol + 0.5)) + if (i < int((LCD_cols - 2) * configuration.vol / 100.0)) lcd.print("*"); else lcd.print(" "); @@ -932,5 +1574,70 @@ void UI_func_volume(uint8_t param) lcd.show(1, 0, 1, "["); lcd.show(1, 15, 1, "]"); } + +void lcd_display_int(int16_t var, uint8_t size, bool zeros, bool brackets, bool sign) +{ + int16_t tmp = 0; + uint16_t p; + + if (size < 1) + return; + + if (brackets == true) + lcd.print(F("[")); + + if (sign == true) + { + if (var < 0) + { + lcd.print(F("-")); + var = abs(var); + } + else if (var > 0) + lcd.print(F("+")); + else + lcd.print(F(" ")); + } + + for (int8_t i = size - 1 ; i >= 0; i--) + { + p = int(pow(10, i)); + tmp = int(var / p); + if (tmp == 0) + { + if (zeros == true) + lcd.print(F("0")); + else + lcd.print(F(" ")); + } + else + lcd.print(tmp); + var -= (tmp * p); + } + + if (brackets == true) + lcd.print(F("]")); +} + +void lcd_display_float(float var, uint8_t size_number, uint8_t size_fraction, bool zeros, bool brackets, bool sign) +{ + float fraction; + float number; + + if (size_number < 1 || size_fraction < 1) + return; + + fraction = modff(var, &number); + + if (brackets == true) + lcd.print(F("[")); + + lcd_display_int(int(number), size_number, zeros, false, sign); + lcd.print(F(".")); + lcd_display_int(round(fraction * pow(10, size_fraction)), size_fraction, true, false, false); + + if (brackets == true) + lcd.print(F("]")); +} #endif #endif diff --git a/config.h b/config.h index 4de49d8..d686b9a 100644 --- a/config.h +++ b/config.h @@ -46,16 +46,20 @@ // MIDI #define MIDI_DEVICE_DIN Serial1 +//#define AUDIO_DEVICE_USB //#define MIDI_DEVICE_USB 1 //#define MIDI_DEVICE_USB_HOST 1 //#define MIDI_DEVICE_NUMBER 0 // AUDIO -// If nothing is defined PT8211 is used as audio output device! -//#define AUDIO_DEVICE_USB -//#define TEENSY_AUDIO_BOARD 1 +// If nothing is defined Teensy internal DAC is used as audio output device! +// Left and right channel audio signal is presented on pins A21 and A22.//#define AUDIO_DEVICE_USB +//#define TEENSY_DAC +//#define TEENSY_DAC_SYMMETRIC +//#define TEENSY_AUDIO_BOARD #define I2S_AUDIO_ONLY //#define TGA_AUDIO_BOARD +//#define PT8211_AUDIO //************************************************************************************************* //* MIDI SETTINGS @@ -63,28 +67,25 @@ #define DEFAULT_MIDI_CHANNEL MIDI_CHANNEL_OMNI #define MIDI_MERGE_THRU 1 -#define DEFAULT_SYSEXBANK 0 -#define DEFAULT_SYSEXSOUND 0 +#define SYSEXBANK_DEFAULT 0 +#define SYSEXSOUND_DEFAULT 0 //************************************************************************************************* //* DEXED AND EFECTS SETTINGS //************************************************************************************************* #define DEXED_ENGINE DEXED_ENGINE_MODERN // DEXED_ENGINE_MARKI // DEXED_ENGINE_OPL -// 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 -//#define USE_REVERB 1 +#define USE_REVERB 1 //************************************************************************************************* //* AUDIO SETTINGS //************************************************************************************************* // https://rechneronline.de/funktionsgraphen/ -#define VOLUME 0.8 #define VOLUME_CURVE 0.07 #ifndef TEENSY_AUDIO_BOARD #if AUDIO_BLOCK_SAMPLES == 64 @@ -92,7 +93,7 @@ #else #define AUDIO_MEM 225 #endif -#define DELAY_MAX_TIME 600.0 +#define DELAY_MAX_TIME 600 #define REDUCE_LOUDNESS 1 #else #if AUDIO_BLOCK_SAMPLES == 64 @@ -100,7 +101,7 @@ #else #define AUDIO_MEM 450 #endif -#define DELAY_MAX_TIME 1200.0 +#define DELAY_MAX_TIME 1200 #define REDUCE_LOUDNESS 1 #endif #define SAMPLE_RATE 44100 @@ -161,12 +162,7 @@ #define SDCARD_SCK_PIN 13 // not actually used // Encoder with button -#define ENC_FILTER_RES_STEPS 100 -#define ENC_FILTER_CUT_STEPS 100 -#define ENC_DELAY_TIME_STEPS 50 -#define ENC_DELAY_FB_STEPS 35 -#define ENC_DELAY_VOLUME_STEPS 50 -#define ENC_VOLUME_STEPS 20 +#define ENCODER_USE_INTERRUPTS #define NUM_ENCODER 2 #define ENC_L_PIN_A 3 #define ENC_L_PIN_B 2 @@ -203,22 +199,30 @@ #ifdef MIDI_DEVICE_USB #define USBCON 1 #endif + #if defined(__IMXRT1062__) //Teensy-4.0 #undef MIDI_DEVICE_USB_HOST +#undef MIDI_DEVICE_USB #define MAX_NOTES 16 #endif #if defined(__MK66FX1M0__) // Teensy-3.6 // Teensy-3.6 settings #define MIDI_DEVICE_USB_HOST 1 +#if defined(USE_REVERB) #define MAX_NOTES 16 +#else +#define MAX_NOTES 14 +#endif #endif #if defined (__MK64FX512__) // Teensy-3.5 settings #undef MIDI_DEVICE_USB_HOST #define MAX_NOTES 11 +#undef USE_REVERB #endif + #define TRANSPOSE_FIX 24 // Audio @@ -238,15 +242,103 @@ #define MOD_BUTTERWORTH_FILTER_OUTPUT 1 #define MOD_LINKWITZ_RILEY_FILTER_OUTPUT 2 +#if defined(TEENSY_DAC_SYMMETRIC) +#define MONO_MIN 1 +#define MONO_MAX 1 +#define MONO_DEFAULT 1 +#else +#define MONO_MIN 0 +#define MONO_MAX 3 +#define MONO_DEFAULT 1 +#endif + +#define VOLUME_MIN 0 +#define VOLUME_MAX 100 +#define VOLUME_DEFAULT 80 +#define VOLUME_ENC_STEPS 20 + +#define PANORAMA_MIN -20 +#define PANORAMA_MAX 20 +#define PANORAMA_DEFAULT 0 + +#define MIDI_CHANNEL_MIN MIDI_CHANNEL_OMNI +#define MIDI_CHANNEL_MAX 16 +#define MIDI_CHANNEL_DEFAULT MIDI_CHANNEL_OMNI + +#define REVERB_ROOMSIZE_MIN 0 +#define REVERB_ROOMSIZE_MAX 100 +#define REVERB_ROOMSIZE_DEFAULT 0 + +#define REVERB_DAMPING_MIN 0 +#define REVERB_DAMPING_MAX 100 +#define REVERB_DAMPING_DEFAULT 0 + +#define REVERB_LEVEL_MIN 0 +#define REVERB_LEVEL_MAX 100 +#define REVERB_LEVEL_DEFAULT 0 + +#define CHORUS_FREQUENCY_MIN 0 +#define CHORUS_FREQUENCY_MAX 100 +#define CHORUS_FREQUENCY_DEFAULT 0 + +#define CHORUS_WAVEFORM_MIN 0 +#define CHORUS_WAVEFORM_MAX 1 +#define CHORUS_WAVEFORM_DEFAULT 0 + +#define CHORUS_DEPTH_MIN 0 +#define CHORUS_DEPTH_MAX 100 +#define CHORUS_DEPTH_DEFAULT 0 + +#define CHORUS_LEVEL_MIN 0 +#define CHORUS_LEVEL_MAX 100 +#define CHORUS_LEVEL_DEFAULT 0 + +#define DELAY_TIME_MIN 0 +#define DELAY_TIME_MAX DELAY_MAX_TIME/10 +#define DELAY_TIME_DEFAULT 0 + +#define DELAY_FEEDBACK_MIN 0 +#define DELAY_FEEDBACK_MAX 100 +#define DELAY_FEEDBACK_DEFAULT 0 + +#define DELAY_LEVEL_MIN 0 +#define DELAY_LEVEL_MAX 100 +#define DELAY_LEVEL_DEFAULT 0 + +#define FILTER_CUTOFF_MIN 0 +#define FILTER_CUTOFF_MAX 100 +#define FILTER_CUTOFF_DEFAULT 0 + +#define FILTER_RESONANCE_MIN 0 +#define FILTER_RESONANCE_MAX 100 +#define FILTER_RESONANCE_DEFAULT 0 + +#define LOUDNESS_MIN 0 +#define LOUDNESS_MAX 100 +#define LOUDNESS_DEFAULT 100 + // struct for holding the current configuration struct config_t { uint32_t checksum; uint8_t bank; uint8_t voice; - float vol; - float pan; + uint8_t vol; + int8_t pan; uint8_t mono; uint8_t midi_channel; + uint8_t reverb_roomsize; + uint8_t reverb_damping; + uint8_t reverb_level; + uint8_t chorus_frequency; + uint8_t chorus_waveform; + uint8_t chorus_depth; + uint8_t chorus_level; + uint8_t delay_time; + uint8_t delay_feedback; + uint8_t delay_level; + uint8_t filter_cutoff; + uint8_t filter_resonance; + uint8_t loudness; }; // struct for smoothing value changes diff --git a/doc/Audio-Chain.png b/doc/Audio-Chain.png new file mode 100644 index 0000000..2f5cca2 Binary files /dev/null and b/doc/Audio-Chain.png differ diff --git a/effect_stereo_mono.cpp b/effect_stereo_mono.cpp new file mode 100644 index 0000000..2d60188 --- /dev/null +++ b/effect_stereo_mono.cpp @@ -0,0 +1,68 @@ +/* Audio Library for Teensy 3.X + 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 "effect_stereo_mono.h" + +/*************************************************************************/ +// A u d i o E f f e c t S t e r e o M o n o +// Written by Holger Wirtz +// 20191023 - inital version + +void AudioEffectStereoMono::stereo(bool mode) +{ + _enabled = mode; +} + +void AudioEffectStereoMono::update(void) +{ + audio_block_t *block[2]; + + block[0] = receiveWritable(0); + block[1] = receiveWritable(1); + + if (_enabled == true) + { + if (block[0] && block[1]) + { + int16_t *bp[2] = { block[0]->data, block[1]->data }; + + for (uint16_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++) + { + *bp[0]++ = *bp[0] >> 1 + *bp[1] >> 1; + *bp[1]++ = *bp[0]; + } + } + } + + if (block[0]) + { + transmit(block[0], 0); + release(block[0]); + } + if (block[1]) + { + transmit(block[1], 1); + release(block[1]); + } +} diff --git a/effect_stereo_mono.h b/effect_stereo_mono.h new file mode 100644 index 0000000..0379326 --- /dev/null +++ b/effect_stereo_mono.h @@ -0,0 +1,51 @@ +/* Audio Library for Teensy 3.X + 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_stereo_mono_h_ +#define effect_stereo_mono_h_ + +#include "Arduino.h" +#include "AudioStream.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 Holger Wirtz +// 20191023 - inital version + +class AudioEffectStereoMono : public AudioStream +{ + public: + AudioEffectStereoMono(void): + AudioStream(2, inputQueueArray) + { + _enabled = false; + } + + virtual void update(void); + virtual void stereo(bool mode); + + private: + audio_block_t *inputQueueArray[2]; + bool _enabled; +}; + +#endif