From 447aa72b0151afe11d3bab72b800ec1187c88940 Mon Sep 17 00:00:00 2001 From: Hiroki KUMAZAKI Date: Mon, 7 Sep 2026 04:41:36 +0900 Subject: [PATCH 1/3] all: fix non-logic mistakes in docs, comments, and names This batch fixes the documentation/comment/naming mistakes reported in the review spreadsheet, items #44, #48, #49 and #50. Items #46 and #47 from the same spreadsheet were already fixed by 901ef3a. Fixed where and how: - context.go (NewPlayer doc): the doc claimed that the returned player implements Player, BufferSizeSetter and io.Seeker, but NewPlayer returns the concrete type *Player and no BufferSizeSetter type exists. Reworded to describe the concrete type and its functions. - README.md (file streaming example): the commented-out seek example type-asserted player to io.Seeker, which does not compile because player is the concrete type *oto.Player. Call player.Seek directly. - README.md (advanced usage): the buffer size example type-asserted myPlayer to the nonexistent oto.BufferSizeSetter, and the explanation referred to a Player interface and a BufferSizeSetter interface that do not exist. Replaced with a direct SetBufferSize call and a concrete-type description. - player.go (SetVolume doc): the doc said the volume must be in the range of [0, math.MaxFloat32] and, at the same time, that a value out of the range is treated as 0. Removed the strict requirement so the doc matches the actual lenient behavior. - player.go (Close doc): fixed the malformed sentence after the version in the Deprecated notice: 'as of v3.4. you' became 'as of v3.4, you'. - all: renamed the mutexError type (formerly atomicError) in context.go and the eight driver files. The name atomicError suggested an atomic operation, but the implementation is a sync.Mutex with errors.Join, so the new name states what actually guards the error. --- README.md | 8 ++++---- context.go | 8 ++++---- driver_alsa_unix.go | 2 +- driver_android.go | 2 +- driver_darwin.go | 2 +- driver_pulseaudio_unix.go | 2 +- driver_unix.go | 2 +- driver_wasapi_windows.go | 2 +- driver_windows.go | 2 +- driver_winmm_windows.go | 2 +- player.go | 4 ++-- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 6c1fe44..a7c268f 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ func main() { } // Now that the sound finished playing, we can restart from the beginning (or go to any location in the sound) using seek - // newPos, err := player.(io.Seeker).Seek(0, io.SeekStart) + // newPos, err := player.Seek(0, io.SeekStart) // if err != nil{ // panic("player.Seek failed: " + err.Error()) // } @@ -209,13 +209,13 @@ Data is moved from io.Reader->internal buffer->audio device, and when the intern is not guaranteed, so there might be a small delay. The amount of data in the buffer can be retrieved using `Player.BufferedSize()`. -The size of the underlying buffer of a player can also be set by type-asserting the player object: +The size of the underlying buffer of a player can also be set by calling the player's `SetBufferSize` function: ```go -myPlayer.(oto.BufferSizeSetter).SetBufferSize(newBufferSize) +myPlayer.SetBufferSize(newBufferSize) ``` -This works because players implement a `Player` interface and a `BufferSizeSetter` interface. +`NewPlayer` returns a `*oto.Player`, which has functions like `SetBufferSize` and `Seek`. ## Crosscompiling diff --git a/context.go b/context.go index 0169c9c..0e554ee 100644 --- a/context.go +++ b/context.go @@ -134,7 +134,7 @@ func NewContext(options *NewContextOptions) (*Context, chan struct{}, error) { // // You cannot share r by multiple players. // -// The returned player implements Player, BufferSizeSetter, and io.Seeker. +// The returned player is a *Player, which has functions like SetBufferSize and Seek. // You can modify the buffer size of a player by the SetBufferSize function. // A small buffer size is useful if you want to play a real-time PCM for example. // Note that the audio quality might be affected if you modify the buffer size. @@ -173,14 +173,14 @@ func (c *Context) Err() error { return c.context.Err() } -type atomicError struct { +type mutexError struct { err error m sync.Mutex } // Join records err in addition to the errors recorded so far. A nil err is // ignored. -func (a *atomicError) Join(err error) { +func (a *mutexError) Join(err error) { if err == nil { return } @@ -190,7 +190,7 @@ func (a *atomicError) Join(err error) { a.err = errors.Join(a.err, err) } -func (a *atomicError) Load() error { +func (a *mutexError) Load() error { a.m.Lock() defer a.m.Unlock() return a.err diff --git a/driver_alsa_unix.go b/driver_alsa_unix.go index a3e223b..70e1f49 100644 --- a/driver_alsa_unix.go +++ b/driver_alsa_unix.go @@ -119,7 +119,7 @@ type alsaContext struct { cond *sync.Cond mux *mux.Mux - err atomicError + err mutexError } func newALSAContextImpl(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int) (*alsaContext, error) { diff --git a/driver_android.go b/driver_android.go index 846603c..31173fe 100644 --- a/driver_android.go +++ b/driver_android.go @@ -24,7 +24,7 @@ import ( type context struct { mux *mux.Mux - err atomicError + err mutexError m sync.Mutex } diff --git a/driver_darwin.go b/driver_darwin.go index 18515ee..159e7d5 100644 --- a/driver_darwin.go +++ b/driver_darwin.go @@ -126,7 +126,7 @@ type context struct { startRetryTimer *time.Timer mux *mux.Mux - err atomicError + err mutexError } // TODO: Convert the error code correctly. diff --git a/driver_pulseaudio_unix.go b/driver_pulseaudio_unix.go index e3ca3b0..2728033 100644 --- a/driver_pulseaudio_unix.go +++ b/driver_pulseaudio_unix.go @@ -35,7 +35,7 @@ type pulseContext struct { cond *sync.Cond mux *mux.Mux - err atomicError + err mutexError } func newPulseContext(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int, applicationName string) (*pulseContext, error) { diff --git a/driver_unix.go b/driver_unix.go index 19879f4..8707ccc 100644 --- a/driver_unix.go +++ b/driver_unix.go @@ -39,7 +39,7 @@ type context struct { backend unixBackend ready chan struct{} - err atomicError + err mutexError } func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int, applicationName string) (*context, chan struct{}, error) { diff --git a/driver_wasapi_windows.go b/driver_wasapi_windows.go index f9da6fa..b9602ad 100644 --- a/driver_wasapi_windows.go +++ b/driver_wasapi_windows.go @@ -107,7 +107,7 @@ type wasapiContext struct { bufferSizeInBytes int comThread *comThread - err atomicError + err mutexError suspended bool suspendedCond *sync.Cond diff --git a/driver_windows.go b/driver_windows.go index d091e3d..a90a982 100644 --- a/driver_windows.go +++ b/driver_windows.go @@ -36,7 +36,7 @@ type context struct { nullContext *nullContext ready chan struct{} - err atomicError + err mutexError } func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int, _ string) (*context, chan struct{}, error) { diff --git a/driver_winmm_windows.go b/driver_winmm_windows.go index 63ff8ad..5885fb5 100644 --- a/driver_winmm_windows.go +++ b/driver_winmm_windows.go @@ -79,7 +79,7 @@ type winmmContext struct { buf32 []float32 mux *mux.Mux - err atomicError + err mutexError loopEndCh chan error cond *sync.Cond diff --git a/player.go b/player.go index 8ec4e3d..af063bb 100644 --- a/player.go +++ b/player.go @@ -67,7 +67,7 @@ func (p *Player) Volume() float64 { return p.player.Volume() } -// SetVolume sets the current volume, which must be in the range of [0, math.MaxFloat32]. +// SetVolume sets the current volume in the range of [0, math.MaxFloat32]. // A volume larger than 1 amplifies the sound and might cause clipping. // A value out of the range, including NaN, is treated as 0. func (p *Player) SetVolume(volume float64) { @@ -109,7 +109,7 @@ func (p *Player) Seek(offset int64, whence int) (int64, error) { // // Close does nothing and always returns nil. // -// Deprecated: as of v3.4. you don't have to call Close. +// Deprecated: as of v3.4, you don't have to call Close. func (p *Player) Close() error { // (*mux.Player).Close() is called by the finalizer. Let's rely on it. return nil From 067fe6f711d02e8a60e4fe03c8b1eb23424192a2 Mon Sep 17 00:00:00 2001 From: Hiroki KUMAZAKI Date: Mon, 7 Sep 2026 09:47:17 +0900 Subject: [PATCH 2/3] all: fix typos, stale comments, and inconsistent names - context.go, player.go: fixed the Format docs ('32 bits floats little endian' -> '32-bit floats in little endian' and so on), 'Usual numbers' -> 'Typical values', the BufferSize doc wording, 'an error if it exists' -> 'an error, if any', the broken 'stop using r e.g., you want to close r' appositive, and 'buffer data' -> 'buffered data' in the BufferedSize doc. - README.md: added PlayStation 5, supported since b794c02 but never listed. Fixed 'not tested well' -> 'not well tested', the article mismatch in 'a Context and Players', and removed the confusing '(aka locations)'. - driver_darwin.go: the _AudioQueueNewOutput error message said 'AudioQueueNewFormat with StreamFormat', a name of no API in use. Fixed the maximum-delay comment: 6144 samples at 48000 Hz is 128[ms], not 100[ms]. - api_wasapi_windows.go, driver_wasapi_windows.go: renamed GetDefaultAudioEndPoint to GetDefaultAudioEndpoint to match the vtbl field and the actual Windows API. Removed the dead errFormatNotSupported sentinel, which nothing returns since the AUTOCONVERTPCM flags replaced the closest-format matching. Also fixed 'are unlikely supported' -> 'are unlikely to be supported'. - driver_winmm_windows.go: removed the stale 'Avoid goroutines on Windows' comment, which contradicts the loop goroutine the driver actually runs, and unified 'Queuing' to 'Queueing'. - api_winmm_windows.go: 'MMRESULT (%d)' -> 'MMRESULT(%d)' for consistency with the other error formats. - example: 'number of channel' -> 'number of channels', and reworded the 'Pin the players' comment. - internal/mux: 'must sync with' -> 'must be kept in sync with', 'in such case' -> 'in such a case', and the defaultBufferSize doc no longer refers to 'unreading', which v3 never does. Test comments: 'make a room' -> 'make room', 'tempt' -> 'prompt', and reworded the 'Pause not to resume' comment. - internal/oboe: removed the unused PlayerID typedef and the 'class Player;' forward declaration, both v2 leftovers. Fixed the garbled kStableRunDuration sentence, 'a reason that can pass', and the 'It is because' sentence. - .github/workflows/test.yml: 'at Visual Studio Code' -> 'in Visual Studio Code'. --- .github/workflows/test.yml | 2 +- README.md | 7 ++++--- api_wasapi_windows.go | 8 ++++---- api_winmm_windows.go | 2 +- context.go | 20 ++++++++++---------- driver_darwin.go | 4 ++-- driver_wasapi_windows.go | 12 +++++------- driver_winmm_windows.go | 5 +---- example/main.go | 4 ++-- internal/mux/mux.go | 6 +++--- internal/mux/mux_test.go | 6 +++--- internal/oboe/binding_android.cpp | 13 +++++-------- internal/oboe/binding_android.h | 2 -- player.go | 2 +- 14 files changed, 42 insertions(+), 51 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f11873b..3a628a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: run: | go build -v ./... # Compile without optimization to check potential stack overflow. - # The option '-gcflags=all=-N -l' is often used at Visual Studio Code. + # The option '-gcflags=all=-N -l' is often used in Visual Studio Code. # See also https://go.googlesource.com/vscode-go/+/HEAD/docs/debugging.md#launch and the issue hajimehoshi/ebiten#2120. go build "-gcflags=all=-N -l" -v ./... diff --git a/README.md b/README.md index a7c268f..3702e25 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,7 @@ A low-level library to play sound. - iOS - WebAssembly (no Cgo required) - Nintendo Switch +- PlayStation 5 - Xbox ## Prerequisite @@ -54,7 +55,7 @@ Add them to "Linked Frameworks and Libraries" on your Xcode project. ### Linux, FreeBSD, OpenBSD Oto uses PulseAudio on Linux and BSD systems via the pure-Go package `github.com/jfreymuth/pulse`, -though BSD systems are not tested well. +though BSD systems are not well tested. If the PulseAudio server is not discoverable automatically, set `PULSE_SERVER`. @@ -68,7 +69,7 @@ is enabled by default, need nothing extra. ## Usage -The two main components of Oto are a `Context` and `Players`. The context handles interactions with +The two main components of Oto are `Context` and `Player`. The context handles interactions with the OS and audio drivers, and as such there can only be **one** context in your program. From a context you can create any number of different players, where each player is given an `io.Reader` that @@ -116,7 +117,7 @@ func main() { // Usually 44100 or 48000. Other values might cause distortions in Oto op.SampleRate = 44100 - // Number of channels (aka locations) to play sounds from. Either 1 or 2. + // Number of channels to play sounds from. Either 1 or 2. // 1 is mono sound, and 2 is stereo (most speakers are stereo). op.ChannelCount = 2 diff --git a/api_wasapi_windows.go b/api_wasapi_windows.go index 679a768..83b53d2 100644 --- a/api_wasapi_windows.go +++ b/api_wasapi_windows.go @@ -486,18 +486,18 @@ type _IMMDeviceEnumerator_Vtbl struct { UnregisterEndpointNotificationCallback uintptr } -func (i *_IMMDeviceEnumerator) GetDefaultAudioEndPoint(dataFlow _EDataFlow, role _ERole) (*_IMMDevice, error) { +func (i *_IMMDeviceEnumerator) GetDefaultAudioEndpoint(dataFlow _EDataFlow, role _ERole) (*_IMMDevice, error) { var endPoint *_IMMDevice r, _, _ := syscall.Syscall6(i.vtbl.GetDefaultAudioEndpoint, 4, uintptr(unsafe.Pointer(i)), uintptr(dataFlow), uintptr(role), uintptr(unsafe.Pointer(&endPoint)), 0, 0) if uint32(r) != uint32(windows.S_OK) { if isWin32Err(uint32(r)) { - return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndPoint failed: %w", _E_NOTFOUND) + return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: %w", _E_NOTFOUND) } if isRPCErr(uint32(r)) { - return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndPoint failed: %w", _RPC_ERR(r)) + return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: %w", _RPC_ERR(r)) } - return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndPoint failed: HRESULT(%d)", uint32(r)) + return nil, fmt.Errorf("oto: IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: HRESULT(%d)", uint32(r)) } return endPoint, nil } diff --git a/api_winmm_windows.go b/api_winmm_windows.go index 821299c..65c9905 100644 --- a/api_winmm_windows.go +++ b/api_winmm_windows.go @@ -101,7 +101,7 @@ func (m _MMRESULT) Error() string { case _WAVERR_SYNC: return "WAVERR_SYNC" } - return fmt.Sprintf("MMRESULT (%d)", m) + return fmt.Sprintf("MMRESULT(%d)", m) } func waveOutOpen(f *_WAVEFORMATEX, callback uintptr) (uintptr, error) { diff --git a/context.go b/context.go index 0e554ee..df7a467 100644 --- a/context.go +++ b/context.go @@ -43,20 +43,20 @@ type Context struct { type Format int const ( - // FormatFloat32LE is the format of 32 bits floats little endian. + // FormatFloat32LE is the format of 32-bit floats in little endian. FormatFloat32LE Format = iota - // FormatUnsignedInt8 is the format of 8 bits integers. + // FormatUnsignedInt8 is the format of 8-bit integers. FormatUnsignedInt8 - // FormatSignedInt16LE is the format of 16 bits integers little endian. + // FormatSignedInt16LE is the format of 16-bit integers in little endian. FormatSignedInt16LE ) // NewContextOptions represents options for NewContext. type NewContextOptions struct { // SampleRate specifies the number of samples that should be played during one second. - // Usual numbers are 44100 or 48000. One context has only one sample rate. You cannot play multiple audio + // Typical values are 44100 or 48000. One context has only one sample rate. You cannot play multiple audio // sources with different sample rates at the same time. SampleRate int @@ -70,9 +70,9 @@ type NewContextOptions struct { // BufferSize specifies a buffer size in the underlying device. // // If 0 is specified, the driver's default buffer size is used. - // Set BufferSize to adjust the buffer size if you want to adjust latency or reduce noises. - // Too big buffer size can increase the latency time. - // On the other hand, too small buffer size can cause glitch noises due to buffer shortage. + // Set BufferSize to adjust the buffer size if you want to adjust latency or reduce noise. + // A buffer size that is too big increases the latency. + // On the other hand, a buffer size that is too small can cause glitch noises due to buffer shortage. BufferSize time.Duration // ApplicationName specifies the name of the client application. @@ -82,7 +82,7 @@ type NewContextOptions struct { // NewContext creates a new context with given options. // A context creates and holds ready-to-use Player objects. -// NewContext returns a context, a channel that closes when initialization finishes, and an error if it exists. +// NewContext returns a context, a channel that closes when initialization finishes, and an error, if any. // After the channel closes, call Context.Err to check whether initialization succeeded. // // Creating multiple contexts is NOT supported. @@ -97,7 +97,7 @@ func NewContext(options *NewContextOptions) (*Context, chan struct{}, error) { var bufferSizeInBytes int if options.BufferSize != 0 { - // The underlying driver always uses 32bit floats. + // The underlying driver always uses 32-bit floats. bytesPerSample := options.ChannelCount * 4 bytesPerSecond := options.SampleRate * bytesPerSample bufferSizeInBytes = int(int64(options.BufferSize) * int64(bytesPerSecond) / int64(time.Second)) @@ -130,7 +130,7 @@ func NewContext(options *NewContextOptions) (*Context, chan struct{}, error) { // Then, r's position and the current playing position don't necessarily match. // If you want to seek the position of r, call the player's Seek function, // which also clears the underlying buffer. -// If you want to stop using r e.g., you want to close r, call the player's PauseAndStopReading function. +// If you want to stop using r (e.g. you want to close r), call the player's PauseAndStopReading function. // // You cannot share r by multiple players. // diff --git a/driver_darwin.go b/driver_darwin.go index 159e7d5..62e9a34 100644 --- a/driver_darwin.go +++ b/driver_darwin.go @@ -54,7 +54,7 @@ func newAudioQueue(sampleRate, channelCount int, oneBufferSizeInBytes int) (_Aud 0, //CFStringRef 0, &audioQueue); osstatus != noErr { - return 0, nil, fmt.Errorf("oto: AudioQueueNewFormat with StreamFormat failed: %d", osstatus) + return 0, nil, fmt.Errorf("oto: AudioQueueNewOutput failed: %d", osstatus) } bufs := make([]_AudioQueueBufferRef, 0, bufferCount) @@ -138,7 +138,7 @@ func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeI // defaultOneBufferSizeInBytes is the default buffer size in bytes. // // 12288 seems necessary at least on iPod touch (7th) and MacBook Pro 2020. - // With 48000[Hz] stereo, the maximum delay is (12288*4[buffers] / 4 / 2)[samples] / 48000 [Hz] = 100[ms]. + // With 48000[Hz] stereo, the maximum delay is (12288*4[buffers] / 4 / 2)[samples] / 48000 [Hz] = 128[ms]. // '4' is float32 size in bytes. '2' is a number of channels for stereo. const defaultOneBufferSizeInBytes = 12288 diff --git a/driver_wasapi_windows.go b/driver_wasapi_windows.go index b9602ad..31c99e5 100644 --- a/driver_wasapi_windows.go +++ b/driver_wasapi_windows.go @@ -125,8 +125,7 @@ type wasapiContext struct { } var ( - errDeviceSwitched = errors.New("oto: device switched") - errFormatNotSupported = errors.New("oto: the specified format is not supported (there is the closest format instead)") + errDeviceSwitched = errors.New("oto: device switched") ) const ( @@ -187,7 +186,7 @@ func (c *wasapiContext) isDeviceSwitched() (bool, error) { var switched bool var cerr error c.comThread.Run(func() { - device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole) + device, err := c.enumerator.GetDefaultAudioEndpoint(eRender, eConsole) if err != nil { cerr = err return @@ -303,7 +302,7 @@ func (c *wasapiContext) startOnCOMThread() (ferr error) { } c.enumerator = (*_IMMDeviceEnumerator)(e) - device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole) + device, err := c.enumerator.GetDefaultAudioEndpoint(eRender, eConsole) if err != nil { if errors.Is(err, _E_NOTFOUND) { return errDeviceNotFound @@ -333,7 +332,7 @@ func (c *wasapiContext) startOnCOMThread() (ferr error) { } // Check the format is supported by WASAPI. - // Stereo with 48000 [Hz] is likely supported, but mono and/or other sample rates are unlikely supported. + // Stereo with 48000 [Hz] is likely supported, but mono and/or other sample rates are unlikely to be supported. // Fallback to WinMM in this case anyway. const bitsPerSample = 32 nBlockAlign := c.channelCount * bitsPerSample / 8 @@ -531,8 +530,7 @@ func (c *wasapiContext) Err() error { // isWASAPIDeviceTransientError reports whether err from (re)starting the client // indicates a temporarily unusable device rather than a permanent failure. func isWASAPIDeviceTransientError(err error) bool { - return errors.Is(err, errFormatNotSupported) || - errors.Is(err, errDeviceNotFound) || + return errors.Is(err, errDeviceNotFound) || errors.Is(err, _AUDCLNT_E_DEVICE_INVALIDATED) || errors.Is(err, _AUDCLNT_E_RESOURCES_INVALIDATED) || errors.Is(err, _RPC_E_DISCONNECTED) diff --git a/driver_winmm_windows.go b/driver_winmm_windows.go index 5885fb5..3f01767 100644 --- a/driver_winmm_windows.go +++ b/driver_winmm_windows.go @@ -26,9 +26,6 @@ import ( "github.com/ebitengine/oto/v3/internal/mux" ) -// Avoid goroutines on Windows (hajimehoshi/ebiten#1768). -// Apparently, switching contexts might take longer than other platforms. - const defaultHeaderBufferSize = 4096 type header struct { @@ -201,7 +198,7 @@ func (c *winmmContext) isHeaderAvailable() bool { } var waveOutOpenCallback = windows.NewCallback(func(hwo, uMsg, dwInstance, dwParam1, dwParam2 uintptr) uintptr { - // Queuing a header in this callback might not work especially when a headset is connected or disconnected. + // Queueing a header in this callback might not work especially when a headset is connected or disconnected. // Just signal the condition variable and don't do other things. const womDone = 0x3bd if uMsg != womDone { diff --git a/example/main.go b/example/main.go index eff2503..7e52649 100644 --- a/example/main.go +++ b/example/main.go @@ -28,7 +28,7 @@ import ( var ( sampleRate = flag.Int("samplerate", 48000, "sample rate") - channelCount = flag.Int("channelcount", 2, "number of channel") + channelCount = flag.Int("channelcount", 2, "number of channels") format = flag.String("format", "s16le", "source format (u8, s16le, or f32le)") ) @@ -210,7 +210,7 @@ func run() error { wg.Wait() - // Pin the players not to GC the players. + // Keep the players alive so that they are not garbage-collected. runtime.KeepAlive(players) return nil diff --git a/internal/mux/mux.go b/internal/mux/mux.go index 1ae6165..3880a96 100644 --- a/internal/mux/mux.go +++ b/internal/mux/mux.go @@ -26,7 +26,7 @@ import ( "time" ) -// Format must sync with oto's Format. +// Format must be kept in sync with oto's Format. type Format int const ( @@ -108,7 +108,7 @@ func (m *Mux) loop() { } // Sleeping is necessary especially on browsers. - // Sometimes a player continues to read 0 bytes from the source and this loop can be a busy loop in such case. + // Sometimes a player continues to read 0 bytes from the source and this loop can be a busy loop in such a case. if allZero { time.Sleep(time.Millisecond) } @@ -630,7 +630,7 @@ func (p *playerImpl) returnBufferToPool() { // TODO: The term 'buffer' is confusing. Name each buffer with good terms. // defaultBufferSize returns the default size of the buffer for the audio source. -// This buffer is used when unreading on pausing the player. +// The mux loop reads the source into this buffer, and ReadFloat32s consumes it. func (m *Mux) defaultBufferSize() int { bytesPerSample := m.channelCount * m.format.ByteLength() s := m.sampleRate * bytesPerSample / 2 // 0.5[s] diff --git a/internal/mux/mux_test.go b/internal/mux/mux_test.go index e6f1ef8..63131a5 100644 --- a/internal/mux/mux_test.go +++ b/internal/mux/mux_test.go @@ -245,7 +245,7 @@ func TestPauseAndStopReadingKeepsOngoingReadResult(t *testing.T) { } // The player must be reusable after PauseAndStopReading. - // Consume the buffered data to make a room for a new read. + // Consume the buffered data to make room for a new read. p.Play() m.ReadFloat32s(make([]float32, 4096)) deadline := time.Now().Add(time.Second) @@ -360,7 +360,7 @@ func TestClosingSourceAfterPauseAndStopReadingIsSafe(t *testing.T) { _ = src.Close() reads := src.reads.Load() - // Consume the mux's buffer to tempt it to read the source again. + // Consume the mux's buffer to prompt it to read the source again. m.ReadFloat32s(make([]float32, 256)) time.Sleep(100 * time.Millisecond) @@ -409,7 +409,7 @@ func TestSeekDiscardsOngoingReadResult(t *testing.T) { // Wait until a read from the source is in flight. <-src.began - // Pause not to resume playing after seeking. + // Pause so that playing does not resume after seeking. p.Pause() done := make(chan struct{}) diff --git a/internal/oboe/binding_android.cpp b/internal/oboe/binding_android.cpp index 10136a8..05bc529 100644 --- a/internal/oboe/binding_android.cpp +++ b/internal/oboe/binding_android.cpp @@ -30,8 +30,6 @@ namespace { -class Player; - // Status is the outcome of an operation. msg is null on success, and describes // the failure otherwise. retryable, which is meaningful only for a failure, is // false for a configuration error, which would fail the same way however many @@ -65,9 +63,8 @@ Status StatusFromResult(oboe::Result result) { } // kStableRunDuration is how long a stream must keep playing for its start to -// count as a success. A stream that goes away sooner reached the device -// without being able to use it, so the delay before the next attempt keeps -// growing. +// count as a success. A stream that goes away sooner managed to start but not +// to keep playing, so the delay before the next attempt keeps growing. constexpr std::chrono::seconds kStableRunDuration{3}; // StartRetryDelay returns how long to wait before the next start attempt after @@ -98,7 +95,7 @@ enum class State { kStopped, // kStartDeferred means that a start attempt failed for a reason that can - // pass, and that LoopStartRetry attempts it again. + // be temporary, and that LoopStartRetry attempts it again. kStartDeferred, // kRunning means that the stream was started and has not been paused, closed @@ -123,8 +120,8 @@ class Stream : public oboe::AudioStreamDataCallback, public oboe::AudioStreamErrorCallback { public: // GetInstance returns the instance of Stream. Only one Stream object is used - // in one process. It is because multiple streams can be problematic in both - // AAudio and OpenSL (#1656, #1660). + // in one process, because multiple streams can be problematic in both AAudio + // and OpenSL (#1656, #1660). static Stream &GetInstance(); const char *Play(int sample_rate, int channel_num, int buffer_size_in_bytes); diff --git a/internal/oboe/binding_android.h b/internal/oboe/binding_android.h index 82c5c07..334ce5d 100644 --- a/internal/oboe/binding_android.h +++ b/internal/oboe/binding_android.h @@ -23,8 +23,6 @@ extern "C" { #endif -typedef uintptr_t PlayerID; - const char *oto_oboe_Play(int sample_rate, int channel_num, int buffer_size_in_bytes); const char *oto_oboe_Suspend(); diff --git a/player.go b/player.go index af063bb..4888937 100644 --- a/player.go +++ b/player.go @@ -74,7 +74,7 @@ func (p *Player) SetVolume(volume float64) { p.player.SetVolume(volume) } -// BufferedSize returns the byte size of the buffer data that is not sent to the audio hardware yet. +// BufferedSize returns the byte size of the buffered data that is not sent to the audio hardware yet. func (p *Player) BufferedSize() int { return p.player.BufferedSize() } From 792a6c1eea12e0c1319a009bab275afbc68c9cfc Mon Sep 17 00:00:00 2001 From: Hiroki KUMAZAKI Date: Tue, 8 Sep 2026 00:49:36 +0900 Subject: [PATCH 3/3] all: address review comments Revert the atomicError rename, which is out of scope for a typo and comment fix, and remove PlayStation 5 from the platform list in README. --- README.md | 1 - context.go | 6 +++--- driver_alsa_unix.go | 2 +- driver_android.go | 2 +- driver_darwin.go | 2 +- driver_pulseaudio_unix.go | 2 +- driver_unix.go | 2 +- driver_wasapi_windows.go | 2 +- driver_windows.go | 2 +- driver_winmm_windows.go | 2 +- 10 files changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 3702e25..52d2cec 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,6 @@ A low-level library to play sound. - iOS - WebAssembly (no Cgo required) - Nintendo Switch -- PlayStation 5 - Xbox ## Prerequisite diff --git a/context.go b/context.go index df7a467..4c7833e 100644 --- a/context.go +++ b/context.go @@ -173,14 +173,14 @@ func (c *Context) Err() error { return c.context.Err() } -type mutexError struct { +type atomicError struct { err error m sync.Mutex } // Join records err in addition to the errors recorded so far. A nil err is // ignored. -func (a *mutexError) Join(err error) { +func (a *atomicError) Join(err error) { if err == nil { return } @@ -190,7 +190,7 @@ func (a *mutexError) Join(err error) { a.err = errors.Join(a.err, err) } -func (a *mutexError) Load() error { +func (a *atomicError) Load() error { a.m.Lock() defer a.m.Unlock() return a.err diff --git a/driver_alsa_unix.go b/driver_alsa_unix.go index 70e1f49..a3e223b 100644 --- a/driver_alsa_unix.go +++ b/driver_alsa_unix.go @@ -119,7 +119,7 @@ type alsaContext struct { cond *sync.Cond mux *mux.Mux - err mutexError + err atomicError } func newALSAContextImpl(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int) (*alsaContext, error) { diff --git a/driver_android.go b/driver_android.go index 31173fe..846603c 100644 --- a/driver_android.go +++ b/driver_android.go @@ -24,7 +24,7 @@ import ( type context struct { mux *mux.Mux - err mutexError + err atomicError m sync.Mutex } diff --git a/driver_darwin.go b/driver_darwin.go index 62e9a34..951f642 100644 --- a/driver_darwin.go +++ b/driver_darwin.go @@ -126,7 +126,7 @@ type context struct { startRetryTimer *time.Timer mux *mux.Mux - err mutexError + err atomicError } // TODO: Convert the error code correctly. diff --git a/driver_pulseaudio_unix.go b/driver_pulseaudio_unix.go index 2728033..e3ca3b0 100644 --- a/driver_pulseaudio_unix.go +++ b/driver_pulseaudio_unix.go @@ -35,7 +35,7 @@ type pulseContext struct { cond *sync.Cond mux *mux.Mux - err mutexError + err atomicError } func newPulseContext(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int, applicationName string) (*pulseContext, error) { diff --git a/driver_unix.go b/driver_unix.go index 8707ccc..19879f4 100644 --- a/driver_unix.go +++ b/driver_unix.go @@ -39,7 +39,7 @@ type context struct { backend unixBackend ready chan struct{} - err mutexError + err atomicError } func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int, applicationName string) (*context, chan struct{}, error) { diff --git a/driver_wasapi_windows.go b/driver_wasapi_windows.go index 31c99e5..e59babf 100644 --- a/driver_wasapi_windows.go +++ b/driver_wasapi_windows.go @@ -107,7 +107,7 @@ type wasapiContext struct { bufferSizeInBytes int comThread *comThread - err mutexError + err atomicError suspended bool suspendedCond *sync.Cond diff --git a/driver_windows.go b/driver_windows.go index a90a982..d091e3d 100644 --- a/driver_windows.go +++ b/driver_windows.go @@ -36,7 +36,7 @@ type context struct { nullContext *nullContext ready chan struct{} - err mutexError + err atomicError } func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int, _ string) (*context, chan struct{}, error) { diff --git a/driver_winmm_windows.go b/driver_winmm_windows.go index 3f01767..2384057 100644 --- a/driver_winmm_windows.go +++ b/driver_winmm_windows.go @@ -76,7 +76,7 @@ type winmmContext struct { buf32 []float32 mux *mux.Mux - err mutexError + err atomicError loopEndCh chan error cond *sync.Cond