// SysEx protocol between the Weights card and its web editor // (web/index.html), used when a computer is plugged into the Computer's USB // socket and the card is acting as a USB MIDI device. // // Every message is F0 7D 47 F7 // (7D is the MIDI 'non-commercial' manufacturer ID, 47 is 'G'). // All values are 7-bit; wider values are sent as two bytes, high 7 bits first. // // Fader values are in 8mu fader units, 0-127, sent page by page (16 pages // of 8 faders), the pages numbered // 0 NOTES 1 OCTAVES 2 GATES 3 RATCHETS (buttons A-D) // 4 NOTES 2 5 OCTAVES 2 6 GATES 2 7 RATCHETS 2 (A-D pressed again) // 8 ATTACK 9 DECAY 10 PLUCK 11 DEPTH (and again) // 12 ATTACK 2 13 DECAY 2 14 PLUCK 2 15 DEPTH 2 (and again) // (Version 1 had only pages 0-7; version 2 had a different list of scales.) // // Web -> card // HELLO 01 card replies with STATE // SET 03 page fader value one fader value // SET_ALL 04 version tempo(2) values[128] // PAGE 05 page page shown on the Computer's LEDs // RESET 06 default faders; card replies with STATE // PING 09 sent every second; STATUS flows while // pings keep arriving // TEMPO 0A tempo(2) internal clock, 40-300 BPM // // Card -> web // STATE 02 version page tempo(2) values[128] // STATUS 07 flags flags2 scale ratio slew attack decay pluck // steplen(2) steppos stepcount voice1[14] voice2[14] tempo(2) busy parts[6] // flags bit 0 following a clock at Pulse In 1, bit 1 8mu on // the card, bit 2 switch up (knobs are scale, clock // and slew, not decay, attack and pluck), bit 3 a // knob is waiting to pick up its setting, bit 4 // switch held down (Main is the tempo), bits 5 and // 6 a jack in Audio In 1 and 2 (Audio Out 1 and 2 // are then that input through the envelope) // flags2 bits 0-2 the X, Y and Main knob is waiting, bits 3-6 // voice 2 shares the NOTES, OCTAVES, GATES, RATCHETS // first page (its second page is all zero) // scale 0-36, as kScales in weights.h // ratio 0-10: /8 /6 /4 /3 /2 x1 x2 x3 x4 x6 x8 // slew attack decay pluck the knob settings, 0-127 // steplen milliseconds a step lasts, as estimated // steppos position through the step, 0-127 // stepcount counts steps, 0-127 and round again // voice note fader, note of the scale, octave fader, gate // fader, ratchet fader (0-7 each), hit count (0-127 // and round), bit 0 gate up, envelope (0-127), // pitch(2) in millivolts offset by 8192, then the // ATTACK, DECAY, PLUCK and DEPTH faders drawn (0-7, // or 8 for none: that page is off) // tempo the internal clock, BPM (from Main with the switch // held down, or from TEMPO) // busy the longest the card's audio interrupt took over the // last second, in microseconds (it has about 20) // parts the same for each part of the work: the engine // alone, the engine drawing new choices, the knobs, // the 8mu, the LEDs and this status #ifndef WEIGHTS_SYSEX_H #define WEIGHTS_SYSEX_H #include namespace sysex { static constexpr uint8_t kMfr = 0x7D; static constexpr uint8_t kProduct = 0x47; static constexpr uint8_t kVersion = 3; static constexpr int kNumValues = 16 * 8; enum Cmd : uint8_t { Hello = 0x01, State = 0x02, Set = 0x03, SetAll = 0x04, Page = 0x05, Reset = 0x06, Status = 0x07, Ping = 0x09, Tempo = 0x0A, }; // Collects one SysEx message from a byte stream. Feed() returns true when // a complete message for this card has arrived; its command and payload are // then in cmd, payload and length. struct Parser { static constexpr int kMax = 160; uint8_t buf[kMax]; int n = 0; bool in = false; uint8_t cmd = 0; const uint8_t *payload = nullptr; int length = 0; bool Feed(uint8_t b) { if (b == 0xF0) { in = true; n = 0; return false; } if (!in) return false; if (b == 0xF7) { in = false; if (n < 3 || buf[0] != kMfr || buf[1] != kProduct) return false; cmd = buf[2]; payload = buf + 3; length = n - 3; return true; } if (b & 0x80) { // Any other status byte aborts the message, except real-time // bytes, which may legally appear inside SysEx if (b < 0xF8) in = false; return false; } if (n < kMax) buf[n++] = b; else in = false; return false; } }; inline int Header(uint8_t *out, uint8_t cmd) { out[0] = 0xF0; out[1] = kMfr; out[2] = kProduct; out[3] = cmd; return 4; } inline int Put14(uint8_t *out, int32_t v) { if (v < 0) v = 0; if (v > 16383) v = 16383; out[0] = uint8_t(v >> 7); out[1] = uint8_t(v & 0x7F); return 2; } inline int32_t Get14(const uint8_t *in) { return (int32_t(in[0] & 0x7F) << 7) | (in[1] & 0x7F); } static constexpr int kVoiceLen = 14; static constexpr int kStateLen = 4 + 4 + kNumValues + 1; static constexpr int kStatusLen = 4 + 8 + 4 + 2 * kVoiceLen + 2 + 1 + 6 + 1; } // namespace sysex #endif