use num_traits::FromPrimitive; use oboe_sys as ffi; use std::{ fmt, marker::PhantomData, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, }; use crate::{set_input_callback, set_output_callback}; use super::{ audio_stream_base_fmt, wrap_status, AudioApi, AudioInputCallback, AudioOutputCallback, AudioStreamAsync, AudioStreamHandle, AudioStreamSync, ContentType, Input, InputPreset, IsChannelCount, IsDirection, IsFormat, IsFrameType, Mono, Output, PerformanceMode, RawAudioStreamBase, Result, SampleRateConversionQuality, SessionId, SharingMode, Stereo, Unspecified, Usage, }; #[repr(transparent)] pub(crate) struct AudioStreamBuilderHandle(ffi::oboe_AudioStreamBuilder); impl AudioStreamBuilderHandle { pub(crate) fn open_stream(&mut self) -> Result { let mut stream = AudioStreamHandle::default(); wrap_status(unsafe { ffi::oboe_AudioStreamBuilder_openStreamShared(&mut **self, stream.as_mut()) }) .map(|_| stream) } } impl Default for AudioStreamBuilderHandle { fn default() -> Self { let mut raw = MaybeUninit::zeroed(); Self(unsafe { ffi::oboe_AudioStreamBuilder_create(raw.as_mut_ptr()); raw.assume_init() }) } } impl Drop for AudioStreamBuilderHandle { fn drop(&mut self) { unsafe { ffi::oboe_AudioStreamBuilder_delete(&mut **self) } } } impl Deref for AudioStreamBuilderHandle { type Target = ffi::oboe_AudioStreamBuilder; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for AudioStreamBuilderHandle { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } /** * Factory for an audio stream. */ #[repr(transparent)] pub struct AudioStreamBuilder { raw: ManuallyDrop, _phantom: PhantomData<(D, C, T)>, } impl Drop for AudioStreamBuilder { fn drop(&mut self) { // SAFETY: self.raw is only drop here, or taken in Self::destructs, which don't drop self. unsafe { ManuallyDrop::drop(&mut self.raw); } } } impl fmt::Debug for AudioStreamBuilder { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { audio_stream_base_fmt(self, f) } } impl RawAudioStreamBase for AudioStreamBuilder { fn _raw_base(&self) -> &ffi::oboe_AudioStreamBase { unsafe { &*ffi::oboe_AudioStreamBuilder_getBase(&**self.raw as *const _ as *mut _) } } fn _raw_base_mut(&mut self) -> &mut ffi::oboe_AudioStreamBase { unsafe { &mut *ffi::oboe_AudioStreamBuilder_getBase(&mut **self.raw) } } } impl Default for AudioStreamBuilder { /** * Create new audio stream builder */ fn default() -> Self { Self { raw: Default::default(), _phantom: PhantomData, } } } impl AudioStreamBuilder { fn convert(self) -> AudioStreamBuilder { AudioStreamBuilder { raw: ManuallyDrop::new(self.destructs()), _phantom: PhantomData, } } /** * Request a specific number of channels * * Default is `Unspecified`. If the value is unspecified then * the application should query for the actual value after the stream is opened. */ pub fn set_channel_count(self) -> AudioStreamBuilder { let mut builder = self.convert(); builder._raw_base_mut().mChannelCount = X::CHANNEL_COUNT as i32; builder } /** * Request mono mode for a stream */ pub fn set_mono(self) -> AudioStreamBuilder { self.set_channel_count::() } /** * Request stereo mode for a stream */ pub fn set_stereo(self) -> AudioStreamBuilder { self.set_channel_count::() } /** * Request the direction for a stream * * The default is `Direction::Output` */ pub fn set_direction(self) -> AudioStreamBuilder { let mut builder = self.convert(); builder._raw_base_mut().mDirection = X::DIRECTION as i32; builder } /** * Request input direction for a stream */ pub fn set_input(self) -> AudioStreamBuilder { self.set_direction::() } /** * Request output direction for a stream * * It is optional because th stream builder already configured as output by default. */ pub fn set_output(self) -> AudioStreamBuilder { self.set_direction::() } /** * Request a specific sample rate in Hz. * * Default is kUnspecified. If the value is unspecified then * the application should query for the actual value after the stream is opened. * * Technically, this should be called the _frame rate_ or _frames per second_, * because it refers to the number of complete frames transferred per second. * But it is traditionally called _sample rate_. Se we use that term. */ pub fn set_sample_rate(mut self, sample_rate: i32) -> Self { self._raw_base_mut().mSampleRate = sample_rate; self } /** * Request a specific number of frames for the data callback. * * Default is kUnspecified. If the value is unspecified then * the actual number may vary from callback to callback. * * If an application can handle a varying number of frames then we recommend * leaving this unspecified. This allow the underlying API to optimize * the callbacks. But if your application is, for example, doing FFTs or other block * oriented operations, then call this function to get the sizes you need. */ pub fn set_frames_per_callback(mut self, frames_per_callback: i32) -> Self { self._raw_base_mut().mFramesPerCallback = frames_per_callback; self } /** * Request a sample data format, for example `f32`. * * Default is unspecified. If the value is unspecified then * the application should query for the actual value after the stream is opened. */ pub fn set_format(self) -> AudioStreamBuilder { let mut builder = self.convert(); builder._raw_base_mut().mFormat = X::FORMAT as i32; builder } pub fn set_i16(self) -> AudioStreamBuilder { self.set_format::() } pub fn set_f32(self) -> AudioStreamBuilder { self.set_format::() } /** * Set the requested buffer capacity in frames. * Buffer capacity in frames is the maximum possible buffer size in frames. * * The final stream capacity may differ. For __AAudio__ it should be at least this big. * For __OpenSL ES__, it could be smaller. * * Default is unspecified. */ pub fn set_buffer_capacity_in_frames(mut self, buffer_capacity_in_frames: i32) -> Self { self._raw_base_mut().mBufferCapacityInFrames = buffer_capacity_in_frames; self } /** * Get the audio API which will be requested when opening the stream. No guarantees that this is * the API which will actually be used. Query the stream itself to find out the API which is * being used. * * If you do not specify the API, then __AAudio__ will be used if isAAudioRecommended() * returns true. Otherwise __OpenSL ES__ will be used. */ pub fn get_audio_api(&self) -> AudioApi { FromPrimitive::from_i32(unsafe { ffi::oboe_AudioStreamBuilder_getAudioApi(&**self.raw) }) .unwrap() } /** * If you leave this unspecified then Oboe will choose the best API * for the device and SDK version at runtime. * * This should almost always be left unspecified, except for debugging purposes. * Specifying __AAudio__ will force Oboe to use AAudio on 8.0, which is extremely risky. * Specifying __OpenSL ES__ should mainly be used to test legacy performance/functionality. * * If the caller requests AAudio and it is supported then AAudio will be used. */ pub fn set_audio_api(mut self, audio_api: AudioApi) -> Self { unsafe { ffi::oboe_AudioStreamBuilder_setAudioApi(&mut **self.raw, audio_api as i32) } self } /** * Is the AAudio API supported on this device? * * AAudio was introduced in the Oreo 8.0 release. */ pub fn is_aaudio_supported() -> bool { unsafe { ffi::oboe_AudioStreamBuilder_isAAudioSupported() } } /** * Is the AAudio API recommended this device? * * AAudio may be supported but not recommended because of version specific issues. * AAudio is not recommended for Android 8.0 or earlier versions. */ pub fn is_aaudio_recommended() -> bool { unsafe { ffi::oboe_AudioStreamBuilder_isAAudioRecommended() } } /** * Request a mode for sharing the device. * The requested sharing mode may not be available. * So the application should query for the actual mode after the stream is opened. */ pub fn set_sharing_mode(mut self, sharing_mode: SharingMode) -> Self { self._raw_base_mut().mSharingMode = sharing_mode as i32; self } /** * Request a shared mode for the device */ pub fn set_shared(self) -> Self { self.set_sharing_mode(SharingMode::Shared) } /** * Request an exclusive mode for the device */ pub fn set_exclusive(self) -> Self { self.set_sharing_mode(SharingMode::Exclusive) } /** * Request a performance level for the stream. * This will determine the latency, the power consumption, and the level of * protection from glitches. */ pub fn set_performance_mode(mut self, performance_mode: PerformanceMode) -> Self { self._raw_base_mut().mPerformanceMode = performance_mode as i32; self } /** * Set the intended use case for the stream. * * The system will use this information to optimize the behavior of the stream. * This could, for example, affect how volume and focus is handled for the stream. * * The default, if you do not call this function, is Usage::Media. * * Added in API level 28. */ pub fn set_usage(mut self, usage: Usage) -> Self { self._raw_base_mut().mUsage = usage as i32; self } /** * Set the type of audio data that the stream will carry. * * The system will use this information to optimize the behavior of the stream. * This could, for example, affect whether a stream is paused when a notification occurs. * * The default, if you do not call this function, is `ContentType::Music`. * * Added in API level 28. */ pub fn set_content_type(mut self, content_type: ContentType) -> Self { self._raw_base_mut().mContentType = content_type as i32; self } /** * Set the input (capture) preset for the stream. * * The system will use this information to optimize the behavior of the stream. * This could, for example, affect which microphones are used and how the * recorded data is processed. * * The default, if you do not call this function, is InputPreset::VoiceRecognition. * That is because VoiceRecognition is the preset with the lowest latency * on many platforms. * * Added in API level 28. */ pub fn set_input_preset(mut self, input_preset: InputPreset) -> Self { self._raw_base_mut().mInputPreset = input_preset as i32; self } /** * Set the requested session ID. * * The session ID can be used to associate a stream with effects processors. * The effects are controlled using the Android AudioEffect Java API. * * The default, if you do not call this function, is `SessionId::None`. * * If set to `SessionId::Allocate` then a session ID will be allocated * when the stream is opened. * * The allocated session ID can be obtained by calling AudioStream::getSessionId() * and then used with this function when opening another stream. * This allows effects to be shared between streams. * * Session IDs from Oboe can be used the Android Java APIs and vice versa. * So a session ID from an Oboe stream can be passed to Java * and effects applied using the Java AudioEffect API. * * Allocated session IDs will always be positive and nonzero. * * Added in API level 28. */ pub fn set_session_id(mut self, session_id: SessionId) -> Self { self._raw_base_mut().mSessionId = session_id as i32; self } /** * Request a stream to a specific audio input/output device given an audio device ID. * * In most cases, the primary device will be the appropriate device to use, and the * device ID can be left unspecified. * * On Android, for example, the ID could be obtained from the Java AudioManager. * AudioManager.getDevices() returns an array of AudioDeviceInfo[], which contains * a getId() method (as well as other type information), that should be passed * to this method. * * When `java-interface` feature is used you can call [`AudioDeviceInfo::request`](crate::AudioDeviceInfo::request) for listing devices info. * * Note that when using OpenSL ES, this will be ignored and the created * stream will have device ID unspecified. */ pub fn set_device_id(mut self, device_id: i32) -> Self { self._raw_base_mut().mDeviceId = device_id; self } /** * If true then Oboe might convert channel counts to achieve optimal results. * On some versions of Android for example, stereo streams could not use a FAST track. * So a mono stream might be used instead and duplicated to two channels. * On some devices, mono streams might be broken, so a stereo stream might be opened * and converted to mono. * * Default is true. */ pub fn set_channel_conversion_allowed(mut self, allowed: bool) -> Self { self._raw_base_mut().mChannelConversionAllowed = allowed; self } /** * If true then Oboe might convert data formats to achieve optimal results. * On some versions of Android, for example, a float stream could not get a * low latency data path. So an I16 stream might be opened and converted to float. * * Default is true. */ pub fn set_format_conversion_allowed(mut self, allowed: bool) -> Self { self._raw_base_mut().mFormatConversionAllowed = allowed; self } /** * Specify the quality of the sample rate converter in Oboe. * * If set to None then Oboe will not do sample rate conversion. But the underlying APIs might * still do sample rate conversion if you specify a sample rate. * That can prevent you from getting a low latency stream. * * If you do the conversion in Oboe then you might still get a low latency stream. * * Default is `SampleRateConversionQuality::None` */ pub fn set_sample_rate_conversion_quality( mut self, quality: SampleRateConversionQuality, ) -> Self { self._raw_base_mut().mSampleRateConversionQuality = quality as i32; self } /** * Returns true if AAudio will be used based on the current settings. */ pub fn will_use_aaudio(&self) -> bool { let audio_api = self.get_audio_api(); (audio_api == AudioApi::AAudio && Self::is_aaudio_supported()) || (audio_api == AudioApi::Unspecified && Self::is_aaudio_recommended()) } /// Descontructs self into its handle, without calling drop. fn destructs(mut self) -> AudioStreamBuilderHandle { // Safety: the std::mem::forget prevents `raw` from being dropped by Self::drop. let raw = unsafe { ManuallyDrop::take(&mut self.raw) }; std::mem::forget(self); raw } } impl AudioStreamBuilder { /** * Create and open a synchronous (blocking) stream based on the current settings. */ pub fn open_stream(self) -> Result> { let mut raw = self.destructs(); let stream = raw.open_stream().map(AudioStreamSync::wrap_handle); drop(raw); stream } } impl AudioStreamBuilder { /** * Specifies an object to handle data or error related callbacks from the underlying API. * * __Important: See AudioStreamCallback for restrictions on what may be called * from the callback methods.__ * * When an error callback occurs, the associated stream will be stopped and closed in a separate thread. * * A note on why the streamCallback parameter is a raw pointer rather than a smart pointer: * * The caller should retain ownership of the object streamCallback points to. At first glance weak_ptr may seem like * a good candidate for streamCallback as this implies temporary ownership. However, a weak_ptr can only be created * from a shared_ptr. A shared_ptr incurs some performance overhead. The callback object is likely to be accessed * every few milliseconds when the stream requires new data so this overhead is something we want to avoid. * * This leaves a raw pointer as the logical type choice. The only caveat being that the caller must not destroy * the callback before the stream has been closed. */ pub fn set_callback(self, stream_callback: F) -> AudioStreamBuilderAsync where F: AudioInputCallback, (T, C): IsFrameType, { let mut raw = self.destructs(); set_input_callback(&mut raw, stream_callback); AudioStreamBuilderAsync { raw: ManuallyDrop::new(raw), _phantom: PhantomData, } } } impl AudioStreamBuilder { /** * Specifies an object to handle data or error related callbacks from the underlying API. * * __Important: See AudioStreamCallback for restrictions on what may be called * from the callback methods.__ * * When an error callback occurs, the associated stream will be stopped and closed in a separate thread. * * A note on why the streamCallback parameter is a raw pointer rather than a smart pointer: * * The caller should retain ownership of the object streamCallback points to. At first glance weak_ptr may seem like * a good candidate for streamCallback as this implies temporary ownership. However, a weak_ptr can only be created * from a shared_ptr. A shared_ptr incurs some performance overhead. The callback object is likely to be accessed * every few milliseconds when the stream requires new data so this overhead is something we want to avoid. * * This leaves a raw pointer as the logical type choice. The only caveat being that the caller must not destroy * the callback before the stream has been closed. */ pub fn set_callback(self, stream_callback: F) -> AudioStreamBuilderAsync where F: AudioOutputCallback, (T, C): IsFrameType, { let mut raw = self.destructs(); set_output_callback(&mut raw, stream_callback); AudioStreamBuilderAsync { raw: ManuallyDrop::new(raw), _phantom: PhantomData, } } } /** * Factory for an audio stream. */ pub struct AudioStreamBuilderAsync { raw: ManuallyDrop, _phantom: PhantomData<(D, F)>, } impl Drop for AudioStreamBuilderAsync { fn drop(&mut self) { // SAFETY: self.raw is only droped here, or taken in Self::destructs, which don't drop self. unsafe { ManuallyDrop::drop(&mut self.raw); } } } impl fmt::Debug for AudioStreamBuilderAsync { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { audio_stream_base_fmt(self, f) } } impl RawAudioStreamBase for AudioStreamBuilderAsync { fn _raw_base(&self) -> &ffi::oboe_AudioStreamBase { unsafe { &*ffi::oboe_AudioStreamBuilder_getBase(&**self.raw as *const _ as *mut _) } } fn _raw_base_mut(&mut self) -> &mut ffi::oboe_AudioStreamBase { unsafe { &mut *ffi::oboe_AudioStreamBuilder_getBase(&mut **self.raw) } } } impl AudioStreamBuilderAsync { /// Descontructs self into its handle without calling drop. fn destructs(mut self) -> AudioStreamBuilderHandle { // Safety: the std::mem::forget prevents `raw` from being dropped by Self::drop. let raw = unsafe { ManuallyDrop::take(&mut self.raw) }; std::mem::forget(self); raw } } impl AudioStreamBuilderAsync { /** * Create and open an asynchronous (callback-driven) input stream based on the current settings. */ pub fn open_stream(self) -> Result> { let mut raw = self.destructs(); let stream = raw.open_stream().map(AudioStreamAsync::wrap_handle); drop(raw); stream } } impl AudioStreamBuilderAsync { /** * Create and open an asynchronous (callback-driven) output stream based on the current settings. */ pub fn open_stream(self) -> Result> { let mut raw = self.destructs(); let stream = raw.open_stream().map(AudioStreamAsync::wrap_handle); drop(raw); stream } }