90 lines
4.4 KiB
C
90 lines
4.4 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include "xtc/xtc_backend.h"
|
|
#include <jack/jack.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
typedef struct { jack_port_t *input, *output; unsigned int heard, bad; } probe;
|
|
static int process(jack_nframes_t frames, void *context)
|
|
{
|
|
probe *p = context;
|
|
const float *input = jack_port_get_buffer(p->input, frames);
|
|
float *output = jack_port_get_buffer(p->output, frames);
|
|
jack_nframes_t i;
|
|
if (!input || !output) { __atomic_store_n(&p->bad, 1U, __ATOMIC_RELAXED); return 0; }
|
|
for (i = 0; i < frames; ++i) {
|
|
output[i] = 0.25f;
|
|
if (!isfinite(input[i]) || fabsf(input[i]) > 0.251f)
|
|
__atomic_store_n(&p->bad, 1U, __ATOMIC_RELAXED);
|
|
if (fabsf(input[i] - 0.25f) < 0.0001f)
|
|
__atomic_store_n(&p->heard, 1U, __ATOMIC_RELAXED);
|
|
}
|
|
return 0;
|
|
}
|
|
#define REQUIRE(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); goto cleanup; } } while (0)
|
|
int main(void)
|
|
{
|
|
char directory[] = "/tmp/xtc-live-audio-XXXXXX", take[256] = "", song[256] = "", name[64];
|
|
xtc_backend_config config = {XTC_BACKEND_JACK, name, 65536, 1};
|
|
xtc_backend *backend = NULL;
|
|
xtc_backend_status status;
|
|
xtc_recorder *recorder = NULL;
|
|
xtc_project *p = NULL, *q = NULL;
|
|
jack_client_t *client = NULL;
|
|
jack_status_t flags = 0;
|
|
probe state = {0};
|
|
struct timespec pause = {0, 50000000L};
|
|
uint64_t track, clip, frames;
|
|
int i, result = EXIT_FAILURE;
|
|
REQUIRE(mkdtemp(directory));
|
|
snprintf(take, sizeof(take), "%s/take.wav", directory);
|
|
snprintf(song, sizeof(song), "%s/song.xtc", directory);
|
|
snprintf(name, sizeof(name), "xtc-audio-%ld", (long)getpid());
|
|
REQUIRE(xtc_backend_open(&config, &backend) == XTC_OK);
|
|
REQUIRE(xtc_backend_get_status(backend, &status) == XTC_OK);
|
|
REQUIRE(xtc_project_create(&p) == XTC_OK && xtc_project_create(&q) == XTC_OK);
|
|
REQUIRE(xtc_project_add_track(p, "Recording", &track) == XTC_OK);
|
|
REQUIRE(xtc_backend_prepare_project(backend, p) == XTC_OK);
|
|
client = jack_client_open("xtc-audio-probe", JackNoStartServer, &flags);
|
|
REQUIRE(client);
|
|
state.input = jack_port_register(client, "input", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
|
state.output = jack_port_register(client, "output", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
|
|
REQUIRE(state.input && state.output);
|
|
REQUIRE(jack_set_process_callback(client, process, &state) == 0);
|
|
REQUIRE(jack_activate(client) == 0);
|
|
REQUIRE(xtc_recorder_create(take, status.sample_rate, (size_t)status.sample_rate * 2, &recorder) == XTC_OK);
|
|
REQUIRE(xtc_backend_set_recorder(backend, recorder) == XTC_OK);
|
|
REQUIRE(xtc_backend_start(backend) == XTC_OK);
|
|
REQUIRE(xtc_backend_connect(backend, XTC_PORT_INPUT, jack_port_name(state.output)) == XTC_OK);
|
|
for (i = 0; i < 20; ++i) {
|
|
nanosleep(&pause, NULL);
|
|
REQUIRE(xtc_recorder_drain(recorder) == XTC_OK);
|
|
REQUIRE(xtc_backend_get_status(backend, &status) == XTC_OK && status.process_result == XTC_OK);
|
|
}
|
|
REQUIRE(xtc_backend_stop(backend) == XTC_OK);
|
|
REQUIRE(xtc_backend_set_recorder(backend, NULL) == XTC_OK);
|
|
REQUIRE(xtc_recorder_finish(recorder) == XTC_OK);
|
|
REQUIRE(xtc_recorder_status(recorder, &frames) == XTC_OK && frames > status.sample_rate / 2);
|
|
xtc_recorder_destroy(recorder); recorder = NULL;
|
|
REQUIRE(xtc_project_import_audio(p, track, take, 0, &clip) == XTC_OK);
|
|
REQUIRE(xtc_project_save(p, song) == XTC_OK && xtc_project_load(q, song) == XTC_OK);
|
|
REQUIRE(xtc_backend_prepare_project(backend, q) == XTC_OK);
|
|
REQUIRE(xtc_backend_seek(backend, 0) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
|
|
REQUIRE(xtc_backend_connect(backend, XTC_PORT_OUTPUT, jack_port_name(state.input)) == XTC_OK);
|
|
for (i = 0; i < 10; ++i) nanosleep(&pause, NULL);
|
|
REQUIRE(__atomic_load_n(&state.heard, __ATOMIC_RELAXED) && !__atomic_load_n(&state.bad, __ATOMIC_RELAXED));
|
|
puts("Live JACK software source -> recording -> save/reopen -> graph playback passed.");
|
|
result = EXIT_SUCCESS;
|
|
cleanup:
|
|
/* Close synchronizes producers before recorder/model cleanup. */
|
|
if (backend && xtc_backend_close(backend) != XTC_OK) return EXIT_FAILURE;
|
|
if (client && jack_client_close(client) != 0) return EXIT_FAILURE;
|
|
xtc_recorder_destroy(recorder);
|
|
xtc_project_destroy(p); xtc_project_destroy(q);
|
|
if (take[0]) unlink(take);
|
|
if (song[0]) unlink(song);
|
|
rmdir(directory);
|
|
return result;
|
|
}
|