Phase 35: add persistent preferences and sequencer loop playback

This commit is contained in:
Johannes Findeisen 2026-10-28 10:37:01 +01:00
commit 47b7d38a66
21 changed files with 852 additions and 108 deletions

68
docs/PREFERENCES.md Normal file
View file

@ -0,0 +1,68 @@
# Application preferences and looping
Open **Edit / Preferences** (Ctrl+,). Each tab edits application settings.
**Apply** validates every tab, saves `~/.xtc.conf`, and applies the values.
**Close** discards edits made since the last Apply. Apply stops playback; stop
recording before opening Preferences. Missing configuration uses defaults.
Malformed configuration reports an error and uses defaults without overwriting
it. Invalid input or failed writes preserve the previous saved configuration.
The file is INI with `[audio]`, `[plugins]`, `[interface]`, `[editing]`,
`[project_defaults]` and `[transport]` sections. Save uses a mode-0600 temporary
file, flushes and fsyncs it, then atomically renames it. Unknown keys/sections,
duplicate keys, invalid ranges, embedded NULs and files larger than 64 KiB are
rejected. Blank lines and full-line `;` / `#` comments are supported. Strings
may be quoted, with `\\` and `\"` escapes. Apply rewrites comments and formatting.
| Section | Settings and defaults |
| --- | --- |
| audio | `client_name="xtc"`, `max_frames=65536`, `channels=2`, `transition_ms=5`, `offline_rate=48000`, `recording_seconds=2` |
| plugins | `clap_paths`, `vst2_paths`, `vst3_paths`, `ladspa_paths`: colon-separated `~/.clap`, `~/.vst`, `~/.vst3`, `~/.ladspa`, followed by corresponding `/usr/local/lib/` and `/usr/lib/` directories |
| interface | `width=1280`, `height=800`, `piano_width=900`, `piano_height=620`, `refresh_ms=100` |
| editing | `sequencer_snap=240`, `piano_snap=240`, `sequencer_zoom=48`, `clip_ticks=3840`, `note_ticks=960`, `note_velocity=100`, `note_channel=0`, `filter_channel=-1`, `filter_low=0`, `filter_high=127` |
| project_defaults | `tempo=120000` (milli-BPM), `volume_cc=7`, `pan_cc=10`, `sustain_cc=64` (128 disables a mapping) |
| transport | `loop_start=0`, `loop_end=3840`, `loop_enabled=0` |
Audio client name, block capacity, output channels and fades take effect on
**Reconnect**. JACK chooses the actual device, rate and hardware buffer size;
these remain JACK server settings. Offline rate is used when disconnected.
Recording capacity applies to the next take. Tempo and CC maps apply to **New**
projects. Track mix, routing, instruments, plugin parameters, automation and
musical content remain project data, saved through Save Project. Internal
safety limits and plugin ABI requirements are not runtime preferences.
Snap accepts 1 (Off), 120, 160, 240, 320, 480, 960, 1920 or 3840 ticks.
Zoom accepts 24, 48, 96, 192 or 384 pixels per beat. MIDI channels are zero-based;
filter channel -1 shows all channels. Window sizes, grids and filters currently
in use populate Preferences when it opens; press Apply to persist them.
Plugin selection now opens a library of `.clap` and `.so` files from the search
paths. `~/` expands to the home directory; empty/nonabsolute roots are skipped.
Traversal does not follow directory symlinks and stops at depth six, 4096 entries
or 512 files. Linux VST3 bundle binaries are found inside bundle directories.
Listing reads filenames only. **Inspect plugin** explicitly loads the selected
trusted binary through the existing descriptor chooser; **Browse files** allows
other locations. It is not a plugin certification or isolation service.
## Loop playback
**Shift-drag the sequencer ruler** to select a region using the selected Snap.
Dragging in either direction works. A band shows the region; **Loop** enables
or disables it. **Loop range...** also accepts start and end in ticks. Selection
and toggling take effect immediately; Apply in Preferences persists the region.
The region is global application configuration and is not stored in `.xtc`
projects. Changing a region during playback stops, reconfigures and restarts the
backend. Selecting a region does not itself enable looping.
Playback wraps at the exclusive end sample, including inside an audio block,
and starts at the region beginning when the transport lies outside the region.
The region follows the current project's tempo. Enabled regions must span at
least 64 samples at the active rate. Scheduled MIDI uses the existing seek/reset
and note-chase behavior at a wrap. Effects retain tails/state. No loop-boundary
crossfade is added, so discontinuous audio can click. Recording requires looping
to be disabled. Export retains its explicit linear range.
Validation covers settings roundtrips, malformed/duplicate/overflow/NUL input,
private permissions, save failure, GUI Apply/Close/reload and plugin listing,
plus JACK callback wrap positions and existing GUI/core regressions. Tests use
temporary configuration paths and do not write the user's configuration.

View file

@ -74,3 +74,8 @@ and trims, MIDI double-click editing, a piano velocity lane/slider and per-track
instrument dropdowns. Snap preferences are UI state; velocity and instrument
bindings use the existing v12 format. No new dependency or engine feature is
required. See WORKSPACE.md for supported divisions, controller semantics and limits.
Phase 35 adds Edit / Preferences with an Apply button and `~/.xtc.conf` INI
persistence, configurable plugin search paths and a file library, plus sequencer
ruler loop selection and sample-boundary playback wrapping. See PREFERENCES.md
for settings, reconnect behavior and looping limits.

View file

@ -989,6 +989,30 @@ patches control its audible response. See WORKSPACE.md and MIDI.md.
---
## Phase 35 — Application Preferences and Sequencer Looping
### Goal
Persist application preferences in `~/.xtc.conf` and provide selectable sequencer loops.
### Tasks and acceptance criteria
- [x] Add a flat, tabbed Edit / Preferences dialog with Apply and Close.
- [x] Validate and atomically save all preference tabs as a private INI file; load at startup.
- [x] Expose audio, interface, editing, new-project, plugin-path and transport settings.
- [x] List plugin files from configurable CLAP, VST2, VST3 and LADSPA search paths without executing modules.
- [x] Select a snapped loop range by Shift-dragging the ruler or entering tick bounds.
- [x] Toggle looping and split audio processing at exact sample boundaries.
- [x] Preserve real-time constraints and reject recording with looping enabled.
- [x] Verify persistence, invalid input, GUI Apply/reload, backend wraps and existing regressions.
### Implementation status
Implemented without dependencies or a project format change. Application settings
are saved only with Apply; audio connection options take effect on Reconnect and
project defaults on New. Existing projects retain their musical data. Loop ranges
are global preferences, not project data. Enabled loops require at least 64 samples.
See [PREFERENCES.md](PREFERENCES.md) for configuration, workflow and limitations.
---
## Recommended Execution Order
Reorder the phases when the audit reveals a blocking dependency. Do not mark a phase complete merely because code exists: its acceptance criteria must be demonstrated by builds, tests, or documented manual verification.