Phase 31: add CLAP, VST2 and VST3 mono effect hosting
This commit is contained in:
parent
90eac33a71
commit
9e0e4562d7
114 changed files with 10369 additions and 75 deletions
|
|
@ -711,7 +711,7 @@ int main(int argc, char **argv)
|
|||
// Exercise the actual modal chooser/editor with copied metadata only.
|
||||
Fl::add_timeout(.05,[](void *) {
|
||||
auto *dialog = Fl::first_window();
|
||||
CHECK(dialog && dialog->label() && std::strcmp(dialog->label(),"LADSPA descriptors") == 0);
|
||||
CHECK(dialog && dialog->label() && std::strcmp(dialog->label(),"Plugin descriptors") == 0);
|
||||
if (!dialog) return;
|
||||
for (int i = 0; i < dialog->children(); ++i)
|
||||
if (auto *list = dynamic_cast<Fl_Hold_Browser *>(dialog->child(i))) {
|
||||
|
|
@ -803,6 +803,30 @@ int main(int argc, char **argv)
|
|||
unlink("/tmp/xtc-gui-plugin-test.xtc");
|
||||
window.do_callback();
|
||||
}
|
||||
for (const char *variable : {"XTC_TEST_CLAP","XTC_TEST_VST2","XTC_TEST_VST3"}) {
|
||||
if (const char *module = std::getenv(variable)) {
|
||||
reset(); MainWindow window(false); auto *sequence = timeline(&window);
|
||||
size_t id = SIZE_MAX; xtc_plugin_spec spec{};
|
||||
CHECK(window.add_plugin(module,0,&id) == XTC_OK);
|
||||
CHECK(xtc_graph_get_plugin(xtc_project_graph(sequence->project()),id,&spec) == XTC_OK);
|
||||
CHECK(spec.format != XTC_PLUGIN_LADSPA && spec.inputs == 1 && !spec.control_count);
|
||||
CHECK(window.undo(false) == XTC_OK && window.undo(true) == XTC_OK);
|
||||
Fl::add_timeout(.05,[](void *) {
|
||||
auto *dialog = Fl::first_window(); CHECK(dialog);
|
||||
auto *choose = find(dialog,"Choose"); CHECK(choose); if (choose) choose->do_callback();
|
||||
});
|
||||
unsigned int chosen = UINT32_MAX;
|
||||
CHECK(xtc_choose_plugin(module,48000,chosen) && chosen == 0);
|
||||
CHECK(window.save_project("/tmp/xtc-gui-modern-test.xtc") == XTC_OK);
|
||||
CHECK(window.new_project() == XTC_OK);
|
||||
CHECK(window.open_project("/tmp/xtc-gui-modern-test.xtc") == XTC_OK);
|
||||
xtc_plugin_spec loaded{};
|
||||
CHECK(xtc_graph_get_plugin(xtc_project_graph(sequence->project()),id,&loaded) == XTC_OK && loaded.format == spec.format);
|
||||
const xtc_project *before = sequence->project();
|
||||
CHECK(window.replace_plugin(id,"/absent/modern.so",0) == XTC_ERR_NOT_FOUND && sequence->project() == before);
|
||||
unlink("/tmp/xtc-gui-modern-test.xtc"); window.do_callback();
|
||||
}
|
||||
}
|
||||
if (failures != 0) return 1;
|
||||
std::printf("GUI controls/errors/views/timers/cleanup passed: %u opens, %u closes.\n", opens, closes);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ def main():
|
|||
assert not (binaries / 'xtc').exists()
|
||||
assert (resources / 'licenses/xtc/LICENSE').read_bytes() == (source / 'LICENSE').read_bytes()
|
||||
assert (docs / 'PROJECT_FORMAT.md').is_file()
|
||||
assert (resources / 'licenses/xtc/CLAP-LICENSE').is_file()
|
||||
assert (resources / 'licenses/xtc/VST3-C-API-LICENSE').is_file()
|
||||
assert os.access(binaries / 'xtc-check', os.X_OK)
|
||||
run([str(binaries / 'xtc-check')])
|
||||
for filename in ('performance-baseline.json', 'project-performance-baseline.json', 'graph-performance-phase29.json'):
|
||||
|
|
@ -52,7 +54,7 @@ def main():
|
|||
run([str(binaries / 'xtc-check'), '--inspect', str(example)])
|
||||
assert (resources / 'xtc/examples/README.md').is_file()
|
||||
diagnose = subprocess.check_output([str(binaries / 'xtc-check'), '--diagnose'], text=True)
|
||||
assert 'Project formats: 1..10' in diagnose and 'JACK compiled:' in diagnose
|
||||
assert 'Project formats: 1..11' in diagnose and 'JACK compiled:' in diagnose
|
||||
# Replacing a file symlink must not overwrite an unrelated target.
|
||||
protected = stage / 'unrelated-target'
|
||||
protected.write_text('keep unrelated target\n')
|
||||
|
|
|
|||
116
tests/integration/test_modern_plugins.c
Normal file
116
tests/integration/test_modern_plugins.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "xtc/xtc_project.h"
|
||||
#include "xtc/xtc_realtime.h"
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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 void render(xtc_realtime *rt)
|
||||
{
|
||||
float in[31],left[31],right[31]; size_t i;
|
||||
for (i = 0; i < 31; ++i) in[i] = i % 2 ? -.5f : .25f;
|
||||
audio = 1;
|
||||
assert(xtc_realtime_process_stereo(rt,in,NULL,NULL,NULL,NULL,left,right,31) == XTC_OK);
|
||||
audio = 0;
|
||||
for (i = 0; i < 31; ++i) assert(left[i] == in[i]*.5f && right[i] == left[i]);
|
||||
xtc_plugin_idle();
|
||||
}
|
||||
static void check(const char *path, xtc_plugin_format format)
|
||||
{
|
||||
xtc_plugin_spec spec, actual, bad;
|
||||
xtc_plugin_details details;
|
||||
xtc_plugin *plugin = NULL, *other = NULL;
|
||||
xtc_project *p = NULL, *loaded = NULL;
|
||||
xtc_graph *graph = NULL;
|
||||
xtc_realtime *rt = NULL;
|
||||
size_t node, output, i;
|
||||
char document[] = "/tmp/xtc-modern-XXXXXX";
|
||||
int fd = mkstemp(document); assert(fd >= 0); close(fd);
|
||||
assert(xtc_plugin_probe(path,0,48000,&spec) == XTC_OK && spec.format == format);
|
||||
assert(xtc_plugin_probe_format(format,path,0,48000,&actual) == XTC_OK);
|
||||
assert(!memcmp(&spec,&actual,sizeof(spec)));
|
||||
assert(xtc_plugin_describe(&spec,48000,&details) == XTC_OK && details.name[0]);
|
||||
assert(xtc_plugin_create(&spec,48000,31,&plugin) == XTC_OK);
|
||||
assert(xtc_plugin_create(&spec,48000,31,&other) == XTC_OK);
|
||||
{ float data[31]; for (i = 0; i < 31; ++i) data[i] = 1;
|
||||
audio = 1; assert(xtc_plugin_process(plugin,data,data,31) == XTC_OK); audio = 0;
|
||||
for (i = 0; i < 31; ++i) assert(data[i] == .5f);
|
||||
audio = 1; assert(xtc_plugin_process(other,NULL,data,31) == XTC_OK); audio = 0;
|
||||
for (i = 0; i < 31; ++i) assert(data[i] == 0);
|
||||
assert(xtc_plugin_process(plugin,data,data,32) == XTC_ERR_INVALID_ARGUMENT); }
|
||||
xtc_plugin_idle(); xtc_plugin_destroy(plugin); xtc_plugin_destroy(other); plugin = NULL;
|
||||
assert(xtc_project_create(&p) == XTC_OK && xtc_project_create(&loaded) == XTC_OK);
|
||||
assert(xtc_graph_create(2,&graph) == XTC_OK);
|
||||
assert(xtc_graph_add_plugin(graph,&spec,&node) == XTC_OK && xtc_graph_add_output(graph,&output) == XTC_OK);
|
||||
assert(xtc_graph_connect(graph,node,output) == XTC_OK);
|
||||
assert(xtc_project_set_graph(p,graph) == XTC_OK);
|
||||
assert(xtc_project_save(p,document) == XTC_OK && xtc_project_load(loaded,document) == XTC_OK);
|
||||
assert(xtc_graph_get_plugin(xtc_project_graph(loaded),node,&actual) == XTC_OK);
|
||||
assert(!memcmp(&actual,&spec,sizeof(spec)));
|
||||
assert(xtc_realtime_create(48000,31,&rt) == XTC_OK);
|
||||
assert(xtc_realtime_submit(rt,xtc_project_graph(loaded)) == XTC_OK); render(rt);
|
||||
for (i = 0; i < 50; ++i) {
|
||||
xtc_realtime_collect(rt);
|
||||
assert(xtc_realtime_submit(rt,graph) == XTC_OK); render(rt);
|
||||
}
|
||||
if (format == XTC_PLUGIN_VST3) {
|
||||
bad = spec; strcpy(bad.identifier,"not-a-class-id");
|
||||
assert(xtc_plugin_spec_validate(&bad) == XTC_ERR_INVALID_ARGUMENT);
|
||||
}
|
||||
bad = spec; bad.control_count = 1;
|
||||
assert(xtc_plugin_spec_validate(&bad) == XTC_ERR_UNSUPPORTED);
|
||||
bad = spec; ++bad.unique_id;
|
||||
assert(xtc_graph_replace_plugin(graph,node,&bad) == XTC_OK);
|
||||
xtc_realtime_collect(rt);
|
||||
assert(xtc_realtime_submit(rt,graph) == XTC_ERR_UNSUPPORTED); render(rt);
|
||||
bad = spec; strcpy(bad.path,"/missing/xtc-modern-plugin.so");
|
||||
assert(xtc_graph_replace_plugin(graph,node,&bad) == XTC_OK && xtc_project_set_graph(p,graph) == XTC_OK);
|
||||
assert(xtc_project_save(p,document) == XTC_OK && xtc_project_load(loaded,document) == XTC_OK);
|
||||
assert(xtc_graph_get_plugin(xtc_project_graph(loaded),node,&actual) == XTC_OK && !strcmp(actual.path,bad.path));
|
||||
assert(xtc_plugin_create(&actual,48000,31,&plugin) == XTC_ERR_NOT_FOUND && !plugin);
|
||||
xtc_realtime_destroy(rt); xtc_graph_destroy(graph); xtc_project_destroy(p); xtc_project_destroy(loaded); unlink(document);
|
||||
printf("%s identity, buffers, stereo graph, retirement, reload and missing dependency passed.\n",xtc_plugin_format_name(format));
|
||||
}
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
unsigned int i; xtc_plugin_spec spec; xtc_plugin *p = NULL; float data[1] = {1};
|
||||
assert(argc == 4);
|
||||
check(argv[1],XTC_PLUGIN_CLAP); check(argv[2],XTC_PLUGIN_VST2); check(argv[3],XTC_PLUGIN_VST3);
|
||||
for (i = 1; i < 4; ++i) assert(xtc_plugin_probe_format(XTC_PLUGIN_CLAP,argv[1],i,48000,&spec) != XTC_OK);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_CLAP,argv[1],4,48000,&spec) == XTC_OK);
|
||||
assert(xtc_plugin_create(&spec,48000,1,&p) == XTC_OK);
|
||||
assert(xtc_plugin_process(p,data,data,1) == XTC_ERR_INVALID_STATE && data[0] == 0); xtc_plugin_destroy(p); p = NULL;
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_CLAP,argv[1],5,48000,&spec) == XTC_OK);
|
||||
assert(xtc_plugin_create(&spec,48000,1,&p) == XTC_OK);
|
||||
assert(xtc_plugin_process(p,data,data,1) == XTC_ERR_INVALID_STATE && data[0] == 0); xtc_plugin_destroy(p);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_CLAP,argv[1],6,48000,&spec) == XTC_OK);
|
||||
p = NULL; data[0] = 1;
|
||||
assert(xtc_plugin_create(&spec,48000,1,&p) == XTC_OK);
|
||||
assert(xtc_plugin_process(p,data,data,1) == XTC_OK && data[0] == .5f);
|
||||
xtc_plugin_idle();
|
||||
assert(xtc_plugin_process(p,data,data,1) == XTC_ERR_INVALID_STATE && data[0] == 0); xtc_plugin_destroy(p);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_CLAP,argv[1],7,48000,&spec) == XTC_ERR_NOT_FOUND);
|
||||
assert(setenv("XTC_VST2_FIXTURE_LAYOUT","1",1) == 0);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_VST2,argv[2],0,48000,&spec) == XTC_ERR_UNSUPPORTED);
|
||||
unsetenv("XTC_VST2_FIXTURE_LAYOUT");
|
||||
assert(setenv("XTC_VST3_FIXTURE_INIT","1",1) == 0);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_VST3,argv[3],0,48000,&spec) == XTC_ERR_INVALID_STATE);
|
||||
unsetenv("XTC_VST3_FIXTURE_INIT");
|
||||
puts("Modern plugin partial initialization/start/process failures passed."); return 0;
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ int main(void)
|
|||
assert(mkdtemp(root));
|
||||
assert(xtc_project_create(&p) == XTC_OK);
|
||||
snprintf(file,sizeof(file),"%s/migration.xtc",root);
|
||||
for (version = 1; version <= 10; ++version) {
|
||||
for (version = 1; version <= 11; ++version) {
|
||||
xtc_track_info t; xtc_clip_info c; size_t notes, points;
|
||||
snprintf(old,sizeof(old),"tests/fixtures/project-v%u.xtc",version);
|
||||
assert(xtc_project_load(p,old) == XTC_OK);
|
||||
|
|
@ -89,6 +89,6 @@ int main(void)
|
|||
assert(xtc_project_load(p,file) == XTC_ERR_FORMAT);
|
||||
{ xtc_clip_info c; assert(xtc_project_find_clip(p,clip,&c) == XTC_OK && c.asset_path[0]); } }
|
||||
xtc_project_destroy(p); unlink(file); rmdir(moved);
|
||||
puts("v1-v10 migration, current folder relocation, cwd independence and missing-media preservation passed.");
|
||||
puts("v1-v11 migration, current folder relocation, cwd independence and missing-media preservation passed.");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,8 +36,37 @@ with tempfile.TemporaryDirectory(prefix="xtc-inspect-") as directory:
|
|||
# Inspection succeeds for readable files without trying dlopen or decoding.
|
||||
module.write_text("Deliberately not an executable module")
|
||||
check("--inspect", project, 0, "no plugin code executed")
|
||||
# Current metadata-only inspection must not execute any modern plugin ABI.
|
||||
maps = base[base.index("CONTROL_MAPS"):]
|
||||
for number, name, identity in ((1, "CLAP", "org.xtc.fixture"), (2, "VST2", ""),
|
||||
(3, "VST3", "0123456789abcdef0123456789abcdef")):
|
||||
lines = []
|
||||
for line in plugin.splitlines():
|
||||
if line.startswith(("CONTROL ", "PARAM ")):
|
||||
continue
|
||||
if line.startswith("XTC_PROJECT "):
|
||||
line = "XTC_PROJECT 11"
|
||||
elif line.startswith("PLUGIN "):
|
||||
fields = line.split()
|
||||
fields[4] = "0"
|
||||
line = " ".join(fields)
|
||||
elif line.startswith("PLUGIN_AUTOMATION "):
|
||||
line = "PLUGIN_AUTOMATION 0"
|
||||
elif line == "END":
|
||||
line = maps.rstrip()
|
||||
lines.append(line)
|
||||
if line.startswith("MODULE "):
|
||||
lines.append(f"FORMAT {number} {identity.encode().hex() if identity else '-'}")
|
||||
modern = "\n".join(lines) + "\n"
|
||||
project.write_text(modern)
|
||||
check("--inspect", project, 0, name + " node 0", "Project format 11")
|
||||
project.write_text(modern.replace(f"FORMAT {number} ", "FORMAT 99 "))
|
||||
check("--validate", project, 1, "Unsupported")
|
||||
if number == 3:
|
||||
project.write_text(modern.replace(identity.encode().hex(), b"bad-class-id".hex()))
|
||||
check("--validate", project, 1)
|
||||
for text, messages in [
|
||||
(base.replace("XTC_PROJECT 10", "XTC_PROJECT 999"), ("line 1", "999", "1..10")),
|
||||
(base.replace("XTC_PROJECT 10", "XTC_PROJECT 999"), ("line 1", "999", "1..11")),
|
||||
("XTC_PROJECT 10\n", ("line 2", "APP")),
|
||||
(base.replace("TEMPO 120000", "TEMPO 0"), ("line 3", "TEMPO")),
|
||||
(base + "OPTIONAL_FUTURE 1\n", ("Unknown fields",)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue