analyzer_base.hpp (8450B)
1 2 #pragma once 3 4 #include <atomic> 5 #include <condition_variable> 6 #include <memory> 7 #include <mutex> 8 #include <thread> 9 10 #include "bogaudio.hpp" 11 #include "dsp/analyzer.hpp" 12 13 using namespace bogaudio::dsp; 14 15 namespace bogaudio { 16 17 struct ChannelAnalyzer { 18 SpectrumAnalyzer _analyzer; 19 int _binsN; 20 float* _bins0; 21 float* _bins1; 22 float* _currentBins; 23 std::atomic<float*>& _currentOutBuf; 24 AveragingBuffer<float>* _averagedBins; 25 const int _stepBufN; 26 float* _stepBuf; 27 int _stepBufI = 0; 28 const int _workerBufN; 29 float* _workerBuf; 30 int _workerBufWriteI = 0; 31 int _workerBufReadI = 0; 32 bool _workerStop = false; 33 std::mutex _workerMutex; 34 std::condition_variable _workerCV; 35 std::thread _worker; 36 37 ChannelAnalyzer( 38 SpectrumAnalyzer::Size size, 39 SpectrumAnalyzer::Overlap overlap, 40 SpectrumAnalyzer::WindowType windowType, 41 float sampleRate, 42 int averageN, 43 int binSize, 44 float* outBuf1, 45 float* outBuf2, 46 std::atomic<float*>& currentOutBuf 47 ) 48 : _analyzer(size, overlap, windowType, sampleRate, false) 49 , _binsN(size / binSize) 50 , _bins0(outBuf1) 51 , _bins1(outBuf2) 52 , _currentBins(_bins0) 53 , _currentOutBuf(currentOutBuf) 54 , _averagedBins(averageN == 1 ? NULL : new AveragingBuffer<float>(_binsN, averageN)) 55 , _stepBufN(size / overlap) 56 , _stepBuf(new float[_stepBufN] {}) 57 , _workerBufN(size + 1) 58 , _workerBuf(new float[_workerBufN] {}) 59 , _worker(&ChannelAnalyzer::work, this) 60 { 61 assert(averageN >= 1); 62 assert(binSize >= 1); 63 } 64 virtual ~ChannelAnalyzer(); 65 66 void step(float sample); 67 void work(); 68 }; 69 70 struct AnalyzerCore { 71 enum Quality { 72 QUALITY_ULTRA, 73 QUALITY_HIGH, 74 QUALITY_GOOD, 75 QUALITY_FIXED_16K, 76 QUALITY_FIXED_32K, 77 QUALITY_ULTRA_ULTRA 78 }; 79 80 enum Window { 81 WINDOW_NONE, 82 WINDOW_HAMMING, 83 WINDOW_KAISER 84 }; 85 86 int _nChannels; 87 ChannelAnalyzer** _channels; 88 SpectrumAnalyzer::Size _size; 89 const int _binAverageN = 2; 90 const int _outBufferN = SpectrumAnalyzer::maxSize / _binAverageN; 91 int _binsN; 92 float* _outBufs; 93 std::atomic<float*>* _currentOutBufs; 94 float _sampleRate = 1000.0f; 95 int _averageN = 1; 96 Quality _quality = QUALITY_GOOD; 97 Window _window = WINDOW_KAISER; 98 SpectrumAnalyzer::Overlap _overlap = SpectrumAnalyzer::OVERLAP_2; 99 std::mutex _channelsMutex; 100 101 AnalyzerCore(int nChannels, SpectrumAnalyzer::Overlap overlap = SpectrumAnalyzer::OVERLAP_2) 102 : _nChannels(nChannels) 103 , _channels(new ChannelAnalyzer*[_nChannels] {}) 104 , _outBufs(new float[2 * nChannels * _outBufferN] {}) 105 , _currentOutBufs(new std::atomic<float*>[nChannels]) 106 , _overlap(overlap) 107 { 108 for (int i = 0; i < nChannels; ++i) { 109 _currentOutBufs[i] = _outBufs + 2 * i * _outBufferN; 110 } 111 } 112 virtual ~AnalyzerCore() { 113 resetChannels(); 114 delete[] _channels; 115 delete[] _outBufs; 116 delete[] _currentOutBufs; 117 } 118 119 void setParams(float sampleRate, int averageN, Quality quality, Window window); 120 void resetChannels(); 121 void resetChannelsLocked(); 122 SpectrumAnalyzer::Size size(); 123 SpectrumAnalyzer::WindowType window(); 124 inline float* getBins(int i) { 125 assert(i >= 0 && i < _nChannels); 126 return _currentOutBufs[i]; 127 } 128 float getPeak(int channel, float minHz, float maxHz); 129 void stepChannel(int channelIndex, Input& input); 130 void stepChannelSample(int channelIndex, float sample); 131 }; 132 133 struct AnalyzerTypes { 134 enum FrequencyPlot { 135 LOG_FP, 136 LINEAR_FP 137 }; 138 139 enum AmplitudePlot { 140 DECIBELS_80_AP, 141 DECIBELS_140_AP, 142 PERCENTAGE_AP 143 }; 144 }; 145 146 struct AnalyzerBase : BGModule, AnalyzerTypes { 147 float _range = 0.0f; 148 float _rangeMinHz = 0.0; 149 float _rangeMaxHz = 0.0; 150 FrequencyPlot _frequencyPlot = LOG_FP; 151 AmplitudePlot _amplitudePlot = DECIBELS_80_AP; 152 AnalyzerCore _core; 153 154 AnalyzerBase( 155 int nChannels, 156 int np, 157 int ni, 158 int no, 159 int nl = 0, 160 SpectrumAnalyzer::Overlap overlap = SpectrumAnalyzer::OVERLAP_2 161 ) 162 : _core(nChannels, overlap) 163 { 164 config(np, ni, no, nl); 165 } 166 167 void frequencyPlotToJson(json_t* root); 168 void frequencyPlotFromJson(json_t* root); 169 void frequencyRangeToJson(json_t* root); 170 void frequencyRangeFromJson(json_t* root); 171 void amplitudePlotToJson(json_t* root); 172 void amplitudePlotFromJson(json_t* root); 173 }; 174 175 struct AnalyzerBaseWidget : BGModuleWidget { 176 void addFrequencyPlotContextMenu(Menu* menu); 177 void addFrequencyRangeContextMenu(Menu* menu); 178 void addAmplitudePlotContextMenu(Menu* menu, bool linearOption = true); 179 }; 180 181 struct AnalyzerDisplay : DisplayWidget, AnalyzerTypes { 182 struct BinsReader { 183 BinsReader() {} 184 virtual ~BinsReader() {} 185 virtual float at(int i) = 0; 186 }; 187 188 struct GenericBinsReader : BinsReader { 189 float* _bins; 190 GenericBinsReader(float* bins) : _bins(bins) {} 191 float at(int i) override { return _bins[i]; } 192 }; 193 194 typedef std::function<std::unique_ptr<BinsReader>(AnalyzerCore&)> BinsReaderFactory; 195 196 const int _insetAround = 2; 197 const int _insetLeft = _insetAround + 12; 198 const int _insetRight = _insetAround + 2; 199 const int _insetTop = _insetAround + 13; 200 const int _insetBottom = _insetAround + 9; 201 202 // const float _displayDB = 140.0; 203 const float _positiveDisplayDB = 20.0; 204 const float _totalLinearAmplitude = 2.0; 205 206 const float baseXAxisLogFactor = 1 / 3.321; // magic number. 207 208 const NVGcolor _axisColor = nvgRGBA(0xff, 0xff, 0xff, 0x70); 209 const NVGcolor _textColor = nvgRGBA(0xff, 0xff, 0xff, 0xc0); 210 static constexpr int channelColorsN = 8; 211 const NVGcolor _channelColors[channelColorsN] = { 212 nvgRGBA(0x00, 0xff, 0x00, 0xd0), 213 nvgRGBA(0xff, 0x00, 0xff, 0xd0), 214 nvgRGBA(0xff, 0x80, 0x00, 0xd0), 215 nvgRGBA(0x00, 0x80, 0xff, 0xd0), 216 217 nvgRGBA(0xff, 0x00, 0x00, 0xd0), 218 nvgRGBA(0xff, 0xff, 0x00, 0xd0), 219 nvgRGBA(0x00, 0xff, 0xff, 0xd0), 220 nvgRGBA(0xff, 0x80, 0x80, 0xd0) 221 }; 222 223 AnalyzerBase* _module; 224 const Vec _size; 225 const Vec _graphSize; 226 bool _drawInset; 227 std::string _fontPath; 228 float _xAxisLogFactor = baseXAxisLogFactor; 229 BinsReaderFactory* _channelBinsReaderFactories = NULL; 230 bool* _displayChannel = NULL; 231 std::string* _channelLabels = NULL; 232 Vec _freezeMouse; 233 bool _freezeDraw = false; 234 float* _freezeBufs = NULL; 235 int _freezeNudgeBin = 0; 236 int _freezeLastBinI = -1; 237 238 AnalyzerDisplay( 239 AnalyzerBase* module, 240 Vec size, 241 bool drawInset 242 ) 243 : DisplayWidget(module) 244 , _module(module) 245 , _size(size) 246 , _graphSize(_size.x - _insetLeft - _insetRight, _size.y - _insetTop - _insetBottom) 247 , _drawInset(drawInset) 248 , _fontPath(asset::plugin(pluginInstance, "res/fonts/inconsolata.ttf")) 249 { 250 if (_module) { 251 _channelBinsReaderFactories = new BinsReaderFactory[_module->_core._nChannels] {}; 252 _displayChannel = new bool[_module->_core._nChannels] {}; 253 _channelLabels = new std::string[_module->_core._nChannels]; 254 std::fill_n(_displayChannel, _module->_core._nChannels, true); 255 } 256 } 257 ~AnalyzerDisplay() { 258 if (_module) { 259 delete[] _channelBinsReaderFactories; 260 delete[] _displayChannel; 261 delete[] _channelLabels; 262 } 263 } 264 265 void onButton(const event::Button& e) override; 266 void onDragMove(const event::DragMove& e) override; 267 void onDragEnd(const event::DragEnd& e) override; 268 void onHoverKey(const event::HoverKey &e) override; 269 void setChannelBinsReaderFactory(int channel, BinsReaderFactory brf); 270 void displayChannel(int channel, bool display); 271 void channelLabel(int channel, std::string label); 272 void drawOnce(const DrawArgs& args, bool screenshot, bool lit) override; 273 void drawBackground(const DrawArgs& args); 274 virtual void drawHeader(const DrawArgs& args, float rangeMinHz, float rangeMaxHz); 275 void drawYAxis(const DrawArgs& args, float strokeWidth, AmplitudePlot plot); 276 void drawXAxis(const DrawArgs& args, float strokeWidth, FrequencyPlot plot, float rangeMinHz, float rangeMaxHz); 277 void drawXAxisLine(const DrawArgs& args, float hz, float rangeMinHz, float rangeMaxHz); 278 void drawGraph(const DrawArgs& args, BinsReader& bins, NVGcolor color, float strokeWidth, FrequencyPlot freqPlot, float rangeMinHz, float rangeMaxHz, AmplitudePlot ampPlot); 279 void freezeValues(float rangeMinHz, float rangeMaxHz, int& binI, float& lowHz, float& highHz); 280 void drawFreezeUnder(const DrawArgs& args, float lowHz, float highHz, float rangeMinHz, float rangeMaxHz, float strokeWidth); 281 void drawFreezeOver(const DrawArgs& args, int binI, int binsN, float lowHz, float highHz, float strokeWidth); 282 void drawText(const DrawArgs& args, const char* s, float x, float y, float rotation = 0.0, const NVGcolor* color = NULL, int fontSize = 10); 283 int binValueToHeight(float value, AmplitudePlot plot); 284 static float binValueToAmplitude(float value); 285 static float binValueToDb(float value); 286 static float dbToBinValue(float db); 287 }; 288 289 } // namespace bogaudio