1 #pragma once
2 
3 #include "util/types.h"
4 
5 namespace mixxx {
6 
7 // DTO for storing replay gain information.
8 //
9 // Parsing & Formatting
10 // --------------------
11 // This class includes functions for formatting and parsing replay gain
12 // metadata according to the ReplayGain 1.0/2.0 specification:
13 // http://wiki.hydrogenaud.io/index.php?title=ReplayGain_1.0_specification
14 // http://wiki.hydrogenaud.io/index.php?title=ReplayGain_2.0_specification
15 //
16 // Normalization
17 // -------------
18 // Formatting a floating point value as a string and parsing it later
19 // might cause rounding errors. In order to avoid "jittering" caused
20 // by subsequently formatting and parsing a floating point value the
21 // ratio and peak values need to be normalized before writing them
22 // as a string into file tags.
23 class ReplayGain final {
24 public:
25     static constexpr double kRatioUndefined = 0.0;
26     static constexpr double kRatioMin = 0.0; // lower bound (exclusive)
27     static constexpr double kRatio0dB = 1.0;
28 
29     static constexpr CSAMPLE kPeakUndefined = -CSAMPLE_PEAK;
30     static constexpr CSAMPLE kPeakMin = CSAMPLE_ZERO; // lower bound (inclusive)
31     static constexpr CSAMPLE kPeakClip = CSAMPLE_PEAK; // upper bound (inclusive) represents digital full scale without clipping
32 
33     static_assert(ReplayGain::kPeakClip == 1.0,
34             "http://wiki.hydrogenaud.io/index.php"
35             "?title=ReplayGain_2.0_specification#Peak_amplitude: "
36             "The maximum peak amplitude value is stored as a floating number, "
37             "where 1.0 represents digital full scale");
38 
ReplayGain()39     ReplayGain()
40         : ReplayGain(kRatioUndefined, kPeakUndefined) {
41     }
ReplayGain(double ratio,CSAMPLE peak)42     ReplayGain(double ratio, CSAMPLE peak)
43         : m_ratio(ratio)
44         , m_peak(peak) {
45     }
46 
isValidRatio(double ratio)47     static bool isValidRatio(double ratio) {
48         return kRatioMin < ratio;
49     }
hasRatio()50     bool hasRatio() const {
51         return isValidRatio(m_ratio);
52     }
getRatio()53     double getRatio() const {
54         return m_ratio;
55     }
setRatio(double ratio)56     void setRatio(double ratio) {
57         m_ratio = ratio;
58     }
resetRatio()59     void resetRatio() {
60         m_ratio = kRatioUndefined;
61     }
62 
63     // Parsing and formatting of gain values according to the
64     // ReplayGain 1.0/2.0 specification.
65     static double ratioFromString(const QString& dBGain, bool* pValid = 0);
66     static QString ratioToString(double ratio);
67 
68     static double normalizeRatio(double ratio);
69 
70     // The peak amplitude of the track or signal.
isValidPeak(CSAMPLE peak)71     static bool isValidPeak(CSAMPLE peak) {
72         return kPeakMin <= peak;
73     }
hasPeak()74     bool hasPeak() const {
75         return isValidPeak(m_peak);
76     }
getPeak()77     CSAMPLE getPeak() const {
78         return m_peak;
79     }
setPeak(CSAMPLE peak)80     void setPeak(CSAMPLE peak) {
81         m_peak = peak;
82     }
resetPeak()83     void resetPeak() {
84         m_peak = kPeakUndefined;
85     }
86 
87     // Parsing and formatting of peak amplitude values according to
88     // the ReplayGain 1.0/2.0 specification.
89     static CSAMPLE peakFromString(const QString& strPeak, bool* pValid = 0);
90     static QString peakToString(CSAMPLE peak);
91 
92     static CSAMPLE normalizePeak(CSAMPLE peak);
93 
94     // Adjusts floating-point values to match their string representation
95     // in file tags to account for rounding errors.
normalizeBeforeExport()96     void normalizeBeforeExport() {
97         m_ratio = normalizeRatio(m_ratio);
98         m_peak = normalizePeak(m_peak);
99     }
100 
101 private:
102     double m_ratio;
103     CSAMPLE m_peak;
104 };
105 
106 inline
107 bool operator==(const ReplayGain& lhs, const ReplayGain& rhs) {
108     return (lhs.getRatio() == rhs.getRatio()) && (lhs.getPeak() == rhs.getPeak());
109 }
110 
111 inline
112 bool operator!=(const ReplayGain& lhs, const ReplayGain& rhs) {
113     return !(lhs == rhs);
114 }
115 
116 inline
117 QDebug operator<<(QDebug dbg, const ReplayGain& arg) {
118     return dbg << "ratio =" << arg.getRatio() << "/" << "peak =" << arg.getPeak();
119 }
120 
121 }
122 
123 Q_DECLARE_TYPEINFO(mixxx::ReplayGain, Q_MOVABLE_TYPE);
124 Q_DECLARE_METATYPE(mixxx::ReplayGain)
125