use std::cell::RefCell;
use std::collections::HashMap;
use std::ops::Deref;
use std::rc::Rc;
use std::sync::mpsc;
use uuid::Uuid;
use anyhow::Context;
use indexmap::IndexMap;
use log::info;
use pw::core::CoreRc;
use pw::keys;
use pw::main_loop::MainLoopRc;
use pw::properties::properties;
use crate::common::callback::AudioCallback;
use crate::common::result::{Error, Result};
use crate::common::source::{AudioSource, AudioSources};
use crate::platform::linux::manager::Command;
use crate::platform::linux::registry::LINK_FACTORY;
use crate::platform::linux::registry::RegState;
use crate::platform::linux::stream::StreamState;
#[derive(Debug)]
pub enum Response {
Ready,
Error(Error),
List(AudioSources),
Init,
Link,
}
#[derive(Default)]
pub struct StreamStates(RefCell<IndexMap<Uuid, StreamState>>);
impl Deref for StreamStates {
type Target = RefCell<IndexMap<Uuid, StreamState>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone)]
pub struct CmdListener {
pub core: CoreRc,
streams: Rc<StreamStates>,
sender: mpsc::Sender<Response>,
mainloop: MainLoopRc,
registry: Rc<RegState>,
}
impl CmdListener {
pub fn new(
mainloop: &MainLoopRc,
core: &CoreRc,
registry: &Rc<RegState>,
sender: &mpsc::Sender<Response>,
streams: &Rc<StreamStates>,
) -> Self {
Self {
mainloop: mainloop.clone(),
core: core.clone(),
registry: registry.clone(),
sender: sender.clone(),
streams: streams.clone(),
}
}
pub fn recv(self: Rc<Self>) -> impl Fn(Command) {
move |cmd| {
let response = match cmd {
Command::Stop => {
self.mainloop.quit();
return;
}
Command::List => Response::List(
self.registry.nodes.borrow()
.iter()
.fold(HashMap::new(), |mut acc, (reg_id, reg_node)| {
acc.entry(reg_node.name.as_ref())
.or_insert_with(Vec::new)
.push(*reg_id);
acc
})
.into_iter()
.map(|(name, ids)| AudioSource {
id: ids.into(),
name: name.into(),
})
.collect()
),
Command::Init(uuid, callback, frames) => {
match self.on_init(uuid, callback, frames).map_err(Response::Error) {
Ok(()) => return,
Err(response) => response,
}
}
Command::Link(uuid, targets) => {
match self.on_link(uuid, targets.as_ref()).map_err(Response::Error) {
Ok(()) => Response::Link,
Err(e) => e,
}
}
Command::End(uuid) => {
self.on_end(uuid);
return;
}
};
send_logged!(self.sender, response);
}
}
fn on_init(&self, uuid: Uuid, callback: AudioCallback, frames: usize) -> Result<()> {
let mut streams = self.streams.borrow_mut();
streams.insert(uuid, StreamState::default());
let stream = &streams[&uuid];
stream.start(self, uuid, callback, frames).context("Failed to start stream")?;
Ok(())
}
pub fn on_end(&self, uuid: Uuid) {
if let Ok(mut streams) = self.streams.try_borrow_mut() {
if streams.swap_remove(&uuid).is_some() {
info!("stream successfully ended.");
}
}
}
pub fn on_link(&self, uuid: Uuid, targets: &[AudioSource]) -> Result<()> {
let link_factory = LINK_FACTORY.get().context("No link factory found")?;
let mut streams = self.streams.borrow_mut();
let stream = streams.get_mut(&uuid)
.with_context(|| format!("State not found for UUID: {uuid}"))?;
let registry = self.registry.nodes.borrow();
for target in targets {
for id in target.id.iter().filter(|&&id| id != 0) {
let reg_node = registry.get(id)
.with_context(|| format!("Invalid node ID: {id}"))?;
let ports = [
(&stream.in_left, ®_node.out_left),
(&stream.in_right, ®_node.out_right),
];
for (stream_input, target_output) in ports {
info!("linking: {}:{} -> {}:{}",id, target_output, stream.id, stream_input);
let link = self.core.create_object(
link_factory, &properties! {
*keys::LINK_OUTPUT_NODE => id.to_string(),
*keys::LINK_OUTPUT_PORT => target_output.as_ref(),
*keys::LINK_INPUT_NODE => stream.id.to_string(),
*keys::LINK_INPUT_PORT => stream_input.as_ref(),
},
).context("Failed to create link")?;
stream.links.push(link);
stream.ids.push(*id);
stream.names.push(target.name.as_ref().into());
}
}
}
Ok(())
}
}