feat(engine): render prepared per-track insert graphs (phase 82)
This commit is contained in:
parent
7489f86351
commit
62cee7a7b0
10 changed files with 215 additions and 5 deletions
2
Makefile
2
Makefile
|
|
@ -27,7 +27,7 @@ CXXFLAGS ?= -O2 -g
|
|||
CXXFLAGS += -std=c++17 -Wall -Wextra -Wpedantic -Iinclude
|
||||
LDFLAGS ?=
|
||||
|
||||
CORE_SOURCES := src/audio/sample.c src/persistence/collect.c src/audio/normalize.c src/project/sections.c src/project/editing.c src/audio/export.c src/plugin/builtin.c src/settings/settings.c src/control/model.c src/audio/waveform.c src/plugin/ladspa.c src/plugin/host.c src/plugin/modern.c src/native/processor.c src/audio/recovery.c src/dsp/ramp.c src/native/native.c src/mixer/model.c src/mixer/render.c src/project/history.c src/midi/model.c src/midi/instrument.c src/audio/wave.c src/audio/arrangement.c src/audio/recorder.c src/core/xtc.c src/project/project.c src/persistence/project_file.c src/graph/graph.c src/dsp/gain.c src/engine/offline_engine.c src/engine/realtime.c src/backend/jack_backend.c
|
||||
CORE_SOURCES := src/mixer/track_fx.c src/audio/sample.c src/persistence/collect.c src/audio/normalize.c src/project/sections.c src/project/editing.c src/audio/export.c src/plugin/builtin.c src/settings/settings.c src/control/model.c src/audio/waveform.c src/plugin/ladspa.c src/plugin/host.c src/plugin/modern.c src/native/processor.c src/audio/recovery.c src/dsp/ramp.c src/native/native.c src/mixer/model.c src/mixer/render.c src/project/history.c src/midi/model.c src/midi/instrument.c src/audio/wave.c src/audio/arrangement.c src/audio/recorder.c src/core/xtc.c src/project/project.c src/persistence/project_file.c src/graph/graph.c src/dsp/gain.c src/engine/offline_engine.c src/engine/realtime.c src/backend/jack_backend.c
|
||||
JACK_AVAILABLE := $(shell $(PKG_CONFIG) --exists jack 2>/dev/null && echo yes)
|
||||
ifeq ($(JACK_AVAILABLE),yes)
|
||||
JACK_CFLAGS := $(shell $(PKG_CONFIG) --cflags jack)
|
||||
|
|
|
|||
|
|
@ -1910,12 +1910,12 @@ Implemented: eight validated metadata-only insert slots per non-bus track, inclu
|
|||
Compile track inserts into serial modular graphs before track gain/pan. Cover audio and MIDI rendering, export, bypass, cleanup and callback safety; explicitly reject unsupported routing.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Implement the described workflow with validated errors and safe ownership.
|
||||
- [ ] Pass relevant core and/or GUI tests.
|
||||
- [ ] Document behavior and limitations; commit this phase separately.
|
||||
- [x] Implement the described workflow with validated errors and safe ownership.
|
||||
- [x] Pass relevant core and/or GUI tests.
|
||||
- [x] Document behavior and limitations; commit this phase separately.
|
||||
|
||||
### Implementation status
|
||||
Planned.
|
||||
Implemented: generated serial graphs process summed pre-fader audio or MIDI per track; prepared renderer plans feed JACK/export and post-effect meters. Wet/dry branches, bypass and output gain are compiled on the control thread. Exact impulse/delay, overlapping clips, block continuity, seek reset, MIDI samples and hybrid rejection pass. Full core suite and allocation-failure/callback guards pass. Bus/hybrid inserts reject explicitly; external adapters retain mono/zero-latency limits. See TRACK_INSERTS.md.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -14,3 +14,16 @@ that a third-party binary is compatible with the host.
|
|||
|
||||
The chain API is declared in `xtc_plugin.h`. Setter errors preserve the old
|
||||
chain; plugin identity and layout are checked when preparing playback.
|
||||
|
||||
Playback prepares an independent serial graph for each affected track. Audio
|
||||
clips on the same track sum before its effects; instrument and scheduled MIDI
|
||||
contributions also pass through their track graph. Effects run before track
|
||||
and bus gain/pan, and meters observe their output. Wet/dry uses parallel gain
|
||||
branches. Internal effect history resets at timeline discontinuities.
|
||||
|
||||
Each renderer owns its plans and preallocated raw buffers (up to 256 KiB per
|
||||
affected track); processing uses blocks of at most 256 frames, with no host
|
||||
allocation or I/O. Mono effect adapters require zero reported latency. External
|
||||
plugin state on seeks follows the existing host contract. Bus inserts and
|
||||
tracks containing both audio assets and MIDI reject preparation explicitly.
|
||||
Live unassigned sine monitoring remains a separate master input.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ typedef struct { xtc_plugin_spec plugin; unsigned int enabled; float wet; } xtc_
|
|||
typedef struct { size_t count; float output_gain; xtc_track_insert slots[XTC_TRACK_INSERTS]; } xtc_track_chain;
|
||||
/* Metadata only; control thread. Empty chain has unity output. No module is
|
||||
* opened here. Nonempty chains are mono effects on non-bus tracks. */
|
||||
/* Allocates the exact serial/parallel graph used by playback. Caller owns it. */
|
||||
xtc_result xtc_track_chain_graph(const xtc_track_chain *chain,xtc_graph **out);
|
||||
xtc_result xtc_track_chain_validate(const xtc_track_chain *chain);
|
||||
xtc_result xtc_project_get_chain(const xtc_project *project,uint64_t track,xtc_track_chain *out);
|
||||
xtc_result xtc_project_set_chain(xtc_project *project,uint64_t track,const xtc_track_chain *chain);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#define _XOPEN_SOURCE 700
|
||||
#include "wave_internal.h"
|
||||
#include "../mixer/mix_internal.h"
|
||||
#include "../mixer/track_fx.h"
|
||||
#include "../graph/graph_internal.h"
|
||||
#include "xtc/xtc_dsp.h"
|
||||
#include <math.h>
|
||||
|
|
@ -17,6 +18,7 @@ struct xtc_audio_plan {
|
|||
size_t fade_frames, monitor_stride;
|
||||
float *monitor;
|
||||
xtc_mix_snapshot mix;
|
||||
xtc_track_fx *fx;
|
||||
xtc_wave waves[64];
|
||||
char paths[64][XTC_ASSET_PATH_BYTES];
|
||||
region regions[XTC_PROJECT_MAX_CLIPS];
|
||||
|
|
@ -112,6 +114,7 @@ void xtc_audio_plan_destroy(xtc_audio_plan *plan)
|
|||
return;
|
||||
for (i = 0; i < plan->assets; ++i)
|
||||
free(plan->waves[i].samples);
|
||||
xtc_track_fx_destroy(plan->fx);
|
||||
free(plan);
|
||||
}
|
||||
|
||||
|
|
@ -149,6 +152,8 @@ xtc_result xtc_audio_plan_create(const xtc_project *project, unsigned int rate,
|
|||
free(plan);
|
||||
return result;
|
||||
}
|
||||
result=xtc_track_fx_create(project,rate,0,&plan->fx);
|
||||
if(result!=XTC_OK){xtc_audio_plan_destroy(plan);return result;}
|
||||
for (i = 0; i < project->clip_count; ++i) {
|
||||
const xtc_clip_info *clip = project->clips + i;
|
||||
size_t a;
|
||||
|
|
@ -246,6 +251,7 @@ xtc_result xtc_audio_plan_render_stereo(const xtc_audio_plan *plan, uint64_t pos
|
|||
memset(output, 0, frames * sizeof(*output));
|
||||
if (right)
|
||||
memset(right, 0, frames * sizeof(*right));
|
||||
xtc_track_fx_begin(plan->fx,position,frames);
|
||||
for (i = 0; i < plan->count; ++i) {
|
||||
const region *r = plan->regions + i;
|
||||
const xtc_wave *wave = plan->waves + r->asset;
|
||||
|
|
@ -289,12 +295,15 @@ xtc_result xtc_audio_plan_render_stereo(const xtc_audio_plan *plan, uint64_t pos
|
|||
}
|
||||
sample *= r->gain * envelope;
|
||||
}
|
||||
if(xtc_track_fx_add(plan->fx,r->track,j,sample))continue;
|
||||
if (plan->monitor) xtc_mix_monitor(&plan->mix, plan->monitor, plan->monitor_stride, r->track, position+j, j, sample, right != NULL);
|
||||
output[j] += sample * xtc_mix_level(&plan->mix, r->track, position + j, right ? 0U : 2U);
|
||||
if (right)
|
||||
right[j] += sample * xtc_mix_level(&plan->mix, r->track, position + j, 1);
|
||||
}
|
||||
}
|
||||
{xtc_result r=xtc_track_fx_finish(plan->fx,&plan->mix,position,output,right,frames,plan->monitor,plan->monitor_stride);
|
||||
if(r!=XTC_OK){memset(output,0,frames*sizeof(*output));if(right)memset(right,0,frames*sizeof(*right));return r;}}
|
||||
for (j = 0; j < frames; ++j) {
|
||||
output[j] = xtc_dsp_finite(output[j]);
|
||||
if (right) right[j] = xtc_dsp_finite(right[j]);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "xtc/xtc_dsp.h"
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "../mixer/mix_internal.h"
|
||||
#include "../mixer/track_fx.h"
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -33,6 +34,7 @@ struct xtc_midi_plan {
|
|||
unsigned int sustain[16];
|
||||
unsigned char control_map[16][3];
|
||||
xtc_mix_snapshot mix;
|
||||
xtc_track_fx *fx;
|
||||
span notes[XTC_PROJECT_MAX_NOTES];
|
||||
xtc_plugin *plugins[XTC_PROJECT_MAX_TRACKS];
|
||||
size_t live_track;
|
||||
|
|
@ -209,6 +211,7 @@ xtc_result xtc_midi_plan_create(const xtc_project *project, unsigned int rate, x
|
|||
}
|
||||
if (project->live_instrument == p->mix.tracks[i].id) p->live_track = i;
|
||||
}
|
||||
{xtc_result r=xtc_track_fx_create(project,rate,1,&p->fx);if(r!=XTC_OK){xtc_midi_plan_destroy(p);return r;}}
|
||||
memcpy(p->control_map,project->control_map,sizeof(p->control_map));
|
||||
p->controller_frames = rate / 200U;
|
||||
if (!p->controller_frames) p->controller_frames = 1;
|
||||
|
|
@ -228,6 +231,7 @@ void xtc_midi_plan_destroy(xtc_midi_plan *p)
|
|||
size_t i;
|
||||
if (!p) return;
|
||||
for (i = 0; i < XTC_PROJECT_MAX_TRACKS; ++i) xtc_plugin_destroy(p->plugins[i]);
|
||||
xtc_track_fx_destroy(p->fx);
|
||||
free(p);
|
||||
}
|
||||
|
||||
|
|
@ -362,6 +366,7 @@ static void segment(xtc_midi_plan *p, uint64_t position, float *out, float *righ
|
|||
if (distance < p->fade_frames)
|
||||
sample *= (float)distance / (float)p->fade_frames;
|
||||
}
|
||||
if(xtc_track_fx_add(p->fx,s->track,(size_t)(position-p->monitor_position)+j,sample))continue;
|
||||
if (p->monitor) xtc_mix_monitor(&p->mix, p->monitor, p->monitor_stride, s->track, position+j,
|
||||
(size_t)(position-p->monitor_position)+j, sample, right != NULL);
|
||||
out[j] += sample * xtc_mix_level(&p->mix, s->track, position + j, right ? 0U : 2U);
|
||||
|
|
@ -445,6 +450,7 @@ static xtc_result instruments(xtc_midi_plan *p, uint64_t position, const xtc_mid
|
|||
if (r != XTC_OK) return r;
|
||||
for (j = 0; j < n; ++j) {
|
||||
float sample = p->scratch[j];
|
||||
if(xtc_track_fx_add(p->fx,track,offset+j,sample))continue;
|
||||
if (p->monitor) xtc_mix_monitor(&p->mix,p->monitor,p->monitor_stride,track,begin+j,offset+j,sample,right != NULL);
|
||||
out[offset+j] += sample*xtc_mix_level(&p->mix,track,begin+j,right ? 0U : 2U);
|
||||
if (right) right[offset+j] += sample*xtc_mix_level(&p->mix,track,begin+j,1);
|
||||
|
|
@ -486,6 +492,7 @@ xtc_result xtc_midi_plan_render_stereo(xtc_midi_plan *p, uint64_t position, cons
|
|||
return XTC_OK;
|
||||
if (p->next_position != position)
|
||||
xtc_midi_plan_reset(p);
|
||||
xtc_track_fx_begin(p->fx,position,frames);
|
||||
memset(out, 0, frames * sizeof(*out));
|
||||
if (right)
|
||||
memset(right, 0, frames * sizeof(*right));
|
||||
|
|
@ -504,6 +511,8 @@ xtc_result xtc_midi_plan_render_stereo(xtc_midi_plan *p, uint64_t position, cons
|
|||
xtc_midi_plan_reset(p); return r;
|
||||
}
|
||||
}
|
||||
{xtc_result r=xtc_track_fx_finish(p->fx,&p->mix,position,out,right,frames,p->monitor,p->monitor_stride);
|
||||
if(r!=XTC_OK){memset(out,0,frames*sizeof(*out));if(right)memset(right,0,frames*sizeof(*right));xtc_midi_plan_reset(p);return r;}}
|
||||
for (i = 0; i < frames; ++i) {
|
||||
out[i] = xtc_dsp_finite(out[i]);
|
||||
if (right) right[i] = xtc_dsp_finite(right[i]);
|
||||
|
|
|
|||
101
src/mixer/track_fx.c
Normal file
101
src/mixer/track_fx.c
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "track_fx.h"
|
||||
#include "../project/project_internal.h"
|
||||
#include "xtc/xtc_dsp.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
struct xtc_track_fx {
|
||||
xtc_plan *plans[XTC_PROJECT_MAX_TRACKS];
|
||||
float *raw[XTC_PROJECT_MAX_TRACKS];
|
||||
uint64_t next;
|
||||
float scratch[256];
|
||||
};
|
||||
/* Public metadata graph: the graph displayed by the editor is also compiled. */
|
||||
xtc_result xtc_track_chain_graph(const xtc_track_chain *c,xtc_graph **out)
|
||||
{
|
||||
xtc_graph *g=NULL;size_t previous=0,output=0,i;xtc_result r;
|
||||
if(!out || xtc_track_chain_validate(c)!=XTC_OK)return XTC_ERR_INVALID_ARGUMENT;
|
||||
r=xtc_graph_create(64,&g);
|
||||
if(r==XTC_OK)r=xtc_graph_add_gain(g,1,&previous);
|
||||
for(i=0;r==XTC_OK && i<c->count;++i) {
|
||||
const xtc_track_insert *s=c->slots+i;size_t plugin=0,wet=0,dry=0,sum=0;
|
||||
if(!s->enabled || s->wet==0)continue;
|
||||
r=xtc_graph_add_plugin(g,&s->plugin,&plugin);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,previous,plugin);
|
||||
if(s->wet==1){previous=plugin;continue;}
|
||||
if(r==XTC_OK)r=xtc_graph_add_gain(g,s->wet,&wet);
|
||||
if(r==XTC_OK)r=xtc_graph_add_gain(g,1-s->wet,&dry);
|
||||
if(r==XTC_OK)r=xtc_graph_add_gain(g,1,&sum);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,plugin,wet);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,previous,dry);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,wet,sum);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,dry,sum);
|
||||
previous=sum;
|
||||
}
|
||||
if(r==XTC_OK)r=xtc_graph_add_gain(g,c->output_gain,&output);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,previous,output);
|
||||
previous=output;
|
||||
if(r==XTC_OK)r=xtc_graph_add_output(g,&output);
|
||||
if(r==XTC_OK)r=xtc_graph_connect(g,previous,output);
|
||||
if(r!=XTC_OK){xtc_graph_destroy(g);return r;}
|
||||
*out=g;return XTC_OK;
|
||||
}
|
||||
void xtc_track_fx_destroy(xtc_track_fx *f)
|
||||
{
|
||||
size_t i;if(!f)return;
|
||||
for(i=0;i<XTC_PROJECT_MAX_TRACKS;++i){xtc_plan_destroy(f->plans[i]);free(f->raw[i]);}
|
||||
free(f);
|
||||
}
|
||||
xtc_result xtc_track_fx_create(const xtc_project *p,unsigned int rate,int midi,xtc_track_fx **out)
|
||||
{
|
||||
size_t i,j;xtc_track_fx *f=NULL;xtc_result r=XTC_OK;int needed=0;
|
||||
if(!p || !out || !rate)return XTC_ERR_INVALID_ARGUMENT;
|
||||
*out=NULL;
|
||||
for(i=0;i<p->track_count;++i)if(p->chains[i].count || p->chains[i].output_gain!=1)needed=1;
|
||||
if(!needed)return XTC_OK;
|
||||
f=calloc(1,sizeof(*f));if(!f)return XTC_ERR_OUT_OF_MEMORY;f->next=UINT64_MAX;
|
||||
for(i=0;r==XTC_OK && i<p->track_count;++i) {
|
||||
const xtc_track_chain *c=p->chains+i;xtc_graph *g=NULL;
|
||||
int has_midi=p->instruments[i].instrument || p->live_instrument==p->tracks[i].id,has_audio=0;
|
||||
if(!c->count && c->output_gain==1)continue;
|
||||
for(j=0;j<p->clip_count;++j)if(p->clips[j].track_id==p->tracks[i].id) {
|
||||
if(p->clips[j].midi)has_midi=1;else if(p->clips[j].asset_path[0])has_audio=1;
|
||||
}
|
||||
if(p->tracks[i].is_bus || (has_midi && has_audio)){r=XTC_ERR_UNSUPPORTED;break;}
|
||||
if(has_midi!=midi)continue;
|
||||
r=xtc_track_chain_graph(c,&g);
|
||||
if(r==XTC_OK)r=xtc_graph_compile(g,rate,256,&f->plans[i]);
|
||||
xtc_graph_destroy(g);
|
||||
if(r==XTC_OK){f->raw[i]=calloc(65536,sizeof(float));if(!f->raw[i])r=XTC_ERR_OUT_OF_MEMORY;}
|
||||
}
|
||||
if(r!=XTC_OK){xtc_track_fx_destroy(f);return r;}
|
||||
*out=f;return XTC_OK;
|
||||
}
|
||||
void xtc_track_fx_begin(xtc_track_fx *f,uint64_t position,size_t frames)
|
||||
{
|
||||
size_t i;if(!f)return;
|
||||
for(i=0;i<XTC_PROJECT_MAX_TRACKS;++i)if(f->raw[i]) {
|
||||
memset(f->raw[i],0,frames*sizeof(float));
|
||||
if(f->next!=position)xtc_plan_reset_history(f->plans[i]);
|
||||
}
|
||||
}
|
||||
int xtc_track_fx_add(xtc_track_fx *f,size_t track,size_t frame,float sample)
|
||||
{
|
||||
if(!f || track>=XTC_PROJECT_MAX_TRACKS || !f->raw[track] || frame>=65536)return 0;
|
||||
f->raw[track][frame]+=sample;return 1;
|
||||
}
|
||||
xtc_result xtc_track_fx_finish(xtc_track_fx *f,const xtc_mix_snapshot *mix,uint64_t position,float *left,float *right,size_t frames,float *monitor,size_t stride)
|
||||
{
|
||||
size_t i,offset,j;if(!f)return XTC_OK;
|
||||
for(i=0;i<mix->count;++i)if(f->raw[i])for(offset=0;offset<frames;offset+=256) {
|
||||
size_t n=frames-offset<256 ? frames-offset : 256;xtc_result r=xtc_plan_position(f->plans[i],position+offset);
|
||||
if(r==XTC_OK)r=xtc_plan_process(f->plans[i],f->raw[i]+offset,f->scratch,n);
|
||||
if(r!=XTC_OK)return r;
|
||||
for(j=0;j<n;++j) {
|
||||
size_t frame=offset+j;float sample=xtc_dsp_finite(f->scratch[j]);
|
||||
if(monitor)xtc_mix_monitor(mix,monitor,stride,i,position+frame,frame,sample,right!=NULL);
|
||||
left[frame]+=sample*xtc_mix_level(mix,i,position+frame,right ? 0U : 2U);
|
||||
if(right)right[frame]+=sample*xtc_mix_level(mix,i,position+frame,1);
|
||||
}
|
||||
}
|
||||
f->next=position+frames;return XTC_OK;
|
||||
}
|
||||
12
src/mixer/track_fx.h
Normal file
12
src/mixer/track_fx.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef XTC_TRACK_FX_H
|
||||
#define XTC_TRACK_FX_H
|
||||
#include "mix_internal.h"
|
||||
typedef struct xtc_track_fx xtc_track_fx;
|
||||
/* Separate renderer owners; mixed audio+MIDI tracks with inserts reject. */
|
||||
xtc_result xtc_track_fx_create(const xtc_project *project,unsigned int rate,int midi,xtc_track_fx **out);
|
||||
void xtc_track_fx_destroy(xtc_track_fx *fx);
|
||||
void xtc_track_fx_begin(xtc_track_fx *fx,uint64_t position,size_t frames);
|
||||
/* Returns nonzero when accumulated, otherwise caller uses legacy mix path. */
|
||||
int xtc_track_fx_add(xtc_track_fx *fx,size_t track,size_t frame,float sample);
|
||||
xtc_result xtc_track_fx_finish(xtc_track_fx *fx,const xtc_mix_snapshot *mix,uint64_t position,float *left,float *right,size_t frames,float *monitor,size_t stride);
|
||||
#endif
|
||||
|
|
@ -294,8 +294,37 @@ static void sample_failures(void)
|
|||
}
|
||||
xtc_sample_destroy(source);xtc_project_destroy(p);assert(rmdir(root)==0);
|
||||
}
|
||||
static void track_insert_failures(void)
|
||||
{
|
||||
xtc_project *p=NULL;xtc_track_chain c={0};uint64_t t,clip;int mode,failure;
|
||||
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Track",&t)==XTC_OK);
|
||||
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=1;c.slots[0].wet=.5f;c.slots[0].enabled=1;
|
||||
assert(xtc_project_set_chain(p,t,&c)==XTC_OK);
|
||||
for(mode=0;mode<2;++mode) {
|
||||
int succeeded=0;
|
||||
if(mode)assert(xtc_project_add_midi_clip(p,t,0,960,&clip)==XTC_OK);
|
||||
for(failure=0;failure<128;++failure) {
|
||||
xtc_audio_plan *a=NULL;xtc_midi_plan *m=NULL;float out[512];xtc_result r;
|
||||
fail_after=failure;
|
||||
r=mode ? xtc_midi_plan_create(p,48000,&m) : xtc_audio_plan_create(p,48000,&a);
|
||||
fail_after=-1;
|
||||
if(r==XTC_OK) {
|
||||
processing=1;
|
||||
if(mode)assert(xtc_midi_plan_render(m,0,NULL,0,out,512)==XTC_OK);
|
||||
else assert(xtc_audio_plan_render(a,0,out,512)==XTC_OK);
|
||||
processing=0;succeeded=1;
|
||||
}else assert(r==XTC_ERR_OUT_OF_MEMORY && !a && !m);
|
||||
xtc_audio_plan_destroy(a);xtc_midi_plan_destroy(m);
|
||||
if(succeeded)break;
|
||||
}
|
||||
assert(succeeded);
|
||||
}
|
||||
xtc_project_destroy(p);
|
||||
}
|
||||
int main(void)
|
||||
{
|
||||
track_insert_failures();
|
||||
sample_failures();
|
||||
composition_failures();
|
||||
modern_edit_failures();
|
||||
|
|
|
|||
|
|
@ -1,14 +1,49 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "xtc/xtc_editing.h"
|
||||
#include "xtc/xtc_audio.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
static void rendering(void)
|
||||
{
|
||||
xtc_project *p=NULL;xtc_audio_plan *audio=NULL;xtc_midi_plan *midi=NULL;
|
||||
xtc_track_chain c={0};xtc_recorder *rec=NULL;uint64_t track,clip,copy,note;
|
||||
char wave[]="/tmp/xtc-fx-audio-XXXXXX";float impulse[32]={1},out[32],right[32];int fd=mkstemp(wave);assert(fd>=0);close(fd);unlink(wave);
|
||||
assert(xtc_recorder_create(wave,8000,32,&rec)==XTC_OK);xtc_recorder_push(rec,impulse,32);assert(xtc_recorder_finish(rec)==XTC_OK);xtc_recorder_destroy(rec);
|
||||
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"Audio",&track)==XTC_OK);
|
||||
assert(xtc_project_import_audio(p,track,wave,0,&clip)==XTC_OK);
|
||||
assert(xtc_project_duplicate_clip(p,clip,track,0,©)==XTC_OK);
|
||||
assert(xtc_plugin_probe_format(XTC_PLUGIN_BUILTIN,XTC_BUILTIN_PATH,2,8000,&c.slots[0].plugin)==XTC_OK);
|
||||
c.count=1;c.output_gain=.5f;c.slots[0].enabled=1;c.slots[0].wet=1;
|
||||
c.slots[0].plugin.values[0]=1;c.slots[0].plugin.values[1]=0;c.slots[0].plugin.values[2]=1;
|
||||
assert(xtc_project_set_chain(p,track,&c)==XTC_OK && xtc_audio_plan_create(p,8000,&audio)==XTC_OK);
|
||||
assert(xtc_audio_plan_set_fade_frames(audio,0)==XTC_OK && xtc_audio_plan_render_stereo(audio,0,out,right,32)==XTC_OK);
|
||||
for(size_t j=0;j<32;++j)assert(fabsf(out[j]-(j==8 ? 1.f : 0.f))<.00001f && right[j]==out[j]);
|
||||
/* Discontinuity resets internal delay, and block boundaries preserve history. */
|
||||
assert(xtc_audio_plan_render(audio,0,out,4)==XTC_OK && out[0]==0);
|
||||
assert(xtc_audio_plan_render(audio,4,out,12)==XTC_OK && fabsf(out[4]-1)<.00001f);
|
||||
xtc_audio_plan_destroy(audio);audio=NULL;
|
||||
c.slots[0].enabled=0;assert(xtc_project_set_chain(p,track,&c)==XTC_OK && xtc_audio_plan_create(p,8000,&audio)==XTC_OK);
|
||||
assert(xtc_audio_plan_set_fade_frames(audio,0)==XTC_OK && xtc_audio_plan_render(audio,0,out,32)==XTC_OK && out[0]==1 && out[8]==0);
|
||||
xtc_audio_plan_destroy(audio);audio=NULL;
|
||||
/* Reject split ownership of a nonlinear chain on a hybrid track. */
|
||||
assert(xtc_project_add_midi_clip(p,track,0,960,&clip)==XTC_OK);
|
||||
assert(xtc_audio_plan_create(p,8000,&audio)==XTC_ERR_UNSUPPORTED && audio==NULL);
|
||||
xtc_project_destroy(p);p=NULL;
|
||||
assert(xtc_project_create(&p)==XTC_OK && xtc_project_add_track(p,"MIDI",&track)==XTC_OK && xtc_project_add_midi_clip(p,track,0,960,&clip)==XTC_OK);
|
||||
assert(xtc_project_add_note(p,clip,0,960,69,127,0,¬e)==XTC_OK);
|
||||
c.slots[0].enabled=1;assert(xtc_project_set_chain(p,track,&c)==XTC_OK && xtc_midi_plan_create(p,8000,&midi)==XTC_OK);
|
||||
assert(xtc_midi_plan_set_fade_frames(midi,0)==XTC_OK && xtc_midi_plan_render(midi,0,NULL,0,out,32)==XTC_OK);
|
||||
for(size_t j=0;j<32;++j)assert(fabsf(out[j]-(j<8 ? 0.f : .05f*sinf(6.28318530718f*440.f*(float)(j-8)/8000.f)))<.00001f);
|
||||
xtc_midi_plan_destroy(midi);xtc_project_destroy(p);unlink(wave);
|
||||
}
|
||||
int main(void)
|
||||
{
|
||||
rendering();
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue