Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
13677c8
chore: little process
Decodetalkers Jan 5, 2026
f385d75
feat: base settings
Decodetalkers Jan 5, 2026
46904a6
chore: base connect function
Decodetalkers Jan 6, 2026
38d35df
chore: nearly finished
Decodetalkers Jan 6, 2026
2ff6ce6
chore: seems working well
Decodetalkers Jan 6, 2026
7fe2e0e
fix: feature wrong in platform/mod.rs
Decodetalkers Jan 6, 2026
fd9527e
chore: tidy up, clippy, fmt
Decodetalkers Jan 6, 2026
06c2909
fix: output error
Decodetalkers Jan 6, 2026
ef3b8ff
feat: sink can do both side
Decodetalkers Jan 6, 2026
448a7dd
chore: complete supported configs
Decodetalkers Jan 6, 2026
4bdea8e
feat: support show the device type
Decodetalkers Jan 6, 2026
9ba9223
chore: modify the _typos.toml
Decodetalkers Jan 6, 2026
aed88a5
chore: reset default backend to alsa
Decodetalkers Jan 6, 2026
b6a17b0
chore: add pipewire dependence
Decodetalkers Jan 6, 2026
272a398
fix: target object should be the node name
Decodetalkers Jan 7, 2026
e13b722
chore: use object_serial instead
Decodetalkers Jan 7, 2026
6f6e413
chore: capture should be output
Decodetalkers Jan 7, 2026
66ec2bb
chore: remove unused timeout
Decodetalkers Jan 8, 2026
a5871e9
fix: test pipewire with rust 1.85
Decodetalkers Jan 9, 2026
87bd324
chore: keep rust version
Decodetalkers Jan 9, 2026
754002b
fix: ci
Decodetalkers Jan 10, 2026
3a86e76
fix: ci problem
Decodetalkers Jan 11, 2026
db94541
fix: remove the unexisted variable
Decodetalkers Jan 11, 2026
54d69da
feat(pipewire): add support for I64, U64, and F32 sample formats
loxoron218 Jan 20, 2026
e7d01be
feat(pipewire): support multiple sample rates in device configuration
loxoron218 Jan 21, 2026
a9e4e40
fix(pipewire): improve error handling for stream initialization timeout
loxoron218 Jan 21, 2026
5277348
fix(pipewire): normalize channel list parsing and improve code format…
loxoron218 Jan 21, 2026
2e7c9a7
fix: support bluetooth devices
Frando Feb 10, 2026
24327a4
feat(pipewire): use stable node.name as device ID and enrich device d…
Frando Feb 10, 2026
e35cc43
fixup device properties
Frando Feb 11, 2026
0823f9e
chore: clippy fix
Decodetalkers Feb 11, 2026
8ac6c74
chore: follow upstream changes
Decodetalkers Feb 11, 2026
b66f059
chore: clippy fix for examples
Decodetalkers Feb 12, 2026
66e1a5a
fix: stream dropped, pipewire still works
Decodetalkers Feb 15, 2026
73f7f92
fix: thread won't stop
Decodetalkers Feb 16, 2026
1c7ba88
fix: support support audio rate
Decodetalkers Feb 16, 2026
58342ea
fix: do not make StreamConfig Copyable
Decodetalkers Feb 16, 2026
58ee7ff
fix: not set rate but the quantum
Decodetalkers Feb 17, 2026
39c22e9
fix: buffer_size not work
Decodetalkers Feb 17, 2026
852c607
chore: use NODE_FORCE_QUANTUM instead
Decodetalkers Feb 17, 2026
7599358
chore: adjust the examples
Decodetalkers Feb 20, 2026
49e88fc
chore: do fmt
Decodetalkers Feb 20, 2026
ce129e5
fix: unit test
Decodetalkers Feb 20, 2026
08234c3
chore: do as suggested, use the type alias, join thread and etc
Decodetalkers Feb 22, 2026
31e3955
chore: comment RT_PROCESS
Decodetalkers Feb 22, 2026
7f7f854
chore: better error handle
Decodetalkers Feb 22, 2026
48af57e
chore: do error callback if the rate or channels does not fit the input
Decodetalkers Feb 22, 2026
b2acf3a
chore: rename class_type to class
Decodetalkers Feb 22, 2026
aa0aeb8
chore: use keys in pipewire crate
Decodetalkers Feb 22, 2026
ae7d5ea
chore: use constants and the keys in pipewire
Decodetalkers Feb 22, 2026
76e1cb2
chore: try downgrade the compiler version
Decodetalkers Feb 22, 2026
efd177e
chore: set default_input to DefaultInput not DefaultSink
Decodetalkers Feb 22, 2026
330e5b8
chore: handle state change in pipewire
Decodetalkers Feb 22, 2026
95360e5
fix: typos pedding and peddinglist
Decodetalkers Feb 22, 2026
72d1782
fix: allow_rates return wrong type
Decodetalkers Feb 22, 2026
b22d907
chore: add pipewire available check
Decodetalkers Feb 22, 2026
3967b47
fix: connect_output does not listen to the FixedBuffer
Decodetalkers Feb 22, 2026
0a75f5a
chore: remove unwrap and use expect for some known results
Decodetalkers Feb 24, 2026
21955f2
chore: try revert the changes in platforms.yml
Decodetalkers Feb 24, 2026
4be348f
fix: message related to pipewire should be pipewire not jack
Decodetalkers Feb 24, 2026
3508e4f
chore: split the logic of parse rates to function and add unit tests
Decodetalkers Feb 24, 2026
efb9dd7
chore: remove limit_quantum
Decodetalkers Feb 25, 2026
cce2ea7
chore: rename init_roundtrip to init_devices
Decodetalkers Feb 25, 2026
b81da72
chore: just check the pipewire service to confirm if it support pipewire
Decodetalkers Feb 25, 2026
7a57d62
feat: support Stream/Output/Audio and Stream/Input/Audio "stream.0"
Decodetalkers Feb 25, 2026
9c01815
chore: update the comment in stream.rs, about the format change
Decodetalkers Feb 25, 2026
17252d3
chore: remove blank in examples/beep.rs
Decodetalkers Feb 25, 2026
c1dcc4c
chore: sync the written way in beep.rs
Decodetalkers Feb 25, 2026
7075e37
chore: sync the comment of pipewire in the commands in examples
Decodetalkers Feb 25, 2026
1bc5358
chore: make the ci same logic with pulseaudio
Decodetalkers Feb 25, 2026
ddbed8c
chore: remove the file added by typos-lsp
Decodetalkers Feb 25, 2026
70ddfab
chore: not commit allow(dead_code) to whole struct
Decodetalkers Feb 25, 2026
6f5d1c0
chore: rename all rv to rx
Decodetalkers Feb 25, 2026
c1ee289
chore: remove useless code
Decodetalkers Feb 26, 2026
da111e4
chore: clippy fix for examples
Decodetalkers Feb 26, 2026
0ba60e9
chore:fix typos in examples and ci, format in Cargo.toml and add docu…
Decodetalkers Feb 26, 2026
de85918
chore: add pipewire information to CHANGELOG and README
Decodetalkers Feb 26, 2026
042788a
chore: remove all expect
Decodetalkers Feb 28, 2026
28fcfcc
chore: stop stream when input params do not match
Decodetalkers Feb 28, 2026
65a9e00
chore: remove useless comment
Decodetalkers Feb 28, 2026
616ee50
chore: remove useless function
Decodetalkers Feb 28, 2026
5ae8f23
chore: use timestamp of pipewire
Decodetalkers Feb 28, 2026
b609389
chore: reset samples to "zero" when doing output
Decodetalkers Mar 1, 2026
fe87df3
chore: modify the stream.rs as suggested
Decodetalkers Mar 1, 2026
6838462
chore: move fill_with_equilibrium to src/host/mod.rs and use it
Decodetalkers Mar 1, 2026
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
10 changes: 9 additions & 1 deletion .github/actions/determine-msrv/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ inputs:
description: The MSRV for PulseAudio backend (optional, Linux only)
required: false
default: ''
pipewire-msrv:
description: The MSRV for Pipewire backend (optional, Linux only)
required: false
default: ''

outputs:
all-features:
Expand All @@ -28,11 +32,15 @@ runs:
PLATFORM_MSRV="${{ inputs.platform-msrv }}"
JACK_MSRV="${{ inputs.jack-msrv }}"
PULSEAUDIO_MSRV="${{ inputs.pulseaudio-msrv }}"
PIPEWIRE_MSRV="${{ inputs.pipewire-msrv }}"
# Use sort -V to find the maximum version
VERSIONS="$PLATFORM_MSRV $JACK_MSRV"
if [ -n "$PULSEAUDIO_MSRV" ]; then
VERSIONS="$VERSIONS $PULSEAUDIO_MSRV"
fi
if [ -n "$PIPEWIRE_MSRV" ]; then
VERSIONS="$VERSIONS $PIPEWIRE_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, PulseAudio MSRV: $PULSEAUDIO_MSRV, Using for --all-features: $MAX_MSRV"
echo "Platform MSRV: $PLATFORM_MSRV, JACK MSRV: $JACK_MSRV, PulseAudio MSRV: $PULSEAUDIO_MSRV, PIPEWIRE_MSRV: $PIPEWIRE_MSRV, Using for --all-features: $MAX_MSRV"
8 changes: 5 additions & 3 deletions .github/workflows/platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ env:
MSRV_COREAUDIO: "1.80"
MSRV_JACK: "1.82"
MSRV_PULSEAUDIO: "1.88"
MSRV_PIPEWIRE: "1.82"
MSRV_WASIP1: "1.78"
MSRV_WASM: "1.82"
MSRV_WINDOWS: "1.82"

PACKAGES_LINUX: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev
PACKAGES_LINUX: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev

ANDROID_COMPILE_SDK: "30"
ANDROID_BUILD_TOOLS: "30.0.3"
Expand Down Expand Up @@ -66,6 +67,7 @@ jobs:
platform-msrv: ${{ env.MSRV_ALSA }}
jack-msrv: ${{ env.MSRV_JACK }}
pulseaudio-msrv: ${{ env.MSRV_PULSEAUDIO }}
pipewire-msrv: ${{ env.MSRV_PIPEWIRE }}

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

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

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

# Windows (x86_64 and i686)
windows:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ jobs:
if: runner.os == 'Linux'
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev
packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev

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

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,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.
- **Pipewire**: New host for Linux and some BSDs using the Pipewire API.

### Changed

Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ pulseaudio = ["dep:pulseaudio", "dep:futures"]
# Note: JACK must be installed separately on all platforms
jack = ["dep:jack"]

# PipeWire backend
# Provides audio I/O on Linux and some BSDs via the PipeWire multimedia server
# Requires: PipeWire server and client libraries installed on the system
# Platform: Linux, DragonFly BSD, FreeBSD, NetBSD
pipewire = ["dep:pipewire"]

# Audio thread priority elevation
# Raises the audio callback thread to real-time priority for lower latency and fewer glitches
# Requires: On Linux, either rtkit or appropriate user permissions (e.g. limits.conf or capabilities)
Expand Down Expand Up @@ -103,6 +109,7 @@ 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 }
pipewire = { version = "0.9", optional = true, features = ["v0_3_53"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v0_3_50 not high enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does not exist


[target.'cfg(target_vendor = "apple")'.dependencies]
mach2 = "0.5"
Expand Down
4 changes: 1 addition & 3 deletions Cross.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,4 @@
dockerfile = "Dockerfile"

[target.armv7-unknown-linux-gnueabihf.env]
passthrough = [
"RUSTFLAGS",
]
passthrough = ["RUSTFLAGS"]
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ ENV PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig/
RUN dpkg --add-architecture armhf && \
apt-get update && \
apt-get install libasound2-dev:armhf -y && \
apt-get install libjack-jackd2-dev:armhf libjack-jackd2-0:armhf -y \
apt-get install libjack-jackd2-dev:armhf libjack-jackd2-0:armhf -y
# TODO: now the cross-rs is based on ubuntu:20.04, so it does not contain pipewire-0.3-dev
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,17 @@ Enables the PulseAudio backend. PulseAudio is a sound server commonly used on Li
-
**Usage:** See the [beep example](examples/beep.rs) for selecting the PulseAudio host at runtime.

### `pipewire`

**Platform:** Linux, DragonFly BSD, FreeBSD, NetBSD

Enables the Pipewire backend. Pipewire is a media server commonly used on Linux desktops.

**Requirements:**
- Pipewire server and client libraries must be installed on the system
-
**Usage:** See the [beep example](examples/beep.rs) for selecting the Pipewire host at runtime.

### `wasm-bindgen`

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

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

## Contributing
Expand Down
15 changes: 14 additions & 1 deletion examples/beep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ struct Opt {
/// Use the PulseAudio host. Requires `--features pulseaudio`.
#[arg(long, default_value_t = false)]
pulseaudio: bool,

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

fn main() -> anyhow::Result<()> {
Expand All @@ -42,7 +46,8 @@ fn main() -> anyhow::Result<()> {
let mut jack_host_id = Err(HostUnavailable);
#[allow(unused_mut, unused_assignments)]
let mut pulseaudio_host_id = Err(HostUnavailable);

#[allow(unused_mut, unused_assignments)]
let mut pipewire_host_id = Err(HostUnavailable);
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
Expand All @@ -59,6 +64,10 @@ fn main() -> anyhow::Result<()> {
{
pulseaudio_host_id = Ok(cpal::HostId::PulseAudio);
}
#[cfg(feature = "pipewire")]
{
pipewire_host_id = Ok(cpal::HostId::PipeWire);
}
}

// Manually check for flags. Can be passed through cargo with -- e.g.
Expand All @@ -71,6 +80,10 @@ fn main() -> anyhow::Result<()> {
pulseaudio_host_id
.and_then(cpal::host_from_id)
.expect("make sure `--features pulseaudio` is specified, and the platform is supported")
} else if opt.pipewire {
pipewire_host_id
.and_then(cpal::host_from_id)
.expect("make sure `--features pipewire` is specified, and the platform is supported")
} else {
cpal::default_host()
};
Expand Down
92 changes: 52 additions & 40 deletions examples/record_wav.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use clap::Parser;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::{FromSample, Sample};
use cpal::{FromSample, HostUnavailable, Sample};
use std::fs::File;
use std::io::BufWriter;
use std::sync::{Arc, Mutex};
Expand All @@ -20,58 +20,70 @@ struct Opt {
#[arg(long, default_value_t = 3)]
duration: u64,

/// 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,

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

fn main() -> Result<(), anyhow::Error> {
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);
#[allow(unused_mut, unused_assignments)]
let mut pipewire_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);
}
#[cfg(feature = "pipewire")]
{
pipewire_host_id = Ok(cpal::HostId::PipeWire);
}
}

// Manually check for flags. Can be passed through cargo with -- e.g.
// cargo run --release --example beep --features jack -- --jack
// cargo run --release --example record_wav --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 if opt.pipewire {
pipewire_host_id
.and_then(cpal::host_from_id)
.expect("make sure `--features pipewire` 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();

// Set up the input device and stream with the default input config.
let device = if let Some(device) = opt.device {
let id = &device.parse().expect("failed to parse input device id");
Expand Down
Loading