-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathloudness.cpp
More file actions
130 lines (114 loc) · 2.94 KB
/
loudness.cpp
File metadata and controls
130 lines (114 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <memory>
#include <ebur128.h>
#include "errors.hpp"
#include "loudness.hpp"
namespace flacsplit::replaygain {
class Analyzer::Internal {
public:
Internal(unsigned num_channels, unsigned long freq) {
_state = ebur128_init(
num_channels, freq, EBUR128_MODE_I | EBUR128_MODE_TRUE_PEAK
);
if (!_state) {
flacsplit::throw_traced(std::runtime_error(
"ebur128_init failed"
));
}
}
~Internal() {
ebur128_destroy(&_state);
}
ebur128_state *_state;
};
std::string
Ebur128_error::message(int errnum) {
switch (error(errnum)) {
case EBUR128_SUCCESS:
return "success";
case EBUR128_ERROR_NOMEM:
return "no memory";
case EBUR128_ERROR_INVALID_MODE:
return "invalid mode";
case EBUR128_ERROR_INVALID_CHANNEL_INDEX:
return "invalid channel index";
case EBUR128_ERROR_NO_CHANGE:
return "no change";
}
return "unknown";
}
Analyzer::Analyzer(unsigned num_channels, unsigned long freq) :
_internal(new Internal(num_channels, freq))
{}
Analyzer::~Analyzer() {
if (_internal)
delete _internal;
}
void
Analyzer::reset(unsigned num_channels, unsigned long freq) {
int err = ebur128_change_parameters(
_internal->_state, num_channels, freq
);
if (err)
flacsplit::throw_traced(Ebur128_error(err));
}
void
Analyzer::add(const double *left_samples, const double *right_samples,
size_t num_samples) {
if (!right_samples) {
int err = ebur128_add_frames_double(
_internal->_state, left_samples, num_samples
);
if (err)
flacsplit::throw_traced(Ebur128_error(err));
}
// Samples need to be interleaved, sadly.
auto merged = std::make_unique<double[]>(num_samples * 2);
auto pos = merged.get();
for (size_t i = 0; i < num_samples; i++) {
*pos++ = *left_samples++;
*pos++ = *right_samples++;
}
int err = ebur128_add_frames_double(
_internal->_state, merged.get(), num_samples
);
if (err)
flacsplit::throw_traced(Ebur128_error(err));
}
double
Analyzer::gain() {
double result;
int err = ebur128_loudness_global(_internal->_state, &result);
if (err)
flacsplit::throw_traced(Ebur128_error(err));
return EBUR128_REFERENCE - result;
}
double
Analyzer::peak() {
double left, right;
int err;
if ((err = ebur128_true_peak(_internal->_state, 0, &left)))
flacsplit::throw_traced(Ebur128_error(err));
else if ((err = ebur128_true_peak(_internal->_state, 1, &right)))
flacsplit::throw_traced(Ebur128_error(err));
return std::max(left, right);
}
double
Analyzer::gain_multiple(std::vector<Analyzer> &vec) {
auto states = std::make_unique<ebur128_state *[]>(vec.size());
int end = 0;
for (auto &state : vec)
states[end++] = state._internal->_state;
double result;
int err = ebur128_loudness_global_multiple(states.get(), end, &result);
if (err)
flacsplit::throw_traced(Ebur128_error(err));
return EBUR128_REFERENCE - result;
}
double
Analyzer::peak_multiple(std::vector<Analyzer> &vec) {
double result = 0.0;
for (auto &state : vec)
result = std::max(result, state.peak());
return result;
}
}