diff --git a/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate b/Aural.xcodeproj/project.xcworkspace/xcuserdata/kven.xcuserdatad/UserInterfaceState.xcuserstate index 06076e4f7..453322ec9 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/LoudnessScan/FFmpegReplayGainScanner+Scan.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner+Scan.swift index 9dea29934..dd6705b32 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner+Scan.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner+Scan.swift @@ -25,11 +25,10 @@ extension FFmpegReplayGainScanner { try self.ebur128.addFramesAsInt16(framesPointer: pointer, frameCount: frame.intSampleCount) self.consecutiveErrors = 0 - } catch let err as EBUR128Error { - print(err.description) - } catch { - print("Unknown error: \(error.localizedDescription)") + + consecutiveErrors.increment() + print((error as? EBUR128Error)?.description ?? error.localizedDescription) } } } @@ -46,11 +45,10 @@ extension FFmpegReplayGainScanner { try self.ebur128.addFramesAsInt32(framesPointer: pointer, frameCount: frame.intSampleCount) self.consecutiveErrors = 0 - } catch let err as EBUR128Error { - print(err.description) - } catch { - print("Unknown error: \(error.localizedDescription)") + + consecutiveErrors.increment() + print((error as? EBUR128Error)?.description ?? error.localizedDescription) } } } @@ -67,11 +65,10 @@ extension FFmpegReplayGainScanner { try self.ebur128.addFramesAsFloat(framesPointer: pointer, frameCount: frame.intSampleCount) self.consecutiveErrors = 0 - } catch let err as EBUR128Error { - print(err.description) - } catch { - print("Unknown error: \(error.localizedDescription)") + + consecutiveErrors.increment() + print((error as? EBUR128Error)?.description ?? error.localizedDescription) } } } @@ -88,11 +85,10 @@ extension FFmpegReplayGainScanner { try self.ebur128.addFramesAsDouble(framesPointer: pointer, frameCount: frame.intSampleCount) self.consecutiveErrors = 0 - } catch let err as EBUR128Error { - print(err.description) - } catch { - print("Unknown error: \(error.localizedDescription)") + + consecutiveErrors.increment() + print((error as? EBUR128Error)?.description ?? error.localizedDescription) } } } @@ -115,7 +111,7 @@ extension FFmpegReplayGainScanner { guard let pkt = try ctx.readPacket(from: stream) else { - consecutiveErrors += 1 + consecutiveErrors.increment() continue } @@ -142,16 +138,16 @@ extension FFmpegReplayGainScanner { if !err.isEOF { - consecutiveErrors += 1 + consecutiveErrors.increment() print("Error: \(err.code.errorDescription)") } } } } catch { - print("Error: \(error)") + print("Error: \(error.localizedDescription)") } - return isCancelled || consecutiveErrors >= 3 ? nil : try ebur128.analyze() + return isCancelled || (consecutiveErrors >= 3) || (!eof) ? nil : try ebur128.analyze() } } diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner.swift index 2c414fffc..beed42de4 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/FFmpegReplayGainScanner.swift @@ -40,7 +40,7 @@ class FFmpegReplayGainScanner: EBUR128LoudnessScannerProtocol { self.ctx = try FFmpegFileContext(for: file) guard let theAudioStream = ctx.bestAudioStream else { - throw FormatContextInitializationError(description: "\nUnable to find audio stream in file: '\(file.path)'") + throw FormatContextInitializationError(description: "Unable to find audio stream in file: '\(file.path)'") } self.stream = theAudioStream @@ -95,11 +95,8 @@ class FFmpegReplayGainScanner: EBUR128LoudnessScannerProtocol { completionHandler(result) - } catch let err as EBUR128Error { - print("Error: \(err.description)") - } catch { - print("Error: \(error)") + print((error as? EBUR128Error)?.description ?? error.localizedDescription) } } } diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/ReplayGainScanner.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/ReplayGainScanner.swift index 378b6de13..c944120d9 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/ReplayGainScanner.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/LoudnessScan/ReplayGainScanner.swift @@ -43,15 +43,12 @@ class ReplayGainScanner { cancelOngoingScan() // First, check the cache -// if let theResult = cache[file] { -// -// // Cache hit -// print("\nReplayGainScanner.init() CACHE HIT !!! \(theResult.replayGain) for file \(file.lastPathComponent)") -// completionHandler(ReplayGain(ebur128AnalysisResult: theResult)) -// return -// } - - print("\nReplayGainScanner.init() CACHE MISS for file \(file.lastPathComponent)") + if let theResult = cache[file] { + + // Cache hit + completionHandler(ReplayGain(ebur128AnalysisResult: theResult)) + return + } // Cache miss, initiate a scan @@ -61,8 +58,6 @@ class ReplayGainScanner { // will prevent rogue completion handler execution. guard self?.scanOp == finishedScanOp else {return} - print("Finished ? \(finishedScanOp.isFinished)") - if let theResult = ebur128Result { // Scan succeeded, cache the result diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift index 8d3d2afd3..f0e45bbb9 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnit.swift @@ -80,25 +80,36 @@ class ReplayGainUnit: EffectsUnit, ReplayGainUnitProtocol { let replayGainDB: Float? + lazy var albumGain = computedAlbumGain + lazy var trackGain = computedTrackGain + switch mode { case .preferAlbumGain: - replayGainDB = computedAlbumGain ?? computedTrackGain + + replayGainDB = albumGain ?? trackGain + appliedGainType = albumGain != nil ? .albumGain : (trackGain != nil ? .trackGain : nil) case .preferTrackGain: - replayGainDB = computedTrackGain ?? computedAlbumGain + + replayGainDB = trackGain ?? albumGain + appliedGainType = trackGain != nil ? .trackGain : (albumGain != nil ? .albumGain : nil) case .trackGainOnly: - replayGainDB = computedTrackGain + + replayGainDB = trackGain + appliedGainType = trackGain != nil ? .trackGain : nil } node.replayGain = replayGainDB ?? 0 } - var appliedGain: Float { - node.replayGain + var appliedGain: Float? { + self.replayGain == nil ? nil : node.replayGain } + private(set) var appliedGainType: ReplayGainType? = nil + var effectiveGain: Float { node.globalGain } diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift index 24982ca3a..ab6292d70 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegate.swift @@ -10,8 +10,6 @@ import Foundation -// TODO: Caching of ReplayGain scan data - class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUnitDelegateProtocol { static let cache: ConcurrentMap = ConcurrentMap() @@ -19,7 +17,11 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni var dataSource: ReplayGainDataSource { get {unit.dataSource} - set {unit.dataSource = newValue} + + set { + unit.dataSource = newValue + applyReplayGain(forTrack: playbackInfoDelegate.playingTrack) + } } var maxPeakLevel: ReplayGainMaxPeakLevel { @@ -31,6 +33,11 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni var mode: ReplayGainMode { get {unit.mode} + + // TODO: When the mode changes, if effective dataSource == .analysis and + // changing from album gain to track gain, need to perform a scan + + // TODO: If dataSource == analysis and new mode is albumGain, perform an album scan set {unit.mode = newValue} } @@ -50,10 +57,14 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni unit.replayGain = replayGain } - var appliedGain: Float { + var appliedGain: Float? { unit.appliedGain } + var appliedGainType: ReplayGainType? { + unit.appliedGainType + } + var hasAppliedGain: Bool { unit.replayGain != nil } @@ -84,7 +95,6 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni // Has metadata unit.replayGain = replayGain - print("Found RG metadata: \(replayGain.trackGain ?? -100) for \(theTrack)") } else { @@ -93,17 +103,12 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni // Analyze analyze(file: theTrack.file) - print("No RG metadata for \(theTrack), analyzing ...") } case .metadataOnly: - - print("Applying RG metadata: \(theTrack.replayGain?.trackGain ?? -100) for \(theTrack)") unit.replayGain = theTrack.replayGain case .analysisOnly: - - print("Analyzing \(theTrack)") analyze(file: theTrack.file) } } @@ -128,7 +133,7 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate, ReplayGainUni } catch { _isScanning.setFalse() - print("Scan failed: \(error.localizedDescription)") + Messenger.publish(.Effects.ReplayGainUnit.scanCompleted) } } } diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift index 860e9d5a6..6a6bb7386 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitDelegateProtocol.swift @@ -22,7 +22,9 @@ protocol ReplayGainUnitDelegateProtocol: EffectsUnitDelegateProtocol { var preventClipping: Bool {get set} - var appliedGain: Float {get} + var appliedGain: Float? {get} + + var appliedGainType: ReplayGainType? {get} var dataSource: ReplayGainDataSource {get set} diff --git a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift index d34c4190f..dd4910754 100644 --- a/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift +++ b/Source/Core/AudioGraph/EffectsUnits/ReplayGain/ReplayGainUnitProtocol.swift @@ -22,7 +22,9 @@ protocol ReplayGainUnitProtocol: EffectsUnitProtocol { var preventClipping: Bool {get set} - var appliedGain: Float {get} + var appliedGain: Float? {get} + + var appliedGainType: ReplayGainType? {get} var effectiveGain: Float {get} @@ -63,6 +65,15 @@ enum ReplayGainMode: Int, Codable { } } +enum ReplayGainType { + + case albumGain, trackGain + + var description: String { + self == .albumGain ? "Album gain" : "Track gain" + } +} + enum ReplayGainDataSource: Int, Codable { case metadataOrAnalysis, metadataOnly, analysisOnly diff --git a/Source/Core/FFmpeg/Utils/FFmpegDecoder.swift b/Source/Core/FFmpeg/Utils/FFmpegDecoder.swift index e6eed3dff..dd88aaf03 100644 --- a/Source/Core/FFmpeg/Utils/FFmpegDecoder.swift +++ b/Source/Core/FFmpeg/Utils/FFmpegDecoder.swift @@ -102,7 +102,7 @@ class FFmpegDecoder { self.fileCtx = fileContext guard let theAudioStream = fileContext.bestAudioStream else { - throw FormatContextInitializationError(description: "\nUnable to find audio stream in file: '\(fileContext.filePath)'") + throw FormatContextInitializationError(description: "Unable to find audio stream in file: '\(fileContext.filePath)'") } self.stream = theAudioStream diff --git a/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift b/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift index 69ad76bf8..6b2b674f1 100644 --- a/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift +++ b/Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift @@ -69,13 +69,7 @@ class ReplayGainUnitViewController: EffectsUnitViewController { preAmpSlider.floatValue = replayGainUnit.preAmp if !replayGainUnit.isScanning { - - if replayGainUnit.hasAppliedGain { - lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" - - } else { - lblGain.stringValue = "" - } + updateGainLabel() } lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB" @@ -101,8 +95,8 @@ class ReplayGainUnitViewController: EffectsUnitViewController { private func updateGainLabel() { - if replayGainUnit.hasAppliedGain { - lblGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))" + if let appliedGain = replayGainUnit.appliedGain, let appliedGainType = replayGainUnit.appliedGainType { + lblGain.stringValue = "\(String(format: "%.2f", appliedGain)) dB (\(appliedGainType.description))" } else { lblGain.stringValue = "" @@ -215,7 +209,9 @@ extension ReplayGainUnitViewController { } @IBAction func dataSourceAction(_ sender: NSMenuItem) { + replayGainUnit.dataSource = .init(rawValue: sender.tag) ?? .metadataOrAnalysis + updateGainLabel() } @IBAction func maxPeakLevelAction(_ sender: NSMenuItem) { diff --git a/Source/UI/Waveform/RenderOperation/Decoders/FFmpegWaveformDecoder.swift b/Source/UI/Waveform/RenderOperation/Decoders/FFmpegWaveformDecoder.swift index 71fa901e5..3e77816ea 100644 --- a/Source/UI/Waveform/RenderOperation/Decoders/FFmpegWaveformDecoder.swift +++ b/Source/UI/Waveform/RenderOperation/Decoders/FFmpegWaveformDecoder.swift @@ -54,7 +54,7 @@ class FFmpegWaveformDecoder: WaveformDecoderProtocol { self.fileCtx = try FFmpegFileContext(for: file) guard let theAudioStream = fileCtx.bestAudioStream else { - throw FormatContextInitializationError(description: "\nUnable to find audio stream in file: '\(fileCtx.filePath)'") + throw FormatContextInitializationError(description: "Unable to find audio stream in file: '\(fileCtx.filePath)'") } self.stream = theAudioStream