Phase 5: Add project persistence and sequencer foundation
This commit is contained in:
parent
5c41d7f6da
commit
65640c4aad
28 changed files with 1307 additions and 84 deletions
|
|
@ -68,3 +68,20 @@ GraphWidget owns only selection, node positions, pointer gestures and drawing.
|
|||
Its fixed canvas is hosted by FLTK scrolling. C owns nodes, ports, edges,
|
||||
validation, ID assignment, compilation and rendering. No audio thread accesses
|
||||
GUI-owned state. Reconnect republishes the same retained editable graph.
|
||||
|
||||
|
||||
## Project ownership and persistence
|
||||
|
||||
Phase 5 introduces a C-owned project containing the graph, bounded track/clip
|
||||
arrays, stable ID allocators and constant tempo. Read APIs return copies;
|
||||
GUI canvases borrow the opaque project/graph on the control thread. A graph
|
||||
edit prepares a candidate project before submitting its graph, then commits
|
||||
ownership only after acceptance. Arrangement edits currently change silent
|
||||
metadata and require no audio publication.
|
||||
|
||||
Persistence is private C/POSIX code; loading validates an isolated candidate
|
||||
and swaps ownership only after a successful close. The GUI similarly prepares
|
||||
a loaded document before stopping/submitting audio. File operations never run
|
||||
in the callback. See PROJECT_FORMAT.md for schema, bounds and durability.
|
||||
SequencerWidget owns selection, zoom and gestures only; musical range checks,
|
||||
source-preserving trims and ID allocation remain C responsibilities.
|
||||
|
|
|
|||
|
|
@ -181,3 +181,25 @@ or publish audio plans. Empty graphs and graphs without explicit outputs are
|
|||
silent under the GUI policy. Terminal fallback remains the default for earlier
|
||||
offline/standalone callers. Input on unconnected nodes retains the existing
|
||||
external-input contract; the GUI does not connect external JACK ports.
|
||||
|
||||
|
||||
## Phase 5 transport
|
||||
|
||||
Backend status includes a uint64 sample_position. Each successful processing
|
||||
callback advances it by its exact frame count (including silence); errors do
|
||||
not advance it. The counter uses compiler atomics, verified always lock-free
|
||||
before backend setup, with overflow detected rather than wrapped. No extra
|
||||
allocation, I/O, mutex or GUI work occurs in callbacks. Fields remain individually
|
||||
coherent snapshots.
|
||||
|
||||
Stop pauses the clock. Start resumes; stopped and running graph submissions
|
||||
preserve position even when plan/revision counters reset. Reopen starts at zero.
|
||||
xtc_backend_seek changes position only on a healthy stopped backend; it does
|
||||
not rewind oscillator phase. Project tick/sample conversions use integer floor
|
||||
arithmetic at constant tempo, with explicit capacity errors and rates 1..768000.
|
||||
The GUI observes the counter every 100 ms, so visual updates are block-quantized
|
||||
and delayed by polling. No external JACK transport synchronization is provided.
|
||||
|
||||
Silent clip metadata is control-thread data, never traversed by the callback.
|
||||
Audio/MIDI scheduling will be added in later phases through compiled event/clip
|
||||
plans rather than direct callback access to the editable project.
|
||||
|
|
|
|||
|
|
@ -109,8 +109,22 @@ playback, reject cycles, reconnect and close active clients. Mouse/visual smoke
|
|||
checks cover node creation, wiring, cycle rejection, dragging and deletion.
|
||||
No physical routes, new dependencies or system changes are introduced.
|
||||
|
||||
## Next milestone
|
||||
## Phase 5 verification
|
||||
|
||||
Phase 5 adds versioned project persistence and the sequencer foundation, stable
|
||||
track/clip IDs and timeline editing. Physical-device playback/endurance remains
|
||||
unverified; graph parameters are fixed GUI presets and undo/zoom are not present.
|
||||
```sh
|
||||
make clean all test gui debug test-gui test-gui-jack
|
||||
make BUILD_DIR=build/nojack PKG_CONFIG=false all test gui test-gui
|
||||
```
|
||||
|
||||
The suite adds test_project: track/clip limits, stable IDs, trims, integer
|
||||
transport conversion, exact file roundtrip with graph rendering, exhaustive
|
||||
fixture truncations, byte mutations, failed saves and temporary cleanup.
|
||||
GUI tests cover region gestures, zoom, save/load and backend rollback.
|
||||
Live JACK tests verify playhead advance, pause and resume without creating routes.
|
||||
The updated core and GUI tests pass Clang ASan/UBSan (the GUI uses the existing
|
||||
fontconfig-only LSan suppression). An Xvfb/xdotool visual check created a track
|
||||
and region, moved it one bar and zoomed the canvas. No dependencies were added.
|
||||
|
||||
Phase 6 will add audio assets and recording. Silent arrangement regions, fixed
|
||||
tempo/4/4, no undo, no persisted GUI layout, and unverified physical-device
|
||||
playback/endurance remain explicit limits.
|
||||
|
|
|
|||
45
docs/GUI.md
45
docs/GUI.md
|
|
@ -26,17 +26,15 @@ from an audio callback. No server is started and no audio ports are connected
|
|||
by the GUI. The initially empty graph produces silence.
|
||||
|
||||
- **Play / F5:** start the engine. **Stop / F8:** deactivate it. **Space:** toggle.
|
||||
These are engine controls, not timeline playback or transport positioning.
|
||||
These advance/pause the sample-counted playhead; regions are currently silent.
|
||||
- **Reconnect / F6:** close the old client and open a fresh stopped client.
|
||||
Use this after server loss or sample-rate changes. A failed close preserves
|
||||
the handle and reports the failure instead of opening another client.
|
||||
- **View menu / Ctrl+1, Ctrl+2, Ctrl+3:** switch between sequencer, mixer and
|
||||
modular graph. Tabs provide the same view switching. Editors remain clearly
|
||||
labeled placeholders for sequencer/mixer; Modular Graph is implemented below.
|
||||
modular graph. Tabs provide the same view switching. The mixer remains a placeholder; graph and sequencer editors are described below.
|
||||
- **File / Quit / Ctrl+Q** or the window close button: close the backend and
|
||||
synchronize audio callbacks before hiding/destroying widgets. A failed close
|
||||
leaves the window open for a retry. Unsupported New/Open/Save actions are
|
||||
visibly disabled. About identifies the development milestone and version.
|
||||
leaves the window open for a retry. New/Open/Save actions are described below. About identifies the development milestone and version.
|
||||
|
||||
The indicator shows disconnected, ready, running, server lost, format changed
|
||||
or process-error states. The status line shows rate, actual block size, mono
|
||||
|
|
@ -78,10 +76,10 @@ to connect. Click a node or wire to select it (yellow outline/wire), then press
|
|||
Delete/Backspace or **Delete selection**. Deleting a node deletes its incident
|
||||
wires. Escape cancels a drag preview. Canvas scrollbars pan the view; positions
|
||||
are retained across edits/reconnect but not across application restart. There
|
||||
is no zoom, multiselection, undo, project persistence or external routing UI yet.
|
||||
is no graph zoom, multiselection, undo or external routing UI yet.
|
||||
The editor supports 64 nodes on a fixed 2000x4000 canvas.
|
||||
|
||||
Every semantic action becomes a C graph command. The window owns the graph
|
||||
Every semantic action becomes a C graph command. The window owns a C project containing the graph
|
||||
handle and publishes the C-created candidate through the backend. Cycle,
|
||||
invalid-direction, duplicate connection, missing-port, capacity and backend
|
||||
failures are shown below the canvas; failed edits leave the graph unchanged.
|
||||
|
|
@ -99,3 +97,36 @@ audio thread. There is no automatic retry/hidden edit queue; users retry a
|
|||
rejected capacity edit. Plan replacement can reset oscillator phase and is not
|
||||
crossfaded. Render validation and active replacement tests are described in
|
||||
tests/integration/README.md.
|
||||
|
||||
|
||||
## Phase 5 projects and sequencer
|
||||
|
||||
File/New (Ctrl+N), Open (Ctrl+O), Save (Ctrl+S), and Save As
|
||||
(Ctrl+Shift+S) use native file dialogs and the C persistence API. New/Open/Quit
|
||||
offer Cancel/Discard when semantic edits are unsaved; save first to retain them.
|
||||
Save As confirms overwrites. The title marks unsaved changes. Failed loads and
|
||||
backend submissions retain the old document. Loading parses before stopping
|
||||
audio; installing a valid candidate stops and resets transport before compiling
|
||||
and submitting. A later failure can leave the retained document stopped at zero.
|
||||
Opening/New resets graph layout; graph positions are not saved. See PROJECT_FORMAT.md.
|
||||
|
||||
Sequencer offers Add track, Add region, Delete region/track, Zoom +/-. Select a
|
||||
track header to add a one-bar region there at the cursor. Select a region to
|
||||
delete it; deleting a selected track cascades to its regions. Delete also works
|
||||
with canvas focus. Drag a region body to move it in time or between tracks;
|
||||
drag either edge to trim within its original source bounds. Edits commit on
|
||||
release with 1/16-note snapping; Escape cancels. There is no drag preview, undo,
|
||||
multiselection, rename or tempo editor yet. Overlapping regions are permitted.
|
||||
|
||||
The scrollable canvas expands to cover the arrangement plus a margin; zoom
|
||||
ranges from 24 to 384 pixels/quarter. Names, tempo and 4/4 meter are drawn.
|
||||
A ruler click seeks while stopped (or positions the cursor when disconnected).
|
||||
The 100 ms GUI refresh converts the backend's actual sample counter to ticks;
|
||||
it never integrates wall-clock time. Play resumes and Stop pauses. Reconnect,
|
||||
New and Open reset position. Regions contain no audio or MIDI and cannot
|
||||
produce sound yet; modular graph processing continues as before. The C model
|
||||
supports a constant tempo; a tempo map and scheduler are future work.
|
||||
|
||||
Tests cover C-model/persistence boundaries, GUI move/trim gestures and zoom,
|
||||
save/reopen, failed load/submission rollback, sample-derived cursor updates,
|
||||
and live JACK playhead advance/pause.
|
||||
|
|
|
|||
|
|
@ -1,18 +1,75 @@
|
|||
# Project format
|
||||
# Project format v1
|
||||
|
||||
Project persistence is not implemented yet. Before implementation, define a
|
||||
versioned format with stable IDs for tracks, nodes, clips, connections, and
|
||||
parameters.
|
||||
Phase 5 uses a bounded text format implemented in C99 without a parser library.
|
||||
The extension is `.xtc`. File operations run exclusively on the control thread.
|
||||
The project owns its graph, tracks and silent arrangement regions; audio files,
|
||||
recording and MIDI content arrive in subsequent phases.
|
||||
|
||||
Requirements:
|
||||
- reject malformed, truncated, unsupported, or unreasonable input safely;
|
||||
- validate sizes before allocation;
|
||||
- store external audio assets separately from project metadata;
|
||||
- define missing-asset behavior;
|
||||
- never execute code during loading;
|
||||
- perform file I/O only off the audio thread;
|
||||
- save atomically using a temporary file and a safe rename;
|
||||
- add explicit migration code when the format changes.
|
||||
## Schema
|
||||
|
||||
Choose a simple format after evaluating dependency, robustness, and human
|
||||
inspectability trade-offs. Document the schema before treating it as stable.
|
||||
Records occur in this exact order, separated by LF, including the final record.
|
||||
Fields use ASCII spaces. Integers are unsigned decimal, without signs.
|
||||
Counts determine the number of subsequent records:
|
||||
|
||||
```text
|
||||
XTC_PROJECT 1
|
||||
APP 0 1 0
|
||||
TEMPO 120000
|
||||
IDS 1 1
|
||||
GRAPH 1 2 2 1
|
||||
NODE 0 1 3e800000
|
||||
NODE 1 4 00000000
|
||||
EDGE 0 1
|
||||
TRACKS 1
|
||||
TRACK 0 547261636b
|
||||
CLIPS 1
|
||||
CLIP 0 0 3840 2880 960 3840 526567696f6e
|
||||
END
|
||||
```
|
||||
|
||||
- `APP`: producer major/minor/patch; informational, never executed.
|
||||
- `TEMPO`: constant milli-BPM (20000..300000, default 120000), fixed 4/4.
|
||||
- `IDS`: next track ID and next clip ID. IDs are uint64, monotonic and never
|
||||
reused; UINT64_MAX is reserved. Track and clip ID namespaces are separate.
|
||||
- `GRAPH`: output-node policy (0/1), next node ID, node count, edge count.
|
||||
Nodes retain stable IDs (limited to the host's size_t range). No pointers or
|
||||
compiled state are serialized.
|
||||
- `NODE`: ID, type (1 gain, 2 oscillator, 3 meter, 4 output), and exactly eight
|
||||
hexadecimal digits encoding the IEEE binary32 parameter bits, independent
|
||||
of locale and host byte order. Nonfinite values are rejected.
|
||||
- `EDGE`: source and destination node IDs. Mono port directions are implied
|
||||
by node type; endpoints, duplicates and cycles are validated by the graph API.
|
||||
- `TRACK`: ID and hex-encoded name bytes.
|
||||
- `CLIP`: ID, track ID, start tick, length tick, source offset tick, source
|
||||
length tick, hex-encoded name bytes. Regions are silent metadata. Trimming
|
||||
preserves source bounds, so a region can re-extend only within its original
|
||||
length. Overlaps are allowed; no audio mixing or scheduling is implied.
|
||||
- Names decode to nonempty valid UTF-8 of at most 63 bytes; ASCII control
|
||||
characters and embedded NULs are forbidden. Hex avoids quoting ambiguity.
|
||||
|
||||
Ticks use 960 per quarter note, 3840 per bar. All region endpoints are bounded
|
||||
by 38400000 ticks (10000 bars). Limits: 64 tracks, 1024 clips, 64 graph nodes,
|
||||
4096 edges, 1 MiB input, 511 bytes per record, 12 tokens per record. The reader
|
||||
rejects invalid references, duplicate IDs, inconsistent next IDs, ranges,
|
||||
truncated records, raw control/non-ASCII bytes, and trailing data.
|
||||
|
||||
Unsupported format versions return UNSUPPORTED. Other malformed data returns
|
||||
FORMAT. No implicit migration is attempted; future revisions require explicit
|
||||
migration code and roundtrip tests.
|
||||
|
||||
## Ownership and failures
|
||||
|
||||
Load builds and validates a separate candidate, closes the input, then replaces
|
||||
the destination. Any failure retains the complete previous project. Saving
|
||||
validates the project, creates a mode-0600 temporary in the destination
|
||||
directory, writes, flushes, fsyncs, closes and renames over the destination.
|
||||
Failures before rename retain the previous destination and remove the temporary.
|
||||
The parent directory is not fsynced: survival of the rename through sudden power
|
||||
loss is not guaranteed. Save replaces symlinks themselves and does not preserve
|
||||
existing file permissions. POSIX I/O is private to the Linux-first persistence
|
||||
module; public headers contain no POSIX types.
|
||||
|
||||
Saved data reproduces the arrangement and graph, including ID allocation state.
|
||||
Graph node positions, timeline zoom/scroll/selection, transport position and DSP
|
||||
oscillator phase are GUI/session state and are not saved. There are no external
|
||||
asset references yet; missing-asset recovery belongs to Phase 6.
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ mono-only processing, fixed canvas and no persistence/undo/zoom remain limits.
|
|||
Versioned persistence, stable IDs, tracks, clips, timeline, selection, zoom,
|
||||
scrolling, and transport synchronization.
|
||||
|
||||
Implemented: bounded version-1 project persistence with transactional loads and
|
||||
temporary-file/fsync/rename saves; stable track/clip/node IDs; silent regions;
|
||||
track and region creation/deletion, selection, move/trim, timeline zoom/scroll;
|
||||
sample-counted playhead, pause/resume and stopped seek. Project/graph arrangement
|
||||
roundtrips, malformed-input, GUI rollback/gesture and live transport tests pass.
|
||||
See PROJECT_FORMAT.md and GUI.md. Constant tempo/4/4, silent metadata regions,
|
||||
no undo, asset playback, MIDI or persisted GUI layout are explicit limits.
|
||||
|
||||
## Phase 6 — Audio clips and recording
|
||||
Audio import/playback, recording through bounded buffers, missing-asset
|
||||
handling, no file I/O in callbacks.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue