- Introduced the `--loopback-no-mix` flag to control 2-node vs 4-node loopback topologies. - Updated topology handling logic to recompute and migrate as needed based on loopback flags. - Enhanced database schema to include `loopback_no_mix` with automatic migration for backward compatibility. - Refactored `create`, `edit`, and `route` commands to integrate new topology management capabilities. - Improved `list` command output to include architecture details.
73 lines
2.8 KiB
Rust
73 lines
2.8 KiB
Rust
use crate::commands::topology;
|
|
use crate::error::Result;
|
|
use crate::pw::PwGraph;
|
|
use crate::state::{self, VmicState};
|
|
use crate::ui;
|
|
|
|
pub fn run() -> Result<()> {
|
|
let conn = state::open_db()?;
|
|
let states = VmicState::list_all(&conn)?;
|
|
if states.is_empty() {
|
|
ui::info("No virtual mics created.");
|
|
return Ok(());
|
|
}
|
|
|
|
let graph = PwGraph::connect().ok();
|
|
|
|
for (i, state) in states.iter().enumerate() {
|
|
let status = liveness(graph.as_ref(), state);
|
|
let status_colored = if status == "active" { ui::green(&status) } else { ui::yellow(&status) };
|
|
|
|
println!("{}", ui::blue(&state.name));
|
|
println!(" output device: {}", state.sink_name);
|
|
println!(" mic device: {}", state.source_name);
|
|
println!(" status: {status_colored}");
|
|
println!(" architecture: {}", topology::architecture_label(state));
|
|
println!(
|
|
" self-monitor loopback: {}",
|
|
state
|
|
.loopback_id
|
|
.map(|id| format!("yes (module {id})"))
|
|
.unwrap_or_else(|| "no".into())
|
|
);
|
|
if let Some(pct) = state.volume_pct {
|
|
println!(" loopback volume: {}", ui::blue(&format!("{pct}%")));
|
|
}
|
|
if let Some(mic) = &state.mic_source {
|
|
println!(" mixed source: {mic}");
|
|
if let Some(pct) = state.mic_volume_pct {
|
|
println!(" source volume: {}", ui::blue(&format!("{pct}%")));
|
|
}
|
|
}
|
|
if i + 1 < states.len() {
|
|
println!();
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Checks `sink_node_id`/`mix_node_id` against the live graph snapshot.
|
|
/// `mix_node_id` is only expected to be alive when this vmic is currently
|
|
/// running as `Pure4Node` (`mix_pid.is_some()`) - a `Simple2Node` vmic never
|
|
/// has one, and that's not a problem.
|
|
fn liveness(graph: Option<&PwGraph>, state: &VmicState) -> String {
|
|
let Some(graph) = graph else {
|
|
return "unknown (could not connect to PipeWire)".to_string();
|
|
};
|
|
let nodes = graph.nodes();
|
|
let sink_alive = state.sink_node_id.is_some_and(|id| nodes.iter().any(|n| n.id == id));
|
|
|
|
if topology::current_topology(state) == topology::Topology::Simple2Node {
|
|
return if sink_alive { "active".to_string() } else { "stale (matching pw-loopback process not found)".to_string() };
|
|
}
|
|
|
|
let mix_alive = state.mix_node_id.is_some_and(|id| nodes.iter().any(|n| n.id == id));
|
|
match (sink_alive, mix_alive) {
|
|
(true, true) => "active".to_string(),
|
|
(true, false) => "degraded (mix loopback process not found)".to_string(),
|
|
(false, true) => "degraded (sink loopback process not found)".to_string(),
|
|
(false, false) => "stale (matching pw-loopback process not found)".to_string(),
|
|
}
|
|
}
|