use std::cell::OnceCell;
use std::sync::mpsc;
use std::thread;
use anyhow::{anyhow, Context};
use log::{error, info};
use uuid::Uuid;
use wasapi::initialize_mta;
use windows::Win32::Foundation::RPC_E_CHANGED_MODE;
use crate::common::audiofox::{Audiofox, AUDIOFOX};
use crate::common::callback::AudioCallbackFn;
use crate::common::result::Result;
use crate::common::source::{AudioSource, AudioSources};
use crate::platform::windows::list::list;
use crate::platform::windows::stream::{start_stream, Response};
#[derive(Debug)]
pub enum Command {
Unlink,
}
pub struct PlatformManager;
pub struct PlatformHandle {
sender: mpsc::Sender<Command>,
handle: OnceCell<thread::JoinHandle<()>>,
}
impl Audiofox for PlatformManager {
type LinkHandle = PlatformHandle;
fn start() -> Result<Self> {
match initialize_mta() {
RPC_E_CHANGED_MODE => {
info!("thread mode already set, skipping MTA initialization");
}
result => {
result.ok()?;
info!("successfully initialized MTA");
}
}
Ok(Self)
}
fn list(&self) -> Result<AudioSources> {
list()
}
fn link<F>(&self, uuid: Uuid, sources: &[AudioSource], callback: F, frames: usize) -> Result<Self::LinkHandle>
where
F: AudioCallbackFn,
{
let callback = callback.into();
let sources = sources.to_vec();
let (sender, stream_receiver) = mpsc::channel::<Command>();
let (stream_sender, receiver) = mpsc::channel::<Response>();
let handle = OnceCell::from(
thread::Builder::new()
.name(format!("{AUDIOFOX}-{uuid}"))
.spawn(move || start_stream(&stream_sender, &stream_receiver, &sources, &callback, frames))
.context("Failed to spawn stream thread")?
);
receive!(receiver, Response::Ready => {
info!("stream thread successfully started");
PlatformHandle { sender, handle } }
).context("Failed to initialize stream thread")
}
}
impl Drop for PlatformHandle {
fn drop(&mut self) {
if let Err(e) = || -> Result<()> {
info!("manager stopped, cleaning up...");
send!(self.sender, Command::Unlink).context("Stream already stopped")?;
self.handle
.take().context("Stream thread handle already taken")?
.join().map_err(|_| anyhow!("Failed to join stream thread"))
}() {
error!("cleanup failed: {e}");
}
}
}