Files
jack-looper/dispatcher.h
2026-05-02 21:29:56 +00:00

181 lines
5.0 KiB
C

#ifndef DISPATCHER_H
#define DISPATCHER_H
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#include <jack/jack.h>
#include <pthread.h>
#include "carla.h"
// ============================================================
// Application State - contains ALL application state
// ============================================================
#define MAX_SCENES 8
#define MAX_CHANNELS 8
#define MAX_CLIPS (MAX_SCENES * MAX_CHANNELS) // 64
#define MAX_BUFFER_SIZE 441000 // 10 seconds at 44.1kHz
#define MAX_UNDO_HISTORY 256
#define CLIP_INDEX(scene, channel) ((scene) * MAX_CHANNELS + (channel))
typedef enum {
CLIP_EMPTY,
CLIP_RECORDING,
CLIP_LOOPING,
CLIP_STOPPED
} ClipState;
typedef enum {
QUANTIZE_OFF,
QUANTIZE_BEAT,
QUANTIZE_BAR
} QuantizeMode;
typedef enum {
TRANSPORT_STOPPED,
TRANSPORT_PLAYING,
TRANSPORT_PAUSED
} TransportState;
typedef enum {
CLOCK_SOURCE_INTERNAL,
CLOCK_SOURCE_MIDI
} ClockSource;
typedef struct {
ClipState state;
float *buffer;
size_t buffer_size;
size_t write_position;
size_t read_position;
} Clip;
typedef struct {
// Transport state
TransportState transport_state;
ClockSource clock_source;
uint32_t clock_count;
uint32_t beat_position;
uint32_t bar_position;
uint32_t sample_position;
double bpm;
double samples_per_beat;
double sample_accumulator;
// Quantization
QuantizeMode quantize_mode;
jack_nframes_t quantize_threshold;
// Clips
Clip clips[MAX_CLIPS];
// Undo history
struct {
int undo_index;
int redo_index;
int count;
ClipState prev_clip_states[MAX_UNDO_HISTORY];
int prev_clip_indices[MAX_UNDO_HISTORY];
size_t prev_buffer_sizes[MAX_UNDO_HISTORY];
size_t prev_write_positions[MAX_UNDO_HISTORY];
size_t prev_read_positions[MAX_UNDO_HISTORY];
} undo;
// Carla host
CarlaHost carla_host;
// JACK info (needed by audio thread)
jack_nframes_t sample_rate;
bool running;
} AppState;
// ============================================================
// Actions
// ============================================================
typedef enum {
ACTION_TRIGGER_CLIP,
ACTION_TRIGGER_SCENE,
ACTION_RESET_CLIP,
ACTION_SET_QUANTIZE_MODE,
ACTION_SET_QUANTIZE_THRESHOLD,
ACTION_TRANSPORT_PLAY,
ACTION_TRANSPORT_PAUSE,
ACTION_TRANSPORT_STOP,
ACTION_TRANSPORT_TOGGLE_PLAY,
ACTION_SET_CLOCK_SOURCE,
ACTION_SET_BPM,
ACTION_RESET_TRANSPORT,
ACTION_UNDO,
ACTION_REDO,
ACTION_SAVE_CLIP,
ACTION_LOAD_CLIP,
ACTION_MIDI_NOTE_ON,
ACTION_MIDI_SCENE_LAUNCH,
ACTION_RACK_ADD_PLUGIN,
ACTION_RACK_REMOVE_PLUGIN,
ACTION_RACK_SET_PARAMETER,
ACTION_RACK_SET_VOLUME,
ACTION_RACK_BYPASS,
ACTION_PROCESS_AUDIO,
ACTION_QUIT
} ActionType;
typedef struct {
ActionType type;
union {
struct { int clip_index; } trigger_clip;
struct { int scene_index; } trigger_scene;
struct { int clip_index; } reset_clip;
struct { QuantizeMode mode; } set_quantize_mode;
struct { jack_nframes_t threshold; } set_quantize_threshold;
struct { ClockSource source; } set_clock_source;
struct { double bpm; } set_bpm;
struct { int clip_index; } save_clip;
struct { int clip_index; char filename[256]; } load_clip;
struct { int note; int velocity; int channel; jack_nframes_t time; } midi_note_on;
struct { int scene_index; jack_nframes_t time; } midi_scene_launch;
struct { int channel; char uri[512]; PluginType type; } rack_add_plugin;
struct { int channel; int plugin_index; } rack_remove_plugin;
struct { int channel; int plugin_index; int param_index; float value; } rack_set_parameter;
struct { int channel; float volume; } rack_set_volume;
struct { int channel; bool bypass; } rack_bypass;
struct { jack_nframes_t nframes; } process_audio;
} data;
} Action;
// ============================================================
// Dispatcher API
// ============================================================
// Thread-safe dispatch function - called by any thread
typedef void (*DispatchFn)(Action action);
// Subscriber callback - called after each state update (from dispatcher thread)
typedef void (*SubscriberFn)(AppState *state, void *user_data);
// Initialize the dispatcher with initial state
// Returns the dispatch function that consumers can call
DispatchFn dispatcher_init(AppState *initial_state);
// Register a subscriber (call before dispatcher_start)
void dispatcher_subscribe(SubscriberFn fn, void *user_data);
// Start the dispatcher thread
void dispatcher_start(void);
// Stop the dispatcher and cleanup
void dispatcher_stop(void);
// Get current state (thread-safe snapshot)
AppState dispatcher_get_state(void);
// ============================================================
// Reducer - pure function
// ============================================================
AppState reducer(AppState state, Action action);
#endif // DISPATCHER_H