Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# `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
```ts
import { AudioCLI } from "@suaveplan/audio-cli";
const cli = new AudioCLI();
await cli.play("input.wav"); // noop on a headless host
```
## Usage: Advanced
```ts
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.