### Monitoring buffered size example (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows how to check how far ahead of the device the player is after starting playback. ```go player.Play() time.Sleep(200 * time.Millisecond) println("buffered:", player.BufferedSize(), "bytes") ``` -------------------------------- ### Player.SetBufferSize usage example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows how to set a small buffer for low-latency real-time PCM and how to restore the default by passing 0. ```go // Use a small buffer for low-latency real-time PCM player.SetBufferSize(4096) // Restore the default player.SetBufferSize(0) ``` -------------------------------- ### Play usage example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Demonstrates that Play returns immediately and does not block. The comment notes the asynchronous behavior. ```go player.Play() // async: returns immediately ``` -------------------------------- ### PauseAndStopReading usage example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows how to safely close the source after pausing. The comment notes that no read is in flight and none will start until Play or Seek is called. ```go player.PauseAndStopReading() file.Close() // safe now: no read is in flight and none will start until Play/Seek ``` -------------------------------- ### Pause usage example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows how to pause a player. The source continues to be read into the buffer while paused. ```go player.Pause() // playing stops; the source continues to be read into the buffer ``` -------------------------------- ### Direct usage example of mathutil.MulDiv Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mathutil.md Shows how to call MulDiv directly, including a division-by-zero case that returns (0, false). ```go import ( "time" "github.com/ebitengine/oto/v3/internal/mathutil" ) q, ok := mathutil.MulDiv(int64(10 * time.Millisecond), 48000, int64(time.Second)) // 480 frames if !ok { panic("overflow or division by zero") } q, ok = mathutil.MulDiv(1, 1, 0) // (0, false): division by zero ``` -------------------------------- ### NewContext error handling examples Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Error handling examples: passing nil options yields "oto: options must not be nil"; a negative BufferSize yields "oto: buffer size must not be negative: ..."; a second NewContext call fails with "oto: context is already created". ```go import ( "fmt" "github.com/ebitengine/oto/v3" ) // nil options ctx, _, err := oto.NewContext(nil) if err != nil { fmt.Println(err.Error()) // "oto: options must not be nil" } // negative buffer size op := &oto.NewContextOptions{} op.SampleRate = 44100 op.ChannelCount = 2 op.Format = oto.FormatFloat32LE op.BufferSize = -time.Millisecond _, _, err = oto.NewContext(op) if err != nil { fmt.Println(err.Error()) // "oto: buffer size must not be negative: ..." } // a second NewContext fails with "oto: context is already created" ``` -------------------------------- ### oto.Player.Play Source: https://github.com/ebitengine/oto/blob/main/README.md Starts playing the sound. This function returns immediately (asynchronous). ```APIDOC ## [oto.Player.Play] Start playing sound ### Description Starts playing the sound. This function returns immediately (asynchronous). ### Method Play ### Endpoint player.Play() ### Parameters None ### Request Example ```go player.Play() ``` ### Response No return value. ``` -------------------------------- ### SetVolume usage examples (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Demonstrates valid and invalid volume values. Negative and NaN values are treated as 0, while values above 1 amplify and may clip. ```go player.SetVolume(0.7) // attenuate player.SetVolume(2.0) // amplify (may clip) player.SetVolume(-1.0) // treated as 0 (silence) player.SetVolume(math.NaN) // treated as 0 ``` -------------------------------- ### Restart playback from beginning using Seek (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Example from README.md showing how to restart playback from the beginning using player.Seek(0, io.SeekStart). It checks for errors and panics on failure, then prints the new position and resumes playback with player.Play(). ```go // Restart from the beginning (from README.md) newPos, err := player.Seek(0, io.SeekStart) if err != nil { panic("player.Seek failed: " + err.Error()) } println("Player is now at position:", newPos) player.Play() ``` -------------------------------- ### Player.Err usage example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows how to wait for playback to finish and then check for errors. If an error occurred, it panics with a message including the error. The error is wrapped with 'oto: audio error: ...'. ```go player.Play() for player.IsPlaying() { time.Sleep(time.Millisecond) } if err := player.Err(); err != nil { panic("audio error while playing: " + err.Error()) // "oto: audio error: ..." } ``` -------------------------------- ### Play method implementation Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Starts playing if the player is not already playing. Returns immediately without reading the source; the buffer is filled asynchronously by the mux loop. Calling Play on an already-playing player is a no-op. ```go // player.go:48-51 func (p *Player) Play() { p.player.Play() } ``` -------------------------------- ### Play Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Starts playing if the player is not already playing. Returns immediately without reading the source. ```APIDOC ## `(p *Player) Play` ### Description Starts playing if the player doesn't play. Returns immediately without reading the source (the buffer is filled asynchronously by the mux loop). Calling `Play` on an already-playing player is a no-op. ### Method `Play` ### Signature `func (p *Player) Play()` ### Parameters None. ### Return Value None. ### Example ```go player.Play() // async: returns immediately ``` ``` -------------------------------- ### New function creating Mux Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Creates a Mux with the given sample rate, channel count, and format, and starts its loop goroutine. Called by every driver's newContext. sampleRate is samples per second, channelCount is 1 (mono) or 2 (stereo), format is the source sample format. ```go // mux.go:63-71 func New(sampleRate int, channelCount int, format Format) *Mux { m := &Mux{ sampleRate: sampleRate, channelCount: channelCount, format: format, cond: sync.NewCond(&sync.Mutex{}), } go m.loop() return m } ``` -------------------------------- ### Handle non-seekable source with Seek (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Example showing how to handle a non-seekable source (e.g., a network stream) by calling Seek and checking the error. The error message is printed, and the comment shows the expected error: 'mux: the source must implement io.Seeker'. ```go p := otoCtx.NewPlayer(liveStreamReader) _, err := p.Seek(0, io.SeekStart) if err != nil { fmt.Println("cannot seek this source:", err.Error()) // "mux: the source must implement io.Seeker" } ``` -------------------------------- ### Player method signatures Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Signatures for all Player methods that mirror oto.Player. Includes comments describing each method's behavior: Play starts playing iff paused/pausedAndStopReading and not drained; Pause transitions playerPlay to playerPaused; PauseAndStopReading transitions to playerPausedAndStopReading and waits for in-flight read; Seek seeks with offset and whence; Reset clears buffered data and reports source error if drained; IsPlaying returns playing state; Volume returns volume; SetVolume sets volume; BufferedSize returns buffered size; Close stops the cleanup finalizer then closes the playerImpl. ```go // mux.go:249-250 func (p *Player) Play() // starts playing iff paused/pausedAndStopReading and not drained // mux.go:314-315 func (p *Player) Pause() // playerPlay -> playerPaused // mux.go:328-329 func (p *Player) PauseAndStopReading() // -> playerPausedAndStopReading; waits for in-flight read // mux.go:359-360 func (p *Player) Seek(offset int64, whence int) (int64, error) // mux.go:398-399 func (p *Player) Reset() // buffered data is cleared, source error reported if drained // mux.go:417-418 func (p *Player) IsPlaying() bool // mux.go:427-428 func (p *Player) Volume() float64 // mux.go:438-439 func (p *Player) SetVolume(volume float64) // mux.go:459-460 func (p *Player) BufferedSize() int // mux.go:471-472 func (p *Player) Close() error // stops the cleanup finalizer, then closes the playerImpl ``` -------------------------------- ### Reset usage example with deprecation comment Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Shows the deprecated usage of Reset. The comment advises preferring Pause, PauseAndStopReading, or Seek. ```go // Deprecated: prefer Pause / PauseAndStopReading / Seek player.Reset() ``` -------------------------------- ### Resume audio playback with Context.Resume Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Resumes the entire audio play after Suspend. Concurrent-safe. On macOS/iOS the actual AudioQueueStart runs on a background goroutine and a deferred start is retried immediately (endDeferStart, reset of startRetries, see driver_darwin.go:323-339). ```go func (c *Context) Resume() error { return c.context.Resume() } ``` -------------------------------- ### Get driver error with Context.Err Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Returns an error that occurred in the audio driver, if any. Errors reported by Err are fatal: once Err returns a non-nil error, this context is no longer usable and Err keeps reporting it. On drivers that initialize asynchronously (Unix, Windows, Android, macOS/iOS), a pending initialization failure is returned as soon as it is recorded, without waiting for the ready channel (driver_unix.go:95-110, driver_windows.go:105-130). ```go func (c *Context) Err() error { return c.context.Err() } ``` -------------------------------- ### Player.Play() Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Starts playing if the player is paused or paused-and-stop-reading and not drained. ```APIDOC ## Player.Play() ### Description Starts playing if the player is paused or paused-and-stop-reading and not drained. ### Method Play ### Signature ```go func (p *Player) Play() ``` ### Behavior - Transitions from `playerPaused` or `playerPausedAndStopReading` to `playerPlay`. - Does nothing if the player is already playing or closed. ``` -------------------------------- ### Create a new player with Context.NewPlayer Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Creates a new Player for the given audio source. The player is initially paused and must be kept reachable to continue playing. The source must be in the context's format (little-endian 32-bit float frames). ```go // context.go:175-183 func (c *Context) NewPlayer(r io.Reader) *Player { return &Player{ player: c.context.mux.NewPlayer(r), } } ``` -------------------------------- ### Basic NewContext usage Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Basic creation of an audio context with 44100 Hz sample rate, stereo, and signed 16-bit little-endian format. Waits for the ready channel to close, then checks Context.Err() for asynchronous driver initialization errors. ```go import ( "time" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) op := &oto.NewContextOptions{} op.SampleRate = 44100 op.ChannelCount = 2 op.Format = oto.FormatSignedInt16LE otoCtx, readyChan, err := oto.NewContext(op) if err != nil { panic("oto.NewContext failed: " + err.Error()) } <-readyChan if err := otoCtx.Err(); err != nil { panic("oto initialization failed: " + err.Error()) } ``` -------------------------------- ### Play MP3 from memory with oto.NewContext and player.Play Source: https://github.com/ebitengine/oto/blob/main/README.md Loads an MP3 file into memory, decodes it with go-mp3, creates an Oto context with a sample rate of 44100 Hz, 2 channels, and signed 16-bit little-endian format, then plays it asynchronously. Waits for playback to finish using a polling loop. The context must be created only once per program, and the ready channel must be received before use. ```go package main import ( "bytes" "time" "os" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) func main() { // Read the mp3 file into memory fileBytes, err := os.ReadFile("./my-file.mp3") if err != nil { panic("reading my-file.mp3 failed: " + err.Error()) } // Convert the pure bytes into a reader object that can be used with the mp3 decoder fileBytesReader := bytes.NewReader(fileBytes) // Decode file decodedMp3, err := mp3.NewDecoder(fileBytesReader) if err != nil { panic("mp3.NewDecoder failed: " + err.Error()) } // Prepare an Oto context (this will use your default audio device) that will // play all our sounds. Its configuration can't be changed later. op := &oto.NewContextOptions{} // Usually 44100 or 48000. Other values might cause distortions in Oto op.SampleRate = 44100 // 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 // Format of the source. go-mp3's format is signed 16bit integers. op.Format = oto.FormatSignedInt16LE // Remember that you should **not** create more than one context otoCtx, readyChan, err := oto.NewContext(op) if err != nil { panic("oto.NewContext failed: " + err.Error()) } // It might take a bit for the hardware audio devices to be ready, so we wait on the channel. <-readyChan if err := otoCtx.Err(); err != nil { panic("oto initialization failed: " + err.Error()) } // Create a new 'player' that will handle our sound. Paused by default. player := otoCtx.NewPlayer(decodedMp3) // Play starts playing the sound and returns without waiting for it (Play() is async). player.Play() // We can wait for the sound to finish playing using something like this for player.IsPlaying() { time.Sleep(time.Millisecond) } // 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.Seek(0, io.SeekStart) // if err != nil{ // panic("player.Seek failed: " + err.Error()) // } // println("Player is now at position:", newPos) // player.Play() } ``` -------------------------------- ### IsPlaying usage example with polling loop Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Idiomatic way to wait for a sound to finish, as shown in README.md. Polls IsPlaying with time.Sleep until playback completes. ```go player.Play() for player.IsPlaying() { time.Sleep(time.Millisecond) } ``` -------------------------------- ### Configure minimal Oto context for go-mp3 Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Use this to create a minimal audio context for playing MP3 files with go-mp3. Set sample rate to 44100 (or 48000), stereo, and signed 16-bit little-endian format as required by go-mp3. The call returns a context, a ready channel, and an error. ```go op := &oto.NewContextOptions{} op.SampleRate = 44100 // or 48000 op.ChannelCount = 2 // 1 = mono, 2 = stereo op.Format = oto.FormatSignedInt16LE // go-mp3's format ctx, readyChan, err := oto.NewContext(op) ``` -------------------------------- ### oto.NewContextOptions Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Configuration options for creating a new audio context via oto.NewContext. ```APIDOC ## oto.NewContextOptions ### Description Configuration options for creating a new audio context via oto.NewContext. ### Type ```go type NewContextOptions struct { SampleRate int ChannelCount int Format Format BufferSize time.Duration ApplicationName string } ``` ### Fields - **SampleRate** (`int`) - Required - Samples/second; typical 44100 or 48000; one context has one sample rate. - **ChannelCount** (`int`) - Required - 1 (mono) or 2 (stereo) only. - **Format** (`Format`) - Required - Source sample format. - **BufferSize** (`time.Duration`) - Optional - Underlying device buffer; must not be negative. Default: `0` (driver default). - **ApplicationName** (`string`) - Optional - Client app name; used by PulseAudio volume UI. Default: `""` (driver-derived). ### Usage Accepted by: `NewContext`. Full semantics: [configuration.md](configuration.md). ``` -------------------------------- ### func New(sampleRate int, channelCount int, format Format) *Mux Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Creates a new Mux instance with the specified sample rate, channel count, and format, and starts its internal loop goroutine. ```APIDOC ## func New(sampleRate int, channelCount int, format Format) *Mux ### Description Creates a new Mux instance with the given sample rate, channel count, and format, and starts its internal loop goroutine. ### Method New ### Signature ```go func New(sampleRate int, channelCount int, format Format) *Mux ``` ### Parameters - `sampleRate` (int) - Required - Samples per second - `channelCount` (int) - Required - 1 (mono) or 2 (stereo) - `format` (Format) - Required - Source sample format ### Returns - `*Mux` - A new Mux instance ### Usage Called by every driver's `newContext` to create a Mux. ``` -------------------------------- ### Play function for Oboe audio Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/oboe.md Starts playback. Callbacks are stored in module variables before the C call because Play can invoke them synchronously. Returns an error wrapping the C message if the C call fails. ```go // binding_android.go:37-46 func Play(sampleRate int, channelCount int, readFunc func(buf []float32), errorFunc func(err error), bufferSizeInBytes int) error { // Play can invoke the callbacks. Set the callbacks before Play. theReadFunc = readFunc theErrorFunc = errorFunc if msg := C.oto_oboe_Play(C.int(sampleRate), C.int(channelCount), C.int(bufferSizeInBytes)); msg != nil { return fmt.Errorf("oboe: Play failed: %s", C.GoString(msg)) } return nil } ``` -------------------------------- ### oto.Context.NewPlayer Source: https://github.com/ebitengine/oto/blob/main/README.md Creates a new player that will handle sound playback. The player is paused by default and reads bytes from the provided io.Reader. ```APIDOC ## [oto.Context.NewPlayer] Create a new player ### Description Creates a new player that will handle sound playback. The player is paused by default and reads bytes from the provided io.Reader. ### Method NewPlayer ### Endpoint otoCtx.NewPlayer(reader io.Reader) *oto.Player ### Parameters #### Request Body - **reader** (io.Reader) - Required - The source of audio bytes to play. Must not be used by multiple players. ### Request Example ```go player := otoCtx.NewPlayer(decodedMp3) ``` ### Response #### Success Response - **player** (*oto.Player) - The created player. #### Response Example ```go player := otoCtx.NewPlayer(decodedMp3) player.Play() ``` ``` -------------------------------- ### Android driver newContext using oboe.Play Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/oboe.md Android driver's newContext that starts Oboe playback in a goroutine. Passes c.mux.ReadFloat32s as readFunc and c.err.Join as errorFunc. The ready channel closes only after oboe.Play returns successfully. ```go // driver_android.go:32-49 func newContext(sampleRate int, channelCount int, format mux.Format, bufferSizeInBytes int, _ string) (*context, chan struct{}, error) { ready := make(chan struct{}) c := &context{ mux: mux.New(sampleRate, channelCount, format), } go func() { c.m.Lock() defer c.m.Unlock() if err := oboe.Play(sampleRate, channelCount, c.mux.ReadFloat32s, c.err.Join, bufferSizeInBytes); err != nil { c.err.Join(err) return } close(ready) }() return c, ready, nil } ``` -------------------------------- ### Context and Context Creation Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Documentation for the Context type and its methods, including NewContext, NewPlayer, Suspend, Resume, Err, and the Format constants. ```APIDOC ## NewContext ### Description Creates a new audio context. This is the main entry point for using Oto. It initializes the audio driver and returns a `Context` object. Only one context can be created per process; subsequent calls will return an error. ### Method NewContext ### Signature `func NewContext(options *NewContextOptions) (*Context, error)` ### Parameters - `options` (*NewContextOptions) - Required - Configuration options for the context, including sample rate, channel count, and format. ### Returns - `*Context` - The created audio context. - `error` - An error if context creation fails, including if a context has already been created. ### Example ```go options := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatFloat32LE, } ctx, err := oto.NewContext(options) if err != nil { panic(err) } ``` ### Notes - Only one context can be created per process. A second call to `NewContext` will fail with an error. - The context represents the single audio output device of the process. ## Context.NewPlayer ### Description Creates a new player that reads audio data from the provided `io.Reader`. The player will play the audio data through the context's output device. ### Method NewPlayer ### Signature `func (c *Context) NewPlayer(r io.Reader) *Player` ### Parameters - `r` (io.Reader) - Required - The source of audio data to be played. ### Returns - `*Player` - A new player instance. ### Example ```go player := ctx.NewPlayer(reader) player.Play() ``` ## Context.Suspend ### Description Suspends the audio context, pausing all audio output. This can be used to temporarily stop audio playback. ### Method Suspend ### Signature `func (c *Context) Suspend() error` ### Returns - `error` - An error if suspension fails. ### Example ```go err := ctx.Suspend() if err != nil { // handle error } ``` ## Context.Resume ### Description Resumes the audio context after it has been suspended, resuming audio output. ### Method Resume ### Signature `func (c *Context) Resume() error` ### Returns - `error` - An error if resumption fails. ### Example ```go err := ctx.Resume() if err != nil { // handle error } ``` ## Context.Err ### Description Returns the current error state of the context. This can be used to check for asynchronous errors that may have occurred during audio processing. ### Method Err ### Signature `func (c *Context) Err() error` ### Returns - `error` - The current error, or nil if no error. ### Example ```go if err := ctx.Err(); err != nil { // handle error } ``` ## Format Constants ### Description Enumeration of PCM source formats supported by Oto. These constants are used to specify the audio format when creating a context. ### Constants - `FormatFloat32LE` (Format) - 32-bit floats in little endian. Value: 0. Bytes per sample: 4. - `FormatUnsignedInt8` (Format) - 8-bit unsigned integers. Value: 1. Bytes per sample: 1. - `FormatSignedInt16LE` (Format) - 16-bit signed integers in little endian. Value: 2. Bytes per sample: 2. ### Example ```go options := &oto.NewContextOptions{ Format: oto.FormatFloat32LE, } ``` ``` -------------------------------- ### Import oto module and internal packages Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/module-graph.md Shows how consumers import the oto module and its internal packages. The module itself is the import unit; there is no single entry file. ```go import ( "github.com/ebitengine/oto/v3" // package oto "github.com/ebitengine/oto/v3/internal/mux" // internal "github.com/ebitengine/oto/v3/internal/mathutil" // internal "github.com/ebitengine/oto/v3/internal/oboe" // internal (Android) ) ``` -------------------------------- ### Format type and constants Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Defines the Format type and its constants for PCM source formats. iota assigns sequential values starting at 0: FormatFloat32LE = 0, FormatUnsignedInt8 = 1, FormatSignedInt16LE = 2. Must be kept in sync with internal mux.Format. ```go type Format int const ( // FormatFloat32LE is the format of 32-bit floats in little endian. FormatFloat32LE Format = iota // FormatUnsignedInt8 is the format of 8-bit integers. FormatUnsignedInt8 // FormatSignedInt16LE is the format of 16-bit integers in little endian. FormatSignedInt16LE ) ``` -------------------------------- ### Format.ByteLength method Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Returns the byte size of one sample: 4 for float32 LE, 1 for u8, 2 for s16 LE. Panics with 'mux: unexpected format: %d' for any other value. ```go // mux.go:38-48 func (f Format) ByteLength() int { switch f { case FormatFloat32LE: return 4 case FormatUnsignedInt8: return 1 case FormatSignedInt16LE: return 2 } panic(fmt.Sprintf("mux: unexpected format: %d", f)) } ``` -------------------------------- ### oto.unixBackend interface (driver_unix.go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Defines the unixBackend interface with Suspend, Resume, and Err methods. Implemented by pulseContext and alsaContext. ```go // driver_unix.go:26-32 type unixBackend interface { Suspend() error Resume() error Err() error } ``` -------------------------------- ### oto.NewContext Source: https://github.com/ebitengine/oto/blob/main/README.md Creates a new Oto context that handles interactions with the OS and audio drivers. There can only be one context in your program. ```APIDOC ## [oto.NewContext] Create an Oto context ### Description Creates a new Oto context that handles interactions with the OS and audio drivers. There can only be one context in your program. ### Method NewContext ### Endpoint oto.NewContext(op *oto.NewContextOptions) (*oto.Context, <-chan struct{}, error) ### Parameters #### Request Body - **op** (*oto.NewContextOptions) - Required - Configuration options for the context. Fields include SampleRate, ChannelCount, and Format. ### Request Example ```go op := &oto.NewContextOptions{} op.SampleRate = 44100 op.ChannelCount = 2 op.Format = oto.FormatSignedInt16LE otoCtx, readyChan, err := oto.NewContext(op) ``` ### Response #### Success Response - **otoCtx** (*oto.Context) - The created context. - **readyChan** (<-chan struct{}) - A channel that is closed when the hardware audio devices are ready. - **err** (error) - An error if context creation failed. #### Response Example ```go otoCtx, readyChan, err := oto.NewContext(op) if err != nil { panic("oto.NewContext failed: " + err.Error()) } <-readyChan if err := otoCtx.Err(); err != nil { panic("oto initialization failed: " + err.Error()) } ``` ``` -------------------------------- ### Player.SetBufferSize method implementation Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Sets the buffer size of the player. The bufferSize parameter is in bytes; 0 restores the default. The default is computed by Mux.defaultBufferSize() (0.5 seconds of audio). ```go // player.go:97-100 func (p *Player) SetBufferSize(bufferSize int) { p.player.SetBufferSize(bufferSize) } ``` -------------------------------- ### Check Context.Err after initialization Source: https://github.com/ebitengine/oto/blob/main/_autodocs/errors.md Checks Context.Err for asynchronous initialization failures on Unix backends after receiving from the ready channel. ```go <-readyChan if err := ctx.Err(); err != nil { // e.g. "oto: initialization failed: PulseAudio: ...; ALSA: ..." } ``` -------------------------------- ### Mux.NewPlayer creating Player Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Creates a Player initially paused, with volume 1 and the default buffer size. Registers a runtime.Cleanup finalizer so an unreachable player is closed automatically. The source reader must not be shared between players. ```go // mux.go:221-236 func (m *Mux) NewPlayer(src io.Reader) *Player { p := &playerImpl{ mux: m, src: src, prevVolume: 1, volume: 1, bufferSize: m.defaultBufferSize(), } p.readCond = sync.NewCond(&p.m) pl := &Player{ p: p, } pl.cleanup = runtime.AddCleanup(pl, func(p *playerImpl) { _ = p.Close() }, pl.p) return pl } ``` -------------------------------- ### Wait for async driver init and check Err Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Waits for asynchronous driver initialization and checks for a fatal error. After receiving from readyChan, Err returns any pending initialization failure. Panics if NewContext fails or if Err reports an error. ```go otoCtx, readyChan, err := oto.NewContext(op) if err != nil { panic("oto.NewContext failed: " + err.Error()) } <-readyChan if err := otoCtx.Err(); err != nil { panic("oto initialization failed: " + err.Error()) } ``` -------------------------------- ### oto.NewContextOptions struct definition Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Defines the NewContextOptions struct with fields for SampleRate, ChannelCount, Format, BufferSize, and ApplicationName. BufferSize defaults to 0 (driver default) and must not be negative; ApplicationName defaults to empty string and is used by PulseAudio volume UI. ```go // context.go:58-88 type NewContextOptions struct { SampleRate int ChannelCount int Format Format BufferSize time.Duration ApplicationName string } ``` -------------------------------- ### (m *Mux) defaultBufferSize - Compute default buffer size Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mux.md Returns the default source buffer size in bytes: 0.5 seconds of audio at the current sample rate, channel count, and format. Aligns the size to a multiple of the bytes per sample to avoid extra bytes. ```go // mux.go:686-700 func (m *Mux) defaultBufferSize() int { bytesPerSample := m.channelCount * m.format.ByteLength() s := m.sampleRate * bytesPerSample / 2 // 0.5[s] // Align s in multiples of bytes per sample, or a buffer could have extra bytes. return s / bytesPerSample * bytesPerSample } ``` -------------------------------- ### func NewContext Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Creates a new audio context with the given options. Returns the context, a ready channel, and an error. The ready channel closes when initialization finishes. ```APIDOC ## func NewContext ### Description Creates a new audio context with the given options. Returns the context, a ready channel, and an error. The ready channel closes when initialization finishes. ### Signature ```go func NewContext(options *NewContextOptions) (*Context, chan struct{}, error) ``` ### Parameters - **options** (`*NewContextOptions`) - Required - Options struct. Must not be nil (`&oto.NewContextOptions{}` is the standard idiom). ### Returns - **1** (`*Context`) - The new context, or nil on error - **2** (`chan struct{}`) - A channel that closes when initialization finishes (the `<-readyChan` idiom from the README) - **3** (`error`) - Non-nil if argument validation or context construction failed synchronously ### Behavior 1. `options == nil` → `fmt.Errorf("oto: options must not be nil")`. 2. `options.BufferSize < 0` → `fmt.Errorf("oto: buffer size must not be negative: %s", options.BufferSize)`. 3. A second concurrent/later call while a context exists → `fmt.Errorf("oto: context is already created")` (guarded by `contextCreationMutex`, `context.go:99-106`). 4. `BufferSize` is converted to bytes via `durationToBufferSize`; a negative duration, `sampleRate <= 0`, or `channelCount <= 0` yields `fmt.Errorf("oto: invalid buffer duration, sample rate, or channel count")`; integer overflow yields `"oto: buffer frame size exceeds int range"` / `"oto: buffer size exceeds int range"`. 5. The platform `newContext(sampleRate, channelCount, mux.Format(format), bufferSizeInBytes, applicationName)` runs; driver initialization failures return that error directly or are recorded in the context and surfaced through `Err()` after the ready channel closes. ### Example - Basic Creation ```go import ( "time" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) op := &oto.NewContextOptions{} op.SampleRate = 44100 op.ChannelCount = 2 op.Format = oto.FormatSignedInt16LE otoCtx, readyChan, err := oto.NewContext(op) if err != nil { panic("oto.NewContext failed: " + err.Error()) } <-readyChan if err := otoCtx.Err(); err != nil { panic("oto initialization failed: " + err.Error()) } ``` ### Example - Error Handling ```go import ( "fmt" "github.com/ebitengine/oto/v3" ) // nil options ctx, _, err := oto.NewContext(nil) if err != nil { fmt.Println(err.Error()) // "oto: options must not be nil" } // negative buffer size op := &oto.NewContextOptions{} op.SampleRate = 44100 op.ChannelCount = 2 op.Format = oto.FormatFloat32LE op.BufferSize = -time.Millisecond _, _, err = oto.NewContext(op) if err != nil { fmt.Println(err.Error()) // "oto: buffer size must not be negative: ..." } // a second NewContext fails with "oto: context is already created" ``` ``` -------------------------------- ### SetVolume method (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Sets the volume. Values outside [0, math.MaxFloat32] (including NaN) are treated as 0. Amplifying above 1 may cause clipping. ```go // player.go:73-76 func (p *Player) SetVolume(volume float64) { p.player.SetVolume(volume) } ``` -------------------------------- ### oto.Format Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Defines the sample format for audio data. Used in NewContextOptions and by the context. ```APIDOC ## oto.Format ### Description Defines the sample format for audio data. Used in NewContextOptions and by the context. ### Type ```go type Format int ``` ### Constants - `FormatFloat32LE` (0) - 4 bytes/sample - `FormatUnsignedInt8` (1) - 1 byte/sample - `FormatSignedInt16LE` (2) - 2 bytes/sample ### Usage Accepted by: `NewContextOptions.Format`, `NewContext`; produced by: reading `NewContextOptions.Format`. Must be kept in sync with `mux.Format`. ``` -------------------------------- ### NewPlayer Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Creates a new Player from an io.Reader audio source. The player is initially paused and must be kept reachable to continue playing. ```APIDOC ## NewPlayer ### Description Creates a new Player from an io.Reader audio source. The player is initially paused and must be kept reachable to continue playing. ### Method FUNCTION ### Endpoint (c *Context) NewPlayer(r io.Reader) *Player ### Parameters #### Parameters - **r** (io.Reader) - Required - The audio source, in the context's format. See format layout below. ### Request Example ```go file, err := os.Open("./my-file.mp3") if err != nil { panic("opening my-file.mp3 failed: " + err.Error()) } decodedMp3, err := mp3.NewDecoder(file) if err != nil { panic("mp3.NewDecoder failed: " + err.Error()) } player := otoCtx.NewPlayer(decodedMp3) // ... keep `file` (and `decodedMp3`) reachable until playback finishes // file.Close() only after the player finished playing ``` ### Response #### Success Response - **player** (*Player) - A ready-to-use (initially paused) player belonging to this context. ### Notes - It is safe to create multiple players; a single io.Reader must **not** be shared by multiple players. - The returned player must be kept reachable while it should keep playing — a player is closed (finalized via runtime.Cleanup) when it becomes unreachable, even mid-play. - Source byte layout: [data] = [sample 1] [sample 2] ..., [sample] = [channel 1] [channel 2] ..., [channel] = [byte 1] [byte 2] ..., little endian. - If r does not implement io.Seeker, the player's Seek returns an error. - NewPlayer and all Player methods are concurrent-safe. ``` -------------------------------- ### Stream MP3 from file with os.Open and oto.NewContext Source: https://github.com/ebitengine/oto/blob/main/README.md Streams an MP3 file from disk instead of loading it entirely into memory, which is suitable for long songs. The file must remain open and referenced until playback finishes; closing it early can cause static. The decoding happens incrementally as the file plays. ```go package main import ( "os" "time" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) func main() { // Open the file for reading. Do NOT close before you finish playing! file, err := os.Open("./my-file.mp3") if err != nil { panic("opening my-file.mp3 failed: " + err.Error()) } // Decode file. This process is done as the file plays so it won't // load the whole thing into memory. decodedMp3, err := mp3.NewDecoder(file) if err != nil { panic("mp3.NewDecoder failed: " + err.Error()) } // Rest is the same... // Close file only after you finish playing file.Close() } ``` -------------------------------- ### MulDiv usage in WASAPI driver (driver_wasapi_windows.go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/mathutil.md Converts buffer size in bytes to 100 ns units for IAudioClient2.Initialize. Returns an error if the conversion fails. ```go bufferSizeInFrames := int64(c.bufferSizeInBytes) / int64(nBlockAlign) duration, ok := mathutil.MulDiv(bufferSizeInFrames, 1e7, int64(c.sampleRate)) if !ok { return fmt.Errorf("oto: WASAPI buffer duration is unrepresentable") } ```