Files
suaveplan/packages/media/audio/audio-cli/src/commands/play.md
2026-05-27 23:47:53 +01:00

2.0 KiB

commands/play — runPlay

Purpose

runPlay plays a file through an injected SpeakerAdapter. The CLI package intentionally carries no platform audio binding — naming a specific speaker binary (sox, mpv, ffplay) would couple the package to whichever the user has installed. Instead, the function exercises argument validation, the input load path, and the adapter dispatch, and consumers (or installer hooks) wire the actual hardware adapter behind the SpeakerAdapter interface.

A noopSpeaker is exported so headless servers, CI, and tests can run the play code path without audio output.

Features

  • Zod-strict PlayOptions accepting an injected SpeakerAdapter and an AbortSignal.
  • Default noopSpeaker adapter for headless environments.
  • Same input-error mapping (AUDIO_CLI.INPUT_NOT_FOUND) as the other load-driven commands.
  • Adapter failures wrap as AUDIO_CLI.PIPELINE_FAILED with the original cause attached.

Usage: Basic

import { AudioCLI } from "@suaveplan/audio-cli";

const cli = new AudioCLI();
await cli.play("input.wav");        // noop on a headless host

Usage: Advanced

import { runPlay } from "@suaveplan/audio-cli/commands/play";
import type { SpeakerAdapter } from "@suaveplan/audio-cli/commands/play";

const mySpeaker: SpeakerAdapter = {
    async play(doc, { signal }) { /* ...write doc to a DAC... */ },
};
await runPlay(cli, "song.flac", { speaker: mySpeaker });

API Reference

  • runPlay(cli, input, opts?): Promise<void>
  • PlayOptions — { speaker?, signal? }
  • SpeakerAdapter.play(doc, { signal? }): Promise<void>
  • noopSpeaker: SpeakerAdapter

Implementation Notes

The function deliberately structures the failure modes so a missing speaker falls through to the no-op rather than throwing — this matches audio play on a headless box: argv parses, the file decodes, and the command returns 0. Real hardware bindings ship as separate adapters consumers add at construction time via new AudioCLI({ ... }) or by passing opts.speaker directly.