diff --git a/Cargo.toml b/Cargo.toml index 174add13d..944881e2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ repository = "https://github.com/RustAudio/cpal" documentation = "https://docs.rs/cpal" license = "Apache-2.0" keywords = ["audio", "sound"] -edition = "2021" +edition = "2024" rust-version = "1.85" [badges] @@ -90,7 +90,7 @@ dasp_sample = "0.11" anyhow = "1.0" hound = "3.5" ringbuf = "0.4" -clap = { version = ">=4.6, <5", features = ["derive"] } +clap = { version = "4.6", features = ["derive"] } # Support a range of versions in order to avoid duplication of this crate. Make sure to test all # versions when bumping to a new release, and only increase the minimum when absolutely necessary. @@ -114,7 +114,7 @@ windows = { version = ">=0.61, <=0.62", features = [ "Win32_UI_Shell_PropertiesSystem", ] } audio_thread_priority = { version = "0.35", optional = true, default-features = false } -asio-sys = { version = "0.3.0", path = "asio-sys", optional = true } +asio-sys = { version = "0.4.0", path = "asio-sys", optional = true } num-traits = { version = "0.2", optional = true } jack = { version = "0.13", optional = true } diff --git a/asio-sys/Cargo.toml b/asio-sys/Cargo.toml index b8cd2b26c..efb8aabf7 100644 --- a/asio-sys/Cargo.toml +++ b/asio-sys/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "asio-sys" -version = "0.3.0" +version = "0.4.0" authors = ["Tom Gowan "] -description = "Low-level interface and binding generation for the steinberg ASIO SDK." +description = "Low-level interface and binding generation for the Steinberg ASIO SDK." repository = "https://github.com/RustAudio/cpal/" documentation = "https://docs.rs/asio-sys" license = "Apache-2.0" keywords = ["audio", "sound", "asio", "steinberg"] -edition = "2021" -rust-version = "1.82" +edition = "2024" +rust-version = "1.85" [build-dependencies] bindgen = "0.72" diff --git a/asio-sys/build.rs b/asio-sys/build.rs index ee085ee83..9d77f4b6f 100644 --- a/asio-sys/build.rs +++ b/asio-sys/build.rs @@ -411,7 +411,7 @@ fn invoke_vcvars_if_not_set() { // Filters the output of vcvarsall.bat to only include lines of the form "VARNAME=VALUE" let parts: Vec<&str> = line.splitn(2, '=').collect(); if parts.len() == 2 { - env::set_var(parts[0], parts[1]); + unsafe { env::set_var(parts[0], parts[1]) }; println!("{}={}", parts[0], parts[1]); } } diff --git a/asio-sys/src/bindings/asio_import.rs b/asio-sys/src/bindings/asio_import.rs index 96615809b..6787ab7b2 100644 --- a/asio-sys/src/bindings/asio_import.rs +++ b/asio-sys/src/bindings/asio_import.rs @@ -3,5 +3,6 @@ #![allow(non_snake_case)] #![allow(dead_code)] #![allow(clippy::missing_safety_doc)] +#![allow(unsafe_op_in_unsafe_fn)] include!(concat!(env!("OUT_DIR"), "/asio_bindings.rs")); diff --git a/asio-sys/src/bindings/mod.rs b/asio-sys/src/bindings/mod.rs index 36be4e102..914bcbfb0 100644 --- a/asio-sys/src/bindings/mod.rs +++ b/asio-sys/src/bindings/mod.rs @@ -11,8 +11,8 @@ use std::{ os::raw::{c_char, c_double, c_void}, ptr::null_mut, sync::{ - atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, Arc, Mutex, MutexGuard, Weak, + atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}, }, time::Duration, }; diff --git a/examples/android/Cargo.toml b/examples/android/Cargo.toml index de751f606..10055843b 100644 --- a/examples/android/Cargo.toml +++ b/examples/android/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "android" -version = "0.1.0" -edition = "2021" +version = "0.2.0" +edition = "2024" +rust-version = "1.85" [dependencies] cpal = { path = "../../" } diff --git a/examples/audioworklet-beep/Cargo.toml b/examples/audioworklet-beep/Cargo.toml index 8647ef385..e7afd3039 100644 --- a/examples/audioworklet-beep/Cargo.toml +++ b/examples/audioworklet-beep/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "audioworklet-beep" description = "cpal beep example for WebAssembly on an AudioWorklet" -version = "0.1.0" -edition = "2021" +version = "0.2.0" +edition = "2024" +rust-version = "1.85" authors = ["Ian Kettlewell "] [lib] diff --git a/examples/beep.rs b/examples/beep.rs index 22065c8e7..3334c9f5e 100644 --- a/examples/beep.rs +++ b/examples/beep.rs @@ -13,9 +13,9 @@ use clap::Parser; use cpal::{ + Device, Error, ErrorKind, FromSample, HostId, I24, OutputCallbackInfo, Sample, SampleFormat, + SizedSample, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}, - Device, Error, ErrorKind, FromSample, HostId, OutputCallbackInfo, Sample, SampleFormat, - SizedSample, StreamConfig, I24, }; #[derive(Parser, Debug)] diff --git a/examples/custom.rs b/examples/custom.rs index 4b09cf911..ef71c9fea 100644 --- a/examples/custom.rs +++ b/examples/custom.rs @@ -1,18 +1,18 @@ use std::{ fmt, sync::{ - atomic::{AtomicBool, Ordering}, Arc, + atomic::{AtomicBool, Ordering}, }, time::{Duration, Instant}, }; use cpal::{ - traits::{DeviceTrait, HostTrait, StreamTrait}, ChannelCount, Data, Device, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, ErrorKind, FrameCount, FromSample, InputCallbackInfo, OutputCallbackInfo, OutputStreamTimestamp, Sample, SampleFormat, Stream, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; #[allow(dead_code)] diff --git a/examples/feedback.rs b/examples/feedback.rs index e8d5b02cb..62f1c53b2 100644 --- a/examples/feedback.rs +++ b/examples/feedback.rs @@ -8,12 +8,12 @@ use clap::Parser; use cpal::{ - traits::{DeviceTrait, HostTrait, StreamTrait}, Error, ErrorKind, HostId, InputCallbackInfo, OutputCallbackInfo, Sample, StreamConfig, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; use ringbuf::{ - traits::{Consumer, Producer, Split}, HeapRb, + traits::{Consumer, Producer, Split}, }; #[derive(Parser, Debug)] diff --git a/examples/ios-feedback/Cargo.toml b/examples/ios-feedback/Cargo.toml index 3a464f598..15b199593 100644 --- a/examples/ios-feedback/Cargo.toml +++ b/examples/ios-feedback/Cargo.toml @@ -1,8 +1,9 @@ [package] name = "cpal-ios-example" -version = "0.1.0" +version = "0.2.0" authors = ["Michael Hills "] -edition = "2021" +edition = "2024" +rust-version = "1.85" [lib] name = "cpal_ios_example" diff --git a/examples/ios-feedback/src/lib.rs b/examples/ios-feedback/src/lib.rs index 091f8f7c8..5098f2b4e 100644 --- a/examples/ios-feedback/src/lib.rs +++ b/examples/ios-feedback/src/lib.rs @@ -1,6 +1,7 @@ mod feedback; -#[no_mangle] +// Required for Xcode to link this entry point from Objective-C. +#[unsafe(no_mangle)] pub extern "C" fn rust_ios_main() { feedback::run_example().unwrap(); } diff --git a/examples/record_wav.rs b/examples/record_wav.rs index 31436ff0b..b2e4aaedc 100644 --- a/examples/record_wav.rs +++ b/examples/record_wav.rs @@ -10,8 +10,8 @@ use std::{ use clap::Parser; use cpal::{ - traits::{DeviceTrait, HostTrait, StreamTrait}, Error, ErrorKind, FromSample, HostId, Sample, SampleFormat, SupportedStreamConfig, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; #[derive(Parser, Debug)] @@ -155,7 +155,7 @@ fn main() -> Result<(), anyhow::Error> { sample_format => { return Err(anyhow::Error::msg(format!( "Unsupported sample format '{sample_format}'" - ))) + ))); } }; diff --git a/examples/synth_tones.rs b/examples/synth_tones.rs index 99400610a..e326e7dcf 100644 --- a/examples/synth_tones.rs +++ b/examples/synth_tones.rs @@ -7,9 +7,9 @@ extern crate clap; extern crate cpal; use cpal::{ + Device, Error, ErrorKind, FromSample, Host, I24, OutputCallbackInfo, Sample, SampleFormat, + SizedSample, Stream, StreamConfig, SupportedStreamConfig, U24, traits::{DeviceTrait, HostTrait, StreamTrait}, - Device, Error, ErrorKind, FromSample, Host, OutputCallbackInfo, Sample, SampleFormat, - SizedSample, Stream, StreamConfig, SupportedStreamConfig, I24, U24, }; fn main() -> anyhow::Result<()> { diff --git a/examples/wasm-beep/Cargo.toml b/examples/wasm-beep/Cargo.toml index cbbedd7fc..b906f8d76 100644 --- a/examples/wasm-beep/Cargo.toml +++ b/examples/wasm-beep/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "wasm-beep" description = "cpal beep example for WebAssembly" -version = "0.1.0" -edition = "2021" +version = "0.2.0" +edition = "2024" rust-version = "1.85" [lib] diff --git a/rustfmt.toml b/rustfmt.toml index 3a26366d4..f216078d9 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1 @@ -edition = "2021" +edition = "2024" diff --git a/src/host/aaudio/java_interface/audio_features.rs b/src/host/aaudio/java_interface/audio_features.rs index 1c3df3368..05f360584 100644 --- a/src/host/aaudio/java_interface/audio_features.rs +++ b/src/host/aaudio/java_interface/audio_features.rs @@ -1,8 +1,8 @@ use super::{ + PackageManager, utils::{ - get_context, get_package_manager, has_system_feature, with_attached, Env, JObject, JResult, + Env, JObject, JResult, get_context, get_package_manager, has_system_feature, with_attached, }, - PackageManager, }; /** diff --git a/src/host/aaudio/java_interface/audio_manager.rs b/src/host/aaudio/java_interface/audio_manager.rs index 35021fff6..be80af1a6 100644 --- a/src/host/aaudio/java_interface/audio_manager.rs +++ b/src/host/aaudio/java_interface/audio_manager.rs @@ -1,6 +1,6 @@ use super::{ - utils::{get_context, get_system_property, with_attached, Env, JResult}, AudioManager, + utils::{Env, JResult, get_context, get_system_property, with_attached}, }; impl AudioManager { diff --git a/src/host/aaudio/java_interface/devices_info.rs b/src/host/aaudio/java_interface/devices_info.rs index 1d63b518e..5c99f20b4 100644 --- a/src/host/aaudio/java_interface/devices_info.rs +++ b/src/host/aaudio/java_interface/devices_info.rs @@ -1,14 +1,13 @@ use num_traits::FromPrimitive; use super::{ - android_device_flags, + AudioDeviceInfo, AudioDeviceType, Context, android_device_flags, utils::{ - call_method_no_args_ret_bool, call_method_no_args_ret_char_sequence, + Env, JObject, JResult, call_method_no_args_ret_bool, call_method_no_args_ret_char_sequence, call_method_no_args_ret_int, call_method_no_args_ret_int_array, call_method_no_args_ret_string, get_context, get_devices, get_system_service, - with_attached, Env, JObject, JResult, + with_attached, }, - AudioDeviceInfo, AudioDeviceType, Context, }; use crate::{DeviceDirection, SampleFormat}; diff --git a/src/host/aaudio/java_interface/utils.rs b/src/host/aaudio/java_interface/utils.rs index 57a8c3fa9..5d4ff8ac8 100644 --- a/src/host/aaudio/java_interface/utils.rs +++ b/src/host/aaudio/java_interface/utils.rs @@ -1,9 +1,9 @@ use jni::sys::jobject; pub use jni::{ + Env, JavaVM, errors::Result as JResult, objects::{JIntArray, JObject, JObjectArray, JString}, strings::JNIString, - Env, JavaVM, }; use ndk_context::AndroidContext; diff --git a/src/host/aaudio/mod.rs b/src/host/aaudio/mod.rs index 28c78f670..911af96aa 100644 --- a/src/host/aaudio/mod.rs +++ b/src/host/aaudio/mod.rs @@ -6,21 +6,21 @@ use std::{ fmt, hash::{Hash, Hasher}, sync::{ - atomic::{AtomicI32, Ordering}, Arc, Mutex, + atomic::{AtomicI32, Ordering}, }, time::Duration, vec::IntoIter as VecIntoIter, }; use crate::{ - host::{emit_error, ErrorCallbackArc}, - traits::{DeviceTrait, HostTrait, StreamTrait}, BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, InterfaceType, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + host::{ErrorCallbackArc, emit_error}, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; extern crate ndk; @@ -378,7 +378,7 @@ where // SAFETY: Stream implements Send + Sync (see unsafe impl below). Arc> // is safe because the Mutex provides exclusive access and AudioStream's thread safety // is documented in the AAudio C API. - #[allow(clippy::arc_with_non_send_sync)] + #[expect(clippy::arc_with_non_send_sync)] Ok(Stream { inner: Arc::new(Mutex::new(stream)), direction: DeviceDirection::Input, @@ -535,7 +535,7 @@ where // SAFETY: Stream implements Send + Sync (see unsafe impl below). Arc> // is safe because the Mutex provides exclusive access and AudioStream's thread safety // is documented in the AAudio C API. - #[allow(clippy::arc_with_non_send_sync)] + #[expect(clippy::arc_with_non_send_sync)] Ok(Stream { inner: Arc::new(Mutex::new(stream)), direction: DeviceDirection::Output, @@ -665,7 +665,7 @@ impl DeviceTrait for Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, format!("Sample format {sample_format} is not supported"), - )) + )); } }; let builder = ndk::audio::AudioStreamBuilder::new()? @@ -709,7 +709,7 @@ impl DeviceTrait for Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, format!("Sample format {sample_format} is not supported"), - )) + )); } }; let builder = ndk::audio::AudioStreamBuilder::new()? diff --git a/src/host/alsa/enumerate.rs b/src/host/alsa/enumerate.rs index d9bd648e0..144a04b54 100644 --- a/src/host/alsa/enumerate.rs +++ b/src/host/alsa/enumerate.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use super::{alsa, Device, Host}; +use super::{Device, Host, alsa}; use crate::{DeviceDirection, Error}; const HW_PREFIX: &str = "hw"; diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 6ab2989d2..c76ea83c2 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -10,8 +10,8 @@ extern crate libc; use std::{ fmt, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, + atomic::{AtomicBool, Ordering}, }, thread::{self, JoinHandle}, time::Duration, @@ -21,18 +21,18 @@ use std::{ use self::alsa::poll::Descriptors; pub use self::enumerate::Devices; use crate::{ + BufferSize, COMMON_SAMPLE_RATES, ChannelCount, Data, DeviceDescription, + DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, + InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, + SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, + SupportedStreamConfig, SupportedStreamConfigRange, host::{ - equilibrium::{fill_equilibrium, DSD_EQUILIBRIUM_BYTE, U8_EQUILIBRIUM_BYTE}, + equilibrium::{DSD_EQUILIBRIUM_BYTE, U8_EQUILIBRIUM_BYTE, fill_equilibrium}, frames_to_duration, latch::Latch, }, iter::{SupportedInputConfigs, SupportedOutputConfigs}, traits::{DeviceTrait, HostTrait, StreamTrait}, - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, - StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, - COMMON_SAMPLE_RATES, }; mod enumerate; @@ -1238,7 +1238,7 @@ fn process_output( // Adapted from `timestamp2ns` here: // https://fossies.org/linux/alsa-lib/test/audio_time.c #[inline] -#[allow(clippy::unnecessary_cast)] +#[expect(clippy::unnecessary_cast)] fn timespec_to_nanos(ts: libc::timespec) -> i64 { ts.tv_sec as i64 * 1_000_000_000 + ts.tv_nsec as i64 } @@ -1542,7 +1542,7 @@ fn sample_format_to_alsa_format( return Err(Error::with_message( ErrorKind::UnsupportedConfig, format!("Sample format {sample_format} is not supported"), - )) + )); } }; @@ -1583,7 +1583,9 @@ fn set_hw_params_from_format( if !(min_period..=max_period).contains(&period_size) { return Err(Error::with_message( ErrorKind::UnsupportedConfig, - format!("Buffer size {period_size} is not in the supported range {min_period}..={max_period}"), + format!( + "Buffer size {period_size} is not in the supported range {min_period}..={max_period}" + ), )); } } @@ -1594,7 +1596,9 @@ fn set_hw_params_from_format( let effective_max = max_buffer / DEFAULT_PERIODS; return Err(Error::with_message( ErrorKind::UnsupportedConfig, - format!("Buffer size {period_size} exceeds the maximum supported value of {effective_max}"), + format!( + "Buffer size {period_size} exceeds the maximum supported value of {effective_max}" + ), )); } } diff --git a/src/host/asio/device.rs b/src/host/asio/device.rs index 713192cd0..444b39a86 100644 --- a/src/host/asio/device.rs +++ b/src/host/asio/device.rs @@ -1,15 +1,15 @@ use std::{ fmt, hash::{Hash, Hasher}, - sync::{atomic::AtomicU32, Arc, Mutex}, + sync::{Arc, Mutex, atomic::AtomicU32}, }; use super::sys; pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; use crate::{ - host::com, ChannelCount, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, - ErrorKind, FrameCount, SampleFormat, SampleRate, SupportedBufferSize, SupportedStreamConfig, - SupportedStreamConfigRange, + ChannelCount, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, ErrorKind, + FrameCount, SampleFormat, SampleRate, SupportedBufferSize, SupportedStreamConfig, + SupportedStreamConfigRange, host::com, }; /// A ASIO Device diff --git a/src/host/asio/mod.rs b/src/host/asio/mod.rs index c529ecee3..1f64e9655 100644 --- a/src/host/asio/mod.rs +++ b/src/host/asio/mod.rs @@ -15,10 +15,10 @@ pub use self::{ stream::Stream, }; use crate::{ - host::com, - traits::{DeviceTrait, HostTrait, StreamTrait}, Data, DeviceDescription, DeviceId, Error, FrameCount, InputCallbackInfo, OutputCallbackInfo, SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, + host::com, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; mod device; diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index 5e2900f5f..5530be4dc 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -3,8 +3,9 @@ extern crate num_traits; use std::{ sync::{ - atomic::{AtomicU32, AtomicU64, AtomicU8, Ordering}, - mpsc, Arc, Mutex, + Arc, Mutex, + atomic::{AtomicU8, AtomicU32, AtomicU64, Ordering}, + mpsc, }, time::Duration, }; @@ -12,14 +13,14 @@ use std::{ use self::num_traits::{FromPrimitive, PrimInt}; use super::Device; use crate::{ + BufferSize, Data, Error, ErrorKind, FrameCount, I24, InputCallbackInfo, InputStreamTimestamp, + OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, + StreamInstant, host::{ com, error_emit::{emit_error, try_emit_error}, frames_to_duration, }, - BufferSize, Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, - StreamInstant, I24, }; /// Shared state for extending the 32-bit `timeGetTime()` millisecond counter into a @@ -245,7 +246,7 @@ impl Device { /// 1. Write from the ASIO buffer to the interleaved CPAL buffer. /// 2. Deliver the CPAL buffer to the user callback. - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] unsafe fn process_input_callback( data_callback: &mut D, interleaved: &mut [u8], @@ -262,27 +263,29 @@ impl Device { F: Fn(A) -> A, { // 1. Write the ASIO channels to the CPAL buffer. - let interleaved: &mut [A] = cast_slice_mut(interleaved); + let interleaved: &mut [A] = unsafe { cast_slice_mut(interleaved) }; let n_frames = asio_stream.buffer_size as usize; let n_channels = interleaved.len() / n_frames; let buffer_index = asio_info.buffer_index as usize; for ch_ix in 0..n_channels { let asio_channel = - asio_channel_slice::(asio_stream, buffer_index, ch_ix, None); + unsafe { asio_channel_slice::(asio_stream, buffer_index, ch_ix, None) }; for (frame, s_asio) in interleaved.chunks_mut(n_channels).zip(asio_channel) { frame[ch_ix] = from_endianness(*s_asio); } } // 2. Deliver the interleaved buffer to the callback. - apply_input_callback_to_data::( - data_callback, - interleaved, - callback_instant, - sample_rate, - format, - hardware_latency_frames, - ); + unsafe { + apply_input_callback_to_data::( + data_callback, + interleaved, + callback_instant, + sample_rate, + format, + hardware_latency_frames, + ); + } } match (&stream_type, sample_format) { @@ -590,7 +593,7 @@ impl Device { /// 2. If required, silence the ASIO buffer. /// 3. Finally, write the interleaved data to the non-interleaved ASIO buffer, /// performing endianness conversions as necessary. - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] unsafe fn process_output_callback( data_callback: &mut D, interleaved: &mut [u8], @@ -607,24 +610,27 @@ impl Device { D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, F: Fn(A, A) -> A, { - let interleaved: &mut [A] = cast_slice_mut(interleaved); - apply_output_callback_to_data::( - data_callback, - interleaved, - callback_instant, - sample_rate, - format, - hardware_latency_frames, - ); + let interleaved: &mut [A] = unsafe { cast_slice_mut(interleaved) }; + unsafe { + apply_output_callback_to_data::( + data_callback, + interleaved, + callback_instant, + sample_rate, + format, + hardware_latency_frames, + ); + } let n_channels = interleaved.len() / asio_stream.buffer_size as usize; let buffer_index = asio_info.buffer_index as usize; // Write interleaved samples to ASIO channels, one channel at a time. for ch_ix in 0..n_channels { - let asio_channel = - asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix, None); + let asio_channel = unsafe { + asio_channel_slice_mut::(asio_stream, buffer_index, ch_ix, None) + }; if silence_asio_buffer { - asio_channel.align_to_mut::().1.fill(0); + unsafe { asio_channel.align_to_mut::() }.1.fill(0); } for (frame, s_asio) in interleaved.chunks(n_channels).zip(asio_channel) { *s_asio = mix_samples(*s_asio, frame[ch_ix]); @@ -1155,7 +1161,7 @@ fn check_config( return Err(Error::with_message( ErrorKind::UnsupportedConfig, format!("Sample format {sample_format} is not supported"), - )) + )); } } if channels > num_asio_channels { @@ -1173,7 +1179,9 @@ fn check_config( #[inline] unsafe fn cast_slice_mut(v: &mut [u8]) -> &mut [T] { debug_assert!(v.len() % std::mem::size_of::() == 0); - std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) + unsafe { + std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut T, v.len() / std::mem::size_of::()) + } } /// Helper function to convert from little endianness. @@ -1202,7 +1210,7 @@ unsafe fn asio_channel_slice( let channel_length = requested_channel_length.unwrap_or(asio_stream.buffer_size as usize); let buff_ptr: *const T = asio_stream.buffer_infos[channel_index].buffers[buffer_index] as *const _; - std::slice::from_raw_parts(buff_ptr, channel_length) + unsafe { std::slice::from_raw_parts(buff_ptr, channel_length) } } /// Shorthand for retrieving the asio buffer slice associated with a channel. @@ -1218,7 +1226,7 @@ unsafe fn asio_channel_slice_mut( ) -> &mut [T] { let channel_length = requested_channel_length.unwrap_or(asio_stream.buffer_size as usize); let buff_ptr: *mut T = asio_stream.buffer_infos[channel_index].buffers[buffer_index] as *mut _; - std::slice::from_raw_parts_mut(buff_ptr, channel_length) + unsafe { std::slice::from_raw_parts_mut(buff_ptr, channel_length) } } fn load_driver_err(e: sys::LoadDriverError) -> Error { @@ -1261,7 +1269,7 @@ fn i24_bytes_to_i32(i24_bytes: &[u8; 3], little_endian: bool) -> i32 { } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] unsafe fn process_output_callback_i24( data_callback: &mut D, interleaved: &mut [u8], @@ -1276,15 +1284,17 @@ unsafe fn process_output_callback_i24( D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, { let format = SampleFormat::I24; - let interleaved: &mut [I24] = cast_slice_mut(interleaved); - apply_output_callback_to_data::( - data_callback, - interleaved, - callback_instant, - sample_rate, - format, - hardware_latency_frames, - ); + let interleaved: &mut [I24] = unsafe { cast_slice_mut(interleaved) }; + unsafe { + apply_output_callback_to_data::( + data_callback, + interleaved, + callback_instant, + sample_rate, + format, + hardware_latency_frames, + ); + } // Size of samples in the ASIO buffer (has to be 3 in this case) let asio_sample_size_bytes = 3; @@ -1294,15 +1304,17 @@ unsafe fn process_output_callback_i24( // Write interleaved samples to ASIO channels, one channel at a time. for ch_ix in 0..n_channels { // Take channel as u8 array ([u8; 3] packets to represent i24) - let asio_channel = asio_channel_slice_mut( - asio_stream, - buffer_index, - ch_ix, - Some(asio_stream.buffer_size as usize * asio_sample_size_bytes), - ); + let asio_channel = unsafe { + asio_channel_slice_mut( + asio_stream, + buffer_index, + ch_ix, + Some(asio_stream.buffer_size as usize * asio_sample_size_bytes), + ) + }; if silence_asio_buffer { - asio_channel.align_to_mut::().1.fill(0); + unsafe { asio_channel.align_to_mut::() }.1.fill(0); } // Fill in every channel from the interleaved vector @@ -1334,7 +1346,7 @@ unsafe fn process_output_callback_i24( } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] unsafe fn process_input_callback_i24( data_callback: &mut D, interleaved: &mut [u8], @@ -1350,19 +1362,21 @@ unsafe fn process_input_callback_i24( let format = SampleFormat::I24; // 1. Write the ASIO channels to the CPAL buffer. - let interleaved: &mut [I24] = cast_slice_mut(interleaved); + let interleaved: &mut [I24] = unsafe { cast_slice_mut(interleaved) }; let n_frames = asio_stream.buffer_size as usize; let n_channels = interleaved.len() / n_frames; let buffer_index = asio_info.buffer_index as usize; let asio_sample_size_bytes = 3; for ch_ix in 0..n_channels { - let asio_channel = asio_channel_slice::( - asio_stream, - buffer_index, - ch_ix, - Some(n_frames * asio_sample_size_bytes), - ); + let asio_channel = unsafe { + asio_channel_slice::( + asio_stream, + buffer_index, + ch_ix, + Some(n_frames * asio_sample_size_bytes), + ) + }; for (channel_sample, sample_in_buffer) in asio_channel .chunks(asio_sample_size_bytes) .zip(interleaved.iter_mut().skip(ch_ix).step_by(n_channels)) @@ -1376,14 +1390,16 @@ unsafe fn process_input_callback_i24( } // 2. Deliver the interleaved buffer to the callback. - apply_input_callback_to_data::( - data_callback, - interleaved, - callback_instant, - sample_rate, - format, - hardware_latency_frames, - ); + unsafe { + apply_input_callback_to_data::( + data_callback, + interleaved, + callback_instant, + sample_rate, + format, + hardware_latency_frames, + ); + } } /// Apply the output callback to the interleaved buffer. @@ -1399,11 +1415,13 @@ unsafe fn apply_output_callback_to_data( A: Copy, D: FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static, { - let mut data = Data::from_parts( - interleaved.as_mut_ptr() as *mut (), - interleaved.len(), - sample_format, - ); + let mut data = unsafe { + Data::from_parts( + interleaved.as_mut_ptr() as *mut (), + interleaved.len(), + sample_format, + ) + }; let delay = frames_to_duration(hardware_latency_frames as FrameCount, sample_rate); let playback = callback_instant + delay; let timestamp = OutputStreamTimestamp { @@ -1427,11 +1445,13 @@ unsafe fn apply_input_callback_to_data( A: Copy, D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, { - let data = Data::from_parts( - interleaved.as_mut_ptr() as *mut (), - interleaved.len(), - format, - ); + let data = unsafe { + Data::from_parts( + interleaved.as_mut_ptr() as *mut (), + interleaved.len(), + format, + ) + }; let delay = frames_to_duration(hardware_latency_frames as FrameCount, sample_rate); let capture = callback_instant .checked_sub(delay) diff --git a/src/host/audioworklet/dependent_module.rs b/src/host/audioworklet/dependent_module.rs index 75db7d14e..601db4b71 100644 --- a/src/host/audioworklet/dependent_module.rs +++ b/src/host/audioworklet/dependent_module.rs @@ -13,14 +13,14 @@ // // See this issue for a further explanation of what this file does: https://github.com/rustwasm/wasm-bindgen/issues/3019 -use js_sys::{wasm_bindgen, Array, JsString}; +use js_sys::{Array, JsString, wasm_bindgen}; use wasm_bindgen::prelude::*; use web_sys::{Blob, BlobPropertyBag, Url}; // This is a not-so-clean approach to get the current bindgen ES module URL // in Rust. This will fail at run time on bindgen targets not using ES modules. #[wasm_bindgen] -extern "C" { +unsafe extern "C" { #[wasm_bindgen] type ImportMeta; diff --git a/src/host/audioworklet/mod.rs b/src/host/audioworklet/mod.rs index 0967c140f..ecc057207 100644 --- a/src/host/audioworklet/mod.rs +++ b/src/host/audioworklet/mod.rs @@ -6,8 +6,8 @@ use std::{ fmt, sync::{ - atomic::{AtomicU64, Ordering}, Arc, + atomic::{AtomicU64, Ordering}, }, time::Duration, }; @@ -18,12 +18,12 @@ use js_sys::wasm_bindgen; use wasm_bindgen::prelude::*; use crate::{ - host::frames_to_duration, - traits::{DeviceTrait, HostTrait, StreamTrait}, BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + host::frames_to_duration, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; mod dependent_module; @@ -402,11 +402,7 @@ impl DeviceTrait for Device { .unwrap_or(0.0); let total_output_latency_secs = { let sum = base_latency_secs + output_latency_secs; - if sum.is_finite() { - sum.max(0.0) - } else { - 0.0 - } + if sum.is_finite() { sum.max(0.0) } else { 0.0 } }; options.set_processor_options(Some(&js_sys::Array::of3( @@ -607,6 +603,6 @@ impl WasmAudioProcessor { /// It must not have already been unpacked or deallocated, and must not be used after this call. /// Using an invalid or already-consumed pointer will result in undefined behavior. pub unsafe fn unpack(val: usize) -> Self { - *Box::from_raw(val as *mut _) + unsafe { *Box::from_raw(val as *mut _) } } } diff --git a/src/host/com.rs b/src/host/com.rs index db76bfc2e..2e9781760 100644 --- a/src/host/com.rs +++ b/src/host/com.rs @@ -4,7 +4,7 @@ use std::{io::Error as IoError, marker::PhantomData}; use windows::Win32::{ Foundation::RPC_E_CHANGED_MODE, - System::Com::{CoInitializeEx, CoTaskMemFree, CoUninitialize, COINIT_APARTMENTTHREADED}, + System::Com::{COINIT_APARTMENTTHREADED, CoInitializeEx, CoTaskMemFree, CoUninitialize}, }; thread_local!(static COM_INITIALIZED: ComInitialized = { diff --git a/src/host/coreaudio/ios/mod.rs b/src/host/coreaudio/ios/mod.rs index 2b560d321..daf57b6fe 100644 --- a/src/host/coreaudio/ios/mod.rs +++ b/src/host/coreaudio/ios/mod.rs @@ -8,25 +8,25 @@ use std::{ }; use coreaudio::audio_unit::{ - render_callback::{self, data}, AudioUnit, Element, Scope, + render_callback::{self, data}, }; use objc2_audio_toolbox::{kAudioOutputUnitProperty_EnableIO, kAudioUnitProperty_StreamFormat}; use objc2_avf_audio::AVAudioSession; use objc2_core_audio_types::AudioBuffer; use self::enumerate::{ - default_input_device, default_output_device, Devices, SupportedInputConfigs, - SupportedOutputConfigs, + Devices, SupportedInputConfigs, SupportedOutputConfigs, default_input_device, + default_output_device, }; use super::{asbd_from_config, host_time_to_stream_instant}; use crate::{ - host::{frames_to_duration, latch::Latch, try_emit_error, ErrorCallbackArc}, - traits::{DeviceTrait, HostTrait, StreamTrait}, BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + host::{ErrorCallbackArc, frames_to_duration, latch::Latch, try_emit_error}, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; pub mod enumerate; @@ -488,14 +488,17 @@ unsafe fn extract_audio_buffer( sample_format: SampleFormat, is_input: bool, ) -> (AudioBuffer, Data) { - let buffer = if is_input { - // Input: access through buffer array - let first_buf_ptr = core::ptr::addr_of!((*args.data.data).mBuffers) as *const AudioBuffer; - core::ptr::read_unaligned(first_buf_ptr) - } else { - // Output: direct access - let buf_ptr = core::ptr::addr_of!((*args.data.data).mBuffers[0]); - core::ptr::read_unaligned(buf_ptr) + let buffer = unsafe { + if is_input { + // Input: access through buffer array + let first_buf_ptr = + core::ptr::addr_of!((*args.data.data).mBuffers) as *const AudioBuffer; + core::ptr::read_unaligned(first_buf_ptr) + } else { + // Output: direct access + let buf_ptr = core::ptr::addr_of!((*args.data.data).mBuffers[0]); + core::ptr::read_unaligned(buf_ptr) + } }; let mut data_ptr = buffer.mData as *mut (); @@ -507,7 +510,7 @@ unsafe fn extract_audio_buffer( len = 0; } - let data = Data::from_parts(data_ptr, len, sample_format); + let data = unsafe { Data::from_parts(data_ptr, len, sample_format) }; (buffer, data) } diff --git a/src/host/coreaudio/ios/session_event_manager.rs b/src/host/coreaudio/ios/session_event_manager.rs index e0b017c49..f465c490a 100644 --- a/src/host/coreaudio/ios/session_event_manager.rs +++ b/src/host/coreaudio/ios/session_event_manager.rs @@ -12,13 +12,13 @@ use objc2_avf_audio::{ use objc2_foundation::{NSNotification, NSNotificationCenter, NSNumber, NSString}; use crate::{ - host::{emit_error, latch::Latch, ErrorCallbackArc}, Error, ErrorKind, + host::{ErrorCallbackArc, emit_error, latch::Latch}, }; unsafe fn route_change_error(notification: &NSNotification) -> Option { let user_info = notification.userInfo()?; - let key = AVAudioSessionRouteChangeReasonKey?; + let key = unsafe { AVAudioSessionRouteChangeReasonKey }?; let dict = unsafe { user_info.cast_unchecked::() }; let value = dict.objectForKey(key)?; let number = value.downcast_ref::()?; diff --git a/src/host/coreaudio/macos/device.rs b/src/host/coreaudio/macos/device.rs index d14d3c9d9..5c635ec62 100644 --- a/src/host/coreaudio/macos/device.rs +++ b/src/host/coreaudio/macos/device.rs @@ -1,37 +1,37 @@ use std::{ fmt, mem::{self, size_of}, - ptr::{null, NonNull}, + ptr::{NonNull, null}, sync::{ - mpsc::{channel, RecvTimeoutError}, Arc, Mutex, + mpsc::{RecvTimeoutError, channel}, }, time::{Duration, Instant}, }; use coreaudio::audio_unit::{ + AudioUnit, Element, SampleFormat as CoreAudioSampleFormat, Scope, StreamFormat, audio_format::LinearPcmFlags, macos_helpers::{ - audio_unit_from_device_id_uninitialized, find_matching_physical_format, get_device_name, - set_device_physical_stream_format, RateListener, + RateListener, audio_unit_from_device_id_uninitialized, find_matching_physical_format, + get_device_name, set_device_physical_stream_format, }, render_callback::{self, data}, - AudioUnit, Element, SampleFormat as CoreAudioSampleFormat, Scope, StreamFormat, }; use objc2_audio_toolbox::{ kAudioOutputUnitProperty_CurrentDevice, kAudioUnitProperty_StreamFormat, }; use objc2_core_audio::{ - kAudioAggregateDeviceClassID, kAudioDevicePropertyAvailableNominalSampleRates, - kAudioDevicePropertyBufferFrameSize, kAudioDevicePropertyBufferFrameSizeRange, - kAudioDevicePropertyDeviceUID, kAudioDevicePropertyLatency, - kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertySafetyOffset, - kAudioDevicePropertyStreamConfiguration, kAudioDevicePropertyStreamFormat, - kAudioObjectPropertyClass, kAudioObjectPropertyElementMain, kAudioObjectPropertyElementMaster, - kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput, - kAudioObjectPropertyScopeOutput, AudioClassID, AudioDeviceID, AudioObjectGetPropertyData, - AudioObjectGetPropertyDataSize, AudioObjectID, AudioObjectPropertyAddress, - AudioObjectPropertyScope, AudioObjectSetPropertyData, + AudioClassID, AudioDeviceID, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, + AudioObjectID, AudioObjectPropertyAddress, AudioObjectPropertyScope, + AudioObjectSetPropertyData, kAudioAggregateDeviceClassID, + kAudioDevicePropertyAvailableNominalSampleRates, kAudioDevicePropertyBufferFrameSize, + kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyDeviceUID, + kAudioDevicePropertyLatency, kAudioDevicePropertyNominalSampleRate, + kAudioDevicePropertySafetyOffset, kAudioDevicePropertyStreamConfiguration, + kAudioDevicePropertyStreamFormat, kAudioObjectPropertyClass, kAudioObjectPropertyElementMain, + kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyScopeInput, kAudioObjectPropertyScopeOutput, }; use objc2_core_audio_types::{ AudioBuffer, AudioBufferList, AudioStreamBasicDescription, AudioValueRange, @@ -40,19 +40,20 @@ use objc2_core_foundation::{CFString, Type}; pub use super::enumerate::{SupportedInputConfigs, SupportedOutputConfigs}; use super::{ - asbd_from_config, check_os_status, host_time_to_stream_instant, DefaultOutputMonitor, - DisconnectManager, Monitor, Stream, + DefaultOutputMonitor, DisconnectManager, Monitor, Stream, asbd_from_config, check_os_status, + host_time_to_stream_instant, }; use crate::{ - host::{ - coreaudio::macos::{loopback::LoopbackDevice, StreamInner}, - frames_to_duration, try_emit_error, ErrorCallbackArc, - }, - traits::DeviceTrait, BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, InterfaceType, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + host::{ + ErrorCallbackArc, + coreaudio::macos::{StreamInner, loopback::LoopbackDevice}, + frames_to_duration, try_emit_error, + }, + traits::DeviceTrait, }; /// Try to find a matching physical stream format on the device and apply it. @@ -466,7 +467,7 @@ impl Device { } // Logic re-used between `supported_input_configs` and `supported_output_configs`. - #[allow(clippy::cast_ptr_alignment)] + #[expect(clippy::cast_ptr_alignment)] fn supported_configs( &self, scope: AudioObjectPropertyScope, @@ -566,7 +567,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedOperation, "Unexpected audio property scope", - )) + )); } } let buffer_size = get_io_buffer_frame_size_range(self.audio_device_id)?; @@ -629,7 +630,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, "Audio format is not linear PCM", - )) + )); } }; let maybe_sample_format = @@ -644,7 +645,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, "Sample format is not supported; supported formats are F32 and I16", - )) + )); } } }; @@ -656,7 +657,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedOperation, "Unexpected audio property scope", - )) + )); } } let buffer_size = get_io_buffer_frame_size_range(self.audio_device_id)?; @@ -689,9 +690,6 @@ impl Device { } impl Device { - #[allow(clippy::cast_ptr_alignment)] - #[allow(clippy::while_immutable_condition)] - #[allow(clippy::float_cmp)] fn build_input_stream_raw( &self, config: StreamConfig, diff --git a/src/host/coreaudio/macos/enumerate.rs b/src/host/coreaudio/macos/enumerate.rs index db08f6850..f1bde25b3 100644 --- a/src/host/coreaudio/macos/enumerate.rs +++ b/src/host/coreaudio/macos/enumerate.rs @@ -1,22 +1,21 @@ use std::{ mem, - ptr::{null, NonNull}, + ptr::{NonNull, null}, vec::IntoIter as VecIntoIter, }; use objc2_core_audio::{ - kAudioHardwareNoError, kAudioHardwarePropertyDefaultInputDevice, + AudioDeviceID, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, + AudioObjectPropertyAddress, kAudioHardwareNoError, kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice, kAudioHardwarePropertyDevices, kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject, - AudioDeviceID, AudioObjectGetPropertyData, AudioObjectGetPropertyDataSize, AudioObjectID, - AudioObjectPropertyAddress, }; -use super::{check_os_status, Device}; -pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; +use super::{Device, check_os_status}; use crate::Error; +pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; -unsafe fn audio_devices() -> Result, Error> { +fn audio_devices() -> Result, Error> { let property_address = AudioObjectPropertyAddress { mSelector: kAudioHardwarePropertyDevices, mScope: kAudioObjectPropertyScopeGlobal, @@ -32,30 +31,34 @@ unsafe fn audio_devices() -> Result, Error> { } let mut data_size = 0u32; - let status = AudioObjectGetPropertyDataSize( - kAudioObjectSystemObject as AudioObjectID, - NonNull::from(&property_address), - 0, - null(), - NonNull::from(&mut data_size), - ); + let status = unsafe { + AudioObjectGetPropertyDataSize( + kAudioObjectSystemObject as AudioObjectID, + NonNull::from(&property_address), + 0, + null(), + NonNull::from(&mut data_size), + ) + }; try_status_or_return!(status); let device_count = data_size / mem::size_of::() as u32; let mut audio_devices = vec![]; audio_devices.reserve_exact(device_count as usize); - let status = AudioObjectGetPropertyData( - kAudioObjectSystemObject as AudioObjectID, - NonNull::from(&property_address), - 0, - null(), - NonNull::from(&mut data_size), - NonNull::new(audio_devices.as_mut_ptr()).unwrap().cast(), - ); + let status = unsafe { + AudioObjectGetPropertyData( + kAudioObjectSystemObject as AudioObjectID, + NonNull::from(&property_address), + 0, + null(), + NonNull::from(&mut data_size), + NonNull::new(audio_devices.as_mut_ptr()).unwrap().cast(), + ) + }; try_status_or_return!(status); - audio_devices.set_len(device_count as usize); + unsafe { audio_devices.set_len(device_count as usize) }; Ok(audio_devices) } @@ -64,7 +67,7 @@ pub struct Devices(VecIntoIter); impl Devices { pub fn new() -> Result { - let devices = unsafe { audio_devices() }?; + let devices = audio_devices()?; Ok(Self(devices.into_iter())) } } diff --git a/src/host/coreaudio/macos/loopback.rs b/src/host/coreaudio/macos/loopback.rs index b38e9a992..9017e56d1 100644 --- a/src/host/coreaudio/macos/loopback.rs +++ b/src/host/coreaudio/macos/loopback.rs @@ -1,7 +1,7 @@ //! Manages loopback recording (recording system audio output) use std::{ - ffi::{c_void, CStr}, + ffi::{CStr, c_void}, mem::MaybeUninit, ptr::NonNull, sync::atomic::{AtomicU32, Ordering}, @@ -9,26 +9,25 @@ use std::{ static AGGREGATE_INSTANCE_COUNTER: AtomicU32 = AtomicU32::new(0); -use objc2::{rc::Retained, AnyThread}; +use objc2::{AnyThread, rc::Retained}; use objc2_core_audio::{ - kAudioAggregateDeviceNameKey, kAudioAggregateDeviceTapAutoStartKey, - kAudioAggregateDeviceTapListKey, kAudioAggregateDeviceUIDKey, kAudioDevicePropertyDeviceUID, - kAudioEndPointDeviceIsPrivateKey, kAudioObjectPropertyElementMain, - kAudioObjectPropertyScopeGlobal, kAudioSubTapDriftCompensationKey, kAudioSubTapUIDKey, AudioHardwareCreateAggregateDevice, AudioHardwareCreateProcessTap, AudioHardwareDestroyAggregateDevice, AudioHardwareDestroyProcessTap, AudioObjectGetPropertyData, AudioObjectID, AudioObjectPropertyAddress, CATapDescription, - CATapMuteBehavior, + CATapMuteBehavior, kAudioAggregateDeviceNameKey, kAudioAggregateDeviceTapAutoStartKey, + kAudioAggregateDeviceTapListKey, kAudioAggregateDeviceUIDKey, kAudioDevicePropertyDeviceUID, + kAudioEndPointDeviceIsPrivateKey, kAudioObjectPropertyElementMain, + kAudioObjectPropertyScopeGlobal, kAudioSubTapDriftCompensationKey, kAudioSubTapUIDKey, }; use objc2_core_foundation::{ + CFArray, CFDictionary, CFMutableDictionary, CFRetained, CFString, CFStringCreateWithCString, kCFAllocatorDefault, kCFTypeArrayCallBacks, kCFTypeDictionaryKeyCallBacks, - kCFTypeDictionaryValueCallBacks, CFArray, CFDictionary, CFMutableDictionary, CFRetained, - CFString, CFStringCreateWithCString, + kCFTypeDictionaryValueCallBacks, }; use objc2_foundation::{NSArray, NSNumber, NSString}; use super::device::Device; -use crate::{host::coreaudio::check_os_status, Error, ErrorKind}; +use crate::{Error, ErrorKind, host::coreaudio::check_os_status}; type CFStringRef = *mut std::os::raw::c_void; impl Device { @@ -218,7 +217,7 @@ pub fn create_audio_aggregate_device_properties( ) .unwrap() }; - let aggregate_dev_properties = unsafe { + unsafe { let dict = CFMutableDictionary::new( kCFAllocatorDefault, 5, @@ -254,7 +253,5 @@ pub fn create_audio_aggregate_device_properties( ); CFRetained::cast_unchecked::(dict) - }; - - aggregate_dev_properties + } } diff --git a/src/host/coreaudio/macos/mod.rs b/src/host/coreaudio/macos/mod.rs index 82b0b0ee1..5ac59176e 100644 --- a/src/host/coreaudio/macos/mod.rs +++ b/src/host/coreaudio/macos/mod.rs @@ -1,21 +1,20 @@ #![allow(deprecated)] -use std::sync::{mpsc, Arc, Mutex, Weak}; +use std::sync::{Arc, Mutex, Weak, mpsc}; use coreaudio::audio_unit::AudioUnit; use objc2_core_audio::{ - kAudioDevicePropertyDeviceIsAlive, kAudioDevicePropertyNominalSampleRate, - kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyElementMain, - kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject, AudioDeviceID, AudioObjectID, - AudioObjectPropertyAddress, + AudioDeviceID, AudioObjectID, AudioObjectPropertyAddress, kAudioDevicePropertyDeviceIsAlive, + kAudioDevicePropertyNominalSampleRate, kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyElementMain, kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject, }; use property_listener::AudioObjectPropertyListener; -pub use self::enumerate::{default_input_device, default_output_device, Devices}; -use super::{asbd_from_config, check_os_status, host_time_to_stream_instant, OSStatus}; +pub use self::enumerate::{Devices, default_input_device, default_output_device}; +use super::{OSStatus, asbd_from_config, check_os_status, host_time_to_stream_instant}; use crate::{ + Error, ErrorKind, FrameCount, ResultExt, StreamInstant, host::{coreaudio::macos::loopback::LoopbackDevice, emit_error, latch::Latch}, traits::{HostTrait, StreamTrait}, - Error, ErrorKind, FrameCount, ResultExt, StreamInstant, }; mod device; @@ -384,9 +383,8 @@ impl StreamTrait for Stream { #[cfg(test)] mod test { use crate::{ - default_host, + InputCallbackInfo, OutputCallbackInfo, Sample, default_host, traits::{DeviceTrait, HostTrait, StreamTrait}, - InputCallbackInfo, OutputCallbackInfo, Sample, }; #[test] diff --git a/src/host/coreaudio/macos/property_listener.rs b/src/host/coreaudio/macos/property_listener.rs index 24f36f597..b5eb5939d 100644 --- a/src/host/coreaudio/macos/property_listener.rs +++ b/src/host/coreaudio/macos/property_listener.rs @@ -84,6 +84,6 @@ unsafe extern "C-unwind" fn property_listener_handler_shim( callback: *mut ::std::os::raw::c_void, ) -> OSStatus { let wrapper = callback as *mut PropertyListenerCallbackWrapper; - (*wrapper).0(); + unsafe { (*wrapper).0() }; 0 } diff --git a/src/host/coreaudio/mod.rs b/src/host/coreaudio/mod.rs index ea548492b..730bc7240 100644 --- a/src/host/coreaudio/mod.rs +++ b/src/host/coreaudio/mod.rs @@ -3,8 +3,8 @@ //! Default backend on macOS, iOS, and tvOS. use objc2_core_audio_types::{ - kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatFlagIsSignedInteger, - kAudioFormatLinearPCM, AudioStreamBasicDescription, + AudioStreamBasicDescription, kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, + kAudioFormatFlagIsSignedInteger, kAudioFormatLinearPCM, }; use crate::{Error, ErrorKind, SampleFormat, StreamConfig, StreamInstant}; @@ -17,13 +17,19 @@ mod ios; mod macos; #[cfg(not(target_os = "macos"))] -#[allow(unused_imports)] +#[expect( + unused_imports, + reason = "re-exported for public API via platform module" +)] pub use self::ios::{ - enumerate::{Devices, SupportedInputConfigs, SupportedOutputConfigs}, Device, Host, Stream, + enumerate::{Devices, SupportedInputConfigs, SupportedOutputConfigs}, }; #[cfg(target_os = "macos")] -#[allow(unused_imports)] +#[expect( + unused_imports, + reason = "re-exported for public API via platform module" +)] pub use self::macos::{Host, Stream}; // Common helper methods used by both macOS and iOS diff --git a/src/host/custom/mod.rs b/src/host/custom/mod.rs index a24601db9..de29d0539 100644 --- a/src/host/custom/mod.rs +++ b/src/host/custom/mod.rs @@ -7,10 +7,10 @@ use core::time::Duration; use std::fmt; use crate::{ - traits::{DeviceTrait, HostTrait, StreamTrait}, Data, DeviceDescription, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, OutputCallbackInfo, SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, SupportedStreamConfigRange, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; /// A host that can be used to write custom [`HostTrait`] implementations. diff --git a/src/host/jack/device.rs b/src/host/jack/device.rs index bdc9db23c..fc64496f9 100644 --- a/src/host/jack/device.rs +++ b/src/host/jack/device.rs @@ -4,13 +4,13 @@ use std::{ time::Duration, }; -use super::{stream::Stream, JACK_SAMPLE_FORMAT}; +use super::{JACK_SAMPLE_FORMAT, stream::Stream}; pub use crate::iter::{SupportedInputConfigs, SupportedOutputConfigs}; use crate::{ - traits::DeviceTrait, BufferSize, ChannelCount, Data, DeviceDescription, - DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, InputCallbackInfo, - OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, - SupportedStreamConfig, SupportedStreamConfigRange, + BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, + DeviceId, Error, ErrorKind, InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, + StreamConfig, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + traits::DeviceTrait, }; const DEFAULT_NUM_CHANNELS: ChannelCount = 2; @@ -47,7 +47,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedOperation, format!("JACK does not support {direction:?} direction"), - )) + )); } }; let max_channels = client @@ -205,7 +205,9 @@ impl DeviceTrait for Device { if sample_format != JACK_SAMPLE_FORMAT { return Err(Error::with_message( ErrorKind::UnsupportedConfig, - format!("Sample format {sample_format} is not supported; required format is {JACK_SAMPLE_FORMAT}"), + format!( + "Sample format {sample_format} is not supported; required format is {JACK_SAMPLE_FORMAT}" + ), )); } @@ -284,7 +286,9 @@ impl DeviceTrait for Device { if sample_format != JACK_SAMPLE_FORMAT { return Err(Error::with_message( ErrorKind::UnsupportedConfig, - format!("Sample format {sample_format} is not supported; required format is {JACK_SAMPLE_FORMAT}"), + format!( + "Sample format {sample_format} is not supported; required format is {JACK_SAMPLE_FORMAT}" + ), )); } diff --git a/src/host/jack/mod.rs b/src/host/jack/mod.rs index 105e778b5..7360ccbb6 100644 --- a/src/host/jack/mod.rs +++ b/src/host/jack/mod.rs @@ -4,12 +4,15 @@ extern crate jack; -use crate::{traits::HostTrait, Error, ErrorKind, SampleFormat}; +use crate::{Error, ErrorKind, SampleFormat, traits::HostTrait}; mod device; mod stream; -#[allow(unused_imports)] // Re-exported for public API via platform module +#[expect( + unused_imports, + reason = "re-exported for public API via platform module" +)] pub use self::{ device::{Device, SupportedInputConfigs, SupportedOutputConfigs}, stream::Stream, diff --git a/src/host/jack/stream.rs b/src/host/jack/stream.rs index 43579c004..7db1e30f9 100644 --- a/src/host/jack/stream.rs +++ b/src/host/jack/stream.rs @@ -1,14 +1,14 @@ use std::sync::{ - atomic::{AtomicU8, Ordering}, Arc, Mutex, + atomic::{AtomicU8, Ordering}, }; use super::JACK_SAMPLE_FORMAT; use crate::{ - host::{emit_error, frames_to_duration, try_emit_error, ErrorCallbackArc}, - traits::StreamTrait, ChannelCount, Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, Sample, SampleRate, StreamInstant, + host::{ErrorCallbackArc, emit_error, frames_to_duration, try_emit_error}, + traits::StreamTrait, }; #[repr(u8)] @@ -291,7 +291,7 @@ struct LocalProcessHandler { } impl LocalProcessHandler { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] fn new( out_ports: Vec>, in_ports: Vec>, @@ -366,9 +366,9 @@ impl jack::ProcessHandler for LocalProcessHandler { mach_init::mach_thread_self, mach_port::mach_port_deallocate, thread_policy::{ + THREAD_TIME_CONSTRAINT_POLICY, THREAD_TIME_CONSTRAINT_POLICY_COUNT, thread_policy_get, thread_policy_t, - thread_time_constraint_policy_data_t, THREAD_TIME_CONSTRAINT_POLICY, - THREAD_TIME_CONSTRAINT_POLICY_COUNT, + thread_time_constraint_policy_data_t, }, traps::mach_task_self, }; diff --git a/src/host/latch.rs b/src/host/latch.rs index 6d979640d..387aca670 100644 --- a/src/host/latch.rs +++ b/src/host/latch.rs @@ -5,8 +5,8 @@ use std::{ sync::{ - atomic::{AtomicBool, Ordering}, Arc, Weak, + atomic::{AtomicBool, Ordering}, }, thread::Thread, }; diff --git a/src/host/null/mod.rs b/src/host/null/mod.rs index c7a4741b9..c5d540afe 100644 --- a/src/host/null/mod.rs +++ b/src/host/null/mod.rs @@ -6,10 +6,10 @@ use std::fmt; use std::time::Duration; use crate::{ - traits::{DeviceTrait, HostTrait, StreamTrait}, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceId, Error, FrameCount, InputCallbackInfo, OutputCallbackInfo, SampleFormat, StreamConfig, StreamInstant, SupportedStreamConfig, SupportedStreamConfigRange, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; pub struct Devices; diff --git a/src/host/pipewire/device.rs b/src/host/pipewire/device.rs index 01998ce5e..593fae719 100644 --- a/src/host/pipewire/device.rs +++ b/src/host/pipewire/device.rs @@ -4,8 +4,9 @@ use std::{ hash::{Hash, Hasher}, rc::Rc, sync::{ + Arc, atomic::{AtomicBool, AtomicU64, Ordering}, - mpsc, Arc, + mpsc, }, thread, time::Duration, @@ -26,22 +27,22 @@ use pipewire::{ use super::stream::Stream; use crate::{ + BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, + DeviceId, DeviceType, Error, ErrorKind, FrameCount, HostId, InputCallbackInfo, InterfaceType, + OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, + SupportedStreamConfig, SupportedStreamConfigRange, host::{ emit_error, latch::Latch, pipewire::{ stream::{ - DefaultDeviceMonitor, PwInitGuard, StreamCommand, StreamData, SUPPORTED_FORMATS, + DefaultDeviceMonitor, PwInitGuard, SUPPORTED_FORMATS, StreamCommand, StreamData, }, - utils::{audio, clock, default, node, DEVICE_ICON_NAME, METADATA_NAME}, + utils::{DEVICE_ICON_NAME, METADATA_NAME, audio, clock, default, node}, }, }, iter::{SupportedInputConfigs, SupportedOutputConfigs}, traits::DeviceTrait, - BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, - DeviceId, DeviceType, Error, ErrorKind, FrameCount, HostId, InputCallbackInfo, InterfaceType, - OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, - SupportedStreamConfig, SupportedStreamConfigRange, }; pub type Devices = std::vec::IntoIter; @@ -1103,7 +1104,7 @@ fn parse_allow_rates(list: &str) -> Option> { #[cfg(test)] mod test { - use super::{parse_allow_rates, parse_fraction, Class, Device}; + use super::{Class, Device, parse_allow_rates, parse_fraction}; use crate::host::pipewire::utils::default; #[test] diff --git a/src/host/pipewire/mod.rs b/src/host/pipewire/mod.rs index 583948514..404b15189 100644 --- a/src/host/pipewire/mod.rs +++ b/src/host/pipewire/mod.rs @@ -1,12 +1,12 @@ use std::sync::{ - atomic::{AtomicBool, Ordering}, Arc, + atomic::{AtomicBool, Ordering}, }; -use device::{init_devices, Class, Device, Devices}; +use device::{Class, Device, Devices, init_devices}; use stream::PwInitGuard; -use crate::{traits::HostTrait, Error, ErrorKind}; +use crate::{Error, ErrorKind, traits::HostTrait}; mod device; mod stream; diff --git a/src/host/pipewire/stream.rs b/src/host/pipewire/stream.rs index fe7256ab4..b0c331714 100644 --- a/src/host/pipewire/stream.rs +++ b/src/host/pipewire/stream.rs @@ -2,8 +2,8 @@ use std::{ cell::RefCell, rc::Rc, sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, Arc, Mutex, + atomic::{AtomicBool, AtomicU64, Ordering}, }, thread::JoinHandle, time::Instant, @@ -19,11 +19,12 @@ use pipewire::{ registry::{Listener as RegistryListener, RegistryRc}, spa::{ param::{ + ParamType, audio::{AudioFormat, AudioInfoRaw}, format::{MediaSubtype, MediaType}, - format_utils, ParamType, + format_utils, }, - pod::{serialize::PodSerializer, Object, Pod, Value}, + pod::{Object, Pod, Value, serialize::PodSerializer}, utils::{Direction, SpaTypes}, }, stream::{StreamFlags, StreamListener, StreamRc, StreamState, Time}, @@ -31,13 +32,13 @@ use pipewire::{ }; use crate::{ + Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, + OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, StreamConfig, StreamInstant, host::{ - emit_error, equilibrium::fill_equilibrium, frames_to_duration, latch::Latch, - try_emit_error, ErrorCallbackArc, + ErrorCallbackArc, emit_error, equilibrium::fill_equilibrium, frames_to_duration, + latch::Latch, try_emit_error, }, traits::StreamTrait, - Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, - OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, StreamConfig, StreamInstant, }; /// Counts the number of live [`PwInitGuard`] instances across all threads. diff --git a/src/host/pulseaudio/mod.rs b/src/host/pulseaudio/mod.rs index 4b679996f..fa347acca 100644 --- a/src/host/pulseaudio/mod.rs +++ b/src/host/pulseaudio/mod.rs @@ -15,12 +15,12 @@ mod stream; pub use stream::Stream; use crate::{ - error::ResultExt, - traits::{DeviceTrait, HostTrait}, BufferSize, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, HostId, InputCallbackInfo, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + error::ResultExt, + traits::{DeviceTrait, HostTrait}, }; const INIT_TIMEOUT: Duration = Duration::from_secs(2); diff --git a/src/host/pulseaudio/stream.rs b/src/host/pulseaudio/stream.rs index 30f3698ba..421ea541b 100644 --- a/src/host/pulseaudio/stream.rs +++ b/src/host/pulseaudio/stream.rs @@ -1,20 +1,20 @@ use std::{ sync::{ - atomic::{AtomicBool, AtomicU64, Ordering}, Arc, Condvar, Mutex, + atomic::{AtomicBool, AtomicU64, Ordering}, }, time::{Duration, Instant}, }; use futures_executor::block_on; use futures_util::FutureExt as _; -use pulseaudio::{protocol, AsPlaybackSource}; +use pulseaudio::{AsPlaybackSource, protocol}; use crate::{ - host::{emit_error, latch::Latch, ErrorCallbackArc}, - traits::StreamTrait, Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, StreamInstant, + host::{ErrorCallbackArc, emit_error, latch::Latch}, + traits::StreamTrait, }; const LATENCY_MAX_INTERVAL: Duration = Duration::from_millis(100); diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index aabb1d257..7f7d34c27 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,10 +1,10 @@ use crate::{ + BufferSize, COMMON_SAMPLE_RATES, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, + InterfaceType, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, + SupportedStreamConfig, SupportedStreamConfigRange, error::ResultExt, - host::{com::ComString, ErrorCallbackArc}, - BufferSize, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, - DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, InterfaceType, OutputCallbackInfo, - SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, SupportedStreamConfig, - SupportedStreamConfigRange, COMMON_SAMPLE_RATES, + host::{ErrorCallbackArc, com::ComString}, }; impl From for DeviceDirection { @@ -30,19 +30,19 @@ use std::{ }; use windows::{ - core::{Interface, GUID}, Win32::{ Devices::Properties, Foundation::{ERROR_TIMEOUT, PROPERTYKEY}, Media::{Audio, Audio::IAudioRenderClient, KernelStreaming, Multimedia}, System::{ Com, - Com::{StructuredStorage, STGM_READ}, + Com::{STGM_READ, StructuredStorage}, Threading, Variant::{VT_LPWSTR, VT_UI4}, }, UI::Shell::PropertiesSystem::IPropertyStore, }, + core::{GUID, Interface}, }; use super::stream::{AudioClientFlow, DefaultDeviceMonitor, Stream, StreamInner}; @@ -188,9 +188,7 @@ unsafe fn immendpoint_from_immdevice(device: Audio::IMMDevice) -> Audio::IMMEndp } unsafe fn data_flow_from_immendpoint(endpoint: &Audio::IMMEndpoint) -> Audio::EDataFlow { - endpoint - .GetDataFlow() - .expect("could not get endpoint data_flow") + unsafe { endpoint.GetDataFlow() }.expect("could not get endpoint data_flow") } // Given the audio client and format, returns whether the audio engine supports it natively in @@ -200,11 +198,13 @@ pub unsafe fn is_format_supported( waveformatex_ptr: *const Audio::WAVEFORMATEX, ) -> Result { let mut closest_match: *mut Audio::WAVEFORMATEX = ptr::null_mut(); - let hr = client.IsFormatSupported( - Audio::AUDCLNT_SHAREMODE_SHARED, - waveformatex_ptr, - Some(&mut closest_match), - ); + let hr = unsafe { + client.IsFormatSupported( + Audio::AUDCLNT_SHAREMODE_SHARED, + waveformatex_ptr, + Some(&mut closest_match), + ) + }; if !closest_match.is_null() { let _free = WaveFormatExPtr(closest_match); } @@ -222,16 +222,18 @@ unsafe fn format_from_waveformatex_ptr( fn cmp_guid(a: &GUID, b: &GUID) -> bool { (a.data1, a.data2, a.data3, a.data4) == (b.data1, b.data2, b.data3, b.data4) } - let sample_format = match ( - (*waveformatex_ptr).wBitsPerSample, - (*waveformatex_ptr).wFormatTag as u32, - ) { + let sample_format = match unsafe { + ( + (*waveformatex_ptr).wBitsPerSample, + (*waveformatex_ptr).wFormatTag as u32, + ) + } { (8, Audio::WAVE_FORMAT_PCM) => SampleFormat::U8, (16, Audio::WAVE_FORMAT_PCM) => SampleFormat::I16, (32, Multimedia::WAVE_FORMAT_IEEE_FLOAT) => SampleFormat::F32, (n_bits, KernelStreaming::WAVE_FORMAT_EXTENSIBLE) => { let waveformatextensible_ptr = waveformatex_ptr as *const Audio::WAVEFORMATEXTENSIBLE; - let sub = (*waveformatextensible_ptr).SubFormat; + let sub = unsafe { (*waveformatextensible_ptr).SubFormat }; if cmp_guid(&sub, &KernelStreaming::KSDATAFORMAT_SUBTYPE_PCM) { match n_bits { @@ -252,7 +254,7 @@ unsafe fn format_from_waveformatex_ptr( _ => return None, }; - let sample_rate = (*waveformatex_ptr).nSamplesPerSec; + let sample_rate = unsafe { (*waveformatex_ptr).nSamplesPerSec }; // GetBufferSizeLimits is only used for Hardware-Offloaded Audio // Processing, which was added in Windows 8, which places hardware @@ -268,7 +270,7 @@ unsafe fn format_from_waveformatex_ptr( let (mut min_buffer_duration, mut max_buffer_duration) = (0, 0); let buffer_size_is_limited = audio_client .cast::() - .and_then(|audio_client| { + .and_then(|audio_client| unsafe { audio_client.GetBufferSizeLimits( waveformatex_ptr, true, @@ -288,7 +290,7 @@ unsafe fn format_from_waveformatex_ptr( }; let format = SupportedStreamConfig { - channels: (*waveformatex_ptr).nChannels as _, + channels: unsafe { (*waveformatex_ptr).nChannels } as _, sample_rate, buffer_size, sample_format, @@ -388,12 +390,14 @@ unsafe fn activate_audio_interface_sync( let (tx, rx) = std::sync::mpsc::channel(); let handler: Audio::IActivateAudioInterfaceCompletionHandler = CompletionHandler(tx).into(); - Audio::ActivateAudioInterfaceAsync( - device_interface_path, - &Audio::IAudioClient::IID, - None, - &handler, - )?; + unsafe { + Audio::ActivateAudioInterfaceAsync( + device_interface_path, + &Audio::IAudioClient::IID, + None, + &handler, + )?; + } // If a timeout was given use it; otherwise block until Windows calls ActivateCompleted. // `handler` holds the sender and remains live on the stack, so `recv` cannot fail. let result = if let Some(dur) = activation_timeout { @@ -873,7 +877,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, "Stream configuration is not supported in shared mode", - )) + )); } Err(e) => return Err(e), _ => (), @@ -984,7 +988,7 @@ impl Device { return Err(Error::with_message( ErrorKind::UnsupportedConfig, "Stream configuration is not supported in shared mode", - )) + )); } Err(e) => return Err(e), _ => (), @@ -1065,14 +1069,14 @@ impl Device { /// /// Both devices must be valid, live `IMMDevice` COM objects. unsafe fn endpoint_ids_equal(a: &Audio::IMMDevice, b: &Audio::IMMDevice) -> bool { - let id_a = a.GetId().expect("cpal: GetId failure"); - let id_b = b.GetId().expect("cpal: GetId failure"); + let id_a = unsafe { a.GetId() }.expect("cpal: GetId failure"); + let id_b = unsafe { b.GetId() }.expect("cpal: GetId failure"); let _ga = ComString(id_a); let _gb = ComString(id_b); let mut off = 0isize; loop { - let wa = *id_a.0.offset(off); - let wb = *id_b.0.offset(off); + let wa = unsafe { *id_a.0.offset(off) }; + let wb = unsafe { *id_b.0.offset(off) }; if wa != wb { return false; } @@ -1088,11 +1092,11 @@ unsafe fn endpoint_ids_equal(a: &Audio::IMMDevice, b: &Audio::IMMDevice) -> bool /// # Safety /// `device` must be a valid, live `IMMDevice` COM object. unsafe fn hash_endpoint_id(device: &Audio::IMMDevice, state: &mut H) { - let id = device.GetId().expect("cpal: GetId failure"); + let id = unsafe { device.GetId() }.expect("cpal: GetId failure"); let _g = ComString(id); let mut off = 0isize; loop { - let w = *id.0.offset(off); + let w = unsafe { *id.0.offset(off) }; if w == 0 { break; } @@ -1207,18 +1211,18 @@ unsafe fn get_property_u32( property_store: &IPropertyStore, property_key: *const PROPERTYKEY, ) -> Option { - let mut property_value = property_store.GetValue(property_key).ok()?; - let prop_variant = &property_value.Anonymous.Anonymous; + let mut property_value = unsafe { property_store.GetValue(property_key) }.ok()?; + let prop_variant = unsafe { &property_value.Anonymous.Anonymous }; // Check if it's a UI4 (unsigned 32-bit integer) if prop_variant.vt != VT_UI4 { return None; } - let value = *(&prop_variant.Anonymous as *const _ as *const u32); + let value = unsafe { *(&prop_variant.Anonymous as *const _ as *const u32) }; // Clean up the property - StructuredStorage::PropVariantClear(&mut property_value).ok(); + unsafe { StructuredStorage::PropVariantClear(&mut property_value) }.ok(); Some(value) } @@ -1228,19 +1232,19 @@ unsafe fn get_property_string( property_store: &IPropertyStore, property_key: *const PROPERTYKEY, ) -> Option { - let mut property_value = property_store.GetValue(property_key).ok()?; - let prop_variant = &property_value.Anonymous.Anonymous; + let mut property_value = unsafe { property_store.GetValue(property_key) }.ok()?; + let prop_variant = unsafe { &property_value.Anonymous.Anonymous }; // Read the string from the union data field, expecting a *const u16. if prop_variant.vt != VT_LPWSTR { return None; } - let ptr_utf16 = *(&prop_variant.Anonymous as *const _ as *const *const u16); + let ptr_utf16 = unsafe { *(&prop_variant.Anonymous as *const _ as *const *const u16) }; // Find the length of the null-terminated string with a safety limit const MAX_STRING_LEN: usize = 32768; // 32K characters should be more than enough let mut len = 0; - while len < MAX_STRING_LEN && *ptr_utf16.add(len) != 0 { + while len < MAX_STRING_LEN && unsafe { *ptr_utf16.add(len) } != 0 { len += 1; } @@ -1250,7 +1254,7 @@ unsafe fn get_property_string( } // Create the utf16 slice and convert it into a string. - let string_slice = slice::from_raw_parts(ptr_utf16, len); + let string_slice = unsafe { slice::from_raw_parts(ptr_utf16, len) }; let os_string: OsString = OsStringExt::from_wide(string_slice); let result = match os_string.into_string() { Ok(string) => Some(string), @@ -1258,7 +1262,7 @@ unsafe fn get_property_string( }; // Clean up the property. - StructuredStorage::PropVariantClear(&mut property_value).ok(); + unsafe { StructuredStorage::PropVariantClear(&mut property_value) }.ok(); result } @@ -1335,9 +1339,7 @@ pub fn default_output_device() -> Option { /// Get the audio clock used to produce `StreamInstant`s. unsafe fn get_audio_clock(audio_client: &Audio::IAudioClient) -> Result { - audio_client - .GetService::() - .context("Failed to get audio clock") + unsafe { audio_client.GetService::() }.context("Failed to get audio clock") } // Sample rate range supported by the Media Foundation Resampler MFT used by AUTOCONVERTPCM. diff --git a/src/host/wasapi/mod.rs b/src/host/wasapi/mod.rs index 18e5e4f86..999d1ab86 100644 --- a/src/host/wasapi/mod.rs +++ b/src/host/wasapi/mod.rs @@ -6,14 +6,20 @@ use std::io::Error as IoError; use windows::Win32::Media::Audio; -#[allow(unused_imports)] +#[expect( + unused_imports, + reason = "re-exported for public API via platform module" +)] pub use self::device::{ - default_input_device, default_output_device, Device, Devices, SupportedInputConfigs, - SupportedOutputConfigs, + Device, Devices, SupportedInputConfigs, SupportedOutputConfigs, default_input_device, + default_output_device, }; -#[allow(unused_imports)] +#[expect( + unused_imports, + reason = "re-exported for public API via platform module" +)] pub use self::stream::Stream; -use crate::{traits::HostTrait, Error, ErrorKind}; +use crate::{Error, ErrorKind, traits::HostTrait}; mod device; mod stream; diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index 14fb923ed..a711a55e5 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -3,9 +3,9 @@ use std::{ ops::ControlFlow, ptr, sync::{ - atomic::{AtomicBool, Ordering}, - mpsc::{channel, Receiver, SendError, Sender}, Arc, + atomic::{AtomicBool, Ordering}, + mpsc::{Receiver, SendError, Sender, channel}, }, thread::{self, JoinHandle}, time::Duration, @@ -18,14 +18,14 @@ use windows::Win32::{ }; use crate::{ - host::{ - emit_error, equilibrium::fill_equilibrium, frames_to_duration, latch::Latch, - ErrorCallbackArc, - }, - traits::StreamTrait, Data, Error, ErrorKind, FrameCount, InputCallbackInfo, InputStreamTimestamp, OutputCallbackInfo, OutputStreamTimestamp, ResultExt, SampleFormat, SampleRate, StreamConfig, StreamInstant, + host::{ + ErrorCallbackArc, emit_error, equilibrium::fill_equilibrium, frames_to_duration, + latch::Latch, + }, + traits::StreamTrait, }; /// Returns the current default audio endpoint for `flow`, or `None` if none exists. diff --git a/src/host/webaudio/mod.rs b/src/host/webaudio/mod.rs index 4bf90b9df..8ff34a41e 100644 --- a/src/host/webaudio/mod.rs +++ b/src/host/webaudio/mod.rs @@ -10,8 +10,8 @@ use std::{ fmt, ops::DerefMut, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, RwLock, + atomic::{AtomicBool, Ordering}, }, time::Duration, }; @@ -19,16 +19,16 @@ use std::{ type OutputDataCallbackArc = Arc>; use self::{ - wasm_bindgen::{prelude::*, JsCast}, + wasm_bindgen::{JsCast, prelude::*}, web_sys::{AudioContext, AudioContextOptions}, }; use crate::{ - host::ErrorCallbackArc, - traits::{DeviceTrait, HostTrait, StreamTrait}, BufferSize, ChannelCount, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, Error, ErrorKind, FrameCount, InputCallbackInfo, OutputCallbackInfo, OutputStreamTimestamp, SampleFormat, SampleRate, StreamConfig, StreamInstant, SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange, + host::ErrorCallbackArc, + traits::{DeviceTrait, HostTrait, StreamTrait}, }; /// Type alias for shared closure handles used in audio callbacks @@ -404,11 +404,7 @@ impl DeviceTrait for Device { .unwrap_or(0.0); let total_hw_latency_secs = { let sum = base_latency_secs + output_latency_secs; - if sum.is_finite() { - sum.max(0.0) - } else { - 0.0 - } + if sum.is_finite() { sum.max(0.0) } else { 0.0 } }; let callback = StreamInstant::from_secs_f64(now); let playback = StreamInstant::from_secs_f64( @@ -686,7 +682,7 @@ fn buffer_time_step_secs(buffer_size_frames: usize, sample_rate: SampleRate) -> #[cfg(target_feature = "atomics")] #[wasm_bindgen] -extern "C" { +unsafe extern "C" { #[wasm_bindgen(js_name = AudioBuffer)] type ExternalArrayAudioBuffer; diff --git a/src/lib.rs b/src/lib.rs index cf11e08eb..19a87d966 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -176,10 +176,10 @@ pub use device_description::{ }; pub use error::*; pub use platform::{ - available_hosts, default_host, host_from_id, Device, Devices, Host, HostId, Stream, - SupportedInputConfigs, SupportedOutputConfigs, ALL_HOSTS, + ALL_HOSTS, Device, Devices, Host, HostId, Stream, SupportedInputConfigs, + SupportedOutputConfigs, available_hosts, default_host, host_from_id, }; -pub use sample_format::{FromSample, Sample, SampleFormat, SizedSample, I24, U24}; +pub use sample_format::{FromSample, I24, Sample, SampleFormat, SizedSample, U24}; #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] use wasm_bindgen::prelude::*; @@ -400,7 +400,7 @@ impl wasm_bindgen::convert::FromWasmAbi for BufferSize { type Abi = as wasm_bindgen::convert::FromWasmAbi>::Abi; unsafe fn from_abi(js: Self::Abi) -> Self { - match Option::::from_abi(js) { + match unsafe { Option::::from_abi(js) } { None => Self::Default, Some(fc) => Self::Fixed(fc), } @@ -536,7 +536,7 @@ impl SupportedStreamConfig { // Note: Data does not implement `is_empty()` because it always contains a valid audio buffer // by design. The buffer may contain silence, but it is never structurally empty. -#[allow(clippy::len_without_is_empty)] +#[expect(clippy::len_without_is_empty)] impl Data { /// Constructor for host implementations to use. /// @@ -952,7 +952,9 @@ mod tests { // Expected order from lowest to highest priority: assert_eq!( sorted_formats, - vec![DsdU8, DsdU16, DsdU32, U8, I8, U64, I64, U16, I16, U24, I24, U32, I32, F64, F32,] + vec![ + DsdU8, DsdU16, DsdU32, U8, I8, U64, I64, U16, I16, U24, I24, U32, I32, F64, F32, + ] ); } @@ -1045,17 +1047,21 @@ mod tests { #[test] fn validate_stream_config_accepts_valid_configs() { - assert!(validate_stream_config(&StreamConfig { - channels: 2, - sample_rate: 44100, - buffer_size: BufferSize::Default, - }) - .is_ok()); - assert!(validate_stream_config(&StreamConfig { - channels: 1, - sample_rate: 1, - buffer_size: BufferSize::Fixed(1), - }) - .is_ok()); + assert!( + validate_stream_config(&StreamConfig { + channels: 2, + sample_rate: 44100, + buffer_size: BufferSize::Default, + }) + .is_ok() + ); + assert!( + validate_stream_config(&StreamConfig { + channels: 1, + sample_rate: 1, + buffer_size: BufferSize::Fixed(1), + }) + .is_ok() + ); } } diff --git a/src/sample_format.rs b/src/sample_format.rs index 5c8b2e655..5efedc6c4 100644 --- a/src/sample_format.rs +++ b/src/sample_format.rs @@ -243,6 +243,11 @@ impl Display for SampleFormat { /// assert_eq!(i16::FORMAT, cpal::SampleFormat::I16); /// assert_eq!(f32::FORMAT, cpal::SampleFormat::F32); /// ``` +#[diagnostic::on_unimplemented( + message = "`{Self}` is not a supported cpal sample type", + label = "this type cannot be used as a cpal audio sample", + note = "cpal supports: i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, I24, U24" +)] pub trait SizedSample: Sample { /// The corresponding [`SampleFormat`] for this sample type. const FORMAT: SampleFormat;