1
0
Fork 0
forked from hanez/xtc
xtc/tests/unit/test_buses.c

115 lines
7 KiB
C

#define _POSIX_C_SOURCE 200809L
#include "xtc/xtc_audio.h"
#include "xtc/xtc_midi.h"
#include "xtc/xtc_mixer.h"
#include "../../src/mixer/mix_internal.h"
#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define TAU 6.28318530717958647692
static void render(xtc_project *p, float *left, float *right, float *meters)
{
xtc_audio_plan *a = NULL;
memset(meters, 0, 8*2*64*sizeof(float));
assert(xtc_audio_plan_create(p, 48000, &a) == XTC_OK);
xtc_audio_plan_monitor(a, meters, 64);
assert(xtc_audio_plan_render_stereo(a, 0, left, right, 64) == XTC_OK);
xtc_audio_plan_destroy(a);
}
int main(void)
{
xtc_project *p = NULL, *q = NULL, *undo = NULL;
xtc_recorder *rec = NULL;
xtc_history *history = NULL;
xtc_audio_plan *audio = NULL;
xtc_midi_plan *midi = NULL;
xtc_track_info info;
xtc_peak_meter meter;
xtc_automation_point point;
uint64_t a,b,c,bus,clip,id;
float in[64], left[64], right[64], meters[8*2*64], ml[64], mr[64];
char directory[] = "/tmp/xtc-buses-XXXXXX", wave[256], song[256];
size_t i;
assert(mkdtemp(directory));
snprintf(wave,sizeof(wave),"%s/source.wav",directory);
snprintf(song,sizeof(song),"%s/song.xtc",directory);
for(i=0;i<64;++i) in[i]=1;
assert(xtc_recorder_create(wave,48000,64,&rec)==XTC_OK);
xtc_recorder_push(rec,in,64); assert(xtc_recorder_finish(rec)==XTC_OK); xtc_recorder_destroy(rec);
assert(xtc_project_create(&p)==XTC_OK && xtc_project_create(&q)==XTC_OK);
assert(xtc_project_add_track(p,"A",&a)==XTC_OK && xtc_project_add_track(p,"B",&b)==XTC_OK);
assert(xtc_project_add_track(p,"Direct",&c)==XTC_OK && xtc_project_add_bus(p,"Group",&bus)==XTC_OK);
assert(xtc_project_route_track(p,a,bus)==XTC_OK && xtc_project_route_track(p,b,bus)==XTC_OK);
assert(xtc_project_route_track(p,bus,bus)==XTC_ERR_INVALID_STATE);
assert(xtc_project_route_track(p,a,b)==XTC_ERR_INVALID_ARGUMENT);
assert(xtc_project_import_audio(p,bus,wave,0,&clip)==XTC_ERR_INVALID_STATE);
assert(xtc_project_import_audio(p,a,wave,0,&clip)==XTC_OK);
assert(xtc_project_duplicate_clip(p,clip,bus,0,&id)==XTC_ERR_INVALID_STATE);
assert(xtc_project_edit_clip(p,clip,bus,0,3,0)==XTC_ERR_INVALID_STATE);
assert(xtc_project_import_audio(p,b,wave,0,&clip)==XTC_OK && xtc_project_import_audio(p,c,wave,0,&clip)==XTC_OK);
assert(xtc_project_set_mix(p,a,.5f,0,0,0)==XTC_OK && xtc_project_set_mix(p,b,.5f,0,0,0)==XTC_OK);
assert(xtc_project_set_mix(p,c,.25f,0,0,0)==XTC_OK && xtc_project_set_mix(p,bus,.5f,1,0,0)==XTC_OK);
render(p,left,right,meters);
assert(left[0]==.25f && right[0]==.75f);
assert(meters[0]==.5f && meters[64]==.5f && meters[(3*2)*64]==0 && meters[(3*2+1)*64]==.5f);
assert(xtc_project_set_mix(p,a,.5f,0,0,1)==XTC_OK);
render(p,left,right,meters); assert(left[0]==0 && right[0]==.25f && meters[2*64]==0);
assert(xtc_project_set_mix(p,a,.5f,0,0,0)==XTC_OK && xtc_project_set_mix(p,bus,.5f,1,0,1)==XTC_OK);
render(p,left,right,meters); assert(left[0]==0 && right[0]==.5f && meters[4*64]==0);
assert(xtc_project_set_mix(p,bus,.5f,1,1,1)==XTC_OK);
render(p,left,right,meters); assert(right[0]==0 && meters[0]==.5f && meters[7*64]==0);
assert(xtc_project_set_mix(p,bus,.5f,0,0,0)==XTC_OK);
point=(xtc_automation_point){bus,0,XTC_AUTOMATION_GAIN,.25f,0};
assert(xtc_project_set_automation(p,&point)==XTC_OK);
render(p,left,right,meters); assert(left[0]==.5f && right[0]==.5f); /* .25 bus + .25 direct */
assert(xtc_project_save(p,song)==XTC_OK && xtc_project_load(q,song)==XTC_OK);
assert(xtc_project_get_track(q,3,&info)==XTC_OK && info.is_bus && info.bus_id==UINT64_MAX);
assert(xtc_project_get_track(q,0,&info)==XTC_OK && info.bus_id==bus);
assert(xtc_history_create(q,&history)==XTC_OK && xtc_project_remove_track(q,bus)==XTC_OK);
assert(xtc_project_get_track(q,0,&info)==XTC_OK && info.bus_id==UINT64_MAX);
assert(xtc_history_record(history,q)==XTC_OK && xtc_history_checkout(history,0,&undo)==XTC_OK);
assert(xtc_project_get_track(undo,0,&info)==XTC_OK && info.bus_id==bus);
xtc_project_destroy(undo); xtc_history_destroy(history);
/* Invalid destination on load is transactional. */
{
char text[10000], *route; size_t length; FILE *file=fopen(song,"rb"); assert(file);
length=fread(text,1,sizeof(text)-1,file); assert(fclose(file)==0); text[length]=0;
route=strstr(text,"ROUTE 0 3\n"); assert(route); route[8]='9';
file=fopen(song,"wb"); assert(file); assert(fwrite(text,1,length,file)==length && fclose(file)==0);
assert(xtc_project_load(q,song)==XTC_ERR_FORMAT);
}
xtc_project_destroy(p); xtc_project_destroy(q);
/* Audio+MIDI on one track cancel before their shared bus/meter tap. */
assert(unlink(wave)==0);
for(i=0;i<64;++i) in[i]=-(float)(sin(TAU*fmod((double)i*(440.0/48000),1.0))*.1);
assert(xtc_recorder_create(wave,48000,64,&rec)==XTC_OK);
xtc_recorder_push(rec,in,64); assert(xtc_recorder_finish(rec)==XTC_OK); xtc_recorder_destroy(rec);
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Cancel",&a)==XTC_OK);
assert(xtc_project_add_bus(p,"Sum",&bus)==XTC_OK && xtc_project_route_track(p,a,bus)==XTC_OK);
assert(xtc_project_import_audio(p,a,wave,0,&clip)==XTC_OK && xtc_project_add_midi_clip(p,a,0,960,&clip)==XTC_OK);
assert(xtc_project_add_note(p,clip,0,960,69,127,0,&id)==XTC_OK);
assert(xtc_audio_plan_create(p,48000,&audio)==XTC_OK && xtc_midi_plan_create(p,48000,&midi)==XTC_OK);
memset(meters,0,sizeof(meters)); xtc_audio_plan_monitor(audio,meters,64); xtc_midi_plan_monitor(midi,meters,64);
assert(xtc_audio_plan_render_stereo(audio,0,left,right,64)==XTC_OK);
assert(xtc_midi_plan_render_stereo(midi,0,NULL,0,ml,mr,17)==XTC_OK);
assert(xtc_midi_plan_render_stereo(midi,17,NULL,0,ml+17,mr+17,47)==XTC_OK);
/* Standalone callers clear/offset scratch per render; backend renders one MIDI call per block. */
memset(meters,0,sizeof(meters));
assert(xtc_audio_plan_render_stereo(audio,0,left,right,64)==XTC_OK);
assert(xtc_midi_plan_render_stereo(midi,0,NULL,0,ml,mr,64)==XTC_OK);
for(i=0;i<64;++i) { assert(fabsf(meters[i])<.000001f); assert(fabsf(meters[2*64+i])<.000001f); }
xtc_audio_plan_destroy(audio); xtc_midi_plan_destroy(midi); xtc_project_destroy(p);
xtc_peak_meter_reset(&meter); assert(meter.db==-90 && !meter.clipped);
assert(xtc_peak_meter_update(&meter,.5f,.1)==XTC_OK && fabsf(meter.db+6.0206f)<.0001f);
assert(xtc_peak_meter_update(&meter,0,.1)==XTC_OK && fabsf(meter.db+8.0206f)<.0001f);
assert(xtc_peak_meter_update(&meter,1,0)==XTC_OK && meter.db==0 && meter.clipped);
assert(xtc_peak_meter_update(&meter,0,1)==XTC_OK && meter.db==-20 && meter.clipped);
assert(xtc_peak_meter_update(&meter,NAN,.1)==XTC_ERR_INVALID_ARGUMENT && meter.db==-20);
xtc_peak_meter_reset(&meter); assert(!meter.clipped);
assert(unlink(wave)==0 && unlink(song)==0 && rmdir(directory)==0);
puts("Bus routing/gain/pan/mute/solo/automation, coherent meters, persistence and undo passed.");
return 0;
}