use crate::PlaySoundParams;
use std::sync::mpsc;
pub use crate::mixer::Playback;
// Slightly reduced OpenSLES implementation
// from an amazing "audir" library: https://github.com/norse-rs/audir/
// and a little bit of glue code to make it work with macroquad
mod opensles {
use audir_sles as sles;
use std::os::raw::c_void;
use std::ptr;
pub mod api {
pub mod channel_mask {
pub const FRONT_LEFT: u32 = 0b0001;
pub const FRONT_RIGHT: u32 = 0b0010;
pub const FRONT_CENTER: u32 = 0b0100;
}
pub type ChannelMask = u32;
#[allow(dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Format {
F32,
U32,
}
/// Sample description.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct SampleDesc {
/// Sample Format.
pub format: Format,
/// Sample Rate.
pub sample_rate: usize,
}
/// Frame description.
///
/// Consists of a channel mask and a sample description.
/// A frame is composed of one samples per channel.
#[derive(Debug, Copy, Clone)]
pub struct FrameDesc {
/// Sample Format.
pub format: Format,
/// Sample Rate.
pub sample_rate: usize,
/// Channel Mask.
pub channels: ChannelMask,
}
#[derive(Debug, Clone)]
pub struct DeviceDesc {
pub sample_desc: SampleDesc,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Channels {
pub input: ChannelMask,
pub output: ChannelMask,
}
/// Device Stream properties.
#[derive(Debug, Clone, Copy)]
pub struct StreamProperties {
pub channels: ChannelMask,
pub sample_rate: usize,
pub buffer_size: usize,
}
impl StreamProperties {
pub fn num_channels(&self) -> usize {
((self.channels & channel_mask::FRONT_LEFT) != 0) as usize
+ ((self.channels & channel_mask::FRONT_RIGHT) != 0) as usize
+ ((self.channels & channel_mask::FRONT_CENTER) != 0) as usize
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StreamBuffers {
/// Number of frames per buffer.
pub frames: usize,
/// Input frame buffer.
///
/// For streams with empty input channels the pointer will be null.
/// The buffer pointer is aligned according to the stream format requirements.
pub input: *const (),
/// Input frame buffer.
///
/// For streams with empty output channels the pointer will be null.
/// The buffer pointer is aligned according to the stream format requirements.
pub output: *mut (),
}
pub struct Stream {
pub properties: StreamProperties,
pub buffers: StreamBuffers,
}
pub type StreamCallback = Box<dyn FnMut(Stream) + Send>;
}
const BUFFER_NUM_FRAMES: usize = 1024; // TODO: random
const BUFFER_CHAIN_SIZE: usize = 3; // TODO
fn map_channel_mask(mask: api::ChannelMask) -> sles::SLuint32 {
let mut channels = 0;
if mask & api::channel_mask::FRONT_LEFT != 0 {
channels |= sles::SL_SPEAKER_FRONT_LEFT;
}
if mask & api::channel_mask::FRONT_RIGHT != 0 {
channels |= sles::SL_SPEAKER_FRONT_RIGHT;
}
channels
}
struct CallbackData {
buffers: Vec<Vec<u32>>,
cur_buffer: usize,
callback: api::StreamCallback,
frame_desc: api::FrameDesc,
}
pub struct Instance {
_instance: sles::SLObjectItf,
engine: sles::SLEngineItf,
}
impl Instance {
pub unsafe fn new() -> Self {
let mut instance = ptr::null();
let result = sles::slCreateEngine(
&mut instance,
0,
ptr::null(),
0,
ptr::null(),
ptr::null_mut(),
);
let result = ((**instance).Realize).unwrap()(instance, sles::SL_BOOLEAN_FALSE as _);
let mut engine = ptr::null();
((**instance).GetInterface).unwrap()(
instance,
sles::SL_IID_ENGINE,
&mut engine as *mut _ as _,
);
Instance {
engine,
_instance: instance,
}
}
pub unsafe fn create_device(
&self,
desc: api::DeviceDesc,
channels: api::Channels,
callback: api::StreamCallback,
) -> Result<Device, ()> {
let mut mix = ptr::null();
((**self.engine).CreateOutputMix).unwrap()(
self.engine,
&mut mix,
0,
ptr::null(),
ptr::null(),
);
((**mix).Realize).unwrap()(mix, sles::SL_BOOLEAN_FALSE as _);
let mut audio_player = ptr::null();
let mut locator_source = sles::SLDataLocator_AndroidSimpleBufferQueue {
locatorType: sles::SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE as _,
numBuffers: BUFFER_CHAIN_SIZE as _,
};
let mut create_player = |format| {
let mut source = sles::SLDataSource {
pLocator: &mut locator_source as *mut _ as _,
pFormat: format,
};
let mut locator_sink = sles::SLDataLocator_OutputMix {
locatorType: sles::SL_DATALOCATOR_OUTPUTMIX as _,
outputMix: mix,
};
let mut sink = sles::SLDataSink {
pLocator: &mut locator_sink as *mut _ as _,
pFormat: ptr::null_mut(),
};
let ids = [sles::SL_IID_BUFFERQUEUE];
let requirements = [sles::SL_BOOLEAN_TRUE];
let _result = ((**self.engine).CreateAudioPlayer).unwrap()(
self.engine,
&mut audio_player,
&mut source,
&mut sink,
1,
ids.as_ptr(),
requirements.as_ptr() as _,
);
};
let sles_channels = map_channel_mask(channels.output);
let num_channels = sles_channels.count_ones();
match desc.sample_desc.format {
api::Format::F32 => {
let mut format_source = sles::SLAndroidDataFormat_PCM_EX {
formatType: sles::SL_ANDROID_DATAFORMAT_PCM_EX as _,
numChannels: num_channels as _,
sampleRate: (desc.sample_desc.sample_rate * 1000) as _,
bitsPerSample: sles::SL_PCMSAMPLEFORMAT_FIXED_32 as _,
containerSize: sles::SL_PCMSAMPLEFORMAT_FIXED_32 as _,
channelMask: sles_channels,
endianness: sles::SL_BYTEORDER_LITTLEENDIAN as _, // TODO
representation: sles::SL_ANDROID_PCM_REPRESENTATION_FLOAT as _,
};
create_player(&mut format_source as *mut _ as _);
}
api::Format::U32 => {
let mut format_source = sles::SLDataFormat_PCM {
formatType: sles::SL_DATAFORMAT_PCM as _,
numChannels: num_channels as _,
samplesPerSec: (desc.sample_desc.sample_rate * 1000) as _,
bitsPerSample: sles::SL_PCMSAMPLEFORMAT_FIXED_32 as _,
containerSize: sles::SL_PCMSAMPLEFORMAT_FIXED_32 as _,
channelMask: sles_channels,
endianness: sles::SL_BYTEORDER_LITTLEENDIAN as _, // TODO
};
create_player(&mut format_source as *mut _ as _);
}
}
((**audio_player).Realize).unwrap()(audio_player, sles::SL_BOOLEAN_FALSE as _);
let mut queue: sles::SLAndroidSimpleBufferQueueItf = ptr::null();
((**audio_player).GetInterface).unwrap()(
audio_player,
sles::SL_IID_BUFFERQUEUE,
&mut queue as *mut _ as _,
);
let mut state: sles::SLPlayItf = ptr::null();
((**audio_player).GetInterface).unwrap()(
audio_player,
sles::SL_IID_PLAY,
&mut state as *mut _ as _,
);
let buffers = (0..BUFFER_CHAIN_SIZE)
.map(|_| {
let buffer_size = num_channels as usize * BUFFER_NUM_FRAMES;
let mut buffer = Vec::<u32>::with_capacity(buffer_size);
buffer.set_len(buffer_size);
buffer
})
.collect();
let frame_desc = api::FrameDesc {
format: desc.sample_desc.format,
channels: channels.output,
sample_rate: desc.sample_desc.sample_rate,
};
let data = Box::new(CallbackData {
buffers,
cur_buffer: 0,
callback,
frame_desc,
});
let data = Box::into_raw(data); // TODO: destroy
extern "C" fn write_cb(queue: sles::SLAndroidSimpleBufferQueueItf, user: *mut c_void) {
unsafe {
let data = &mut *(user as *mut CallbackData);
data.cur_buffer = (data.cur_buffer + 1) % data.buffers.len();
let buffer = &mut data.buffers[data.cur_buffer];
let stream = api::Stream {
properties: api::StreamProperties {
channels: data.frame_desc.channels,
sample_rate: data.frame_desc.sample_rate,
buffer_size: BUFFER_NUM_FRAMES,
},
buffers: api::StreamBuffers {
output: buffer.as_mut_ptr() as _,
input: ptr::null(),
frames: buffer.len() / 2, // TODO: data.frame_desc_channels_count(), but always 2 now
},
};
(data.callback)(stream); // TODO: sizeof u32
((**queue).Enqueue).unwrap()(
queue,
buffer.as_mut_ptr() as _,
(buffer.len() * 4) as _,
);
}
}
let _result = (**queue).RegisterCallback.unwrap()(queue, Some(write_cb), data as _);
// Enqueue one frame to get the ball rolling
write_cb(queue, data as _);
Ok(Device {
_engine: self.engine,
state,
_queue: queue,
})
}
}
pub struct Device {
state: sles::SLPlayItf,
_engine: sles::SLEngineItf,
_queue: sles::SLAndroidSimpleBufferQueueItf,
}
impl Device {
pub unsafe fn start(&self) {
let result =
((**self.state).SetPlayState).unwrap()(self.state, sles::SL_PLAYSTATE_PLAYING as _);
}
#[allow(dead_code)]
pub unsafe fn pause(&self) {
let result =
((**self.state).SetPlayState).unwrap()(self.state, sles::SL_PLAYSTATE_PAUSED as _);
}
#[allow(dead_code)]
pub unsafe fn stop(&self) {
let result =
((**self.state).SetPlayState).unwrap()(self.state, sles::SL_PLAYSTATE_STOPPED as _);
}
}
}
unsafe fn audio_thread(mut mixer: crate::mixer::Mixer, rx1: mpsc::Receiver<ControlMessage>) {
use opensles::api::*;
use std::collections::HashMap;
let instance = opensles::Instance::new();
let device = instance
.create_device(
DeviceDesc {
sample_desc: SampleDesc {
format: Format::F32,
sample_rate: 44100,
},
},
Channels {
input: 0,
output: channel_mask::FRONT_LEFT | channel_mask::FRONT_RIGHT,
},
Box::new(move |stream| {
let properties = stream.properties;
let num_channels = properties.num_channels();
let buffer = std::slice::from_raw_parts_mut(
stream.buffers.output as *mut f32,
stream.buffers.frames as usize * num_channels,
);
mixer.fill_audio_buffer(buffer, stream.buffers.frames as usize);
}),
)
.unwrap();
device.start();
loop {
let message = rx1.recv().unwrap();
match message {
ControlMessage::Pause => {
device.pause();
}
ControlMessage::Resume => {
device.start();
}
}
}
}
pub enum ControlMessage {
Pause,
Resume,
}
pub struct AudioContext {
pub(crate) mixer_ctrl: crate::mixer::MixerControl,
tx1: mpsc::Sender<ControlMessage>,
}
impl AudioContext {
pub fn new() -> AudioContext {
use crate::mixer::Mixer;
let (tx1, rx1) = mpsc::channel();
let (mixer_builder, mixer_ctrl) = Mixer::new();
std::thread::spawn(move || unsafe {
audio_thread(mixer_builder.build(), rx1);
});
AudioContext { mixer_ctrl, tx1 }
}
pub fn pause(&mut self) {
self.tx1.send(ControlMessage::Pause).unwrap()
}
pub fn resume(&mut self) {
self.tx1.send(ControlMessage::Resume).unwrap()
}
}
pub struct Sound {
sound_id: u32,
}
impl Sound {
pub fn load(ctx: &AudioContext, data: &[u8]) -> Sound {
let sound_id = ctx.mixer_ctrl.load(data);
Sound { sound_id }
}
pub fn play(&self, ctx: &AudioContext, params: PlaySoundParams) -> Playback {
ctx.mixer_ctrl.play(self.sound_id, params)
}
pub fn stop(&self, ctx: &AudioContext) {
ctx.mixer_ctrl.stop_all(self.sound_id);
}
pub fn set_volume(&self, ctx: &AudioContext, volume: f32) {
ctx.mixer_ctrl.set_volume_all(self.sound_id, volume);
}
pub fn delete(&self, ctx: &AudioContext) {
ctx.mixer_ctrl.delete(self.sound_id);
}
}