tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_LevelMeasurer.cpp
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14//==============================================================================
15template <typename FloatType>
16static void getSumAndDiff (const juce::AudioBuffer<FloatType>& buffer,
17 FloatType& sum, FloatType& diff,
18 int startIndex, int numSamples)
19{
20 if (buffer.getNumChannels() == 0)
21 {
22 sum = 0;
23 diff = 0;
24 }
25 else
26 {
27 FloatType s = 0;
28 FloatType lo = (FloatType) 1;
29 FloatType hi = 0;
30
31 for (int i = buffer.getNumChannels(); --i >= 0;)
32 {
33 auto mag = buffer.getMagnitude (i, startIndex, numSamples);
34 s += mag;
35 lo = juce::jmin (lo, mag);
36 hi = juce::jmax (hi, mag);
37 }
38
39 sum = s / buffer.getNumChannels();
40 diff = juce::jmax (FloatType(), hi - lo);
41 }
42}
43
44//==============================================================================
45LevelMeasurer::LevelMeasurer()
46{
47 clear();
48}
49
50LevelMeasurer::~LevelMeasurer()
51{
52 TRACKTION_ASSERT_MESSAGE_THREAD
53}
54
55//==============================================================================
56int LevelMeasurer::Client::getNumChannelsUsed() const noexcept
57{
58 return numChannelsUsed;
59}
60
61void LevelMeasurer::Client::reset() noexcept
62{
64 for (auto& l : audioLevels)
65 l = {};
66
67 for (auto& o : overload)
68 o = false;
69
70 midiLevels = {};
71 clearOverload = true;
72}
73
74bool LevelMeasurer::Client::getAndClearOverload() noexcept
75{
77 auto result = clearOverload;
78 clearOverload = false;
79 return result;
80}
81
82bool LevelMeasurer::Client::getAndClearPeak() noexcept
83{
85 auto result = clearPeak;
86 clearPeak = false;
87 return result;
88}
89
90DbTimePair LevelMeasurer::Client::getAndClearMidiLevel() noexcept
91{
93 auto result = midiLevels;
94 midiLevels.dB = -100.0f;
95 return result;
96}
97
98DbTimePair LevelMeasurer::Client::getAndClearAudioLevel (int chan) noexcept
99{
101 jassert (chan >= 0 && chan < maxNumChannels);
102 auto result = audioLevels[chan];
103 audioLevels[chan].dB = -100.0f;
104 return result;
105}
106
107void LevelMeasurer::Client::setNumChannelsUsed (int numChannels) noexcept
108{
110 numChannelsUsed = numChannels;
111}
112
113void LevelMeasurer::Client::setOverload (int channel, bool hasOverloaded) noexcept
114{
116 overload[channel] = hasOverloaded;
117}
118
119void LevelMeasurer::Client::setClearOverload (bool clear) noexcept
120{
122 clearOverload = clear;
123}
124
125void LevelMeasurer::Client::setClearPeak (bool clear) noexcept
126{
128 clearPeak = clear;
129}
130
131void LevelMeasurer::Client::updateAudioLevel (int channel, DbTimePair newAudioLevel) noexcept
132{
134
135 if (newAudioLevel.dB >= audioLevels[channel].dB)
136 audioLevels[channel] = newAudioLevel;
137}
138
139void LevelMeasurer::Client::updateMidiLevel (DbTimePair newMidiLevel) noexcept
140{
142
143 if (newMidiLevel.dB >= midiLevels.dB)
144 midiLevels = newMidiLevel;
145}
146
147
148//==============================================================================
149void LevelMeasurer::processBuffer (juce::AudioBuffer<float>& buffer, int start, int numSamples)
150{
151 const std::scoped_lock sl (clientsMutex);
152
153 if (clients.isEmpty())
154 return;
155
156 auto numChans = std::min ((int) Client::maxNumChannels, buffer.getNumChannels());
157 numActiveChannels = numChans;
159
160 if (mode == LevelMeasurer::peakMode)
161 {
162 // peak mode
163 for (int i = numChans; --i >= 0;)
164 {
165 auto gain = buffer.getMagnitude (i, start, numSamples);
166 bool overloaded = gain > 0.999f;
167 auto newDB = gainToDb (gain);
168
169 for (auto c : clients)
170 {
171 c->updateAudioLevel (i, { now, newDB });
172
173 if (overloaded)
174 c->setOverload (i, true);
175
176 c->setNumChannelsUsed (numChans);
177 }
178 }
179 }
180 else if (mode == LevelMeasurer::RMSMode)
181 {
182 // rms mode
183 for (int i = numChans; --i >= 0;)
184 {
185 auto gain = buffer.getRMSLevel (i, start, numSamples);
186 bool overloaded = gain > 0.999f;
187 auto newDB = gainToDb (gain);
188
189 for (auto c : clients)
190 {
191 c->updateAudioLevel (i, { now, newDB });
192
193 if (overloaded)
194 c->setOverload (i, true);
195
196 c->setNumChannelsUsed (numChans);
197 }
198 }
199 }
200 else
201 {
202 // sum + diff
203 float sum, diff;
204 getSumAndDiff (buffer, sum, diff, start, numSamples);
205
206 auto sumDB = gainToDb (sum);
207 auto diffDB = gainToDb (diff);
208
209 for (auto c : clients)
210 {
211 c->updateAudioLevel (0, { now, sumDB });
212 c->updateAudioLevel (1, { now, diffDB });
213
214 if (sum > 0.999f) c->setOverload (0, true);
215 if (diff > 0.999f) c->setOverload (1, true);
216
217 c->setNumChannelsUsed (2);
218 }
219
220 numActiveChannels = 2;
221 }
222}
223
224void LevelMeasurer::processMidi (MidiMessageArray& midiBuffer, const float*)
225{
226 const std::scoped_lock sl (clientsMutex);
227
228 if (clients.isEmpty() || ! showMidi)
229 return;
230
231 float max = 0.0f;
232
233 for (auto& m : midiBuffer)
234 if (m.isNoteOn())
235 max = juce::jmax (max, m.getFloatVelocity());
236
238
239 for (auto c : clients)
240 c->updateMidiLevel ({ now, gainToDb (max) });
241}
242
243void LevelMeasurer::processMidiLevel (float level)
244{
245 const std::scoped_lock sl (clientsMutex);
246
247 if (clients.isEmpty() || ! showMidi)
248 return;
249
251
252 for (auto c : clients)
253 c->updateMidiLevel ({ now, gainToDb (level) });
254}
255
256void LevelMeasurer::clearOverload()
257{
258 const std::scoped_lock sl (clientsMutex);
259
260 for (auto c : clients)
261 c->setClearOverload (true);
262}
263
264void LevelMeasurer::clearPeak()
265{
266 const std::scoped_lock sl (clientsMutex);
267
268 for (auto c : clients)
269 c->setClearPeak (true);
270}
271
272void LevelMeasurer::clear()
273{
274 const std::scoped_lock sl (clientsMutex);
275
276 for (auto c : clients)
277 c->reset();
278
279 levelCacheL = -100.0f;
280 levelCacheR = -100.0f;
281 numActiveChannels = 1;
282}
283
284void LevelMeasurer::setMode (LevelMeasurer::Mode m)
285{
286 clear();
287 mode = m;
288}
289
290void LevelMeasurer::addClient (Client& c)
291{
292 const std::scoped_lock sl (clientsMutex);
293 jassert (! clients.contains (&c));
294 clients.add (&c);
295}
296
297void LevelMeasurer::removeClient (Client& c)
298{
299 const std::scoped_lock sl (clientsMutex);
300 clients.removeFirstMatchingValue (&c);
301}
302
303void LevelMeasurer::setShowMidi (bool show)
304{
305 showMidi = show;
306}
307
308//==============================================================================
309void SharedLevelMeasurer::startNextBlock (double streamTime)
310{
312
313 if (streamTime != lastStreamTime)
314 {
315 lastStreamTime = streamTime;
316
317 processBuffer (sumBuffer, 0, sumBuffer.getNumSamples());
318 sumBuffer.clear();
319 }
320}
321
322void SharedLevelMeasurer::setSize (int channels, int numSamples)
323{
325
326 if (channels > sumBuffer.getNumChannels() || numSamples > sumBuffer.getNumSamples())
327 sumBuffer.setSize (channels, numSamples);
328}
329
330void SharedLevelMeasurer::addBuffer (const juce::AudioBuffer<float>& inBuffer, int startSample, int numSamples)
331{
332 setSize (2, numSamples);
333
335
336 for (int i = 0; i < juce::jmin (sumBuffer.getNumChannels(), inBuffer.getNumChannels()); ++i)
337 sumBuffer.addFrom (i, 0, inBuffer, i, startSample, numSamples);
338}
339
340}} // namespace tracktion { inline namespace engine
Type getMagnitude(int channel, int startSample, int numSamples) const noexcept
Type getRMSLevel(int channel, int startSample, int numSamples) const noexcept
int getNumChannels() const noexcept
static uint32 getApproximateMillisecondCounter() noexcept
#define jassert(expression)
T lock(T... args)
T max(T... args)
T min(T... args)
constexpr Type jmin(Type a, Type b)
constexpr Type jmax(Type a, Type b)