From 4211a85562882ddb52d82d37a8e24ae531e194b5 Mon Sep 17 00:00:00 2001 From: Brian Hrebec Date: Tue, 30 Jan 2024 22:16:15 -0600 Subject: [PATCH] New poly modes, processing improvements --- NuEVI/src/globals.h | 115 +++++++++++---------- NuEVI/src/hardware.cpp | 2 - NuEVI/src/hardware.h | 36 +++---- NuEVI/src/menu.cpp | 31 +++++- NuEVI/src/settings.cpp | 2 +- NuEVI/src/settings.h | 5 +- NuEVI/src/test.cpp | 6 +- NuEVI/src/test.h | 2 +- NuEVI/src/xEVI.cpp | 221 ++++++++++++++++++++++++++--------------- 9 files changed, 258 insertions(+), 162 deletions(-) diff --git a/NuEVI/src/globals.h b/NuEVI/src/globals.h index 0b47679..4e2dfc5 100644 --- a/NuEVI/src/globals.h +++ b/NuEVI/src/globals.h @@ -65,7 +65,7 @@ enum ExtraControl : uint8_t { enum PolySelect : uint8_t { EHarmonizerOff = 0, EDuo = 1, - EChord = 2, + EDouble = 2, }; enum PortamentoMode : uint8_t { @@ -75,64 +75,75 @@ enum PortamentoMode : uint8_t { PGLIDE_ONLY = 3, }; +enum InstrumentMode : uint8_t { + MODE_NORMAL, + MODE_TEST, + MODE_CONFIG, + MODE_DEBUG, +}; + struct instrument_state_t { - uint8_t patch; // 1-128 - byte activeMIDIchannel = 1; // MIDI channel - byte activeNote = 0; // note playing - byte activePatch = 0; - byte doPatchUpdate = 0; - int8_t transpose = 0; - int8_t octave = 0; - PolySelect polyMode = PolySelect::EHarmonizerOff; + InstrumentMode mode = MODE_NORMAL; + uint8_t patch; // 1-128 + byte activeMIDIchannel = 1; // MIDI channel + byte activeNote = 0; // note playing + byte activeNote2 = 0; // note playing + byte activePatch = 0; + byte doPatchUpdate = 0; + int8_t transpose = 0; + int8_t octave = 0; - // Raw sensor signals - int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value - int16_t breathAltSignal = 0; - int16_t biteSignal = 0; // capacitance data from bite sensor, for midi cc and threshold checks - int16_t leverSignal = 0; - int16_t pbUpSignal = 0; - int16_t pbDnSignal = 0; - int16_t extraSignal = 0; - int16_t vibSignal = 0; - int16_t avgCTouchSignal = 0; + // Raw sensor signals + int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value + int16_t breathAltSignal = 0; + int16_t biteSignal = 0; // capacitance data from bite sensor, for midi cc and threshold checks + int16_t leverSignal = 0; + int16_t pbUpSignal = 0; + int16_t pbDnSignal = 0; + int16_t extraSignal = 0; + int16_t vibSignal = 0; + int16_t avgCTouchSignal = 0; - // MIDI values - int breathCCVal = 0; - byte biteVal = 0; // keep track and make sure we send CC with 0 value when off threshold - byte portamentoVal = 0; // keep track and make sure we send CC with 0 value when off threshold - byte extraVal = 0; // keep track and make sure we send CC with 0 value when off threshold - byte leverVal = 0; // keep track and make sure we send CC with 0 value when off threshold - int pitchBend = 8192; - int pbSend = 8192; // Pitch bend actually sent, modified by vibrato, etc - byte knobVals[4]; - byte lastKnobVal; - unsigned long lastKnobTime; - byte rollCCVal = 0; - byte tiltCCVal = 0; + // MIDI values + int breathCCVal = 0; + byte biteVal = 0; // keep track and make sure we send CC with 0 value when off threshold + byte portamentoVal = 0; // keep track and make sure we send CC with 0 value when off threshold + byte extraVal = 0; // keep track and make sure we send CC with 0 value when off threshold + byte leverVal = 0; // keep track and make sure we send CC with 0 value when off threshold + int pitchBend = 8192; + int pbSend = 8192; // Pitch bend actually sent, modified by vibrato, etc + byte knobVals[4]; + byte lastKnobVal; + unsigned long lastKnobTime; + byte rollCCVal = 0; + byte tiltCCVal = 0; - // Key states - byte quarterToneTrigger; - byte pinkyKey = 0; + // Key states + byte quarterToneTrigger; + byte pinkyKey = 0; + byte fingeredNote = 0; + byte fingeredNote2 = 0; + byte fingeredOctave = 0; - // CV values - int cvPitch; - int targetPitch; + // CV values + int cvPitch; + int targetPitch; - // Calibration - int16_t breathZero; // this gets auto calibrated in setup - int16_t breathThrVal; // this gets auto calibrated in setup - int16_t breathMovingThrVal; - int16_t breathMaxVal; // this gets auto calibrated in setup - int16_t breathAltZero; // this gets auto calibrated in setup - int16_t breathAltThrVal; // this gets auto calibrated in setup - int16_t breathAltMaxVal; // this gets auto calibrated in setup + // Calibration + int16_t breathZero; // this gets auto calibrated in setup + int16_t breathThrVal; // this gets auto calibrated in setup + int16_t breathMovingThrVal; + int16_t breathMaxVal; // this gets auto calibrated in setup + int16_t breathAltZero; // this gets auto calibrated in setup + int16_t breathAltThrVal; // this gets auto calibrated in setup + int16_t breathAltMaxVal; // this gets auto calibrated in setup - int16_t vibThr; // this gets auto calibrated in setup - int16_t vibThrLo; - int16_t vibZero; - int16_t vibZeroBite; - int16_t vibThrBite; - int16_t vibThrBiteLo; + int16_t vibThr; // this gets auto calibrated in setup + int16_t vibThrLo; + int16_t vibZero; + int16_t vibZeroBite; + int16_t vibThrBite; + int16_t vibThrBiteLo; }; extern const std::array curves; diff --git a/NuEVI/src/hardware.cpp b/NuEVI/src/hardware.cpp index bd2c293..f0115a6 100644 --- a/NuEVI/src/hardware.cpp +++ b/NuEVI/src/hardware.cpp @@ -166,8 +166,6 @@ int readSpikePressure() { int readPressure() { float p = pressureSensorMain.readPressure(); - Serial.print(">raw:"); - Serial.println(p * PRESSURE_SENS_MULTIPLIER); return breathFilter.input(p) * PRESSURE_SENS_MULTIPLIER; } diff --git a/NuEVI/src/hardware.h b/NuEVI/src/hardware.h index 7dad9ad..7bcdc58 100644 --- a/NuEVI/src/hardware.h +++ b/NuEVI/src/hardware.h @@ -73,28 +73,28 @@ icm_result_t readICM(); // Key pins // RH keys -#define K1Pin 5 -#define K2Pin 6 -#define K3Pin 2 -#define K4Pin 11 -#define K5Pin 4 -#define K6Pin 7 -#define K7Pin 9 -#define K8Pin 10 +#define K1Pin 1 +#define K2Pin 7 +#define K3Pin 3 +#define K4Pin 10 +#define K5Pin 2 +#define K6Pin 6 +#define K7Pin 4 +#define K8Pin 11 // LH keys -#define K9Pin 3 -#define K10Pin 8 -#define K11Pin 0 -#define K12Pin 1 +#define K9Pin 0 +#define K10Pin 5 +#define K11Pin 8 +#define K12Pin 9 // Octave roller pins -#define R1Pin 11 -#define R2Pin 10 -#define R3Pin 9 -#define R4Pin 8 -#define R5Pin 7 -#define R6Pin 6 +#define R1Pin 6 +#define R2Pin 7 +#define R3Pin 8 +#define R4Pin 9 +#define R5Pin 10 +#define R6Pin 11 // Additional pins #define bitePin 0 diff --git a/NuEVI/src/menu.cpp b/NuEVI/src/menu.cpp index a04d733..062f1e3 100644 --- a/NuEVI/src/menu.cpp +++ b/NuEVI/src/menu.cpp @@ -1024,6 +1024,11 @@ ChoiceMenu<4, FingeringMode> fingeringMenu("FINGERING", &preset_t::fingering, { "TPT", "HRN", }); +ChoiceMenu<3, PolySelect> polyMenu("POLYMODE", &preset_t::polyMode, { EHarmonizerOff, EDouble, EDuo }, { + "OFF", + "DOUBLE", + "DUO", + }); ChoiceMenu<6, RollerMode> rollerMenu("ROLLRMODE", &preset_t::rollerMode, { HIGHEST, HIGHEST_EXTEND, @@ -1048,6 +1053,19 @@ FuncMenu factoryResetMenu("FACT. RESET", [](state_t &state, InputState& input) { FuncMenu saveAllPresetsMenu("SAVE PRESETS", [](state_t &state, InputState& input) { writePresets(); }); +FuncMenu debugModeMenu("DEBUG MODE", [](state_t &state, InputState& input) { + Serial.begin(9600); // debug + Serial.println("Debug Startup"); + state.instrument->mode = state.instrument->mode == MODE_DEBUG ? MODE_NORMAL : MODE_DEBUG; +}); +FuncMenu testModeMenu("TEST MODE", [](state_t &state, InputState& input) { + display.clearDisplay(); + state.instrument->mode = MODE_TEST; +}); +FuncMenu configModeMenu("CONFIG MGT", [](state_t &state, InputState& input) { + configModeSetup(); + state.instrument->mode = MODE_CONFIG; +}); std::array breathMenuEntries = { &breathModeMenu, @@ -1064,8 +1082,9 @@ std::array breathMenuEntries = { }; SubMenu<11> breathMenu("BR SETUP", breathMenuEntries); -const std::array controlMenuEntries = { +const std::array controlMenuEntries = { &fingeringMenu, + &polyMenu, &rollerMenu, &biteCtlMenu, &biteCCMenu, @@ -1085,7 +1104,7 @@ const std::array controlMenuEntries = { &accelModeMenu, &accelCCMenu, }; -SubMenu<19> controlMenu("CTL SETUP", controlMenuEntries); +SubMenu<20> controlMenu("CTL SETUP", controlMenuEntries); const std::array vibratoMenuEntries = { &vibDepthMenu, @@ -1099,7 +1118,7 @@ SubMenu<5> vibratoMenu("VIBRATO", vibratoMenuEntries); AboutMenu aboutMenu = AboutMenu(); -const std::array extrasMenuEntries = { +const std::array extrasMenuEntries = { &trill3Menu, &cvTuneMenu, &cvScaleMenu, @@ -1107,8 +1126,11 @@ const std::array extrasMenuEntries = { &recalibrateMenu, &factoryResetMenu, &saveAllPresetsMenu, + &testModeMenu, + &debugModeMenu, + &configModeMenu, }; -SubMenu<7> extrasMenu("EXTRAS", extrasMenuEntries); +SubMenu<10> extrasMenu("EXTRAS", extrasMenuEntries); ExitMenu exitMenu = ExitMenu(); @@ -1225,6 +1247,7 @@ void initDisplay() { // internally, this will display the splashscreen. display.clearDisplay(); + display.setRotation(2); display.drawBitmap(0, 0, nuevi_logo_bmp, LOGO16_GLCD_WIDTH, LOGO16_GLCD_HEIGHT, 1); display.display(); currentMenu = &statusMenu; diff --git a/NuEVI/src/settings.cpp b/NuEVI/src/settings.cpp index 5b287b4..6d68dc6 100644 --- a/NuEVI/src/settings.cpp +++ b/NuEVI/src/settings.cpp @@ -313,7 +313,7 @@ void configModeSetup() { } //"Main loop". Just sits and wait for midi messages and lets the sysex handler do all the work. -void configModeLoop() { +void configModeLoop(state_t &state) { usbMIDI.read(); } diff --git a/NuEVI/src/settings.h b/NuEVI/src/settings.h index c121546..f9c8ace 100644 --- a/NuEVI/src/settings.h +++ b/NuEVI/src/settings.h @@ -94,8 +94,9 @@ struct preset_t { uint8_t spikeFilterFreq = 20; int8_t spikeOnFactor = 5; int8_t spikeOffFactor = 5; + PolySelect polyMode = PolySelect::EHarmonizerOff; - uint8_t _reserved[83]; + uint8_t _reserved[82]; }; static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes"); @@ -132,6 +133,6 @@ void configInitScreen(); void configShowMessage(const char* message); void configModeSetup(); -void configModeLoop(); +void configModeLoop(state_t &state); #endif diff --git a/NuEVI/src/test.cpp b/NuEVI/src/test.cpp index 3619129..a3b33e0 100644 --- a/NuEVI/src/test.cpp +++ b/NuEVI/src/test.cpp @@ -5,7 +5,7 @@ uint16_t oldKeys = 0; uint16_t oldUtil = 0; bool plotCap = false; -void handleTestMode() { +void handleTestMode(state_t &state) { uint8_t buttons = buttonState(); if (buttons != oldButtons) { oldButtons = buttons; @@ -41,6 +41,10 @@ void handleTestMode() { plotCap = !plotCap; } + if (buttons == 0x08) { + state.instrument->mode = MODE_NORMAL; + } + if (plotCap) { for (int i = 0; i < 12; i++) { Serial.print(">key"); diff --git a/NuEVI/src/test.h b/NuEVI/src/test.h index d36fb67..576da4f 100644 --- a/NuEVI/src/test.h +++ b/NuEVI/src/test.h @@ -1,6 +1,6 @@ #ifndef __TEST_H #define __TEST_H -void handleTestMode(); +void handleTestMode(state_t &state); #endif \ No newline at end of file diff --git a/NuEVI/src/xEVI.cpp b/NuEVI/src/xEVI.cpp index 7f0c3e9..2a8de21 100644 --- a/NuEVI/src/xEVI.cpp +++ b/NuEVI/src/xEVI.cpp @@ -89,9 +89,6 @@ const int rollerHarmonic[2][7] = { {0, 7, 12, 16, 19, 24, 26}, // F horn 2,3,4 const int trumpetHarmonic[2][7] = { {0, 7, 12, 16, 19, 26, 31}, //! K4: hrm 8->9, 10->12 {0, 7, 12, 16, 19, 24, 28} }; // trumpet 2,3,4,5,6,8,10 hrm -bool configManagementMode = false; -bool testMode = false; - FilterOnePole breathCCFilter; FilterOnePole breathBaselineFilter; @@ -242,8 +239,8 @@ int breath() { oldbreath = breathCCval; } - if (breathCCvalHires != oldbreathhires - && (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) { + if (breathCCvalHires != oldbreathhires && + (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) { midiSendControlChange(state.currentPreset->breathCC + 32, breathCCvalFine); } @@ -641,25 +638,17 @@ int readOctave() { octave = 12 * octaveR + offset + instrument.octave * 12; } - if (octave != lastOctave) { // - // reset the debouncing timer - lastDeglitchTime = millis(); - } - - if ((millis() - lastDeglitchTime) > state.currentPreset->deglitch) { - fingeredOctave = octave; - } - - lastOctave = octave; - - return fingeredOctave; + return octave; } -int readSwitches() { +void readSwitches() { // Keep the last fingering value for debouncing static int lastFingering = 0; - static int fingeredNote = 0; + static int lastFingering2 = 0; + static int lastOctave = 0; static unsigned long lastDeglitchTime = 0; // The last time the fingering was changed + int fingeredNoteUntransposed = 0; + int fingeredNote2Untransposed = 0; // Read touch pads (MPR121), compare against threshold value bool touchKeys[12]; @@ -676,34 +665,54 @@ int readSwitches() { byte K1 = touchKeys[K1Pin]; // Valve 1 (pitch change -2) byte K2 = touchKeys[K2Pin]; // Valve 2 (pitch change -1) byte K3 = touchKeys[K3Pin]; // Valve 3 (pitch change -3) - byte K4 = touchKeys[K4Pin]; // Left Hand index finger (pitch change -5) + byte K4 = touchKeys[K4Pin]; // Value 4 (pitch change -5) byte K5 = touchKeys[K5Pin]; // Trill key 1 (pitch change +2) byte K6 = touchKeys[K6Pin]; // Trill key 2 (pitch change +1) byte K7 = touchKeys[K7Pin]; // Trill key 3 (pitch change +4) + byte K8 = touchKeys[K8Pin]; // Pinky Key + byte K9 = touchKeys[K9Pin]; // LH Key 1 + byte K10 = touchKeys[K10Pin]; // LH Key 2 + byte K11 = touchKeys[K11Pin]; // LH Key 3 + byte K12 = touchKeys[K12Pin]; // LH Key 4 - instrument.pinkyKey = touchKeys[K8Pin]; + instrument.pinkyKey = K8; - int qTransp = (instrument.pinkyKey && (state.currentPreset->pinkySetting < 25)) ? state.currentPreset->pinkySetting - 12 : 0; + int qTransp = (K8 && (state.currentPreset->pinkySetting < 25)) ? state.currentPreset->pinkySetting - 12 : 0; // Calculate midi note number from pressed keys - int fingeredNoteUntransposed = 0; - if (EVI == state.currentPreset->fingering) { // EVI fingering - fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" + switch (state.currentPreset->fingering) { + case EVI: + case EVR: + fingeredNoteUntransposed = START_NOTE + - 2 * K1 + - K2 + - 3 * K3 //"Trumpet valves" - 5 * K4 // Fifth key - + 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting - } else if (EVR == state.currentPreset->fingering) { // EVR fingering - fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" - - 5 * K4 // Fifth key - + 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting - } else if (TPT == state.currentPreset->fingering) { // TPT fingering - fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" - - 2 // Trumpet in B flat - + 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting - } else if (HRN == state.currentPreset->fingering) { // HRN fingering - fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" - + 5 * K4 // Switch to Bb horn - + 5 // Horn in F - + 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting + + 2 * K5 + + K6 + + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting + break; + case TPT: + fingeredNoteUntransposed = START_NOTE + - 2 * K1 + - K2 + - 3 * K3 //"Trumpet valves" + - 2 // Trumpet in B flat + + 2 * K5 + + K6 + + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting + break; + case HRN: + fingeredNoteUntransposed = START_NOTE + - 2 * K1 + - K2 + - 3 * K3 //"Trumpet valves" + + 5 * K4 // Switch to Bb horn + + 5 // Horn in F + + 2 * K5 + + K6 + + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting + break; } if (K3 && K7) { @@ -713,9 +722,31 @@ int readSwitches() { fingeredNoteUntransposed += 4; } - int fingeredNoteRead = fingeredNoteUntransposed + instrument.transpose - 12 + qTransp; + fingeredNote2Untransposed = + - 2 * K9 + - K10 + - 3 * K11 + - 7 * K12; - if (fingeredNoteRead != lastFingering) { // + switch (state.currentPreset->polyMode) { + case EHarmonizerOff: // LH affects main instrument + fingeredNoteUntransposed += fingeredNote2Untransposed; + fingeredNote2Untransposed = fingeredNoteUntransposed; + break; + case EDuo: // Duo (absolute) mode + fingeredNote2Untransposed = START_NOTE + fingeredNote2Untransposed; + break; + case EDouble: // Double-stop (relative) mode + fingeredNote2Untransposed = fingeredNoteUntransposed + fingeredNote2Untransposed; + break; + } + + int fingeredNoteRead = fingeredNoteUntransposed + instrument.transpose - 12 + qTransp; + int fingeredNote2Read = fingeredNote2Untransposed + instrument.transpose - 12 + qTransp; + int fingeredOctave = readOctave(); + + + if (fingeredOctave != lastOctave || fingeredNoteRead != lastFingering || fingeredNote2Read != lastFingering2) { // // reset the debouncing timer lastDeglitchTime = millis(); } @@ -723,12 +754,14 @@ int readSwitches() { if ((millis() - lastDeglitchTime) > state.currentPreset->deglitch) { // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current instrument - fingeredNote = fingeredNoteRead; + instrument.fingeredNote = noteValueCheck(fingeredNoteRead + fingeredOctave); + instrument.fingeredNote2 = noteValueCheck(fingeredNote2Read + fingeredOctave); + instrument.fingeredOctave = fingeredOctave; } lastFingering = fingeredNoteRead; - - return fingeredNote; + lastFingering2 = fingeredNote2Read; + lastOctave = fingeredOctave; } void noteOn(int fingeredNote, int pressureSensor, int initial_breath_value) { @@ -744,8 +777,39 @@ void noteOn(int fingeredNote, int pressureSensor, int initial_breath_value) { velocitySend = state.currentPreset->fixedVelocity; } - breath(); // send breath data - midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note + midiSendNoteOn(fingeredNote, velocitySend); // send Note Off message +} + +/** + * Send a new note message if it has changed +*/ +void changeNote(int fingeredNote, int activeNote, int fingeredNote2, int activeNote2) { + if (fingeredNote == activeNote && fingeredNote2 == activeNote2) { + // No change + return; + } + + // 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); + } + + if (fingeredNote2 != activeNote && fingeredNote2 != activeNote2) { + noteOn(fingeredNote2, instrument.breathSignal, 0); + } + + delayMicroseconds(2000); // delay for midi recording fix + + // send Note Off messages + if (activeNote != fingeredNote && activeNote != fingeredNote2) { + midiSendNoteOff(activeNote); + } + + if (activeNote2 != fingeredNote && activeNote2 != fingeredNote2) { + midiSendNoteOff(activeNote2); + } } void handleOffStateActions() { @@ -825,6 +889,7 @@ void handleCCs() { void readBreath() { int16_t breathSignal = constrain(readPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP + int16_t breathAltSignal = constrain(readAltPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP int16_t spikeSignal = constrain(readSpikePressure(), -SPIKE_HI_LIMIT, SPIKE_HI_LIMIT); instrument.breathMovingThrVal = constrain( breathBaselineFilter.input((breathSignal + instrument.breathZero) / 2), @@ -840,20 +905,24 @@ void readBreath() { } else { instrument.breathSignal = breathSignal + (spikeSignal > 0 ? spikeSignal * state.currentPreset->spikeOnFactor : spikeSignal * state.currentPreset->spikeOffFactor); } - instrument.breathAltSignal = constrain(readAltPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP + instrument.breathAltSignal = breathAltSignal; - Serial.print(">breathThr:"); - Serial.println(instrument.breathThrVal); + if (instrument.mode == MODE_DEBUG) { + Serial.print(">breath:"); + Serial.println(breathSignal); + Serial.print(">breathAlt:"); + Serial.println(breathAltSignal); + Serial.print(">Diff:"); + Serial.println(breathSignal - breathAltSignal); Serial.print(">breathMovingThr:"); Serial.println(instrument.breathMovingThrVal); Serial.print(">note:"); Serial.println(state.mainState); Serial.print(">spike:"); Serial.println(spikeSignal); - Serial.print(">breath:"); - Serial.println(breathSignal); Serial.print(">combo:"); Serial.println(instrument.breathSignal); + } } /** @@ -862,7 +931,6 @@ 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 - int fingeredNote = noteValueCheck(readSwitches() + readOctave()); if (state.mainState == NOTE_OFF) { handleOffStateActions(); if (instrument.breathSignal > instrument.breathMovingThrVal && state.mainState == NOTE_OFF) { @@ -876,9 +944,14 @@ void runStateMachine() { if ((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(fingeredNote, instrument.breathSignal, initial_breath_value); + noteOn(instrument.fingeredNote, instrument.breathSignal, initial_breath_value); + if (instrument.fingeredNote != instrument.fingeredNote2) { + noteOn(instrument.fingeredNote2, instrument.breathSignal, initial_breath_value); + } + breath(); // send breath data state.mainState = NOTE_ON; - instrument.activeNote = fingeredNote; + instrument.activeNote = instrument.fingeredNote; + instrument.activeNote2 = instrument.fingeredNote2; } } else { // Value fell below threshold before velocity sample delay time passed. Return to NOTE_OFF state @@ -888,38 +961,30 @@ void runStateMachine() { if (instrument.breathSignal < instrument.breathMovingThrVal) { // Value has fallen below threshold - turn the note off midiSendNoteOff(instrument.activeNote); // send Note Off message + if (instrument.activeNote != instrument.activeNote2) { + midiSendNoteOff(instrument.activeNote2); // send Note Off message + } instrument.breathSignal = 0; state.mainState = NOTE_OFF; } else { - if (fingeredNote != instrument.activeNote) { - // 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. - noteOn(fingeredNote, instrument.breathSignal, 0); - delayMicroseconds(2000); // delay for midi recording fix - midiSendNoteOff(instrument.activeNote); // send Note Off message - instrument.activeNote = fingeredNote; - } + changeNote(instrument.fingeredNote, instrument.activeNote, instrument.fingeredNote2, instrument.activeNote2); + instrument.activeNote = instrument.fingeredNote; + instrument.activeNote2 = instrument.fingeredNote2; } } } void setup() { - if (checkButtonState(DEBUG_CONFIG)) { - } - Serial.begin(9600); // debug - Serial.println("Debug Startup"); if (CrashReport) { + Serial.begin(9600); // debug + Serial.println("Debug Startup"); while (!Serial); // wait for serial monitor open Serial.print(CrashReport); } initHardware(); delay(100); // Make sure the inputs settle - Serial.println(buttonState()); bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET); - configManagementMode = checkButtonState(STARTUP_CONFIG); - testMode = checkButtonState(TEST_CONFIG); initDisplay(); // Start up display and show logo @@ -927,12 +992,6 @@ void setup() { displayError("CRASH WARNING"); } - // If going into config management mode, stop here before we even touch the EEPROM. - if (configManagementMode) { - configModeSetup(); - return; - } - // Read eeprom data into global vars readEEPROM(factoryReset, *state.calibration); updateFilters(*state.currentPreset); @@ -967,18 +1026,18 @@ void loop() { lastUpdate = time; // If in config mgmt loop, do that and nothing else - if (configManagementMode) { - configModeLoop(); + if (instrument.mode == MODE_CONFIG) { + configModeLoop(state); return; } - if (testMode) { - handleTestMode(); + if (instrument.mode == MODE_TEST) { + handleTestMode(state); return; } readBreath(); - + readSwitches(); runStateMachine(); handleCCs();