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>
156 lines
6.1 KiB
C
156 lines
6.1 KiB
C
/* AUTO-GENERATED by codegen/gen.mjs — DO NOT EDIT */
|
||
/* Source: protocol.yaml version 1 */
|
||
#pragma once
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
|
||
#define PROTOCOL_VERSION 1
|
||
#define FRAME_HEADER_SIZE 3
|
||
#define DIRECTION_C_TO_NODE(t) ((t) >= 0x80)
|
||
|
||
/* Record types */
|
||
typedef enum {
|
||
RT_HELLO = 0x01,
|
||
RT_DEFINE_PATTERN = 0x02,
|
||
RT_CLEAR_PATTERN = 0x03,
|
||
RT_ADD_NOTE = 0x04,
|
||
RT_ADD_SUB_PATTERN = 0x05,
|
||
RT_PLAY = 0x06,
|
||
RT_STOP = 0x07,
|
||
RT_SET_TEMPO = 0x08,
|
||
RT_PREVIEW_NOTE = 0x09,
|
||
RT_ADD_TRACK = 0x0A,
|
||
RT_REMOVE_TRACK = 0x0B,
|
||
RT_PLAY_TRACKS = 0x0C,
|
||
RT_SET_TRACK_MUTE = 0x0D,
|
||
RT_ACK = 0x81,
|
||
RT_ERROR = 0x82,
|
||
RT_BEAT_TICK = 0x83,
|
||
RT_PATTERN_END = 0x84,
|
||
} Record_Type;
|
||
|
||
/* Payload sizes (bytes, not including 3-byte frame header) */
|
||
#define PAYLOAD_SIZE_HELLO 1
|
||
#define PAYLOAD_SIZE_DEFINE_PATTERN 4
|
||
#define PAYLOAD_SIZE_CLEAR_PATTERN 2
|
||
#define PAYLOAD_SIZE_ADD_NOTE 6
|
||
#define PAYLOAD_SIZE_ADD_SUB_PATTERN 5
|
||
#define PAYLOAD_SIZE_PLAY 2
|
||
#define PAYLOAD_SIZE_STOP 0
|
||
#define PAYLOAD_SIZE_SET_TEMPO 2
|
||
#define PAYLOAD_SIZE_PREVIEW_NOTE 5
|
||
#define PAYLOAD_SIZE_ADD_TRACK 2
|
||
#define PAYLOAD_SIZE_REMOVE_TRACK 2
|
||
#define PAYLOAD_SIZE_PLAY_TRACKS 0
|
||
#define PAYLOAD_SIZE_SET_TRACK_MUTE 3
|
||
#define PAYLOAD_SIZE_ACK 1
|
||
#define PAYLOAD_SIZE_ERROR 2
|
||
#define PAYLOAD_SIZE_BEAT_TICK 4
|
||
#define PAYLOAD_SIZE_PATTERN_END 2
|
||
|
||
/* Record payload structs */
|
||
typedef struct {
|
||
uint8_t version;
|
||
} Msg_Hello;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
uint8_t steps; /* Total step count e.g. 16 for a one-bar pattern at 16th-note resolution */
|
||
uint8_t channel; /* MIDI channel 0-15 */
|
||
} Msg_Define_Pattern;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
} Msg_Clear_Pattern;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
uint8_t step; /* 0-based step index within the pattern */
|
||
uint8_t note; /* MIDI note number 0-127 (middle C = 60) */
|
||
uint8_t velocity; /* 0-127 */
|
||
uint8_t duration_steps; /* Duration in steps (1 = one step) */
|
||
} Msg_Add_Note;
|
||
typedef struct {
|
||
uint16_t pattern_id; /* Parent pattern ID */
|
||
uint8_t step; /* Step within parent at which the sub-pattern begins playing */
|
||
uint16_t sub_pattern_id;
|
||
} Msg_Add_Sub_Pattern;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
} Msg_Play;
|
||
typedef struct {
|
||
char _empty;
|
||
} Msg_Stop;
|
||
typedef struct {
|
||
uint16_t bpm_x10; /* BPM × 10 for 0.1 BPM resolution — e.g. 1200 = 120.0 BPM */
|
||
} Msg_Set_Tempo;
|
||
typedef struct {
|
||
uint8_t channel;
|
||
uint8_t note;
|
||
uint8_t velocity;
|
||
uint16_t duration_ms; /* Note-off will be sent after this many milliseconds */
|
||
} Msg_Preview_Note;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
} Msg_Add_Track;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
} Msg_Remove_Track;
|
||
typedef struct {
|
||
char _empty;
|
||
} Msg_Play_Tracks;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
uint8_t muted;
|
||
} Msg_Set_Track_Mute;
|
||
typedef struct {
|
||
uint8_t acked_type; /* Record type being acknowledged */
|
||
} Msg_Ack;
|
||
typedef struct {
|
||
uint8_t code;
|
||
uint8_t context_type; /* Record type that triggered this error */
|
||
} Msg_Error;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
uint8_t step; /* Current step within the pattern (0-based) */
|
||
uint8_t beat; /* Current beat number within the current cycle (wraps at 255) */
|
||
} Msg_Beat_Tick;
|
||
typedef struct {
|
||
uint16_t pattern_id;
|
||
} Msg_Pattern_End;
|
||
|
||
/* Encode: write complete frame to buf, return total bytes written */
|
||
int proto_encode_hello(uint8_t *buf, const Msg_Hello *r);
|
||
int proto_encode_define_pattern(uint8_t *buf, const Msg_Define_Pattern *r);
|
||
int proto_encode_clear_pattern(uint8_t *buf, const Msg_Clear_Pattern *r);
|
||
int proto_encode_add_note(uint8_t *buf, const Msg_Add_Note *r);
|
||
int proto_encode_add_sub_pattern(uint8_t *buf, const Msg_Add_Sub_Pattern *r);
|
||
int proto_encode_play(uint8_t *buf, const Msg_Play *r);
|
||
int proto_encode_stop(uint8_t *buf);
|
||
int proto_encode_set_tempo(uint8_t *buf, const Msg_Set_Tempo *r);
|
||
int proto_encode_preview_note(uint8_t *buf, const Msg_Preview_Note *r);
|
||
int proto_encode_add_track(uint8_t *buf, const Msg_Add_Track *r);
|
||
int proto_encode_remove_track(uint8_t *buf, const Msg_Remove_Track *r);
|
||
int proto_encode_play_tracks(uint8_t *buf);
|
||
int proto_encode_set_track_mute(uint8_t *buf, const Msg_Set_Track_Mute *r);
|
||
int proto_encode_ack(uint8_t *buf, const Msg_Ack *r);
|
||
int proto_encode_error(uint8_t *buf, const Msg_Error *r);
|
||
int proto_encode_beat_tick(uint8_t *buf, const Msg_Beat_Tick *r);
|
||
int proto_encode_pattern_end(uint8_t *buf, const Msg_Pattern_End *r);
|
||
|
||
/* Decode: parse payload into struct, return 0 on success, -1 on error */
|
||
int proto_decode_hello(const uint8_t *payload, uint16_t len, Msg_Hello *r);
|
||
int proto_decode_define_pattern(const uint8_t *payload, uint16_t len, Msg_Define_Pattern *r);
|
||
int proto_decode_clear_pattern(const uint8_t *payload, uint16_t len, Msg_Clear_Pattern *r);
|
||
int proto_decode_add_note(const uint8_t *payload, uint16_t len, Msg_Add_Note *r);
|
||
int proto_decode_add_sub_pattern(const uint8_t *payload, uint16_t len, Msg_Add_Sub_Pattern *r);
|
||
int proto_decode_play(const uint8_t *payload, uint16_t len, Msg_Play *r);
|
||
int proto_decode_stop(const uint8_t *payload, uint16_t len, Msg_Stop *r);
|
||
int proto_decode_set_tempo(const uint8_t *payload, uint16_t len, Msg_Set_Tempo *r);
|
||
int proto_decode_preview_note(const uint8_t *payload, uint16_t len, Msg_Preview_Note *r);
|
||
int proto_decode_add_track(const uint8_t *payload, uint16_t len, Msg_Add_Track *r);
|
||
int proto_decode_remove_track(const uint8_t *payload, uint16_t len, Msg_Remove_Track *r);
|
||
int proto_decode_play_tracks(const uint8_t *payload, uint16_t len, Msg_Play_Tracks *r);
|
||
int proto_decode_set_track_mute(const uint8_t *payload, uint16_t len, Msg_Set_Track_Mute *r);
|
||
int proto_decode_ack(const uint8_t *payload, uint16_t len, Msg_Ack *r);
|
||
int proto_decode_error(const uint8_t *payload, uint16_t len, Msg_Error *r);
|
||
int proto_decode_beat_tick(const uint8_t *payload, uint16_t len, Msg_Beat_Tick *r);
|
||
int proto_decode_pattern_end(const uint8_t *payload, uint16_t len, Msg_Pattern_End *r);
|