Neale Pickett
·
2024-01-07
MockBand.ino
1#include <stdint.h>
2#include <Arduino.h>
3#include <PluggableUSB.h>
4
5#include "hid.hh" // Modified HID library: doesn't prefix each packet with ID
6#include "instrument.hh"
7#include "standard.hh" // Standard pins
8
9// If defined, we will check the wammy bar input
10//#define WAMMY
11
12// Maximum time between wammy bar updates.
13#define UPDATE_INTERVAL_MS 20
14
15// After an edge on a pin, stop listening for this long, to debounce it
16#define SILENCE_INTERVAL_MS 40
17
18// Your measured samples per frame, more or less
19#define SAMPLES_PER_FRAME 127
20
21// Some arithmetic for the compiler, to make the code fast
22#define SAMPLES_PER_MS (SAMPLES_PER_FRAME / UPDATE_INTERVAL_MS)
23#define SILENCE_SAMPLES (SAMPLES_PER_MS * SILENCE_INTERVAL_MS)
24
25#if USB_VID != 0x1bad
26#error USB_VID must be set to 0x1bad: see INSTALL.md
27#endif
28
29#if USB_PID == 0x0003
30#define DRUM // XBox
31#elif USB_PID == 0x0004
32#define GUITAR
33#elif USB_PID == 0x0005
34#define DRUM // Wii RB1
35#elif USB_PID == 0x3110
36#define DRUM // Wii RB2
37#else
38#error USB_PID not recognized: see INSTALL.md
39#endif
40
41InstrumentButtonState buttonState = {0};
42
43void setup() {
44 pinMode(STRUM_DOWN, INPUT_PULLUP);
45 pinMode(STRUM_UP, INPUT_PULLUP);
46 pinMode(TILT_SWITCH, INPUT_PULLUP);
47 pinMode(BUTTON_GREEN, INPUT_PULLUP);
48 pinMode(BUTTON_RED, INPUT_PULLUP);
49 pinMode(BUTTON_YELLOW, INPUT_PULLUP);
50 pinMode(BUTTON_BLUE, INPUT_PULLUP);
51 pinMode(BUTTON_ORANGE, INPUT_PULLUP);
52 pinMode(SOLO_GREEN, INPUT_PULLUP);
53 pinMode(SOLO_RED, INPUT_PULLUP);
54 pinMode(SOLO_YELLOW, INPUT_PULLUP);
55 pinMode(SOLO_BLUE, INPUT_PULLUP);
56 pinMode(SOLO_ORANGE, INPUT_PULLUP);
57 pinMode(BUTTON_PLUS, INPUT_PULLUP);
58 pinMode(BUTTON_MINUS, INPUT_PULLUP);
59 pinMode(ANALOG_WAMMY, INPUT);
60 pinMode(ANALOG_DPAD, INPUT);
61
62 // Initialize HID
63 static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
64 HID().AppendDescriptor(&node);
65
66 buttonState.finalConstant = 0x0200020002000200;
67}
68
69// Order of pins in sample
70uint8_t pins[] = {
71 BUTTON_BLUE,
72 BUTTON_GREEN,
73 BUTTON_RED,
74 BUTTON_YELLOW,
75 BUTTON_ORANGE,
76 TILT_SWITCH,
77 STRUM_UP, // Not in USB packet
78 STRUM_DOWN, // Not in USB packet
79 BUTTON_MINUS,
80 BUTTON_PLUS,
81 SOLO_BLUE, // Not in USB packet
82 SOLO_GREEN, // Not in USB packet
83 SOLO_RED, // Not in USB packet
84 SOLO_YELLOW, // Not in USB packet
85 SOLO_ORANGE, // Not in USB packet
86};
87#define npins (sizeof(pins) / sizeof(*pins))
88
89// The 3.3v Pro Micro is on the slow side.
90// Our strategy is to poll button state as quickly as possible,
91// and hope we don't miss anything while we're doing USB stuff.
92void loop() {
93 uint16_t buttons = 0;
94 uint16_t samples = 0;
95 unsigned long next = 0;
96 uint16_t silence[npins] = {0};
97
98 while (1) {
99 uint16_t edge = 0;
100
101 samples++;
102
103 for (uint8_t i = 0; i < npins; i++) {
104 if (silence[i]) {
105 silence[i]--;
106 } else if (bitRead(buttons, i) != !digitalRead(pins[i])) {
107 edge |= bit(i);
108 silence[i] = SILENCE_SAMPLES;
109 }
110 }
111 buttons ^= edge;
112
113 // We've sampled everything. Is it time to do calculations and USB?
114#ifdef WAMMY
115 unsigned long now = millis();
116 if (!edge && (next > now)) {
117 continue;
118 }
119 next = now + UPDATE_INTERVAL_MS;
120 buttonState.axis[2] = analogRead(ANALOG_WAMMY) / 4; // Wammy bar
121#else
122 if (!edge) {
123 continue;
124 }
125#endif
126
127 //
128 // Calculate and send an HID update
129 //
130 uint16_t vbuttons = buttons; // We're going to mess with the button state
131
132 buttonState.buttons = (vbuttons & 0b1100111111); // +-..!OYRGB
133#ifdef GUITAR
134 buttonState.buttons |= (vbuttons >> 10) & 0b11111; // Solo keys
135 bitWrite(buttonState.buttons, 6, (vbuttons >> 10) & 0b11111); // Solo modifier
136
137 if (bitRead(vbuttons, 6)) {
138 buttonState.hatAndConstant = 0; // up
139 } else if bitRead(vbuttons, 7) {
140 buttonState.hatAndConstant = 4; // down
141 } else {
142 buttonState.hatAndConstant = 8; // nothing
143 }
144#else // DRUMS
145
146 // Hi hat pedal (SOLO_RED) makes yellow cymbal strike a blue cymbal strike
147 if (bitRead(vbuttons, 12) && bitRead(vbuttons, 13)) {
148 bitClear(vbuttons, 13);
149 bitSet(vbuttons, 10);
150 }
151
152 buttonState.buttons |= (vbuttons >> 10) & 0b01011; // Cymbals
153 bitWrite(buttonState.buttons, 10, (vbuttons >> 0) & 0b01111); // Drum pad modifier
154 bitWrite(buttonState.buttons, 11, (vbuttons >> 10) & 0b01011); // Cymbals modifier
155
156
157 // rbdrum2midi wants these set: it ignores the button states.
158 buttonState.velocity[0] = bitRead(buttonState.buttons, 3)?127:0; // Y
159 buttonState.velocity[1] = bitRead(buttonState.buttons, 2)?127:0; // R
160 buttonState.velocity[2] = bitRead(buttonState.buttons, 1)?127:0; // G
161 buttonState.velocity[3] = bitRead(buttonState.buttons, 0)?127:0; // B
162
163 // Clone Hero 1.0.0.4080-final needs blue and yellow cymbals to send up and down on d-pad.
164 // This is what the mysterous CymExt1 and CymExt2 mappings mean.
165 // If these aren't set, all pads (except red) register as simultaneous drum and cymbal hits.
166 if (bitRead(vbuttons, 13)) {
167 buttonState.hatAndConstant = 0; // up
168 } else if (bitRead(vbuttons, 10)) {
169 buttonState.hatAndConstant = 4; // down
170 } else {
171 buttonState.hatAndConstant = 8; // nothing
172 }
173#endif
174
175#ifdef DPAD
176#error DPAD isn't implemented yet
177#endif
178
179#ifdef DEBUGY0
180 // Log sample rate to the first Y axis
181 buttonState.axis[1] = samples & 0xff;
182#endif
183
184 // Send an update
185 HID().SendReport(0, (uint8_t *)&buttonState, 27);
186
187 samples = 0;
188 }
189}