Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

README.md

orba-protocol — JavaScript

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

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".

Build and send a command

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 04

Over BLE (Web Bluetooth)

const 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));
    }
  }
});

Over USB (Web MIDI)

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

API surface

  • 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.