Analog Keys: USB, SysEx and the file/RPC surface
Read-only hardware observations of USB interfaces, two SysEx namespaces, file transfers and the decoded LCD.
Technical research · edition 2 · reviewed 2026-10-24

Prismal Labs hardware capture, 8 October 2026: decoded 122 by 32 monochrome LCD, enlarged in the retained research preview. View full-size image
What we found
Our Analog Keys investigation separated three interfaces that are easy to conflate: ordinary USB-MIDI, two SysEx families carried over that MIDI port, and the private Overbridge audio/control endpoints. We measured OS 1.52 in Overbridge mode, recovered all 21 advertised file/RPC commands, verified seeded CRCs across 532 file chunks, and corrected the LCD bitmap interpretation.
The practical result is a transport reference that can support backup tools, librarians and diagnostic clients. It also establishes where endpoint discovery stops: the observed descriptors do not reveal the proprietary audio packet format. Device-specific identifiers and private project contents are omitted here.
The measured USB surface
Endpoint directions are relative to the host: IN receives from the Keys; OUT sends to it.
| Interface | Class/subclass | Alternate settings | Endpoints | Function |
|---|---|---|---|---|
| 0 | Vendor 0xFF/0/0 | 0 has no endpoints; 1–7 | 0x83 IN, isochronous, attributes 0x05, interval 1 | Overbridge audio from Keys |
| 1 | Vendor 0xFF/0/0 | 0 has no endpoints; 1–7 | 0x03 OUT, isochronous, attributes 0x05, interval 1 | Overbridge audio to Keys |
| 2 | Vendor 0xFF/0/0 | 0 | 0x02 OUT, 0x82 IN; bulk, 64-byte max packet | Private Overbridge control/asynchronous traffic |
| 3 | AudioControl 0x01/0x01/0 | 0 | None | Audio class header associates MIDI interface 4 |
| 4 | MIDIStreaming 0x01/0x03/0 | 0 | 0x01 OUT, 0x81 IN; bulk, 64-byte max packet | USB-MIDI 1.0, one embedded cable |
IAD 0 groups vendor interfaces 0–2; IAD 1 groups AudioControl/MIDIStreaming interfaces 3–4. AudioControl and MIDIStreaming class headers declare version 0x0100. The vendor audio interfaces have no standard UAC audio-streaming format descriptors.
| Alternate setting | Interface 0 / IN max bytes | Interface 1 / OUT max bytes |
|---|---|---|
| 0 | No endpoint | No endpoint |
| 1 | 6 | 104 |
| 2 | 202 | 300 |
| 3 | 300 | 398 |
| 4 | 398 | 496 |
| 5 | 594 | 692 |
| 6 | 790 | 888 |
| 7 | 888 | 986 |
These are capacities, not measured active audio packet lengths or channel assignments. Each size equals 6 + 49 × n for a different n; that arithmetic alone does not establish sample packing or header semantics. The active alternate settings were not changed or captured in bus traffic.
MIDI jack IDs are embedded IN 1, external IN 2, embedded OUT 3, external OUT 4. The standard OUT endpoint is associated with embedded IN jack 1; standard IN with embedded OUT jack 3. This IN/OUT jack terminology describes the MIDI function and is distinct from host endpoint direction.
Evidence: Elektron/KeysSequencer/research/usb-api/usb-transport.md.
RPC framing and correlation
Unpacked request body:
| Offset | Type | Meaning |
|---|---|---|
| 0 | u16 BE | Request sequence/message ID |
| 2 | u16 BE | Response correlation ID; zero for a new request |
| 4 | u8 | Opcode |
| 5 | Bytes | Opcode arguments |
Pack the entire body in groups of seven bytes. Each group begins with one flag byte: bit 6 is the high bit of the first original byte, bit 5 of the second, down to bit 0 of the seventh. Follow it with the low seven bits of each byte. The last group can be shorter. Encoded length is n + ceil(n/7); an encoded length congruent to 1 modulo 8 is malformed.
Replies use the same envelope. Their second u16 equals the outstanding request ID, and their opcode is request_opcode | 0x80. The first u16 is the device's own message ID, not the correlation field. One outstanding RPC per pair is the measured implementation strategy; pipelining is unverified.
Measured first ping:
The ordinary RPC envelope has no legacy checksum/count trailer. File partial transfers and containers have their own CRCs. BE below means big endian; cstr means a NUL-terminated string. Stable numeric paths were sent as ASCII; existing implementations decode directory names as CP1252. Full encoding coverage for arbitrary names was not measured.
Evidence: Elektron/KeysSequencer/research/usb-api/file-rpc-api.md.
The complete advertised command set
Reply IDs below are the response opcode, not the sequence ID. Argument offsets in this document start after the five-byte envelope.
| Request / reply | Name | Request arguments | Response arguments / evidence |
|---|---|---|---|
01 / 81 | Ping/capabilities | Empty | u8 product, u8 count, count × u8 opcode, cstr name; Live product 4/count 21/name Analog Keys; no status byte |
02 / 82 | OS/build | Empty | cstr build, cstr OS; Live 0183, 1.52; no status byte |
03 / 83 | UID | Empty | Live four opaque bytes [device-specific UID omitted]; no status byte |
04 / 84 | LCD screenshot | u32 selector; Live zero | Status, u16 width, u16 height, u32 byte_count, bitmap; live 122×32/488 bytes; selector meanings unassigned |
06 / 86 | Tempo read | Empty | Status followed by five u32; Live 14400,14400,0,0,0; last three semantics unassigned |
07 / 87 | Tempo write | u8 selector, u32 tempo | Firmware typed fields; selector semantics/range and response payload unverified; not sent |
50 / D0 | OS upgrade start | u32 size, cstr format, u8 flag | Firmware + source, normally sysex format; field endian/flag/result handling requires upgrade-specific validation; not sent |
51 / D1 | OS upgrade write | CRC, length, offset (u32), then data | Source + firmware; pinned Elektroid writes BE fields in 2048-byte blocks; not sent |
52 / D2 | OS upgrade end | u8 flag | Firmware field exists; finalization/reboot behavior and reply unverified; not sent |
53 / D3 | Directory list | cstr path, u32 start, u32 end, optional u8 all | Live status + pagination + records; zero end/no all byte worked |
54 / D4 | Read open | cstr path, u32 chunk_size, optional u8 compressed | Live status + handle + negotiated chunk size + compression flag |
55 / D5 | Read partial | u32 handle, u32 sequence | Live status, handle, sequence, progress, final flag, CRC, length, data |
56 / D6 | Read close | u32 handle | Live status, handle, transferred byte count |
57 / D7 | Write open | u32 total_size, cstr path | Local implementation + firmware status/handle; not sent here |
58 / D8 | Write partial | u32 handle, u32 sequence, u32 CRC, u32 length, data | Local implementation + source status/handle/sequence/total-written; not sent here |
59 / D9 | Write close | u32 handle, u32 finalization_value | Firmware two integers; local Keys backend uses 1, Elektroid uses byte count; see conflict below |
5A / DA | Move | cstr source, cstr destination | Source + firmware status; destination-path semantics must be device scoped; not sent |
5B / DB | Copy | cstr source, cstr destination | Source + firmware status; not sent |
5C / DC | Clear | cstr path | Source + local implementation + firmware status; removes stored data; not sent |
5D / DD | Swap | cstr first, cstr second | Source + firmware status; not sent |
5E / DE | Rename | cstr path, cstr new_name | Firmware two string fields and advertised opcode; response format/name constraints unverified; not sent |
Typed request classes and constructor evidence are in firmware-api-vtables.json (evidence/firmware-api-vtables.json, retained research) and firmware-request-fields.json (evidence/firmware-request-fields.json, retained research). A class linked into firmware does not imply the command is enabled: StorageSpace 0x05 and sample/raw filesystem families occur in shared code but were not advertised by this Keys. Do not import Digitakt commands merely because its manufacturer or RPC envelope matches.
Evidence: Elektron/KeysSequencer/research/usb-api/file-rpc-api.md.
File reads: three independent integrity checks
The measured native request used 54 + path + NUL + u32(4096); compressed project reads added byte 01. Open response was status at 0, handle u32 at 1, negotiated chunk u32 at 5, compression byte at 9. Read partial reply:
| Offset | Type | Field |
|---|---|---|
| 0 | u8 | Status |
| 1 | u32 | Handle |
| 5 | u32 | Sequence |
| 9 | u32 | Progress; unit/rounding not established |
| 13 | u8 | Last/final chunk flag |
| 14 | u32 | CRC of returned data |
| 18 | u32 | Data length |
| 22 | Bytes | Data; exact declared length |
Use zlib.crc32(data, 0xFFFFFFFF) & 0xFFFFFFFF, not the default zero seed. The independent validator confirmed it on 532 chunks. This definition avoids ambiguous “standard CRC32” descriptions concerning initial/final XOR conventions.
The local Keys backend starts read sequences at 1; all sample reads succeeded with that convention. Pinned Elektroid starts at 0. Sequence zero/warm-up semantics on this Keys were not measured, so do not assert universal zero-based reads. Writes in the prior local backend start at 0. Validate echoed handle and sequence independently of the outer RPC ID.
Accept negotiated sizes only within a deliberate bound; treat length, final flag and close byte count consistently. Close returned the full container byte count. Release handles after failures where possible, keeping the original error; this research tool currently inherits the old client's success-path close behavior, so abnormal read failures may require reconnecting before reuse.
The sample project's native and compressed bodies, their SHA-256s, container fields and block lengths are in validation.json (evidence/validation.json, retained research). Container layout and LZ4 (sysex-and-payloads.md#native-file-container-and-compression, retained research) describes the transferred bytes.
Evidence: Elektron/KeysSequencer/research/usb-api/file-rpc-api.md.
The LCD decoding correction
The measured 488-byte bitmap uses column-major big-endian 32-bit words. Bit zero of each word is the top pixel. For pixel (x,y), read bitmap byte 4*x + 3 - y//8, bit y%8. Column zero is leftmost and row zero is the top. The four bytes of each column must be reversed before treating them as top-to-bottom eight-pixel pages.
Eight candidate layouts (evidence/lcd-layout-candidates.png, retained research) were rendered offline. The verified interpretation is columns-reversed, LSB first. The earlier little-endian interpretation preserved readable text within individual eight-pixel strips, but reordered those strips and split the knob glyphs at the screen edges. The correction restores complete glyphs, tempo at the upper left, kit below it and pattern at the lower left, matching the main-interface diagram on printed page 9 of the official Analog Keys manual. That diagram verifies screen arrangement; the OS 1.52 RPC capture establishes the bytes. Fresh read-only selectors 0, 1 and 2 returned identical 122×32/488-byte snapshots on this screen; their meanings and alternate sizes remain unassigned. The tool maps a set bit to black for this preview; display colour/polarity is a presentation choice. Correction evidence (evidence/2026-10-08-lcd-correction/layout.json, retained research).
Illustration reference: Decoded Analog Keys LCD capture (evidence/lcd-preview.png, retained research)
Evidence: Elektron/KeysSequencer/research/usb-api/file-rpc-api.md.
What Overbridge discovery does and does not establish
Interface 2 carries vendor bulk endpoints 02 OUT and 82 IN, max packet 64. The installed driver exposes separate asynchronous-read/write and USB-message subscriptions. This establishes host API entry points, but not the complete message serializer on that USB pipe.
Some open-source Overbridge implementations describe an EP0 name/version query (bmRequestType=C0, request 1/name32 or 2/version32), a name-setting vendor OUT request, seven-frame blocks, counters, 07FF markers and 32-bit big-endian audio words. Those are other-device protocol candidates, not verified Analog Keys formats. The pinned Overwitch project explicitly excludes Analog Keys, Analog Four MKI and Analog Rytm MKI; its interface assignments and interrupt/audio assumptions differ from the descriptors here. Overwitch primary source and support list.
The pinned dtdump tool targets Digitakt OS 1.11 BETA6/Digitone and covers some other-device private message headers. It was inspected but not run against the Keys. Its opcodes/structures are not a substitute for Keys evidence. dtdump primary source.
No USBPcap/Wireshark capture was available in the existing tool environment. WinMM captures retain decoded MIDI bytes, while descriptor GETs retain descriptors; neither captures active vendor bulk/audio URBs. This distinction is the principal limit of the Overbridge wire review.
Evidence: Elektron/KeysSequencer/research/usb-api/overbridge.md.
Applying the findings
Dispatch by transport family before interpreting an opcode: legacy SysEx uses manufacturer-family byte 06, while file RPC uses 10. Correlate the RPC reply ID, then verify the file handle/sequence, then verify the returned data CRC and length. None of those checks substitutes for the others.
Use one-based numeric +Drive paths (/projects/1, /soundbanks/A/1) independently of zero-based legacy slots. Serialize transactions, preserve unknown records, and give every stream a bounded timeout. Receiving MIDI clocks is not evidence that a file transfer is progressing. The USB-MIDI packet boundary is also unrelated to a SysEx or file-block boundary.
Limits and open questions
Reads were exercised; advertised writes and firmware-update commands were not replayed in this session. The write-close second integer differs between reviewed implementations, and an upgrade-start endian question remains unresolved. A timeout does not prove an opcode unsupported. Overbridge sample packing, active channel maps, clocking and the full private control ABI still need passive traffic evidence. The paper inventories those gaps rather than supplying guessed payloads.
Primary references
- Elektron Analog Keys user manual
- USB-IF: USB MIDI 1.0 specification
- Elektron: backing up and restoring Analog Keys/Four
Research provenance
The methods and results above are drawn from these retained research records. Dates and product versions inside the article identify the scope of each experiment; publication review does not mean the hardware or numerical tests were rerun.
Elektron/KeysSequencer/research/usb-api/usb-transport.md— SHA-256b5a043a99690076c97f46027005d9fd6ce72c4e7b3a37592cc5e39b48421e71fElektron/KeysSequencer/research/usb-api/file-rpc-api.md— SHA-256afb74cabdbcbda58cf28df2518b1bd35dc9b1a2295ddd390a1fbefae6186203aElektron/KeysSequencer/research/usb-api/overbridge.md— SHA-25644b390c2778021e670212c232a89188dd2f5be4000319b6fc06cbc127a48a5bf
