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.

1415 lines
47KB

  1. #include "global_pre.hpp"
  2. #include "global_ui.hpp"
  3. #include "Widget_oscCV.hpp"
  4. #include "widgets.hpp"
  5. using namespace rack;
  6. #include "trowaSoft.hpp"
  7. #include "dsp/digital.hpp"
  8. #include "trowaSoftComponents.hpp"
  9. #include "trowaSoftUtilities.hpp"
  10. #include "Module_oscCV.hpp"
  11. // Channel colors
  12. const NVGcolor oscCVWidget::CHANNEL_COLORS[TROWA_OSCCV_NUM_COLORS] = {
  13. COLOR_TS_RED, COLOR_DARK_ORANGE, COLOR_YELLOW, COLOR_TS_GREEN,
  14. COLOR_CYAN, COLOR_TS_BLUE, COLOR_PURPLE, COLOR_PINK
  15. };
  16. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  17. // oscCVWidget()
  18. // Instantiate a oscCV widget. v0.60 must have module as param.
  19. // @oscModule : (IN) Pointer to the osc module.
  20. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  21. oscCVWidget::oscCVWidget(oscCV* oscModule) : TSSModuleWidgetBase(oscModule)
  22. {
  23. box.size = Vec(26 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  24. bool isPreview = oscModule == NULL; // If this is null, this for a preview??? Just get the controls layed out
  25. this->module = oscModule;
  26. this->numberChannels = (isPreview) ? TROWA_OSCCV_DEFAULT_NUM_CHANNELS : oscModule->numberChannels;
  27. Vec topScreenSize = Vec(363, 48);
  28. //////////////////////////////////////////////
  29. // Background
  30. //////////////////////////////////////////////
  31. {
  32. SVGPanel *panel = new SVGPanel();
  33. panel->box.size = box.size;
  34. panel->setBackground(SVG::load(assetPlugin(plugin, "res/cvOSCcv.svg")));
  35. addChild(panel);
  36. }
  37. ////////////////////////////////////
  38. // Top Screen
  39. ////////////////////////////////////
  40. {
  41. this->display = new TSOscCVTopDisplay(this);
  42. this->display->showDisplay = true;
  43. display->box.pos = Vec(TROWA_HORIZ_MARGIN, 24);
  44. display->box.size = topScreenSize;
  45. addChild(display);
  46. }
  47. ////////////////////////////////////
  48. // OSC configuration screen.
  49. ////////////////////////////////////
  50. if (!isPreview)
  51. {
  52. TSOSCConfigWidget* oscConfig = new TSOSCConfigWidget(oscModule, oscCV::ParamIds::OSC_SAVE_CONF_PARAM, oscCV::ParamIds::OSC_AUTO_RECONNECT_PARAM,
  53. oscModule->currentOSCSettings.oscTxIpAddress.c_str(), oscModule->currentOSCSettings.oscTxPort, oscModule->currentOSCSettings.oscRxPort,
  54. false, OSCClient::GenericClient, true, TROWA_OSCCV_DEFAULT_NAMESPACE);
  55. oscConfig->setVisible(false);
  56. oscConfig->box.pos = Vec(TROWA_HORIZ_MARGIN, 24);
  57. oscConfig->box.size = topScreenSize;
  58. this->oscConfigurationScreen = oscConfig;
  59. addChild(oscConfig);
  60. }
  61. //////////////////////////////////////
  62. // Labels
  63. //////////////////////////////////////
  64. {
  65. TSOscCVLabels* labelArea = new TSOscCVLabels();
  66. labelArea->box.pos = Vec(TROWA_HORIZ_MARGIN, topScreenSize.y + 24);
  67. labelArea->box.size = Vec(box.size.x - TROWA_HORIZ_MARGIN * 2, box.size.y - labelArea->box.pos.y - 15);
  68. addChild(labelArea);
  69. }
  70. int x, y, dx, dy;
  71. int xStart, yStart;
  72. //////////////////////////////////////
  73. // Parameters / UI Buttons
  74. //////////////////////////////////////
  75. Vec ledSize = Vec(15, 15);
  76. dx = 28;
  77. //---------------------------
  78. // Button: Enable OSC button
  79. //---------------------------
  80. LEDButton* btn;
  81. y = topScreenSize.y + 30;
  82. x = 76; // 80
  83. Vec btnSize = Vec(ledSize.x - 2, ledSize.y - 2);
  84. btn = dynamic_cast<LEDButton*>(ParamWidget::create<LEDButton>(Vec(x, y), oscModule, oscCV::ParamIds::OSC_SHOW_CONF_PARAM, 0, 1, 0));
  85. btn->box.size = btnSize;
  86. addParam(btn);
  87. addChild(TS_createColorValueLight<ColorValueLight>(Vec(x, y), oscModule, oscCV::LightIds::OSC_CONFIGURE_LIGHT, ledSize, COLOR_WHITE));
  88. addChild(TS_createColorValueLight<ColorValueLight>(Vec(x + 2, y + 2), oscModule, oscCV::LightIds::OSC_ENABLED_LIGHT, Vec(ledSize.x - 4, ledSize.y - 4), TSOSC_STATUS_COLOR));
  89. xStart = TROWA_HORIZ_MARGIN;
  90. yStart = 98; // 88
  91. dx = 28;
  92. dy = 30; // 32
  93. const float tbYOffset = 6.0;
  94. const float tbXOffset = 6.0;
  95. ////////////////////////////////////
  96. // Middle Screen
  97. ////////////////////////////////////
  98. x = xStart + dx * 2 + tbXOffset / 2.0;
  99. y = yStart;
  100. {
  101. int xEnd = box.size.x - xStart - dx * 2 - tbXOffset / 2.0;
  102. middleDisplay = new TSOscCVMiddleDisplay(this);
  103. middleDisplay->setDisplayMode(TSOscCVMiddleDisplay::DisplayMode::Default);
  104. middleDisplay->box.size = Vec(xEnd - x, numberChannels* dy);
  105. middleDisplay->box.pos = Vec(x, y);
  106. addChild(middleDisplay);
  107. }
  108. ////////////////////////////////////
  109. // Channel Configuration
  110. ////////////////////////////////////
  111. {
  112. this->oscChannelConfigScreen = new TSOscCVChannelConfigScreen(this, Vec(x, y), middleDisplay->box.size);
  113. //debug("Hiding screen");
  114. oscChannelConfigScreen->setVisibility(false);
  115. //oscChannelConfigScreen->box.size = middleDisplay->box.size;
  116. //oscChannelConfigScreen->box.pos = Vec(x, y);
  117. addChild(oscChannelConfigScreen);
  118. }
  119. ////////////////////////////////////
  120. // Input Ports
  121. ////////////////////////////////////
  122. // Trigger and Value CV inputs for each channel
  123. //debug("Starting ports");
  124. y = yStart;
  125. ledSize = Vec(5, 5);
  126. float ledYOffset = 12.5;
  127. #if TROWA_OSSCV_SHOW_ADV_CH_CONFIG
  128. Vec tbPathSize = Vec(92, 20); // 88, 20 // 108, 20
  129. #else
  130. Vec tbPathSize = Vec(108, 20); // 88, 20 // 108, 20
  131. #endif
  132. btnSize = Vec(24, 20);
  133. for (int r = 0; r < numberChannels; r++)
  134. {
  135. // Light (to indicate when we send OSC)
  136. addChild(TS_createColorValueLight<ColorValueLight>(Vec(xStart - ledSize.x, y + ledYOffset), oscModule, oscCV::LightIds::CH_LIGHT_START + r * TROWA_OSCCV_NUM_LIGHTS_PER_CHANNEL, ledSize, CHANNEL_COLORS[r]));
  137. // Trigger Input:
  138. x = xStart;
  139. if (colorizeChannels)
  140. addInput(TS_createInput<TS_Port>(Vec(x, y), oscModule, oscCV::InputIds::CH_INPUT_START + r * 2, !plugLightsEnabled, CHANNEL_COLORS[r]));
  141. else
  142. addInput(TS_createInput<TS_Port>(Vec(x, y), oscModule, oscCV::InputIds::CH_INPUT_START + r * 2, !plugLightsEnabled));
  143. // Value input:
  144. x += dx;
  145. if (colorizeChannels)
  146. addInput(TS_createInput<TS_Port>(Vec(x, y), oscModule, oscCV::InputIds::CH_INPUT_START + r * 2 + 1, !plugLightsEnabled, CHANNEL_COLORS[r]));
  147. else
  148. addInput(TS_createInput<TS_Port>(Vec(x, y), oscModule, oscCV::InputIds::CH_INPUT_START + r * 2 + 1, !plugLightsEnabled));
  149. //---* OSC Channel Configuration *---
  150. // OSC Input Path (this is OSC outgoing message, our input)
  151. x += dx + tbXOffset;
  152. std::string path = (isPreview) ? "/ch/" + std::to_string(r + 1) : oscModule->inputChannels[r].path;
  153. TSTextField* txtField = new TSTextField(TSTextField::TextType::Any, 50);
  154. txtField->box.size = tbPathSize;
  155. txtField->box.pos = Vec(x, y + tbYOffset);
  156. txtField->text = path;
  157. if (colorizeChannels) {
  158. txtField->borderColor = CHANNEL_COLORS[r];
  159. txtField->caretColor = CHANNEL_COLORS[r];
  160. txtField->caretColor.a = 0.70;
  161. }
  162. tbOscInputPaths.push_back(txtField);
  163. addChild(txtField);
  164. #if TROWA_OSSCV_SHOW_ADV_CH_CONFIG
  165. // OSC Advanced Channel Config Button (INPUTS)
  166. x += txtField->box.size.x;
  167. //int paramId = oscCV::ParamIds::CH_PARAM_START + (r*TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG) * 2;
  168. int paramId = oscCV::ParamIds::CH_PARAM_START
  169. + r*TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS
  170. + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG;
  171. //debug("Input Ch %d, paramId = %d", r, paramId);
  172. TS_ScreenBtn* btn = new TS_ScreenBtn(btnSize, oscModule, paramId, std::string( "ADV" ), /*minVal*/ 0.0f, /*maxVal*/ 1.0f, /*defVal*/ 0.0f);
  173. btn->box.pos = Vec(x, y + tbYOffset);
  174. btn->borderColor = CHANNEL_COLORS[r];
  175. btn->color = CHANNEL_COLORS[r];
  176. btn->borderWidth = 1;
  177. btn->backgroundColor = nvgRGB(clamp(CHANNEL_COLORS[r].r - 0.3f, 0.0f, 1.0f), clamp(CHANNEL_COLORS[r].g - 0.3f, 0.0f, 1.0f), clamp(CHANNEL_COLORS[r].b - 0.3f, 0.0f, 1.0f));
  178. btn->setVisible(false);
  179. addParam(btn);
  180. btnDrawInputAdvChConfig.push_back(btn);
  181. #endif
  182. y += dy;
  183. } // end input channels
  184. ////////////////////////////////////
  185. // Output Ports
  186. ////////////////////////////////////
  187. // Trigger and Value CV inputs for each channel
  188. xStart = box.size.x - TROWA_HORIZ_MARGIN - dx * 2;
  189. y = yStart;
  190. for (int r = 0; r < numberChannels; r++)
  191. {
  192. //---* OSC Channel Configuration *---
  193. // OSC Output Path (this is OSC incoming message, our output)
  194. x = xStart - tbXOffset - tbPathSize.x;
  195. std::string path = (isPreview) ? "/ch/" + std::to_string(r + 1) : oscModule->outputChannels[r].path;
  196. TSTextField* txtField = new TSTextField(TSTextField::TextType::Any, 50);
  197. txtField->box.size = tbPathSize;
  198. txtField->box.pos = Vec(x, y + tbYOffset);
  199. txtField->text = path;
  200. if (colorizeChannels) {
  201. txtField->borderColor = CHANNEL_COLORS[r];
  202. txtField->caretColor = CHANNEL_COLORS[r];
  203. txtField->caretColor.a = 0.70;
  204. }
  205. tbOscOutputPaths.push_back(txtField);
  206. addChild(txtField);
  207. #if TROWA_OSSCV_SHOW_ADV_CH_CONFIG
  208. // OSC Advanced Channel Config (OUTPUT)
  209. x -= btnSize.x;
  210. //int paramId = oscCV::ParamIds::CH_PARAM_START + (r*TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG) * 2 + 1;
  211. //int paramId = oscCV::ParamIds::CH_PARAM_START
  212. // + ( *TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG;
  213. int paramId = oscCV::ParamIds::CH_PARAM_START
  214. + (numberChannels + r) * TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG;
  215. //debug("Output Ch %d, paramId = %d", r, paramId);
  216. TS_ScreenBtn* btn = new TS_ScreenBtn(btnSize, oscModule, paramId, std::string("ADV"), /*minVal*/ 0.0f, /*maxVal*/ 1.0f, /*defVal*/ 0.0f);
  217. btn->box.pos = Vec(x, y + tbYOffset);
  218. btn->borderColor = CHANNEL_COLORS[r];
  219. btn->color = CHANNEL_COLORS[r];
  220. btn->borderWidth = 1;
  221. btn->backgroundColor = nvgRGB(clamp(CHANNEL_COLORS[r].r - 0.3f, 0.0f, 1.0f), clamp(CHANNEL_COLORS[r].g - 0.3f, 0.0f, 1.0f), clamp(CHANNEL_COLORS[r].b - 0.3f, 0.0f, 1.0f));
  222. btn->setVisible(false);
  223. addParam(btn);
  224. btnDrawOutputAdvChConfig.push_back(btn);
  225. #endif
  226. // Trigger Input:
  227. x = xStart;
  228. if (colorizeChannels)
  229. addOutput(TS_createOutput<TS_Port>(Vec(x, y), oscModule, oscCV::OutputIds::CH_OUTPUT_START + r * 2, !plugLightsEnabled, CHANNEL_COLORS[r]));
  230. else
  231. addOutput(TS_createOutput<TS_Port>(Vec(x, y), oscModule, oscCV::OutputIds::CH_OUTPUT_START + r * 2, !plugLightsEnabled));
  232. // Value input:
  233. x += dx;
  234. if (colorizeChannels)
  235. addOutput(TS_createOutput<TS_Port>(Vec(x, y), oscModule, oscCV::OutputIds::CH_OUTPUT_START + r * 2 + 1, !plugLightsEnabled, CHANNEL_COLORS[r]));
  236. else
  237. addOutput(TS_createOutput<TS_Port>(Vec(x, y), oscModule, oscCV::OutputIds::CH_OUTPUT_START + r * 2 + 1, !plugLightsEnabled));
  238. // Light (to indicate when we receive OSC)
  239. addChild(TS_createColorValueLight<ColorValueLight>(Vec(x + dx + ledSize.x/2.0, y + ledYOffset), oscModule, oscCV::LightIds::CH_LIGHT_START + r * TROWA_OSCCV_NUM_LIGHTS_PER_CHANNEL + 1, ledSize, CHANNEL_COLORS[r]));
  240. y += dy;
  241. } // end output channels
  242. // Set Tab Order:
  243. for (int c = 0; c < numberChannels; c++)
  244. {
  245. tbOscInputPaths[c]->nextField = tbOscOutputPaths[c];
  246. tbOscOutputPaths[c]->prevField = tbOscInputPaths[c];
  247. if (c > 0)
  248. {
  249. tbOscInputPaths[c]->prevField = tbOscOutputPaths[c - 1];
  250. }
  251. else
  252. {
  253. tbOscInputPaths[c]->prevField = tbOscOutputPaths[numberChannels - 1];
  254. }
  255. if (c < numberChannels - 1)
  256. {
  257. tbOscOutputPaths[c]->nextField = tbOscInputPaths[c + 1];
  258. }
  259. else
  260. {
  261. tbOscOutputPaths[c]->nextField = tbOscInputPaths[0]; // Loop back around
  262. }
  263. } // end loop through channels - put tab order on text fields
  264. // Screws:
  265. addChild(Widget::create<ScrewBlack>(Vec(0, 0)));
  266. addChild(Widget::create<ScrewBlack>(Vec(box.size.x - 15, 0)));
  267. addChild(Widget::create<ScrewBlack>(Vec(0, box.size.y - 15)));
  268. addChild(Widget::create<ScrewBlack>(Vec(box.size.x - 15, box.size.y - 15)));
  269. if (oscModule != NULL)
  270. {
  271. oscModule->isInitialized = true;
  272. }
  273. toggleChannelPathConfig(false);
  274. return;
  275. } //end oscCVWidget()
  276. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  277. // step(void)
  278. // Handle UI controls.
  279. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  280. void oscCVWidget::step()
  281. {
  282. if (this->module == NULL)
  283. return;
  284. oscCV* thisModule = dynamic_cast<oscCV*>(module);
  285. if (thisModule->oscConfigTrigger.process(thisModule->params[oscCV::ParamIds::OSC_SHOW_CONF_PARAM].value))
  286. {
  287. //debug("Button clicked");
  288. thisModule->oscShowConfigurationScreen = !thisModule->oscShowConfigurationScreen;
  289. thisModule->lights[oscCV::LightIds::OSC_CONFIGURE_LIGHT].value = (thisModule->oscShowConfigurationScreen) ? 1.0 : 0.0;
  290. this->oscConfigurationScreen->setVisible(thisModule->oscShowConfigurationScreen);
  291. this->display->showDisplay = !thisModule->oscShowConfigurationScreen;
  292. if (thisModule->oscShowConfigurationScreen) {
  293. this->middleDisplay->setDisplayMode(TSOscCVMiddleDisplay::DisplayMode::None);
  294. }
  295. else {
  296. this->middleDisplay->setDisplayMode(TSOscCVMiddleDisplay::DisplayMode::Default);
  297. }
  298. if (thisModule->oscShowConfigurationScreen)
  299. {
  300. if (!thisModule->oscInitialized)
  301. {
  302. // Make sure the ports are available
  303. int p = TSOSCConnector::PortInUse(thisModule->currentOSCSettings.oscTxPort);
  304. if (p > 0 && p != thisModule->oscId)
  305. thisModule->currentOSCSettings.oscTxPort = TSOSCConnector::GetAvailablePort(thisModule->oscId, thisModule->currentOSCSettings.oscTxPort);
  306. p = TSOSCConnector::PortInUse(thisModule->currentOSCSettings.oscRxPort);
  307. if (p > 0 && p != thisModule->oscId)
  308. thisModule->currentOSCSettings.oscRxPort = TSOSCConnector::GetAvailablePort(thisModule->oscId, thisModule->currentOSCSettings.oscRxPort);
  309. }
  310. this->oscConfigurationScreen->setValues(thisModule->currentOSCSettings.oscTxIpAddress, thisModule->currentOSCSettings.oscTxPort, thisModule->currentOSCSettings.oscRxPort, thisModule->oscNamespace);
  311. this->oscConfigurationScreen->ckAutoReconnect->checked = thisModule->oscReconnectAtLoad;
  312. //this->oscConfigurationScreen->setSelectedClient(thisModule->oscCurrentClient); // OSC Client
  313. this->oscConfigurationScreen->btnActionEnable = !thisModule->oscInitialized;
  314. setChannelPathConfig(); // Set the channel text boxes
  315. toggleChannelPathConfig(true);
  316. this->oscConfigurationScreen->errorMsg = "";
  317. if (thisModule->oscError)
  318. {
  319. this->oscConfigurationScreen->errorMsg = "Error connecting to " + thisModule->currentOSCSettings.oscTxIpAddress;
  320. }
  321. this->oscConfigurationScreen->setVisible(true);
  322. }
  323. else
  324. {
  325. this->oscConfigurationScreen->setVisible(false);
  326. toggleChannelPathConfig(false);
  327. readChannelPathConfig(); // Read any changes that may have happened. (we don't have room for a save button)
  328. oscChannelConfigScreen->setVisibility(false); // Hide
  329. }
  330. }
  331. if (thisModule->oscShowConfigurationScreen)
  332. {
  333. //------------------------------------------
  334. // Check for show channel config buttons
  335. //------------------------------------------
  336. TSOSCCVChannel* editChannelPtr = NULL;
  337. bool isInput = false;
  338. for (int c = 0; c < thisModule->numberChannels; c++) {
  339. // Input Channel:
  340. int paramId = oscCV::ParamIds::CH_PARAM_START
  341. + c*TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG;
  342. if (thisModule->inputChannels[c].showChannelConfigTrigger.process(thisModule->params[paramId].value)) {
  343. //debug("btnClick: Input Ch %d, paramId = %d", c, paramId);
  344. editChannelPtr = &(thisModule->inputChannels[c]);
  345. isInput = true;
  346. break;
  347. }
  348. paramId = oscCV::ParamIds::CH_PARAM_START
  349. + (thisModule->numberChannels + c) * TSOSCCVChannel::BaseParamIds::CH_NUM_PARAMS + TSOSCCVChannel::BaseParamIds::CH_SHOW_CONFIG;
  350. // Output Channel:
  351. if (thisModule->outputChannels[c].showChannelConfigTrigger.process(thisModule->params[paramId].value)) {
  352. //debug("btnClick: Output Ch %d, paramId = %d", c, paramId);
  353. editChannelPtr = &(thisModule->outputChannels[c]);
  354. isInput = false;
  355. break;
  356. }
  357. } // end for (loop through channels)
  358. if (editChannelPtr != NULL)
  359. {
  360. this->toggleChannelPathConfig(false); // Hide the channel paths
  361. this->oscChannelConfigScreen->showControl(editChannelPtr, isInput);
  362. }
  363. else if (this->oscChannelConfigScreen->visible)
  364. {
  365. // Check for enable/disable data massaging
  366. // Check for Save or Cancel
  367. if (oscChannelConfigScreen->saveTrigger.process(thisModule->params[oscCV::ParamIds::OSC_CH_SAVE_PARAM].value)) {
  368. //debug("Save Clicked");
  369. // Validate form & save
  370. if (oscChannelConfigScreen->saveValues()) {
  371. oscChannelConfigScreen->setVisibility(false); // Hide
  372. this->toggleChannelPathConfig(true); // Show paths again
  373. //thisModule->params[oscCV::ParamIds::OSC_CH_SAVE_PARAM].value = 0.0f; // Reset
  374. }
  375. }
  376. else if (oscChannelConfigScreen->cancelTrigger.process(thisModule->params[oscCV::ParamIds::OSC_CH_CANCEL_PARAM].value)) {
  377. // Just hide and go back to paths
  378. oscChannelConfigScreen->setVisibility(false); // Hide
  379. this->toggleChannelPathConfig(true); // Show paths again
  380. //thisModule->params[oscCV::ParamIds::OSC_CH_CANCEL_PARAM].value = 0.0f; // Reset
  381. }
  382. }
  383. //------------------------------------------
  384. // Check for enable/disable OSC
  385. //------------------------------------------
  386. if (thisModule->oscConnectTrigger.process(thisModule->params[oscCV::ParamIds::OSC_SAVE_CONF_PARAM].value))
  387. {
  388. if (oscConfigurationScreen->btnActionEnable)
  389. {
  390. // Enable OSC ------------------------------------------------------------------------
  391. // User checked to connect
  392. if (!this->oscConfigurationScreen->isValidIpAddress())
  393. {
  394. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  395. debug("IP Address is not valid.");
  396. #endif
  397. this->oscConfigurationScreen->errorMsg = "Invalid IP Address.";
  398. this->oscConfigurationScreen->tbIpAddress->requestFocus();
  399. }
  400. else if (!this->oscConfigurationScreen->isValidTxPort())
  401. {
  402. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  403. debug("Tx Port is not valid.");
  404. #endif
  405. this->oscConfigurationScreen->errorMsg = "Invalid Output Port (0-" + std::to_string(0xFFFF) + ").";
  406. this->oscConfigurationScreen->tbTxPort->requestFocus();
  407. }
  408. else if (!this->oscConfigurationScreen->isValidRxPort())
  409. {
  410. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  411. debug("Rx Port is not valid.");
  412. #endif
  413. this->oscConfigurationScreen->errorMsg = "Invalid Input Port (0-" + std::to_string(0xFFFF) + ").";
  414. this->oscConfigurationScreen->tbRxPort->requestFocus();
  415. }
  416. else
  417. {
  418. // Try to connect
  419. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  420. debug("Save OSC Configuration clicked, save information for module.");
  421. #endif
  422. readChannelPathConfig();
  423. this->oscConfigurationScreen->errorMsg = "";
  424. thisModule->oscNewSettings.oscTxIpAddress = this->oscConfigurationScreen->tbIpAddress->text.c_str();
  425. thisModule->oscNewSettings.oscTxPort = this->oscConfigurationScreen->getTxPort();
  426. thisModule->oscNewSettings.oscRxPort = this->oscConfigurationScreen->getRxPort();
  427. //thisModule->oscCurrentClient = this->oscConfigurationScreen->getSelectedClient();
  428. //debug("Setting namespace");
  429. thisModule->setOscNamespace(this->oscConfigurationScreen->tbNamespace->text);
  430. thisModule->oscCurrentAction = oscCV::OSCAction::Enable;
  431. thisModule->oscReconnectAtLoad = this->oscConfigurationScreen->ckAutoReconnect->checked;
  432. }
  433. } // end if enable osc
  434. else
  435. {
  436. // Disable OSC ------------------------------------------------------------------
  437. #if TROWA_DEBUG_MSGS >= TROWA_DEBUG_LVL_MED
  438. debug("Disable OSC clicked.");
  439. #endif
  440. this->oscConfigurationScreen->errorMsg = "";
  441. thisModule->oscCurrentAction = oscCV::OSCAction::Disable;
  442. } // end else disable OSC
  443. } // end if OSC Save btn pressed
  444. else
  445. {
  446. if (thisModule->oscError)
  447. {
  448. if (this->oscConfigurationScreen->errorMsg.empty())
  449. this->oscConfigurationScreen->errorMsg = "Error connecting to " + thisModule->currentOSCSettings.oscTxIpAddress + ".";
  450. }
  451. }
  452. // Current status of OSC
  453. if (thisModule->oscInitialized)
  454. {
  455. this->oscConfigurationScreen->statusMsg2 = thisModule->currentOSCSettings.oscTxIpAddress;
  456. this->oscConfigurationScreen->btnActionEnable = false;
  457. }
  458. else
  459. {
  460. this->oscConfigurationScreen->successMsg = "";
  461. this->oscConfigurationScreen->statusMsg2 = "OSC Not Connected";
  462. this->oscConfigurationScreen->btnActionEnable = true;
  463. }
  464. } // end if show OSC config screen
  465. ModuleWidget::step();
  466. return;
  467. }
  468. // Show or hide the channel configuration
  469. void oscCVWidget::toggleChannelPathConfig(bool show)
  470. {
  471. for (int i = 0; i < this->numberChannels; i++)
  472. {
  473. this->tbOscInputPaths[i]->visible = show;
  474. this->tbOscOutputPaths[i]->visible = show;
  475. #if TROWA_OSSCV_SHOW_ADV_CH_CONFIG
  476. btnDrawInputAdvChConfig[i]->setVisible(show);
  477. btnDrawOutputAdvChConfig[i]->setVisible(show);
  478. #endif
  479. }
  480. return;
  481. }
  482. // Read the channel path configs and store in module's channels.
  483. void oscCVWidget::readChannelPathConfig()
  484. {
  485. if (module != NULL)
  486. {
  487. oscCV* thisModule = dynamic_cast<oscCV*>(module);
  488. try
  489. {
  490. for (int i = 0; i < this->numberChannels; i++)
  491. {
  492. thisModule->inputChannels[i].setPath(this->tbOscInputPaths[i]->text);
  493. thisModule->outputChannels[i].setPath(this->tbOscOutputPaths[i]->text);
  494. }
  495. }
  496. catch (const std::exception& e)
  497. {
  498. warn("Error %s.", e.what());
  499. }
  500. }
  501. return;
  502. } // end readChannelPathConfig()
  503. // Set the channel path text boxes.
  504. void oscCVWidget::setChannelPathConfig() {
  505. if (module != NULL)
  506. {
  507. oscCV* thisModule = dynamic_cast<oscCV*>(module);
  508. try
  509. {
  510. for (int i = 0; i < this->numberChannels; i++)
  511. {
  512. this->tbOscInputPaths[i]->text = thisModule->inputChannels[i].getPath();
  513. this->tbOscOutputPaths[i]->text = thisModule->outputChannels[i].getPath();
  514. }
  515. }
  516. catch (const std::exception& e)
  517. {
  518. warn("Error %s.", e.what());
  519. }
  520. }
  521. return;
  522. } // end setChannelPathConfig()
  523. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  524. // draw()
  525. // @vg : (IN) NVGcontext to draw on
  526. // Draw labels on our widget.
  527. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  528. void TSOscCVLabels::draw(/*in*/ NVGcontext *vg) {
  529. // Default Font:
  530. nvgFontSize(vg, fontSize);
  531. nvgFontFaceId(vg, font->handle);
  532. nvgTextLetterSpacing(vg, 1);
  533. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  534. nvgFillColor(vg, textColor);
  535. nvgFontSize(vg, fontSize);
  536. int x, y, dx;// , dy;
  537. int xStart, yStart;
  538. xStart = 0;
  539. yStart = 25; // 17
  540. dx = 28;
  541. //dy = 32;
  542. //-- * Top Buttons *--//
  543. x = 84;
  544. y = 18;
  545. nvgTextAlign(vg, NVG_ALIGN_LEFT);
  546. nvgText(vg, x, y, "CONFIG", NULL);
  547. //--- * Inputs *---//
  548. // (Left hand side)
  549. // TRIG:
  550. x = xStart + dx / 2;
  551. y = yStart;
  552. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  553. nvgText(vg, x, y, "TRG", NULL);
  554. // VAL:
  555. x += dx;
  556. y = yStart;
  557. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  558. nvgText(vg, x, y, "VAL", NULL);
  559. // Bottom (INPUTS)
  560. x = xStart + dx;
  561. y = box.size.y - 13;
  562. nvgText(vg, x, y, "INPUTS", NULL);
  563. //--- * Outputs *---//
  564. // (Right hand side)
  565. // TRIG:
  566. x = box.size.x - dx / 2 - dx;
  567. y = yStart;
  568. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  569. nvgText(vg, x, y, "TRG", NULL);
  570. // VAL:
  571. x += dx;
  572. y = yStart;
  573. nvgTextAlign(vg, NVG_ALIGN_CENTER);
  574. nvgText(vg, x, y, "VAL", NULL);
  575. // Bottom (INPUTS)
  576. x = box.size.x - dx;
  577. y = box.size.y - 13;
  578. nvgText(vg, x, y, "OUTPUTS", NULL);
  579. } // end TSOscCVLabels::draw()
  580. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  581. // step()
  582. // Calculate scrolling and stuff?
  583. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  584. void TSOscCVTopDisplay::step() {
  585. //debug("Top Display: step(%8.6f)", dt);
  586. bool connected = false;
  587. bool isPreview = parentWidget->module == NULL;
  588. std::string thisIp = std::string("NO CONNECTION ");
  589. oscCV* thisModule = NULL;
  590. if (!isPreview)
  591. {
  592. thisModule = dynamic_cast<oscCV*>(parentWidget->module);
  593. connected = thisModule->oscInitialized;
  594. if (connected)
  595. thisIp = thisModule->currentOSCSettings.oscTxIpAddress
  596. + std::string(" Tx:") + std::to_string(thisModule->currentOSCSettings.oscTxPort)
  597. + std::string(" Rx:") + std::to_string(thisModule->currentOSCSettings.oscRxPort)
  598. + ((thisModule->oscNamespace.at(0) == '/') ? " " : " /") + thisModule->oscNamespace + " ";
  599. }
  600. if (thisIp.compare(lastIp) != 0)
  601. {
  602. sprintf(scrollingMsg, "trowaSoft - %s - cv<->OSC<->cv - ", thisIp.c_str());
  603. }
  604. //dt += engineGetSampleTime() / scrollTime_sec;
  605. //if (dt > 1.0f)
  606. dt += 100.0 / engineGetSampleRate();
  607. if (dt > scrollTime_sec)
  608. {
  609. //debug("Dt has grown. Increment Ix: %d", scrollIx);
  610. dt = 0.0f;
  611. if (static_cast<size_t>(scrollIx) == strlen(scrollingMsg) - 1)
  612. scrollIx = 0;
  613. else
  614. scrollIx++;
  615. }
  616. lastIp = thisIp;
  617. TransparentWidget::step(); // parent whatever he does
  618. return;
  619. }
  620. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  621. // draw()
  622. // @vg : (IN) NVGcontext to draw on
  623. // Top display
  624. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  625. void TSOscCVTopDisplay::draw(/*in*/ NVGcontext *vg)
  626. {
  627. //bool isPreview = parentWidget->module == NULL; // May get a NULL module for preview
  628. // Background Colors:
  629. NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  630. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  631. // Screen:
  632. nvgBeginPath(vg);
  633. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  634. nvgFillColor(vg, backgroundColor);
  635. nvgFill(vg);
  636. nvgStrokeWidth(vg, 1.0);
  637. nvgStrokeColor(vg, borderColor);
  638. nvgStroke(vg);
  639. if (!showDisplay)
  640. return;
  641. Rect b = Rect(Vec(0, 0), Vec(box.size.x - 13, box.size.y));
  642. nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  643. //oscCV* thisModule = NULL;
  644. //if (!isPreview)
  645. // thisModule = dynamic_cast<oscCV*>(parentWidget->module);
  646. int x, y;
  647. // Default Font:
  648. nvgFontSize(vg, fontSize);
  649. nvgFontFaceId(vg, font->handle);
  650. nvgTextLetterSpacing(vg, 2.5);
  651. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  652. nvgFillColor(vg, textColor);
  653. // SCROLLING MESSAGE ====================================
  654. x = 13;// -scrollIx * 0.5;
  655. y = 13;
  656. nvgFontSize(vg, fontSize * 1.5); // Large font
  657. //nvgFontFaceId(vg, font->handle);
  658. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  659. // Start (left on screen) of scrolling message:
  660. const char * subStr = scrollingMsg + scrollIx;
  661. nvgText(vg, x, y, subStr, NULL);
  662. // Get circular wrap (right part of screen) - start of message again:
  663. float txtBounds[4] = { 0,0,0,0 };
  664. float nextX = nvgTextBounds(vg, x, y, subStr, NULL, txtBounds);
  665. x = nextX; // +24
  666. if (x < b.size.x) {
  667. // Wrap the start of the string around
  668. nvgText(vg, x, y, scrollingMsg, subStr);
  669. }
  670. //// Measures the specified text string. Parameter bounds should be a pointer to float[4],
  671. //// if the bounding box of the text should be returned. The bounds value are [xmin,ymin, xmax,ymax]
  672. //// Returns the horizontal advance of the measured text (i.e. where the next character should drawn).
  673. //// Measured values are returned in local coordinate space.
  674. //float nvgTextBounds(NVGcontext* ctx, float x, float y, const char* string, const char* end, float* bounds);
  675. nvgResetScissor(vg);
  676. //const char* asciiArt[] = {
  677. // "♫♪.ılılıll|̲̅̅●̲̅̅|̲̅̅=̲̅̅|̲̅̅●̲̅̅|llılılı.♫♪",
  678. // "°º¤ø,¸¸,ø¤º°`°º¤ø,¸,ø¤°º¤ø,¸¸,ø¤º°`°º¤ø,¸"
  679. //}
  680. // Always display connected
  681. return;
  682. } // end TSOscCVTopDisplay::draw()
  683. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  684. // Process
  685. // Middle display -- If we have to do scrolling, calculate it.
  686. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  687. void TSOscCVMiddleDisplay::step() {
  688. if (displayMode == DisplayMode::Default) {
  689. dt += 100.0 / engineGetSampleRate();
  690. if (dt > scrollTime)
  691. {
  692. dt = 0.0f;
  693. chPathPosition += 0.05f;
  694. if (chPathPosition > 1.0f)
  695. chPathPosition = 0.0f;
  696. }
  697. }
  698. TransparentWidget::step(); // parent whatever he does
  699. return;
  700. } // end step()
  701. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  702. // draw()
  703. // @vg : (IN) NVGcontext to draw on
  704. // Middle display drawing.
  705. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  706. void TSOscCVMiddleDisplay::draw(/*in*/ NVGcontext *vg) {
  707. bool isPreview = parentWidget->module == NULL; // May get a NULL module for preview
  708. // Background Colors:
  709. NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  710. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  711. // Screen:
  712. nvgBeginPath(vg);
  713. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 5.0);
  714. nvgFillColor(vg, backgroundColor);
  715. nvgFill(vg);
  716. nvgStrokeWidth(vg, 1.0);
  717. nvgStrokeColor(vg, borderColor);
  718. nvgStroke(vg);
  719. if (!displayMode)
  720. return;
  721. if (!isPreview)
  722. {
  723. oscCV* thisModule = dynamic_cast<oscCV*>(parentWidget->module);
  724. // Default Font:
  725. nvgFontSize(vg, 9);
  726. nvgFontFaceId(vg, font->handle);
  727. nvgTextLetterSpacing(vg, 1);
  728. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  729. nvgFillColor(vg, textColor);
  730. int dy = 2;
  731. int dx = 4;
  732. int height = 28;
  733. int width = box.size.x / 2.0 - 4;
  734. int y = 2;
  735. float txtBounds[4] = { 0,0,0,0 };
  736. const int txtPadding = 4;
  737. int txtWidth = width - txtPadding;
  738. bool drawBoxes = false;
  739. for (int c = 0; c < thisModule->numberChannels; c++) {
  740. int ix = 0;
  741. float nextX;
  742. //---- INPUT ----
  743. int x = 2;
  744. drawChannelChart(vg, &(thisModule->inputChannels[c]), x, y, width, height, oscCVWidget::CHANNEL_COLORS[c]);
  745. if (drawBoxes) {
  746. // Debug draw box:
  747. nvgBeginPath(vg);
  748. nvgRect(vg, x, y, width, height);
  749. nvgStrokeColor(vg, oscCVWidget::CHANNEL_COLORS[c]);
  750. nvgStrokeWidth(vg, 1.0);
  751. nvgStroke(vg);
  752. }
  753. // Label:
  754. nextX = nvgTextBounds(vg, x, y, thisModule->inputChannels[c].path.c_str(), NULL, txtBounds);
  755. ix = 0;
  756. if (nextX > txtWidth) {
  757. ix = chPathPosition * thisModule->inputChannels[c].path.length();
  758. }
  759. nvgScissor(vg, x, y, txtWidth, height);
  760. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  761. nvgText(vg, x, y + 1, &(thisModule->inputChannels[c].path.c_str()[ix]), NULL);
  762. nvgResetScissor(vg);
  763. //---- OUTPUT ----
  764. x += dx + width;
  765. drawChannelChart(vg, &(thisModule->outputChannels[c]), x, y, width, height, oscCVWidget::CHANNEL_COLORS[c]);
  766. if (drawBoxes) {
  767. // Debug draw box:
  768. nvgBeginPath(vg);
  769. nvgRect(vg, x, y, width, height);
  770. nvgStrokeColor(vg, oscCVWidget::CHANNEL_COLORS[thisModule->numberChannels - c - 1]);
  771. nvgStrokeWidth(vg, 1.0);
  772. nvgStroke(vg);
  773. }
  774. // Label:
  775. nextX = nvgTextBounds(vg, x, y, thisModule->outputChannels[c].path.c_str(), NULL, txtBounds);
  776. ix = thisModule->outputChannels[c].path.length();
  777. if (nextX > txtWidth) {
  778. ix = thisModule->outputChannels[c].path.length() - chPathPosition * thisModule->outputChannels[c].path.length();
  779. }
  780. nvgScissor(vg, x + txtPadding, y, txtWidth, height);
  781. nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP);
  782. nvgText(vg, x + width, y + 1, thisModule->outputChannels[c].path.c_str(), &(thisModule->outputChannels[c].path.c_str()[ix]));
  783. nvgResetScissor(vg);
  784. y += dy + height;
  785. }
  786. } // end if we actually have a module
  787. return;
  788. } // end draw()
  789. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  790. // drawChannelChart()
  791. // Draw the channel data.
  792. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  793. void TSOscCVMiddleDisplay::drawChannelChart(/*in*/ NVGcontext *vg, /*in*/ TSOSCCVChannel* channelData,
  794. /*in*/ int x, /*in*/ int y, /*in*/ int width, /*in*/ int height,
  795. /*in*/ NVGcolor lineColor)
  796. {
  797. nvgScissor(vg, x, y, width, height);
  798. nvgBeginPath(vg);
  799. float dx = static_cast<float>(width) / TROWA_OSCCV_VAL_BUFFER_SIZE;
  800. //if (dx < 0.5)
  801. // dx = 0.5;
  802. float px = x;
  803. nvgMoveTo(vg, x, y);
  804. float startY = y + height;
  805. for (int i = 0; i < TROWA_OSCCV_VAL_BUFFER_SIZE; i++)
  806. {
  807. float py = startY - rescale(channelData->valBuffer[i], channelData->minVoltage, channelData->maxVoltage, 0, height);
  808. nvgLineTo(vg, px, py);
  809. px += dx;
  810. }
  811. nvgStrokeColor(vg, lineColor);
  812. nvgStrokeWidth(vg, 1.0);
  813. nvgStroke(vg);
  814. nvgResetScissor(vg);
  815. return;
  816. }
  817. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  818. // drawChannelChart()
  819. // Draw the channel data.
  820. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  821. void TSOscCVMiddleDisplay::drawChannelBar(/*in*/ NVGcontext *vg, /*in*/ TSOSCCVChannel* channelData,
  822. /*in*/ int x, /*in*/ int y, /*in*/ int width, /*in*/ int height,
  823. /*in*/ NVGcolor lineColor)
  824. {
  825. nvgScissor(vg, x, y, width, height);
  826. nvgBeginPath(vg);
  827. float dx = static_cast<float>(width) / TROWA_OSCCV_VAL_BUFFER_SIZE;
  828. float px = x;
  829. nvgMoveTo(vg, x, y);
  830. for (int i = 0; i < TROWA_OSCCV_VAL_BUFFER_SIZE; i++)
  831. {
  832. float py = y + rescale(channelData->valBuffer[i], channelData->minVoltage, channelData->maxVoltage, 0, height);
  833. nvgLineTo(vg, px, py);
  834. px += dx;
  835. }
  836. nvgStrokeColor(vg, lineColor);
  837. nvgStrokeWidth(vg, 1.0);
  838. nvgStroke(vg);
  839. nvgResetScissor(vg);
  840. return;
  841. }
  842. // On click
  843. void TSOscCVDataTypeSelectBtn::onAction(EventAction &e) {
  844. if (visible)
  845. {
  846. Menu *menu = rack::global_ui->ui.gScene->createMenu();
  847. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  848. menu->box.size.x = box.size.x;
  849. for (int i = 0; i < numVals; i++) {
  850. TSOscCVDataTypeItem *option = new TSOscCVDataTypeItem(this, i);
  851. option->itemVal = itemVals[i];
  852. option->text = itemStrs[i];
  853. menu->addChild(option);
  854. }
  855. }
  856. return;
  857. }
  858. // Draw if visible
  859. void TSOscCVDataTypeSelectBtn::draw(NVGcontext *vg) {
  860. if (visible) {
  861. nvgScissor(vg, 0, 0, box.size.x, box.size.y);
  862. // Background
  863. nvgBeginPath(vg);
  864. nvgRoundedRect(vg, 0, 0, box.size.x, box.size.y, 5.0);
  865. nvgFillColor(vg, backgroundColor);
  866. nvgFill(vg);
  867. // Border
  868. if (borderWidth > 0) {
  869. nvgStrokeWidth(vg, borderWidth);
  870. nvgStrokeColor(vg, borderColor);
  871. nvgStroke(vg);
  872. }
  873. nvgResetScissor(vg);
  874. if (font->handle >= 0) {
  875. nvgScissor(vg, 0, 0, box.size.x - 5, box.size.y);
  876. nvgFillColor(vg, color);
  877. nvgFontFaceId(vg, font->handle);
  878. //nvgTextLetterSpacing(vg, 0.0);
  879. nvgFontSize(vg, fontSize);
  880. nvgText(vg, textOffset.x, textOffset.y, text.c_str(), NULL);
  881. nvgResetScissor(vg);
  882. bndUpDownArrow(vg, box.size.x - 10, 10, 5, color);
  883. }
  884. }
  885. return;
  886. } // end draw()
  887. // When the selected item chagnes.
  888. void TSOscCVDataTypeSelectBtn::onSelectedIndexChanged() {
  889. if (parentScreen != NULL)
  890. {
  891. parentScreen->setDataType(static_cast<TSOSCCVChannel::ArgDataType>(this->selectedVal));
  892. }
  893. return;
  894. }
  895. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  896. // TSOscCVChannelConfigScreen()
  897. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  898. TSOscCVChannelConfigScreen::TSOscCVChannelConfigScreen(oscCVWidget* widget, Vec pos, Vec boxSize)
  899. {
  900. visible = false;
  901. box.size = boxSize;
  902. parentWidget = widget;
  903. Module* thisModule = (widget != NULL) ? widget->module : NULL;
  904. font = Font::load(assetPlugin(plugin, TROWA_DIGITAL_FONT));
  905. labelFont = Font::load(assetPlugin(plugin, TROWA_LABEL_FONT));
  906. fontSize = 10;
  907. box.pos = pos;
  908. //visible = true;
  909. //debug("Init Channel Config screen");
  910. int x, y;
  911. Vec tbSize = Vec(180, 20);
  912. int dx = 20;
  913. int dy = 15;
  914. // -- Toggle Translate Buton and Light
  915. x = startX;
  916. y = startY;
  917. Vec ledSize = Vec(15, 15);
  918. //NVGcolor backgroundColor = nvgRGBA(0, 0, 0, 0);
  919. //NVGcolor color = COLOR_TS_GRAY;
  920. //NVGcolor borderColor = COLOR_TS_GRAY;
  921. Vec btnSize = Vec(100, 20);
  922. x = box.size.x - startX - btnSize.x;
  923. btnToggleTranslateVals = new TS_ScreenCheckBox(/*size*/ btnSize, /*module*/ thisModule, /*paramId*/ oscCV::ParamIds::OSC_CH_TRANSLATE_VALS_PARAM, /*text*/ "Convert Values", /*minVal*/ 0.0f, /*maxVal*/ 1.0f, /*defVal*/ 0.0f);
  924. btnToggleTranslateVals->fontSize = 9;
  925. btnToggleTranslateVals->color = COLOR_TS_GRAY;
  926. btnToggleTranslateVals->borderWidth = 0;
  927. btnToggleTranslateVals->padding = 2;
  928. btnToggleTranslateVals->textAlign = TS_ScreenBtn::TextAlignment::Right;
  929. btnToggleTranslateVals->box.pos = Vec(x, y);
  930. //btnToggleTranslateVals->minValue = 0.0f;
  931. //btnToggleTranslateVals->maxValue = 1.0f;
  932. addChild(btnToggleTranslateVals);
  933. //if (widget != NULL)
  934. //{
  935. // // Offset the position.
  936. // btnToggleTranslateVals->box.pos.x += box.pos.x;
  937. // btnToggleTranslateVals->box.pos.y += box.pos.y;
  938. // widget->addParam(btnToggleTranslateVals); //widget->addParam(btnToggleTranslateVals);
  939. //}
  940. //else
  941. // addChild(btnToggleTranslateVals);
  942. //debug("Light at (%d, %d). %.2fx%.2f.", x, y, ledSize.x, ledSize.y);
  943. //lightTranslateVals = dynamic_cast<ColorValueLight*>(TS_createColorValueLight<ColorValueLight>(Vec(x+3, y), thisModule, oscCV::LightIds::OSC_CH_TRANSLATE_LIGHT, ledSize, COLOR_WHITE));
  944. //if (widget != NULL)
  945. //{
  946. // lightTranslateVals->box.pos.x += this->box.pos.x;
  947. // lightTranslateVals->box.pos.y += this->box.pos.y;
  948. // widget->addChild(lightTranslateVals);
  949. //}
  950. //else
  951. // addChild(lightTranslateVals);
  952. //debug("Starting text boxes");
  953. // -- Min/Max Text Boxes
  954. btnSize = Vec(70, 20);
  955. tbSize = Vec(70, 20);
  956. x = startX;
  957. y = startY + ledSize.y + dy + fontSize * 2 + 5; // 13 + 15 + 20 + fontSize
  958. for (int i = 0; i < TextBoxIx::NumTextBoxes; i++) {
  959. tbNumericBounds[i] = new TSTextField(TSTextField::TextType::RealNumberOnly, 25);
  960. tbNumericBounds[i]->setRealPrecision(3); // mV? why not
  961. tbNumericBounds[i]->box.size = tbSize;
  962. tbNumericBounds[i]->box.pos = Vec(x, y);
  963. tbNumericBounds[i]->id = i;
  964. //debug("TextBox %d at (%d, %d)", i, x, y);
  965. // Next Field
  966. if (i > 0)
  967. {
  968. tbNumericBounds[i]->prevField = tbNumericBounds[i-1];
  969. tbNumericBounds[i-1]->nextField = tbNumericBounds[i];
  970. }
  971. addChild(tbNumericBounds[i]);
  972. // Position:
  973. if (i % 2)
  974. {
  975. if (i < TextBoxIx::NumTextBoxes - 1) {
  976. x = startX; // New row
  977. y += dy + tbSize.y + fontSize * 2 + 5;
  978. }
  979. }
  980. else
  981. {
  982. x += tbSize.x + dx; // Next column
  983. }
  984. } // end for
  985. //TextBoxIx::NumTextBoxes
  986. tbNumericBounds[TextBoxIx::NumTextBoxes - 1]->nextField = tbNumericBounds[0]; // Loop back around
  987. //debug("Starting btn select");
  988. // Data Type
  989. x = startX;
  990. y += tbSize.y + dy + dy;
  991. //debug("Select at (%d, %d). %.2fx%.2f.", x, y, btnSize.x, btnSize.y);
  992. btnSelectDataType = new TSOscCVDataTypeSelectBtn(numDataTypes, reinterpret_cast<int*>(oscDataTypeVals), oscDataTypeStr, static_cast<int>(selectedDataType));
  993. btnSelectDataType->box.size = btnSize;
  994. btnSelectDataType->box.pos = Vec(x, y);
  995. btnSelectDataType->parentScreen = this;
  996. addChild(btnSelectDataType);
  997. //debug("Save Btn");
  998. // Save Button
  999. y += btnSelectDataType->box.size.y + dy;
  1000. //debug("Save Button at (%d, %d). %.2fx%.2f.", x, y, btnSize.x, btnSize.y);
  1001. //Vec size, Module* module, int paramId, std::string text, float minVal, float maxVal, float defVal
  1002. btnSave = new TS_ScreenBtn(/*size*/ btnSize, /*module*/ thisModule, /*paramId*/ oscCV::ParamIds::OSC_CH_SAVE_PARAM, /*text*/ "Save", /*minVal*/ 0.0f, /*maxVal*/ 1.0f, /*defVal*/ 0.0f);
  1003. btnSave->box.pos = Vec(x, y);
  1004. if (widget != NULL)
  1005. addChild(btnSave);// widget->addParam(btnSave);
  1006. else
  1007. addChild(btnSave);
  1008. //debug("Cancel Btn");
  1009. // Cancel button
  1010. x += btnSize.x + dx;
  1011. //debug("Cancel Button at (%d, %d). %.2fx%.2f.", x, y, btnSize.x, btnSize.y);
  1012. btnCancel = new TS_ScreenBtn(/*size*/ btnSize, /*module*/ thisModule, /*paramId*/ oscCV::ParamIds::OSC_CH_CANCEL_PARAM, /*text*/ "Cancel", /*minVal*/ 0.0f, /*maxVal*/ 1.0f, /*defVal*/ 0.0f);
  1013. btnCancel->box.pos = Vec(x, y);
  1014. if (widget != NULL)
  1015. addChild(btnCancel);// widget->addParam(btnCancel);
  1016. else
  1017. addChild(btnCancel);
  1018. return;
  1019. } // end TSOscCVChannelConfigScreen()
  1020. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1021. // draw()
  1022. // @vg : (IN) NVGcontext to draw on
  1023. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1024. void TSOscCVChannelConfigScreen::draw(/*in*/ NVGcontext *vg)
  1025. {
  1026. if (this->visible) {
  1027. // Default Font:
  1028. nvgFontSize(vg, fontSize);
  1029. nvgFontFaceId(vg, font->handle);
  1030. nvgTextLetterSpacing(vg, 1);
  1031. NVGcolor textColor = nvgRGB(0xee, 0xee, 0xee);
  1032. NVGcolor errorColor = COLOR_TS_RED;
  1033. // Draw labels
  1034. int x, y;
  1035. Vec tbSize = Vec(70, 20);
  1036. //Vec ledSize = Vec(15, 15);
  1037. int dx = 20;
  1038. int dy = 15;
  1039. const int errorDy = 32;
  1040. NVGcolor channelColor = this->parentWidget->CHANNEL_COLORS[this->currentChannelPtr->channelNum - 1];
  1041. // -- Heading --
  1042. // Channel Number and address
  1043. x = startX;// +ledSize.x + 10;
  1044. y = startY;
  1045. sprintf(buffer, "CH %d %sPUT", this->currentChannelPtr->channelNum, (isInput) ? "IN" : "OUT");
  1046. float txtBounds[4] = { 0,0,0,0 };
  1047. const float padding = 2.0f;
  1048. nvgFontSize(vg, fontSize*1.1);
  1049. nvgFontFaceId(vg, font->handle);
  1050. nvgTextBounds(vg, x, y, buffer, NULL, txtBounds); //float txtWidth =
  1051. nvgBeginPath(vg);
  1052. nvgRect(vg, txtBounds[0], y, txtBounds[2] - txtBounds[0] + padding*2, txtBounds[3] - txtBounds[1] + padding*2);
  1053. nvgFillColor(vg, channelColor);
  1054. nvgFill(vg);
  1055. nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
  1056. nvgFillColor(vg, COLOR_BLACK);
  1057. nvgText(vg, x + padding, y + padding, buffer, NULL);
  1058. nvgFillColor(vg, textColor);
  1059. // -- Min/Max Text Boxes
  1060. x = startX;
  1061. y = startY + 15 + dy;
  1062. // y = startY + ledSize.y + dy + fontSize*2; // 13 + 15 + 20 + fontSize
  1063. nvgFontSize(vg, fontSize*0.9);
  1064. sprintf(buffer, "Control Voltage (%s)", (isInput) ? "IN" : "OUT");
  1065. nvgText(vg, x, y, buffer, NULL);
  1066. y += fontSize + 1;
  1067. const char* labels[] = { "Min", "Max", "Min", "Max" };
  1068. for (int i = 0; i < TextBoxIx::NumTextBoxes; i++) {
  1069. if (i == 2) {
  1070. nvgFontSize(vg, fontSize*0.9);
  1071. nvgFontFaceId(vg, font->handle);
  1072. sprintf(buffer, "OSC Value (%s)", (isInput) ? "OUT" : "IN");
  1073. nvgText(vg, x, y, buffer, NULL);
  1074. y += fontSize + 1;
  1075. }
  1076. nvgFontSize(vg, fontSize);
  1077. nvgFontFaceId(vg, labelFont->handle);
  1078. nvgText(vg, x, y, labels[i], NULL);
  1079. // Errors if any
  1080. if (tbErrors[i].length() > 0) {
  1081. nvgFontFaceId(vg, labelFont->handle);
  1082. nvgFillColor(vg, errorColor);
  1083. nvgText(vg, x, y + errorDy, tbErrors[i].c_str(), NULL);
  1084. nvgFillColor(vg, textColor);
  1085. }
  1086. // Position:
  1087. if (i % 2)
  1088. {
  1089. x = startX; // New row
  1090. y += dy + tbSize.y + fontSize + 5;
  1091. }
  1092. else
  1093. {
  1094. x += tbSize.x + dx; // Next column
  1095. }
  1096. } // end for
  1097. // Data Type
  1098. nvgFontSize(vg, fontSize);
  1099. nvgFontFaceId(vg, labelFont->handle);
  1100. nvgText(vg, x, y, "Data Type", NULL);
  1101. OpaqueWidget::draw(vg); // Parent
  1102. }
  1103. return;
  1104. } // end draw()
  1105. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1106. // showControl()
  1107. // @channel: (IN) The channel which we are configuring.
  1108. // @isInput: (IN) If this an input or output.
  1109. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1110. void TSOscCVChannelConfigScreen::showControl(TSOSCCVChannel* channel, bool isInput)
  1111. {
  1112. this->currentChannelPtr = channel;
  1113. this->isInput = isInput;
  1114. // Translation On/Off
  1115. translateValsEnabled = currentChannelPtr->convertVals;
  1116. if (currentChannelPtr->convertVals)
  1117. {
  1118. this->btnToggleTranslateVals->value = 1.0f;
  1119. //parentWidget->module->lights[oscCV::LightIds::OSC_CH_TRANSLATE_LIGHT].value = 1.0f;
  1120. }
  1121. else
  1122. {
  1123. this->btnToggleTranslateVals->value = 0.0f;
  1124. //parentWidget->module->lights[oscCV::LightIds::OSC_CH_TRANSLATE_LIGHT].value = 0.0f;
  1125. }
  1126. btnToggleTranslateVals->checked = translateValsEnabled;
  1127. for (int i = 0; i < TextBoxIx::NumTextBoxes; i++)
  1128. {
  1129. tbErrors[i] = std::string("");
  1130. }
  1131. // Text Box Values
  1132. char buffer[50] = { '\0' };
  1133. sprintf(buffer, "%.3f", currentChannelPtr->minVoltage);
  1134. tbNumericBounds[TextBoxIx::MinCVVolt]->text = std::string(buffer);
  1135. sprintf(buffer, "%.3f", currentChannelPtr->maxVoltage);
  1136. tbNumericBounds[TextBoxIx::MaxCVVolt]->text = std::string(buffer);
  1137. tbNumericBounds[TextBoxIx::MinOSCVal]->enabled = true;
  1138. tbNumericBounds[TextBoxIx::MaxOSCVal]->enabled = true;
  1139. switch (currentChannelPtr->dataType)
  1140. {
  1141. case TSOSCCVChannel::ArgDataType::OscInt:
  1142. {
  1143. const char * format = "%.0f";
  1144. sprintf(buffer, format, currentChannelPtr->minOscVal);
  1145. tbNumericBounds[TextBoxIx::MinOSCVal]->text = std::string(buffer);
  1146. sprintf(buffer, format, currentChannelPtr->maxOscVal);
  1147. tbNumericBounds[TextBoxIx::MaxOSCVal]->text = std::string(buffer);
  1148. break;
  1149. }
  1150. case TSOSCCVChannel::ArgDataType::OscBool:
  1151. {
  1152. tbNumericBounds[TextBoxIx::MinOSCVal]->enabled = false;
  1153. tbNumericBounds[TextBoxIx::MaxOSCVal]->enabled = false;
  1154. tbNumericBounds[TextBoxIx::MinOSCVal]->text = std::string("0");
  1155. tbNumericBounds[TextBoxIx::MaxOSCVal]->text = std::string("1");
  1156. //const char * format = "%.0f";
  1157. //sprintf(buffer, format, currentChannelPtr->minOscVal);
  1158. //tbNumericBounds[TextBoxIx::MinOSCVal]->text = std::string(buffer);
  1159. //sprintf(buffer, format, currentChannelPtr->maxOscVal);
  1160. //tbNumericBounds[TextBoxIx::MaxOSCVal]->text = std::string(buffer);
  1161. break;
  1162. }
  1163. case TSOSCCVChannel::ArgDataType::OscFloat:
  1164. default:
  1165. {
  1166. const char * format = "%.3f";
  1167. sprintf(buffer, format, currentChannelPtr->minOscVal);
  1168. tbNumericBounds[TextBoxIx::MinOSCVal]->text = std::string(buffer);
  1169. sprintf(buffer, format, currentChannelPtr->maxOscVal);
  1170. tbNumericBounds[TextBoxIx::MaxOSCVal]->text = std::string(buffer);
  1171. break;
  1172. }
  1173. } // end switch (data type)
  1174. // Data Type
  1175. selectedDataType = currentChannelPtr->dataType;
  1176. this->btnSelectDataType->setSelectedValue(static_cast<int>(currentChannelPtr->dataType));
  1177. setVisibility(true);
  1178. return;
  1179. } // end showControl()
  1180. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1181. // Process
  1182. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1183. void TSOscCVChannelConfigScreen::step()
  1184. {
  1185. if (this->visible && parentWidget != NULL) {
  1186. oscCV* thisModule = dynamic_cast<oscCV*>( parentWidget->module );
  1187. if (thisModule) {
  1188. // Check for enable/disable data massaging
  1189. if (translateTrigger.process(thisModule->params[oscCV::ParamIds::OSC_CH_TRANSLATE_VALS_PARAM].value)) {
  1190. //debug("Translate button clicked");
  1191. translateValsEnabled = !translateValsEnabled;
  1192. }
  1193. }
  1194. btnToggleTranslateVals->checked = translateValsEnabled;
  1195. OpaqueWidget::step(); // Parent
  1196. }
  1197. return;
  1198. }
  1199. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1200. // validateValues(void)
  1201. // @returns : True if valid, false if not.
  1202. // POST CONDITION: tbErrors is set and errors may be displayed on the screen.
  1203. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1204. bool TSOscCVChannelConfigScreen::validateValues()
  1205. {
  1206. //debug("TSOscCVChannelConfigScreen::validateValues()");
  1207. bool isValid = true;
  1208. for (int i = 0; i < TextBoxIx::NumTextBoxes; i++)
  1209. {
  1210. bool valid = tbNumericBounds[i]->isValid();
  1211. tbErrors[i] = (valid) ? std::string("") : std::string("Invalid value.");
  1212. isValid = isValid && valid;
  1213. }
  1214. if (isValid)
  1215. {
  1216. //---------------------
  1217. // Check Min < Max
  1218. //---------------------
  1219. isValid = false;
  1220. try
  1221. {
  1222. // 1. Rack voltage
  1223. bool valid = std::stof(tbNumericBounds[TextBoxIx::MinCVVolt]->text, NULL) < std::stof(tbNumericBounds[TextBoxIx::MaxCVVolt]->text, NULL);
  1224. if (valid)
  1225. isValid = true;
  1226. else
  1227. tbErrors[TextBoxIx::MinCVVolt] = std::string("Min should be < Max.");
  1228. // 2. OSC Values
  1229. valid = std::stof(tbNumericBounds[TextBoxIx::MinOSCVal]->text, NULL) < std::stof(tbNumericBounds[TextBoxIx::MaxOSCVal]->text, NULL);
  1230. if (!valid)
  1231. tbErrors[TextBoxIx::MinOSCVal] = std::string("Min should be < Max.");
  1232. isValid = isValid && valid;
  1233. }
  1234. catch (const std::exception& e)
  1235. {
  1236. warn("Error %s.", e.what());
  1237. }
  1238. } // end if valid values
  1239. return isValid;
  1240. } // end validateValues()
  1241. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1242. // Save the values to the ptr.
  1243. // @channelPtr : (OUT) Place to save the values.
  1244. // @returns : True if saved, false if there was an error.
  1245. //-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-
  1246. bool TSOscCVChannelConfigScreen::saveValues(/*out*/ TSOSCCVChannel* channelPtr)
  1247. {
  1248. bool saved = false;
  1249. if (channelPtr != NULL && validateValues())
  1250. {
  1251. channelPtr->convertVals = translateValsEnabled;// btnToggleTranslateVals->value > 0;
  1252. channelPtr->dataType = static_cast<TSOSCCVChannel::ArgDataType>(btnSelectDataType->selectedVal);
  1253. try
  1254. {
  1255. channelPtr->minVoltage = std::stof(tbNumericBounds[TextBoxIx::MinCVVolt]->text);
  1256. channelPtr->maxVoltage = std::stof(tbNumericBounds[TextBoxIx::MaxCVVolt]->text);
  1257. channelPtr->minOscVal = std::stof(tbNumericBounds[TextBoxIx::MinOSCVal]->text);
  1258. channelPtr->maxOscVal = std::stof(tbNumericBounds[TextBoxIx::MaxOSCVal]->text);
  1259. saved = true;
  1260. }
  1261. catch (const std::exception& e)
  1262. {
  1263. warn("Error %s.", e.what());
  1264. }
  1265. }
  1266. return saved;
  1267. } // end saveValues()