Audio plugin host https://kx.studio/carla
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.

1678 lines
55KB

  1. /*
  2. * Carla plugin host
  3. * Copyright (C) 2011-2019 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. //---------------------------------------------------------------------------------------------------------------------
  18. // Imports (Global)
  19. #include <QtCore/QStringList>
  20. #include <QtCore/QTimer>
  21. #include <QtGui/QPainter>
  22. #include <QtWidgets/QFileDialog>
  23. #include <QtWidgets/QFileSystemModel>
  24. #include <QtWidgets/QMainWindow>
  25. #include <QtWidgets/QMessageBox>
  26. //---------------------------------------------------------------------------------------------------------------------
  27. // Imports (Custom)
  28. #include "host.hpp"
  29. #include "ui_carla_host.hpp"
  30. #include "../../backend/CarlaHost.h"
  31. #include "../../backend/CarlaUtils.h"
  32. #include "../../utils/CarlaBackendUtils.hpp"
  33. #include "../../utils/CarlaMathUtils.hpp"
  34. #include "../../utils/CarlaString.hpp"
  35. //---------------------------------------------------------------------------------------------------------------------
  36. CarlaObject gCarla;
  37. //---------------------------------------------------------------------------------------------------------------------
  38. Host::Host()
  39. : isControl(false),
  40. isPlugin(false),
  41. isRemote(false),
  42. nsmOK(false),
  43. processMode(ENGINE_PROCESS_MODE_PATCHBAY),
  44. transportMode(ENGINE_TRANSPORT_MODE_INTERNAL),
  45. transportExtra(),
  46. nextProcessMode(processMode),
  47. processModeForced(false),
  48. audioDriverForced(),
  49. experimental(false),
  50. exportLV2(false),
  51. forceStereo(false),
  52. manageUIs(false),
  53. maxParameters(false),
  54. preferPluginBridges(false),
  55. preferUIBridges(false),
  56. preventBadBehaviour(false),
  57. showLogs(false),
  58. showPluginBridges(false),
  59. showWineBridges(false),
  60. uiBridgesTimeout(0),
  61. uisAlwaysOnTop(false),
  62. pathBinaries(),
  63. pathResources() {}
  64. //---------------------------------------------------------------------------------------------------------------------
  65. struct CachedSavedSettings {
  66. uint _CARLA_KEY_MAIN_REFRESH_INTERVAL = 0;
  67. bool _CARLA_KEY_MAIN_CONFIRM_EXIT = false;
  68. bool _CARLA_KEY_CANVAS_FANCY_EYE_CANDY = false;
  69. };
  70. //---------------------------------------------------------------------------------------------------------------------
  71. // Host Private Data
  72. struct CarlaHostWindow::PrivateData {
  73. Ui::CarlaHostW ui;
  74. int fIdleTimerNull = 0;
  75. int fIdleTimerFast = 0;
  76. int fIdleTimerSlow = 0;
  77. bool fLadspaRdfNeedsUpdate = true;
  78. QStringList fLadspaRdfList;
  79. int fPluginCount = 0;
  80. QList<QWidget*> fPluginList;
  81. void* fPluginDatabaseDialog = nullptr;
  82. QStringList fFavoritePlugins;
  83. QCarlaString fProjectFilename;
  84. bool fIsProjectLoading = false;
  85. bool fCurrentlyRemovingAllPlugins = false;
  86. double fLastTransportBPM = 0.0;
  87. uint64_t fLastTransportFrame = 0;
  88. bool fLastTransportState = false;
  89. uint fBufferSize = 0;
  90. double fSampleRate = 0.0;
  91. QCarlaString fOscAddressTCP;
  92. QCarlaString fOscAddressUDP;
  93. QCarlaString fOscReportedHost;
  94. #ifdef CARLA_OS_MAC
  95. bool fMacClosingHelper = true;
  96. #endif
  97. // CancelableActionCallback Box
  98. void* fCancelableActionBox = nullptr;
  99. // run a custom action after engine is properly closed
  100. CustomActions fCustomStopAction = CUSTOM_ACTION_NONE;
  101. // first attempt of auto-start engine doesn't show an error
  102. bool fFirstEngineInit = true;
  103. // to be filled with key-value pairs of current settings
  104. CachedSavedSettings fSavedSettings;
  105. QCarlaString fClientName;
  106. QCarlaString fSessionManagerName;
  107. //-----------------------------------------------------------------------------------------------------------------
  108. // Internal stuff (patchbay)
  109. QImage fExportImage;
  110. bool fPeaksCleared = true;
  111. bool fExternalPatchbay = false;
  112. QList<uint> fSelectedPlugins;
  113. int fCanvasWidth = 0;
  114. int fCanvasHeight = 0;
  115. int fMiniCanvasUpdateTimeout = 0;
  116. bool fWithCanvas;
  117. //-----------------------------------------------------------------------------------------------------------------
  118. // GUI stuff (disk)
  119. QFileSystemModel fDirModel;
  120. //-----------------------------------------------------------------------------------------------------------------
  121. // CarlaHostWindow* hostWindow,
  122. PrivateData(CarlaHostWindow* const hostWindow, const bool withCanvas)
  123. : fWithCanvas(withCanvas),
  124. fDirModel(hostWindow)
  125. {
  126. ui.setupUi(hostWindow);
  127. }
  128. };
  129. //---------------------------------------------------------------------------------------------------------------------
  130. // Host Window
  131. CarlaHostWindow::CarlaHostWindow(Host& h, const bool withCanvas, QWidget* const parent)
  132. : QMainWindow(parent),
  133. host(h),
  134. self(new PrivateData(this, withCanvas))
  135. {
  136. gCarla.gui = this;
  137. static const char* const CARLA_CLIENT_NAME = getenv("CARLA_CLIENT_NAME");
  138. static const char* const LADISH_APP_NAME = getenv("LADISH_APP_NAME");
  139. static const char* const NSM_URL = getenv("NSM_URL");
  140. //-----------------------------------------------------------------------------------------------------------------
  141. // Internal stuff
  142. // keep application signals alive
  143. self->fIdleTimerNull = startTimer(1000);
  144. if (host.isControl)
  145. {
  146. self->fClientName = "Carla-Control";
  147. self->fSessionManagerName = "Control";
  148. }
  149. else if (host.isPlugin)
  150. {
  151. self->fClientName = "Carla-Plugin";
  152. self->fSessionManagerName = "Plugin";
  153. }
  154. else if (LADISH_APP_NAME != nullptr)
  155. {
  156. self->fClientName = LADISH_APP_NAME;
  157. self->fSessionManagerName = "LADISH";
  158. }
  159. else if (NSM_URL != nullptr && host.nsmOK)
  160. {
  161. self->fClientName = "Carla.tmp";
  162. self->fSessionManagerName = "Non Session Manager TMP";
  163. }
  164. else
  165. {
  166. self->fClientName = CARLA_CLIENT_NAME != nullptr ? CARLA_CLIENT_NAME : "Carla";
  167. self->fSessionManagerName = "";
  168. }
  169. //-----------------------------------------------------------------------------------------------------------------
  170. // Set up GUI (engine stopped)
  171. if (host.isPlugin || host.isControl)
  172. {
  173. self->ui.act_file_save->setVisible(false);
  174. self->ui.act_engine_start->setEnabled(false);
  175. self->ui.act_engine_start->setVisible(false);
  176. self->ui.act_engine_stop->setEnabled(false);
  177. self->ui.act_engine_stop->setVisible(false);
  178. self->ui.menu_Engine->setEnabled(false);
  179. self->ui.menu_Engine->setVisible(false);
  180. if (QAction* const action = self->ui.menu_Engine->menuAction())
  181. action->setVisible(false);
  182. self->ui.tabWidget->removeTab(2);
  183. if (host.isControl)
  184. {
  185. self->ui.act_file_new->setVisible(false);
  186. self->ui.act_file_open->setVisible(false);
  187. self->ui.act_file_save_as->setVisible(false);
  188. self->ui.tabUtils->removeTab(0);
  189. }
  190. else
  191. {
  192. self->ui.act_file_save_as->setText(tr("Export as..."));
  193. if (! withCanvas)
  194. if (QTabBar* const tabBar = self->ui.tabWidget->tabBar())
  195. tabBar->hide();
  196. }
  197. }
  198. else
  199. {
  200. self->ui.act_engine_start->setEnabled(true);
  201. #ifdef CARLA_OS_WIN
  202. self->ui.tabWidget->removeTab(2);
  203. #endif
  204. }
  205. if (host.isControl)
  206. {
  207. self->ui.act_file_refresh->setEnabled(false);
  208. }
  209. else
  210. {
  211. self->ui.act_file_connect->setEnabled(false);
  212. self->ui.act_file_connect->setVisible(false);
  213. self->ui.act_file_refresh->setEnabled(false);
  214. self->ui.act_file_refresh->setVisible(false);
  215. }
  216. if (self->fSessionManagerName.isNotEmpty() && ! host.isPlugin)
  217. self->ui.act_file_new->setEnabled(false);
  218. self->ui.act_file_open->setEnabled(false);
  219. self->ui.act_file_save->setEnabled(false);
  220. self->ui.act_file_save_as->setEnabled(false);
  221. self->ui.act_engine_stop->setEnabled(false);
  222. self->ui.act_plugin_remove_all->setEnabled(false);
  223. self->ui.act_canvas_show_internal->setChecked(false);
  224. self->ui.act_canvas_show_internal->setVisible(false);
  225. self->ui.act_canvas_show_external->setChecked(false);
  226. self->ui.act_canvas_show_external->setVisible(false);
  227. self->ui.menu_PluginMacros->setEnabled(false);
  228. self->ui.menu_Canvas->setEnabled(false);
  229. QWidget* const dockWidgetTitleBar = new QWidget(this);
  230. self->ui.dockWidget->setTitleBarWidget(dockWidgetTitleBar);
  231. if (! withCanvas)
  232. {
  233. self->ui.act_canvas_show_internal->setVisible(false);
  234. self->ui.act_canvas_show_external->setVisible(false);
  235. self->ui.act_canvas_arrange->setVisible(false);
  236. self->ui.act_canvas_refresh->setVisible(false);
  237. self->ui.act_canvas_save_image->setVisible(false);
  238. self->ui.act_canvas_zoom_100->setVisible(false);
  239. self->ui.act_canvas_zoom_fit->setVisible(false);
  240. self->ui.act_canvas_zoom_in->setVisible(false);
  241. self->ui.act_canvas_zoom_out->setVisible(false);
  242. self->ui.act_settings_show_meters->setVisible(false);
  243. self->ui.act_settings_show_keyboard->setVisible(false);
  244. self->ui.menu_Canvas_Zoom->setEnabled(false);
  245. self->ui.menu_Canvas_Zoom->setVisible(false);
  246. if (QAction* const action = self->ui.menu_Canvas_Zoom->menuAction())
  247. action->setVisible(false);
  248. self->ui.menu_Canvas->setEnabled(false);
  249. self->ui.menu_Canvas->setVisible(false);
  250. if (QAction* const action = self->ui.menu_Canvas->menuAction())
  251. action->setVisible(false);
  252. self->ui.tw_miniCanvas->hide();
  253. self->ui.tabWidget->removeTab(1);
  254. #ifdef CARLA_OS_WIN
  255. self->ui.tabWidget->tabBar().hide()
  256. #endif
  257. }
  258. //-----------------------------------------------------------------------------------------------------------------
  259. // Set up GUI (disk)
  260. const QString home(QDir::homePath());
  261. self->fDirModel.setRootPath(home);
  262. if (const char* const* const exts = carla_get_supported_file_extensions())
  263. {
  264. QStringList filters;
  265. const QString prefix("*.");
  266. for (uint i=0; exts[i] != nullptr; ++i)
  267. filters.append(prefix + exts[i]);
  268. self->fDirModel.setNameFilters(filters);
  269. }
  270. self->ui.fileTreeView->setModel(&self->fDirModel);
  271. self->ui.fileTreeView->setRootIndex(self->fDirModel.index(home));
  272. self->ui.fileTreeView->setColumnHidden(1, true);
  273. self->ui.fileTreeView->setColumnHidden(2, true);
  274. self->ui.fileTreeView->setColumnHidden(3, true);
  275. self->ui.fileTreeView->setHeaderHidden(true);
  276. //-----------------------------------------------------------------------------------------------------------------
  277. // Set up GUI (transport)
  278. const QFontMetrics fontMetrics(self->ui.l_transport_bbt->fontMetrics());
  279. int minValueWidth = fontMetricsHorizontalAdvance(fontMetrics, "000|00|0000");
  280. int minLabelWidth = fontMetricsHorizontalAdvance(fontMetrics, self->ui.label_transport_frame->text());
  281. int labelTimeWidth = fontMetricsHorizontalAdvance(fontMetrics, self->ui.label_transport_time->text());
  282. int labelBBTWidth = fontMetricsHorizontalAdvance(fontMetrics, self->ui.label_transport_bbt->text());
  283. if (minLabelWidth < labelTimeWidth)
  284. minLabelWidth = labelTimeWidth;
  285. if (minLabelWidth < labelBBTWidth)
  286. minLabelWidth = labelBBTWidth;
  287. self->ui.label_transport_frame->setMinimumWidth(minLabelWidth + 3);
  288. self->ui.label_transport_time->setMinimumWidth(minLabelWidth + 3);
  289. self->ui.label_transport_bbt->setMinimumWidth(minLabelWidth + 3);
  290. self->ui.l_transport_bbt->setMinimumWidth(minValueWidth + 3);
  291. self->ui.l_transport_frame->setMinimumWidth(minValueWidth + 3);
  292. self->ui.l_transport_time->setMinimumWidth(minValueWidth + 3);
  293. if (host.isPlugin)
  294. {
  295. self->ui.b_transport_play->setEnabled(false);
  296. self->ui.b_transport_stop->setEnabled(false);
  297. self->ui.b_transport_backwards->setEnabled(false);
  298. self->ui.b_transport_forwards->setEnabled(false);
  299. self->ui.group_transport_controls->setEnabled(false);
  300. self->ui.group_transport_controls->setVisible(false);
  301. self->ui.cb_transport_link->setEnabled(false);
  302. self->ui.cb_transport_link->setVisible(false);
  303. self->ui.cb_transport_jack->setEnabled(false);
  304. self->ui.cb_transport_jack->setVisible(false);
  305. self->ui.dsb_transport_bpm->setEnabled(false);
  306. self->ui.dsb_transport_bpm->setReadOnly(true);
  307. }
  308. self->ui.w_transport->setEnabled(false);
  309. //-----------------------------------------------------------------------------------------------------------------
  310. // Set up GUI (rack)
  311. // self->ui.listWidget->setHostAndParent(self->host, self);
  312. if (QScrollBar* const sb = self->ui.listWidget->verticalScrollBar())
  313. {
  314. self->ui.rackScrollBar->setMinimum(sb->minimum());
  315. self->ui.rackScrollBar->setMaximum(sb->maximum());
  316. self->ui.rackScrollBar->setValue(sb->value());
  317. /*
  318. sb->rangeChanged.connect(self->ui.rackScrollBar.setRange);
  319. sb->valueChanged.connect(self->ui.rackScrollBar.setValue);
  320. self->ui.rackScrollBar->rangeChanged.connect(sb.setRange);
  321. self->ui.rackScrollBar->valueChanged.connect(sb.setValue);
  322. */
  323. }
  324. /*
  325. updateStyle();
  326. */
  327. self->ui.rack->setStyleSheet(" \
  328. CarlaRackList#CarlaRackList { \
  329. background-color: black; \
  330. } \
  331. ");
  332. //-----------------------------------------------------------------------------------------------------------------
  333. // Set up GUI (patchbay)
  334. /*
  335. self->ui.peak_in->setChannelCount(2);
  336. self->ui.peak_in->setMeterColor(DigitalPeakMeter.COLOR_BLUE);
  337. self->ui.peak_in->setMeterOrientation(DigitalPeakMeter.VERTICAL);
  338. */
  339. self->ui.peak_in->setFixedWidth(25);
  340. /*
  341. self->ui.peak_out->setChannelCount(2);
  342. self->ui.peak_out->setMeterColor(DigitalPeakMeter.COLOR_GREEN);
  343. self->ui.peak_out->setMeterOrientation(DigitalPeakMeter.VERTICAL);
  344. */
  345. self->ui.peak_out->setFixedWidth(25);
  346. /*
  347. self->ui.scrollArea = PixmapKeyboardHArea(self->ui.patchbay);
  348. self->ui.keyboard = self->ui.scrollArea.keyboard;
  349. self->ui.patchbay.layout().addWidget(self->ui.scrollArea, 1, 0, 1, 0);
  350. self->ui.scrollArea->setEnabled(false);
  351. self->ui.miniCanvasPreview->setRealParent(self);
  352. */
  353. if (QTabBar* const tabBar = self->ui.tw_miniCanvas->tabBar())
  354. tabBar->hide();
  355. //-----------------------------------------------------------------------------------------------------------------
  356. // Set up GUI (special stuff for Mac OS)
  357. #ifdef CARLA_OS_MAC
  358. self->ui.act_file_quit->setMenuRole(QAction::QuitRole);
  359. self->ui.act_settings_configure->setMenuRole(QAction::PreferencesRole);
  360. self->ui.act_help_about->setMenuRole(QAction::AboutRole);
  361. self->ui.act_help_about_qt->setMenuRole(QAction::AboutQtRole);
  362. self->ui.menu_Settings->setTitle("Panels");
  363. if (QAction* const action = self->ui.menu_Help.menuAction())
  364. action->setVisible(false);
  365. #endif
  366. //-----------------------------------------------------------------------------------------------------------------
  367. // Load Settings
  368. loadSettings(true);
  369. //-----------------------------------------------------------------------------------------------------------------
  370. // Set-up Canvas
  371. /*
  372. if (withCanvas)
  373. {
  374. self->scene = patchcanvas.PatchScene(self, self->ui.graphicsView);
  375. self->ui.graphicsView->setScene(self->scene);
  376. if (self->fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL])
  377. {
  378. self->ui.glView = QGLWidget(self);
  379. self->ui.graphicsView.setViewport(self->ui.glView);
  380. }
  381. setupCanvas();
  382. }
  383. */
  384. //-----------------------------------------------------------------------------------------------------------------
  385. // Connect actions to functions
  386. // TODO
  387. connect(self->ui.b_transport_play, SIGNAL(clicked(bool)), SLOT(slot_transportPlayPause(bool)));
  388. connect(self->ui.b_transport_stop, SIGNAL(clicked()), SLOT(slot_transportStop()));
  389. connect(self->ui.b_transport_backwards, SIGNAL(clicked()), SLOT(slot_transportBackwards()));
  390. connect(self->ui.b_transport_forwards, SIGNAL(clicked()), SLOT(slot_transportForwards()));
  391. connect(self->ui.dsb_transport_bpm, SIGNAL(valueChanged(qreal)), SLOT(slot_transportBpmChanged(qreal)));
  392. connect(self->ui.cb_transport_jack, SIGNAL(clicked(bool)), SLOT(slot_transportJackEnabled(bool)));
  393. connect(self->ui.cb_transport_link, SIGNAL(clicked(bool)), SLOT(slot_transportLinkEnabled(bool)));
  394. connect(self->ui.b_xruns, SIGNAL(clicked()), SLOT(slot_xrunClear()));
  395. connect(&host, SIGNAL(EngineStartedCallback(uint, int, int, int, float, QString)), SLOT(slot_handleEngineStartedCallback(uint, int, int, int, float, QString)));
  396. connect(&host, SIGNAL(EngineStoppedCallback()), SLOT(slot_handleEngineStoppedCallback()));
  397. //-----------------------------------------------------------------------------------------------------------------
  398. // Final setup
  399. self->ui.text_logs->clear();
  400. setProperWindowTitle();
  401. // Disable non-supported features
  402. const char* const* const features = carla_get_supported_features();
  403. if (! stringArrayContainsString(features, "link"))
  404. {
  405. self->ui.cb_transport_link->setEnabled(false);
  406. self->ui.cb_transport_link->setVisible(false);
  407. }
  408. if (! stringArrayContainsString(features, "juce"))
  409. {
  410. self->ui.act_help_about_juce->setEnabled(false);
  411. self->ui.act_help_about_juce->setVisible(false);
  412. }
  413. // Plugin needs to have timers always running so it receives messages
  414. if (host.isPlugin || host.isRemote)
  415. startTimers();
  416. // Qt needs this so it properly creates & resizes the canvas
  417. self->ui.tabWidget->blockSignals(true);
  418. self->ui.tabWidget->setCurrentIndex(1);
  419. self->ui.tabWidget->setCurrentIndex(0);
  420. self->ui.tabWidget->blockSignals(false);
  421. // Start in patchbay tab if using forced patchbay mode
  422. if (host.processModeForced && host.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
  423. self->ui.tabWidget->setCurrentIndex(1);
  424. // Load initial project file if set
  425. if (! (host.isControl || host.isPlugin))
  426. {
  427. /*
  428. projectFile = getInitialProjectFile(QApplication.instance());
  429. if (projectFile)
  430. loadProjectLater(projectFile);
  431. */
  432. }
  433. // For NSM we wait for the open message
  434. if (NSM_URL != nullptr && host.nsmOK)
  435. {
  436. carla_nsm_ready(NSM_CALLBACK_INIT);
  437. return;
  438. }
  439. if (! host.isControl)
  440. QTimer::singleShot(0, this, SLOT(slot_engineStart()));
  441. }
  442. CarlaHostWindow::~CarlaHostWindow()
  443. {
  444. delete self;
  445. }
  446. //---------------------------------------------------------------------------------------------------------------------
  447. // Setup
  448. #if 0
  449. def compactPlugin(self, pluginId):
  450. if pluginId > self->fPluginCount:
  451. return
  452. pitem = self->fPluginList[pluginId]
  453. if pitem is None:
  454. return
  455. pitem.recreateWidget(true)
  456. def changePluginColor(self, pluginId, color, colorStr):
  457. if pluginId > self->fPluginCount:
  458. return
  459. pitem = self->fPluginList[pluginId]
  460. if pitem is None:
  461. return
  462. self->host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaColor", colorStr)
  463. pitem.recreateWidget(newColor = color)
  464. def changePluginSkin(self, pluginId, skin):
  465. if pluginId > self->fPluginCount:
  466. return
  467. pitem = self->fPluginList[pluginId]
  468. if pitem is None:
  469. return
  470. self->host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkin", skin)
  471. if skin not in ("default","rncbc","presets","mpresets"):
  472. pitem.recreateWidget(newSkin = skin, newColor = (255,255,255))
  473. else:
  474. pitem.recreateWidget(newSkin = skin)
  475. def switchPlugins(self, pluginIdA, pluginIdB):
  476. if pluginIdA == pluginIdB:
  477. return
  478. if pluginIdA < 0 or pluginIdB < 0:
  479. return
  480. if pluginIdA >= self->fPluginCount or pluginIdB >= self->fPluginCount:
  481. return
  482. self->host.switch_plugins(pluginIdA, pluginIdB)
  483. itemA = self->fPluginList[pluginIdA]
  484. compactA = itemA.isCompacted()
  485. guiShownA = itemA.isGuiShown()
  486. itemB = self->fPluginList[pluginIdB]
  487. compactB = itemB.isCompacted()
  488. guiShownB = itemB.isGuiShown()
  489. itemA.setPluginId(pluginIdA)
  490. itemA.recreateWidget2(compactB, guiShownB)
  491. itemB.setPluginId(pluginIdB)
  492. itemB.recreateWidget2(compactA, guiShownA)
  493. if self->fWithCanvas:
  494. self->slot_canvasRefresh()
  495. #endif
  496. void CarlaHostWindow::setLoadRDFsNeeded() noexcept
  497. {
  498. self->fLadspaRdfNeedsUpdate = true;
  499. }
  500. void CarlaHostWindow::setProperWindowTitle()
  501. {
  502. QString title(self->fClientName);
  503. /*
  504. if (self->fProjectFilename.isNotEmpty() && ! host.nsmOK)
  505. title += QString(" - %s").arg(os.path.basename(self->fProjectFilename));
  506. */
  507. if (self->fSessionManagerName.isNotEmpty())
  508. title += QString(" (%s)").arg(self->fSessionManagerName);
  509. setWindowTitle(title);
  510. }
  511. void CarlaHostWindow::updateBufferSize(const uint newBufferSize)
  512. {
  513. if (self->fBufferSize == newBufferSize)
  514. return;
  515. self->fBufferSize = newBufferSize;
  516. self->ui.cb_buffer_size->clear();
  517. self->ui.cb_buffer_size->addItem(QString("%1").arg(newBufferSize));
  518. self->ui.cb_buffer_size->setCurrentIndex(0);
  519. }
  520. void CarlaHostWindow::updateSampleRate(const double newSampleRate)
  521. {
  522. if (carla_isEqual(self->fSampleRate, newSampleRate))
  523. return;
  524. self->fSampleRate = newSampleRate;
  525. self->ui.cb_sample_rate->clear();
  526. self->ui.cb_sample_rate->addItem(QString("%1").arg(newSampleRate));
  527. self->ui.cb_sample_rate->setCurrentIndex(0);
  528. refreshTransport(true);
  529. }
  530. //---------------------------------------------------------------------------------------------------------------------
  531. // Engine (menu actions)
  532. void CarlaHostWindow::slot_engineStart()
  533. {
  534. /*
  535. audioDriver = setEngineSettings(self->host)
  536. */
  537. const char* const audioDriver = "JACK";
  538. const bool firstInit = self->fFirstEngineInit;
  539. self->fFirstEngineInit = false;
  540. self->ui.text_logs->appendPlainText("======= Starting engine =======");
  541. if (carla_engine_init(audioDriver, self->fClientName.toUtf8()))
  542. {
  543. if (firstInit && ! (host.isControl or host.isPlugin))
  544. {
  545. QSafeSettings settings;
  546. const double lastBpm = settings.valueDouble("LastBPM", 120.0);
  547. if (lastBpm >= 20.0)
  548. carla_transport_bpm(lastBpm);
  549. }
  550. return;
  551. }
  552. else if (firstInit)
  553. {
  554. self->ui.text_logs->appendPlainText("Failed to start engine on first try, ignored");
  555. return;
  556. }
  557. const QCarlaString audioError(carla_get_last_error());
  558. if (audioError.isNotEmpty())
  559. {
  560. QMessageBox::critical(this,
  561. tr("Error"),
  562. tr("Could not connect to Audio backend '%1', possible reasons:\n%2").arg(audioDriver).arg(audioError));
  563. }
  564. else
  565. {
  566. QMessageBox::critical(this,
  567. tr("Error"),
  568. tr("Could not connect to Audio backend '%1'").arg(audioDriver));
  569. }
  570. }
  571. bool CarlaHostWindow::slot_engineStop(const bool forced)
  572. {
  573. self->ui.text_logs->appendPlainText("======= Stopping engine =======");
  574. if (self->fPluginCount == 0)
  575. {
  576. engineStopFinal();
  577. return true;
  578. }
  579. if (! forced)
  580. {
  581. const QMessageBox::StandardButton ask = QMessageBox::question(this,
  582. tr("Warning"),
  583. tr("There are still some plugins loaded, you need to remove them to stop the engine.\n"
  584. "Do you want to do this now?"),
  585. QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
  586. if (ask != QMessageBox::Yes)
  587. return false;
  588. }
  589. return slot_engineStopTryAgain();
  590. }
  591. void CarlaHostWindow::slot_engineConfig()
  592. {
  593. /*
  594. dialog = RuntimeDriverSettingsW(self->fParentOrSelf, self->host)
  595. if not dialog.exec_():
  596. return
  597. audioDevice, bufferSize, sampleRate = dialog.getValues()
  598. if self->host.is_engine_running():
  599. self->host.set_engine_buffer_size_and_sample_rate(bufferSize, sampleRate)
  600. else:
  601. self->host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
  602. self->host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, bufferSize, "")
  603. self->host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, sampleRate, "")
  604. */
  605. }
  606. bool CarlaHostWindow::slot_engineStopTryAgain()
  607. {
  608. if (carla_is_engine_running() && ! carla_set_engine_about_to_close())
  609. {
  610. QTimer::singleShot(0, this, SLOT(slot_engineStopTryAgain()));
  611. return false;
  612. }
  613. engineStopFinal();
  614. return true;
  615. }
  616. void CarlaHostWindow::engineStopFinal()
  617. {
  618. killTimers();
  619. if (carla_is_engine_running())
  620. {
  621. if (self->fCustomStopAction == CUSTOM_ACTION_PROJECT_LOAD)
  622. {
  623. /*
  624. removeAllPlugins();
  625. */
  626. }
  627. else if (self->fPluginCount != 0)
  628. {
  629. self->fCurrentlyRemovingAllPlugins = true;
  630. /*
  631. projectLoadingStarted();
  632. */
  633. }
  634. if (! carla_remove_all_plugins())
  635. {
  636. self->ui.text_logs->appendPlainText("Failed to remove all plugins, error was:");
  637. self->ui.text_logs->appendPlainText(carla_get_last_error());
  638. }
  639. if (! carla_engine_close())
  640. {
  641. self->ui.text_logs->appendPlainText("Failed to stop engine, error was:");
  642. self->ui.text_logs->appendPlainText(carla_get_last_error());
  643. }
  644. }
  645. if (self->fCustomStopAction == CUSTOM_ACTION_APP_CLOSE)
  646. {
  647. close();
  648. }
  649. else if (self->fCustomStopAction == CUSTOM_ACTION_PROJECT_LOAD)
  650. {
  651. slot_engineStart();
  652. /*
  653. loadProjectNow();
  654. */
  655. carla_nsm_ready(NSM_CALLBACK_OPEN);
  656. }
  657. self->fCustomStopAction = CUSTOM_ACTION_NONE;
  658. }
  659. //---------------------------------------------------------------------------------------------------------------------
  660. // Engine (host callbacks)
  661. void CarlaHostWindow::slot_handleEngineStartedCallback(uint pluginCount, int processMode, int transportMode, int bufferSize, float sampleRate, QString driverName)
  662. {
  663. self->ui.menu_PluginMacros->setEnabled(true);
  664. self->ui.menu_Canvas->setEnabled(true);
  665. self->ui.w_transport->setEnabled(true);
  666. self->ui.act_canvas_show_internal->blockSignals(true);
  667. self->ui.act_canvas_show_external->blockSignals(true);
  668. if (processMode == ENGINE_PROCESS_MODE_PATCHBAY) // and not self->host.isPlugin:
  669. {
  670. self->ui.act_canvas_show_internal->setChecked(true);
  671. self->ui.act_canvas_show_internal->setVisible(true);
  672. self->ui.act_canvas_show_external->setChecked(false);
  673. self->ui.act_canvas_show_external->setVisible(true);
  674. self->fExternalPatchbay = false;
  675. }
  676. else
  677. {
  678. self->ui.act_canvas_show_internal->setChecked(false);
  679. self->ui.act_canvas_show_internal->setVisible(false);
  680. self->ui.act_canvas_show_external->setChecked(true);
  681. self->ui.act_canvas_show_external->setVisible(false);
  682. self->fExternalPatchbay = true;
  683. }
  684. self->ui.act_canvas_show_internal->blockSignals(false);
  685. self->ui.act_canvas_show_external->blockSignals(false);
  686. if (! (host.isControl or host.isPlugin))
  687. {
  688. /*
  689. const bool canSave = (self->fProjectFilename.isNotEmpty() and os.path.exists(self->fProjectFilename)) or not self->fSessionManagerName;
  690. self->ui.act_file_save->setEnabled(canSave);
  691. */
  692. self->ui.act_engine_start->setEnabled(false);
  693. self->ui.act_engine_stop->setEnabled(true);
  694. }
  695. /*
  696. if (! host.isPlugin)
  697. enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED);
  698. */
  699. if (host.isPlugin || self->fSessionManagerName.isEmpty())
  700. {
  701. self->ui.act_file_open->setEnabled(true);
  702. self->ui.act_file_save_as->setEnabled(true);
  703. }
  704. self->ui.cb_transport_jack->setChecked(transportMode == ENGINE_TRANSPORT_MODE_JACK);
  705. self->ui.cb_transport_jack->setEnabled(driverName == "JACK" and processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS);
  706. if (self->ui.cb_transport_link->isEnabled())
  707. self->ui.cb_transport_link->setChecked(host.transportExtra.contains(":link:"));
  708. updateBufferSize(bufferSize);
  709. updateSampleRate(int(sampleRate));
  710. /*
  711. refreshRuntimeInfo(0.0, 0);
  712. */
  713. startTimers();
  714. self->ui.text_logs->appendPlainText("======= Engine started ========");
  715. self->ui.text_logs->appendPlainText("Carla engine started, details:");
  716. self->ui.text_logs->appendPlainText(QString(" Driver name: %1").arg(driverName));
  717. self->ui.text_logs->appendPlainText(QString(" Sample rate: %1").arg(int(sampleRate)));
  718. self->ui.text_logs->appendPlainText(QString(" Process mode: %1").arg(EngineProcessMode2Str((EngineProcessMode)processMode)));
  719. }
  720. void CarlaHostWindow::slot_handleEngineStoppedCallback()
  721. {
  722. self->ui.text_logs->appendPlainText("======= Engine stopped ========");
  723. /*
  724. patchcanvas.clear();
  725. */
  726. killTimers();
  727. // just in case
  728. /*
  729. removeAllPlugins();
  730. refreshRuntimeInfo(0.0, 0);
  731. */
  732. self->ui.menu_PluginMacros->setEnabled(false);
  733. self->ui.menu_Canvas->setEnabled(false);
  734. self->ui.w_transport->setEnabled(false);
  735. if (! (host.isControl || host.isPlugin))
  736. {
  737. self->ui.act_file_save->setEnabled(false);
  738. self->ui.act_engine_start->setEnabled(true);
  739. self->ui.act_engine_stop->setEnabled(false);
  740. }
  741. if (host.isPlugin || self->fSessionManagerName.isEmpty())
  742. {
  743. self->ui.act_file_open->setEnabled(false);
  744. self->ui.act_file_save_as->setEnabled(false);
  745. }
  746. }
  747. //---------------------------------------------------------------------------------------------------------------------
  748. // Settings
  749. void CarlaHostWindow::saveSettings()
  750. {
  751. QSafeSettings settings;
  752. settings.setValue("Geometry", saveGeometry());
  753. settings.setValue("ShowToolbar", self->ui.toolBar->isEnabled());
  754. if (! host.isControl)
  755. settings.setValue("ShowSidePanel", self->ui.dockWidget->isEnabled());
  756. QStringList diskFolders;
  757. /*
  758. for i in range(self->ui.cb_disk.count()):
  759. diskFolders.append(self->ui.cb_disk->itemData(i))
  760. */
  761. settings.setValue("DiskFolders", diskFolders);
  762. settings.setValue("LastBPM", self->fLastTransportBPM);
  763. settings.setValue("ShowMeters", self->ui.act_settings_show_meters->isChecked());
  764. settings.setValue("ShowKeyboard", self->ui.act_settings_show_keyboard->isChecked());
  765. settings.setValue("HorizontalScrollBarValue", self->ui.graphicsView->horizontalScrollBar()->value());
  766. settings.setValue("VerticalScrollBarValue", self->ui.graphicsView->verticalScrollBar()->value());
  767. settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_MODE, host.transportMode);
  768. settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_EXTRA, host.transportExtra);
  769. }
  770. void CarlaHostWindow::loadSettings(bool firstTime)
  771. {
  772. const QSafeSettings settings;
  773. if (firstTime)
  774. {
  775. const QByteArray geometry(settings.valueByteArray("Geometry"));
  776. if (! geometry.isNull())
  777. restoreGeometry(geometry);
  778. const bool showToolbar = settings.valueBool("ShowToolbar", true);
  779. self->ui.act_settings_show_toolbar->setChecked(showToolbar);
  780. self->ui.toolBar->setEnabled(showToolbar);
  781. self->ui.toolBar->setVisible(showToolbar);
  782. // if settings.contains("SplitterState"):
  783. //self->ui.splitter.restoreState(settings.value("SplitterState", b""))
  784. //else:
  785. //self->ui.splitter.setSizes([210, 99999])
  786. const bool showSidePanel = settings.valueBool("ShowSidePanel", true) && ! host.isControl;
  787. self->ui.act_settings_show_side_panel->setChecked(showSidePanel);
  788. slot_showSidePanel(showSidePanel);
  789. QStringList diskFolders;
  790. diskFolders.append(QDir::homePath());
  791. diskFolders = settings.valueStringList("DiskFolders", diskFolders);
  792. self->ui.cb_disk->setItemData(0, QDir::homePath());
  793. for (const auto& folder : diskFolders)
  794. {
  795. /*
  796. if i == 0: continue;
  797. folder = diskFolders[i];
  798. self->ui.cb_disk->addItem(os.path.basename(folder), folder);
  799. */
  800. }
  801. //if MACOS and not settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, true, bool):
  802. // self->setUnifiedTitleAndToolBarOnMac(true)
  803. const bool showMeters = settings.valueBool("ShowMeters", true);
  804. self->ui.act_settings_show_meters->setChecked(showMeters);
  805. self->ui.peak_in->setVisible(showMeters);
  806. self->ui.peak_out->setVisible(showMeters);
  807. const bool showKeyboard = settings.valueBool("ShowKeyboard", true);
  808. self->ui.act_settings_show_keyboard->setChecked(showKeyboard);
  809. /*
  810. self->ui.scrollArea->setVisible(showKeyboard);
  811. */
  812. const QSafeSettings settingsDBf("falkTX", "CarlaDatabase2");
  813. self->fFavoritePlugins = settingsDBf.valueStringList("PluginDatabase/Favorites");
  814. /*
  815. QTimer::singleShot(100, slot_restoreCanvasScrollbarValues);
  816. */
  817. }
  818. // TODO - complete this
  819. self->fSavedSettings._CARLA_KEY_MAIN_CONFIRM_EXIT = settings.valueBool(CARLA_KEY_MAIN_CONFIRM_EXIT,
  820. CARLA_DEFAULT_MAIN_CONFIRM_EXIT);
  821. self->fSavedSettings._CARLA_KEY_MAIN_REFRESH_INTERVAL = settings.valueUInt(CARLA_KEY_MAIN_REFRESH_INTERVAL,
  822. CARLA_DEFAULT_MAIN_REFRESH_INTERVAL);
  823. self->fSavedSettings._CARLA_KEY_CANVAS_FANCY_EYE_CANDY = settings.valueBool(CARLA_KEY_CANVAS_FANCY_EYE_CANDY,
  824. CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY);
  825. /*
  826. {
  827. CARLA_KEY_MAIN_PROJECT_FOLDER: settings.value(CARLA_KEY_MAIN_PROJECT_FOLDER, CARLA_DEFAULT_MAIN_PROJECT_FOLDER, str),
  828. CARLA_KEY_MAIN_EXPERIMENTAL: settings.value(CARLA_KEY_MAIN_EXPERIMENTAL, CARLA_DEFAULT_MAIN_EXPERIMENTAL, bool),
  829. CARLA_KEY_CANVAS_THEME: settings.value(CARLA_KEY_CANVAS_THEME, CARLA_DEFAULT_CANVAS_THEME, str),
  830. CARLA_KEY_CANVAS_SIZE: settings.value(CARLA_KEY_CANVAS_SIZE, CARLA_DEFAULT_CANVAS_SIZE, str),
  831. CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS: settings.value(CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS, CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS, bool),
  832. CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS: settings.value(CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS, CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS, bool),
  833. CARLA_KEY_CANVAS_USE_BEZIER_LINES: settings.value(CARLA_KEY_CANVAS_USE_BEZIER_LINES, CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES, bool),
  834. CARLA_KEY_CANVAS_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_EYE_CANDY, CARLA_DEFAULT_CANVAS_EYE_CANDY, bool),
  835. CARLA_KEY_CANVAS_USE_OPENGL: settings.value(CARLA_KEY_CANVAS_USE_OPENGL, CARLA_DEFAULT_CANVAS_USE_OPENGL, bool),
  836. CARLA_KEY_CANVAS_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_ANTIALIASING, CARLA_DEFAULT_CANVAS_ANTIALIASING, int),
  837. CARLA_KEY_CANVAS_HQ_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_HQ_ANTIALIASING, CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING, bool),
  838. CARLA_KEY_CANVAS_FULL_REPAINTS: settings.value(CARLA_KEY_CANVAS_FULL_REPAINTS, CARLA_DEFAULT_CANVAS_FULL_REPAINTS, bool),
  839. CARLA_KEY_CANVAS_INLINE_DISPLAYS: settings.value(CARLA_KEY_CANVAS_INLINE_DISPLAYS, CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS, bool),
  840. CARLA_KEY_CUSTOM_PAINTING: (settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, true, bool) and
  841. settings.value(CARLA_KEY_MAIN_PRO_THEME_COLOR, "Black", str).lower() == "black"),
  842. }
  843. */
  844. const QSafeSettings settings2("falkTX", "Carla2");
  845. if (host.experimental)
  846. {
  847. const bool visible = settings2.valueBool(CARLA_KEY_EXPERIMENTAL_JACK_APPS,
  848. CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS);
  849. self->ui.act_plugin_add_jack->setVisible(visible);
  850. }
  851. else
  852. {
  853. self->ui.act_plugin_add_jack->setVisible(false);
  854. }
  855. self->fMiniCanvasUpdateTimeout = self->fSavedSettings._CARLA_KEY_CANVAS_FANCY_EYE_CANDY ? 1000 : 0;
  856. /*
  857. setEngineSettings(host);
  858. */
  859. restartTimersIfNeeded();
  860. }
  861. //---------------------------------------------------------------------------------------------------------------------
  862. // Settings (menu actions)
  863. void CarlaHostWindow::slot_showSidePanel(const bool yesNo)
  864. {
  865. self->ui.dockWidget->setEnabled(yesNo);
  866. self->ui.dockWidget->setVisible(yesNo);
  867. }
  868. void CarlaHostWindow::slot_showToolbar(const bool yesNo)
  869. {
  870. self->ui.toolBar->setEnabled(yesNo);
  871. self->ui.toolBar->setVisible(yesNo);
  872. }
  873. void CarlaHostWindow::slot_showCanvasMeters(const bool yesNo)
  874. {
  875. self->ui.peak_in->setVisible(yesNo);
  876. self->ui.peak_out->setVisible(yesNo);
  877. /*
  878. QTimer::singleShot(0, slot_miniCanvasCheckAll);
  879. */
  880. }
  881. void CarlaHostWindow::slot_showCanvasKeyboard(const bool yesNo)
  882. {
  883. /*
  884. self->ui.scrollArea->setVisible(yesNo);
  885. QTimer::singleShot(0, slot_miniCanvasCheckAll);
  886. */
  887. }
  888. void CarlaHostWindow::slot_configureCarla()
  889. {
  890. /*
  891. CarlaSettingsW dialog(self->fParentOrSelf, self->host, true, hasGL);
  892. if not dialog.exec_():
  893. return
  894. */
  895. loadSettings(false);
  896. /*
  897. patchcanvas.clear()
  898. setupCanvas();
  899. slot_miniCanvasCheckAll();
  900. */
  901. if (host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and host.isPlugin)
  902. pass();
  903. else if (carla_is_engine_running())
  904. carla_patchbay_refresh(self->fExternalPatchbay);
  905. }
  906. //---------------------------------------------------------------------------------------------------------------------
  907. // Transport
  908. void CarlaHostWindow::refreshTransport(const bool forced)
  909. {
  910. if (not self->ui.l_transport_time->isVisible())
  911. return;
  912. if (carla_isZero(self->fSampleRate) or ! carla_is_engine_running())
  913. return;
  914. const CarlaTransportInfo* const timeInfo = carla_get_transport_info();
  915. const bool playing = timeInfo->playing;
  916. const uint64_t frame = timeInfo->frame;
  917. const double bpm = timeInfo->bpm;
  918. if (playing != self->fLastTransportState || forced)
  919. {
  920. if (playing)
  921. {
  922. const QIcon icon(":/16x16/media-playback-pause.svgz");
  923. self->ui.b_transport_play->setChecked(true);
  924. self->ui.b_transport_play->setIcon(icon);
  925. // self->ui.b_transport_play->setText(tr("&Pause"));
  926. }
  927. else
  928. {
  929. const QIcon icon(":/16x16/media-playback-start.svgz");
  930. self->ui.b_transport_play->setChecked(false);
  931. self->ui.b_transport_play->setIcon(icon);
  932. // self->ui.b_play->setText(tr("&Play"));
  933. }
  934. self->fLastTransportState = playing;
  935. }
  936. if (frame != self->fLastTransportFrame || forced)
  937. {
  938. self->fLastTransportFrame = frame;
  939. const uint32_t time = frame / self->fSampleRate;
  940. const uint32_t secs = time % 60;
  941. const uint32_t mins = (time / 60) % 60;
  942. const uint32_t hrs = (time / 3600) % 60;
  943. self->ui.l_transport_time->setText(QString("%1:%2:%3").arg(hrs, 2, 10, QChar('0')).arg(mins, 2, 10, QChar('0')).arg(secs, 2, 10, QChar('0')));
  944. const uint32_t frame1 = frame % 1000;
  945. const uint32_t frame2 = (frame / 1000) % 1000;
  946. const uint32_t frame3 = (frame / 1000000) % 1000;
  947. self->ui.l_transport_frame->setText(QString("%1'%2'%3").arg(frame3, 3, 10, QChar('0')).arg(frame2, 3, 10, QChar('0')).arg(frame1, 3, 10, QChar('0')));
  948. const int32_t bar = timeInfo->bar;
  949. const int32_t beat = timeInfo->beat;
  950. const int32_t tick = timeInfo->tick;
  951. self->ui.l_transport_bbt->setText(QString("%1|%2|%3").arg(bar, 3, 10, QChar('0')).arg(beat, 2, 10, QChar('0')).arg(tick, 4, 10, QChar('0')));
  952. }
  953. if (carla_isNotEqual(bpm, self->fLastTransportBPM) || forced)
  954. {
  955. self->fLastTransportBPM = bpm;
  956. if (bpm > 0.0)
  957. {
  958. self->ui.dsb_transport_bpm->blockSignals(true);
  959. self->ui.dsb_transport_bpm->setValue(bpm);
  960. self->ui.dsb_transport_bpm->blockSignals(false);
  961. self->ui.dsb_transport_bpm->setStyleSheet("");
  962. }
  963. else
  964. {
  965. self->ui.dsb_transport_bpm->setStyleSheet("QDoubleSpinBox { color: palette(mid); }");
  966. }
  967. }
  968. }
  969. //---------------------------------------------------------------------------------------------------------------------
  970. // Transport (menu actions)
  971. void CarlaHostWindow::slot_transportPlayPause(const bool toggled)
  972. {
  973. if (host.isPlugin || ! carla_is_engine_running())
  974. return;
  975. if (toggled)
  976. carla_transport_play();
  977. else
  978. carla_transport_pause();
  979. refreshTransport();
  980. }
  981. void CarlaHostWindow::slot_transportStop()
  982. {
  983. if (host.isPlugin || ! carla_is_engine_running())
  984. return;
  985. carla_transport_pause();
  986. carla_transport_relocate(0);
  987. refreshTransport();
  988. }
  989. void CarlaHostWindow::slot_transportBackwards()
  990. {
  991. if (host.isPlugin || ! carla_is_engine_running())
  992. return;
  993. int64_t newFrame = carla_get_current_transport_frame() - 100000;
  994. if (newFrame < 0)
  995. newFrame = 0;
  996. carla_transport_relocate(newFrame);
  997. }
  998. void CarlaHostWindow::slot_transportBpmChanged(const qreal newValue)
  999. {
  1000. carla_transport_bpm(newValue);
  1001. }
  1002. void CarlaHostWindow::slot_transportForwards()
  1003. {
  1004. if (carla_isZero(self->fSampleRate) || host.isPlugin || ! carla_is_engine_running())
  1005. return;
  1006. const int64_t newFrame = carla_get_current_transport_frame() + int(self->fSampleRate*2.5);
  1007. carla_transport_relocate(newFrame);
  1008. }
  1009. void CarlaHostWindow::slot_transportJackEnabled(const bool clicked)
  1010. {
  1011. if (! carla_is_engine_running())
  1012. return;
  1013. host.transportMode = clicked ? ENGINE_TRANSPORT_MODE_JACK : ENGINE_TRANSPORT_MODE_INTERNAL;
  1014. carla_set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, host.transportExtra.toUtf8());
  1015. }
  1016. void CarlaHostWindow::slot_transportLinkEnabled(const bool clicked)
  1017. {
  1018. if (! carla_is_engine_running())
  1019. return;
  1020. const char* const extra = clicked ? ":link:" : "";
  1021. host.transportExtra = extra;
  1022. carla_set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, host.transportExtra.toUtf8());
  1023. }
  1024. //---------------------------------------------------------------------------------------------------------------------
  1025. // Other
  1026. void CarlaHostWindow::slot_xrunClear()
  1027. {
  1028. carla_clear_engine_xruns();
  1029. }
  1030. //---------------------------------------------------------------------------------------------------------------------
  1031. // Timers
  1032. void CarlaHostWindow::startTimers()
  1033. {
  1034. if (self->fIdleTimerFast == 0)
  1035. self->fIdleTimerFast = startTimer(self->fSavedSettings._CARLA_KEY_MAIN_REFRESH_INTERVAL);
  1036. if (self->fIdleTimerSlow == 0)
  1037. self->fIdleTimerSlow = startTimer(self->fSavedSettings._CARLA_KEY_MAIN_REFRESH_INTERVAL*4);
  1038. }
  1039. void CarlaHostWindow::restartTimersIfNeeded()
  1040. {
  1041. if (self->fIdleTimerFast != 0)
  1042. {
  1043. killTimer(self->fIdleTimerFast);
  1044. self->fIdleTimerFast = startTimer(self->fSavedSettings._CARLA_KEY_MAIN_REFRESH_INTERVAL);
  1045. }
  1046. if (self->fIdleTimerSlow != 0)
  1047. {
  1048. killTimer(self->fIdleTimerSlow);
  1049. self->fIdleTimerSlow = startTimer(self->fSavedSettings._CARLA_KEY_MAIN_REFRESH_INTERVAL*4);;
  1050. }
  1051. }
  1052. void CarlaHostWindow::killTimers()
  1053. {
  1054. if (self->fIdleTimerFast != 0)
  1055. {
  1056. killTimer(self->fIdleTimerFast);
  1057. self->fIdleTimerFast = 0;
  1058. }
  1059. if (self->fIdleTimerSlow != 0)
  1060. {
  1061. killTimer(self->fIdleTimerSlow);
  1062. self->fIdleTimerSlow = 0;
  1063. }
  1064. }
  1065. //---------------------------------------------------------------------------------------------------------------------
  1066. // timer event
  1067. void CarlaHostWindow::refreshRuntimeInfo(const float load, const int xruns)
  1068. {
  1069. const QString txt1(xruns >= 0 ? QString("%1").arg(xruns) : QString("--"));
  1070. const QString txt2(xruns == 1 ? "" : "s");
  1071. self->ui.b_xruns->setText(QString("%1 Xrun%2").arg(txt1).arg(txt2));
  1072. self->ui.pb_dsp_load->setValue(int(load));
  1073. }
  1074. void CarlaHostWindow::getAndRefreshRuntimeInfo()
  1075. {
  1076. if (! self->ui.pb_dsp_load->isVisible())
  1077. return;
  1078. if (! carla_is_engine_running())
  1079. return;
  1080. const CarlaRuntimeEngineInfo* const info = carla_get_runtime_engine_info();
  1081. refreshRuntimeInfo(info->load, info->xruns);
  1082. }
  1083. void CarlaHostWindow::idleFast()
  1084. {
  1085. carla_engine_idle();
  1086. refreshTransport();
  1087. if (self->fPluginCount == 0 || self->fCurrentlyRemovingAllPlugins)
  1088. return;
  1089. for (auto& pitem : self->fPluginList)
  1090. {
  1091. if (pitem == nullptr)
  1092. break;
  1093. /*
  1094. pitem->getWidget().idleFast();
  1095. */
  1096. }
  1097. for (uint pluginId : self->fSelectedPlugins)
  1098. {
  1099. self->fPeaksCleared = false;
  1100. if (self->ui.peak_in->isVisible())
  1101. {
  1102. /*
  1103. self->ui.peak_in->displayMeter(1, carla_get_input_peak_value(pluginId, true))
  1104. self->ui.peak_in->displayMeter(2, carla_get_input_peak_value(pluginId, false))
  1105. */
  1106. }
  1107. if (self->ui.peak_out->isVisible())
  1108. {
  1109. /*
  1110. self->ui.peak_out->displayMeter(1, carla_get_output_peak_value(pluginId, true))
  1111. self->ui.peak_out->displayMeter(2, carla_get_output_peak_value(pluginId, false))
  1112. */
  1113. }
  1114. return;
  1115. }
  1116. if (self->fPeaksCleared)
  1117. return;
  1118. self->fPeaksCleared = true;
  1119. /*
  1120. self->ui.peak_in->displayMeter(1, 0.0, true);
  1121. self->ui.peak_in->displayMeter(2, 0.0, true);
  1122. self->ui.peak_out->displayMeter(1, 0.0, true);
  1123. self->ui.peak_out->displayMeter(2, 0.0, true);
  1124. */
  1125. }
  1126. void CarlaHostWindow::idleSlow()
  1127. {
  1128. getAndRefreshRuntimeInfo();
  1129. if (self->fPluginCount == 0 || self->fCurrentlyRemovingAllPlugins)
  1130. return;
  1131. for (auto& pitem : self->fPluginList)
  1132. {
  1133. if (pitem == nullptr)
  1134. break;
  1135. /*
  1136. pitem->getWidget().idleSlow();
  1137. */
  1138. }
  1139. }
  1140. void CarlaHostWindow::timerEvent(QTimerEvent* const event)
  1141. {
  1142. if (event->timerId() == self->fIdleTimerFast)
  1143. idleFast();
  1144. else if (event->timerId() == self->fIdleTimerSlow)
  1145. idleSlow();
  1146. QMainWindow::timerEvent(event);
  1147. }
  1148. //---------------------------------------------------------------------------------------------------------------------
  1149. // color/style change event
  1150. void CarlaHostWindow::changeEvent(QEvent* const event)
  1151. {
  1152. switch (event->type())
  1153. {
  1154. case QEvent::PaletteChange:
  1155. case QEvent::StyleChange:
  1156. updateStyle();
  1157. break;
  1158. default:
  1159. break;
  1160. }
  1161. QWidget::changeEvent(event);
  1162. }
  1163. void CarlaHostWindow::updateStyle()
  1164. {
  1165. // Rack padding images setup
  1166. QImage rack_imgL(":/bitmaps/rack_padding_left.png");
  1167. QImage rack_imgR(":/bitmaps/rack_padding_right.png");
  1168. const qreal min_value = 0.07;
  1169. #if QT_VERSION >= 0x50600
  1170. const qreal value_fix = 1.0/(1.0-rack_imgL.scaled(1, 1, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).pixelColor(0,0).blackF());
  1171. #else
  1172. const qreal value_fix = 1.5;
  1173. #endif
  1174. const QColor bg_color = self->ui.rack->palette().window().color();
  1175. const qreal bg_value = 1.0 - bg_color.blackF();
  1176. QColor pad_color;
  1177. if (carla_isNotZero(bg_value) && bg_value < min_value)
  1178. pad_color = bg_color.lighter(100*min_value/bg_value*value_fix);
  1179. else
  1180. pad_color = QColor::fromHsvF(0.0, 0.0, min_value*value_fix);
  1181. QPainter painter;
  1182. QRect fillRect(rack_imgL.rect().adjusted(-1,-1,1,1));
  1183. painter.begin(&rack_imgL);
  1184. painter.setCompositionMode(QPainter::CompositionMode_Multiply);
  1185. painter.setBrush(pad_color);
  1186. painter.drawRect(fillRect);
  1187. painter.end();
  1188. const QPixmap rack_pixmapL(QPixmap::fromImage(rack_imgL));
  1189. QPalette imgL_palette; //(self->ui.pad_left->palette());
  1190. imgL_palette.setBrush(QPalette::Window, QBrush(rack_pixmapL));
  1191. self->ui.pad_left->setPalette(imgL_palette);
  1192. self->ui.pad_left->setAutoFillBackground(true);
  1193. painter.begin(&rack_imgR);
  1194. painter.setCompositionMode(QPainter::CompositionMode_Multiply);
  1195. painter.setBrush(pad_color);
  1196. painter.drawRect(fillRect);
  1197. painter.end();
  1198. const QPixmap rack_pixmapR(QPixmap::fromImage(rack_imgR));
  1199. QPalette imgR_palette; //(self->ui.pad_right->palette());
  1200. imgR_palette.setBrush(QPalette::Window, QBrush(rack_pixmapR));
  1201. self->ui.pad_right->setPalette(imgR_palette);
  1202. self->ui.pad_right->setAutoFillBackground(true);
  1203. }
  1204. //---------------------------------------------------------------------------------------------------------------------
  1205. // close event
  1206. bool CarlaHostWindow::shouldIgnoreClose()
  1207. {
  1208. if (host.isControl || host.isPlugin)
  1209. return false;
  1210. if (self->fCustomStopAction == CUSTOM_ACTION_APP_CLOSE)
  1211. return false;
  1212. if (self->fSavedSettings._CARLA_KEY_MAIN_CONFIRM_EXIT)
  1213. return QMessageBox::question(this, tr("Quit"),
  1214. tr("Are you sure you want to quit Carla?"),
  1215. QMessageBox::Yes|QMessageBox::No) == QMessageBox::No;
  1216. return false;
  1217. }
  1218. void CarlaHostWindow::closeEvent(QCloseEvent* const event)
  1219. {
  1220. if (shouldIgnoreClose())
  1221. {
  1222. event->ignore();
  1223. return;
  1224. }
  1225. #ifdef CARLA_OS_MAC
  1226. if (self->fMacClosingHelper && ! (host.isControl || host.isPlugin))
  1227. {
  1228. self->fCustomStopAction = CUSTOM_ACTION_APP_CLOSE;
  1229. self->fMacClosingHelper = false;
  1230. event->ignore();
  1231. for i in reversed(range(self->fPluginCount)):
  1232. carla_show_custom_ui(i, false);
  1233. QTimer::singleShot(100, SIGNAL(close()));
  1234. return;
  1235. }
  1236. #endif
  1237. killTimers();
  1238. saveSettings();
  1239. if (carla_is_engine_running() && ! (host.isControl or host.isPlugin))
  1240. {
  1241. if (! slot_engineStop(true))
  1242. {
  1243. self->fCustomStopAction = CUSTOM_ACTION_APP_CLOSE;
  1244. event->ignore();
  1245. return;
  1246. }
  1247. }
  1248. QMainWindow::closeEvent(event);
  1249. }
  1250. //---------------------------------------------------------------------------------------------------------------------
  1251. // Engine callback
  1252. void _engineCallback(void* const ptr, EngineCallbackOpcode action, uint pluginId, int value1, int value2, int value3, float valuef, const char* const valueStr)
  1253. {
  1254. /*
  1255. carla_stdout("_engineCallback(%p, %i:%s, %u, %i, %i, %i, %f, %s)",
  1256. ptr, action, EngineCallbackOpcode2Str(action), pluginId, value1, value2, value3, valuef, valueStr);
  1257. */
  1258. Host* const host = (Host*)(ptr);
  1259. CARLA_SAFE_ASSERT_RETURN(host != nullptr,);
  1260. switch (action)
  1261. {
  1262. case ENGINE_CALLBACK_ENGINE_STARTED:
  1263. host->processMode = static_cast<EngineProcessMode>(value1);
  1264. host->transportMode = static_cast<EngineTransportMode>(value2);
  1265. break;
  1266. case ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
  1267. host->processMode = static_cast<EngineProcessMode>(value1);
  1268. break;
  1269. case ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
  1270. host->transportMode = static_cast<EngineTransportMode>(value1);
  1271. host->transportExtra = valueStr;
  1272. break;
  1273. default:
  1274. break;
  1275. }
  1276. // TODO
  1277. switch (action)
  1278. {
  1279. case ENGINE_CALLBACK_ENGINE_STARTED:
  1280. emit host->EngineStartedCallback(pluginId, value1, value2, value3, valuef, valueStr);
  1281. break;
  1282. case ENGINE_CALLBACK_ENGINE_STOPPED:
  1283. emit host->EngineStoppedCallback();
  1284. break;
  1285. // FIXME
  1286. default:
  1287. break;
  1288. }
  1289. }
  1290. //---------------------------------------------------------------------------------------------------------------------
  1291. // File callback
  1292. static const char* _fileCallback(void*, const FileCallbackOpcode action, const bool isDir, const char* const title, const char* const filter)
  1293. {
  1294. QString ret;
  1295. const QFileDialog::Options options = QFileDialog::Options(isDir ? QFileDialog::ShowDirsOnly : 0x0);
  1296. switch (action)
  1297. {
  1298. case FILE_CALLBACK_DEBUG:
  1299. break;
  1300. case FILE_CALLBACK_OPEN:
  1301. ret = QFileDialog::getOpenFileName(gCarla.gui, title, "", filter, nullptr, options);
  1302. break;
  1303. case FILE_CALLBACK_SAVE:
  1304. ret = QFileDialog::getSaveFileName(gCarla.gui, title, "", filter, nullptr, options);
  1305. break;
  1306. }
  1307. if (ret.isEmpty())
  1308. return nullptr;
  1309. static const char* fileRet = nullptr;
  1310. if (fileRet != nullptr)
  1311. delete[] fileRet;
  1312. fileRet = carla_strdup_safe(ret.toUtf8());
  1313. return fileRet;
  1314. }
  1315. //---------------------------------------------------------------------------------------------------------------------
  1316. // Init host
  1317. Host& initHost(const QString /*initName*/, const bool isControl, const bool isPlugin, const bool failError)
  1318. {
  1319. CarlaString pathBinaries, pathResources;
  1320. // = getPaths(libPrefix)
  1321. //-----------------------------------------------------------------------------------------------------------------
  1322. // Print info
  1323. // ----------------------------------------------------------------------------------------------------------------
  1324. // Init host
  1325. if (failError)
  1326. {
  1327. // # no try
  1328. // host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
  1329. }
  1330. else
  1331. {
  1332. // try:
  1333. // host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
  1334. // except:
  1335. // host = CarlaHostQtNull()
  1336. }
  1337. static Host host;
  1338. host.isControl = isControl;
  1339. host.isPlugin = isPlugin;
  1340. carla_set_engine_callback(_engineCallback, &host);
  1341. carla_set_file_callback(_fileCallback, nullptr);
  1342. // If it's a plugin the paths are already set
  1343. if (! isPlugin)
  1344. {
  1345. host.pathBinaries = pathBinaries;
  1346. host.pathResources = pathResources;
  1347. carla_set_engine_option(ENGINE_OPTION_PATH_BINARIES, 0, pathBinaries);
  1348. carla_set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources);
  1349. /*
  1350. if (! isControl)
  1351. host.nsmOK = carla_nsm_init(getpid(), initName);
  1352. */
  1353. }
  1354. // ----------------------------------------------------------------------------------------------------------------
  1355. // Done
  1356. return host;
  1357. }
  1358. //---------------------------------------------------------------------------------------------------------------------
  1359. // Load host settings
  1360. void loadHostSettings(Host& /*host*/)
  1361. {
  1362. }
  1363. //---------------------------------------------------------------------------------------------------------------------