Skip to content

Commit f9b939e

Browse files
authored
Add mic-detection (#751)
1 parent a48c6b1 commit f9b939e

File tree

4 files changed

+259
-6
lines changed

4 files changed

+259
-6
lines changed

crates/detect/src/lib.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
mod app;
22
mod browser;
3+
mod mic;
34
mod utils;
45

56
pub use app::*;
67
pub use browser::*;
8+
pub use mic::*;
9+
710
use utils::*;
811

912
pub type DetectCallback = std::sync::Arc<dyn Fn(String) + Send + Sync + 'static>;
@@ -22,8 +25,9 @@ trait Observer: Send + Sync {
2225

2326
#[derive(Default)]
2427
pub struct Detector {
25-
app_detector: AppDetector,
26-
browser_detector: BrowserDetector,
28+
// app_detector: AppDetector,
29+
// browser_detector: BrowserDetector,
30+
mic_detector: MicDetector,
2731
}
2832

2933
impl Detector {
@@ -59,13 +63,15 @@ impl Detector {
5963
// }
6064

6165
pub fn start(&mut self, f: DetectCallback) {
62-
self.app_detector.start(f.clone());
63-
self.browser_detector.start(f);
66+
// self.app_detector.start(f.clone());
67+
// self.browser_detector.start(f.clone());
68+
self.mic_detector.start(f);
6469
}
6570

6671
pub fn stop(&mut self) {
67-
self.app_detector.stop();
68-
self.browser_detector.stop();
72+
// self.app_detector.stop();
73+
// self.browser_detector.stop();
74+
self.mic_detector.stop();
6975
}
7076
}
7177

crates/detect/src/mic/macos.rs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
use cidre::{core_audio as ca, os};
2+
3+
use crate::BackgroundTask;
4+
5+
pub struct Detector {
6+
background: BackgroundTask,
7+
}
8+
9+
impl Default for Detector {
10+
fn default() -> Self {
11+
Self {
12+
background: BackgroundTask::default(),
13+
}
14+
}
15+
}
16+
17+
const DEVICE_IS_RUNNING_SOMEWHERE: ca::PropAddr = ca::PropAddr {
18+
selector: ca::PropSelector::DEVICE_IS_RUNNING_SOMEWHERE,
19+
scope: ca::PropScope::GLOBAL,
20+
element: ca::PropElement::MAIN,
21+
};
22+
23+
impl crate::Observer for Detector {
24+
fn start(&mut self, f: crate::DetectCallback) {
25+
self.background.start(|running, mut rx| async move {
26+
let (tx, mut notify_rx) = tokio::sync::mpsc::channel(1);
27+
28+
std::thread::spawn(move || {
29+
let callback = std::sync::Arc::new(std::sync::Mutex::new(f));
30+
let current_device = std::sync::Arc::new(std::sync::Mutex::new(None::<ca::Device>));
31+
32+
let callback_for_device = callback.clone();
33+
let current_device_for_device = current_device.clone();
34+
35+
extern "C-unwind" fn device_listener(
36+
_obj_id: ca::Obj,
37+
number_addresses: u32,
38+
addresses: *const ca::PropAddr,
39+
client_data: *mut (),
40+
) -> os::Status {
41+
let data = unsafe {
42+
&*(client_data
43+
as *const (
44+
std::sync::Arc<std::sync::Mutex<crate::DetectCallback>>,
45+
std::sync::Arc<std::sync::Mutex<Option<ca::Device>>>,
46+
))
47+
};
48+
let callback = &data.0;
49+
50+
let addresses =
51+
unsafe { std::slice::from_raw_parts(addresses, number_addresses as usize) };
52+
53+
for addr in addresses {
54+
if addr.selector == ca::PropSelector::DEVICE_IS_RUNNING_SOMEWHERE {
55+
if let Ok(device) = ca::System::default_input_device() {
56+
if let Ok(is_running) =
57+
device.prop::<u32>(&DEVICE_IS_RUNNING_SOMEWHERE)
58+
{
59+
if is_running != 0 {
60+
if let Ok(guard) = callback.lock() {
61+
(*guard)("microphone_in_use".to_string());
62+
}
63+
}
64+
}
65+
}
66+
}
67+
}
68+
69+
os::Status::NO_ERR
70+
}
71+
72+
extern "C-unwind" fn system_listener(
73+
_obj_id: ca::Obj,
74+
number_addresses: u32,
75+
addresses: *const ca::PropAddr,
76+
client_data: *mut (),
77+
) -> os::Status {
78+
let data = unsafe {
79+
&*(client_data
80+
as *const (
81+
std::sync::Arc<std::sync::Mutex<crate::DetectCallback>>,
82+
std::sync::Arc<std::sync::Mutex<Option<ca::Device>>>,
83+
*mut (),
84+
))
85+
};
86+
let current_device = &data.1;
87+
let device_listener_data = data.2;
88+
89+
let addresses =
90+
unsafe { std::slice::from_raw_parts(addresses, number_addresses as usize) };
91+
92+
for addr in addresses {
93+
if addr.selector == ca::PropSelector::HW_DEFAULT_INPUT_DEVICE {
94+
if let Ok(mut device_guard) = current_device.lock() {
95+
if let Some(old_device) = device_guard.take() {
96+
let _ = old_device.remove_prop_listener(
97+
&DEVICE_IS_RUNNING_SOMEWHERE,
98+
device_listener,
99+
device_listener_data,
100+
);
101+
}
102+
103+
if let Ok(new_device) = ca::System::default_input_device() {
104+
if new_device
105+
.add_prop_listener(
106+
&DEVICE_IS_RUNNING_SOMEWHERE,
107+
device_listener,
108+
device_listener_data,
109+
)
110+
.is_ok()
111+
{
112+
*device_guard = Some(new_device);
113+
}
114+
}
115+
}
116+
}
117+
}
118+
119+
os::Status::NO_ERR
120+
}
121+
122+
let device_listener_data = Box::new((
123+
callback_for_device.clone(),
124+
current_device_for_device.clone(),
125+
));
126+
let device_listener_ptr = Box::into_raw(device_listener_data) as *mut ();
127+
128+
let system_listener_data = Box::new((
129+
callback.clone(),
130+
current_device.clone(),
131+
device_listener_ptr,
132+
));
133+
let system_listener_ptr = Box::into_raw(system_listener_data) as *mut ();
134+
135+
ca::System::OBJ
136+
.add_prop_listener(
137+
&ca::PropSelector::HW_DEFAULT_INPUT_DEVICE.global_addr(),
138+
system_listener,
139+
system_listener_ptr,
140+
)
141+
.unwrap();
142+
143+
if let Ok(device) = ca::System::default_input_device() {
144+
if device
145+
.add_prop_listener(
146+
&DEVICE_IS_RUNNING_SOMEWHERE,
147+
device_listener,
148+
device_listener_ptr,
149+
)
150+
.is_ok()
151+
{
152+
if let Ok(mut device_guard) = current_device.lock() {
153+
*device_guard = Some(device);
154+
}
155+
}
156+
}
157+
158+
let _ = tx.blocking_send(());
159+
160+
ca::System::OBJ
161+
.remove_prop_listener(
162+
&ca::PropSelector::HW_DEFAULT_INPUT_DEVICE.global_addr(),
163+
system_listener,
164+
system_listener_ptr,
165+
)
166+
.unwrap();
167+
168+
if let Ok(device_guard) = current_device.lock() {
169+
if let Some(device) = &*device_guard {
170+
let _ = device.remove_prop_listener(
171+
&DEVICE_IS_RUNNING_SOMEWHERE,
172+
device_listener,
173+
device_listener_ptr,
174+
);
175+
}
176+
}
177+
178+
unsafe {
179+
let _ = Box::from_raw(
180+
system_listener_ptr
181+
as *mut (
182+
std::sync::Arc<std::sync::Mutex<crate::DetectCallback>>,
183+
std::sync::Arc<std::sync::Mutex<Option<ca::Device>>>,
184+
*mut (),
185+
),
186+
);
187+
let _ = Box::from_raw(
188+
device_listener_ptr
189+
as *mut (
190+
std::sync::Arc<std::sync::Mutex<crate::DetectCallback>>,
191+
std::sync::Arc<std::sync::Mutex<Option<ca::Device>>>,
192+
),
193+
);
194+
}
195+
});
196+
197+
let _ = notify_rx.recv().await;
198+
199+
loop {
200+
tokio::select! {
201+
_ = &mut rx => {
202+
break;
203+
}
204+
_ = tokio::time::sleep(tokio::time::Duration::from_millis(500)) => {
205+
if !running.load(std::sync::atomic::Ordering::SeqCst) {
206+
break;
207+
}
208+
}
209+
}
210+
}
211+
});
212+
}
213+
214+
fn stop(&mut self) {
215+
self.background.stop();
216+
}
217+
}

crates/detect/src/mic/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[cfg(target_os = "macos")]
2+
mod macos;
3+
#[cfg(target_os = "macos")]
4+
type PlatformDetector = macos::Detector;
5+
6+
#[cfg(target_os = "windows")]
7+
mod windows;
8+
#[cfg(target_os = "windows")]
9+
type PlatformDetector = windows::Detector;
10+
11+
#[derive(Default)]
12+
pub struct MicDetector {
13+
inner: PlatformDetector,
14+
}
15+
16+
impl crate::Observer for MicDetector {
17+
fn start(&mut self, f: crate::DetectCallback) {
18+
self.inner.start(f);
19+
}
20+
fn stop(&mut self) {
21+
self.inner.stop();
22+
}
23+
}

crates/detect/src/mic/windows.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[derive(Default)]
2+
pub struct Detector {}
3+
4+
impl crate::Observer for Detector {
5+
fn start(&mut self, f: crate::DetectCallback) {}
6+
fn stop(&mut self) {}
7+
}

0 commit comments

Comments
 (0)