nandi/jolt-nativepublic Fork 0
6391496e8bf6bb0aa9007b9b18d01c75da07306e
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

mod.rs · 134 lines · 4.7 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 20d ago1//! CoreAudio backend implementation.
2//!
3//! Default backend on macOS and iOS.
4
5use objc2_core_audio_types::{
6 kAudioFormatFlagIsFloat, kAudioFormatFlagIsPacked, kAudioFormatFlagIsSignedInteger,
7 kAudioFormatLinearPCM, AudioStreamBasicDescription,
8};
9
10use crate::DefaultStreamConfigError;
11use crate::{BuildStreamError, SupportedStreamConfigsError};
12
13use crate::{BackendSpecificError, SampleFormat, StreamConfig};
14
15#[cfg(target_os = "ios")]
16mod ios;
17#[cfg(target_os = "macos")]
18mod macos;
19
20#[cfg(target_os = "ios")]
21#[allow(unused_imports)]
22pub use self::ios::{
23 enumerate::{Devices, SupportedInputConfigs, SupportedOutputConfigs},
24 Device, Host, Stream,
25};
26
27#[cfg(target_os = "macos")]
28pub use self::macos::{Host, Stream};
29
30// Common helper methods used by both macOS and iOS
31
32fn check_os_status(os_status: OSStatus) -> Result<(), BackendSpecificError> {
33 match coreaudio::Error::from_os_status(os_status) {
34 Ok(()) => Ok(()),
35 Err(err) => {
36 let description = err.to_string();
37 Err(BackendSpecificError { description })
38 }
39 }
40}
41
42// Create a coreaudio AudioStreamBasicDescription from a CPAL Format.
43fn asbd_from_config(
44 config: StreamConfig,
45 sample_format: SampleFormat,
46) -> AudioStreamBasicDescription {
47 let n_channels = config.channels as usize;
48 let sample_rate = config.sample_rate;
49 let bytes_per_channel = sample_format.sample_size();
50 let bits_per_channel = bytes_per_channel * 8;
51 let bytes_per_frame = n_channels * bytes_per_channel;
52 let frames_per_packet = 1;
53 let bytes_per_packet = frames_per_packet * bytes_per_frame;
54 let format_flags = match sample_format {
55 SampleFormat::F32 | SampleFormat::F64 => kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked,
56 SampleFormat::I8
57 | SampleFormat::I16
58 | SampleFormat::I24
59 | SampleFormat::I32
60 | SampleFormat::I64 => kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked,
61 _ => kAudioFormatFlagIsPacked,
62 };
63 AudioStreamBasicDescription {
64 mBitsPerChannel: bits_per_channel as _,
65 mBytesPerFrame: bytes_per_frame as _,
66 mChannelsPerFrame: n_channels as _,
67 mBytesPerPacket: bytes_per_packet as _,
68 mFramesPerPacket: frames_per_packet as _,
69 mFormatFlags: format_flags,
70 mFormatID: kAudioFormatLinearPCM,
71 mSampleRate: sample_rate as _,
72 mReserved: 0,
73 }
74}
75
76#[inline]
77fn host_time_to_stream_instant(
78 m_host_time: u64,
79) -> Result<crate::StreamInstant, BackendSpecificError> {
80 let mut info: mach2::mach_time::mach_timebase_info = Default::default();
81 let res = unsafe { mach2::mach_time::mach_timebase_info(&mut info) };
82 check_os_status(res)?;
83 let nanos = m_host_time as u128 * info.numer as u128 / info.denom as u128;
84 crate::StreamInstant::from_nanos_i128(nanos as i128).ok_or(BackendSpecificError {
85 description: "host time out of range of `StreamInstant` representation".to_string(),
86 })
87}
88
89// Convert the given duration in frames at the given sample rate to a `std::time::Duration`.
90#[inline]
91fn frames_to_duration(frames: usize, rate: crate::SampleRate) -> std::time::Duration {
92 let secsf = frames as f64 / rate as f64;
93 let secs = secsf as u64;
94 let nanos = ((secsf - secs as f64) * 1_000_000_000.0) as u32;
95 std::time::Duration::new(secs, nanos)
96}
97
98// TODO need stronger error identification
99impl From<coreaudio::Error> for BuildStreamError {
100 fn from(err: coreaudio::Error) -> BuildStreamError {
101 match err {
102 coreaudio::Error::RenderCallbackBufferFormatDoesNotMatchAudioUnitStreamFormat
103 | coreaudio::Error::NoKnownSubtype
104 | coreaudio::Error::AudioUnit(coreaudio::error::AudioUnitError::FormatNotSupported)
105 | coreaudio::Error::AudioCodec(_)
106 | coreaudio::Error::AudioFormat(_) => BuildStreamError::StreamConfigNotSupported,
107 _ => BuildStreamError::DeviceNotAvailable,
108 }
109 }
110}
111
112impl From<coreaudio::Error> for SupportedStreamConfigsError {
113 fn from(err: coreaudio::Error) -> SupportedStreamConfigsError {
114 let description = format!("{err}");
115 let err = BackendSpecificError { description };
116 // Check for possible DeviceNotAvailable variant
117 SupportedStreamConfigsError::BackendSpecific { err }
118 }
119}
120
121impl From<coreaudio::Error> for DefaultStreamConfigError {
122 fn from(err: coreaudio::Error) -> DefaultStreamConfigError {
123 let description = format!("{err}");
124 let err = BackendSpecificError { description };
125 // Check for possible DeviceNotAvailable variant
126 DefaultStreamConfigError::BackendSpecific { err }
127 }
128}
129
130pub(crate) type OSStatus = i32;
131
132// Compile-time assertion that Stream is Send and Sync
133crate::assert_stream_send!(Stream);
134crate::assert_stream_sync!(Stream);