nativeaudio

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

commit 1f7e8a70d558e0b31085092b28648558188e9b21
parent 0c9c77611e8067c20f4e99b7fc374434a54904c0
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Wed, 16 Sep 2026 21:29:38 -0400

audio: use os.CreateTemp for the ffmpeg decode scratch file

The scratch file was named from a process-local counter, so two
processes decoding at once both wrote nativeaudio-1 and could corrupt
each other's input. os.CreateTemp guarantees a unique name and lets the
unused counter type go.

Diffstat:
Maudio_ffmpeg.go | 41+++++++++++++----------------------------
1 file changed, 13 insertions(+), 28 deletions(-)

diff --git a/audio_ffmpeg.go b/audio_ffmpeg.go @@ -12,10 +12,8 @@ import ( "fmt" "os" "os/exec" - "path/filepath" "strconv" "strings" - "sync" ) // FFmpegPlay an audio file with ffplay. @@ -65,26 +63,6 @@ func FFmpegLoad(path string) ([]byte, Format, error) { return buffer.Bytes(), f, nil } -var count = counter{} - -type counter struct { - count int - m sync.Mutex -} - -func (c *counter) Next() int { - c.m.Lock() - defer c.m.Unlock() - c.count++ - return c.count -} - -func (c *counter) Done() { - c.m.Lock() - defer c.m.Unlock() - c.count-- -} - // FFmpegDecode raw PCM with ffmpeg. // // ffmpeg -f m4a -i pipe: -f s16le - @@ -95,14 +73,21 @@ func (c *counter) Done() { // NOTE(jfm): unfortunately, some formats cannot be piped, so we will // create a temporary file instead. func FFmpegDecode(by []byte) ([]byte, Format, error) { - // id ensures that multiple concurrent tmp files do not collide. - id := count.Next() - tmp := filepath.Join(os.TempDir(), fmt.Sprintf("nativeaudio-%d", id)) - if err := os.WriteFile(tmp, by, 0644); err != nil { + // CreateTemp picks a unique name, so concurrent decodes in this or any + // other process cannot collide. + tmp, err := os.CreateTemp("", "nativeaudio-*") + if err != nil { return nil, Format{}, fmt.Errorf("creating tmp file: %w", err) } - defer os.Remove(tmp) - return FFmpegLoad(tmp) + defer os.Remove(tmp.Name()) + if _, err := tmp.Write(by); err != nil { + tmp.Close() + return nil, Format{}, fmt.Errorf("writing tmp file: %w", err) + } + if err := tmp.Close(); err != nil { + return nil, Format{}, fmt.Errorf("closing tmp file: %w", err) + } + return FFmpegLoad(tmp.Name()) } // probe queries the format information for a given audio file by parsing