refactor: implement unidirectional data flow with dispatcher pattern
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
This commit is contained in:
166
dispatcher.h
166
dispatcher.h
@@ -0,0 +1,166 @@
|
||||
#ifndef DISPATCHER_H
|
||||
#define DISPATCHER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <jack/jack.h>
|
||||
#include <pthread.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;
|
||||
|
||||
// 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_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 { 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
|
||||
|
||||
Reference in New Issue
Block a user