commit 8dc5b190bcd70ca6e54f423bd9693c6216153aeb
parent ca727e87a55a9f2101e6d0d2bdb61850a3c677db
Author: Matt Demanett <matt@demanett.net>
Date: Sat, 24 Nov 2018 21:26:47 -0500
Use worker threads in ANALYZER; moves some load off the audio thread.
Diffstat:
4 files changed, 125 insertions(+), 42 deletions(-)
diff --git a/src/Analyzer.cpp b/src/Analyzer.cpp
@@ -1,11 +1,27 @@
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
#include "Analyzer.hpp"
#include "dsp/signal.hpp"
-struct bogaudio::ChannelAnalyzer : SpectrumAnalyzer {
+struct bogaudio::ChannelAnalyzer {
+ SpectrumAnalyzer _analyzer;
int _binsN;
float* _bins;
AveragingBuffer<float>* _averagedBins;
+ const int _stepBufN;
+ float* _stepBuf;
+ int _stepBufI = 0;
+ const int _workerBufN;
+ float* _workerBuf;
+ int _workerBufWriteI = 0;
+ int _workerBufReadI = 0;
+ bool _workerStop = false;
+ std::mutex _workerMutex;
+ std::condition_variable _workerCV;
+ std::thread _worker;
ChannelAnalyzer(
SpectrumAnalyzer::Size size,
@@ -15,15 +31,28 @@ struct bogaudio::ChannelAnalyzer : SpectrumAnalyzer {
int averageN,
int binSize
)
- : SpectrumAnalyzer(size, overlap, windowType, sampleRate)
+ : _analyzer(size, overlap, windowType, sampleRate, false)
, _binsN(size / binSize)
, _bins(averageN == 1 ? new float[_binsN] {} : NULL)
, _averagedBins(averageN == 1 ? NULL : new AveragingBuffer<float>(_binsN, averageN))
+ , _stepBufN(size / overlap)
+ , _stepBuf(new float[_stepBufN] {})
+ , _workerBufN(size)
+ , _workerBuf(new float[_workerBufN] {})
+ , _worker(&ChannelAnalyzer::work, this)
{
assert(averageN >= 1);
assert(binSize >= 1);
}
virtual ~ChannelAnalyzer() {
+ {
+ std::lock_guard<std::mutex> lock(_workerMutex);
+ _workerStop = true;
+ }
+ _workerCV.notify_one();
+ _worker.join();
+ delete[] _workerBuf;
+ delete[] _stepBuf;
if (_bins) {
delete[] _bins;
}
@@ -39,25 +68,11 @@ struct bogaudio::ChannelAnalyzer : SpectrumAnalyzer {
return _averagedBins->getAverages();
}
- bool step(float sample) override;
float getPeak();
+ void step(float sample);
+ void work();
};
-bool ChannelAnalyzer::step(float sample) {
- if (SpectrumAnalyzer::step(sample)) {
- if (_bins) {
- getMagnitudes(_bins, _binsN);
- }
- else {
- float* frame = _averagedBins->getInputFrame();
- getMagnitudes(frame, _binsN);
- _averagedBins->commitInputFrame();
- }
- return true;
- }
- return false;
-}
-
float ChannelAnalyzer::getPeak() {
float max = 0.0;
float sum = 0.0;
@@ -70,11 +85,69 @@ float ChannelAnalyzer::getPeak() {
}
sum += bins[bin];
}
- const int bandsPerBin = _size / _binsN;
- const float fWidth = (_sampleRate / 2.0f) / (float)(_size / bandsPerBin);
+ const int bandsPerBin = _analyzer._size / _binsN;
+ const float fWidth = (_analyzer._sampleRate / 2.0f) / (float)(_analyzer._size / bandsPerBin);
return (maxBin + 0.5f)*fWidth;
}
+void ChannelAnalyzer::step(float sample) {
+ _stepBuf[_stepBufI++] = sample;
+ if (_stepBufI >= _stepBufN) {
+ _stepBufI = 0;
+
+ {
+ std::lock_guard<std::mutex> lock(_workerMutex);
+ for (int i = 0; i < _stepBufN; ++i) {
+ _workerBuf[_workerBufWriteI] = _stepBuf[i];
+ _workerBufWriteI = (_workerBufWriteI + 1) % _workerBufN;
+ if (_workerBufWriteI == _workerBufReadI) {
+ _workerBufWriteI = _workerBufReadI = 0;
+ break;
+ }
+ }
+ }
+ _workerCV.notify_one();
+ }
+}
+
+void ChannelAnalyzer::work() {
+ bool process = false;
+ MAIN: while (true) {
+ if (_workerStop) {
+ return;
+ }
+
+ if (process) {
+ process = false;
+
+ _analyzer.process();
+ _analyzer.postProcess();
+ if (_bins) {
+ _analyzer.getMagnitudes(_bins, _binsN);
+ }
+ else {
+ float* frame = _averagedBins->getInputFrame();
+ _analyzer.getMagnitudes(frame, _binsN);
+ _averagedBins->commitInputFrame();
+ }
+ }
+
+ while (_workerBufReadI != _workerBufWriteI) {
+ float sample = _workerBuf[_workerBufReadI];
+ _workerBufReadI = (_workerBufReadI + 1) % _workerBufN;
+ if (_analyzer.step(sample)) {
+ process = true;
+ goto MAIN;
+ }
+ }
+
+ std::unique_lock<std::mutex> lock(_workerMutex);
+ while (!(_workerBufReadI != _workerBufWriteI || _workerStop)) {
+ _workerCV.wait(lock);
+ }
+ }
+}
+
void Analyzer::onReset() {
_modulationStep = modulationSteps;
@@ -87,6 +160,7 @@ void Analyzer::onSampleRateChange() {
}
void Analyzer::resetChannels() {
+ std::lock_guard<std::mutex> lock(_channelsMutex);
if (_channelA) {
delete _channelA;
_channelA = NULL;
@@ -208,6 +282,7 @@ void Analyzer::step() {
void Analyzer::stepChannel(ChannelAnalyzer*& channelPointer, bool running, Input& input, Output& output) {
if (running && input.active) {
if (!channelPointer) {
+ std::lock_guard<std::mutex> lock(_channelsMutex);
channelPointer = new ChannelAnalyzer(
size(),
_overlap,
@@ -217,17 +292,13 @@ void Analyzer::stepChannel(ChannelAnalyzer*& channelPointer, bool running, Input
_binAverageN
);
}
-
channelPointer->step(input.value);
output.value = input.value;
}
- else {
- if (channelPointer) {
- delete channelPointer;
- channelPointer = NULL;
- }
-
- output.value = input.value;
+ else if (channelPointer) {
+ std::lock_guard<std::mutex> lock(_channelsMutex);
+ delete channelPointer;
+ channelPointer = NULL;
}
}
@@ -280,6 +351,8 @@ struct AnalyzerDisplay : TransparentWidget {
};
void AnalyzerDisplay::draw(NVGcontext* vg) {
+ std::lock_guard<std::mutex> lock(_module->_channelsMutex);
+
drawBackground(vg);
if (_module->_running) {
float strokeWidth = std::max(1.0f, 3 - gRackScene->zoomWidget->zoom);
diff --git a/src/Analyzer.hpp b/src/Analyzer.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mutex>
+
#include "bogaudio.hpp"
#include "dsp/analyzer.hpp"
@@ -76,6 +78,7 @@ struct Analyzer : Module {
Window _window = WINDOW_KAISER;
const SpectrumAnalyzer::Overlap _overlap = SpectrumAnalyzer::OVERLAP_2;
const int _binAverageN = 2;
+ std::mutex _channelsMutex;
Analyzer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
onReset();
diff --git a/src/dsp/analyzer.hpp b/src/dsp/analyzer.hpp
@@ -147,9 +147,10 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> {
Size size,
Overlap overlap,
WindowType windowType,
- float sampleRate
+ float sampleRate,
+ bool autoProcess = true
)
- : OverlappingBuffer(size, overlap)
+ : OverlappingBuffer(size, overlap, autoProcess)
, _sampleRate(sampleRate)
, _fft(NULL)
, _fft1024(NULL)
@@ -223,7 +224,7 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> {
delete[] _fftOut;
}
- void process(float* samples) override {
+ void processBuffer(float* samples) override {
float* input = samples;
if (_window) {
_window->apply(samples, _windowOut);
diff --git a/src/dsp/buffer.hpp b/src/dsp/buffer.hpp
@@ -11,14 +11,16 @@ template<typename T>
struct OverlappingBuffer {
const int _size;
const int _overlap;
+ const bool _autoProcess;
const int _overlapN;
const int _samplesN;
T* _samples;
int _sample;
- OverlappingBuffer(int size, int o)
+ OverlappingBuffer(int size, int o, bool autoProcess = true)
: _size(size)
, _overlap(o)
+ , _autoProcess(autoProcess)
, _overlapN(_size / _overlap)
, _samplesN(2*_size - _overlapN)
, _samples(new T[_samplesN])
@@ -31,23 +33,27 @@ struct OverlappingBuffer {
delete[] _samples;
}
- virtual void process(T* samples) = 0;
+ inline void process() { processBuffer(_samples + _sample - _size); }
+ virtual void processBuffer(T* samples) = 0;
+ void postProcess() {
+ if (_overlap == 1) {
+ _sample = 0;
+ }
+ else if (_sample == _samplesN) {
+ std::copy(_samples + _size, _samples + _samplesN, _samples);
+ _sample = _samplesN - _size;
+ }
+ }
virtual bool step(T sample) {
_samples[_sample++] = sample;
assert(_sample <= _samplesN);
if (_sample >= _size && _sample % _overlapN == 0) {
- process(_samples + _sample - _size);
-
- if (_overlap == 1) {
- _sample = 0;
- }
- else if (_sample == _samplesN) {
- std::copy(_samples + _size, _samples + _samplesN, _samples);
- _sample = _samplesN - _size;
+ if (_autoProcess) {
+ process();
+ postProcess();
}
-
return true;
}
return false;