feat(project): persist bounded per-track effect chains (phase 81)

This commit is contained in:
Johannes Findeisen 2026-11-06 01:34:08 +01:00
commit 7489f86351
15 changed files with 159 additions and 12 deletions

View file

@ -56,7 +56,7 @@ def main():
run([str(binaries / 'xtc-check'), '--inspect', str(resources / 'xtc/examples/modern-session.xtc')])
assert (docs / 'WORKSPACE.md').is_file()
diagnose = subprocess.check_output([str(binaries / 'xtc-check'), '--diagnose'], text=True)
assert 'Project formats: 1..16' in diagnose and 'JACK compiled:' in diagnose
assert 'Project formats: 1..17' 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')

View file

@ -84,7 +84,7 @@ with tempfile.TemporaryDirectory(prefix="xtc-inspect-") as directory:
project.write_text(malformed)
check("--validate", project, 1)
for text, messages in [
(base.replace("XTC_PROJECT 10", "XTC_PROJECT 999"), ("line 1", "999", "1..16")),
(base.replace("XTC_PROJECT 10", "XTC_PROJECT 999"), ("line 1", "999", "1..17")),
("XTC_PROJECT 10\n", ("line 2", "APP")),
(base.replace("TEMPO 120000", "TEMPO 0"), ("line 3", "TEMPO")),
(base + "OPTIONAL_FUTURE 1\n", ("Unknown fields",)),

View file

@ -167,8 +167,8 @@ static void editable(void)
assert(xtc_plugin_preset_save(&legacy,path)==XTC_OK);
{FILE *f=fopen(path,"r");char lines[32768],line[2048];size_t used=0;assert(f);
while(fgets(line,sizeof(line),f)) {
if(!strncmp(line,"MARKERS ",8) || !strncmp(line,"SAMPLE ",7) || !strncmp(line,"INSTRUMENT_CONTROLS",19) || !strncmp(line,"INSTRUMENT_AUTOMATION",21))continue;
if(!strncmp(line,"XTC_PROJECT 16",14))strcpy(line,"XTC_PROJECT 13\n");
if(!strncmp(line,"CHAINS ",7) || !strncmp(line,"CHAIN ",6) || !strncmp(line,"MARKERS ",8) || !strncmp(line,"SAMPLE ",7) || !strncmp(line,"INSTRUMENT_CONTROLS",19) || !strncmp(line,"INSTRUMENT_AUTOMATION",21))continue;
if(!strncmp(line,"XTC_PROJECT 17",14))strcpy(line,"XTC_PROJECT 13\n");
assert(used+strlen(line)<sizeof(lines));strcpy(lines+used,line);used+=strlen(line);
}fclose(f);f=fopen(path,"w");assert(f && fwrite(lines,1,used,f)==used);fclose(f);}
assert(xtc_plugin_preset_load(path,&t)==XTC_OK && !t.control_count && t.index==0);

View file

@ -197,7 +197,7 @@ int main(void)
fputc('\n',legacy); continue;
}
if (strncmp(line, "MIX ", 4) && strncmp(line, "AUTOMATION ", 11) &&
strncmp(line, "POINT ", 6) && strncmp(line, "AUDIO ", 6) && strncmp(line, "ROUTE ", 6) && strncmp(line,"CONTROL_MAPS ",13) && strncmp(line,"MAP ",4) && strncmp(line,"TRACK_INSTRUMENTS ",18) && strncmp(line,"LIVE_INSTRUMENT ",16) && strncmp(line,"MARKERS ",8) && strncmp(line,"MARKER ",7))
strncmp(line, "POINT ", 6) && strncmp(line, "AUDIO ", 6) && strncmp(line, "ROUTE ", 6) && strncmp(line,"CONTROL_MAPS ",13) && strncmp(line,"MAP ",4) && strncmp(line,"TRACK_INSTRUMENTS ",18) && strncmp(line,"LIVE_INSTRUMENT ",16) && strncmp(line,"CHAINS ",7) && strncmp(line,"CHAIN ",6) && strncmp(line,"MARKERS ",8) && strncmp(line,"MARKER ",7))
assert(fputs(line, legacy) >= 0);
}
assert(fclose(f) == 0 && fclose(legacy) == 0);

View file

@ -81,7 +81,7 @@ int main(void)
}
write_text(bad,"XTC_PROJECT 999\n");
assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_UNSUPPORTED && diagnostic.line == 1 &&
diagnostic.format_version == 999 && !strcmp(diagnostic.record,"XTC_PROJECT") && strstr(diagnostic.message,"1..16"));
diagnostic.format_version == 999 && !strcmp(diagnostic.record,"XTC_PROJECT") && strstr(diagnostic.message,"1..17"));
write_text(bad,"XTC_PROJECT 10\nAPP 0 1 0\nTEMPO nope\n");
assert(xtc_project_load_diagnostic(loaded,bad,&diagnostic) == XTC_ERR_FORMAT && diagnostic.line == 3 &&
!strcmp(diagnostic.record,"TEMPO"));

View file

@ -0,0 +1,28 @@
#define _POSIX_C_SOURCE 200809L
#include "xtc/xtc_plugin.h"
#include "xtc/xtc_editing.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(void)
{
xtc_project *p=NULL,*q=NULL;uint64_t a,b,duplicate;xtc_track_chain c={0},got={0};
char file[]="/tmp/xtc-chain-XXXXXX";int fd=mkstemp(file);assert(fd>=0);close(fd);
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Audio",&a)==XTC_OK && xtc_project_add_track(p,"Other",&b)==XTC_OK);
assert(xtc_project_get_chain(p,a,&got)==XTC_OK && got.count==0 && got.output_gain==1);
assert(xtc_plugin_probe_format(XTC_PLUGIN_BUILTIN,XTC_BUILTIN_PATH,2,48000,&c.slots[0].plugin)==XTC_OK);
c.count=1;c.output_gain=.75f;c.slots[0].enabled=1;c.slots[0].wet=.5f;
assert(xtc_project_set_chain(p,a,&c)==XTC_OK);
assert(xtc_project_save(p,file)==XTC_OK && xtc_project_create(&q)==XTC_OK && xtc_project_load(q,file)==XTC_OK);
assert(xtc_project_get_chain(q,a,&got)==XTC_OK && got.count==1 && got.output_gain==.75f && got.slots[0].wet==.5f && !strcmp(got.slots[0].plugin.label,c.slots[0].plugin.label));
assert(xtc_project_duplicate_track(q,a,"Copy",&duplicate)==XTC_OK && xtc_project_get_chain(q,duplicate,&got)==XTC_OK && got.count==1);
assert(xtc_project_move_track(q,a,2)==XTC_OK && xtc_project_remove_track(q,b)==XTC_OK && xtc_project_get_chain(q,a,&got)==XTC_OK && got.count==1);
c.output_gain=NAN;assert(xtc_project_set_chain(q,a,&c)==XTC_ERR_INVALID_ARGUMENT);
assert(xtc_project_get_chain(q,a,&got)==XTC_OK && got.output_gain==.75f);
c.output_gain=1;c.slots[0].wet=2;assert(xtc_project_set_chain(q,a,&c)==XTC_ERR_INVALID_ARGUMENT);
c.slots[0].wet=1;c.count=XTC_TRACK_INSERTS+1;assert(xtc_track_chain_validate(&c)==XTC_ERR_INVALID_ARGUMENT);
xtc_project_destroy(p);xtc_project_destroy(q);unlink(file);puts("Track insert metadata/persistence passed.");return 0;
}