Skip to content

Commit

Permalink
New ios 18.2 audio session api
Browse files Browse the repository at this point in the history
  • Loading branch information
yury committed Oct 28, 2024
1 parent 6dbe11c commit bcb668d
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 4 deletions.
2 changes: 2 additions & 0 deletions cidre/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ ios_16_0 = ["ios_15_0"]
ios_16_4 = ["ios_16_0"]
ios_17_0 = ["ios_16_4"]
ios_18_0 = ["ios_17_0"]
ios_18_2 = ["ios_18_0"]

tvos_7_0 = []
tvos_8_0 = ["tvos_7_0"]
Expand Down Expand Up @@ -206,6 +207,7 @@ visionos_1_0 = []
visionos_1_1 = ["visionos_1_0"]
visionos_1_2 = ["visionos_1_1"]
visionos_2_0 = ["visionos_1_2"]
visionos_2_2 = ["visionos_2_0"]

# end of deployment targets

Expand Down
2 changes: 2 additions & 0 deletions cidre/src/av/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ pub use session::InterruptionType as SessionInterruptionType;
#[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub use session::IoType as SessionIoType;
#[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub use session::MicInjectionMode as SessionMicInjectionMode;
#[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub use session::Mode as SessionMode;
#[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub use session::Port as SessionPort;
Expand Down
62 changes: 60 additions & 2 deletions cidre/src/av/audio/application.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,40 @@
use crate::{av, blocks, define_cls, define_obj_type, ns, objc};
use crate::{arc, av, blocks, define_cls, define_obj_type, ns, objc};

#[doc(alias = "AVAudioApplicationRecordPermission")]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[repr(isize)]
pub enum RecordPermission {
/// The user has not yet been asked for permission.
#[doc(alias = "AVAudioApplicationRecordPermissionUndetermined")]
Undetermined = i32::from_be_bytes(*b"undt") as _,

/// The user has been asked and has denied permission.
#[doc(alias = "AVAudioApplicationRecordPermissionDenied")]
Denied = i32::from_be_bytes(*b"deny") as _,

/// The user has been asked and has granted permission.
#[doc(alias = "AVAudioApplicationRecordPermissionGranted")]
Granted = i32::from_be_bytes(*b"grnt") as _,
}

#[doc(alias = "AVAudioApplicationMicrophoneInjectionPermission")]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[repr(isize)]
pub enum MicInjectionPermission {
/// The user has disabled this service for all apps.
#[doc(alias = "AVAudioApplicationMicrophoneInjectionPermissionServiceDisabled")]
ServiceDisabled = i32::from_be_bytes(*b"srds") as _,

/// The user has not yet been asked for permission.
#[doc(alias = "AVAudioApplicationMicrophoneInjectionPermissionUndetermined")]
Undetermined = i32::from_be_bytes(*b"undt") as _,

/// The user has been asked and has denied permission.
#[doc(alias = "AVAudioApplicationMicrophoneInjectionPermissionDenied")]
Denied = i32::from_be_bytes(*b"deny") as _,

/// The user has been asked and has granted permission.
#[doc(alias = "AVAudioApplicationMicrophoneInjectionPermissionGranted")]
Granted = i32::from_be_bytes(*b"grnt") as _,
}

Expand Down Expand Up @@ -35,7 +65,7 @@ impl App {
}

#[objc::msg_send(sharedInstance)]
pub fn shared() -> &'static mut Self;
pub fn shared() -> arc::R<Self>;

#[objc::msg_send(isInputMuted)]
pub fn is_input_mutted(&self) -> bool;
Expand Down Expand Up @@ -76,6 +106,34 @@ impl App {
Self::request_record_permission_ch_block(&mut block);
future.await
}

/// Returns an enum indicating whether the user has granted or denied permission to inject audio into input,
/// or has not been asked
#[objc::msg_send(microphoneInjectionPermission)]
#[objc::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn mic_injection_permission(&self) -> MicInjectionPermission;

#[objc::msg_send(requestMicrophoneInjectionPermissionWithCompletionHandler:)]
#[objc::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn request_mic_injection_permission_ch_block(
handler: &mut blocks::SendBlock<fn(MicInjectionPermission)>,
);

#[objc::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn request_mic_injection_permission_ch(
handler: impl FnMut(MicInjectionPermission) + 'static + std::marker::Send,
) {
let mut handler = blocks::SendBlock::new1(handler);
Self::request_mic_injection_permission_ch_block(&mut handler)
}

#[cfg(feature = "async")]
#[objc::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub async fn request_mic_injection_permission() -> MicInjectionPermission {
let (future, mut block) = blocks::comp1();
Self::request_mic_injection_permission_ch_block(&mut block);
future.await
}
}

#[link(name = "AVFAudio", kind = "framework")]
Expand Down
42 changes: 41 additions & 1 deletion cidre/src/av/audio/session.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::api;
use crate::{arc, define_cls, define_obj_type, ns, objc};

mod types;
Expand All @@ -8,6 +9,7 @@ pub use types::InterruptionOpts;
pub use types::InterruptionReason;
pub use types::InterruptionType;
pub use types::IoType;
pub use types::MicInjectionMode;
pub use types::Mode;
pub use types::Port;
pub use types::PortOverride;
Expand Down Expand Up @@ -438,8 +440,33 @@ impl Session {
pub fn prefers_interruption_on_route_disconnect(&self) -> bool;
}

/// MicrophoneInjection
impl Session {
#[objc::msg_send(setPreferredMicrophoneInjectionMode:error:)]
#[api::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub unsafe fn set_preferred_mic_injection_mode_err<'ear>(
&mut self,
val: MicInjectionMode,
err: *mut Option<&'ear ns::Error>,
) -> bool;

/// Set the preferred form of audio injection into another app's input stream
#[objc::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn set_preferred_mic_injection_mode<'ear>(&mut self, val: MicInjectionMode) -> ns::Result {
ns::if_false(|err| unsafe { self.set_preferred_mic_injection_mode_err(val, err) })
}

#[objc::msg_send(preferredMicrophoneInjectionMode)]
#[api::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn preferred_mic_injection_mode(&self) -> MicInjectionMode;

#[objc::msg_send(isMicrophoneInjectionAvailable)]
#[api::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
pub fn is_mic_injection_available(&self) -> bool;
}

#[cfg(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))]
#[link(name = "AVFAudio", kind = "framework")]
#[link(name = "av", kind = "static")]
extern "C" {
static AV_AUDIO_SESSION: &'static objc::Class<Session>;
}
Expand Down Expand Up @@ -492,8 +519,17 @@ impl Session {
pub fn rendering_capabilities_change_notification() -> &'static ns::NotificationName {
unsafe { AVAudioSessionRenderingCapabilitiesChangeNotification }
}

#[doc(alias = "AVAudioSessionMicrophoneInjectionCapabilitiesChangeNotification")]
#[api::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
#[inline]
pub fn mic_injection_capabilities_change_notification() -> &'static ns::NotificationName {
unsafe { AVAudioSessionMicrophoneInjectionCapabilitiesChangeNotification }
}
}

#[link(name = "AVAudio", kind = "framework")]
#[api::weak]
extern "C" {
static AVAudioSessionInterruptionNotification: &'static ns::NotificationName;
static AVAudioSessionRouteChangeNotification: &'static ns::NotificationName;
Expand All @@ -504,4 +540,8 @@ extern "C" {
&'static ns::NotificationName;
static AVAudioSessionRenderingModeChangeNotification: &'static ns::NotificationName;
static AVAudioSessionRenderingCapabilitiesChangeNotification: &'static ns::NotificationName;

#[api::available(ios = 18.2, maccatalyst = 18.2, visionos = 2.2)]
static AVAudioSessionMicrophoneInjectionCapabilitiesChangeNotification:
&'static ns::NotificationName;
}
19 changes: 19 additions & 0 deletions cidre/src/av/audio/session/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -917,3 +917,22 @@ pub enum RenderingMode {
#[doc(alias = "AVAudioSessionRenderingModeDolbyAtmos")]
DolbyAtmos = 5,
}

/// Various modes to inject audio coming from a session to another app’s input stream
///
/// Applications can state their intent to mix locally generated audio, which should consist primarily of
/// synthesized speech, to another app's input stream. This feature is intended to be used by accessibility apps
/// implementing augmentative and alternative communication systems that enable users with disabilities to
/// communicate with synthesized speech. When input is muted, microphone injection will also be muted.
#[doc(alias = "AVAudioSessionMicrophoneInjectionMode")]
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[repr(isize)]
pub enum MicInjectionMode {
/// Default state, microphone injection is not preferred
#[doc(alias = "AVAudioSessionMicrophoneInjectionModeNone")]
None = 0,

/// Inject Spoken Audio, like synthesized speech, with microphone audio
#[doc(alias = "AVAudioSessionMicrophoneInjectionModeSpokenAudio")]
SpokenAudio = 1,
}
2 changes: 1 addition & 1 deletion cidre/src/mtl/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ impl Lib {
#[objc::msg_send(type)]
pub fn type_(&self) -> mtl::LibType;

/// The installName provided when this mtl::Lib was created.
/// The install_name provided when this mtl::Lib was created.
///
/// Always nil if the type of the library is not mtl::LibType::Dynamic.
/// [read more](https://developer.apple.com/documentation/metal/mtllibrary/3554039-installname?language=objc)
Expand Down

0 comments on commit bcb668d

Please sign in to comment.