Skip to content

Programmatic Track & Clip API

Choose a tag to compare

@naomiaro naomiaro released this 30 Apr 06:38
· 5 commits to main since this release

Summary

Adds an imperative API on <daw-editor> for tracks and clips, wires <daw-clip> lifecycle events so dynamic DOM mutation works alongside the imperative methods, and adds indefinite-playback so the ruler renders before any audio is loaded.

Closes #381.

Published Packages

Package Version Change
@dawcore/components 0.0.13 Programmatic track/clip API + indefinite-playback + helper refactor

Programmatic API

Method Behavior
editor.ready() Builds engine eagerly without requiring a track
editor.addTrack(config) Builds and appends a <daw-track>, returns the element when loaded
editor.updateTrack(id, partial) Mutates reflected attrs on the <daw-track> (or descriptor + engine for non-DOM tracks)
editor.removeTrack(id) Removes via MutationObserver, falls back for non-DOM tracks
editor.addClip(trackId, config) Validates src, builds and appends a <daw-clip>, returns the clipId
editor.updateClip(trackId, clipId, partial) Sets reflected props on the <daw-clip>
editor.removeClip(trackId, clipId) Removes the <daw-clip> DOM element

Both declarative DOM mutation and these methods feed the same load pipeline — descriptors, peaks, and clip buffers populate identically.

Declarative DOM parity

<daw-clip> now dispatches daw-clip-connected (deferred via setTimeout(0)) and daw-clip-update (post-render, includes trackId resolved at dispatch). The editor handles both events plus clip-removal via the existing MutationObserver. Engine clip ids are aligned with <daw-clip>.clipId so DOM and engine refer to the same id.

indefinite-playback for empty editors

New boolean attribute. When set, the timeline fills the visible viewport even with no clips. The ruler covers max(naturalDuration, viewportWidth) so a short clip on an empty editor doesn't shrink the ruler. An empty controls-column placeholder prevents horizontal shift when the first track loads. ViewportController gains a ResizeObserver for window-resize support.

New Example

  • examples/dawcore-native/programmatic.html — Imperative addTrack / addClip / updateClip / removeClip plus declarative DOM mutation, side-by-side. Uses indefinite-playback so the ruler renders from the start.

Usage

import '@dawcore/components';
import { NativePlayoutAdapter } from '@dawcore/transport';

const editor = document.querySelector('daw-editor');
editor.adapter = new NativePlayoutAdapter(new AudioContext());

// Build engine before any track exists — useful for wiring analyzers,
// effects, or master taps before content arrives.
await editor.ready();

// Imperative track creation
const track = await editor.addTrack({
  name: 'Drums',
  volume: 0.8,
  clips: [{ src: '/audio/drums.opus', start: 0 }],
});

// Imperative clip mutation
const clipId = await editor.addClip(track.trackId, {
  src: '/audio/snare.opus',
  start: 4,
});
editor.updateClip(track.trackId, clipId, { start: 6, gain: 0.5 });
editor.removeClip(track.trackId, clipId);

// indefinite-playback in HTML
// <daw-editor timescale indefinite-playback>...</daw-editor>

Internal Refactor

  • Extracted _resolvePeaks and _finalizeAudioClip helpers to consolidate the per-clip load pipeline previously duplicated between _loadTrack and _loadAndAppendClip. _finalizeAudioClip self-purges its caches on generatePeaks failure so callers don't leak.
  • ClipDescriptor is now a discriminated union: DomClipDescriptor (kind: 'dom', clipId) | DropClipDescriptor (kind: 'drop'). Replaces the optional clipId? field. New exported isDomClip(c) type predicate.
  • Per-clip isolation in _loadTrack: a single bad clip dispatches daw-clip-error and skips, rather than aborting the whole track and leaking earlier clips' cache writes.
  • Many silent paths now warn (unknown removeTrack/removeClip/updateClip ids, DOM/engine clip-id misalignment, etc.).

Breaking Changes (@dawcore/components 0.0.x line)

  • ClipConfig.src is now required.
  • ClipDescriptor.clipId? replaced with discriminated union (kind: 'dom' | 'drop').

Test Plan

  • pnpm typecheck
  • pnpm lint (0 errors) ✓
  • cd packages/dawcore && npx vitest run374/374 tests pass (was 320 before, +54 new test cases)

Documentation

PRs

  • #382 — programmatic track & clip API + indefinite-playback
  • #383 — extract clip-load helpers + discriminated ClipDescriptor

Deferred

  • Engine-side fade application from <daw-clip> fadeIn/fadeOut/fadeType properties — needs adapter-side investigation to know if Fade objects are honored by NativePlayoutAdapter and TonePlayoutAdapter.