Refactored for teensy 4.0, xEvi hardware

- Switched to platformio, ino -> cpp
- MPRLS for pressure sensor
- Added basic ICM support
- Removed widi, battery, other features not supported in xEvi
- Removed legacy options/processing
- Added LED strip support
- Added encoder support
- Reworked menu code to use encoders/be more flexible
This commit is contained in:
Brian Hrebec 2023-08-27 11:52:08 -05:00
parent c58c3f9e46
commit 01d193c9b3
92 changed files with 69119 additions and 73272 deletions

755
NuEVI/src/menu.cpp Normal file
View file

@ -0,0 +1,755 @@
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_MPR121.h>
#include <Arduino.h>
#include <array>
#include <cstdio>
#include "menu.h"
#include "hardware.h"
#include "config.h"
#include "settings.h"
#include "globals.h"
#include "midi.h"
#include "led.h"
// constants
const unsigned long debounceDelay = 30; // the debounce time; increase if the output flickers
const unsigned long buttonRepeatInterval = 50;
const unsigned long buttonRepeatDelay = 400;
const unsigned long cursorBlinkInterval = 300; // the cursor blink toggle interval time
const unsigned long patchViewTimeUp = 2000; // ms until patch view shuts off
const unsigned long menuTimeUp = 60000; // menu shuts off after one minute of button inactivity
static unsigned long menuTime = 0;
static unsigned long patchViewTime = 0;
unsigned long cursorBlinkTime = 0; // the last time the cursor was toggled
std::array<const char *, 128> CC_NAMES = {
"Bank Select", // 0
"Mod Wheel", // 1
"Breath", // 2
"Undefined", // 3
"Foot Pedal", // 4
"Port. Time", // 5
"Data Entry", // 6
"Volume", // 7
"Balance", // 8
"Undefined", // 9
"Pan", // 10
"Expression", // 11
"Effect 1", // 12
"Effect 2", // 13
"Undefined", // 14
"Undefined", // 15
"GP 1", // 16
"GP 2", // 17
"GP 3", // 18
"GP 3", // 19
"Undefined", // 20
"Undefined", // 21
"Undefined", // 22
"Undefined", // 23
"Undefined", // 24
"Undefined", // 25
"Undefined", // 26
"Undefined", // 27
"Undefined", // 28
"Undefined", // 29
"Undefined", // 30
"Undefined", // 31
"LSB 0", // 32
"LSB 1", // 33
"LSB 2", // 34
"LSB 3", // 35
"LSB 4", // 36
"LSB 5", // 37
"LSB 6", // 38
"LSB 7", // 39
"LSB 8", // 40
"LSB 9", // 41
"LSB 10", // 42
"LSB 11", // 43
"LSB 12", // 44
"LSB 13", // 45
"LSB 14", // 46
"LSB 15", // 47
"LSB 16", // 48
"LSB 17", // 49
"LSB 18", // 50
"LSB 19", // 51
"LSB 20", // 52
"LSB 21", // 53
"LSB 22", // 54
"LSB 23", // 55
"LSB 24", // 56
"LSB 25", // 57
"LSB 26", // 58
"LSB 27", // 59
"LSB 28", // 60
"LSB 29", // 61
"LSB 30", // 62
"LSB 31", // 63
"Sustain", // 64
"Portamento", // 65
"Sostenuto", // 66
"Soft Pedal", // 67
"Legato", // 68
"Hold 2", // 69
"Variation", // 70
"Resonance", // 71
"Release", // 72
"Attack", // 73
"Cutoff", // 74
"Sound 6", // 75
"Sound 7", // 76
"Sound 8", // 77
"Sound 9", // 78
"Sound 10", // 79
"Decay", // 80
"Hi Pass", // 81
"GP Button 3", // 82
"GP Button 4", // 83
"Port. Amount", // 84
"Undefined", // 85
"Undefined", // 86
"Undefined", // 87
"Undefined", // 88
"Undefined", // 89
"Undefined", // 90
"Reverb", // 91
"Tremolo", // 92
"Chorus", // 93
"Detune", // 94
"Phaser", // 95
"Data Inc", // 96
"Data Dec", // 97
"NRPN LSB", // 98
"NRPN MSB", // 99
"RP LSB", // 100
"RP MSB", // 101
"Undefined", // 102
"Undefined", // 103
"Undefined", // 104
"Undefined", // 105
"Undefined", // 106
"Undefined", // 107
"Undefined", // 108
"Undefined", // 109
"Undefined", // 110
"Undefined", // 111
"Undefined", // 112
"Undefined", // 113
"Undefined", // 114
"Undefined", // 115
"Undefined", // 116
"Undefined", // 117
"Undefined", // 118
"Undefined", // 119
"All Sound Off", // 120
"All CCC Off", // 121
"Keyboard On", // 122
"All Notes Off", // 123
"Omni Mode Off", // 124
"Omni Mode On", // 125
"Mono", // 126
"Poly Mode", // 127
};
// 'NuEVI' or 'NuRAD' logo
#define LOGO16_GLCD_WIDTH 128
#define LOGO16_GLCD_HEIGHT 64
static const unsigned char PROGMEM nuevi_logo_bmp[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x07, 0x73, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x0e, 0xe3, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x00, 0x1d, 0xc3, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x3b, 0x83, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x77, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x00, 0xee, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x01, 0xdc, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x03, 0xb8, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x07, 0x70, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x0e, 0xe0, 0x03, 0x60, 0x00,
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x1d, 0xc0, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x3b, 0x80, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0xe0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x77, 0x00, 0x03, 0x60, 0x00,
0x00, 0x03, 0x00, 0xc0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0xee, 0x00, 0x03, 0x60, 0x00,
0x00, 0x03, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x61, 0xdc, 0x00, 0x03, 0x60, 0x00,
0x00, 0x07, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x63, 0xb8, 0x00, 0x03, 0x60, 0x00,
0x00, 0x07, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x67, 0x70, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x6e, 0xe0, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0x60, 0xc1, 0x01, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x7d, 0xc0, 0x00, 0x03, 0x60, 0x00,
0x00, 0x06, 0x30, 0xc3, 0x03, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x7b, 0x80, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x30, 0xc3, 0x07, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x77, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x1c, 0xc3, 0x06, 0x01, 0x80, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x0c, 0xc2, 0x0e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xfc, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x0e, 0xc6, 0x1e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xf8, 0x00, 0x00, 0x03, 0x60, 0x00,
0x00, 0x0c, 0x07, 0xc6, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x03, 0xc6, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x01, 0xc7, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0xc7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
extern void readSwitches(void);
#define OLED_RESET 4
Adafruit_SSD1306 display(128, 64, &Wire1, OLED_RESET,1000000,1000000);
MenuScreen *currentMenu = NULL;
void plotSubOption(const char* label, const char* unit = nullptr) {
int text_x, unit_x;
int label_pixel_width = strlen(label)*12;
if(unit == nullptr) {
text_x = 96 - (label_pixel_width/2);
} else {
int unit_pixel_width = strlen(unit)*6;
int halfSum = (label_pixel_width + unit_pixel_width)/2;
text_x = 96 - halfSum;
unit_x = 96 + halfSum - unit_pixel_width;
display.setCursor(unit_x,40);
display.setTextSize(1);
display.println(unit);
}
display.setTextSize(2);
display.setCursor(text_x,33);
display.println(label);
}
template<size_t N>
static void plotMenuEntries(std::array<MenuScreen, N> entries, size_t cursorPos) {
display.fillRect(0, MENU_HEADER_OFFSET, 63, 64-MENU_HEADER_OFFSET, BLACK);
display.setTextSize(1);
size_t scrollPos = 0;
if (entries.size() >= MENU_NUM_ROWS) {
if ((cursorPos - scrollPos) > (MENU_NUM_ROWS-2) ) {
scrollPos = cursorPos - (MENU_NUM_ROWS-2);
} else if( (cursorPos - scrollPos) < 1) {
scrollPos = cursorPos - 1;
}
scrollPos = constrain(scrollPos, 0, entries.size() - MENU_NUM_ROWS);
}
int row = 0;
int end = constrain(scrollPos + MENU_NUM_ROWS, 0, entries.size());
for (size_t i = scrollPos; i < end; i++, row++) {
int rowPixel = (row)*MENU_ROW_HEIGHT + MENU_HEADER_OFFSET;
display.setCursor(0,rowPixel);
if (cursorPos == i) {
display.setTextColor(BLACK, WHITE);
} else {
display.setTextColor(WHITE);
}
display.println(entries[i].title());
}
}
class AboutMenu : public MenuScreen {
const char *title() {
return "ABOUT";
}
void update(InputState input, bool redraw) {
if (redraw) {
display.clearDisplay();
display.setCursor(49,0);
display.setTextColor(WHITE);
display.setTextSize(0);
display.println("xEVI");
display.setCursor(16,12);
display.print("firmware v.");
display.println(FIRMWARE_VERSION);
display.print("eeprom v.");
display.println(EEPROM_VERSION);
}
}
};
template<size_t N>
class MainMenu : public MenuScreen {
public:
MainMenu(std::array<MenuScreen, N> entries) : _entries(entries) { }
void update(InputState input, bool redraw) {
if (input.changed && input.btnMenu) {
_inSubMenu = !_inSubMenu;
input.changed = false;
redraw = true;
}
if (_inSubMenu) {
_entries[_cursorPos].update(input, redraw);
return;
}
if (input.changed) {
_cursorPos = (_cursorPos + input.knobMenu) % _entries.size();
}
draw(redraw);
};
const char *title() {
return "MENU";
}
private:
void draw(bool redraw) {
if (redraw) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.drawLine(0, MENU_ROW_HEIGHT, 127, MENU_ROW_HEIGHT, WHITE);
display.println("MENU");
}
plotMenuEntries(_entries, _cursorPos);
}
bool _inSubMenu = false;
size_t _cursorPos;
std::array<MenuScreen, N> _entries;
};
template<size_t N>
class SubMenu : public MenuScreen {
public:
SubMenu(const char *title, std::array<MenuScreen, N> entries) : _title(title), _entries(entries) {}
void update(InputState input, bool redraw) {
bool redrawValue = false;
if (input.changed && input.knobMenu != 0) {
_cursorPos = (_cursorPos + input.knobMenu) % _entries.size();
draw(false);
input.changed = false;
redrawValue = true;
}
if (redraw) {
draw(redraw);
}
_entries[_cursorPos].update(input, redrawValue);
};
const char *title() {
return _title;
}
private:
void draw(bool redraw) {
if (redraw) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.drawLine(0, MENU_ROW_HEIGHT, 127, MENU_ROW_HEIGHT, WHITE);
display.println(_title);
}
plotMenuEntries(_entries, _cursorPos);
}
const char *_title;
size_t _cursorPos;
std::array<MenuScreen, N> _entries;
};
template<
size_t L,
typename T
>
class ValueMenu : public MenuScreen {
public:
ValueMenu(
const char * title, T &value,
const T min, const T max, const bool wrap = false,
const std::array<const char *, L> labels = {}
) : _title(title), _value(value), _min(min), _max(max), _wrap(wrap), _labels(labels) {}
void update(InputState input, bool redraw) {
if (input.knobMenu) {
_value = (_value + input.knobMenu);
if (_value > _max) {
_value = _min;
} else if (_value < _min) {
_value = _max;
}
draw(redraw);
} else if (redraw) {
draw(redraw);
}
}
private:
T &_value;
const T _min;
const T _max;
const bool _wrap;
const std::array<const char *, L> _labels;
const char *_title;
void draw(bool redraw) {
if (redraw) {
display.fillRect(63,11,64,52,BLACK);
display.drawRect(63,11,64,52,WHITE);
display.setTextSize(1);
int len = strlen(this->_title);
display.setCursor(95-len*3,15);
display.println(this->_title);
}
char buffer[12];
snprintf(buffer, 12, "%+d", _value);
size_t label_idx = _value - _min;
if (_labels.size() > 0 && label_idx >= 0 && label_idx <= _labels.size()) {
plotSubOption(_labels[label_idx], buffer);
} else {
plotSubOption(buffer);
}
}
};
void drawFlash(int x, int y){
display.drawLine(x+5,y,x,y+6,WHITE);
display.drawLine(x,y+6,x+5,y+6,WHITE);
display.drawLine(x,y+12,x+5,y+6,WHITE);
}
void initDisplay() {
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // initialize with the I2C addr 0x3D (for the 128x64)
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.clearDisplay();
#if defined(NURAD)
display.drawBitmap(0,0,nurad_logo_bmp,LOGO16_GLCD_WIDTH,LOGO16_GLCD_HEIGHT,1);
#else
display.drawBitmap(0,0,nuevi_logo_bmp,LOGO16_GLCD_WIDTH,LOGO16_GLCD_HEIGHT,1);
#endif
display.display();
}
void displayError(const char *error) {
display.clearDisplay();
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(0,0);
display.println(error);
display.display();
Serial.print("ERROR: ");
Serial.println(error);
}
void showVersion() {
display.setTextColor(WHITE);
display.setTextSize(1);
display.setCursor(85,52);
display.print("v.");
display.println(FIRMWARE_VERSION);
display.display();
}
static void clearSub(){
display.fillRect(63,11,64,52,BLACK);
}
static void clearSubValue() {
display.fillRect(65, 24, 60, 37, BLACK);
}
//***********************************************************
const MenuScreen breathModeMenu = ValueMenu<4, BreathMode>("BREATH MODE", currentPreset->breathMode, 0, 1, true, {{ "STANDARD", "LSB", "AT", "LSB_AT" }});
const MenuScreen breathCCMenu = ValueMenu<128, uint8_t>("BREATH CC", currentPreset->breathCC, 0, 127, true, CC_NAMES);
const MenuScreen velocityMenu = ValueMenu<1, uint8_t>("VELOCITY", currentPreset->fixedVelocity, 0, 127, true, {{ "DYN" }});
const MenuScreen curveMenu = ValueMenu<0, uint8_t>("CURVE", currentPreset->breathCurve, 0, 12); // TODO: curve display
const MenuScreen velSmpDlMenu = ValueMenu<1, uint8_t>("VEL DELAY", currentPreset->velSmpDl, 0, 30, true, { "OFF" }); // TODO: unit ms
const MenuScreen velBiasMenu = ValueMenu<1, uint8_t>("VEL BOOST", currentPreset->velBias, 0, 30, true, { "OFF" });
const MenuScreen breathIntervalMenu = ValueMenu<0, uint8_t>("BR INTERV", currentPreset->breathInterval, 0, 30, true);
const MenuScreen trill3Menu = ValueMenu<0, int8_t>("TRILL3", currentPreset->trill3_interval, 3, 4, true, {});
const MenuScreen cvTuneMenu = ValueMenu<0, int8_t>("CV Tune", currentPreset->cvTune, -100, 100, false, {});
const MenuScreen cvVibMenu = ValueMenu<0, uint8_t>("CV Vib LFO", currentPreset->cvVibRate, 0, 8, false, {});
const MenuScreen cvScaleMenu = ValueMenu<0, int8_t>("CV SCALING", currentPreset->cvScale, -100, 100, false, {});
const std::array<const char *, 25> transposeLabels = {
"C>", "C#>", "D>", "D#>", "E>", "F>", "F#>", "G>", "G#>", "A>", "Bb>", "B>",
">C<", "<C#", "<D", "<D#", "<E", "<F", "<F#", "<G", "<G#", "<A", "<Bb", "<B", "<C"
};
const MenuScreen transposeMenu = ValueMenu<25, int8_t>("TRANSPOSE", state.transpose, -12, 12, true, transposeLabels);
const MenuScreen octaveMenu = ValueMenu<0, uint8_t>("OCTAVE", state.octave, -3, 3, true, {});
const MenuScreen midiMenu = ValueMenu<0, byte>("MIDI CH", currentPreset->MIDIchannel, 1, 16, false, {});
const MenuScreen vibDepthMenu = ValueMenu<1, uint8_t>("DEPTH", currentPreset->vibratoDepth, 0, 9, true, {"OFF"});
const MenuScreen vibRetnMenu = ValueMenu<1, uint8_t>("RETURN", currentPreset->vibRetn, 0, 4, true, {"OFF"});
const MenuScreen vibSenseMenu = ValueMenu<0, uint8_t>("SENSE LVR", currentPreset->vibSens, 0, 12);
const MenuScreen vibSquelchMenu = ValueMenu<0, uint8_t>("SQUELCH LVR", currentPreset->vibSquelch, 0, 12);
const MenuScreen vibDirMenu = ValueMenu<2, VibratoMode>("DIRECTION", currentPreset->vibratoMode, VSTART_DOWN, VSTART_UP, true, { "START DOWN", "START UP"});
const MenuScreen biteCtlMenu = ValueMenu<4, ExtraControl>("BITE CTL", currentPreset->biteControl, 0, 3, true, {{
"OFF",
"VIBRATO",
"GLIDE",
"CC"
}});
const MenuScreen biteCCMenu = ValueMenu<128, uint8_t>("BITE CC", currentPreset->biteCC, 0, 127, true, CC_NAMES);
const MenuScreen leverCtlMenu = ValueMenu<4, ExtraControl>("LEVER CTL", currentPreset->leverControl, 0, 3, true, {
"OFF",
"VIBRATO",
"GLIDE",
"CC"
});
const MenuScreen leverCCMenu = ValueMenu<128, uint8_t>("LEVER CC", currentPreset->leverCC, 0, 127, true, CC_NAMES);
const MenuScreen portMenu = ValueMenu<4, PortamentoMode>("GLIDE MOD", currentPreset->portamentoMode, 0, 3, true, {
"OFF",
"ON",
"SWITCH_ONLY",
"GLIDE_ONLY",
});
const MenuScreen portLimitMenu = ValueMenu<0, uint8_t>("GLIDE LMT", currentPreset->portamentoLimit, 1, 127, true);
const MenuScreen pitchBendMenu = ValueMenu<0, uint8_t>("PITCHBEND", currentPreset->PBdepth, 0, 12, true);
const MenuScreen extraCtlMenu = ValueMenu<4, ExtraControl>("EXCT CC A", currentPreset->extraControl, 0,4, true, {
"OFF",
"ON",
"SWITCH_ONLY",
"GLIDE_ONLY",
});
const MenuScreen extraCCMenu = ValueMenu<128, uint8_t>("EXCT CC", currentPreset->extraCC, 0,4, true, CC_NAMES);
const MenuScreen deglitchMenu = ValueMenu<1, uint8_t>("DEGLITCH", currentPreset->deglitch, 0, 70, true, {"OFF"});
const MenuScreen pinkyMenu = ValueMenu<29, uint8_t>("PINKY KEY", currentPreset->pinkySetting, 0, 29, true, {
"-12", "-11", "-10", "-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1",
"PB/2",
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"PBD", "GLD", "MOD", "QTN"
});
const MenuScreen fingeringMenu = ValueMenu<4, FingeringMode>("FINGERING", currentPreset->fingering, 0, 3, true, {
"EVI",
"EVR",
"TPT",
"HRN",
});
const MenuScreen rollerMenu = ValueMenu<6, RollerMode>("ROLLRMODE", currentPreset->rollerMode, 1, 6, true, {
"HIGHEST",
"HIGHEST_EXTEND",
"HIGHEST_PAIR",
"HIGHEST_PAIR_EXTEND",
"PARTIAL",
"PARTIAL_EXTEND",
});
std::array<MenuScreen, 7> breathMenuEntries = {
breathModeMenu,
breathCCMenu,
velocityMenu,
curveMenu,
velSmpDlMenu,
velBiasMenu,
breathIntervalMenu,
};
const MenuScreen breathMenu = SubMenu<7>("BREATH SETUP", breathMenuEntries);
const std::array<MenuScreen, 13> controlMenuEntries = {
fingeringMenu,
rollerMenu,
biteCtlMenu,
biteCCMenu,
leverCtlMenu,
leverCCMenu,
extraCtlMenu,
extraCCMenu,
portMenu,
portLimitMenu,
deglitchMenu,
pinkyMenu,
pitchBendMenu
};
const MenuScreen controlMenu = SubMenu<13>("CONTROL SETUP", controlMenuEntries);
const std::array<MenuScreen, 5> vibratoMenuEntries = {
vibDepthMenu,
vibRetnMenu,
vibDirMenu,
vibSenseMenu,
vibSquelchMenu,
};
const MenuScreen vibratoMenu = SubMenu<5>("VIBRATO", vibratoMenuEntries);
const MenuScreen aboutMenu = AboutMenu();
std::array <MenuScreen, 4> extrasMenuEntries = {
trill3Menu,
cvTuneMenu,
cvScaleMenu,
cvVibMenu,
};
const MenuScreen extrasMenu = SubMenu<4>("EXTRAS", extrasMenuEntries);
// Top-level screens
const std::array<MenuScreen, 9> mainMenuEntries = {
transposeMenu,
octaveMenu,
midiMenu,
breathMenu,
controlMenu,
vibratoMenu,
adjustMenu,
extrasMenu,
aboutMenu,
};
const MenuScreen mainMenuPage = MainMenu<9>(mainMenuEntries);
// const MenuScreen patchPage
// const MenuScreen presetPage
// const MenuScreen ccPage
static void curveCustomDraw() {
const char* curveMenuLabels[] = {"-4", "-3", "-2", "-1", "LIN", "+1", "+2",
"+3", "+4", "S1", "S2", "Z1", "Z2" };
int y0 = 0, x0 = 0;
int scale = ((1<<14)-1)/60;
for(int x = x0; x < 60; x+=1) {
int y = multiMap(x*scale, curveIn, curves[currentPreset->breathCurve], 17);
y = (y*37) / ((1<<14)-1);
display.drawLine(x0 + 65, 60 - y0, x + 65, 60 - y, WHITE);
x0 = x; y0 = y;
}
display.setCursor(125 - 3*6, 60-8 );
display.setTextSize(0);
display.print(curveMenuLabels[currentPreset->breathCurve]);
}
static bool updateSensorPixelsFlag = false;
void drawSensorPixels() {
updateSensorPixelsFlag = true;
}
//***********************************************************
static InputState readInput(uint32_t timeNow) {
static uint32_t lastDebounceTime = 0; // the last time the output pin was toggled
static uint32_t buttonRepeatTime = 0;
static uint32_t buttonPressedTime = 0;
static uint8_t lastDeumButtons = 0;
static uint8_t deumButtonState = 0;
static int lastKnobs[] = {0, 0, 0, 0};
InputState input;
uint8_t deumButtons = buttonState();
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited long enough
// since the last press to ignore any noise:
// If the switch changed, due to noise or pressing:
if (deumButtons != lastDeumButtons) {
// reset the debouncing timer
lastDebounceTime = timeNow;
}
if ((timeNow - lastDebounceTime) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (deumButtons != deumButtonState) {
// keys.current = deumButtons;
input.btnMenu = deumButtons & BTN_MENU;
input.btnVal1 = deumButtons & BTN_VAL1;
input.btnVal2 = deumButtons & BTN_VAL2;
input.btnPreset = deumButtons & BTN_PRESET;
input.changed = true;
deumButtonState = deumButtons;
menuTime = timeNow;
buttonPressedTime = timeNow;
}
}
for (int i = 0; i < 4; i++) {
int val = readKnob(i);
if (val != lastKnobs[i]) {
input.changed = 1;
switch (i) {
case 0:
input.knobMenu = val;
break;
case 1:
input.knobVal1 = val;
break;
case 2:
input.knobVal2 = val;
break;
case 3:
input.knobPreset = val;
break;
}
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastDeumButtons = deumButtons;
return input;
}
void handleMenu(bool draw) {
unsigned long timeNow = millis();
InputState input = readInput(timeNow);
// shut off menu system if not used for a while (changes not stored by exiting a setting manually will not be stored in EEPROM)
if (currentMenu && ((timeNow - menuTime) > menuTimeUp)) {
display.ssd1306_command(SSD1306_DISPLAYOFF);
display.clearDisplay();
currentMenu = NULL;
}
if (currentMenu && (draw || input.changed)) {
currentMenu->update(input, false);
}
}