moosicbox_audio_output 0.2.0

MoosicBox audio outputs package
docs.rs failed to build moosicbox_audio_output-0.2.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: moosicbox_audio_output-0.1.4

MoosicBox Audio Output

A cross-platform audio output abstraction layer for high-quality audio playback.

Overview

The MoosicBox Audio Output package provides:

  • Cross-Platform Support: Works on Windows, macOS, and Linux
  • CPAL-Based Audio Output: Uses CPAL (Cross-Platform Audio Library) for audio playback
  • Professional Audio Backend Support: Optional JACK and ASIO support via CPAL
  • Audio Format Encoding: Built-in support for encoding to AAC, FLAC, MP3, and Opus
  • Device Management: Scan and select audio output devices
  • Command-Based Control: Pause, resume, reset, and volume control via AudioHandle (seek commands currently return an error for CPAL outputs)
  • Progress Tracking: Real-time playback position tracking with callbacks

Supported Audio Backends

All audio backends are provided through CPAL (Cross-Platform Audio Library):

CPAL (Cross-Platform Audio Library)

  • Platforms: Windows (WASAPI, DirectSound), macOS (Core Audio), Linux (ALSA)
  • Use Case: General-purpose audio output with good cross-platform compatibility
  • Default Backend: Enabled by default

JACK (JACK Audio Connection Kit)

  • Platforms: Linux, macOS, Windows
  • Use Case: Professional audio, low-latency applications
  • Features: Real-time audio routing, minimal latency
  • Enabled via: jack feature flag

ASIO (Audio Stream Input/Output)

  • Platforms: Windows
  • Use Case: Professional audio interfaces, ultra-low latency
  • Requirements: ASIO-compatible audio hardware
  • Enabled via: asio feature flag

Usage

Basic Audio Output

use moosicbox_audio_output::{scan_outputs, default_output};
use moosicbox_audio_output::AudioWrite;
use symphonia::core::audio::AudioBuffer;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Scan available audio outputs
    scan_outputs().await?;

    // Get the default audio output
    let mut audio_output = default_output().await?;

    // Write audio samples (expects AudioBuffer<f32> from Symphonia)
    // The AudioBuffer would typically come from an audio decoder
    // audio_output.write(audio_buffer)?;

    // Flush when done
    audio_output.flush()?;

    Ok(())
}

Device Enumeration

use moosicbox_audio_output::{scan_outputs, output_factories};

async fn list_available_devices() -> Result<(), Box<dyn std::error::Error>> {
    // Scan available audio outputs
    scan_outputs().await?;

    // List all available audio output factories
    let factories = output_factories().await;

    for factory in factories {
        println!("Device: {} ({})", factory.name, factory.id);
        println!("  Sample rate: {} Hz", factory.spec.rate);
        println!("  Channels: {}", factory.spec.channels.count());
    }

    Ok(())
}

Volume and Playback Control

use moosicbox_audio_output::{default_output, AudioWrite};

async fn playback_control_example() -> Result<(), Box<dyn std::error::Error>> {
    let mut audio_output = default_output().await?;

    // Get the audio handle for command-based control
    let handle = audio_output.handle();

    // Set volume (0.0 to 1.0)
    handle.set_volume(0.8).await?;

    // Pause playback
    handle.pause().await?;

    // Resume playback
    handle.resume().await?;

    // Seek command exists but currently returns an error for CPAL outputs
    if let Err(err) = handle.seek(30.0).await {
        eprintln!("Seek not supported by current output backend: {err}");
    }

    // Reset the audio output
    handle.reset().await?;

    Ok(())
}

Progress Tracking

use moosicbox_audio_output::{default_output, AudioWrite};

async fn progress_tracking_example() -> Result<(), Box<dyn std::error::Error>> {
    let mut audio_output = default_output().await?;

    // Set a progress callback that gets called with playback position (in seconds)
    audio_output.set_progress_callback(Some(Box::new(|position| {
        println!("Current playback position: {:.2}s", position);
    })));

    // Get current playback position
    if let Some(position) = audio_output.get_playback_position() {
        println!("Position: {:.2}s", position);
    }

    Ok(())
}

Feature Flags

Audio Backends

  • cpal - Enable CPAL backend (cross-platform, enabled by default)
  • jack - Enable JACK backend via CPAL (professional audio)
  • asio - Enable ASIO backend via CPAL (Windows professional audio)

Audio Encoding Formats

  • aac - Support for encoding to AAC format
  • flac - Support for encoding to FLAC format
  • mp3 - Support for encoding to MP3 format
  • opus - Support for encoding to Opus format

API Features

  • api - Enable API models for integration (enabled by default)
  • openapi - Enable OpenAPI/utoipa support (enabled by default)

Default Features

The default feature enables: api, default-windows, and openapi.

The default-windows feature enables: aac, cpal, flac, mp3, and opus.

Architecture

The audio output package uses a layered architecture:

  1. AudioOutput: Main wrapper that handles resampling and delegates to AudioWrite
  2. AudioWrite: Core trait for writing audio buffers to output devices
  3. AudioOutputFactory: Factory pattern for creating audio outputs with specific configurations
  4. CpalAudioOutput: CPAL-based implementation of AudioWrite
  5. AudioHandle: Command-based interface for controlling playback (pause, resume, reset, volume; seek command currently returns an error for CPAL outputs)
  6. ProgressTracker: Tracks and reports playback progress with callbacks

Audio Specifications

The package uses Symphonia's SignalSpec for audio specifications:

use symphonia::core::audio::SignalSpec;

// The SignalSpec defines the audio format
let spec = SignalSpec {
    rate: 44100,  // Sample rate in Hz
    channels: symphonia::core::audio::Layout::Stereo.into_channels(),
};

Audio quality is determined by the source material and the audio device's capabilities. The package automatically handles:

  • Sample rate conversion (resampling)
  • Channel configuration
  • Buffer management (30-second ring buffer with 10-second initial buffering)

Platform-Specific Notes

Linux

  • ALSA: Default backend via CPAL (direct hardware access)
  • JACK: Optional professional audio backend (enable with jack feature)
# JACK is optional - install only if you need professional audio features
sudo apt-get install libjack-jackd2-dev

macOS

  • Core Audio: Native macOS audio (via CPAL)
  • JACK: Optional professional audio backend (enable with jack feature)
# JACK is optional - install only if you need professional audio features
brew install jack

Windows

  • WASAPI: Modern Windows audio API (default via CPAL)
  • DirectSound: Legacy Windows audio (fallback via CPAL)
  • ASIO: Optional professional audio backend (enable with asio feature)

For ASIO support:

  • ASIO-compatible audio hardware
  • ASIO drivers from hardware manufacturer
  • Enable the asio feature flag in Cargo.toml

Error Handling

use moosicbox_audio_output::AudioOutputError;

match audio_output.write(audio_buffer) {
    Ok(samples_written) => println!("Audio written successfully: {} samples", samples_written),
    Err(AudioOutputError::NoOutputs) => {
        eprintln!("No audio outputs available");
    },
    Err(AudioOutputError::UnsupportedOutputConfiguration) => {
        eprintln!("Unsupported output configuration");
    },
    Err(AudioOutputError::UnsupportedChannels(channels)) => {
        eprintln!("Unsupported channel count: {}", channels);
    },
    Err(AudioOutputError::OpenStream) => {
        eprintln!("Failed to open audio stream");
    },
    Err(AudioOutputError::PlayStream) => {
        eprintln!("Failed to play audio stream");
    },
    Err(AudioOutputError::StreamClosed) => {
        eprintln!("Audio stream was closed");
    },
    Err(AudioOutputError::StreamEnd) => {
        eprintln!("Audio stream ended");
    },
    Err(e) => {
        eprintln!("Audio output error: {}", e);
    }
}

Implementation Details

Buffer Management

The CPAL implementation uses a ring buffer architecture:

  • Ring buffer size: 30 seconds of audio
  • Initial buffering: 10 seconds before playback starts
  • Purpose: Prevents audio underruns and ensures smooth playback

This approach ensures that:

  • Short audio clips (< 10 seconds) start immediately on flush
  • Long audio content has ample buffering to prevent crackling
  • Volume changes are applied immediately in the CPAL callback

Sample Rate Handling

The package automatically handles sample rate conversion:

  • Uses the moosicbox_resampler crate for high-quality resampling
  • Converts input audio to match the output device's sample rate
  • Maintains audio quality during conversion

Progress Tracking

Progress tracking uses a dedicated ProgressTracker:

  • Tracks consumed samples from the CPAL audio callback
  • Calculates playback position based on actual output sample rate
  • Triggers callbacks when position changes by ≥0.1 seconds
  • Thread-safe via atomic operations

Integration with MoosicBox

The audio output package integrates with other MoosicBox components:

  • moosicbox_audio_decoder: Provides decoded audio buffers via the AudioDecode trait
  • moosicbox_resampler: Handles sample rate conversion automatically
  • moosicbox_player: Uses AudioOutput for playback
  • moosicbox_stream_utils: Provides streaming utilities

The AudioOutput implements the AudioDecode trait, allowing it to receive decoded audio directly from audio decoders.

Troubleshooting

Common Issues

  1. No audio output:

    • Run scan_outputs().await to ensure devices are detected
    • Check system audio settings and permissions
    • Verify CPAL feature is enabled
  2. Audio crackling or stuttering:

    • This is typically a system resource issue
    • The package uses a 30-second ring buffer with 10-second initial buffering to prevent this
    • Check CPU usage and system load
  3. Device not found:

    • Verify the device is connected and enabled in system settings
    • Run output_factories().await to list available devices
  4. Build errors with JACK/ASIO:

    • Ensure you have the required development libraries installed
    • JACK requires libjack-jackd2-dev on Linux
    • ASIO requires ASIO SDK and drivers on Windows

Debug Logging

Enable debug logging to troubleshoot issues:

# Enable audio output debugging
RUST_LOG=moosicbox_audio_output=debug cargo run

# Enable trace-level logging for detailed information
RUST_LOG=moosicbox_audio_output=trace cargo run

Platform-Specific Troubleshooting

Linux

# List ALSA devices
aplay -l

# Check JACK status (if using JACK feature)
jack_control status

Windows

# List audio devices
powershell "Get-WmiObject Win32_SoundDevice"

macOS

# List audio devices
system_profiler SPAudioDataType

See Also