Files
midi-sequencer/c-backend/src/sequencer.h
mikael-lovqvists-claude-agent 9b45905d80 Add multi-track playback with mute/solo support
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>
2026-04-25 04:16:06 +00:00

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);