mockband

Firmware for Wii Rock Band guitar and drum kit
git clone https://git.woozle.org/neale/mockband.git

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}