1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
// class definitions are in a header so that
// they get read before compiling the main program.
class tuningDef {
public:
std::string name; // limit is 17 characters for GEM menu
byte cycleLength; // steps before period/cycle/octave repeats
float stepSize; // in cents, 100 = "normal" semitone.
SelectOptionInt keyChoices[MAX_SCALE_DIVISIONS];
int spanCtoA() {
return (- keyChoices[0].val_int);
};
};
class layoutDef {
public:
std::string name; // limit is 17 characters for GEM menu
bool isPortrait; // affects orientation of the GEM menu only.
byte hexMiddleC; // instead of "what note is button 1", "what button is the middle"
int8_t acrossSteps; // defined this way to be compatible with original v1.1 firmare
int8_t dnLeftSteps; // defined this way to be compatible with original v1.1 firmare
byte tuning; // index of the tuning that this layout is designed for
};
class colorDef {
public:
float hue;
byte sat;
byte val;
colorDef mixWithWhite() {
colorDef temp;
temp.hue = this->hue;
temp.sat = ((this->sat > SAT_TINT) ? SAT_TINT : this->sat);
temp.val = VALUE_FULL;
return temp;
};
};
class paletteDef {
public:
colorDef swatch[MAX_SCALE_DIVISIONS]; // the different colors used in this palette
byte colorNum[MAX_SCALE_DIVISIONS]; // map key (c,d...) to swatches
colorDef getColor(byte givenStepFromC) {
return swatch[colorNum[givenStepFromC] - 1];
};
float getHue(byte givenStepFromC) {
return getColor(givenStepFromC).hue;
};
byte getSat(byte givenStepFromC) {
return getColor(givenStepFromC).sat;
};
byte getVal(byte givenStepFromC) {
return getColor(givenStepFromC).val;
};
};
class buttonDef {
public:
byte btnState = 0; // binary 00 = off, 01 = just pressed, 10 = just released, 11 = held
void interpBtnPress(bool isPress) {
btnState = (((btnState << 1) + isPress) & 3);
};
int8_t coordRow = 0; // hex coordinates
int8_t coordCol = 0; // hex coordinates
uint32_t timePressed = 0; // timecode of last press
uint32_t LEDcolorAnim = 0; // calculate it once and store value, to make LED playback snappier
uint32_t LEDcolorPlay = 0; // calculate it once and store value, to make LED playback snappier
uint32_t LEDcolorOn = 0; // calculate it once and store value, to make LED playback snappier
uint32_t LEDcolorOff = 0; // calculate it once and store value, to make LED playback snappier
bool animate = 0; // hex is flagged as part of the animation in this frame, helps make animations smoother
int16_t stepsFromC = 0; // number of steps from C4 (semitones in 12EDO; microtones if >12EDO)
bool isCmd = 0; // 0 if it's a MIDI note; 1 if it's a MIDI control cmd
bool inScale = 0; // 0 if it's not in the selected scale; 1 if it is
byte note = UNUSED_NOTE; // MIDI note or control parameter corresponding to this hex
int16_t bend = 0; // in microtonal mode, the pitch bend for this note needed to be tuned correctly
byte channel = 0; // what MIDI channel this note is playing on
float frequency = 0.0; // what frequency to ring on the buzzer
};
class wheelDef {
public:
bool alternateMode; // two ways to control
bool isSticky; // TRUE if you leave value unchanged when no buttons pressed
byte* topBtn; // pointer to the key Status of the button you use as this button
byte* midBtn;
byte* botBtn;
int16_t minValue;
int16_t maxValue;
int* stepValue; // this can be changed via GEM menu
int16_t defValue; // snapback value
int16_t curValue;
int16_t targetValue;
uint32_t timeLastChanged;
void setTargetValue() {
if (alternateMode) {
if (*midBtn >> 1) { // middle button toggles target (0) vs. step (1) mode
int16_t temp = curValue;
if (*topBtn == 1) {temp += *stepValue;}; // tap button
if (*botBtn == 1) {temp -= *stepValue;}; // tap button
if (temp > maxValue) {temp = maxValue;}
else if (temp <= minValue) {temp = minValue;};
targetValue = temp;
} else {
switch (((*topBtn >> 1) << 1) + (*botBtn >> 1)) {
case 0b10: targetValue = maxValue; break;
case 0b11: targetValue = defValue; break;
case 0b01: targetValue = minValue; break;
default: targetValue = curValue; break;
};
};
} else {
switch (((*topBtn >> 1) << 2) + ((*midBtn >> 1) << 1) + (*botBtn >> 1)) {
case 0b100: targetValue = maxValue; break;
case 0b110: targetValue = (3 * maxValue + minValue) / 4; break;
case 0b010:
case 0b111:
case 0b101: targetValue = (maxValue + minValue) / 2; break;
case 0b011: targetValue = (maxValue + 3 * minValue) / 4; break;
case 0b001: targetValue = minValue; break;
case 0b000: targetValue = (isSticky ? curValue : defValue); break;
default: break;
};
}
};
bool updateValue(uint32_t givenTime) {
int16_t temp = targetValue - curValue;
if (temp != 0) {
if ((givenTime - timeLastChanged) >= CC_MSG_COOLDOWN_MICROSECONDS ) {
timeLastChanged = givenTime;
if (abs(temp) < *stepValue) {
curValue = targetValue;
} else {
curValue = curValue + (*stepValue * (temp / abs(temp)));
};
return 1;
} else {
return 0;
};
} else {
return 0;
};
};
};
// back button
class scaleDef {
public:
std::string name;
byte tuning;
byte pattern[MAX_SCALE_DIVISIONS];
};
// this class should only be touched by the 2nd core
class oscillator {
public:
uint16_t increment = 0;
uint16_t counter = 0;
};
|