### Install ALSA Runtime Libraries on Fedora/RHEL Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Install the necessary ALSA runtime libraries on Fedora or RHEL systems to ensure Oto can use ALSA as a fallback audio system. ```bash sudo dnf install alsa-lib ``` -------------------------------- ### Specify Audio Format in Options (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Example of setting the audio sample format using the Format type within NewContextOptions. FormatSignedInt16LE is a common choice. ```go op := &oto.NewContextOptions{ Format: oto.FormatSignedInt16LE, // Most common format } ``` -------------------------------- ### Configure Player Buffer Size Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Examples of setting a small buffer for low-latency playback and resetting to the default buffer size. ```go // Set a small buffer for low-latency real-time PCM playback player.SetBufferSize(4096) // Reset to default player.SetBufferSize(0) ``` -------------------------------- ### Buffer Size Calculation Example Source: https://github.com/ebitengine/oto/blob/main/_autodocs/ARCHITECTURE.md Demonstrates the buffer size calculation for a specific configuration: 50ms buffer at 44100 Hz, 2 channels, and 16-bit format. This results in 8820 bytes. ```go bufferSize = (50ms / 1000ms) * (44100 * 2 * 2) = 0.05 * 176400 = 8820 bytes ``` -------------------------------- ### Install ALSA Runtime Libraries on Debian/Ubuntu Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Install the necessary ALSA runtime libraries on Debian-based or Ubuntu systems to ensure Oto can use ALSA as a fallback audio system. ```bash sudo apt-get install libasound2 ``` -------------------------------- ### Start Playback Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Starts audio playback. If the player is already playing, this call has no effect. Playback is asynchronous and returns immediately. ```go player := ctx.NewPlayer(audioSource) player.Play() ``` -------------------------------- ### Example of Closing a Player (Deprecated) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md An example demonstrating how to call the Close method on a Player. This is no longer necessary as players are automatically cleaned up by the garbage collector. ```go // No longer necessary, but safe: // player.Close() ``` -------------------------------- ### Play Multiple Simultaneous Audio Sources Source: https://github.com/ebitengine/oto/blob/main/_autodocs/USAGE_EXAMPLES.md This example shows how to play multiple audio files concurrently using the same Oto context. Ensure that the audio files (sound1.mp3, sound2.mp3, sound3.mp3) exist in the same directory as the executable. ```Go package main import ( "bytes" "os" "sync" "time" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) func main() { // Create context once op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { panic(err) } <-ready var wg sync.WaitGroup players := make([]*oto.Player, 0) var mu sync.Mutex // Play first sound wg.Add(1) go func() { defer wg.Done() b, _ := os.ReadFile("sound1.mp3") d, _ := mp3.NewDecoder(bytes.NewReader(b)) p := ctx.NewPlayer(d) mu.Lock() players = append(players, p) mu.Unlock() p.Play() }() // Play second sound after 1 second wg.Add(1) go func() { defer wg.Done() time.Sleep(1 * time.Second) b, _ := os.ReadFile("sound2.mp3") d, _ := mp3.NewDecoder(bytes.NewReader(b)) p := ctx.NewPlayer(d) mu.Lock() players = append(players, p) mu.Unlock() p.Play() }() // Play third sound after 2 seconds wg.Add(1) go func() { defer wg.Done() time.Sleep(2 * time.Second) b, _ := os.ReadFile("sound3.mp3") d, _ := mp3.NewDecoder(bytes.NewReader(b)) p := ctx.NewPlayer(d) mu.Lock() players = append(players, p) mu.Unlock() p.Play() }() wg.Wait() // Wait for all players to finish for { mu.Lock() allDone := true for _, p := range players { if p.IsPlaying() { allDone = false break } } mu.Unlock() if allDone { break } time.Sleep(time.Millisecond) } println("All sounds finished") } ``` -------------------------------- ### Manage Multiple Players with Goroutines Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Utilize goroutines for concurrent audio playback. This example demonstrates creating multiple players, managing them with a WaitGroup and mutex, and waiting for all to finish. ```go package main import ( "fmt" "sync" "time" "github.com/hajimehoshi/ebiten/v2/audio/oto" ) var wg sync.WaitGroup var mu sync.Mutex var players []*oto.Player // Assume ctx and loadSound are defined elsewhere // var ctx *oto.Context // func loadSound(name string) io.Reader { return nil } // Create and play sounds func manageMultiplePlayers(ctx *oto.Context) { for i := 0; i < 5; i++ { wg.Add(1) go func(soundName string) { defer wg.Done() player := ctx.NewPlayer(loadSound(soundName)) mu.Lock() players = append(players, player) mu.Unlock() player.Play() }(fmt.Sprintf("sound%d", i)) } // Wait for all to finish go func() { wg.Wait() mu.Lock() for _, p := range players { for p.IsPlaying() { time.Sleep(time.Millisecond) } } mu.Unlock() }() } ``` -------------------------------- ### Full Oto Configuration with Latency Optimization Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Configure Oto with all options, including buffer size and application name, for optimized audio playback. This setup aims to balance latency and stability. ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, BufferSize: 25 * time.Millisecond, // 25ms for lower latency ApplicationName: "My Audio Player", } ctx, ready, err := oto.NewContext(op) if err != nil { panic(err) } <-ready ``` -------------------------------- ### Oto Project Documentation Structure Source: https://github.com/ebitengine/oto/blob/main/_autodocs/00_START_HERE.md This tree displays the organization of the Oto project's documentation files. It helps users locate specific files like API references, examples, and configuration guides. ```text output/ ├── 00_START_HERE.md ← You are here! ├── INDEX.md ← Navigation hub ├── README.md ← Project overview ├── api-reference/ │ ├── context.md │ └── player.md ├── configuration.md ├── errors.md ├── types.md ├── USAGE_EXAMPLES.md ├── ARCHITECTURE.md ├── BEST_PRACTICES.md ├── PLATFORM_NOTES.md └── Supporting files ├── DOCUMENTATION_SUMMARY.txt └── FILE_STRUCTURE.txt ``` -------------------------------- ### Create Audio Context with Options (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Demonstrates creating a new audio context using NewContextOptions. It configures sample rate, channel count, format, buffer size, and application name. Ensure to wait for the ready channel before playing audio. ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, // Stereo Format: oto.FormatSignedInt16LE, BufferSize: 50 * time.Millisecond, // Reduce latency ApplicationName: "My Audio App", } ctx, ready, err := oto.NewContext(op) if err != nil { panic(err) } <-ready ``` -------------------------------- ### Minimal Oto Configuration Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Initialize Oto with only the required fields for audio playback. Ensure to handle potential errors and wait for the audio context to be ready. ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { panic(err) } <-ready ``` -------------------------------- ### Check Context Error at Startup Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Always verify the audio system is working correctly during initialization. This ensures that the audio context is valid before proceeding. ```go // Good: verify audio system is working ctx, ready, err := oto.NewContext(op) if err != nil { log.Fatalf("Audio initialization failed: %v", err) } <-ready if err := ctx.Err(); err != nil { log.Fatalf("Audio context error: %v", err) } ``` -------------------------------- ### Play Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Starts playback if the player is not already playing. Playback is asynchronous and returns immediately. ```APIDOC ## Play ### Description Starts playback if the player is not already playing. Playback is asynchronous and returns immediately without waiting for audio to be produced. ### Method func (p *Player) Play() ### Notes - If the player is already playing, this call is a no-op. - The underlying buffer must have data before audio is actually sent to the device. - Safe to call from multiple goroutines. ### Example ```go player := ctx.NewPlayer(audioSource) player.Play() ``` ``` -------------------------------- ### Get Player Error Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Returns any error that occurred during playback for this player. This reflects only the first error encountered. ```go func (p *Player) Err() error ``` -------------------------------- ### Handle Platform-Specific Audio Driver Errors Source: https://github.com/ebitengine/oto/blob/main/_autodocs/errors.md This snippet shows how to handle general audio device initialization failures. It logs a warning if audio is unavailable and allows the program to continue without audio functionality. ```Go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { // Could be any driver-specific error log.Fatal("Audio initialization failed: " + err.Error()) } ``` ```Go ctx, ready, err := oto.NewContext(op) if err != nil { // Log the error and fall back to silent mode or error message log.Println("Warning: audio unavailable: " + err.Error()) // Continue without audio, or show UI message to user return } ``` -------------------------------- ### Get Current Volume Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Retrieves the current playback volume, which is a float64 in the range [0, 1]. Values outside this range are clamped during playback. ```go currentVol := player.Volume() fmt.Printf("Current volume: %.2f\n", currentVol) ``` -------------------------------- ### Build Native FreeBSD Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Build the project directly on a native FreeBSD system. ```bash go build . ``` -------------------------------- ### Get Buffered Size of Player Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Returns the number of unplayed bytes in the player's internal buffer. Useful for estimating latency and detecting playback completion. ```go func (p *Player) BufferedSize() int ``` -------------------------------- ### Create Oto Context with Platform-Specific Options Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Use this function to create an Oto audio context, adjusting options based on the target operating system. Ensure the 'ready' channel is read to confirm context initialization. ```go import ( "runtime" "github.com/ebitengine/oto/v3" ) func createContext() (*oto.Context, error) { op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } // Adjust options per platform switch runtime.GOOS { case "linux": // Linux: use lower latency if possible op.ApplicationName = "MyApp" case "darwin": // macOS/iOS: use default settings case "windows": // Windows: use default settings case "js": // WASM: note that audio may require user interaction } ctx, ready, err := oto.NewContext(op) if err != nil { return nil, err } <-ready return ctx, nil } ``` -------------------------------- ### Standard Linux Build Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Build the application for Linux with standard audio support, which includes PulseAudio as primary and ALSA as a fallback. ```bash # Standard build (PulseAudio + ALSA fallback) go build . ``` -------------------------------- ### CI/CD Workflow for Testing Oto Applications Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md This GitHub Actions workflow demonstrates how to set up Go and run tests across multiple operating systems (Linux, macOS, Windows) for your Oto application. ```yaml # .github/workflows/test.yml jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 - run: go test ./... ``` -------------------------------- ### Get Context Error State Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Retrieve the current error state of the audio context. This is useful for monitoring playback issues. Call this periodically to detect fatal errors. ```go func (c *Context) Err() error ``` ```go // Periodically check for context errors ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { if err := ctx.Err(); err != nil { log.Println("Audio context error: " + err.Error()) break } } ``` -------------------------------- ### Avoid Ignoring Initialization Errors Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Never ignore initialization errors, as this can lead to a nil context and subsequent panics when attempting to use audio resources. ```go // Bad: no error checking ctx, ready, _ := oto.NewContext(op) // Error ignored player := ctx.NewPlayer(source) // ctx might be nil! player.Play() ``` -------------------------------- ### Implement Reasonable Error Check Interval Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Use a ticker with a reasonable interval for checking player errors to avoid burning CPU. This example demonstrates an efficient way to monitor for playback issues. ```go // Good: reasonable check interval ticker := time.NewTicker(100 * time.Millisecond) for range ticker.C { if err := player.Err(); err != nil { log.Println(err) break } } ``` -------------------------------- ### Build for WebAssembly with Go Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Use this command to build your Go project for WebAssembly. Ensure GOOS and GOARCH are set correctly. ```bash # Build for WebAssembly GOOS=js GOARCH=wasm go build -o main.wasm . ``` -------------------------------- ### Create New Oto Context Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Initializes a new audio context with specified configuration. Must be called before creating any players. Block on the returned channel to ensure the audio device is ready. ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { panic("Failed to create context: " + err.Error()) } <-ready // Wait for audio device to be ready ``` -------------------------------- ### Build for WebAssembly with TinyGo Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Use this command to build your Go project for WebAssembly using TinyGo. This is an alternative to the standard Go toolchain. ```bash # Or with TinyGo tinygo build -target=wasm -o main.wasm . ``` -------------------------------- ### Run Oto Tests Source: https://github.com/ebitengine/oto/blob/main/_autodocs/README.md Execute the test suite for the Oto library using the standard Go testing command. ```bash go test ./... ``` -------------------------------- ### Configuration Options Source: https://github.com/ebitengine/oto/blob/main/_autodocs/DOCUMENTATION_SUMMARY.txt Documentation for the configurable options available when creating an audio context. ```APIDOC ## Configuration Options ### Description These options can be provided when creating a new audio context to customize its behavior. ### Options - **SampleRate**: The audio sample rate (e.g., 44100 Hz). - **ChannelCount**: The number of audio channels (e.g., 1 for mono, 2 for stereo). - **Format**: The audio data format (enum). - **BufferSize**: The default buffer size for audio data. - **ApplicationName**: The name of the application using the audio context. ``` -------------------------------- ### Context Creation Flow Source: https://github.com/ebitengine/oto/blob/main/_autodocs/ARCHITECTURE.md Illustrates the sequence of operations for creating a new audio context. It includes option validation, singleton checks, and the creation of internal components like the mux and platform driver. ```go NewContext(options) ↓ Validate options (sampleRate, channelCount, format required) ↓ Check singleton: only one context allowed ↓ Create internal context struct ↓ Create mux with options ↓ Create platform driver with options ↓ Return context, ready channel, error ``` -------------------------------- ### Play Audio File Source: https://github.com/ebitengine/oto/blob/main/_autodocs/README.md Opens an audio file, decodes it, and plays it using Oto. Ensure the audio file is accessible and correctly formatted. The context must be set up before creating a player. ```Go // Open and decode file file, _ := os.Open("audio.wav") decoder, _ := wav.NewDecoder(file) // Create context op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready // Play player := ctx.NewPlayer(decoder) player.Play() // Wait for completion for player.IsPlaying() { time.Sleep(time.Millisecond) } file.Close() ``` -------------------------------- ### Wait for Oto Context Readiness Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Always wait for the context's ready signal before creating players or attempting to play audio. This ensures the audio device is properly initialized and available. ```go // Good: wait for ready signal ctx, ready, err := oto.NewContext(op) if err != nil { // Handle error } <-ready // Block until device is ready player := ctx.NewPlayer(source) player.Play() ``` -------------------------------- ### Wait for Ready Channel Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Always wait for the ready channel before creating a player to ensure the audio device is initialized. Failing to do so can lead to errors. ```go // Bad: assumes context is ready immediately ctx, ready, err := oto.NewContext(op) player := ctx.NewPlayer(source) // May fail if device not ready <-ready // Oops, too late ``` -------------------------------- ### Create New Player from io.Reader Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Creates a new player that reads audio data from a provided io.Reader. The data format must match the context's configuration. Each reader instance can only be used by one player. ```go // Assuming we have an io.Reader with MP3 data that was decoded to PCM decodedAudio := mp3.NewDecoder(mp3File) // Create a player for this audio data player := ctx.NewPlayer(decodedAudio) // Start playback player.Play() // Wait for playback to finish for player.IsPlaying() { time.Sleep(time.Millisecond) } ``` -------------------------------- ### NewContextOptions Structure (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Defines the NewContextOptions struct, which holds all configuration parameters for creating an audio context. Fields like SampleRate, ChannelCount, and Format are required. ```go type NewContextOptions struct { // SampleRate specifies the number of samples per second SampleRate int // ChannelCount specifies the number of audio channels (1=mono, 2=stereo) ChannelCount int // Format specifies the format of audio samples (FormatFloat32LE, etc.) Format Format // BufferSize specifies the buffer duration in the underlying device (0=default) BufferSize time.Duration // ApplicationName is used by some audio systems for identification ApplicationName string } ``` -------------------------------- ### Build Oto with Cgo Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Use CGO_ENABLED=1 for native builds when Cgo is available and desired. This is the default behavior on most native compilation targets. ```bash CGO_ENABLED=1 go build . ``` -------------------------------- ### Build Oto for Windows Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Builds Oto for Windows. The standard build uses the default system audio API. Cross-compilation requires setting GOOS and GOARCH environment variables. ```bash # Standard build (uses default system audio API) go build . ``` ```bash # For cross-compilation from non-Windows GOOS=windows GOARCH=amd64 go build . ``` -------------------------------- ### Abrupt Volume Changes (Bad Practice) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Avoid setting volume to 0 or 1 instantly, as this can cause audible clicks or pops in the audio output. Use gradual transitions instead. ```Go // Bad: sudden change causes click sound player.SetVolume(0) // Mute instantly time.Sleep(1 * time.Second) player.SetVolume(1.0) // Unmute instantly — audible click! ``` -------------------------------- ### Build Oto for macOS and Cross-Compilation Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Commands for building Oto on macOS. Includes native builds and cross-compilation to other systems like Linux. ```bash # Native macOS build go build . ``` ```bash # Cross-compile from macOS to other systems GOOS=linux GOARCH=amd64 go build . ``` ```bash # Cross-compile to macOS from other systems GOOS=darwin GOARCH=amd64 go build . ``` -------------------------------- ### Handle Context Creation Errors Source: https://github.com/ebitengine/oto/blob/main/_autodocs/errors.md Safely create an audio context and handle specific errors like 'oto: context is already created' or general initialization failures. This allows for fallback mechanisms, such as silent mode. ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { switch err.Error() { case "oto: context is already created": log.Fatal("Context already exists") default: log.Printf("Audio initialization failed: %v\n", err) log.Println("Continuing without audio") return // Fall back to silent mode } } <-ready ``` -------------------------------- ### Configure Oto for Music Playback Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Initialize an Oto context for music playback with balanced latency. This configuration uses CD quality sample rate, stereo channels, common signed 16-bit little-endian format, and a buffer size of 50 milliseconds. Ensure the audio device is ready before creating players. ```go package main import ( "time" "github.com/ebitengine/oto/v3" ) func main() { // Configure for music playback with balanced latency op := &oto.NewContextOptions{ SampleRate: 44100, // CD quality ChannelCount: 2, // Stereo Format: oto.FormatSignedInt16LE, // Common MP3/WAV format BufferSize: 50 * time.Millisecond, // Balanced latency ApplicationName: "My Music Player", } ctx, ready, err := oto.NewContext(op) if err != nil { panic("Failed to initialize audio: " + err.Error()) } <-ready // Wait for audio device // Now create players as needed player := ctx.NewPlayer(audioSourceReader) player.Play() } ``` -------------------------------- ### Build iOS App with Oto Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md Use this command to build an iOS application that includes Oto audio. This process must be performed through Xcode or gomobile. ```bash # Build iOS app with Oto audio # Must be done through Xcode or gomobile # Using gomobile gomobile build -target=ios . ``` -------------------------------- ### NewContext Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/context.md Creates a new audio context with specified configuration options. This function must be called before creating any players. It returns the context, a channel that closes when the context is ready, and an error if creation fails. ```APIDOC ## NewContext ### Description Creates a new audio context with specified configuration options. This function must be called before creating any players. ### Method `func NewContext(options *NewContextOptions) (*Context, chan struct{}, error)` ### Parameters #### Path Parameters (None) #### Query Parameters (None) #### Request Body - **options** (*NewContextOptions) - Required - Configuration options for the context (sample rate, channel count, format, buffer size, and application name). ### Request Example ```go op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, err := oto.NewContext(op) if err != nil { panic("Failed to create context: " + err.Error()) } <-ready // Wait for audio device to be ready ``` ### Response #### Success Response (200) - **Context** (*Context) - The newly created context, ready to create players - **ready** (chan struct{}) - A channel that closes when the context is ready. Block on this channel before using the context. #### Response Example (No explicit example provided in source, but the function signature indicates the return types) ### Error Handling - **"oto: context is already created"**: If a context was already instantiated (contexts are singletons). - Platform-specific errors if audio device initialization fails. ``` -------------------------------- ### Seeking on Non-Seekable Sources (Bad Practice) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Attempting to seek on sources that do not support seeking, such as live network streams, will result in an error. Always check for seek support first. ```Go // Bad: network stream doesn't support seeking resp, _ := http.Get("https://stream.example.com/audio") decoder, _ := mp3.NewDecoder(resp.Body) player := ctx.NewPlayer(decoder) _, err := player.Seek(5_000_000, io.SeekStart) // Will fail! if err != nil { println("Can't seek on streams") } ``` -------------------------------- ### Create and Reuse Oto Context Once Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Create the Oto context once at application startup and reuse it for the entire application lifetime. This ensures efficient resource management and avoids errors related to multiple context creations. ```go // Good: single context for entire app lifetime func init() { op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } var err error globalContext, globalReady, err = oto.NewContext(op) if err != nil { log.Fatal(err) } } func main() { <-globalReady // Wait for audio device ready // Now use globalContext throughout app } ``` -------------------------------- ### Suspend and Resume Audio Context Source: https://github.com/ebitengine/oto/blob/main/_autodocs/USAGE_EXAMPLES.md Demonstrates how to suspend and resume the entire audio context, useful for scenarios where the application loses focus or system audio changes. ```Go package main import ( "log" "os" "time" "github.com/ebitengine/oto/v3" ) func main() { op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready // Create a player (details omitted for brevity) // player := ctx.NewPlayer(source) // player.Play() // Simulate app losing focus println("Suspending audio...") if err := ctx.Suspend(); err != nil { log.Printf("Suspend error: %v\n", err) } time.Sleep(2 * time.Second) // Simulate app regaining focus println("Resuming audio...") if err := ctx.Resume(); err != nil { log.Printf("Resume error: %v\n", err) } // Continue playback time.Sleep(2 * time.Second) } ``` -------------------------------- ### Play MP3 from Memory Source: https://github.com/ebitengine/oto/blob/main/README.md Loads an MP3 file into memory and plays it using Oto. Ensure the audio context is configured correctly for sample rate, channels, and format. The player is asynchronous, so a loop is used to wait for playback completion. ```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 (aka locations) 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 // 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.(io.Seeker).Seek(0, io.SeekStart) // if err != nil{ // panic("player.Seek failed: " + err.Error()) // } // println("Player is now at position:", newPos) // player.Play() } ``` -------------------------------- ### Seeking and Pause/Resume Audio Playback Source: https://github.com/ebitengine/oto/blob/main/_autodocs/USAGE_EXAMPLES.md Demonstrates how to pause, resume, and seek within an audio file. Seeking requires the audio source to implement io.Seeker. Errors during seeking should be handled as not all sources support it. Pause stops playback without clearing the buffer, and Play resumes from the paused position unless a seek operation has occurred. ```go package main import ( "io" "os" "time" "github.com/ebitengine/oto/v3" "github.com/go-audio/wav" ) func main() { // Open WAV file (supports seeking via io.Seeker) file, err := os.Open("music.wav") if err != nil { panic(err) } defer file.Close() decoder := wav.NewDecoder(file) if err := decoder.Err(); err != nil { panic(err) } op := &oto.NewContextOptions{ SampleRate: int(decoder.SampleRate), ChannelCount: int(decoder.NumChans), Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready player := ctx.NewPlayer(decoder) player.Play() println("Playing...") // Play for 3 seconds time.Sleep(3 * time.Second) // Pause player.Pause() println("Paused") time.Sleep(2 * time.Second) // Resume player.Play() println("Resumed") time.Sleep(3 * time.Second) // Try to seek to beginning (10 seconds into the file) // Byte offset calculation: // 10 seconds at 44100 Hz, 2 channels, 16-bit (2 bytes) = 1,764,000 bytes targetBytes := int64(10 * 44100 * 2 * 2) newPos, err := player.Seek(targetBytes, io.SeekStart) if err != nil { println("Seek error:", err.Error()) } else { println("Seeked to position:", newPos) // Player automatically resumes playing from new position if it was playing } for player.IsPlaying() { time.Sleep(time.Millisecond) } } ``` -------------------------------- ### Browser Integration for WebAssembly Source: https://github.com/ebitengine/oto/blob/main/_autodocs/PLATFORM_NOTES.md This HTML snippet demonstrates how to load and run a WebAssembly Go program in a browser. It includes the necessary script for wasm_exec.js and instantiates the WebAssembly module. ```html ``` -------------------------------- ### Custom Sine Wave Audio Source Source: https://github.com/ebitengine/oto/blob/main/_autodocs/USAGE_EXAMPLES.md Implement io.Reader to create a custom audio source that generates a sine wave tone. Return io.EOF when audio playback is finished. For seekable sources, also implement io.Seeker. ```go package main import ( "io" "math" "time" "github.com/ebitengine/oto/v3" ) // SineWaveGenerator generates a sine wave tone type SineWaveGenerator struct { freq float64 // Frequency in Hz sampleRate int // Samples per second channelCount int // Number of channels duration time.Duration position int64 // Current position in bytes remaining []byte // Buffered bytes } func NewSineWaveGenerator(freq float64, duration time.Duration, sampleRate, channelCount int) *SineWaveGenerator { return &SineWaveGenerator{ freq: freq, sampleRate: sampleRate, channelCount: channelCount, duration: duration, } } func (s *SineWaveGenerator) Read(buf []byte) (int, error) { // Handle remaining buffered bytes if len(s.remaining) > 0 { n := copy(buf, s.remaining) s.remaining = s.remaining[n:] return n, nil } // Calculate total bytes bytesPerSample := 2 // 16-bit = 2 bytes per channel totalBytes := int64(s.sampleRate) * int64(s.channelCount) * int64(bytesPerSample) * int64(s.duration) / int64(time.Second) if s.position >= totalBytes { return 0, io.EOF } // Truncate if needed if s.position+int64(len(buf)) > totalBytes { buf = buf[:totalBytes-s.position] } // Generate sine wave samples wavelength := float64(s.sampleRate) / s.freq const maxInt16 = 32767 const amplitude = 0.3 sampleCount := len(buf) / (2 * s.channelCount) for i := 0; i < sampleCount; i++ { sampleIndex := (s.position / int64(2*s.channelCount)) + int64(i) phase := math.Sin(2*math.Pi*float64(sampleIndex)/wavelength) * amplitude * maxInt16 // Convert to int16 little-endian val := int16(phase) for ch := 0; ch < s.channelCount; ch++ { offset := i*s.channelCount*2 + ch*2 buf[offset] = byte(val) buf[offset+1] = byte(val >> 8) } } s.position += int64(len(buf)) return len(buf), nil } func main() { op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready // Play a 3-second 440 Hz sine wave (musical note A) gen := NewSineWaveGenerator(440, 3*time.Second, 44100, 2) player := ctx.NewPlayer(gen) player.Play() for player.IsPlaying() { time.Sleep(time.Millisecond) } println("Tone finished") } ``` -------------------------------- ### Context Methods Source: https://github.com/ebitengine/oto/blob/main/_autodocs/DOCUMENTATION_SUMMARY.txt Documentation for methods associated with the audio context, including creation, player management, and state control. ```APIDOC ## Context Methods ### Description Provides methods for managing the audio context, including creating new contexts, creating players, suspending and resuming audio playback, and retrieving context errors. ### Methods - **NewContext**: Creates a new audio context. - **NewPlayer**: Creates a new audio player associated with the context. - **Suspend**: Suspends all audio playback. - **Resume**: Resumes audio playback after suspension. - **Err**: Returns the last error encountered by the context. ### Parameters - **NewContext**: Takes `NewContextOptions` to configure the context. - **NewPlayer**: Takes no parameters. - **Suspend**: Takes no parameters. - **Resume**: Takes no parameters. - **Err**: Takes no parameters. ### Return Types - **NewContext**: Returns a `*Context` and an `error`. - **NewPlayer**: Returns a `*Player` and an `error`. - **Suspend**: Returns an `error`. - **Resume**: Returns an `error`. - **Err**: Returns an `error`. ``` -------------------------------- ### Configure Oto for Real-Time Audio Synthesis Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Initialize an Oto context for low-latency audio synthesis or real-time effects. This configuration uses a professional sample rate, stereo channels, high-precision float 32-bit little-endian format, and a minimal buffer size of 5 milliseconds. It's crucial to wait for the audio device to be ready. ```go // For low-latency audio synthesis or real-time effects op := &oto.NewContextOptions{ SampleRate: 48000, // Professional quality ChannelCount: 2, // Stereo Format: oto.FormatFloat32LE, // High precision BufferSize: 5 * time.Millisecond, // Minimal latency } ctx, ready, err := oto.NewContext(op) if err != nil { panic(err) } <-ready ``` -------------------------------- ### Buffer Pool Initialization Source: https://github.com/ebitengine/oto/blob/main/_autodocs/ARCHITECTURE.md Initializes a sync.Pool for byte slices to manage audio buffers efficiently. This reduces garbage collection pressure and improves performance by reusing allocated memory. ```go var theBufPool = sync.Pool{ New: func() any { var buf []byte return &buf }, } ``` -------------------------------- ### Handle "oto: context is already created" Error Source: https://github.com/ebitengine/oto/blob/main/_autodocs/errors.md This snippet demonstrates how to detect and handle the error when attempting to create a second audio context in the same process. It logs a specific message for this condition. ```Go ctx, ready, err := oto.NewContext(op) if err != nil { if err.Error() == "oto: context is already created" { log.Fatal("Only one audio context is allowed per program") } log.Fatal("Failed to create audio context: " + err.Error()) } ``` -------------------------------- ### Adjust Volume and Create Fade Effects in Go Source: https://github.com/ebitengine/oto/blob/main/_autodocs/USAGE_EXAMPLES.md Demonstrates how to adjust audio volume and create fade-in/fade-out effects using linear interpolation. Volume is in the range [0, 1]. ```go package main import ( "bytes" "os" "time" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" ) func main() { op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready b, _ := os.ReadFile("song.mp3") d, _ := mp3.NewDecoder(bytes.NewReader(b)) player := ctx.NewPlayer(d) // Start at half volume player.SetVolume(0.5) println("Starting at volume: 0.5") player.Play() // Fade in from 0.5 to 1.0 over 2 seconds fadeInDuration := 2 * time.Second fadeInSteps := 20 fadeInInterval := fadeInDuration / time.Duration(fadeInSteps) for i := 0; i <= fadeInSteps; i++ { vol := 0.5 + float64(i)/float64(fadeInSteps)*0.5 // 0.5 to 1.0 player.SetVolume(vol) println("Volume:", vol) time.Sleep(fadeInInterval) } // Play for a while time.Sleep(5 * time.Second) // Fade out from 1.0 to 0.0 over 3 seconds fadeOutDuration := 3 * time.Second fadeOutSteps := 30 fadeOutInterval := fadeOutDuration / time.Duration(fadeOutSteps) for i := 0; i <= fadeOutSteps; i++ { vol := 1.0 - float64(i)/float64(fadeOutSteps) // 1.0 to 0.0 player.SetVolume(vol) println("Volume:", vol) time.Sleep(fadeOutInterval) } player.Pause() println("Fade out complete") } ``` -------------------------------- ### Gracefully Handle Oto Context Creation Errors Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Check for errors during Oto context creation. Handle specific errors like 'context is already created' or gracefully fall back to a silent mode if the audio device is unavailable. ```go ctx, ready, err := oto.NewContext(op) if err != nil { if err.Error() == "oto: context is already created" { log.Fatal("Audio context already exists (bug in app)") } // Audio unavailable; fall back to silent mode log.Println("Warning: audio unavailable: " + err.Error()) // Continue without audio return } <-ready ``` -------------------------------- ### Basic Audio Playback Source: https://github.com/ebitengine/oto/blob/main/_autodocs/README.md This snippet shows how to decode an MP3 file and play it using the Oto library. Ensure you have an 'audio.mp3' file in the same directory. The audio context is configured for a sample rate of 44100 Hz, 2 channels, and signed 16-bit little-endian format. ```Go package main import ( "bytes" "github.com/ebitengine/oto/v3" "github.com/hajimehoshi/go-mp3" "os" "time" ) func main() { // Decode MP3 file to PCM fileBytes, _ := os.ReadFile("audio.mp3") decoder, _ := mp3.NewDecoder(bytes.NewReader(fileBytes)) // Create audio context op := &oto.NewContextOptions{ SampleRate: 44100, ChannelCount: 2, Format: oto.FormatSignedInt16LE, } ctx, ready, _ := oto.NewContext(op) <-ready // Create and play audio player := ctx.NewPlayer(decoder) player.Play() // Wait for playback for player.IsPlaying() { time.Sleep(time.Millisecond) } } ``` -------------------------------- ### Choose Appropriate Sample Rate Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Select sample rates that align with common audio standards for optimal quality and compatibility. Use 44100 for music, 48000 for video, and 16000 for voice. ```go // Good: match common audio rates sampleRate := 44100 // Music, CD quality // or sampleRate := 48000 // Video, professional // or sampleRate := 16000 // Voice communication ``` -------------------------------- ### Perform Seek Operations Source: https://github.com/ebitengine/oto/blob/main/_autodocs/api-reference/player.md Demonstrates seeking to the beginning of the audio source and seeking to a specific time offset, calculating the byte position based on audio format. ```go // Seek to the beginning newPos, err := player.Seek(0, io.SeekStart) if err != nil { log.Println("Seeking not supported") return } fmt.Printf("Seeked to position: %d\n", newPos) // Seek to 10 seconds (assuming FormatSignedInt16LE, 2 channels, 44100 Hz) // 10 seconds = 10 * 44100 * 2 * 2 = 1,764,000 bytes newPos, err = player.Seek(1764000, io.SeekStart) ``` -------------------------------- ### Balanced Buffer Size Configuration Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Configure a moderate buffer size for a balance between audio latency and stability, suitable for general-purpose audio playback. This provides a good compromise for most applications. ```go op.BufferSize = 30 * time.Millisecond // Default-like behavior ``` -------------------------------- ### Checking Seek Support Before Seeking Source: https://github.com/ebitengine/oto/blob/main/_autodocs/BEST_PRACTICES.md Before attempting to seek, verify if the audio source supports seeking by checking the error returned from the Seek method. This prevents runtime errors on non-seekable sources like network streams. ```Go // Good: graceful error handling _, err := player.Seek(targetOffset, io.SeekStart) if err != nil { log.Println("Seeking not supported on this source") // Offer user alternative: restart playback } ``` -------------------------------- ### Low Latency Buffer Size Configuration Source: https://github.com/ebitengine/oto/blob/main/_autodocs/configuration.md Set a small buffer size for minimal audio latency, suitable for real-time applications. Be aware of the increased risk of glitches if audio data production is slow. ```go op.BufferSize = 10 * time.Millisecond // Minimal delay ``` -------------------------------- ### Define Audio Sample Formats (Go) Source: https://github.com/ebitengine/oto/blob/main/_autodocs/types.md Defines the Format integer type and its constants for specifying audio sample formats. Used in NewContextOptions.Format to determine how audio data bytes are interpreted. ```go type Format int const ( FormatFloat32LE Format = iota // 32-bit IEEE 754 float, little-endian FormatUnsignedInt8 // 8-bit unsigned integer FormatSignedInt16LE // 16-bit signed integer, little-endian ) ```