valentine

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

commit c89c962772742c013271b3b881fc1f8ba948ce51
parent 4358539aeb5174202e6c812c014e725e629b9f93
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date:   Mon,  1 Jun 2026 13:51:45 -0500

feat: 'c' clears the mixer to silence (auto-backs-up first)

The device sits in a wall-of-unity mixer (288 crosspoints at 0 dB = up to
~+27 dB summing per bus) — a prime loudness suspect. Mixer tab 'c' saves the
current mixer to a 'mixer-backup' preset, then sets every crosspoint to
silence via Scarlett::clear_mixer. Reversible: load 'mixer-backup' with p.
Diagnostic step to confirm whether the mixer feeds the monitors.

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

Diffstat:
Mscarlett-core/src/matrix.rs | 11+++++++++++
Mvalentine/src/main.rs | 22++++++++++++++++++++++
Mvalentine/src/panels/mixer.rs | 2+-
3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/scarlett-core/src/matrix.rs b/scarlett-core/src/matrix.rs @@ -230,6 +230,17 @@ impl<T: Transport> Scarlett<T> { Ok(()) } + /// Set every crosspoint of every mix bus to silence (all inputs off). This + /// is the bulk "clear mixer" — useful to tame a factory wall-of-unity matrix. + /// `buses`/`inputs` are the device's mix dimensions. + pub fn clear_mixer(&mut self, buses: u16, inputs: usize) -> Result<(), TransportError> { + let silence = vec![0u16; inputs]; + for bus in 0..buses { + self.set_mix(bus, &silence)?; + } + Ok(()) + } + /// Read every mix bus into a `buses × inputs` grid of dB gains (for a UI /// refresh of the whole mixer matrix). pub fn read_mixer_db( diff --git a/valentine/src/main.rs b/valentine/src/main.rs @@ -495,10 +495,32 @@ impl App { KeyCode::Char('-') | KeyCode::Char('_') => self.nudge_mix(-1.0), KeyCode::Char('0') => self.set_mix_abs(0.0), KeyCode::Char('m') => self.set_mix_abs(scarlett_core::matrix::MIXER_MIN_DB), + KeyCode::Char('c') => self.clear_mixer(), _ => {} } } + /// Clear the entire mixer to silence — but first auto-save the current mixer + /// to a `mixer-backup` preset so it's fully reversible (load it with `p`). + fn clear_mixer(&mut self) { + // Back up current mixer first. + self.save_preset_as("mixer-backup"); + let inputs = S18I20_GEN3.mixer_inputs(); + let buses = S18I20_GEN3.mix_buses(); + let dev = match &mut self.device { + Ok(d) => d, + Err(_) => return, + }; + match dev.scarlett.clear_mixer(buses, inputs as usize) { + Ok(()) => { + dev.mixer.clear(); // force reload on next view + self.status = + Some("mixer cleared (backup saved → 'mixer-backup'; p to restore)".into()); + } + Err(e) => self.status = Some(format!("clear failed: {e}")), + } + } + fn nudge_mix(&mut self, delta: f32) { let cur = self.mixer_cell(); self.set_mix_abs(mixer::clamp_db(cur + delta)); diff --git a/valentine/src/panels/mixer.rs b/valentine/src/panels/mixer.rs @@ -152,7 +152,7 @@ pub fn render( lines.push(Line::from(Span::styled( format!( - "↑↓←→ move +/- 1dB 0 unity m mute [in {}/{} bus {}/{}]", + "↑↓←→ move +/- 1dB 0 unity m mute c clear-all [in {}/{} bus {}/{}]", cursor.input + 1, inputs, bus_label(cursor.bus),