#define _POSIX_C_SOURCE 200809L #include "xtc/xtc_plugin.h" #include "xtc/xtc_project.h" #include "xtc/xtc_realtime.h" #include #include #include #include #include #include #include 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 z) { assert(!audio); return __real_calloc(n,z); } 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 flags) { assert(!audio); return __real_dlopen(p,flags); } int __wrap_dlclose(void *p) { assert(!audio); return __real_dlclose(p); } static void graph_roundtrip(const xtc_plugin_spec *spec) { xtc_graph *g = NULL; xtc_plan *p = NULL, *again = NULL; xtc_project *project = NULL, *loaded = NULL; xtc_plugin_spec copy; char path[] = "/tmp/xtc-plugin-project-XXXXXX"; float input[31], out[31], expected[31]; size_t i, plugin, output; int fd = mkstemp(path); assert(fd >= 0); close(fd); assert(xtc_graph_create(8,&g) == XTC_OK); assert(xtc_graph_add_plugin(g,spec,&plugin) == XTC_OK); assert(xtc_graph_add_output(g,&output) == XTC_OK); assert(xtc_graph_connect(g,plugin,output) == XTC_OK); assert(xtc_graph_compile(g,48000,31,&p) == XTC_OK); for (i = 0; i < 31; ++i) input[i] = (float)i / 100; audio = 1; assert(xtc_plan_process(p,input,expected,31) == XTC_OK); audio = 0; { int nonzero = 0; for (i = 0; i < 31; ++i) if (expected[i] != 0) nonzero = 1; assert(nonzero); } assert(xtc_project_create(&project) == XTC_OK); assert(xtc_project_set_graph(project,g) == XTC_OK); assert(xtc_project_save(project,path) == XTC_OK); assert(xtc_project_create(&loaded) == XTC_OK); assert(xtc_project_load(loaded,path) == XTC_OK); assert(xtc_graph_get_plugin(xtc_project_graph(loaded),plugin,©) == XTC_OK); assert(!strcmp(copy.path,spec->path) && !strcmp(copy.label,spec->label)); assert(xtc_graph_compile(xtc_project_graph(loaded),48000,31,&again) == XTC_OK); audio = 1; assert(xtc_plan_process(again,input,out,31) == XTC_OK); audio = 0; for (i = 0; i < 31; ++i) assert(out[i] == expected[i] && isfinite(out[i])); xtc_plan_destroy(again); again = NULL; if (spec->control_count) { char malformed[] = "/tmp/xtc-plugin-malformed-XXXXXX", record[1536]; int descriptor = mkstemp(malformed); FILE *source = fopen(path,"r"), *dest = fdopen(descriptor,"w"); assert(descriptor >= 0 && source && dest); while (fgets(record,sizeof(record),source)) { if (!strncmp(record,"CONTROL ",8)) fputs("CONTROL 64 3f800000\n",dest); else fputs(record,dest); } assert(!ferror(source)); fclose(source); assert(fclose(dest) == 0); assert(xtc_project_load(loaded,malformed) == XTC_ERR_FORMAT); assert(xtc_graph_get_plugin(xtc_project_graph(loaded),plugin,©) == XTC_OK); assert(!strcmp(copy.path,spec->path)); unlink(malformed); } /* Metadata-only loading/saving preserves absent modules. */ strcpy(copy.path,"/nonexistent/xtc-preserved-plugin.so"); xtc_graph_destroy(g); g = NULL; assert(xtc_graph_create(8,&g) == XTC_OK); assert(xtc_graph_add_plugin(g,©,&plugin) == XTC_OK); assert(xtc_project_set_graph(project,g) == XTC_OK); assert(xtc_project_save(project,path) == XTC_OK); xtc_project_destroy(loaded); loaded = NULL; assert(xtc_project_create(&loaded) == XTC_OK); assert(xtc_project_load(loaded,path) == XTC_OK); assert(xtc_graph_compile(xtc_project_graph(loaded),48000,31,&again) == XTC_ERR_NOT_FOUND && !again); assert(xtc_project_save(loaded,path) == XTC_OK); xtc_graph_destroy(g); xtc_project_destroy(project); xtc_project_destroy(loaded); xtc_plan_destroy(p); unlink(path); /* Repeated publication, audio adoption and control retirement. */ { xtc_realtime *rt = NULL; assert(xtc_graph_create(8,&g) == XTC_OK); assert(xtc_graph_add_plugin(g,spec,&plugin) == XTC_OK); assert(xtc_realtime_create(48000,31,&rt) == XTC_OK); for (i = 0; i < 100; ++i) { assert(xtc_realtime_submit(rt,g) == XTC_OK); audio = 1; assert(xtc_realtime_process(rt,input,out,31) == XTC_OK); audio = 0; xtc_realtime_collect(rt); } xtc_realtime_destroy(rt); xtc_graph_destroy(g); } } int main(int argc, char **argv) { xtc_plugin_spec s, bad; xtc_plugin *p = NULL; float data[17]; size_t i; assert(argc == 2 || argc == 3); assert(xtc_plugin_probe(argv[1],argc == 3 && strcmp(argv[2],"fixture") ? (unsigned int)strtoul(argv[2],NULL,10) : 0,48000,&s) == XTC_OK); printf("Testing %s (%u controls, %u inputs)\n",s.label,s.control_count,s.inputs); graph_roundtrip(&s); { xtc_parameter_info info[XTC_PLUGIN_CONTROLS]; assert(xtc_plugin_parameters(&s,48000,info) == XTC_OK); for (i = 0; i < s.control_count; ++i) assert(info[i].id == s.ports[i] && info[i].name[0]); } assert(xtc_plugin_create(&s,48000,17,&p) == XTC_OK); for (i = 0; i < 17; ++i) data[i] = .5f; audio = 1; assert(xtc_plugin_process(p,data,data,17) == XTC_OK); audio = 0; for (i = 0; i < 17; ++i) assert(isfinite(data[i])); xtc_plugin_destroy(p); p = NULL; bad = s; ++bad.unique_id; assert(xtc_plugin_create(&bad,48000,17,&p) == XTC_ERR_UNSUPPORTED && !p); bad = s; if (bad.control_count) { bad.values[0] = NAN; assert(xtc_plugin_create(&bad,48000,17,&p) == XTC_ERR_INVALID_ARGUMENT); } if (argc == 3 && !strcmp(argv[2],"fixture")) { assert(s.control_count == 1 && s.values[0] == 1); s.values[0] = 2; assert(xtc_plugin_create(&s,48000,17,&p) == XTC_OK); for (i = 0; i < 17; ++i) data[i] = .5f; audio = 1; assert(xtc_plugin_process(p,data,data,17) == XTC_OK); audio = 0; for (i = 0; i < 17; ++i) assert(data[i] == 1); xtc_plugin_destroy(p); p = NULL; graph_roundtrip(&s); s.values[0] = 3; assert(xtc_plugin_create(&s,48000,17,&p) == XTC_ERR_UNSUPPORTED && !p); assert(xtc_plugin_probe(argv[1],1,48000,&s) == XTC_OK && !s.inputs); graph_roundtrip(&s); assert(xtc_plugin_probe(argv[1],2,48000,&s) == XTC_ERR_UNSUPPORTED); assert(xtc_plugin_probe(argv[1],3,48000,&s) == XTC_OK); assert(xtc_plugin_create(&s,48000,17,&p) == XTC_ERR_INVALID_STATE && !p); assert(xtc_plugin_probe(argv[1],4,48000,&s) == XTC_ERR_NOT_FOUND); } puts("Plugin lifecycle, identity, layouts, processing and persisted graph checks passed."); return 0; }