feat: collect portable project media with transactional cleanup and qualify composition workflows

This commit is contained in:
Johannes Findeisen 2026-11-06 00:49:37 +01:00
commit b0cfa79131
15 changed files with 225 additions and 10 deletions

View file

@ -1396,6 +1396,8 @@ int main(int argc, char **argv)
CHECK(window.import_audio(sample)==XTC_OK);CHECK(s->normalize_audio(.25f)==XTC_OK);
xtc_clip_info normalized{};xtc_project_find_clip(s->project(),s->selected_clip(),&normalized);CHECK(normalized.gain>0 && normalized.gain<=4);
CHECK(window.undo()==XTC_OK);
std::string folder="/tmp/xtc-gui-collect-"+std::to_string(getpid());CHECK(window.collect_project(folder.c_str())==XTC_OK);
CHECK(access((folder+"/project.xtc").c_str(),F_OK)==0);unlink((folder+"/project.xtc").c_str());unlink((folder+"/media-000.wav").c_str());rmdir(folder.c_str());
window.do_callback();
}
if (failures != 0) return 1;

View file

@ -1,3 +1,4 @@
#define _XOPEN_SOURCE 700
/* GNU ld allocation interposition: fail preparation at every allocation and
* reject host allocation/reclamation inside processing. No production hooks. */
#include "xtc/xtc_realtime.h"
@ -5,6 +6,7 @@
#include "xtc/xtc_control.h"
#include "xtc/xtc_plugin.h"
#include "xtc/xtc_editing.h"
#include "xtc/xtc_audio.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
@ -221,8 +223,54 @@ static void modern_edit_failures(void)
}
xtc_clipboard_destroy(board);xtc_mix_snapshot_destroy(mix);xtc_project_destroy(p);
}
static void composition_failures(void)
{
xtc_project *p=NULL;uint64_t t,ids[2],note,audio;char sample[4096],root[]="/tmp/xtc-compose-fail-XXXXXX";
int operation,failure;xtc_midi_transform human={0};int intervals[2]={4,7};
assert(mkdtemp(root));assert(getcwd(sample,sizeof(sample)));assert(strlen(sample)+40<sizeof(sample));strcat(sample,"/examples/projects/original-pluck.wav");
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Track",&t)==XTC_OK);
assert(xtc_project_add_midi_clip(p,t,0,1920,&ids[0])==XTC_OK && xtc_project_add_midi_clip(p,t,3840,1920,&ids[1])==XTC_OK);
assert(xtc_project_add_note(p,ids[0],480,240,60,100,0,&note)==XTC_OK && xtc_project_add_note(p,ids[1],480,240,64,100,0,&note)==XTC_OK);
assert(xtc_project_import_audio(p,t,sample,7680,&audio)==XTC_OK);
fail_after=0;assert(xtc_project_set_marker(p,3840,"Section")==XTC_OK);fail_after=-1;
human.type=XTC_MIDI_TRANSFORM_HUMANIZE;human.seed=42;human.timing_variation=12;human.velocity_variation=8;
for(operation=0;operation<9;++operation) {
int succeeded=0;
for(failure=0;failure<128;++failure) {
xtc_project *copy=NULL;xtc_clip_info c;uint64_t output=999;xtc_result r;size_t tracks,clips,notes;
char dest[512],file[512];snprintf(dest,sizeof(dest),"%s/copy-%d-%d",root,operation,failure);
assert(xtc_project_clone(p,&copy)==XTC_OK);fail_after=failure;
switch(operation) {
case 0:r=xtc_project_ripple_time(copy,1920,960,0);break;
case 1:r=xtc_project_duplicate_track(copy,t,"Copy",&output);break;
case 2:r=xtc_project_split_midi(copy,ids[0],960,&output);break;
case 3:r=xtc_project_merge_midi(copy,ids,2,&output);break;
case 4:r=xtc_project_transform_midi(copy,ids,2,&human);break;
case 5:r=xtc_project_chordify(copy,ids,2,intervals,2);break;
case 6:r=xtc_project_arpeggiate(copy,ids,2,120,50,0);break;
case 7:r=xtc_project_normalize_audio(copy,&audio,1,.25f);break;
default:r=xtc_project_collect(copy,dest);break;
}
fail_after=-1;
if(r==XTC_OK)succeeded=1;
else {
assert((r==XTC_ERR_OUT_OF_MEMORY || (operation==8 && r==XTC_ERR_IO)) && output==999);
assert(xtc_project_get_counts(copy,&tracks,&clips)==XTC_OK && tracks==1 && clips==3);
assert(xtc_project_note_count(copy,&notes)==XTC_OK && notes==2);
assert(xtc_project_find_clip(copy,ids[1],&c)==XTC_OK && c.start_tick==3840);
assert(access(dest,F_OK)!=0);
{uint64_t probe;assert(xtc_project_duplicate_clip(copy,ids[0],t,9600,&probe)==XTC_OK && probe==3);}
}
if(operation==8 && succeeded){snprintf(file,sizeof(file),"%s/copy-%d-%d/project.xtc",root,operation,failure);assert(unlink(file)==0);snprintf(file,sizeof(file),"%s/copy-%d-%d/media-000.wav",root,operation,failure);assert(unlink(file)==0 && rmdir(dest)==0);}
xtc_project_destroy(copy);if(succeeded)break;
}
assert(succeeded);
}
xtc_project_destroy(p);assert(rmdir(root)==0);
}
int main(void)
{
composition_failures();
modern_edit_failures();
builtin_failures();
graph_limits();