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.

840 lines
28KB

  1. #ifndef MODULE_MULTISCOPE_HPP
  2. #define MODULE_MULTISCOPE_HPP
  3. #include "Features.hpp"
  4. #if USE_NEW_SCOPE
  5. #include <string.h>
  6. #include <stdint.h>
  7. #include "trowaSoft.hpp"
  8. #include "trowaSoftComponents.hpp"
  9. #include "trowaSoftUtilities.hpp"
  10. #include "dsp/digital.hpp"
  11. #include "TSScopeBase.hpp"
  12. // For now, the color picker is disabled in Apple because it will crash Rack & Eat your soul...
  13. // Linux & Windows seem fine though
  14. // Now it seems the scope just crashes Rack on OSX no matter what.
  15. #define ENABLE_BG_COLOR_PICKER (!(__APPLE__))
  16. #define TROWA_SCOPE_NUM_WAVEFORMS 3
  17. // Laying out controls
  18. #define TROWA_SCOPE_CONTROL_START_X 47 // 47
  19. #define TROWA_SCOPE_CONTROL_START_Y 40 // 94
  20. #define TROWA_SCOPE_CONTROL_DX 35 // 35
  21. #define TROWA_SCOPE_CONTROL_DY 30 // 26
  22. #define TROWA_SCOPE_CONTROL_SHAPE_SPACING 16 // 8
  23. // Labels for each waveform / shape
  24. #define TROWA_SCOPE_SHAPE_FORMAT_STRING "Shp %d"
  25. // multiScope model.
  26. extern Model *modelMultiScope;
  27. //===============================================================================
  28. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  29. // multiScope
  30. // Scope module that draws multiple waveforms.
  31. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  32. //===============================================================================
  33. struct multiScope : Module {
  34. // Should probably make this linearly growing in case of course, we add more freaken waveforms...
  35. // TODO: Refactor to make variable num waveforms.
  36. enum ParamIds {
  37. COLOR_PARAM,
  38. EXTERNAL_PARAM = COLOR_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  39. ROTATION_PARAM = EXTERNAL_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  40. ROTATION_MODE_PARAM = ROTATION_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  41. TIME_PARAM = ROTATION_MODE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  42. TRIG_PARAM = TIME_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  43. X_POS_PARAM = TRIG_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  44. X_SCALE_PARAM = X_POS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  45. Y_POS_PARAM = X_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  46. Y_SCALE_PARAM = Y_POS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  47. LINK_XY_SCALE_PARAM = Y_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS, // Force Scale X = Scale Y.
  48. OPACITY_PARAM = LINK_XY_SCALE_PARAM + TROWA_SCOPE_NUM_WAVEFORMS, // Alpha channel
  49. LISSAJOUS_PARAM = OPACITY_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  50. THICKNESS_PARAM = LISSAJOUS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  51. FILL_ON_PARAM = THICKNESS_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  52. FILL_COLOR_PARAM = FILL_ON_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  53. FILL_OPACITY_PARAM = FILL_COLOR_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  54. EFFECT_PARAM = FILL_OPACITY_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  55. // Single Inputs:
  56. // Info display
  57. INFO_DISPLAY_TOGGLE_PARAM = EFFECT_PARAM + TROWA_SCOPE_NUM_WAVEFORMS,
  58. // BG Color Display
  59. BGCOLOR_DISPLAY_PARAM = INFO_DISPLAY_TOGGLE_PARAM + 1,
  60. NUM_PARAMS = BGCOLOR_DISPLAY_PARAM + 1
  61. };
  62. enum InputIds {
  63. COLOR_INPUT,
  64. ROTATION_INPUT = COLOR_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  65. TIME_INPUT = ROTATION_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  66. X_INPUT = TIME_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  67. Y_INPUT = X_INPUT+TROWA_SCOPE_NUM_WAVEFORMS,
  68. OPACITY_INPUT = Y_INPUT + TROWA_SCOPE_NUM_WAVEFORMS, // Opacity/Alpha channel
  69. PEN_ON_INPUT = OPACITY_INPUT + TROWA_SCOPE_NUM_WAVEFORMS, // Turn on/off drawing lines in between points.
  70. THICKNESS_INPUT = PEN_ON_INPUT + TROWA_SCOPE_NUM_WAVEFORMS,
  71. FILL_COLOR_INPUT = THICKNESS_INPUT + TROWA_SCOPE_NUM_WAVEFORMS,
  72. FILL_OPACITY_INPUT = FILL_COLOR_INPUT + TROWA_SCOPE_NUM_WAVEFORMS,
  73. NUM_INPUTS = FILL_OPACITY_INPUT + TROWA_SCOPE_NUM_WAVEFORMS
  74. };
  75. enum LightIds {
  76. COLOR_LED,
  77. ROT_LED = COLOR_LED+TROWA_SCOPE_NUM_WAVEFORMS,
  78. LINK_XY_SCALE_LED = ROT_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  79. LISSAJOUS_LED = LINK_XY_SCALE_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  80. HIGHLIGHT_LED = LISSAJOUS_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  81. FILL_ON_LED = HIGHLIGHT_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  82. INFO_DISPLAY_TOGGLE_LED = FILL_ON_LED + TROWA_SCOPE_NUM_WAVEFORMS,
  83. BGCOLOR_DISPLAY_LED = INFO_DISPLAY_TOGGLE_LED + 1,
  84. NUM_LEDS = BGCOLOR_DISPLAY_LED + 1
  85. };
  86. enum OutputIds {
  87. NUM_OUTPUTS
  88. };
  89. bool initialized = false;
  90. bool firstLoad = true;
  91. SchmittTrigger infoDisplayOnTrigger;
  92. bool negativeImage = false;
  93. // Background color for our scope/plotting area
  94. NVGcolor plotBackgroundColor;
  95. SchmittTrigger plotBackgroundDisplayOnTrigger;
  96. bool showColorPicker = false;
  97. // Current Color Picker Color (ptr to which color we are editing)
  98. NVGcolor* editColorPointer;
  99. // Information about what we are plotting. In future may become dynamically allocated.
  100. TSWaveform* waveForms[TROWA_SCOPE_NUM_WAVEFORMS];
  101. multiScope();
  102. ~multiScope();
  103. void step() override;
  104. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  105. // toJson(void)
  106. // Save to json.
  107. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  108. json_t *toJson() override {
  109. // Really should just serialize the TSWaveForm object.
  110. json_t *rootJ = json_object();
  111. json_t *huesJ = json_array();
  112. json_t *fillHuesJ = json_array();
  113. json_t *fillOnJ = json_array();
  114. json_t *linkXYScalesJ = json_array();
  115. json_t* lissajousJ = json_array();
  116. json_t *rotModeJ = json_array();
  117. json_t* gEffectsIxJ = json_array();
  118. json_t* waveColorJ = json_array();
  119. json_t* waveFillColorJ = json_array();
  120. json_t* waveDoFillJ = json_array();
  121. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  122. {
  123. json_t* itemJ = json_real(waveForms[wIx]->waveHue);
  124. json_array_append_new(huesJ, itemJ);
  125. itemJ = json_real(waveForms[wIx]->fillHue);
  126. json_array_append_new(fillHuesJ, itemJ);
  127. itemJ = json_integer(waveForms[wIx]->doFill);
  128. json_array_append_new(fillOnJ, itemJ);
  129. itemJ = json_integer((int)waveForms[wIx]->linkXYScales);
  130. json_array_append_new(linkXYScalesJ, itemJ);
  131. itemJ = json_integer((int)waveForms[wIx]->lissajous);
  132. json_array_append_new(lissajousJ, itemJ);
  133. itemJ = json_integer(waveForms[wIx]->rotMode);
  134. json_array_append_new(rotModeJ, itemJ);
  135. itemJ = json_integer((int)waveForms[wIx]->gEffectIx);
  136. json_array_append_new(gEffectsIxJ, itemJ);
  137. itemJ = json_integer((int)waveForms[wIx]->doFill);
  138. json_array_append_new(waveDoFillJ, itemJ);
  139. json_t* colorArr = json_array();
  140. json_t* fillColorArr = json_array();
  141. for (int i = 0; i < 3; i++)
  142. {
  143. itemJ = json_real(waveForms[wIx]->waveColor.rgba[i]);
  144. json_array_append(colorArr, itemJ);
  145. itemJ = json_real(waveForms[wIx]->fillColor.rgba[i]);
  146. json_array_append(fillColorArr, itemJ);
  147. }
  148. json_array_append(waveColorJ, colorArr);
  149. json_array_append(waveFillColorJ, fillColorArr);
  150. }
  151. json_object_set_new(rootJ, "hues", huesJ);
  152. json_object_set_new(rootJ, "fillHues", fillHuesJ);
  153. json_object_set_new(rootJ, "fillOn", huesJ);
  154. json_object_set_new(rootJ, "linkXYScales", linkXYScalesJ);
  155. json_object_set_new(rootJ, "lissajous", lissajousJ);
  156. json_object_set_new(rootJ, "rotMode", rotModeJ);
  157. json_object_set_new(rootJ, "gEffectsIx", gEffectsIxJ);
  158. json_object_set_new(rootJ, "waveColor", waveColorJ);
  159. json_object_set_new(rootJ, "waveFillColor", waveFillColorJ);
  160. json_object_set_new(rootJ, "waveDoFill", waveDoFillJ);
  161. // Background color:
  162. json_t* bgColorJ = json_array();
  163. for (int i = 0; i < 3; i++)
  164. {
  165. json_t* itemJ = json_real(plotBackgroundColor.rgba[i]);
  166. json_array_append_new(bgColorJ, itemJ);
  167. }
  168. json_object_set_new(rootJ, "bgColor", bgColorJ);
  169. //debug("background color saved to json (rgb): %0.2f, %0.2f, %0.2f", plotBackgroundColor.r, plotBackgroundColor.g, plotBackgroundColor.b);
  170. return rootJ;
  171. }
  172. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  173. // fromJson(void)
  174. // Load settings.
  175. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  176. void fromJson(json_t *rootJ) override {
  177. json_t *huesJ = json_object_get(rootJ, "hues");
  178. json_t *rotModeJ = json_object_get(rootJ, "rotMode");
  179. json_t *linkXYScalesJ = json_object_get(rootJ, "linkXYScales");
  180. json_t* lissajousJ = json_object_get(rootJ, "lissajous");
  181. json_t* gEffectsIxJ = json_object_get(rootJ, "gEffectsIx");
  182. json_t* fillHuesJ = json_object_get(rootJ, "fillHues");
  183. json_t* fillOnJ = json_object_get(rootJ, "fillOn");
  184. json_t* waveColorJ = json_object_get(rootJ, "waveColor");
  185. json_t* waveFillColorJ = json_object_get(rootJ, "waveFillColor");
  186. json_t* waveDoFillJ = json_object_get(rootJ, "waveDoFill");
  187. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  188. {
  189. json_t* itemJ = json_array_get(huesJ, wIx);
  190. if (itemJ)
  191. waveForms[wIx]->setHue((float)json_real_value(itemJ));
  192. itemJ = NULL;
  193. itemJ = json_array_get(fillHuesJ, wIx);
  194. if (itemJ)
  195. waveForms[wIx]->setFillHue((float)json_real_value(itemJ));
  196. itemJ = NULL;
  197. itemJ = json_array_get(fillOnJ, wIx);
  198. if (itemJ)
  199. waveForms[wIx]->doFill = (bool)json_integer_value(itemJ);
  200. itemJ = NULL;
  201. itemJ = json_array_get(rotModeJ, wIx);
  202. if (itemJ)
  203. waveForms[wIx]->rotMode = json_integer_value(itemJ);
  204. itemJ = NULL;
  205. itemJ = json_array_get(linkXYScalesJ, wIx);
  206. if (itemJ)
  207. waveForms[wIx]->linkXYScales = (bool)json_integer_value(itemJ);
  208. itemJ = NULL;
  209. itemJ = json_array_get(lissajousJ, wIx);
  210. if (itemJ)
  211. waveForms[wIx]->lissajous = (bool)json_integer_value(itemJ);
  212. else
  213. waveForms[wIx]->lissajous = true;
  214. itemJ = NULL;
  215. itemJ = json_array_get(gEffectsIxJ, wIx);
  216. if (itemJ)
  217. waveForms[wIx]->gEffectIx = (int)clamp((int)json_integer_value(itemJ), 0, TROWA_SCOPE_NUM_EFFECTS - 1);
  218. itemJ = NULL;
  219. itemJ = json_array_get(waveDoFillJ, wIx);
  220. if (itemJ)
  221. waveForms[wIx]->doFill = (bool)json_integer_value(itemJ);
  222. itemJ = NULL;
  223. json_t* colorArrJ = json_array_get(waveColorJ, wIx);
  224. json_t* fillColorArrJ = json_array_get(waveFillColorJ, wIx);
  225. for (int i = 0; i < 3; i++)
  226. {
  227. itemJ = json_array_get(colorArrJ, i);
  228. if (itemJ)
  229. waveForms[wIx]->waveColor.rgba[i] = (float)json_real_value(itemJ);
  230. itemJ = NULL;
  231. itemJ = json_array_get(fillColorArrJ, i);
  232. if (itemJ)
  233. waveForms[wIx]->fillColor.rgba[i] = (float)json_real_value(itemJ);
  234. itemJ = NULL;
  235. }
  236. waveForms[wIx]->waveColor.a = 1.0;
  237. waveForms[wIx]->fillColor.a = 1.0;
  238. }
  239. // Background color:
  240. json_t* bgColorJ = json_object_get(rootJ, "bgColor");
  241. for (int i = 0; i < 3; i++)
  242. {
  243. json_t* itemJ = json_array_get(bgColorJ, i);
  244. if (itemJ)
  245. plotBackgroundColor.rgba[i] = (float)(json_real_value(itemJ));
  246. }
  247. //info("BG COLOR loaded from json (rgb): %0.2f, %0.2f, %0.2f", plotBackgroundColor.r, plotBackgroundColor.g, plotBackgroundColor.b);
  248. plotBackgroundColor.a = 1.0f;
  249. firstLoad = true;
  250. return;
  251. }
  252. void reset() override {
  253. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  254. {
  255. waveForms[wIx]->doFill = false;
  256. waveForms[wIx]->linkXYScales = false; // Added
  257. waveForms[wIx]->rotMode = false; // Added
  258. waveForms[wIx]->lissajous = true;
  259. }
  260. }
  261. };
  262. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  263. // TSScopeDisplay
  264. // A top digital display for trowaSoft scope.
  265. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  266. struct TSScopeDisplay : TransparentWidget {
  267. multiScope *module;
  268. std::shared_ptr<Font> font;
  269. std::shared_ptr<Font> labelFont;
  270. int fontSize;
  271. char messageStr[TROWA_DISP_MSG_SIZE];
  272. bool visible = true;
  273. int originalWidth = 240;
  274. TSScopeDisplay() {
  275. visible = true;
  276. font = Font::load(assetPlugin(plugin, TROWA_DIGITAL_FONT));
  277. labelFont = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  278. fontSize = 12;
  279. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  280. messageStr[i] = '\0';
  281. }
  282. void draw(NVGcontext *vg) override {
  283. if (!visible)
  284. return; // Don't draw anything if we are not visible.
  285. bool isPreview = module == NULL; // May have NULL module? Make sure we don't just eat it.
  286. nvgSave(vg);
  287. Rect b = Rect(Vec(0, 0), box.size);
  288. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  289. // Default Font:
  290. nvgFontSize(vg, fontSize);
  291. nvgTextLetterSpacing(vg, 1);
  292. // Background Colors:
  293. NVGcolor backgroundColor = nvgRGBA(0x20, 0x20, 0x20, 0x80);
  294. NVGcolor borderColor = nvgRGBA(0x10, 0x10, 0x10, 0x80);
  295. // Screen:
  296. nvgBeginPath(vg);
  297. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  298. nvgFillColor(vg, backgroundColor);
  299. nvgFill(vg);
  300. nvgStrokeWidth(vg, 1.0);
  301. nvgStrokeColor(vg, borderColor);
  302. nvgStroke(vg);
  303. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  304. ////////////// Labels /////////////////
  305. const int yStart = 10;
  306. int xStart = 10;
  307. const int dxRotation = 3; // 10
  308. const int dxEffect = 12;
  309. int y = yStart;
  310. int x = xStart;
  311. int dx = 59; //41 // 37; // 35
  312. int dy = 16; //14
  313. //nvgFontSize(vg, fontSize); // Small font
  314. nvgFontFaceId(vg, labelFont->handle);
  315. nvgFillColor(vg, textColor);
  316. // Row Labels
  317. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  318. y = yStart + 5;
  319. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  320. {
  321. NVGcolor currColor = (isPreview) ? COLOR_RED : module->waveForms[wIx]->waveColor;
  322. nvgFillColor(vg, currColor);
  323. sprintf(messageStr, "S%d", wIx + 1);
  324. nvgText(vg, 5, y, messageStr, NULL);
  325. y += dy;
  326. }
  327. // Column Labels:
  328. nvgTextAlign(vg, NVG_ALIGN_RIGHT);
  329. nvgFillColor(vg, textColor);
  330. // Column 1 (Labels)
  331. y = yStart;
  332. xStart = 35; // 40
  333. x = xStart + dx / 2.0;
  334. nvgText(vg, x, y, "X Offset", NULL);
  335. x += dx;
  336. nvgText(vg, x, y, "X Scale", NULL);
  337. x += dx;
  338. nvgText(vg, x, y, "Y Offset", NULL);
  339. x += dx;
  340. nvgText(vg, x, y, "Y Scale", NULL);
  341. // Rotation (wider)
  342. x += dx + dxRotation;
  343. nvgText(vg, x, y, "Rotate", NULL);
  344. // Effect (wider)
  345. x += (dx + dxEffect)/2.0;
  346. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  347. nvgText(vg, x, y, "Effect", NULL);
  348. // Values:
  349. y = yStart + 5;
  350. NVGcolor absRotColor = TROWA_SCOPE_ABS_ROT_ON_COLOR;
  351. absRotColor.a = 0.50;
  352. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  353. {
  354. // NVGcolor currColor = module->waveForms[wIx]->waveColor;
  355. nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
  356. float val = 0.0f;
  357. // X Offset
  358. x = xStart + dx / 2.0;
  359. val = (isPreview) ? 0.0f : module->params[multiScope::X_POS_PARAM + wIx].value;
  360. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, val);
  361. nvgText(vg, x, y, messageStr, NULL);
  362. // X Gain
  363. x += dx;
  364. val = (isPreview) ? 1.0f : module->params[multiScope::X_SCALE_PARAM + wIx].value;
  365. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, val);
  366. nvgText(vg, x, y, messageStr, NULL);
  367. // Y Offset
  368. x += dx;
  369. val = (isPreview) ? 0.0f : module->params[multiScope::Y_POS_PARAM + wIx].value;
  370. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, val);
  371. nvgText(vg, x, y, messageStr, NULL);
  372. // Y Gain
  373. x += dx;
  374. val = (isPreview) ? 1.0f : module->params[multiScope::Y_SCALE_PARAM + wIx].value;
  375. sprintf(messageStr, TROWA_SCOPE_ROUND_FORMAT, val);
  376. nvgText(vg, x, y, messageStr, NULL);
  377. // Rotation
  378. x += dx + dxRotation;
  379. float v = 0.0;
  380. if (module->waveForms[wIx]->rotMode)
  381. {
  382. // Absolute
  383. v = (isPreview) ? 0.0 : module->waveForms[wIx]->rotAbsValue;
  384. // Background:
  385. nvgBeginPath(vg);
  386. nvgRoundedRect(vg, x - dx + 2, y - 2, dx + dxRotation - 2, fontSize + 2, 2);
  387. nvgFillColor(vg, absRotColor);
  388. nvgFill(vg);
  389. // Text will be black for this
  390. nvgFillColor(vg, COLOR_BLACK);
  391. sprintf(messageStr, "%.1f", v * 180.0 / NVG_PI);
  392. }
  393. else
  394. {
  395. // Differential
  396. v = (isPreview) ? 0.0 : module->waveForms[wIx]->rotDiffValue;
  397. sprintf(messageStr, "%+.1f", v * 180.0 / NVG_PI);
  398. }
  399. nvgText(vg, x, y, messageStr, NULL);
  400. // Effect
  401. x += (dx + dxEffect)/2.0;
  402. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
  403. nvgFillColor(vg, textColor);
  404. int gIx = (isPreview) ? 0 : module->waveForms[wIx]->gEffectIx;
  405. nvgText(vg, x, y, SCOPE_GLOBAL_EFFECTS[gIx]->label, NULL);
  406. // Advance y to next
  407. y += dy;
  408. } // end loop through wave forms
  409. nvgResetScissor(vg);
  410. nvgRestore(vg);
  411. return;
  412. }
  413. }; // end TSScopeDisplay
  414. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  415. // multiScopeDisplay
  416. // Draws a waveform.
  417. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  418. struct multiScopeDisplay : TransparentWidget {
  419. multiScope *module;
  420. int frame = 0;
  421. float rot = 0;
  422. std::shared_ptr<Font> font;
  423. int wIx = 0; // Waveform index
  424. multiScopeDisplay() {
  425. //spoutInitSpout();
  426. return;
  427. }
  428. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  429. // drawWaveform()
  430. // @vg : (IN) NVGcontext
  431. // @valX: (IN) Pointer to x values.
  432. // @valY: (IN) Pointer to y values.
  433. // @rotRate: (IN) Rotation rate in radians
  434. // @lineThickness: (IN) Line thickness
  435. // @compositeOp: (IN) Some global effect if any
  436. // @flipX: (IN) Flip along x (at x=0)
  437. // @flipY: (IN) Flip along y
  438. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  439. void drawWaveform(NVGcontext *vg, float *valX, float *valY, bool* penOn,
  440. float rotRate, float lineThickness, NVGcolor lineColor,
  441. bool doFill, NVGcolor fillColor,
  442. NVGcompositeOperation compositeOp, bool flipX, bool flipY);
  443. void draw(NVGcontext *vg) override {
  444. if (module == NULL || !module->initialized)
  445. return;
  446. float gainX = ((int)(module->params[multiScope::X_SCALE_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  447. float gainY = ((int)(module->params[multiScope::Y_SCALE_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  448. float offsetX = ((int)(module->params[multiScope::X_POS_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  449. float offsetY = ((int)(module->params[multiScope::Y_POS_PARAM + wIx].value * TROWA_SCOPE_ROUND_VALUE)) / (float)(TROWA_SCOPE_ROUND_VALUE);
  450. TSWaveform* waveForm = module->waveForms[wIx];
  451. float valuesX[BUFFER_SIZE];
  452. float valuesY[BUFFER_SIZE];
  453. bool penOn[BUFFER_SIZE];
  454. float multX = gainX / 10.0;
  455. float multY = gainY / 10.0;
  456. for (int i = 0; i < BUFFER_SIZE; i++) {
  457. int j = i;
  458. // Lock display to buffer if buffer update deltaTime <= 2^-11
  459. if (waveForm->lissajous)
  460. j = (i + waveForm->bufferIndex) % BUFFER_SIZE;
  461. valuesX[i] = (waveForm->bufferX[j] + offsetX) * multX;
  462. valuesY[i] = (waveForm->bufferY[j] + offsetY) * multY;
  463. penOn[i] = waveForm->bufferPenOn[j];
  464. }
  465. // Draw waveforms
  466. // 1. Line Color:
  467. NVGcolor waveColor = waveForm->waveColor;
  468. waveColor.a = waveForm->waveOpacity;
  469. if (waveForm->negativeImage)
  470. waveColor = ColorInvertToNegative(waveColor);
  471. nvgStrokeColor(vg, waveColor); // Color has already been calculated by main module
  472. // 2. Fill color:
  473. NVGcolor fillColor = waveForm->fillColor;
  474. if (waveForm->doFill)
  475. {
  476. fillColor.a = waveForm->fillOpacity;
  477. nvgFillColor(vg, fillColor);
  478. }
  479. // 3. Rotation
  480. float rotRate = 0;
  481. if (waveForm->rotMode)
  482. {
  483. // Absolute position:
  484. rot = waveForm->rotAbsValue;
  485. }
  486. else
  487. {
  488. // Differential rotation
  489. rotRate = waveForm->rotDiffValue;
  490. }
  491. if (waveForm->lissajous) {
  492. // X x Y
  493. if (module->inputs[multiScope::X_INPUT + wIx].active || module->inputs[multiScope::Y_INPUT + wIx].active) {
  494. drawWaveform(vg, valuesX, valuesY, penOn, rotRate, waveForm->lineThickness, waveColor, waveForm->doFill, fillColor, SCOPE_GLOBAL_EFFECTS[module->waveForms[wIx]->gEffectIx]->compositeOperation, false, false);
  495. }
  496. }
  497. else {
  498. // Y
  499. if (module->inputs[multiScope::Y_INPUT + wIx].active) {
  500. drawWaveform(vg, valuesY, NULL, penOn, rotRate, waveForm->lineThickness, waveColor, waveForm->doFill, fillColor, SCOPE_GLOBAL_EFFECTS[module->waveForms[wIx]->gEffectIx]->compositeOperation, false, false);
  501. }
  502. // X
  503. if (module->inputs[multiScope::X_INPUT + wIx].active) {
  504. drawWaveform(vg, valuesX, NULL, penOn, rotRate, waveForm->lineThickness, waveColor, waveForm->doFill, fillColor, SCOPE_GLOBAL_EFFECTS[module->waveForms[wIx]->gEffectIx]->compositeOperation, false, false);
  505. }
  506. }
  507. return;
  508. } // end draw()
  509. }; // end multiScopeDisplay
  510. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  511. // TSScopeLabelArea
  512. // Draw labels on our scope.
  513. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  514. struct TSScopeLabelArea : TransparentWidget {
  515. multiScope *module;
  516. std::shared_ptr<Font> font;
  517. int fontSize;
  518. char messageStr[TROWA_DISP_MSG_SIZE];
  519. TSScopeLabelArea() {
  520. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  521. fontSize = 10;
  522. for (int i = 0; i < TROWA_DISP_MSG_SIZE; i++)
  523. messageStr[i] = '\0';
  524. }
  525. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  526. // drawColorGrandientArc()
  527. // @cx : (IN) x-coordinate for center of arc.
  528. // @cy : (IN) y-coordinate for center of arc.
  529. // @radius: (IN) radius of arc.
  530. // @thickness: (IN) Border thickness of arc.
  531. // @start_radians : (IN) Arc start angle [radians].
  532. // @end_radians : (IN) Arc end angle [radians].
  533. // @startHue: (IN) Starting hue.
  534. // @endHue: (IN) Ending hue.
  535. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  536. void drawColorGradientArc(NVGcontext* vg, float cx, float cy, float radius, float thickness, float start_radians, float end_radians, float startHue, float endHue)
  537. {
  538. NVGcolor startColor = HueToColorGradient(startHue);
  539. NVGcolor endColor = HueToColorGradient(endHue);
  540. nvgBeginPath(vg);
  541. nvgArc(vg, /*cx*/ cx, /*cy*/ cy, radius - thickness/2.0,
  542. /*a0*/ start_radians, /*a1*/ end_radians, /*dir*/ NVG_CW);
  543. // Creates and returns a linear gradient. Parameters (sx,sy)-(ex,ey) specify the start and end coordinates
  544. // of the linear gradient, icol specifies the start color and ocol the end color.
  545. // The gradient is transformed by the current transform when it is passed to nvgFillPaint() or nvgStrokePaint().
  546. //float circum = radius * (end_radians - start_radians);
  547. float sx = cx + radius * cos(start_radians);
  548. float sy = cy + radius * sin(start_radians);
  549. float ex = cx + radius * cos(end_radians) + 1;
  550. float ey = cy + radius * sin(end_radians) + 1;
  551. NVGpaint paint = nvgLinearGradient(vg, sx, sy, ex, ey, startColor, endColor);
  552. nvgStrokeWidth(vg, thickness);
  553. nvgStrokePaint(vg, paint);
  554. nvgStroke(vg);
  555. return;
  556. } // end drawColorGradientArc()
  557. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  558. // drawColorGrandientArc()
  559. // @cx : (IN) x-coordinate for center of arc.
  560. // @cy : (IN) y-coordinate for center of arc.
  561. // @radius: (IN) radius of arc.
  562. // @thickness: (IN) Border thickness of arc.
  563. // @start_radians : (IN) Arc start angle [radians].
  564. // @end_radians : (IN) Arc end angle [radians].
  565. // @startHue: (IN) Starting hue.
  566. // @endHue: (IN) Ending hue.
  567. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  568. void drawColorGradientArc(NVGcontext* vg, float cx, float cy, float radius, float thickness)
  569. {
  570. const float startAngle = 0.67*NVG_PI;
  571. const float endAngle = 2.33*NVG_PI;
  572. const int numStops = 20;
  573. float dH = 1.0 / numStops;
  574. float dA = (endAngle - startAngle) / numStops;
  575. float hue = 0;
  576. float start_radians = startAngle;
  577. while (hue < 1.0)
  578. {
  579. drawColorGradientArc(vg, cx, cy, radius, thickness,
  580. start_radians, start_radians + dA*1.2,
  581. hue, hue + dH);
  582. start_radians += dA;
  583. hue += dH;
  584. }
  585. return;
  586. } // end drawColorGradientArc()
  587. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  588. // draw()
  589. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  590. void draw(NVGcontext *vg) override {
  591. // Default Font:
  592. nvgFontSize(vg, fontSize);
  593. nvgFontFaceId(vg, font->handle);
  594. nvgTextLetterSpacing(vg, 1);
  595. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  596. nvgFillColor(vg, textColor);
  597. nvgFontSize(vg, fontSize);
  598. const int xStart = 17; // 23
  599. const int yStart = 6; // 10
  600. int knobOffset = 5;
  601. int dx = TROWA_SCOPE_CONTROL_DX; // 35
  602. int dy = TROWA_SCOPE_CONTROL_DY; // 26
  603. int shapeSpacingY = TROWA_SCOPE_CONTROL_SHAPE_SPACING; // 8 An extra amount between shapes
  604. int x, y;
  605. int shapeDy = dy * 3 + knobOffset + shapeSpacingY + 3;
  606. x = xStart;
  607. y = yStart;
  608. for (int wIx = 0; wIx < TROWA_SCOPE_NUM_WAVEFORMS; wIx++)
  609. {
  610. nvgFontSize(vg, fontSize);
  611. x = xStart;
  612. int waveY = y;
  613. // Shape Label:
  614. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  615. // BG Box
  616. nvgBeginPath(vg);
  617. nvgRect(vg, x - 3, y - 2, TROWA_SCOPE_CONTROL_START_X - x + 3, fontSize + 3);
  618. nvgFillColor(vg, textColor);
  619. nvgFill(vg);
  620. nvgFillColor(vg, COLOR_BLACK);
  621. sprintf(messageStr, TROWA_SCOPE_SHAPE_FORMAT_STRING, (wIx + 1));
  622. nvgText(vg, x, y, messageStr, NULL);
  623. nvgFillColor(vg, textColor);
  624. // Line down lhs:
  625. nvgBeginPath(vg);
  626. nvgRect(vg, x - 3, y - 2, TROWA_SCOPE_CONTROL_START_X - x + 3, fontSize + 3);
  627. nvgMoveTo(vg, /*x*/ x - 3, /*y*/ y - 2);
  628. nvgLineTo(vg, /*x*/ x - 3, /*y*/ y + shapeDy - 10);
  629. nvgStrokeWidth(vg, 1.0);
  630. nvgStrokeColor(vg, textColor);
  631. nvgStroke(vg);
  632. // Row Labels:
  633. x = TROWA_SCOPE_CONTROL_START_X - 5;
  634. y += fontSize + 8;
  635. nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
  636. nvgText(vg, x, y, "IN", NULL);
  637. y += dy;
  638. nvgText(vg, x, y, "OFF", NULL);
  639. y += dy;
  640. nvgText(vg, x, y, "SCL", NULL);
  641. const char* colLabels[] = { "X", "Y", "C", "A", "FC", "FA", "R", "T", "TH" };
  642. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
  643. x = TROWA_SCOPE_CONTROL_START_X + 15;
  644. y = waveY;
  645. for (int i = 0; i < 9; i++)
  646. {
  647. nvgText(vg, x, y, colLabels[i], NULL);
  648. x += dx;
  649. }
  650. int x1 = TROWA_SCOPE_CONTROL_START_X + 15 + 0.5*dx;
  651. int y1 = waveY + dy * 2.3 + 1;
  652. nvgFontSize(vg, fontSize*0.8);
  653. nvgText(vg, x1, y1, "LNK", NULL);
  654. x1 += 2*dx;
  655. int y2 = y1 + 9;
  656. nvgText(vg, x1 - 8, y2, "BLANK", NULL);
  657. y2 += fontSize * 0.8 + 0.5;
  658. nvgFontSize(vg, fontSize*1.05);
  659. nvgText(vg, x1 - 8, y2, "<= 0", NULL);
  660. // Line to port
  661. nvgBeginPath(vg);
  662. nvgMoveTo(vg, /*x*/ x1 + 4, /*y*/ y2 + 4); // Near Text
  663. nvgLineTo(vg, /*x*/ TROWA_SCOPE_CONTROL_START_X + 15 + 3*dx, /*y*/ y2 - 5); // Near port
  664. nvgStrokeWidth(vg, 1.0);
  665. nvgStrokeColor(vg, textColor);
  666. nvgStroke(vg);
  667. nvgFontSize(vg, fontSize*0.8);
  668. x1 += 1.5*dx; // Fill Color
  669. nvgText(vg, x1+1, y1+10, "FILL", NULL);
  670. nvgText(vg, x1 + 1, y1 + 10 + fontSize * 0.8, "ON", NULL);
  671. x1 += 2*dx;
  672. nvgText(vg, x1, y1, "ABS", NULL);
  673. x1 += dx;
  674. nvgText(vg, x1, y1, "X*Y", NULL);
  675. x1 += dx / 3.0 + 2;
  676. nvgText(vg, x1, y1 + 20, "EFFECT", NULL);
  677. // Color Knob gradient:
  678. drawColorGradientArc(vg,
  679. /*cx*/ TROWA_SCOPE_CONTROL_START_X + 15 + 2*dx,
  680. /*cy*/ y + dy + 23 + TROWA_SCOPE_COLOR_KNOB_Y_OFFSET,
  681. /*radius*/ 14,
  682. /*thickness*/ 4.0);
  683. // Fill Color Knob gradient:
  684. drawColorGradientArc(vg,
  685. /*cx*/ TROWA_SCOPE_CONTROL_START_X + 15 + 4 * dx,
  686. /*cy*/ y + dy + 23 + TROWA_SCOPE_COLOR_KNOB_Y_OFFSET,
  687. /*radius*/ 14,
  688. /*thickness*/ 4.0);
  689. y += shapeDy - 3;
  690. } // end loop through shapes/waveforms
  691. return;
  692. } // end draw()
  693. }; // end TSScopeLabelArea
  694. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  695. // TSScopeSideBarArea
  696. // Draw labels on the RHS bar of our scope.
  697. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  698. struct TSScopeSideBarLabelArea : TransparentWidget {
  699. //multiScope *module;
  700. std::shared_ptr<Font> font;
  701. int fontSize;
  702. TSScopeSideBarLabelArea() {
  703. font = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  704. fontSize = 10;
  705. }
  706. TSScopeSideBarLabelArea(Vec bsize) : TSScopeSideBarLabelArea() {
  707. this->box.size = bsize;
  708. return;
  709. }
  710. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  711. // draw()
  712. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  713. void draw(NVGcontext *vg) override {
  714. nvgSave(vg);
  715. //nvgTranslate(vg, -box.size.y / 2.0, -box.size.x / 2.0);
  716. nvgRotate(vg, NVG_PI*0.5);
  717. // Default Font:
  718. nvgFontSize(vg, fontSize);
  719. nvgFontFaceId(vg, font->handle);
  720. nvgTextLetterSpacing(vg, 1);
  721. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  722. nvgFillColor(vg, textColor);
  723. nvgFontSize(vg, fontSize);
  724. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
  725. float x, y;
  726. x = 34;// box.size.x / 2.0;
  727. y = -box.size.x / 2.0; // 0;// 32;
  728. nvgText(vg, x, y, "INFO", NULL); // Info display btn toggle
  729. #if ENABLE_BG_COLOR_PICKER
  730. x += 45;
  731. nvgText(vg, x, y, "BG COLOR", NULL); // Info display btn toggle
  732. #endif
  733. nvgRestore(vg);
  734. return;
  735. } // end draw()
  736. }; // end TSScopeSideBarLabelArea
  737. #endif // end if use new scope
  738. #endif // end if not defined