Menu system fixes; made state less global

This commit is contained in:
Brian Hrebec 2023-08-29 13:32:56 -05:00
parent cfc2390b8b
commit 209959e2de
14 changed files with 964 additions and 731 deletions

View file

@ -12,7 +12,7 @@
platform = teensy platform = teensy
board = teensy40 board = teensy40
framework = arduino framework = arduino
build_flags = -D USB_MIDI_SERIAL -D TEENSY_OPT_FASTER build_flags = -D USB_MIDI_SERIAL -g
board_build.f_cpu = 528000000L board_build.f_cpu = 528000000L
lib_deps = lib_deps =
adafruit/Adafruit MPR121@^1.1.1 adafruit/Adafruit MPR121@^1.1.1

View file

@ -1,8 +1,11 @@
1. LED abstraction code 1. LED abstraction code
2. Encoder code
3. Menu refactor
4. Refactor note play behavior into module 4. Refactor note play behavior into module
5. Refactor CV behavior into module 5. Refactor CV behavior into module
6. 9dof sensor code 6. 9dof sensor code
7. Alternate fingerings 7. Alternate fingerings
8. Encoder midi 8. Encoder midi
- Lever mode: pb
- Breath mode: relative
- Breath suck control?
-

View file

@ -1,187 +0,0 @@
#include <array>
#include <Arduino.h>
#include <Adafruit_SSD1306.h>
#include "menu.h"
#include "globals.h"
#include "config.h"
#include "hardware.h"
#include "settings.h"
//***********************************************************
extern Adafruit_SSD1306 display;
extern Adafruit_MPR121 touchSensorUtil;
extern Adafruit_MPR121 touchSensorKeys;
extern Adafruit_MPRLS pressureSensorMain;
extern Adafruit_MPRLS pressureSensorAlt;
extern byte cursorNow;
int16_t ctouchVal = 0;
// Track pixels for faster redrawing
struct AdjustDrawing {
int row;
int thrX;
int maxX;
int valX;
};
struct AdjustValue {
const char *title;
const int16_t &value;
int16_t &thrVal;
int16_t &maxVal;
const int16_t limitLow;
const int16_t limitHigh;
// If not null, thr and max are relative to zeroPoint
const int16_t *zeroPoint;
};
template<size_t N>
class AdjustMenuScreen : public MenuScreen {
public:
AdjustMenuScreen(const char* title, std::array<AdjustValue, N> entries) : _title(title), _entries(entries) { }
void update(InputState input, bool redraw) {
bool redrawIndicators = false;
if (input.changed) {
if (input.knobMenu) {
_selectedEntry = _selectedEntry + input.knobMenu;
redraw = true;
}
AdjustValue value = _entries[_selectedEntry];
if (input.knobVal1) {
value.thrVal += input.knobVal1;
redrawIndicators = true;
}
if (input.knobVal2) {
value.maxVal += input.knobVal2;
redrawIndicators = true;
}
} else {
draw(redrawIndicators, redraw);
}
}
const char *title() {
return _title;
}
private:
void draw(bool redrawIndicators, bool redraw) {
size_t scrollPos = 0;
if (_entries.size() >= ADJUST_NUM_ROWS) {
if ((_selectedEntry - scrollPos) > (ADJUST_NUM_ROWS-2) ) {
scrollPos = _selectedEntry - (ADJUST_NUM_ROWS-2);
} else if( (_selectedEntry - scrollPos) < 1) {
scrollPos = _selectedEntry - 1;
}
scrollPos = constrain(scrollPos, 0, _entries.size() - ADJUST_NUM_ROWS);
}
int end = constrain(scrollPos + ADJUST_NUM_ROWS, 0, N);
for (size_t i = scrollPos; i < end; i++) {
if (redraw) {
drawAdjustRow(_entries[i], _rowDrawings[i], i == _selectedEntry);
} else if (redrawIndicators && i == _selectedEntry) {
drawAdjustIndicators(_entries[i], _rowDrawings[i]);
} else {
drawAdjustValues(_entries[i], _rowDrawings[i]);
}
}
}
const char* _title;
size_t _selectedEntry = 0;
std::array<AdjustValue, N> _entries;
std::array<AdjustDrawing, N> _rowDrawings;
};
std::array<AdjustValue, 8> adjustValues = {{
{"BREATH", state.breathSignal, calibration.breathThrValOffset, calibration.breathMaxValOffset,
BREATH_LO_LIMIT, BREATH_HI_LIMIT, &state.breathZero},
{"BR ALT", state.breathAltSignal, calibration.breathAltThrValOffset, calibration.breathAltMaxValOffset,
BREATH_LO_LIMIT, BREATH_HI_LIMIT, &state.breathAltZero},
{"BITE",state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, BITE_LO_LIMIT, BITE_HI_LIMIT, NULL},
{"PB DOWN",state.pbDnSignal, calibration.pbDnThrVal, calibration.pbDnMaxVal, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL},
{"PB UP", state.pbUpSignal, calibration.pbUpThrVal, calibration.pbUpMaxVal, PITCHB_LO_LIMIT, PITCHB_HI_LIMIT, NULL},
{"EXTRA", state.extraSignal, calibration.extraThrVal, calibration.extraMaxVal, EXTRA_LO_LIMIT, EXTRA_HI_LIMIT, NULL},
{"LEVER", state.leverSignal, calibration.leverThrVal, calibration.leverMaxVal, LEVER_LO_LIMIT, LEVER_HI_LIMIT, NULL},
{"TOUCH", ctouchVal, calibration.ctouchThrVal, calibration.ctouchThrVal, CTOUCH_LO_LIMIT, CTOUCH_HI_LIMIT, NULL},
}};
const MenuScreen adjustMenu = AdjustMenuScreen<8>("ADJUST", adjustValues);
void autoCalSelected() {
}
//***********************************************************
static void drawIndicator(int x, int row, int color) {
display.fillTriangle(x-2, row+1, x+2, row+1, x, row+3, color);
display.fillTriangle(x-2, row+10, x+2, row+10, x, row+7, color);
}
static void drawAdjustIndicators(const AdjustValue &value, AdjustDrawing &drawing) {
const int thrX = mapConstrain(value.thrVal, value.limitLow, value.limitHigh, 1, 127);
const int maxX = mapConstrain(value.maxVal, value.limitLow, value.limitHigh, 1, 127);
if (drawing.maxX != maxX) {
drawIndicator(drawing.thrX, drawing.row, BLACK);
drawIndicator(thrX, drawing.row, WHITE);
drawing.maxX = maxX;
}
if (drawing.thrX != thrX) {
drawIndicator(drawing.thrX, drawing.row, BLACK);
drawIndicator(thrX, drawing.row, WHITE);
drawing.thrX = thrX;
}
}
static void drawAdjustTitle(const AdjustValue &value, AdjustDrawing &drawing, bool highlight) {
display.setTextSize(1);
if (highlight) {
display.setTextColor(BLACK, WHITE);
} else {
display.setTextColor(WHITE, BLACK);
}
display.setCursor(0, drawing.row);
display.println(value.title);
}
static void drawAdjustValues(const AdjustValue &value, AdjustDrawing &drawing) {
char buffer[13];
snprintf(buffer, 13, "%d>%d<%d", value.thrVal, value.value, value.maxVal);
display.setTextSize(1);
display.setCursor(128 - 6 * strlen(buffer), drawing.row);
display.println(buffer);
const int valX = mapConstrain(value.value, value.limitLow, value.limitHigh, 1, 127);
if (drawing.valX != valX) {
display.drawFastVLine(drawing.valX, drawing.row+4, 4, BLACK);
display.drawFastVLine(valX, drawing.row+4, 4, WHITE);
drawing.valX = valX;
}
}
static void drawAdjustFrame(int line) {
display.drawLine(25,line,120,line,WHITE); // Top line
display.drawLine(25,line+12,120,line+12,WHITE); // Bottom line
display.drawLine(25,line+1,25,line+2,WHITE);
display.drawLine(120,line+1,120,line+2,WHITE);
display.drawLine(120,line+10,120,line+11,WHITE);
display.drawLine(25,line+10,25,line+11,WHITE);
}
static void drawAdjustRow(const AdjustValue &value, AdjustDrawing &drawing, bool highlight) {
display.fillRect(0, drawing.row, 128, 21, BLACK);
drawAdjustFrame(drawing.row);
drawAdjustTitle(value, drawing, highlight);
drawAdjustValues(value, drawing);
drawAdjustIndicators(value, drawing);
}

View file

@ -15,6 +15,7 @@
#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
#define MENU_AUTO_OFF_TIME 30000
// Statup buttons // Statup buttons

View file

@ -74,14 +74,13 @@ enum PortamentoMode : uint8_t {
}; };
struct instrument_state_t { struct instrument_state_t {
int mainState; // The state of the main state machine
uint8_t patch; // 1-128 uint8_t patch; // 1-128
byte activeMIDIchannel = 1; // MIDI channel byte activeMIDIchannel = 1; // MIDI channel
byte activeNote = 0; // note playing byte activeNote = 0; // note playing
byte activePatch = 0; byte activePatch = 0;
byte doPatchUpdate = 0; byte doPatchUpdate = 0;
int8_t transpose = 0; int8_t transpose = 0;
uint8_t octave = 0; int8_t octave = 0;
PolySelect polyMode = PolySelect::EHarmonizerOff; PolySelect polyMode = PolySelect::EHarmonizerOff;
// Raw sensor signals // Raw sensor signals
@ -93,6 +92,7 @@ struct instrument_state_t {
int16_t pbDnSignal = 0; int16_t pbDnSignal = 0;
int16_t extraSignal = 0; int16_t extraSignal = 0;
int16_t vibSignal = 0; int16_t vibSignal = 0;
int16_t avgCTouchSignal = 0;
// MIDI values // MIDI values
int breathCCVal = 0; int breathCCVal = 0;
@ -127,8 +127,6 @@ struct instrument_state_t {
int16_t vibThrBiteLo; int16_t vibThrBiteLo;
}; };
extern instrument_state_t state;
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[];

View file

@ -103,8 +103,22 @@ 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) { int readKnob(uint8_t n) {
return knobs[n].readAndReset(); int out = 0;
int32_t val = knobs[n].read();
if (val > 4) {
out = val / 4;
} else if (val < -4) {
out = val / 4;
}
if (out != 0) {
knobs[n].write(0);
}
return out;
} }
int readTouchKey(uint8_t n) { int readTouchKey(uint8_t n) {

View file

@ -31,24 +31,24 @@ int readAltPressure();
// I2C // I2C
#define MainI2CBus Wire1 #define MainI2CBus Wire1
#define AuxI2CBus Wire #define AuxI2CBus Wire
#define KeysI2CAddr 0x5B #define KeysI2CAddr 0x5A
#define UtilI2CAddr 0x5A #define UtilI2CAddr 0x5B
// Digital pins for encoder buttons // Digital pins for encoder buttons
#define b1Pin 0 #define b1Pin 4
#define b2Pin 2 #define b2Pin 3
#define b3Pin 3 #define b3Pin 2
#define b4Pin 4 #define b4Pin 0
// Digital pins for encoder quadrature // Digital pins for encoder quadrature
#define e1aPin 5 #define e1aPin 6
#define e2aPin 6 #define e2aPin 8
#define e3aPin 7 #define e3aPin 7
#define e4aPin 8 #define e4aPin 5
#define e1bPin 20 #define e1bPin 22
#define e2bPin 21 #define e2bPin 21
#define e3bPin 22 #define e3bPin 20
#define e4bPin 23 #define e4bPin 23
// CV pins // CV pins
@ -64,33 +64,34 @@ int readAltPressure();
// Key pins // Key pins
// RH keys // RH keys
#define K1Pin 0 #define K1Pin 5
#define K2Pin 1 #define K2Pin 6
#define K3Pin 2 #define K3Pin 2
#define K4Pin 3 #define K4Pin 11
#define K5Pin 4 #define K5Pin 4
#define K6Pin 5 #define K6Pin 7
#define K7Pin 6 #define K7Pin 9
#define K8Pin 7 #define K8Pin 10
// LH keys // LH keys
#define K9Pin 8 #define K9Pin 3
#define K10Pin 9 #define K10Pin 8
#define K11Pin 10 #define K11Pin 0
#define K12Pin 11 #define K12Pin 1
// Octave roller pins // Octave roller pins
#define R1Pin 0 #define R1Pin 11
#define R2Pin 1 #define R2Pin 10
#define R3Pin 2 #define R3Pin 9
#define R4Pin 3 #define R4Pin 8
#define R5Pin 4 #define R5Pin 7
#define R6Pin 5 #define R6Pin 6
// Additional pins // Additional pins
#define bitePin 6 #define bitePin 0
#define pbUpPin 7 #define extraPin 2
#define pbDnPin 8 #define pbUpPin 4
#define vibratoPin 9 #define pbDnPin 5
#define vibratoPin 3
#endif #endif

View file

@ -43,7 +43,7 @@ void statusLedBlink() {
statusLedFlash(300); statusLedFlash(300);
} }
void updateSensorLEDs() { 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);
} }

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
#define __MENU_H #define __MENU_H
#include "wiring.h" #include "wiring.h"
#include "settings.h"
#define MENU_ROW_HEIGHT 9 #define MENU_ROW_HEIGHT 9
#define MENU_HEADER_OFFSET 12 #define MENU_HEADER_OFFSET 12
@ -28,17 +29,18 @@ struct InputState {
int knobPreset = 0; int knobPreset = 0;
}; };
struct MenuScreen { class MenuScreen {
public:
MenuScreen() {}; MenuScreen() {};
virtual const char *title() { return ""; }; virtual const char *title() { return ""; };
virtual void update(InputState input, bool redraw) {}; virtual bool update(state_t &state, InputState &input, bool redraw) = 0;
virtual ~MenuScreen() {}; virtual ~MenuScreen() {};
}; };
extern const MenuScreen adjustMenu;
void initDisplay(); void initDisplay();
void displayOff();
void showVersion(); void showVersion();
void displayError(const char *error); void displayError(const char *error);
void handleMenu(bool draw); void handleMenu(state_t &state, bool draw);
#endif #endif

View file

@ -32,11 +32,11 @@ uint16_t readInt(uint16_t address) {
return data.val; return data.val;
} }
void writeCalibration() { void writeCalibration(calibration_t &calibration) {
EEPROM.put(SETTINGS_OFFSET, calibration); EEPROM.put(SETTINGS_OFFSET, calibration);
} }
void readCalibration() { void readCalibration(calibration_t &calibration) {
EEPROM.get(SETTINGS_OFFSET, calibration); EEPROM.get(SETTINGS_OFFSET, calibration);
} }
@ -181,7 +181,6 @@ bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
uint16_t *preset_buffer = (uint16_t*)presets; uint16_t *preset_buffer = (uint16_t*)presets;
for(uint16_t i=0; i<payload_size/2; i++) { for(uint16_t i=0; i<payload_size/2; i++) {
uint16_t addr = i*2; uint16_t addr = i*2;
uint16_t val;
preset_buffer[addr] = convertFromMidiValue(data+(payload_pos+addr)); preset_buffer[addr] = convertFromMidiValue(data+(payload_pos+addr));
} }
@ -270,7 +269,7 @@ void handleSysex(uint8_t *data, unsigned int length) {
} }
//Get message code //Get message code
char messageCode[3]; char messageCode[4];
strncpy(messageCode, (char*)(data+9), 3); strncpy(messageCode, (char*)(data+9), 3);
if(!strncmp(messageCode, "c00", 3)) { //Config dump request if(!strncmp(messageCode, "c00", 3)) { //Config dump request
@ -319,7 +318,7 @@ void configModeLoop() {
} }
//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade) //Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade)
void readEEPROM(const bool factoryReset) { void readEEPROM(const bool factoryReset, calibration_t &calibration) {
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings // if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
uint16_t settings_version = readInt(EEPROM_VERSION_ADDR); uint16_t settings_version = readInt(EEPROM_VERSION_ADDR);
@ -329,7 +328,7 @@ void readEEPROM(const bool factoryReset) {
settings_version = 0; settings_version = 0;
} else { } else {
readPresets(); readPresets();
readCalibration(); readCalibration(calibration);
} }
if(settings_version != EEPROM_VERSION) { if(settings_version != EEPROM_VERSION) {

View file

@ -95,8 +95,15 @@ struct preset_t {
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");
extern preset_t presets[PRESET_COUNT]; extern preset_t presets[PRESET_COUNT];
extern calibration_t calibration;
extern preset_t *currentPreset; // Application state
struct state_t {
int mainState; // The state of the main state machine
instrument_state_t *instrument;
preset_t *currentPreset;
calibration_t *calibration;
};
#define NO_CHECKSUM 0x7F007F00 #define NO_CHECKSUM 0x7F007F00

View file

@ -27,12 +27,14 @@ void handleTestMode() {
if (keys != oldKeys) { if (keys != oldKeys) {
Serial.print("Keys:"); Serial.print("Keys:");
Serial.println(keys, HEX); Serial.println(keys, HEX);
oldKeys = keys;
} }
uint16_t util = utilTouched(); uint16_t util = utilTouched();
if (util != oldUtil) { if (util != oldUtil) {
Serial.print("Util:"); Serial.print("Util:");
Serial.println(util, HEX); Serial.println(util, HEX);
oldUtil = util;
} }
if (buttons == 0x01) { if (buttons == 0x01) {
@ -54,4 +56,6 @@ void handleTestMode() {
Serial.println(readTouchUtil(i)); Serial.println(readTouchUtil(i));
} }
} }
delay(2);
} }

View file

@ -11,6 +11,7 @@
#include "config.h" #include "config.h"
#include "settings.h" #include "settings.h"
#include "led.h" #include "led.h"
#include "test.h"
/* /*
NAME: xEVI NAME: xEVI
@ -28,9 +29,15 @@ FUNCTION: EVI Wind Controller using MPRLS pressure sensors and capac
#endif #endif
preset_t presets[PRESET_COUNT]; preset_t presets[PRESET_COUNT];
instrument_state_t state; instrument_state_t instrument;
preset_t *currentPreset; preset_t *currentPreset = &presets[0];
calibration_t calibration; calibration_t calibration;
state_t state = {
NOTE_OFF,
&instrument,
currentPreset,
&calibration,
};
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 };
static const float vibDepth[10] = { 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.40, 0.45 }; // max pitch bend values (+/-) for the vibrato settings static const float vibDepth[10] = { 0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.40, 0.45 }; // max pitch bend values (+/-) for the vibrato settings
@ -98,20 +105,20 @@ inline int noteValueCheck(int note) {
//*********************************************************** //***********************************************************
void port(int portCC) { void port(int portCC) {
if (portCC == state.portamentoVal) { if (portCC == instrument.portamentoVal) {
return; return;
} }
if (currentPreset->portamentoMode == PortamentoMode::PON || currentPreset->portamentoMode == PortamentoMode::PGLIDE_ONLY) { if (currentPreset->portamentoMode == PortamentoMode::PON || currentPreset->portamentoMode == PortamentoMode::PGLIDE_ONLY) {
if (state.portamentoVal > 0 && portCC == 0) { if (instrument.portamentoVal > 0 && portCC == 0) {
midiSendControlChange(CCN_PortOnOff, 0); midiSendControlChange(CCN_PortOnOff, 0);
} else if (state.portamentoVal == 0 && portCC > 0) { } else if (instrument.portamentoVal == 0 && portCC > 0) {
midiSendControlChange(CCN_PortOnOff, 127); midiSendControlChange(CCN_PortOnOff, 127);
} }
} }
midiSendControlChange(CCN_Port, portCC); midiSendControlChange(CCN_Port, portCC);
state.portamentoVal = portCC; instrument.portamentoVal = portCC;
} }
// Update CV output pin, run from timer. // Update CV output pin, run from timer.
@ -120,43 +127,43 @@ void cvUpdate() {
uint32_t currentTime = millis(); uint32_t currentTime = millis();
int cvPressure = readPressure(); int cvPressure = readPressure();
analogWrite(cvBreathPin, cvPressure); analogWrite(cvBreathPin, cvPressure);
state.targetPitch = (state.activeNote - 24) * 42; instrument.targetPitch = (instrument.activeNote - 24) * 42;
state.targetPitch += map(state.pitchBend, 0, 16383, -84, 84); instrument.targetPitch += map(instrument.pitchBend, 0, 16383, -84, 84);
state.targetPitch -= state.quarterToneTrigger * 21; instrument.targetPitch -= instrument.quarterToneTrigger * 21;
if (state.portamentoVal > 0) { if (instrument.portamentoVal > 0) {
if (state.targetPitch > state.cvPitch) { if (instrument.targetPitch > instrument.cvPitch) {
if (!cvPortaTuneCount) { if (!cvPortaTuneCount) {
state.cvPitch += 1 + (127 - state.portamentoVal) / 4; instrument.cvPitch += 1 + (127 - instrument.portamentoVal) / 4;
} else { } else {
cvPortaTuneCount++; cvPortaTuneCount++;
if (cvPortaTuneCount > CVPORTATUNE) if (cvPortaTuneCount > CVPORTATUNE)
cvPortaTuneCount = 0; cvPortaTuneCount = 0;
} }
if (state.cvPitch > state.targetPitch) if (instrument.cvPitch > instrument.targetPitch)
state.cvPitch = state.targetPitch; instrument.cvPitch = instrument.targetPitch;
} else if (state.targetPitch < state.cvPitch) { } else if (instrument.targetPitch < instrument.cvPitch) {
if (!cvPortaTuneCount) { if (!cvPortaTuneCount) {
state.cvPitch -= 1 + (127 - state.portamentoVal) / 4; instrument.cvPitch -= 1 + (127 - instrument.portamentoVal) / 4;
} else { } else {
cvPortaTuneCount++; cvPortaTuneCount++;
if (cvPortaTuneCount > CVPORTATUNE) if (cvPortaTuneCount > CVPORTATUNE)
cvPortaTuneCount = 0; cvPortaTuneCount = 0;
} }
if (state.cvPitch < state.targetPitch) if (instrument.cvPitch < instrument.targetPitch)
state.cvPitch = state.targetPitch; instrument.cvPitch = instrument.targetPitch;
} else { } else {
state.cvPitch = state.targetPitch; instrument.cvPitch = instrument.targetPitch;
} }
} else { } else {
state.cvPitch = state.targetPitch; instrument.cvPitch = instrument.targetPitch;
} }
if (currentPreset->cvVibRate) { if (currentPreset->cvVibRate) {
int timeDivider = timeDividerList[currentPreset->cvVibRate]; int timeDivider = timeDividerList[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);
state.cvPitch += cvVib; instrument.cvPitch += cvVib;
} }
int cvPitchTuned = 2 * (currentPreset->cvTune) + map(state.cvPitch, 0, 4032, 0, 4032 + 2 * (currentPreset->cvScale)); int cvPitchTuned = 2 * (currentPreset->cvTune) + map(instrument.cvPitch, 0, 4032, 0, 4032 + 2 * (currentPreset->cvScale));
analogWrite(cvPitchPin, constrain(cvPitchTuned, 0, 4095)); analogWrite(cvPitchPin, constrain(cvPitchTuned, 0, 4095));
} }
@ -209,7 +216,7 @@ int breath() {
int breathCCval, breathCCvalFine; int breathCCval, breathCCvalFine;
unsigned int breathCCvalHires; unsigned int breathCCvalHires;
breathCCvalHires = breathCurve(mapConstrain(state.breathSignal, state.breathThrVal, state.breathMaxVal, 0, 16383)); breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathThrVal, instrument.breathMaxVal, 0, 16383));
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
@ -246,10 +253,10 @@ void pitch_bend() {
byte pbTouched = 0; byte pbTouched = 0;
int vibRead = 0; int vibRead = 0;
int vibReadBite = 0; int vibReadBite = 0;
state.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu" instrument.pbUpSignal = readTouchUtil(pbUpPin); // PCB PIN "Pu"
state.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd" instrument.pbDnSignal = readTouchUtil(pbDnPin); // PCB PIN "Pd"
bool halfPitchBendKey = (currentPreset->pinkySetting == PBD) && state.pinkyKey; // hold pinky key for 1/2 pitchbend value bool halfPitchBendKey = (currentPreset->pinkySetting == PBD) && instrument.pinkyKey; // hold pinky key for 1/2 pitchbend value
state.quarterToneTrigger = (currentPreset->pinkySetting == QTN) && state.pinkyKey; // pinky key for a quarter tone down using pitch bend (assuming PB range on synth is set to 2 semitones) 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[currentPreset->PBdepth];
if (halfPitchBendKey) if (halfPitchBendKey)
@ -268,31 +275,31 @@ void pitch_bend() {
vibMaxBite = vibMaxBiteList[currentPreset->vibSens - 1]; 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) vibReadBite = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (vibReadBite < state.vibThrBite) { if (vibReadBite < instrument.vibThrBite) {
state.vibSignal = (state.vibSignal + mapConstrain( instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (state.vibZeroBite - vibMaxBite), state.vibThrBite, calculatedDepth, 0) vibReadBite, (instrument.vibZeroBite - vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2; ) / 2;
} else if (vibReadBite > state.vibThrBiteLo) { } else if (vibReadBite > instrument.vibThrBiteLo) {
state.vibSignal = (state.vibSignal + mapConstrain( instrument.vibSignal = (instrument.vibSignal + mapConstrain(
vibReadBite, (state.vibZeroBite + vibMaxBite), state.vibThrBite, calculatedDepth, 0) vibReadBite, (instrument.vibZeroBite + vibMaxBite), instrument.vibThrBite, calculatedDepth, 0)
) / 2; ) / 2;
} else { } else {
state.vibSignal = state.vibSignal / 2; instrument.vibSignal = instrument.vibSignal / 2;
} }
} }
if (ExtraControl::VIBRATO == currentPreset->leverControl) { // lever vibrato if (ExtraControl::VIBRATO == currentPreset->leverControl) { // lever vibrato
vibRead = readTouchUtil(vibratoPin); vibRead = readTouchUtil(vibratoPin);
if (vibRead < state.vibThr) { if (vibRead < instrument.vibThr) {
state.vibSignal = (state.vibSignal + instrument.vibSignal = (instrument.vibSignal +
mapConstrain(vibRead, (state.vibZero - vibMax), state.vibThr, calculatedDepth, 0) mapConstrain(vibRead, (instrument.vibZero - vibMax), instrument.vibThr, calculatedDepth, 0)
) / 2; ) / 2;
} else if (vibRead > state.vibThrLo) { } else if (vibRead > instrument.vibThrLo) {
state.vibSignal = (state.vibSignal + instrument.vibSignal = (instrument.vibSignal +
mapConstrain(vibRead, (state.vibZero + vibMax), state.vibThr, calculatedDepth, 0) mapConstrain(vibRead, (instrument.vibZero + vibMax), instrument.vibThr, calculatedDepth, 0)
) / 2; ) / 2;
} else { } else {
state.vibSignal = state.vibSignal / 2; instrument.vibSignal = instrument.vibSignal / 2;
} }
} }
@ -301,53 +308,53 @@ void pitch_bend() {
// keep vibZero value // keep vibZero value
break; break;
case 1: case 1:
state.vibZero = state.vibZero * 0.95 + vibRead * 0.05; instrument.vibZero = instrument.vibZero * 0.95 + vibRead * 0.05;
state.vibZeroBite = state.vibZeroBite * 0.95 + vibReadBite * 0.05; instrument.vibZeroBite = instrument.vibZeroBite * 0.95 + vibReadBite * 0.05;
break; break;
case 2: case 2:
state.vibZero = state.vibZero * 0.9 + vibRead * 0.1; instrument.vibZero = instrument.vibZero * 0.9 + vibRead * 0.1;
state.vibZeroBite = state.vibZeroBite * 0.9 + vibReadBite * 0.1; instrument.vibZeroBite = instrument.vibZeroBite * 0.9 + vibReadBite * 0.1;
break; break;
case 3: case 3:
state.vibZero = state.vibZero * 0.8 + vibRead * 0.2; instrument.vibZero = instrument.vibZero * 0.8 + vibRead * 0.2;
state.vibZeroBite = state.vibZeroBite * 0.8 + vibReadBite * 0.2; instrument.vibZeroBite = instrument.vibZeroBite * 0.8 + vibReadBite * 0.2;
break; break;
case 4: case 4:
state.vibZero = state.vibZero * 0.6 + vibRead * 0.4; instrument.vibZero = instrument.vibZero * 0.6 + vibRead * 0.4;
state.vibZeroBite = state.vibZeroBite * 0.6 + vibReadBite * 0.4; instrument.vibZeroBite = instrument.vibZeroBite * 0.6 + vibReadBite * 0.4;
} }
state.vibThr = state.vibZero - currentPreset->vibSquelch; instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch;
state.vibThrLo = state.vibZero + currentPreset->vibSquelch; instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch;
state.vibThrBite = state.vibZeroBite - currentPreset->vibSquelch; instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch;
state.vibThrBiteLo = state.vibZeroBite + currentPreset->vibSquelch; instrument.vibThrBiteLo = instrument.vibZeroBite + currentPreset->vibSquelch;
int pbPos = mapConstrain(state.pbUpSignal, calibration.pbUpMaxVal, calibration.pbUpThrVal, calculatedPBdepth, 0); int pbPos = mapConstrain(instrument.pbUpSignal, calibration.pbUpMaxVal, calibration.pbUpThrVal, calculatedPBdepth, 0);
int pbNeg = mapConstrain(state.pbDnSignal, calibration.pbDnMaxVal, calibration.pbDnThrVal, calculatedPBdepth, 0); int pbNeg = mapConstrain(instrument.pbDnSignal, calibration.pbDnMaxVal, calibration.pbDnThrVal, calculatedPBdepth, 0);
int pbSum = 8193 + pbPos - pbNeg; int pbSum = 8193 + pbPos - pbNeg;
int pbDif = abs(pbPos - pbNeg); int pbDif = abs(pbPos - pbNeg);
if ((state.pbUpSignal < calibration.pbUpThrVal || state.pbDnSignal < calibration.pbDnThrVal) && currentPreset->PBdepth) { if ((instrument.pbUpSignal < calibration.pbUpThrVal || instrument.pbDnSignal < calibration.pbDnThrVal) && currentPreset->PBdepth) {
if (pbDif < 10) { if (pbDif < 10) {
state.pitchBend = 8192; instrument.pitchBend = 8192;
} else { } else {
state.pitchBend = state.pitchBend * 0.6 + 0.4 * pbSum; instrument.pitchBend = instrument.pitchBend * 0.6 + 0.4 * pbSum;
} }
pbTouched = 1; pbTouched = 1;
} }
if (!pbTouched) { if (!pbTouched) {
state.pitchBend = state.pitchBend * 0.6 + 8192 * 0.4; // released, so smooth your way back to zero instrument.pitchBend = instrument.pitchBend * 0.6 + 8192 * 0.4; // released, so smooth your way back to zero
if ((state.pitchBend > 8187) && (state.pitchBend < 8197)) if ((instrument.pitchBend > 8187) && (instrument.pitchBend < 8197))
state.pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing instrument.pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing
} }
state.pitchBend = state.pitchBend + state.vibSignal; instrument.pitchBend = instrument.pitchBend + instrument.vibSignal;
state.pitchBend = constrain(state.pitchBend, 0, 16383); instrument.pitchBend = constrain(instrument.pitchBend, 0, 16383);
state.pbSend = state.pitchBend - state.quarterToneTrigger * calculatedPBdepth * 0.25; instrument.pbSend = instrument.pitchBend - instrument.quarterToneTrigger * calculatedPBdepth * 0.25;
state.pbSend = constrain(state.pbSend, 0, 16383); instrument.pbSend = constrain(instrument.pbSend, 0, 16383);
if (state.pbSend != oldpb) { // only send midi data if pitch bend has changed from previous value if (instrument.pbSend != oldpb) { // only send midi data if pitch bend has changed from previous value
midiSendPitchBend(state.pbSend); midiSendPitchBend(instrument.pbSend);
oldpb = state.pbSend; oldpb = instrument.pbSend;
} }
} }
@ -361,23 +368,23 @@ void portamento_() {
int portSumCC = 0; int portSumCC = 0;
if (currentPreset->pinkySetting == GLD) { if (currentPreset->pinkySetting == GLD) {
if (state.pinkyKey) { if (instrument.pinkyKey) {
portSumCC += currentPreset->portamentoLimit; portSumCC += currentPreset->portamentoLimit;
} }
} }
if (ExtraControl::GLIDE == currentPreset->biteControl) { if (ExtraControl::GLIDE == currentPreset->biteControl) {
// Portamento is controlled with the bite sensor in the mouthpiece // Portamento is controlled with the bite sensor in the mouthpiece
state.biteSignal = readTouchUtil(bitePin); instrument.biteSignal = readTouchUtil(bitePin);
if (state.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(state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, currentPreset->portamentoLimit); portSumCC += mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, currentPreset->portamentoLimit);
} }
} }
if (ExtraControl::GLIDE == currentPreset->leverControl) { if (ExtraControl::GLIDE == currentPreset->leverControl) {
// Portamento is controlled with thumb lever // Portamento is controlled with thumb lever
state.leverSignal = readTouchUtil(vibratoPin); instrument.leverSignal = readTouchUtil(vibratoPin);
if (((3000 - state.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 - state.leverSignal), calibration.leverThrVal, calibration.leverMaxVal, 0, currentPreset->portamentoLimit); portSumCC += mapConstrain((3000 - instrument.leverSignal), calibration.leverThrVal, calibration.leverMaxVal, 0, currentPreset->portamentoLimit);
} }
} }
@ -389,41 +396,41 @@ void portamento_() {
void biteCC_() { void biteCC_() {
int biteVal = 0; int biteVal = 0;
if (ExtraControl::CC == currentPreset->biteControl) { if (ExtraControl::CC == currentPreset->biteControl) {
state.biteSignal = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right) instrument.biteSignal = readTouchUtil(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
if (state.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(state.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127); biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127);
} }
if (biteVal != state.biteVal) { if (biteVal != instrument.biteVal) {
midiSendControlChange(currentPreset->biteCC, biteVal); midiSendControlChange(currentPreset->biteCC, biteVal);
} }
state.biteVal = biteVal; instrument.biteVal = biteVal;
} }
} }
void autoCal() { void autoCal() {
state.vibZero = state.vibZeroBite = 0; instrument.vibZero = instrument.vibZeroBite = 0;
for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) { for(int i = 1 ; i <= CALIBRATE_SAMPLE_COUNT; ++i) {
state.breathZero += readPressure(); instrument.breathZero += readPressure();
state.breathAltZero += readAltPressure(); instrument.breathAltZero += readAltPressure();
state.vibZero += readTouchUtil(vibratoPin); instrument.vibZero += readTouchUtil(vibratoPin);
state.vibZeroBite += readTouchUtil(bitePin); instrument.vibZeroBite += readTouchUtil(bitePin);
} }
state.breathZero /= CALIBRATE_SAMPLE_COUNT; instrument.breathZero /= CALIBRATE_SAMPLE_COUNT;
state.breathAltZero /= CALIBRATE_SAMPLE_COUNT; instrument.breathAltZero /= CALIBRATE_SAMPLE_COUNT;
state.vibZero /= CALIBRATE_SAMPLE_COUNT; instrument.vibZero /= CALIBRATE_SAMPLE_COUNT;
state.vibZeroBite /= CALIBRATE_SAMPLE_COUNT; instrument.vibZeroBite /= CALIBRATE_SAMPLE_COUNT;
state.vibThr = state.vibZero - currentPreset->vibSquelch; instrument.vibThr = instrument.vibZero - currentPreset->vibSquelch;
state.vibThrLo = state.vibZero + currentPreset->vibSquelch; instrument.vibThrLo = instrument.vibZero + currentPreset->vibSquelch;
state.vibThrBite = state.vibZeroBite - currentPreset->vibSquelch; instrument.vibThrBite = instrument.vibZeroBite - currentPreset->vibSquelch;
state.vibThrBiteLo = state.vibZeroBite + currentPreset->vibSquelch; instrument.vibThrBiteLo = instrument.vibZeroBite + currentPreset->vibSquelch;
state.breathThrVal = state.breathZero + calibration.breathThrValOffset; instrument.breathThrVal = instrument.breathZero + calibration.breathThrValOffset;
state.breathMaxVal = state.breathThrVal + calibration.breathMaxValOffset; instrument.breathMaxVal = instrument.breathThrVal + calibration.breathMaxValOffset;
state.breathAltThrVal = state.breathAltZero + calibration.breathAltThrValOffset; instrument.breathAltThrVal = instrument.breathAltZero + calibration.breathAltThrValOffset;
state.breathAltMaxVal = state.breathAltThrVal + calibration.breathAltMaxValOffset; instrument.breathAltMaxVal = instrument.breathAltThrVal + calibration.breathAltMaxValOffset;
} }
@ -550,9 +557,9 @@ int readOctave() {
} else if (FingeringMode::HRN == fingering) { // HRN fingering } else if (FingeringMode::HRN == fingering) { // HRN fingering
return 12 + offset + rollerHarmonic[K4][octaveR]; // roller harmonics return 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; return 12 * (6 - octaveR) + offset + instrument.octave;
} else { // EVI } else { // EVI
return 12 * octaveR + offset; return 12 * octaveR + offset + instrument.octave;
} }
} }
@ -564,10 +571,15 @@ int readSwitches() {
// Read touch pads (MPR121), compare against threshold value // Read touch pads (MPR121), compare against threshold value
bool touchKeys[12]; bool touchKeys[12];
int16_t touchSum = 0;
for (byte i = 0; i < 12; i++) { for (byte i = 0; i < 12; i++) {
touchKeys[i] = readTouchKey(i) < calibration.ctouchThrVal; int16_t val = readTouchKey(i);
touchKeys[i] = calibration.ctouchThrVal;
touchSum += val;
} }
instrument.avgCTouchSignal = touchSum / 12;
// Valves and trill keys, TRUE (1) for pressed, FALSE (0) for not pressed // Valves and trill keys, TRUE (1) for pressed, FALSE (0) for not pressed
byte K1 = touchKeys[K1Pin]; // Valve 1 (pitch change -2) byte K1 = touchKeys[K1Pin]; // Valve 1 (pitch change -2)
byte K2 = touchKeys[K2Pin]; // Valve 2 (pitch change -1) byte K2 = touchKeys[K2Pin]; // Valve 2 (pitch change -1)
@ -577,9 +589,9 @@ int readSwitches() {
byte K6 = touchKeys[K6Pin]; // Trill key 2 (pitch change +1) byte K6 = touchKeys[K6Pin]; // Trill key 2 (pitch change +1)
byte K7 = touchKeys[K7Pin]; // Trill key 3 (pitch change +4) byte K7 = touchKeys[K7Pin]; // Trill key 3 (pitch change +4)
state.pinkyKey = touchKeys[K8Pin]; instrument.pinkyKey = touchKeys[K8Pin];
int qTransp = (state.pinkyKey && (currentPreset->pinkySetting < 25)) ? currentPreset->pinkySetting - 12 : 0; int qTransp = (instrument.pinkyKey && (currentPreset->pinkySetting < 25)) ? currentPreset->pinkySetting - 12 : 0;
// Calculate midi note number from pressed keys // Calculate midi note number from pressed keys
int fingeredNoteUntransposed = 0; int fingeredNoteUntransposed = 0;
@ -609,7 +621,7 @@ int readSwitches() {
fingeredNoteUntransposed += 4; fingeredNoteUntransposed += 4;
} }
int fingeredNoteRead = fingeredNoteUntransposed + state.transpose - 12 + qTransp; int fingeredNoteRead = fingeredNoteUntransposed + instrument.transpose - 12 + qTransp;
if (fingeredNoteRead != lastFingering) { // if (fingeredNoteRead != lastFingering) { //
// reset the debouncing timer // reset the debouncing timer
@ -618,7 +630,7 @@ int readSwitches() {
if ((millis() - lastDeglitchTime) > currentPreset->deglitch) { if ((millis() - lastDeglitchTime) > 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 state // than the debounce delay, so take it as the actual current instrument
fingeredNote = fingeredNoteRead; fingeredNote = fingeredNoteRead;
} }
@ -630,10 +642,10 @@ int readSwitches() {
void noteOn(int fingeredNote, float pressureSensor, int initial_breath_value) { void noteOn(int fingeredNote, float 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
state.breathSignal = constrain(max(pressureSensor, initial_breath_value), state.breathThrVal, state.breathMaxVal); instrument.breathSignal = constrain(max(pressureSensor, initial_breath_value), instrument.breathThrVal, instrument.breathMaxVal);
byte velocitySend; byte velocitySend;
if (!currentPreset->fixedVelocity) { if (!currentPreset->fixedVelocity) {
unsigned int breathValHires = breathCurve(mapConstrain(state.breathSignal, state.breathThrVal, state.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 * currentPreset->velBias, 1, 127);
} else { } else {
@ -642,18 +654,18 @@ void noteOn(int fingeredNote, float pressureSensor, int initial_breath_value) {
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
state.activeNote = fingeredNote; instrument.activeNote = fingeredNote;
} }
void handleOffStateActions() { void handleOffStateActions() {
if (state.activeMIDIchannel != currentPreset->MIDIchannel) { if (instrument.activeMIDIchannel != currentPreset->MIDIchannel) {
state.activeMIDIchannel = currentPreset->MIDIchannel; // only switch channel if no active note instrument.activeMIDIchannel = currentPreset->MIDIchannel; // only switch channel if no active note
midiSetChannel(state.activeMIDIchannel); midiSetChannel(instrument.activeMIDIchannel);
} }
if ((state.activePatch != state.patch) && state.doPatchUpdate) { if ((instrument.activePatch != instrument.patch) && instrument.doPatchUpdate) {
state.activePatch = state.patch; instrument.activePatch = instrument.patch;
midiSendProgramChange(state.activePatch); midiSendProgramChange(instrument.activePatch);
state.doPatchUpdate = 0; instrument.doPatchUpdate = 0;
} }
} }
@ -661,10 +673,10 @@ void handleOffStateActions() {
Initialize the main instrument state Initialize the main instrument state
*/ */
void initState() { void initState() {
state.activePatch = 0; instrument.activePatch = 0;
state.mainState = NOTE_OFF; // initialize main state machine state.mainState = NOTE_OFF; // initialize main state machine
state.activeMIDIchannel = currentPreset->MIDIchannel; instrument.activeMIDIchannel = currentPreset->MIDIchannel;
midiInitialize(currentPreset->MIDIchannel); midiInitialize(currentPreset->MIDIchannel);
} }
@ -709,18 +721,18 @@ void runStateMachine() {
int fingeredNote = noteValueCheck(readSwitches() + readOctave()); int fingeredNote = noteValueCheck(readSwitches() + readOctave());
if (state.mainState == NOTE_OFF) { if (state.mainState == NOTE_OFF) {
handleOffStateActions(); handleOffStateActions();
if ((state.breathSignal > state.breathThrVal)) { if ((instrument.breathSignal > instrument.breathThrVal)) {
// 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();
initial_breath_value = state.breathSignal; initial_breath_value = instrument.breathSignal;
state.mainState = RISE_WAIT; // Go to next state state.mainState = RISE_WAIT; // Go to next state
} }
} else if (state.mainState == RISE_WAIT) { } else if (state.mainState == RISE_WAIT) {
if ((state.breathSignal > state.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 > currentPreset->velSmpDl) || (0 == currentPreset->velSmpDl) || currentPreset->fixedVelocity) {
noteOn(fingeredNote, state.breathSignal, initial_breath_value); noteOn(fingeredNote, instrument.breathSignal, initial_breath_value);
state.mainState = NOTE_ON; state.mainState = NOTE_ON;
} }
} else { } else {
@ -728,19 +740,19 @@ void runStateMachine() {
state.mainState = NOTE_OFF; state.mainState = NOTE_OFF;
} }
} else if (state.mainState == NOTE_ON) { } else if (state.mainState == NOTE_ON) {
if ((state.breathSignal < state.breathThrVal)) { if ((instrument.breathSignal < instrument.breathThrVal)) {
// Value has fallen below threshold - turn the note off // Value has fallen below threshold - turn the note off
midiSendNoteOff(state.activeNote); // send Note Off message midiSendNoteOff(instrument.activeNote); // send Note Off message
state.breathSignal = 0; instrument.breathSignal = 0;
state.mainState = NOTE_OFF; state.mainState = NOTE_OFF;
} else { } else {
if (fingeredNote != state.activeNote) { if (fingeredNote != instrument.activeNote) {
// Player has moved to a new fingering while still blowing. // Player has moved to a new fingering while still blowing.
// Send a note off for the current note and a note on for // Send a note off for the current note and a note on for
// the new note. // the new note.
noteOn(fingeredNote, state.breathSignal, 0); noteOn(fingeredNote, instrument.breathSignal, 0);
delayMicroseconds(2000); // delay for midi recording fix delayMicroseconds(2000); // delay for midi recording fix
midiSendNoteOff(state.activeNote); // send Note Off message midiSendNoteOff(instrument.activeNote); // send Note Off message
} }
} }
} }
@ -748,16 +760,26 @@ void runStateMachine() {
void setup() { void setup() {
if (checkButtonState(DEBUG_CONFIG)) { if (checkButtonState(DEBUG_CONFIG)) {
}
Serial.begin(9600); // debug Serial.begin(9600); // debug
Serial.println("Debug Startup"); Serial.println("Debug Startup");
if (CrashReport) {
while (!Serial) ; // wait for serial monitor open
Serial.print(CrashReport);
} }
initHardware();
delay(100);
Serial.println(buttonState());
bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET); bool factoryReset = checkButtonState(STARTUP_FACTORY_RESET);
configManagementMode = checkButtonState(STARTUP_CONFIG); configManagementMode = checkButtonState(STARTUP_CONFIG);
testMode = checkButtonState(TEST_CONFIG); testMode = checkButtonState(TEST_CONFIG);
initDisplay(); // Start up display and show logo initDisplay(); // Start up display and show logo
initHardware();
if (CrashReport) {
displayError("CRASH WARNING");
}
// If going into config management mode, stop here before we even touch the EEPROM. // If going into config management mode, stop here before we even touch the EEPROM.
if (configManagementMode) { if (configManagementMode) {
@ -766,24 +788,25 @@ void setup() {
} }
// Read eeprom data into global vars // Read eeprom data into global vars
readEEPROM(factoryReset); // readEEPROM(factoryReset);
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();
} }
//_______________________________________________________________________________________________ MAIN LOOP //_______________________________________________________________________________________________ MAIN LOOP
@ -792,6 +815,9 @@ 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();
@ -799,10 +825,11 @@ void loop() {
} }
if (testMode) { if (testMode) {
handleTestMode();
return;
} }
state.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
runStateMachine(); runStateMachine();
sendCCs(); sendCCs();
@ -815,9 +842,9 @@ void loop() {
// this is one of the big reasons the display is for setup use only // this is one of the big reasons the display is for setup use only
// TODO: is this still true on teensy 4? // TODO: is this still true on teensy 4?
pixelUpdateTime = millis(); pixelUpdateTime = millis();
handleMenu(true); handleMenu(state, true);
} else { } else {
handleMenu(false); handleMenu(state, false);
} }
} }