xtc/tests/integration/test_instruments.c

121 lines
6.7 KiB
C
Raw Permalink Normal View History

#define _POSIX_C_SOURCE 200809L
#include "xtc/xtc_plugin.h"
#include "xtc/xtc_mixer.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>
static int audio;
void *__real_malloc(size_t);
void *__real_calloc(size_t,size_t);
void *__real_realloc(void *,size_t);
void __real_free(void *);
void *__real_dlopen(const char *,int);
int __real_dlclose(void *);
void *__wrap_malloc(size_t n) { assert(!audio); return __real_malloc(n); }
void *__wrap_calloc(size_t n,size_t s) { assert(!audio); return __real_calloc(n,s); }
void *__wrap_realloc(void *p,size_t n) { assert(!audio); return __real_realloc(p,n); }
void __wrap_free(void *p) { assert(!audio); __real_free(p); }
void *__wrap_dlopen(const char *p,int mode) { assert(!audio); return __real_dlopen(p,mode); }
int __wrap_dlclose(void *p) { assert(!audio); return __real_dlclose(p); }
static xtc_result render(xtc_midi_plan *plan, uint64_t position, const xtc_midi_event *events, size_t count, float *out, size_t frames)
{
xtc_result r; audio = 1;
r = xtc_midi_plan_render(plan,position,events,count,out,frames);
audio = 0; return r;
}
static void check(const char *path)
{
xtc_project *p = NULL, *q = NULL;
xtc_midi_plan *plan = NULL;
xtc_plugin_spec spec, actual;
xtc_plugin *plugin = NULL;
uint64_t track, clip, note, start, end, live;
float out[600]; size_t i;
char file[256];
xtc_midi_event events[] = {{3,XTC_MIDI_NOTE_ON,0,60,127},{19,XTC_MIDI_NOTE_OFF,0,60,0}};
assert(xtc_plugin_probe(path,0,48000,&spec) == XTC_OK && spec.instrument && !spec.inputs);
assert(xtc_plugin_create(&spec,48000,256,&plugin) == XTC_OK);
assert(xtc_plugin_process_midi(plugin,events,2,out,31) == XTC_OK);
for (i = 0; i < 31; ++i) assert(out[i] == (i >= 3 && i < 19 ? 1.0f : 0));
xtc_plugin_destroy(plugin);
assert(xtc_project_create(&p) == XTC_OK && xtc_project_create(&q) == XTC_OK);
assert(xtc_project_add_track(p,"Synth",&track) == XTC_OK);
assert(xtc_project_add_midi_clip(p,track,0,960,&clip) == XTC_OK);
assert(xtc_project_add_note(p,clip,2,10,60,127,0,&note) == XTC_OK);
assert(xtc_project_tick_to_sample(p,2,48000,&start) == XTC_OK);
assert(xtc_project_tick_to_sample(p,12,48000,&end) == XTC_OK);
assert(xtc_project_set_instrument(p,track,&spec) == XTC_OK);
assert(xtc_project_get_live_instrument(p,&live) == XTC_OK && live == track);
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_OK);
assert(render(plan,0,NULL,0,out,600) == XTC_OK);
for (i = 0; i < 600; ++i) assert(fabsf(out[i]-(i >= start && i < end ? 1.0f : 0)) < .000001f);
/* Seek chases notes, panic suppresses them until the next discontinuity. */
assert(render(plan,start+1,NULL,0,out,1) == XTC_OK && out[0] == 1);
xtc_midi_plan_panic(plan,start+2);
assert(render(plan,start+2,NULL,0,out,1) == XTC_OK && out[0] == 0);
assert(render(plan,1000,events,2,out,31) == XTC_OK);
for (i = 0; i < 31; ++i) assert(out[i] == (i >= 3 && i < 19 ? 1.0f : 0));
xtc_midi_plan_destroy(plan);
snprintf(file,sizeof(file),"/tmp/xtc-instrument-%ld.xtc",(long)getpid());
assert(xtc_project_save(p,file) == XTC_OK);
assert(xtc_project_load(q,file) == XTC_OK);
assert(xtc_project_get_instrument(q,track,&actual) == XTC_OK && actual.instrument && !strcmp(actual.path,path));
assert(xtc_project_get_live_instrument(q,&live) == XTC_OK && live == track);
assert(xtc_midi_plan_create(q,48000,&plan) == XTC_OK);
xtc_midi_plan_destroy(plan);
assert(xtc_project_set_instrument(q,track,NULL) == XTC_OK);
assert(xtc_project_get_instrument(q,track,&actual) == XTC_ERR_NOT_FOUND);
assert(xtc_project_get_live_instrument(q,&live) == XTC_OK && live == UINT64_MAX);
/* Independent instances sum through track gains, and stable IDs survive reorder. */
{
uint64_t second, clip2, note2, bus;
assert(xtc_project_add_track(p,"Second",&second) == XTC_OK);
assert(xtc_project_add_midi_clip(p,second,0,960,&clip2) == XTC_OK);
assert(xtc_project_add_note(p,clip2,2,10,62,127,1,&note2) == XTC_OK);
assert(xtc_project_set_instrument(p,second,&spec) == XTC_OK);
assert(xtc_project_set_mix(p,second,.25f,0,0,0) == XTC_OK);
assert(xtc_project_move_track(p,second,0) == XTC_OK);
assert(xtc_project_get_instrument(p,track,&actual) == XTC_OK);
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_OK);
assert(render(plan,0,NULL,0,out,600) == XTC_OK);
assert(fabsf(out[start]-1.25f) < .000001f);
xtc_midi_plan_destroy(plan);
assert(xtc_project_add_bus(p,"Synth bus",&bus) == XTC_OK);
assert(xtc_project_set_instrument(p,bus,&spec) == XTC_ERR_INVALID_STATE);
assert(xtc_project_route_track(p,second,bus) == XTC_OK);
assert(xtc_project_set_mix(p,bus,.5f,0,0,0) == XTC_OK);
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_OK);
assert(render(plan,0,NULL,0,out,600) == XTC_OK && fabsf(out[start]-1.125f) < .000001f);
xtc_midi_plan_destroy(plan);
assert(xtc_project_set_mix(p,second,.25f,0,1,0) == XTC_OK);
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_OK);
assert(render(plan,0,NULL,0,out,600) == XTC_OK && out[start] == 1);
xtc_midi_plan_destroy(plan);
assert(xtc_project_remove_track(p,second) == XTC_OK);
assert(xtc_project_get_live_instrument(p,&live) == XTC_OK && live == UINT64_MAX);
}
/* Exhausting a chunk's event budget fails silent and recovers on next render. */
{
xtc_midi_event many[256];
assert(xtc_project_set_live_instrument(p,track) == XTC_OK);
for (i = 0; i < 256; ++i) many[i] = (xtc_midi_event){0,XTC_MIDI_NOTE_ON,0,60,127};
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_OK);
assert(render(plan,1000,many,256,out,31) == XTC_ERR_CAPACITY);
for (i = 0; i < 31; ++i) assert(out[i] == 0);
assert(render(plan,1031,events,2,out,31) == XTC_OK);
xtc_midi_plan_destroy(plan);
}
/* Missing modules remain model data; playback preparation fails cleanly. */
actual = spec; strcpy(actual.path,"/absent/xtc-instrument.so");
assert(xtc_project_set_instrument(q,track,&actual) == XTC_OK);
assert(xtc_project_save(q,file) == XTC_OK && xtc_project_load(p,file) == XTC_OK);
plan = NULL;
assert(xtc_midi_plan_create(p,48000,&plan) == XTC_ERR_NOT_FOUND && !plan);
xtc_project_destroy(q); xtc_project_destroy(p); unlink(file);
printf("%s instrument offsets, scheduling, live routing, seek/panic and v12 reload passed.\n",xtc_plugin_format_name(spec.format));
}
int main(int argc, char **argv)
{ int i; assert(argc == 4); for (i = 1; i < argc; ++i) check(argv[i]); assert(setenv("XTC_TEST_NATIVE_NOTES","1",1) == 0); check(argv[1]); return 0; }