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");