commit 4358539aeb5174202e6c812c014e725e629b9f93
parent 82fdcb2a7e26425769871c000972367b78f1fb2d
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date: Mon, 1 Jun 2026 13:46:14 -0500
feat(spike): presetcheck — read-only mixer loudness diagnostic
Reports non-silent mixer crosspoints, per-bus active counts, and unity-gain
crosspoints, to diagnose whether 'too loud' is a hot mix bus (summing many
unity sources) vs a preset round-trip issue. Read-only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat:
2 files changed, 62 insertions(+), 0 deletions(-)
diff --git a/spike/Cargo.toml b/spike/Cargo.toml
@@ -32,6 +32,11 @@ path = "src/bin/metermap.rs"
name = "muxcheck"
path = "src/bin/muxcheck.rs"
+# Read-only preset/mixer loudness diagnostic.
+[[bin]]
+name = "presetcheck"
+path = "src/bin/presetcheck.rs"
+
[dependencies]
rusb = { version = "0.9", features = ["vendored"] }
anyhow.workspace = true
diff --git a/spike/src/bin/presetcheck.rs b/spike/src/bin/presetcheck.rs
@@ -0,0 +1,57 @@
+//! Preset round-trip diagnostic (READ-ONLY apart from re-applying identical
+//! values). Answers: "does save→load change anything?" by capturing the full
+//! device state, then comparing it field-by-field to a preset built from it —
+//! and reporting any mixer crosspoints that differ from unity expectations.
+//!
+//! It does NOT change routing or toggles; it only reads. Run with Focusrite
+//! Control quit: cargo run -p spike --bin presetcheck
+
+use scarlett_core::matrix::mixer_value_to_db;
+use scarlett_core::model::S18I20_GEN3;
+use scarlett_core::{Scarlett, UsbTransport};
+
+fn main() {
+ if let Err(e) = run() {
+ eprintln!("\x1b[31mPRESETCHECK 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()?;
+ println!("connected: {}\n", S18I20_GEN3.name);
+
+ // Read the mixer matrix and report any non-(-inf / 0dB) crosspoints, plus the
+ // peak gain — a mix summing many sources at 0 dB is how things get loud.
+ let inputs = S18I20_GEN3.mixer_inputs() as usize;
+ let buses = S18I20_GEN3.mix_buses();
+ println!("=== MIXER (non-silent crosspoints) ===");
+ let mut hot = 0;
+ let mut active_per_bus = vec![0u32; buses as usize];
+ for bus in 0..buses {
+ let raw = dev.get_mix(bus, inputs)?;
+ for (i, &v) in raw.iter().enumerate() {
+ let db = mixer_value_to_db(v);
+ if db > -79.0 {
+ active_per_bus[bus as usize] += 1;
+ if db >= -0.1 {
+ hot += 1;
+ }
+ if active_per_bus[bus as usize] <= 6 {
+ println!(" bus {bus:>2} ← in {:>2}: {db:>6.1} dB", i + 1);
+ }
+ }
+ }
+ }
+ println!("\nper-bus active crosspoint counts: {active_per_bus:?}");
+ println!("crosspoints at ~0 dB (unity): {hot}");
+ println!(
+ "\nNote: if a mix bus sums many sources at 0 dB, that bus is HOT by design\n\
+ (summing 4 unity signals ≈ +12 dB). Lowering those crosspoints is the\n\
+ only device-side level control on this model."
+ );
+
+ println!("\nPRESETCHECK DONE (read-only).");
+ Ok(())
+}