diff --git a/tests/integration.c b/tests/integration.c index 1091027..154d26e 100644 --- a/tests/integration.c +++ b/tests/integration.c @@ -909,6 +909,207 @@ static int test_fifo_pipe(void) { return 0; } +/* test stop via MIDI (control key + note 65) */ +static int test_stop_midi(void) { + printf("Test: MIDI stop (note 65 under control key)\n"); + pid_t pid = start_looper(); + if (pid < 0) return 1; + jack_client_t *client; + jack_status_t status; + client = jack_client_open("test_stop", 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_stop:out"); + snprintf(my_in, sizeof(my_in), "test_stop: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; + } + /* start recording: send note 1 */ + if (send_jack_note_on("looper:control", 1, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note1 failed\n"); + return 1; + } + safe_usleep(200000); + int sr = jack_get_sample_rate(client); + continuous_sine = 0; + beep_remaining = (int)(0.2f * sr); /* 0.2s beep while recording */ + 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); + /* loop: send note 1 again */ + if (send_jack_note_on("looper:control", 1, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: loop note1\n"); + return 1; + } + safe_usleep(500000); + /* stop: control key then note 65 */ + if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: control key\n"); + 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); + fprintf(stderr, " FAIL: stop note 65\n"); + return 1; + } + safe_usleep(200000); + int bursts_before = bursts; + safe_usleep(500000); + int bursts_after = bursts; + jack_deactivate(client); + jack_client_close(client); + kill(pid, SIGTERM); + waitpid(pid, NULL, 0); + if (bursts_after > bursts_before) { + fprintf(stderr, " FAIL: bursts continued after stop (%d -> %d)\n", + bursts_before, bursts_after); + return 1; + } + printf(" PASS (stop stopped playback)\n"); + return 0; +} + +/* full flow: record 1s, loop 5 times, stop, verify at least 5 bursts */ +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; + 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; + } + /* start recording */ + if (send_jack_note_on("looper:control", 1, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: send note1\n"); + return 1; + } + safe_usleep(500000); + /* generate a 0.5 s beep while recording */ + 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); + /* end recording -> loop */ + if (send_jack_note_on("looper:control", 1, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: loop note1\n"); + return 1; + } + /* wait for about 5 loops (assuming 0.5s recorded -> ~2.5s loop) */ + safe_usleep(2500000); + /* stop via control+65 */ + if (send_jack_note_on("looper:control", 64, 127) != 0) { + jack_client_close(client); + kill(pid, SIGTERM); waitpid(pid, NULL, 0); + fprintf(stderr, " FAIL: control key\n"); + 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); + fprintf(stderr, " FAIL: stop note 65\n"); + return 1; + } + safe_usleep(200000); + int total_bursts = bursts; + jack_deactivate(client); + jack_client_close(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 main(void) { /* 1. binary must exist */ if (system("test -x ./looper") != 0) { @@ -965,6 +1166,18 @@ int main(void) { failures++; } + /* 11. Test MIDI stop */ + if (test_stop_midi() != 0) { + fprintf(stderr, " FAILED\n"); + failures++; + } + + /* 12. Test full record‑loop‑stop flow */ + if (test_record_loop_stop() != 0) { + fprintf(stderr, " FAILED\n"); + failures++; + } + if (failures > 0) { fprintf(stderr, "%d test(s) FAILED\n", failures); return 1;