Files
jack-looper/engine.h
Loic Coenen a47598df8c feat: refactor transport into separate module with master/slave clock support
Co-authored-by: aider (deepseek/deepseek-coder) <aider@aider.chat>
2026-05-01 21:08:38 +00:00

201 lines
5.6 KiB
C

#ifndef ENGINE_H
#define ENGINE_H
#include <jack/jack.h>
#include <jack/midiport.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#include "transport.h"
#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
// Convert scene/channel to flat clip index
#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 struct {
ClipState state;
float *buffer;
size_t buffer_size;
size_t write_position;
size_t read_position;
bool is_playing;
} Clip;
// Maximum number of queued commands from frontend to audio thread
#define MAX_QUEUED_COMMANDS 64
typedef enum {
CMD_TRIGGER_CLIP,
CMD_TRIGGER_SCENE,
CMD_RESET_CLIP,
CMD_SET_QUANTIZE_MODE,
CMD_SET_QUANTIZE_THRESHOLD,
CMD_RESET_TRANSPORT,
CMD_UNDO,
CMD_REDO,
CMD_TRANSPORT_PLAY,
CMD_TRANSPORT_PAUSE,
CMD_TRANSPORT_STOP,
CMD_TRANSPORT_TOGGLE_PLAY,
CMD_SET_CLOCK_SOURCE,
CMD_SET_BPM
} CommandType;
// Undo/Redo action types
typedef enum {
ACTION_TRIGGER_CLIP,
ACTION_TRIGGER_SCENE,
ACTION_RESET_CLIP,
ACTION_SET_QUANTIZE_MODE,
ACTION_SET_QUANTIZE_THRESHOLD,
ACTION_RESET_TRANSPORT
} ActionType;
// Undo/Redo action record
typedef struct {
ActionType type;
int index; // clip_index, scene_index, or mode value
jack_nframes_t value; // threshold value or other numeric param
ClipState previous_state; // For clip state changes
size_t previous_buffer_size;
size_t previous_write_position;
size_t previous_read_position;
bool previous_rolling;
uint32_t previous_clock_count;
uint32_t previous_beat_position;
uint32_t previous_bar_position;
uint32_t previous_sample_position;
QuantizeMode previous_quantize_mode;
jack_nframes_t previous_quantize_threshold;
} UndoAction;
// Undo/Redo history
#define MAX_UNDO_HISTORY 256
typedef struct {
UndoAction actions[MAX_UNDO_HISTORY];
int undo_index; // Points to next action to undo
int redo_index; // Points to next action to redo
int count; // Total actions in history
} UndoHistory;
typedef struct {
CommandType type;
int index; // clip_index, scene_index, or mode value
jack_nframes_t value; // threshold value or other numeric param
} Command;
// Lock-free single-producer single-consumer ring buffer
typedef struct {
Command buffer[MAX_QUEUED_COMMANDS];
atomic_uint write_index;
atomic_uint read_index;
} CommandQueue;
// Queued trigger for quantization
typedef struct QueuedTrigger {
int clip_index;
bool is_scene;
jack_nframes_t trigger_time;
struct QueuedTrigger *next;
} QueuedTrigger;
typedef struct {
jack_client_t *client;
jack_port_t *audio_in_ports[MAX_CHANNELS];
jack_port_t *audio_out_ports[MAX_CHANNELS];
jack_port_t *midi_in_port; // Control channel MIDI
jack_port_t *midi_scene_in_port; // Scene launch MIDI
jack_port_t *midi_clock_in_port; // MIDI clock input
jack_port_t *midi_out_port;
Clip clips[MAX_CLIPS];
int control_channel;
jack_nframes_t sample_rate;
// Transport and clock
Transport *transport;
// Quantization
QuantizeMode quantize_mode;
jack_nframes_t quantize_threshold; // in samples (lookahead)
QueuedTrigger *queued_triggers;
// Thread-safe command queue for frontend -> audio thread communication
CommandQueue command_queue;
// Atomic flags for simple state that frontend reads
atomic_int quantize_mode_atomic; // QuantizeMode
atomic_uint quantize_threshold_atomic;
bool running;
// Undo/Redo
UndoHistory undo_history;
} Engine;
// Engine lifecycle
int engine_init(Engine *engine, const char *client_name);
void engine_cleanup(Engine *engine);
int engine_start(Engine *engine);
void engine_stop(Engine *engine);
// Clip management
void engine_trigger_clip(Engine *engine, int clip_index);
void engine_trigger_scene(Engine *engine, int scene_index);
void engine_reset_clip(Engine *engine, int clip_index);
// Transport
void engine_set_quantize_mode(Engine *engine, QuantizeMode mode);
void engine_set_quantize_threshold(Engine *engine, jack_nframes_t samples);
void engine_transport_play(Engine *engine);
void engine_transport_pause(Engine *engine);
void engine_transport_stop(Engine *engine);
void engine_transport_toggle_play(Engine *engine);
void engine_set_clock_source(Engine *engine, ClockSource source);
void engine_set_bpm(Engine *engine, double bpm);
// Queue management (exposed for testing)
void queue_trigger(Engine *engine, int clip_index, bool is_scene, jack_nframes_t time);
// Thread-safe command submission (called from frontend threads)
int engine_submit_command(Engine *engine, CommandType type, int index, jack_nframes_t value);
// Process pending commands (called from audio thread)
void engine_process_commands(Engine *engine);
// Initialize command queue (exposed for testing)
void command_queue_init(CommandQueue *q);
// Utility
const char* clip_state_to_string(ClipState state);
uint8_t clip_state_to_velocity(ClipState state);
const char* quantize_mode_to_string(QuantizeMode mode);
// Undo/Redo
void engine_undo(Engine *engine);
void engine_redo(Engine *engine);
void engine_push_undo_action(Engine *engine, UndoAction *action);
void engine_undo_action(Engine *engine);
void engine_redo_action(Engine *engine);
#endif // ENGINE_H