- protocol.yaml: SSoT for the binary framing protocol (12 record types) - codegen/gen.mjs: generates C header/source and Node ESM from protocol.yaml - c-backend: ALSA sequencer with drift-free clock_nanosleep tick thread, pattern store (hierarchical sub-patterns), Unix socket server - node-server: Express 5 web app — REST API, SSE for real-time beat events, step-sequencer frontend with pending-edit / explicit-save flow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
117 lines
4.6 KiB
C
117 lines
4.6 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_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_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 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_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_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);
|