1use {
2 super::{constants::*, load_class},
3 jni::{
4 JNIEnv,
5 errors::Error as JniError,
6 objects::{GlobalRef, JByteArray, JObject, JString},
7 },
8 std::{
9 collections::HashMap,
10 error::Error,
11 fmt::{Display, Formatter, Result as FmtResult},
12 mem::transmute,
13 sync::{
14 LazyLock, Mutex, PoisonError,
15 atomic::{AtomicI16, Ordering},
16 },
17 },
18 tokio::sync::oneshot::{Sender, channel, error::RecvError},
19 tracing::error,
20};
21
22#[derive(Debug)]
23pub enum MicError {
24 Jni(JniError),
25 Poison(String),
26 Recv(RecvError),
27 Read(String),
28}
29
30impl Display for MicError {
31 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
32 write!(f, "MicError: ")?;
33 match self {
34 Self::Jni(e) => Display::fmt(e, f),
35 Self::Poison(msg) => write!(f, "PoisonError: {}", msg),
36 Self::Recv(e) => Display::fmt(e, f),
37 Self::Read(msg) => write!(f, "ReadError: {}", msg),
38 }
39 }
40}
41
42impl Error for MicError {}
43
44impl From<JniError> for MicError {
45 fn from(value: JniError) -> Self {
46 Self::Jni(value)
47 }
48}
49
50impl From<RecvError> for MicError {
51 fn from(value: RecvError) -> Self {
52 Self::Recv(value)
53 }
54}
55
56impl<T> From<PoisonError<T>> for MicError {
57 fn from(value: PoisonError<T>) -> Self {
58 Self::Poison(value.to_string())
59 }
60}
61
62type MicResult<T> = Result<T, MicError>;
63
64static MIC_READ_CALLBACKS: LazyLock<Mutex<HashMap<i16, Sender<MicResult<Vec<u8>>>>>> =
66 LazyLock::new(|| Mutex::new(Default::default()));
67
68pub struct AudioMicrophone {
70 env: JNIEnv<'static>,
72 this: GlobalRef,
74 sample_rate: SampleRate,
76 channel_count: i32,
78 audio_format: AudioEncoding,
80}
81
82impl AudioMicrophone {
84 pub fn new(
100 mut env: JNIEnv,
101 context: &GlobalRef,
102 sample_rate: SampleRate,
103 channel_config: ChannelInConfig,
104 audio_format: AudioEncoding,
105 ) -> MicResult<Self> {
106 let sample_rate_value = sample_rate.value();
107 let channel_config_value = channel_config.value();
108 let audio_format_value = audio_format.value();
109
110 let mic_class = load_class(&mut env, context, "rust.android.Mic")?;
112
113 let mic_obj = env.new_object(
115 &mic_class,
116 "(III)V",
117 &[
118 sample_rate_value.into(),
119 channel_config_value.into(),
120 audio_format_value.into(),
121 ],
122 )?;
123 let this = env.new_global_ref(mic_obj)?;
124
125 env.call_method(
127 &this,
128 "init",
129 "(IIII)V",
130 &[
131 AudioSource::Mic.value().into(),
132 sample_rate_value.into(),
133 channel_config_value.into(),
134 audio_format_value.into(),
135 ],
136 )?;
137
138 unsafe {
139 Ok(Self {
140 env: transmute(env),
141 this,
142 sample_rate,
143 channel_count: channel_config.channel_count(),
144 audio_format,
145 })
146 }
147 }
148
149 fn get_env(&self) -> JNIEnv<'_> {
150 unsafe { self.env.unsafe_clone() }
151 }
152
153 pub fn start(&self) -> MicResult<()> {
155 self.get_env()
156 .call_method(&self.this, "startRecording", "()V", &[])?;
157
158 Ok(())
159 }
160
161 pub fn stop(&self) -> MicResult<()> {
163 self.get_env().call_method(&self.this, "stop", "()V", &[])?;
164 Ok(())
165 }
166
167 pub fn release(&self) -> MicResult<()> {
169 self.get_env()
170 .call_method(&self.this, "release", "()V", &[])?;
171
172 Ok(())
173 }
174
175 pub async fn read(&self, duration_ms: i32) -> MicResult<Vec<u8>> {
192 static ID: AtomicI16 = AtomicI16::new(0);
193 let task_id = ID.fetch_add(1, Ordering::AcqRel);
194 let (tx, rx) = channel();
195
196 {
197 let mut lock = MIC_READ_CALLBACKS.lock()?;
198 lock.insert(task_id, tx);
199 }
200
201 let bytes_per_sample = self.audio_format.bytes_per_sample();
204 let samples_per_channel = (duration_ms * self.sample_rate.value()) / 1000;
205 let total_samples = samples_per_channel * self.channel_count;
206 let requested_bytes = total_samples * bytes_per_sample;
207
208 let buffer_multiplier = 2;
210 let buffer_bytes = requested_bytes * buffer_multiplier;
211
212 self.get_env()
214 .call_method(
215 &self.this,
216 "performRead",
217 "(SII)V",
218 &[task_id.into(), requested_bytes.into(), buffer_bytes.into()],
219 )?
220 .v()?;
221
222 rx.await?
223 }
224
225 pub fn get_sample_rate(&self) -> i32 {
227 self.sample_rate.value()
228 }
229
230 pub fn get_channel_count(&self) -> i32 {
232 self.channel_count
233 }
234
235 pub fn get_audio_format(&self) -> i32 {
237 self.audio_format.value()
238 }
239
240 pub fn get_buffer_size(&self) -> MicResult<i32> {
242 Ok(self
243 .get_env()
244 .call_method(&self.this, "getBufferSize", "()I", &[])?
245 .i()?)
246 }
247
248 pub fn calculate_bytes_for_duration(&self, duration_ms: i32) -> i32 {
250 let bytes_per_sample = self.audio_format.bytes_per_sample();
251 let samples_per_channel = (duration_ms * self.sample_rate.value()) / 1000;
252 samples_per_channel * self.channel_count * bytes_per_sample
253 }
254}
255
256unsafe impl Send for AudioMicrophone {}
257unsafe impl Sync for AudioMicrophone {}
258
259impl Drop for AudioMicrophone {
260 fn drop(&mut self) {
261 if let Err(e) = self.release() {
262 error!(?e, "Dropping error.");
263 }
264 }
265}
266
267#[allow(non_snake_case)]
278#[unsafe(no_mangle)]
279extern "C" fn Java_rust_android_Mic_onReadSuccess(
280 env: JNIEnv,
281 _this: JObject,
282 task_id: i16,
283 audio_data: JByteArray,
284) {
285 let Ok(mut lock) = MIC_READ_CALLBACKS.lock() else {
286 return;
287 };
288 let Some(tx) = lock.remove(&task_id) else {
289 return;
290 };
291 drop(lock);
292
293 if audio_data.is_null() {
294 let _ = tx.send(Err(MicError::Read("Read succeeded but audio data is empty".into())));
295 return;
296 }
297
298 let mut buf = vec![0; env.get_array_length(&audio_data).unwrap_or(0) as _];
299 let _ = match env.get_byte_array_region(&audio_data, 0, &mut buf) {
300 Ok(()) => tx.send(Ok(buf.into_iter().map(|i| i as _).collect::<Vec<_>>())),
301 Err(e) => tx.send(Err(e.into())),
302 };
303}
304
305#[allow(non_snake_case)]
316#[unsafe(no_mangle)]
317extern "C" fn Java_rust_android_Mic_onReadFailure(
318 mut env: JNIEnv,
319 _this: JObject,
320 task_id: i16,
321 error_message: JString,
322) {
323 let Ok(mut lock) = MIC_READ_CALLBACKS.lock() else {
324 return;
325 };
326 let Some(tx) = lock.remove(&task_id) else {
327 return;
328 };
329
330 let error_msg = match env.get_string(&error_message) {
331 Ok(msg) => msg.to_string_lossy().to_string(),
332 Err(e) => format!("Parsing error failed: {}", e),
333 };
334 if let Err(e) = tx.send(Err(MicError::Read(format!(
335 "Mic read failed: {}",
336 error_msg
337 )))) {
338 error!(?e, "Sending error failed.");
339 }
340}