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>
80 lines
2.2 KiB
C
80 lines
2.2 KiB
C
#pragma once
|
|
#include <stdint.h>
|
|
#include <pthread.h>
|
|
#include <stdatomic.h>
|
|
#include <time.h>
|
|
#include <alsa/asoundlib.h>
|
|
#include "pattern_store.h"
|
|
|
|
#define NOTE_OFF_QUEUE_SIZE 256
|
|
#define MAX_SUBS_PER_TRACK 16
|
|
#define MAX_TRACKS 16
|
|
|
|
typedef struct {
|
|
struct timespec fire_at;
|
|
uint8_t channel;
|
|
uint8_t note;
|
|
} Pending_Note_Off;
|
|
|
|
typedef struct {
|
|
uint16_t pattern_id;
|
|
uint8_t local_step;
|
|
int active;
|
|
} Pending_Sub;
|
|
|
|
typedef struct {
|
|
uint16_t pattern_id;
|
|
uint8_t current_step;
|
|
uint8_t current_beat;
|
|
uint8_t muted;
|
|
int active;
|
|
Pending_Sub pending_subs[MAX_SUBS_PER_TRACK];
|
|
int pending_sub_count;
|
|
} Track;
|
|
|
|
/* Function pointer used to write frames back to the Node client */
|
|
typedef void (*Send_Fn)(void *ctx, const uint8_t *buf, int len);
|
|
|
|
typedef struct {
|
|
/* ALSA */
|
|
snd_seq_t *seq;
|
|
int port_id;
|
|
int client_id;
|
|
|
|
/* Pattern store (shared with socket thread) */
|
|
Pattern_Store *ps;
|
|
|
|
/* Callback for sending frames to Node */
|
|
Send_Fn send_fn;
|
|
void *send_ctx;
|
|
|
|
/* Tracks — protected by tracks_mutex */
|
|
Track tracks[MAX_TRACKS];
|
|
pthread_mutex_t tracks_mutex;
|
|
|
|
/* Note-off queue — tick thread only, no mutex needed */
|
|
Pending_Note_Off note_offs[NOTE_OFF_QUEUE_SIZE];
|
|
int note_off_count;
|
|
|
|
/* Tempo — protected by tempo_mutex */
|
|
uint16_t bpm_x10;
|
|
pthread_mutex_t tempo_mutex;
|
|
|
|
/* Thread control */
|
|
atomic_int running;
|
|
pthread_t tick_thread;
|
|
} Sequencer;
|
|
|
|
int sequencer_init(Sequencer *seq, Pattern_Store *ps, const char *alsa_name,
|
|
Send_Fn send_fn, void *send_ctx);
|
|
void sequencer_destroy(Sequencer *seq);
|
|
void sequencer_add_track(Sequencer *seq, uint16_t pattern_id);
|
|
void sequencer_remove_track(Sequencer *seq, uint16_t pattern_id);
|
|
void sequencer_set_track_mute(Sequencer *seq, uint16_t pattern_id, uint8_t muted);
|
|
void sequencer_clear_tracks(Sequencer *seq);
|
|
void sequencer_play(Sequencer *seq);
|
|
void sequencer_stop(Sequencer *seq);
|
|
void sequencer_set_tempo(Sequencer *seq, uint16_t bpm_x10);
|
|
void sequencer_preview_note(Sequencer *seq, uint8_t channel, uint8_t note,
|
|
uint8_t velocity, uint16_t duration_ms);
|