use std::rc::Rc;
use pipewire::{
node::{Node, NodeChangeMask, NodeInfoRef},
proxy::Listener,
registry::{GlobalObject, Registry},
};
use libspa::{
param::ParamType,
pod::{Object, Value, ValueArray},
utils::dict::DictRef,
};
use crate::wirehose::event_sender::EventSender;
use crate::wirehose::{
deserialize::deserialize, ObjectId, PropertyStore, StateEvent,
};
pub fn monitor_node(
registry: &Registry,
object: &GlobalObject<&DictRef>,
sender: &Rc<EventSender>,
) -> Option<(Rc<Node>, Box<dyn Listener>)> {
let object_id = ObjectId::from(object);
let props = object.props?;
let media_class = props.get("media.class")?;
match media_class {
"Audio/Sink" => (),
"Audio/Source" => (),
"Stream/Output/Audio" => (),
"Stream/Input/Audio" => (),
_ => return None,
}
let node: Node = registry.bind(object).ok()?;
let node = Rc::new(node);
let listener = node
.add_listener_local()
.info({
let sender_weak = Rc::downgrade(sender);
move |info| {
let Some(sender) = sender_weak.upgrade() else {
return;
};
for change in info.change_mask().iter() {
if change == NodeChangeMask::PROPS {
node_info_props(&sender, object_id, info);
}
}
}
})
.param({
let sender_weak = Rc::downgrade(sender);
move |_seq, id, _index, _next, param| {
let Some(sender) = sender_weak.upgrade() else {
return;
};
if let Some(param) = deserialize(param) {
match id {
ParamType::Props => {
node_param_props(&sender, object_id, param);
}
ParamType::PortConfig => {
node_param_port_config(&sender, object_id, param);
}
_ => {}
}
}
}
})
.register();
node.subscribe_params(&[ParamType::Props, ParamType::PortConfig]);
Some((node, Box::new(listener)))
}
fn node_info_props(
sender: &EventSender,
object_id: ObjectId,
node_info: &NodeInfoRef,
) {
let Some(props) = node_info.props() else {
return;
};
let property_store = PropertyStore::from(props);
sender.send(StateEvent::NodeProperties {
object_id,
props: property_store,
});
}
fn node_param_props(sender: &EventSender, object_id: ObjectId, param: Object) {
for prop in param.properties {
match prop.key {
libspa_sys::SPA_PROP_channelVolumes => {
if let Value::ValueArray(ValueArray::Float(value)) = prop.value
{
sender.send(StateEvent::NodeVolumes {
object_id,
volumes: value,
});
}
}
libspa_sys::SPA_PROP_mute => {
if let Value::Bool(value) = prop.value {
sender.send(StateEvent::NodeMute {
object_id,
mute: value,
});
}
}
_ => {}
}
}
}
fn node_param_port_config(
sender: &EventSender,
object_id: ObjectId,
param: Object,
) {
let Some(format_prop) = param
.properties
.into_iter()
.find(|prop| prop.key == libspa_sys::SPA_PARAM_PORT_CONFIG_format)
else {
return;
};
let Value::Object(Object { properties, .. }) = format_prop.value else {
return;
};
let Some(position_prop) = properties
.into_iter()
.find(|prop| prop.key == libspa_sys::SPA_FORMAT_AUDIO_position)
else {
return;
};
let Value::ValueArray(ValueArray::Id(value)) = position_prop.value else {
return;
};
let positions = value.into_iter().map(|x| x.0).collect();
sender.send(StateEvent::NodePositions {
object_id,
positions,
});
}