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: i32,
76 channel_count: i32,
78 audio_format: i32,
80}
81
82impl AudioMicrophone {
84 pub fn new(
100 mut env: JNIEnv,
101 context: &GlobalRef,
102 sample_rate: i32,
103 channel_config: i32,
104 audio_format: i32,
105 ) -> MicResult<Self> {
106 let mic_class = load_class(&mut env, context, "rust.android.Mic")?;
108
109 let mic_obj = env.new_object(
111 &mic_class,
112 "(III)V",
113 &[
114 sample_rate.into(),
115 channel_config.into(),
116 audio_format.into(),
117 ],
118 )?;
119 let this = env.new_global_ref(mic_obj)?;
120
121 env.call_method(
123 &this,
124 "init",
125 "(IIII)V",
126 &[
127 AudioSource::Mic.value().into(),
128 sample_rate.into(),
129 channel_config.into(),
130 audio_format.into(),
131 ],
132 )?;
133
134 unsafe {
135 Ok(Self {
136 env: transmute(env),
137 this,
138 sample_rate,
139 channel_count: if channel_config == ChannelInConfig::Mono.value() {
140 1
141 } else {
142 2
143 },
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 audio_encoding = match self.audio_format {
204 x if x == AudioEncoding::Pcm8bit.value() => AudioEncoding::Pcm8bit,
205 x if x == AudioEncoding::Pcm16bit.value() => AudioEncoding::Pcm16bit,
206 x if x == AudioEncoding::PcmFloat.value() => AudioEncoding::PcmFloat,
207 _ => AudioEncoding::Pcm16bit,
208 };
209 let bytes_per_sample = audio_encoding.bytes_per_sample();
210 let samples_per_channel = (duration_ms * self.sample_rate) / 1000;
211 let total_samples = samples_per_channel * self.channel_count;
212 let requested_bytes = total_samples * bytes_per_sample;
213
214 let buffer_multiplier = 2;
216 let buffer_bytes = requested_bytes * buffer_multiplier;
217
218 self.get_env()
220 .call_method(
221 &self.this,
222 "performRead",
223 "(SII)V",
224 &[task_id.into(), requested_bytes.into(), buffer_bytes.into()],
225 )?
226 .v()?;
227
228 rx.await?
229 }
230
231 pub fn get_sample_rate(&self) -> i32 {
233 self.sample_rate
234 }
235
236 pub fn get_channel_count(&self) -> i32 {
238 self.channel_count
239 }
240
241 pub fn get_audio_format(&self) -> i32 {
243 self.audio_format
244 }
245
246 pub fn get_buffer_size(&self) -> MicResult<i32> {
248 Ok(self
249 .get_env()
250 .call_method(&self.this, "getBufferSize", "()I", &[])?
251 .i()?)
252 }
253
254 pub fn calculate_bytes_for_duration(&self, duration_ms: i32) -> i32 {
256 let audio_encoding = match self.audio_format {
257 x if x == AudioEncoding::Pcm8bit.value() => AudioEncoding::Pcm8bit,
258 x if x == AudioEncoding::Pcm16bit.value() => AudioEncoding::Pcm16bit,
259 x if x == AudioEncoding::PcmFloat.value() => AudioEncoding::PcmFloat,
260 _ => AudioEncoding::Pcm16bit,
261 };
262 let bytes_per_sample = audio_encoding.bytes_per_sample();
263 let samples_per_channel = (duration_ms * self.sample_rate) / 1000;
264 samples_per_channel * self.channel_count * bytes_per_sample
265 }
266}
267
268unsafe impl Send for AudioMicrophone {}
269unsafe impl Sync for AudioMicrophone {}
270
271impl Drop for AudioMicrophone {
272 fn drop(&mut self) {
273 if let Err(e) = self.release() {
274 error!(?e, "Dropping error.");
275 }
276 }
277}
278
279#[allow(non_snake_case)]
290#[unsafe(no_mangle)]
291extern "C" fn Java_rust_android_Mic_onReadSuccess(
292 env: JNIEnv,
293 _this: JObject,
294 task_id: i16,
295 audio_data: JByteArray,
296) {
297 let Ok(mut lock) = MIC_READ_CALLBACKS.lock() else {
298 return;
299 };
300 let Some(tx) = lock.remove(&task_id) else {
301 return;
302 };
303 drop(lock);
304
305 if audio_data.is_null() {
306 let _ = tx.send(Err(MicError::Read("Read succeeded but audio data is empty".into())));
307 return;
308 }
309
310 let mut buf = vec![0; env.get_array_length(&audio_data).unwrap_or(0) as _];
311 let _ = match env.get_byte_array_region(&audio_data, 0, &mut buf) {
312 Ok(()) => tx.send(Ok(buf.into_iter().map(|i| i as _).collect::<Vec<_>>())),
313 Err(e) => tx.send(Err(e.into())),
314 };
315}
316
317#[allow(non_snake_case)]
328#[unsafe(no_mangle)]
329extern "C" fn Java_rust_android_Mic_onReadFailure(
330 mut env: JNIEnv,
331 _this: JObject,
332 task_id: i16,
333 error_message: JString,
334) {
335 let Ok(mut lock) = MIC_READ_CALLBACKS.lock() else {
336 return;
337 };
338 let Some(tx) = lock.remove(&task_id) else {
339 return;
340 };
341
342 let error_msg = match env.get_string(&error_message) {
343 Ok(msg) => msg.to_string_lossy().to_string(),
344 Err(e) => format!("Parsing error failed: {}", e),
345 };
346 if let Err(e) = tx.send(Err(MicError::Read(format!(
347 "Mic read failed: {}",
348 error_msg
349 )))) {
350 error!(?e, "Sending error failed.");
351 }
352}