Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion FineTune/Audio/Engine/AudioEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,12 @@ final class AudioEngine {
self?.deviceVolumeMonitor.refreshAfterDDCProbe()
self?.refreshAllTapOutputStates()
}
ddc.start()
// DDC probing writes to displays over I2C. Some USB-C→HDMI / DP
// adapters mishandle this and drop the video link. Gate behind a
// user setting (default on) so affected users can disable it.
if manager.appSettings.ddcVolumeControlEnabled {
ddc.start()
}
#endif

// Start device volume monitor AFTER deviceMonitor.start() populates devices
Expand Down Expand Up @@ -808,6 +813,20 @@ final class AudioEngine {
}
}

#if !APP_STORE
/// Starts or stops DDC/CI monitor-volume probing at runtime when the user
/// toggles the setting. Stopping halts all I2C writes to displays — the fix
/// for adapters that blank the screen when probed. Starting requires a relaunch
/// to take full effect for displays that were skipped at launch.
func setDDCVolumeControlEnabled(_ enabled: Bool) {
if enabled {
ddcController.start()
} else {
ddcController.stop()
}
}
#endif

/// Apply AutoEQ profile to all taps currently routed to the given device.
private func applyAutoEQToTaps(for deviceUID: String) {
for tap in taps.values {
Expand Down
8 changes: 8 additions & 0 deletions FineTune/Settings/SettingsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ nonisolated struct AppSettings: Codable, Equatable {
var loudnessCompensationEnabled: Bool = false // ISO 226:2023 equal-loudness contour compensation
var loudnessEqualizationEnabled: Bool = false // Real-time loudness equalization

// Monitor (DDC/CI) volume control
// When enabled, FineTune probes external displays over I2C (DDC/CI) so monitor
// speakers appear as volume-controllable outputs. Probing writes to the display's
// DDC bus; some USB-C→HDMI / DisplayPort adapters mishandle this and drop the
// video link (monitor goes dark). Disable to skip all DDC probing/writes.
var ddcVolumeControlEnabled: Bool = true

// Media Keys & HUD
var hudStyle: HUDStyle = .tahoe // Visual style of the volume HUD
var mediaKeyControlEnabled: Bool = true // Intercept F10/F11/F12 to drive the default output device
Expand Down Expand Up @@ -72,6 +79,7 @@ nonisolated struct AppSettings: Codable, Equatable {
showDeviceDisconnectAlerts = try c.decodeIfPresent(Bool.self, forKey: .showDeviceDisconnectAlerts) ?? true
loudnessCompensationEnabled = try c.decodeIfPresent(Bool.self, forKey: .loudnessCompensationEnabled) ?? false
loudnessEqualizationEnabled = try c.decodeIfPresent(Bool.self, forKey: .loudnessEqualizationEnabled) ?? false
ddcVolumeControlEnabled = try c.decodeIfPresent(Bool.self, forKey: .ddcVolumeControlEnabled) ?? true
hudStyle = try c.decodeIfPresent(HUDStyle.self, forKey: .hudStyle) ?? .tahoe
mediaKeyControlEnabled = try c.decodeIfPresent(Bool.self, forKey: .mediaKeyControlEnabled) ?? true
volumeHotkeyStep = try c.decodeIfPresent(VolumeHotkeyStep.self, forKey: .volumeHotkeyStep) ?? .normal
Expand Down
17 changes: 17 additions & 0 deletions FineTune/Views/Settings/Tabs/AudioTab.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ struct AudioTab: View {
.onChange(of: settings.appSettings.loudnessEqualizationEnabled) { _, newValue in
audioEngine.setLoudnessEqualizationEnabled(newValue)
}
#if !APP_STORE
.onChange(of: settings.appSettings.ddcVolumeControlEnabled) { _, newValue in
audioEngine.setDDCVolumeControlEnabled(newValue)
}
#endif
}

// MARK: - Volume
Expand Down Expand Up @@ -88,6 +93,18 @@ struct AudioTab: View {
.controlSize(.small)
.labelsHidden()
}
#if !APP_STORE
SettingsRowDivider()
SettingsRow(
"Monitor Volume Control (DDC)",
description: "Control monitor speaker volume over HDMI/DisplayPort. Disable if an external display goes dark when FineTune launches."
) {
Toggle("", isOn: $settings.appSettings.ddcVolumeControlEnabled)
.toggleStyle(.switch)
.controlSize(.small)
.labelsHidden()
}
#endif
SettingsRowDivider()
SettingsRow(
"System Sounds",
Expand Down