Skip to content

Releases: naomiaro/waveform-playlist

v9.5.1

08 Mar 07:37

Choose a tag to compare

Spectrogram Performance & Reliability

Performance

  • Worker pool — Parallel per-channel FFT computation (~1.5s instead of ~2.9s for stereo). Pool size defaults to 2; configurable via workerPoolSize prop. FFT fan-out capped to actual channel count (excess workers sit idle).
  • Three-tier rendering — Viewport-first paint, then buffer zone, then background batches
  • Generation-based abort — Cancels stale FFT requests on scroll, preventing queue blocking
  • LRU cache — 16-entry FFT cache prevents recomputation on scroll-back
  • Lazy per-batch FFT — Bounded memory per render batch, prevents OOM on 1hr+ files
  • SpectrogramAbortError — Type-safe abort detection via instanceof instead of string matching

Cleanup

  • Fix canvasIds[i] undefined variable in DPR scaling error path
  • Remove dead spectrogramDataMap from SpectrogramIntegration interface
  • Use string interpolation for all console.error calls

Documentation

  • Update llm-reference.md with current SpectrogramWorkerApi interface
  • Add workerPoolSize, SpectrogramAbortError, createSpectrogramWorkerPool to docs
  • Add spectrogram architecture decisions to packages/spectrogram/CLAUDE.md

v9.5.0

08 Mar 04:20

Choose a tag to compare

What's New

  • Multi-channel recording pipeline — Recording now captures all channels from the microphone stream with per-channel peaks. Channel count is auto-detected from the stream via getSettings().channelCount.
  • Configurable peak bit depthRecordingOptions accepts bits?: 8 | 16, flowing through recordingState.bits to the renderer.

Bug Fixes

  • AudioWorklet buffer overflow — At 44100Hz, the 128-sample AudioWorklet quantum doesn't divide evenly into the ~16ms buffer (705 samples), causing ~9% of recorded samples to be silently dropped. Fixed with a loop that handles frame boundary crossings.
  • Real AudioContext sample rateuseAudioTracks now passes the actual audioContext.sampleRate to track building instead of always falling back to a hardcoded 48000.
  • Canvas flickerChannel, PianoRollChannel, and SpectrogramChannel switched from useEffect to useLayoutEffect for canvas drawing, preventing clearRect from being visible for one frame.
  • Recording preview width syncdurationSamples uses duration * sampleRate (zoom-independent) instead of peaks-derived calculations that depend on samplesPerPixel.
  • Mono consistency — Live preview and post-recording paths both respect the mono flag consistently.

Full Changelog: v9.4.1...v9.5.0

v9.4.1

08 Mar 00:13

Choose a tag to compare

Bug Fix

  • Fix audio bleeding between pagesTonePlayout.dispose() now stops the Tone.js Transport and all active AudioBufferSourceNodes before cleanup. Previously, audio continued playing through the global AudioContext after provider unmount (e.g., during SPA client-side navigation). (#312)
  • Resilient cleanupstop() and dispose() now guard per-track operations with individual try-catch blocks, ensuring one track failure doesn't skip cleanup of remaining tracks.

v9.4.0

07 Mar 23:12

Choose a tag to compare

What's New

  • renderPlayhead prop for MediaElementMediaElementWaveform and MediaElementPlaylist now accept a renderPlayhead prop, matching the existing API on Waveform. Pass PlayheadWithMarker or a custom component for a triangle-marker playhead.

  • Hook isolation for custom playheads — Both PlaylistVisualization (WebAudio) and MediaElementPlaylist now wrap renderPlayhead calls in dedicated components (CustomPlayhead, CustomMediaElementPlayhead), preventing React's "Rendered more hooks" error when the prop is conditionally provided.

  • Time display fix — Media element example now uses currentTimeRef with a requestAnimationFrame loop for smooth 60fps time updates during playback.

  • Stem tracks custom playhead — Stem tracks example now uses PlayheadWithMarker to exercise the WebAudio custom playhead path.

  • Media Element Playout guide — New documentation page covering usage, custom playheads, and the 4 context hooks.

Full Changelog

v9.3.3...v9.4.0

v9.3.3

07 Mar 20:58

Choose a tag to compare

New

  • KeyboardShortcuts — Self-closing component for declarative keyboard shortcut setup. Eliminates ~100 lines of boilerplate. Three boolean props (all default false):
    • playback — Space (play/pause), Escape (stop), 0 (rewind)
    • clipSplitting — 's' key splits clip at playhead
    • annotations — Arrow nav, boundary editing, Enter to play
    • additionalShortcuts — Append custom shortcuts
<KeyboardShortcuts playback clipSplitting />

Install

npm install @waveform-playlist/browser@9.3.3

v9.3.2

07 Mar 20:53

Choose a tag to compare

New

  • KeyboardShortcuts — Self-closing component for declarative keyboard shortcut setup. Eliminates ~100 lines of boilerplate across examples. Three boolean props (all default false):
    • playback — Space (play/pause), Escape (stop), 0 (rewind)
    • clipSplitting — 's' key splits clip at playhead
    • annotations — Arrow nav, boundary editing, Enter to play
    • additionalShortcuts — Append custom shortcuts
    • enabled — Disable all shortcuts
<KeyboardShortcuts playback clipSplitting />

Install

npm install @waveform-playlist/browser@9.3.2

v9.3.1

07 Mar 20:22

Choose a tag to compare

New

  • ClearAllButton — Reusable button component that stops playback before clearing tracks. Fixes a bug where clearing all tracks during playback left orphaned audio continuing. Accepts onClearAll callback prop.

Install

npm install @waveform-playlist/browser@9.3.1

v9.3.0

07 Mar 10:11

Choose a tag to compare

ClipInteractionProvider

New declarative wrapper that encapsulates all clip drag/move/trim/snap/collision setup, replacing ~120 lines of boilerplate per interactive example.

Before

<DragDropProvider
  sensors={sensors}
  onDragStart={onDragStart}
  onDragMove={onDragMove}
  onDragEnd={onDragEnd}
  modifiers={[RestrictToHorizontalAxis, SnapToGridModifier.configure({...}), ClipCollisionModifier.configure({...})]}
  plugins={noDropAnimationPlugins}
>
  <Waveform showClipHeaders interactiveClips />
</DragDropProvider>

After

<ClipInteractionProvider snap>
  <Waveform showClipHeaders />
</ClipInteractionProvider>

What's new

  • ClipInteractionProvider — wraps DragDropProvider with pre-wired sensors, handlers, modifiers, and snap logic
  • snap boolean prop — auto-detects beats vs timescale snapping from BeatsAndBarsProvider context
  • Auto-enables interactiveClips on descendant Waveform via context
  • useClipInteractionEnabled — hook to check if inside a ClipInteractionProvider
  • onTracksChange exposed from usePlaylistData() — no more prop threading for drag handlers
  • Custom Drag Setup guide — manual DragDropProvider setup for power users

v9.2.1

07 Mar 08:42

Choose a tag to compare

Features

  • scaleMode prop on BeatsAndBarsProvider — set to 'temporal' to show minutes:seconds timescale while keeping snap-to-grid active. Defaults to 'beats'. This avoids mounting/unmounting the provider when switching scale modes.

Fixes

  • Resolve website type errors for @docusaurus/Head, Link, and Layout
  • Remove stray timescale prop from <Waveform> (belongs on provider)
  • Replace duplicated progressive loading code block with link to guide

Docs

🤖 Generated with Claude Code

v9.2.0

07 Mar 08:22

Choose a tag to compare

Beats & Bars

Musical timescale with bar and beat markers, PPQN-based snap-to-grid, and configurable time signatures. Closes #226.

Features

  • BeatsAndBarsProvider — context for BPM, time signature, and snap config (@waveform-playlist/ui-components)
  • SmartScale beats/bars mode — timescale renders bar and beat markers instead of minutes:seconds
  • SnapToGridModifier — snaps clip absolute position to beat or bar grid boundaries (@waveform-playlist/browser)
  • PPQN math utilitiesticksPerBeat, ticksPerBar, samplesToTicks, ticksToSamples, snapToGrid (@waveform-playlist/core)
  • Configurable PPQN — defaults to 192 (Tone.js transport resolution), configurable for custom grids
  • deferEngineRebuild prop — prevents double engine rebuilds during progressive loading
  • Immediate mode (useAudioTracks) — placeholder tracks render instantly, peaks fill in as audio decodes

Bug Fixes

  • Null filter bug in useAudioTracks — loose equality (!= null) to catch both null and undefined
  • Initial mount flash in immediate mode — reordered hooks so useState initializes from useMemo

Docs

  • New Beats & Bars guide
  • Immediate Mode section in Loading Audio guide
  • deferEngineRebuild prop documented in WaveformPlaylistProvider API
  • Interactive example with BPM, time signature, and snap controls
  • PPQN explainer with BPM relationship on the example page

🤖 Generated with Claude Code