From ee438c200d16dbbb4b9ce7c4f68754e610778a21 Mon Sep 17 00:00:00 2001 From: Johan Berglund Date: Wed, 19 Aug 2020 23:07:38 +0200 Subject: [PATCH] * Adjustment setting for thumb lever (for optimizing thumb portamento control). By setting THR (level of push force to activate) and MAX (level of push force to achieve maximum set portamento) values close to eachother at the desired point of activation, the new glide limit setting to desired rate will create a switching type set rate controller (similar to Crumar EVI glide key). With THR and MAX setup with separation to taste, a continous control up to level set by glide limit is achieved. * Glide setting SWO, SWitching Only, sending only Glide on/off (CC#65) for use with synths where glide rate CC#5 is used in non standard ways, for example some DSI/Sequential synths like the Prophet REV2 or Prophet 12 where glide rates are set individually for each oscillator. * Glide limit setting for portamento. Doubles as setting for portamento level sent using pinky key/mod key in GLD mode. (Can be changed both in menu and in GLD mode.) * Setting of level for LVL, LVP and GLD now reqires touching both pinky/mod and third trill/RHp3 for setting mode activation, this to avoid accidental change of setting when pinky/mod key is touched. * Rate of setting movement up and down for LVL, LVP and GLD has been adjusted. Became very much too fast after the timing issues were solved in 1.5b1 * A short delay before note offs in legato transitions is added to make playback of recorded midi behave correctly (keeping note on and note off from being registered on the same timestamp). --- NuEVI/NuEVI.ino | 87 +- NuEVI/adjustmenu.cpp | 23 + NuEVI/config.h | 5 +- NuEVI/globals.h | 5 + NuEVI/menu.cpp | 20 +- NuEVI/settings.cpp | 11 +- NuEVI/settings.h | 10 +- uploadable hex files/NuEVI-v15b2.ino.hex | 5339 +++++++++++++++++++++ uploadable hex files/NuRAD-v15b2.ino.hex | 5448 ++++++++++++++++++++++ 9 files changed, 10909 insertions(+), 39 deletions(-) create mode 100644 uploadable hex files/NuEVI-v15b2.ino.hex create mode 100644 uploadable hex files/NuRAD-v15b2.ino.hex diff --git a/NuEVI/NuEVI.ino b/NuEVI/NuEVI.ino index 204a63f..e500075 100644 --- a/NuEVI/NuEVI.ino +++ b/NuEVI/NuEVI.ino @@ -64,6 +64,8 @@ unsigned short pitchbMaxVal;// = 2400; unsigned short extracThrVal;// = 1200; unsigned short extracMaxVal;// = 2400; unsigned short ctouchThrVal;// = 120; +unsigned short leverThrVal; +unsigned short leverMaxVal; unsigned short transpose; unsigned short MIDIchannel; unsigned short breathCC; // OFF:MW:BR:VL:EX:MW+:BR+:VL+:EX+:CF @@ -72,6 +74,7 @@ unsigned short breathCC2Rise; // 1X:2X:3X:4X:5X unsigned short breathAT; unsigned short velocity; unsigned short portamento;// switching on cc65? just cc5 enabled? SW:ON:OFF +unsigned short portLimit; // 1-127 unsigned short PBdepth; // OFF:1-12 divider unsigned short extraCT; // OFF:MW:FP:CF:SP unsigned short vibrato; // OFF:1-9 @@ -157,6 +160,7 @@ unsigned long lastDeglitchTime = 0; // The last time the fingering was c unsigned long ccSendTime = 0L; // The last time we sent CC values unsigned long ccSendTime2 = 0L; // The last time we sent CC values 2 (slower) unsigned long ccSendTime3 = 0L; // The last time we sent CC values 3 (and slower) +unsigned long lvlTime = 0L; unsigned long ccBreathSendTime = 0L; // The last time we sent breath CC values unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold unsigned long currentTime; @@ -194,9 +198,9 @@ int breathCalZero; int leverPortZero; #if defined(NURAD) -int leverPortThr = 50; +int leverPortThr = 70; #else -int leverPortThr = 50; +int leverPortThr = 70; #endif int leverPortRead; @@ -909,33 +913,45 @@ void loop() { subOctaveDouble = 0; } if ((pinkySetting == LVL) || (pinkySetting == LVLP)){ - if (pinkyKey){ + if (pinkyKey && K7){ ledMeter(levelVal); if (K6 && (levelVal < 127)){ - levelVal++; - if (levelCC) midiSendControlChange(levelCC, levelVal); - else midiSendAfterTouch(levelVal); + if (currentTime - lvlTime > (LVL_TIMER_INTERVAL)){ + levelVal++; + if (levelCC) midiSendControlChange(levelCC, levelVal); + else midiSendAfterTouch(levelVal); + lvlTime = currentTime; + } } else if (K5 && (levelVal > 0)){ - levelVal--; - if (levelCC) midiSendControlChange(levelCC, levelVal); - else midiSendAfterTouch(levelVal); + if (currentTime - lvlTime > (LVL_TIMER_INTERVAL)){ + levelVal--; + if (levelCC) midiSendControlChange(levelCC, levelVal); + else midiSendAfterTouch(levelVal); + lvlTime = currentTime; + } } - } else if (lastPinkyKey){ + } else if (!pinkyKey && lastPinkyKey){ writeSetting(LEVEL_VAL_ADDR,levelVal); } lastPinkyKey = pinkyKey; } else if (pinkySetting == GLD){ - if (pinkyKey){ - ledMeter(levelVal); - if (K6 && (levelVal < 127)){ - levelVal++; - midiSendControlChange(CCN_Port, levelVal); - } else if (K5 && (levelVal > 0)){ - levelVal--; - midiSendControlChange(CCN_Port, levelVal); + if (pinkyKey && K7){ + ledMeter(portLimit); + if (K6 && (portLimit < 127)){ + if (currentTime - lvlTime > (LVL_TIMER_INTERVAL)){ + portLimit++; + if (portamento && (portamento != 5)) midiSendControlChange(CCN_Port, portLimit); + lvlTime = currentTime; + } + } else if (K5 && (portLimit > 0)){ + if (currentTime - lvlTime > (LVL_TIMER_INTERVAL)){ + portLimit--; + if (portamento && (portamento != 5)) midiSendControlChange(CCN_Port, portLimit); + lvlTime = currentTime; + } } - } else if (lastPinkyKey){ - writeSetting(LEVEL_VAL_ADDR,levelVal); + } else if (!pinkyKey && lastPinkyKey){ + writeSetting(PORTLIMIT_ADDR,portLimit); } lastPinkyKey = pinkyKey; } @@ -1283,6 +1299,7 @@ void loop() { } if (!parallelChord && !subOctaveDouble && !rotatorOn) { // mono playing, send old note off after new note on + delayMicroseconds(2000); //delay for midi recording fix midiSendNoteOff(activeNote); // send Note Off message } @@ -1317,7 +1334,7 @@ void loop() { if (currentTime - ccSendTime3 > CC_INTERVAL3) { if (gateOpenEnable || gateOpen) doorKnobCheck(); battCheck(); - if (((pinkySetting == LVL) || (pinkySetting == LVLP) || (pinkySetting == GLD)) && pinkyKey && (mainState == NOTE_OFF)){ + if (((pinkySetting == LVL) || (pinkySetting == LVLP) || (pinkySetting == GLD)) && pinkyKey && K7 && (mainState == NOTE_OFF)){ // show LVL indication } else updateSensorLEDs(); ccSendTime3 = currentTime; @@ -1768,7 +1785,7 @@ void portamento_() { biteSensor = touchRead(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right) } if (pinkySetting == GLD){ - if (pinkyKey){ + if (portamento && pinkyKey){ if (!portIsOn) { portOn(); } @@ -1789,7 +1806,7 @@ void portamento_() { } else if (1 == vibControl) { // Portamento is switched to lever control leverPortRead = touchRead(vibratoPin); - if (portamento && (leverPortRead <= (leverPortZero-leverPortThr))) { // if we are enabled and over the threshold, send portamento + if (portamento && ((3000-leverPortRead) >= leverThrVal)) { // if we are enabled and over the threshold, send portamento if (!portIsOn) { portOn(); } @@ -1807,7 +1824,7 @@ void portamento_() { //*********************************************************** void portOn() { - if (portamento == 2) { // if portamento midi switching is enabled + if ((portamento == 2) || (portamento == 5)) { // if portamento midi switching is enabled midiSendControlChange(CCN_PortOnOff, 127); } else if (portamento == 3) { // if portamento midi switching is enabled - SE02 OFF/LIN midiSendControlChange(CCN_PortSE02, 64); @@ -1822,12 +1839,12 @@ void portOn() { void port() { int portCC; if (pinkySetting == GLD){ - portCC = levelVal; - } else if (1 != vibControl) - portCC = map(constrain(biteSensor, portamThrVal, portamMaxVal), portamThrVal, portamMaxVal, 0, 127); - else - portCC = constrain((leverPortZero-leverPortThr-leverPortRead),0,127); - if (portCC != oldport) { + portCC = portLimit; + } else if (1 == vibControl) + portCC = map(constrain((3000-leverPortRead), leverThrVal, leverMaxVal), leverThrVal, leverMaxVal, 0, portLimit); + else + portCC = map(constrain(biteSensor, portamThrVal, portamMaxVal), portamThrVal, portamMaxVal, 0, portLimit); + if ((portamento != 5) && (portCC != oldport)) { // portamento setting 5 is switch only, do not transmit glide rate midiSendControlChange(CCN_Port, portCC); } oldport = portCC; @@ -1836,10 +1853,10 @@ void port() { //*********************************************************** void portOff() { - if (oldport != 0) { //did a zero get sent? if not, then send one + if ((portamento != 5) && (oldport != 0)) { //did a zero get sent? if not, then send one (unless portamento is switch only) midiSendControlChange(CCN_Port, 0); } - if (portamento == 2) { // if portamento midi switching is enabled + if ((portamento == 2) || (portamento == 5)) { // if portamento midi switching is enabled midiSendControlChange(CCN_PortOnOff, 0); } else if (portamento == 3) { // if portamento midi switching is enabled - SE02 OFF/LIN midiSendControlChange(CCN_PortSE02, 0); @@ -1876,6 +1893,12 @@ void autoCal() { pitchbMaxVal = constrain(pitchbThrVal+800, pitchbLoLimit, pitchbHiLimit); writeSetting(PITCHB_THR_ADDR, pitchbThrVal); writeSetting(PITCHB_MAX_ADDR, pitchbMaxVal); + // Lever + calRead = 3000-touchRead(vibratoPin); + leverThrVal = constrain(calRead+70, leverLoLimit, leverHiLimit); + leverMaxVal = constrain(calRead+150, leverLoLimit, leverHiLimit); + writeSetting(LEVER_THR_ADDR, leverThrVal); + writeSetting(LEVER_MAX_ADDR, leverMaxVal); #if defined(NURAD) // NuRAD sensor calibration // Pressure sensor calRead = analogRead(bitePressurePin); diff --git a/NuEVI/adjustmenu.cpp b/NuEVI/adjustmenu.cpp index 02d50d7..c4bd7e7 100644 --- a/NuEVI/adjustmenu.cpp +++ b/NuEVI/adjustmenu.cpp @@ -113,12 +113,29 @@ const AdjustMenuEntry ctouchAdjustMenu = { ctouchThrSave }; + +static void leverSave(const AdjustMenuEntry& e) { + writeSetting(LEVER_THR_ADDR, *e.entries[0].value); + writeSetting(LEVER_MAX_ADDR, *e.entries[1].value); +} + +const AdjustMenuEntry leverAdjustMenu = { + "THUMB LEVER", + { + { &leverThrVal, leverLoLimit, leverHiLimit }, + { &leverMaxVal, leverLoLimit, leverHiLimit } + }, + leverSave +}; + + const AdjustMenuEntry* adjustMenuEntries[] = { &breathAdjustMenu, &portamentoAdjustMenu, &pitchBendAdjustMenu, &extraSensorAdjustMenu, &ctouchAdjustMenu, + &leverAdjustMenu, }; static const int numAdjustEntries = ARR_LEN(adjustMenuEntries); @@ -277,6 +294,12 @@ void plotSensorPixels(){ redraw = 1; } #endif + else if(adjustOption == 5) { + int pos = map(constrain(3000-touchRead(vibratoPin), leverLoLimit, leverHiLimit), leverLoLimit, leverHiLimit, 28, 118); + redraw = updateSensorPixel(pos, -1); + } + + if (redraw){ display.display(); } diff --git a/NuEVI/config.h b/NuEVI/config.h index 703b441..d390f45 100644 --- a/NuEVI/config.h +++ b/NuEVI/config.h @@ -5,7 +5,7 @@ // Compile options, comment/uncomment to change -#define FIRMWARE_VERSION "1.5b1" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<< +#define FIRMWARE_VERSION "1.5b2" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<< #define ON_Delay 20 // Set Delay after ON threshold before velocity is checked (wait for tounging peak) #define CCN_Port 5 // Controller number for portamento level @@ -19,6 +19,7 @@ #define CC_INTERVAL 13 #define CC_INTERVAL2 19 #define CC_INTERVAL3 37 +#define LVL_TIMER_INTERVAL 15 #define CVPORTATUNE 2 @@ -34,6 +35,8 @@ #define ctouchHiLimit 350 #define ttouchLoLimit 50 #define ttouchHiLimit 1900 +#define leverLoLimit 1400 +#define leverHiLimit 2000 #define MIN_LED_BRIGHTNESS 5 // lowest PWM value that still is visible diff --git a/NuEVI/globals.h b/NuEVI/globals.h index 53747dd..e77bb24 100644 --- a/NuEVI/globals.h +++ b/NuEVI/globals.h @@ -65,6 +65,8 @@ extern unsigned short pitchbMaxVal; extern unsigned short extracThrVal; extern unsigned short extracMaxVal; extern unsigned short ctouchThrVal; +extern unsigned short leverThrVal; +extern unsigned short leverMaxVal; extern unsigned short transpose; extern unsigned short MIDIchannel; extern unsigned short breathCC; // OFF:MW:BR:VL:EX:MW+:BR+:VL+:EX+:CF:UNO @@ -73,6 +75,7 @@ extern unsigned short breathCC2Rise; // 1X:2X:3X:4X:5X extern unsigned short breathAT; extern unsigned short velocity; extern unsigned short portamento;// switching on cc65? just cc5 enabled? SW:ON:OFF +extern unsigned short portLimit; // 1-127 extern unsigned short PBdepth; // OFF:1-12 divider extern unsigned short extraCT; // OFF:MW:FP:CF:SP extern unsigned short vibrato; // OFF:1-9 @@ -128,6 +131,8 @@ extern uint16_t dacMode; extern int touch_Thr; +extern int leverPortZero; + extern unsigned long cursorBlinkTime; // the last time the cursor was toggled extern byte activePatch; diff --git a/NuEVI/menu.cpp b/NuEVI/menu.cpp index 7f6b6a7..c90a432 100644 --- a/NuEVI/menu.cpp +++ b/NuEVI/menu.cpp @@ -413,7 +413,7 @@ static void mainTitleGetStr(char* out) { case 2: vLowLimit = LIP_BAT_LOW; } - if (vMeterReading < vLowLimit) { //2300 alkaline, 2250 lipo, 2200 nimh + if (vMeterReading <= vLowLimit) { //2300 alkaline, 2250 lipo, 2200 nimh memcpy(splice2, "LOW ", 4); } else { int voltage = map(vMeterReading,2200,3060,36,50); @@ -1269,15 +1269,27 @@ const MenuPage breathMenuPage = { //*********************************************************** // Control menu const MenuEntrySub portMenu = { - MenuType::ESub, "PORT/GLD", "PORT/GLD", &portamento, 0, 4, MenuEntryFlags::EMenuEntryWrap, + MenuType::ESub, "GLIDE CTL", "PORT/GLD", &portamento, 0, 5, MenuEntryFlags::EMenuEntryWrap, [](SubMenuRef __unused,char* out, const char ** __unused unit) { - const char* labs[] = { "OFF", "ON", "SW", "SEL", "SEE" }; + const char* labs[] = { "OFF", "ON", "SW", "SEL", "SEE", "SWO" }; strncpy(out, labs[portamento], 4); }, [](SubMenuRef __unused sub) { writeSetting(PORTAM_ADDR,portamento); } , nullptr }; + +const MenuEntrySub portLimitMenu = { + MenuType::ESub, "GLIDE LMT", "MAX LEVEL", &portLimit, 1, 127, MenuEntryFlags::EMenuEntryWrap, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + numToString(portLimit, out); + }, +[](const SubMenuRef & __unused sub) { writeSetting(PORTLIMIT_ADDR,portLimit); } + , nullptr +}; + + + const MenuEntrySub pitchBendMenu = { MenuType::ESub, "PITCHBEND", "PITCHBEND", &PBdepth, 0, 12, MenuEntryFlags::ENone, [](SubMenuRef __unused, char* out, const char** __unused unit) { @@ -1429,6 +1441,7 @@ const MenuEntrySub lpinky3Menu = { #if defined(NURAD) const MenuEntry* controlMenuEntries[] = { (MenuEntry*)&portMenu, + (MenuEntry*)&portLimitMenu, (MenuEntry*)&extraMenu, (MenuEntry*)&extraCC2Menu, (MenuEntry*)&harmonicsMenu, @@ -1444,6 +1457,7 @@ const MenuEntry* controlMenuEntries[] = { #else const MenuEntry* controlMenuEntries[] = { (MenuEntry*)&portMenu, + (MenuEntry*)&portLimitMenu, (MenuEntry*)&extraMenu, (MenuEntry*)&extraCC2Menu, (MenuEntry*)&harmonicsMenu, diff --git a/NuEVI/settings.cpp b/NuEVI/settings.cpp index 16c7251..2acad39 100644 --- a/NuEVI/settings.cpp +++ b/NuEVI/settings.cpp @@ -151,6 +151,12 @@ void readEEPROM(const bool factoryReset) { writeSetting(BRINTERV_ADDR, BRINTERV_FACTORY); writeSetting(OTFKEY_ADDR, OTFKEY_FACTORY); } + + if(settingsVersion < 40) { + writeSetting(PORTLIMIT_ADDR, PORTLIMIT_FACTORY); + writeSetting(LEVER_THR_ADDR, LEVER_THR_FACTORY); + writeSetting(LEVER_MAX_ADDR, LEVER_MAX_FACTORY); + } writeSetting(VERSION_ADDR, EEPROM_VERSION); @@ -168,7 +174,7 @@ void readEEPROM(const bool factoryReset) { breathCC = readSettingBounded(BREATH_CC_ADDR, 0, 10, BREATH_CC_FACTORY); breathAT = readSettingBounded(BREATH_AT_ADDR, 0, 1, BREATH_AT_FACTORY); velocity = readSettingBounded(VELOCITY_ADDR, 0, 127, VELOCITY_FACTORY); - portamento = readSettingBounded(PORTAM_ADDR, 0, 4, PORTAM_FACTORY); + portamento = readSettingBounded(PORTAM_ADDR, 0, 5, PORTAM_FACTORY); PBdepth = readSettingBounded(PB_ADDR, 0, 12, PB_FACTORY); extraCT = readSettingBounded(EXTRA_ADDR, 0, 4, EXTRA_FACTORY); vibrato = readSettingBounded(VIBRATO_ADDR, 0, 9, VIBRATO_FACTORY); @@ -237,6 +243,9 @@ void readEEPROM(const bool factoryReset) { rotationsc[3] = readSettingBounded(ROTC4_ADDR, 0, 48, ROTC4_FACTORY); otfKey = readSettingBounded(OTFKEY_ADDR, 0, 1, OTFKEY_FACTORY); breathInterval = readSettingBounded(BRINTERV_ADDR, 3, 15, BRINTERV_FACTORY); + portLimit = readSettingBounded(PORTLIMIT_ADDR, 1, 127, PORTLIMIT_FACTORY); + leverThrVal = readSettingBounded(LEVER_THR_ADDR, leverLoLimit, leverHiLimit, LEVER_THR_FACTORY); + leverMaxVal = readSettingBounded(LEVER_MAX_ADDR, leverLoLimit, leverHiLimit, LEVER_MAX_FACTORY); //Flags stored in bit field fastBoot = (dipSwBits & (1<