Describe the bug
On a device controlled by FineTune's software volume, pressing the mute key to unmute correctly restores the previous volume (audio plays at the right level), but the HUD displays 0%. Cosmetic only, but confusing — it looks like the volume didn't come back when it actually did.
To reproduce
- Default output on a software-volume device (e.g. an external monitor), volume at ~60%
- Press the mute key (F10) — HUD shows muted
- Press it again to unmute
- Audio resumes at 60%, but the HUD shows "0%"
MacBook built-in speakers (hardware volume) don't show the problem.
Root cause (found by Claude Code while debugging)
Software-tier mute works by writing the visible volume to 0 (saving the old level) and unmute restores it — both inside DeviceVolumeMonitor.setMute. But MediaKeyMonitor.handleCore's .muteToggle case computes the HUD's slider fraction from a volume snapshot taken before the toggle, so right after unmuting it still shows the muted 0%. Hardware devices never hit this because their volume property stays untouched across mute.
Suggested fix
Give handleCore a way to re-read the volume after the toggle, and use the post-toggle value on unmute (mute keeps showing the pre-mute level, which matches how hardware devices display):
--- a/FineTune/Audio/Keys/MediaKeyMonitor.swift
+++ b/FineTune/Audio/Keys/MediaKeyMonitor.swift
@@ -240,6 +240,7 @@ final class MediaKeyMonitor {
currentMute: volumeMonitor.muteStates[deviceID] ?? false,
setVolume: { id, vol in volumeMonitor.setVolume(for: id, to: vol) },
setMute: { id, mute in volumeMonitor.setMute(for: id, to: mute) },
+ getVolume: { id in volumeMonitor.volumes[id] ?? 0 },
playFeedback: { gain in
self.feedbackPlayer?.requestFeedback(
gain: gain,
@@ -262,6 +263,7 @@ final class MediaKeyMonitor {
currentMute: Bool,
setVolume: (AudioDeviceID, Float) -> Void,
setMute: (AudioDeviceID, Bool) -> Void,
+ getVolume: ((AudioDeviceID) -> Float)? = nil,
playFeedback: (Float) -> Void = { _ in }
) {
let shouldShowHUD = !popupVisibility.isVisible
@@ -312,7 +314,16 @@ final class MediaKeyMonitor {
let newMute = !currentMute
setMute(deviceID, newMute)
if shouldShowHUD {
- hudController.show(sliderFraction: currentSlider, mute: newMute, deviceName: deviceName)
+ // Software-tier mute zeroes the visible volume and unmute restores
+ // the saved level inside setMute, so the pre-toggle snapshot reads
+ // 0% after unmuting — re-read to show the restored level.
+ let slider: Double
+ if !newMute, let getVolume {
+ slider = VolumeMapping.sliderFraction(forSystemGain: getVolume(deviceID), tier: tier)
+ } else {
+ slider = currentSlider
+ }
+ hudController.show(sliderFraction: slider, mute: newMute, deviceName: deviceName)
}
iconCoordinator?.flashDevice()
}
The getVolume parameter defaults to nil so existing call sites are unaffected. There are two regression tests (spy HUD presenter asserting the exact fraction shown in both mute directions) that I can share if useful.
I'm not a developer so I can't verify Claude's fix. But I tested it locally and confirm it was fixed. You decide to accept the suggested fix or implement your own.
Environment
- MacBook Air (Apple M5), macOS 27.0 beta (26A5378n)
- FineTune v1.9.0 (built from source)
- Monitor: Dell S2725QC over USB-C, software volume backend
Describe the bug
On a device controlled by FineTune's software volume, pressing the mute key to unmute correctly restores the previous volume (audio plays at the right level), but the HUD displays 0%. Cosmetic only, but confusing — it looks like the volume didn't come back when it actually did.
To reproduce
MacBook built-in speakers (hardware volume) don't show the problem.
Root cause (found by Claude Code while debugging)
Software-tier mute works by writing the visible volume to 0 (saving the old level) and unmute restores it — both inside
DeviceVolumeMonitor.setMute. ButMediaKeyMonitor.handleCore's.muteTogglecase computes the HUD's slider fraction from a volume snapshot taken before the toggle, so right after unmuting it still shows the muted 0%. Hardware devices never hit this because their volume property stays untouched across mute.Suggested fix
Give
handleCorea way to re-read the volume after the toggle, and use the post-toggle value on unmute (mute keeps showing the pre-mute level, which matches how hardware devices display):The
getVolumeparameter defaults tonilso existing call sites are unaffected. There are two regression tests (spy HUD presenter asserting the exact fraction shown in both mute directions) that I can share if useful.I'm not a developer so I can't verify Claude's fix. But I tested it locally and confirm it was fixed. You decide to accept the suggested fix or implement your own.
Environment