Skip to content

Commit 4b22530

Browse files
Decodetalkersmidoriiroloxoron218Frandoroderickvd
authored
feat: add pipewire host (#1093)
Co-authored-by: Alexis Bekhdadi <alexis.bekhdadi@gmail.com> Co-authored-by: loxoron218 <enriquermcd@proton.me> Co-authored-by: Frando <frando@unbiskant.org> Co-authored-by: Roderick van Domburg <roderick@vandomburg.net>
1 parent 11f7726 commit 4b22530

17 files changed

Lines changed: 1598 additions & 108 deletions

File tree

.github/actions/determine-msrv/action.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ inputs:
1212
description: The MSRV for PulseAudio backend (optional, Linux only)
1313
required: false
1414
default: ''
15+
pipewire-msrv:
16+
description: The MSRV for Pipewire backend (optional, Linux only)
17+
required: false
18+
default: ''
1519

1620
outputs:
1721
all-features:
@@ -28,11 +32,15 @@ runs:
2832
PLATFORM_MSRV="${{ inputs.platform-msrv }}"
2933
JACK_MSRV="${{ inputs.jack-msrv }}"
3034
PULSEAUDIO_MSRV="${{ inputs.pulseaudio-msrv }}"
35+
PIPEWIRE_MSRV="${{ inputs.pipewire-msrv }}"
3136
# Use sort -V to find the maximum version
3237
VERSIONS="$PLATFORM_MSRV $JACK_MSRV"
3338
if [ -n "$PULSEAUDIO_MSRV" ]; then
3439
VERSIONS="$VERSIONS $PULSEAUDIO_MSRV"
3540
fi
41+
if [ -n "$PIPEWIRE_MSRV" ]; then
42+
VERSIONS="$VERSIONS $PIPEWIRE_MSRV"
43+
fi
3644
MAX_MSRV=$(printf '%s\n' $VERSIONS | sort -V | tail -n1)
3745
echo "all-features=$MAX_MSRV" >> $GITHUB_OUTPUT
38-
echo "Platform MSRV: $PLATFORM_MSRV, JACK MSRV: $JACK_MSRV, PulseAudio MSRV: $PULSEAUDIO_MSRV, Using for --all-features: $MAX_MSRV"
46+
echo "Platform MSRV: $PLATFORM_MSRV, JACK MSRV: $JACK_MSRV, PulseAudio MSRV: $PULSEAUDIO_MSRV, PIPEWIRE_MSRV: $PIPEWIRE_MSRV, Using for --all-features: $MAX_MSRV"

.github/workflows/platforms.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ env:
2929
MSRV_COREAUDIO: "1.80"
3030
MSRV_JACK: "1.82"
3131
MSRV_PULSEAUDIO: "1.88"
32+
MSRV_PIPEWIRE: "1.82"
3233
MSRV_WASIP1: "1.78"
3334
MSRV_WASM: "1.82"
3435
MSRV_WINDOWS: "1.82"
3536

36-
PACKAGES_LINUX: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev
37+
PACKAGES_LINUX: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev
3738

3839
ANDROID_COMPILE_SDK: "30"
3940
ANDROID_BUILD_TOOLS: "30.0.3"
@@ -66,6 +67,7 @@ jobs:
6667
platform-msrv: ${{ env.MSRV_ALSA }}
6768
jack-msrv: ${{ env.MSRV_JACK }}
6869
pulseaudio-msrv: ${{ env.MSRV_PULSEAUDIO }}
70+
pipewire-msrv: ${{ env.MSRV_PIPEWIRE }}
6971

7072
- name: Install Rust MSRV (${{ env.MSRV_ALSA }})
7173
uses: dtolnay/rust-toolchain@master
@@ -153,10 +155,10 @@ jobs:
153155
run: cross +${{ env.MSRV_ALSA }} test --no-default-features --workspace --verbose --target ${{ env.TARGET }}
154156

155157
- name: Run tests (all features)
156-
run: cross +${{ steps.msrv.outputs.all-features }} test --all-features --workspace --verbose --target ${{ env.TARGET }}
158+
run: cross +${{ steps.msrv.outputs.all-features }} test --features=jack,pulseaudio --workspace --verbose --target ${{ env.TARGET }}
157159

158160
- name: Check examples (all features)
159-
run: cross +${{ steps.msrv.outputs.all-features }} test --all-features --workspace --verbose --target ${{ env.TARGET }}
161+
run: cross +${{ steps.msrv.outputs.all-features }} test --features=jack,pulseaudio --workspace --verbose --target ${{ env.TARGET }}
160162

161163
# Windows (x86_64 and i686)
162164
windows:

.github/workflows/quality.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ jobs:
9292
if: runner.os == 'Linux'
9393
uses: awalsh128/cache-apt-pkgs-action@latest
9494
with:
95-
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev
95+
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev
9696

9797
- name: Setup ASIO SDK
9898
if: runner.os == 'Windows'
@@ -128,7 +128,7 @@ jobs:
128128
- name: Cache Linux audio packages
129129
uses: awalsh128/cache-apt-pkgs-action@latest
130130
with:
131-
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev
131+
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev
132132

133133
- name: Install Rust toolchain
134134
uses: dtolnay/rust-toolchain@nightly

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- `DeviceBusy` error variant to `SupportedStreamConfigsError`, `DefaultStreamConfigError`, and
1313
`BuildStreamError` for retryable device access errors (EBUSY, EAGAIN).
1414
- **PulseAudio**: New host for Linux and some BSDs using the PulseAudio API.
15+
- **Pipewire**: New host for Linux and some BSDs using the Pipewire API.
1516

1617
### Changed
1718

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ pulseaudio = ["dep:pulseaudio", "dep:futures"]
3232
# Note: JACK must be installed separately on all platforms
3333
jack = ["dep:jack"]
3434

35+
# PipeWire backend
36+
# Provides audio I/O on Linux and some BSDs via the PipeWire multimedia server
37+
# Requires: PipeWire server and client libraries installed on the system
38+
# Platform: Linux, DragonFly BSD, FreeBSD, NetBSD
39+
pipewire = ["dep:pipewire"]
40+
3541
# Audio thread priority elevation
3642
# Raises the audio callback thread to real-time priority for lower latency and fewer glitches
3743
# Requires: On Linux, either rtkit or appropriate user permissions (e.g. limits.conf or capabilities)
@@ -103,6 +109,7 @@ audio_thread_priority = { version = "0.34", optional = true }
103109
jack = { version = "0.13", optional = true }
104110
pulseaudio = { version = "0.3", optional = true }
105111
futures = { version = "0.3", optional = true }
112+
pipewire = { version = "0.9", optional = true, features = ["v0_3_53"] }
106113

107114
[target.'cfg(target_vendor = "apple")'.dependencies]
108115
mach2 = "0.5"

Cross.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,4 @@
22
dockerfile = "Dockerfile"
33

44
[target.armv7-unknown-linux-gnueabihf.env]
5-
passthrough = [
6-
"RUSTFLAGS",
7-
]
5+
passthrough = ["RUSTFLAGS"]

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ ENV PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/
77
RUN dpkg --add-architecture armhf && \
88
apt-get update && \
99
apt-get install libasound2-dev:armhf -y && \
10-
apt-get install libjack-jackd2-dev:armhf libjack-jackd2-0:armhf -y \
10+
apt-get install libjack-jackd2-dev:armhf libjack-jackd2-0:armhf -y
11+
# TODO: now the cross-rs is based on ubuntu:20.04, so it does not contain pipewire-0.3-dev

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,17 @@ Enables the PulseAudio backend. PulseAudio is a sound server commonly used on Li
112112
-
113113
**Usage:** See the [beep example](examples/beep.rs) for selecting the PulseAudio host at runtime.
114114

115+
### `pipewire`
116+
117+
**Platform:** Linux, DragonFly BSD, FreeBSD, NetBSD
118+
119+
Enables the Pipewire backend. Pipewire is a media server commonly used on Linux desktops.
120+
121+
**Requirements:**
122+
- Pipewire server and client libraries must be installed on the system
123+
-
124+
**Usage:** See the [beep example](examples/beep.rs) for selecting the Pipewire host at runtime.
125+
115126
### `wasm-bindgen`
116127

117128
**Platform:** WebAssembly (wasm32-unknown-unknown)
@@ -198,6 +209,7 @@ If you receive errors about no default input or output device:
198209

199210
- **Linux/ALSA:** Ensure your user is in the `audio` group and that ALSA is properly configured
200211
- **Linux/PulseAudio:** Check that PulseAudio is running: `pulseaudio --check`
212+
- **Linux/Pipewire:** Check that Pipewire is running: `systemd --user status pipewire`
201213
- **Windows:** Verify your audio device is enabled in Sound Settings
202214
- **macOS:** Check System Preferences > Sound for available devices
203215
- **Mobile (iOS/Android):** Ensure your app has microphone/audio permissions
@@ -236,6 +248,8 @@ For platform-specific features, enable the relevant features:
236248
```bash
237249
cargo run --example beep --features asio # Windows ASIO
238250
cargo run --example beep --features jack # JACK backend
251+
cargo run --example beep --features pulseaudio # PulseAudio backend
252+
cargo run --example beep --features pipewire # Pipewire backend
239253
```
240254

241255
## Contributing

examples/beep.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ struct Opt {
3131
/// Use the PulseAudio host. Requires `--features pulseaudio`.
3232
#[arg(long, default_value_t = false)]
3333
pulseaudio: bool,
34+
35+
/// Use the Pipewire host. Requires `--features pipewire`
36+
#[arg(long, default_value_t = false)]
37+
pipewire: bool,
3438
}
3539

3640
fn main() -> anyhow::Result<()> {
@@ -42,7 +46,8 @@ fn main() -> anyhow::Result<()> {
4246
let mut jack_host_id = Err(HostUnavailable);
4347
#[allow(unused_mut, unused_assignments)]
4448
let mut pulseaudio_host_id = Err(HostUnavailable);
45-
49+
#[allow(unused_mut, unused_assignments)]
50+
let mut pipewire_host_id = Err(HostUnavailable);
4651
#[cfg(any(
4752
target_os = "linux",
4853
target_os = "dragonfly",
@@ -59,6 +64,10 @@ fn main() -> anyhow::Result<()> {
5964
{
6065
pulseaudio_host_id = Ok(cpal::HostId::PulseAudio);
6166
}
67+
#[cfg(feature = "pipewire")]
68+
{
69+
pipewire_host_id = Ok(cpal::HostId::PipeWire);
70+
}
6271
}
6372

6473
// Manually check for flags. Can be passed through cargo with -- e.g.
@@ -71,6 +80,10 @@ fn main() -> anyhow::Result<()> {
7180
pulseaudio_host_id
7281
.and_then(cpal::host_from_id)
7382
.expect("make sure `--features pulseaudio` is specified, and the platform is supported")
83+
} else if opt.pipewire {
84+
pipewire_host_id
85+
.and_then(cpal::host_from_id)
86+
.expect("make sure `--features pipewire` is specified, and the platform is supported")
7487
} else {
7588
cpal::default_host()
7689
};

examples/record_wav.rs

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use clap::Parser;
66
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
7-
use cpal::{FromSample, Sample};
7+
use cpal::{FromSample, HostUnavailable, Sample};
88
use std::fs::File;
99
use std::io::BufWriter;
1010
use std::sync::{Arc, Mutex};
@@ -20,58 +20,70 @@ struct Opt {
2020
#[arg(long, default_value_t = 3)]
2121
duration: u64,
2222

23-
/// Use the JACK host
24-
#[cfg(all(
25-
any(
26-
target_os = "linux",
27-
target_os = "dragonfly",
28-
target_os = "freebsd",
29-
target_os = "netbsd"
30-
),
31-
feature = "jack"
32-
))]
33-
#[arg(short, long)]
34-
#[allow(dead_code)]
23+
/// Use the JACK host. Requires `--features jack`.
24+
#[arg(long, default_value_t = false)]
3525
jack: bool,
26+
27+
/// Use the PulseAudio host. Requires `--features pulseaudio`.
28+
#[arg(long, default_value_t = false)]
29+
pulseaudio: bool,
30+
31+
/// Use the Pipewire host. Requires `--features pipewire`
32+
#[arg(long, default_value_t = false)]
33+
pipewire: bool,
3634
}
3735

3836
fn main() -> Result<(), anyhow::Error> {
3937
let opt = Opt::parse();
4038

41-
// Conditionally compile with jack if the feature is specified.
42-
#[cfg(all(
43-
any(
44-
target_os = "linux",
45-
target_os = "dragonfly",
46-
target_os = "freebsd",
47-
target_os = "netbsd"
48-
),
49-
feature = "jack"
39+
// Jack/PulseAudio support must be enabled at compile time, and is
40+
// only available on some platforms.
41+
#[allow(unused_mut, unused_assignments)]
42+
let mut jack_host_id = Err(HostUnavailable);
43+
#[allow(unused_mut, unused_assignments)]
44+
let mut pulseaudio_host_id = Err(HostUnavailable);
45+
#[allow(unused_mut, unused_assignments)]
46+
let mut pipewire_host_id = Err(HostUnavailable);
47+
#[cfg(any(
48+
target_os = "linux",
49+
target_os = "dragonfly",
50+
target_os = "freebsd",
51+
target_os = "netbsd"
5052
))]
53+
{
54+
#[cfg(feature = "jack")]
55+
{
56+
jack_host_id = Ok(cpal::HostId::Jack);
57+
}
58+
59+
#[cfg(feature = "pulseaudio")]
60+
{
61+
pulseaudio_host_id = Ok(cpal::HostId::PulseAudio);
62+
}
63+
#[cfg(feature = "pipewire")]
64+
{
65+
pipewire_host_id = Ok(cpal::HostId::PipeWire);
66+
}
67+
}
68+
5169
// Manually check for flags. Can be passed through cargo with -- e.g.
52-
// cargo run --release --example beep --features jack -- --jack
70+
// cargo run --release --example record_wav --features jack -- --jack
5371
let host = if opt.jack {
54-
cpal::host_from_id(cpal::available_hosts()
55-
.into_iter()
56-
.find(|id| *id == cpal::HostId::Jack)
57-
.expect(
58-
"make sure --features jack is specified. only works on OSes where jack is available",
59-
)).expect("jack host unavailable")
72+
jack_host_id
73+
.and_then(cpal::host_from_id)
74+
.expect("make sure `--features jack` is specified, and the platform is supported")
75+
} else if opt.pulseaudio {
76+
pulseaudio_host_id
77+
.and_then(cpal::host_from_id)
78+
.expect("make sure `--features pulseaudio` is specified, and the platform is supported")
79+
} else if opt.pipewire {
80+
pipewire_host_id
81+
.and_then(cpal::host_from_id)
82+
.expect("make sure `--features pipewire` is specified, and the platform is supported")
6083
} else {
6184
cpal::default_host()
6285
};
6386

64-
#[cfg(any(
65-
not(any(
66-
target_os = "linux",
67-
target_os = "dragonfly",
68-
target_os = "freebsd",
69-
target_os = "netbsd"
70-
)),
71-
not(feature = "jack")
72-
))]
73-
let host = cpal::default_host();
74-
7587
// Set up the input device and stream with the default input config.
7688
let device = if let Some(device) = opt.device {
7789
let id = &device.parse().expect("failed to parse input device id");

0 commit comments

Comments
 (0)