Files
midi-sequencer/c-backend/generated/protocol.c
mikael-lovqvists-claude-agent 515cc87382 Add PREVIEW_NOTE, auto-save, per-row play button, and click-to-rename
Protocol:
- Add PREVIEW_NOTE record (id 0x09, node→C): channel, note, velocity,
  duration_ms — plays a note immediately without touching sequencer state

C backend:
- sequencer_preview_note(): fires ALSA note-on, spawns detached thread
  for note-off after duration_ms
- RT_PREVIEW_NOTE handler in on_frame()

Node server:
- encode_preview_note imported from generated protocol
- POST /api/preview route forwards to backend

Frontend (app.mjs):
- Remove pending_notes / is_dirty / Save Notes button; every step-button
  toggle now calls PUT /api/patterns/:id/notes immediately (auto-save)
- Single delegated click handler on #content — no per-render listener
  accumulation
- Row-level ▶ play button per note row → POST /api/preview
- Click-to-rename on note/percussion labels: inline <input>, saves to
  state.custom_labels (Map keyed by "patternId:note"), Escape to cancel
- pattern_updated SSE no longer guarded by is_dirty

CSS (index.html):
- .note-label: cursor pointer + hover highlight
- .note-label-input for inline rename field
- .row-play-btn and .row-play-spacer for per-row preview buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-25 03:33:05 +00:00

236 lines
6.4 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_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_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;
}