tests/integration.c
```c
<<<<<<< SEARCH
static volatile int passthrough_done = 0;
static volatile int beep_remaining = 0;
static volatile int bursts = 0;
static volatile int prev_above = 0;
=======
static volatile int passthrough_done = 0;
static volatile int beep_remaining = 0;
static volatile int bursts = 0;
static volatile int prev_above = 0;
/* variables for MIDI injection (used by send_jack_note_on) */
static volatile int midi_inject_pending = 0;
static jack_port_t *midi_inject_port = NULL;
static jack_client_t *midi_inject_client = NULL;
static unsigned char midi_inject_note = 0;
static unsigned char midi_inject_velocity = 0;
static int midi_inject_process(jack_nframes_t nframes, void *arg) {
(void)arg;
void *port_buf = jack_port_get_buffer(midi_inject_port, nframes);
if (!port_buf) return 0;
jack_midi_clear_buffer(port_buf);
if (!midi_inject_pending) return 0;
jack_midi_data_t *buf = jack_midi_event_reserve(port_buf, 0, 3);
if (!buf) return 0;
buf[0] = 0x90;
buf[1] = midi_inject_note;
buf[2] = midi_inject_velocity;
midi_inject_pending = 0;
return 0;
}
>>>>>>> REPLACE
```
tests/integration.c
```c
<<<<<<< SEARCH
/* Helper: open a transient JACK client, send a MIDI note‑on, close */
static int send_jack_note_on(const char *target_port, unsigned char note, unsigned char velocity) {
/* The correct JACK API requires writing events inside a process callback.
For now we stub this function; the test will skip the MIDI transition
tests and the pass‑through test suffices. */
(void)target_port;
(void)note;
(void)velocity;
return 0;
}
=======
/* Helper: open a transient JACK client, send a MIDI note‑on, close */
static int send_jack_note_on(const char *target_port, unsigned char note, unsigned char velocity) {
midi_inject_note = note;
midi_inject_velocity = velocity;
jack_status_t st;
midi_inject_client = jack_client_open("test_midi_inject", JackNoStartServer, &st);
if (!midi_inject_client) return -1;
midi_inject_port = jack_port_register(midi_inject_client, "out",
JACK_DEFAULT_MIDI_TYPE,
JackPortIsOutput, 0);
if (!midi_inject_port) {
jack_client_close(midi_inject_client);
midi_inject_client = NULL;
return -1;
}
char src[64];
snprintf(src, sizeof(src), "test_midi_inject:out");
if (jack_connect(midi_inject_client, src, target_port) != 0) {
jack_client_close(midi_inject_client);
midi_inject_client = NULL;
midi_inject_port = NULL;
return -1;
}
jack_set_process_callback(midi_inject_client, midi_inject_process, NULL);
if (jack_activate(midi_inject_client) != 0) {
jack_client_close(midi_inject_client);
midi_inject_client = NULL;
midi_inject_port = NULL;
return -1;
}
midi_inject_pending = 1;
/* wait for one process cycle to deliver the event */
usleep(200000);
jack_deactivate(midi_inject_client);
jack_client_close(midi_inject_client);
midi_inject_client = NULL;
midi_inject_port = NULL;
return 0;
}
>>>>>>> REPLACE
```
tests/integration.c
```c
<<<<<<< SEARCH
/*
* Full loop recording test (stub – the MIDI API is non‑trivial without external tools,
* so we skip the actual instrumentation and just verify the looper doesn't crash).
*/
static int test_looper_looping(void) {
printf("Test: loop recording and playback (skip – no external MIDI tool)\n");
printf(" SUCCESS: nothing was measured (stub)\n");
return 0;
}
=======
/*
* Full loop recording test:
* 1. start looper
* 2. open JACK test client (audio)
* 3. send note‑on to move IDLE->RECORD
* 4. generate a short 440 Hz beep (~0.1 s) while recording
* 5. send note‑on to move RECORD->LOOPING
* 6. monitor looper output for the beep being repeated (≥3 times)
*/
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;
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;
}
usleep(200000); /* wait for ports to appear */
/* connect test:out -> looper:input, looper:output -> test:in */
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;
}
/* first note‑on: IDLE -> RECORD */
if (send_jack_note_on("looper:control", 1, 127) != 0) {
jack_client_close(client);
kill(pid, SIGTERM); waitpid(pid, NULL, 0);
return 1;
}
usleep(200000); /* allow state to change */
int sr = jack_get_sample_rate(client);
beep_remaining = (int)(0.1f * sr); /* 0.1 second beep */
bursts = 0;
prev_above = 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;
}
usleep(150000); /* let beep start */
/* after beep finishes, give it a moment then send note‑on to stop recording */
usleep(500000);
beep_remaining = 0;
if (send_jack_note_on("looper:control", 1, 127) != 0) {
jack_client_close(client);
kill(pid, SIGTERM); waitpid(pid, NULL, 0);
return 1;
}
/* wait enough time for several loops (3 seconds) */
usleep(3000000);
jack_deactivate(client);
jack_client_close(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;
}
>>>>>>> REPLACE
```
Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
@@ -24,6 +24,28 @@ static volatile int beep_remaining = 0;
|
|||||||
static volatile int bursts = 0;
|
static volatile int bursts = 0;
|
||||||
static volatile int prev_above = 0;
|
static volatile int prev_above = 0;
|
||||||
|
|
||||||
|
/* variables for MIDI injection (used by send_jack_note_on) */
|
||||||
|
static volatile int midi_inject_pending = 0;
|
||||||
|
static jack_port_t *midi_inject_port = NULL;
|
||||||
|
static jack_client_t *midi_inject_client = NULL;
|
||||||
|
static unsigned char midi_inject_note = 0;
|
||||||
|
static unsigned char midi_inject_velocity = 0;
|
||||||
|
|
||||||
|
static int midi_inject_process(jack_nframes_t nframes, void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
void *port_buf = jack_port_get_buffer(midi_inject_port, nframes);
|
||||||
|
if (!port_buf) return 0;
|
||||||
|
jack_midi_clear_buffer(port_buf);
|
||||||
|
if (!midi_inject_pending) return 0;
|
||||||
|
jack_midi_data_t *buf = jack_midi_event_reserve(port_buf, 0, 3);
|
||||||
|
if (!buf) return 0;
|
||||||
|
buf[0] = 0x90;
|
||||||
|
buf[1] = midi_inject_note;
|
||||||
|
buf[2] = midi_inject_velocity;
|
||||||
|
midi_inject_pending = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* The test code uses this callback in two ways:
|
/* The test code uses this callback in two ways:
|
||||||
- For the audio passthrough test (existing function) it still works.
|
- For the audio passthrough test (existing function) it still works.
|
||||||
- For the loop test we need a version that respects the static variables
|
- For the loop test we need a version that respects the static variables
|
||||||
@@ -192,22 +214,140 @@ static int test_audio_pass_through(void) {
|
|||||||
|
|
||||||
/* Helper: open a transient JACK client, send a MIDI note‑on, close */
|
/* Helper: open a transient JACK client, send a MIDI note‑on, close */
|
||||||
static int send_jack_note_on(const char *target_port, unsigned char note, unsigned char velocity) {
|
static int send_jack_note_on(const char *target_port, unsigned char note, unsigned char velocity) {
|
||||||
/* The correct JACK API requires writing events inside a process callback.
|
midi_inject_note = note;
|
||||||
For now we stub this function; the test will skip the MIDI transition
|
midi_inject_velocity = velocity;
|
||||||
tests and the pass‑through test suffices. */
|
|
||||||
(void)target_port;
|
jack_status_t st;
|
||||||
(void)note;
|
midi_inject_client = jack_client_open("test_midi_inject", JackNoStartServer, &st);
|
||||||
(void)velocity;
|
if (!midi_inject_client) return -1;
|
||||||
|
|
||||||
|
midi_inject_port = jack_port_register(midi_inject_client, "out",
|
||||||
|
JACK_DEFAULT_MIDI_TYPE,
|
||||||
|
JackPortIsOutput, 0);
|
||||||
|
if (!midi_inject_port) {
|
||||||
|
jack_client_close(midi_inject_client);
|
||||||
|
midi_inject_client = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
char src[64];
|
||||||
|
snprintf(src, sizeof(src), "test_midi_inject:out");
|
||||||
|
if (jack_connect(midi_inject_client, src, target_port) != 0) {
|
||||||
|
jack_client_close(midi_inject_client);
|
||||||
|
midi_inject_client = NULL;
|
||||||
|
midi_inject_port = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
jack_set_process_callback(midi_inject_client, midi_inject_process, NULL);
|
||||||
|
if (jack_activate(midi_inject_client) != 0) {
|
||||||
|
jack_client_close(midi_inject_client);
|
||||||
|
midi_inject_client = NULL;
|
||||||
|
midi_inject_port = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
midi_inject_pending = 1;
|
||||||
|
/* wait for one process cycle to deliver the event */
|
||||||
|
usleep(200000);
|
||||||
|
jack_deactivate(midi_inject_client);
|
||||||
|
jack_client_close(midi_inject_client);
|
||||||
|
midi_inject_client = NULL;
|
||||||
|
midi_inject_port = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Full loop recording test (stub – the MIDI API is non‑trivial without external tools,
|
* Full loop recording test:
|
||||||
* so we skip the actual instrumentation and just verify the looper doesn't crash).
|
* 1. start looper
|
||||||
|
* 2. open JACK test client (audio)
|
||||||
|
* 3. send note‑on to move IDLE->RECORD
|
||||||
|
* 4. generate a short 440 Hz beep (~0.1 s) while recording
|
||||||
|
* 5. send note‑on to move RECORD->LOOPING
|
||||||
|
* 6. monitor looper output for the beep being repeated (≥3 times)
|
||||||
*/
|
*/
|
||||||
static int test_looper_looping(void) {
|
static int test_looper_looping(void) {
|
||||||
printf("Test: loop recording and playback (skip – no external MIDI tool)\n");
|
printf("Test: loop recording and playback (expect ≥3 repetitions)\n");
|
||||||
printf(" SUCCESS: nothing was measured (stub)\n");
|
|
||||||
|
pid_t pid = start_looper();
|
||||||
|
if (pid < 0) 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;
|
||||||
|
}
|
||||||
|
usleep(200000); /* wait for ports to appear */
|
||||||
|
/* connect test:out -> looper:input, looper:output -> test:in */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* first note‑on: IDLE -> RECORD */
|
||||||
|
if (send_jack_note_on("looper:control", 1, 127) != 0) {
|
||||||
|
jack_client_close(client);
|
||||||
|
kill(pid, SIGTERM); waitpid(pid, NULL, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
usleep(200000); /* allow state to change */
|
||||||
|
|
||||||
|
int sr = jack_get_sample_rate(client);
|
||||||
|
beep_remaining = (int)(0.1f * sr); /* 0.1 second beep */
|
||||||
|
bursts = 0;
|
||||||
|
prev_above = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(150000); /* let beep start */
|
||||||
|
|
||||||
|
/* after beep finishes, give it a moment then send note‑on to stop recording */
|
||||||
|
usleep(500000);
|
||||||
|
beep_remaining = 0;
|
||||||
|
|
||||||
|
if (send_jack_note_on("looper:control", 1, 127) != 0) {
|
||||||
|
jack_client_close(client);
|
||||||
|
kill(pid, SIGTERM); waitpid(pid, NULL, 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wait enough time for several loops (3 seconds) */
|
||||||
|
usleep(3000000);
|
||||||
|
|
||||||
|
jack_deactivate(client);
|
||||||
|
jack_client_close(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user