A dependency-free ES module: the codec (8↔7-bit + CRC-16 + SysEx framing), BLE-MIDI packetization (and a parser for incoming notifications), helpers, and command builders for every documented register. Runs in the browser (Web Bluetooth / Web MIDI) and in Node.
Implements ../spec/SPEC.md. Verified byte-identical to the
Python port.
import * as orba from "./orba-protocol.js";
// or named: import { buildCommand, setActivePart, bleWrapMidiPackets } from "./orba-protocol.js";In a page: <script type="module">. In Node: run with --input-type=module or use a
.mjs file / "type": "module".
Command builders return a payload array; buildCommand wraps it into framed SysEx bytes.
const sysex = orba.buildCommand(orba.setActivePart("bass")); // [0xF0, .., 0xF7]
orba.buildCommand(orba.setTempo(120)); // 120.00 BPM
orba.buildCommand(orba.play()); // transport: play/resume
orba.setFx("bass", "volume", 50); // payload: 03 02 50 10 80 00 01 04const device = await navigator.bluetooth.requestDevice({
filters: [{ services: [orba.BLE_MIDI_SERVICE_UUID] }, { namePrefix: "Orba" }],
optionalServices: [orba.BLE_MIDI_SERVICE_UUID],
});
const server = await device.gatt.connect();
const svc = await server.getPrimaryService(orba.BLE_MIDI_SERVICE_UUID);
const ch = await svc.getCharacteristic(orba.BLE_MIDI_CHARACTERISTIC_UUID);
// Send (serialize writes — only one GATT op may be in flight):
for (const pkt of orba.bleWrapMidiPackets(orba.buildCommand(orba.setActivePart("lead")))) {
await ch.writeValueWithoutResponse(new Uint8Array(pkt));
}
// Receive: reassemble notifications, then decode replies.
const parser = orba.createBleMidiParser();
await ch.startNotifications();
ch.addEventListener("characteristicvaluechanged", e => {
for (const midi of parser.feed(new Uint8Array(e.target.value.buffer))) {
if (midi[0] === 0xf0) {
const reply = orba.decodeMessage(midi); // { payload, crcOk, msgId }
if (reply?.crcOk) console.log(orba.parseReply(reply.payload));
}
}
});const access = await navigator.requestMIDIAccess({ sysex: true });
const out = /* the Orba "Lead" output port */;
out.send(orba.buildCommand(orba.play())); // raw SysEx, no BLE wrapping- Codec:
crc16,encode7,decode7,buildCommand(payload, msgId?),decodeMessage(bytes),createMsgIdCounter(start?). - BLE-MIDI:
bleWrapMidiPackets(midi, writeSize?),createBleMidiParser(). - Helpers:
ascii,filenameBytes,partIndex,parseReply,fxValueToPercent,tempoToBpm. - Builders (return payloads):
getActivePart/setActivePart,transport+play/pause/recordOverdub/eraseActiveLoop/eraseSong,setFx/getFx,selectPreset(returns[select, commit?]),getName,setTempo/getTempo,setKey/setScale(+ gets),setQuantize/getQuantize,setMetronome/getMetronome, and the misc registers (setMidiMode,setPitchBendRange,getBatteryPercent, …). - Constants:
PARTS,SELECT_ADDRS,NAME_ADDRS,FX_BASE,FX_OFFSET,SCALES,ROOTS,QUANTIZE,MIDI_MODE, BLE UUIDs.