From e3c91fa6ee73b5b479f330e9df3cd2deba01baa4 Mon Sep 17 00:00:00 2001 From: Brian Hrebec Date: Fri, 8 Nov 2024 22:29:39 -0600 Subject: [PATCH] Added better support for ACC mode --- NuEVI/src/globals.h | 7 +++ NuEVI/src/menu.cpp | 22 ++++--- NuEVI/src/settings.cpp | 3 + NuEVI/src/settings.h | 5 +- NuEVI/src/xEVI.cpp | 128 +++++++++++++++++++++++++++++------------ 5 files changed, 118 insertions(+), 47 deletions(-) diff --git a/NuEVI/src/globals.h b/NuEVI/src/globals.h index 9f33853..5e88100 100644 --- a/NuEVI/src/globals.h +++ b/NuEVI/src/globals.h @@ -32,6 +32,12 @@ enum FingeringMode : uint8_t { HRN = 3, }; +enum NoteMode : uint8_t { + BREATH_TRIGGER = 1, + LEVER_TRIGGER = 2, + NO_TRIGGER = 3, +}; + enum RollerMode : uint8_t { HIGHEST = 1, HIGHEST_EXTEND = 2, @@ -62,6 +68,7 @@ enum ExtraControl : uint8_t { CC = 3, BEND = 4, VIB_BEND = 5, + VELOCITY_RETRIGGER = 6, }; enum PolySelect : uint8_t { diff --git a/NuEVI/src/menu.cpp b/NuEVI/src/menu.cpp index 37abf68..f368774 100644 --- a/NuEVI/src/menu.cpp +++ b/NuEVI/src/menu.cpp @@ -982,18 +982,25 @@ PresetValueMenu<1, uint8_t> vibRetnMenu("RETURN", &preset_t::vibRetn, 0, 4, true PresetValueMenu<0, uint8_t> vibSenseMenu("SENSE LVR", &preset_t::vibSens, 0, 12); PresetValueMenu<0, uint8_t> vibSquelchMenu("SQUELCH LVR", &preset_t::vibSquelch, 0, 12); ChoiceMenu<2, VibratoMode> vibDirMenu("DIRECTION", &preset_t::vibratoMode, { {VSTART_DOWN, VSTART_UP} }, { "DWN", "UP" }); -ChoiceMenu<4, ExtraControl> biteCtlMenu("BITE CTL", &preset_t::biteControl, { {OFF, VIBRATO, GLIDE, CC} }, { { +ChoiceMenu<3, NoteMode> noteModeMenu("NOTE MODE", &preset_t::noteMode, { {BREATH_TRIGGER, LEVER_TRIGGER, NO_TRIGGER} }, { { + "BREATH", + "LEVER", + "OFF", +} }); +ChoiceMenu<5, ExtraControl> biteCtlMenu("BITE CTL", &preset_t::biteControl, { {OFF, VIBRATO, GLIDE, CC, VELOCITY_RETRIGGER} }, { { "OFF", "VIBRATO", "GLIDE", - "CC" + "CC", + "VEL RT" } }); PresetValueMenu<128, uint8_t> biteCCMenu("BITE CC", &preset_t::biteCC, 0, 127, true, CC_NAMES); -ChoiceMenu<4, ExtraControl> leverCtlMenu("LEVER CTL", &preset_t::leverControl, { {OFF, VIBRATO, GLIDE, CC} }, { +ChoiceMenu<5, ExtraControl> leverCtlMenu("LEVER CTL", &preset_t::leverControl, { {OFF, VIBRATO, GLIDE, CC, VELOCITY_RETRIGGER} }, { "OFF", "VIB", "GLD", - "CC" + "CC", + "VEL RT" }); PresetValueMenu<128, uint8_t> leverCCMenu("LEVER CC", &preset_t::leverCC, 0, 127, true, CC_NAMES); ChoiceMenu<4, PortamentoMode> portMenu("GLIDE MOD", &preset_t::portamentoMode, { {POFF, PON, PSWITCH_ONLY, PGLIDE_ONLY} }, { @@ -1006,7 +1013,7 @@ PresetValueMenu<0, uint8_t> portLimitMenu("GLIDE LMT", &preset_t::portamentoLimi PresetValueMenu<0, uint8_t> pitchBendMenu("PITCHBEND", &preset_t::PBdepth, 0, 12, true); ChoiceMenu<4, ExtraControl> extraCtlMenu("EXT CTL", &preset_t::extraControl, { {OFF, VIBRATO, GLIDE, CC} }, { "OFF", "VIBRATO", "GLIDE", "CC" }); PresetValueMenu<128, uint8_t> extraCCMenu("EXT CC", &preset_t::extraCC, 0, 127, true, CC_NAMES); -ChoiceMenu<6, ExtraControl> pbCtlMenu("PB CTL", &preset_t::pbControl, { {OFF, VIBRATO, GLIDE, CC, BEND, VIB_BEND} }, { "OFF", "VIBRATO", "GLIDE", "CC", "BEND", "VIB_BEND" }); +ChoiceMenu<7, ExtraControl> pbCtlMenu("PB CTL", &preset_t::pbControl, { {OFF, VIBRATO, GLIDE, CC, BEND, VIB_BEND, VELOCITY_RETRIGGER} }, { "OFF", "VIBRATO", "GLIDE", "CC", "BEND", "VIB+BEND", "VEL RT" }); PresetValueMenu<128, uint8_t> pbCCMenu("PB CC", &preset_t::pbCC, 0, 127, true, CC_NAMES); PresetValueMenu<128, uint8_t> pbYCCMenu("PB Y CC", &preset_t::pbYCC, 0, 127, true, CC_NAMES); PresetValueMenu<128, uint8_t> stickXCCMenu("STICK X CC", &preset_t::stickXCC, 0, 127, true, CC_NAMES); @@ -1088,10 +1095,11 @@ std::array breathMenuEntries = { }; SubMenu<11> breathMenu("BR SETUP", breathMenuEntries); -const std::array controlMenuEntries = { +const std::array controlMenuEntries = { &fingeringMenu, &polyMenu, &rollerMenu, + ¬eModeMenu, &biteCtlMenu, &biteCCMenu, &pbCtlMenu, @@ -1115,7 +1123,7 @@ const std::array controlMenuEntries = { &accelModeMenu, &accelCCMenu, }; -SubMenu<25> controlMenu("CTL SETUP", controlMenuEntries); +SubMenu<26> controlMenu("CTL SETUP", controlMenuEntries); const std::array vibratoMenuEntries = { &vibDepthMenu, diff --git a/NuEVI/src/settings.cpp b/NuEVI/src/settings.cpp index 6d68dc6..55283b3 100644 --- a/NuEVI/src/settings.cpp +++ b/NuEVI/src/settings.cpp @@ -347,6 +347,9 @@ void readEEPROM(const bool factoryReset, calibration_t &calibration) { presets[i].spikeOnFactor = 5; presets[i].spikeOffFactor = 5; } + if (settings_version < 6) { + presets[i].noteMode = BREATH_TRIGGER; + } } writePresets(); diff --git a/NuEVI/src/settings.h b/NuEVI/src/settings.h index 9d636b2..59130d0 100644 --- a/NuEVI/src/settings.h +++ b/NuEVI/src/settings.h @@ -5,7 +5,7 @@ #include "globals.h" -#define EEPROM_VERSION 5 +#define EEPROM_VERSION 6 #define EEPROM_VERSION_ADDR 0 #define SETTINGS_OFFSET 2 #define PRESET_MAX_SIZE 128 // Leave extra space for future settings @@ -106,8 +106,9 @@ struct preset_t { uint8_t pbYCC = 13; // pbY CC selection uint8_t stickXCC = 14; // stickX CC selection uint8_t stickYCC = 15; // stickY CC selection + NoteMode noteMode = NoteMode::BREATH_TRIGGER; - uint8_t _reserved[77]; + uint8_t _reserved[76]; }; static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes"); diff --git a/NuEVI/src/xEVI.cpp b/NuEVI/src/xEVI.cpp index 12675fd..490f8b4 100644 --- a/NuEVI/src/xEVI.cpp +++ b/NuEVI/src/xEVI.cpp @@ -217,12 +217,36 @@ int patchLimit(int value) { int breath() { static int oldbreath = 0; - static unsigned int oldbreathhires = 0; + static int oldbreathhires = 0; int breathCCval, breathCCvalFine; - unsigned int breathCCvalHires; - breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383)); - breathCCvalHires = breathCCFilter.input(breathCCvalHires); + int breathCCvalHires; + if (state.currentPreset->breathMode == BREATH_ACC || state.currentPreset->breathMode == BREATH_ACC_AT) { + int diff = instrument.breathSignal - instrument.breathZero; + if (instrument.mode == MODE_DEBUG) { + Serial.print(">accdiff:"); + Serial.println(diff); + } + if (abs(diff) > calibration.breathThrValOffset) { + if (diff > 0) { + breathCCvalHires = breathCurve(mapConstrain(diff, calibration.breathThrValOffset, calibration.breathMaxValOffset, 0, 16383)); + } else { + breathCCvalHires = -breathCurve(mapConstrain(-diff, calibration.breathThrValOffset, calibration.breathMaxValOffset, 0, 16383)); + } + // breathCCvalHires = breathCCFilter.input(breathCCvalHires); + if (instrument.mode == MODE_DEBUG) { + Serial.print(">accdelta:"); + Serial.println(breathCCvalHires); + } + breathCCvalHires = constrain(oldbreathhires + breathCCvalHires / 4, 0, 16383); + } else { + breathCCvalHires = oldbreathhires; + } + } else { + breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383)); + breathCCvalHires = breathCCFilter.input(breathCCvalHires); + } + breathCCval = (breathCCvalHires >> 7) & 0x007F; breathCCvalFine = breathCCvalHires & 0x007F; if (breathCCval != oldbreath) { // only send midi data if breath has changed from previous value @@ -240,7 +264,7 @@ int breath() { } if (breathCCvalHires != oldbreathhires && - (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) { + (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT|| state.currentPreset->breathMode == BreathMode::BREATH_ACC)) { midiSendControlChange(state.currentPreset->breathCC + 32, breathCCvalFine); } @@ -396,7 +420,7 @@ void portamento_() { void sendCC() { if (ExtraControl::CC == state.currentPreset->biteControl) { - int biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127); + int biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 127, 0); if (biteVal != instrument.biteVal) { midiSendControlChange(state.currentPreset->biteCC, biteVal); @@ -413,14 +437,13 @@ void sendCC() { instrument.extraVal = extraVal; } + int leverVal = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, 127); if (ExtraControl::CC == state.currentPreset->leverControl) { - int leverVal = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, 127); - if (leverVal != instrument.leverVal) { midiSendControlChange(state.currentPreset->leverCC, leverVal); } - instrument.leverVal = leverVal; } + instrument.leverVal = leverVal; if (ExtraControl::CC == state.currentPreset->pbControl) { int pbVal = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbMaxVal, 0, 127); @@ -778,20 +801,42 @@ void readSwitches() { lastOctave = fingeredOctave; } -void noteOn(int fingeredNote, int pressureSensor, int initial_breath_value) { - // Yes, so calculate MIDI note and velocity, then send a note on event - // We should be at tonguing peak, so set velocity based on current pressureSensor value unless fixed velocity is set - instrument.breathSignal = constrain(max(pressureSensor, initial_breath_value), instrument.breathBaseline, instrument.breathMaxVal); - byte velocitySend; - if (!state.currentPreset->fixedVelocity) { - unsigned int breathValHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383)); - velocitySend = (breathValHires >> 7) & 0x007F; - velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127); - } else { - velocitySend = state.currentPreset->fixedVelocity; +uint8_t controlVel() { + uint8_t velocity = 0; + if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->biteControl) { + velocity = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 127, 1); } - midiSendNoteOn(fingeredNote, velocitySend); // send Note Off message + if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->leverControl) { + velocity = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 1, 127); + } + + if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->pbControl) { + velocity = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbMaxVal, 1, 127); + } + + return velocity; +} + +uint8_t breathVel() { + // Set velocity based on current pressureSensor value unless fixed velocity is set + int velocitySend; + if (state.currentPreset->fixedVelocity) { + return state.currentPreset->fixedVelocity; + } + if (state.currentPreset->noteMode == LEVER_TRIGGER) { + return instrument.leverVal; + } + + unsigned int breathValHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383)); + velocitySend = (breathValHires >> 7) & 0x007F; + velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127); + + return velocitySend; +} + +void noteOn(int fingeredNote) { + midiSendNoteOn(fingeredNote, breathVel()); } /** @@ -803,15 +848,20 @@ void changeNote(int fingeredNote, int activeNote, int fingeredNote2, int activeN return; } + int velocity = controlVel(); + if (!velocity) { + velocity = breathVel(); + } + // Player has moved to a new fingering while still blowing. // Send a note off for the current note and a note on for // the new note. if (fingeredNote != activeNote && fingeredNote != activeNote2) { - noteOn(fingeredNote, instrument.breathSignal, 0); + midiSendNoteOn(fingeredNote, velocity); } if (fingeredNote2 != fingeredNote && fingeredNote2 != activeNote && fingeredNote2 != activeNote2) { - noteOn(fingeredNote2, instrument.breathSignal, 0); + midiSendNoteOn(fingeredNote2, velocity); } delayMicroseconds(2000); // delay for midi recording fix @@ -896,6 +946,11 @@ void handleCCs() { } } +int adjustRetriggerVel(int initialVel) { + int vel = initialVel; + +} + /** * Read the breath sensors according to the active mode */ @@ -906,14 +961,7 @@ void readBreath() { int16_t breathAltSignal = constrain(readAltPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT) + instrument.breathAltZeroOffset; int16_t spikeSignal = constrain(readSpikePressure(), -SPIKE_HI_LIMIT, SPIKE_HI_LIMIT); int16_t diffSignal = breathAltSignal - breathSignal; - if (state.currentPreset->breathMode == BREATH_ACC || state.currentPreset->breathMode == BREATH_ACC_AT) { - int delta = breathSignal - instrument.breathZero; - if (abs(delta) > state.calibration->breathThrValOffset) { - instrument.breathSignal = constrain(instrument.breathSignal + delta / 15, instrument.breathZero, instrument.breathMaxVal); - } - } else { - instrument.breathSignal = breathSignal + diffSignal + (spikeSignal > 0 ? spikeSignal * state.currentPreset->spikeOnFactor : spikeSignal * state.currentPreset->spikeOffFactor); - } + instrument.breathSignal = breathSignal + diffSignal + (spikeSignal > 0 ? spikeSignal * state.currentPreset->spikeOnFactor : spikeSignal * state.currentPreset->spikeOffFactor); instrument.breathAltSignal = breathAltSignal; @@ -962,23 +1010,27 @@ void readBreath() { */ void runStateMachine() { static unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold - static int initial_breath_value = 0; // The breath value at the time we observed the transition + bool lever_trigger = state.currentPreset->noteMode == LEVER_TRIGGER; + if (state.currentPreset->noteMode == NO_TRIGGER) { + return; + } + if (state.mainState == NOTE_OFF) { handleOffStateActions(); - if (instrument.breathSignal > instrument.breathThrVal && state.mainState == NOTE_OFF) { + bool crossed_threshold = lever_trigger ? instrument.leverVal > 0 : instrument.breathSignal > instrument.breathThrVal; + if (crossed_threshold && state.mainState == NOTE_OFF) { // Value has risen above threshold. Move to the RISE_WAIT // state. Record time and initial breath value. breath_on_time = millis(); - initial_breath_value = instrument.breathSignal; state.mainState = RISE_WAIT; // Go to next state } } else if (state.mainState == RISE_WAIT) { - if ((instrument.breathSignal > instrument.breathMovingThrVal)) { + if ((lever_trigger ? instrument.leverVal > 0 : instrument.breathSignal > instrument.breathMovingThrVal)) { // Has enough time passed for us to collect our second sample? if ((millis() - breath_on_time > state.currentPreset->velSmpDl) || (0 == state.currentPreset->velSmpDl) || state.currentPreset->fixedVelocity) { - noteOn(instrument.fingeredNote, instrument.breathSignal, initial_breath_value); + noteOn(instrument.fingeredNote); if (instrument.fingeredNote != instrument.fingeredNote2) { - noteOn(instrument.fingeredNote2, instrument.breathSignal, initial_breath_value); + noteOn(instrument.fingeredNote2); } breath(); // send breath data state.mainState = NOTE_ON; @@ -990,7 +1042,7 @@ void runStateMachine() { state.mainState = NOTE_OFF; } } else if (state.mainState == NOTE_ON) { - if (instrument.breathSignal < instrument.breathThrOffVal) { + if (lever_trigger ? instrument.leverVal <= 0 : instrument.breathSignal < instrument.breathThrOffVal) { // Value has fallen below threshold - turn the note off midiSendNoteOff(instrument.activeNote); // send Note Off message if (instrument.activeNote != instrument.activeNote2) {