Saving and loading¶
The alignment session is persisted as a single JSON file (Schema v1). Saving and loading go through alignment_tool.core.persistence, which is intentionally free of UI code so it can be round-tripped in tests and in downstream scripts.
Save (Ctrl+S)¶
File → Save Alignment… shows a standard file picker for *.json. After you choose a path:
- The full
AlignmentStateis serialized (see JSON schema for the exact layout). - A temp file is created next to the target (
{target}.{random}.tmp), the JSON is written withindent=2, UTF-8,ensure_ascii=False,allow_nan=False. os.replace(temp, target)renames the temp to the final path — an atomic replacement on all supported platforms. Either the old file is intact or the new file is intact; there is no partial-write window.- On success, the
_dirtyflag is cleared, the trailing*drops from the window title, and the status bar briefly showsSaved: /path/to/alignment.json.
If the write fails for any reason, the temp file is removed (best-effort) and the exception propagates through the standard error dialog router. Common failure modes:
PermissionError— the target folder is not writable.json.JSONDecodeError/ValueErrorviaallow_nan=False— should never happen with a validAlignmentState; indicates a bug.
Load (Ctrl+L)¶
File → Load Alignment… shows a file picker for *.json. The file is read in one shot and validated before any state is swapped in:
- UTF-8 decode and
json.load— ajson.JSONDecodeErrorbubbles up asCorruptAlignmentFileError. schema_versionis checked againstSCHEMA_VERSION = 1. A mismatch raisesUnsupportedSchemaVersionErrorwith both the found and expected version.- The dict is converted to dataclasses; missing or type-wrong fields raise
CorruptAlignmentFileError. _validate_stateruns post-rehydration — see JSON schema: validation. Any failure here raisesCorruptAlignmentFileError.- Path resolution: every file path stored in the JSON is joined with the saved
participant_folderif relative, or passed through if absolute/empty. See Moving participant folders for what happens when the folder no longer exists.
If the load succeeds, the new AlignmentState replaces the current session entirely (MainWindow._set_state), Level 1 renders its timeline, Level 2 is reset, and Save is enabled.
Schema v1 is self-contained¶
A Schema v1 file alone is enough to rehydrate a fully-functional session — you do not need to re-open the participant folder first. Every field the app needs at runtime is stored: MIDI tempos, ticks-per-beat, camera FPS, frame counts, unix timestamps, anchor pairs. The source media files are only re-read when you actively scrub a camera clip (via the cv2 frame worker) or inspect a MIDI file (via MidiAdapter).
This means:
- You can inspect a saved JSON on a machine without the source media and confirm what the session says happened.
- You can share the JSON with collaborators who have their own copy of the media at a different path — they'll hit the rebase dialog once on load (see Moving participant folders).
- You cannot load a pre-v1 file; there is no migration layer. If you have an older save format, the tool raises
UnsupportedSchemaVersionErrorimmediately.
Unsaved-changes protection¶
Three actions check _dirty and prompt Save/Discard/Cancel before proceeding if you have unsaved changes:
- Open Participant… — would replace the session.
- Load Alignment… — same.
- Exit / close window — would lose the session.
See Main window: Unsaved-changes prompt for the dialog's behaviour.
What the save captures¶
participant_id,participant_folderglobal_shift_secondsalignment_notes(reserved; currently always"")- All MIDI file descriptors with their timing metadata
- All camera file descriptors with their timing metadata and frame counts
- Every anchor on every camera clip
What the save does NOT capture¶
active_anchor_index— session-only (cleared on every load).- Level 2 marker state (
midi_marker,camera_marker) — session-only, not in any model. - UI state — zoom levels, pan offsets, selected bars, active panel, probe dot, mode (Independent vs Locked).
- Any decoded frames, note lists, or cached tempo tables — those are recomputed from the source files when needed.
Re-loading a session is deliberately a clean slate for UI state, with the alignment itself fully restored.