BogaudioModules

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

AnalyzerXL.cpp (7480B)


      1 
      2 #include "string.h"
      3 
      4 #include "AnalyzerXL.hpp"
      5 
      6 #define SMOOTH_KEY "smooth"
      7 #define QUALITY_KEY "quality"
      8 #define QUALITY_GOOD_KEY "good"
      9 #define QUALITY_HIGH_KEY "high"
     10 #define QUALITY_ULTRA_KEY "ultra"
     11 #define QUALITY_ULTRA_ULTRA_KEY "ultra_ultra"
     12 #define WINDOW_KEY "window"
     13 #define WINDOW_NONE_KEY "none"
     14 #define WINDOW_HAMMING_KEY "hamming"
     15 #define WINDOW_KAISER_KEY "kaiser"
     16 
     17 void AnalyzerXL::reset() {
     18 	_core.resetChannels();
     19 }
     20 
     21 void AnalyzerXL::sampleRateChange() {
     22 	_sampleRate = APP->engine->getSampleRate();
     23 }
     24 
     25 json_t* AnalyzerXL::saveToJson(json_t* root) {
     26 	frequencyPlotToJson(root);
     27 	frequencyRangeToJson(root);
     28 	amplitudePlotToJson(root);
     29 	json_object_set_new(root, SMOOTH_KEY, json_real(_smooth));
     30 
     31 	switch (_quality) {
     32 		case AnalyzerCore::QUALITY_GOOD: {
     33 			json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_GOOD_KEY));
     34 			break;
     35 		}
     36 		case AnalyzerCore::QUALITY_HIGH: {
     37 			json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_HIGH_KEY));
     38 			break;
     39 		}
     40 		case AnalyzerCore::QUALITY_ULTRA: {
     41 			json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_ULTRA_KEY));
     42 			break;
     43 		}
     44 		case AnalyzerCore::QUALITY_ULTRA_ULTRA: {
     45 			json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_ULTRA_ULTRA_KEY));
     46 			break;
     47 		}
     48 		default:;
     49 	}
     50 
     51 	switch (_window) {
     52 		case AnalyzerCore::WINDOW_NONE: {
     53 			json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_NONE_KEY));
     54 			break;
     55 		}
     56 		case AnalyzerCore::WINDOW_HAMMING: {
     57 			json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_HAMMING_KEY));
     58 			break;
     59 		}
     60 		case AnalyzerCore::WINDOW_KAISER: {
     61 			json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_KAISER_KEY));
     62 			break;
     63 		}
     64 	}
     65 
     66 	return root;
     67 }
     68 
     69 void AnalyzerXL::loadFromJson(json_t* root) {
     70 	frequencyPlotFromJson(root);
     71 	frequencyRangeFromJson(root);
     72 	amplitudePlotFromJson(root);
     73 
     74 	json_t* js = json_object_get(root, SMOOTH_KEY);
     75 	if (js) {
     76 		_smooth = clamp(json_real_value(js), 0.0f, 0.5f);
     77 	}
     78 
     79 	json_t* jq = json_object_get(root, QUALITY_KEY);
     80 	if (jq) {
     81 		const char *s = json_string_value(jq);
     82 		if (strcmp(s, QUALITY_GOOD_KEY) == 0) {
     83 			_quality = AnalyzerCore::QUALITY_GOOD;
     84 		}
     85 		else if (strcmp(s, QUALITY_HIGH_KEY) == 0) {
     86 			_quality = AnalyzerCore::QUALITY_HIGH;
     87 		}
     88 		else if (strcmp(s, QUALITY_ULTRA_KEY) == 0) {
     89 			_quality = AnalyzerCore::QUALITY_ULTRA;
     90 		}
     91 		else if (strcmp(s, QUALITY_ULTRA_ULTRA_KEY) == 0) {
     92 			_quality = AnalyzerCore::QUALITY_ULTRA_ULTRA;
     93 		}
     94 	}
     95 
     96 	json_t* jw = json_object_get(root, WINDOW_KEY);
     97 	if (jw) {
     98 		const char *s = json_string_value(jw);
     99 		if (strcmp(s, WINDOW_NONE_KEY) == 0) {
    100 			_window = AnalyzerCore::WINDOW_NONE;
    101 		}
    102 		else if (strcmp(s, WINDOW_HAMMING_KEY) == 0) {
    103 			_window = AnalyzerCore::WINDOW_HAMMING;
    104 		}
    105 		else if (strcmp(s, WINDOW_KAISER_KEY) == 0) {
    106 			_window = AnalyzerCore::WINDOW_KAISER;
    107 		}
    108 	}
    109 }
    110 
    111 void AnalyzerXL::modulate() {
    112 	_rangeMinHz = 0.0f;
    113 	_rangeMaxHz = 0.5f * _sampleRate;
    114 	if (_range < 0.0f) {
    115 		_rangeMaxHz *= 1.0f + _range;
    116 	}
    117 	else if (_range > 0.0f) {
    118 		_rangeMinHz = _range * _rangeMaxHz;
    119 	}
    120 
    121 	float smooth = _smooth / (_core.size() / (_core._overlap * _sampleRate));
    122 	int averageN = std::max(1, (int)roundf(smooth));
    123 	_core.setParams(_sampleRate, averageN, _quality, _window);
    124 }
    125 
    126 void AnalyzerXL::processAll(const ProcessArgs& args) {
    127 	for (int i = 0; i < 8; ++i) {
    128 		_core.stepChannel(i, inputs[SIGNALA_INPUT + i]);
    129 	}
    130 }
    131 
    132 struct AnalyzerXLWidget : AnalyzerBaseWidget {
    133 	static constexpr int hp = 42;
    134 
    135 	AnalyzerXLWidget(AnalyzerXL* module) {
    136 		setModule(module);
    137 		box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
    138 		setPanel(box.size, "AnalyzerXL", false);
    139 
    140 		{
    141 			auto inset = Vec(30, 1);
    142 			auto size = Vec(box.size.x - inset.x - 1, 378);
    143 			auto display = new AnalyzerDisplay(module, size, false);
    144 			display->box.pos = inset;
    145 			display->box.size = size;
    146 			addChild(display);
    147 		}
    148 
    149 		// generated by svg_widgets.rb
    150 		auto signalaInputPosition = Vec(3.0, 13.0);
    151 		auto signalbInputPosition = Vec(3.0, 47.0);
    152 		auto signalcInputPosition = Vec(3.0, 81.0);
    153 		auto signaldInputPosition = Vec(3.0, 115.0);
    154 		auto signaleInputPosition = Vec(3.0, 149.0);
    155 		auto signalfInputPosition = Vec(3.0, 183.0);
    156 		auto signalgInputPosition = Vec(3.0, 217.0);
    157 		auto signalhInputPosition = Vec(3.0, 251.0);
    158 		// end generated by svg_widgets.rb
    159 
    160 		addInput(createInput<Port24>(signalaInputPosition, module, AnalyzerXL::SIGNALA_INPUT));
    161 		addInput(createInput<Port24>(signalbInputPosition, module, AnalyzerXL::SIGNALB_INPUT));
    162 		addInput(createInput<Port24>(signalcInputPosition, module, AnalyzerXL::SIGNALC_INPUT));
    163 		addInput(createInput<Port24>(signaldInputPosition, module, AnalyzerXL::SIGNALD_INPUT));
    164 		addInput(createInput<Port24>(signaleInputPosition, module, AnalyzerXL::SIGNALE_INPUT));
    165 		addInput(createInput<Port24>(signalfInputPosition, module, AnalyzerXL::SIGNALF_INPUT));
    166 		addInput(createInput<Port24>(signalgInputPosition, module, AnalyzerXL::SIGNALG_INPUT));
    167 		addInput(createInput<Port24>(signalhInputPosition, module, AnalyzerXL::SIGNALH_INPUT));
    168 	}
    169 
    170 	void contextMenu(Menu* menu) override {
    171 		auto a = dynamic_cast<AnalyzerXL*>(module);
    172 		assert(a);
    173 
    174 		menu->addChild(new MenuLabel());
    175 		addFrequencyPlotContextMenu(menu);
    176 		addFrequencyRangeContextMenu(menu);
    177 		addAmplitudePlotContextMenu(menu);
    178 		{
    179 			OptionsMenuItem* mi = new OptionsMenuItem("Smoothing");
    180 			mi->addItem(OptionMenuItem("None", [a]() { return a->_smooth == 0.0f; }, [a]() { a->_smooth = 0.0f; }));
    181 			mi->addItem(OptionMenuItem("10ms", [a]() { return a->_smooth == 0.01f; }, [a]() { a->_smooth = 0.01f; }));
    182 			mi->addItem(OptionMenuItem("50ms", [a]() { return a->_smooth == 0.05f; }, [a]() { a->_smooth = 0.05f; }));
    183 			mi->addItem(OptionMenuItem("100ms", [a]() { return a->_smooth == 0.1f; }, [a]() { a->_smooth = 0.1f; }));
    184 			mi->addItem(OptionMenuItem("250ms", [a]() { return a->_smooth == 0.25f; }, [a]() { a->_smooth = 0.25f; }));
    185 			mi->addItem(OptionMenuItem("500ms", [a]() { return a->_smooth == 0.5f; }, [a]() { a->_smooth = 0.5f; }));
    186 			OptionsMenuItem::addToMenu(mi, menu);
    187 		}
    188 		{
    189 			OptionsMenuItem* mi = new OptionsMenuItem("Quality");
    190 			mi->addItem(OptionMenuItem("Good", [a]() { return a->_quality == AnalyzerCore::QUALITY_GOOD; }, [a]() { a->_quality = AnalyzerCore::QUALITY_GOOD; }));
    191 			mi->addItem(OptionMenuItem("High", [a]() { return a->_quality == AnalyzerCore::QUALITY_HIGH; }, [a]() { a->_quality = AnalyzerCore::QUALITY_HIGH; }));
    192 			mi->addItem(OptionMenuItem("Ultra", [a]() { return a->_quality == AnalyzerCore::QUALITY_ULTRA; }, [a]() { a->_quality = AnalyzerCore::QUALITY_ULTRA; }));
    193 			mi->addItem(OptionMenuItem("Ultra+", [a]() { return a->_quality == AnalyzerCore::QUALITY_ULTRA_ULTRA; }, [a]() { a->_quality = AnalyzerCore::QUALITY_ULTRA_ULTRA; }));
    194 			OptionsMenuItem::addToMenu(mi, menu);
    195 		}
    196 		{
    197 			OptionsMenuItem* mi = new OptionsMenuItem("Window");
    198 			mi->addItem(OptionMenuItem("Kaiser", [a]() { return a->_window == AnalyzerCore::WINDOW_KAISER; }, [a]() { a->_window = AnalyzerCore::WINDOW_KAISER; }));
    199 			mi->addItem(OptionMenuItem("Hamming", [a]() { return a->_window == AnalyzerCore::WINDOW_HAMMING; }, [a]() { a->_window = AnalyzerCore::WINDOW_HAMMING; }));
    200 			mi->addItem(OptionMenuItem("None", [a]() { return a->_window == AnalyzerCore::WINDOW_NONE; }, [a]() { a->_window = AnalyzerCore::WINDOW_NONE; }));
    201 			OptionsMenuItem::addToMenu(mi, menu);
    202 		}
    203 	}
    204 };
    205 
    206 Model* modelAnalyzerXL = bogaudio::createModel<AnalyzerXL, AnalyzerXLWidget>("Bogaudio-AnalyzerXL", "ANALYZER-XL", "8-channel spectrum analyzer", "Visual");