You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

324 lines
9.2KB

  1. #include "string.h"
  2. #include "AnalyzerXL.hpp"
  3. #define RANGE_KEY "range"
  4. #define RANGE_DB_KEY "range_db"
  5. #define SMOOTH_KEY "smooth"
  6. #define QUALITY_KEY "quality"
  7. #define QUALITY_GOOD_KEY "good"
  8. #define QUALITY_HIGH_KEY "high"
  9. #define QUALITY_ULTRA_KEY "ultra"
  10. #define WINDOW_KEY "window"
  11. #define WINDOW_NONE_KEY "none"
  12. #define WINDOW_HAMMING_KEY "hamming"
  13. #define WINDOW_KAISER_KEY "kaiser"
  14. void AnalyzerXL::onReset() {
  15. _modulationStep = modulationSteps;
  16. _range = 0.0f;
  17. _smooth = 0.25f;
  18. _quality = AnalyzerCore::QUALITY_GOOD;
  19. _window = AnalyzerCore::WINDOW_KAISER;
  20. setCoreParams();
  21. _core.resetChannels();
  22. }
  23. void AnalyzerXL::onSampleRateChange() {
  24. _modulationStep = modulationSteps;
  25. setCoreParams();
  26. _core.resetChannels();
  27. }
  28. void AnalyzerXL::setCoreParams() {
  29. _rangeMinHz = 0.0f;
  30. _rangeMaxHz = 0.5f * engineGetSampleRate();
  31. if (_range < 0.0f) {
  32. _rangeMaxHz *= 1.0f + _range;
  33. }
  34. else if (_range > 0.0f) {
  35. _rangeMinHz = _range * _rangeMaxHz;
  36. }
  37. float smooth = _smooth / (_core.size() / (_core._overlap * engineGetSampleRate()));
  38. int averageN = std::max(1, (int)roundf(smooth));
  39. _core.setParams(averageN, _quality, _window);
  40. }
  41. json_t* AnalyzerXL::toJson() {
  42. json_t* root = json_object();
  43. json_object_set_new(root, RANGE_KEY, json_real(_range));
  44. json_object_set_new(root, RANGE_DB_KEY, json_real(_rangeDb));
  45. json_object_set_new(root, SMOOTH_KEY, json_real(_smooth));
  46. switch (_quality) {
  47. case AnalyzerCore::QUALITY_GOOD: {
  48. json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_GOOD_KEY));
  49. break;
  50. }
  51. case AnalyzerCore::QUALITY_HIGH: {
  52. json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_HIGH_KEY));
  53. break;
  54. }
  55. case AnalyzerCore::QUALITY_ULTRA: {
  56. json_object_set_new(root, QUALITY_KEY, json_string(QUALITY_ULTRA_KEY));
  57. break;
  58. }
  59. }
  60. switch (_window) {
  61. case AnalyzerCore::WINDOW_NONE: {
  62. json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_NONE_KEY));
  63. break;
  64. }
  65. case AnalyzerCore::WINDOW_HAMMING: {
  66. json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_HAMMING_KEY));
  67. break;
  68. }
  69. case AnalyzerCore::WINDOW_KAISER: {
  70. json_object_set_new(root, WINDOW_KEY, json_string(WINDOW_KAISER_KEY));
  71. break;
  72. }
  73. }
  74. return root;
  75. }
  76. void AnalyzerXL::fromJson(json_t* root) {
  77. json_t* jr = json_object_get(root, RANGE_KEY);
  78. if (jr) {
  79. _range = clamp(json_real_value(jr), -0.9f, 0.8f);
  80. }
  81. json_t* jrd = json_object_get(root, RANGE_DB_KEY);
  82. if (jrd) {
  83. _rangeDb = clamp(json_real_value(jrd), 80.0f, 140.0f);
  84. }
  85. json_t* js = json_object_get(root, SMOOTH_KEY);
  86. if (js) {
  87. _smooth = clamp(json_real_value(js), 0.0f, 0.5f);
  88. }
  89. json_t* jq = json_object_get(root, QUALITY_KEY);
  90. if (jq) {
  91. const char *s = json_string_value(jq);
  92. if (strcmp(s, QUALITY_GOOD_KEY) == 0) {
  93. _quality = AnalyzerCore::QUALITY_GOOD;
  94. }
  95. else if (strcmp(s, QUALITY_HIGH_KEY) == 0) {
  96. _quality = AnalyzerCore::QUALITY_HIGH;
  97. }
  98. else if (strcmp(s, QUALITY_ULTRA_KEY) == 0) {
  99. _quality = AnalyzerCore::QUALITY_ULTRA;
  100. }
  101. }
  102. json_t* jw = json_object_get(root, WINDOW_KEY);
  103. if (jw) {
  104. const char *s = json_string_value(jw);
  105. if (strcmp(s, WINDOW_NONE_KEY) == 0) {
  106. _window = AnalyzerCore::WINDOW_NONE;
  107. }
  108. else if (strcmp(s, WINDOW_HAMMING_KEY) == 0) {
  109. _window = AnalyzerCore::WINDOW_HAMMING;
  110. }
  111. else if (strcmp(s, WINDOW_KAISER_KEY) == 0) {
  112. _window = AnalyzerCore::WINDOW_KAISER;
  113. }
  114. }
  115. }
  116. void AnalyzerXL::step() {
  117. ++_modulationStep;
  118. if (_modulationStep >= modulationSteps) {
  119. _modulationStep = 0;
  120. setCoreParams();
  121. }
  122. for (int i = 0; i < 8; ++i) {
  123. _core.stepChannel(i, inputs[SIGNALA_INPUT + i]);
  124. }
  125. }
  126. struct RangeMenuItem : MenuItem {
  127. AnalyzerXL* _module;
  128. const float _range;
  129. RangeMenuItem(AnalyzerXL* module, const char* label, float range)
  130. : _module(module)
  131. , _range(range)
  132. {
  133. this->text = label;
  134. }
  135. void onAction(EventAction &e) override {
  136. _module->_range = _range;
  137. }
  138. void step() override {
  139. rightText = _module->_range == _range ? "✔" : "";
  140. }
  141. };
  142. struct RangeDbMenuItem : MenuItem {
  143. AnalyzerXL* _module;
  144. const float _rangeDb;
  145. RangeDbMenuItem(AnalyzerXL* module, const char* label, float rangeDb)
  146. : _module(module)
  147. , _rangeDb(rangeDb)
  148. {
  149. this->text = label;
  150. }
  151. void onAction(EventAction &e) override {
  152. _module->_rangeDb = _rangeDb;
  153. }
  154. void step() override {
  155. rightText = _module->_rangeDb == _rangeDb ? "✔" : "";
  156. }
  157. };
  158. struct SmoothMenuItem : MenuItem {
  159. AnalyzerXL* _module;
  160. const float _smooth;
  161. SmoothMenuItem(AnalyzerXL* module, const char* label, float smooth)
  162. : _module(module)
  163. , _smooth(smooth)
  164. {
  165. this->text = label;
  166. }
  167. void onAction(EventAction &e) override {
  168. _module->_smooth = _smooth;
  169. }
  170. void step() override {
  171. rightText = _module->_smooth == _smooth ? "✔" : "";
  172. }
  173. };
  174. struct QualityMenuItem : MenuItem {
  175. AnalyzerXL* _module;
  176. const AnalyzerCore::Quality _quality;
  177. QualityMenuItem(AnalyzerXL* module, const char* label, AnalyzerCore::Quality quality)
  178. : _module(module)
  179. , _quality(quality)
  180. {
  181. this->text = label;
  182. }
  183. void onAction(EventAction &e) override {
  184. _module->_quality = _quality;
  185. }
  186. void step() override {
  187. rightText = _module->_quality == _quality ? "✔" : "";
  188. }
  189. };
  190. struct WindowMenuItem : MenuItem {
  191. AnalyzerXL* _module;
  192. const AnalyzerCore::Window _window;
  193. WindowMenuItem(AnalyzerXL* module, const char* label, AnalyzerCore::Window window)
  194. : _module(module)
  195. , _window(window)
  196. {
  197. this->text = label;
  198. }
  199. void onAction(EventAction &e) override {
  200. _module->_window = _window;
  201. }
  202. void step() override {
  203. rightText = _module->_window == _window ? "✔" : "";
  204. }
  205. };
  206. struct AnalyzerXLWidget : ModuleWidget {
  207. static constexpr int hp = 42;
  208. AnalyzerXLWidget(AnalyzerXL* module) : ModuleWidget(module) {
  209. box.size = Vec(RACK_GRID_WIDTH * hp, RACK_GRID_HEIGHT);
  210. {
  211. SVGPanel *panel = new SVGPanel();
  212. panel->box.size = box.size;
  213. panel->setBackground(SVG::load(assetPlugin(plugin, "res/AnalyzerXL.svg")));
  214. addChild(panel);
  215. }
  216. {
  217. auto inset = Vec(30, 1);
  218. auto size = Vec(box.size.x - inset.x - 1, 378);
  219. auto display = new AnalyzerDisplay(module, size, false);
  220. display->box.pos = inset;
  221. display->box.size = size;
  222. addChild(display);
  223. }
  224. // generated by svg_widgets.rb
  225. auto signalaInputPosition = Vec(3.0, 13.0);
  226. auto signalbInputPosition = Vec(3.0, 47.0);
  227. auto signalcInputPosition = Vec(3.0, 81.0);
  228. auto signaldInputPosition = Vec(3.0, 115.0);
  229. auto signaleInputPosition = Vec(3.0, 149.0);
  230. auto signalfInputPosition = Vec(3.0, 183.0);
  231. auto signalgInputPosition = Vec(3.0, 217.0);
  232. auto signalhInputPosition = Vec(3.0, 251.0);
  233. // end generated by svg_widgets.rb
  234. addInput(Port::create<Port24>(signalaInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALA_INPUT));
  235. addInput(Port::create<Port24>(signalbInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALB_INPUT));
  236. addInput(Port::create<Port24>(signalcInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALC_INPUT));
  237. addInput(Port::create<Port24>(signaldInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALD_INPUT));
  238. addInput(Port::create<Port24>(signaleInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALE_INPUT));
  239. addInput(Port::create<Port24>(signalfInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALF_INPUT));
  240. addInput(Port::create<Port24>(signalgInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALG_INPUT));
  241. addInput(Port::create<Port24>(signalhInputPosition, Port::INPUT, module, AnalyzerXL::SIGNALH_INPUT));
  242. }
  243. void appendContextMenu(Menu* menu) override {
  244. AnalyzerXL* a = dynamic_cast<AnalyzerXL*>(module);
  245. assert(a);
  246. menu->addChild(new MenuLabel());
  247. menu->addChild(new RangeMenuItem(a, "Frequency range: lower 25%", -0.75f));
  248. menu->addChild(new RangeMenuItem(a, "Frequency range: lower 50%", -0.5f));
  249. menu->addChild(new RangeMenuItem(a, "Frequency range: full", 0.0f));
  250. menu->addChild(new RangeMenuItem(a, "Frequency range: upper 50%", 0.5f));
  251. menu->addChild(new RangeMenuItem(a, "Frequency range: upper 25%", 0.75f));
  252. menu->addChild(new MenuLabel());
  253. menu->addChild(new RangeDbMenuItem(a, "Amplitude range: to -60dB", 80.0f));
  254. menu->addChild(new RangeDbMenuItem(a, "Amplitude range: to -120dB", 140.0f));
  255. menu->addChild(new MenuLabel());
  256. menu->addChild(new SmoothMenuItem(a, "Smooth: none", 0.0f));
  257. menu->addChild(new SmoothMenuItem(a, "Smooth: 10ms", 0.01f));
  258. menu->addChild(new SmoothMenuItem(a, "Smooth: 50ms", 0.05f));
  259. menu->addChild(new SmoothMenuItem(a, "Smooth: 100ms", 0.1f));
  260. menu->addChild(new SmoothMenuItem(a, "Smooth: 250ms", 0.25f));
  261. menu->addChild(new SmoothMenuItem(a, "Smooth: 500ms", 0.5f));
  262. menu->addChild(new MenuLabel());
  263. menu->addChild(new QualityMenuItem(a, "Quality: good", AnalyzerCore::QUALITY_GOOD));
  264. menu->addChild(new QualityMenuItem(a, "Quality: high", AnalyzerCore::QUALITY_HIGH));
  265. menu->addChild(new QualityMenuItem(a, "Quality: ultra", AnalyzerCore::QUALITY_ULTRA));
  266. menu->addChild(new MenuLabel());
  267. menu->addChild(new WindowMenuItem(a, "Window: Kaiser", AnalyzerCore::WINDOW_KAISER));
  268. menu->addChild(new WindowMenuItem(a, "Window: Hamming", AnalyzerCore::WINDOW_HAMMING));
  269. menu->addChild(new WindowMenuItem(a, "Window: none", AnalyzerCore::WINDOW_NONE));
  270. }
  271. };
  272. RACK_PLUGIN_MODEL_INIT(Bogaudio, AnalyzerXL) {
  273. Model *modelAnalyzerXL = createModel<AnalyzerXL, AnalyzerXLWidget>("Bogaudio-AnalyzerXL", "Analyzer-XL", "spectrum analyzer", VISUAL_TAG);
  274. return modelAnalyzerXL;
  275. }