use super::SeedlingContext;
use firewheel::{FirewheelConfig, FirewheelCtx, backend::AudioBackend};
use std::sync::mpsc;
#[derive(Debug)]
pub struct InnerContext(mpsc::Sender<ThreadLocalCall>);
type ThreadLocalCall = Box<dyn FnOnce(&mut SeedlingContext) + Send + 'static>;
impl InnerContext {
#[inline(always)]
pub fn new<B>(
settings: FirewheelConfig,
stream_settings: B::Config,
) -> bevy_ecs::prelude::Result<Self>
where
B: AudioBackend + 'static,
B::Config: Send + 'static,
B::StreamError: Send + Sync + 'static,
{
let (audio_to_bev_tx, audio_to_bev_rx) = mpsc::channel::<Result<(), String>>();
let (bev_to_audio_tx, bev_to_audio_rx) = mpsc::channel::<ThreadLocalCall>();
std::thread::spawn(move || {
let mut context = FirewheelCtx::<B>::new(settings);
let result = context.start_stream(stream_settings);
if let Err(e) = result {
audio_to_bev_tx
.send(Err(format!("failed to start audio stream: {e:?}")))
.unwrap();
return;
} else {
audio_to_bev_tx.send(Ok(())).unwrap();
}
let mut context = SeedlingContext::new(context);
while let Ok(func) = bev_to_audio_rx.recv() {
(func)(&mut context);
}
});
audio_to_bev_rx.recv()??;
Ok(InnerContext(bev_to_audio_tx))
}
#[inline(always)]
pub fn with<F, O>(&mut self, f: F) -> O
where
F: FnOnce(&mut SeedlingContext) -> O + Send,
O: Send + 'static,
{
let (send, receive) = mpsc::sync_channel(1);
let func: Box<dyn FnOnce(&mut SeedlingContext) + Send> = Box::new(move |ctx| {
let result = f(ctx);
send.send(result).unwrap();
});
let func = unsafe {
core::mem::transmute::<
Box<dyn FnOnce(&mut SeedlingContext) + Send>,
Box<dyn FnOnce(&mut SeedlingContext) + Send + 'static>,
>(func)
};
self.0.send(func).unwrap();
receive.recv().unwrap()
}
}