mod device;
mod driver;
mod mixer;
mod sink;
use std::sync::Arc;
use std::sync::mpsc::Sender;
pub use device::{Device, devices};
pub use sink::{Control, Input, Sink};
use crate::Error;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct Config {
pub device: Option<String>,
}
#[derive(Clone)]
pub struct Engine {
shared: Arc<driver::Shared>,
handle: Arc<Handle>,
}
pub(crate) struct Handle {
commands: Sender<driver::Command>,
}
impl Handle {
pub(crate) fn wake(&self) {
let _ = self.commands.send(driver::Command::Sync);
}
}
impl Drop for Handle {
fn drop(&mut self) {
let _ = self.commands.send(driver::Command::Shutdown);
}
}
impl Engine {
pub async fn open(config: Config) -> Result<Self, Error> {
let (commands, requests) = std::sync::mpsc::channel();
let (opened, ready) = tokio::sync::oneshot::channel();
let shared = Arc::new(driver::Shared::default());
let engine = Self {
handle: Arc::new(Handle {
commands: commands.clone(),
}),
shared: shared.clone(),
};
std::thread::Builder::new()
.name("moq-audio-playback".into())
.spawn(move || driver::run(requests, commands, shared, config.device, opened))
.map_err(|err| Error::Playback(format!("cannot spawn the playback thread: {err}")))?;
ready
.await
.map_err(|_| Error::Playback("the playback thread stopped".into()))??;
Ok(engine)
}
pub fn sink(&self, input: Input) -> Result<Sink, Error> {
let sink = self
.shared
.add(|id, rate| sink::new(id, rate, input, self.shared.clone(), self.handle.clone()))?;
self.handle.wake();
Ok(sink)
}
pub async fn switch(&self, config: Config) -> Result<(), Error> {
let (reply, response) = tokio::sync::oneshot::channel();
self.handle
.commands
.send(driver::Command::Switch {
device: config.device,
reply,
})
.map_err(|_| Error::Playback("the playback thread stopped".into()))?;
response
.await
.map_err(|_| Error::Playback("the playback thread stopped".into()))?
}
}