From fc3b0614813cf17f34db7e04ad07c3d6a49c8471 Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Mon, 18 Nov 2019 11:39:59 +0100 Subject: [PATCH] Added Portamento function from https://github.com/asb2m10/dexed/pull/183 --- .MicroDexed.ino.swp | Bin 0 -> 81920 bytes MicroDexed.ino | 60 ++++++++------ UI.hpp | 189 +++++++++++++++++++++++++++++++++++++++++--- config.h | 15 ++++ controllers.h | 3 + dexed.cpp | 29 ++++++- dexed.h | 14 +++- dx7note.cpp | 37 ++++++++- dx7note.h | 57 ++++++------- porta.cpp | 35 ++++++++ porta.h | 28 +++++++ 11 files changed, 397 insertions(+), 70 deletions(-) create mode 100644 .MicroDexed.ino.swp create mode 100644 porta.cpp create mode 100644 porta.h diff --git a/.MicroDexed.ino.swp b/.MicroDexed.ino.swp new file mode 100644 index 0000000000000000000000000000000000000000..fd725493f05d7a7526e153dddac710b6f6ad8b56 GIT binary patch literal 81920 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.foot_cc = inValue; MicroDexed[instance_id]->controllers.refresh(); break; + case 5: // Portamento time + configuration.dexed[instance_id].portamento_time = inValue; + MicroDexed[instance_id]->setPortamentoMode(configuration.dexed[instance_id].portamento_mode, configuration.dexed[instance_id].portamento_glissando, configuration.dexed[instance_id].portamento_time); + break; case 7: // Volume #ifdef DEBUG Serial.println(F("VOLUME CC")); @@ -644,6 +648,9 @@ void handleControlChange(byte inChannel, byte inCtrl, byte inValue) } } break; + case 65: + MicroDexed[instance_id]->setPortamentoMode(configuration.dexed[instance_id].portamento_mode, configuration.dexed[instance_id].portamento_glissando, configuration.dexed[instance_id].portamento_time); + break; case 103: // CC 103: filter resonance configuration.dexed[instance_id].filter_resonance = map(inValue, 0, 0x7f, FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX); MicroDexed[instance_id]->fx.Reso = mapfloat(configuration.dexed[instance_id].filter_resonance, FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX, 1.0, 0.0); @@ -832,6 +839,7 @@ void handleSystemExclusive(byte * sysex, uint len) MicroDexed[instance_id]->controllers.at.setRange(MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_RANGE]); MicroDexed[instance_id]->controllers.at.setTarget(MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_ASSIGN]); MicroDexed[instance_id]->controllers.masterTune = (MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MASTER_TUNE] * 0x4000 << 11) * (1.0 / 12); + MicroDexed[instance_id]->setPortamentoMode(MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_MODE], MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_GLISSANDO], MicroDexed[instance_id]->data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_TIME]); MicroDexed[instance_id]->controllers.refresh(); data_index = DEXED_GLOBAL_PARAMETER_OFFSET - 63 + sysex[4]; } @@ -1359,31 +1367,33 @@ void show_configuration(void) Serial.print(F("=== DEXED INSTANCE ")); Serial.print(instance_id, DEC); Serial.println(" ==="); - Serial.print(F(" MIDI-Channel ")); Serial.println(configuration.dexed[instance_id].midi_channel, DEC); - Serial.print(F(" Bank ")); Serial.println(configuration.dexed[instance_id].bank, DEC); - Serial.print(F(" Voice ")); Serial.println(configuration.dexed[instance_id].voice, DEC); - Serial.print(F(" Reverb Send ")); Serial.println(configuration.dexed[instance_id].reverb_send, DEC); - Serial.print(F(" Chorus Send ")); Serial.println(configuration.dexed[instance_id].chorus_send, DEC); - Serial.print(F(" Delay Send ")); Serial.println(configuration.dexed[instance_id].delay_send, DEC); - Serial.print(F(" Filter Cutoff ")); Serial.println(configuration.dexed[instance_id].filter_cutoff, DEC); - Serial.print(F(" Filter Resonance ")); Serial.println(configuration.dexed[instance_id].filter_resonance, DEC); - Serial.print(F(" Loudness ")); Serial.println(configuration.dexed[instance_id].loudness, DEC); - Serial.print(F(" Transpose ")); Serial.println(configuration.dexed[instance_id].transpose, DEC); - Serial.print(F(" Tune ")); Serial.println(configuration.dexed[instance_id].tune, DEC); - Serial.print(F(" Polyphony ")); Serial.println(configuration.dexed[instance_id].polyphony, DEC); - Serial.print(F(" Engine ")); Serial.println(configuration.dexed[instance_id].engine, DEC); - Serial.print(F(" Mono/Poly ")); Serial.println(configuration.dexed[instance_id].monopoly, DEC); - Serial.print(F(" Pitchbend Range ")); Serial.println(configuration.dexed[instance_id].pb_range, DEC); - Serial.print(F(" Pitchbend Step ")); Serial.println(configuration.dexed[instance_id].pb_step, DEC); - Serial.print(F(" Modwheel Range ")); Serial.println(configuration.dexed[instance_id].mw_range, DEC); - Serial.print(F(" Modwheel Assign ")); Serial.println(configuration.dexed[instance_id].mw_assign, DEC); - Serial.print(F(" Footctrl Range ")); Serial.println(configuration.dexed[instance_id].fc_range, DEC); - Serial.print(F(" Footctrl Assign ")); Serial.println(configuration.dexed[instance_id].fc_assign, DEC); - Serial.print(F(" BreathCtrl Range ")); Serial.println(configuration.dexed[instance_id].bc_range, DEC); - Serial.print(F(" Breathctrl Assign ")); Serial.println(configuration.dexed[instance_id].bc_assign, DEC); - Serial.print(F(" Aftertouch Range ")); Serial.println(configuration.dexed[instance_id].at_range, DEC); - Serial.print(F(" Aftertouch Assign ")); Serial.println(configuration.dexed[instance_id].at_assign, DEC); - Serial.print(F(" OP Enabled ")); Serial.println(configuration.dexed[instance_id].op_enabled, DEC); + Serial.print(F(" MIDI-Channel ")); Serial.println(configuration.dexed[instance_id].midi_channel, DEC); + Serial.print(F(" Bank ")); Serial.println(configuration.dexed[instance_id].bank, DEC); + Serial.print(F(" Voice ")); Serial.println(configuration.dexed[instance_id].voice, DEC); + Serial.print(F(" Reverb Send ")); Serial.println(configuration.dexed[instance_id].reverb_send, DEC); + Serial.print(F(" Chorus Send ")); Serial.println(configuration.dexed[instance_id].chorus_send, DEC); + Serial.print(F(" Delay Send ")); Serial.println(configuration.dexed[instance_id].delay_send, DEC); + Serial.print(F(" Filter Cutoff ")); Serial.println(configuration.dexed[instance_id].filter_cutoff, DEC); + Serial.print(F(" Filter Resonance ")); Serial.println(configuration.dexed[instance_id].filter_resonance, DEC); + Serial.print(F(" Loudness ")); Serial.println(configuration.dexed[instance_id].loudness, DEC); + Serial.print(F(" Transpose ")); Serial.println(configuration.dexed[instance_id].transpose, DEC); + Serial.print(F(" Tune ")); Serial.println(configuration.dexed[instance_id].tune, DEC); + Serial.print(F(" Polyphony ")); Serial.println(configuration.dexed[instance_id].polyphony, DEC); + Serial.print(F(" Engine ")); Serial.println(configuration.dexed[instance_id].engine, DEC); + Serial.print(F(" Mono/Poly ")); Serial.println(configuration.dexed[instance_id].monopoly, DEC); + Serial.print(F(" Pitchbend Range ")); Serial.println(configuration.dexed[instance_id].pb_range, DEC); + Serial.print(F(" Pitchbend Step ")); Serial.println(configuration.dexed[instance_id].pb_step, DEC); + Serial.print(F(" Modwheel Range ")); Serial.println(configuration.dexed[instance_id].mw_range, DEC); + Serial.print(F(" Modwheel Assign ")); Serial.println(configuration.dexed[instance_id].mw_assign, DEC); + Serial.print(F(" Footctrl Range ")); Serial.println(configuration.dexed[instance_id].fc_range, DEC); + Serial.print(F(" Footctrl Assign ")); Serial.println(configuration.dexed[instance_id].fc_assign, DEC); + Serial.print(F(" BreathCtrl Range ")); Serial.println(configuration.dexed[instance_id].bc_range, DEC); + Serial.print(F(" Breathctrl Assign ")); Serial.println(configuration.dexed[instance_id].bc_assign, DEC); + Serial.print(F(" Aftertouch Range ")); Serial.println(configuration.dexed[instance_id].at_range, DEC); + Serial.print(F(" Portamento Mode ")); Serial.println(configuration.dexed[instance_id].portamento_mode, DEC); + Serial.print(F(" Portamento Glissando ")); Serial.println(configuration.dexed[instance_id].portamento_glissando, DEC); + Serial.print(F(" Portamento TIme ")); Serial.println(configuration.dexed[instance_id].portamento_time, DEC); + Serial.print(F(" OP Enabled ")); Serial.println(configuration.dexed[instance_id].op_enabled, DEC); Serial.flush(); } Serial.println(); diff --git a/UI.hpp b/UI.hpp index 917d42e..f404070 100644 --- a/UI.hpp +++ b/UI.hpp @@ -158,6 +158,9 @@ void UI_func_bc_range(uint8_t param); void UI_func_bc_assign(uint8_t param); void UI_func_at_range(uint8_t param); void UI_func_at_assign(uint8_t param); +void UI_func_portamento_mode(uint8_t param); +void UI_func_portamento_glissando(uint8_t param); +void UI_func_portamento_time(uint8_t param); void UI_func_OP1(uint8_t param); void UI_func_OP2(uint8_t param); void UI_func_OP3(uint8_t param); @@ -211,9 +214,9 @@ LCDML_add(28, LCDML_0_1_1, 17, "Aftertouch 1", NULL); LCDML_add(29, LCDML_0_1_1_17, 1, "AT Range 1", UI_func_at_range); LCDML_add(30, LCDML_0_1_1_17, 2, "AT Assign 1", UI_func_at_assign); LCDML_add(31, LCDML_0_1_1, 18, "Portamento 1", NULL); -LCDML_add(32, LCDML_0_1_1_18, 1, "Port. Mode 1", UI_function_not_enabled); -LCDML_add(33, LCDML_0_1_1_18, 2, "Port. Gliss 1", UI_function_not_enabled); -LCDML_add(34, LCDML_0_1_1_18, 3, "Port. Time 1", UI_function_not_enabled); +LCDML_add(32, LCDML_0_1_1_18, 1, "Port. Mode 1", UI_func_portamento_mode); +LCDML_add(33, LCDML_0_1_1_18, 2, "Port. Gliss 1", UI_func_portamento_glissando); +LCDML_add(34, LCDML_0_1_1_18, 3, "Port. Time 1", UI_func_portamento_time); LCDML_add(35, LCDML_0_1_1, 19, "Operator 1", NULL); LCDML_add(36, LCDML_0_1_1_19, 1, "OP1 1", UI_func_OP1); LCDML_add(37, LCDML_0_1_1_19, 2, "OP2 1", UI_func_OP2); @@ -252,9 +255,9 @@ LCDML_add(69, LCDML_0_1_2, 17, "Aftertouch 2", NULL); LCDML_add(70, LCDML_0_1_2_17, 1, "AT Range 2", UI_func_at_range); LCDML_add(71, LCDML_0_1_2_17, 2, "AT Assign 2", UI_func_at_assign); LCDML_add(72, LCDML_0_1_2, 18, "Portamento 2", NULL); -LCDML_add(73, LCDML_0_1_2_18, 1, "Port. Mode 2", UI_function_not_enabled); -LCDML_add(74, LCDML_0_1_2_18, 2, "Port. Gliss 2", UI_function_not_enabled); -LCDML_add(75, LCDML_0_1_2_18, 3, "Port. Time 2", UI_function_not_enabled); +LCDML_add(73, LCDML_0_1_2_18, 1, "Port. Mode 2", UI_func_portamento_mode); +LCDML_add(74, LCDML_0_1_2_18, 2, "Port. Gliss 2", UI_func_portamento_glissando); +LCDML_add(75, LCDML_0_1_2_18, 3, "Port. Time 2", UI_func_portamento_time); LCDML_add(76, LCDML_0_1_2, 19, "Operator 2", NULL); LCDML_add(77, LCDML_0_1_2_19, 1, "OP1 2", UI_func_OP1); LCDML_add(78, LCDML_0_1_2_19, 2, "OP2 2", UI_func_OP2); @@ -311,9 +314,9 @@ LCDML_add(27, LCDML_0_1, 17, "Aftertouch", NULL); LCDML_add(28, LCDML_0_1_17, 1, "AT Range", UI_func_at_range); LCDML_add(29, LCDML_0_1_17, 2, "AT Assign", UI_func_at_assign); LCDML_add(30, LCDML_0_1, 18, "Portamento", NULL); -LCDML_add(31, LCDML_0_1_18, 1, "Port. Mode", UI_function_not_implemented); -LCDML_add(32, LCDML_0_1_18, 2, "Port. Gliss", UI_function_not_implemented); -LCDML_add(33, LCDML_0_1_18, 3, "Port. Time", UI_function_not_implemented); +LCDML_add(31, LCDML_0_1_18, 1, "Port. Mode", UI_func_portamento_mode); +LCDML_add(32, LCDML_0_1_18, 2, "Port. Gliss", UI_func_portamento_glissando); +LCDML_add(33, LCDML_0_1_18, 3, "Port. Time", UI_func_portamento_time); LCDML_add(34, LCDML_0_1, 19, "Operator", NULL); LCDML_add(35, LCDML_0_1_19, 1, "OP1", UI_func_OP1); LCDML_add(36, LCDML_0_1_19, 2, "OP2", UI_func_OP2); @@ -2663,7 +2666,175 @@ void UI_func_at_assign(uint8_t param) lcd.print(F("[PTCH AMP EG-BS]")); break; } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + eeprom_write(); + } +} + +void UI_func_portamento_mode(uint8_t param) +{ + uint8_t instance_id = 0; + + if (LCDML.FUNC_getID() > MENU_ID_OF_INSTANCE_2) + instance_id = 1; + + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Portamento Mode")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown() || LCDML.BT_checkUp()) + { + if (LCDML.BT_checkDown()) + { + if (configuration.dexed[instance_id].portamento_mode < PORTMAMENTO_MODE_MAX) + { + configuration.dexed[instance_id].portamento_mode++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.dexed[instance_id].portamento_mode > PORTMAMENTO_MODE_MIN) + { + configuration.dexed[instance_id].portamento_mode--; + } + } + + MicroDexed[instance_id]->setPortamentoMode(configuration.dexed[instance_id].portamento_mode, configuration.dexed[instance_id].portamento_glissando, configuration.dexed[instance_id].portamento_time); + } + + lcd.setCursor(0, 1); + switch (configuration.dexed[instance_id].portamento_mode) + { + case 0: + lcd.print(F("[RETAIN ]")); + break; + case 1: + lcd.print(F("[FOLLOW ]")); + break; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + eeprom_write(); + } +} + +void UI_func_portamento_glissando(uint8_t param) +{ + uint8_t instance_id = 0; + + if (LCDML.FUNC_getID() > MENU_ID_OF_INSTANCE_2) + instance_id = 1; + + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Portam. Gliss.")); + } + + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown() || LCDML.BT_checkUp()) + { + if (LCDML.BT_checkDown()) + { + if (configuration.dexed[instance_id].portamento_glissando < PORTMAMENTO_GLISSANDO_MAX) + { + configuration.dexed[instance_id].portamento_glissando++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.dexed[instance_id].portamento_glissando > PORTMAMENTO_GLISSANDO_MIN) + { + configuration.dexed[instance_id].portamento_glissando--; + } + } + + MicroDexed[instance_id]->setPortamentoMode(configuration.dexed[instance_id].portamento_mode, configuration.dexed[instance_id].portamento_glissando, configuration.dexed[instance_id].portamento_time); + } + + lcd.setCursor(0, 1); + switch (configuration.dexed[instance_id].portamento_glissando) + { + case 0: + lcd.print(F("[OFF ]")); + break; + case 1: + lcd.print(F("[ON ]")); + break; + } + } + + if (LCDML.FUNC_close()) // ****** STABLE END ********* + { + // you can here reset some global vars or do nothing + eeprom_write(); + } +} + +void UI_func_portamento_time(uint8_t param) +{ + uint8_t instance_id = 0; + + if (LCDML.FUNC_getID() > MENU_ID_OF_INSTANCE_2) + instance_id = 1; + + if (LCDML.FUNC_setup()) // ****** SETUP ********* + { + // setup function + lcd.setCursor(0, 0); + lcd.print(F("Portam. Time")); + } + if (LCDML.FUNC_loop()) // ****** LOOP ********* + { + if (LCDML.BT_checkEnter()) + { + LCDML.FUNC_goBackToMenu(); + } + else if (LCDML.BT_checkDown() || LCDML.BT_checkUp()) + { + if (LCDML.BT_checkDown()) + { + if (configuration.dexed[instance_id].portamento_time < PORTMAMENTO_TIME_MAX) + { + configuration.dexed[instance_id].portamento_time++; + } + } + else if (LCDML.BT_checkUp()) + { + if (configuration.dexed[instance_id].portamento_time > PORTMAMENTO_TIME_MIN) + { + configuration.dexed[instance_id].portamento_time--; + } + } + + MicroDexed[instance_id]->setPortamentoMode(configuration.dexed[instance_id].portamento_mode, configuration.dexed[instance_id].portamento_glissando, configuration.dexed[instance_id].portamento_time); + } + + lcd.setCursor(0, 1); + lcd_display_int(configuration.dexed[instance_id].portamento_time, 2, false, true, false); } if (LCDML.FUNC_close()) // ****** STABLE END ********* diff --git a/config.h b/config.h index f1f45e3..f4203ed 100644 --- a/config.h +++ b/config.h @@ -395,6 +395,18 @@ enum { DEXED, REVERB, DELAY, CHORUS }; #define AT_ASSIGN_MAX 7 #define AT_ASSIGN_DEFAULT 0 // Bitmapped: 0: Pitch, 1: Amp, 2: Bias +#define PORTMAMENTO_MODE_MIN 0 +#define PORTMAMENTO_MODE_MAX 1 +#define PORTMAMENTO_MODE_DEFAULT 0 + +#define PORTMAMENTO_GLISSANDO_MIN 0 +#define PORTMAMENTO_GLISSANDO_MAX 1 +#define PORTMAMENTO_GLISSANDO_DEFAULT 0 + +#define PORTMAMENTO_TIME_MIN 0 +#define PORTMAMENTO_TIME_MAX 99 +#define PORTMAMENTO_TIME_DEFAULT 0 + #define OP_ENABLED_MIN 0 #define OP_ENABLED_MAX 0x3f #define OP_ENABLED_DEFAULT OP_ENABLED_MAX @@ -449,6 +461,9 @@ typedef struct { uint8_t bc_assign; uint8_t at_range; uint8_t at_assign; + uint8_t portamento_mode; + uint8_t portamento_glissando; + uint8_t portamento_time; uint8_t op_enabled; } dexed_t; diff --git a/controllers.h b/controllers.h index a2e83be..a20c930 100644 --- a/controllers.h +++ b/controllers.h @@ -21,6 +21,7 @@ #include "synth.h" #include #include +#include // State of MIDI controllers const int kControllerPitch = 0; @@ -79,6 +80,8 @@ class Controllers { uint8_t breath_cc; uint8_t foot_cc; uint8_t modwheel_cc; + bool portamento_enable_cc; + int portamento_cc; int masterTune; diff --git a/dexed.cpp b/dexed.cpp index a10962c..05afa3c 100644 --- a/dexed.cpp +++ b/dexed.cpp @@ -36,6 +36,7 @@ #include "PluginFx.h" #include #include +#include "porta.h" #ifdef USE_TEENSY_DSP #include #endif @@ -52,6 +53,7 @@ Dexed::Dexed(int rate) Lfo::init(rate); PitchEnv::init(rate); Env::init_sr(rate); + Porta::init_sr(rate); fx.init(rate); engineMkI = new EngineMkI; @@ -71,6 +73,7 @@ Dexed::Dexed(int rate) controllers.masterTune = 0; controllers.opSwitch = 0x3f; // enable all operators //controllers.opSwitch=0x00; + lastKeyDown = -1; lfo.reset(data + 137); @@ -188,6 +191,13 @@ void Dexed::keydown(uint8_t pitch, uint8_t velo) { pitch += data[144] - TRANSPOSE_FIX; + int previousKeyDown = lastKeyDown; + lastKeyDown = pitch; + + int porta = -1; + if ( controllers.portamento_enable_cc && previousKeyDown >= 0 ) + porta = controllers.portamento_cc; + uint8_t note = currentNote; uint8_t keydown_counter = 0; @@ -198,7 +208,7 @@ void Dexed::keydown(uint8_t pitch, uint8_t velo) { voices[note].velocity = velo; voices[note].sustained = sustain; voices[note].keydown = true; - voices[note].dx7_note->init(data, (int)pitch, (int)velo); + voices[note].dx7_note->init(data, pitch, velo, previousKeyDown, porta); if ( data[136] ) voices[note].dx7_note->oscSync(); break; @@ -355,6 +365,8 @@ void Dexed::resetControllers(void) controllers.foot_cc = 0; controllers.breath_cc = 0; controllers.aftertouch_cc = 0; + controllers.portamento_enable_cc = false; + controllers.portamento_cc = 0; controllers.refresh(); } @@ -718,3 +730,18 @@ void Dexed::setATController(uint8_t at_range, uint8_t at_assign) controllers.refresh(); } + +void Dexed::setPortamentoMode(uint8_t portamento_mode, uint8_t portamento_glissando, uint8_t portamento_time) +{ + data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_MODE] = portamento_mode; + data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_GLISSANDO] = portamento_glissando; + data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PORTAMENTO_TIME] = portamento_time; + controllers.portamento_cc = portamento_time; + + if (portamento_time > 0) + controllers.portamento_enable_cc = true; + else + controllers.portamento_enable_cc = false; + + controllers.refresh(); +} diff --git a/dexed.h b/dexed.h index 5174f69..3cbac74 100644 --- a/dexed.h +++ b/dexed.h @@ -56,8 +56,8 @@ struct ProcessorVoice { enum DexedEngineResolution { DEXED_ENGINE_MODERN, // 0 - DEXED_ENGINE_MARKI, // 1 - DEXED_ENGINE_OPL // 2 + DEXED_ENGINE_MARKI, // 1 + DEXED_ENGINE_OPL // 2 }; enum DexedVoiceOPParameters { @@ -128,7 +128,9 @@ enum DexedGlobalParameters { DEXED_OP5_ENABLE, // 15 DEXED_OP6_ENABLE, // 16 DEXED_MAX_NOTES, // 17 - DEXED_VOICE_VOLUME // 18 + DEXED_PORTAMENTO_MODE, // 18 + DEXED_PORTAMENTO_GLISSANDO, // 19 + DEXED_PORTAMENTO_TIME, // 20 }; // GLOBALS @@ -168,12 +170,13 @@ class Dexed void setFCController(uint8_t fc_range, uint8_t fc_assign); void setBCController(uint8_t bc_range, uint8_t bc_assign); void setATController(uint8_t at_range, uint8_t pb_assign); + void setPortamentoMode(uint8_t portamento_mode, uint8_t portamento_glissando, uint8_t portamento_time); ProcessorVoice voices[MAX_NOTES]; Controllers controllers; PluginFx fx; - uint8_t data[173] = { + uint8_t data[176] = { 95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5 95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4 @@ -188,10 +191,13 @@ class Dexed 01, 00, 99, 00, 99, 00, 99, 00, 99, 00, // pitch_bend_range, pitch_bend_step, mod_wheel_range, mod_wheel_assign, foot_ctrl_range, foot_ctrl_assign, breath_ctrl_range, breath_ctrl_assign, aftertouch_range, aftertouch_assign 00, // master tune 01, 01, 01, 01, 01, 01, // OP1-6 enable + 00, 00, 00, // portamento_mode, portamento_glissando, portamento_time MAX_NOTES // number of voices }; // FM-Piano uint32_t overload = 0; + int lastKeyDown; + protected: static const uint8_t MAX_ACTIVE_NOTES = MAX_NOTES; uint8_t max_notes = MAX_ACTIVE_NOTES; diff --git a/dx7note.cpp b/dx7note.cpp index 49d6114..0124ee5 100644 --- a/dx7note.cpp +++ b/dx7note.cpp @@ -19,6 +19,7 @@ #include #include "synth.h" #include "freqlut.h" +#include "porta.h" #include "exp2.h" #include "controllers.h" #include "dx7note.h" @@ -147,7 +148,7 @@ Dx7Note::Dx7Note() { } //void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { -void Dx7Note::init(const uint8_t patch[173], int midinote, int velocity) { +void Dx7Note::init(const uint8_t patch[173], int midinote, int velocity, int srcnote, int porta) { int rates[4]; int levels[4]; for (int op = 0; op < 6; op++) { @@ -176,6 +177,9 @@ void Dx7Note::init(const uint8_t patch[173], int midinote, int velocity) { opMode[op] = mode; basepitch_[op] = freq; ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; + + if (porta >= 0) + porta_curpitch_[op] = osc_freq(srcnote, mode, coarse, fine, detune); } for (int i = 0; i < 4; i++) { rates[i] = patch[126 + i]; @@ -188,6 +192,7 @@ void Dx7Note::init(const uint8_t patch[173], int midinote, int velocity) { pitchmoddepth_ = (patch[139] * 165) >> 6; pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; ampmoddepth_ = (patch[140] * 165) >> 6; + porta_rateindex_ = (porta < 128) ? porta : 127; } void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) { @@ -236,10 +241,15 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co } else { //int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24))); + int32_t basepitch = basepitch_[op]; + if ( opMode[op] ) - params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_base); - else - params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod); + params_[op].freq = Freqlut::lookup(basepitch + pitch_base); + else { + if ( porta_rateindex_ >= 0 ) + basepitch = porta_curpitch_[op]; + params_[op].freq = Freqlut::lookup(basepitch + pitch_mod); + } int32_t level = env_[op].getsample(); if (ampmodsens_[op] != 0) { @@ -254,6 +264,25 @@ void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Co params_[op].level_in = level; } } + + // ==== PORTAMENTO ==== + int porta = porta_rateindex_; + if ( porta >= 0 ) { + int32_t rate = Porta::rates[porta]; + for (int op = 0; op < 6; op++) { + int32_t cur = porta_curpitch_[op]; + int32_t dst = basepitch_[op]; + + bool going_up = cur < dst; + int32_t newpitch = cur + (going_up ? +rate : -rate); + + if ( going_up ? (cur > dst) : (cur < dst) ) + newpitch = dst; + + porta_curpitch_[op] = newpitch; + } + } + ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_); } diff --git a/dx7note.h b/dx7note.h index e221d40..508b418 100644 --- a/dx7note.h +++ b/dx7note.h @@ -1,19 +1,19 @@ /* - * Copyright 2016-2017 Pascal Gauthier. - * Copyright 2012 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ + Copyright 2016-2017 Pascal Gauthier. + Copyright 2012 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ #ifndef SYNTH_DX7NOTE_H_ #define SYNTH_DX7NOTE_H_ @@ -30,36 +30,36 @@ #include "fm_core.h" struct VoiceStatus { - uint32_t amp[6]; - char ampStep[6]; - char pitchStep; + uint32_t amp[6]; + char ampStep[6]; + char pitchStep; }; class Dx7Note { -public: + public: Dx7Note(); - void init(const uint8_t patch[156], int midinote, int velocity); - + void init(const uint8_t patch[156], int midinote, int velocity, int srcnote, int porta); + // Note: this _adds_ to the buffer. Interesting question whether it's // worth it... void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls); - + void keyup(); - + // TODO: some way of indicating end-of-note. Maybe should be a return // value from the compute method? (Having a count return from keyup // is also tempting, but if there's a dynamic parameter change after // keyup, that won't work. - + // PG:add the update void update(const uint8_t patch[156], int midinote, int velocity); void peekVoiceStatus(VoiceStatus &status); void transferState(Dx7Note& src); void transferSignal(Dx7Note &src); void oscSync(); - -private: + + private: Env env_[6]; FmOpParams params_[6]; PitchEnv pitchenv_; @@ -68,11 +68,14 @@ private: int32_t fb_shift_; int32_t ampmodsens_[6]; int32_t opMode[6]; - + int ampmoddepth_; int algorithm_; int pitchmoddepth_; int pitchmodsens_; + + int porta_rateindex_; + int32_t porta_curpitch_[6]; }; #endif // SYNTH_DX7NOTE_H_ diff --git a/porta.cpp b/porta.cpp new file mode 100644 index 0000000..059c06b --- /dev/null +++ b/porta.cpp @@ -0,0 +1,35 @@ +/* + Copyright 2019 Jean Pierre Cimalando. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include +#include "porta.h" +#include "synth.h" + +void Porta::init_sr(double sampleRate) +{ + // compute portamento for CC 7-bit range + + for (unsigned int i = 0; i < 128; ++i) { + // number of semitones travelled + double sps = 350.0 * pow(2.0, -0.062 * i); // per second + double spf = sps / sampleRate; // per frame + double spp = spf * _N_; // per period + const int step = (1 << 24) / 12; + rates[i] = (int32_t)(0.5f + step * spp); // to pitch units + } +} + +int32_t Porta::rates[128]; diff --git a/porta.h b/porta.h new file mode 100644 index 0000000..d8a6b8c --- /dev/null +++ b/porta.h @@ -0,0 +1,28 @@ +/* + Copyright 2019 Jean Pierre Cimalando. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#ifndef SYNTH_PORTA_H_ +#define SYNTH_PORTA_H_ + +#include + +struct Porta { + public: + static void init_sr(double sampleRate); + static int32_t rates[128]; +}; + +#endif // SYNTH_PORTA_H_