nativeaudio

audio playback for Go
Log | Files | Refs | README | LICENSE

commit c7adf6fb145b50d5e016235d8f8c92b138465526
parent 561ed97c1e04e65ca49c0aaae4f2f03e0524b342
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Mon, 21 Sep 2026 07:30:21 -0300

docs: record that every native backend now streams

Both items the TODO tracked are done, so the section goes with them.

Diffstat:
MREADME.md | 9+++------
Maudio.go | 11++++++-----
2 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md @@ -75,8 +75,9 @@ play.File("audio.m4a") - `Decoder.StreamFile(path)` and `Decoder.Stream(data)` return a `Stream`, an `io.Reader` over the same PCM, so a long track never has to sit in memory whole. `Format` is known before the first read. Close it when - done. Windows and Linux decode incrementally, and the sub-process - backend pipes; macOS currently decodes up front and serves from memory. + done. Every native backend decodes incrementally, and the sub-process + backend pipes when given a file; only decoding from memory without a + native backend buffers up front. - `Format` reports `SampleRate`, `Channels` and `BytesPerSample`, which is always 2. - A `Decoder` is safe for concurrent use, and `Close` waits for decodes @@ -161,7 +162,3 @@ ship by default. | HE-AAC v2 | yes, stereo | yes | yes | | xHE-AAC | yes, Windows 11 | yes | partial | | AAC-LD and AAC-ELD | no | yes | yes | - -## TODO - -- [ ] macOS: decode incrementally rather than buffering behind `Stream` diff --git a/audio.go b/audio.go @@ -129,9 +129,10 @@ type Format struct { // Stream decodes compressed audio held in memory, returning PCM through // an [io.Reader] rather than a single buffer. // -// The returned Stream must be closed. The Windows and Linux backends -// decode incrementally; macOS and the subprocess fallback decode up -// front and serve from memory, which is correct but saves nothing. +// The returned Stream must be closed. Every native backend decodes +// incrementally; only the subprocess fallback decodes up front and +// serves from memory, because ffmpeg cannot read every container this +// package supports from a pipe. func (d *Decoder) Stream(compressed []byte) (*Stream, error) { d.mu.RLock() defer d.mu.RUnlock() @@ -152,8 +153,8 @@ func (d *Decoder) Stream(compressed []byte) (*Stream, error) { // // The returned Stream must be closed. The Linux backend and the // subprocess fallback read the file directly, so the PCM is never held -// whole; the Windows backend reads the compressed file into memory -// first, which is small next to the PCM it avoids buffering. +// whole; Windows and macOS read the compressed file into memory first, +// which is small next to the PCM it avoids buffering. func (d *Decoder) StreamFile(path string) (*Stream, error) { d.mu.RLock() defer d.mu.RUnlock()