* Glide setting SWO, SWitching Only, sending only Glide on/off (CC#65) for use with synths where glide rate CC#5 is used in non standard ways, for example some DSI/Sequential synths like the Prophet REV2 or Prophet 12 where glide rates are set individually for each oscillator. * Glide limit setting for portamento. Doubles as setting for portamento level sent using pinky key/mod key in GLD mode. (Can be changed both in menu and in GLD mode.) * Setting of level for LVL, LVP and GLD now reqires touching both pinky/mod and third trill/RHp3 for setting mode activation, this to avoid accidental change of setting when pinky/mod key is touched. * Rate of setting movement up and down for LVL, LVP and GLD has been adjusted. Became very much too fast after the timing issues were solved in 1.5b1 * A short delay before note offs in legato transitions is added to make playback of recorded midi behave correctly (keeping note on and note off from being registered on the same timestamp).
431 lines
12 KiB
C++
431 lines
12 KiB
C++
#include <Arduino.h>
|
|
#include <Adafruit_SSD1306.h>
|
|
#include <Adafruit_MPR121.h>
|
|
|
|
#include "menu.h"
|
|
#include "numenu.h"
|
|
#include "globals.h"
|
|
#include "config.h"
|
|
#include "hardware.h"
|
|
#include "settings.h"
|
|
|
|
//***********************************************************
|
|
|
|
extern Adafruit_SSD1306 display;
|
|
#if defined(NURAD)
|
|
extern Adafruit_MPR121 touchSensorRH;
|
|
extern Adafruit_MPR121 touchSensorLH;
|
|
extern Adafruit_MPR121 touchSensorRollers;
|
|
#else
|
|
extern Adafruit_MPR121 touchSensor;
|
|
#endif
|
|
extern byte cursorNow;
|
|
#if defined(NURAD)
|
|
extern int calOffsetRollers[6];
|
|
extern int calOffsetRH[12];
|
|
extern int calOffsetLH[12];
|
|
#endif
|
|
|
|
//***********************************************************
|
|
// Variables used for the adjust menu
|
|
static byte forcePix;
|
|
|
|
static uint16_t pos1;
|
|
static uint16_t pos2;
|
|
|
|
static int16_t adjustOption;
|
|
static int16_t adjustCurrent;
|
|
|
|
static int16_t sensorPixelPos1 = -1;
|
|
static int16_t sensorPixelPos2 = -1;
|
|
|
|
static bool refreshScreen;
|
|
|
|
//***********************************************************
|
|
|
|
static void breathSave(const AdjustMenuEntry& e) {
|
|
writeSetting(BREATH_THR_ADDR, *e.entries[0].value);
|
|
writeSetting(BREATH_MAX_ADDR, *e.entries[1].value);
|
|
}
|
|
|
|
const AdjustMenuEntry breathAdjustMenu = {
|
|
"BREATH",
|
|
{
|
|
{ &breathThrVal, breathLoLimit, breathHiLimit },
|
|
{ &breathMaxVal, breathLoLimit, breathHiLimit }
|
|
},
|
|
breathSave
|
|
};
|
|
|
|
static void portamentoSave(const AdjustMenuEntry& e) {
|
|
writeSetting(PORTAM_THR_ADDR, *e.entries[0].value);
|
|
writeSetting(PORTAM_MAX_ADDR, *e.entries[1].value);
|
|
}
|
|
|
|
const AdjustMenuEntry portamentoAdjustMenu = {
|
|
"BITE",
|
|
{
|
|
{ &portamThrVal, portamLoLimit, portamHiLimit },
|
|
{ &portamMaxVal, portamLoLimit, portamHiLimit }
|
|
},
|
|
portamentoSave
|
|
};
|
|
|
|
static void pbSave(const AdjustMenuEntry& e) {
|
|
writeSetting(PITCHB_THR_ADDR, *e.entries[0].value);
|
|
writeSetting(PITCHB_MAX_ADDR, *e.entries[1].value);
|
|
}
|
|
|
|
const AdjustMenuEntry pitchBendAdjustMenu = {
|
|
"PITCH BEND",
|
|
{
|
|
{ &pitchbThrVal, pitchbLoLimit, pitchbHiLimit },
|
|
{ &pitchbMaxVal, pitchbLoLimit, pitchbHiLimit }
|
|
},
|
|
pbSave
|
|
};
|
|
|
|
static void extracSave(const AdjustMenuEntry& e) {
|
|
writeSetting(EXTRAC_THR_ADDR, *e.entries[0].value);
|
|
writeSetting(EXTRAC_MAX_ADDR, *e.entries[1].value);
|
|
}
|
|
|
|
const AdjustMenuEntry extraSensorAdjustMenu = {
|
|
"EXTRA CONTROLLER",
|
|
{
|
|
{ &extracThrVal, extracLoLimit, extracHiLimit },
|
|
{ &extracMaxVal, extracLoLimit, extracHiLimit }
|
|
},
|
|
extracSave
|
|
};
|
|
|
|
|
|
static void ctouchThrSave(const AdjustMenuEntry& e) {
|
|
writeSetting(CTOUCH_THR_ADDR, *e.entries[0].value);
|
|
}
|
|
|
|
const AdjustMenuEntry ctouchAdjustMenu = {
|
|
"TOUCH SENSE",
|
|
{
|
|
{ &ctouchThrVal, ctouchLoLimit, ctouchHiLimit },
|
|
{ nullptr, 0, 0 }
|
|
},
|
|
ctouchThrSave
|
|
};
|
|
|
|
|
|
static void leverSave(const AdjustMenuEntry& e) {
|
|
writeSetting(LEVER_THR_ADDR, *e.entries[0].value);
|
|
writeSetting(LEVER_MAX_ADDR, *e.entries[1].value);
|
|
}
|
|
|
|
const AdjustMenuEntry leverAdjustMenu = {
|
|
"THUMB LEVER",
|
|
{
|
|
{ &leverThrVal, leverLoLimit, leverHiLimit },
|
|
{ &leverMaxVal, leverLoLimit, leverHiLimit }
|
|
},
|
|
leverSave
|
|
};
|
|
|
|
|
|
const AdjustMenuEntry* adjustMenuEntries[] = {
|
|
&breathAdjustMenu,
|
|
&portamentoAdjustMenu,
|
|
&pitchBendAdjustMenu,
|
|
&extraSensorAdjustMenu,
|
|
&ctouchAdjustMenu,
|
|
&leverAdjustMenu,
|
|
};
|
|
|
|
static const int numAdjustEntries = ARR_LEN(adjustMenuEntries);
|
|
|
|
//***********************************************************
|
|
|
|
static void drawAdjCursor(byte color) {
|
|
display.drawTriangle(16,4,20,4,18,1,color);
|
|
display.drawTriangle(16,6,20,6,18,9,color);
|
|
}
|
|
|
|
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 drawAdjustBase(const char* title, bool all) {
|
|
display.clearDisplay();
|
|
|
|
drawAdjustFrame(17);
|
|
|
|
// sensor marker lines.
|
|
display.drawLine(25,36,25,40,WHITE);
|
|
display.drawLine(120,36,120,40,WHITE);
|
|
|
|
display.setTextSize(1);
|
|
display.setCursor(25,2);
|
|
display.println(title);
|
|
|
|
display.setCursor(0,20);
|
|
display.println("THR");
|
|
display.setCursor(0,35);
|
|
display.println("SNS");
|
|
|
|
|
|
if(all) {
|
|
drawAdjustFrame(47);
|
|
display.setCursor(0,50);
|
|
display.println("MAX");
|
|
}
|
|
cursorNow = WHITE;
|
|
drawAdjCursor(WHITE);
|
|
}
|
|
|
|
static void drawLineCursor(uint16_t hPos, uint16_t vPos, int color) {
|
|
display.drawLine(hPos, vPos,hPos, vPos+6, color);
|
|
}
|
|
|
|
static bool updateAdjustLineCursor(uint32_t timeNow, uint16_t hPos, uint16_t vPos ) {
|
|
if ((timeNow - cursorBlinkTime) > cursorBlinkInterval) {
|
|
if (cursorNow == WHITE) cursorNow = BLACK; else cursorNow = WHITE;
|
|
drawLineCursor(hPos, vPos, cursorNow);
|
|
cursorBlinkTime = timeNow;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void drawAdjustMenu(const AdjustMenuEntry *menu) {
|
|
|
|
bool haveSecondValue = menu->entries[1].value != nullptr;
|
|
drawAdjustBase( menu->title, haveSecondValue );
|
|
|
|
pos1 = map( *menu->entries[0].value, menu->entries[0].limitLow, menu->entries[0].limitHigh, 27, 119);
|
|
display.drawLine( pos1, 20, pos1, 26, WHITE );
|
|
|
|
if(haveSecondValue) {
|
|
pos2 = map( *menu->entries[1].value, menu->entries[1].limitLow, menu->entries[1].limitHigh, 27, 119);
|
|
display.drawLine( pos2, 50, pos2, 56, WHITE );
|
|
}
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
static bool updateSensorPixel(int pos, int pos2) {
|
|
bool update = pos != sensorPixelPos1 || pos2 != sensorPixelPos2;
|
|
if(update) {
|
|
display.drawFastHLine(28, 38, 119-28, BLACK); // Clear old line
|
|
display.drawPixel(pos, 38, WHITE);
|
|
if( pos2 >= 0) display.drawPixel(pos2, 38, WHITE);
|
|
|
|
sensorPixelPos1 = pos;
|
|
sensorPixelPos2 = pos2;
|
|
}
|
|
return update;
|
|
}
|
|
|
|
void plotSensorPixels(){
|
|
int redraw = 0;
|
|
|
|
if(forcePix)
|
|
sensorPixelPos1 = -1;
|
|
|
|
// This is hacky. It depends on the order of items in the adjust menu list.
|
|
if(adjustOption == 0) {
|
|
int pos = map(constrain(pressureSensor, breathLoLimit, breathHiLimit), breathLoLimit, breathHiLimit, 28, 118);
|
|
redraw = updateSensorPixel(pos, -1);
|
|
}
|
|
else if(adjustOption == 1) {
|
|
int pos = map(constrain(biteSensor,portamLoLimit,portamHiLimit), portamLoLimit, portamHiLimit, 28, 118);
|
|
redraw = updateSensorPixel(pos, -1);
|
|
}
|
|
else if(adjustOption == 2) {
|
|
int pos = map(constrain(pbUp, pitchbLoLimit, pitchbHiLimit), pitchbLoLimit, pitchbHiLimit, 28, 118);
|
|
int pos2 = map(constrain(pbDn, pitchbLoLimit, pitchbHiLimit), pitchbLoLimit, pitchbHiLimit, 28, 118);
|
|
redraw = updateSensorPixel(pos, pos2);
|
|
}
|
|
else if(adjustOption == 3) {
|
|
int pos = map(constrain(exSensor, extracLoLimit, extracHiLimit), extracLoLimit, extracHiLimit, 28, 118);
|
|
redraw = updateSensorPixel(pos, -1);
|
|
}
|
|
#if defined(NURAD)
|
|
else if(adjustOption == 4) {
|
|
display.drawLine(28,37,118,37,BLACK);
|
|
for (byte i=0; i<12; i++){
|
|
//int pos = map(constrain(touchSensorRH.filteredData(i) - calOffsetRH[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
int pos = map(constrain(touchSensorRH.filteredData(i) * (300-calOffsetRH[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
display.drawPixel(pos, 37, WHITE);
|
|
}
|
|
display.drawLine(28,38,118,38,BLACK);
|
|
for (byte i=0; i<12; i++){
|
|
//int pos = map(constrain(touchSensorLH.filteredData(i) - calOffsetLH[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
int pos = map(constrain(touchSensorLH.filteredData(i) * (300-calOffsetLH[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
display.drawPixel(pos, 38, WHITE);
|
|
}
|
|
display.drawLine(28,39,118,39,BLACK);
|
|
for (byte i=0; i<6; i++){
|
|
//int pos = map(constrain(touchSensorRollers.filteredData(i) - calOffsetRollers[i], ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
int pos = map(constrain(touchSensorRollers.filteredData(i) * (300-calOffsetRollers[i])/300, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
display.drawPixel(pos, 39, WHITE);
|
|
}
|
|
redraw = 1;
|
|
}
|
|
#else //NuEVI
|
|
else if(adjustOption == 4) {
|
|
display.drawLine(28,39,118,39,BLACK);
|
|
for (byte i=0; i<12; i++){
|
|
int pos = map(constrain(touchSensor.filteredData(i), ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
display.drawPixel(pos, 39, WHITE);
|
|
}
|
|
|
|
int posRead = map(touchRead(halfPitchBendKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
|
|
int pos = map(constrain(posRead, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
|
|
posRead = map(touchRead(specialKeyPin),ttouchLoLimit,ttouchHiLimit,ctouchHiLimit,ctouchLoLimit);
|
|
int pos2 = map(constrain(posRead, ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
|
|
|
updateSensorPixel(pos, pos2);
|
|
|
|
redraw = 1;
|
|
}
|
|
#endif
|
|
else if(adjustOption == 5) {
|
|
int pos = map(constrain(3000-touchRead(vibratoPin), leverLoLimit, leverHiLimit), leverLoLimit, leverHiLimit, 28, 118);
|
|
redraw = updateSensorPixel(pos, -1);
|
|
}
|
|
|
|
|
|
if (redraw){
|
|
display.display();
|
|
}
|
|
forcePix = 0;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
static bool drawAdjustBar(uint16_t buttons, int row, const AdjustValue* entry, uint16_t *pos) {
|
|
bool updated = false;
|
|
uint16_t step = (entry->limitHigh-entry->limitLow)/92;
|
|
int val = *entry->value;
|
|
switch(buttons) {
|
|
case BTN_UP:
|
|
val += step;
|
|
updated = true;
|
|
break;
|
|
|
|
case BTN_DOWN:
|
|
val -= step;
|
|
updated = true;
|
|
break;
|
|
}
|
|
if(updated) {
|
|
*entry->value = constrain(val, entry->limitLow, entry->limitHigh);
|
|
auto p = *pos;
|
|
display.drawLine(p, row, p, row+6, BLACK);
|
|
*pos = p = map(*entry->value, entry->limitLow, entry->limitHigh, 27, 119);
|
|
display.drawLine(p, row, p, row+6, WHITE);
|
|
cursorNow = BLACK;
|
|
}
|
|
return updated;
|
|
}
|
|
|
|
static bool updateAdjustCursor(uint32_t timeNow) {
|
|
if ((timeNow - cursorBlinkTime) > cursorBlinkInterval) {
|
|
if (cursorNow == WHITE) cursorNow = BLACK; else cursorNow = WHITE;
|
|
drawAdjCursor(cursorNow);
|
|
cursorBlinkTime = timeNow;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool handleInput(const AdjustMenuEntry *currentMenu, uint32_t timeNow, uint8_t buttons, uint16_t *xpos, int ypos, int index) {
|
|
if (buttons) {
|
|
drawAdjustBar( buttons, ypos, ¤tMenu->entries[index], xpos );
|
|
int last = adjustCurrent;
|
|
if(buttons == BTN_ENTER) adjustCurrent += 1;
|
|
else if( buttons == BTN_MENU) adjustCurrent = 0;
|
|
|
|
if(last != adjustCurrent) drawLineCursor(*xpos, ypos, WHITE);
|
|
|
|
return true;
|
|
} else {
|
|
return updateAdjustLineCursor(timeNow, *xpos, ypos);
|
|
}
|
|
}
|
|
|
|
int updateAdjustMenu(uint32_t timeNow, KeyState &input, bool firstRun, bool drawSensor) {
|
|
bool redraw = false;
|
|
int result = 0;
|
|
|
|
const AdjustMenuEntry *currentMenu = adjustMenuEntries[adjustOption];
|
|
|
|
uint8_t buttons = input.changed ? input.current : 0;
|
|
|
|
if(firstRun || refreshScreen) {
|
|
adjustCurrent = 0;
|
|
refreshScreen = false;
|
|
drawAdjustMenu(currentMenu);
|
|
// the sensor pixel stuff should be refactored (to work again)
|
|
forcePix = 1;
|
|
sensorPixelPos1 = -1; // Force draw of sensor pixels
|
|
}
|
|
|
|
if(adjustCurrent == 0) {
|
|
// Currently selecting what option to modify
|
|
redraw |= updateAdjustCursor(timeNow);
|
|
|
|
bool save = false;
|
|
if( buttons == BTN_DOWN ) {
|
|
adjustOption += 1;
|
|
refreshScreen = 1;
|
|
save = true;
|
|
}
|
|
else if ( buttons == BTN_UP ) {
|
|
adjustOption -= 1;
|
|
refreshScreen = 1;
|
|
save = true;
|
|
}
|
|
else if ( buttons == BTN_ENTER ) {
|
|
adjustCurrent = 1;
|
|
}
|
|
else if (buttons == BTN_MENU ) {
|
|
adjustCurrent = 0;
|
|
result = -1;
|
|
save = true;
|
|
}
|
|
|
|
if(save && currentMenu->saveFunc)
|
|
currentMenu->saveFunc(*currentMenu);
|
|
|
|
} else if( adjustCurrent == 1) {
|
|
redraw |= handleInput(currentMenu, timeNow, buttons, &pos1, 20, 0);
|
|
} else {
|
|
redraw |= handleInput(currentMenu, timeNow, buttons, &pos2, 50, 1);
|
|
}
|
|
|
|
// Keep adjustCurrent in range
|
|
if( (adjustCurrent > 2) || ((adjustCurrent == 2) && (currentMenu->entries[1].value == nullptr)))
|
|
adjustCurrent = 0;
|
|
|
|
// Keep adjust option in range.
|
|
if(adjustOption < 0)
|
|
adjustOption = numAdjustEntries-1;
|
|
else if (adjustOption >= numAdjustEntries)
|
|
adjustOption = 0;
|
|
|
|
|
|
if(drawSensor) {
|
|
plotSensorPixels();
|
|
}
|
|
|
|
if(result >= 0)
|
|
result = redraw;
|
|
|
|
return result;
|
|
}
|