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>
289 lines
7.8 KiB
C
289 lines
7.8 KiB
C
/* AUTO-GENERATED by codegen/gen.mjs — DO NOT EDIT */
|
|
#include "protocol.h"
|
|
|
|
/* ── serialization helpers ───────────────────────────────────── */
|
|
|
|
static void put_u8(uint8_t *buf, int *off, uint8_t v) {
|
|
buf[(*off)++] = v;
|
|
}
|
|
static void put_u16(uint8_t *buf, int *off, uint16_t v) {
|
|
buf[(*off)++] = (uint8_t)(v & 0xFF);
|
|
buf[(*off)++] = (uint8_t)(v >> 8);
|
|
}
|
|
static uint8_t get_u8(const uint8_t *buf, int *off) {
|
|
return buf[(*off)++];
|
|
}
|
|
static uint16_t get_u16(const uint8_t *buf, int *off) {
|
|
uint16_t v = (uint16_t)((uint16_t)buf[*off] | ((uint16_t)buf[*off + 1] << 8));
|
|
*off += 2;
|
|
return v;
|
|
}
|
|
static int write_frame(uint8_t *buf, uint8_t record_type, int payload_len) {
|
|
buf[0] = record_type;
|
|
buf[1] = (uint8_t)(payload_len & 0xFF);
|
|
buf[2] = (uint8_t)(payload_len >> 8);
|
|
return FRAME_HEADER_SIZE + payload_len;
|
|
}
|
|
|
|
/* ── encode ──────────────────────────────────────────────────── */
|
|
|
|
int proto_encode_hello(uint8_t *buf, const Msg_Hello *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u8(buf, &off, r->version);
|
|
return write_frame(buf, RT_HELLO, 1);
|
|
}
|
|
|
|
int proto_encode_define_pattern(uint8_t *buf, const Msg_Define_Pattern *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
put_u8(buf, &off, r->steps);
|
|
put_u8(buf, &off, r->channel);
|
|
return write_frame(buf, RT_DEFINE_PATTERN, 4);
|
|
}
|
|
|
|
int proto_encode_clear_pattern(uint8_t *buf, const Msg_Clear_Pattern *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
return write_frame(buf, RT_CLEAR_PATTERN, 2);
|
|
}
|
|
|
|
int proto_encode_add_note(uint8_t *buf, const Msg_Add_Note *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
put_u8(buf, &off, r->step);
|
|
put_u8(buf, &off, r->note);
|
|
put_u8(buf, &off, r->velocity);
|
|
put_u8(buf, &off, r->duration_steps);
|
|
return write_frame(buf, RT_ADD_NOTE, 6);
|
|
}
|
|
|
|
int proto_encode_add_sub_pattern(uint8_t *buf, const Msg_Add_Sub_Pattern *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
put_u8(buf, &off, r->step);
|
|
put_u16(buf, &off, r->sub_pattern_id);
|
|
return write_frame(buf, RT_ADD_SUB_PATTERN, 5);
|
|
}
|
|
|
|
int proto_encode_play(uint8_t *buf, const Msg_Play *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
return write_frame(buf, RT_PLAY, 2);
|
|
}
|
|
|
|
int proto_encode_stop(uint8_t *buf) {
|
|
return write_frame(buf, RT_STOP, 0);
|
|
}
|
|
|
|
int proto_encode_set_tempo(uint8_t *buf, const Msg_Set_Tempo *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->bpm_x10);
|
|
return write_frame(buf, RT_SET_TEMPO, 2);
|
|
}
|
|
|
|
int proto_encode_preview_note(uint8_t *buf, const Msg_Preview_Note *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u8(buf, &off, r->channel);
|
|
put_u8(buf, &off, r->note);
|
|
put_u8(buf, &off, r->velocity);
|
|
put_u16(buf, &off, r->duration_ms);
|
|
return write_frame(buf, RT_PREVIEW_NOTE, 5);
|
|
}
|
|
|
|
int proto_encode_add_track(uint8_t *buf, const Msg_Add_Track *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
return write_frame(buf, RT_ADD_TRACK, 2);
|
|
}
|
|
|
|
int proto_encode_remove_track(uint8_t *buf, const Msg_Remove_Track *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
return write_frame(buf, RT_REMOVE_TRACK, 2);
|
|
}
|
|
|
|
int proto_encode_play_tracks(uint8_t *buf) {
|
|
return write_frame(buf, RT_PLAY_TRACKS, 0);
|
|
}
|
|
|
|
int proto_encode_set_track_mute(uint8_t *buf, const Msg_Set_Track_Mute *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
put_u8(buf, &off, r->muted);
|
|
return write_frame(buf, RT_SET_TRACK_MUTE, 3);
|
|
}
|
|
|
|
int proto_encode_ack(uint8_t *buf, const Msg_Ack *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u8(buf, &off, r->acked_type);
|
|
return write_frame(buf, RT_ACK, 1);
|
|
}
|
|
|
|
int proto_encode_error(uint8_t *buf, const Msg_Error *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u8(buf, &off, r->code);
|
|
put_u8(buf, &off, r->context_type);
|
|
return write_frame(buf, RT_ERROR, 2);
|
|
}
|
|
|
|
int proto_encode_beat_tick(uint8_t *buf, const Msg_Beat_Tick *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
put_u8(buf, &off, r->step);
|
|
put_u8(buf, &off, r->beat);
|
|
return write_frame(buf, RT_BEAT_TICK, 4);
|
|
}
|
|
|
|
int proto_encode_pattern_end(uint8_t *buf, const Msg_Pattern_End *r) {
|
|
int off = FRAME_HEADER_SIZE;
|
|
put_u16(buf, &off, r->pattern_id);
|
|
return write_frame(buf, RT_PATTERN_END, 2);
|
|
}
|
|
|
|
/* ── decode ──────────────────────────────────────────────────── */
|
|
|
|
int proto_decode_hello(const uint8_t *p, uint16_t len, Msg_Hello *r) {
|
|
if (len < 1) return -1;
|
|
int off = 0;
|
|
r->version = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_define_pattern(const uint8_t *p, uint16_t len, Msg_Define_Pattern *r) {
|
|
if (len < 4) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
r->steps = get_u8(p, &off);
|
|
r->channel = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_clear_pattern(const uint8_t *p, uint16_t len, Msg_Clear_Pattern *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_add_note(const uint8_t *p, uint16_t len, Msg_Add_Note *r) {
|
|
if (len < 6) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
r->step = get_u8(p, &off);
|
|
r->note = get_u8(p, &off);
|
|
r->velocity = get_u8(p, &off);
|
|
r->duration_steps = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_add_sub_pattern(const uint8_t *p, uint16_t len, Msg_Add_Sub_Pattern *r) {
|
|
if (len < 5) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
r->step = get_u8(p, &off);
|
|
r->sub_pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_play(const uint8_t *p, uint16_t len, Msg_Play *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_stop(const uint8_t *p, uint16_t len, Msg_Stop *r) {
|
|
(void)p; (void)len; (void)r;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_set_tempo(const uint8_t *p, uint16_t len, Msg_Set_Tempo *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->bpm_x10 = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_preview_note(const uint8_t *p, uint16_t len, Msg_Preview_Note *r) {
|
|
if (len < 5) return -1;
|
|
int off = 0;
|
|
r->channel = get_u8(p, &off);
|
|
r->note = get_u8(p, &off);
|
|
r->velocity = get_u8(p, &off);
|
|
r->duration_ms = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_add_track(const uint8_t *p, uint16_t len, Msg_Add_Track *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_remove_track(const uint8_t *p, uint16_t len, Msg_Remove_Track *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_play_tracks(const uint8_t *p, uint16_t len, Msg_Play_Tracks *r) {
|
|
(void)p; (void)len; (void)r;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_set_track_mute(const uint8_t *p, uint16_t len, Msg_Set_Track_Mute *r) {
|
|
if (len < 3) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
r->muted = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_ack(const uint8_t *p, uint16_t len, Msg_Ack *r) {
|
|
if (len < 1) return -1;
|
|
int off = 0;
|
|
r->acked_type = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_error(const uint8_t *p, uint16_t len, Msg_Error *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->code = get_u8(p, &off);
|
|
r->context_type = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_beat_tick(const uint8_t *p, uint16_t len, Msg_Beat_Tick *r) {
|
|
if (len < 4) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
r->step = get_u8(p, &off);
|
|
r->beat = get_u8(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|
|
|
|
int proto_decode_pattern_end(const uint8_t *p, uint16_t len, Msg_Pattern_End *r) {
|
|
if (len < 2) return -1;
|
|
int off = 0;
|
|
r->pattern_id = get_u16(p, &off);
|
|
(void)off;
|
|
return 0;
|
|
}
|