BogaudioModules

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

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