diff --git a/FineTune/Audio/Engine/AudioEngine.swift b/FineTune/Audio/Engine/AudioEngine.swift index cd3d07e6..019c3e08 100644 --- a/FineTune/Audio/Engine/AudioEngine.swift +++ b/FineTune/Audio/Engine/AudioEngine.swift @@ -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 @@ -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 { diff --git a/FineTune/Settings/SettingsManager.swift b/FineTune/Settings/SettingsManager.swift index f7d56478..ed41ae50 100644 --- a/FineTune/Settings/SettingsManager.swift +++ b/FineTune/Settings/SettingsManager.swift @@ -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 @@ -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 diff --git a/FineTune/Views/Settings/Tabs/AudioTab.swift b/FineTune/Views/Settings/Tabs/AudioTab.swift index 2c3597fe..411dee40 100644 --- a/FineTune/Views/Settings/Tabs/AudioTab.swift +++ b/FineTune/Views/Settings/Tabs/AudioTab.swift @@ -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 @@ -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",