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:
parent
c58c3f9e46
commit
01d193c9b3
92 changed files with 69119 additions and 73272 deletions
755
NuEVI/src/menu.cpp
Normal file
755
NuEVI/src/menu.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue