Phase 7: Add MIDI clips, instrument and piano roll

This commit is contained in:
Johannes Findeisen 2026-10-27 00:53:41 +01:00
commit cacc086805
38 changed files with 1373 additions and 74 deletions

View file

@ -4,6 +4,7 @@
* separately by test_realtime. No fake header replaces installed declarations. */
#include "xtc/xtc_backend.h"
#include <jack/jack.h>
#include <jack/midiport.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -11,13 +12,16 @@
#include <unistd.h>
#define CHECK(c) do { if (!(c)) { fprintf(stderr,"FAIL %s:%d: %s\n",__FILE__,__LINE__,#c); return 1; } } while (0)
static JackProcessCallback process_cb;
static JackPortConnectCallback connection_cb;
static JackSampleRateCallback rate_cb;
static JackBufferSizeCallback size_cb;
static JackXRunCallback xrun_cb;
static JackShutdownCallback shutdown_cb;
static void *context;
static float input[128], output[128];
static int client_token, input_token, output_token;
static int client_token, input_token, output_token, midi_token;
static unsigned int midi_count;
static jack_midi_event_t midi_events[256];
static int open_fail, register_fail, callback_fail, activate_fail, deactivate_fail, close_fail;
static int active, closed, port_calls, null_buffer;
static unsigned int server_frames = 32U;
@ -40,8 +44,13 @@ jack_port_t *jack_port_register(jack_client_t *client, const char *name, const c
(void)client; (void)name; (void)type; (void)buffer_size;
++port_calls;
if (register_fail == port_calls) return NULL;
if (!strcmp(type, JACK_DEFAULT_MIDI_TYPE)) return (jack_port_t *)&midi_token;
return (jack_port_t *)(flags == JackPortIsInput ? &input_token : &output_token);
}
int jack_set_port_connect_callback(jack_client_t *client, JackPortConnectCallback callback, void *arg)
{ (void)client; (void)arg; connection_cb = callback; return 0; }
jack_port_t *jack_port_by_id(jack_client_t *client, jack_port_id_t id)
{ (void)client; return id == 3 ? (jack_port_t *)&midi_token : NULL; }
int jack_set_process_callback(jack_client_t *client, JackProcessCallback callback, void *arg)
{ (void)client; process_cb = callback; context = arg; return callback_fail ? -1 : 0; }
int jack_set_sample_rate_callback(jack_client_t *client, JackSampleRateCallback callback, void *arg)
@ -57,9 +66,12 @@ int jack_activate(jack_client_t *client)
int jack_deactivate(jack_client_t *client)
{ (void)client; if (deactivate_fail) return -1; active = 0; return 0; }
void *jack_port_get_buffer(jack_port_t *port, jack_nframes_t frames)
{ (void)frames; if (null_buffer) return NULL; return port == (jack_port_t *)&input_token ? input : output; }
{ (void)frames; if (null_buffer) return NULL; if (port == (jack_port_t *)&midi_token) return &midi_token; return port == (jack_port_t *)&input_token ? input : output; }
uint32_t jack_midi_get_event_count(void *buffer) { (void)buffer; return midi_count; }
int jack_midi_event_get(jack_midi_event_t *event, void *buffer, uint32_t index)
{ (void)buffer; if (index >= midi_count || index >= 256) return -1; *event = midi_events[index]; return 0; }
const char *jack_port_name(const jack_port_t *port)
{ return port == (jack_port_t *)&input_token ? "test:input" : "test:output"; }
{ if (port == (jack_port_t *)&midi_token) return "test:midi_in"; return port == (jack_port_t *)&input_token ? "test:input" : "test:output"; }
const char **jack_get_ports(jack_client_t *client, const char *pattern, const char *type, unsigned long flags)
{
const char **list = malloc(2U * sizeof(*list));
@ -99,6 +111,8 @@ int main(void)
server_frames = 32U;
register_fail = 2;
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
register_fail = 3;
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
register_fail = 0;
callback_fail = 1;
CHECK(xtc_backend_open(&config, &backend) == XTC_ERR_IO && backend == NULL);
@ -236,6 +250,53 @@ int main(void)
xtc_project_destroy(p); xtc_project_destroy(q);
CHECK(unlink(song) == 0 && rmdir(directory) == 0);
}
{
xtc_project *p = NULL;
uint64_t track, clip;
unsigned char on[3] = {0x90, 69, 127}, off[3] = {0x90, 69, 0}, bad[3] = {0x90, 255, 127};
CHECK(xtc_project_create(&p) == XTC_OK && xtc_project_add_track(p, "MIDI", &track) == XTC_OK);
CHECK(xtc_project_add_midi_clip(p, track, 0, 3840, &clip) == XTC_OK);
CHECK(xtc_backend_open(&config, &backend) == XTC_OK && xtc_backend_prepare_project(backend, p) == XTC_OK);
CHECK(xtc_backend_midi_port_name(backend, &name) == XTC_OK && strcmp(name, "test:midi_in") == 0);
CHECK(xtc_backend_start(backend) == XTC_OK);
midi_count = 1; midi_events[0].time = 4; midi_events[0].size = 3; midi_events[0].buffer = on;
CHECK(process_cb(32, context) == 0 && output[0] == 0 && output[4] == 0 && output[5] > 0);
midi_events[0].time = 8; midi_events[0].buffer = off;
CHECK(process_cb(32, context) == 0 && output[0] != 0 && output[8] == 0 && output[31] == 0);
midi_events[0].time = 0; midi_events[0].buffer = on;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 0;
CHECK(xtc_backend_panic(backend) == XTC_OK);
CHECK(process_cb(32, context) == 0 && output[0] == 0 && output[31] == 0);
midi_count = 1;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 257;
CHECK(process_cb(32, context) == 0 && output[1] == 0);
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.midi_dropped == 257);
midi_count = 1; midi_events[0].buffer = bad;
CHECK(process_cb(32, context) == 0 && output[1] == 0);
CHECK(xtc_backend_get_status(backend, &status) == XTC_OK && status.midi_dropped == 258);
midi_events[0].buffer = on;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 0;
connection_cb(999, 3, 0, context);
CHECK(process_cb(32, context) == 0 && output[1] == 0);
midi_count = 1;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 0; CHECK(xrun_cb(context) == 0);
CHECK(process_cb(32, context) == 0 && output[1] == 0);
midi_count = 1;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 0; null_buffer = 1; CHECK(process_cb(32, context) == 0); null_buffer = 0;
CHECK(process_cb(32, context) == 0 && output[1] == 0);
midi_count = 1;
CHECK(process_cb(32, context) == 0 && output[1] > 0);
midi_count = 0;
CHECK(xtc_backend_stop(backend) == XTC_OK && xtc_backend_seek(backend, 0) == XTC_OK && xtc_backend_start(backend) == XTC_OK);
CHECK(process_cb(32, context) == 0 && output[1] == 0);
CHECK(xtc_backend_close(backend) == XTC_OK);
xtc_project_destroy(p);
}
CHECK(closed >= 104);
CHECK(xtc_backend_close(NULL) == XTC_OK);
xtc_graph_destroy(invalid_graph);