-
Notifications
You must be signed in to change notification settings - Fork 353
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
847 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use std::sync::Arc; | ||
|
||
extern crate jni; | ||
extern crate ndk_glue; | ||
|
||
use self::jni::Executor; | ||
use self::jni::{errors::Result as JResult, objects::JObject, JNIEnv, JavaVM}; | ||
|
||
// constants from android.media.AudioFormat | ||
pub const ENCODING_PCM_16BIT: i32 = 2; | ||
pub const ENCODING_PCM_FLOAT: i32 = 4; | ||
pub const CHANNEL_OUT_MONO: i32 = 4; | ||
pub const CHANNEL_OUT_STEREO: i32 = 12; | ||
|
||
fn with_attached<F, R>(closure: F) -> JResult<R> | ||
where | ||
F: FnOnce(&JNIEnv, JObject) -> JResult<R>, | ||
{ | ||
let activity = ndk_glue::native_activity(); | ||
let vm = Arc::new(unsafe { JavaVM::from_raw(activity.vm())? }); | ||
let activity = activity.activity(); | ||
Executor::new(vm).with_attached(|env| closure(env, activity.into())) | ||
} | ||
|
||
fn get_min_buffer_size( | ||
class: &'static str, | ||
sample_rate: i32, | ||
channel_mask: i32, | ||
format: i32, | ||
) -> i32 { | ||
// Unwrapping everything because these operations are not expected to fail | ||
// or throw exceptions. Android returns negative values for invalid parameters, | ||
// which is what we expect. | ||
with_attached(|env, _activity| { | ||
let class = env.find_class(class).unwrap(); | ||
env.call_static_method( | ||
class, | ||
"getMinBufferSize", | ||
"(III)I", | ||
&[sample_rate.into(), channel_mask.into(), format.into()], | ||
) | ||
.unwrap() | ||
.i() | ||
}) | ||
.unwrap() | ||
} | ||
|
||
pub fn get_audio_track_min_buffer_size(sample_rate: i32, channel_mask: i32, format: i32) -> i32 { | ||
get_min_buffer_size( | ||
"android/media/AudioTrack", | ||
sample_rate, | ||
channel_mask, | ||
format, | ||
) | ||
} | ||
|
||
pub fn get_audio_record_min_buffer_size(sample_rate: i32, channel_mask: i32, format: i32) -> i32 { | ||
get_min_buffer_size( | ||
"android/media/AudioRecord", | ||
sample_rate, | ||
channel_mask, | ||
format, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use std::convert::TryInto; | ||
use std::time::Duration; | ||
|
||
extern crate oboe; | ||
|
||
use crate::{ | ||
BackendSpecificError, BuildStreamError, PauseStreamError, PlayStreamError, SampleRate, | ||
StreamError, StreamInstant, | ||
}; | ||
|
||
pub fn to_stream_instant(duration: Duration) -> StreamInstant { | ||
StreamInstant::new( | ||
duration.as_secs().try_into().unwrap(), | ||
duration.subsec_nanos(), | ||
) | ||
} | ||
|
||
pub fn stream_instant<T: oboe::AudioStream + ?Sized>(stream: &mut T) -> StreamInstant { | ||
const CLOCK_MONOTONIC: i32 = 1; | ||
let ts = stream | ||
.get_timestamp(CLOCK_MONOTONIC) | ||
.unwrap_or(oboe::FrameTimestamp { | ||
position: 0, | ||
timestamp: 0, | ||
}); | ||
to_stream_instant(Duration::from_nanos(ts.timestamp as u64)) | ||
} | ||
|
||
impl From<oboe::Error> for StreamError { | ||
fn from(error: oboe::Error) -> Self { | ||
use self::oboe::Error::*; | ||
match error { | ||
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable, | ||
e => (BackendSpecificError { | ||
description: e.to_string(), | ||
}) | ||
.into(), | ||
} | ||
} | ||
} | ||
|
||
impl From<oboe::Error> for PlayStreamError { | ||
fn from(error: oboe::Error) -> Self { | ||
use self::oboe::Error::*; | ||
match error { | ||
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable, | ||
e => (BackendSpecificError { | ||
description: e.to_string(), | ||
}) | ||
.into(), | ||
} | ||
} | ||
} | ||
|
||
impl From<oboe::Error> for PauseStreamError { | ||
fn from(error: oboe::Error) -> Self { | ||
use self::oboe::Error::*; | ||
match error { | ||
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable, | ||
e => (BackendSpecificError { | ||
description: e.to_string(), | ||
}) | ||
.into(), | ||
} | ||
} | ||
} | ||
|
||
impl From<oboe::Error> for BuildStreamError { | ||
fn from(error: oboe::Error) -> Self { | ||
use self::oboe::Error::*; | ||
match error { | ||
Disconnected | Unavailable | Closed => Self::DeviceNotAvailable, | ||
NoFreeHandles => Self::StreamIdOverflow, | ||
InvalidFormat | InvalidRate => Self::StreamConfigNotSupported, | ||
IllegalArgument => Self::InvalidArgument, | ||
e => (BackendSpecificError { | ||
description: e.to_string(), | ||
}) | ||
.into(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
use std::marker::PhantomData; | ||
use std::time::{Duration, Instant}; | ||
|
||
extern crate oboe; | ||
|
||
use super::convert::{stream_instant, to_stream_instant}; | ||
use crate::{Data, InputCallbackInfo, InputStreamTimestamp, Sample, SampleRate, StreamError}; | ||
|
||
pub struct CpalInputCallback<I, C> { | ||
data_cb: Box<dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static>, | ||
error_cb: Box<dyn FnMut(StreamError) + Send + 'static>, | ||
sample_rate: SampleRate, | ||
created: Instant, | ||
phantom_channel: PhantomData<C>, | ||
phantom_input: PhantomData<I>, | ||
} | ||
|
||
impl<I, C> CpalInputCallback<I, C> { | ||
pub fn new<D, E>(data_cb: D, error_cb: E, sample_rate: SampleRate) -> Self | ||
where | ||
D: FnMut(&Data, &InputCallbackInfo) + Send + 'static, | ||
E: FnMut(StreamError) + Send + 'static, | ||
{ | ||
Self { | ||
data_cb: Box::new(data_cb), | ||
error_cb: Box::new(error_cb), | ||
sample_rate, | ||
created: Instant::now(), | ||
phantom_channel: PhantomData, | ||
phantom_input: PhantomData, | ||
} | ||
} | ||
|
||
fn make_callback_info( | ||
&self, | ||
audio_stream: &mut dyn oboe::AudioInputStream, | ||
) -> InputCallbackInfo { | ||
InputCallbackInfo { | ||
timestamp: InputStreamTimestamp { | ||
callback: to_stream_instant(self.created.elapsed()), | ||
capture: stream_instant(audio_stream), | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl<T: Sample, C: oboe::IsChannelCount> oboe::AudioInputCallback for CpalInputCallback<T, C> | ||
where | ||
(T, C): oboe::IsFrameType, | ||
{ | ||
type FrameType = (T, C); | ||
|
||
fn on_audio_ready( | ||
&mut self, | ||
audio_stream: &mut dyn oboe::AudioInputStream, | ||
audio_data: &[<<Self as oboe::AudioInputCallback>::FrameType as oboe::IsFrameType>::Type], | ||
) -> oboe::DataCallbackResult { | ||
let cb_info = self.make_callback_info(audio_stream); | ||
let channel_count = if C::CHANNEL_COUNT == oboe::ChannelCount::Mono { | ||
1 | ||
} else { | ||
2 | ||
}; | ||
(self.data_cb)( | ||
&unsafe { | ||
Data::from_parts( | ||
audio_data.as_ptr() as *mut _, | ||
audio_data.len() * channel_count, | ||
T::FORMAT, | ||
) | ||
}, | ||
&cb_info, | ||
); | ||
oboe::DataCallbackResult::Continue | ||
} | ||
|
||
fn on_error_before_close( | ||
&mut self, | ||
_audio_stream: &mut dyn oboe::AudioInputStream, | ||
error: oboe::Error, | ||
) { | ||
(self.error_cb)(StreamError::from(error)) | ||
} | ||
|
||
fn on_error_after_close( | ||
&mut self, | ||
_audio_stream: &mut dyn oboe::AudioInputStream, | ||
error: oboe::Error, | ||
) { | ||
(self.error_cb)(StreamError::from(error)) | ||
} | ||
} |
Oops, something went wrong.