Fixes; additional input filtering

This commit is contained in:
Brian Hrebec 2023-08-30 19:49:22 -05:00
parent 209959e2de
commit 7740c09375
11 changed files with 802 additions and 513 deletions

View file

@ -11,7 +11,7 @@
#define CCN_Port 5 // Controller number for portamento level #define CCN_Port 5 // Controller number for portamento level
#define CCN_PortOnOff 65// Controller number for portamento on/off #define CCN_PortOnOff 65// Controller number for portamento on/off
#define START_NOTE 24 // set startNote to C (change this value in steps of 12 to start in other octaves) #define START_NOTE 24 // set startNote to C (change this value in steps of 12 to start in other octaves)
#define FILTER_FREQ 30.0 #define FILTER_FREQ 10.0
#define CAP_SENS_ABSOLUTE_MAX 1000 // For inverting capacitive sensors #define CAP_SENS_ABSOLUTE_MAX 1000 // For inverting capacitive sensors
#define PRESSURE_SENS_MULTIPLIER 10 // Multiply pressure sens so it's not a float #define PRESSURE_SENS_MULTIPLIER 10 // Multiply pressure sens so it's not a float
#define CALIBRATE_SAMPLE_COUNT 4 #define CALIBRATE_SAMPLE_COUNT 4
@ -29,6 +29,8 @@
#define BTN_VAL1 0x2 #define BTN_VAL1 0x2
#define BTN_VAL2 0x4 #define BTN_VAL2 0x4
#define BTN_PRESET 0x8 #define BTN_PRESET 0x8
#define KNOB_CURVE_THRESHOLD 100
#define KNOB_CURVE_MULTIPLIER 5
// Send breath CC data no more than every CC_BREATH_INTERVAL // Send breath CC data no more than every CC_BREATH_INTERVAL
// milliseconds // milliseconds
@ -42,17 +44,19 @@
#define maxSamplesNum 120 #define maxSamplesNum 120
#define BREATH_LO_LIMIT 8000 #define BREATH_LO_LIMIT 9000
#define BREATH_HI_LIMIT 10000 #define BREATH_HI_LIMIT 12000
#define BITE_LO_LIMIT 0 #define BITE_LO_LIMIT 500
#define BITE_HI_LIMIT 1000 #define BITE_HI_LIMIT 1000
#define PITCHB_LO_LIMIT 0 #define PITCHB_LO_LIMIT 500
#define PITCHB_HI_LIMIT 1000 #define PITCHB_HI_LIMIT 1000
#define EXTRA_LO_LIMIT 0 #define EXTRA_LO_LIMIT 500
#define EXTRA_HI_LIMIT 1000 #define EXTRA_HI_LIMIT 1000
#define CTOUCH_LO_LIMIT 0 #define CTOUCH_LO_LIMIT 500
#define CTOUCH_HI_LIMIT 1000 #define CTOUCH_HI_LIMIT 1000
#define LEVER_LO_LIMIT 0 #define LEVER_LO_LIMIT 500
#define LEVER_HI_LIMIT 1000 #define LEVER_HI_LIMIT 1000
#define SPIKE_LO_LIMIT 0
#define SPIKE_HI_LIMIT 100
#endif #endif

View file

@ -18,6 +18,9 @@
// A note is sounding // A note is sounding
#define NOTE_ON 3 #define NOTE_ON 3
// We turned the note off due to a HF spike
#define SPIKE_HOLD 4
enum PinkyMode : uint8_t { enum PinkyMode : uint8_t {
PBD = 12, PBD = 12,
GLD = 25, GLD = 25,
@ -86,6 +89,7 @@ struct instrument_state_t {
// Raw sensor signals // Raw sensor signals
int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value
int16_t breathAltSignal = 0; int16_t breathAltSignal = 0;
int16_t spikeSignal = 0; // breath level (smoothed) not mapped to CC value
int16_t biteSignal = 0; // capacitance data from bite sensor, for midi cc and threshold checks int16_t biteSignal = 0; // capacitance data from bite sensor, for midi cc and threshold checks
int16_t leverSignal = 0; int16_t leverSignal = 0;
int16_t pbUpSignal = 0; int16_t pbUpSignal = 0;
@ -130,6 +134,10 @@ struct instrument_state_t {
extern const std::array<const unsigned short*, 13> curves; extern const std::array<const unsigned short*, 13> curves;
extern const unsigned short curveIn[]; extern const unsigned short curveIn[];
// Re-zero floating calibration values
extern void rezero();
extern void fullAutoCal();
extern unsigned int multiMap(unsigned short val, const unsigned short * _in, const unsigned short * _out, uint8_t size); extern unsigned int multiMap(unsigned short val, const unsigned short * _in, const unsigned short * _out, uint8_t size);
#define mapConstrain(val, in_lo, in_hi, out_lo, out_hi) map(constrain(val, in_lo, in_hi), in_lo, in_hi, out_lo, out_hi) #define mapConstrain(val, in_lo, in_hi, out_lo, out_hi) map(constrain(val, in_lo, in_hi), in_lo, in_hi, out_lo, out_hi)

View file

@ -8,6 +8,7 @@
#include "FilterOnePole.h" // for the breath signal low-pass filtering, from https://github.com/JonHub/Filters #include "FilterOnePole.h" // for the breath signal low-pass filtering, from https://github.com/JonHub/Filters
FilterOnePole breathFilter; FilterOnePole breathFilter;
FilterOnePole breathAltFilter; FilterOnePole breathAltFilter;
FilterOnePole spikeFilter;
Adafruit_MPR121 touchSensorKeys = Adafruit_MPR121(); Adafruit_MPR121 touchSensorKeys = Adafruit_MPR121();
Adafruit_MPR121 touchSensorUtil = Adafruit_MPR121(); Adafruit_MPR121 touchSensorUtil = Adafruit_MPR121();
@ -42,6 +43,7 @@ void initHardware() {
breathFilter.setFilter(LOWPASS, FILTER_FREQ, 0.0); // create a one pole (RC) lowpass filter 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 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
ledStrip.begin(); ledStrip.begin();
@ -71,6 +73,12 @@ void initHardware() {
} }
} }
void updateFilters(preset_t &preset) {
breathFilter.setFrequency(preset.breathFilterFreq);
breathAltFilter.setFrequency(preset.breathFilterFreq);
spikeFilter.setFrequency(preset.spikeFilterFreq);
}
/* /*
Return true if the given button bitmask is pressed Return true if the given button bitmask is pressed
*/ */
@ -107,16 +115,27 @@ void errorWait() {
* Read the knob value accounting for 4x precision - NB this might not work on other kinds of encoder * Read the knob value accounting for 4x precision - NB this might not work on other kinds of encoder
*/ */
int readKnob(uint8_t n) { int readKnob(uint8_t n) {
// Make it temporarily more sensitive when switching directions, the hardware loses a tick frequently
static int lastOut = 0;
static unsigned long lastOutTime = 0;
int out = 0; int out = 0;
int32_t val = knobs[n].read(); int32_t val = knobs[n].read();
if (val > 4) { if (val > (lastOut < 0) ? 3 : 4) {
out = val / 4; out = val / 4;
} else if (val < -4) { } else if (val < (lastOut > 0) ? -3 : -4) {
out = val / 4; out = val / 4;
} }
if (out != 0) { if (out != 0) {
knobs[n].write(0); unsigned long time = millis();
knobs[n].write(val - (out * 4));
lastOut = out;
if (time - lastOutTime < KNOB_CURVE_THRESHOLD) {
out *= KNOB_CURVE_MULTIPLIER;
}
lastOutTime = time;
} }
return out; return out;
} }
@ -136,8 +155,15 @@ uint16_t utilTouched() {
return touchSensorKeys.touched(); return touchSensorKeys.touched();
} }
int readSpikePressure() {
return spikeFilter.input(pressureSensorMain.readPressure()) * PRESSURE_SENS_MULTIPLIER;
}
int readPressure() { int readPressure() {
return breathFilter.input(pressureSensorMain.readPressure()) * PRESSURE_SENS_MULTIPLIER; float p = pressureSensorMain.readPressure();
Serial.print(">raw:");
Serial.println(p * PRESSURE_SENS_MULTIPLIER);
return breathFilter.input(p) * PRESSURE_SENS_MULTIPLIER;
} }
int readAltPressure() { int readAltPressure() {

View file

@ -7,6 +7,7 @@
#include <WS2812Serial.h> #include <WS2812Serial.h>
#include <Adafruit_ICM20948.h> #include <Adafruit_ICM20948.h>
#include <stdint.h> #include <stdint.h>
#include "settings.h"
// Hardware sensor definitions // Hardware sensor definitions
// TODO: remove these // TODO: remove these
@ -16,6 +17,7 @@ extern Adafruit_Sensor *accelSensor;
extern Adafruit_ICM20948 icmSensor; extern Adafruit_ICM20948 icmSensor;
void initHardware(); void initHardware();
void updateFilters(preset_t &preset);
bool checkButtonState(uint8_t mask); // return true if the given buttons are pressed bool checkButtonState(uint8_t mask); // return true if the given buttons are pressed
uint8_t buttonState(); // return true if the given buttons are pressed uint8_t buttonState(); // return true if the given buttons are pressed
int readKnob(uint8_t n); int readKnob(uint8_t n);
@ -25,6 +27,7 @@ uint16_t keysTouched();
uint16_t utilTouched(); uint16_t utilTouched();
int readPressure(); int readPressure();
int readAltPressure(); int readAltPressure();
int readSpikePressure();
// xEVI hardware setup // xEVI hardware setup
@ -92,6 +95,6 @@ int readAltPressure();
#define extraPin 2 #define extraPin 2
#define pbUpPin 4 #define pbUpPin 4
#define pbDnPin 5 #define pbDnPin 5
#define vibratoPin 3 #define leverPin 3
#endif #endif

View file

@ -43,7 +43,7 @@ void statusLedBlink() {
statusLedFlash(300); statusLedFlash(300);
} }
void updateSensorLEDs(instrument_state_t *state) { void updateSensorLEDs(instrument_state_t &state) {
ledHalfMeter(1, state->breathCCVal, 0x00FF00); ledHalfMeter(1, state.breathCCVal, 0x00FF00);
ledQuarterMeter(3, state->biteVal, 0x0000FF); ledQuarterMeter(3, state.biteVal, 0x0000FF);
} }

View file

@ -1,13 +1,15 @@
#ifndef __LED_H #ifndef __LED_H
#define __LED_H #define __LED_H
#include "globals.h"
void statusLedOn(); void statusLedOn();
void statusLedOff(); void statusLedOff();
void statusLedFlip(); void statusLedFlip();
void statusLed(bool state); void statusLed(bool state);
void statusLedFlash(uint16_t delayTime); void statusLedFlash(uint16_t delayTime);
void statusLedBlink(); void statusLedBlink();
void updateSensorLEDs(); void updateSensorLEDs(instrument_state_t &state);
void ledMeter(byte indicatedValue); void ledMeter(byte indicatedValue);
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@
#define MENU_HEADER_OFFSET 12 #define MENU_HEADER_OFFSET 12
#define MENU_NUM_ROWS 6 #define MENU_NUM_ROWS 6
#define ADJUST_NUM_ROWS 3 #define ADJUST_NUM_ROWS 3
#define ADJUST_ROW_HEIGHT 21 #define ADJUST_ROW_HEIGHT 22
extern const unsigned long debounceDelay; // the debounce time; increase if the output flickers extern const unsigned long debounceDelay; // the debounce time; increase if the output flickers
extern const unsigned long buttonRepeatInterval; extern const unsigned long buttonRepeatInterval;
@ -33,12 +33,12 @@ class MenuScreen {
public: public:
MenuScreen() {}; MenuScreen() {};
virtual const char *title() { return ""; }; virtual const char *title() { return ""; };
virtual bool update(state_t &state, InputState &input, bool redraw) = 0; virtual void update(state_t &state, InputState &input, bool redraw) = 0;
virtual ~MenuScreen() {}; virtual ~MenuScreen() {};
}; };
void initDisplay(); void initDisplay();
void displayOff(); void displayOff(state_t &state);
void showVersion(); void showVersion();
void displayError(const char *error); void displayError(const char *error);
void handleMenu(state_t &state, bool draw); void handleMenu(state_t &state, bool draw);

View file

@ -41,7 +41,7 @@ void readCalibration(calibration_t &calibration) {
} }
void writePreset(uint8_t preset) { void writePreset(uint8_t preset) {
EEPROM.put(SETTINGS_OFFSET + preset * sizeof(preset_t), presets[preset]); EEPROM.put(SETTINGS_OFFSET + CALIBRATION_MAX_SIZE + preset * sizeof(preset_t), presets[preset]);
} }
void writePresets() { void writePresets() {
@ -326,16 +326,27 @@ void readEEPROM(const bool factoryReset, calibration_t &calibration) {
// blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten. // blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten.
if (factoryReset || settings_version == 0xffffu) { if (factoryReset || settings_version == 0xffffu) {
settings_version = 0; settings_version = 0;
preset_t defaultPreset;
calibration_t defaultCalibration;
for (int i = 0; i < PRESET_COUNT; i++) {
presets[i] = defaultPreset;
}
calibration = defaultCalibration;
} else { } else {
readPresets(); readPresets();
readCalibration(calibration); readCalibration(calibration);
} }
if(settings_version != EEPROM_VERSION) { if(settings_version != EEPROM_VERSION) {
// Add default settings here for (int i = 0; i < PRESET_COUNT; i++) {
if (settings_version < 3) {
presets[i].breathFilterFreq = 20;
presets[i].spikeFilterFreq = 20;
}
}
writePresets(); writePresets();
writeCalibration(); writeCalibration(calibration);
writeInt(EEPROM_VERSION_ADDR, EEPROM_VERSION); writeInt(EEPROM_VERSION_ADDR, EEPROM_VERSION);
} }
} }

View file

@ -5,7 +5,7 @@
#include "globals.h" #include "globals.h"
#define EEPROM_VERSION 1 #define EEPROM_VERSION 3
#define EEPROM_VERSION_ADDR 0 #define EEPROM_VERSION_ADDR 0
#define SETTINGS_OFFSET 2 #define SETTINGS_OFFSET 2
#define PRESET_MAX_SIZE 128 // Leave extra space for future settings #define PRESET_MAX_SIZE 128 // Leave extra space for future settings
@ -24,18 +24,19 @@ struct calibration_t {
int16_t breathMaxValOffset = 1500; int16_t breathMaxValOffset = 1500;
int16_t breathAltThrValOffset = 5; int16_t breathAltThrValOffset = 5;
int16_t breathAltMaxValOffset = 1500; int16_t breathAltMaxValOffset = 1500;
int16_t biteThrVal = 50; int16_t biteThrVal = 850;
int16_t biteMaxVal = 150; int16_t biteMaxVal = 1000;
int16_t pbDnThrVal = 50; int16_t pbDnThrVal = 850;
int16_t pbDnMaxVal = 150; int16_t pbDnMaxVal = 1000;
int16_t pbUpThrVal = 50; int16_t pbUpThrVal = 850;
int16_t pbUpMaxVal = 150; int16_t pbUpMaxVal = 1000;
int16_t leverThrVal = 50; int16_t leverThrVal = 850;
int16_t leverMaxVal = 150; int16_t leverMaxVal = 1000;
int16_t extraThrVal = 50; int16_t extraThrVal = 850;
int16_t extraMaxVal = 150; int16_t extraMaxVal = 1000;
int16_t ctouchThrVal = 80; int16_t ctouchThrVal = 900;
uint8_t _reserved[24]; int16_t spikeThrVal= 25;
uint8_t _reserved[22];
}; };
static_assert(sizeof(calibration_t) == CALIBRATION_MAX_SIZE, "calibration data wrong size"); static_assert(sizeof(calibration_t) == CALIBRATION_MAX_SIZE, "calibration data wrong size");
@ -58,7 +59,7 @@ struct preset_t {
uint8_t velSmpDl = 20; // velocity sample delay uint8_t velSmpDl = 20; // velocity sample delay
uint8_t velBias = 0; // velocity bias uint8_t velBias = 0; // velocity bias
uint8_t pinkySetting = 12; // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 24 (QuickTranspose +1 to +12), 25 (EC2), 26 (ECSW), 27 (LVL), 28 (LVLP) uint8_t pinkySetting = 12; // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 24 (QuickTranspose +1 to +12), 25 (EC2), 26 (ECSW), 27 (LVL), 28 (LVLP)
uint8_t breathInterval; // 3-15 uint8_t breathInterval = 10; // 3-15
int8_t trill3_interval = 4; int8_t trill3_interval = 4;
uint8_t vibSquelch = 12; // vibrato signal squelch uint8_t vibSquelch = 12; // vibrato signal squelch
uint8_t cvVibRate = 0; // OFF, 1 - 8 CV extra controller LFO vibrato rate 4.5Hz to 8Hz uint8_t cvVibRate = 0; // OFF, 1 - 8 CV extra controller LFO vibrato rate 4.5Hz to 8Hz
@ -90,7 +91,10 @@ struct preset_t {
uint8_t icmRotationMode; uint8_t icmRotationMode;
uint8_t icmRotationCC; uint8_t icmRotationCC;
uint8_t _reserved[87]; uint8_t breathFilterFreq = 20;
uint8_t spikeFilterFreq = 20;
uint8_t _reserved[85];
}; };
static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes"); static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes");
@ -103,13 +107,15 @@ struct state_t {
instrument_state_t *instrument; instrument_state_t *instrument;
preset_t *currentPreset; preset_t *currentPreset;
calibration_t *calibration; calibration_t *calibration;
size_t currentPresetIdx;
}; };
#define NO_CHECKSUM 0x7F007F00 #define NO_CHECKSUM 0x7F007F00
void readEEPROM(const bool factoryReset); void readEEPROM(const bool factoryReset, calibration_t &calibration);
void writePreset(uint8_t preset); void writePreset(uint8_t preset);
void writeCalibration(); void writePresets();
void writeCalibration(calibration_t &calibration);
//Functions for config management mode //Functions for config management mode
void sendSysexSettings(); void sendSysexSettings();

View file

@ -12,6 +12,7 @@
#include "settings.h" #include "settings.h"
#include "led.h" #include "led.h"
#include "test.h" #include "test.h"
#include "FilterOnePole.h"
/* /*
NAME: xEVI NAME: xEVI
@ -30,13 +31,13 @@ FUNCTION: EVI Wind Controller using MPRLS pressure sensors and capac
preset_t presets[PRESET_COUNT]; preset_t presets[PRESET_COUNT];
instrument_state_t instrument; instrument_state_t instrument;
preset_t *currentPreset = &presets[0];
calibration_t calibration; calibration_t calibration;
state_t state = { state_t state = {
NOTE_OFF, NOTE_OFF,
&instrument, &instrument,
currentPreset, &presets[0],
&calibration, &calibration,
0,
}; };
static const int pbDepthList[13] = { 8192, 8192, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 744, 683 }; static const int pbDepthList[13] = { 8192, 8192, 4096, 2731, 2048, 1638, 1365, 1170, 1024, 910, 819, 744, 683 };
@ -90,6 +91,8 @@ const int trumpetHarmonic[2][7] = { {0, 7, 12, 16, 19, 26, 31}, //! K4: hrm 8->
bool configManagementMode = false; bool configManagementMode = false;
bool testMode = false; bool testMode = false;
FilterOnePole breathCCFilter;
//_______________________________________________________________________________________________ SETUP //_______________________________________________________________________________________________ SETUP
// MIDI note value check with out of range octave repeat // MIDI note value check with out of range octave repeat
@ -109,7 +112,7 @@ void port(int portCC) {
return; return;
} }
if (currentPreset->portamentoMode == PortamentoMode::PON || currentPreset->portamentoMode == PortamentoMode::PGLIDE_ONLY) { if (state.currentPreset->portamentoMode == PortamentoMode::PON || state.currentPreset->portamentoMode == PortamentoMode::PGLIDE_ONLY) {
if (instrument.portamentoVal > 0 && portCC == 0) { if (instrument.portamentoVal > 0 && portCC == 0) {
midiSendControlChange(CCN_PortOnOff, 0); midiSendControlChange(CCN_PortOnOff, 0);
} else if (instrument.portamentoVal == 0 && portCC > 0) { } else if (instrument.portamentoVal == 0 && portCC > 0) {
@ -158,12 +161,12 @@ void cvUpdate() {
instrument.cvPitch = instrument.targetPitch; instrument.cvPitch = instrument.targetPitch;
} }
if (currentPreset->cvVibRate) { if (state.currentPreset->cvVibRate) {
int timeDivider = timeDividerList[currentPreset->cvVibRate]; int timeDivider = timeDividerList[state.currentPreset->cvVibRate];
int cvVib = map(((waveformsTable[map(currentTime % timeDivider, 0, timeDivider, 0, maxSamplesNum - 1)] - 2047)), -259968, 259969, -11, 11); int cvVib = map(((waveformsTable[map(currentTime % timeDivider, 0, timeDivider, 0, maxSamplesNum - 1)] - 2047)), -259968, 259969, -11, 11);
instrument.cvPitch += cvVib; instrument.cvPitch += cvVib;
} }
int cvPitchTuned = 2 * (currentPreset->cvTune) + map(instrument.cvPitch, 0, 4032, 0, 4032 + 2 * (currentPreset->cvScale)); int cvPitchTuned = 2 * (state.currentPreset->cvTune) + map(instrument.cvPitch, 0, 4032, 0, 4032 + 2 * (state.currentPreset->cvScale));
analogWrite(cvPitchPin, constrain(cvPitchTuned, 0, 4095)); analogWrite(cvPitchPin, constrain(cvPitchTuned, 0, 4095));
} }
@ -197,9 +200,12 @@ unsigned int multiMap(unsigned short val, const unsigned short* _in, const unsig
// map breath values to selected curve // map breath values to selected curve
unsigned int breathCurve(unsigned int inputVal) { unsigned int breathCurve(unsigned int inputVal) {
if (currentPreset->breathCurve >= curves.size())
if (state.currentPreset->breathCurve >= curves.size()) {
return inputVal; return inputVal;
return multiMap(inputVal, curveIn, curves[currentPreset->breathCurve], 17); }
return multiMap(inputVal, curveIn, curves[state.currentPreset->breathCurve], 17);
} }
//************************************************************** //**************************************************************
@ -217,14 +223,15 @@ int breath() {
int breathCCval, breathCCvalFine; int breathCCval, breathCCvalFine;
unsigned int breathCCvalHires; unsigned int breathCCvalHires;
breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383)); breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383));
breathCCvalHires = breathCCFilter.input(breathCCvalHires);
breathCCval = (breathCCvalHires >> 7) & 0x007F; breathCCval = (breathCCvalHires >> 7) & 0x007F;
breathCCvalFine = breathCCvalHires & 0x007F; breathCCvalFine = breathCCvalHires & 0x007F;
if (breathCCval != oldbreath) { // only send midi data if breath has changed from previous value if (breathCCval != oldbreath) { // only send midi data if breath has changed from previous value
if (currentPreset->breathCC) { if (state.currentPreset->breathCC) {
// send midi cc // send midi cc
midiSendControlChange(currentPreset->breathCC, breathCCval); midiSendControlChange(state.currentPreset->breathCC, breathCCval);
} }
if (currentPreset->breathMode == BreathMode::BREATH_AT || currentPreset->breathMode == BreathMode::BREATH_LSB_AT) { if (state.currentPreset->breathMode == BreathMode::BREATH_AT || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT) {
// send aftertouch // send aftertouch
midiSendAfterTouch(breathCCval); midiSendAfterTouch(breathCCval);
} }
@ -232,8 +239,8 @@ int breath() {
} }
if (breathCCvalHires != oldbreathhires if (breathCCvalHires != oldbreathhires
&& (currentPreset->breathMode == BreathMode::BREATH_LSB || currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) { && (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) {
midiSendControlChange(currentPreset->breathCC + 32, breathCCvalFine); midiSendControlChange(state.currentPreset->breathCC + 32, breathCCvalFine);
} }
oldbreathhires = breathCCvalHires; oldbreathhires = breathCCvalHires;
@ -253,33 +260,31 @@ void pitch_bend() {
byte pbTouched = 0; byte pbTouched = 0;
int vibRead = 0; int vibRead = 0;
int vibReadBite = 0; int vibReadBite = 0;
instrument.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu" bool halfPitchBendKey = (state.currentPreset->pinkySetting == PBD) && instrument.pinkyKey; // hold pinky key for 1/2 pitchbend value
instrument.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd" 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)
bool halfPitchBendKey = (currentPreset->pinkySetting == PBD) && instrument.pinkyKey; // hold pinky key for 1/2 pitchbend value
instrument.quarterToneTrigger = (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[currentPreset->PBdepth]; calculatedPBdepth = pbDepthList[state.currentPreset->PBdepth];
if (halfPitchBendKey) if (halfPitchBendKey)
calculatedPBdepth = calculatedPBdepth * 0.5; calculatedPBdepth = calculatedPBdepth * 0.5;
vibMax = vibMaxList[currentPreset->vibSens - 1]; vibMax = vibMaxList[state.currentPreset->vibSens - 1];
float calculatedDepth = 0; float calculatedDepth = 0;
if (currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) { if (state.currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) {
calculatedDepth = calculatedPBdepth * vibDepth[currentPreset->vibratoDepth]; calculatedDepth = calculatedPBdepth * vibDepth[state.currentPreset->vibratoDepth];
} else { } else {
calculatedDepth = (0 - calculatedPBdepth * vibDepth[currentPreset->vibratoDepth]); calculatedDepth = (0 - calculatedPBdepth * vibDepth[state.currentPreset->vibratoDepth]);
} }
if (ExtraControl::VIBRATO == currentPreset->biteControl) { // bite vibrato if (ExtraControl::VIBRATO == state.currentPreset->biteControl) { // bite vibrato
vibMaxBite = vibMaxBiteList[currentPreset->vibSens - 1]; vibMaxBite = vibMaxBiteList[state.currentPreset->vibSens - 1];
vibReadBite = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right) vibReadBite = instrument.biteSignal;
if (vibReadBite < instrument.vibThrBite) { if (vibReadBite > instrument.vibThrBite) {
instrument.vibSignal = (instrument.vibSignal + mapConstrain( instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (instrument.vibZeroBite - vibMaxBite), instrument.vibThrBite, calculatedDepth, 0) vibReadBite, (instrument.vibZeroBite - vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2; ) / 2;
} else if (vibReadBite > instrument.vibThrBiteLo) { } else if (vibReadBite < instrument.vibThrBiteLo) {
instrument.vibSignal = (instrument.vibSignal + mapConstrain( instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (instrument.vibZeroBite + vibMaxBite), instrument.vibThrBite, calculatedDepth, 0) vibReadBite, (instrument.vibZeroBite + vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2; ) / 2;
@ -288,13 +293,13 @@ void pitch_bend() {
} }
} }
if (ExtraControl::VIBRATO == currentPreset->leverControl) { // lever vibrato if (ExtraControl::VIBRATO == state.currentPreset->leverControl) { // lever vibrato
vibRead = readTouchUtil(vibratoPin); vibRead = instrument.leverSignal;
if (vibRead < instrument.vibThr) { if (vibRead > instrument.vibThr) {
instrument.vibSignal = (instrument.vibSignal + instrument.vibSignal = (instrument.vibSignal +
mapConstrain(vibRead, (instrument.vibZero - vibMax), instrument.vibThr, calculatedDepth, 0) mapConstrain(vibRead, (instrument.vibZero - vibMax), instrument.vibThr, calculatedDepth, 0)
) / 2; ) / 2;
} else if (vibRead > instrument.vibThrLo) { } else if (vibRead < instrument.vibThrLo) {
instrument.vibSignal = (instrument.vibSignal + instrument.vibSignal = (instrument.vibSignal +
mapConstrain(vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0) mapConstrain(vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0)
) / 2; ) / 2;
@ -303,7 +308,7 @@ void pitch_bend() {
} }
} }
switch (currentPreset->vibRetn) { // moving baseline switch (state.currentPreset->vibRetn) { // moving baseline
case 0: case 0:
// keep vibZero value // keep vibZero value
break; break;
@ -323,16 +328,18 @@ void pitch_bend() {
instrument.vibZero = instrument.vibZero * 0.6 + vibRead * 0.4; instrument.vibZero = instrument.vibZero * 0.6 + vibRead * 0.4;
instrument.vibZeroBite = instrument.vibZeroBite * 0.6 + vibReadBite * 0.4; instrument.vibZeroBite = instrument.vibZeroBite * 0.6 + vibReadBite * 0.4;
} }
instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch; instrument.vibThr = instrument.vibZero + state.currentPreset->vibSquelch;
instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch; instrument.vibThrLo = instrument.vibZero - state.currentPreset->vibSquelch;
instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch; instrument.vibThrBite = instrument.vibZeroBite + state.currentPreset->vibSquelch;
instrument.vibThrBiteLo = instrument.vibZeroBite + currentPreset->vibSquelch; instrument.vibThrBiteLo = instrument.vibZeroBite - state.currentPreset->vibSquelch;
int pbPos = mapConstrain(instrument.pbUpSignal, calibration.pbUpMaxVal, calibration.pbUpThrVal, calculatedPBdepth, 0);
int pbNeg = mapConstrain(instrument.pbDnSignal, calibration.pbDnMaxVal, calibration.pbDnThrVal, calculatedPBdepth, 0); // PB calculation
int pbPos = mapConstrain(instrument.pbUpSignal, calibration.pbUpThrVal, calibration.pbUpMaxVal, calculatedPBdepth, 0);
int pbNeg = mapConstrain(instrument.pbDnSignal, calibration.pbDnThrVal, calibration.pbDnMaxVal, calculatedPBdepth, 0);
int pbSum = 8193 + pbPos - pbNeg; int pbSum = 8193 + pbPos - pbNeg;
int pbDif = abs(pbPos - pbNeg); int pbDif = abs(pbPos - pbNeg);
if ((instrument.pbUpSignal < calibration.pbUpThrVal || instrument.pbDnSignal < calibration.pbDnThrVal) && currentPreset->PBdepth) { if ((instrument.pbUpSignal > calibration.pbUpThrVal || instrument.pbDnSignal > calibration.pbDnThrVal) && state.currentPreset->PBdepth) {
if (pbDif < 10) { if (pbDif < 10) {
instrument.pitchBend = 8192; instrument.pitchBend = 8192;
} else { } else {
@ -361,93 +368,127 @@ void pitch_bend() {
//*********************************************************** //***********************************************************
void portamento_() { void portamento_() {
if (currentPreset->portamentoMode == PortamentoMode::POFF) { if (state.currentPreset->portamentoMode == PortamentoMode::POFF) {
port(0); // ensure it's off port(0); // ensure it's off
return; return;
} }
int portSumCC = 0; int portSumCC = 0;
if (currentPreset->pinkySetting == GLD) { if (state.currentPreset->pinkySetting == GLD) {
if (instrument.pinkyKey) { if (instrument.pinkyKey) {
portSumCC += currentPreset->portamentoLimit; portSumCC += state.currentPreset->portamentoLimit;
} }
} }
if (ExtraControl::GLIDE == currentPreset->biteControl) { if (ExtraControl::GLIDE == state.currentPreset->biteControl) {
// Portamento is controlled with the bite sensor in the mouthpiece // Portamento is controlled with the bite sensor in the mouthpiece
instrument.biteSignal = readTouchUtil(bitePin);
if (instrument.biteSignal >= calibration.biteThrVal) { // if we are enabled and over the threshold, send portamento if (instrument.biteSignal >= calibration.biteThrVal) { // if we are enabled and over the threshold, send portamento
portSumCC += mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, currentPreset->portamentoLimit); portSumCC += mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, state.currentPreset->portamentoLimit);
} }
} }
if (ExtraControl::GLIDE == currentPreset->leverControl) { if (ExtraControl::GLIDE == state.currentPreset->leverControl) {
// Portamento is controlled with thumb lever // Portamento is controlled with thumb lever
instrument.leverSignal = readTouchUtil(vibratoPin);
if (((3000 - instrument.leverSignal) >= calibration.leverThrVal)) { // if we are enabled and over the threshold, send portamento if (((3000 - instrument.leverSignal) >= calibration.leverThrVal)) { // if we are enabled and over the threshold, send portamento
portSumCC += mapConstrain((3000 - instrument.leverSignal), calibration.leverThrVal, calibration.leverMaxVal, 0, currentPreset->portamentoLimit); portSumCC += mapConstrain((3000 - instrument.leverSignal), calibration.leverThrVal, calibration.leverMaxVal, 0, state.currentPreset->portamentoLimit);
} }
} }
port(constrain(portSumCC, 0, currentPreset->portamentoLimit)); // Total output glide rate limited to glide max setting port(constrain(portSumCC, 0, state.currentPreset->portamentoLimit)); // Total output glide rate limited to glide max setting
} }
//*********************************************************** //***********************************************************
void biteCC_() { void sendCC() {
int biteVal = 0; int biteVal = 0;
if (ExtraControl::CC == currentPreset->biteControl) { if (ExtraControl::CC == state.currentPreset->biteControl) {
instrument.biteSignal = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (instrument.biteSignal >= calibration.biteThrVal) { // we are over the threshold, calculate CC value if (instrument.biteSignal >= calibration.biteThrVal) { // we are over the threshold, calculate CC value
biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127); biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127);
} }
if (biteVal != instrument.biteVal) { if (biteVal != instrument.biteVal) {
midiSendControlChange(currentPreset->biteCC, biteVal); midiSendControlChange(state.currentPreset->biteCC, biteVal);
} }
instrument.biteVal = biteVal; instrument.biteVal = biteVal;
} }
}
void autoCal() { int extraVal = 0;
instrument.vibZero = instrument.vibZeroBite = 0; if (ExtraControl::CC == state.currentPreset->extraControl) {
for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) { if (instrument.extraSignal >= calibration.extraThrVal) { // we are over the threshold, calculate CC value
instrument.breathZero += readPressure(); extraVal = mapConstrain(instrument.extraSignal, calibration.extraThrVal, calibration.extraMaxVal, 0, 127);
instrument.breathAltZero += readAltPressure();
instrument.vibZero += readTouchUtil(vibratoPin);
instrument.vibZeroBite += readTouchUtil(bitePin);
} }
instrument.breathZero /= CALIBRATE_SAMPLE_COUNT; if (extraVal != instrument.extraVal) {
instrument.breathAltZero /= CALIBRATE_SAMPLE_COUNT; midiSendControlChange(state.currentPreset->extraCC, extraVal);
instrument.vibZero /= CALIBRATE_SAMPLE_COUNT; }
instrument.vibZeroBite /= CALIBRATE_SAMPLE_COUNT; instrument.extraVal = extraVal;
}
}
instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch; // Re-zero floating calibration values
instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch; void rezero() {
instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch; instrument.vibThr = instrument.vibZero + state.currentPreset->vibSquelch;
instrument.vibThrBiteLo = instrument.vibZeroBite + 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.breathThrVal = instrument.breathZero + calibration.breathThrValOffset;
instrument.breathMaxVal = instrument.breathThrVal + calibration.breathMaxValOffset; instrument.breathMaxVal = instrument.breathThrVal + calibration.breathMaxValOffset;
instrument.breathAltThrVal = instrument.breathAltZero + calibration.breathAltThrValOffset; instrument.breathAltThrVal = instrument.breathAltZero + calibration.breathAltThrValOffset;
instrument.breathAltMaxVal = instrument.breathAltThrVal + calibration.breathAltMaxValOffset; instrument.breathAltMaxVal = instrument.breathAltThrVal + calibration.breathAltMaxValOffset;
}
void autoCal() {
instrument.vibZero = instrument.vibZeroBite = 0;
long int bZero = 0;
long int bAltZero = 0;
for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) {
bZero += readPressure();
bAltZero += readAltPressure();
instrument.vibZero += readTouchUtil(leverPin);
instrument.vibZeroBite += readTouchUtil(bitePin);
}
instrument.breathZero = bZero / CALIBRATE_SAMPLE_COUNT;
instrument.breathAltZero = bAltZero / CALIBRATE_SAMPLE_COUNT;
instrument.vibZero /= CALIBRATE_SAMPLE_COUNT;
instrument.vibZeroBite /= CALIBRATE_SAMPLE_COUNT;
rezero();
} }
void fullAutoCal() { void fullAutoCal() {
int calRead; int calRead;
int calReadNext; int calReadNext;
calibration.breathAltThrValOffset = 5;
calibration.breathAltMaxValOffset = 1500 ;
calibration.breathThrValOffset = 5;
calibration.breathMaxValOffset = 1500 ;
autoCal(); autoCal();
// Lever // Lever
calRead = 3000 - readTouchUtil(vibratoPin); calRead = readTouchUtil(leverPin);
calibration.leverThrVal = constrain(calRead + 60, LEVER_LO_LIMIT, LEVER_HI_LIMIT); calibration.leverThrVal = constrain(calRead + 100, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
calibration.leverMaxVal = constrain(calRead + 120, LEVER_LO_LIMIT, LEVER_HI_LIMIT); calibration.leverMaxVal = constrain(calRead + 300, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
// Extra
calRead = readTouchUtil(extraPin);
calibration.extraThrVal = constrain(calRead + 100, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT);
calibration.extraMaxVal = constrain(calRead + 300, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT);
// Bite sensor // Bite sensor
calRead = readTouchUtil(bitePin); calRead = readTouchUtil(bitePin);
calibration.biteThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT); calibration.biteThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT);
calibration.biteMaxVal = constrain(calibration.biteThrVal + 300, BITE_LO_LIMIT, BITE_HI_LIMIT); calibration.biteMaxVal = constrain(calRead + 300, BITE_LO_LIMIT, BITE_HI_LIMIT);
// PB
calRead = readTouchUtil(pbDnPin);
calibration.pbDnThrVal = constrain(calRead + 100, BITE_LO_LIMIT, BITE_HI_LIMIT);
calibration.pbDnMaxVal = constrain(calRead + 300, BITE_LO_LIMIT, BITE_HI_LIMIT);
calRead = readTouchUtil(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 // Touch sensors
calRead = CTOUCH_HI_LIMIT; calRead = CTOUCH_HI_LIMIT;
@ -457,7 +498,7 @@ void fullAutoCal() {
calRead = calReadNext; // use lowest value calRead = calReadNext; // use lowest value
} }
calibration.ctouchThrVal = calRead - 20; calibration.ctouchThrVal = calRead + 50;
} }
//*********************************************************** //***********************************************************
@ -467,6 +508,9 @@ void fullAutoCal() {
*/ */
int readOctave() { int readOctave() {
static byte lastOctaveR = 0; static byte lastOctaveR = 0;
static unsigned long lastDeglitchTime = 0; // The last time the fingering was changed
static unsigned long lastOctave = 0; // The last time the fingering was changed
static unsigned long fingeredOctave = 0; // The last time the fingering was changed
// Roller modes // Roller modes
// 1: Highest touched roller, release memory // 1: Highest touched roller, release memory
@ -475,20 +519,20 @@ int readOctave() {
// 4: Touched roller pair, extend range // 4: Touched roller pair, extend range
// 5: Touched roller, pair = 5th partial // 5: Touched roller, pair = 5th partial
// 6: Touched roller, pair = 5th partial, extend range // 6: Touched roller, pair = 5th partial, extend range
RollerMode rollerMode = currentPreset->rollerMode; RollerMode rollerMode = state.currentPreset->rollerMode;
byte extend = rollerMode == RollerMode::HIGHEST_EXTEND || rollerMode == RollerMode::HIGHEST_PAIR_EXTEND || rollerMode == RollerMode::PARTIAL_EXTEND ? 1 : 0; bool extend = rollerMode == RollerMode::HIGHEST_EXTEND || rollerMode == RollerMode::HIGHEST_PAIR_EXTEND || rollerMode == RollerMode::PARTIAL_EXTEND ? 1 : 0;
byte rollers[6]; bool rollers[6];
uint16_t ctouchThrVal = calibration.ctouchThrVal; uint16_t ctouchThrVal = calibration.ctouchThrVal;
rollers[0] = readTouchUtil(R1Pin) < ctouchThrVal; rollers[0] = readTouchUtil(R1Pin) > ctouchThrVal;
rollers[1] = readTouchUtil(R2Pin) < ctouchThrVal; rollers[1] = readTouchUtil(R2Pin) > ctouchThrVal;
rollers[2] = readTouchUtil(R3Pin) < ctouchThrVal; rollers[2] = readTouchUtil(R3Pin) > ctouchThrVal;
rollers[3] = readTouchUtil(R4Pin) < ctouchThrVal; rollers[3] = readTouchUtil(R4Pin) > ctouchThrVal;
rollers[4] = readTouchUtil(R5Pin) < ctouchThrVal; rollers[4] = readTouchUtil(R5Pin) > ctouchThrVal;
rollers[5] = readTouchUtil(R6Pin) < ctouchThrVal; rollers[5] = readTouchUtil(R6Pin) > ctouchThrVal;
byte offset = 0; int offset = 0;
byte octaveR = 0; int octaveR = 0;
if (rollerMode == RollerMode::HIGHEST || rollerMode == RollerMode::HIGHEST_EXTEND) { if (rollerMode == RollerMode::HIGHEST || rollerMode == RollerMode::HIGHEST_EXTEND) {
for (int i = 5; i >= 0; i--) { for (int i = 5; i >= 0; i--) {
if (rollers[i]) { if (rollers[i]) {
@ -550,17 +594,32 @@ int readOctave() {
lastOctaveR = octaveR; lastOctaveR = octaveR;
FingeringMode fingering = currentPreset->fingering; int octave = 0;
FingeringMode fingering = state.currentPreset->fingering;
byte K4 = readTouchKey(K4Pin) < calibration.ctouchThrVal; byte K4 = readTouchKey(K4Pin) < calibration.ctouchThrVal;
if (FingeringMode::TPT == fingering) { // TPT fingering if (FingeringMode::TPT == fingering) { // TPT fingering
return 24 + offset + trumpetHarmonic[K4][octaveR]; // roller harmonics octave = 24 + offset + trumpetHarmonic[K4][octaveR]; // roller harmonics
} else if (FingeringMode::HRN == fingering) { // HRN fingering } else if (FingeringMode::HRN == fingering) { // HRN fingering
return 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics octave = 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics
} else if (FingeringMode::EVR == fingering) { // HRN fingering } else if (FingeringMode::EVR == fingering) { // HRN fingering
return 12 * (6 - octaveR) + offset + instrument.octave; octave = 12 * (6 - octaveR) + offset + instrument.octave * 12;
} else { // EVI } else { // EVI
return 12 * octaveR + offset + instrument.octave; 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;
} }
int readSwitches() { int readSwitches() {
@ -574,7 +633,7 @@ int readSwitches() {
int16_t touchSum = 0; int16_t touchSum = 0;
for (byte i = 0; i < 12; i++) { for (byte i = 0; i < 12; i++) {
int16_t val = readTouchKey(i); int16_t val = readTouchKey(i);
touchKeys[i] = calibration.ctouchThrVal; touchKeys[i] = val > calibration.ctouchThrVal;
touchSum += val; touchSum += val;
} }
@ -591,31 +650,31 @@ int readSwitches() {
instrument.pinkyKey = touchKeys[K8Pin]; instrument.pinkyKey = touchKeys[K8Pin];
int qTransp = (instrument.pinkyKey && (currentPreset->pinkySetting < 25)) ? currentPreset->pinkySetting - 12 : 0; int qTransp = (instrument.pinkyKey && (state.currentPreset->pinkySetting < 25)) ? state.currentPreset->pinkySetting - 12 : 0;
// Calculate midi note number from pressed keys // Calculate midi note number from pressed keys
int fingeredNoteUntransposed = 0; int fingeredNoteUntransposed = 0;
if (EVI == currentPreset->fingering) { // EVI fingering if (EVI == state.currentPreset->fingering) { // EVI fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 5 * K4 // Fifth key - 5 * K4 // Fifth key
+ 2 * K5 + K6 + 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
} else if (EVR == currentPreset->fingering) { // EVR fingering } else if (EVR == state.currentPreset->fingering) { // EVR fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 5 * K4 // Fifth key - 5 * K4 // Fifth key
+ 2 * K5 + K6 + 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
} else if (TPT == currentPreset->fingering) { // TPT fingering } else if (TPT == state.currentPreset->fingering) { // TPT fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 2 // Trumpet in B flat - 2 // Trumpet in B flat
+ 2 * K5 + K6 + 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
} else if (HRN == currentPreset->fingering) { // HRN fingering } else if (HRN == state.currentPreset->fingering) { // HRN fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves" fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
+ 5 * K4 // Switch to Bb horn + 5 * K4 // Switch to Bb horn
+ 5 // Horn in F + 5 // Horn in F
+ 2 * K5 + K6 + 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
} }
if (K3 && K7) { if (K3 && K7) {
if (4 == currentPreset->trill3_interval) if (4 == state.currentPreset->trill3_interval)
fingeredNoteUntransposed += 2; fingeredNoteUntransposed += 2;
else else
fingeredNoteUntransposed += 4; fingeredNoteUntransposed += 4;
@ -628,7 +687,7 @@ int readSwitches() {
lastDeglitchTime = millis(); lastDeglitchTime = millis();
} }
if ((millis() - lastDeglitchTime) > currentPreset->deglitch) { if ((millis() - lastDeglitchTime) > state.currentPreset->deglitch) {
// whatever the reading is at, it's been there for longer // whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current instrument // than the debounce delay, so take it as the actual current instrument
fingeredNote = fingeredNoteRead; fingeredNote = fingeredNoteRead;
@ -639,27 +698,26 @@ int readSwitches() {
return fingeredNote; return fingeredNote;
} }
void noteOn(int fingeredNote, float pressureSensor, int initial_breath_value) { void noteOn(int fingeredNote, int pressureSensor, int initial_breath_value) {
// Yes, so calculate MIDI note and velocity, then send a note on event // 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 // 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.breathThrVal, instrument.breathMaxVal);
byte velocitySend; byte velocitySend;
if (!currentPreset->fixedVelocity) { 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.breathThrVal, instrument.breathMaxVal, 0, 16383));
velocitySend = (breathValHires >> 7) & 0x007F; velocitySend = (breathValHires >> 7) & 0x007F;
velocitySend = constrain(velocitySend + velocitySend * .1 * currentPreset->velBias, 1, 127); velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127);
} else { } else {
velocitySend = currentPreset->fixedVelocity; velocitySend = state.currentPreset->fixedVelocity;
} }
breath(); // send breath data breath(); // send breath data
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
instrument.activeNote = fingeredNote;
} }
void handleOffStateActions() { void handleOffStateActions() {
if (instrument.activeMIDIchannel != currentPreset->MIDIchannel) { if (instrument.activeMIDIchannel != state.currentPreset->MIDIchannel) {
instrument.activeMIDIchannel = currentPreset->MIDIchannel; // only switch channel if no active note instrument.activeMIDIchannel = state.currentPreset->MIDIchannel; // only switch channel if no active note
midiSetChannel(instrument.activeMIDIchannel); midiSetChannel(instrument.activeMIDIchannel);
} }
if ((instrument.activePatch != instrument.patch) && instrument.doPatchUpdate) { if ((instrument.activePatch != instrument.patch) && instrument.doPatchUpdate) {
@ -676,8 +734,20 @@ void initState() {
instrument.activePatch = 0; instrument.activePatch = 0;
state.mainState = NOTE_OFF; // initialize main state machine state.mainState = NOTE_OFF; // initialize main state machine
instrument.activeMIDIchannel = currentPreset->MIDIchannel; instrument.activeMIDIchannel = state.currentPreset->MIDIchannel;
midiInitialize(currentPreset->MIDIchannel); midiInitialize(state.currentPreset->MIDIchannel);
breathCCFilter.setFilter(LOWPASS, 3, 0.0); // create a one pole (RC) lowpass filter
}
/**
* Read all utility sensors
*/
void readUtil() {
instrument.biteSignal = readTouchUtil(bitePin);
instrument.leverSignal = readTouchUtil(leverPin);
instrument.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu"
instrument.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd"
instrument.extraSignal = readTouchUtil(extraPin);
} }
/** /**
@ -690,23 +760,26 @@ void sendCCs() {
static unsigned long ccSendTime3 = 0L; // The last time we sent CC values 3 (and slower) static unsigned long ccSendTime3 = 0L; // The last time we sent CC values 3 (and slower)
// Is it time to send more CC data? // Is it time to send more CC data?
uint32_t currentTime = millis(); unsigned long currentTime = millis();
if ((currentTime - ccBreathSendTime) > (currentPreset->breathInterval - 1u)) { if ((currentTime - ccBreathSendTime) > (state.currentPreset->breathInterval)) {
breath(); breath();
ccBreathSendTime = currentTime; ccBreathSendTime = currentTime;
} }
if (currentTime - ccSendTime > CC_INTERVAL_PRIMARY) { if (currentTime - ccSendTime > CC_INTERVAL_PRIMARY) {
// deal with Pitch Bend, Modulation, etc. // deal with Pitch Bend, Modulation, etc.
readUtil();
pitch_bend(); pitch_bend();
biteCC_(); sendCC();
ccSendTime = currentTime; ccSendTime = currentTime;
} }
if (currentTime - ccSendTime2 > CC_INTERVAL_PORT) { if (currentTime - ccSendTime2 > CC_INTERVAL_PORT) {
readUtil();
portamento_(); portamento_();
ccSendTime2 = currentTime; ccSendTime2 = currentTime;
} }
if (currentTime - ccSendTime3 > CC_INTERVAL_OTHER) { if (currentTime - ccSendTime3 > CC_INTERVAL_OTHER) {
updateSensorLEDs(); readUtil();
updateSensorLEDs(*state.instrument);
ccSendTime3 = currentTime; ccSendTime3 = currentTime;
} }
} }
@ -718,10 +791,19 @@ void runStateMachine() {
static unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold 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 static int initial_breath_value = 0; // The breath value at the time we observed the transition
Serial.print(">breath:");
Serial.println(instrument.breathSignal);
Serial.print(">breathThr:");
Serial.println(instrument.breathThrVal);
Serial.print(">spike:");
Serial.println(instrument.spikeSignal);
Serial.print(">note:");
Serial.println(state.mainState);
int fingeredNote = noteValueCheck(readSwitches() + readOctave()); int fingeredNote = noteValueCheck(readSwitches() + readOctave());
if (state.mainState == NOTE_OFF) { if (state.mainState == NOTE_OFF) {
handleOffStateActions(); handleOffStateActions();
if ((instrument.breathSignal > instrument.breathThrVal)) { if (instrument.breathSignal > instrument.breathThrVal && state.mainState == NOTE_OFF) {
// Value has risen above threshold. Move to the RISE_WAIT // Value has risen above threshold. Move to the RISE_WAIT
// state. Record time and initial breath value. // state. Record time and initial breath value.
breath_on_time = millis(); breath_on_time = millis();
@ -731,16 +813,20 @@ void runStateMachine() {
} else if (state.mainState == RISE_WAIT) { } else if (state.mainState == RISE_WAIT) {
if ((instrument.breathSignal > instrument.breathThrVal)) { if ((instrument.breathSignal > instrument.breathThrVal)) {
// Has enough time passed for us to collect our second sample? // Has enough time passed for us to collect our second sample?
if ((millis() - breath_on_time > currentPreset->velSmpDl) || (0 == currentPreset->velSmpDl) || currentPreset->fixedVelocity) { if ((millis() - breath_on_time > state.currentPreset->velSmpDl) || (0 == state.currentPreset->velSmpDl) || state.currentPreset->fixedVelocity) {
noteOn(fingeredNote, instrument.breathSignal, initial_breath_value); noteOn(fingeredNote, instrument.breathSignal, initial_breath_value);
state.mainState = NOTE_ON; state.mainState = NOTE_ON;
instrument.activeNote = fingeredNote;
} }
} else { } else {
// Value fell below threshold before velocity sample delay time passed. Return to NOTE_OFF state // Value fell below threshold before velocity sample delay time passed. Return to NOTE_OFF state
state.mainState = NOTE_OFF; state.mainState = NOTE_OFF;
} }
} else if (state.mainState == NOTE_ON) { } else if (state.mainState == NOTE_ON) {
if ((instrument.breathSignal < instrument.breathThrVal)) { if (state.currentPreset->spikeFilterFreq && instrument.spikeSignal < -calibration.spikeThrVal) {
midiSendNoteOff(instrument.activeNote); // send Note Off message
state.mainState = SPIKE_HOLD;
} else if (instrument.breathSignal < instrument.breathThrVal) {
// Value has fallen below threshold - turn the note off // Value has fallen below threshold - turn the note off
midiSendNoteOff(instrument.activeNote); // send Note Off message midiSendNoteOff(instrument.activeNote); // send Note Off message
instrument.breathSignal = 0; instrument.breathSignal = 0;
@ -753,8 +839,17 @@ void runStateMachine() {
noteOn(fingeredNote, instrument.breathSignal, 0); noteOn(fingeredNote, instrument.breathSignal, 0);
delayMicroseconds(2000); // delay for midi recording fix delayMicroseconds(2000); // delay for midi recording fix
midiSendNoteOff(instrument.activeNote); // send Note Off message midiSendNoteOff(instrument.activeNote); // send Note Off message
instrument.activeNote = fingeredNote;
} }
} }
} else if (state.mainState == SPIKE_HOLD) {
if (state.currentPreset->spikeFilterFreq && instrument.spikeSignal > calibration.spikeThrVal) {
noteOn(fingeredNote, instrument.breathSignal, initial_breath_value);
state.mainState = NOTE_ON;
instrument.activeNote = fingeredNote;
} else if ((instrument.breathSignal < instrument.breathThrVal)) {
state.mainState = NOTE_OFF;
}
} }
} }
@ -769,7 +864,7 @@ void setup() {
} }
initHardware(); initHardware();
delay(100); delay(100); // Make sure the inputs settle
Serial.println(buttonState()); Serial.println(buttonState());
bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET); bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET);
configManagementMode = checkButtonState(STARTUP_CONFIG); configManagementMode = checkButtonState(STARTUP_CONFIG);
@ -788,25 +883,26 @@ void setup() {
} }
// Read eeprom data into global vars // Read eeprom data into global vars
// readEEPROM(factoryReset); readEEPROM(factoryReset, *state.calibration);
updateFilters(*state.currentPreset);
statusLedFlash(500); statusLedFlash(500);
statusLedOff(); statusLedOff();
if (factoryReset) { if (factoryReset) {
// Full calibration // Full calibration
// fullAutoCal(); fullAutoCal();
} else { } else {
// Minimal startup calibration (atmo pressure) // Minimal startup calibration (atmo pressure)
// autoCal(); autoCal();
} }
showVersion(); showVersion();
delay(1000); delay(1000);
// initState(); // Set up midi/etc initState(); // Set up midi/etc
statusLedOn(); // Switch on the onboard LED to indicate power on/ready statusLedOn(); // Switch on the onboard LED to indicate power on/ready
displayOff(); displayOff(state);
} }
//_______________________________________________________________________________________________ MAIN LOOP //_______________________________________________________________________________________________ MAIN LOOP
@ -815,9 +911,6 @@ void loop() {
static unsigned long pixelUpdateTime = 0; static unsigned long pixelUpdateTime = 0;
static const unsigned long pixelUpdateInterval = 80; static const unsigned long pixelUpdateInterval = 80;
handleMenu(state, false);
return;
// If in config mgmt loop, do that and nothing else // If in config mgmt loop, do that and nothing else
if (configManagementMode) { if (configManagementMode) {
configModeLoop(); configModeLoop();
@ -830,6 +923,8 @@ void loop() {
} }
instrument.breathSignal = constrain(readPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP instrument.breathSignal = constrain(readPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP
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.spikeSignal = constrain(readSpikePressure(), -SPIKE_HI_LIMIT, SPIKE_HI_LIMIT);
runStateMachine(); runStateMachine();
sendCCs(); sendCCs();