BogaudioModules

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

commit 75632c31331d17bf0e630c17775cecb4f5e7d826
parent e7e898c92b989c6174415c911ec066a2e5496218
Author: Matt Demanett <matt@demanett.net>
Date:   Fri, 22 Jan 2021 22:32:27 -0500

*ANALYZER* cleanups: single source of truth for sample rate; better lock scoping; getBins() only at start of drawGraph for derived plots.

Diffstat:
Msrc/Analyzer.cpp | 8++++----
Msrc/Analyzer.hpp | 2++
Msrc/AnalyzerXL.cpp | 8++++----
Msrc/AnalyzerXL.hpp | 1+
Msrc/Ranalyzer.cpp | 23+++++++++++++----------
Msrc/analyzer_base.cpp | 55+++++++++++++++++++++++++++++++++----------------------
Msrc/analyzer_base.hpp | 26+++++++++++++-------------
7 files changed, 70 insertions(+), 53 deletions(-)

diff --git a/src/Analyzer.cpp b/src/Analyzer.cpp @@ -6,7 +6,7 @@ void Analyzer::reset() { } void Analyzer::sampleRateChange() { - _core.resetChannels(); + _sampleRate = APP->engine->getSampleRate(); } json_t* Analyzer::toJson(json_t* root) { @@ -23,7 +23,7 @@ void Analyzer::fromJson(json_t* root) { void Analyzer::modulate() { float range = params[RANGE2_PARAM].getValue(); _rangeMinHz = 0.0f; - _rangeMaxHz = 0.5f * APP->engine->getSampleRate(); + _rangeMaxHz = 0.5f * _sampleRate; if (range < 0.0f) { range *= 0.9f; _rangeMaxHz *= 1.0f + range; @@ -36,7 +36,7 @@ void Analyzer::modulate() { const float maxTime = 0.5; float smooth = params[SMOOTH_PARAM].getValue() * maxTime; - smooth /= _core.size() / (_core._overlap * APP->engine->getSampleRate()); + smooth /= _core.size() / (_core._overlap * _sampleRate); int averageN = std::max(1, (int)roundf(smooth)); AnalyzerCore::Quality quality = AnalyzerCore::QUALITY_GOOD; @@ -55,7 +55,7 @@ void Analyzer::modulate() { window = AnalyzerCore::WINDOW_HAMMING; } - _core.setParams(averageN, quality, window); + _core.setParams(_sampleRate, averageN, quality, window); } void Analyzer::processAll(const ProcessArgs& args) { diff --git a/src/Analyzer.hpp b/src/Analyzer.hpp @@ -45,6 +45,8 @@ struct Analyzer : AnalyzerBase { NUM_LIGHTS }; + float _sampleRate = 1000.0f; + Analyzer() : AnalyzerBase(4, NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { configParam(RANGE2_PARAM, -1.0f, 1.0f, 0.0f, "Range"); configParam(SMOOTH_PARAM, 0.0f, 1.0f, 0.5f, "Smoothing", " ms", 0.0f, 500.0f); diff --git a/src/AnalyzerXL.cpp b/src/AnalyzerXL.cpp @@ -19,7 +19,7 @@ void AnalyzerXL::reset() { } void AnalyzerXL::sampleRateChange() { - _core.resetChannels(); + _sampleRate = APP->engine->getSampleRate(); } json_t* AnalyzerXL::toJson(json_t* root) { @@ -110,7 +110,7 @@ void AnalyzerXL::fromJson(json_t* root) { void AnalyzerXL::modulate() { _rangeMinHz = 0.0f; - _rangeMaxHz = 0.5f * APP->engine->getSampleRate(); + _rangeMaxHz = 0.5f * _sampleRate; if (_range < 0.0f) { _rangeMaxHz *= 1.0f + _range; } @@ -118,9 +118,9 @@ void AnalyzerXL::modulate() { _rangeMinHz = _range * _rangeMaxHz; } - float smooth = _smooth / (_core.size() / (_core._overlap * APP->engine->getSampleRate())); + float smooth = _smooth / (_core.size() / (_core._overlap * _sampleRate)); int averageN = std::max(1, (int)roundf(smooth)); - _core.setParams(averageN, _quality, _window); + _core.setParams(_sampleRate, averageN, _quality, _window); } void AnalyzerXL::processAll(const ProcessArgs& args) { diff --git a/src/AnalyzerXL.hpp b/src/AnalyzerXL.hpp @@ -28,6 +28,7 @@ struct AnalyzerXL : AnalyzerBase { NUM_OUTPUTS }; + float _sampleRate = 1000.0f; float _smooth = 0.25f; AnalyzerCore::Quality _quality = AnalyzerCore::QUALITY_GOOD; AnalyzerCore::Window _window = AnalyzerCore::WINDOW_KAISER; diff --git a/src/Ranalyzer.cpp b/src/Ranalyzer.cpp @@ -30,10 +30,10 @@ void Ranalyzer::sampleRateChange() { _rangeMinHz = 0.0f; _rangeMaxHz = 0.5f * _sampleRate; if (_sampleRate >= 96000.0f) { - _core.setParams(1, AnalyzerCore::QUALITY_FIXED_32K, AnalyzerCore::WINDOW_NONE); + _core.setParams(_sampleRate, 1, AnalyzerCore::QUALITY_FIXED_32K, AnalyzerCore::WINDOW_NONE); } else { - _core.setParams(1, AnalyzerCore::QUALITY_FIXED_16K, AnalyzerCore::WINDOW_NONE); + _core.setParams(_sampleRate, 1, AnalyzerCore::QUALITY_FIXED_16K, AnalyzerCore::WINDOW_NONE); } setWindow(_windowType); _run = false; @@ -268,18 +268,21 @@ void Ranalyzer::setWindow(WindowType wt) { } - struct AnalysisBinsReader : AnalyzerDisplay::BinsReader { - AnalyzerBase* _base; + float* _testBins; + float* _responseBins; - AnalysisBinsReader(AnalyzerBase* base) : _base(base) {} + AnalysisBinsReader(float* testBins, float* responseBins) : _testBins(testBins), _responseBins(responseBins) {} float at(int i) override { - assert(_base->_core._nChannels == 3); + float test = AnalyzerDisplay::binValueToDb(_testBins[i]); + float response = AnalyzerDisplay::binValueToDb(_responseBins[i]); + return AnalyzerDisplay::dbToBinValue(response = test); + } - float test = AnalyzerDisplay::binValueToDb(_base->_core.getBins(0)[i]); - float response = AnalyzerDisplay::binValueToDb(_base->_core.getBins(1)[i]); - return AnalyzerDisplay::dbToBinValue(response - test); + static std::unique_ptr<BinsReader> factory(AnalyzerCore& core) { + assert(core._nChannels == 3); + return std::unique_ptr<BinsReader>(new AnalysisBinsReader(core.getBins(0), core.getBins(1))); } }; @@ -345,7 +348,7 @@ struct RanalyzerWidget : AnalyzerBaseWidget { display->box.pos = inset; display->box.size = size; if (module) { - display->setChannelBinsReader(2, new AnalysisBinsReader(module)); + display->setChannelBinsReaderFactory(2, AnalysisBinsReader::factory); module->setChannelDisplayListener(display); display->channelLabel(0, "Test"); display->channelLabel(1, "Response"); diff --git a/src/analyzer_base.cpp b/src/analyzer_base.cpp @@ -84,8 +84,14 @@ void ChannelAnalyzer::work() { } -void AnalyzerCore::setParams(int averageN, Quality quality, Window window) { +void AnalyzerCore::setParams(float sampleRate, int averageN, Quality quality, Window window) { + std::lock_guard<std::mutex> lock(_channelsMutex); + bool reset = false; + if (_sampleRate != sampleRate) { + _sampleRate = sampleRate; + reset = true; + } if (_averageN != averageN) { _averageN = averageN; reset = true; @@ -99,15 +105,19 @@ void AnalyzerCore::setParams(int averageN, Quality quality, Window window) { reset = true; } if (reset) { - resetChannels(); + resetChannelsLocked(); } } void AnalyzerCore::resetChannels() { + std::lock_guard<std::mutex> lock(_channelsMutex); + resetChannelsLocked(); +} + +void AnalyzerCore::resetChannelsLocked() { _size = size(); _binsN = _size / _binAverageN; - std::lock_guard<std::mutex> lock(_channelsMutex); for (int i = 0; i < _nChannels; ++i) { if (_channels[i]) { delete _channels[i]; @@ -127,7 +137,7 @@ SpectrumAnalyzer::Size AnalyzerCore::size() { default:; } - if (APP->engine->getSampleRate() < 96000.0f) { + if (_sampleRate < 96000.0f) { switch (_quality) { case QUALITY_ULTRA_ULTRA: { return SpectrumAnalyzer::SIZE_16384; @@ -179,7 +189,7 @@ float AnalyzerCore::getPeak(int channel, float minHz, float maxHz) { assert(channel >= 0 && channel < _nChannels); const float* bins = getBins(channel); const int bandsPerBin = _size / _binsN; - const float fWidth = (APP->engine->getSampleRate() / 2.0f) / (float)(_size / bandsPerBin); + const float fWidth = (_sampleRate / 2.0f) / (float)(_size / bandsPerBin); float max = 0.0f; int maxBin = 0; int i = std::max(0, (int)(minHz / fWidth)); @@ -217,7 +227,7 @@ void AnalyzerCore::stepChannelSample(int channelIndex, float sample) { _size, _overlap, window(), - APP->engine->getSampleRate(), + _sampleRate, _averageN, _binAverageN, _outBufs + 2 * channelIndex * _outBufferN, @@ -373,10 +383,11 @@ void AnalyzerDisplay::onButton(const event::Button& e) { } _freezeBufs = new float[_module->_core._nChannels * _module->_core._outBufferN]; for (int i = 0; i < _module->_core._nChannels; ++i) { - if (_channelBinsReaders[i]) { + if (_channelBinsReaderFactories[i]) { + std::unique_ptr<BinsReader> br = _channelBinsReaderFactories[i](_module->_core); float* dest = _freezeBufs + i * _module->_core._outBufferN; for (int j = 0; j < _module->_core._outBufferN; ++j) { - *(dest + j) = _channelBinsReaders[i]->at(j); + *(dest + j) = br->at(j); } } else { @@ -421,14 +432,11 @@ void AnalyzerDisplay::onHoverKey(const event::HoverKey &e) { } } -void AnalyzerDisplay::setChannelBinsReader(int channel, BinsReader* br) { - assert(_channelBinsReaders); +void AnalyzerDisplay::setChannelBinsReaderFactory(int channel, BinsReaderFactory brf) { + assert(_channelBinsReaderFactories); assert(_module); assert(channel >= 0 && channel < _module->_core._nChannels); - if (_channelBinsReaders[channel]) { - delete _channelBinsReaders[channel]; - } - _channelBinsReaders[channel] = br; // br now owned here. + _channelBinsReaderFactories[channel] = brf; } void AnalyzerDisplay::displayChannel(int channel, bool display) { @@ -459,7 +467,7 @@ void AnalyzerDisplay::draw(const DrawArgs& args) { amplitudePlot = _module->_amplitudePlot; rangeMinHz = _module->_rangeMinHz; rangeMaxHz = _module->_rangeMaxHz; - assert(rangeMaxHz <= 0.5f * APP->engine->getSampleRate()); + assert(rangeMaxHz <= 0.5f * _module->_core._sampleRate); assert(rangeMinHz < rangeMaxHz); } else { @@ -501,8 +509,9 @@ void AnalyzerDisplay::draw(const DrawArgs& args) { GenericBinsReader br(_freezeBufs ? _freezeBufs + i * _module->_core._outBufferN : _module->_core.getBins(i)); drawGraph(args, br, _channelColors[i % channelColorsN], strokeWidth, frequencyPlot, rangeMinHz, rangeMaxHz, amplitudePlot); } - else if (_channelBinsReaders[i]) { - drawGraph(args, *_channelBinsReaders[i], _channelColors[i % channelColorsN], strokeWidth, frequencyPlot, rangeMinHz, rangeMaxHz, amplitudePlot); + else if (_channelBinsReaderFactories[i]) { + std::unique_ptr<BinsReader> br = _channelBinsReaderFactories[i](_module->_core); + drawGraph(args, *br, _channelColors[i % channelColorsN], strokeWidth, frequencyPlot, rangeMinHz, rangeMaxHz, amplitudePlot); } } } @@ -538,7 +547,7 @@ void AnalyzerDisplay::drawHeader(const DrawArgs& args, float rangeMinHz, float r const int charPx = 5; int x = _insetAround + 2; - std::string s = format("Peaks (+/-%0.1f):", (APP->engine->getSampleRate() / 2.0f) / (float)(_module->_core._size / _module->_core._binAverageN)); + std::string s = format("Peaks (+/-%0.1f):", (_module->_core._sampleRate / 2.0f) / (float)(_module->_core._size / _module->_core._binAverageN)); drawText(args, s.c_str(), x, _insetTop + textY); x += s.size() * charPx - 0; @@ -809,7 +818,7 @@ void AnalyzerDisplay::drawGraph( float rangeMaxHz, AmplitudePlot ampPlot ) { - float nyquist = 0.5f * APP->engine->getSampleRate(); + float nyquist = 0.5f * _module->_core._sampleRate; int binsN = _module->_core._size / _module->_core._binAverageN; float binHz = nyquist / (float)binsN; float range = (rangeMaxHz - rangeMinHz) / nyquist; @@ -821,7 +830,9 @@ void AnalyzerDisplay::drawGraph( nvgStrokeWidth(args.vg, strokeWidth); nvgBeginPath(args.vg); for (int i = 0; i < pointsN; ++i) { - int height = binValueToHeight(bins.at(pointsOffset + i), ampPlot); + int oi = pointsOffset + i; + assert(oi < _module->_core._outBufferN); + int height = binValueToHeight(bins.at(oi), ampPlot); if (i == 0) { nvgMoveTo(args.vg, _insetLeft, _insetTop + (_graphSize.y - height)); } @@ -835,7 +846,7 @@ void AnalyzerDisplay::drawGraph( void AnalyzerDisplay::freezeValues(float rangeMinHz, float rangeMaxHz, int& binI, float& lowHz, float& highHz) { int binsN = _module->_core._size / _module->_core._binAverageN; - float binHz = (0.5f * APP->engine->getSampleRate()) / (float)binsN; + float binHz = (0.5f * _module->_core._sampleRate) / (float)binsN; float mouseHz = powf((_freezeMouse.x - _insetLeft) / (float)_graphSize.x, 1.0f / _xAxisLogFactor); mouseHz *= rangeMaxHz - rangeMinHz; mouseHz += rangeMinHz; @@ -886,7 +897,7 @@ void AnalyzerDisplay::drawFreezeOver(const DrawArgs& args, int binI, int binsN, values.push_back(formatHz(highHz)); colors.push_back(NULL); for (int i = 0; i < _module->_core._nChannels; ++i) { - if (_displayChannel[i] && (_module->_core._channels[i] || _channelBinsReaders[i])) { + if (_displayChannel[i] && (_module->_core._channels[i] || _channelBinsReaderFactories[i])) { if (_channelLabels[i].empty()) { labels.push_back(format("Ch. %d", i + 1)); } diff --git a/src/analyzer_base.hpp b/src/analyzer_base.hpp @@ -1,10 +1,11 @@ #pragma once -#include <thread> -#include <mutex> -#include <condition_variable> #include <atomic> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <thread> #include "bogaudio.hpp" #include "dsp/analyzer.hpp" @@ -90,6 +91,7 @@ struct AnalyzerCore { int _binsN; float* _outBufs; std::atomic<float*>* _currentOutBufs; + float _sampleRate = 1000.0f; int _averageN = 1; Quality _quality = QUALITY_GOOD; Window _window = WINDOW_KAISER; @@ -114,8 +116,9 @@ struct AnalyzerCore { delete[] _currentOutBufs; } - void setParams(int averageN, Quality quality, Window window); + void setParams(float sampleRate, int averageN, Quality quality, Window window); void resetChannels(); + void resetChannelsLocked(); SpectrumAnalyzer::Size size(); SpectrumAnalyzer::WindowType window(); inline float* getBins(int i) { @@ -188,6 +191,8 @@ struct AnalyzerDisplay : TransparentWidget, AnalyzerTypes { float at(int i) override { return _bins[i]; } }; + typedef std::function<std::unique_ptr<BinsReader>(AnalyzerCore&)> BinsReaderFactory; + const int _insetAround = 2; const int _insetLeft = _insetAround + 12; const int _insetRight = _insetAround + 2; @@ -221,7 +226,7 @@ struct AnalyzerDisplay : TransparentWidget, AnalyzerTypes { bool _drawInset; std::shared_ptr<Font> _font; float _xAxisLogFactor = baseXAxisLogFactor; - BinsReader** _channelBinsReaders = NULL; + BinsReaderFactory* _channelBinsReaderFactories = NULL; bool* _displayChannel = NULL; std::string* _channelLabels = NULL; Vec _freezeMouse; @@ -242,7 +247,7 @@ struct AnalyzerDisplay : TransparentWidget, AnalyzerTypes { , _font(APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/inconsolata.ttf"))) { if (_module) { - _channelBinsReaders = new BinsReader*[_module->_core._nChannels] {}; + _channelBinsReaderFactories = new BinsReaderFactory[_module->_core._nChannels] {}; _displayChannel = new bool[_module->_core._nChannels] {}; _channelLabels = new std::string[_module->_core._nChannels]; std::fill_n(_displayChannel, _module->_core._nChannels, true); @@ -250,12 +255,7 @@ struct AnalyzerDisplay : TransparentWidget, AnalyzerTypes { } ~AnalyzerDisplay() { if (_module) { - for (int i = 0; i < _module->_core._nChannels; ++i) { - if (_channelBinsReaders) { - delete _channelBinsReaders[i]; - } - } - delete[] _channelBinsReaders; + delete[] _channelBinsReaderFactories; delete[] _displayChannel; delete[] _channelLabels; } @@ -265,7 +265,7 @@ struct AnalyzerDisplay : TransparentWidget, AnalyzerTypes { void onDragMove(const event::DragMove& e) override; void onDragEnd(const event::DragEnd& e) override; void onHoverKey(const event::HoverKey &e) override; - void setChannelBinsReader(int channel, BinsReader* br); + void setChannelBinsReaderFactory(int channel, BinsReaderFactory brf); void displayChannel(int channel, bool display); void channelLabel(int channel, std::string label); void draw(const DrawArgs& args) override;