Replaces the single-root-pattern sequencer with a Track[] array that allows multiple patterns to loop independently. Adds ADD_TRACK (0x0A), REMOVE_TRACK (0x0B), PLAY_TRACKS (0x0C), and SET_TRACK_MUTE (0x0D) protocol records. The C backend gains per-track pending_subs and a tracks_mutex. The Node server gains track-state APIs (/api/tracks/:id/ active, mute, solo) and the frontend shows per-pattern track/mute/solo buttons in the sidebar list. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
195 lines
4.8 KiB
YAML
195 lines
4.8 KiB
YAML
version: 1
|
||
description: Binary protocol between Node sequencer and C ALSA MIDI backend
|
||
|
||
# Frame format: [record_type: uint8][payload_length: uint16le][payload: bytes...]
|
||
# Header size: 3 bytes
|
||
# High bit set (0x80+) means C → Node direction
|
||
|
||
frame:
|
||
- name: record_type
|
||
type: uint8
|
||
- name: payload_length
|
||
type: uint16
|
||
|
||
# Type mapping used by codegen
|
||
types:
|
||
uint8: { c_type: uint8_t, c_put: put_u8, c_get: get_u8, node_write: writeUInt8, node_read: readUInt8, size: 1 }
|
||
uint16: { c_type: uint16_t, c_put: put_u16, c_get: get_u16, node_write: writeUInt16LE, node_read: readUInt16LE, size: 2 }
|
||
|
||
records:
|
||
HELLO:
|
||
id: 0x01
|
||
direction: node_to_c
|
||
description: Protocol version handshake
|
||
fields:
|
||
- name: version
|
||
type: uint8
|
||
|
||
DEFINE_PATTERN:
|
||
id: 0x02
|
||
direction: node_to_c
|
||
description: Define or redefine a pattern (clears existing data)
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
- name: steps
|
||
type: uint8
|
||
note: Total step count e.g. 16 for a one-bar pattern at 16th-note resolution
|
||
- name: channel
|
||
type: uint8
|
||
note: MIDI channel 0-15
|
||
|
||
CLEAR_PATTERN:
|
||
id: 0x03
|
||
direction: node_to_c
|
||
description: Remove all notes and sub-pattern references from a pattern
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
|
||
ADD_NOTE:
|
||
id: 0x04
|
||
direction: node_to_c
|
||
description: Add a note event to a pattern at a specific step
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
- name: step
|
||
type: uint8
|
||
note: 0-based step index within the pattern
|
||
- name: note
|
||
type: uint8
|
||
note: MIDI note number 0-127 (middle C = 60)
|
||
- name: velocity
|
||
type: uint8
|
||
note: 0-127
|
||
- name: duration_steps
|
||
type: uint8
|
||
note: Duration in steps (1 = one step)
|
||
|
||
ADD_SUB_PATTERN:
|
||
id: 0x05
|
||
direction: node_to_c
|
||
description: Schedule a sub-pattern to start at a given step within a parent pattern
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
note: Parent pattern ID
|
||
- name: step
|
||
type: uint8
|
||
note: Step within parent at which the sub-pattern begins playing
|
||
- name: sub_pattern_id
|
||
type: uint16
|
||
|
||
PLAY:
|
||
id: 0x06
|
||
direction: node_to_c
|
||
description: Start playing a pattern from step 0
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
|
||
STOP:
|
||
id: 0x07
|
||
direction: node_to_c
|
||
description: Stop all playback and send MIDI all-notes-off
|
||
fields: []
|
||
|
||
SET_TEMPO:
|
||
id: 0x08
|
||
direction: node_to_c
|
||
description: Set global tempo (applies immediately, even mid-sequence)
|
||
fields:
|
||
- name: bpm_x10
|
||
type: uint16
|
||
note: "BPM × 10 for 0.1 BPM resolution — e.g. 1200 = 120.0 BPM"
|
||
|
||
PREVIEW_NOTE:
|
||
id: 0x09
|
||
direction: node_to_c
|
||
description: Play a single note immediately without affecting sequencer state
|
||
fields:
|
||
- name: channel
|
||
type: uint8
|
||
- name: note
|
||
type: uint8
|
||
- name: velocity
|
||
type: uint8
|
||
- name: duration_ms
|
||
type: uint16
|
||
note: Note-off will be sent after this many milliseconds
|
||
|
||
ADD_TRACK:
|
||
id: 0x0A
|
||
direction: node_to_c
|
||
description: Add a pattern as an independently-looping track
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
|
||
REMOVE_TRACK:
|
||
id: 0x0B
|
||
direction: node_to_c
|
||
description: Remove a track and stop its playback
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
|
||
PLAY_TRACKS:
|
||
id: 0x0C
|
||
direction: node_to_c
|
||
description: Start the multi-track engine with all added tracks
|
||
fields: []
|
||
|
||
SET_TRACK_MUTE:
|
||
id: 0x0D
|
||
direction: node_to_c
|
||
description: Mute or unmute a track without resetting its position
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
- name: muted
|
||
type: uint8
|
||
|
||
ACK:
|
||
id: 0x81
|
||
direction: c_to_node
|
||
description: Acknowledge a received command
|
||
fields:
|
||
- name: acked_type
|
||
type: uint8
|
||
note: Record type being acknowledged
|
||
|
||
ERROR:
|
||
id: 0x82
|
||
direction: c_to_node
|
||
description: Report an error condition
|
||
fields:
|
||
- name: code
|
||
type: uint8
|
||
- name: context_type
|
||
type: uint8
|
||
note: Record type that triggered this error
|
||
|
||
BEAT_TICK:
|
||
id: 0x83
|
||
direction: c_to_node
|
||
description: Timing notification sent on every sequencer step
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|
||
- name: step
|
||
type: uint8
|
||
note: Current step within the pattern (0-based)
|
||
- name: beat
|
||
type: uint8
|
||
note: Current beat number within the current cycle (wraps at 255)
|
||
|
||
PATTERN_END:
|
||
id: 0x84
|
||
direction: c_to_node
|
||
description: Notification that a pattern has completed one full cycle
|
||
fields:
|
||
- name: pattern_id
|
||
type: uint16
|