valentine

Terminal control panel for the Focusrite Scarlett 18i20 — a from-scratch replacement for Focusrite Control.
Log | Files | Refs | README | LICENSE

commit acf91fce8cecdf5ac8d5aabb9e20601124513252
parent d3394aa85a304fca36ade45a3e9e114bdcbc0ccb
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date:   Mon,  1 Jun 2026 15:59:37 -0500

feat(spike): adatverify — fresh-process read of actual ADAT routing

Reads (no writes) what truly feeds each ADAT out, to confirm whether the
device accepted ADAT<-Mix or silently kept ADAT<-PCM. Run after adatset to
distinguish 'write not applied' from 'mix bus not attenuating'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Diffstat:
Mspike/Cargo.toml | 4++++
Aspike/src/bin/adatverify.rs | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/spike/Cargo.toml b/spike/Cargo.toml @@ -55,3 +55,7 @@ scarlett-core = { path = "../scarlett-core" } [[bin]] name = "adatset" path = "src/bin/adatset.rs" + +[[bin]] +name = "adatverify" +path = "src/bin/adatverify.rs" diff --git a/spike/src/bin/adatverify.rs b/spike/src/bin/adatverify.rs @@ -0,0 +1,57 @@ +//! Fresh-read ADAT routing verifier. Opens the device, reads routing, prints +//! what ACTUALLY feeds each ADAT output — no writes at all. Run this AFTER +//! `adatset -- -80` (in a separate process) to see whether the device truly +//! accepted ADAT Out ← Mix, or silently kept ADAT Out ← PCM. +//! +//! cargo run -p spike --bin adatverify + +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[31mADATVERIFY FAILED:\x1b[0m {e}"); + std::process::exit(1); + } +} + +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 entries = dev.get_mux(num_dsts(&pc))?; + let st = MuxState::from_entries(pc, &entries); + + println!("FRESH read — what actually feeds each ADAT output:"); + let mut via_mix = 0; + for i in 0..8u16 { + let out = id_to_num(&pc, Dir::Out, 0x200 + i).unwrap_or(0); + let src_hw = num_to_id(&pc, Dir::In, st.get(out)); + let is_mix = (src_hw & 0xf00) == 0x300; + if is_mix { + via_mix += 1; + } + println!(" ADAT Out {} ← {}", i + 1, source_name(src_hw)); + } + println!( + "\n{}/8 ADAT outs via mixer.\n{}", + via_mix, + if via_mix == 8 { + "Device DID accept ADAT←Mix. So if -80 still played, the Mix bus itself\n\ + is NOT attenuating — the mixer isn't in the ADAT signal path as assumed." + } else if via_mix == 0 { + "Device kept ADAT←PCM despite the write 'succeeding' — the route write is\n\ + NOT taking on ADAT outputs (the bug). Our get_mux read back our own\n\ + optimistic value earlier; the device never applied it." + } else { + "Partial — some ADAT outs took the route, some didn't." + } + ); + + // Also show the meter so we can see if ADAT-feeding PCM is even active. + println!("\n(For reference, this is read-only — no writes made.)"); + let _ = S18I20_GEN3.name; + Ok(()) +}