BogaudioModules

BogaudioModules for VCV Rack
Log | Files | Refs | README | LICENSE

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:
Msrc/Analyzer.cpp | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msrc/Analyzer.hpp | 3+++
Msrc/dsp/analyzer.hpp | 7++++---
Msrc/dsp/buffer.hpp | 28+++++++++++++++++-----------
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;