Added better support for ACC mode

This commit is contained in:
Brian Hrebec 2024-11-08 22:29:39 -06:00
parent b401b34bb4
commit e3c91fa6ee
5 changed files with 118 additions and 47 deletions

View file

@ -32,6 +32,12 @@ enum FingeringMode : uint8_t {
HRN = 3,
};
enum NoteMode : uint8_t {
BREATH_TRIGGER = 1,
LEVER_TRIGGER = 2,
NO_TRIGGER = 3,
};
enum RollerMode : uint8_t {
HIGHEST = 1,
HIGHEST_EXTEND = 2,
@ -62,6 +68,7 @@ enum ExtraControl : uint8_t {
CC = 3,
BEND = 4,
VIB_BEND = 5,
VELOCITY_RETRIGGER = 6,
};
enum PolySelect : uint8_t {

View file

@ -982,18 +982,25 @@ PresetValueMenu<1, uint8_t> vibRetnMenu("RETURN", &preset_t::vibRetn, 0, 4, true
PresetValueMenu<0, uint8_t> vibSenseMenu("SENSE LVR", &preset_t::vibSens, 0, 12);
PresetValueMenu<0, uint8_t> vibSquelchMenu("SQUELCH LVR", &preset_t::vibSquelch, 0, 12);
ChoiceMenu<2, VibratoMode> vibDirMenu("DIRECTION", &preset_t::vibratoMode, { {VSTART_DOWN, VSTART_UP} }, { "DWN", "UP" });
ChoiceMenu<4, ExtraControl> biteCtlMenu("BITE CTL", &preset_t::biteControl, { {OFF, VIBRATO, GLIDE, CC} }, { {
ChoiceMenu<3, NoteMode> noteModeMenu("NOTE MODE", &preset_t::noteMode, { {BREATH_TRIGGER, LEVER_TRIGGER, NO_TRIGGER} }, { {
"BREATH",
"LEVER",
"OFF",
} });
ChoiceMenu<5, ExtraControl> biteCtlMenu("BITE CTL", &preset_t::biteControl, { {OFF, VIBRATO, GLIDE, CC, VELOCITY_RETRIGGER} }, { {
"OFF",
"VIBRATO",
"GLIDE",
"CC"
"CC",
"VEL RT"
} });
PresetValueMenu<128, uint8_t> biteCCMenu("BITE CC", &preset_t::biteCC, 0, 127, true, CC_NAMES);
ChoiceMenu<4, ExtraControl> leverCtlMenu("LEVER CTL", &preset_t::leverControl, { {OFF, VIBRATO, GLIDE, CC} }, {
ChoiceMenu<5, ExtraControl> leverCtlMenu("LEVER CTL", &preset_t::leverControl, { {OFF, VIBRATO, GLIDE, CC, VELOCITY_RETRIGGER} }, {
"OFF",
"VIB",
"GLD",
"CC"
"CC",
"VEL RT"
});
PresetValueMenu<128, uint8_t> leverCCMenu("LEVER CC", &preset_t::leverCC, 0, 127, true, CC_NAMES);
ChoiceMenu<4, PortamentoMode> portMenu("GLIDE MOD", &preset_t::portamentoMode, { {POFF, PON, PSWITCH_ONLY, PGLIDE_ONLY} }, {
@ -1006,7 +1013,7 @@ PresetValueMenu<0, uint8_t> portLimitMenu("GLIDE LMT", &preset_t::portamentoLimi
PresetValueMenu<0, uint8_t> pitchBendMenu("PITCHBEND", &preset_t::PBdepth, 0, 12, true);
ChoiceMenu<4, ExtraControl> extraCtlMenu("EXT CTL", &preset_t::extraControl, { {OFF, VIBRATO, GLIDE, CC} }, { "OFF", "VIBRATO", "GLIDE", "CC" });
PresetValueMenu<128, uint8_t> extraCCMenu("EXT CC", &preset_t::extraCC, 0, 127, true, CC_NAMES);
ChoiceMenu<6, ExtraControl> pbCtlMenu("PB CTL", &preset_t::pbControl, { {OFF, VIBRATO, GLIDE, CC, BEND, VIB_BEND} }, { "OFF", "VIBRATO", "GLIDE", "CC", "BEND", "VIB_BEND" });
ChoiceMenu<7, ExtraControl> pbCtlMenu("PB CTL", &preset_t::pbControl, { {OFF, VIBRATO, GLIDE, CC, BEND, VIB_BEND, VELOCITY_RETRIGGER} }, { "OFF", "VIBRATO", "GLIDE", "CC", "BEND", "VIB+BEND", "VEL RT" });
PresetValueMenu<128, uint8_t> pbCCMenu("PB CC", &preset_t::pbCC, 0, 127, true, CC_NAMES);
PresetValueMenu<128, uint8_t> pbYCCMenu("PB Y CC", &preset_t::pbYCC, 0, 127, true, CC_NAMES);
PresetValueMenu<128, uint8_t> stickXCCMenu("STICK X CC", &preset_t::stickXCC, 0, 127, true, CC_NAMES);
@ -1088,10 +1095,11 @@ std::array<MenuScreen *const, 11> breathMenuEntries = {
};
SubMenu<11> breathMenu("BR SETUP", breathMenuEntries);
const std::array<MenuScreen *const, 25> controlMenuEntries = {
const std::array<MenuScreen *const, 26> controlMenuEntries = {
&fingeringMenu,
&polyMenu,
&rollerMenu,
&noteModeMenu,
&biteCtlMenu,
&biteCCMenu,
&pbCtlMenu,
@ -1115,7 +1123,7 @@ const std::array<MenuScreen *const, 25> controlMenuEntries = {
&accelModeMenu,
&accelCCMenu,
};
SubMenu<25> controlMenu("CTL SETUP", controlMenuEntries);
SubMenu<26> controlMenu("CTL SETUP", controlMenuEntries);
const std::array<MenuScreen *const, 5> vibratoMenuEntries = {
&vibDepthMenu,

View file

@ -347,6 +347,9 @@ void readEEPROM(const bool factoryReset, calibration_t &calibration) {
presets[i].spikeOnFactor = 5;
presets[i].spikeOffFactor = 5;
}
if (settings_version < 6) {
presets[i].noteMode = BREATH_TRIGGER;
}
}
writePresets();

View file

@ -5,7 +5,7 @@
#include "globals.h"
#define EEPROM_VERSION 5
#define EEPROM_VERSION 6
#define EEPROM_VERSION_ADDR 0
#define SETTINGS_OFFSET 2
#define PRESET_MAX_SIZE 128 // Leave extra space for future settings
@ -106,8 +106,9 @@ struct preset_t {
uint8_t pbYCC = 13; // pbY CC selection
uint8_t stickXCC = 14; // stickX CC selection
uint8_t stickYCC = 15; // stickY CC selection
NoteMode noteMode = NoteMode::BREATH_TRIGGER;
uint8_t _reserved[77];
uint8_t _reserved[76];
};
static_assert(sizeof(preset_t) == PRESET_MAX_SIZE, "preset_t must be 128 bytes");

View file

@ -217,12 +217,36 @@ int patchLimit(int value) {
int breath() {
static int oldbreath = 0;
static unsigned int oldbreathhires = 0;
static int oldbreathhires = 0;
int breathCCval, breathCCvalFine;
unsigned int breathCCvalHires;
breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383));
breathCCvalHires = breathCCFilter.input(breathCCvalHires);
int breathCCvalHires;
if (state.currentPreset->breathMode == BREATH_ACC || state.currentPreset->breathMode == BREATH_ACC_AT) {
int diff = instrument.breathSignal - instrument.breathZero;
if (instrument.mode == MODE_DEBUG) {
Serial.print(">accdiff:");
Serial.println(diff);
}
if (abs(diff) > calibration.breathThrValOffset) {
if (diff > 0) {
breathCCvalHires = breathCurve(mapConstrain(diff, calibration.breathThrValOffset, calibration.breathMaxValOffset, 0, 16383));
} else {
breathCCvalHires = -breathCurve(mapConstrain(-diff, calibration.breathThrValOffset, calibration.breathMaxValOffset, 0, 16383));
}
// breathCCvalHires = breathCCFilter.input(breathCCvalHires);
if (instrument.mode == MODE_DEBUG) {
Serial.print(">accdelta:");
Serial.println(breathCCvalHires);
}
breathCCvalHires = constrain(oldbreathhires + breathCCvalHires / 4, 0, 16383);
} else {
breathCCvalHires = oldbreathhires;
}
} else {
breathCCvalHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, 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
@ -240,7 +264,7 @@ int breath() {
}
if (breathCCvalHires != oldbreathhires &&
(state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT)) {
(state.currentPreset->breathMode == BreathMode::BREATH_LSB || state.currentPreset->breathMode == BreathMode::BREATH_LSB_AT|| state.currentPreset->breathMode == BreathMode::BREATH_ACC)) {
midiSendControlChange(state.currentPreset->breathCC + 32, breathCCvalFine);
}
@ -396,7 +420,7 @@ void portamento_() {
void sendCC() {
if (ExtraControl::CC == state.currentPreset->biteControl) {
int biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 0, 127);
int biteVal = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 127, 0);
if (biteVal != instrument.biteVal) {
midiSendControlChange(state.currentPreset->biteCC, biteVal);
@ -413,14 +437,13 @@ void sendCC() {
instrument.extraVal = extraVal;
}
int leverVal = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, 127);
if (ExtraControl::CC == state.currentPreset->leverControl) {
int leverVal = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 0, 127);
if (leverVal != instrument.leverVal) {
midiSendControlChange(state.currentPreset->leverCC, leverVal);
}
instrument.leverVal = leverVal;
}
instrument.leverVal = leverVal;
if (ExtraControl::CC == state.currentPreset->pbControl) {
int pbVal = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbMaxVal, 0, 127);
@ -778,20 +801,42 @@ void readSwitches() {
lastOctave = fingeredOctave;
}
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.breathBaseline, instrument.breathMaxVal);
byte velocitySend;
if (!state.currentPreset->fixedVelocity) {
unsigned int breathValHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383));
velocitySend = (breathValHires >> 7) & 0x007F;
velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127);
} else {
velocitySend = state.currentPreset->fixedVelocity;
uint8_t controlVel() {
uint8_t velocity = 0;
if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->biteControl) {
velocity = mapConstrain(instrument.biteSignal, calibration.biteThrVal, calibration.biteMaxVal, 127, 1);
}
midiSendNoteOn(fingeredNote, velocitySend); // send Note Off message
if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->leverControl) {
velocity = mapConstrain(instrument.leverSignal, calibration.leverMinVal, calibration.leverMaxVal, 1, 127);
}
if (ExtraControl::VELOCITY_RETRIGGER == state.currentPreset->pbControl) {
velocity = mapConstrain(instrument.pbSignal, calibration.pbMinVal, calibration.pbMaxVal, 1, 127);
}
return velocity;
}
uint8_t breathVel() {
// Set velocity based on current pressureSensor value unless fixed velocity is set
int velocitySend;
if (state.currentPreset->fixedVelocity) {
return state.currentPreset->fixedVelocity;
}
if (state.currentPreset->noteMode == LEVER_TRIGGER) {
return instrument.leverVal;
}
unsigned int breathValHires = breathCurve(mapConstrain(instrument.breathSignal, instrument.breathBaseline, instrument.breathMaxVal, 0, 16383));
velocitySend = (breathValHires >> 7) & 0x007F;
velocitySend = constrain(velocitySend + velocitySend * .1 * state.currentPreset->velBias, 1, 127);
return velocitySend;
}
void noteOn(int fingeredNote) {
midiSendNoteOn(fingeredNote, breathVel());
}
/**
@ -803,15 +848,20 @@ void changeNote(int fingeredNote, int activeNote, int fingeredNote2, int activeN
return;
}
int velocity = controlVel();
if (!velocity) {
velocity = breathVel();
}
// Player has moved to a new fingering while still blowing.
// Send a note off for the current note and a note on for
// the new note.
if (fingeredNote != activeNote && fingeredNote != activeNote2) {
noteOn(fingeredNote, instrument.breathSignal, 0);
midiSendNoteOn(fingeredNote, velocity);
}
if (fingeredNote2 != fingeredNote && fingeredNote2 != activeNote && fingeredNote2 != activeNote2) {
noteOn(fingeredNote2, instrument.breathSignal, 0);
midiSendNoteOn(fingeredNote2, velocity);
}
delayMicroseconds(2000); // delay for midi recording fix
@ -896,6 +946,11 @@ void handleCCs() {
}
}
int adjustRetriggerVel(int initialVel) {
int vel = initialVel;
}
/**
* Read the breath sensors according to the active mode
*/
@ -906,14 +961,7 @@ void readBreath() {
int16_t breathAltSignal = constrain(readAltPressure(), BREATH_LO_LIMIT, BREATH_HI_LIMIT) + instrument.breathAltZeroOffset;
int16_t spikeSignal = constrain(readSpikePressure(), -SPIKE_HI_LIMIT, SPIKE_HI_LIMIT);
int16_t diffSignal = breathAltSignal - breathSignal;
if (state.currentPreset->breathMode == BREATH_ACC || state.currentPreset->breathMode == BREATH_ACC_AT) {
int delta = breathSignal - instrument.breathZero;
if (abs(delta) > state.calibration->breathThrValOffset) {
instrument.breathSignal = constrain(instrument.breathSignal + delta / 15, instrument.breathZero, instrument.breathMaxVal);
}
} else {
instrument.breathSignal = breathSignal + diffSignal + (spikeSignal > 0 ? spikeSignal * state.currentPreset->spikeOnFactor : spikeSignal * state.currentPreset->spikeOffFactor);
}
instrument.breathSignal = breathSignal + diffSignal + (spikeSignal > 0 ? spikeSignal * state.currentPreset->spikeOnFactor : spikeSignal * state.currentPreset->spikeOffFactor);
instrument.breathAltSignal = breathAltSignal;
@ -962,23 +1010,27 @@ void readBreath() {
*/
void runStateMachine() {
static unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold
static int initial_breath_value = 0; // The breath value at the time we observed the transition
bool lever_trigger = state.currentPreset->noteMode == LEVER_TRIGGER;
if (state.currentPreset->noteMode == NO_TRIGGER) {
return;
}
if (state.mainState == NOTE_OFF) {
handleOffStateActions();
if (instrument.breathSignal > instrument.breathThrVal && state.mainState == NOTE_OFF) {
bool crossed_threshold = lever_trigger ? instrument.leverVal > 0 : instrument.breathSignal > instrument.breathThrVal;
if (crossed_threshold && 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();
initial_breath_value = instrument.breathSignal;
state.mainState = RISE_WAIT; // Go to next state
}
} else if (state.mainState == RISE_WAIT) {
if ((instrument.breathSignal > instrument.breathMovingThrVal)) {
if ((lever_trigger ? instrument.leverVal > 0 : instrument.breathSignal > instrument.breathMovingThrVal)) {
// Has enough time passed for us to collect our second sample?
if ((millis() - breath_on_time > state.currentPreset->velSmpDl) || (0 == state.currentPreset->velSmpDl) || state.currentPreset->fixedVelocity) {
noteOn(instrument.fingeredNote, instrument.breathSignal, initial_breath_value);
noteOn(instrument.fingeredNote);
if (instrument.fingeredNote != instrument.fingeredNote2) {
noteOn(instrument.fingeredNote2, instrument.breathSignal, initial_breath_value);
noteOn(instrument.fingeredNote2);
}
breath(); // send breath data
state.mainState = NOTE_ON;
@ -990,7 +1042,7 @@ void runStateMachine() {
state.mainState = NOTE_OFF;
}
} else if (state.mainState == NOTE_ON) {
if (instrument.breathSignal < instrument.breathThrOffVal) {
if (lever_trigger ? instrument.leverVal <= 0 : instrument.breathSignal < instrument.breathThrOffVal) {
// Value has fallen below threshold - turn the note off
midiSendNoteOff(instrument.activeNote); // send Note Off message
if (instrument.activeNote != instrument.activeNote2) {