metermap.rs (4846B)
1 //! Meter-map probe v2 (read-only). Two parts: 2 //! 1. Prints your full decoded routing (sink ← source) so we can see where 3 //! each physical input (esp. ADAT) is actually routed. 4 //! 2. Peak-holds all 65 raw GET_METER indices over ~12s and prints each with 5 //! its destination + the source currently routed there — so when you feed a 6 //! specific input we can see EXACTLY which raw index moves and how high 7 //! (for both channel mapping AND full-scale calibration vs your DAW). 8 //! 9 //! Run with Focusrite Control quit: cargo run -p spike --bin metermap 10 //! Feed ONE input you can name (e.g. ADAT 1) the whole time. Reads only. 11 12 use scarlett_core::model::{PortType, S18I20_GEN3}; 13 use scarlett_core::ports::{sink_name, source_name}; 14 use scarlett_core::{Scarlett, UsbTransport}; 15 16 fn main() { 17 if let Err(e) = run() { 18 eprintln!("\x1b[31mMETERMAP FAILED:\x1b[0m {e}"); 19 eprintln!("(If access/busy: quit Focusrite Control first.)"); 20 std::process::exit(1); 21 } 22 } 23 24 /// Destination index → human label, in the gen3c destination numbering 25 /// (Analogue Out 1-10, S/PDIF Out 1-2, ADAT Out 1-8, Mixer In 1-25, PCM cap 1-20). 26 fn dest_label(d: usize) -> String { 27 let an = S18I20_GEN3.port_count(PortType::Analogue).1 as usize; // 10 28 let sp = S18I20_GEN3.port_count(PortType::Spdif).1 as usize; // 2 29 let ad = S18I20_GEN3.port_count(PortType::Adat).1 as usize; // 8 30 let mx = S18I20_GEN3.port_count(PortType::Mix).1 as usize; // 25 31 let mut base = 0; 32 if d < base + an { 33 return format!("AnalogueOut {}", d - base + 1); 34 } 35 base += an; 36 if d < base + sp { 37 return format!("S/PDIF Out {}", d - base + 1); 38 } 39 base += sp; 40 if d < base + ad { 41 return format!("ADAT Out {}", d - base + 1); 42 } 43 base += ad; 44 if d < base + mx { 45 return format!("MixerIn {}", d - base + 1); 46 } 47 base += mx; 48 format!("PCM cap {}", d - base + 1) 49 } 50 51 fn run() -> Result<(), Box<dyn std::error::Error>> { 52 let mut dev = Scarlett::new(UsbTransport::open_default()?); 53 dev.init()?; 54 println!("connected: {}\n", S18I20_GEN3.name); 55 56 // --- Part 1: full routing, decoded --- 57 let num_dsts = S18I20_GEN3.mux_dst_count(); 58 let mux = dev.get_mux(num_dsts)?; 59 println!("=== ROUTING (sink ← source), non-Off only ==="); 60 for e in &mux { 61 if e.source != 0 { 62 println!( 63 " {:<16} ← {:<16} (dest hw {:#05x} / src hw {:#05x})", 64 sink_name(e.dest), 65 source_name(e.source), 66 e.dest, 67 e.source 68 ); 69 } 70 } 71 72 // --- Part 2: peak-hold every raw index over ~12s while you feed an input --- 73 let n = S18I20_GEN3.meter_count as usize; 74 let mut peak = vec![0u32; n]; 75 println!("\n=== Feed ONE input now. Holding peaks for ~12s… ==="); 76 // ~12s: 60 reads with 4 throwaway reads between each as a crude delay. 77 for _ in 0..60 { 78 let raw = dev.get_meters(S18I20_GEN3.meter_count)?; 79 for (i, &v) in raw.iter().enumerate() { 80 if v > peak[i] { 81 peak[i] = v; 82 } 83 } 84 for _ in 0..6 { 85 let _ = dev.get_meters(4); 86 } 87 } 88 89 // Print every raw index that saw signal, with which destination it is and 90 // what source is routed there — sorted by peak descending. 91 println!("\n=== RAW INDICES THAT MOVED (peak desc) ==="); 92 // raw index i corresponds to the i-th destination in meter_map order; we 93 // reconstruct that destination list to label each raw index. 94 let meter_map: [(u16, u16); 5] = [(45, 8), (55, 10), (0, 20), (53, 2), (20, 25)]; 95 let mut raw_to_dest = Vec::with_capacity(n); 96 for (start, count) in meter_map { 97 for j in 0..count { 98 raw_to_dest.push((start + j) as usize); 99 } 100 } 101 // source routed to a dest (for labeling), from the mux table 102 let mut src_at = vec![0u16; num_dsts]; 103 for e in &mux { 104 if (e.dest as usize) < src_at.len() { 105 src_at[e.dest as usize] = e.source; 106 } 107 } 108 109 let mut rows: Vec<(u32, usize, usize)> = peak 110 .iter() 111 .enumerate() 112 .filter(|(_, &p)| p > 20) 113 .map(|(i, &p)| (p, i, raw_to_dest.get(i).copied().unwrap_or(usize::MAX))) 114 .collect(); 115 rows.sort_by(|a, b| b.0.cmp(&a.0)); 116 117 for (p, raw_i, dest) in rows { 118 let src = src_at.get(dest).copied().unwrap_or(0); 119 let src_txt = if src == 0 { "(off/none)".to_string() } else { source_name(src) }; 120 let dlabel = if dest == usize::MAX { "?".to_string() } else { dest_label(dest) }; 121 println!( 122 " raw{raw_i:>2} peak {p:>5} dest{dest:>2} {dlabel:<16} source: {src_txt}" 123 ); 124 } 125 126 println!("\nMETERMAP DONE (read-only). Tell me which input you fed."); 127 Ok(()) 128 }