Add support for --loopback-no-mix flag and topology synchronization
- 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.
This commit is contained in:
@@ -188,6 +188,64 @@ fn parse_sink_input_for_module(text: &str, module_id: u32) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Numeric pulse-side id for `name` within `kind` ("sinks" or "sources"),
|
||||
/// via `pactl list short <kind>` - same id/name column layout used by
|
||||
/// `match_source`'s numeric branch and `wipe::first_non_vmic`.
|
||||
fn resolve_pulse_id(kind: &str, name: &str) -> Result<Option<u32>> {
|
||||
let output = Command::new("pactl").args(["list", "short", kind]).output()?;
|
||||
Ok(String::from_utf8_lossy(&output.stdout)
|
||||
.lines()
|
||||
.find_map(|l| {
|
||||
let mut cols = l.split_whitespace();
|
||||
let id = cols.next()?.parse::<u32>().ok()?;
|
||||
(cols.next()? == name).then_some(id)
|
||||
}))
|
||||
}
|
||||
|
||||
/// Ids of every sink-input currently targeting `sink_name`. Used by
|
||||
/// `commands::topology::migrate` to snapshot stream routing before a
|
||||
/// migration recreates the sink node, so they can be moved back afterward.
|
||||
pub fn sink_inputs_on(sink_name: &str) -> Result<Vec<u32>> {
|
||||
let Some(id) = resolve_pulse_id("sinks", sink_name)? else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let output = Command::new("pactl").args(["list", "sink-inputs"]).output()?;
|
||||
Ok(parse_ids_matching(&String::from_utf8_lossy(&output.stdout), "Sink Input #", "Sink: ", id))
|
||||
}
|
||||
|
||||
/// Ids of every source-output currently reading from `source_name`. See
|
||||
/// `sink_inputs_on`.
|
||||
pub fn source_outputs_on(source_name: &str) -> Result<Vec<u32>> {
|
||||
let Some(id) = resolve_pulse_id("sources", source_name)? else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let output = Command::new("pactl").args(["list", "source-outputs"]).output()?;
|
||||
Ok(parse_ids_matching(&String::from_utf8_lossy(&output.stdout), "Source Output #", "Source: ", id))
|
||||
}
|
||||
|
||||
/// Ids of every block (headed by `header_prefix<id>`) whose `key_prefix<id>`
|
||||
/// property line equals `target_id`.
|
||||
fn parse_ids_matching(text: &str, header_prefix: &str, key_prefix: &str, target_id: u32) -> Vec<u32> {
|
||||
let mut ids = Vec::new();
|
||||
let mut current_id: Option<u32> = None;
|
||||
|
||||
for line in text.lines() {
|
||||
if let Some(rest) = line.strip_prefix(header_prefix) {
|
||||
current_id = rest.trim().parse().ok();
|
||||
continue;
|
||||
}
|
||||
let trimmed = line.trim_start();
|
||||
if let Some(rest) = trimmed.strip_prefix(key_prefix) {
|
||||
if rest.trim().parse::<u32>().ok() == Some(target_id) {
|
||||
if let Some(id) = current_id {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ids
|
||||
}
|
||||
|
||||
/// Every `module-loopback` whose `Argument:` line mentions a `vmic_` node,
|
||||
/// tracked in state or not.
|
||||
pub fn list_vmic_loopback_module_ids() -> Result<Vec<u32>> {
|
||||
@@ -238,6 +296,31 @@ Sink Input #12
|
||||
assert_eq!(parse_sink_input_for_module(text, 99), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_sink_inputs_targeting_sink() {
|
||||
let text = "\
|
||||
Sink Input #10
|
||||
\tSink: 65
|
||||
Sink Input #11
|
||||
\tSink: 99
|
||||
Sink Input #12
|
||||
\tSink: 65
|
||||
";
|
||||
assert_eq!(parse_ids_matching(text, "Sink Input #", "Sink: ", 65), vec![10, 12]);
|
||||
assert_eq!(parse_ids_matching(text, "Sink Input #", "Sink: ", 1), Vec::<u32>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finds_source_outputs_targeting_source() {
|
||||
let text = "\
|
||||
Source Output #20
|
||||
\tSource: 3
|
||||
Source Output #21
|
||||
\tSource: 4
|
||||
";
|
||||
assert_eq!(parse_ids_matching(text, "Source Output #", "Source: ", 3), vec![20]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lists_only_vmic_loopback_modules() {
|
||||
let text = "\
|
||||
|
||||
Reference in New Issue
Block a user