From ccc2c1aefea0a204de6df04618635c60510a20f8 Mon Sep 17 00:00:00 2001 From: Chandler Newman Date: Sun, 12 Apr 2026 14:19:09 +0100 Subject: [PATCH 1/4] Add tvOS support for CoreAudio backend Use target_vendor = "apple" for platform gates that apply to all Apple platforms. Use not(target_os = "macos") within the coreaudio module to select the iOS/tvOS code path. Bump mach2 to 0.6 (uses core::ffi instead of ::libc, fixing tvOS builds). Bump CoreAudio MSRV to 1.85 to match mach2 0.6's edition2024 requirement. --- .github/workflows/platforms.yml | 2 +- Cargo.toml | 4 ++-- README.md | 2 +- src/host/coreaudio/mod.rs | 4 ++-- src/host/mod.rs | 5 ++--- src/platform/mod.rs | 10 ++++------ 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/platforms.yml b/.github/workflows/platforms.yml index db148e375..7e71fb437 100644 --- a/.github/workflows/platforms.yml +++ b/.github/workflows/platforms.yml @@ -26,7 +26,7 @@ env: # MSRV varies by backend due to platform-specific dependencies MSRV_AAUDIO: "1.85" MSRV_ALSA: "1.82" - MSRV_COREAUDIO: "1.80" + MSRV_COREAUDIO: "1.85" MSRV_JACK: "1.82" MSRV_PIPEWIRE: "1.85" MSRV_PULSEAUDIO: "1.88" diff --git a/Cargo.toml b/Cargo.toml index 7a02ead0c..f187ccd3d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -114,7 +114,7 @@ futures = { version = "0.3", optional = true } pipewire = { version = "0.9", optional = true, features = ["v0_3_53"] } [target.'cfg(target_vendor = "apple")'.dependencies] -mach2 = "0.5" +mach2 = "0.6" coreaudio-rs = { version = "0.14", default-features = false, features = [ "core_audio", "audio_toolbox", @@ -147,7 +147,7 @@ objc2 = { version = "0.6" } [target.'cfg(target_os = "macos")'.dependencies] jack = { version = "0.13", optional = true } -[target.'cfg(target_os = "ios")'.dependencies] +[target.'cfg(any(target_os = "ios", target_os = "tvos"))'.dependencies] block2 = "0.6" objc2-foundation = { version = "0.3", features = [ "block2", diff --git a/README.md b/README.md index 53f64a2e3..b2520ef25 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The minimum Rust version required depends on which audio backend and features yo - **AAudio (Android):** Rust **1.85** - **ALSA (Linux/BSD):** Rust **1.82** -- **CoreAudio (macOS/iOS):** Rust **1.80** +- **CoreAudio (macOS/iOS/tvOS):** Rust **1.85** - **JACK (Linux/BSD/macOS/Windows):** Rust **1.82** - **PipeWire (Linux/BSD):** Rust **1.85** - **PulseAudio (Linux/BSD):** Rust **1.88** diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index 3255a7992..20b3fdc0f 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -12,12 +12,12 @@ use crate::{BuildStreamError, SupportedStreamConfigsError}; use crate::{BackendSpecificError, SampleFormat, StreamConfig}; -#[cfg(target_os = "ios")] +#[cfg(not(target_os = "macos"))] mod ios; #[cfg(target_os = "macos")] mod macos; -#[cfg(target_os = "ios")] +#[cfg(not(target_os = "macos"))] #[allow(unused_imports)] pub use self::ios::{ enumerate::{Devices, SupportedInputConfigs, SupportedOutputConfigs}, diff --git a/src/host/mod.rs b/src/host/mod.rs index 8f46cdb08..5a9e2e46b 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -19,7 +19,7 @@ pub(crate) mod asio; pub(crate) mod audioworklet; #[cfg(windows)] pub(crate) mod com; -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(target_vendor = "apple")] pub(crate) mod coreaudio; #[cfg(all( feature = "jack", @@ -66,8 +66,7 @@ pub(crate) mod custom; target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", - target_os = "macos", - target_os = "ios", + target_vendor = "apple", target_os = "android", all(target_arch = "wasm32", feature = "wasm-bindgen"), )))] diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 9dc091d96..365b89c6d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -811,9 +811,9 @@ mod platform_impl { } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(target_vendor = "apple")] mod platform_impl { - #[cfg_attr(docsrs, doc(cfg(any(target_os = "macos", target_os = "ios"))))] + #[cfg_attr(docsrs, doc(cfg(target_vendor = "apple")))] pub use crate::host::coreaudio::Host as CoreAudioHost; #[cfg(all(feature = "jack", target_os = "macos"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "jack", target_os = "macos"))))] @@ -915,8 +915,7 @@ mod platform_impl { target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", - target_os = "macos", - target_os = "ios", + target_vendor = "apple", target_os = "android", all(target_arch = "wasm32", feature = "wasm-bindgen"), )))] @@ -929,8 +928,7 @@ mod platform_impl { target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", - target_os = "macos", - target_os = "ios", + target_vendor = "apple", target_os = "android", all(target_arch = "wasm32", feature = "wasm-bindgen") )))) From 36ba8bccaad38533029e3c3588ba6c91fdaefa71 Mon Sep 17 00:00:00 2001 From: Chandler Newman Date: Sun, 12 Apr 2026 14:33:10 +0100 Subject: [PATCH 2/4] Add tvOS CI job Tier 3 target (aarch64-apple-tvos) using nightly with -Z build-std. Checks default, no-default, and all-features configurations. Added to the publish gate. --- .github/workflows/platforms.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/platforms.yml b/.github/workflows/platforms.yml index 7e71fb437..5389870c0 100644 --- a/.github/workflows/platforms.yml +++ b/.github/workflows/platforms.yml @@ -405,6 +405,36 @@ jobs: working-directory: examples/ios-feedback run: xcodebuild -scheme cpal-ios-example -configuration Debug -derivedDataPath build -sdk iphonesimulator -arch arm64 + # tvOS (Tier 3 target, requires nightly + build-std) + tvos: + runs-on: macOS-latest + env: + TARGET: aarch64-apple-tvos + steps: + - uses: actions/checkout@v5 + + - name: Install dependencies + run: brew install llvm + + - name: Install Rust nightly (for build-std) + uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src + + - name: Rust Cache + uses: Swatinem/rust-cache@v2 + with: + key: tvos + + - name: Check examples (default features) + run: cargo +nightly check --examples --workspace --verbose --target ${{ env.TARGET }} -Z build-std + + - name: Check examples (no default features) + run: cargo +nightly check --examples --no-default-features --workspace --verbose --target ${{ env.TARGET }} -Z build-std + + - name: Check examples (all features) + run: cargo +nightly check --examples --all-features --workspace --verbose --target ${{ env.TARGET }} -Z build-std + # WebAssembly - wasm-bindgen wasm-bindgen: runs-on: ubuntu-latest @@ -546,6 +576,7 @@ jobs: - macos - android - ios + - tvos - wasm-bindgen - wasm-audioworklet - wasm-wasip1 From be723c7fa2412ae7737f26d1154b4339ff4c9cc2 Mon Sep 17 00:00:00 2001 From: Chandler Newman Date: Sun, 12 Apr 2026 14:42:26 +0100 Subject: [PATCH 3/4] Add tvOS to clippy nightly matrix Convert lints-nightly from a single WASM-worklet job into a matrix with WASM-worklet and tvOS entries. --- .github/workflows/quality.yml | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index e13a9f271..ffead0882 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -114,12 +114,33 @@ jobs: run: cargo clippy --all --target ${{ matrix.target }} ${{ matrix.features }} -- -D warnings lints-nightly: - name: clippy-WASM-worklet - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + # WASM - AudioWorklet (requires atomics + build-std) + - target: wasm32-unknown-unknown + name: WASM-worklet + features: --features audioworklet + os: ubuntu-latest + build_std: std,panic_abort + rustflags: -C target-feature=+atomics,+bulk-memory,+mutable-globals + + # tvOS - CoreAudio (Tier 3 target) + - target: aarch64-apple-tvos + name: tvOS + features: "" + os: macOS-latest + build_std: std + rustflags: "" + + name: clippy-${{ matrix.name }} + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 - name: Cache Linux audio packages + if: runner.os == 'Linux' uses: awalsh128/cache-apt-pkgs-action@latest with: packages: libasound2-dev libjack-jackd2-dev libjack-jackd2-0 libdbus-1-dev libpipewire-0.3-dev @@ -128,14 +149,14 @@ jobs: uses: dtolnay/rust-toolchain@nightly with: components: clippy, rust-src - targets: wasm32-unknown-unknown + targets: ${{ matrix.target }} - name: Rust Cache uses: Swatinem/rust-cache@v2 with: - key: clippy-wasm32-worklet + key: clippy-${{ matrix.target }}-${{ matrix.name }} - - name: Run clippy (audioworklet) + - name: Run clippy env: - RUSTFLAGS: -C target-feature=+atomics,+bulk-memory,+mutable-globals - run: cargo +nightly clippy --all --target wasm32-unknown-unknown --features audioworklet -Z build-std=std,panic_abort -- -D warnings + RUSTFLAGS: ${{ matrix.rustflags }} + run: cargo +nightly clippy --all --target ${{ matrix.target }} ${{ matrix.features }} -Z build-std=${{ matrix.build_std }} -- -D warnings From 8a01f7b1b1bb601bbce99dcdff5d68d54e8adf84 Mon Sep 17 00:00:00 2001 From: Chandler Newman Date: Sat, 18 Apr 2026 14:47:21 +0100 Subject: [PATCH 4/4] Document tvOS MSRV/mach2 bumps and drop unused llvm step --- .github/workflows/platforms.yml | 3 --- CHANGELOG.md | 3 +++ src/host/coreaudio/mod.rs | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/platforms.yml b/.github/workflows/platforms.yml index 5389870c0..335963762 100644 --- a/.github/workflows/platforms.yml +++ b/.github/workflows/platforms.yml @@ -413,9 +413,6 @@ jobs: steps: - uses: actions/checkout@v5 - - name: Install dependencies - run: brew install llvm - - name: Install Rust nightly (for build-std) uses: dtolnay/rust-toolchain@nightly with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 014c275dc..9a5b6a94f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 backends to override it. - `StreamTrait::now()` to query the current instant on the stream's clock. - **ALSA**: `device_by_id()` now accepts PCM shorthand names such as `hw:0,0` and `plughw:foo`. +- **CoreAudio**: tvOS target support (Tier 3, requires nightly). - **PipeWire**: New host for Linux and some BSDs using the PipeWire API. - **PulseAudio**: New host for Linux and some BSDs using the PulseAudio API. @@ -50,6 +51,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **ASIO**: Stream error callback now receives `StreamError::StreamInvalidated` when the driver reports a sample rate change (`sampleRateDidChange`) of 1 Hz or more from the configured rate. - **AudioWorklet**: `BufferSize::Fixed` now sets `renderSizeHint` on the `AudioContext`. +- **CoreAudio**: Bump MSRV to 1.85. +- **CoreAudio**: Bump `mach2` to 0.6 (uses `core::ffi` instead of `libc`, enables tvOS builds). - **CoreAudio**: Timestamps now include device latency and safety offset. - **CoreAudio**: Poisoned stream mutex in stream functions now propagate panics. - **CoreAudio**: Physical stream format is now set directly on the hardware device. diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index 20b3fdc0f..5bf24a058 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -1,6 +1,6 @@ //! CoreAudio backend implementation. //! -//! Default backend on macOS and iOS. +//! Default backend on macOS, iOS, and tvOS. use objc2_core_audio_types::{ kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatFlagIsSignedInteger, @@ -12,6 +12,8 @@ use crate::{BuildStreamError, SupportedStreamConfigsError}; use crate::{BackendSpecificError, SampleFormat, StreamConfig}; +// iOS and tvOS share the same CoreAudio / AudioUnit surface (RemoteIO, +// AVAudioSession), so both target the `ios` submodule. #[cfg(not(target_os = "macos"))] mod ios; #[cfg(target_os = "macos")]