Files
skills/docs/media/audio/audio-sprite-player/src/adapter/null/null.md
2026-05-29 08:20:56 +01:00

3.6 KiB

NullSpritePlaybackAdapter

(no submodules)

Purpose

NullSpritePlaybackAdapter is the recording adapter that conforms to the SpritePlaybackAdapter contract without producing any audio. It is the fixture every test in this package uses, and it is exported so downstream engine adapters can use it as the contract-conformance baseline before plugging in real audio output.

Every method on the adapter appends a typed entry to a public read-only calls array. Every play returns a SpritePlayHandle whose stop, setVolume, and isPlaying invocations are also recorded. The timestamps come from an injectable Clock, so tests using FakeClock (or any deterministic clock) produce identical traces on every run.

Features

  • Implements the full SpritePlaybackAdapter interface: init, play, dispose.
  • Records every method call with { kind, at, payload } where at is the clock reading and payload carries the relevant arguments.
  • Records every handle lifecycle event (stop with fadeOutMs, setVolume with value).
  • Honours AbortSignal: throws an aborted error when invoked with an already-aborted signal.
  • Pure — no platform globals, no side effects beyond the in-memory calls array.

Usage: Basic Example

import { NullSpritePlaybackAdapter, createSpritePlayer } from "@suaveplan/audio-sprite-player";

const adapter = new NullSpritePlaybackAdapter();
const player = createSpritePlayer({ adapter });
await player.load(sprite);
await player.play("hit");
console.log(adapter.calls.map((c) => c.kind));
// → ["init", "play"]

Usage: Advanced Example

import {
  createNullSpritePlaybackAdapter,
  createSpritePlayer,
} from "@suaveplan/audio-sprite-player";
import { FakeClock } from "@suaveplan/testing";

const fakeClock = new FakeClock({ startTime: 1_000_000 });
const adapter = createNullSpritePlaybackAdapter({
  clock: () => fakeClock.now(),
});
const player = createSpritePlayer({
  adapter,
  clock: () => fakeClock.now(),
  idGenerator: (() => {
    let i = 0;
    return () => `id-${++i}`;
  })(),
});

await player.load(sprite);
await player.play("hit");
fakeClock.advance(100);
await player.play("hit");
const playRecords = adapter.calls.filter((c) => c.kind === "play");
console.log(playRecords.length); // 2

API Reference

new NullSpritePlaybackAdapter(options?)

options.clock?: Clock — defaults to () => Date.now().

calls: ReadonlyArray<NullCall>

Append-only log. NullCall is { kind: "init" | "play" | "dispose" | "stop" | "setVolume"; at: number; payload?: unknown }.

init(sprite, signal?)

Records { kind: "init" }. Throws if signal.aborted.

play(entry, opts, signal?)

Records { kind: "play", payload: { entryName, id, opts } }. Returns a handle. Throws if signal.aborted.

dispose()

Records { kind: "dispose" }.

createNullSpritePlaybackAdapter(options?)

Functional alias for new NullSpritePlaybackAdapter(options).

Implementation Notes

  • The adapter is exported from the package root and from the ./adapter/null/null subpath so consumers can import it without pulling the rest of the surface.
  • It is a valid implementation of SpritePlaybackAdapter from the type level, so the adapter contract compiles from type-only imports smoke test in null.test.ts confirms a downstream engine could ship an adapter using nothing but import type from this package.
  • The handle returned by play carries the same id recorded in the play call's payload, so consumers can correlate handle lifecycle calls back to their originating play deterministically.