This repository has been archived on 2024-12-14. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/bindings/rustboyadvance-jni/src/audio/connector.rs
Michel Heily ba2eff82ac platform/android: Big re-write of native interface
Mainly convert mainloop and audio thread into native code for
performance increase. (Calling into JNI every frame was costy)

The code was cleaned up quite a bit, but I may have introduced new bugs
in this process :<


Former-commit-id: fdbc21b5ab39f3d2e36647fd1177dc9a84a16980
Former-commit-id: ac765dbee8c994e1b69cc694846511837c2685b9
2020-09-30 00:27:00 +03:00

129 lines
4.3 KiB
Rust

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
],
);
}
}