Phase 6: Add WAV clips, graph playback and bounded recording

This commit is contained in:
Johannes Findeisen 2026-10-27 00:31:03 +01:00
commit 8a37090b81
40 changed files with 1450 additions and 69 deletions

View file

@ -16,7 +16,7 @@
/* Fake only the public C backend boundary. Real FLTK widgets, callbacks and
* timers run under Xvfb; no server/device is required or touched. */
struct xtc_backend { int running = 0; xtc_plan *plan = nullptr; };
struct xtc_backend { int running = 0; xtc_plan *plan = nullptr; xtc_audio_plan *audio = nullptr; xtc_recorder *recorder = nullptr; };
namespace {
xtc_backend handle;
xtc_result open_result = XTC_OK, start_result = XTC_OK, stop_result = XTC_OK, close_result = XTC_OK;
@ -98,11 +98,11 @@ xtc_result xtc_backend_close(xtc_backend *backend)
{
if (backend == nullptr) return XTC_OK;
++closes;
if (close_result == XTC_OK) { CHECK(alive == 1U); --alive; handle.running = 0; xtc_plan_destroy(handle.plan); handle.plan = nullptr; }
if (close_result == XTC_OK) { CHECK(alive == 1U); --alive; handle.running = 0; xtc_plan_destroy(handle.plan); handle.plan = nullptr; xtc_audio_plan_destroy(handle.audio); handle.audio = nullptr; }
return close_result;
}
xtc_result xtc_backend_start(xtc_backend *backend)
{ CHECK(backend == &handle); ++starts; if (start_result == XTC_OK) handle.running = 1; return start_result; }
{ CHECK(backend == &handle); ++starts; if (start_result == XTC_OK) { handle.running = 1; engine_status.active_revision = engine_status.submitted_revision; } return start_result; }
xtc_result xtc_backend_stop(xtc_backend *backend)
{ CHECK(backend == &handle); ++stops; if (stop_result == XTC_OK) handle.running = 0; return stop_result; }
xtc_result xtc_backend_get_status(const xtc_backend *backend, xtc_backend_status *status)
@ -126,6 +126,20 @@ xtc_result xtc_backend_submit(xtc_backend *backend, const xtc_graph *graph)
++engine_status.submitted_revision;
return XTC_OK;
}
xtc_result xtc_backend_prepare_project(xtc_backend *backend, const xtc_project *project)
{
xtc_audio_plan *plan = nullptr;
xtc_result r = xtc_audio_plan_create(project, 48000, &plan);
if (r == XTC_OK) r = xtc_backend_submit(backend, xtc_project_graph(project));
if (r != XTC_OK) { xtc_audio_plan_destroy(plan); return r; }
xtc_audio_plan_destroy(handle.audio); handle.audio = plan;
engine_status.asset_issues = static_cast<unsigned int>(xtc_audio_plan_issues(plan, nullptr));
return XTC_OK;
}
const char *xtc_backend_asset_issue(const xtc_backend *)
{ const char *path = ""; xtc_audio_plan_issues(handle.audio, &path); return path; }
xtc_result xtc_backend_set_recorder(xtc_backend *, xtc_recorder *recorder)
{ if (handle.running) return XTC_ERR_INVALID_STATE; handle.recorder = recorder; return XTC_OK; }
void xtc_backend_collect(xtc_backend *backend)
{ CHECK(backend == &handle && alive == 1U); ++collects; }
}
@ -268,6 +282,24 @@ int main(int argc, char **argv)
CHECK(canvas->add_node(XTC_NODE_GAIN) == XTC_OK);
CHECK(sequence->position() == 1920); // Graph edits preserve the transport.
engine_status.sample_position = 0;
std::string take = std::string(directory) + "/take.wav";
CHECK(window.record_audio(take.c_str()) == XTC_OK && handle.recorder != nullptr);
CHECK(canvas->add_node(XTC_NODE_GAIN) == XTC_ERR_INVALID_STATE);
CHECK(window.save_project(filename.c_str()) == XTC_ERR_INVALID_STATE);
window.do_callback();
CHECK(window.shown() && contains(&window, "Stop recording before closing"));
float recorded[64]; for (float &value : recorded) value = 0.25f;
xtc_recorder_push(handle.recorder, recorded, 64);
pump();
press(stop);
CHECK(handle.recorder == nullptr);
CHECK(xtc_project_get_counts(sequence->project(), &track_count, &clip_count) == XTC_OK && clip_count == 2);
CHECK(window.save_project(filename.c_str()) == XTC_OK);
CHECK(window.open_project(filename.c_str()) == XTC_OK);
float mix[64]; CHECK(xtc_audio_plan_render(handle.audio, 0, mix, 64) == XTC_OK && mix[0] == 0.25f);
CHECK(unlink(take.c_str()) == 0);
CHECK(window.open_project(filename.c_str()) == XTC_OK && contains(&window, "Missing/unreadable audio"));
CHECK(window.import_audio("/missing/xtc-audio.wav") == XTC_ERR_IO);
CHECK(unlink(filename.c_str()) == 0 && rmdir(directory) == 0);
engine_status.xruns = 3U;
engine_status.processed_blocks = 123U;