#ifndef DISPATCHER_H #define DISPATCHER_H #include #include #include #include #include #include "carla.h" // ============================================================ // Application State - contains ALL application state // ============================================================ #define MAX_SCENES 8 #define MAX_CHANNELS 8 #define MAX_CLIPS 512 // 8 grids * 8 scenes * 8 channels #define GRID_ROWS MAX_SCENES #define GRID_COLS MAX_CHANNELS #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 { uint8_t note; uint8_t velocity; uint32_t timestamp; // sample offset within the clip } MidiEvent; #define MAX_MIDI_EVENTS 44100 // ~1 second of dense MIDI data typedef struct { ClipState state; MidiEvent *events; // Dynamically allocated int event_count; int max_events; // Allocated size int read_index; char channel_name[64]; // per-channel name for MIDI grid } MidiClip; 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]; // MIDI clips (separate grid) MidiClip midi_clips[MAX_CLIPS]; bool show_midi_grid; // View toggle: true = MIDI grid, false = Audio grid char channel_names[MAX_CHANNELS][64]; // Channel names for audio grid // 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]; int batch_sizes[MAX_UNDO_HISTORY]; // NEW: track batch sizes int current_batch_size; // NEW: current batch being recorded } undo; // Ring buffers for real-time recording (written by JACK callback, read by dispatcher) // Lock-free: single producer (JACK callback), single consumer (dispatcher thread) float record_buffer[MAX_CHANNELS][MAX_BUFFER_SIZE]; atomic_size_t record_write_pos[MAX_CHANNELS]; // Only written by JACK callback atomic_size_t record_read_pos[MAX_CHANNELS]; // Only written by dispatcher thread // 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_SAVE_PROJECT, ACTION_LOAD_PROJECT, ACTION_PROCESS_AUDIO, ACTION_QUIT, ACTION_SET_SHOW_MIDI_GRID, ACTION_MIDI_CLIP_TRIGGER, ACTION_MIDI_CLIP_RESET, ACTION_SET_CHANNEL_NAME } 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 { char filename[512]; } save_project; struct { char filename[512]; } load_project; struct { jack_nframes_t nframes; } process_audio; struct { bool show; } set_show_midi_grid; struct { int clip_index; } midi_clip_trigger; struct { int clip_index; } midi_clip_reset; struct { int channel; char name[64]; } set_channel_name; } 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) void dispatcher_get_state(AppState *out); // Get pointer to dispatcher's state (for real-time threads that can't block) // WARNING: This is NOT thread-safe. Only use in JACK process callback // which is serialized per client. AppState* dispatcher_get_state_ptr(void); // ============================================================ // Reducer - pure function // ============================================================ AppState reducer(AppState state, Action action); #endif // DISPATCHER_H