#![cfg_attr(target_os = "macos", no_std)]
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
use hashbrown::{HashMap, HashSet};
} else {
use std::collections::{HashMap, HashSet};
}
}
extern crate alloc;
extern crate core;
#[macro_use]
mod common;
mod platform;
use alloc::borrow::Cow;
use core::cell::RefCell;
use anyhow::bail;
use log::{info, warn};
use uuid::Uuid;
use crate::common::audiofox::Audiofox;
use crate::platform::{PlatformHandle, PlatformManager};
pub use crate::common::chunk::Chunk;
pub use crate::common::result::{Error, Result};
pub use crate::common::source::{AudioSource, AudioSources, SourceId, SourceName};
pub fn start() -> Result<AudioManager> {
Ok(AudioManager {
manager: Audiofox::start()?,
streams: RefCell::new(HashMap::new()),
})
}
#[must_use = "the manager must be stored to be kept alive"]
pub struct AudioManager {
manager: PlatformManager,
streams: RefCell<HashMap<Uuid, PlatformHandle>>,
}
impl AudioManager {
pub fn list(&self) -> Result<AudioSources> {
self.manager.list()
}
pub fn link<F>(&self, sources: &[AudioSource], callback: F) -> Result<LinkHandle<'_>>
where
F: Fn(Chunk) + Send + Sync + 'static,
{
if sources.is_empty() {
bail!("No sources provided.");
}
let new = self.list()?;
if new.is_empty() {
bail!("All provided sources are no longer valid.")
}
let targets: Cow<[AudioSource]> = if *sources == *new {
Cow::Borrowed(sources)
} else {
let old: HashSet<&SourceName> = sources.iter().map(|node| &node.name).collect();
Cow::Owned(new
.iter()
.filter_map(|new| {
if old.contains(&new.name) {
Some(new.clone())
} else {
warn!("{new} is no longer valid, skipping...");
None
}
})
.collect())
};
if targets.is_empty() {
bail!("All provided sources are no longer valid.")
}
let uuid = Uuid::new_v4();
let handle = self.manager.link(uuid, &targets, callback)?;
self.streams.borrow_mut().insert(uuid, handle);
Ok(LinkHandle { uuid, manager: self })
}
pub fn stop(self) {
drop(self);
}
}
impl Drop for AudioManager {
fn drop(&mut self) {
info!("manager stopped, cleaning up...");
}
}
#[must_use = "the stream will end immediately if the handle is not stored"]
pub struct LinkHandle<'a> {
manager: &'a AudioManager,
uuid: Uuid,
}
impl LinkHandle<'_> {
pub fn unlink(self) {
drop(self);
}
}
impl Drop for LinkHandle<'_> {
fn drop(&mut self) {
info!("handle dropped, ending stream...");
self.manager.streams.borrow_mut().remove(&self.uuid);
}
}