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: SampleRate,
76 channel_count: i32,
78 audio_format: AudioEncoding,
80 mode: PlayerMode,
82}
83
84#[allow(dead_code)]
86impl AudioPlayer {
87 pub fn new(
104 mut env: JNIEnv,
105 context: &GlobalRef,
106 sample_rate: SampleRate,
107 channel_config: ChannelOutConfig,
108 audio_format: AudioEncoding,
109 mode: PlayerMode,
110 ) -> PlayerResult<Self> {
111 let sample_rate_value = sample_rate.value();
112 let channel_config_value = channel_config.value();
113 let audio_format_value = audio_format.value();
114 let mode_value = mode.value();
115
116 let player_class = load_class(&mut env, context, "rust.android.Player")?;
118
119 let player_obj = env.new_object(
121 &player_class,
122 "(IIII)V",
123 &[
124 sample_rate_value.into(),
125 channel_config_value.into(),
126 audio_format_value.into(),
127 mode_value.into(),
128 ],
129 )?;
130 let this = env.new_global_ref(player_obj)?;
131
132 env.call_method(
134 &this,
135 "init",
136 "(IIII)V",
137 &[
138 sample_rate_value.into(),
139 channel_config_value.into(),
140 audio_format_value.into(),
141 mode_value.into(),
142 ],
143 )?;
144
145 unsafe {
146 Ok(Self {
147 env: transmute(env),
148 this,
149 sample_rate,
150 channel_count: channel_config.channel_count(),
151 audio_format,
152 mode,
153 })
154 }
155 }
156
157 fn get_env(&self) -> JNIEnv<'_> {
158 unsafe { self.env.unsafe_clone() }
159 }
160
161 pub fn play(&self) -> PlayerResult<()> {
163 self.get_env().call_method(&self.this, "play", "()V", &[])?;
164 Ok(())
165 }
166
167 pub fn stop(&self) -> PlayerResult<()> {
169 self.get_env().call_method(&self.this, "stop", "()V", &[])?;
170
171 Ok(())
172 }
173
174 pub fn pause(&self) -> PlayerResult<()> {
176 self.get_env()
177 .call_method(&self.this, "pause", "()V", &[])?;
178 Ok(())
179 }
180
181 pub fn flush(&self) -> PlayerResult<()> {
183 self.get_env()
184 .call_method(&self.this, "flush", "()V", &[])?;
185
186 Ok(())
187 }
188
189 pub fn release(&self) -> PlayerResult<()> {
191 self.get_env()
192 .call_method(&self.this, "release", "()V", &[])?;
193
194 Ok(())
195 }
196
197 pub async fn write(&self, audio_data: &[u8]) -> PlayerResult<()> {
209 static ID: AtomicI16 = AtomicI16::new(0);
210 let task_id = ID.fetch_add(1, Ordering::AcqRel);
211 let (tx, rx) = channel();
212
213 {
214 let mut lock = PLAYER_WRITE_CALLBACKS.lock()?;
215 lock.insert(task_id, tx);
216 }
217
218 {
220 let jbyte_array = self.env.byte_array_from_slice(audio_data)?;
221
222 self.get_env()
224 .call_method(
225 &self.this,
226 "performWrite",
227 "(S[B)V",
228 &[task_id.into(), (&jbyte_array).into()],
229 )?
230 .v()?;
231 }
232
233 rx.await?
234 }
235
236 pub fn get_sample_rate(&self) -> i32 {
238 self.sample_rate.value()
239 }
240
241 pub fn get_channel_count(&self) -> i32 {
243 self.channel_count
244 }
245
246 pub fn get_audio_format(&self) -> i32 {
248 self.audio_format.value()
249 }
250
251 pub fn get_mode(&self) -> i32 {
253 self.mode.value()
254 }
255
256 pub fn get_buffer_size(&self) -> PlayerResult<i32> {
258 Ok(self
259 .get_env()
260 .call_method(&self.this, "getBufferSize", "()I", &[])?
261 .i()?)
262 }
263}
264
265unsafe impl Send for AudioPlayer {}
266unsafe impl Sync for AudioPlayer {}
267
268impl Drop for AudioPlayer {
269 fn drop(&mut self) {
270 if let Err(e) = self.release() {
271 error!(?e, "Dropping error.");
272 }
273 }
274}
275
276#[allow(non_snake_case)]
286#[unsafe(no_mangle)]
287extern "C" fn Java_rust_android_Player_onWriteSuccess(_env: JNIEnv, _this: JObject, task_id: i16) {
288 let Ok(mut lock) = PLAYER_WRITE_CALLBACKS.lock() else {
289 return;
290 };
291 let Some(tx) = lock.remove(&task_id) else {
292 return;
293 };
294 drop(lock);
295
296 let _ = tx.send(Ok(()));
297}
298
299#[allow(non_snake_case)]
310#[unsafe(no_mangle)]
311extern "C" fn Java_rust_android_Player_onWriteFailure(
312 mut env: JNIEnv,
313 _this: JObject,
314 task_id: i16,
315 error_message: JString,
316) {
317 let Ok(mut lock) = PLAYER_WRITE_CALLBACKS.lock() else {
318 return;
319 };
320 let Some(tx) = lock.remove(&task_id) else {
321 return;
322 };
323
324 let error_msg = match env.get_string(&error_message) {
325 Ok(msg) => msg.to_string_lossy().to_string(),
326 Err(e) => format!("Parsing error failed: {}", e),
327 };
328
329 if let Err(e) = tx.send(Err(PlayerError::Write(format!(
330 "Player write failed: {}",
331 error_msg
332 )))) {
333 error!(?e, "Sending error failed.");
334 }
335}