From 80c8ccd54bbb343d29e0156e4a8c02ed17f52042 Mon Sep 17 00:00:00 2001 From: Brian Hrebec Date: Tue, 20 Aug 2024 13:45:22 -0500 Subject: [PATCH] Sensor updates --- NuEVI/src/config.h | 17 +-- NuEVI/src/globals.h | 17 ++- NuEVI/src/hardware.cpp | 97 +++++++++++---- NuEVI/src/hardware.h | 4 +- NuEVI/src/icm.cpp | 2 + NuEVI/src/menu.cpp | 14 ++- NuEVI/src/settings.h | 20 ++-- NuEVI/src/xEVI.cpp | 259 +++++++++++++++++++++-------------------- 8 files changed, 251 insertions(+), 179 deletions(-) diff --git a/NuEVI/src/config.h b/NuEVI/src/config.h index 45b7202..f47f177 100644 --- a/NuEVI/src/config.h +++ b/NuEVI/src/config.h @@ -11,7 +11,9 @@ #define CCN_Port 5 // Controller number for portamento level #define CCN_PortOnOff 65// Controller number for portamento on/off #define START_NOTE 36 // set startNote to C (change this value in steps of 12 to start in other octaves) -#define FILTER_FREQ 10.0 +#define FILTER_FREQ 30.0 +#define SPIKE_FILTER_FREQ 150.0 // Detect fast changes in breath +#define ICM_FILTER_FREQ 2 #define BREATH_THR_MAX_BOOST 40.0 #define CAP_SENS_ABSOLUTE_MAX 1000 // For inverting capacitive sensors #define PRESSURE_SENS_MULTIPLIER 10 // Multiply pressure sens so it's not a float @@ -50,15 +52,16 @@ #define BREATH_HI_LIMIT 12000 #define BITE_LO_LIMIT 500 #define BITE_HI_LIMIT 1000 -#define PITCHB_LO_LIMIT -100 -#define PITCHB_HI_LIMIT 100 -#define EXTRA_LO_LIMIT -100 -#define EXTRA_HI_LIMIT 100 +#define PITCHB_LO_LIMIT -2000 +#define PITCHB_HI_LIMIT 2000 +#define EXTRA_LO_LIMIT -2000 +#define EXTRA_HI_LIMIT 2000 #define CTOUCH_LO_LIMIT 500 #define CTOUCH_HI_LIMIT 1000 -#define LEVER_LO_LIMIT -100 -#define LEVER_HI_LIMIT 100 +#define LEVER_LO_LIMIT -2000 +#define LEVER_HI_LIMIT 2000 #define SPIKE_LO_LIMIT 0 #define SPIKE_HI_LIMIT 200 +#define SLIDER_TOUCH_OFFSET 4 #endif diff --git a/NuEVI/src/globals.h b/NuEVI/src/globals.h index 3cf55ef..d86f1ed 100644 --- a/NuEVI/src/globals.h +++ b/NuEVI/src/globals.h @@ -60,6 +60,8 @@ enum ExtraControl : uint8_t { VIBRATO = 1, GLIDE = 2, CC = 3, + BEND = 4, + VIB_BEND = 5, }; enum PolySelect : uint8_t { @@ -109,8 +111,10 @@ struct instrument_state_t { 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 + byte pbVal = 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 + bool pbActive = false; // extra CC selection byte knobVals[4]; byte lastKnobVal; unsigned long lastKnobTime; @@ -130,19 +134,20 @@ struct instrument_state_t { // Calibration int16_t breathZero; // this gets auto calibrated in setup + int16_t breathAltZero; // this gets auto calibrated in setup int16_t breathThrVal; // this gets auto calibrated in setup int16_t breathMovingThrVal; + int16_t breathBaseline; // this gets auto calibrated in setup 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 breathAltZeroOffset; // this gets auto calibrated in setup + int16_t breathThrOffVal; // 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 sliderPBThr; + int16_t sliderLeverThr; + int16_t sliderExtraThr; }; extern const std::array curves; diff --git a/NuEVI/src/hardware.cpp b/NuEVI/src/hardware.cpp index 34e4511..d20d846 100644 --- a/NuEVI/src/hardware.cpp +++ b/NuEVI/src/hardware.cpp @@ -14,6 +14,9 @@ FilterOnePole rollFilter; FilterOnePole sliderFilterExtra; FilterOnePole sliderFilterPB; FilterOnePole sliderFilterLever; +float sliderMAExtra = 0.0; +float sliderMAPB = 0.0; +float sliderMALever = 0.0; Adafruit_MPR121 touchSensorKeys = Adafruit_MPR121(); Adafruit_MPR121 touchSensorRoller = Adafruit_MPR121(); @@ -49,13 +52,13 @@ void initHardware() { breathFilter.setFilter(LOWPASS, FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter breathAltFilter.setFilter(LOWPASS, FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter - spikeFilter.setFilter(HIGHPASS, 200, 0.0); // create a one pole (RC) lowpass filter - tiltFilter.setFilter(LOWPASS, 2, 0.0); // create a one pole (RC) lowpass filter - rollFilter.setFilter(LOWPASS, 2, 0.0); // create a one pole (RC) lowpass filter + spikeFilter.setFilter(HIGHPASS, SPIKE_FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter + tiltFilter.setFilter(LOWPASS, ICM_FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter + rollFilter.setFilter(LOWPASS, ICM_FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter icmSensor.begin_I2C(ICM20948_I2CADDR_DEFAULT, &MainI2CBus); - sliderFilterExtra.setFilter(LOWPASS, 1.0, 0.0); - sliderFilterLever.setFilter(LOWPASS, 1.0, 0.0); - sliderFilterPB.setFilter(LOWPASS, 1.0, 0.0); + sliderFilterExtra.setFilter(INTEGRATOR, 0.75, 0.0); + sliderFilterLever.setFilter(INTEGRATOR, 0.75, 0.0); + sliderFilterPB.setFilter(INTEGRATOR, 0.75, 0.0); ledStrip.begin(); @@ -76,16 +79,17 @@ void initHardware() { //touchSensorUtil.writeRegister(MPR121_CONFIG1, 0x3f); // default, 16uA charge current //touchSensorUtil.writeRegister(MPR121_CONFIG2, 0xE0); // 0.5uS encoding, 1ms period - if (!pressureSensorMain.begin(MPRLS_DEFAULT_ADDR, &MainI2CBus)) { + if (!pressureSensorMain.begin(MPRLS_DEFAULT_ADDR, &AuxI2CBus)) { displayError("Main pressure sensor error"); errorWait(); } - if (!pressureSensorAlt.begin(MPRLS_DEFAULT_ADDR, &AuxI2CBus)) { + if (!pressureSensorAlt.begin(MPRLS_DEFAULT_ADDR, &MainI2CBus)) { displayError("Alt pressure sensor error"); errorWait(); } + if (!icmSensor.begin_I2C(ICM20948_I2CADDR_DEFAULT, &MainI2CBus)) { displayError("ICM sensor error"); errorWait(); @@ -180,37 +184,61 @@ uint16_t utilTouched() { } int readSpikePressure() { - return spikeFilter.input(pressureSensorMain.readPressure()) * PRESSURE_SENS_MULTIPLIER; + return spikeFilter.output(); } int readPressure() { float p = pressureSensorMain.readPressure(); - return breathFilter.input(p) * PRESSURE_SENS_MULTIPLIER; + int r = breathFilter.input(p) * PRESSURE_SENS_MULTIPLIER; + spikeFilter.input(r); + return r; } int readAltPressure() { return breathAltFilter.input(pressureSensorAlt.readPressure()) * PRESSURE_SENS_MULTIPLIER; } -float readSlider(Slider id) { - float diff = 0.0; +int16_t readSlider(Slider id, int thr) { float filtered = 0.0; + float a = 0, b = 0; + FilterOnePole *filter; + switch (id) { case SLIDER_PITCH_BEND: - diff = touchSensorUtil.filteredData(pbSliderPin1) - touchSensorUtil.filteredData(pbSliderPin2); - filtered = sliderFilterPB.input(diff); - break; + a = touchSensorUtil.filteredData(pbSliderPin1); + b = touchSensorUtil.filteredData(pbSliderPin2); + filter = &sliderFilterPB; + break; case SLIDER_EXTRA: - diff = touchSensorRoller.filteredData(extraSliderPin1) - touchSensorRoller.filteredData(extraSliderPin2); - filtered = sliderFilterExtra.input(diff); - break; + a = touchSensorRoller.filteredData(extraSliderPin1); + b = touchSensorRoller.filteredData(extraSliderPin2); + filter = &sliderFilterExtra; + /* + Serial.print(">a:"); + Serial.println(a); + Serial.print(">b:"); + Serial.println(b); + Serial.print(">thr:"); + Serial.println(thr); + */ + break; case SLIDER_LEVER: - diff = touchSensorUtil.filteredData(leverSliderPin1) - touchSensorUtil.filteredData(leverSliderPin2); - filtered = sliderFilterLever.input(diff); - break; + a = touchSensorUtil.filteredData(leverSliderPin1); + b = touchSensorUtil.filteredData(leverSliderPin2); + filter = &sliderFilterLever; + break; + default: + return 0.0; + break; } - return filtered; + if ((a + b) <= (thr + thr - SLIDER_TOUCH_OFFSET)) { + filtered = filter->input(a - b); + } else { + return INT16_MIN; + } + + return filtered * 100; } icm_result_t readICM() { @@ -224,4 +252,27 @@ icm_result_t readICM() { tiltFilter.input(mag.magnetic.y), rollFilter.input(mag.magnetic.x), }; -} \ No newline at end of file +} + +int readRawSlider(Slider id) { + float a, b; + switch (id) { + case SLIDER_PITCH_BEND: + a = touchSensorUtil.filteredData(pbSliderPin1); + b = touchSensorUtil.filteredData(pbSliderPin2); + break; + case SLIDER_EXTRA: + a = touchSensorRoller.filteredData(extraSliderPin1); + b = touchSensorRoller.filteredData(extraSliderPin2); + break; + case SLIDER_LEVER: + a = touchSensorUtil.filteredData(leverSliderPin1); + b = touchSensorUtil.filteredData(leverSliderPin2); + break; + default: + return 0.0; + break; + } + + return (a + b) / 2; +} diff --git a/NuEVI/src/hardware.h b/NuEVI/src/hardware.h index 4493123..36f6e8b 100644 --- a/NuEVI/src/hardware.h +++ b/NuEVI/src/hardware.h @@ -35,7 +35,9 @@ uint8_t buttonState(); // return true if the given buttons are pressed int readKnob(uint8_t n); int readTouchKey(uint8_t n); int readTouchRoller(uint8_t n); -float readSlider(Slider id); +int readTouchUtil(uint8_t n); +int readRawSlider(Slider id); +int16_t readSlider(Slider id, int thr); uint16_t keysTouched(); uint16_t utilTouched(); int readPressure(); diff --git a/NuEVI/src/icm.cpp b/NuEVI/src/icm.cpp index c274902..342b312 100644 --- a/NuEVI/src/icm.cpp +++ b/NuEVI/src/icm.cpp @@ -3,10 +3,12 @@ void checkICM(state_t &state) { icm_result_t icmSignal = readICM(); + /* Serial.print(">roll: "); Serial.println(icmSignal.roll); Serial.print(">tilt: "); Serial.println(icmSignal.tilt); +*/ if (ExtraControl::CC == state.currentPreset->icmRollMode) { byte roll = mapConstrain(abs(icmSignal.roll), 0, 40, 127, 0); if (roll != state.instrument->rollCCVal) { diff --git a/NuEVI/src/menu.cpp b/NuEVI/src/menu.cpp index 1fbcca7..7c114f9 100644 --- a/NuEVI/src/menu.cpp +++ b/NuEVI/src/menu.cpp @@ -939,11 +939,9 @@ private: std::array adjustValues = { { {"BREATH", &instrument_state_t::breathSignal, &calibration_t::breathThrValOffset, &calibration_t::breathMaxValOffset, BREATH_LO_LIMIT, BREATH_HI_LIMIT, &instrument_state_t::breathZero}, - {"BR ALT", &instrument_state_t::breathAltSignal, &calibration_t::breathAltThrValOffset, &calibration_t::breathAltMaxValOffset, - BREATH_LO_LIMIT, BREATH_HI_LIMIT, &instrument_state_t::breathAltZero}, {"BITE",&instrument_state_t::biteSignal, &calibration_t::biteThrVal, &calibration_t::biteMaxVal, BITE_LO_LIMIT, BITE_HI_LIMIT, NULL}, {"PB",&instrument_state_t::pbSignal, &calibration_t::pbMinVal, &calibration_t::pbMaxVal, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL}, - {"PB DZ",&instrument_state_t::pbSignal, &calibration_t::pbDeadzone, &calibration_t::pbDeadzone, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL}, + {"PB Z/DZ",&instrument_state_t::pbSignal, &calibration_t::pbCenterVal, &calibration_t::pbDeadzone, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL}, {"EXTRA", &instrument_state_t::extraSignal, &calibration_t::extraMinVal, &calibration_t::extraMaxVal, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT, NULL}, {"LEVER", &instrument_state_t::leverSignal, &calibration_t::leverMinVal, &calibration_t::leverMaxVal, LEVER_LO_LIMIT, LEVER_HI_LIMIT, NULL}, {"TOUCH", &instrument_state_t::avgCTouchSignal, &calibration_t::ctouchThrVal, &calibration_t::ctouchThrVal, CTOUCH_LO_LIMIT, CTOUCH_HI_LIMIT, NULL}, @@ -960,7 +958,7 @@ CurveValueMenu<0, uint8_t> curveMenu("CURVE", &preset_t::breathCurve, 0, 12); PresetValueMenu<1, uint8_t> velSmpDlMenu("VEL DELAY", &preset_t::velSmpDl, 0, 30, true, { "OFF" }, "ms"); PresetValueMenu<1, uint8_t> velBiasMenu("VEL BOOST", &preset_t::velBias, 0, 30, true, { "OFF" }); PresetValueMenu<0, uint8_t> breathIntervalMenu("BR INTV", &preset_t::breathInterval, 0, 30, true, {}, "ms"); -PresetValueMenu<0, uint8_t> filterMenu("FILTER CT", &preset_t::breathFilterFreq, 1, 100, false, {}, "hz"); +PresetValueMenu<0, uint8_t> filterMenu("FILTER CT", &preset_t::breathFilterFreq, 1, 200, false, {}, "hz"); PresetValueMenu<1, uint8_t> spikeFilterMenu("FILTER TRG", &preset_t::spikeFilterFreq, 0, 100, false, {"OFF"}, "hz"); PresetValueMenu<0, int8_t> spikeOnFactorMenu("TRG ON MUL", &preset_t::spikeOnFactor, -25, 25, false, {}, "x"); PresetValueMenu<0, int8_t> spikeOffFactorMenu("TRG OFF MUL", &preset_t::spikeOffFactor, -25, 25, false, {}, "x"); @@ -1005,6 +1003,8 @@ 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" }); +PresetValueMenu<128, uint8_t> pbCCMenu("PB CC", &preset_t::pbCC, 0, 127, true, CC_NAMES); ChoiceMenu<4, ExtraControl> tiltModeMenu("TILT MODE", &preset_t::icmTiltMode, { {OFF, VIBRATO, GLIDE, CC} }, { "OFF", "VIBRATO", "GLIDE", "CC" }); PresetValueMenu<128, uint8_t> tiltCCMenu("TILT CC", &preset_t::icmTiltCC, 0, 127, true, CC_NAMES); ChoiceMenu<4, ExtraControl> rollModeMenu("ROLL MODE", &preset_t::icmRollMode, { {OFF, VIBRATO, GLIDE, CC} }, { "OFF", "VIBRATO", "GLIDE", "CC" }); @@ -1082,12 +1082,14 @@ std::array breathMenuEntries = { }; SubMenu<11> breathMenu("BR SETUP", breathMenuEntries); -const std::array controlMenuEntries = { +const std::array controlMenuEntries = { &fingeringMenu, &polyMenu, &rollerMenu, &biteCtlMenu, &biteCCMenu, + &pbCtlMenu, + &pbCCMenu, &leverCtlMenu, &leverCCMenu, &extraCtlMenu, @@ -1104,7 +1106,7 @@ const std::array controlMenuEntries = { &accelModeMenu, &accelCCMenu, }; -SubMenu<20> controlMenu("CTL SETUP", controlMenuEntries); +SubMenu<22> controlMenu("CTL SETUP", controlMenuEntries); const std::array vibratoMenuEntries = { &vibDepthMenu, diff --git a/NuEVI/src/settings.h b/NuEVI/src/settings.h index f1ff17e..3e2a299 100644 --- a/NuEVI/src/settings.h +++ b/NuEVI/src/settings.h @@ -5,7 +5,7 @@ #include "globals.h" -#define EEPROM_VERSION 4 +#define EEPROM_VERSION 5 #define EEPROM_VERSION_ADDR 0 #define SETTINGS_OFFSET 2 #define PRESET_MAX_SIZE 128 // Leave extra space for future settings @@ -26,14 +26,14 @@ struct calibration_t { int16_t breathAltMaxValOffset = 1500; int16_t biteThrVal = 850; int16_t biteMaxVal = 1000; - int16_t pbMinVal = -10; - int16_t pbMaxVal = 10; + int16_t pbMinVal = -1000; + int16_t pbMaxVal = 1000; int16_t pbCenterVal = 0; - int16_t pbDeadzone = 2; - int16_t leverMinVal = -10; - int16_t leverMaxVal = 10; - int16_t extraMinVal = -10; - int16_t extraMaxVal = 10; + int16_t pbDeadzone = 20; + int16_t leverMinVal = -1000; + int16_t leverMaxVal = 1000; + int16_t extraMinVal = -1000; + int16_t extraMaxVal = 1000; int16_t ctouchThrVal = 900; uint8_t _reserved[24]; }; @@ -95,8 +95,10 @@ struct preset_t { int8_t spikeOnFactor = 5; int8_t spikeOffFactor = 5; PolySelect polyMode = PolySelect::EHarmonizerOff; + ExtraControl pbControl = ExtraControl::VIB_BEND; + uint8_t pbCC = 12; // extra CC selection - uint8_t _reserved[82]; + uint8_t _reserved[80]; }; 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 3eb7564..8a31b17 100644 --- a/NuEVI/src/xEVI.cpp +++ b/NuEVI/src/xEVI.cpp @@ -221,7 +221,7 @@ int breath() { int breathCCval, breathCCvalFine; unsigned int breathCCvalHires; - breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383)); + breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383)); breathCCvalHires = breathCCFilter.input(breathCCvalHires); breathCCval = (breathCCvalHires >> 7) & 0x007F; breathCCvalFine = breathCCvalHires & 0x007F; @@ -252,24 +252,9 @@ int breath() { //************************************************************** -void pitch_bend() { - // handle input from pitchbend touchpads and - // on-pcb variable capacitor for vibrato. - static int oldpb = 0; +void vibrato(int calculatedPBdepth) { int vibMax; - int vibMaxBite; - int calculatedPBdepth; - byte pbTouched = 0; int vibRead = 0; - int vibReadBite = 0; - bool halfPitchBendKey = (state.currentPreset->pinkySetting == PBD) && instrument.pinkyKey; // hold pinky key for 1/2 pitchbend value - instrument.quarterToneTrigger = (state.currentPreset->pinkySetting == QTN) && instrument.pinkyKey; // pinky key for a quarter tone down using pitch bend (assuming PB range on synth is set to 2 semitones) - - calculatedPBdepth = pbDepthList[state.currentPreset->PBdepth]; - if (halfPitchBendKey) - calculatedPBdepth = calculatedPBdepth * 0.5; - - vibMax = vibMaxList[state.currentPreset->vibSens - 1]; float calculatedDepth = 0; if (state.currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) { @@ -278,36 +263,26 @@ void pitch_bend() { calculatedDepth = (0 - calculatedPBdepth * vibDepth[state.currentPreset->vibratoDepth]); } - if (ExtraControl::VIBRATO == state.currentPreset->biteControl) { // bite vibrato - vibMaxBite = vibMaxBiteList[state.currentPreset->vibSens - 1]; - vibReadBite = instrument.biteSignal; + vibMax = vibMaxList[state.currentPreset->vibSens - 1]; - if (vibReadBite > instrument.vibThrBite) { - instrument.vibSignal = (instrument.vibSignal + mapConstrain( - vibReadBite, (instrument.vibZeroBite - vibMaxBite), instrument.vibThrBite, calculatedDepth, 0) - ) / 2; - } else if (vibReadBite < instrument.vibThrBiteLo) { - instrument.vibSignal = (instrument.vibSignal + mapConstrain( - vibReadBite, (instrument.vibZeroBite + vibMaxBite), instrument.vibThrBite, calculatedDepth, 0) - ) / 2; - } else { - instrument.vibSignal = instrument.vibSignal / 2; - } + if (ExtraControl::VIBRATO == state.currentPreset->biteControl) { // bite vibrato + vibRead = instrument.biteSignal; + } else if (ExtraControl::VIBRATO == state.currentPreset->leverControl) { // lever vibrato + vibRead = instrument.leverSignal; + } else if (ExtraControl::VIBRATO == state.currentPreset->extraControl) { // lever vibrato + vibRead = instrument.extraSignal; + } else if (ExtraControl::VIBRATO == state.currentPreset->pbControl) { // lever vibrato + vibRead = instrument.pbSignal; + } else if (ExtraControl::VIB_BEND == state.currentPreset->pbControl && !instrument.pbActive) { // lever vibrato + vibRead = instrument.pbSignal; } - if (ExtraControl::VIBRATO == state.currentPreset->leverControl) { // lever vibrato - vibRead = instrument.leverSignal; - if (vibRead > instrument.vibThr) { - instrument.vibSignal = (instrument.vibSignal + - mapConstrain(vibRead, (instrument.vibZero - vibMax), instrument.vibThr, calculatedDepth, 0) - ) / 2; - } else if (vibRead < instrument.vibThrLo) { - instrument.vibSignal = (instrument.vibSignal + - mapConstrain(vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0) - ) / 2; - } else { - instrument.vibSignal = instrument.vibSignal / 2; - } + if (vibRead < instrument.vibThrLo) { + instrument.vibSignal = (instrument.vibSignal + mapConstrain( + vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0) + ) / 2; + } else { + instrument.vibSignal = instrument.vibSignal / 2; } switch (state.currentPreset->vibRetn) { // moving baseline @@ -316,40 +291,59 @@ void pitch_bend() { break; case 1: instrument.vibZero = instrument.vibZero * 0.95 + vibRead * 0.05; - instrument.vibZeroBite = instrument.vibZeroBite * 0.95 + vibReadBite * 0.05; break; case 2: instrument.vibZero = instrument.vibZero * 0.9 + vibRead * 0.1; - instrument.vibZeroBite = instrument.vibZeroBite * 0.9 + vibReadBite * 0.1; break; case 3: instrument.vibZero = instrument.vibZero * 0.8 + vibRead * 0.2; - instrument.vibZeroBite = instrument.vibZeroBite * 0.8 + vibReadBite * 0.2; break; case 4: instrument.vibZero = instrument.vibZero * 0.6 + vibRead * 0.4; - instrument.vibZeroBite = instrument.vibZeroBite * 0.6 + vibReadBite * 0.4; } instrument.vibThr = instrument.vibZero + state.currentPreset->vibSquelch; instrument.vibThrLo = instrument.vibZero - state.currentPreset->vibSquelch; - instrument.vibThrBite = instrument.vibZeroBite + state.currentPreset->vibSquelch; - instrument.vibThrBiteLo = instrument.vibZeroBite - state.currentPreset->vibSquelch; +} + +void pitch_bend() { + // handle input from pitchbend touchpads and + // on-pcb variable capacitor for vibrato. + static int oldpb = 0; + int calculatedPBdepth; + bool halfPitchBendKey = (state.currentPreset->pinkySetting == PBD) && instrument.pinkyKey; // hold pinky key for 1/2 pitchbend value + instrument.quarterToneTrigger = (state.currentPreset->pinkySetting == QTN) && instrument.pinkyKey; // pinky key for a quarter tone down using pitch bend (assuming PB range on synth is set to 2 semitones) + + calculatedPBdepth = pbDepthList[state.currentPreset->PBdepth]; + if (halfPitchBendKey) + calculatedPBdepth = calculatedPBdepth * 0.5; + + instrument.pbActive = false; + + if (ExtraControl::BEND == state.currentPreset->pbControl || ExtraControl::VIB_BEND == state.currentPreset->pbControl) { + // Only activate PB if we're outside the deadzone + if ( + (instrument.pbSignal > state.calibration->pbCenterVal + state.calibration->pbDeadzone || + instrument.pbSignal < state.calibration->pbCenterVal - state.calibration->pbDeadzone) + && instrument.pbSignal != INT16_MIN) { + instrument.pbActive = true; + } + } + + vibrato(calculatedPBdepth); // PB calculation - int pbPos = mapConstrain(instrument.pbSignal, calibration.pbCenterVal, calibration.pbMaxVal, calculatedPBdepth, 0); - int pbNeg = mapConstrain(instrument.pbSignal, calibration.pbCenterVal, calibration.pbMinVal, calculatedPBdepth, 0); + int pbPos = mapConstrain(instrument.pbSignal, calibration.pbCenterVal + calibration.pbDeadzone, calibration.pbMaxVal, 0, calculatedPBdepth); + int pbNeg = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbCenterVal - calibration.pbDeadzone, calculatedPBdepth, 0); int pbSum = 8193 + pbPos - pbNeg; int pbDif = abs(pbPos - pbNeg); - if ((pbPos > calibration.pbDeadzone || pbNeg < calibration.pbDeadzone) && state.currentPreset->PBdepth) { + if (instrument.pbActive) { if (pbDif < 10) { instrument.pitchBend = 8192; } else { instrument.pitchBend = instrument.pitchBend * 0.6 + 0.4 * pbSum; } - pbTouched = 1; - } - if (!pbTouched) { + } else { instrument.pitchBend = instrument.pitchBend * 0.6 + 8192 * 0.4; // released, so smooth your way back to zero if ((instrument.pitchBend > 8187) && (instrument.pitchBend < 8197)) instrument.pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing @@ -390,9 +384,8 @@ void portamento_() { if (ExtraControl::GLIDE == state.currentPreset->leverControl) { // Portamento is controlled with thumb lever - // FIXME: Fix this for new lever signal - if (((3000 - instrument.leverSignal) >= calibration.leverMinVal)) { // if we are enabled and over the threshold, send portamento - portSumCC += mapConstrain((3000 - instrument.leverSignal), calibration.leverMinVal, calibration.leverMaxVal, 0, state.currentPreset->portamentoLimit); + if (instrument.leverSignal >= calibration.leverMinVal) { // if we are enabled and over the threshold, send portamento + portSumCC += mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, state.currentPreset->portamentoLimit); } } @@ -402,7 +395,7 @@ void portamento_() { //*********************************************************** void sendCC() { - int biteVal = 0; + int biteVal = instrument.biteSignal; if (ExtraControl::CC == state.currentPreset->biteControl) { if (instrument.biteSignal >= calibration.biteThrVal) { // we are over the threshold, calculate CC value biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127); @@ -414,10 +407,9 @@ void sendCC() { instrument.biteVal = biteVal; } - int extraVal = 0; + int extraVal = instrument.extraVal; if (ExtraControl::CC == state.currentPreset->extraControl) { - // FIXME: Fix this for new extra signal - if (instrument.extraSignal >= calibration.extraMinVal) { // we are over the threshold, calculate CC value + if (instrument.extraSignal != INT16_MIN) { // we are over the threshold, calculate CC value extraVal = mapConstrain(instrument.extraSignal, calibration.extraMinVal, calibration.extraMaxVal, 0, 127); } @@ -427,6 +419,30 @@ void sendCC() { instrument.extraVal = extraVal; } + int leverVal = instrument.leverVal; + if (ExtraControl::CC == state.currentPreset->leverControl) { + if (instrument.leverSignal != INT16_MIN) { // we are over the threshold, calculate CC value + leverVal = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, 127); + } + + if (leverVal != instrument.leverVal) { + midiSendControlChange(state.currentPreset->leverCC, leverVal); + } + instrument.leverVal = leverVal; + } + + int pbVal = instrument.pbVal; + if (ExtraControl::CC == state.currentPreset->pbControl) { + if (instrument.pbSignal != INT16_MIN) { // we are over the threshold, calculate CC value + pbVal = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbMaxVal, 0, 127); + } + + if (pbVal != instrument.pbVal) { + midiSendControlChange(state.currentPreset->pbCC, pbVal); + } + instrument.pbVal = pbVal; + } + if (!inMenu()) { for (int i = 0; i < 4; i++) { byte val = constrain((int)state.instrument->knobVals[i] + readKnob(i), 0, 127); @@ -456,31 +472,34 @@ void sendCC() { // Re-zero floating calibration values void rezero() { + instrument.vibZero = 0; instrument.vibThr = instrument.vibZero + state.currentPreset->vibSquelch; instrument.vibThrLo = instrument.vibZero - state.currentPreset->vibSquelch; - instrument.vibThrBite = instrument.vibZeroBite + state.currentPreset->vibSquelch; - instrument.vibThrBiteLo = instrument.vibZeroBite - state.currentPreset->vibSquelch; instrument.breathThrVal = instrument.breathZero + calibration.breathThrValOffset; + instrument.breathBaseline = instrument.breathZero + calibration.breathThrValOffset; + instrument.breathThrOffVal = instrument.breathZero + calibration.breathThrValOffset / 2; instrument.breathMaxVal = instrument.breathThrVal + calibration.breathMaxValOffset; - instrument.breathAltThrVal = instrument.breathAltZero + calibration.breathAltThrValOffset; - instrument.breathAltMaxVal = instrument.breathAltThrVal + calibration.breathAltMaxValOffset; + instrument.breathAltZeroOffset = instrument.breathZero - instrument.breathAltZero; } void autoCal() { - instrument.vibZero = instrument.vibZeroBite = 0; long int bZero = 0; long int bAltZero = 0; + long int bLeverTouchZero = 0, bExtraTouchZero = 0, bPBTouchZero = 0; for (int i = 1; i <= CALIBRATE_SAMPLE_COUNT; ++i) { bZero += readPressure(); bAltZero += readAltPressure(); - instrument.vibZeroBite += readTouchRoller(bitePin); + bLeverTouchZero += readRawSlider(Slider::SLIDER_LEVER); + bExtraTouchZero += readRawSlider(Slider::SLIDER_EXTRA); + bPBTouchZero += readRawSlider(Slider::SLIDER_PITCH_BEND); } instrument.breathZero = bZero / CALIBRATE_SAMPLE_COUNT; - instrument.breathAltZero = bAltZero / CALIBRATE_SAMPLE_COUNT; - instrument.vibZero /= CALIBRATE_SAMPLE_COUNT; - instrument.vibZeroBite /= CALIBRATE_SAMPLE_COUNT; + instrument.breathAltZero = (bAltZero / CALIBRATE_SAMPLE_COUNT); + instrument.sliderPBThr = bPBTouchZero /= CALIBRATE_SAMPLE_COUNT; + instrument.sliderExtraThr = bExtraTouchZero /= CALIBRATE_SAMPLE_COUNT; + instrument.sliderLeverThr = bLeverTouchZero /= CALIBRATE_SAMPLE_COUNT; rezero(); } @@ -500,26 +519,6 @@ void fullAutoCal() { calibration.biteThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT); calibration.biteMaxVal = constrain(calRead + 300, BITE_LO_LIMIT, BITE_HI_LIMIT); -/* - // Lever - calRead = readTouchRoller(leverPin); - calibration.leverThrVal = constrain(calRead + 100, LEVER_LO_LIMIT, LEVER_HI_LIMIT); - calibration.leverMaxVal = constrain(calRead + 300, LEVER_LO_LIMIT, LEVER_HI_LIMIT); - - // Extra - calRead = readTouchRoller(extraPin); - calibration.extraThrVal = constrain(calRead + 100, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT); - calibration.extraMaxVal = constrain(calRead + 300, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT); - - // PB - calRead = readTouchRoller(pbDnPin); - calibration.pbDnThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT); - calibration.pbDnMaxVal = constrain(calRead + 300, BITE_LO_LIMIT, BITE_HI_LIMIT); - - calRead = readTouchRoller(pbUpPin); - calibration.pbUpThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT); - calibration.pbUpMaxVal = constrain(calRead + 300, BITE_LO_LIMIT, BITE_HI_LIMIT); -*/ // Touch sensors calRead = CTOUCH_HI_LIMIT; for (byte i = 0; i < 12; i++) { @@ -769,15 +768,17 @@ void readSwitches() { 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.breathThrVal, instrument.breathMaxVal); + 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.breathThrVal, instrument.breathMaxVal, 0, 16383)); + 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; } + Serial.print(">velocity:"); + Serial.println(velocitySend); midiSendNoteOn(fingeredNote, velocitySend); // send Note Off message } @@ -844,9 +845,9 @@ void initState() { */ void readUtil() { instrument.biteSignal = readTouchRoller(bitePin); - instrument.pbSignal = readSlider(SLIDER_PITCH_BEND); - instrument.leverSignal = readSlider(SLIDER_LEVER); - instrument.extraSignal = readSlider(SLIDER_EXTRA); + instrument.pbSignal = readSlider(Slider::SLIDER_PITCH_BEND, instrument.sliderPBThr); + instrument.leverSignal = readSlider(Slider::SLIDER_LEVER, instrument.sliderLeverThr); + instrument.extraSignal = readSlider(Slider::SLIDER_EXTRA, instrument.sliderExtraThr); } /** @@ -866,19 +867,16 @@ void handleCCs() { } if (currentTime - ccSendTime > CC_INTERVAL_PRIMARY) { // deal with Pitch Bend, Modulation, etc. - readUtil(); pitch_bend(); sendCC(); checkICM(state); ccSendTime = currentTime; } if (currentTime - ccSendTime2 > CC_INTERVAL_PORT) { - readUtil(); portamento_(); ccSendTime2 = currentTime; } if (currentTime - ccSendTime3 > CC_INTERVAL_OTHER) { - readUtil(); updateSensorLEDs(*state.instrument); ccSendTime3 = currentTime; } @@ -889,32 +887,36 @@ 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 + // Get the pressure sensor reading + int16_t breathSignal = constrain(readPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); + int16_t breathAltSignal = constrain(readAltPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT) + instrument.breathAltZeroOffset; int16_t spikeSignal = constrain(readSpikePressure(), -SPIKE_HI_LIMIT, SPIKE_HI_LIMIT); - instrument.breathMovingThrVal = constrain( - breathBaselineFilter.input((breathSignal + instrument.breathZero) / 2), - instrument.breathThrVal, - instrument.breathMaxVal - ); - + int16_t diffSignal = breathAltSignal - breathSignal; + int16_t halfOffset = state.calibration->breathThrValOffset / 2; if (state.currentPreset->breathMode == BREATH_ACC || state.currentPreset->breathMode == BREATH_ACC_AT) { int delta = breathSignal - instrument.breathZero; - if (abs(delta) > state.calibration->breathAltThrValOffset) { + if (abs(delta) > state.calibration->breathThrValOffset) { instrument.breathSignal = constrain(instrument.breathSignal + delta / 15, instrument.breathZero, instrument.breathMaxVal); } } else { - instrument.breathSignal = breathSignal + (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; + instrument.breathMovingThrVal = constrain( + breathBaselineFilter.input(instrument.breathBaseline + (instrument.breathSignal - instrument.breathBaseline) * .75), + instrument.breathZero + halfOffset, + instrument.breathMaxVal - halfOffset + ); + + instrument.breathThrVal = instrument.breathMovingThrVal + halfOffset; + instrument.breathThrOffVal = instrument.breathMovingThrVal - halfOffset; + 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.println(diffSignal); Serial.print(">breathMovingThr:"); Serial.println(instrument.breathMovingThrVal); Serial.print(">note:"); @@ -923,6 +925,12 @@ void readBreath() { Serial.println(spikeSignal); Serial.print(">combo:"); Serial.println(instrument.breathSignal); + Serial.print(">zero:"); + Serial.println(instrument.breathZero); + Serial.print(">thr:"); + Serial.println(instrument.breathThrVal); + Serial.print(">off:"); + Serial.println(instrument.breathThrOffVal); } } @@ -934,7 +942,7 @@ void runStateMachine() { static int initial_breath_value = 0; // The breath value at the time we observed the transition if (state.mainState == NOTE_OFF) { handleOffStateActions(); - if (instrument.breathSignal > instrument.breathMovingThrVal && state.mainState == NOTE_OFF) { + if (instrument.breathSignal > instrument.breathThrVal && 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(); @@ -945,21 +953,21 @@ 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(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 = instrument.fingeredNote; - instrument.activeNote2 = instrument.fingeredNote2; + 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 = instrument.fingeredNote; + instrument.activeNote2 = instrument.fingeredNote2; } } else { // Value fell below threshold before velocity sample delay time passed. Return to NOTE_OFF state state.mainState = NOTE_OFF; } } else if (state.mainState == NOTE_ON) { - if (instrument.breathSignal < instrument.breathMovingThrVal) { + if (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) { @@ -1019,12 +1027,8 @@ void setup() { //_______________________________________________________________________________________________ MAIN LOOP void loop() { - static unsigned long lastUpdate = millis(); static unsigned long pixelUpdateTime = 0; static const unsigned long pixelUpdateInterval = 80; - unsigned long time = millis(); - unsigned long deltaTime = time - lastUpdate; - lastUpdate = time; // If in config mgmt loop, do that and nothing else if (instrument.mode == MODE_CONFIG) { @@ -1039,6 +1043,7 @@ void loop() { readBreath(); readSwitches(); + readUtil(); runStateMachine(); handleCCs();