diff --git a/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate b/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate index a5aef58f0..797e78c1a 100644 Binary files a/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate and b/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift index 2714caf18..029e0a8c0 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift @@ -30,6 +30,34 @@ class ReplayGainUnit: EffectsUnit, ReplayGainUnitProtocol { } } + var preAmp: Float { + + didSet { + node.preAmp = preAmp + } + } + + var preventClipping: Bool { + + didSet { + parmsChanged() + } + } + + private var computedTrackGain: Float? { + + preventClipping ? + replayGain?.trackGainToPreventClipping ?? replayGain?.trackGain : + replayGain?.trackGain + } + + private var computedAlbumGain: Float? { + + preventClipping ? + replayGain?.albumGainToPreventClipping ?? replayGain?.albumGain : + replayGain?.albumGain + } + private func parmsChanged() { let replayGainDB: Float? @@ -37,25 +65,18 @@ class ReplayGainUnit: EffectsUnit, ReplayGainUnitProtocol { switch mode { case .preferAlbumGain: - replayGainDB = replayGain?.albumGain ?? replayGain?.trackGain + replayGainDB = computedAlbumGain ?? computedTrackGain case .preferTrackGain: - replayGainDB = replayGain?.trackGain ?? replayGain?.albumGain + replayGainDB = computedTrackGain ?? computedAlbumGain case .trackGainOnly: - replayGainDB = replayGain?.trackGain + replayGainDB = computedTrackGain } node.replayGain = replayGainDB ?? 0 } - var preAmp: Float { - - didSet { - node.preAmp = preAmp - } - } - var appliedGain: Float { node.replayGain } @@ -71,6 +92,7 @@ class ReplayGainUnit: EffectsUnit, ReplayGainUnitProtocol { mode = persistentState?.mode ?? AudioGraphDefaults.replayGainMode replayGain = nil preAmp = persistentState?.preAmp ?? AudioGraphDefaults.replayGainPreAmp + preventClipping = false presets = ReplayGainPresets(persistentState: persistentState) diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift index 2e31a6c74..2deeecb7d 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift @@ -24,6 +24,12 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni set {unit.preAmp = newValue} } + var preventClipping: Bool { + + get {unit.preventClipping} + set {unit.preventClipping = newValue} + } + func applyGain(_ replayGain: ReplayGain?) { unit.replayGain = replayGain } diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift index 1a2acfd2b..429532776 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift @@ -20,6 +20,8 @@ protocol ReplayGainUnitDelegateProtocol: EffectsUnitDelegateProtocol { var preAmp: Float {get set} + var preventClipping: Bool {get set} + func applyGain(_ replayGain: ReplayGain?) var appliedGain: Float {get} diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift index 9c7364ec0..05b354b98 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift @@ -20,6 +20,8 @@ protocol ReplayGainUnitProtocol: EffectsUnitProtocol { var preAmp: Float {get set} + var preventClipping: Bool {get set} + var appliedGain: Float {get} var effectiveGain: Float {get} diff --git a/Source/Core/TrackIO/Model/ReplayGain.swift b/Source/Core/TrackIO/Model/ReplayGain.swift index cd3911f00..3eebbbc5d 100644 --- a/Source/Core/TrackIO/Model/ReplayGain.swift +++ b/Source/Core/TrackIO/Model/ReplayGain.swift @@ -18,6 +18,7 @@ struct ReplayGain { let albumGain: Float? let albumPeak: Float? + let albumGainToPreventClipping: Float? private static let maxPeak: Float = 1 @@ -25,22 +26,17 @@ struct ReplayGain { guard trackGain != nil || albumGain != nil else {return nil} + func gainToPreventClipping(gain: Float, peak: Float) -> Float { + + let newPeak = pow(10.0, gain / 20) * peak + return newPeak > Self.maxPeak ? gain - (20 * log10(newPeak / Self.maxPeak)) : gain + } + self.trackGain = trackGain self.trackPeak = trackPeak - var trackGainToPreventClipping: Float - if let theTrackGain = trackGain, let theTrackPeak = trackPeak { - - trackGainToPreventClipping = theTrackGain - - let newPeak = pow(10.0, theTrackGain / 20) * theTrackPeak - - if newPeak > Self.maxPeak { - trackGainToPreventClipping -= (20 * log10(newPeak / Self.maxPeak)) - } - - self.trackGainToPreventClipping = trackGainToPreventClipping + self.trackGainToPreventClipping = gainToPreventClipping(gain: theTrackGain, peak: theTrackPeak) } else { self.trackGainToPreventClipping = nil @@ -48,6 +44,12 @@ struct ReplayGain { self.albumGain = albumGain self.albumPeak = albumPeak + + if let theAlbumGain = albumGain, let theAlbumPeak = albumPeak { + self.albumGainToPreventClipping = gainToPreventClipping(gain: theAlbumGain, peak: theAlbumPeak) + } else { + self.albumGainToPreventClipping = nil + } } init(ebur128AnalysisResult: EBUR128AnalysisResult) { @@ -58,5 +60,6 @@ struct ReplayGain { self.albumGain = nil self.albumPeak = nil + self.albumGainToPreventClipping = nil } } diff --git a/Source/UI/Effects/ReplayGain/ReplayGainUnit.xib b/Source/UI/Effects/ReplayGain/ReplayGainUnit.xib index 921ed348a..bbe8fce59 100644 --- a/Source/UI/Effects/ReplayGain/ReplayGainUnit.xib +++ b/Source/UI/Effects/ReplayGain/ReplayGainUnit.xib @@ -9,16 +9,15 @@ - + + - - @@ -109,7 +108,7 @@ - + @@ -118,7 +117,7 @@ - + @@ -141,50 +140,17 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -193,7 +159,7 @@ - + @@ -202,7 +168,7 @@ - + @@ -210,7 +176,7 @@ - + @@ -219,7 +185,7 @@ - + @@ -228,7 +194,7 @@ - + @@ -236,21 +202,23 @@ - - + + + - + - + @@ -261,6 +229,8 @@ + + diff --git a/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift b/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift index 0131e8f6d..d9067ad55 100644 --- a/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift +++ b/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift @@ -19,14 +19,13 @@ class ReplayGainUnitViewController: EffectsUnitViewController { // MARK: UI fields @IBOutlet weak var modeMenuButton: NSPopUpButton! - @IBOutlet weak var sourceMenuButton: NSPopUpButton! - @IBOutlet weak var lblAppliedGain: NSTextField! + @IBOutlet weak var lblGain: NSTextField! @IBOutlet weak var preAmpSlider: EffectsUnitSlider! @IBOutlet weak var lblPreAmp: NSTextField! - @IBOutlet weak var lblTotalGain: NSTextField! + @IBOutlet weak var btnPreventClipping: EffectsUnitToggle! // ------------------------------------------------------------------------ @@ -49,16 +48,13 @@ class ReplayGainUnitViewController: EffectsUnitViewController { fxUnitStateObserverRegistry.registerObserver(popupMenuCell, forFXUnit: graph.replayGainUnit) } - if let popupMenuCell = sourceMenuButton.cell as? EffectsUnitPopupMenuCell { - fxUnitStateObserverRegistry.registerObserver(popupMenuCell, forFXUnit: graph.replayGainUnit) - } + fxUnitStateObserverRegistry.registerObserver(btnPreventClipping, forFXUnit: audioGraphDelegate.replayGainUnit) } override func initControls() { super.initControls() - sourceMenuButton.selectItem(withTitle: "Metadata or analysis") modeMenuButton.selectItem(withTitle: replayGainUnit.mode.description) preAmpSlider.floatValue = replayGainUnit.preAmp @@ -66,15 +62,15 @@ class ReplayGainUnitViewController: EffectsUnitViewController { if !replayGainUnit.isScanning { if replayGainUnit.hasAppliedGain { - lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" + lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" } else { - lblAppliedGain.stringValue = "" + lblGain.stringValue = "" } } lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB" - lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB" + btnPreventClipping.onIf(replayGainUnit.preventClipping) } override func initSubscriptions() { @@ -92,31 +88,74 @@ class ReplayGainUnitViewController: EffectsUnitViewController { replayGainUnit.mode = .init(rawValue: sender.selectedTag()) ?? .defaultMode if replayGainUnit.hasAppliedGain { - lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" + lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" } else { - lblAppliedGain.stringValue = "" + lblGain.stringValue = "" } lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB" - lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB" } @IBAction func preAmpAction(_ sender: NSSlider) { replayGainUnit.preAmp = sender.floatValue - lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB" - lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB" + } + + @IBAction func preventClippingAction(_ sender: EffectsUnitToggle) { + + replayGainUnit.preventClipping = sender.isOn + + if !replayGainUnit.isScanning { + + if replayGainUnit.hasAppliedGain { + lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" + + } else { + lblGain.stringValue = "" + } + } } private func scanInitiated() { - lblAppliedGain.stringValue = "Analyzing file loudness ..." + lblGain.stringValue = "Analyzing file loudness ..." } private func scanCompleted() { + lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" + } + + override func colorSchemeChanged() { - lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" - lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB" + super.colorSchemeChanged() + btnPreventClipping.redraw(forState: replayGainUnit.state) + } + + override func activeControlColorChanged(_ newColor: NSColor) { + + super.activeControlColorChanged(newColor) + + if replayGainUnit.state == .active { + btnPreventClipping.redraw(forState: .active) + } + } + + override func inactiveControlColorChanged(_ newColor: NSColor) { + + super.inactiveControlColorChanged(newColor) + + if replayGainUnit.state == .bypassed { + btnPreventClipping.redraw(forState: .bypassed) + } + } + + override func suppressedControlColorChanged(_ newColor: NSColor) { + + super.suppressedControlColorChanged(newColor) + + if replayGainUnit.state == .suppressed { + btnPreventClipping.redraw(forState: .suppressed) + } } }