#include "test_common.h" static int test_looper_looping(void) { printf("Test: loop recording and playback (expect ≥3 repetitions)\n"); pid_t pid = start_looper(); if (pid < 0) return 1; if (init_persistent_midi_client() != 0) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: cannot initialise persistent MIDI client\n"); return 1; } jack_client_t *client; jack_status_t status; client = jack_client_open("test_looping", JackNoStartServer, &status); if (!client) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " SKIP: JACK not running?\n"); return 1; } jack_port_t *audio_out = jack_port_register(client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); jack_port_t *audio_in = jack_port_register(client, "in", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (!audio_out || !audio_in) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(200000); char my_out[64], my_in[64]; snprintf(my_out, sizeof(my_out), "test_looping:out"); snprintf(my_in, sizeof(my_in), "test_looping:in"); if (jack_connect(client, my_out, "looper:input") || jack_connect(client, "looper:output", my_in)) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } if (send_jack_note_on("looper:control", 1, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(500000); int sr = jack_get_sample_rate(client); continuous_sine = 0; beep_remaining = (int)(0.1f * sr); bursts = 0; prev_above = 0; passthrough_output_port = audio_out; passthrough_input_port = audio_in; passthrough_phase = 0.0f; passthrough_freq = 440.0f; passthrough_sample_rate = sr; passthrough_total_samples = 0; passthrough_sum_sq = 0.0; passthrough_done = 0; jack_set_process_callback(client, passthrough_process, NULL); if (jack_activate(client)) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(150000); safe_usleep(800000); if (send_jack_note_on("looper:control", 1, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(4000000); jack_deactivate(client); jack_client_close(client); cleanup_persistent_midi_client(); kill(pid, SIGTERM); waitpid(pid, NULL, 0); int got_bursts = bursts; printf(" detected bursts: %d\n", got_bursts); if (got_bursts < 3) { fprintf(stderr, " FAIL: expected ≥3 bursts, got %d\n", got_bursts); return 1; } printf(" PASS (at least 3 repetitions)\n"); return 0; } static int test_record_loop_stop(void) { printf("Test: full record‑loop‑stop (≥5 repetitions)\n"); pid_t pid = start_looper(); if (pid < 0) return 1; if (init_persistent_midi_client() != 0) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " FAIL: cannot initialise persistent MIDI client\n"); return 1; } jack_client_t *client; jack_status_t status; client = jack_client_open("test_full", JackNoStartServer, &status); if (!client) { kill(pid, SIGTERM); waitpid(pid, NULL, 0); fprintf(stderr, " SKIP: no JACK\n"); return 1; } jack_port_t *audio_out = jack_port_register(client, "out", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); jack_port_t *audio_in = jack_port_register(client, "in", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); if (!audio_out || !audio_in) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(200000); char my_out[64], my_in[64]; snprintf(my_out, sizeof(my_out), "test_full:out"); snprintf(my_in, sizeof(my_in), "test_full:in"); if (jack_connect(client, my_out, "looper:input") || jack_connect(client, "looper:output", my_in)) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } if (send_jack_note_on("looper:control", 1, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(500000); int sr = jack_get_sample_rate(client); continuous_sine = 0; beep_remaining = (int)(0.5f * sr); bursts = 0; prev_above = 0; passthrough_output_port = audio_out; passthrough_input_port = audio_in; passthrough_phase = 0.0f; passthrough_freq = 440.0f; passthrough_sample_rate = sr; passthrough_total_samples = 0; passthrough_sum_sq = 0.0; passthrough_done = 0; jack_set_process_callback(client, passthrough_process, NULL); if (jack_activate(client)) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(200000); if (send_jack_note_on("looper:control", 1, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(2500000); if (send_jack_note_on("looper:control", 64, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(200000); if (send_jack_note_on("looper:control", 65, 127) != 0) { jack_client_close(client); kill(pid, SIGTERM); waitpid(pid, NULL, 0); return 1; } safe_usleep(200000); int total_bursts = bursts; jack_deactivate(client); jack_client_close(client); cleanup_persistent_midi_client(); kill(pid, SIGTERM); waitpid(pid, NULL, 0); if (total_bursts < 5) { fprintf(stderr, " FAIL: expected ≥5 bursts, got %d\n", total_bursts); return 1; } printf(" PASS (≥5 repetitions, stopped cleanly)\n"); return 0; } int test_loop(void) { int failures = 0; failures += test_looper_looping(); failures += test_record_loop_stop(); return failures; }