commit 05eef3c033b90cf482b966a39dfc89d624796a38
parent bfea67a8601b792059b95d180ffc2a1c41663346
Author: Matthew Gantenbein <ganten1998@gmail.com>
Date: Sun, 31 May 2026 23:03:04 -0500
fix: usable capture level — makeup-gain default + multiplicative gain steps
Routing to Hydra worked after the tap-buffer fix but was unusably quiet:
process taps attenuate ~-20dB and +/- stepped additively by 0.05, so reaching
~10x makeup gain took ~180 presses (the "too quiet even maxed" report).
- New routes default to gain 10.0 (DEFAULT_GAIN): a 0.6 source now lands at
peak 0.60 out of the Hydra device immediately — verified.
- +/- are now multiplicative (×1.4 ≈ +3dB/press), spanning 0..16 in a few keys.
Verified through the rebuilt installed bundle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat:
2 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/crates/hydra/src/app.rs b/crates/hydra/src/app.rs
@@ -4,6 +4,11 @@ use hydra_ipc::{AudioApp, AudioDevice, Command, Response, RouteSummary};
use crate::client;
+/// Default gain for a new route. Core Audio process taps attenuate the captured signal
+/// (~-20 dB observed), so a fresh route at unity is far too quiet to be usable; ~10x makeup
+/// gain restores roughly the source level. Tunable per-route with +/- in the TUI.
+const DEFAULT_GAIN: f32 = 10.0;
+
/// Whether we're currently talking to the daemon.
#[derive(Debug, Clone)]
pub enum Connection {
@@ -166,7 +171,7 @@ impl App {
let (pid, name) = (app.pid, app.name.clone());
let output_uid = self.selected_output().map(|d| d.uid.clone());
let dest = self.selected_output().map(|d| d.name.as_str()).unwrap_or("default output").to_string();
- match client::request(Command::StartMonitor { pid, output_uid, gain: 1.0 }) {
+ match client::request(Command::StartMonitor { pid, output_uid, gain: DEFAULT_GAIN }) {
Ok(Response::RouteStarted { id }) => self.status = format!("{id}: {name} → {dest}"),
Ok(Response::Error(e)) => self.status = format!("start failed: {e}"),
Ok(other) => self.status = format!("unexpected: {other:?}"),
@@ -199,7 +204,7 @@ impl App {
}
let output_uid = self.selected_output().map(|d| d.uid.clone());
let dest = self.selected_output().map(|d| d.name.as_str()).unwrap_or("default output").to_string();
- match client::request(Command::StartCombined { pids: pids.clone(), output_uid, gain: 1.0 }) {
+ match client::request(Command::StartCombined { pids: pids.clone(), output_uid, gain: DEFAULT_GAIN }) {
Ok(Response::RouteStarted { id }) => {
self.status = format!("{id}: {} sources → {dest}", pids.len());
self.marked.clear();
@@ -224,12 +229,18 @@ impl App {
self.refresh();
}
- /// Nudge the selected route's gain by `delta`, clamped to 0.0..=2.0.
- pub fn adjust_gain(&mut self, delta: f32) {
+ /// Scale the selected route's gain by `factor` (multiplicative, so a few presses span
+ /// the whole range). `up=true` multiplies, `up=false` divides. Clamped to 0..=16.
+ /// Multiplicative because Core Audio process taps attenuate ~-20 dB, so useful makeup
+ /// gain is ~10x — unreachable with additive 0.05 steps.
+ pub fn adjust_gain(&mut self, up: bool) {
let Some(route) = self.routes.get(self.route_sel) else { return };
- // Ceiling is generous: Core Audio process taps attenuate the captured signal
- // (~-20 dB observed), so makeup gain well above unity is normal here.
- let gain = (route.gain + delta).clamp(0.0, 16.0);
+ const STEP: f32 = 1.4; // ~+3 dB per press
+ let gain = if up {
+ (route.gain.max(0.05) * STEP).clamp(0.0, 16.0)
+ } else {
+ (route.gain / STEP).clamp(0.0, 16.0)
+ };
let _ = client::request(Command::SetGain { id: route.id.clone(), gain });
self.refresh();
}
diff --git a/crates/hydra/src/main.rs b/crates/hydra/src/main.rs
@@ -78,8 +78,8 @@ fn handle_key(app: &mut App, code: KeyCode) {
KeyCode::Char('a') => app.toggle_show_all(),
KeyCode::Char('m') => app.toggle_mute_selected(),
KeyCode::Char('d') | KeyCode::Char('x') => app.stop_selected(),
- KeyCode::Char('+') | KeyCode::Char('=') => app.adjust_gain(0.05),
- KeyCode::Char('-') | KeyCode::Char('_') => app.adjust_gain(-0.05),
+ KeyCode::Char('+') | KeyCode::Char('=') => app.adjust_gain(true),
+ KeyCode::Char('-') | KeyCode::Char('_') => app.adjust_gain(false),
_ => {}
}
}