1 #include "analyzer/analyzerebur128.h"
2 
3 #include <QtDebug>
4 
5 #include "track/track.h"
6 #include "util/math.h"
7 #include "util/sample.h"
8 #include "util/timer.h"
9 
10 namespace {
11 const double kReplayGain2ReferenceLUFS = -18;
12 } // anonymous namespace
13 
AnalyzerEbur128(UserSettingsPointer pConfig)14 AnalyzerEbur128::AnalyzerEbur128(UserSettingsPointer pConfig)
15         : m_rgSettings(pConfig),
16           m_pState(nullptr) {
17 }
18 
~AnalyzerEbur128()19 AnalyzerEbur128::~AnalyzerEbur128() {
20     cleanup(); // ...to prevent memory leaks
21 }
22 
initialize(TrackPointer tio,int sampleRate,int totalSamples)23 bool AnalyzerEbur128::initialize(TrackPointer tio,
24         int sampleRate,
25         int totalSamples) {
26     if (m_rgSettings.isAnalyzerDisabled(2, tio) || totalSamples == 0) {
27         qDebug() << "Skipping AnalyzerEbur128";
28         return false;
29     }
30     DEBUG_ASSERT(m_pState == nullptr);
31     m_pState = ebur128_init(2u,
32             static_cast<unsigned long>(sampleRate),
33             EBUR128_MODE_I);
34     return m_pState != nullptr;
35 }
36 
cleanup()37 void AnalyzerEbur128::cleanup() {
38     if (m_pState) {
39         ebur128_destroy(&m_pState);
40         // ebur128_destroy clears the pointer but let's not rely on that.
41         m_pState = nullptr;
42     }
43 }
44 
processSamples(const CSAMPLE * pIn,const int iLen)45 bool AnalyzerEbur128::processSamples(const CSAMPLE *pIn, const int iLen) {
46     VERIFY_OR_DEBUG_ASSERT(m_pState) {
47         return false;
48     }
49     ScopedTimer t("AnalyzerEbur128::processSamples()");
50     size_t frames = iLen / 2;
51     int e = ebur128_add_frames_float(m_pState, pIn, frames);
52     VERIFY_OR_DEBUG_ASSERT(e == EBUR128_SUCCESS) {
53         qWarning() << "AnalyzerEbur128::processSamples() failed with" << e;
54         return false;
55     }
56     return true;
57 }
58 
storeResults(TrackPointer tio)59 void AnalyzerEbur128::storeResults(TrackPointer tio) {
60     VERIFY_OR_DEBUG_ASSERT(m_pState) {
61         return;
62     }
63     double averageLufs;
64     int e = ebur128_loudness_global(m_pState, &averageLufs);
65     VERIFY_OR_DEBUG_ASSERT(e == EBUR128_SUCCESS) {
66         qWarning() << "AnalyzerEbur128::storeResults() failed with" << e;
67         return;
68     }
69     if (averageLufs == -HUGE_VAL || averageLufs == 0.0) {
70         qWarning() << "AnalyzerEbur128::storeResults() averageLufs invalid:"
71                    << averageLufs;
72         return;
73     }
74 
75     const double fReplayGain2 = kReplayGain2ReferenceLUFS - averageLufs;
76     mixxx::ReplayGain replayGain(tio->getReplayGain());
77     replayGain.setRatio(db2ratio(fReplayGain2));
78     tio->setReplayGain(replayGain);
79     qDebug() << "ReplayGain 2.0 (libebur128) result is" << fReplayGain2 << "dB for" << tio->getFileInfo();
80 }
81