fuzzy sample

This commit is contained in:
Loic Coenen
2026-05-03 11:31:54 +00:00
parent 7ffcd67436
commit 5e4d4e4d44
30 changed files with 0 additions and 542 deletions

View File

@@ -1,83 +0,0 @@
#ifndef CARLA_H
#define CARLA_H
#include <stdint.h>
#include <stdbool.h>
#include <jack/jack.h>
// Carla plugin types we support
typedef enum {
PLUGIN_TYPE_NONE,
PLUGIN_TYPE_LV2,
PLUGIN_TYPE_VST2,
PLUGIN_TYPE_VST3,
PLUGIN_TYPE_CLAP,
PLUGIN_TYPE_INTERNAL // Our built-in plugins (gain, etc.)
} PluginType;
// Plugin descriptor
typedef struct {
char name[256];
char uri[512];
PluginType type;
int carla_plugin_id; // Carla's internal plugin ID
int num_parameters;
float *parameters; // Current parameter values
char **parameter_names;
} PluginInfo;
// Rack for a single channel
typedef struct {
int num_plugins;
PluginInfo plugins[16]; // Max 16 plugins per channel
float volume; // Channel volume (0.0 - 2.0)
bool bypassed;
} ChannelRack;
// Carla host state
typedef struct {
bool initialized;
ChannelRack channel_racks[8]; // One rack per channel (MAX_CHANNELS)
jack_client_t *client;
// For plugin scanning
char **available_plugins;
int num_available_plugins;
} CarlaHost;
// Initialize Carla host
int carla_init(CarlaHost *host, jack_client_t *client);
// Cleanup Carla host
void carla_cleanup(CarlaHost *host);
// Add a plugin to a channel's rack
int carla_add_plugin(CarlaHost *host, int channel, const char *uri, PluginType type);
// Remove a plugin from a channel's rack
int carla_remove_plugin(CarlaHost *host, int channel, int plugin_index);
// Set plugin parameter
int carla_set_parameter(CarlaHost *host, int channel, int plugin_index, int param_index, float value);
// Get plugin parameter
float carla_get_parameter(CarlaHost *host, int channel, int plugin_index, int param_index);
// Set channel volume
void carla_set_channel_volume(CarlaHost *host, int channel, float volume);
// Get channel volume
float carla_get_channel_volume(CarlaHost *host, int channel);
// Process audio through the rack (called from audio thread)
void carla_process(CarlaHost *host, int channel, float *in_buffer, float *out_buffer, jack_nframes_t nframes);
// Scan for available plugins
int carla_scan_plugins(CarlaHost *host);
// Get available plugin names for fuzzy search
const char** carla_get_available_plugins(CarlaHost *host, int *count);
// Get plugin display name
const char* carla_get_plugin_name(CarlaHost *host, int channel, int plugin_index);
#endif // CARLA_H

BIN
carla.o Normal file
View File

Binary file not shown.

BIN
cli.o
View File

Binary file not shown.

BIN
dispatcher.o Normal file
View File

Binary file not shown.

BIN
engine.o
View File

Binary file not shown.

BIN
fs.o Normal file
View File

Binary file not shown.

BIN
gui.o
View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

BIN
main.o
View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,381 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdatomic.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include "dispatcher.h"
#include <jack/jack.h>
static volatile int keep_running = 1;
static void handle_sigint(int sig) {
(void)sig;
keep_running = 0;
}
// Helper: create a dispatcher with initial state
static DispatchFn setup_dispatcher(AppState *state) {
// Initialize default state
memset(state, 0, sizeof(AppState));
state->transport_state = TRANSPORT_STOPPED;
state->clock_source = CLOCK_SOURCE_INTERNAL;
state->bpm = 120.0;
state->samples_per_beat = (48000 * 60.0) / 120.0;
state->quantize_mode = QUANTIZE_OFF;
state->quantize_threshold = 0;
for (int i = 0; i < MAX_CLIPS; i++) {
state->clips[i].state = CLIP_EMPTY;
state->clips[i].buffer_size = 0;
state->clips[i].write_position = 0;
state->clips[i].read_position = 0;
}
state->undo.undo_index = 0;
state->undo.redo_index = 0;
DispatchFn dispatch = dispatcher_init(state);
assert(dispatch != NULL);
dispatcher_start();
return dispatch;
}
static void teardown_dispatcher(DispatchFn dispatch) {
(void)dispatch;
dispatcher_stop();
}
// Helper to build an Action
static Action make_action(ActionType type, int int_arg, jack_nframes_t nframes_arg) {
Action action;
memset(&action, 0, sizeof(action));
action.type = type;
// Set the appropriate union member based on type
switch (type) {
case ACTION_TRIGGER_CLIP:
action.data.trigger_clip.clip_index = int_arg;
break;
case ACTION_TRIGGER_SCENE:
action.data.trigger_scene.scene_index = int_arg;
break;
case ACTION_RESET_CLIP:
action.data.reset_clip.clip_index = int_arg;
break;
case ACTION_SET_QUANTIZE_MODE:
action.data.set_quantize_mode.mode = (QuantizeMode)int_arg;
break;
case ACTION_SET_QUANTIZE_THRESHOLD:
action.data.set_quantize_threshold.threshold = nframes_arg;
break;
case ACTION_SET_CLOCK_SOURCE:
action.data.set_clock_source.source = (ClockSource)int_arg;
break;
case ACTION_SET_BPM:
action.data.set_bpm.bpm = (double)int_arg;
break;
case ACTION_SAVE_CLIP:
action.data.save_clip.clip_index = int_arg;
break;
case ACTION_LOAD_CLIP:
action.data.load_clip.clip_index = int_arg;
break;
case ACTION_MIDI_NOTE_ON:
action.data.midi_note_on.note = int_arg;
action.data.midi_note_on.time = nframes_arg;
break;
case ACTION_MIDI_SCENE_LAUNCH:
action.data.midi_scene_launch.scene_index = int_arg;
action.data.midi_scene_launch.time = nframes_arg;
break;
case ACTION_PROCESS_AUDIO:
action.data.process_audio.nframes = nframes_arg;
break;
default:
// For actions with no data (TRANSPORT_PLAY, etc.), nothing to set
break;
}
return action;
}
// Stress test 1: Rapid clip triggers via dispatch
static void stress_rapid_triggers(void) {
printf("Stress test 1: Rapid clip triggers...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 10000;
for (int i = 0; i < num_ops; i++) {
int clip_idx = i % MAX_CLIPS;
Action action = make_action(ACTION_TRIGGER_CLIP, clip_idx, 0);
dispatch(action);
}
// Give dispatcher time to process
usleep(100000);
// Verify some clips were triggered (state changed)
AppState current = dispatcher_get_state();
int triggered = 0;
for (int i = 0; i < MAX_CLIPS; i++) {
if (current.clips[i].state != CLIP_EMPTY) triggered++;
}
printf(" Triggered %d clips\n", triggered);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 2: Mix of clip and scene triggers
static void stress_mixed_triggers(void) {
printf("Stress test 2: Mixed clip/scene triggers...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 5000;
for (int i = 0; i < num_ops; i++) {
if (i % 3 == 0) {
int scene_idx = (i / 3) % MAX_SCENES;
Action action = make_action(ACTION_TRIGGER_SCENE, scene_idx, 0);
dispatch(action);
} else {
int clip_idx = i % MAX_CLIPS;
Action action = make_action(ACTION_TRIGGER_CLIP, clip_idx, 0);
dispatch(action);
}
}
usleep(100000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 3: Queue overflow (should not crash)
static void stress_queue_overflow(void) {
printf("Stress test 3: Queue overflow (should drop gracefully)...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
// Submit more than queue capacity (256)
int num_ops = 500;
for (int i = 0; i < num_ops; i++) {
Action action = make_action(ACTION_TRIGGER_CLIP, i % MAX_CLIPS, 0);
dispatch(action);
}
usleep(100000);
teardown_dispatcher(dispatch);
printf(" PASSED (no crash)\n");
}
// Stress test 4: Undo/Redo stress
static void stress_undo_redo(void) {
printf("Stress test 4: Undo/Redo stress...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 1000;
for (int i = 0; i < num_ops; i++) {
int clip_idx = i % MAX_CLIPS;
Action action = make_action(ACTION_TRIGGER_CLIP, clip_idx, 0);
dispatch(action);
usleep(1000);
// Undo every other operation
if (i % 2 == 0) {
Action undo_action = make_action(ACTION_UNDO, 0, 0);
dispatch(undo_action);
usleep(1000);
}
}
// Redo some
for (int i = 0; i < 100; i++) {
Action redo_action = make_action(ACTION_REDO, 0, 0);
dispatch(redo_action);
usleep(1000);
}
usleep(50000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 5: Transport state changes
static void stress_transport(void) {
printf("Stress test 5: Transport state changes...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 5000;
for (int i = 0; i < num_ops; i++) {
Action action;
memset(&action, 0, sizeof(action));
switch (i % 5) {
case 0:
action.type = ACTION_TRANSPORT_PLAY;
break;
case 1:
action.type = ACTION_TRANSPORT_PAUSE;
break;
case 2:
action.type = ACTION_TRANSPORT_STOP;
break;
case 3:
action.type = ACTION_TRANSPORT_TOGGLE_PLAY;
break;
case 4:
action.type = ACTION_SET_CLOCK_SOURCE;
action.data.set_clock_source.source = CLOCK_SOURCE_MIDI;
break;
}
dispatch(action);
usleep(100);
}
usleep(50000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 6: Quantize mode changes
static void stress_quantize(void) {
printf("Stress test 6: Quantize mode changes...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 5000;
for (int i = 0; i < num_ops; i++) {
QuantizeMode mode = (QuantizeMode)(i % 3);
Action mode_action = make_action(ACTION_SET_QUANTIZE_MODE, (int)mode, 0);
dispatch(mode_action);
Action thresh_action = make_action(ACTION_SET_QUANTIZE_THRESHOLD, 0, (jack_nframes_t)(i * 100));
dispatch(thresh_action);
usleep(100);
}
usleep(50000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 7: Reset clips while triggering
static void stress_reset_while_triggering(void) {
printf("Stress test 7: Reset clips while triggering...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_ops = 2000;
for (int i = 0; i < num_ops; i++) {
int clip_idx = i % MAX_CLIPS;
Action trigger_action = make_action(ACTION_TRIGGER_CLIP, clip_idx, 0);
dispatch(trigger_action);
usleep(500);
if (i % 10 == 0) {
Action reset_action = make_action(ACTION_RESET_CLIP, clip_idx, 0);
dispatch(reset_action);
usleep(500);
}
}
usleep(50000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 8: Concurrent dispatch from multiple threads
typedef struct {
DispatchFn dispatch;
int thread_id;
int num_ops;
} ThreadArg;
static void* thread_worker(void *arg) {
ThreadArg *targ = (ThreadArg *)arg;
for (int i = 0; i < targ->num_ops; i++) {
int clip_idx = (targ->thread_id * 1000 + i) % MAX_CLIPS;
Action action = make_action(ACTION_TRIGGER_CLIP, clip_idx, 0);
targ->dispatch(action);
}
return NULL;
}
static void stress_concurrent(void) {
printf("Stress test 8: Concurrent dispatch from multiple threads...\n");
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
int num_threads = 4;
int ops_per_thread = 2500;
pthread_t threads[num_threads];
ThreadArg args[num_threads];
for (int t = 0; t < num_threads; t++) {
args[t].dispatch = dispatch;
args[t].thread_id = t;
args[t].num_ops = ops_per_thread;
pthread_create(&threads[t], NULL, thread_worker, &args[t]);
}
for (int t = 0; t < num_threads; t++) {
pthread_join(threads[t], NULL);
}
usleep(100000);
teardown_dispatcher(dispatch);
printf(" PASSED\n");
}
// Stress test 9: Repeated init/start/stop cycles
static void stress_create_destroy(void) {
printf("Stress test 9: Repeated init/start/stop cycles...\n");
for (int i = 0; i < 100; i++) {
AppState state;
DispatchFn dispatch = setup_dispatcher(&state);
// Do some operations
for (int j = 0; j < 10; j++) {
Action trigger_action = make_action(ACTION_TRIGGER_CLIP, j % MAX_CLIPS, 0);
dispatch(trigger_action);
Action toggle_action;
memset(&toggle_action, 0, sizeof(toggle_action));
toggle_action.type = ACTION_TRANSPORT_TOGGLE_PLAY;
dispatch(toggle_action);
Action mode_action = make_action(ACTION_SET_QUANTIZE_MODE, j % 3, 0);
dispatch(mode_action);
}
usleep(1000);
teardown_dispatcher(dispatch);
}
printf(" PASSED\n");
}
int main(void) {
signal(SIGINT, handle_sigint);
printf("Running JACK Looper stress tests...\n\n");
stress_rapid_triggers();
stress_mixed_triggers();
stress_queue_overflow();
stress_undo_redo();
stress_transport();
stress_quantize();
stress_reset_while_triggering();
stress_concurrent();
stress_create_destroy();
printf("\nAll stress tests passed!\n");
return 0;
}

BIN
test_cli
View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -1,78 +0,0 @@
// TODO: Remove this test after debugging the double status line issue
// test_double_process.c
#include "engine.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
void test_command_processed_only_once(void) {
printf("Test: Command processed only once...\n");
Engine engine;
memset(&engine, 0, sizeof(engine));
engine.sample_rate = 48000;
engine.control_channel = 0;
engine.quantize_mode = QUANTIZE_OFF;
engine.quantize_threshold = 0;
engine.queued_triggers = NULL;
// Initialize command queue
command_queue_init(&engine.command_queue);
// Initialize clips
for (int i = 0; i < MAX_CLIPS; i++) {
engine.clips[i].state = CLIP_EMPTY;
engine.clips[i].buffer = NULL;
engine.clips[i].buffer_size = 0;
engine.clips[i].write_position = 0;
engine.clips[i].read_position = 0;
}
// Submit a trigger command
engine_trigger_clip(&engine, 0);
// Check queue state
CommandQueue *q = &engine.command_queue;
unsigned int write = atomic_load(&q->write_index);
unsigned int read = atomic_load(&q->read_index);
printf(" Queue before process: write=%u, read=%u, pending=%u\n",
write, read, write - read);
assert(write - read == 1); // One command pending
// Process once
engine_process_commands(&engine);
printf(" After first process: state=%d (expected %d=CLIP_RECORDING)\n",
engine.clips[0].state, CLIP_RECORDING);
assert(engine.clips[0].state == CLIP_RECORDING);
// Check queue state after first process
write = atomic_load(&q->write_index);
read = atomic_load(&q->read_index);
printf(" Queue after first process: write=%u, read=%u, pending=%u\n",
write, read, write - read);
assert(write - read == 0); // No commands pending
// Process again (simulating audio thread calling it)
engine_process_commands(&engine);
printf(" After second process: state=%d (expected %d=CLIP_RECORDING)\n",
engine.clips[0].state, CLIP_RECORDING);
// If state changed, the command was processed twice
if (engine.clips[0].state != CLIP_RECORDING) {
printf(" BUG: State changed to %d on second process!\n", engine.clips[0].state);
} else {
printf(" OK: State unchanged after second process\n");
}
// Cleanup
for (int i = 0; i < MAX_CLIPS; i++) {
free(engine.clips[i].buffer);
}
printf("PASSED\n");
}
int main(void) {
test_command_processed_only_once();
return 0;
}

View File

Binary file not shown.

View File

Binary file not shown.

BIN
test_gui
View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

BIN
test_tui
View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

BIN
tui.o
View File

Binary file not shown.

BIN
wav_io.o
View File

Binary file not shown.