nativeaudio

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

commit d5dde756fc3200d476325eda5c69f54e6ff9b4d4
parent bf19fbe42f8f291e04a1ded34c3261a5c18007c3
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Fri, 18 Sep 2026 13:16:18 -0400

mf: pin the decoder output to 16 bits per sample

Asking the source reader only for PCM lets it hand back whatever width
the source uses, so a 24-bit WAV decoded to 24-bit. That breaks the
s16le output the package documents and Format.BytesPerSample promises:
playback rejected it outright, and anything treating the bytes as int16
would have read garbage.

Diffstat:
Minternal/mf/mf.go | 21+++++++++++++++++++++
1 file changed, 21 insertions(+), 0 deletions(-)

diff --git a/internal/mf/mf.go b/internal/mf/mf.go @@ -234,6 +234,13 @@ func configureAudioStream(pReader *IMFSourceReader) error { return fmt.Errorf("setting sub type: %w", err) } + // Pin the sample size. Asking only for PCM lets the reader hand back + // whatever width the source happens to use, so a 24-bit file decodes + // to 24-bit and breaks the s16le output this package promises. + if err := pPartialType.SetUINT32(&MF_MT_AUDIO_BITS_PER_SAMPLE, 16); err != nil { + return fmt.Errorf("setting bits per sample: %w", err) + } + // Select the first audio stream, and deselect all other streams. if err := pReader.SetStreamSelection(MF_SOURCE_READER_ALL_STREAMS, false); err != nil { return fmt.Errorf("deselecting audio streams: %w", err) @@ -776,6 +783,20 @@ func (v *IMFMediaType) SetGUID(guid *GUID, value *GUID) error { return nil } +// SetUINT32 stores an unsigned 32-bit attribute on the media type. +func (v *IMFMediaType) SetUINT32(guid *GUID, value uint32) error { + r, _, _ := syscall.SyscallN( + v.VTable.SetUINT32, + uintptr(unsafe.Pointer(v)), + uintptr(unsafe.Pointer(guid)), + uintptr(value), + ) + if r != S_OK { + return MFErr{Code: r} + } + return nil +} + func (v *IMFMediaType) GetUINT32(guid *GUID, punValue *uint32) error { r, _, _ := syscall.SyscallN( v.VTable.GetUINT32,