valentine

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

adatcheck.rs (3530B)


      1 //! ADAT-output diagnostic (READ-ONLY). Answers: are the ADAT outputs fed
      2 //! straight from the DAW (PCM, like a normal Focusrite Control setup) or through
      3 //! a mix bus (which, with the wall-of-unity mixer, would sum to a hot level)?
      4 //! Also compares the meter levels feeding ADAT (PCM 13-20) vs headphones/main
      5 //! (PCM 1-2) so we can see if the DAW itself is sending ADAT hotter.
      6 //!
      7 //! Run with Focusrite Control quit, ideally while playing audio:
      8 //!   cargo run -p spike --bin adatcheck
      9 
     10 use scarlett_core::matrix::mixer_value_to_db;
     11 use scarlett_core::meter::pcm_capture_level;
     12 use scarlett_core::model::S18I20_GEN3;
     13 use scarlett_core::mux::{id_to_num, num_dsts, Dir, MuxState, PORT_COUNT_18I20_GEN3};
     14 use scarlett_core::ports::source_name;
     15 use scarlett_core::{Scarlett, UsbTransport};
     16 
     17 fn main() {
     18     if let Err(e) = run() {
     19         eprintln!("\x1b[31mADATCHECK FAILED:\x1b[0m {e}");
     20         std::process::exit(1);
     21     }
     22 }
     23 
     24 fn run() -> Result<(), Box<dyn std::error::Error>> {
     25     let mut dev = Scarlett::new(UsbTransport::open_default()?);
     26     dev.init()?;
     27     let pc = PORT_COUNT_18I20_GEN3;
     28     println!("connected: {}\n", S18I20_GEN3.name);
     29 
     30     // 1. What feeds each ADAT output right now?
     31     let entries = dev.get_mux(num_dsts(&pc))?;
     32     let st = MuxState::from_entries(pc, &entries);
     33     println!("=== ADAT OUTPUT ROUTING ===");
     34     let mut via_mix = 0;
     35     for i in 0..8u16 {
     36         let out_num = id_to_num(&pc, Dir::Out, 0x200 + i).unwrap_or(0);
     37         let src_num = st.get(out_num);
     38         let src_id = id_to_num(&pc, Dir::In, src_num) // round-trip for label
     39             .map(|_| src_num)
     40             .unwrap_or(src_num);
     41         let src_hw = scarlett_core::mux::num_to_id(&pc, Dir::In, src_id);
     42         let name = source_name(src_hw);
     43         let is_mix = (src_hw & 0xf00) == 0x300;
     44         if is_mix {
     45             via_mix += 1;
     46         }
     47         println!("  ADAT Out {} ← {name}{}", i + 1, if is_mix { "   <-- via MIXER" } else { "" });
     48     }
     49     if via_mix > 0 {
     50         println!(
     51             "\n\x1b[33m{via_mix}/8 ADAT outs are fed by a MIX bus.\x1b[0m With the wall-of-unity\n\
     52              mixer this sums many sources → HOT. Focusrite Control feeds ADAT from\n\
     53              PCM directly, which is why FC wasn't loud. Fix: route ADAT ← PCM\n\
     54              (Routing tab), or lower those mix buses."
     55         );
     56     } else {
     57         println!("\n\x1b[32mAll ADAT outs are fed directly from PCM (like FC).\x1b[0m");
     58         println!("So the level is the DAW's output or the external converter, not routing.");
     59     }
     60 
     61     // 2. Compare DAW send levels: PCM 13-20 (ADAT) vs PCM 1-2 (main/HP).
     62     println!("\n=== DAW SEND LEVELS (play audio; peak over ~4s) ===");
     63     let mut peak = vec![0u32; S18I20_GEN3.meter_count as usize];
     64     for _ in 0..20 {
     65         let raw = dev.get_meters(S18I20_GEN3.meter_count)?;
     66         for (i, &v) in raw.iter().enumerate() {
     67             if v > peak[i] {
     68                 peak[i] = v;
     69             }
     70         }
     71         for _ in 0..4 {
     72             let _ = dev.get_meters(4);
     73         }
     74     }
     75     let db = |raw: u32| scarlett_core::meter::raw_to_dbfs(raw);
     76     let pk = |n| pcm_capture_level(&peak, n);
     77     println!("  PCM 1  (main/HP L): {:>6.1} dBFS", db(pk(1)));
     78     println!("  PCM 2  (main/HP R): {:>6.1} dBFS", db(pk(2)));
     79     println!("  PCM 13 (ADAT 1):    {:>6.1} dBFS", db(pk(13)));
     80     println!("  PCM 14 (ADAT 2):    {:>6.1} dBFS", db(pk(14)));
     81     let _ = mixer_value_to_db; // (kept for parity)
     82 
     83     println!("\nADATCHECK DONE (read-only).");
     84     Ok(())
     85 }