valentine

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

hwcheck.rs (3107B)


      1 //! Headless hardware check for Valentine's core ops (the TUI needs a real TTY,
      2 //! so this exercises scarlett-core directly). Read-modify-read-restore on a
      3 //! couple of safe controls, plus a meter snapshot. Run with Focusrite Control
      4 //! quit:  cargo run -p spike --bin hwcheck
      5 //!
      6 //! It is careful: it records each switch's original value and restores it, so
      7 //! your device ends up exactly as it started.
      8 
      9 use scarlett_core::controls::InputSwitch;
     10 use scarlett_core::model::S18I20_GEN3;
     11 use scarlett_core::{Scarlett, UsbTransport};
     12 
     13 fn main() {
     14     if let Err(e) = run() {
     15         eprintln!("\x1b[31mHWCHECK FAILED:\x1b[0m {e}");
     16         eprintln!("(If access/busy: quit Focusrite Control first.)");
     17         std::process::exit(1);
     18     }
     19 }
     20 
     21 fn run() -> Result<(), Box<dyn std::error::Error>> {
     22     let mut dev = Scarlett::new(UsbTransport::open_default()?);
     23     let fw = dev.init()?;
     24     println!("connected: {}  firmware {}", S18I20_GEN3.name, fw);
     25     println!("clock locked: {}", dev.get_sync()?);
     26 
     27     // --- Read the full input strip ---
     28     let before = dev.read_input_state()?;
     29     println!("\ninput strip read back:");
     30     println!("  air:     {:?}", before.air);
     31     println!("  pad:     {:?}", before.pad);
     32     println!("  inst:    {:?}", before.inst);
     33     println!("  phantom: {:?}", before.phantom);
     34 
     35     // --- Monitor + master volume ---
     36     let mon = dev.read_monitor_state()?;
     37     println!("\nmonitor: master {} dB, mute {}, dim {}", mon.master_db, mon.mute, mon.dim);
     38 
     39     // --- Round-trip test: toggle AIR on input 1, verify, restore ---
     40     let orig_air1 = dev.get_input_switch(InputSwitch::Air, 0)?;
     41     println!("\nround-trip AIR input 1 (currently {orig_air1}):");
     42     dev.set_input_switch(InputSwitch::Air, 0, !orig_air1)?;
     43     let flipped = dev.get_input_switch(InputSwitch::Air, 0)?;
     44     println!("  after toggle -> {flipped}  (expected {})", !orig_air1);
     45     let ok_air = flipped == !orig_air1;
     46     dev.set_input_switch(InputSwitch::Air, 0, orig_air1)?; // restore
     47     let restored = dev.get_input_switch(InputSwitch::Air, 0)?;
     48     println!("  restored      -> {restored}  (expected {orig_air1})");
     49 
     50     // --- Mixer: read bus A, report the first few crosspoints in dB ---
     51     let inputs = S18I20_GEN3.mixer_inputs() as usize;
     52     let bus0 = dev.get_mix(0, inputs)?;
     53     let db: Vec<f32> = bus0
     54         .iter()
     55         .take(4)
     56         .map(|&v| scarlett_core::matrix::mixer_value_to_db(v))
     57         .collect();
     58     println!("\nmix bus A, first 4 inputs (dB): {db:?}");
     59 
     60     // --- Meters snapshot ---
     61     let meters = dev.get_meters(S18I20_GEN3.meter_count)?;
     62     let peak = meters.iter().copied().max().unwrap_or(0);
     63     let nonzero = meters.iter().filter(|&&m| m > 0).count();
     64     println!(
     65         "\nmeters: {} points, {nonzero} nonzero, peak raw {peak}",
     66         meters.len()
     67     );
     68 
     69     let verdict = ok_air && restored == orig_air1;
     70     if verdict {
     71         println!("\n\x1b[32mHWCHECK PASSED\x1b[0m — read/write/restore all correct.");
     72     } else {
     73         println!("\n\x1b[33mHWCHECK INCOMPLETE\x1b[0m — air round-trip mismatch (see above).");
     74     }
     75     Ok(())
     76 }