fuzzy sample
This commit is contained in:
@@ -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
dispatcher.o
Normal file
BIN
dispatcher.o
Normal file
Binary file not shown.
BIN
jack-looper
BIN
jack-looper
Binary file not shown.
BIN
lib/microui.o
BIN
lib/microui.o
Binary file not shown.
Binary file not shown.
381
stress_test.c
381
stress_test.c
@@ -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.o
BIN
test_cli.o
Binary file not shown.
@@ -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;
|
|
||||||
}
|
|
||||||
BIN
test_engine
BIN
test_engine
Binary file not shown.
BIN
test_engine.o
BIN
test_engine.o
Binary file not shown.
BIN
test_gui.o
BIN
test_gui.o
Binary file not shown.
BIN
test_stress
BIN
test_stress
Binary file not shown.
BIN
test_stress.o
BIN
test_stress.o
Binary file not shown.
BIN
test_tui.o
BIN
test_tui.o
Binary file not shown.
BIN
test_wav_io
BIN
test_wav_io
Binary file not shown.
BIN
test_wav_io.o
BIN
test_wav_io.o
Binary file not shown.
BIN
transport.o
BIN
transport.o
Binary file not shown.
Reference in New Issue
Block a user