use core::ffi::{c_int, c_void};
use core::marker::PhantomData;
use core::time::Duration;
use std::thread;
use crate::application::{BelaApplication, trampoline};
use crate::error::Error;
use crate::settings::Settings;
pub struct Bela<T: BelaApplication> {
app: *mut T,
started: bool,
_marker: PhantomData<T>,
}
impl<T: BelaApplication> Bela<T> {
pub fn new(application: T, settings: &Settings) -> Result<Self, Error> {
let app = Box::into_raw(Box::new(application));
let ret = unsafe {
let raw = bela_sys::Bela_InitSettings_alloc();
bela_sys::Bela_defaultSettings(raw);
settings.apply_to(&mut *raw);
(*raw).setup = Some(trampoline::setup::<T>);
(*raw).render = Some(trampoline::render::<T>);
(*raw).cleanup = Some(trampoline::cleanup::<T>);
let ret = bela_sys::Bela_initAudio(raw, app.cast::<c_void>());
bela_sys::Bela_InitSettings_free(raw);
ret
};
if ret != 0 {
drop(unsafe { Box::from_raw(app) });
return Err(Error::Init(ret));
}
Ok(Self {
app,
started: false,
_marker: PhantomData,
})
}
pub fn start(&mut self) -> Result<(), Error> {
if self.started {
return Ok(());
}
let ret = unsafe { bela_sys::Bela_startAudio() };
if ret != 0 {
return Err(Error::Start(ret));
}
self.started = true;
Ok(())
}
pub fn stop(&mut self) {
if self.started {
unsafe { bela_sys::Bela_stopAudio() };
self.started = false;
}
}
#[must_use]
pub fn stop_requested() -> bool {
unsafe { bela_sys::Bela_stopRequested() != 0 }
}
pub fn request_stop() {
unsafe { bela_sys::Bela_requestStop() }
}
pub fn run(application: T, settings: &Settings) -> Result<(), Error> {
let mut bela = Self::new(application, settings)?;
let handler = request_stop_on_signal as extern "C" fn(c_int);
for signal in [libc::SIGINT, libc::SIGTERM, libc::SIGHUP] {
unsafe { libc::signal(signal, handler as libc::sighandler_t) };
}
bela.start()?;
while !Self::stop_requested() {
thread::sleep(Duration::from_millis(10));
}
bela.stop();
Ok(())
}
}
extern "C" fn request_stop_on_signal(_signal: c_int) {
unsafe { bela_sys::Bela_requestStop() }
}
impl<T: BelaApplication> Drop for Bela<T> {
fn drop(&mut self) {
self.stop();
unsafe {
bela_sys::Bela_cleanupAudio();
drop(Box::from_raw(self.app));
}
}
}