commit 8291d0f2b2aa4f48658acc4421a3b08a4065f72f
parent 9d02e362e6366d46b63e5282b34f919a9c37a2ce
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date: Mon, 1 Jun 2026 15:12:46 -0500
feat(spike): adatfader — trace the ADAT via-mixer fader chain on hardware
Runs route_group_via_mixer + set_group_level for the ADAT group and reads the
device back at each step (ADAT out routing, mixer-input feeds, per-bus gains)
to pinpoint why the fader has no audible effect. Restores ADAT to direct after.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat:
2 files changed, 96 insertions(+), 0 deletions(-)
diff --git a/spike/Cargo.toml b/spike/Cargo.toml
@@ -42,6 +42,11 @@ path = "src/bin/presetcheck.rs"
name = "adatcheck"
path = "src/bin/adatcheck.rs"
+# ADAT-fader chain diagnostic (routes via mixer, reads back, restores).
+[[bin]]
+name = "adatfader"
+path = "src/bin/adatfader.rs"
+
[dependencies]
rusb = { version = "0.9", features = ["vendored"] }
anyhow.workspace = true
diff --git a/spike/src/bin/adatfader.rs b/spike/src/bin/adatfader.rs
@@ -0,0 +1,91 @@
+//! ADAT-fader chain diagnostic. Runs the exact via-mixer + level sequence the
+//! TUI does, reading device state back at each step so we can see precisely
+//! where the fader chain breaks. Restores ADAT to direct-from-DAW at the end.
+//!
+//! Run with Focusrite Control quit: cargo run -p spike --bin adatfader
+
+use scarlett_core::matrix::mixer_value_to_db;
+use scarlett_core::model::S18I20_GEN3;
+use scarlett_core::mux::{id_to_num, num_dsts, num_to_id, Dir, MuxState, PORT_COUNT_18I20_GEN3};
+use scarlett_core::ports::source_name;
+use scarlett_core::{Scarlett, UsbTransport};
+
+fn main() {
+ if let Err(e) = run() {
+ eprintln!("\x1b[31mADATFADER FAILED:\x1b[0m {e}");
+ std::process::exit(1);
+ }
+}
+
+fn adat_group() -> &'static scarlett_core::matrix::MonitorGroup {
+ scarlett_core::matrix::MONITOR_GROUPS
+ .iter()
+ .find(|g| g.name.starts_with("ADAT"))
+ .expect("ADAT group exists")
+}
+
+fn show_adat_routing(dev: &mut Scarlett<UsbTransport>, pc: [(u16, u16); 6], tag: &str) {
+ let entries = dev.get_mux(num_dsts(&pc)).unwrap();
+ let st = MuxState::from_entries(pc, &entries);
+ println!("--- ADAT Out routing [{tag}] ---");
+ for i in 0..8u16 {
+ let out = id_to_num(&pc, Dir::Out, 0x200 + i).unwrap_or(0);
+ let src = num_to_id(&pc, Dir::In, st.get(out));
+ println!(" ADAT Out {} ← {}", i + 1, source_name(src));
+ }
+}
+
+fn run() -> Result<(), Box<dyn std::error::Error>> {
+ let mut dev = Scarlett::new(UsbTransport::open_default()?);
+ dev.init()?;
+ let pc = PORT_COUNT_18I20_GEN3;
+ let g = adat_group();
+ let inputs = S18I20_GEN3.mixer_inputs() as usize;
+ println!("connected: {}\n", S18I20_GEN3.name);
+
+ show_adat_routing(&mut dev, pc, "before");
+
+ // Step 1: route ADAT via mixer.
+ println!("\n>>> route_group_via_mixer(ADAT)");
+ dev.route_group_via_mixer(pc, g)?;
+ show_adat_routing(&mut dev, pc, "after via-mixer");
+
+ // What feeds the ADAT mixer-inputs now?
+ let entries = dev.get_mux(num_dsts(&pc))?;
+ let st = MuxState::from_entries(pc, &entries);
+ println!("--- ADAT mixer-input feeds ---");
+ for i in 0..g.count {
+ let mi = id_to_num(&pc, Dir::Out, 0x300 + g.mix_in_base + i).unwrap_or(0);
+ let src = num_to_id(&pc, Dir::In, st.get(mi));
+ println!(" MixerIn {} ← {}", g.mix_in_base + i + 1, source_name(src));
+ }
+
+ // Step 2: set group level to -40 dB and read the buses back.
+ println!("\n>>> set_group_level(ADAT, -40 dB)");
+ dev.set_group_level(g, -40.0, inputs)?;
+ for i in 0..g.count {
+ let bus = g.bus_base + i;
+ let raw = dev.get_mix(bus, inputs)?;
+ let in_idx = (g.mix_in_base + i) as usize;
+ let this = raw.get(in_idx).map(|&v| mixer_value_to_db(v)).unwrap_or(-99.0);
+ let nonzero = raw.iter().filter(|&&v| v > 0).count();
+ println!(
+ " Mix bus {bus:>2}: input {} = {this:>6.1} dB ({nonzero} non-silent inputs total)",
+ in_idx + 1
+ );
+ }
+
+ println!(
+ "\nEXPECTED: ADAT Out ← Mix C..J; MixerIn ← PCM 13..20; each bus shows its\n\
+ one input at -40 dB. If routing still shows ← PCM, the route write failed.\n\
+ If the bus input isn't -40, the level write/index is wrong."
+ );
+
+ // Restore: ADAT back to direct from DAW.
+ println!("\n>>> restoring ADAT to direct-from-DAW");
+ dev.route_group_direct(pc, g)?;
+ show_adat_routing(&mut dev, pc, "restored");
+
+ println!("\nADATFADER DONE.");
+ Ok(())
+}