Skip to content

Undo

Session::checkpoint(label, key, plugins) pushes a full clone of the Project (with plug-in states captured when plugins is true) onto undo_stack before an edit, and clears the redo stack.

  • Coalescing: when key matches the top entry’s key and less than 1.5 s has passed since the last edit with that key, no new entry is made (the timer restarts). Fader drags use track:<id>:volume, the EQ eq:<id>, sends send:<id>:<slot>, nudges nudge:<ids>, the tempo tempo, and so on.
  • Limit: prefs.general.undo_limit (Settings › General, default 100) trims the oldest entries.
  • undo() / redo() swap the current project (with plug-in states captured) onto the other stack and call restore(target).
  • restore() diffs the target against the current project and touches only what differs: removed tracks are sent RemoveTrack; returning tracks are rebuilt with their plug-ins from the snapshot’s state; changed regions, EQ, automation and modes are resent; plug-in slots whose instance id changed are reinstantiated, bypass changes are sent; then the order, all parameters, Stereo Out’s inserts and the globals. Plug-ins that didn’t change keep running with their state. Freeze transitions go through restore_freeze.
  • Clips are never unloaded by undo: recordings and imports made after a snapshot stay in memory, so redo can bring their regions back.
  • VST3 editors: restore() leaves a plug-in that’s still there alone, so an edit in a VST3’s own editor can’t undo through the snapshot. Its entry carries the state to load as well (UndoEntry::plugin), and step_to swaps that with the plug-in’s present one, as it does the project. See Undo of editor edits.
  • frozen_guard(), called after every checkpointed edit, compares frozen tracks with the checkpoint; if their regions, plug-ins or EQ changed it restores them and, when nothing else changed, drops the now-empty undo step.
  • Opening or creating a project clears both stacks.

Rule for new features: anything new you send to the engine must also be restored by restore(), and built by build_track() / rebuild_graph() for project load.