Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions .github/actions/determine-msrv/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ inputs:
jack-msrv:
description: The MSRV for JACK backend
required: true
pulseaudio-msrv:
description: The MSRV for PulseAudio backend (optional, Linux only)
required: false
default: ''

outputs:
all-features:
Expand All @@ -23,7 +27,12 @@ runs:
run: |
PLATFORM_MSRV="${{ inputs.platform-msrv }}"
JACK_MSRV="${{ inputs.jack-msrv }}"
PULSEAUDIO_MSRV="${{ inputs.pulseaudio-msrv }}"
# Use sort -V to find the maximum version
MAX_MSRV=$(printf '%s\n' "$PLATFORM_MSRV" "$JACK_MSRV" | sort -V | tail -n1)
VERSIONS="$PLATFORM_MSRV $JACK_MSRV"
if [ -n "$PULSEAUDIO_MSRV" ]; then
VERSIONS="$VERSIONS $PULSEAUDIO_MSRV"
fi
MAX_MSRV=$(printf '%s\n' $VERSIONS | sort -V | tail -n1)
echo "all-features=$MAX_MSRV" >> $GITHUB_OUTPUT
echo "Platform MSRV: $PLATFORM_MSRV, JACK MSRV: $JACK_MSRV, Using for --all-features: $MAX_MSRV"
echo "Platform MSRV: $PLATFORM_MSRV, JACK MSRV: $JACK_MSRV, PulseAudio MSRV: $PULSEAUDIO_MSRV, Using for --all-features: $MAX_MSRV"
20 changes: 19 additions & 1 deletion .github/workflows/platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ env:
MSRV_ALSA: "1.82"
MSRV_COREAUDIO: "1.80"
MSRV_JACK: "1.82"
MSRV_PULSEAUDIO: "1.88"
MSRV_WASIP1: "1.78"
MSRV_WASM: "1.82"
MSRV_WINDOWS: "1.82"
Expand Down Expand Up @@ -64,6 +65,7 @@ jobs:
with:
platform-msrv: ${{ env.MSRV_ALSA }}
jack-msrv: ${{ env.MSRV_JACK }}
pulseaudio-msrv: ${{ env.MSRV_PULSEAUDIO }}

- name: Install Rust MSRV (${{ env.MSRV_ALSA }})
uses: dtolnay/rust-toolchain@master
Expand Down Expand Up @@ -113,6 +115,7 @@ jobs:
with:
platform-msrv: ${{ env.MSRV_ALSA }}
jack-msrv: ${{ env.MSRV_JACK }}
pulseaudio-msrv: ${{ env.MSRV_PULSEAUDIO }}

- name: Install Rust MSRV (${{ env.MSRV_ALSA }})
uses: dtolnay/rust-toolchain@master
Expand Down Expand Up @@ -279,12 +282,27 @@ jobs:
steps:
- uses: actions/checkout@v5

- name: Determine MSRV for all-features
id: msrv
uses: ./.github/actions/determine-msrv
with:
platform-msrv: ${{ env.MSRV_AAUDIO }}
jack-msrv: ${{ env.MSRV_JACK }}
pulseaudio-msrv: ${{ env.MSRV_PULSEAUDIO }}

- name: Install Rust MSRV (${{ env.MSRV_AAUDIO }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MSRV_AAUDIO }}
targets: ${{ env.TARGET }},aarch64-linux-android,i686-linux-android,x86_64-linux-android

- name: Install Rust MSRV (${{ steps.msrv.outputs.all-features }})
if: steps.msrv.outputs.all-features != env.MSRV_AAUDIO
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.msrv.outputs.all-features }}
targets: ${{ env.TARGET }}

- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
Expand All @@ -297,7 +315,7 @@ jobs:
run: cargo +${{ env.MSRV_AAUDIO }} check --examples --no-default-features --workspace --verbose --target ${{ env.TARGET }}

- name: Check examples (all features)
run: cargo +${{ env.MSRV_AAUDIO }} check --examples --all-features --workspace --verbose --target ${{ env.TARGET }}
run: cargo +${{ steps.msrv.outputs.all-features }} check --examples --all-features --workspace --verbose --target ${{ env.TARGET }}

- name: Check Android project
working-directory: examples/android
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- `DeviceBusy` error variant to `SupportedStreamConfigsError`, `DefaultStreamConfigError`, and
`BuildStreamError` for retryable device access errors (EBUSY, EAGAIN).
- **PulseAudio**: New host for Linux and some BSDs using the PulseAudio API.

### Changed

Expand Down
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ asio = [
"dep:num-traits",
]

# PulseAudio backend
# Provides audio I/O support on Linux and some BSDs using the PulseAudio sound server
# Requires: PulseAudio server and client libraries installed on the system
# Platform: Linux, DragonFly BSD, FreeBSD, NetBSD
pulseaudio = ["dep:pulseaudio", "dep:futures"]

# JACK Audio Connection Kit backend
# Provides low-latency connections between applications and audio hardware
# Requires: JACK server and client libraries installed on the system
Expand Down Expand Up @@ -89,6 +95,8 @@ alsa = "0.11"
libc = "0.2"
audio_thread_priority = { version = "0.34", optional = true }
jack = { version = "0.13", optional = true }
pulseaudio = { version = "0.3", optional = true }
futures = { version = "0.3", optional = true }

[target.'cfg(target_vendor = "apple")'.dependencies]
mach2 = "0.5"
Expand Down
87 changes: 51 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ Low-level library for audio input and output in pure Rust.

The minimum Rust version required depends on which audio backend and features you're using, as each platform has different dependencies:

- **AAudio (Android):** Rust **1.82** (due to `ndk` crate requirements)
- **ALSA (Linux/BSD):** Rust **1.82** (due to `alsa-sys` crate requirements)
- **CoreAudio (macOS/iOS):** Rust **1.80** (due to `coreaudio-rs` crate requirements)
- **JACK (Linux/BSD/macOS/Windows):** Rust **1.82** (due to `jack` crate requirements)
- **WASAPI/ASIO (Windows):** Rust **1.82** (due to `windows` crate requirements)
- **WASM (`wasm32-unknown`):** Rust **1.82** (due to `gloo` crate requirements)
- **WASM (`wasm32-wasip1`):** Rust **1.78** (target stabilized in 1.78)
- **AAudio (Android):** Rust **1.82**
- **ALSA (Linux/BSD):** Rust **1.82**
- **CoreAudio (macOS/iOS):** Rust **1.80**
- **JACK (Linux/BSD/macOS/Windows):** Rust **1.82**
- **PulseAudio (Linux/BSD):** Rust **1.88**
- **WASAPI/ASIO (Windows):** Rust **1.82**
- **WASM (`wasm32-unknown`):** Rust **1.82**
- **WASM (`wasm32-wasip1`):** Rust **1.78**
- **WASM (`audioworklet`):** Rust **nightly** (requires `-Zbuild-std` for atomics support)

## Supported Platforms
Expand All @@ -29,17 +30,20 @@ This library currently supports the following:
- Get the current default input and output stream formats for a device.
- Build and run input and output PCM streams on a chosen device with a given stream format.

Currently, supported hosts include:
Currently, supported platforms include:

- Linux (via ALSA or JACK)
- Windows (via WASAPI by default, ASIO or JACK optionally)
- macOS (via CoreAudio or JACK)
- iOS (via CoreAudio)
- Android (via AAudio)
- BSD (via ALSA by default, JACK or PulseAudio optionally)
- Emscripten
- iOS (via CoreAudio)
- Linux (via ALSA by default, JACK or PulseAudio optionally)
- macOS (via CoreAudio by default, JACK optionally)
- WebAssembly (via Web Audio API or Audio Worklet)
- Windows (via WASAPI by default, ASIO or JACK optionally)

Note that on Linux, the ALSA development files are required for building (even when using JACK). These are provided as part of the `libasound2-dev` package on Debian and Ubuntu distributions and `alsa-lib-devel` on Fedora.
Note that on Linux, the ALSA development files are required for building (even when using JACK or
PulseAudio). These are provided as part of the `libasound2-dev` package on Debian and Ubuntu
distributions and `alsa-lib-devel` on Fedora.

## Compiling for WebAssembly

Expand All @@ -61,6 +65,29 @@ Enables the ASIO (Audio Stream Input/Output) backend. ASIO provides low-latency

**Setup:** See the [ASIO setup guide](#asio-on-windows) below for detailed installation instructions.

### `audioworklet`

**Platform:** WebAssembly (wasm32-unknown-unknown)

Enables the Audio Worklet backend for lower-latency web audio processing compared to the default Web Audio API backend.

**Requirements:**
- The `wasm-bindgen` feature (automatically enabled)
- Build with atomics support: `RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+mutable-globals"`
- Web server must send Cross-Origin headers for SharedArrayBuffer support

**Setup:** See the `audioworklet-beep` example README for complete setup instructions.

**Note:** Audio Worklet provides better performance than the default Web Audio API by running audio processing on a separate thread.

### `custom`

**Platform:** All platforms

Enables support for user-defined custom host implementations, allowing integration with audio systems not natively supported by CPAL.

**Usage:** See `examples/custom.rs` for implementation details.

### `jack`

**Platform:** Linux, DragonFly BSD, FreeBSD, NetBSD, macOS, Windows
Expand All @@ -74,40 +101,28 @@ Enables the JACK (JACK Audio Connection Kit) backend. JACK is an audio server pr

**Note:** JACK is available as an alternative backend on all supported platforms. It provides an option for pro-audio users who need JACK's routing and inter-application audio connectivity. The native backends (ALSA for Linux/BSD, WASAPI/ASIO for Windows, CoreAudio for macOS) remain the default and recommended choice for most applications.

### `wasm-bindgen`
### `pulseaudio`

**Platform:** WebAssembly (wasm32-unknown-unknown)
**Platform:** Linux, DragonFly BSD, FreeBSD, NetBSD

Enables the Web Audio API backend for browser-based audio. This is the base feature required for any WebAssembly audio support.
Enables the PulseAudio backend. PulseAudio is a sound server commonly used on Linux desktops.

**Requirements:**
- Target `wasm32-unknown-unknown`
- Web browser with Web Audio API support
- PulseAudio server and client libraries must be installed on the system
-
**Usage:** See the [beep example](examples/beep.rs) for selecting the PulseAudio host at runtime.

**Usage:** See the `wasm-beep` example for basic WebAssembly audio setup.

### `audioworklet`
### `wasm-bindgen`

**Platform:** WebAssembly (wasm32-unknown-unknown)

Enables the Audio Worklet backend for lower-latency web audio processing compared to the default Web Audio API backend.
Enables the Web Audio API backend for browser-based audio. This is the base feature required for any WebAssembly audio support.

**Requirements:**
- The `wasm-bindgen` feature (automatically enabled)
- Build with atomics support: `RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+mutable-globals"`
- Web server must send Cross-Origin headers for SharedArrayBuffer support

**Setup:** See the `audioworklet-beep` example README for complete setup instructions.

**Note:** Audio Worklet provides better performance than the default Web Audio API by running audio processing on a separate thread.

### `custom`

**Platform:** All platforms

Enables support for user-defined custom host implementations, allowing integration with audio systems not natively supported by CPAL.
- Target `wasm32-unknown-unknown`
- Web browser with Web Audio API support

**Usage:** See `examples/custom.rs` for implementation details.
**Usage:** See the `wasm-beep` example for basic WebAssembly audio setup.

## ASIO on Windows

Expand Down
77 changes: 38 additions & 39 deletions examples/beep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use clap::Parser;
use cpal::{
traits::{DeviceTrait, HostTrait, StreamTrait},
FromSample, Sample, SizedSample, I24,
FromSample, HostUnavailable, Sample, SizedSample, I24,
};

#[derive(Parser, Debug)]
Expand All @@ -24,58 +24,57 @@ struct Opt {
#[arg(short, long)]
device: Option<String>,

/// Use the JACK host
#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd"
),
feature = "jack"
))]
#[arg(short, long)]
#[allow(dead_code)]
/// Use the JACK host. Requires `--features jack`.
#[arg(long, default_value_t = false)]
jack: bool,

/// Use the PulseAudio host. Requires `--features pulseaudio`.
#[arg(long, default_value_t = false)]
pulseaudio: bool,
}

fn main() -> anyhow::Result<()> {
let opt = Opt::parse();

// Conditionally compile with jack if the feature is specified.
#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd"
),
feature = "jack"
// Jack/PulseAudio support must be enabled at compile time, and is
// only available on some platforms.
#[allow(unused_mut, unused_assignments)]
let mut jack_host_id = Err(HostUnavailable);
#[allow(unused_mut, unused_assignments)]
let mut pulseaudio_host_id = Err(HostUnavailable);

#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd"
))]
{
#[cfg(feature = "jack")]
{
jack_host_id = Ok(cpal::HostId::Jack);
}

#[cfg(feature = "pulseaudio")]
{
pulseaudio_host_id = Ok(cpal::HostId::PulseAudio);
}
}

// Manually check for flags. Can be passed through cargo with -- e.g.
// cargo run --release --example beep --features jack -- --jack
let host = if opt.jack {
cpal::host_from_id(cpal::available_hosts()
.into_iter()
.find(|id| *id == cpal::HostId::Jack)
.expect(
"make sure --features jack is specified. only works on OSes where jack is available",
)).expect("jack host unavailable")
jack_host_id
.and_then(cpal::host_from_id)
.expect("make sure `--features jack` is specified, and the platform is supported")
} else if opt.pulseaudio {
pulseaudio_host_id
.and_then(cpal::host_from_id)
.expect("make sure `--features pulseaudio` is specified, and the platform is supported")
} else {
cpal::default_host()
};

#[cfg(any(
not(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd"
)),
not(feature = "jack")
))]
let host = cpal::default_host();

let device = if let Some(device) = opt.device {
let id = &device.parse().expect("failed to parse device id");
host.device_by_id(id)
Expand Down
Loading