Skip to content

Commit

Permalink
#12 Integrated loudness scanning
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-venugopal committed Aug 19, 2024
1 parent 62f37fc commit f9fb311
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 35 deletions.
4 changes: 4 additions & 0 deletions Aural.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@
3E772EC12C73F1B900DC3137 /* FFmpegReplayGainScanner+Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E772EBD2C73F1B900DC3137 /* FFmpegReplayGainScanner+Double.swift */; };
3E772EC22C73F1B900DC3137 /* FFmpegReplayGainScanner+Int32.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E772EBE2C73F1B900DC3137 /* FFmpegReplayGainScanner+Int32.swift */; };
3E772EC32C73F1B900DC3137 /* FFmpegReplayGainScanner+Float.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E772EBF2C73F1B900DC3137 /* FFmpegReplayGainScanner+Float.swift */; };
3E772EC52C73F80D00DC3137 /* ReplayGainScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E772EC42C73F80C00DC3137 /* ReplayGainScanner.swift */; };
3E7AF10528065441006DC98F /* PlayQueueMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E7AF10428065441006DC98F /* PlayQueueMenuController.swift */; };
3E7C35FE26AADD2A00B3552D /* FilterPresetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E7C35FD26AADD2A00B3552D /* FilterPresetView.swift */; };
3E7C360126AADFA000B3552D /* FilterBandView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E7C360026AADFA000B3552D /* FilterBandView.swift */; };
Expand Down Expand Up @@ -1916,6 +1917,7 @@
3E772EBD2C73F1B900DC3137 /* FFmpegReplayGainScanner+Double.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FFmpegReplayGainScanner+Double.swift"; sourceTree = "<group>"; };
3E772EBE2C73F1B900DC3137 /* FFmpegReplayGainScanner+Int32.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FFmpegReplayGainScanner+Int32.swift"; sourceTree = "<group>"; };
3E772EBF2C73F1B900DC3137 /* FFmpegReplayGainScanner+Float.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "FFmpegReplayGainScanner+Float.swift"; sourceTree = "<group>"; };
3E772EC42C73F80C00DC3137 /* ReplayGainScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReplayGainScanner.swift; sourceTree = "<group>"; };
3E7AF10428065441006DC98F /* PlayQueueMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayQueueMenuController.swift; sourceTree = "<group>"; };
3E7C35FD26AADD2A00B3552D /* FilterPresetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterPresetView.swift; sourceTree = "<group>"; };
3E7C360026AADFA000B3552D /* FilterBandView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterBandView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5366,6 +5368,7 @@
3EE882782C72A04D00E270B8 /* LoudnessScan */ = {
isa = PBXGroup;
children = (
3E772EC42C73F80C00DC3137 /* ReplayGainScanner.swift */,
3EE882792C72A0DF00E270B8 /* AVFReplayGainScanner.swift */,
3EE8827A2C72A0DF00E270B8 /* FFmpegReplayGainScanner.swift */,
3E772EBD2C73F1B900DC3137 /* FFmpegReplayGainScanner+Double.swift */,
Expand Down Expand Up @@ -5954,6 +5957,7 @@
3E0217E52C23490E00865AC2 /* DelayUnitDelegate.swift in Sources */,
3E02181E2C23490E00865AC2 /* Bookmark.swift in Sources */,
3E6C12AE25CEBE2700BF0D07 /* DialogsAndAlerts.swift in Sources */,
3E772EC52C73F80D00DC3137 /* ReplayGainScanner.swift in Sources */,
3EF172F526A989BA00BB4556 /* EffectsUnitLabels.swift in Sources */,
3E0219812C23490E00865AC2 /* EnumExtensions.swift in Sources */,
3E0218462C23490E00865AC2 /* FFmpegResamplingContext.swift in Sources */,
Expand Down
Binary file not shown.
10 changes: 8 additions & 2 deletions Source/Core/AudioGraph/AudioGraphDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,9 +275,15 @@ class AudioGraphDelegate: AudioGraphDelegateProtocol {
}

// Replay gain ------------------------------------------------------------
print("Applying replay gain: \(newTrack?.playbackContext?.replayGain?.trackGain) for new track: \(newTrack)")
replayGainUnit.applyGain(newTrack?.playbackContext?.replayGain)

// TODO: Only do this if using metadata
// print("Applying replay gain: \(newTrack?.playbackContext?.replayGain?.trackGain) for new track: \(newTrack)")
// replayGainUnit.applyGain(newTrack?.playbackContext?.replayGain)
replayGainUnit.applyGain(nil)

if let newTrackFile = newTrack?.file {
replayGainUnit.initiateScan(forFile: newTrackFile)
}
}

@inline(__always)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import AVFoundation

class AVFReplayGainScanner: ReplayGainScanner {
class AVFReplayGainScanner: EBUR128LoudnessScannerProtocol {

let file: URL

Expand Down Expand Up @@ -50,17 +50,12 @@ class AVFReplayGainScanner: ReplayGainScanner {
channelLayout: channelLayout)
}

func scan(_ completionHandler: @escaping (EBUR128AnalysisResult) -> Void) {
func scan(_ completionHandler: @escaping (EBUR128AnalysisResult?) -> Void) {

DispatchQueue.global(qos: .userInitiated).async {

do {

if let result: EBUR128AnalysisResult = try self.doScan() {
completionHandler(result)
} else {
print("No result")
}
completionHandler(try self.doScan())

} catch let err as EBUR128Error {
print("Error: \(err.description)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@

import Foundation

protocol ReplayGainScanner {

var file: URL {get}

func scan(_ completionHandler: @escaping (EBUR128AnalysisResult) -> Void)
}

class FFmpegReplayGainScanner: ReplayGainScanner {
class FFmpegReplayGainScanner: EBUR128LoudnessScannerProtocol {

let file: URL

Expand Down Expand Up @@ -72,13 +65,13 @@ class FFmpegReplayGainScanner: ReplayGainScanner {
}
}

func scan(_ completionHandler: @escaping (EBUR128AnalysisResult) -> Void) {
func scan(_ completionHandler: @escaping (EBUR128AnalysisResult?) -> Void) {

DispatchQueue.global(qos: .userInitiated).async {

do {

var result: EBUR128AnalysisResult?
var result: EBUR128AnalysisResult? = nil

switch self.targetFormat {

Expand All @@ -98,11 +91,7 @@ class FFmpegReplayGainScanner: ReplayGainScanner {
break
}

if let theResult = result {
completionHandler(theResult)
} else {
print("No result")
}
completionHandler(result)

} catch let err as EBUR128Error {
print("Error: \(err.description)")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// ReplayGainScanner.swift
// Aural
//
// Copyright © 2021 Kartik Venugopal. All rights reserved.
//
// This software is licensed under the MIT software license.
// See the file "LICENSE" in the project root directory for license terms.
//

import Foundation

protocol EBUR128LoudnessScannerProtocol {

var file: URL {get}

func scan(_ completionHandler: @escaping (EBUR128AnalysisResult?) -> Void)
}

class ReplayGainScanner {

let file: URL
let scanner: EBUR128LoudnessScannerProtocol

init(file: URL) throws {

self.file = file
scanner = file.isNativelySupported ? try AVFReplayGainScanner(file: file) : try FFmpegReplayGainScanner(file: file)
}

func scan(_ completionHandler: @escaping (ReplayGain?) -> Void) {

lazy var filePath = file.path

scanner.scan {ebur128Result in

if let theResult = ebur128Result {
completionHandler(ReplayGain(ebur128AnalysisResult: theResult))
} else {
completionHandler(nil)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,19 @@ class ReplayGainUnit: EffectsUnit, ReplayGainUnitProtocol {

private func parmsChanged() {

let replayGainDB = mode == .preferTrackGain ?
(replayGain?.trackGain ?? replayGain?.albumGain) :
(replayGain?.albumGain ?? replayGain?.trackGain)
let replayGainDB: Float?

switch mode {

case .preferAlbumGain:
replayGainDB = replayGain?.albumGain ?? replayGain?.trackGain

case .preferTrackGain:
replayGainDB = replayGain?.trackGain ?? replayGain?.albumGain

case .trackGainOnly:
replayGainDB = replayGain?.trackGain
}

node.replayGain = replayGainDB ?? 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,33 @@ class ReplayGainUnitDelegate: EffectsUnitDelegate<ReplayGainUnit>, ReplayGainUni
var effectiveGain: Float {
unit.effectiveGain
}

var isScanning: Bool {_isScanning.value}
private var _isScanning: AtomicBool = AtomicBool(value: false)

func initiateScan(forFile file: URL) {

do {

let scanner = try ReplayGainScanner(file: file)

_isScanning.setTrue()
Messenger.publish(.Effects.ReplayGainUnit.scanInitiated)

scanner.scan {[weak self] replayGain in

guard let strongSelf = self else {return}

strongSelf.unit.replayGain = replayGain
strongSelf._isScanning.setFalse()

Messenger.publish(.Effects.ReplayGainUnit.scanCompleted)
}

} catch {

_isScanning.setFalse()
print("Scan failed: \(error.localizedDescription)")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ protocol ReplayGainUnitDelegateProtocol: EffectsUnitDelegateProtocol {
var hasAppliedGain: Bool {get}

var effectiveGain: Float {get}

func initiateScan(forFile file: URL)

var isScanning: Bool {get}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,11 @@ extension Notification.Name {
// Notifies the filter unit that one of its bands' bypass state has been updated. Payload includes the band index.
static let bandBypassStateUpdated = Notification.Name("effects_filterUnit_bandBypassStateUpdated")
}

struct ReplayGainUnit {

static let scanInitiated = Notification.Name("effects_replayGainUnit_scanInitiated")
static let scanCompleted = Notification.Name("effects_replayGainUnit_scanCompleted")
}
}
}
29 changes: 22 additions & 7 deletions Source/UI/Effects/ReplayGain/ReplayGainUnitViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ class ReplayGainUnitViewController: EffectsUnitViewController {

preAmpSlider.floatValue = replayGainUnit.preAmp

if replayGainUnit.hasAppliedGain {
lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))"
if !replayGainUnit.isScanning {

} else {
lblAppliedGain.stringValue = "<None>"
if replayGainUnit.hasAppliedGain {
lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))"

} else {
lblAppliedGain.stringValue = "<None>"
}
}

lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB"
Expand All @@ -77,14 +80,16 @@ class ReplayGainUnitViewController: EffectsUnitViewController {
override func initSubscriptions() {

super.initSubscriptions()

messenger.subscribeAsync(to: .Player.trackTransitioned, handler: initControls)

messenger.subscribeAsync(to: .Effects.ReplayGainUnit.scanInitiated, handler: scanInitiated)
messenger.subscribeAsync(to: .Effects.ReplayGainUnit.scanCompleted, handler: scanCompleted)
}

@IBAction func modeAction(_ sender: NSPopUpButton) {

guard let modeDescription = sender.titleOfSelectedItem else {return}

replayGainUnit.mode = .init(rawValue: sender.tag) ?? .defaultMode
replayGainUnit.mode = .init(rawValue: sender.selectedTag()) ?? .defaultMode

if replayGainUnit.hasAppliedGain {
lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))"
Expand All @@ -104,4 +109,14 @@ class ReplayGainUnitViewController: EffectsUnitViewController {
lblPreAmp.stringValue = "\(String(format: "%.2f", replayGainUnit.preAmp)) dB"
lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB"
}

private func scanInitiated() {
lblAppliedGain.stringValue = "Analyzing file loudness ..."
}

private func scanCompleted() {

lblAppliedGain.stringValue = "\(String(format: "%.2f", replayGainUnit.appliedGain)) dB (\(replayGainUnit.mode.description))"
lblTotalGain.stringValue = "\(String(format: "%.2f", replayGainUnit.effectiveGain)) dB"
}
}

0 comments on commit f9fb311

Please sign in to comment.