use crate::context::{SoundContext, SAMPLE_RATE};
use fyrox_core::visitor::{Visit, VisitResult, Visitor};
use fyrox_core::SafeLock;
use std::error::Error;
use std::sync::{Arc, Mutex, MutexGuard};
#[derive(Clone)]
pub struct SoundEngine(Arc<Mutex<State>>);
impl Default for SoundEngine {
fn default() -> Self {
Self::without_device()
}
}
pub struct State {
contexts: Vec<SoundContext>,
output_device: Option<tinyaudio::OutputDevice>,
}
impl SoundEngine {
pub fn new() -> Result<Self, Box<dyn Error>> {
let engine = Self::without_device();
engine.initialize_audio_output_device()?;
Ok(engine)
}
pub fn without_device() -> Self {
Self(Arc::new(Mutex::new(State {
contexts: Default::default(),
output_device: None,
})))
}
pub fn initialize_audio_output_device(&self) -> Result<(), Box<dyn Error>> {
let state = self.clone();
let device = tinyaudio::run_output_device(
tinyaudio::OutputDeviceParameters {
sample_rate: SAMPLE_RATE as usize,
channels_count: 2,
channel_sample_count: SoundContext::SAMPLES_PER_CHANNEL,
},
{
move |buf| {
let data = unsafe {
std::slice::from_raw_parts_mut(
buf.as_mut_ptr() as *mut (f32, f32),
buf.len() / 2,
)
};
state.state().render(data);
}
},
)?;
self.state().output_device = Some(device);
Ok(())
}
pub fn destroy_audio_output_device(&self) {
self.state().output_device = None;
}
pub fn state(&self) -> MutexGuard<State> {
self.0.safe_lock().unwrap()
}
}
impl State {
pub fn add_context(&mut self, context: SoundContext) {
self.contexts.push(context);
}
pub fn remove_context(&mut self, context: SoundContext) {
if let Some(position) = self.contexts.iter().position(|c| c == &context) {
self.contexts.remove(position);
}
}
pub fn remove_all_contexts(&mut self) {
self.contexts.clear()
}
pub fn has_context(&self, context: &SoundContext) -> bool {
self.contexts
.iter()
.any(|c| Arc::ptr_eq(c.state.as_ref().unwrap(), context.state.as_ref().unwrap()))
}
pub fn contexts(&self) -> &[SoundContext] {
&self.contexts
}
pub fn render_buffer_len() -> usize {
SoundContext::SAMPLES_PER_CHANNEL
}
pub fn render(&mut self, buf: &mut [(f32, f32)]) {
buf.fill((0.0, 0.0));
self.render_inner(buf);
}
fn render_inner(&mut self, buf: &mut [(f32, f32)]) {
for context in self.contexts.iter_mut() {
context.state().render(buf);
}
}
}
impl Visit for State {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
if visitor.is_reading() {
self.contexts.clear();
}
let mut region = visitor.enter_region(name)?;
self.contexts.visit("Contexts", &mut region)?;
Ok(())
}
}