rust-portaudio

repository·master·Indexed 18 days ago

https://github.com/rustaudio/rust-portaudio

Rust bindings and wrappers for PortAudio, a free, cross-platform, open-source audio I/O library. Version 0.8.0 provides tools for initializing PortAudio, enumerating host APIs and devices, configuring stream parameters, and managing audio streams via either real-time callbacks or blocking read/write modes.

Tokens
11.5K
Snippets
33
Records
50
Agent score
64%

What's inside rust-portaudio

  1. Install rust-portaudio

    master

    To install rust-portaudio, use cargo build at the root of the repository.

    By default, the build process attempts to detect an existing PortAudio installation on your system. If no installation is found, or if the PORTAUDIO_ONLY_STATIC environment variable is set during the build process, the crate will attempt to download and build PortAudio statically.

    Platform Specifics:

    • macOS: You may need to manually install portaudio and pkg-config using Homebrew:
      brew install portaudio
      brew install pkg-config

    If the automatic build fails, you can manually download and install PortAudio from the official PortAudio website.

    cargo build
  2. Build tests, examples, and documentation for rust-portaudio

    master

    You can use standard Cargo commands to interact with the project:

    • To build and run tests: cargo test
    • To build and run examples: cargo test (Note: examples are typically included in the test suite or run via cargo run --example <name>)
    • To generate documentation: cargo doc
    cargo test
    cargo doc
  3. Configure stream parameters with PaStreamParameters

    master

    To open an audio stream, you must define PaStreamParameters for input and/or output.

    Key fields include:

    • device: A PaDeviceIndex (0 to Pa_GetDeviceCount()-1).
    • channelCount: Number of audio channels.
    • sampleFormat: The PaSampleFormat used for the buffer.
    • suggestedLatency: Desired latency in seconds (PaTime).
    • hostApiSpecificStreamInfo: An optional pointer for API-specific configuration.
    let params = PaStreamParameters {
        device: 0, // Use first device
        channelCount: 2,
        sampleFormat: /* e.g. paFloat32 */, 
        suggestedLatency: 0.05,
        hostApiSpecificStreamInfo: std::ptr::null_mut(),
    };
  4. Identify and select audio devices using DeviceIndex and DeviceKind

    master

    To specify an audio device for a stream, use DeviceIndex or DeviceKind.

    • DeviceIndex(u32): A direct index to a device, typically ranging from 0 to PortAudio::device_count - 1.
    • DeviceKind::Index(DeviceIndex): Wraps a specific device index.
    • DeviceKind::UseHostApiSpecificDeviceSpecification: A special variant used to indicate that device details are provided via host-API-specific stream information structures rather than a simple index.
    use portaudio::{DeviceIndex, DeviceKind};
    
    let my_device = DeviceKind::Index(DeviceIndex(0));
    let host_specific = DeviceKind::UseHostApiSpecificDeviceSpecification;
  5. Understand Stream Flows: Input, Output, and Duplex

    master

    The Flow of a Stream defines the direction of audio data movement:

    • Input (Input<I>): Receives audio data from an input device's ADC (Analog-to-Digital Converter).
    • Output (Output<O>): Sends audio data to an output device's DAC (Digital-to-Analog Converter).
    • Duplex (Duplex<I, O>): A bi-directional stream that receives and sends data on two devices synchronously.

    Each flow type has associated Settings used to open the stream and specific CallbackArgs used when operating in NonBlocking mode.

  6. Implement a PortAudio stream callback

    master

    A PaStreamCallback is a high-priority function responsible for processing audio data. Because it runs in a real-time context, you must adhere to strict constraints to avoid audio glitches:

    • Do NOT allocate memory.
    • Do NOT access the file system.
    • Do NOT call blocking library functions or any functions with unpredictable execution times.
    • Do NOT call any PortAudio API functions from within the callback (except Pa_GetStreamCpuLoad()).

    Callback Signature & Parameters:

    • input/output: Arrays of interleaved samples, or an array of buffer pointers if paNonInterleaved was requested.
    • frameCount: Number of sample frames to process.
    • timeInfo: Timestamps for ADC capture, DAC output, and callback invocation.
    • statusFlags: Indicates if buffers were inserted or dropped due to underflow/overflow.
    • userData: A user-supplied pointer for synthesis data or state.

    Return Values (PaStreamCallbackResult):

    • paContinue (0): Keep the stream running.
    • paComplete: Finish the stream after all generated buffers are played (useful for soundfile players).
    • paAbort: Finish the stream as soon as possible.

    Note: The callback must always fill the entire output buffer regardless of the return value.

    pub type PaStreamCallback = 
        ::std::option::Option<unsafe extern "C" fn(
            input: *const ::std::os::raw::c_void,
            output: *mut ::std::os::raw::c_void,
            frameCount: ::std::os::raw::c_ulong,
            timeInfo: *const PaStreamCallbackTimeInfo,
            statusFlags: PaStreamCallbackFlags,
            userData: *mut ::std::os::raw::c_void
        ) -> ::std::os::raw::c_int>;
  7. Understand Stream Modes: Blocking vs Non-Blocking

    master

    A PortAudio Stream can operate in two distinct modes, which determine how you interact with audio data:

    1. Blocking (Blocking<B>): The stream runs on the caller's thread. You manually manage audio data by reading from Input or Duplex streams and writing to Output or Duplex streams using methods like read, read_available, write, and write_available.

    2. Non-Blocking (NonBlocking): The stream runs on a separate thread managed by PortAudio. You interact with the audio data via a callback function. The specific arguments provided to your callback depend on the stream's Flow (Input, Output, or Duplex).

    Choose Blocking for simpler, synchronous logic where you control the timing of audio processing, and Non-Blocking for low-latency, high-performance applications where audio processing should not interrupt the main application logic.

  8. Install rust-portaudio via Cargo

    master

    Add portaudio to your Cargo.toml dependencies. The build script will attempt to automatically download and install the PortAudio C library if it is not already present on your system. If automatic installation fails, you may need to install PortAudio manually from the official website.

    [dependencies]
    portaudio = "*"
  9. Initialize and terminate PortAudio

    master

    Before using most PortAudio API functions, you must call Pa_Initialize() to initialize internal data structures and prepare host APIs. If Pa_Initialize() returns an error, do not call Pa_Terminate().

    When finished, call Pa_Terminate() to deallocate resources. Failure to call this before exiting can lead to resource leaks, such as audio devices remaining unavailable until a reboot. If Pa_Initialize() was called multiple times, it must be matched with an equal number of Pa_Terminate() calls.

    // Example lifecycle
    let err = unsafe { Pa_Initialize() };
    if err == PaErrorCode_paNoError {
        // ... use PortAudio ...
        unsafe { Pa_Terminate() };
    }
  10. Open a blocking audio stream

    master

    A blocking stream allows you to read or write audio samples synchronously using Stream::read and Stream::write. The stream is opened in Blocking mode.

    To open a blocking stream, use open_blocking_stream(settings) where settings implements StreamSettings (such as those produced by default_input_stream_settings or default_output_stream_settings).

    // Example: Opening a default output stream
    let settings = pa.default_output_stream_settings::<f32>(2, 44100.0, 256)?;
    let stream = pa.open_blocking_stream(settings)?;
    
    // The stream is returned in an inactive (stopped) state.
    stream.start();
  11. Read and write available frames in Blocking streams

    master

    In Blocking mode, you can check how much data is ready to be processed without causing the thread to block using the _available methods. This is useful for preventing unnecessary waiting or managing buffer overflows/underflows.

    • read_available(): Returns an Available enum. It can return Available::Frames(n) if n frames are ready, or error flags like Available::InputOverflowed or Available::OutputUnderflowed.
    • write_available(): Returns an Available enum indicating how many frames can be written without blocking, or error flags like Available::OutputUnderflowed.