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