test: qualify internal devices across DSP, persistence and GUI workflows
This commit is contained in:
parent
f6dcf207ce
commit
e76552e8dd
7 changed files with 389 additions and 8 deletions
7
Makefile
7
Makefile
|
|
@ -527,3 +527,10 @@ $(BUILD_DIR)/test_builtin: tests/unit/test_builtin.c $(BUILD_DIR)/libxtc.a
|
|||
test: test-builtins
|
||||
test-builtins: $(BUILD_DIR)/test_builtin
|
||||
./$(BUILD_DIR)/test_builtin
|
||||
|
||||
$(BUILD_DIR)/builtin-preview: $(BUILD_DIR)/examples/builtin_preview.o $(BUILD_DIR)/libxtc.a
|
||||
$(CC) $(LDFLAGS) -o $@ $^ -lm -ldl
|
||||
.PHONY: builtin-preview
|
||||
builtin-preview: $(BUILD_DIR)/builtin-preview
|
||||
./$(BUILD_DIR)/builtin-preview "$(abspath $(BUILD_DIR))/builtin"
|
||||
-include $(BUILD_DIR)/examples/builtin_preview.d
|
||||
|
|
|
|||
|
|
@ -39,3 +39,21 @@ Validation: `make BUILD_DIR=build/phase38 all gui test test-gui-instruments
|
|||
test-gui-modern-plugins test-gui-plugins analyze` (join onto one line).
|
||||
`test-builtins` checks impulse tails, dry mix, feedback bounds, block partition
|
||||
consistency, note release/panic and instrument/effect project round trips.
|
||||
|
||||
## Phase 40 acceptance checks
|
||||
|
||||
`make BUILD_DIR=build/phase40 all gui test test-reliability test-gui builtin-preview`
|
||||
checks all four device identities and GUI paths without external plugin binaries.
|
||||
The reliability runner fails each internal preparation allocation in turn and
|
||||
forbids allocation/free, direct file I/O and blocking locks during processing.
|
||||
Core tests compare persisted rendering for both instruments and both automated
|
||||
effects, corrupt an internal module identity transactionally, and cover voice
|
||||
exhaustion, sustain, repeated notes, partitioning and transport reset/seek/panic.
|
||||
The existing portability suite verifies older project migrations.
|
||||
|
||||
For repeatable manual listening, run `make builtin-preview` and audition
|
||||
`build/builtin-{0,1,2,3}.wav` at a conservative level: 0 is Soft Keys, 1 Sub Bass,
|
||||
2 Delay and 3 Reverb. Each six-second file includes held tones or two impulses
|
||||
followed by silence for tails. Check onset/release, unwanted discontinuities,
|
||||
finite decay and the documented mono character. Generation is verified;
|
||||
human listening and physical-device qualification remain pending.
|
||||
|
|
|
|||
183
docs/ROADMAP.md
183
docs/ROADMAP.md
|
|
@ -1073,6 +1073,8 @@ They require separate model/layout designs and acceptance tests.
|
|||
|
||||
## Phase 38 — Internal instruments and effects
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
|
||||
- [x] Add always-available Soft Keys and Sub Bass MIDI instrument presets.
|
||||
- [x] Add Delay and Reverb with bounded controls and effect automation.
|
||||
- [x] Integrate track selection, graph wire insertion, undo and saved projects.
|
||||
|
|
@ -1080,19 +1082,196 @@ They require separate model/layout designs and acceptance tests.
|
|||
- [x] Verify DSP tails, block partition consistency, note release/panic and persistence.
|
||||
- [x] Refresh application screenshots and add internal device editor views.
|
||||
|
||||
See [BUILTIN_DEVICES.md](BUILTIN_DEVICES.md) for controls and explicit limitations.
|
||||
### Implementation status
|
||||
|
||||
Implemented for the bounded initial device set. Audited 2026-11-05 against
|
||||
`src/plugin/builtin.c`, the shared plugin host, track instrument selector,
|
||||
graph commands and v13 persistence. Soft Keys and Sub Bass are fixed 32-voice
|
||||
presets; Delay and Reverb each expose three bounded, automatable controls.
|
||||
Preparation owns buffer allocation; processing uses preallocated storage.
|
||||
Internal devices use virtual identities rather than loading external modules;
|
||||
the reader retains v1–v12 compatibility. Actual device control screenshots are
|
||||
listed in [screenshots/README.md](../screenshots/README.md).
|
||||
|
||||
`tests/unit/test_builtin.c` covers both effect tails and block partitions, dry
|
||||
mix, invalid feedback, both instruments' release/panic, mix automation and a
|
||||
keys/delay project round trip. Shared GUI tests cover instrument selection,
|
||||
history and plugin editing, but do not constitute a dedicated end-to-end matrix
|
||||
for all four internal devices. Additional qualification is tracked in Phase 40.
|
||||
|
||||
Missing extensions: editable instrument parameters/presets, pitch bend, sample
|
||||
instruments, click-free/tempo-synced delay, stereo reverb/diffusion, explicit seek
|
||||
history policy and automatic tail rendering. These are planned in Phases 41–44;
|
||||
they are not part of the checked initial subset. See
|
||||
[BUILTIN_DEVICES.md](BUILTIN_DEVICES.md) for controls and explicit limitations.
|
||||
|
||||
---
|
||||
|
||||
## Phase 39 — Detachable transport and mixer
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
|
||||
- [x] Float/dock the existing transport and mixer without duplicating state.
|
||||
- [x] Keep BPM and loop settings in the shared transport controls.
|
||||
- [x] Recover arrangement space and retain resizable mixer channels.
|
||||
- [x] Support close-to-dock, Dock all, floating playback shortcuts and safe shutdown.
|
||||
- [x] Exercise lifecycle, settings and resizing in GUI tests and capture floating views.
|
||||
|
||||
See [WORKSPACE.md](WORKSPACE.md) for usage and session-only layout limitations.
|
||||
### Implementation status
|
||||
|
||||
Implemented for the transport and mixer. Audited 2026-11-05 against
|
||||
`src/gui/main_window.cpp` and `tests/integration/test_gui.cpp`. Detaching
|
||||
reparents the existing widgets on the GUI thread; BPM, loop settings and mixer
|
||||
state remain shared. Docking restores ownership and arrangement space, and the
|
||||
destructor docks panels before destroying their windows. Tests exercise repeated
|
||||
detach/dock, close-to-dock, shared BPM/loop state, floating playback shortcuts,
|
||||
main/floating resizing and failed/successful shutdown. Floating screenshots are
|
||||
listed in [screenshots/README.md](../screenshots/README.md).
|
||||
|
||||
Layout and window positions are session-only. The floating transport requires
|
||||
1220 pixels of width; mixer minimum size is 600 × 420. Other editors cannot be
|
||||
detached, and general dock layouts are absent. Persistence, compact transport
|
||||
and additional panels are tracked in Phases 45–46. See
|
||||
[WORKSPACE.md](WORKSPACE.md) for usage and limits.
|
||||
|
||||
### Audit validation (Phases 38–39)
|
||||
|
||||
Warning-enabled `make BUILD_DIR=build/roadmap-audit all gui test` passed,
|
||||
including `test-builtins`. Sequential GUI runs with the same build passed:
|
||||
`test-gui`, `test-gui-instruments`, `test-gui-modern-plugins` and
|
||||
`test-gui-plugins`. The initial parallel GUI invocation reported passing test
|
||||
assertions but failed Xvfb cleanup; sequential reruns exited successfully.
|
||||
Historical sanitizer/live-JACK evidence in WORKSPACE.md is separate from this
|
||||
audit; sanitizers, live JACK and physical listening were not rerun here.
|
||||
|
||||
---
|
||||
|
||||
## Phase 40 — Internal Device Acceptance Coverage
|
||||
|
||||
### Goal
|
||||
Close the dedicated validation gaps in the initial Phase 38 device set.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [x] Exercise all four internal devices through GUI selection/addition, wire insertion where applicable, control editing, undo/redo and save/reopen.
|
||||
- [x] Round-trip both instruments and both effects, including automated controls, and compare rendered output.
|
||||
- [x] Verify older-format migration alongside v13 identities and reject malformed internal identities transactionally.
|
||||
- [x] Test sustain, repeated notes, voice exhaustion, seek/stop/panic and instrument block partition consistency.
|
||||
- [x] Cover preparation/allocation failures and callback allocation/free guards for internal devices.
|
||||
- [x] Document repeatable manual listening checks for both presets and representative effect settings.
|
||||
|
||||
### Implementation status
|
||||
Implemented: dedicated all-device GUI/history/persistence tests, exact persisted
|
||||
render comparisons, malformed-identity rollback, voice/transport regressions and
|
||||
allocation-failure/callback guards. Warning-enabled core tests, reliability, GUI
|
||||
and listening-preview generation pass. See BUILTIN_DEVICES.md for repeatable
|
||||
manual checks; human listening and physical hardware remain unverified.
|
||||
|
||||
---
|
||||
|
||||
## Phase 41 — Editable Internal Instruments and Presets
|
||||
|
||||
### Goal
|
||||
Extend the fixed Soft Keys/Sub Bass presets into editable instruments.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Define bounded oscillator, envelope and filter controls with metadata and defaults that preserve existing preset sounds.
|
||||
- [ ] Provide instrument parameter editing, automation and reusable preset save/load through public C APIs.
|
||||
- [ ] Support channel pitch bend with a documented range and sustain/note lifecycle behavior.
|
||||
- [ ] Persist parameters and presets with explicit version migration, undo and failed-preparation rollback.
|
||||
- [ ] Verify sample timing, block partition consistency, bounds, aliasing behavior and callback safety.
|
||||
|
||||
### Implementation status
|
||||
Planned. The current instruments expose no editable parameters or pitch bend.
|
||||
|
||||
---
|
||||
|
||||
## Phase 42 — Internal Sample Instrument
|
||||
|
||||
### Goal
|
||||
Provide sample-based sounds beyond the current oscillator presets.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Define supported sample formats, mapping, looping, envelopes, voice limits and memory budgets.
|
||||
- [ ] Load/prepare samples outside callbacks and expose an instrument editor with undo.
|
||||
- [ ] Persist sample references and mappings with missing-media reporting and relinking.
|
||||
- [ ] Provide original or appropriately licensed example sounds and document their licenses.
|
||||
- [ ] Test playback rate conversion, boundaries, voice release, missing assets and project reload.
|
||||
|
||||
### Implementation status
|
||||
Planned. Phase 38 supplies no sample instrument or sample library.
|
||||
|
||||
---
|
||||
|
||||
## Phase 43 — Smooth Delay and Stereo Reverb
|
||||
|
||||
### Goal
|
||||
Address the documented sound-design and transition limits of the basic effects.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Smooth or crossfade delay-time changes and define automation behavior without abrupt read-head jumps.
|
||||
- [ ] Add tempo-synced delay divisions with deterministic tempo/seek behavior.
|
||||
- [ ] Add true stereo processing and reverb diffusion/room controls rather than independent mono instances alone.
|
||||
- [ ] Preserve old projects' effect identities, parameters and sound, or provide an explicit compatibility mode.
|
||||
- [ ] Test stability at parameter extremes, stereo response, automation partitions and transition artifacts; document listening results.
|
||||
|
||||
### Implementation status
|
||||
Planned. Delay time can click; reverb uses four damped mono combs without a room
|
||||
model or stereo diffusion. Current decay is a feedback coefficient, not seconds.
|
||||
|
||||
---
|
||||
|
||||
## Phase 44 — Effect History and Tail Rendering
|
||||
|
||||
### Goal
|
||||
Make discontinuities and effect tails explicit in playback and export.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Define retained/reset effect history for seek, stop, loop wrap and project/plan replacement.
|
||||
- [ ] Implement the chosen history policy without callback allocation or unsafe state sharing.
|
||||
- [ ] Add bounded automatic or user-configurable tail rendering after the arrangement/export endpoint.
|
||||
- [ ] Expose tail limits and cancellation, including silent input and non-decaying/high-feedback cases.
|
||||
- [ ] Test seek/loop reproducibility and complete delay/reverb tails across processing block sizes.
|
||||
|
||||
### Implementation status
|
||||
Planned. Seeks currently retain effect history; tails need continued rendering
|
||||
and do not automatically extend playback/export.
|
||||
|
||||
---
|
||||
|
||||
## Phase 45 — Persistent Panel Layouts and Compact Transport
|
||||
|
||||
### Goal
|
||||
Retain useful floating layouts across sessions and support narrower displays.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Persist docked/floating state and panel geometry through the application preferences workflow.
|
||||
- [ ] Restore layouts safely when monitors or display sizes change; keep all panels reachable.
|
||||
- [ ] Provide a compact/responsive floating transport while retaining BPM, loop and playback controls.
|
||||
- [ ] Preserve close-to-dock, Dock all, shortcuts, editing focus protection and safe shutdown.
|
||||
- [ ] Test restart, invalid settings, off-screen geometry, narrow sizes and repeated dock/resize cycles.
|
||||
|
||||
### Implementation status
|
||||
Planned. Phase 39 starts docked each session and its transport minimum is 1220 pixels.
|
||||
|
||||
---
|
||||
|
||||
## Phase 46 — Additional Dockable Editors
|
||||
|
||||
### Goal
|
||||
Extend Phase 39's two-panel arrangement to a general editor workspace.
|
||||
|
||||
### Tasks and acceptance criteria
|
||||
- [ ] Define supported dock locations and layout ownership for sequencer, piano roll and modular graph panels.
|
||||
- [ ] Detach/dock each supported editor without duplicating project, selection or history state.
|
||||
- [ ] Support resizable pinned track headers and preserve editing coordinates, scrolling and focus after reparenting.
|
||||
- [ ] Integrate with Phase 45 layout persistence and provide a safe reset-layout action.
|
||||
- [ ] Verify minimum/large layouts, cross-panel editing, repeated reparenting and shutdown/error paths.
|
||||
|
||||
### Implementation status
|
||||
Planned. Phase 39 only detaches transport and mixer; broader docking and
|
||||
resizable pinned headers from Phase 37 remain outstanding. These follow-up
|
||||
phases cover gaps found in Phases 38–39 and their documented limitations;
|
||||
unrelated outstanding features in earlier phases retain their existing status.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
40
examples/builtin_preview.c
Normal file
40
examples/builtin_preview.c
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* Repeatable internal-device listening material; never opens audio hardware. */
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "xtc/xtc_audio.h"
|
||||
#include "xtc/xtc_midi.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
static xtc_result preview(const char *prefix, unsigned int id)
|
||||
{
|
||||
xtc_plugin_spec spec; xtc_plugin *plugin=NULL;xtc_recorder *recorder=NULL;
|
||||
char path[1024];size_t block;xtc_result result;
|
||||
int n=snprintf(path,sizeof(path),"%s-%u.wav",prefix,id);
|
||||
if(n<0 || (size_t)n>=sizeof(path))return XTC_ERR_CAPACITY;
|
||||
result=xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&spec);
|
||||
if(result==XTC_OK)result=xtc_plugin_create(&spec,48000,256,&plugin);
|
||||
if(result==XTC_OK)result=xtc_recorder_create_replace(path,48000,256,1,&recorder);
|
||||
for(block=0;result==XTC_OK && block<1125;++block) {
|
||||
float input[256]={0},output[256];xtc_midi_event event={0,XTC_MIDI_NOTE_ON,0,60,100};
|
||||
if(id<2) {
|
||||
size_t count=0;
|
||||
if(block==0 || block==94 || block==188) {event.pitch=block==0 ? 48U : block==94 ? 60U : 67U;count=1;}
|
||||
if(block==282) {event.type=XTC_MIDI_CONTROL;event.pitch=123;event.velocity=0;count=1;}
|
||||
result=xtc_plugin_process_midi(plugin,&event,count,output,256);
|
||||
} else {
|
||||
if(block==0 || block==94) input[0]=.5f;
|
||||
result=xtc_plugin_process(plugin,input,output,256);
|
||||
}
|
||||
if(result==XTC_OK) {xtc_recorder_push(recorder,output,256);result=xtc_recorder_drain(recorder);}
|
||||
}
|
||||
if(result==XTC_OK)result=xtc_recorder_finish(recorder);
|
||||
xtc_recorder_destroy(recorder);xtc_plugin_destroy(plugin);
|
||||
if(result==XTC_OK)printf("Wrote %s\n",path);
|
||||
return result;
|
||||
}
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
unsigned int id;
|
||||
if(argc!=2) {fprintf(stderr,"Usage: %s OUTPUT_PREFIX\n",argv[0]);return EXIT_FAILURE;}
|
||||
for(id=0;id<4;++id) if(preview(argv[1],id)!=XTC_OK)return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -712,6 +712,41 @@ int main(int argc, char **argv)
|
|||
CHECK(!canvas->select_at(-20,-20) && canvas->selected_node() == SIZE_MAX);
|
||||
window.do_callback();
|
||||
}
|
||||
// Internal devices must work without plugin fixture environment variables.
|
||||
for (unsigned int instrument=0;instrument<2;++instrument) {
|
||||
reset(); MainWindow window(false,""); window.show();
|
||||
CHECK(window.create_track(true)==XTC_OK);
|
||||
auto *sequence=timeline(&window); auto *canvas=graph_canvas(&window);
|
||||
uint64_t track=sequence->selected_track();
|
||||
auto *selector=sequence->instrument_choice(track); CHECK(selector);
|
||||
selector->value(static_cast<int>(instrument+1));selector->do_callback();
|
||||
xtc_plugin_spec spec{};
|
||||
CHECK(xtc_project_get_instrument(sequence->project(),track,&spec)==XTC_OK && spec.index==instrument);
|
||||
CHECK(window.undo()==XTC_OK && window.undo(true)==XTC_OK);
|
||||
xtc_connection_info wire{};
|
||||
CHECK(xtc_graph_get_connection(xtc_project_graph(sequence->project()),0,&wire)==XTC_OK);
|
||||
unsigned int effect=instrument+2;size_t id=SIZE_MAX;
|
||||
CHECK(window.insert_plugin(wire,XTC_BUILTIN_PATH,effect,&id)==XTC_OK);
|
||||
CHECK(canvas->select_node(id));
|
||||
CHECK(window.set_plugin_control(id,2,.75f)==XTC_OK);
|
||||
CHECK(window.set_plugin_automation(id,{960,2,0,.4f},false)==XTC_OK);
|
||||
CHECK(window.undo()==XTC_OK && window.undo(true)==XTC_OK);
|
||||
CHECK(xtc_graph_get_plugin(xtc_project_graph(sequence->project()),id,&spec)==XTC_OK);
|
||||
CHECK(spec.index==effect && spec.values[2]==.75f && spec.point_count==1);
|
||||
xtc_plugin_details details{};xtc_plugin_spec edited{};
|
||||
CHECK(xtc_plugin_describe(&spec,48000,&details)==XTC_OK);
|
||||
Fl::add_timeout(.05,[](void *) {press(find(Fl::first_window(),"Apply"));});
|
||||
CHECK(xtc_edit_plugin(spec,details,edited) && edited.index==effect);
|
||||
CHECK(window.save_project("/tmp/xtc-internal-gui.xtc")==XTC_OK);
|
||||
CHECK(window.new_project()==XTC_OK && window.open_project("/tmp/xtc-internal-gui.xtc")==XTC_OK);
|
||||
CHECK(xtc_project_get_instrument(sequence->project(),track,&spec)==XTC_OK && spec.index==instrument);
|
||||
CHECK(xtc_graph_get_plugin(xtc_project_graph(sequence->project()),id,&spec)==XTC_OK && spec.index==effect && spec.point_count==1);
|
||||
size_t added=SIZE_MAX;CHECK(window.add_plugin(XTC_BUILTIN_PATH,effect,&added)==XTC_OK);
|
||||
CHECK(added!=id);CHECK(window.undo()==XTC_OK);
|
||||
const xtc_project *before=sequence->project();reserve_result=XTC_ERR_OUT_OF_MEMORY;
|
||||
CHECK(window.add_plugin(XTC_BUILTIN_PATH,effect)==XTC_ERR_OUT_OF_MEMORY && sequence->project()==before);
|
||||
reserve_result=XTC_OK;unlink("/tmp/xtc-internal-gui.xtc");window.do_callback();
|
||||
}
|
||||
if (const char *module = std::getenv("XTC_TEST_PLUGIN")) {
|
||||
reset();
|
||||
MainWindow window(false,"");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "xtc/xtc_realtime.h"
|
||||
#include "xtc/xtc_native.h"
|
||||
#include "xtc/xtc_control.h"
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -152,8 +153,29 @@ static void history_reservations(void)
|
|||
xtc_project_destroy(snapshot); xtc_project_destroy(project); xtc_history_destroy(history);
|
||||
}
|
||||
|
||||
static void builtin_failures(void)
|
||||
{
|
||||
unsigned int id;
|
||||
for(id=0;id<4;++id) {
|
||||
int failure, succeeded=0; xtc_plugin_spec spec;
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&spec)==XTC_OK);
|
||||
for(failure=0;failure<16;++failure) {
|
||||
xtc_plugin *plugin=NULL; xtc_result result; float in[256]={0},out[256];
|
||||
fail_after=failure;result=xtc_plugin_create(&spec,48000,256,&plugin);fail_after=-1;
|
||||
if(result!=XTC_OK) {assert(result==XTC_ERR_OUT_OF_MEMORY && !plugin);continue;}
|
||||
processing=1;
|
||||
if(id<2) {xtc_midi_event event={0,XTC_MIDI_NOTE_ON,0,60,100};
|
||||
assert(xtc_plugin_process_midi(plugin,&event,1,out,256)==XTC_OK);
|
||||
} else assert(xtc_plugin_process(plugin,in,out,256)==XTC_OK);
|
||||
processing=0;xtc_plugin_destroy(plugin);succeeded=1;break;
|
||||
}
|
||||
assert(succeeded);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
builtin_failures();
|
||||
graph_limits();
|
||||
history_reservations();
|
||||
xtc_native_descriptor descriptor = {XTC_NATIVE_ABI_VERSION, sizeof(descriptor),
|
||||
|
|
|
|||
|
|
@ -53,21 +53,101 @@ static void automation(void)
|
|||
for(i=0;i<128;++i) assert(fabsf(out[i]-(i<100 ? 1-(float)i/100 : 0))<1e-6f);
|
||||
xtc_plugin_destroy(p);
|
||||
}
|
||||
static void roundtrip(void)
|
||||
static void roundtrip(unsigned int instrument, unsigned int effect)
|
||||
{
|
||||
xtc_project *p=NULL,*q=NULL; xtc_plugin_spec s,t; uint64_t track; size_t id; xtc_graph *g=NULL;
|
||||
xtc_plugin *a=NULL,*b=NULL; float input[256]={0},x[256],y[256]; size_t k;
|
||||
char path[]="/tmp/xtc-builtin-XXXXXX"; int fd=mkstemp(path); assert(fd>=0); close(fd);
|
||||
assert(xtc_project_create(&p)==XTC_OK); assert(xtc_project_create(&q)==XTC_OK);
|
||||
assert(xtc_project_add_track(p,"Keys",&track)==XTC_OK);
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,0,48000,&s)==XTC_OK);
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,instrument,48000,&s)==XTC_OK);
|
||||
assert(xtc_project_set_instrument(p,track,&s)==XTC_OK);
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,2,48000,&s)==XTC_OK); s.values[0]=250;
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,effect,48000,&s)==XTC_OK);
|
||||
s.point_count=2; s.points[0]=(xtc_plugin_point){0,2,0,.1f}; s.points[1]=(xtc_plugin_point){960,2,0,.6f};
|
||||
assert(xtc_graph_clone(xtc_project_graph(p),&g)==XTC_OK);
|
||||
assert(xtc_graph_add_plugin(g,&s,&id)==XTC_OK); assert(xtc_project_set_graph(p,g)==XTC_OK);xtc_graph_destroy(g);
|
||||
assert(xtc_project_save(p,path)==XTC_OK); assert(xtc_project_load(q,path)==XTC_OK);
|
||||
assert(xtc_graph_get_plugin(xtc_project_graph(q),id,&t)==XTC_OK); assert(t.values[0]==250 && t.point_count==2);
|
||||
assert(xtc_project_get_instrument(q,track,&t)==XTC_OK && t.index==0 && t.format==XTC_PLUGIN_BUILTIN);
|
||||
assert(xtc_graph_get_plugin(xtc_project_graph(q),id,&t)==XTC_OK); assert(!memcmp(&s,&t,sizeof(s)));
|
||||
assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&t,48000,256,&b)==XTC_OK);
|
||||
for(k=0;k<256;++k) {
|
||||
input[0]=k==0 ? 1 : 0;
|
||||
assert(xtc_plugin_process(a,input,x,256)==XTC_OK && xtc_plugin_process(b,input,y,256)==XTC_OK);
|
||||
assert(!memcmp(x,y,sizeof(x)));
|
||||
}
|
||||
xtc_plugin_destroy(a);xtc_plugin_destroy(b);
|
||||
assert(xtc_project_get_instrument(q,track,&t)==XTC_OK && t.index==instrument && t.format==XTC_PLUGIN_BUILTIN);
|
||||
assert(xtc_project_get_instrument(p,track,&s)==XTC_OK);
|
||||
assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&t,48000,256,&b)==XTC_OK);
|
||||
{ xtc_midi_event e={19,XTC_MIDI_NOTE_ON,0,60,100};
|
||||
assert(xtc_plugin_process_midi(a,&e,1,x,256)==XTC_OK && xtc_plugin_process_midi(b,&e,1,y,256)==XTC_OK);
|
||||
assert(!memcmp(x,y,sizeof(x))); }
|
||||
xtc_plugin_destroy(a);xtc_plugin_destroy(b);
|
||||
/* Corrupt a serialized identity, retaining a valid destination project. */
|
||||
{ FILE *file=fopen(path,"r+"); char data[32768]; size_t n; char *label;
|
||||
assert(file);n=fread(data,1,sizeof(data)-1,file);data[n]=0;
|
||||
label=strstr(data,effect==2 ? "44656c6179" : "526576657262");
|
||||
/* Names are hex encoded; module corruption is independent of name codec. */
|
||||
(void)label; label=strstr(data,"2f7874632f696e7465726e616c");
|
||||
assert(label);label[0]='3';rewind(file);assert(fwrite(data,1,n,file)==n);fclose(file);
|
||||
assert(xtc_project_load(q,path)!=XTC_OK);
|
||||
assert(xtc_project_get_instrument(q,track,&t)==XTC_OK && t.index==instrument); }
|
||||
xtc_project_destroy(p);xtc_project_destroy(q);unlink(path);
|
||||
}
|
||||
int main(void) {automation();roundtrip();effects(2);effects(3);synth(0);synth(1);puts("Built-in devices passed.");return 0;}
|
||||
static void synth_partition(unsigned int id)
|
||||
{
|
||||
xtc_plugin_spec s; xtc_plugin *a=NULL,*b=NULL; float x[256],y[256]; size_t i,k;
|
||||
xtc_midi_event events[40];
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&s)==XTC_OK);
|
||||
assert(xtc_plugin_create(&s,48000,256,&a)==XTC_OK && xtc_plugin_create(&s,48000,256,&b)==XTC_OK);
|
||||
/* Exhaust voices, repeat a key, sustain its note-off, then release pedal. */
|
||||
for(i=0;i<36;++i) events[i]=(xtc_midi_event){(unsigned int)i,XTC_MIDI_NOTE_ON,0,40+(unsigned int)i,100};
|
||||
events[36]=(xtc_midi_event){40,XTC_MIDI_NOTE_ON,0,60,80};
|
||||
events[37]=(xtc_midi_event){50,XTC_MIDI_CONTROL,0,64,127};
|
||||
events[38]=(xtc_midi_event){60,XTC_MIDI_NOTE_OFF,0,60,0};
|
||||
events[39]=(xtc_midi_event){80,XTC_MIDI_CONTROL,0,64,0};
|
||||
assert(xtc_plugin_process_midi(a,events,40,x,256)==XTC_OK);
|
||||
assert(xtc_plugin_process_midi(b,events,40,y,128)==XTC_OK);
|
||||
assert(xtc_plugin_process_midi(b,NULL,0,y+128,128)==XTC_OK);
|
||||
assert(!memcmp(x,y,sizeof(x)));
|
||||
for(k=0;k<100;++k) {
|
||||
assert(xtc_plugin_process_midi(a,NULL,0,x,256)==XTC_OK);
|
||||
assert(xtc_plugin_process_midi(b,NULL,0,y,128)==XTC_OK);
|
||||
assert(xtc_plugin_process_midi(b,NULL,0,y+128,128)==XTC_OK);
|
||||
for(i=0;i<256;++i) assert(isfinite(x[i]) && x[i]==y[i]);
|
||||
}
|
||||
events[0]=(xtc_midi_event){0,XTC_MIDI_ALL_OFF,0,0,0};
|
||||
assert(xtc_plugin_process_midi(a,events,1,x,256)==XTC_OK);
|
||||
for(i=0;i<256;++i) assert(x[i]==0);
|
||||
xtc_plugin_destroy(a);xtc_plugin_destroy(b);
|
||||
}
|
||||
static void transport(unsigned int id)
|
||||
{
|
||||
xtc_project *project=NULL;xtc_midi_plan *plan=NULL,*fresh=NULL;xtc_plugin_spec spec;
|
||||
uint64_t track,clip,note;float x[256],y[256];size_t k;
|
||||
assert(xtc_project_create(&project)==XTC_OK);
|
||||
assert(xtc_project_add_track(project,"Transport",&track)==XTC_OK);
|
||||
assert(xtc_project_add_midi_clip(project,track,0,3840,&clip)==XTC_OK);
|
||||
assert(xtc_project_add_note(project,clip,0,1920,60,100,0,¬e)==XTC_OK);
|
||||
assert(xtc_plugin_probe(XTC_BUILTIN_PATH,id,48000,&spec)==XTC_OK);
|
||||
assert(xtc_project_set_instrument(project,track,&spec)==XTC_OK);
|
||||
assert(xtc_midi_plan_create(project,48000,&plan)==XTC_OK);
|
||||
assert(xtc_midi_plan_create(project,48000,&fresh)==XTC_OK);
|
||||
assert(xtc_midi_plan_render(plan,0,NULL,0,x,256)==XTC_OK);
|
||||
/* Discontinuity chases with a fresh envelope, identical to a fresh plan. */
|
||||
assert(xtc_midi_plan_render(plan,1000,NULL,0,x,256)==XTC_OK);
|
||||
assert(xtc_midi_plan_render(fresh,1000,NULL,0,y,256)==XTC_OK);
|
||||
assert(!memcmp(x,y,sizeof(x)));
|
||||
xtc_midi_plan_panic(plan,1256);
|
||||
for(k=0;k<200;++k)assert(xtc_midi_plan_render(plan,1256+k*256,NULL,0,x,256)==XTC_OK);
|
||||
for(k=0;k<256;++k)assert(fabsf(x[k])<1e-5f);
|
||||
xtc_midi_plan_reset(plan);
|
||||
assert(xtc_midi_plan_render(plan,0,NULL,0,x,256)==XTC_OK);
|
||||
xtc_midi_plan_reset(fresh);
|
||||
assert(xtc_midi_plan_render(fresh,0,NULL,0,y,256)==XTC_OK);
|
||||
assert(!memcmp(x,y,sizeof(x)));
|
||||
xtc_midi_plan_destroy(plan);xtc_midi_plan_destroy(fresh);xtc_project_destroy(project);
|
||||
}
|
||||
int main(void) {
|
||||
transport(0);transport(1);automation();roundtrip(0,2);roundtrip(1,3);effects(2);effects(3);synth(0);synth(1);
|
||||
synth_partition(0);synth_partition(1);puts("Built-in devices passed.");return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue