Files
skills/docs/media/audio/audio-timeline/src/engine/engine.md
2026-05-29 08:20:56 +01:00

2.2 KiB

TimelineEngine

(no submodules)

Purpose

TimelineEngine is the central state container for the @suaveplan/audio-timeline package. It owns the multi-track arrangement, playback transport, mixer state, zoom/selection view state, and a full undo/redo history. All rendering is delegated to a pluggable TimelineAdapter, keeping the engine headless and suitable for both web and native targets.

Features

  • Track CRUD: setTracks, addTrack, removeTrack, updateTrack, selectTrack
  • Clip CRUD: addClip, removeClip, moveClip, trimClip, splitClip
  • Transport: play, pause, stop, seek guarded by an internal FSM
  • Mixer: setMasterVolume, setTrackVolume, setTrackMute, setTrackSolo, setTrackPan
  • View: zoomIn, zoomOut, setZoomLevel, setSelection, setLoopRegion, setLoopEnabled
  • Undo/redo with transaction support (beginTransaction, commitTransaction, abortTransaction)
  • Serialization via toJSON and static fromJSON
  • Typed events via on / off
  • OTel spans and counters for clip operations and transport

Usage

Basic

import { TimelineEngine, NullTimelineAdapter } from "@suaveplan/audio-timeline";

const engine = new TimelineEngine({ sampleRate: 44100 });
engine.setAdapter(new NullTimelineAdapter());
engine.addTrack({ name: "Drums" });
engine.play();

Advanced

engine.beginTransaction();
engine.addClip(trackId, clip);
engine.moveClip(trackId, clip.id, 500);
engine.commitTransaction();
engine.undo();

API Reference

TimelineEngine exposes the full public surface documented in the spec. Key getters: getState() returns an immutable snapshot; getCurrentTime() reads from the adapter; getTempo() returns current BPM.

Implementation Notes

The engine is deliberately headless. It does not touch the DOM or Web Audio directly; every output goes through the adapter. State mutations push Command<Track[]> instances onto an UndoStack. On undo/redo, if the track count is unchanged the engine calls adapter.updateTrack per changed track instead of adapter.setTracks to avoid playback interruption. Events are emitted through an EventBus so UI layers can subscribe to granular changes without polling.