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:
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;