Skip to content

Commit 7fad13b

Browse files
committed
FixedTermLoudnessMeter now uses DoubleBuffering for Realtime safety
1 parent 1b49965 commit 7fad13b

5 files changed

Lines changed: 57 additions & 37 deletions

File tree

include/ChannelProcessor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class ChannelProcessor
2727

2828
private:
2929
void initialiseFilters();
30-
30+
3131
float weighting;
3232

3333
Filter filt1;

include/DoubleBuffer.h

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ template<typename T>
1212
class DoubleBuffer
1313
{
1414
public:
15-
DoubleBuffer(const T& initialVal) : buffers{{initialVal}, {initialVal}}, realtimeCopy{initialVal} {}
15+
DoubleBuffer(const T& initialVal) : buffers{initialVal, initialVal}, realtimeCopy{initialVal} {}
1616

1717
~DoubleBuffer()
1818
{
@@ -35,27 +35,27 @@ class DoubleBuffer
3535
control.store((writeIndex & IndexBit) | NewDataBit);
3636
}
3737

38-
T& nonRealtimeRead()
38+
const T& nonRealtimeRead() const
3939
{
4040
int current = control.load();
4141

42-
if(!current & NewDataBit)
42+
if(current & NewDataBit)
4343
{
44-
return;
45-
}
44+
int newValue;
4645

47-
int newValue;
46+
//CAS loop to spin while busy bit is set, then set the new buffer to write to
47+
do
48+
{
49+
current &= ~BusyBit;
50+
newValue = (current ^ IndexBit) & IndexBit;
51+
}
52+
while(!control.compare_exchange_weak(current, newValue));
4853

49-
//CAS loop to spin while busy bit is set, then set the new buffer to write to
50-
do
51-
{
52-
current &= ~BusyBit;
53-
newValue = (comp ^ IndexBit) & IndexBit;
54+
current = newValue;
5455
}
55-
while(!control.compare_exchange_weak(current, newValue));
5656

5757
//Return the last buffer that was written to
58-
return buffers[(newValue & IndexBit) & 0x1]
58+
return buffers[(current & IndexBit) ^ 0x1];
5959
}
6060

6161
//This is not realtime safe with the other calls
@@ -74,9 +74,9 @@ class DoubleBuffer
7474
IndexBit = (1 << 0),
7575
NewDataBit = (1 << 1),
7676
BusyBit = (1 << 2)
77-
}
77+
};
7878

79-
std::atomic<int> control;
79+
mutable std::atomic<int> control;
8080

8181
std::array<T, 2> buffers;
8282
T realtimeCopy;

include/FixedTermLoudnessMeter.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <span>
66

77
#include "LiblufsAPI.h"
8+
#include "DoubleBuffer.h"
89
#include "ChannelProcessor.h"
910

1011
namespace LUFS
@@ -13,7 +14,7 @@ namespace LUFS
1314
class LIBLUFS_API FixedTermLoudnessMeter
1415
{
1516
public:
16-
FixedTermLoudnessMeter(const std::vector<Channel>& channels, const std::chrono::milliseconds& windowLength);
17+
FixedTermLoudnessMeter(const std::vector<Channel>& channelSet, const std::chrono::milliseconds& windowLength);
1718
~FixedTermLoudnessMeter();
1819

1920
//Deinterleaved and interleaved
@@ -23,15 +24,18 @@ class LIBLUFS_API FixedTermLoudnessMeter
2324
//This is not threadsafe with process calls
2425
void reset();
2526

26-
//This is threadsafe with process calls
27+
//This is threadsafe with process calls, but must only be called from a single thread at a time
2728
float getLoudness() const;
2829

2930
private:
3031
int getBlockLengthSamples(const std::chrono::milliseconds& windowLength) const;
3132

33+
std::vector<ChannelProcessor> generateChannelProcessors() const;
34+
35+
const std::vector<Channel> channels;
3236
const int blockLengthSamples;
3337

34-
std::vector<ChannelProcessor> channelProcessors;
38+
DoubleBuffer<std::vector<ChannelProcessor>> channelProcessors;
3539

3640
size_t blockWritePos = 0;
3741

src/ChannelProcessor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace LUFS
44
{
55

6-
ChannelProcessor::ChannelProcessor(float channelWeighting, size_t blockSizeSamples) : weighting(channelWeighting), currentBlockData(blockSizeSamples, 0.0f)
6+
ChannelProcessor::ChannelProcessor(float channelWeighting, size_t blockSizeSamples) : weighting{channelWeighting}, currentBlockData(blockSizeSamples, 0.0f)
77
{
88
initialiseFilters();
99

src/FixedTermLoudnessMeter.cpp

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,9 @@
33
namespace LUFS
44
{
55

6-
FixedTermLoudnessMeter::FixedTermLoudnessMeter(const std::vector<Channel>& channels, const std::chrono::milliseconds& windowLength) : blockLengthSamples{getBlockLengthSamples(windowLength)}
6+
FixedTermLoudnessMeter::FixedTermLoudnessMeter(const std::vector<Channel>& channelSet, const std::chrono::milliseconds& windowLength) : channels{channelSet}, blockLengthSamples{getBlockLengthSamples(windowLength)}, channelProcessors{generateChannelProcessors()}
77
{
8-
std::transform(channels.begin(), channels.end(), std::back_insert_iterator(channelProcessors), [this](const Channel& channel)
9-
{
10-
return ChannelProcessor(channel.getWeighting(), blockLengthSamples);
11-
});
8+
129
}
1310

1411
FixedTermLoudnessMeter::~FixedTermLoudnessMeter()
@@ -18,59 +15,66 @@ FixedTermLoudnessMeter::~FixedTermLoudnessMeter()
1815

1916
void FixedTermLoudnessMeter::process(std::span<const float* const> audio, int numSamplesPerChannel)
2017
{
21-
assert(audio.size() == channelProcessors.size());
18+
std::vector<ChannelProcessor>& processors = channelProcessors.realtimeAquire();
19+
20+
assert(audio.size() == processors.size());
2221

2322
for(size_t sampleIndex = 0; sampleIndex < numSamplesPerChannel; ++sampleIndex)
2423
{
25-
for(int channelIndex = 0; channelIndex < channelProcessors.size(); ++channelIndex)
24+
for(int channelIndex = 0; channelIndex < processors.size(); ++channelIndex)
2625
{
27-
channelProcessors[channelIndex].currentBlockData[blockWritePos] = channelProcessors[channelIndex].filterSample(audio[channelIndex][sampleIndex]);
26+
processors[channelIndex].currentBlockData[blockWritePos] = processors[channelIndex].filterSample(audio[channelIndex][sampleIndex]);
2827
}
2928

3029
if(++blockWritePos >= blockLengthSamples)
3130
{
3231
blockWritePos = 0;
3332
}
3433
}
34+
35+
channelProcessors.realtimeRelease();
3536
}
3637

3738
void FixedTermLoudnessMeter::process(std::span<const float> audio)
3839
{
39-
assert(audio.size() % channelProcessors.size() == 0);
40+
std::vector<ChannelProcessor>& processors = channelProcessors.realtimeAquire();
41+
42+
assert(audio.size() % processors.size() == 0);
4043

41-
const size_t numSamplesPerChannel = audio.size() / channelProcessors.size();
44+
const size_t numSamplesPerChannel = audio.size() / processors.size();
4245

4346
const float* inputData = audio.data();
4447

4548
for(size_t sampleIndex = 0; sampleIndex < numSamplesPerChannel; ++sampleIndex)
4649
{
47-
for(int channelIndex = 0; channelIndex < channelProcessors.size(); ++channelIndex)
50+
for(int channelIndex = 0; channelIndex < processors.size(); ++channelIndex)
4851
{
49-
channelProcessors[channelIndex].currentBlockData[blockWritePos] = channelProcessors[channelIndex].filterSample(*inputData++);
52+
processors[channelIndex].currentBlockData[blockWritePos] = processors[channelIndex].filterSample(*inputData++);
5053
}
5154

5255
if(++blockWritePos >= blockLengthSamples)
5356
{
5457
blockWritePos = 0;
5558
}
5659
}
60+
61+
channelProcessors.realtimeRelease();
5762
}
5863

5964
void FixedTermLoudnessMeter::reset()
6065
{
61-
std::for_each(channelProcessors.begin(), channelProcessors.end(), [](ChannelProcessor& processor)
62-
{
63-
processor.reset();
64-
});
66+
channelProcessors.reset(generateChannelProcessors());
6567

6668
blockWritePos = 0;
6769
}
6870

6971
float FixedTermLoudnessMeter::getLoudness() const
7072
{
73+
const std::vector<ChannelProcessor>& processors = channelProcessors.nonRealtimeRead();
74+
7175
float accumulatedChannels = 0.0f;
7276

73-
std::for_each(channelProcessors.begin(), channelProcessors.end(), [&accumulatedChannels](const ChannelProcessor& processor)
77+
std::for_each(processors.begin(), processors.end(), [&accumulatedChannels](const ChannelProcessor& processor)
7478
{
7579
accumulatedChannels += processor.getCurrentBlockMeanSquares() * processor.getWeighting();
7680
});
@@ -83,4 +87,16 @@ int FixedTermLoudnessMeter::getBlockLengthSamples(const std::chrono::millisecond
8387
return (windowLength.count() / 1000.0f) * sampleRate;
8488
}
8589

90+
std::vector<ChannelProcessor> FixedTermLoudnessMeter::generateChannelProcessors() const
91+
{
92+
std::vector<ChannelProcessor> tempChannelProcessors;
93+
94+
std::transform(channels.begin(), channels.end(), std::back_insert_iterator(tempChannelProcessors), [this](const Channel& channel)
95+
{
96+
return ChannelProcessor(channel.getWeighting(), blockLengthSamples);
97+
});
98+
99+
return tempChannelProcessors;
100+
}
101+
86102
}

0 commit comments

Comments
 (0)