Phase 26: add common parameter targets, step automation and MIDI control mapping
This commit is contained in:
parent
7bff7b535d
commit
901a536f10
38 changed files with 740 additions and 67 deletions
2
tests/fixtures/README.md
vendored
2
tests/fixtures/README.md
vendored
|
|
@ -3,7 +3,7 @@
|
|||
Store small, redistributable fixtures here. Document their origin and license.
|
||||
Do not commit large recordings or assets with unclear licensing.
|
||||
|
||||
`project-v1.xtc` through `project-v8.xtc` are hand-authored xtc metadata fixtures
|
||||
`project-v1.xtc` through `project-v9.xtc` are hand-authored xtc metadata fixtures
|
||||
created for this repository, under the project license. They contain no audio
|
||||
or third-party resources. The v5/v6 plugin fixtures intentionally reference a
|
||||
missing module to exercise metadata migration without loading code.
|
||||
|
|
|
|||
22
tests/fixtures/project-v9.xtc
vendored
Normal file
22
tests/fixtures/project-v9.xtc
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
XTC_PROJECT 9
|
||||
APP 0 1 0
|
||||
TEMPO 120000
|
||||
IDS 1 1
|
||||
GRAPH 1 2 2 1
|
||||
NODE 0 1 3f000000
|
||||
NODE 1 4 00000000
|
||||
EDGE 0 1
|
||||
TRACKS 1
|
||||
TRACK 0 4b657973
|
||||
MIX 3f800000 00000000 0 0
|
||||
ROUTE 0 18446744073709551615
|
||||
CLIPS 1
|
||||
CLIP 0 0 0 960 0 960 5665727365
|
||||
ASSET -
|
||||
MIDI 1
|
||||
AUDIO 3f800000 0 0
|
||||
NOTES 1 1
|
||||
NOTE 0 0 0 480 69 100 0
|
||||
AUTOMATION 1
|
||||
POINT 0 0 0 3f800000
|
||||
END
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../src/gui/mixer_widget.hpp"
|
||||
#include "../../src/gui/plugin_dialog.hpp"
|
||||
#include <FL/Fl_Hold_Browser.H>
|
||||
#include <FL/Fl_Choice.H>
|
||||
#include <unistd.h>
|
||||
#include <FL/Fl.H>
|
||||
#include <FL/Fl_Box.H>
|
||||
|
|
@ -630,6 +631,31 @@ int main(int argc, char **argv)
|
|||
mixer->reset_meters(); CHECK(!mixer->master_clipped());
|
||||
window.do_callback();
|
||||
}
|
||||
{
|
||||
reset(); MainWindow window(false);
|
||||
auto *sequence = timeline(&window);
|
||||
unsigned int bindings[3] = {1,74,11}, actual[3];
|
||||
CHECK(window.set_control_map(2,bindings) == XTC_OK);
|
||||
CHECK(xtc_project_get_control_map(sequence->project(),2,actual) == XTC_OK && actual[0] == 1);
|
||||
CHECK(window.undo(false) == XTC_OK);
|
||||
CHECK(xtc_project_get_control_map(sequence->project(),2,actual) == XTC_OK && actual[0] == 7);
|
||||
CHECK(window.undo(true) == XTC_OK);
|
||||
const xtc_project *before = sequence->project();
|
||||
bindings[1] = 1;
|
||||
CHECK(window.set_control_map(2,bindings) == XTC_ERR_INVALID_ARGUMENT && sequence->project() == before);
|
||||
auto *mixer_ui = window.mixer();
|
||||
CHECK(sequence->add_track() == XTC_OK);
|
||||
auto *mode = dynamic_cast<Fl_Choice *>(find(&window,"Interpolation")); CHECK(mode);
|
||||
if (mode) mode->value(1);
|
||||
CHECK(mixer_ui->point(0,.25f) == XTC_OK);
|
||||
xtc_automation_point point{};
|
||||
CHECK(xtc_project_get_automation(sequence->project(),0,&point) == XTC_OK && point.step == 1);
|
||||
CHECK(window.save_project("/tmp/xtc-gui-controls-test.xtc") == XTC_OK);
|
||||
CHECK(window.new_project() == XTC_OK && window.open_project("/tmp/xtc-gui-controls-test.xtc") == XTC_OK);
|
||||
CHECK(xtc_project_get_control_map(sequence->project(),2,actual) == XTC_OK && actual[0] == 1 && actual[1] == 74);
|
||||
CHECK(xtc_project_get_automation(sequence->project(),0,&point) == XTC_OK && point.step == 1);
|
||||
unlink("/tmp/xtc-gui-controls-test.xtc"); window.do_callback();
|
||||
}
|
||||
if (const char *module = std::getenv("XTC_TEST_PLUGIN")) {
|
||||
reset();
|
||||
MainWindow window(false);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ def main():
|
|||
assert os.access(root / 'bin/xtc-check', os.X_OK)
|
||||
run([str(root / 'bin/xtc-check')])
|
||||
diagnose = subprocess.check_output([str(root / 'bin/xtc-check'), '--diagnose'], text=True)
|
||||
assert 'Project formats: 1..9' in diagnose and 'JACK compiled:' in diagnose
|
||||
assert 'Project formats: 1..10' in diagnose and 'JACK compiled:' in diagnose
|
||||
# Upgrade/repair repeats the same owned-file install and keeps unrelated data.
|
||||
(root / 'include/xtc/xtc.h').write_text('old installation marker\n')
|
||||
run(make + ['install'] + variables, env=env)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ int main(int argc, char **argv)
|
|||
for (j = 0; j < 2; ++j)
|
||||
REQUIRE(xtc_project_add_note(project, clip, 0, 384000, 48 + (unsigned int)i, 100, 0,
|
||||
¬e) == XTC_OK);
|
||||
point = (xtc_automation_point){track, 0, XTC_AUTOMATION_GAIN, 0.1f};
|
||||
point = (xtc_automation_point){track, 0, XTC_AUTOMATION_GAIN, 0.1f,0};
|
||||
REQUIRE(xtc_project_set_automation(project, &point) == XTC_OK);
|
||||
point.tick = 384000;
|
||||
point.value = 0.2f;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "xtc/xtc_control.h"
|
||||
#include "xtc/xtc_project.h"
|
||||
#include "xtc/xtc_realtime.h"
|
||||
#include <assert.h>
|
||||
|
|
@ -79,6 +80,12 @@ static void graph_roundtrip(const xtc_plugin_spec *spec)
|
|||
assert(xtc_project_create(&loaded) == XTC_OK);
|
||||
assert(xtc_project_load(loaded,path) == XTC_OK);
|
||||
assert(xtc_graph_compile(xtc_project_graph(loaded),48000,31,&again) == XTC_ERR_NOT_FOUND && !again);
|
||||
{
|
||||
xtc_parameter_key key = {XTC_PARAMETER_PLUGIN,plugin,copy.control_count ? copy.ports[0] : 0};
|
||||
xtc_parameter_state state = {0}; state.value = .75f;
|
||||
assert(xtc_project_parameter(loaded,key,48000,&state) == XTC_ERR_NOT_FOUND && state.value == .75f);
|
||||
assert(xtc_project_automate(loaded,key,48000,0,1,1,0) == XTC_ERR_NOT_FOUND);
|
||||
}
|
||||
assert(xtc_project_save(loaded,path) == XTC_OK);
|
||||
xtc_graph_destroy(g); xtc_project_destroy(project); xtc_project_destroy(loaded);
|
||||
xtc_plan_destroy(p); unlink(path);
|
||||
|
|
@ -167,6 +174,25 @@ int main(int argc, char **argv)
|
|||
assert(xtc_plugin_process_at(other,in,split,1,25) == XTC_OK && split[0] == 2);
|
||||
xtc_plugin_destroy(p); xtc_plugin_destroy(other); p = NULL;
|
||||
}
|
||||
{
|
||||
xtc_project *project = NULL; xtc_graph *g = NULL;
|
||||
xtc_parameter_key key;
|
||||
xtc_parameter_state state;
|
||||
xtc_plugin_spec copy;
|
||||
size_t id;
|
||||
assert(xtc_project_create(&project) == XTC_OK && xtc_graph_create(8,&g) == XTC_OK);
|
||||
assert(xtc_graph_add_plugin(g,&s,&id) == XTC_OK && xtc_project_set_graph(project,g) == XTC_OK);
|
||||
key = (xtc_parameter_key){XTC_PARAMETER_PLUGIN,id,s.ports[0]};
|
||||
assert(xtc_project_parameter(project,key,48000,&state) == XTC_OK && state.automated && state.info.id == s.ports[0]);
|
||||
assert(xtc_project_set_parameter(project,key,48000,.75f) == XTC_OK);
|
||||
assert(xtc_project_automate(project,key,48000,3,1,1,0) == XTC_OK);
|
||||
assert(xtc_project_automate(project,key,48000,3,99,NAN,1) == XTC_OK); // Removal ignores mode/value.
|
||||
assert(xtc_project_set_parameter(project,key,48000,3) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_graph_get_plugin(xtc_project_graph(project),id,©) == XTC_OK && copy.values[0] == .75f && copy.point_count == 2);
|
||||
key.parameter = 63;
|
||||
assert(xtc_project_automate(project,key,48000,0,1,0,0) == XTC_ERR_NOT_FOUND);
|
||||
xtc_graph_destroy(g); xtc_project_destroy(project);
|
||||
}
|
||||
s.point_count = 0;
|
||||
s.values[0] = 3; assert(xtc_plugin_create(&s,48000,17,&p) == XTC_ERR_UNSUPPORTED && !p);
|
||||
assert(xtc_plugin_probe(argv[1],1,48000,&s) == XTC_OK && !s.inputs);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "xtc/xtc_mixer.h"
|
||||
#include "xtc/xtc_midi.h"
|
||||
#include "xtc/xtc_plugin.h"
|
||||
#include "xtc/xtc_control.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -18,7 +19,7 @@ int main(void)
|
|||
assert(mkdtemp(root));
|
||||
assert(xtc_project_create(&p) == XTC_OK);
|
||||
snprintf(file,sizeof(file),"%s/migration.xtc",root);
|
||||
for (version = 1; version <= 8; ++version) {
|
||||
for (version = 1; version <= 9; ++version) {
|
||||
xtc_track_info t; xtc_clip_info c; size_t notes, points;
|
||||
snprintf(old,sizeof(old),"tests/fixtures/project-v%u.xtc",version);
|
||||
assert(xtc_project_load(p,old) == XTC_OK);
|
||||
|
|
@ -28,6 +29,9 @@ int main(void)
|
|||
assert(c.gain == 1 && c.fade_in_tick == 0 && c.fade_out_tick == 0);
|
||||
assert(xtc_project_note_count(p,¬es) == XTC_OK && notes == (size_t)(version >= 3));
|
||||
assert(xtc_project_automation_count(p,&points) == XTC_OK && points == (size_t)(version >= 4));
|
||||
{ unsigned int map[3]; xtc_automation_point point;
|
||||
assert(xtc_project_get_control_map(p,0,map) == XTC_OK && map[0] == 7 && map[1] == 10 && map[2] == 64);
|
||||
if (points) assert(xtc_project_get_automation(p,0,&point) == XTC_OK && !point.step); }
|
||||
assert(xtc_project_save(p,file) == XTC_OK && xtc_project_load(p,file) == XTC_OK);
|
||||
}
|
||||
for (version = 5; version <= 6; ++version) {
|
||||
|
|
@ -85,6 +89,6 @@ int main(void)
|
|||
assert(xtc_project_load(p,file) == XTC_ERR_FORMAT);
|
||||
{ xtc_clip_info c; assert(xtc_project_find_clip(p,clip,&c) == XTC_OK && c.asset_path[0]); } }
|
||||
xtc_project_destroy(p); unlink(file); rmdir(moved);
|
||||
puts("v1-v8 migration, current folder relocation, cwd independence and missing-media preservation passed.");
|
||||
puts("v1-v9 migration, current folder relocation, cwd independence and missing-media preservation passed.");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ int main(void)
|
|||
for (i = 0; i < XTC_PROJECT_MAX_NOTES; ++i) assert(xtc_project_add_note(p,clips[i%1024],i/1024,1,60,100,0,&id) == XTC_OK);
|
||||
assert(xtc_project_add_note(p,clips[0],0,1,60,100,0,&id) == XTC_ERR_CAPACITY);
|
||||
for (i = 0; i < XTC_MAX_AUTOMATION_POINTS; ++i) {
|
||||
xtc_automation_point a = {tracks[i%64],i/64,XTC_AUTOMATION_GAIN,.1f};
|
||||
xtc_automation_point a = {tracks[i%64],i/64,XTC_AUTOMATION_GAIN,.1f,0};
|
||||
assert(xtc_project_set_automation(p,&a) == XTC_OK);
|
||||
}
|
||||
{ xtc_automation_point a = {tracks[0],1000,XTC_AUTOMATION_GAIN,1}; assert(xtc_project_set_automation(p,&a) == XTC_ERR_CAPACITY); }
|
||||
{ xtc_automation_point a = {tracks[0],1000,XTC_AUTOMATION_GAIN,1,0}; assert(xtc_project_set_automation(p,&a) == XTC_ERR_CAPACITY); }
|
||||
assert(xtc_graph_create(256,&graph) == XTC_OK && xtc_graph_add_instrument(graph,&previous) == XTC_OK);
|
||||
for (i = 1; i < 256; ++i) {
|
||||
assert((i == 255 ? xtc_graph_add_output(graph,&node) : xtc_graph_add_meter(graph,&node)) == XTC_OK);
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ int main(void)
|
|||
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};
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#include "xtc/xtc_midi.h"
|
||||
#include "xtc/xtc_control.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -29,10 +30,55 @@ int main(void)
|
|||
for (i = 0; i < 400; ++i) assert(whole[i] == split[i]);
|
||||
assert(whole[6] != 0); /* Sustain retains the released note. */
|
||||
for (i = 250; i < 400; ++i) assert(whole[i] == 0); /* CC7 ramp reaches zero in 240 samples. */
|
||||
{
|
||||
xtc_midi_plan *mapped = NULL, *disabled = NULL;
|
||||
xtc_parameter_state state;
|
||||
xtc_parameter_key key = {XTC_PARAMETER_LIVE,2,2};
|
||||
unsigned int bindings[3] = {1,74,11};
|
||||
xtc_midi_event remapped[9];
|
||||
float actual[400], right[400];
|
||||
unsigned char message[3] = {0xb2,1,0};
|
||||
assert(xtc_midi_decode(message,3,0,&decoded) == XTC_OK && decoded.type == XTC_MIDI_CONTROL);
|
||||
message[1] = 120;
|
||||
assert(xtc_midi_decode(message,3,0,&decoded) == XTC_OK && decoded.type == XTC_MIDI_ALL_OFF);
|
||||
assert(xtc_project_parameter(project,key,48000,&state) == XTC_OK && !state.automated &&
|
||||
(state.flags & XTC_PLUGIN_TOGGLED) && state.value == 0);
|
||||
assert(xtc_project_automate(project,key,48000,0,1,1,0) == XTC_ERR_UNSUPPORTED);
|
||||
assert(xtc_project_set_control_map(project,2,bindings) == XTC_OK);
|
||||
assert(xtc_midi_plan_create(project,48000,&mapped) == XTC_OK);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
remapped[i+(i ? 1 : 0)] = events[i];
|
||||
if (events[i].type == XTC_MIDI_CONTROL)
|
||||
remapped[i+1].pitch = events[i].pitch == 7 ? 1 : events[i].pitch == 10 ? 74 : 11;
|
||||
}
|
||||
// Former CC7 binding is ignored, not an additional volume binding.
|
||||
remapped[1] = (xtc_midi_event){1,XTC_MIDI_CONTROL,2,7,0};
|
||||
assert(xtc_midi_plan_render(mapped,0,remapped,9,actual,400) == XTC_OK);
|
||||
for (i = 0; i < 400; ++i) assert(actual[i] == whole[i]);
|
||||
{
|
||||
const xtc_midi_event pan[] = {{0,XTC_MIDI_NOTE_ON,2,69,127},{0,XTC_MIDI_CONTROL,2,74,127}};
|
||||
xtc_midi_plan_reset(mapped);
|
||||
assert(xtc_midi_plan_render_stereo(mapped,0,pan,2,actual,right,400) == XTC_OK);
|
||||
assert(actual[300] == 0 && right[300] != 0);
|
||||
}
|
||||
// Mutating the model does not change an already prepared mapping.
|
||||
bindings[0] = bindings[1] = bindings[2] = XTC_CC_DISABLED;
|
||||
assert(xtc_project_set_control_map(project,2,bindings) == XTC_OK);
|
||||
xtc_midi_plan_reset(mapped);
|
||||
assert(xtc_midi_plan_render(mapped,0,remapped,9,actual,400) == XTC_OK);
|
||||
for (i = 0; i < 400; ++i) assert(actual[i] == whole[i]);
|
||||
assert(xtc_midi_plan_create(project,48000,&disabled) == XTC_OK);
|
||||
{
|
||||
const xtc_midi_event unbound[] = {{0,XTC_MIDI_NOTE_ON,2,69,127},{3,XTC_MIDI_CONTROL,2,1,0}};
|
||||
assert(xtc_midi_plan_render(disabled,0,unbound,2,actual,400) == XTC_OK);
|
||||
assert(actual[300] != 0); // No mapped volume ramp reaches zero.
|
||||
}
|
||||
xtc_midi_plan_destroy(mapped); xtc_midi_plan_destroy(disabled);
|
||||
}
|
||||
xtc_midi_plan_reset(a);
|
||||
assert(xtc_midi_plan_render(a,400,NULL,0,whole,400) == XTC_OK);
|
||||
for (i = 0; i < 400; ++i) assert(whole[i] == 0);
|
||||
xtc_midi_plan_destroy(a); xtc_midi_plan_destroy(b); xtc_project_destroy(project);
|
||||
puts("Sample-timed CC7/CC10/sustain, retrigger, note-off and block partition checks passed.");
|
||||
puts("Sample-timed default/remapped/disabled controllers, immutable maps, sustain, retrigger, note-off and block partition checks passed.");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,12 +71,12 @@ int main(void)
|
|||
assert(xtc_project_set_mix(p, a, NAN, 0, 0, 0) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_project_set_mix(p, a, 1, 2, 0, 0) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_project_set_mix(p, a, 1, 0, 2, 0) == XTC_ERR_INVALID_ARGUMENT);
|
||||
point = (xtc_automation_point){a, 0, XTC_AUTOMATION_GAIN, 0};
|
||||
point = (xtc_automation_point){a, 0, XTC_AUTOMATION_GAIN, 0,0};
|
||||
assert(xtc_project_set_automation(p, &point) == XTC_OK);
|
||||
point.tick = 20;
|
||||
point.value = 1;
|
||||
assert(xtc_project_set_automation(p, &point) == XTC_OK); /* sample 500 */
|
||||
point = (xtc_automation_point){a, 0, XTC_AUTOMATION_PAN, -1};
|
||||
point = (xtc_automation_point){a, 0, XTC_AUTOMATION_PAN, -1,0};
|
||||
assert(xtc_project_set_automation(p, &point) == XTC_OK);
|
||||
point.tick = 20;
|
||||
point.value = 1;
|
||||
|
|
@ -165,8 +165,8 @@ int main(void)
|
|||
{
|
||||
const char *bad[] = {"MIX 7fc00000 00000000 0 0", "MIX 40800001 00000000 0 0",
|
||||
"MIX 3f800000 40000000 0 0", "MIX 3f800000 00000000 2 0",
|
||||
"POINT 999 0 0 3f800000", "POINT 0 2 0 3f800000",
|
||||
"POINT 0 0 0 7f800000", "POINT 0 0 20 3f800000"};
|
||||
"POINT 999 0 0 3f800000 0", "POINT 0 2 0 3f800000 0",
|
||||
"POINT 0 0 0 7f800000 0", "POINT 0 0 20 3f800000 0", "POINT 0 0 0 3f800000 2"};
|
||||
for (i = 0; i < sizeof(bad) / sizeof(*bad); ++i) {
|
||||
char *start = strstr(bytes, i < 4 ? "MIX " : "POINT "), *end;
|
||||
assert(start);
|
||||
|
|
@ -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, "POINT ", 6) && strncmp(line, "AUDIO ", 6) && strncmp(line, "ROUTE ", 6) && strncmp(line,"CONTROL_MAPS ",13) && strncmp(line,"MAP ",4))
|
||||
assert(fputs(line, legacy) >= 0);
|
||||
}
|
||||
assert(fclose(f) == 0 && fclose(legacy) == 0);
|
||||
|
|
@ -237,7 +237,7 @@ int main(void)
|
|||
xtc_history_destroy(history);
|
||||
assert(xtc_project_remove_track(p, a) == XTC_OK &&
|
||||
xtc_project_automation_count(p, &count) == XTC_OK && count == 0);
|
||||
point = (xtc_automation_point){b, 0, XTC_AUTOMATION_GAIN, 1};
|
||||
point = (xtc_automation_point){b, 0, XTC_AUTOMATION_GAIN, 1,0};
|
||||
for (i = 0; i < XTC_MAX_AUTOMATION_POINTS; ++i) {
|
||||
point.tick = i;
|
||||
assert(xtc_project_set_automation(p, &point) == XTC_OK);
|
||||
|
|
|
|||
97
tests/unit/test_parameters.c
Normal file
97
tests/unit/test_parameters.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "xtc/xtc_control.h"
|
||||
#include "xtc/xtc_midi.h"
|
||||
#include "../../src/mixer/mix_internal.h"
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
xtc_project *p = NULL, *loaded = NULL;
|
||||
xtc_midi_plan *reference = NULL, *whole = NULL, *split = NULL;
|
||||
xtc_parameter_state state, sentinel;
|
||||
uint64_t track, clip, note;
|
||||
float base[80], left[80], right[80], sl[80], sr[80];
|
||||
unsigned int map[3] = {1,2,3}, actual[3];
|
||||
size_t i, cursor;
|
||||
char path[] = "/tmp/xtc-parameters-XXXXXX";
|
||||
int fd = mkstemp(path); assert(fd >= 0); close(fd);
|
||||
assert(xtc_project_create(&p) == XTC_OK);
|
||||
assert(xtc_project_add_track(p,"Parameters",&track) == XTC_OK);
|
||||
assert(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);
|
||||
assert(xtc_midi_plan_create(p,48000,&reference) == XTC_OK);
|
||||
assert(xtc_midi_plan_render(reference,0,NULL,0,base,80) == XTC_OK);
|
||||
{
|
||||
xtc_parameter_key gain = {XTC_PARAMETER_TRACK,track,0}, pan = {XTC_PARAMETER_TRACK,track,1};
|
||||
xtc_parameter_key missing = {XTC_PARAMETER_TRACK,UINT64_MAX,0};
|
||||
xtc_mix_snapshot mix;
|
||||
assert(xtc_project_parameter(p,gain,48000,&state) == XTC_OK && !state.automated && state.value == 1);
|
||||
assert(!strcmp(state.info.unit,"linear") && state.info.minimum == 0 && state.info.maximum == 4);
|
||||
sentinel = state;
|
||||
assert(xtc_project_parameter(p,missing,48000,&state) == XTC_ERR_NOT_FOUND && !memcmp(&state,&sentinel,sizeof(state)));
|
||||
assert(xtc_project_automate(p,missing,48000,0,0,1,0) == XTC_ERR_NOT_FOUND);
|
||||
assert(xtc_project_automate(p,gain,48000,0,1,.25f,0) == XTC_OK);
|
||||
assert(xtc_project_automate(p,gain,48000,1,0,1,0) == XTC_OK);
|
||||
assert(xtc_project_automate(p,gain,48000,2,0,.5f,0) == XTC_OK);
|
||||
assert(xtc_project_automate(p,pan,48000,0,1,-1,0) == XTC_OK);
|
||||
assert(xtc_project_automate(p,pan,48000,2,1,1,0) == XTC_OK);
|
||||
assert(xtc_project_automate(p,gain,48000,3,2,.5f,0) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_project_set_parameter(p,gain,48000,3) == XTC_OK);
|
||||
assert(xtc_project_parameter(p,gain,48000,&state) == XTC_OK && state.automated && state.value == 3);
|
||||
assert(xtc_project_set_parameter(p,gain,48000,NAN) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_mix_prepare(p,1,&mix) == XTC_OK && xtc_mix_level(&mix,0,0,2) == .5f); // Colliding ticks: latest wins.
|
||||
}
|
||||
assert(xtc_project_get_control_map(p,0,actual) == XTC_OK && actual[0] == 7 && actual[1] == 10 && actual[2] == 64);
|
||||
assert(xtc_project_set_control_map(p,0,map) == XTC_OK);
|
||||
map[1] = 1;
|
||||
assert(xtc_project_set_control_map(p,0,map) == XTC_ERR_INVALID_ARGUMENT);
|
||||
map[1] = 120;
|
||||
assert(xtc_project_set_control_map(p,0,map) == XTC_ERR_INVALID_ARGUMENT);
|
||||
assert(xtc_project_get_control_map(p,0,actual) == XTC_OK && actual[0] == 1 && actual[1] == 2 && actual[2] == 3);
|
||||
assert(xtc_project_save(p,path) == XTC_OK);
|
||||
assert(xtc_project_create(&loaded) == XTC_OK && xtc_project_load(loaded,path) == XTC_OK);
|
||||
assert(xtc_project_get_control_map(loaded,0,actual) == XTC_OK && actual[0] == 1 && actual[1] == 2 && actual[2] == 3);
|
||||
{
|
||||
xtc_automation_point point;
|
||||
assert(xtc_project_get_automation(loaded,0,&point) == XTC_OK && point.step == 1);
|
||||
}
|
||||
assert(xtc_midi_plan_create(loaded,48000,&whole) == XTC_OK && xtc_midi_plan_create(loaded,48000,&split) == XTC_OK);
|
||||
assert(xtc_midi_plan_render_stereo(whole,0,NULL,0,left,right,80) == XTC_OK);
|
||||
for (cursor = 0; cursor < 80;) {
|
||||
size_t frames = 13; if (frames > 80-cursor) frames = 80-cursor;
|
||||
assert(xtc_midi_plan_render_stereo(split,cursor,NULL,0,sl+cursor,sr+cursor,frames) == XTC_OK);
|
||||
cursor += frames;
|
||||
}
|
||||
for (i = 0; i < 80; ++i) {
|
||||
float gain = i < 25 ? .25f : i < 50 ? 1-(float)(i-25)/50 : .5f;
|
||||
assert(left[i] == sl[i] && right[i] == sr[i]);
|
||||
assert(fabsf(left[i] - (i < 50 ? base[i]*gain : 0)) < .000001f);
|
||||
assert(fabsf(right[i] - (i < 50 ? 0 : base[i]*gain)) < .000001f);
|
||||
}
|
||||
assert(xtc_midi_plan_render_stereo(split,29,NULL,0,sl,sr,13) == XTC_OK);
|
||||
for (i = 0; i < 13; ++i) assert(sl[i] == left[i+29] && sr[i] == right[i+29]);
|
||||
// Explicit backward transport wrap (no loop transport feature is implied).
|
||||
assert(xtc_midi_plan_render_stereo(split,0,NULL,0,sl,sr,80) == XTC_OK);
|
||||
for (i = 0; i < 80; ++i) assert(sl[i] == left[i] && sr[i] == right[i]);
|
||||
{
|
||||
FILE *source = fopen(path,"r"), *dest;
|
||||
char bad[128], line[1024];
|
||||
snprintf(bad,sizeof(bad),"%s-bad",path); dest = fopen(bad,"w"); assert(source && dest);
|
||||
while (fgets(line,sizeof(line),source)) {
|
||||
if (!strncmp(line,"MAP 0 ",6)) fputs("MAP 0 1 1 3\n",dest); else fputs(line,dest);
|
||||
}
|
||||
fclose(source); assert(fclose(dest) == 0);
|
||||
assert(xtc_project_load(loaded,bad) == XTC_ERR_FORMAT);
|
||||
assert(xtc_project_get_control_map(loaded,0,actual) == XTC_OK && actual[1] == 2);
|
||||
unlink(bad);
|
||||
}
|
||||
xtc_midi_plan_destroy(reference); xtc_midi_plan_destroy(whole); xtc_midi_plan_destroy(split);
|
||||
xtc_project_destroy(p); xtc_project_destroy(loaded); unlink(path);
|
||||
puts("Common parameter identity, hold/linear lanes, static precedence, persistence, seeks and wrap passed.");
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue