BogaudioModules

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

commit 137b225415e389067bc945b7094f5212d15b39da
parent 0ea03ef1285261ae14fc658c98fafd58499ebaef
Author: Matt Demanett <matt@demanett.net>
Date:   Sun,  3 Dec 2017 21:41:46 -0500

Analyzer: add Quality control to panel; high mode uses 4096 fft; redesign power switch.

Diffstat:
Mbenchmarks/analyzer.cpp | 30+++++++++++++++++++++++++++---
Mres/Analyzer-src.svg | 0
Mres/Analyzer.svg | 0
Msrc/Analyzer.cpp | 53+++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/BogaudioModules.hpp | 8++++++++
Msrc/dsp/analyzer.cpp | 24++++++++++++++++++++----
Msrc/dsp/analyzer.hpp | 21+++++++++++++++++++++
7 files changed, 123 insertions(+), 13 deletions(-)

diff --git a/benchmarks/analyzer.cpp b/benchmarks/analyzer.cpp @@ -25,6 +25,18 @@ static void BM_HanningWindowApply(benchmark::State& state) { } BENCHMARK(BM_HanningWindowApply); +static void BM_RuntimeFFT1024(benchmark::State& state) { + const int n = 1024; + ffft::FFTReal<float> fft(n); + float in[n]; + std::fill_n(in, n, 1.1); + float out[n] {}; + for (auto _ : state) { + fft.do_fft(out, in); + } +} +BENCHMARK(BM_RuntimeFFT1024); + static void BM_CompileTimeFFT1024(benchmark::State& state) { FFT1024 fft; const int n = 1024; @@ -37,8 +49,8 @@ static void BM_CompileTimeFFT1024(benchmark::State& state) { } BENCHMARK(BM_CompileTimeFFT1024); -static void BM_RuntimeFFT1024(benchmark::State& state) { - const int n = 1024; +static void BM_RuntimeFFT4096(benchmark::State& state) { + const int n = 4096; ffft::FFTReal<float> fft(n); float in[n]; std::fill_n(in, n, 1.1); @@ -47,7 +59,19 @@ static void BM_RuntimeFFT1024(benchmark::State& state) { fft.do_fft(out, in); } } -BENCHMARK(BM_RuntimeFFT1024); +BENCHMARK(BM_RuntimeFFT4096); + +static void BM_CompileTimeFFT4096(benchmark::State& state) { + FFT4096 fft; + const int n = 4096; + float in[n]; + std::fill_n(in, n, 1.1); + float out[n] {}; + for (auto _ : state) { + fft.do_fft(out, in); + } +} +BENCHMARK(BM_CompileTimeFFT4096); static void BM_SpectrumAnalyzerStep(benchmark::State& state) { SpectrumAnalyzer sa( diff --git a/res/Analyzer-src.svg b/res/Analyzer-src.svg Binary files differ. diff --git a/res/Analyzer.svg b/res/Analyzer.svg Binary files differ. diff --git a/src/Analyzer.cpp b/src/Analyzer.cpp @@ -77,6 +77,7 @@ struct Analyzer : Module { enum ParamsIds { RANGE_PARAM, SMOOTH_PARAM, + QUALITY_PARAM, POWER_PARAM, NUM_PARAMS }; @@ -98,10 +99,17 @@ struct Analyzer : Module { }; enum LightsIds { + QUALITY_HIGH_LIGHT, + QUALITY_GOOD_LIGHT, + POWER_ON_LIGHT, NUM_LIGHTS }; - const SpectrumAnalyzer::Size _size = SpectrumAnalyzer::SIZE_1024; + enum Quality { + QUALITY_HIGH, + QUALITY_GOOD + }; + int _averageN; ChannelAnalyzer* _channelA = NULL; ChannelAnalyzer* _channelB = NULL; @@ -109,6 +117,7 @@ struct Analyzer : Module { ChannelAnalyzer* _channelD = NULL; float _range = 0.0; float _smooth = 0.0; + Quality _quality = QUALITY_GOOD; const int _binAverageN = 2; Analyzer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { @@ -120,6 +129,7 @@ struct Analyzer : Module { virtual void reset() override; void resetChannels(); + SpectrumAnalyzer::Size size(); virtual void step() override; void stepChannel(ChannelAnalyzer*& channelPointer, bool running, Input& input, Output& output); }; @@ -147,6 +157,17 @@ void Analyzer::resetChannels() { } } +SpectrumAnalyzer::Size Analyzer::size() { + switch (_quality) { + case QUALITY_HIGH: { + return SpectrumAnalyzer::SIZE_4096; + } + case QUALITY_GOOD: { + return SpectrumAnalyzer::SIZE_1024; + } + } +} + void Analyzer::step() { _range = clampf(params[RANGE_PARAM].value, 0.1, 1.0); @@ -156,18 +177,28 @@ void Analyzer::step() { resetChannels(); } + Quality quality = ((int)params[QUALITY_PARAM].value) == 2 ? QUALITY_HIGH : QUALITY_GOOD; + if (_quality != quality) { + _quality = quality; + resetChannels(); + } + bool running = params[POWER_PARAM].value == 1.0; stepChannel(_channelA, running, inputs[SIGNALA_INPUT], outputs[SIGNALA_OUTPUT]); stepChannel(_channelB, running, inputs[SIGNALB_INPUT], outputs[SIGNALB_OUTPUT]); stepChannel(_channelC, running, inputs[SIGNALC_INPUT], outputs[SIGNALC_OUTPUT]); stepChannel(_channelD, running, inputs[SIGNALD_INPUT], outputs[SIGNALD_OUTPUT]); + + lights[QUALITY_HIGH_LIGHT].value = running && quality == QUALITY_HIGH; + lights[QUALITY_GOOD_LIGHT].value = running && quality == QUALITY_GOOD; + lights[POWER_ON_LIGHT].value = running; } void Analyzer::stepChannel(ChannelAnalyzer*& channelPointer, bool running, Input& input, Output& output) { if (running && input.active) { if (!channelPointer) { channelPointer = new ChannelAnalyzer( - _size, + size(), SpectrumAnalyzer::OVERLAP_2, SpectrumAnalyzer::WINDOW_HAMMING, engineGetSampleRate(), @@ -284,7 +315,7 @@ void AnalyzerDisplay::drawHeader(NVGcontext* vg) { char s[sLen]; int x = _insetAround + 2; - int n = snprintf(s, sLen, "Peaks (+/-%0.1f):", engineGetSampleRate() / (float)(_module->_size / (2 * _module->_binAverageN))); + int n = snprintf(s, sLen, "Peaks (+/-%0.1f):", engineGetSampleRate() / (float)(_module->size() / (2 * _module->_binAverageN))); drawText(vg, s, x, _insetTop + textY); x += n * charPx - 2; @@ -423,7 +454,7 @@ void AnalyzerDisplay::drawXAxisLine(NVGcontext* vg, float hz, float maxHz) { } void AnalyzerDisplay::drawGraph(NVGcontext* vg, float* bins, int binsN, NVGcolor color) { - const int pointsN = roundf(_module->_range*(_module->_size/2)); + const int pointsN = roundf(_module->_range*(_module->size()/2)); nvgSave(vg); nvgScissor(vg, _insetLeft, _insetTop, _graphSize.x, _graphSize.y); nvgStrokeColor(vg, color); @@ -507,7 +538,8 @@ AnalyzerWidget::AnalyzerWidget() { // generated by svg_widgets.rb auto rangeParamPosition = Vec(35.08, 271.08); auto smoothParamPosition = Vec(109.08, 271.08); - auto powerParamPosition = Vec(272.9, 279.9); + auto qualityParamPosition = Vec(186.02, 298.02); + auto powerParamPosition = Vec(259.02, 298.02); auto signalaInputPosition = Vec(13.5, 323.0); auto signalbInputPosition = Vec(86.5, 323.0); @@ -518,11 +550,16 @@ AnalyzerWidget::AnalyzerWidget() { auto signalbOutputPosition = Vec(115.5, 323.0); auto signalcOutputPosition = Vec(189.5, 323.0); auto signaldOutputPosition = Vec(262.5, 323.0); + + auto qualityHighLightPosition = Vec(179.0, 274.0); + auto qualityGoodLightPosition = Vec(179.0, 289.0); + auto powerOnLightPosition = Vec(252.0, 289.0); // end generated by svg_widgets.rb addParam(createParam<OneTenKnob>(rangeParamPosition, module, Analyzer::RANGE_PARAM, 0.1, 1.0, 0.5)); addParam(createParam<IntegerOneTenKnob>(smoothParamPosition, module, Analyzer::SMOOTH_PARAM, 1.0, 10.0, 5.0)); - addParam(createParam<CKSS>(powerParamPosition, module, Analyzer::POWER_PARAM, 0.0, 1.0, 1.0)); + addParam(createParam<Button9Toggle2>(qualityParamPosition, module, Analyzer::QUALITY_PARAM, 1.0, 2.0, 1.0)); + addParam(createParam<Button9Toggle2>(powerParamPosition, module, Analyzer::POWER_PARAM, 0.0, 1.0, 1.0)); addInput(createInput<PJ301MPort>(signalaInputPosition, module, Analyzer::SIGNALA_INPUT)); addInput(createInput<PJ301MPort>(signalbInputPosition, module, Analyzer::SIGNALB_INPUT)); @@ -533,4 +570,8 @@ AnalyzerWidget::AnalyzerWidget() { addOutput(createOutput<PJ301MPort>(signalbOutputPosition, module, Analyzer::SIGNALB_OUTPUT)); addOutput(createOutput<PJ301MPort>(signalcOutputPosition, module, Analyzer::SIGNALC_OUTPUT)); addOutput(createOutput<PJ301MPort>(signaldOutputPosition, module, Analyzer::SIGNALD_OUTPUT)); + + addChild(createLight<TinyLight<GreenLight>>(qualityHighLightPosition, module, Analyzer::QUALITY_HIGH_LIGHT)); + addChild(createLight<TinyLight<GreenLight>>(qualityGoodLightPosition, module, Analyzer::QUALITY_GOOD_LIGHT)); + addChild(createLight<TinyLight<GreenLight>>(powerOnLightPosition, module, Analyzer::POWER_ON_LIGHT)); } diff --git a/src/BogaudioModules.hpp b/src/BogaudioModules.hpp @@ -63,6 +63,14 @@ struct Button18 : SVGSwitch, MomentarySwitch { } }; +struct Button9Toggle2 : SVGSwitch, ToggleSwitch { + Button9Toggle2() { + addFrame(SVG::load(assetPlugin(plugin, "res/button_9px.svg"))); + addFrame(SVG::load(assetPlugin(plugin, "res/button_9px.svg"))); + box.size = Vec(9, 9); + } +}; + struct Button9Toggle3 : SVGSwitch, ToggleSwitch { Button9Toggle3() { addFrame(SVG::load(assetPlugin(plugin, "res/button_9px.svg"))); diff --git a/src/dsp/analyzer.cpp b/src/dsp/analyzer.cpp @@ -6,16 +6,32 @@ using namespace bogaudio::dsp; -typedef ffft::FFTRealFixLen<10> FFT; + +typedef ffft::FFTRealFixLen<10> FIXED_FFT1024; FFT1024::FFT1024() { - _fft = new FFT(); + _fft = new FIXED_FFT1024(); } FFT1024::~FFT1024() { - delete (FFT*)_fft; + delete (FIXED_FFT1024*)_fft; } void FFT1024::do_fft(float* out, float* in) { - ((FFT*)_fft)->do_fft(out, in); + ((FIXED_FFT1024*)_fft)->do_fft(out, in); +} + + +typedef ffft::FFTRealFixLen<12> FIXED_FFT4096; + +FFT4096::FFT4096() { + _fft = new FIXED_FFT4096(); +} + +FFT4096::~FFT4096() { + delete (FIXED_FFT4096*)_fft; +} + +void FFT4096::do_fft(float* out, float* in) { + ((FIXED_FFT4096*)_fft)->do_fft(out, in); } diff --git a/src/dsp/analyzer.hpp b/src/dsp/analyzer.hpp @@ -55,6 +55,15 @@ struct FFT1024 { }; +struct FFT4096 { + void* _fft = NULL; + FFT4096(); + ~FFT4096(); + + void do_fft(float* out, float* in); +}; + + struct SpectrumAnalyzer : OverlappingBuffer<float> { enum Size { SIZE_128 = 128, @@ -81,6 +90,7 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> { const float _sampleRate; ffft::FFTReal<float>* _fft; FFT1024* _fft1024; + FFT4096* _fft4096; Window* _window; float* _windowOut; float* _fftOut; @@ -95,6 +105,7 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> { , _sampleRate(sampleRate) , _fft(NULL) , _fft1024(NULL) + , _fft4096(NULL) , _window(NULL) , _windowOut(NULL) , _fftOut(new float[_size]) @@ -106,6 +117,10 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> { _fft1024 = new FFT1024(); break; } + case SIZE_4096: { + _fft4096 = new FFT4096(); + break; + } default: { _fft = new ffft::FFTReal<float>(size); } @@ -135,6 +150,9 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> { if (_fft1024) { delete _fft1024; } + if (_fft4096) { + delete _fft4096; + } if (_window) { delete _window; @@ -153,6 +171,9 @@ struct SpectrumAnalyzer : OverlappingBuffer<float> { if (_fft1024) { _fft1024->do_fft(_fftOut, input); } + else if (_fft4096) { + _fft4096->do_fft(_fftOut, input); + } else { _fft->do_fft(_fftOut, input); }