Skip to content

New Smplr player#115

Merged
danigb merged 15 commits intomainfrom
feat/new-smplr-player
Feb 19, 2026
Merged

New Smplr player#115
danigb merged 15 commits intomainfrom
feat/new-smplr-player

Conversation

@danigb
Copy link
Owner

@danigb danigb commented Feb 19, 2026

New Smplr Player

Co-authored with Claude

Summary

Replaces the old DefaultPlayer / RegionPlayer audio stack with a new
Smplr player. All nine instruments are migrated to the new player. The public API is
unchanged for most instruments; the main additions are onStart / onEnded note callbacks, a onLoadProgress callback,
and a loadProgress getter available on every instrument.


What changed

New player: src/smplr/

A complete rewrite of the audio playback layer, built from scratch with full unit test
coverage.

File Role
smplr.ts Main class. Accepts SmplrJson + options; exposes start, stop, disconnect, loadProgress, output
types.ts Public type surface: SmplrJson, SmplrGroup, SmplrRegion, NoteEvent, StopTarget, LoadProgress, PlaybackParams
region-matcher.ts Stateless note → region lookup. Handles key/velocity/CC ranges, round-robin sequencing, exclusive groups
voice.ts Single playing note. Wraps AudioBufferSourceNode with attack/release envelope, detune, LPF, looping
voice-manager.ts Tracks active voices; stopById, stopGroup, stopAll with optional scheduled time
scheduler.ts Interval-based lookahead scheduler. Queues future start events and dispatches them on time
sample-loader.ts Fetches and decodes sample buffers from SmplrJson; accepts pre-built Map<string, AudioBuffer> to skip fetch
params.ts Resolves per-voice playback params from the three-level hierarchy: defaults → group → region → note event
sfz-convert.ts Converts SFZ / websfz region descriptors to SmplrJson
websfz-convert.ts Converts .websfz.json format to SmplrJson
utils.ts spreadKeyRanges helper for building key-mapped region arrays
channel.ts / signals.ts / volume.ts / midi.ts / sorted-queue.ts / load-audio.ts / connect.ts Moved from src/player/ (unchanged)

New capabilities vs. the old stack:

  • onStart(event) / onEnded(event) note lifecycle callbacks — settable globally on
    instrument options and/or per-note on the event object; both levels are composed when set
    together. onStart fires once per note event (at scheduler dispatch time); onEnded fires
    once per matched voice when its AudioBufferSourceNode ends.
  • onLoadProgress({ loaded, total }) callback + loadProgress getter — total is known before
    loading starts, enabling determinate progress bars
  • MIDI CC range matching (ccRange) for sustain-pedal-style region gating
  • Velocity curve support (ampVelCurve) per region
  • Exclusive group / off-by voice stealing
  • trigger: "first" | "legato" region triggers
  • Round-robin sequencing (seqPosition / seqLength)
  • Instruments share a SampleLoader or Scheduler via options for cache reuse and
    coordinated timing

Instrument migrations

Every instrument now delegates to a private #smplr: Smplr instance. The public start /
stop / disconnect / output / load interface is preserved on all instruments.

Instrument Notes
SplendidGrandPiano notesToLoad subset loading preserved; hasLoops flag preserved
Soundfont Loop data loading preserved; hasLoops flag preserved
Sampler Accepts AudioBuffer values directly in buffers map (new)
DrumMachine getGroupNames, getSampleNames, getSampleNamesForGroup API unchanged
ElectricPiano Via SfzSampler base class (shared with Mallet, Mellotron, Smolken, Versilian)
Mallet Via SfzSampler
Mellotron Via SfzSampler
Smolken Via SfzSampler
Versilian Via SfzSampler
Soundfont2Sampler Now uses Smplr.loadInstrument(json, buffers) with pre-decoded AudioBuffer map

Removed

  • src/player/region-player.ts — no longer imported anywhere
  • src/tests/splendid-grand-piano.spec.ts — replaced by src/splendid-grand-piano.test.ts

Tests

All 15 test suites pass (237 tests total). New suites added for every component:

src/smplr/params.test.ts          — param resolution hierarchy
src/smplr/region-matcher.test.ts  — note/velocity/CC/round-robin matching
src/smplr/sample-loader.test.ts   — fetch, decode, pre-loaded buffer path
src/smplr/scheduler.test.ts       — lookahead scheduling, cancel
src/smplr/smplr.test.ts           — integration: load → start → stop → disconnect,
                                     onStart/onEnded global + per-note + composition
src/smplr/voice.test.ts           — envelope, loop, LPF
src/smplr/voice-manager.test.ts   — stopById, stopGroup, stopAll, scheduled stop
src/splendid-grand-piano.test.ts  — migration regression tests

Documentation

  • README.md — new Load progress section with onLoadProgress and loadProgress examples;
    onLoadProgress added to the shared options table

Site

  • site/pages/test.tsx — new manual test page at /test. Single ▶/⏸ button cycles through
    all 8 instruments: loads a random preset, shows live Loading… N / M progress, plays a
    chromatic scale (C3–C5, 200 ms/note, random velocity) or each drum group in sequence.
    Each note uses onStart/onEnded callbacks: the log line shows ▶ C4 vel:80 on start
    and updates to ■ C4 vel:80 when the audio ends, visually confirming both callbacks fire.
    Pause exits within one note's delay via AbortController.
  • package.json — added testPathIgnorePatterns: ["/site/"] so Jest does not pick up
    Next.js pages whose filename contains test.

Breaking changes

  • Soundfont2Sampler: the public player: RegionPlayer property is removed.
    Use output, start, and stop instead (already the documented API).
  • Soundfont2Options no longer extends RegionPlayerOptions. Only destination,
    volume, and velocity are accepted (the options RegionPlayer exposed beyond those
    were implementation details not reachable from Soundfont2Sampler).

@danigb danigb merged commit f21d85e into main Feb 19, 2026
1 check passed
@danigb danigb deleted the feat/new-smplr-player branch February 19, 2026 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant