use jni::objects::{GlobalRef, JMethodID, JObject, JValue}; use jni::signature::{JavaType, Primitive}; use jni::sys::{jlong, jmethodID}; use jni::JNIEnv; pub struct AudioJNIConnector { pub audio_player_ref: GlobalRef, pub audio_buffer_ref: GlobalRef, /// jmethodID is safe to pass between threads but the jni-sys crate marked them as !Send /// TODO send patch to jni-sys mid_audio_write: jlong, mid_audio_play: jlong, mid_audio_pause: jlong, pub sample_rate: i32, pub sample_count: usize, } impl AudioJNIConnector { pub fn new(env: &JNIEnv, audio_player: JObject) -> AudioJNIConnector { let audio_player_ref = env.new_global_ref(audio_player).unwrap(); let audio_player_klass = env.get_object_class(audio_player_ref.as_obj()).unwrap(); let mid_audio_write = env .get_method_id(audio_player_klass, "audioWrite", "([SII)I") .expect("failed to get methodID for audioWrite") .into_inner() as jlong; let mid_audio_play = env .get_method_id(audio_player_klass, "play", "()V") .expect("failed to get methodID for audioPlay") .into_inner() as jlong; let mid_audio_pause = env .get_method_id(audio_player_klass, "pause", "()V") .expect("failed to get methodID for audioPause") .into_inner() as jlong; let mid_get_sample_rate = env .get_method_id(audio_player_klass, "getSampleRate", "()I") .expect("failed to get methodID for getSampleRate"); let mid_get_sample_count = env .get_method_id(audio_player_klass, "getSampleCount", "()I") .expect("failed to get methodID for getSampleCount"); let result = env .call_method_unchecked( audio_player_ref.as_obj(), mid_get_sample_count, JavaType::Primitive(Primitive::Int), &[], ) .unwrap(); let sample_count = match result { JValue::Int(sample_count) => sample_count as usize, _ => panic!("bad return value"), }; let result = env .call_method_unchecked( audio_player_ref.as_obj(), mid_get_sample_rate, JavaType::Primitive(Primitive::Int), &[], ) .unwrap(); let sample_rate = match result { JValue::Int(sample_rate) => sample_rate as i32, _ => panic!("bad return value"), }; let audio_buffer = env .new_short_array(sample_count as i32) .expect("failed to create sound buffer"); let audio_buffer_ref = env.new_global_ref(audio_buffer).unwrap(); // Don't need this ref anymore drop(audio_player_klass); AudioJNIConnector { audio_player_ref, audio_buffer_ref, mid_audio_pause, mid_audio_play, mid_audio_write, sample_rate, sample_count, } } #[inline] pub fn pause(&self, env: &JNIEnv) { // TODO handle errors let _ = env.call_method_unchecked( self.audio_player_ref.as_obj(), JMethodID::from(self.mid_audio_pause as jmethodID), JavaType::Primitive(Primitive::Void), &[], ); } #[inline] pub fn play(&self, env: &JNIEnv) { // TODO handle errors let _ = env.call_method_unchecked( self.audio_player_ref.as_obj(), JMethodID::from(self.mid_audio_play as jmethodID), JavaType::Primitive(Primitive::Void), &[], ); } #[inline] pub fn write_audio_samples(&self, env: &JNIEnv, samples: &[i16]) { // TODO handle errors env.set_short_array_region(self.audio_buffer_ref.as_obj().into_inner(), 0, &samples) .unwrap(); let _ = env.call_method_unchecked( self.audio_player_ref.as_obj(), JMethodID::from(self.mid_audio_write as jmethodID), JavaType::Primitive(Primitive::Int), &[ JValue::from(self.audio_buffer_ref.as_obj()), JValue::Int(0), // offset_in_shorts JValue::Int(samples.len() as i32), // size_in_shorts ], ); } }