Handle sub menus in a generic way and moved adjust menu logic to separate file
This is a big one where I can finally reap what I sown. Sub menu entries now provide two functions, one to get the text for the current value and one to apply changes aka save to EEPROM. With this I can replace so much code in the menu() function that handles input, but was _almost_ identical. The process of converting the old menus are not completed, and I can probably remove about 200 lines more code. The question is still what to do with less general menus like the rotator and fast patch menu. One problem with the current implementation is that it is RAM heavy. It seems the const MenuEntry structs are placed in ram, because there is a pointer to RAM that I assume is allocated during execution and therefore the address cannot be stored in ROM. My plan has been to put all the configuration fields (that are stored in EEPROM) into a struct or an array. When that is implemented, I can instead store the offset into the array in the const struct, which should be available at compile time and therefore can reside completely in ROM.
This commit is contained in:
parent
ca5b2b9484
commit
699546df8c
6 changed files with 724 additions and 762 deletions
359
NuEVI/adjustmenu.cpp
Normal file
359
NuEVI/adjustmenu.cpp
Normal file
|
@ -0,0 +1,359 @@
|
|||
#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;
|
||||
extern Adafruit_MPR121 touchSensor;
|
||||
extern byte cursorNow;
|
||||
|
||||
//***********************************************************
|
||||
// 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;
|
||||
|
||||
//***********************************************************
|
||||
|
||||
|
||||
const AdjustMenuEntry breathAdjustMenu = {
|
||||
"BREATH",
|
||||
{
|
||||
{ &breathThrVal, breathLoLimit, breathHiLimit },
|
||||
{ &breathMaxVal, breathLoLimit, breathHiLimit }
|
||||
},
|
||||
[] (const AdjustMenuEntry& e) {
|
||||
writeSetting(BREATH_THR_ADDR, *e.entries[0].value);
|
||||
writeSetting(BREATH_MAX_ADDR, *e.entries[1].value);
|
||||
}
|
||||
};
|
||||
|
||||
const AdjustMenuEntry portamentoAdjustMenu = {
|
||||
"PORTAMENTO",
|
||||
{
|
||||
{ &portamThrVal, portamLoLimit, portamHiLimit },
|
||||
{ &portamMaxVal, portamLoLimit, portamHiLimit }
|
||||
},
|
||||
[] (const AdjustMenuEntry& e) {
|
||||
writeSetting(PORTAM_THR_ADDR, *e.entries[0].value);
|
||||
writeSetting(PORTAM_MAX_ADDR, *e.entries[1].value);
|
||||
}
|
||||
};
|
||||
|
||||
const AdjustMenuEntry pitchBendAdjustMenu = {
|
||||
"PITCH BEND",
|
||||
{
|
||||
{ &pitchbThrVal, pitchbLoLimit, pitchbHiLimit },
|
||||
{ &pitchbMaxVal, pitchbLoLimit, pitchbHiLimit }
|
||||
},
|
||||
[] (const AdjustMenuEntry& e) {
|
||||
writeSetting(PITCHB_THR_ADDR, *e.entries[0].value);
|
||||
writeSetting(PITCHB_MAX_ADDR, *e.entries[1].value);
|
||||
}
|
||||
};
|
||||
|
||||
const AdjustMenuEntry extraSensorAdjustMenu = {
|
||||
"EXTRA CONTROLLER",
|
||||
{
|
||||
{ &extracThrVal, extracLoLimit, extracHiLimit },
|
||||
{ &extracMaxVal, extracLoLimit, extracHiLimit }
|
||||
},
|
||||
[] (const AdjustMenuEntry& e) {
|
||||
writeSetting(EXTRAC_THR_ADDR, *e.entries[0].value);
|
||||
writeSetting(EXTRAC_MAX_ADDR, *e.entries[1].value);
|
||||
}
|
||||
};
|
||||
|
||||
const AdjustMenuEntry ctouchAdjustMenu = {
|
||||
"TOUCH SENSE",
|
||||
{
|
||||
{ &ctouchThrVal, ctouchLoLimit, ctouchHiLimit },
|
||||
{ nullptr, 0, 0 }
|
||||
},
|
||||
[] (const AdjustMenuEntry& e) {
|
||||
writeSetting(CTOUCH_THR_ADDR, *e.entries[0].value);
|
||||
}
|
||||
};
|
||||
|
||||
const AdjustMenuEntry* adjustMenuEntries[] = {
|
||||
&breathAdjustMenu,
|
||||
&portamentoAdjustMenu,
|
||||
&pitchBendAdjustMenu,
|
||||
&extraSensorAdjustMenu,
|
||||
&ctouchAdjustMenu,
|
||||
};
|
||||
|
||||
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.setTextColor(WHITE);
|
||||
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 bool updateAdjustLineCursor(uint32_t timeNow, uint16_t hPos, uint16_t vPos ) {
|
||||
if ((timeNow - cursorBlinkTime) > cursorBlinkInterval) {
|
||||
if (cursorNow == WHITE) cursorNow = BLACK; else cursorNow = WHITE;
|
||||
display.drawLine(hPos, vPos,hPos, vPos+6, 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);
|
||||
}
|
||||
else if(adjustOption == 4) {
|
||||
display.drawLine(28,38,118,38,BLACK);
|
||||
for (byte i=0; i<12; i++){
|
||||
int pos = map(constrain(touchSensor.filteredData(i), ctouchLoLimit, ctouchHiLimit), ctouchLoLimit, ctouchHiLimit, 28, 118);
|
||||
display.drawPixel(pos, 38, 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
int updateAdjustMenu(uint32_t timeNow, uint8_t buttons, bool firstRun, bool drawSensor) {
|
||||
bool redraw = false;
|
||||
int result = 0;
|
||||
|
||||
const AdjustMenuEntry *currentMenu = adjustMenuEntries[adjustOption];
|
||||
|
||||
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) {
|
||||
if (buttons) {
|
||||
drawAdjustBar( buttons, 20, ¤tMenu->entries[0], &pos1 );
|
||||
if(buttons == BTN_ENTER) adjustCurrent += 1;
|
||||
else if( buttons == BTN_MENU) adjustCurrent = 0;
|
||||
redraw = true;
|
||||
} else {
|
||||
redraw |= updateAdjustLineCursor( timeNow, pos1, 20 );
|
||||
}
|
||||
} else {
|
||||
if (buttons) {
|
||||
drawAdjustBar( buttons, 50, ¤tMenu->entries[1], &pos2 );
|
||||
if(buttons == BTN_ENTER) adjustCurrent += 1;
|
||||
else if( buttons == BTN_MENU) adjustCurrent = 0;
|
||||
redraw = true;
|
||||
} else {
|
||||
redraw |= updateAdjustLineCursor( timeNow, pos2, 50 );
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
1080
NuEVI/menu.cpp
1080
NuEVI/menu.cpp
File diff suppressed because it is too large
Load diff
24
NuEVI/menu.h
24
NuEVI/menu.h
|
@ -4,8 +4,8 @@
|
|||
#include "Wiring.h"
|
||||
|
||||
#define MENU_ROW_HEIGHT 9
|
||||
#define MENU_HEADER_OFFSET 3
|
||||
#define MENU_NUM_ROWS 5
|
||||
#define MENU_HEADER_OFFSET 12
|
||||
#define MENU_NUM_ROWS 6
|
||||
|
||||
//display states
|
||||
#define DISPLAYOFF_IDL 0
|
||||
|
@ -17,6 +17,23 @@
|
|||
#define ROTATOR_MENU 100
|
||||
#define VIBRATO_MENU 110
|
||||
|
||||
#define ARR_LEN(a) (sizeof (a) / sizeof (a[0]))
|
||||
|
||||
#define BTN_DOWN 1
|
||||
#define BTN_ENTER 2
|
||||
#define BTN_UP 4
|
||||
#define BTN_MENU 8
|
||||
|
||||
|
||||
|
||||
extern const unsigned long debounceDelay; // the debounce time; increase if the output flickers
|
||||
extern const unsigned long buttonRepeatInterval;
|
||||
extern const unsigned long buttonRepeatDelay;
|
||||
extern const unsigned long cursorBlinkInterval; // the cursor blink toggle interval time
|
||||
extern const unsigned long patchViewTimeUp; // ms until patch view shuts off
|
||||
extern const unsigned long menuTimeUp; // menu shuts off after one minute of button inactivity
|
||||
|
||||
|
||||
|
||||
extern byte subVibSquelch;
|
||||
|
||||
|
@ -27,4 +44,7 @@ void drawSensorPixels();
|
|||
unsigned short readSetting(byte address);
|
||||
void writeSetting(byte address, unsigned short value);
|
||||
|
||||
int updateAdjustMenu(uint32_t timeNow, uint8_t buttons, bool firstRun, bool updateSensor);
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
enum MenuType {
|
||||
ESub,
|
||||
ESubNew,
|
||||
ESubRotator,
|
||||
EStateChange,
|
||||
};
|
||||
|
@ -22,6 +23,25 @@ struct MenuEntrySub {
|
|||
void (*subMenuFunc)(int color);
|
||||
};
|
||||
|
||||
enum MenuEntryFlags {
|
||||
ENone = 0,
|
||||
EWrap = (1<<0),
|
||||
ECustom = (1<<1),
|
||||
};
|
||||
|
||||
struct MenuEntrySubNew {
|
||||
enum MenuType type;
|
||||
const char* title;
|
||||
const char* subTitle;
|
||||
uint16_t* valuePtr;
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
uint16_t flags;
|
||||
void (*getSubTextFunc)(char*textBuffer, const char**label);
|
||||
void (*applyFunc)(void);
|
||||
};
|
||||
|
||||
|
||||
struct MenuEntrySubRotator {
|
||||
enum MenuType type;
|
||||
const char* title;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue