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_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 FILTER_FREQ 30.0
#define FILTER_FREQ 10.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
#define CALIBRATE_SAMPLE_COUNT 4
@ -29,6 +29,8 @@
#define BTN_VAL1 0x2
#define BTN_VAL2 0x4
#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
// milliseconds
@ -42,17 +44,19 @@
#define maxSamplesNum 120
#define BREATH_LO_LIMIT 8000
#define BREATH_HI_LIMIT 10000
#define BITE_LO_LIMIT 0
#define BREATH_LO_LIMIT 9000
#define BREATH_HI_LIMIT 12000
#define BITE_LO_LIMIT 500
#define BITE_HI_LIMIT 1000
#define PITCHB_LO_LIMIT 0
#define PITCHB_LO_LIMIT 500
#define PITCHB_HI_LIMIT 1000
#define EXTRA_LO_LIMIT 0
#define EXTRA_LO_LIMIT 500
#define EXTRA_HI_LIMIT 1000
#define CTOUCH_LO_LIMIT 0
#define CTOUCH_LO_LIMIT 500
#define CTOUCH_HI_LIMIT 1000
#define LEVER_LO_LIMIT 0
#define LEVER_LO_LIMIT 500
#define LEVER_HI_LIMIT 1000
#define SPIKE_LO_LIMIT 0
#define SPIKE_HI_LIMIT 100
#endif

View file

@ -18,6 +18,9 @@
// A note is sounding
#define NOTE_ON 3
// We turned the note off due to a HF spike
#define SPIKE_HOLD 4
enum PinkyMode : uint8_t {
PBD = 12,
GLD = 25,
@ -86,6 +89,7 @@ struct instrument_state_t {
// Raw sensor signals
int16_t breathSignal = 0; // breath level (smoothed) not mapped to CC value
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 leverSignal = 0;
int16_t pbUpSignal = 0;
@ -130,6 +134,10 @@ struct instrument_state_t {
extern const std::array<const unsigned short*, 13> curves;
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);
#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
FilterOnePole breathFilter;
FilterOnePole breathAltFilter;
FilterOnePole spikeFilter;
Adafruit_MPR121 touchSensorKeys = 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
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();
@ -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
*/
@ -107,16 +115,27 @@ void errorWait() {
* Read the knob value accounting for 4x precision - NB this might not work on other kinds of encoder
*/
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;
int32_t val = knobs[n].read();
if (val > 4) {
if (val > (lastOut < 0) ? 3 : 4) {
out = val / 4;
} else if (val < -4) {
} else if (val < (lastOut > 0) ? -3 : -4) {
out = val / 4;
}
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;
}
@ -136,8 +155,15 @@ uint16_t utilTouched() {
return touchSensorKeys.touched();
}
int readSpikePressure() {
return spikeFilter.input(pressureSensorMain.readPressure()) * PRESSURE_SENS_MULTIPLIER;
}
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() {

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,7 @@
#define MENU_HEADER_OFFSET 12
#define MENU_NUM_ROWS 6
#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 buttonRepeatInterval;
@ -33,12 +33,12 @@ class MenuScreen {
public:
MenuScreen() {};
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() {};
};
void initDisplay();
void displayOff();
void displayOff(state_t &state);
void showVersion();
void displayError(const char *error);
void handleMenu(state_t &state, bool draw);

View file

@ -41,7 +41,7 @@ void readCalibration(calibration_t &calibration) {
}
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() {
@ -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.
if (factoryReset || settings_version == 0xffffu) {
settings_version = 0;
preset_t defaultPreset;
calibration_t defaultCalibration;
for (int i = 0; i < PRESET_COUNT; i++) {
presets[i] = defaultPreset;
}
calibration = defaultCalibration;
} else {
readPresets();
readCalibration(calibration);
}
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();
writeCalibration();
writeCalibration(calibration);
writeInt(EEPROM_VERSION_ADDR, EEPROM_VERSION);
}
}

View file

@ -5,7 +5,7 @@
#include "globals.h"
#define EEPROM_VERSION 1
#define EEPROM_VERSION 3
#define EEPROM_VERSION_ADDR 0
#define SETTINGS_OFFSET 2
#define PRESET_MAX_SIZE 128 // Leave extra space for future settings
@ -24,18 +24,19 @@ struct calibration_t {
int16_t breathMaxValOffset = 1500;
int16_t breathAltThrValOffset = 5;
int16_t breathAltMaxValOffset = 1500;
int16_t biteThrVal = 50;
int16_t biteMaxVal = 150;
int16_t pbDnThrVal = 50;
int16_t pbDnMaxVal = 150;
int16_t pbUpThrVal = 50;
int16_t pbUpMaxVal = 150;
int16_t leverThrVal = 50;
int16_t leverMaxVal = 150;
int16_t extraThrVal = 50;
int16_t extraMaxVal = 150;
int16_t ctouchThrVal = 80;
uint8_t _reserved[24];
int16_t biteThrVal = 850;
int16_t biteMaxVal = 1000;
int16_t pbDnThrVal = 850;
int16_t pbDnMaxVal = 1000;
int16_t pbUpThrVal = 850;
int16_t pbUpMaxVal = 1000;
int16_t leverThrVal = 850;
int16_t leverMaxVal = 1000;
int16_t extraThrVal = 850;
int16_t extraMaxVal = 1000;
int16_t ctouchThrVal = 900;
int16_t spikeThrVal= 25;
uint8_t _reserved[22];
};
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 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 breathInterval; // 3-15
uint8_t breathInterval = 10; // 3-15
int8_t trill3_interval = 4;
uint8_t vibSquelch = 12; // vibrato signal squelch
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 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");
@ -103,13 +107,15 @@ struct state_t {
instrument_state_t *instrument;
preset_t *currentPreset;
calibration_t *calibration;
size_t currentPresetIdx;
};
#define NO_CHECKSUM 0x7F007F00
void readEEPROM(const bool factoryReset);
void readEEPROM(const bool factoryReset, calibration_t &calibration);
void writePreset(uint8_t preset);
void writeCalibration();
void writePresets();
void writeCalibration(calibration_t &calibration);
//Functions for config management mode
void sendSysexSettings();

View file

@ -12,6 +12,7 @@
#include "settings.h"
#include "led.h"
#include "test.h"
#include "FilterOnePole.h"
/*
NAME: xEVI
@ -30,13 +31,13 @@ FUNCTION: EVI Wind Controller using MPRLS pressure sensors and capac
preset_t presets[PRESET_COUNT];
instrument_state_t instrument;
preset_t *currentPreset = &presets[0];
calibration_t calibration;
state_t state = {
NOTE_OFF,
&instrument,
currentPreset,
&presets[0],
&calibration,
0,
};
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 testMode = false;
FilterOnePole breathCCFilter;
//_______________________________________________________________________________________________ SETUP
// MIDI note value check with out of range octave repeat
@ -109,7 +112,7 @@ void port(int portCC) {
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) {
midiSendControlChange(CCN_PortOnOff, 0);
} else if (instrument.portamentoVal == 0 && portCC > 0) {
@ -158,12 +161,12 @@ void cvUpdate() {
instrument.cvPitch = instrument.targetPitch;
}
if (currentPreset->cvVibRate) {
int timeDivider = timeDividerList[currentPreset->cvVibRate];
if (state.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);
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));
}
@ -197,9 +200,12 @@ unsigned int multiMap(unsigned short val, const unsigned short* _in, const unsig
// map breath values to selected curve
unsigned int breathCurve(unsigned int inputVal) {
if (currentPreset->breathCurve >= curves.size())
if (state.currentPreset->breathCurve >= curves.size()) {
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;
unsigned int breathCCvalHires;
breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383));
breathCCvalHires = breathCCFilter.input(breathCCvalHires);
breathCCval = (breathCCvalHires >> 7) & 0x007F;
breathCCvalFine = breathCCvalHires & 0x007F;
if (breathCCval != oldbreath) { // only send midi data if breath has changed from previous value
if (currentPreset->breathCC) {
if (state.currentPreset->breathCC) {
// 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
midiSendAfterTouch(breathCCval);
}
@ -232,8 +239,8 @@ int breath() {
}
if (breathCCvalHires != oldbreathhires
&& (currentPreset->breathMode == BreathMode::BREATH_LSB || currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) {
midiSendControlChange(currentPreset->breathCC + 32, breathCCvalFine);
&& (state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) {
midiSendControlChange(state.currentPreset->breathCC + 32, breathCCvalFine);
}
oldbreathhires = breathCCvalHires;
@ -253,33 +260,31 @@ void pitch_bend() {
byte pbTouched = 0;
int vibRead = 0;
int vibReadBite = 0;
instrument.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu"
instrument.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd"
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)
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[currentPreset->PBdepth];
calculatedPBdepth = pbDepthList[state.currentPreset->PBdepth];
if (halfPitchBendKey)
calculatedPBdepth = calculatedPBdepth * 0.5;
vibMax = vibMaxList[currentPreset->vibSens - 1];
vibMax = vibMaxList[state.currentPreset->vibSens - 1];
float calculatedDepth = 0;
if (currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) {
calculatedDepth = calculatedPBdepth * vibDepth[currentPreset->vibratoDepth];
if (state.currentPreset->vibratoMode == VibratoMode::VSTART_DOWN) {
calculatedDepth = calculatedPBdepth * vibDepth[state.currentPreset->vibratoDepth];
} else {
calculatedDepth = (0 - calculatedPBdepth * vibDepth[currentPreset->vibratoDepth]);
calculatedDepth = (0 - calculatedPBdepth * vibDepth[state.currentPreset->vibratoDepth]);
}
if (ExtraControl::VIBRATO == currentPreset->biteControl) { // bite vibrato
vibMaxBite = vibMaxBiteList[currentPreset->vibSens - 1];
vibReadBite = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (ExtraControl::VIBRATO == state.currentPreset->biteControl) { // bite vibrato
vibMaxBite = vibMaxBiteList[state.currentPreset->vibSens - 1];
vibReadBite = instrument.biteSignal;
if (vibReadBite < instrument.vibThrBite) {
if (vibReadBite > instrument.vibThrBite) {
instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (instrument.vibZeroBite - vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2;
} else if (vibReadBite > instrument.vibThrBiteLo) {
} else if (vibReadBite < instrument.vibThrBiteLo) {
instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (instrument.vibZeroBite + vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2;
@ -288,13 +293,13 @@ void pitch_bend() {
}
}
if (ExtraControl::VIBRATO == currentPreset->leverControl) { // lever vibrato
vibRead = readTouchUtil(vibratoPin);
if (vibRead < instrument.vibThr) {
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) {
} else if (vibRead < instrument.vibThrLo) {
instrument.vibSignal = (instrument.vibSignal +
mapConstrain(vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0)
) / 2;
@ -303,7 +308,7 @@ void pitch_bend() {
}
}
switch (currentPreset->vibRetn) { // moving baseline
switch (state.currentPreset->vibRetn) { // moving baseline
case 0:
// keep vibZero value
break;
@ -323,16 +328,18 @@ void pitch_bend() {
instrument.vibZero = instrument.vibZero * 0.6 + vibRead * 0.4;
instrument.vibZeroBite = instrument.vibZeroBite * 0.6 + vibReadBite * 0.4;
}
instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch;
instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch;
instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch;
instrument.vibThrBiteLo = instrument.vibZeroBite + currentPreset->vibSquelch;
int pbPos = mapConstrain(instrument.pbUpSignal, calibration.pbUpMaxVal, calibration.pbUpThrVal, calculatedPBdepth, 0);
int pbNeg = mapConstrain(instrument.pbDnSignal, calibration.pbDnMaxVal, calibration.pbDnThrVal, calculatedPBdepth, 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;
// 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 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) {
instrument.pitchBend = 8192;
} else {
@ -361,93 +368,127 @@ void pitch_bend() {
//***********************************************************
void portamento_() {
if (currentPreset->portamentoMode == PortamentoMode::POFF) {
if (state.currentPreset->portamentoMode == PortamentoMode::POFF) {
port(0); // ensure it's off
return;
}
int portSumCC = 0;
if (currentPreset->pinkySetting == GLD) {
if (state.currentPreset->pinkySetting == GLD) {
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
instrument.biteSignal = readTouchUtil(bitePin);
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
instrument.leverSignal = readTouchUtil(vibratoPin);
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;
if (ExtraControl::CC == 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 (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);
}
if (biteVal != instrument.biteVal) {
midiSendControlChange(currentPreset->biteCC, biteVal);
midiSendControlChange(state.currentPreset->biteCC, biteVal);
}
instrument.biteVal = biteVal;
}
int extraVal = 0;
if (ExtraControl::CC == state.currentPreset->extraControl) {
if (instrument.extraSignal >= calibration.extraThrVal) { // we are over the threshold, calculate CC value
extraVal = mapConstrain(instrument.extraSignal, calibration.extraThrVal, calibration.extraMaxVal, 0, 127);
}
if (extraVal != instrument.extraVal) {
midiSendControlChange(state.currentPreset->extraCC, extraVal);
}
instrument.extraVal = extraVal;
}
}
void autoCal() {
instrument.vibZero = instrument.vibZeroBite = 0;
for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) {
instrument.breathZero += readPressure();
instrument.breathAltZero += readAltPressure();
instrument.vibZero += readTouchUtil(vibratoPin);
instrument.vibZeroBite += readTouchUtil(bitePin);
}
instrument.breathZero /= CALIBRATE_SAMPLE_COUNT;
instrument.breathAltZero /= CALIBRATE_SAMPLE_COUNT;
instrument.vibZero /= CALIBRATE_SAMPLE_COUNT;
instrument.vibZeroBite /= CALIBRATE_SAMPLE_COUNT;
instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch;
instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch;
instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch;
instrument.vibThrBiteLo = instrument.vibZeroBite + currentPreset->vibSquelch;
// Re-zero floating calibration values
void rezero() {
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.breathMaxVal = instrument.breathThrVal + calibration.breathMaxValOffset;
instrument.breathAltThrVal = instrument.breathAltZero + calibration.breathAltThrValOffset;
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() {
int calRead;
int calReadNext;
calibration.breathAltThrValOffset = 5;
calibration.breathAltMaxValOffset = 1500 ;
calibration.breathThrValOffset = 5;
calibration.breathMaxValOffset = 1500 ;
autoCal();
// Lever
calRead = 3000 - readTouchUtil(vibratoPin);
calibration.leverThrVal = constrain(calRead + 60, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
calibration.leverMaxVal = constrain(calRead + 120, LEVER_LO_LIMIT, LEVER_HI_LIMIT);
calRead = readTouchUtil(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 = 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
calRead = readTouchUtil(bitePin);
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
calRead = CTOUCH_HI_LIMIT;
@ -457,7 +498,7 @@ void fullAutoCal() {
calRead = calReadNext; // use lowest value
}
calibration.ctouchThrVal = calRead - 20;
calibration.ctouchThrVal = calRead + 50;
}
//***********************************************************
@ -467,6 +508,9 @@ void fullAutoCal() {
*/
int readOctave() {
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
// 1: Highest touched roller, release memory
@ -475,20 +519,20 @@ int readOctave() {
// 4: Touched roller pair, extend range
// 5: Touched roller, pair = 5th partial
// 6: Touched roller, pair = 5th partial, extend range
RollerMode rollerMode = currentPreset->rollerMode;
byte extend = rollerMode == RollerMode::HIGHEST_EXTEND || rollerMode == RollerMode::HIGHEST_PAIR_EXTEND || rollerMode == RollerMode::PARTIAL_EXTEND ? 1 : 0;
RollerMode rollerMode = state.currentPreset->rollerMode;
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;
rollers[0] = readTouchUtil(R1Pin) < ctouchThrVal;
rollers[1] = readTouchUtil(R2Pin) < ctouchThrVal;
rollers[2] = readTouchUtil(R3Pin) < ctouchThrVal;
rollers[3] = readTouchUtil(R4Pin) < ctouchThrVal;
rollers[4] = readTouchUtil(R5Pin) < ctouchThrVal;
rollers[5] = readTouchUtil(R6Pin) < ctouchThrVal;
rollers[0] = readTouchUtil(R1Pin) > ctouchThrVal;
rollers[1] = readTouchUtil(R2Pin) > ctouchThrVal;
rollers[2] = readTouchUtil(R3Pin) > ctouchThrVal;
rollers[3] = readTouchUtil(R4Pin) > ctouchThrVal;
rollers[4] = readTouchUtil(R5Pin) > ctouchThrVal;
rollers[5] = readTouchUtil(R6Pin) > ctouchThrVal;
byte offset = 0;
byte octaveR = 0;
int offset = 0;
int octaveR = 0;
if (rollerMode == RollerMode::HIGHEST || rollerMode == RollerMode::HIGHEST_EXTEND) {
for (int i = 5; i >= 0; i--) {
if (rollers[i]) {
@ -550,17 +594,32 @@ int readOctave() {
lastOctaveR = octaveR;
FingeringMode fingering = currentPreset->fingering;
int octave = 0;
FingeringMode fingering = state.currentPreset->fingering;
byte K4 = readTouchKey(K4Pin) < calibration.ctouchThrVal;
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
return 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics
octave = 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics
} else if (FingeringMode::EVR == fingering) { // HRN fingering
return 12 * (6 - octaveR) + offset + instrument.octave;
octave = 12 * (6 - octaveR) + offset + instrument.octave * 12;
} 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() {
@ -574,7 +633,7 @@ int readSwitches() {
int16_t touchSum = 0;
for (byte i = 0; i < 12; i++) {
int16_t val = readTouchKey(i);
touchKeys[i] = calibration.ctouchThrVal;
touchKeys[i] = val > calibration.ctouchThrVal;
touchSum += val;
}
@ -591,31 +650,31 @@ int readSwitches() {
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
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"
- 5 * K4 // Fifth key
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (EVR == currentPreset->fingering) { // EVR fingering
+ 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (EVR == state.currentPreset->fingering) { // EVR fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 5 * K4 // Fifth key
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (TPT == currentPreset->fingering) { // TPT fingering
+ 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (TPT == state.currentPreset->fingering) { // TPT fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
- 2 // Trumpet in B flat
+ 2 * K5 + K6 + currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (HRN == currentPreset->fingering) { // HRN fingering
+ 2 * K5 + K6 + state.currentPreset->trill3_interval * K7; // Trill keys. 3rd trill key interval controlled by setting
} else if (HRN == state.currentPreset->fingering) { // HRN fingering
fingeredNoteUntransposed = START_NOTE - 2 * K1 - K2 - 3 * K3 //"Trumpet valves"
+ 5 * K4 // Switch to Bb horn
+ 5 // Horn in F
+ 2 * K5 + K6 + 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 (4 == currentPreset->trill3_interval)
if (4 == state.currentPreset->trill3_interval)
fingeredNoteUntransposed += 2;
else
fingeredNoteUntransposed += 4;
@ -628,7 +687,7 @@ int readSwitches() {
lastDeglitchTime = millis();
}
if ((millis() - lastDeglitchTime) > currentPreset->deglitch) {
if ((millis() - lastDeglitchTime) > state.currentPreset->deglitch) {
// whatever the reading is at, it's been there for longer
// than the debounce delay, so take it as the actual current instrument
fingeredNote = fingeredNoteRead;
@ -639,27 +698,26 @@ int readSwitches() {
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
// 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);
byte velocitySend;
if (!currentPreset->fixedVelocity) {
if (!state.currentPreset->fixedVelocity) {
unsigned int breathValHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383));
velocitySend = (breathValHires >> 7) & 0x007F;
velocitySend = constrain(velocitySend + velocitySend * .1 * currentPreset->velBias, 1, 127);
velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127);
} else {
velocitySend = currentPreset->fixedVelocity;
velocitySend = state.currentPreset->fixedVelocity;
}
breath(); // send breath data
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
instrument.activeNote = fingeredNote;
}
void handleOffStateActions() {
if (instrument.activeMIDIchannel != currentPreset->MIDIchannel) {
instrument.activeMIDIchannel = currentPreset->MIDIchannel; // only switch channel if no active note
if (instrument.activeMIDIchannel != state.currentPreset->MIDIchannel) {
instrument.activeMIDIchannel = state.currentPreset->MIDIchannel; // only switch channel if no active note
midiSetChannel(instrument.activeMIDIchannel);
}
if ((instrument.activePatch != instrument.patch) && instrument.doPatchUpdate) {
@ -676,8 +734,20 @@ void initState() {
instrument.activePatch = 0;
state.mainState = NOTE_OFF; // initialize main state machine
instrument.activeMIDIchannel = currentPreset->MIDIchannel;
midiInitialize(currentPreset->MIDIchannel);
instrument.activeMIDIchannel = state.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)
// Is it time to send more CC data?
uint32_t currentTime = millis();
if ((currentTime - ccBreathSendTime) > (currentPreset->breathInterval - 1u)) {
unsigned long currentTime = millis();
if ((currentTime - ccBreathSendTime) > (state.currentPreset->breathInterval)) {
breath();
ccBreathSendTime = currentTime;
}
if (currentTime - ccSendTime > CC_INTERVAL_PRIMARY) {
// deal with Pitch Bend, Modulation, etc.
readUtil();
pitch_bend();
biteCC_();
sendCC();
ccSendTime = currentTime;
}
if (currentTime - ccSendTime2 > CC_INTERVAL_PORT) {
readUtil();
portamento_();
ccSendTime2 = currentTime;
}
if (currentTime - ccSendTime3 > CC_INTERVAL_OTHER) {
updateSensorLEDs();
readUtil();
updateSensorLEDs(*state.instrument);
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 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());
if (state.mainState == NOTE_OFF) {
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
// state. Record time and initial breath value.
breath_on_time = millis();
@ -731,16 +813,20 @@ void runStateMachine() {
} else if (state.mainState == RISE_WAIT) {
if ((instrument.breathSignal > instrument.breathThrVal)) {
// 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);
state.mainState = NOTE_ON;
instrument.activeNote = fingeredNote;
}
} 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.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
midiSendNoteOff(instrument.activeNote); // send Note Off message
instrument.breathSignal = 0;
@ -753,8 +839,17 @@ void runStateMachine() {
noteOn(fingeredNote, instrument.breathSignal, 0);
delayMicroseconds(2000); // delay for midi recording fix
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();
delay(100);
delay(100); // Make sure the inputs settle
Serial.println(buttonState());
bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET);
configManagementMode = checkButtonState(STARTUP_CONFIG);
@ -788,25 +883,26 @@ void setup() {
}
// Read eeprom data into global vars
// readEEPROM(factoryReset);
readEEPROM(factoryReset, *state.calibration);
updateFilters(*state.currentPreset);
statusLedFlash(500);
statusLedOff();
if (factoryReset) {
// Full calibration
// fullAutoCal();
fullAutoCal();
} else {
// Minimal startup calibration (atmo pressure)
// autoCal();
autoCal();
}
showVersion();
delay(1000);
// initState(); // Set up midi/etc
initState(); // Set up midi/etc
statusLedOn(); // Switch on the onboard LED to indicate power on/ready
displayOff();
displayOff(state);
}
//_______________________________________________________________________________________________ MAIN LOOP
@ -815,9 +911,6 @@ void loop() {
static unsigned long pixelUpdateTime = 0;
static const unsigned long pixelUpdateInterval = 80;
handleMenu(state, false);
return;
// If in config mgmt loop, do that and nothing else
if (configManagementMode) {
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.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();
sendCCs();