|
|
@ -4,7 +4,7 @@ |
|
|
|
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
|
|
|
MicroMDAEPiano is a port of the MDA-EPiano sound engine |
|
|
|
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
|
|
|
(https://sourceforge.net/projects/mda-vst/) for the Teensy-3.5/3.6 with audio shield.
|
|
|
|
|
|
|
|
|
|
|
|
(c)2019 H. Wirtz <wirtz@parasitstudio.de> |
|
|
|
(c)2019-2020 H. Wirtz <wirtz@parasitstudio.de> |
|
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
|
it under the terms of the GNU General Public License as published by |
|
|
@ -28,6 +28,7 @@ |
|
|
|
#include <EEPROM.h> |
|
|
|
#include <EEPROM.h> |
|
|
|
#include "EEPROMAnything.h" |
|
|
|
#include "EEPROMAnything.h" |
|
|
|
#include "mdaEPiano.h" |
|
|
|
#include "mdaEPiano.h" |
|
|
|
|
|
|
|
#include "source_micromdaepiano.h" |
|
|
|
#include "effect_modulated_delay.h" |
|
|
|
#include "effect_modulated_delay.h" |
|
|
|
#ifdef USE_XFADE_DATA |
|
|
|
#ifdef USE_XFADE_DATA |
|
|
|
#include "mdaEPianoDataXfade.h" |
|
|
|
#include "mdaEPianoDataXfade.h" |
|
|
@ -43,8 +44,7 @@ |
|
|
|
//*************************************************************************************************
|
|
|
|
//*************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
// Audio configuration
|
|
|
|
// Audio configuration
|
|
|
|
AudioPlayQueue queue_r; |
|
|
|
AudioSourceMicroMDAEPiano ep; |
|
|
|
AudioPlayQueue queue_l; |
|
|
|
|
|
|
|
AudioAnalyzePeak peak_r; |
|
|
|
AudioAnalyzePeak peak_r; |
|
|
|
AudioAnalyzePeak peak_l; |
|
|
|
AudioAnalyzePeak peak_l; |
|
|
|
AudioEffectFreeverb freeverb_r; |
|
|
|
AudioEffectFreeverb freeverb_r; |
|
|
@ -61,17 +61,17 @@ AudioFilterBiquad modchorus_filter_r; |
|
|
|
AudioFilterBiquad modchorus_filter_l; |
|
|
|
AudioFilterBiquad modchorus_filter_l; |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
AudioSynthWaveform modulator; |
|
|
|
AudioSynthWaveform modulator; |
|
|
|
AudioConnection patchCord0(queue_r, peak_r); |
|
|
|
AudioConnection patchCord0(ep, 0, peak_r, 0); |
|
|
|
AudioConnection patchCord1(queue_l, peak_l); |
|
|
|
AudioConnection patchCord1(ep, 1, peak_l, 0); |
|
|
|
AudioConnection patchCord2(queue_r, freeverb_r); |
|
|
|
AudioConnection patchCord2(ep, 0, freeverb_r, 0); |
|
|
|
AudioConnection patchCord3(queue_l, freeverb_l); |
|
|
|
AudioConnection patchCord3(ep, 1, freeverb_l, 0); |
|
|
|
AudioConnection patchCord4(queue_r, 0, modchorus_r, 0); |
|
|
|
AudioConnection patchCord4(ep, 0, modchorus_r, 0); |
|
|
|
AudioConnection patchCord5(queue_l, 0, modchorus_l, 0); |
|
|
|
AudioConnection patchCord5(ep, 1, modchorus_l, 0); |
|
|
|
AudioConnection patchCord6(modulator, 0, modchorus_r, 1); |
|
|
|
AudioConnection patchCord6(modulator, 0, modchorus_r, 1); |
|
|
|
AudioConnection patchCord7(modulator, inverter); |
|
|
|
AudioConnection patchCord7(modulator, inverter); |
|
|
|
AudioConnection patchCord8(inverter, 0, modchorus_l, 1); |
|
|
|
AudioConnection patchCord8(inverter, 0, modchorus_l, 1); |
|
|
|
AudioConnection patchCord9(queue_r, 0, mixer_r, 0); |
|
|
|
AudioConnection patchCord9(ep, 0, mixer_r, 0); |
|
|
|
AudioConnection patchCord10(queue_l, 0, mixer_l, 0); |
|
|
|
AudioConnection patchCord10(ep, 1, mixer_l, 0); |
|
|
|
#if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT |
|
|
|
#if MOD_FILTER_OUTPUT != MOD_NO_FILTER_OUTPUT |
|
|
|
AudioConnection patchCord11(modchorus_r, modchorus_filter_r); |
|
|
|
AudioConnection patchCord11(modchorus_r, modchorus_filter_r); |
|
|
|
AudioConnection patchCord12(modchorus_l, modchorus_filter_l); |
|
|
|
AudioConnection patchCord12(modchorus_l, modchorus_filter_l); |
|
|
@ -95,9 +95,6 @@ AudioConnection patchCord21(volume_r, 0, i2s1, 0); |
|
|
|
AudioConnection patchCord22(volume_l, 0, i2s1, 1); |
|
|
|
AudioConnection patchCord22(volume_l, 0, i2s1, 1); |
|
|
|
AudioControlSGTL5000 sgtl5000_1; |
|
|
|
AudioControlSGTL5000 sgtl5000_1; |
|
|
|
|
|
|
|
|
|
|
|
// Objects
|
|
|
|
|
|
|
|
mdaEPiano* ep; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern void init_menus(void); |
|
|
|
extern void init_menus(void); |
|
|
|
extern int32_t encoder_value[NUM_ENCODER]; |
|
|
|
extern int32_t encoder_value[NUM_ENCODER]; |
|
|
|
extern Bounce but[NUM_ENCODER]; |
|
|
|
extern Bounce but[NUM_ENCODER]; |
|
|
@ -184,7 +181,7 @@ void setup() |
|
|
|
|
|
|
|
|
|
|
|
// Debug output
|
|
|
|
// Debug output
|
|
|
|
Serial.println(F("MicroMDAEPiano based on https://sourceforge.net/projects/mda-vst")); |
|
|
|
Serial.println(F("MicroMDAEPiano based on https://sourceforge.net/projects/mda-vst")); |
|
|
|
Serial.println(F("(c)2018/2019 H. Wirtz <wirtz@parasitstudio.de>")); |
|
|
|
Serial.println(F("(c)2019-2020 H. Wirtz <wirtz@parasitstudio.de>")); |
|
|
|
Serial.println(F("https://codeberg.org/dcoredump/MicroMDAEPiano")); |
|
|
|
Serial.println(F("https://codeberg.org/dcoredump/MicroMDAEPiano")); |
|
|
|
Serial.print(F("Data in PROGMEM: ")); |
|
|
|
Serial.print(F("Data in PROGMEM: ")); |
|
|
|
Serial.print(sizeof(epianoDataXfade), DEC); |
|
|
|
Serial.print(sizeof(epianoDataXfade), DEC); |
|
|
@ -192,9 +189,6 @@ void setup() |
|
|
|
Serial.println(); |
|
|
|
Serial.println(); |
|
|
|
Serial.println(F("<setup start>")); |
|
|
|
Serial.println(F("<setup start>")); |
|
|
|
|
|
|
|
|
|
|
|
// create EPiano object
|
|
|
|
|
|
|
|
ep = new mdaEPiano(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// set initial init configuration
|
|
|
|
// set initial init configuration
|
|
|
|
set_complete_configuration(); |
|
|
|
set_complete_configuration(); |
|
|
|
|
|
|
|
|
|
|
@ -309,55 +303,6 @@ void setup() |
|
|
|
|
|
|
|
|
|
|
|
void loop() |
|
|
|
void loop() |
|
|
|
{ |
|
|
|
{ |
|
|
|
int16_t* audio_buffer_r; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
|
|
|
|
|
|
|
|
int16_t* audio_buffer_l; // pointer to AUDIO_BLOCK_SAMPLES * sizeof(int16_t)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Main sound calculation
|
|
|
|
|
|
|
|
if (queue_r.available() && queue_l.available() && fill_audio_buffer > audio_block_time_us - 10) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
fill_audio_buffer = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (SHOW_DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
|
|
|
|
|
|
|
if (cpu_mem_millis > SHOW_CPU_LOAD_MSEC) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
show_cpu_and_mem_usage(); |
|
|
|
|
|
|
|
cpu_mem_millis = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
audio_buffer_r = queue_r.getBuffer(); |
|
|
|
|
|
|
|
if (audio_buffer_r == NULL) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Serial.println(F("E: audio_buffer_r allocation problems!")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
audio_buffer_l = queue_l.getBuffer(); |
|
|
|
|
|
|
|
if (audio_buffer_l == NULL) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Serial.println(F("E: audio_buffer_l allocation problems!")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elapsedMicros t1; |
|
|
|
|
|
|
|
ep->process(audio_buffer_r, audio_buffer_l); |
|
|
|
|
|
|
|
uint32_t t2 = t1; |
|
|
|
|
|
|
|
if (t2 > audio_block_time_us) // everything greater 2.9ms is a buffer underrun!
|
|
|
|
|
|
|
|
xrun++; |
|
|
|
|
|
|
|
if (t2 > render_time_max) |
|
|
|
|
|
|
|
render_time_max = t2; |
|
|
|
|
|
|
|
if (peak_r.available()) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (peak_r.read() > 1.00) |
|
|
|
|
|
|
|
peak++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (peak_l.available()) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
if (peak_l.read() > 1.00) |
|
|
|
|
|
|
|
peak++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
queue_r.playBuffer(); |
|
|
|
|
|
|
|
queue_l.playBuffer(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
check_midi_devices(); |
|
|
|
check_midi_devices(); |
|
|
|
|
|
|
|
|
|
|
|
// CONTROL-RATE-EVENT-HANDLING
|
|
|
|
// CONTROL-RATE-EVENT-HANDLING
|
|
|
@ -366,17 +311,25 @@ void loop() |
|
|
|
control_rate = 0; |
|
|
|
control_rate = 0; |
|
|
|
handle_ui(); |
|
|
|
handle_ui(); |
|
|
|
|
|
|
|
|
|
|
|
if ( eeprom_config_update_flag > 0 && ep->getActiveVoices() == 0) // write only to eeprom when no voice is active
|
|
|
|
if ( eeprom_config_update_flag > 0 && ep.getActiveVoices() == 0) // write only to eeprom when no voice is active
|
|
|
|
eeprom_config_update(); |
|
|
|
eeprom_config_update(); |
|
|
|
|
|
|
|
|
|
|
|
if (eeprom_master_volume_update_flag == true && eeprom_master_volume_update_timer > STORE_MASTER_VOLUME_MS && ep->getActiveVoices() == 0) |
|
|
|
if (eeprom_master_volume_update_flag == true && eeprom_master_volume_update_timer > STORE_MASTER_VOLUME_MS && ep.getActiveVoices() == 0) |
|
|
|
eeprom_master_volume_update(); |
|
|
|
eeprom_master_volume_update(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined (SHOW_DEBUG) && defined (SHOW_CPU_LOAD_MSEC) |
|
|
|
|
|
|
|
if (cpu_mem_millis > SHOW_CPU_LOAD_MSEC) |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
show_cpu_and_mem_usage(); |
|
|
|
|
|
|
|
cpu_mem_millis = 0; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_AUDIO |
|
|
|
#ifdef DEBUG_AUDIO |
|
|
|
if (debug_audio_timer > DEBUG_AUDIO) |
|
|
|
if (debug_audio_timer > DEBUG_AUDIO) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ep->noteOn(60 + rand() % 108, rand() % 128); |
|
|
|
ep.noteOn(60 + rand() % 108, rand() % 128); |
|
|
|
debug_audio_timer = 0; |
|
|
|
debug_audio_timer = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
#endif |
|
|
@ -389,7 +342,7 @@ void handleNoteOn(byte inChannel, byte inNumber, byte inVelocity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (checkMidiChannel(inChannel)) |
|
|
|
if (checkMidiChannel(inChannel)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ep->noteOn(inNumber + configuration.transpose, inVelocity); |
|
|
|
ep.noteOn(inNumber + configuration.transpose, inVelocity); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -397,7 +350,7 @@ void handleNoteOff(byte inChannel, byte inNumber, byte inVelocity) |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (checkMidiChannel(inChannel)) |
|
|
|
if (checkMidiChannel(inChannel)) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ep->noteOn(inNumber + configuration.transpose, 0); |
|
|
|
ep.noteOn(inNumber + configuration.transpose, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -415,41 +368,41 @@ void handleControlChange(byte inChannel, byte inData1, byte inData2) |
|
|
|
set_reverb_level(map(inData2, 0, 127, ENC_REVERB_LEVEL_MIN, ENC_REVERB_LEVEL_MAX)); |
|
|
|
set_reverb_level(map(inData2, 0, 127, ENC_REVERB_LEVEL_MIN, ENC_REVERB_LEVEL_MAX)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_TREMOLO_DEPTH: // Tremolo level (same as modwheel)
|
|
|
|
case MIDI_CC_TREMOLO_DEPTH: // Tremolo level (same as modwheel)
|
|
|
|
inData1 = 1; // now it's modwheel and can be processd by ep->processMidiController :-)
|
|
|
|
inData1 = 1; // now it's modwheel and can be processd by ep.processMidiController :-)
|
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_CHORUS_SEND: // Chorus level
|
|
|
|
case MIDI_CC_CHORUS_SEND: // Chorus level
|
|
|
|
set_chorus_level(map(inData2, 0, 127, ENC_CHORUS_LEVEL_MIN, ENC_CHORUS_LEVEL_MAX)); |
|
|
|
set_chorus_level(map(inData2, 0, 127, ENC_CHORUS_LEVEL_MIN, ENC_CHORUS_LEVEL_MAX)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_DETUNE_DEPTH: // Detune level
|
|
|
|
case MIDI_CC_DETUNE_DEPTH: // Detune level
|
|
|
|
ep->setDetune(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setDetune(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
// Own MIDI-CC mapping
|
|
|
|
// Own MIDI-CC mapping
|
|
|
|
case MIDI_CC_EP_DECAY: |
|
|
|
case MIDI_CC_EP_DECAY: |
|
|
|
ep->setDecay(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setDecay(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_RELEASE: |
|
|
|
case MIDI_CC_EP_RELEASE: |
|
|
|
ep->setRelease(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setRelease(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_HARDNESS: |
|
|
|
case MIDI_CC_EP_HARDNESS: |
|
|
|
ep->setHardness(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setHardness(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_TREBLE: |
|
|
|
case MIDI_CC_EP_TREBLE: |
|
|
|
ep->setTreble(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setTreble(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_STEREO: |
|
|
|
case MIDI_CC_EP_STEREO: |
|
|
|
ep->setStereo(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setStereo(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_TUNE: |
|
|
|
case MIDI_CC_EP_TUNE: |
|
|
|
ep->setTune(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setTune(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_VELOCITY_SENSE: |
|
|
|
case MIDI_CC_EP_VELOCITY_SENSE: |
|
|
|
ep->setVelocitySense(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setVelocitySense(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_TREM_FRQ: |
|
|
|
case MIDI_CC_EP_TREM_FRQ: |
|
|
|
ep->setPanLFO(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setPanLFO(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_EP_OVERDRIVE: |
|
|
|
case MIDI_CC_EP_OVERDRIVE: |
|
|
|
ep->setOverdrive(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
ep.setOverdrive(mapfloat(float(inData2), 0, 127, 0.0, 1.0)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
case MIDI_CC_COMP_GAIN: |
|
|
|
case MIDI_CC_COMP_GAIN: |
|
|
|
set_comp_gain(map(inData2, 0, 127, ENC_COMP_GAIN_MIN, ENC_COMP_GAIN_MAX)); |
|
|
|
set_comp_gain(map(inData2, 0, 127, ENC_COMP_GAIN_MIN, ENC_COMP_GAIN_MAX)); |
|
|
@ -503,7 +456,7 @@ void handleControlChange(byte inChannel, byte inData1, byte inData2) |
|
|
|
set_mono(map(inData2, 0, 127, ENC_MONO_MIN, ENC_MONO_MAX)); |
|
|
|
set_mono(map(inData2, 0, 127, ENC_MONO_MIN, ENC_MONO_MAX)); |
|
|
|
break; |
|
|
|
break; |
|
|
|
default: |
|
|
|
default: |
|
|
|
ep->processMidiController(inData1, inData2); |
|
|
|
ep.processMidiController(inData1, inData2); |
|
|
|
break; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -780,7 +733,7 @@ void show_cpu_and_mem_usage(void) |
|
|
|
Serial.print(F(" PEAK: ")); |
|
|
|
Serial.print(F(" PEAK: ")); |
|
|
|
Serial.print(peak, DEC); |
|
|
|
Serial.print(peak, DEC); |
|
|
|
Serial.print(F(" ACTIVE_VOICES: ")); |
|
|
|
Serial.print(F(" ACTIVE_VOICES: ")); |
|
|
|
Serial.print(ep->getActiveVoices(), DEC); |
|
|
|
Serial.print(ep.getActiveVoices(), DEC); |
|
|
|
Serial.println(); |
|
|
|
Serial.println(); |
|
|
|
AudioProcessorUsageMaxReset(); |
|
|
|
AudioProcessorUsageMaxReset(); |
|
|
|
AudioMemoryUsageMaxReset(); |
|
|
|
AudioMemoryUsageMaxReset(); |
|
|
|