1use {
2 super::{constants::*, load_class},
3 jni::{
4 JNIEnv,
5 errors::Error as JniError,
6 objects::{GlobalRef, 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 PlayerError {
24 Jni(JniError),
25 Poison(String),
26 Recv(RecvError),
27 Write(String),
28}
29
30impl Display for PlayerError {
31 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
32 write!(f, "PlayerError: ")?;
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::Write(msg) => write!(f, "WriteError: {}", msg),
38 }
39 }
40}
41
42impl Error for PlayerError {}
43
44impl From<JniError> for PlayerError {
45 fn from(value: JniError) -> Self {
46 Self::Jni(value)
47 }
48}
49
50impl From<RecvError> for PlayerError {
51 fn from(value: RecvError) -> Self {
52 Self::Recv(value)
53 }
54}
55
56impl<T> From<PoisonError<T>> for PlayerError {
57 fn from(value: PoisonError<T>) -> Self {
58 Self::Poison(value.to_string())
59 }
60}
61
62type PlayerResult<T> = Result<T, PlayerError>;
63
64static PLAYER_WRITE_CALLBACKS: LazyLock<Mutex<HashMap<i16, Sender<PlayerResult<()>>>>> =
66 LazyLock::new(|| Mutex::new(Default::default()));
67
68pub struct AudioPlayer {
70 env: JNIEnv<'static>,
72 this: GlobalRef,
74 sample_rate: i32,
76 channel_count: i32,
78 audio_format: i32,
80 mode: i32,
82}
83
84#[allow(dead_code)]
86impl AudioPlayer {
87 pub fn new(
104 mut env: JNIEnv,
105 context: &GlobalRef,
106 sample_rate: i32,
107 channel_config: i32,
108 audio_format: i32,
109 mode: i32,
110 ) -> PlayerResult<Self> {
111 let player_class = load_class(&mut env, context, "rust.android.Player")?;
113
114 let player_obj = env.new_object(
116 &player_class,
117 "(IIII)V",
118 &[
119 sample_rate.into(),
120 channel_config.into(),
121 audio_format.into(),
122 mode.into(),
123 ],
124 )?;
125 let this = env.new_global_ref(player_obj)?;
126
127 env.call_method(
129 &this,
130 "init",
131 "(IIII)V",
132 &[
133 sample_rate.into(),
134 channel_config.into(),
135 audio_format.into(),
136 mode.into(),
137 ],
138 )?;
139
140 unsafe {
141 Ok(Self {
142 env: transmute(env),
143 this,
144 sample_rate,
145 channel_count: if channel_config == ChannelOutConfig::Mono.value() {
146 1
147 } else {
148 2
149 },
150 audio_format,
151 mode,
152 })
153 }
154 }
155
156 fn get_env(&self) -> JNIEnv<'_> {
157 unsafe { self.env.unsafe_clone() }
158 }
159
160 pub fn play(&self) -> PlayerResult<()> {
162 self.get_env().call_method(&self.this, "play", "()V", &[])?;
163 Ok(())
164 }
165
166 pub fn stop(&self) -> PlayerResult<()> {
168 self.get_env().call_method(&self.this, "stop", "()V", &[])?;
169
170 Ok(())
171 }
172
173 pub fn pause(&self) -> PlayerResult<()> {
175 self.get_env()
176 .call_method(&self.this, "pause", "()V", &[])?;
177 Ok(())
178 }
179
180 pub fn flush(&self) -> PlayerResult<()> {
182 self.get_env()
183 .call_method(&self.this, "flush", "()V", &[])?;
184
185 Ok(())
186 }
187
188 pub fn release(&self) -> PlayerResult<()> {
190 self.get_env()
191 .call_method(&self.this, "release", "()V", &[])?;
192
193 Ok(())
194 }
195
196 pub async fn write(&self, audio_data: &[u8]) -> PlayerResult<()> {
208 static ID: AtomicI16 = AtomicI16::new(0);
209 let task_id = ID.fetch_add(1, Ordering::AcqRel);
210 let (tx, rx) = channel();
211
212 {
213 let mut lock = PLAYER_WRITE_CALLBACKS.lock()?;
214 lock.insert(task_id, tx);
215 }
216
217 {
219 let jbyte_array = self.env.byte_array_from_slice(audio_data)?;
220
221 self.get_env()
223 .call_method(
224 &self.this,
225 "performWrite",
226 "(S[B)V",
227 &[task_id.into(), (&jbyte_array).into()],
228 )?
229 .v()?;
230 }
231
232 rx.await?
233 }
234
235 pub fn get_sample_rate(&self) -> i32 {
237 self.sample_rate
238 }
239
240 pub fn get_channel_count(&self) -> i32 {
242 self.channel_count
243 }
244
245 pub fn get_audio_format(&self) -> i32 {
247 self.audio_format
248 }
249
250 pub fn get_mode(&self) -> i32 {
252 self.mode
253 }
254
255 pub fn get_buffer_size(&self) -> PlayerResult<i32> {
257 Ok(self
258 .get_env()
259 .call_method(&self.this, "getBufferSize", "()I", &[])?
260 .i()?)
261 }
262}
263
264unsafe impl Send for AudioPlayer {}
265unsafe impl Sync for AudioPlayer {}
266
267impl Drop for AudioPlayer {
268 fn drop(&mut self) {
269 if let Err(e) = self.release() {
270 error!(?e, "Dropping error.");
271 }
272 }
273}
274
275#[allow(non_snake_case)]
285#[unsafe(no_mangle)]
286extern "C" fn Java_rust_android_Player_onWriteSuccess(_env: JNIEnv, _this: JObject, task_id: i16) {
287 let Ok(mut lock) = PLAYER_WRITE_CALLBACKS.lock() else {
288 return;
289 };
290 let Some(tx) = lock.remove(&task_id) else {
291 return;
292 };
293 drop(lock);
294
295 let _ = tx.send(Ok(()));
296}
297
298#[allow(non_snake_case)]
309#[unsafe(no_mangle)]
310extern "C" fn Java_rust_android_Player_onWriteFailure(
311 mut env: JNIEnv,
312 _this: JObject,
313 task_id: i16,
314 error_message: JString,
315) {
316 let Ok(mut lock) = PLAYER_WRITE_CALLBACKS.lock() else {
317 return;
318 };
319 let Some(tx) = lock.remove(&task_id) else {
320 return;
321 };
322
323 let error_msg = match env.get_string(&error_message) {
324 Ok(msg) => msg.to_string_lossy().to_string(),
325 Err(e) => format!("Parsing error failed: {}", e),
326 };
327
328 if let Err(e) = tx.send(Err(PlayerError::Write(format!(
329 "Player write failed: {}",
330 error_msg
331 )))) {
332 error!(?e, "Sending error failed.");
333 }
334}