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.

2217 lines
85KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla host code
  4. # Copyright (C) 2011-2014 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as
  8. # published by the Free Software Foundation; either version 2 of
  9. # the License, or any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # For a full copy of the GNU General Public License see the doc/GPL.txt file.
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. if config_UseQt5:
  23. from PyQt5.QtCore import qCritical, QFileInfo, QModelIndex, QPointF, QTimer
  24. from PyQt5.QtGui import QImage, QPalette
  25. from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
  26. from PyQt5.QtWidgets import QAction, QApplication, QFileSystemModel, QListWidgetItem, QMainWindow
  27. else:
  28. from PyQt4.QtCore import qCritical, QFileInfo, QModelIndex, QPointF, QTimer
  29. from PyQt4.QtGui import QAction, QApplication, QFileSystemModel, QImage, QListWidgetItem, QMainWindow, QPalette, QPrinter, QPrintDialog
  30. # ------------------------------------------------------------------------------------------------------------
  31. # Imports (Custom)
  32. import patchcanvas
  33. import ui_carla_host
  34. from carla_app import *
  35. from carla_database import *
  36. from carla_settings import *
  37. from carla_widgets import *
  38. from digitalpeakmeter import DigitalPeakMeter
  39. from pixmapkeyboard import PixmapKeyboardHArea
  40. # ------------------------------------------------------------------------------------------------------------
  41. # Try Import OpenGL
  42. try:
  43. if config_UseQt5:
  44. from PyQt5.QtOpenGL import QGLWidget
  45. else:
  46. from PyQt4.QtOpenGL import QGLWidget
  47. hasGL = True
  48. except:
  49. hasGL = False
  50. # ------------------------------------------------------------------------------------------------------------
  51. # Session Management support
  52. LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
  53. NSM_URL = os.getenv("NSM_URL")
  54. # ------------------------------------------------------------------------------------------------------------
  55. # Host Window
  56. class HostWindow(QMainWindow, PluginEditParentMeta):
  57. #class HostWindow(QMainWindow, PluginEditParentMeta, metaclass=PyQtMetaClass):
  58. # signals
  59. SIGTERM = pyqtSignal()
  60. SIGUSR1 = pyqtSignal()
  61. # --------------------------------------------------------------------------------------------------------
  62. def __init__(self, host, withCanvas=True, parent=None):
  63. QMainWindow.__init__(self, parent)
  64. self.host = host
  65. self.ui = ui_carla_host.Ui_CarlaHostW()
  66. self.ui.setupUi(self)
  67. gCarla.gui = self
  68. if False:
  69. # kdevelop likes this :)
  70. host = CarlaHostMeta()
  71. self.host = host
  72. self._true = c_char_p("true".encode("utf-8"))
  73. # ----------------------------------------------------------------------------------------------------
  74. # Internal stuff
  75. self.fIdleTimerFast = 0
  76. self.fIdleTimerSlow = 0
  77. self.fLadspaRdfNeedsUpdate = True
  78. self.fLadspaRdfList = []
  79. self.fPluginCount = 0
  80. self.fPluginList = []
  81. self.fProjectFilename = ""
  82. self.fIsProjectLoading = False
  83. # first attempt of auto-start engine doesn't show an error
  84. self.fFirstEngineInit = True
  85. # to be filled with key-value pairs of current settings
  86. self.fSavedSettings = {}
  87. if host.isPlugin:
  88. self.fClientName = "Carla-Plugin"
  89. self.fSessionManagerName = "Plugin"
  90. elif LADISH_APP_NAME:
  91. self.fClientName = LADISH_APP_NAME
  92. self.fSessionManagerName = "LADISH"
  93. elif NSM_URL:
  94. self.fClientName = "Carla" # "Carla.tmp"
  95. self.fSessionManagerName = "Non Session Manager"
  96. else:
  97. self.fClientName = "Carla"
  98. self.fSessionManagerName = ""
  99. # ----------------------------------------------------------------------------------------------------
  100. # Internal stuff (rack)
  101. self.fCurrentRow = -1
  102. self.fLastSelectedItem = None
  103. # ----------------------------------------------------------------------------------------------------
  104. # Internal stuff (patchbay)
  105. self.fExportImage = QImage()
  106. self.fExportPrinter = QPrinter()
  107. self.fPeaksCleared = True
  108. self.fExternalPatchbay = False
  109. self.fSelectedPlugins = []
  110. self.fCanvasWidth = 0
  111. self.fCanvasHeight = 0
  112. # ----------------------------------------------------------------------------------------------------
  113. # Internal stuff (transport, TODO remove)
  114. self.fSampleRate = 0.0
  115. self.fLastTransportFrame = 0
  116. self.fLastTransportState = False
  117. self.fTransportText = ""
  118. # ----------------------------------------------------------------------------------------------------
  119. # Set up GUI (engine stopped)
  120. if self.host.isPlugin:
  121. self.ui.act_engine_start.setEnabled(False)
  122. self.ui.menu_Engine.setEnabled(False)
  123. else:
  124. self.ui.act_engine_start.setEnabled(True)
  125. if self.fSessionManagerName:
  126. self.ui.act_file_new.setEnabled(False)
  127. self.ui.act_file_open.setEnabled(False)
  128. self.ui.act_file_save.setEnabled(False)
  129. self.ui.act_file_save_as.setEnabled(False)
  130. self.ui.act_engine_stop.setEnabled(False)
  131. self.ui.act_plugin_remove_all.setEnabled(False)
  132. self.ui.act_canvas_show_internal.setChecked(True)
  133. self.ui.act_canvas_show_external.setChecked(False)
  134. self.ui.menu_PluginMacros.setEnabled(False)
  135. self.ui.menu_Canvas.setEnabled(False)
  136. self.ui.dockWidgetTitleBar = QWidget(self)
  137. self.ui.dockWidget.setTitleBarWidget(self.ui.dockWidgetTitleBar)
  138. self.setTransportMenuEnabled(False)
  139. if not withCanvas:
  140. self.ui.act_canvas_show_internal.setEnabled(False)
  141. self.ui.act_canvas_show_external.setEnabled(False)
  142. self.ui.act_canvas_arrange.setVisible(False)
  143. self.ui.act_canvas_print.setVisible(False)
  144. self.ui.act_canvas_refresh.setVisible(False)
  145. self.ui.act_canvas_save_image.setVisible(False)
  146. self.ui.act_canvas_zoom_100.setVisible(False)
  147. self.ui.act_canvas_zoom_fit.setVisible(False)
  148. self.ui.act_canvas_zoom_in.setVisible(False)
  149. self.ui.act_canvas_zoom_out.setVisible(False)
  150. self.ui.act_settings_show_meters.setVisible(False)
  151. self.ui.act_settings_show_keyboard.setVisible(False)
  152. self.ui.menu_Canvas.setEnabled(False)
  153. self.ui.menu_Canvas.setVisible(False)
  154. self.ui.menu_Canvas_Zoom.setEnabled(False)
  155. self.ui.menu_Canvas_Zoom.setVisible(False)
  156. self.ui.miniCanvasPreview.hide()
  157. # ----------------------------------------------------------------------------------------------------
  158. # Set up GUI (disk)
  159. self.fDirModel = QFileSystemModel(self)
  160. self.fDirModel.setRootPath(HOME)
  161. self.fDirModel.setNameFilters(host.get_supported_file_extensions().split(";"))
  162. self.ui.fileTreeView.setModel(self.fDirModel)
  163. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
  164. self.ui.fileTreeView.setColumnHidden(1, True)
  165. self.ui.fileTreeView.setColumnHidden(2, True)
  166. self.ui.fileTreeView.setColumnHidden(3, True)
  167. self.ui.fileTreeView.setHeaderHidden(True)
  168. # ----------------------------------------------------------------------------------------------------
  169. # Set up GUI (rack)
  170. sb = self.ui.listWidget.verticalScrollBar()
  171. self.ui.rackScrollBar.setMinimum(sb.minimum())
  172. self.ui.rackScrollBar.setMaximum(sb.maximum())
  173. self.ui.rackScrollBar.setValue(sb.value())
  174. sb.rangeChanged.connect(self.ui.rackScrollBar.setRange)
  175. sb.valueChanged.connect(self.ui.rackScrollBar.setValue)
  176. self.ui.rackScrollBar.rangeChanged.connect(sb.setRange)
  177. self.ui.rackScrollBar.valueChanged.connect(sb.setValue)
  178. self.ui.listWidget.currentRowChanged.connect(self.slot_currentRowChanged)
  179. self.ui.rack.setStyleSheet("""
  180. QLabel#pad_left {
  181. background-image: url(:/bitmaps/rack_padding_left.png);
  182. background-repeat: repeat-y;
  183. }
  184. QLabel#pad_right {
  185. background-image: url(:/bitmaps/rack_padding_right.png);
  186. background-repeat: repeat-y;
  187. }
  188. CarlaRackList#CarlaRackList {
  189. background-color: black;
  190. }
  191. """)
  192. # ----------------------------------------------------------------------------------------------------
  193. # Set up GUI (patchbay)
  194. self.ui.peak_in.setColor(DigitalPeakMeter.BLUE)
  195. self.ui.peak_in.setChannels(2)
  196. self.ui.peak_in.setOrientation(DigitalPeakMeter.VERTICAL)
  197. self.ui.peak_in.setFixedWidth(25)
  198. self.ui.peak_out.setColor(DigitalPeakMeter.GREEN)
  199. self.ui.peak_out.setChannels(2)
  200. self.ui.peak_out.setOrientation(DigitalPeakMeter.VERTICAL)
  201. self.ui.peak_out.setFixedWidth(25)
  202. self.ui.scrollArea = PixmapKeyboardHArea(self.ui.patchbay)
  203. self.ui.keyboard = self.ui.scrollArea.keyboard
  204. self.ui.patchbay.layout().addWidget(self.ui.scrollArea, 1, 0, 1, 0)
  205. self.ui.scrollArea.setEnabled(False)
  206. self.ui.miniCanvasPreview.setRealParent(self)
  207. # ----------------------------------------------------------------------------------------------------
  208. # Set up GUI (special stuff for Mac OS)
  209. if MACOS and config_UseQt5:
  210. self.ui.act_file_quit.setMenuRole(QAction.QuitRole)
  211. self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole)
  212. self.ui.act_help_about.setMenuRole(QAction.AboutRole)
  213. self.ui.act_help_about_juce.setMenuRole(QAction.AboutQtRole)
  214. self.ui.act_help_about_qt.setMenuRole(QAction.AboutQtRole)
  215. self.ui.menu_Settings.setTitle("Panels")
  216. #self.ui.menu_Help.hide()
  217. # ----------------------------------------------------------------------------------------------------
  218. # Load Settings
  219. self.loadSettings(True)
  220. # ----------------------------------------------------------------------------------------------------
  221. # Set-up Canvas
  222. self.scene = patchcanvas.PatchScene(self, self.ui.graphicsView)
  223. self.ui.graphicsView.setScene(self.scene)
  224. self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, bool(self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL))
  225. if self.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL:
  226. self.ui.glView = QGLWidget(self)
  227. self.ui.graphicsView.setViewport(self.ui.glView)
  228. self.ui.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING])
  229. self.setupCanvas()
  230. # ----------------------------------------------------------------------------------------------------
  231. # Connect actions to functions
  232. self.ui.act_file_new.triggered.connect(self.slot_fileNew)
  233. self.ui.act_file_open.triggered.connect(self.slot_fileOpen)
  234. self.ui.act_file_save.triggered.connect(self.slot_fileSave)
  235. self.ui.act_file_save_as.triggered.connect(self.slot_fileSaveAs)
  236. self.ui.act_engine_start.triggered.connect(self.slot_engineStart)
  237. self.ui.act_engine_stop.triggered.connect(self.slot_engineStop)
  238. self.ui.act_plugin_add.triggered.connect(self.slot_pluginAdd)
  239. self.ui.act_plugin_add2.triggered.connect(self.slot_pluginAdd)
  240. self.ui.act_plugin_remove_all.triggered.connect(self.slot_pluginRemoveAll)
  241. self.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  242. self.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  243. self.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  244. self.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  245. self.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  246. self.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  247. self.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  248. self.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  249. self.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal)
  250. self.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal)
  251. self.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange)
  252. self.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh)
  253. self.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit)
  254. self.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn)
  255. self.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut)
  256. self.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset)
  257. self.ui.act_canvas_print.triggered.connect(self.slot_canvasPrint)
  258. self.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage)
  259. self.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  260. self.ui.act_transport_play.triggered.connect(self.slot_transportPlayPause)
  261. self.ui.act_transport_stop.triggered.connect(self.slot_transportStop)
  262. self.ui.act_transport_backwards.triggered.connect(self.slot_transportBackwards)
  263. self.ui.act_transport_forwards.triggered.connect(self.slot_transportForwards)
  264. self.ui.act_settings_show_meters.toggled.connect(self.slot_showCanvasMeters)
  265. self.ui.act_settings_show_keyboard.toggled.connect(self.slot_showCanvasKeyboard)
  266. self.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  267. self.ui.act_help_about.triggered.connect(self.slot_aboutCarla)
  268. self.ui.act_help_about_juce.triggered.connect(self.slot_aboutJuce)
  269. self.ui.act_help_about_qt.triggered.connect(self.slot_aboutQt)
  270. self.ui.cb_disk.currentIndexChanged.connect(self.slot_diskFolderChanged)
  271. self.ui.b_disk_add.clicked.connect(self.slot_diskFolderAdd)
  272. self.ui.b_disk_remove.clicked.connect(self.slot_diskFolderRemove)
  273. self.ui.fileTreeView.doubleClicked.connect(self.slot_fileTreeDoubleClicked)
  274. self.ui.graphicsView.horizontalScrollBar().valueChanged.connect(self.slot_horizontalScrollBarChanged)
  275. self.ui.graphicsView.verticalScrollBar().valueChanged.connect(self.slot_verticalScrollBarChanged)
  276. self.ui.keyboard.noteOn.connect(self.slot_noteOn)
  277. self.ui.keyboard.noteOff.connect(self.slot_noteOff)
  278. self.ui.miniCanvasPreview.miniCanvasMoved.connect(self.slot_miniCanvasMoved)
  279. self.scene.scaleChanged.connect(self.slot_canvasScaleChanged)
  280. self.scene.sceneGroupMoved.connect(self.slot_canvasItemMoved)
  281. self.scene.pluginSelected.connect(self.slot_canvasPluginSelected)
  282. self.SIGUSR1.connect(self.slot_handleSIGUSR1)
  283. self.SIGTERM.connect(self.slot_handleSIGTERM)
  284. host.EngineStartedCallback.connect(self.slot_handleEngineStartedCallback)
  285. host.EngineStoppedCallback.connect(self.slot_handleEngineStoppedCallback)
  286. host.SampleRateChangedCallback.connect(self.slot_handleSampleRateChangedCallback)
  287. host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback)
  288. host.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback)
  289. host.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  290. host.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  291. host.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  292. host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback)
  293. host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback)
  294. host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback)
  295. host.PatchbayClientDataChangedCallback.connect(self.slot_handlePatchbayClientDataChangedCallback)
  296. host.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback)
  297. host.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback)
  298. host.PatchbayPortRenamedCallback.connect(self.slot_handlePatchbayPortRenamedCallback)
  299. host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
  300. host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)
  301. host.DebugCallback.connect(self.slot_handleDebugCallback)
  302. host.InfoCallback.connect(self.slot_handleInfoCallback)
  303. host.ErrorCallback.connect(self.slot_handleErrorCallback)
  304. host.QuitCallback.connect(self.slot_handleQuitCallback)
  305. # ----------------------------------------------------------------------------------------------------
  306. # Final setup
  307. self.setProperWindowTitle()
  308. # Qt4 needs this so it properly creates & resizes the canvas
  309. self.ui.tabWidget.setCurrentIndex(1)
  310. self.ui.tabWidget.setCurrentIndex(0)
  311. self.fixCanvasPreviewSize()
  312. QTimer.singleShot(0, self.slot_engineStart)
  313. # --------------------------------------------------------------------------------------------------------
  314. # Setup
  315. def refreshTransport(self, forced = False):
  316. if self.fSampleRate == 0.0 or not self.host.is_engine_running():
  317. return
  318. timeInfo = self.host.get_transport_info()
  319. playing = timeInfo['playing']
  320. frame = timeInfo['frame']
  321. if playing != self.fLastTransportState or forced:
  322. if playing:
  323. icon = getIcon("media-playback-pause")
  324. self.ui.act_transport_play.setChecked(True)
  325. self.ui.act_transport_play.setIcon(icon)
  326. self.ui.act_transport_play.setText(self.tr("&Pause"))
  327. else:
  328. icon = getIcon("media-playback-start")
  329. self.ui.act_transport_play.setChecked(False)
  330. self.ui.act_transport_play.setIcon(icon)
  331. self.ui.act_transport_play.setText(self.tr("&Play"))
  332. self.fLastTransportState = playing
  333. if frame != self.fLastTransportFrame or forced:
  334. time = frame / self.fSampleRate
  335. secs = time % 60
  336. mins = (time / 60) % 60
  337. hrs = (time / 3600) % 60
  338. self.fTransportText = "Transport %s, at %02i:%02i:%02i" % ("playing" if playing else "stopped", hrs, mins, secs)
  339. self.fLastTransportFrame = frame
  340. def setLoadRDFsNeeded(self):
  341. self.fLadspaRdfNeedsUpdate = True
  342. def setProperWindowTitle(self):
  343. title = self.fClientName
  344. if self.fProjectFilename:
  345. title += " - %s" % os.path.basename(self.fProjectFilename)
  346. if self.fSessionManagerName:
  347. title += " (%s)" % self.fSessionManagerName
  348. self.setWindowTitle(title)
  349. # --------------------------------------------------------------------------------------------------------
  350. # Files
  351. def loadProjectNow(self):
  352. if not self.fProjectFilename:
  353. return qCritical("ERROR: loading project without filename set")
  354. self.projectLoadingStarted()
  355. self.fIsProjectLoading = True
  356. self.host.load_project(self.fProjectFilename)
  357. self.fIsProjectLoading = False
  358. self.projectLoadingFinished()
  359. def loadProjectLater(self, filename):
  360. self.fProjectFilename = QFileInfo(filename).absoluteFilePath()
  361. self.setProperWindowTitle()
  362. QTimer.singleShot(0, self.slot_loadProjectNow)
  363. def saveProjectNow(self):
  364. if not self.fProjectFilename:
  365. return qCritical("ERROR: saving project without filename set")
  366. self.host.save_project(self.fProjectFilename)
  367. def projectLoadingStarted(self):
  368. self.ui.rack.setEnabled(False)
  369. self.ui.graphicsView.setEnabled(False)
  370. def projectLoadingFinished(self):
  371. self.ui.rack.setEnabled(True)
  372. self.ui.graphicsView.setEnabled(True)
  373. QTimer.singleShot(1000, self.slot_canvasRefresh)
  374. # --------------------------------------------------------------------------------------------------------
  375. # Files (menu actions)
  376. @pyqtSlot()
  377. def slot_fileNew(self):
  378. self.removeAllPlugins()
  379. self.fProjectFilename = ""
  380. self.setProperWindowTitle()
  381. @pyqtSlot()
  382. def slot_fileOpen(self):
  383. fileFilter = self.tr("Carla Project File (*.carxp)")
  384. filename = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
  385. if config_UseQt5:
  386. filename = filename[0]
  387. if not filename:
  388. return
  389. newFile = True
  390. if self.fPluginCount > 0:
  391. ask = QMessageBox.question(self, self.tr("Question"), self.tr("There are some plugins loaded, do you want to remove them now?"),
  392. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  393. newFile = (ask == QMessageBox.Yes)
  394. if newFile:
  395. self.removeAllPlugins()
  396. self.fProjectFilename = filename
  397. self.setProperWindowTitle()
  398. self.loadProjectNow()
  399. else:
  400. filenameOld = self.fProjectFilename
  401. self.fProjectFilename = filename
  402. self.loadProjectNow()
  403. self.fProjectFilename = filenameOld
  404. @pyqtSlot()
  405. def slot_fileSave(self, saveAs=False):
  406. if self.fProjectFilename and not saveAs:
  407. return self.saveProjectNow()
  408. fileFilter = self.tr("Carla Project File (*.carxp)")
  409. filename = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
  410. if config_UseQt5:
  411. filename = filename[0]
  412. if not filename:
  413. return
  414. if not filename.lower().endswith(".carxp"):
  415. filename += ".carxp"
  416. if self.fProjectFilename != filename:
  417. self.fProjectFilename = filename
  418. self.setProperWindowTitle()
  419. self.saveProjectNow()
  420. @pyqtSlot()
  421. def slot_fileSaveAs(self):
  422. self.slot_fileSave(True)
  423. @pyqtSlot()
  424. def slot_loadProjectNow(self):
  425. self.loadProjectNow()
  426. # --------------------------------------------------------------------------------------------------------
  427. # Engine (menu actions)
  428. @pyqtSlot()
  429. def slot_engineStart(self):
  430. audioDriver = self.setEngineSettings()
  431. firstInit = self.fFirstEngineInit
  432. self.fFirstEngineInit = False
  433. if self.host.engine_init(audioDriver, self.fClientName) or firstInit:
  434. return
  435. audioError = self.host.get_last_error()
  436. if audioError:
  437. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
  438. else:
  439. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  440. @pyqtSlot()
  441. def slot_engineStop(self):
  442. if self.fPluginCount > 0:
  443. ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n"
  444. "Do you want to do this now?"),
  445. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  446. if ask != QMessageBox.Yes:
  447. return
  448. self.removeAllPlugins()
  449. if self.host.is_engine_running() and not self.host.engine_close():
  450. print(self.host.get_last_error())
  451. # --------------------------------------------------------------------------------------------------------
  452. # Engine (host callbacks)
  453. @pyqtSlot(str)
  454. def slot_handleEngineStartedCallback(self, processMode, transportMode, driverName):
  455. self.fSampleRate = self.host.get_sample_rate()
  456. self.ui.menu_PluginMacros.setEnabled(True)
  457. self.ui.menu_Canvas.setEnabled(True)
  458. if not self.host.isPlugin:
  459. self.ui.act_engine_start.setEnabled(False)
  460. self.ui.act_engine_stop.setEnabled(True)
  461. if not self.fSessionManagerName:
  462. self.ui.act_file_open.setEnabled(True)
  463. self.ui.act_file_save.setEnabled(True)
  464. self.ui.act_file_save_as.setEnabled(True)
  465. self.setTransportMenuEnabled(True)
  466. self.fLastTransportFrame = 0
  467. self.fLastTransportState = False
  468. self.refreshTransport(True)
  469. self.startTimers()
  470. @pyqtSlot()
  471. def slot_handleEngineStoppedCallback(self):
  472. patchcanvas.clear()
  473. self.killTimers()
  474. # just in case
  475. self.removeAllPlugins()
  476. self.ui.menu_PluginMacros.setEnabled(False)
  477. self.ui.menu_Canvas.setEnabled(False)
  478. if not self.host.isPlugin:
  479. self.ui.act_engine_start.setEnabled(True)
  480. self.ui.act_engine_stop.setEnabled(False)
  481. if not self.fSessionManagerName:
  482. self.ui.act_file_open.setEnabled(False)
  483. self.ui.act_file_save.setEnabled(False)
  484. self.ui.act_file_save_as.setEnabled(False)
  485. self.setTransportMenuEnabled(False)
  486. self.fSampleRate = 0.0
  487. self.fTransportText = ""
  488. # --------------------------------------------------------------------------------------------------------
  489. # Plugins
  490. def removeAllPlugins(self):
  491. self.ui.act_plugin_remove_all.setEnabled(False)
  492. patchcanvas.handleAllPluginsRemoved()
  493. while self.ui.listWidget.takeItem(0):
  494. pass
  495. self.clearSideStuff()
  496. for pitem in self.fPluginList:
  497. if pitem is None:
  498. continue
  499. pitem.closeEditDialog()
  500. del pitem
  501. self.fPluginCount = 0
  502. self.fPluginList = []
  503. # --------------------------------------------------------------------------------------------------------
  504. # Plugins (menu actions)
  505. @pyqtSlot()
  506. def slot_pluginAdd(self, pluginToReplace = -1):
  507. dialog = PluginDatabaseW(self, self.host)
  508. if not dialog.exec_():
  509. return
  510. if not self.host.is_engine_running():
  511. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  512. return
  513. btype = dialog.fRetPlugin['build']
  514. ptype = dialog.fRetPlugin['type']
  515. filename = dialog.fRetPlugin['filename']
  516. label = dialog.fRetPlugin['label']
  517. uniqueId = dialog.fRetPlugin['uniqueId']
  518. extraPtr = self.getExtraPtr(dialog.fRetPlugin)
  519. if pluginToReplace >= 0:
  520. if not self.host.replace_plugin(pluginToReplace):
  521. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to replace plugin"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  522. return
  523. ok = self.host.add_plugin(btype, ptype, filename, None, label, uniqueId, extraPtr)
  524. if pluginToReplace >= 0:
  525. self.host.replace_plugin(self.host.get_max_plugin_number())
  526. if not ok:
  527. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  528. @pyqtSlot()
  529. def slot_pluginRemoveAll(self):
  530. if self.fPluginCount == 0:
  531. return
  532. if True:
  533. # animated
  534. self.projectLoadingStarted()
  535. app = QApplication.instance()
  536. app.processEvents()
  537. for i in range(self.fPluginCount):
  538. self.host.remove_plugin(self.fPluginCount-i-1)
  539. app.processEvents()
  540. self.projectLoadingFinished()
  541. # just in case
  542. self.removeAllPlugins()
  543. self.host.remove_all_plugins()
  544. # --------------------------------------------------------------------------------------------------------
  545. # Plugins (macros)
  546. @pyqtSlot()
  547. def slot_pluginsEnable(self):
  548. if not self.host.is_engine_running():
  549. return
  550. for pitem in self.fPluginList:
  551. if pitem is None:
  552. break
  553. pitem.getWidget().setActive(True, True, True)
  554. @pyqtSlot()
  555. def slot_pluginsDisable(self):
  556. if not self.host.is_engine_running():
  557. return
  558. for pitem in self.fPluginList:
  559. if pitem is None:
  560. break
  561. pitem.getWidget().setActive(False, True, True)
  562. @pyqtSlot()
  563. def slot_pluginsVolume100(self):
  564. if not self.host.is_engine_running():
  565. return
  566. for pitem in self.fPluginList:
  567. if pitem is None:
  568. break
  569. pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 1.0)
  570. @pyqtSlot()
  571. def slot_pluginsMute(self):
  572. if not self.host.is_engine_running():
  573. return
  574. for pitem in self.fPluginList:
  575. if pitem is None:
  576. break
  577. pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 0.0)
  578. @pyqtSlot()
  579. def slot_pluginsWet100(self):
  580. if not self.host.is_engine_running():
  581. return
  582. for pitem in self.fPluginList:
  583. if pitem is None:
  584. break
  585. pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 1.0)
  586. @pyqtSlot()
  587. def slot_pluginsBypass(self):
  588. if not self.host.is_engine_running():
  589. return
  590. for pitem in self.fPluginList:
  591. if pitem is None:
  592. break
  593. pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 0.0)
  594. @pyqtSlot()
  595. def slot_pluginsCenter(self):
  596. if not self.host.is_engine_running():
  597. return
  598. for pitem in self.fPluginList:
  599. if pitem is None:
  600. break
  601. pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_LEFT, -1.0)
  602. pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_RIGHT, 1.0)
  603. pitem.getWidget().setInternalParameter(PARAMETER_PANNING, 0.0)
  604. # --------------------------------------------------------------------------------------------------------
  605. # Plugins (host callbacks)
  606. @pyqtSlot(int, str)
  607. def slot_handlePluginAddedCallback(self, pluginId, pluginName):
  608. #pitem = CarlaRackItem(self.fRack, pluginId, self.getSavedSettings()[CARLA_KEY_MAIN_USE_CUSTOM_SKINS])
  609. #self.fPluginList.append(pitem)
  610. self.fPluginCount += 1
  611. #if not self.isProjectLoading():
  612. #pitem.getWidget().setActive(True, True, True)
  613. self.ui.act_plugin_remove_all.setEnabled(self.fPluginCount > 0)
  614. @pyqtSlot(int)
  615. def slot_handlePluginRemovedCallback(self, pluginId):
  616. patchcanvas.handlePluginRemoved(pluginId)
  617. if pluginId in self.fSelectedPlugins:
  618. self.clearSideStuff()
  619. #pitem = self.getPluginItem(pluginId)
  620. self.fPluginCount -= 1
  621. #self.fPluginList.pop(pluginId)
  622. #self.fRack.takeItem(pluginId)
  623. #if pitem is not None:
  624. #pitem.closeEditDialog()
  625. #del pitem
  626. ## push all plugins 1 slot back
  627. #for i in range(pluginId, self.fPluginCount):
  628. #pitem = self.fPluginList[i]
  629. #pitem.setPluginId(i)
  630. self.ui.act_plugin_remove_all.setEnabled(self.fPluginCount > 0)
  631. # --------------------------------------------------------------------------------------------------------
  632. # Canvas
  633. def clearSideStuff(self):
  634. self.scene.clearSelection()
  635. self.fSelectedPlugins = []
  636. self.ui.keyboard.allNotesOff(False)
  637. self.ui.scrollArea.setEnabled(False)
  638. self.fPeaksCleared = True
  639. self.ui.peak_in.displayMeter(1, 0.0, True)
  640. self.ui.peak_in.displayMeter(2, 0.0, True)
  641. self.ui.peak_out.displayMeter(1, 0.0, True)
  642. self.ui.peak_out.displayMeter(2, 0.0, True)
  643. def setupCanvas(self):
  644. pOptions = patchcanvas.options_t()
  645. pOptions.theme_name = self.fSavedSettings[CARLA_KEY_CANVAS_THEME]
  646. pOptions.auto_hide_groups = self.fSavedSettings[CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS]
  647. pOptions.use_bezier_lines = self.fSavedSettings[CARLA_KEY_CANVAS_USE_BEZIER_LINES]
  648. pOptions.antialiasing = self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING]
  649. pOptions.eyecandy = self.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY]
  650. pFeatures = patchcanvas.features_t()
  651. pFeatures.group_info = False
  652. pFeatures.group_rename = False
  653. pFeatures.port_info = False
  654. pFeatures.port_rename = False
  655. pFeatures.handle_group_pos = True
  656. patchcanvas.setOptions(pOptions)
  657. patchcanvas.setFeatures(pFeatures)
  658. patchcanvas.init("Carla2", self.scene, canvasCallback, False)
  659. tryCanvasSize = self.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x")
  660. if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit() and tryCanvasSize[1].isdigit():
  661. self.fCanvasWidth = int(tryCanvasSize[0])
  662. self.fCanvasHeight = int(tryCanvasSize[1])
  663. else:
  664. self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH
  665. self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT
  666. patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight)
  667. patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2)
  668. self.ui.graphicsView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight)
  669. self.ui.miniCanvasPreview.setViewTheme(patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color())
  670. self.ui.miniCanvasPreview.init(self.scene, self.fCanvasWidth, self.fCanvasHeight, self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING])
  671. def updateCanvasInitialPos(self):
  672. x = self.ui.graphicsView.horizontalScrollBar().value() + self.width()/4
  673. y = self.ui.graphicsView.verticalScrollBar().value() + self.height()/4
  674. patchcanvas.setInitialPos(x, y)
  675. # --------------------------------------------------------------------------------------------------------
  676. # Canvas (menu actions)
  677. @pyqtSlot()
  678. def slot_canvasShowInternal(self):
  679. self.fExternalPatchbay = False
  680. self.ui.act_canvas_show_internal.blockSignals(True)
  681. self.ui.act_canvas_show_external.blockSignals(True)
  682. self.ui.act_canvas_show_internal.setChecked(True)
  683. self.ui.act_canvas_show_external.setChecked(False)
  684. self.ui.act_canvas_show_internal.blockSignals(False)
  685. self.ui.act_canvas_show_external.blockSignals(False)
  686. self.slot_canvasRefresh()
  687. @pyqtSlot()
  688. def slot_canvasShowExternal(self):
  689. self.fExternalPatchbay = True
  690. self.ui.act_canvas_show_internal.blockSignals(True)
  691. self.ui.act_canvas_show_external.blockSignals(True)
  692. self.ui.act_canvas_show_internal.setChecked(False)
  693. self.ui.act_canvas_show_external.setChecked(True)
  694. self.ui.act_canvas_show_internal.blockSignals(False)
  695. self.ui.act_canvas_show_external.blockSignals(False)
  696. self.slot_canvasRefresh()
  697. @pyqtSlot()
  698. def slot_canvasArrange(self):
  699. patchcanvas.arrange()
  700. @pyqtSlot()
  701. def slot_canvasRefresh(self):
  702. patchcanvas.clear()
  703. if self.host.is_engine_running():
  704. self.host.patchbay_refresh(self.fExternalPatchbay)
  705. for pedit in self.fPluginList:
  706. if pedit is None:
  707. break
  708. pedit.reloadAll()
  709. QTimer.singleShot(1000 if self.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY] else 0, self.ui.miniCanvasPreview.update)
  710. @pyqtSlot()
  711. def slot_canvasZoomFit(self):
  712. self.scene.zoom_fit()
  713. @pyqtSlot()
  714. def slot_canvasZoomIn(self):
  715. self.scene.zoom_in()
  716. @pyqtSlot()
  717. def slot_canvasZoomOut(self):
  718. self.scene.zoom_out()
  719. @pyqtSlot()
  720. def slot_canvasZoomReset(self):
  721. self.scene.zoom_reset()
  722. @pyqtSlot()
  723. def slot_canvasPrint(self):
  724. self.scene.clearSelection()
  725. dialog = QPrintDialog(self.fExportPrinter, self)
  726. if dialog.exec_():
  727. painter = QPainter(self.fExportPrinter)
  728. painter.save()
  729. painter.setRenderHint(QPainter.Antialiasing)
  730. painter.setRenderHint(QPainter.TextAntialiasing)
  731. self.scene.render(painter)
  732. painter.restore()
  733. @pyqtSlot()
  734. def slot_canvasSaveImage(self):
  735. newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)"))
  736. if config_UseQt5:
  737. newPath = newPath[0]
  738. if not newPath:
  739. return
  740. self.scene.clearSelection()
  741. if newPath.lower().endswith((".jpg", ".jpeg")):
  742. imgFormat = "JPG"
  743. elif newPath.lower().endswith((".png",)):
  744. imgFormat = "PNG"
  745. else:
  746. # File-dialog may not auto-add the extension
  747. imgFormat = "PNG"
  748. newPath += ".png"
  749. self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32)
  750. painter = QPainter(self.fExportImage)
  751. painter.save()
  752. painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this
  753. painter.setRenderHint(QPainter.TextAntialiasing)
  754. self.scene.render(painter)
  755. self.fExportImage.save(newPath, imgFormat, 100)
  756. painter.restore()
  757. # --------------------------------------------------------------------------------------------------------
  758. # Canvas (canvas callbacks)
  759. @pyqtSlot(int, int, QPointF)
  760. def slot_canvasItemMoved(self, group_id, split_mode, pos):
  761. self.ui.miniCanvasPreview.update()
  762. @pyqtSlot(float)
  763. def slot_canvasScaleChanged(self, scale):
  764. self.ui.miniCanvasPreview.setViewScale(scale)
  765. @pyqtSlot(list)
  766. def slot_canvasPluginSelected(self, pluginList):
  767. self.ui.keyboard.allNotesOff(False)
  768. self.ui.scrollArea.setEnabled(len(pluginList) != 0) # and self.fPluginCount > 0
  769. self.fSelectedPlugins = pluginList
  770. # --------------------------------------------------------------------------------------------------------
  771. # Canvas (host callbacks)
  772. @pyqtSlot(int, int, int, str)
  773. def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName):
  774. pcSplit = patchcanvas.SPLIT_UNDEF
  775. pcIcon = patchcanvas.ICON_APPLICATION
  776. if clientIcon == PATCHBAY_ICON_PLUGIN:
  777. pcIcon = patchcanvas.ICON_PLUGIN
  778. if clientIcon == PATCHBAY_ICON_HARDWARE:
  779. pcIcon = patchcanvas.ICON_HARDWARE
  780. elif clientIcon == PATCHBAY_ICON_CARLA:
  781. pass
  782. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  783. pcIcon = patchcanvas.ICON_DISTRHO
  784. elif clientIcon == PATCHBAY_ICON_FILE:
  785. pcIcon = patchcanvas.ICON_FILE
  786. patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
  787. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  788. if pluginId < 0:
  789. return
  790. if pluginId >= self.fPluginCount:
  791. print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
  792. return
  793. patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI))
  794. @pyqtSlot(int)
  795. def slot_handlePatchbayClientRemovedCallback(self, clientId):
  796. patchcanvas.removeGroup(clientId)
  797. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  798. @pyqtSlot(int, str)
  799. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  800. patchcanvas.renameGroup(clientId, newClientName)
  801. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  802. @pyqtSlot(int, int, int)
  803. def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId):
  804. pcIcon = patchcanvas.ICON_APPLICATION
  805. if clientIcon == PATCHBAY_ICON_PLUGIN:
  806. pcIcon = patchcanvas.ICON_PLUGIN
  807. if clientIcon == PATCHBAY_ICON_HARDWARE:
  808. pcIcon = patchcanvas.ICON_HARDWARE
  809. elif clientIcon == PATCHBAY_ICON_CARLA:
  810. pass
  811. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  812. pcIcon = patchcanvas.ICON_DISTRHO
  813. elif clientIcon == PATCHBAY_ICON_FILE:
  814. pcIcon = patchcanvas.ICON_FILE
  815. patchcanvas.setGroupIcon(clientId, pcIcon)
  816. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  817. if pluginId < 0:
  818. return
  819. if pluginId >= self.fPluginCount:
  820. print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
  821. return
  822. patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI))
  823. @pyqtSlot(int, int, int, str)
  824. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  825. if portFlags & PATCHBAY_PORT_IS_INPUT:
  826. portMode = patchcanvas.PORT_MODE_INPUT
  827. else:
  828. portMode = patchcanvas.PORT_MODE_OUTPUT
  829. if portFlags & PATCHBAY_PORT_TYPE_AUDIO:
  830. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  831. isAlternate = False
  832. elif portFlags & PATCHBAY_PORT_TYPE_CV:
  833. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  834. isAlternate = True
  835. elif portFlags & PATCHBAY_PORT_TYPE_MIDI:
  836. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  837. isAlternate = False
  838. else:
  839. portType = patchcanvas.PORT_TYPE_NULL
  840. isAlternate = False
  841. patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate)
  842. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  843. @pyqtSlot(int, int)
  844. def slot_handlePatchbayPortRemovedCallback(self, groupId, portId):
  845. patchcanvas.removePort(groupId, portId)
  846. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  847. @pyqtSlot(int, int, str)
  848. def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName):
  849. patchcanvas.renamePort(groupId, portId, newPortName)
  850. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  851. @pyqtSlot(int, int, int, int, int)
  852. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId):
  853. patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId)
  854. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  855. @pyqtSlot(int, int, int)
  856. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId):
  857. patchcanvas.disconnectPorts(connectionId)
  858. QTimer.singleShot(0, self.ui.miniCanvasPreview.update)
  859. # --------------------------------------------------------------------------------------------------------
  860. # Transport
  861. def setTransportMenuEnabled(self, enabled):
  862. self.ui.act_transport_play.setEnabled(enabled)
  863. self.ui.act_transport_stop.setEnabled(enabled)
  864. self.ui.act_transport_backwards.setEnabled(enabled)
  865. self.ui.act_transport_forwards.setEnabled(enabled)
  866. self.ui.menu_Transport.setEnabled(enabled)
  867. # --------------------------------------------------------------------------------------------------------
  868. # Transport (menu actions)
  869. @pyqtSlot(bool)
  870. def slot_transportPlayPause(self, toggled):
  871. if not self.host.is_engine_running():
  872. return
  873. if toggled:
  874. self.host.transport_play()
  875. else:
  876. self.host.transport_pause()
  877. self.refreshTransport()
  878. @pyqtSlot()
  879. def slot_transportStop(self):
  880. if not self.host.is_engine_running():
  881. return
  882. self.host.transport_pause()
  883. self.host.transport_relocate(0)
  884. self.refreshTransport()
  885. @pyqtSlot()
  886. def slot_transportBackwards(self):
  887. if not self.host.is_engine_running():
  888. return
  889. newFrame = self.host.get_current_transport_frame() - 100000
  890. if newFrame < 0:
  891. newFrame = 0
  892. self.host.transport_relocate(newFrame)
  893. @pyqtSlot()
  894. def slot_transportForwards(self):
  895. if not self.host.is_engine_running():
  896. return
  897. newFrame = self.host.get_current_transport_frame() + 100000
  898. self.host.transport_relocate(newFrame)
  899. # --------------------------------------------------------------------------------------------------------
  900. # Settings
  901. def setEngineSettings(self):
  902. settings = QSettings("falkTX", "Carla2")
  903. # ----------------------------------------------------------------------------------------------------
  904. # main settings
  905. setHostSettings(self.host)
  906. # ----------------------------------------------------------------------------------------------------
  907. # plugin paths
  908. LADSPA_PATH = toList(settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH))
  909. DSSI_PATH = toList(settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH))
  910. LV2_PATH = toList(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH))
  911. VST_PATH = toList(settings.value(CARLA_KEY_PATHS_VST, CARLA_DEFAULT_VST_PATH))
  912. VST3_PATH = toList(settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH))
  913. AU_PATH = toList(settings.value(CARLA_KEY_PATHS_AU, CARLA_DEFAULT_AU_PATH))
  914. GIG_PATH = toList(settings.value(CARLA_KEY_PATHS_GIG, CARLA_DEFAULT_GIG_PATH))
  915. SF2_PATH = toList(settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH))
  916. SFZ_PATH = toList(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH))
  917. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, splitter.join(LADSPA_PATH))
  918. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, splitter.join(DSSI_PATH))
  919. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, splitter.join(LV2_PATH))
  920. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST, splitter.join(VST_PATH))
  921. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, splitter.join(VST3_PATH))
  922. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_AU, splitter.join(AU_PATH))
  923. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_GIG, splitter.join(GIG_PATH))
  924. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, splitter.join(SF2_PATH))
  925. self.host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, splitter.join(SFZ_PATH))
  926. # ----------------------------------------------------------------------------------------------------
  927. # don't continue if plugin
  928. if self.host.isPlugin:
  929. return "Plugin"
  930. # ----------------------------------------------------------------------------------------------------
  931. # driver and device settings
  932. # driver name
  933. try:
  934. audioDriver = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, type=str)
  935. except:
  936. audioDriver = CARLA_DEFAULT_AUDIO_DRIVER
  937. # driver options
  938. try:
  939. audioDevice = settings.value("%s%s/Device" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), "", type=str)
  940. except:
  941. audioDevice = ""
  942. try:
  943. audioNumPeriods = settings.value("%s%s/NumPeriods" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_NUM_PERIODS, type=int)
  944. except:
  945. audioNumPeriods = CARLA_DEFAULT_AUDIO_NUM_PERIODS
  946. try:
  947. audioBufferSize = settings.value("%s%s/BufferSize" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_BUFFER_SIZE, type=int)
  948. except:
  949. audioBufferSize = CARLA_DEFAULT_AUDIO_BUFFER_SIZE
  950. try:
  951. audioSampleRate = settings.value("%s%s/SampleRate" % (CARLA_KEY_ENGINE_DRIVER_PREFIX, audioDriver), CARLA_DEFAULT_AUDIO_SAMPLE_RATE, type=int)
  952. except:
  953. audioSampleRate = CARLA_DEFAULT_AUDIO_SAMPLE_RATE
  954. self.host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
  955. self.host.set_engine_option(ENGINE_OPTION_AUDIO_NUM_PERIODS, audioNumPeriods, "")
  956. self.host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, audioBufferSize, "")
  957. self.host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, audioSampleRate, "")
  958. # ----------------------------------------------------------------------------------------------------
  959. # fix things if needed
  960. if audioDriver != "JACK" and self.host.transportMode == ENGINE_TRANSPORT_MODE_JACK:
  961. self.host.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL
  962. self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, ENGINE_TRANSPORT_MODE_INTERNAL, "")
  963. # ----------------------------------------------------------------------------------------------------
  964. # return selected driver name
  965. return audioDriver
  966. def saveSettings(self):
  967. settings = QSettings()
  968. settings.setValue("Geometry", self.saveGeometry())
  969. #settings.setValue("SplitterState", self.ui.splitter.saveState())
  970. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  971. diskFolders = []
  972. for i in range(self.ui.cb_disk.count()):
  973. diskFolders.append(self.ui.cb_disk.itemData(i))
  974. settings.setValue("DiskFolders", diskFolders)
  975. settings.setValue("ShowMeters", self.ui.act_settings_show_meters.isChecked())
  976. settings.setValue("ShowKeyboard", self.ui.act_settings_show_keyboard.isChecked())
  977. settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
  978. settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())
  979. def loadSettings(self, firstTime):
  980. settings = QSettings()
  981. if firstTime:
  982. self.restoreGeometry(settings.value("Geometry", ""))
  983. showToolbar = settings.value("ShowToolbar", True, type=bool)
  984. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  985. self.ui.toolBar.setVisible(showToolbar)
  986. #if settings.contains("SplitterState"):
  987. #self.ui.splitter.restoreState(settings.value("SplitterState", ""))
  988. #else:
  989. #self.ui.splitter.setSizes([210, 99999])
  990. diskFolders = toList(settings.value("DiskFolders", [HOME]))
  991. self.ui.cb_disk.setItemData(0, HOME)
  992. for i in range(len(diskFolders)):
  993. if i == 0: continue
  994. folder = diskFolders[i]
  995. self.ui.cb_disk.addItem(os.path.basename(folder), folder)
  996. #if MACOS and not settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, type=bool):
  997. # self.setUnifiedTitleAndToolBarOnMac(True)
  998. showMeters = settings.value("ShowMeters", False, type=bool)
  999. self.ui.act_settings_show_meters.setChecked(showMeters)
  1000. self.ui.peak_in.setVisible(showMeters)
  1001. self.ui.peak_out.setVisible(showMeters)
  1002. showKeyboard = settings.value("ShowKeyboard", not(MACOS or WINDOWS), type=bool)
  1003. self.ui.act_settings_show_keyboard.setChecked(showKeyboard)
  1004. self.ui.scrollArea.setVisible(showKeyboard)
  1005. QTimer.singleShot(100, self.slot_restoreCanvasScrollbarValues)
  1006. # TODO - complete this
  1007. self.fSavedSettings = {
  1008. CARLA_KEY_MAIN_PROJECT_FOLDER: settings.value(CARLA_KEY_MAIN_PROJECT_FOLDER, CARLA_DEFAULT_MAIN_PROJECT_FOLDER, type=str),
  1009. CARLA_KEY_MAIN_REFRESH_INTERVAL: settings.value(CARLA_KEY_MAIN_REFRESH_INTERVAL, CARLA_DEFAULT_MAIN_REFRESH_INTERVAL, type=int),
  1010. CARLA_KEY_MAIN_USE_CUSTOM_SKINS: settings.value(CARLA_KEY_MAIN_USE_CUSTOM_SKINS, CARLA_DEFAULT_MAIN_USE_CUSTOM_SKINS, type=bool),
  1011. CARLA_KEY_CANVAS_THEME: settings.value(CARLA_KEY_CANVAS_THEME, CARLA_DEFAULT_CANVAS_THEME, type=str),
  1012. CARLA_KEY_CANVAS_SIZE: settings.value(CARLA_KEY_CANVAS_SIZE, CARLA_DEFAULT_CANVAS_SIZE, type=str),
  1013. CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS: settings.value(CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS, CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS, type=bool),
  1014. CARLA_KEY_CANVAS_USE_BEZIER_LINES: settings.value(CARLA_KEY_CANVAS_USE_BEZIER_LINES, CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES, type=bool),
  1015. CARLA_KEY_CANVAS_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_EYE_CANDY, CARLA_DEFAULT_CANVAS_EYE_CANDY, type=int),
  1016. CARLA_KEY_CANVAS_USE_OPENGL: settings.value(CARLA_KEY_CANVAS_USE_OPENGL, CARLA_DEFAULT_CANVAS_USE_OPENGL, type=bool),
  1017. CARLA_KEY_CANVAS_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_ANTIALIASING, CARLA_DEFAULT_CANVAS_ANTIALIASING, type=int),
  1018. CARLA_KEY_CANVAS_HQ_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_HQ_ANTIALIASING, CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING, type=bool),
  1019. CARLA_KEY_CUSTOM_PAINTING: (settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, type=bool) and
  1020. settings.value(CARLA_KEY_MAIN_PRO_THEME_COLOR, "Black", type=str).lower() == "black")
  1021. }
  1022. self.setEngineSettings()
  1023. self.restartTimersIfNeeded()
  1024. # --------------------------------------------------------------------------------------------------------
  1025. # Settings (helpers)
  1026. @pyqtSlot()
  1027. def slot_restoreCanvasScrollbarValues(self):
  1028. settings = QSettings()
  1029. self.ui.graphicsView.horizontalScrollBar().setValue(settings.value("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().maximum()/2, type=int))
  1030. self.ui.graphicsView.verticalScrollBar().setValue(settings.value("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().maximum()/2, type=int))
  1031. # --------------------------------------------------------------------------------------------------------
  1032. # Settings (menu actions)
  1033. @pyqtSlot(bool)
  1034. def slot_showCanvasMeters(self, yesNo):
  1035. self.ui.peak_in.setVisible(yesNo)
  1036. self.ui.peak_out.setVisible(yesNo)
  1037. @pyqtSlot(bool)
  1038. def slot_showCanvasKeyboard(self, yesNo):
  1039. self.ui.scrollArea.setVisible(yesNo)
  1040. @pyqtSlot()
  1041. def slot_configureCarla(self):
  1042. dialog = CarlaSettingsW(self, self.host, True, hasGL)
  1043. if not dialog.exec_():
  1044. return
  1045. self.loadSettings(False)
  1046. patchcanvas.clear()
  1047. self.setupCanvas()
  1048. self.slot_miniCanvasCheckAll()
  1049. if self.host.is_engine_running():
  1050. self.host.patchbay_refresh(self.fExternalPatchbay)
  1051. # --------------------------------------------------------------------------------------------------------
  1052. # About (menu actions)
  1053. @pyqtSlot()
  1054. def slot_aboutCarla(self):
  1055. CarlaAboutW(self, self.host).exec_()
  1056. @pyqtSlot()
  1057. def slot_aboutJuce(self):
  1058. JuceAboutW(self, self.host).exec_()
  1059. @pyqtSlot()
  1060. def slot_aboutQt(self):
  1061. QApplication.instance().aboutQt()
  1062. # --------------------------------------------------------------------------------------------------------
  1063. # Disk (menu actions)
  1064. @pyqtSlot(int)
  1065. def slot_diskFolderChanged(self, index):
  1066. if index < 0:
  1067. return
  1068. elif index == 0:
  1069. filename = HOME
  1070. self.ui.b_disk_remove.setEnabled(False)
  1071. else:
  1072. filename = self.ui.cb_disk.itemData(index)
  1073. self.ui.b_disk_remove.setEnabled(True)
  1074. self.fDirModel.setRootPath(filename)
  1075. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(filename))
  1076. @pyqtSlot()
  1077. def slot_diskFolderAdd(self):
  1078. newPath = QFileDialog.getExistingDirectory(self, self.tr("New Folder"), "", QFileDialog.ShowDirsOnly)
  1079. if newPath:
  1080. if newPath[-1] == os.sep:
  1081. newPath = newPath[:-1]
  1082. self.ui.cb_disk.addItem(os.path.basename(newPath), newPath)
  1083. self.ui.cb_disk.setCurrentIndex(self.ui.cb_disk.count()-1)
  1084. self.ui.b_disk_remove.setEnabled(True)
  1085. @pyqtSlot()
  1086. def slot_diskFolderRemove(self):
  1087. index = self.ui.cb_disk.currentIndex()
  1088. if index <= 0:
  1089. return
  1090. self.ui.cb_disk.removeItem(index)
  1091. if self.ui.cb_disk.currentIndex() == 0:
  1092. self.ui.b_disk_remove.setEnabled(False)
  1093. @pyqtSlot(QModelIndex)
  1094. def slot_fileTreeDoubleClicked(self, modelIndex):
  1095. filename = self.fDirModel.filePath(modelIndex)
  1096. if not self.host.load_file(filename):
  1097. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  1098. self.tr("Failed to load file"),
  1099. self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
  1100. # --------------------------------------------------------------------------------------------------------
  1101. # Canvas scrollbars
  1102. @pyqtSlot(int)
  1103. def slot_horizontalScrollBarChanged(self, value):
  1104. maximum = self.ui.graphicsView.horizontalScrollBar().maximum()
  1105. if maximum == 0:
  1106. xp = 0
  1107. else:
  1108. xp = float(value) / maximum
  1109. self.ui.miniCanvasPreview.setViewPosX(xp)
  1110. self.updateCanvasInitialPos()
  1111. @pyqtSlot(int)
  1112. def slot_verticalScrollBarChanged(self, value):
  1113. maximum = self.ui.graphicsView.verticalScrollBar().maximum()
  1114. if maximum == 0:
  1115. yp = 0
  1116. else:
  1117. yp = float(value) / maximum
  1118. self.ui.miniCanvasPreview.setViewPosY(yp)
  1119. self.updateCanvasInitialPos()
  1120. # --------------------------------------------------------------------------------------------------------
  1121. # Canvas keyboard
  1122. @pyqtSlot(int)
  1123. def slot_noteOn(self, note):
  1124. for pluginId in self.fSelectedPlugins:
  1125. self.host.send_midi_note(pluginId, 0, note, 100)
  1126. pedit = self.getPluginEditDialog(pluginId)
  1127. pedit.noteOn(0, note, 100)
  1128. @pyqtSlot(int)
  1129. def slot_noteOff(self, note):
  1130. for pluginId in self.fSelectedPlugins:
  1131. self.host.send_midi_note(pluginId, 0, note, 0)
  1132. pedit = self.getPluginEditDialog(pluginId)
  1133. pedit.noteOff(0, note)
  1134. # --------------------------------------------------------------------------------------------------------
  1135. # Canvas keyboard (host callbacks)
  1136. @pyqtSlot(int, int, int, int)
  1137. def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity):
  1138. if pluginId in self.fSelectedPlugins:
  1139. self.ui.keyboard.sendNoteOn(note, False)
  1140. @pyqtSlot(int, int, int)
  1141. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  1142. if pluginId in self.fSelectedPlugins:
  1143. self.ui.keyboard.sendNoteOff(note, False)
  1144. # --------------------------------------------------------------------------------------------------------
  1145. # MiniCanvas stuff
  1146. @pyqtSlot()
  1147. def slot_miniCanvasCheckAll(self):
  1148. self.slot_miniCanvasCheckSize()
  1149. self.slot_horizontalScrollBarChanged(self.ui.graphicsView.horizontalScrollBar().value())
  1150. self.slot_verticalScrollBarChanged(self.ui.graphicsView.verticalScrollBar().value())
  1151. @pyqtSlot()
  1152. def slot_miniCanvasCheckSize(self):
  1153. if self.fCanvasWidth == 0 or self.fCanvasHeight == 0:
  1154. return
  1155. self.ui.miniCanvasPreview.setViewSize(float(self.width()) / self.fCanvasWidth, float(self.height()) / self.fCanvasHeight)
  1156. @pyqtSlot(float, float)
  1157. def slot_miniCanvasMoved(self, xp, yp):
  1158. hsb = self.ui.graphicsView.horizontalScrollBar()
  1159. vsb = self.ui.graphicsView.verticalScrollBar()
  1160. hsb.blockSignals(True)
  1161. vsb.blockSignals(True)
  1162. hsb.setValue(xp * hsb.maximum())
  1163. vsb.setValue(yp * vsb.maximum())
  1164. hsb.blockSignals(False)
  1165. vsb.blockSignals(False)
  1166. self.updateCanvasInitialPos()
  1167. # --------------------------------------------------------------------------------------------------------
  1168. # Rack stuff
  1169. @pyqtSlot(int)
  1170. def slot_currentRowChanged(self, row):
  1171. self.fCurrentRow = row
  1172. if self.fLastSelectedItem is not None:
  1173. self.fLastSelectedItem.setSelected(False)
  1174. if row < 0 or row >= self.fPluginCount or self.fPluginList[row] is None:
  1175. self.fLastSelectedItem = None
  1176. return
  1177. pitem = self.fPluginList[row]
  1178. pitem.getWidget().setSelected(True)
  1179. self.fLastSelectedItem = pitem.getWidget()
  1180. # --------------------------------------------------------------------------------------------------------
  1181. # Timers
  1182. def startTimers(self):
  1183. if self.fIdleTimerFast == 0:
  1184. self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
  1185. if self.fIdleTimerSlow == 0:
  1186. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
  1187. def restartTimersIfNeeded(self):
  1188. if self.fIdleTimerFast != 0:
  1189. self.killTimer(self.fIdleTimerFast)
  1190. self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
  1191. if self.fIdleTimerSlow != 0:
  1192. self.killTimer(self.fIdleTimerSlow)
  1193. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
  1194. def killTimers(self):
  1195. if self.fIdleTimerFast != 0:
  1196. self.killTimer(self.fIdleTimerFast)
  1197. self.fIdleTimerFast = 0
  1198. if self.fIdleTimerSlow != 0:
  1199. self.killTimer(self.fIdleTimerSlow)
  1200. self.fIdleTimerSlow = 0
  1201. # --------------------------------------------------------------------------------------------------------
  1202. # Misc
  1203. @pyqtSlot(int)
  1204. def slot_handleReloadAllCallback(self, pluginId):
  1205. if pluginId >= self.fPluginCount:
  1206. return
  1207. pitem = self.fPluginList[pluginId]
  1208. if pitem is None:
  1209. return
  1210. self.ui.listWidget.setCurrentRow(-1)
  1211. self.fCurrentRow = -1
  1212. self.fLastSelectedItem = None
  1213. pitem.recreateWidget()
  1214. @pyqtSlot(float)
  1215. def slot_handleSampleRateChangedCallback(self, newSampleRate):
  1216. self.fSampleRate = newSampleRate
  1217. self.refreshTransport(True)
  1218. # --------------------------------------------------------------------------------------------------------
  1219. @pyqtSlot(int, int, int, float, str)
  1220. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  1221. print("DEBUG:", pluginId, value1, value2, value3, valueStr)
  1222. @pyqtSlot(str)
  1223. def slot_handleInfoCallback(self, info):
  1224. QMessageBox.information(self, "Information", info)
  1225. @pyqtSlot(str)
  1226. def slot_handleErrorCallback(self, error):
  1227. QMessageBox.critical(self, "Error", error)
  1228. @pyqtSlot()
  1229. def slot_handleQuitCallback(self):
  1230. pass # TODO
  1231. # --------------------------------------------------------------------------------------------------------
  1232. @pyqtSlot()
  1233. def slot_handleSIGUSR1(self):
  1234. print("Got SIGUSR1 -> Saving project now")
  1235. self.slot_fileSave()
  1236. @pyqtSlot()
  1237. def slot_handleSIGTERM(self):
  1238. print("Got SIGTERM -> Closing now")
  1239. self.close()
  1240. # --------------------------------------------------------------------------------------------------------
  1241. # Internal stuff
  1242. def getExtraPtr(self, plugin):
  1243. ptype = plugin['type']
  1244. if ptype == PLUGIN_LADSPA:
  1245. uniqueId = plugin['uniqueId']
  1246. self.maybeLoadRDFs()
  1247. for rdfItem in self.fLadspaRdfList:
  1248. if rdfItem.UniqueID == uniqueId:
  1249. return pointer(rdfItem)
  1250. elif ptype in (PLUGIN_GIG, PLUGIN_SF2):
  1251. if plugin['name'].lower().endswith(" (16 outputs)"):
  1252. return self._true
  1253. return None
  1254. def maybeLoadRDFs(self):
  1255. if not self.fLadspaRdfNeedsUpdate:
  1256. return
  1257. self.fLadspaRdfNeedsUpdate = False
  1258. self.fLadspaRdfList = []
  1259. if not haveLRDF:
  1260. return
  1261. settingsDir = os.path.join(HOME, ".config", "falkTX")
  1262. frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
  1263. if os.path.exists(frLadspaFile):
  1264. frLadspa = open(frLadspaFile, 'r')
  1265. try:
  1266. self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
  1267. except:
  1268. pass
  1269. frLadspa.close()
  1270. # --------------------------------------------------------------------------------------------------------
  1271. def getPluginItem(self, pluginId):
  1272. if pluginId >= self.fPluginCount:
  1273. return None
  1274. pitem = self.fPluginList[pluginId]
  1275. if pitem is None:
  1276. return None
  1277. #if False:
  1278. #pitem = CarlaRackItem(self, 0, False)
  1279. return pitem
  1280. def getPluginEditDialog(self, pluginId):
  1281. if pluginId >= self.fPluginCount:
  1282. return None
  1283. pitem = self.fPluginList[pluginId]
  1284. if pitem is None:
  1285. return None
  1286. #if False:
  1287. #pitem = CarlaRackItem(self, 0, False)
  1288. return pitem.getEditDialog()
  1289. def getPluginSlotWidget(self, pluginId):
  1290. if pluginId >= self.fPluginCount:
  1291. return None
  1292. pitem = self.fPluginList[pluginId]
  1293. if pitem is None:
  1294. return None
  1295. #if False:
  1296. #pitem = CarlaRackItem(self, 0, False)
  1297. return pitem.getWidget()
  1298. # --------------------------------------------------------------------------------------------------------
  1299. # show/hide event
  1300. def showEvent(self, event):
  1301. QMainWindow.showEvent(self, event)
  1302. # set our gui as parent for all plugins UIs
  1303. winIdStr = "%x" % self.winId()
  1304. self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr)
  1305. def hideEvent(self, event):
  1306. # disable parent
  1307. self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")
  1308. QMainWindow.hideEvent(self, event)
  1309. # --------------------------------------------------------------------------------------------------------
  1310. # resize event
  1311. def fixCanvasPreviewSize(self):
  1312. self.ui.patchbay.resize(self.ui.rack.size())
  1313. self.slot_miniCanvasCheckSize()
  1314. def resizeEvent(self, event):
  1315. QMainWindow.resizeEvent(self, event)
  1316. if self.ui.tabWidget.currentIndex() != 1:
  1317. self.fixCanvasPreviewSize()
  1318. else:
  1319. self.slot_miniCanvasCheckSize()
  1320. # --------------------------------------------------------------------------------------------------------
  1321. # timer event
  1322. def idleFast(self):
  1323. self.host.engine_idle()
  1324. self.refreshTransport()
  1325. if self.fPluginCount == 0:
  1326. return
  1327. for pitem in self.fPluginList:
  1328. if pitem is None:
  1329. break
  1330. pitem.getWidget().idleFast()
  1331. for pluginId in self.fSelectedPlugins:
  1332. self.fPeaksCleared = False
  1333. if self.ui.peak_in.isVisible():
  1334. self.ui.peak_in.displayMeter(1, self.host.get_input_peak_value(pluginId, True))
  1335. self.ui.peak_in.displayMeter(2, self.host.get_input_peak_value(pluginId, False))
  1336. if self.ui.peak_out.isVisible():
  1337. self.ui.peak_out.displayMeter(1, self.host.get_output_peak_value(pluginId, True))
  1338. self.ui.peak_out.displayMeter(2, self.host.get_output_peak_value(pluginId, False))
  1339. return
  1340. if self.fPeaksCleared:
  1341. return
  1342. self.fPeaksCleared = True
  1343. self.ui.peak_in.displayMeter(1, 0.0, True)
  1344. self.ui.peak_in.displayMeter(2, 0.0, True)
  1345. self.ui.peak_out.displayMeter(1, 0.0, True)
  1346. self.ui.peak_out.displayMeter(2, 0.0, True)
  1347. def idleSlow(self):
  1348. if self.fPluginCount == 0:
  1349. return
  1350. for pitem in self.fPluginList:
  1351. if pitem is None:
  1352. break
  1353. pitem.getWidget().idleSlow()
  1354. def timerEvent(self, event):
  1355. if event.timerId() == self.fIdleTimerFast:
  1356. self.idleFast()
  1357. elif event.timerId() == self.fIdleTimerSlow:
  1358. self.idleSlow()
  1359. QMainWindow.timerEvent(self, event)
  1360. # --------------------------------------------------------------------------------------------------------
  1361. # close event
  1362. def closeEvent(self, event):
  1363. self.killTimers()
  1364. self.saveSettings()
  1365. if self.host.is_engine_running() and not self.host.isPlugin:
  1366. self.host.set_engine_about_to_close()
  1367. count = self.host.get_current_plugin_count()
  1368. if self.fPluginCount > 0:
  1369. # simulate project loading, to disable container
  1370. self.ui.act_plugin_remove_all.setEnabled(False)
  1371. self.projectLoadingStarted() # FIXME
  1372. app = QApplication.instance()
  1373. for i in range(count):
  1374. app.processEvents()
  1375. self.host.remove_plugin(count-i-1)
  1376. app.processEvents()
  1377. #self.removeAllPlugins()
  1378. #self.host.remove_all_plugins()
  1379. self.slot_engineStop()
  1380. QMainWindow.closeEvent(self, event)
  1381. # ------------------------------------------------------------------------------------------------
  1382. # Canvas callback
  1383. def canvasCallback(action, value1, value2, valueStr):
  1384. host = gCarla.gui.host
  1385. if action == patchcanvas.ACTION_GROUP_INFO:
  1386. pass
  1387. elif action == patchcanvas.ACTION_GROUP_RENAME:
  1388. pass
  1389. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  1390. groupId = value1
  1391. patchcanvas.splitGroup(groupId)
  1392. gCarla.gui.ui.miniCanvasPreview.update()
  1393. elif action == patchcanvas.ACTION_GROUP_JOIN:
  1394. groupId = value1
  1395. patchcanvas.joinGroup(groupId)
  1396. gCarla.gui.ui.miniCanvasPreview.update()
  1397. elif action == patchcanvas.ACTION_PORT_INFO:
  1398. pass
  1399. elif action == patchcanvas.ACTION_PORT_RENAME:
  1400. pass
  1401. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  1402. gOut, pOut, gIn, pIn = [int(i) for i in valueStr.split(":")]
  1403. if not host.patchbay_connect(gOut, pOut, gIn, pIn):
  1404. print("Connection failed:", host.get_last_error())
  1405. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  1406. connectionId = value1
  1407. if not host.patchbay_disconnect(connectionId):
  1408. print("Disconnect failed:", host.get_last_error())
  1409. elif action == patchcanvas.ACTION_PLUGIN_CLONE:
  1410. pluginId = value1
  1411. host.clone_plugin(pluginId)
  1412. elif action == patchcanvas.ACTION_PLUGIN_EDIT:
  1413. pluginId = value1
  1414. dialog = gCarla.gui.getPluginEditDialog(pluginId)
  1415. if dialog is None:
  1416. return
  1417. dialog.show()
  1418. elif action == patchcanvas.ACTION_PLUGIN_RENAME:
  1419. pluginId = value1
  1420. newName = valueStr
  1421. host.rename_plugin(pluginId, newName)
  1422. elif action == patchcanvas.ACTION_PLUGIN_REMOVE:
  1423. pluginId = value1
  1424. host.remove_plugin(pluginId)
  1425. elif action == patchcanvas.ACTION_PLUGIN_SHOW_UI:
  1426. pluginId = value1
  1427. host.show_custom_ui(pluginId, True)
  1428. # ------------------------------------------------------------------------------------------------------------
  1429. # Engine callback
  1430. def engineCallback(host, action, pluginId, value1, value2, value3, valueStr):
  1431. # kdevelop likes this :)
  1432. if False: host = CarlaHostMeta()
  1433. if action == ENGINE_CALLBACK_ENGINE_STARTED:
  1434. host.processMode = value1
  1435. host.transportMode = value2
  1436. elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
  1437. host.processMode = value1
  1438. elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
  1439. host.transportMode = value1
  1440. valueStr = charPtrToString(valueStr)
  1441. if action == ENGINE_CALLBACK_DEBUG:
  1442. host.DebugCallback.emit(pluginId, value1, value2, value3, valueStr)
  1443. elif action == ENGINE_CALLBACK_PLUGIN_ADDED:
  1444. host.PluginAddedCallback.emit(pluginId, valueStr)
  1445. elif action == ENGINE_CALLBACK_PLUGIN_REMOVED:
  1446. host.PluginRemovedCallback.emit(pluginId)
  1447. elif action == ENGINE_CALLBACK_PLUGIN_RENAMED:
  1448. host.PluginRenamedCallback.emit(pluginId, valueStr)
  1449. elif action == ENGINE_CALLBACK_PLUGIN_UNAVAILABLE:
  1450. host.PluginUnavailableCallback.emit(pluginId, valueStr)
  1451. elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
  1452. host.ParameterValueChangedCallback.emit(pluginId, value1, value3)
  1453. elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
  1454. host.ParameterDefaultChangedCallback.emit(pluginId, value1, value3)
  1455. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  1456. host.ParameterMidiCcChangedCallback.emit(pluginId, value1, value2)
  1457. elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  1458. host.ParameterMidiChannelChangedCallback.emit(pluginId, value1, value2)
  1459. elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
  1460. host.ProgramChangedCallback.emit(pluginId, value1)
  1461. elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
  1462. host.MidiProgramChangedCallback.emit(pluginId, value1)
  1463. elif action == ENGINE_CALLBACK_OPTION_CHANGED:
  1464. host.OptionChangedCallback.emit(pluginId, value1, bool(value2))
  1465. elif action == ENGINE_CALLBACK_UI_STATE_CHANGED:
  1466. host.UiStateChangedCallback.emit(pluginId, value1)
  1467. elif action == ENGINE_CALLBACK_NOTE_ON:
  1468. host.NoteOnCallback.emit(pluginId, value1, value2, int(value3))
  1469. elif action == ENGINE_CALLBACK_NOTE_OFF:
  1470. host.NoteOffCallback.emit(pluginId, value1, value2)
  1471. elif action == ENGINE_CALLBACK_UPDATE:
  1472. host.UpdateCallback.emit(pluginId)
  1473. elif action == ENGINE_CALLBACK_RELOAD_INFO:
  1474. host.ReloadInfoCallback.emit(pluginId)
  1475. elif action == ENGINE_CALLBACK_RELOAD_PARAMETERS:
  1476. host.ReloadParametersCallback.emit(pluginId)
  1477. elif action == ENGINE_CALLBACK_RELOAD_PROGRAMS:
  1478. host.ReloadProgramsCallback.emit(pluginId)
  1479. elif action == ENGINE_CALLBACK_RELOAD_ALL:
  1480. host.ReloadAllCallback.emit(pluginId)
  1481. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
  1482. host.PatchbayClientAddedCallback.emit(pluginId, value1, value2, valueStr)
  1483. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED:
  1484. host.PatchbayClientRemovedCallback.emit(pluginId)
  1485. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED:
  1486. host.PatchbayClientRenamedCallback.emit(pluginId, valueStr)
  1487. elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_DATA_CHANGED:
  1488. host.PatchbayClientDataChangedCallback.emit(pluginId, value1, value2)
  1489. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
  1490. host.PatchbayPortAddedCallback.emit(pluginId, value1, value2, valueStr)
  1491. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED:
  1492. host.PatchbayPortRemovedCallback.emit(pluginId, value1)
  1493. elif action == ENGINE_CALLBACK_PATCHBAY_PORT_RENAMED:
  1494. host.PatchbayPortRenamedCallback.emit(pluginId, value1, valueStr)
  1495. elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED:
  1496. gOut, pOut, gIn, pIn = [int(i) for i in valueStr.split(":")] # FIXME
  1497. host.PatchbayConnectionAddedCallback.emit(pluginId, gOut, pOut, gIn, pIn)
  1498. elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED:
  1499. host.PatchbayConnectionRemovedCallback.emit(pluginId, value1, value2)
  1500. elif action == ENGINE_CALLBACK_ENGINE_STARTED:
  1501. host.EngineStartedCallback.emit(value1, value2, valueStr)
  1502. elif action == ENGINE_CALLBACK_ENGINE_STOPPED:
  1503. host.EngineStoppedCallback.emit()
  1504. elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
  1505. host.ProcessModeChangedCallback.emit(value1)
  1506. elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
  1507. host.TransportModeChangedCallback.emit(value1)
  1508. elif action == ENGINE_CALLBACK_BUFFER_SIZE_CHANGED:
  1509. host.BufferSizeChangedCallback.emit(value1)
  1510. elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
  1511. host.SampleRateChangedCallback.emit(value3)
  1512. elif action == ENGINE_CALLBACK_IDLE:
  1513. QApplication.instance().processEvents()
  1514. elif action == ENGINE_CALLBACK_INFO:
  1515. host.InfoCallback.emit(valueStr)
  1516. elif action == ENGINE_CALLBACK_ERROR:
  1517. host.ErrorCallback.emit(valueStr)
  1518. elif action == ENGINE_CALLBACK_QUIT:
  1519. host.QuitCallback.emit()
  1520. # ------------------------------------------------------------------------------------------------------------
  1521. # File callback
  1522. def fileCallback(ptr, action, isDir, title, filter):
  1523. ret = ("", "") if config_UseQt5 else ""
  1524. if action == FILE_CALLBACK_DEBUG:
  1525. pass
  1526. elif action == FILE_CALLBACK_OPEN:
  1527. ret = QFileDialog.getOpenFileName(gCarla.gui, charPtrToString(title), "", charPtrToString(filter) ) #, QFileDialog.ShowDirsOnly if isDir else 0x0)
  1528. elif action == FILE_CALLBACK_SAVE:
  1529. ret = QFileDialog.getSaveFileName(gCarla.gui, charPtrToString(title), "", charPtrToString(filter), QFileDialog.ShowDirsOnly if isDir else 0x0)
  1530. if config_UseQt5:
  1531. ret = ret[0]
  1532. if not ret:
  1533. return None
  1534. # FIXME
  1535. global fileRet
  1536. fileRet = c_char_p(ret.encode("utf-8"))
  1537. retval = cast(byref(fileRet), POINTER(c_uintptr))
  1538. return retval.contents.value
  1539. # ------------------------------------------------------------------------------------------------------------
  1540. # Init host
  1541. def initHost(initName, libPrefixOrPluginClass, isControl, isPlugin, failError):
  1542. if isPlugin:
  1543. PluginClass = libPrefixOrPluginClass
  1544. libPrefix = None
  1545. else:
  1546. PluginClass = None
  1547. libPrefix = libPrefixOrPluginClass
  1548. # --------------------------------------------------------------------------------------------------------
  1549. # Set Carla library name
  1550. libname = "libcarla_"
  1551. if isControl:
  1552. libname += "control2"
  1553. else:
  1554. libname += "standalone2"
  1555. if WINDOWS:
  1556. libname += ".dll"
  1557. elif MACOS:
  1558. libname += ".dylib"
  1559. else:
  1560. libname += ".so"
  1561. # --------------------------------------------------------------------------------------------------------
  1562. # Set binary dir
  1563. CWDl = CWD.lower()
  1564. # standalone, installed system-wide linux
  1565. if libPrefix is not None:
  1566. pathBinaries = os.path.join(libPrefix, "lib", "carla")
  1567. pathResources = os.path.join(libPrefix, "share", "carla", "resources")
  1568. # standalone, local source
  1569. elif CWDl.endswith("source"):
  1570. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "bin"))
  1571. pathResources = os.path.join(pathBinaries, "resources")
  1572. # plugin
  1573. elif CWDl.endswith("resources"):
  1574. # installed system-wide linux
  1575. if CWDl.endswith("/share/carla/resources"):
  1576. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "lib", "carla"))
  1577. pathResources = CWD
  1578. # local source
  1579. elif CWDl.endswith("native-plugins%sresources" % os.sep):
  1580. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "..", "bin"))
  1581. pathResources = CWD
  1582. # other
  1583. else:
  1584. pathBinaries = os.path.abspath(os.path.join(CWD, ".."))
  1585. pathResources = CWD
  1586. # everything else
  1587. else:
  1588. pathBinaries = CWD
  1589. pathResources = os.path.join(pathBinaries, "resources")
  1590. # --------------------------------------------------------------------------------------------------------
  1591. # Fail if binary dir is not found
  1592. if not os.path.exists(pathBinaries):
  1593. if failError:
  1594. QMessageBox.critical(None, "Error", "Failed to find the carla binaries, cannot continue")
  1595. sys.exit(1)
  1596. return
  1597. # --------------------------------------------------------------------------------------------------------
  1598. # Print info
  1599. print("Carla %s started, status:" % VERSION)
  1600. print(" Python version: %s" % sys.version.split(" ",1)[0])
  1601. print(" Qt version: %s" % qVersion())
  1602. print(" PyQt version: %s" % PYQT_VERSION_STR)
  1603. print(" Binary dir: %s" % pathBinaries)
  1604. print(" Resources dir: %s" % pathResources)
  1605. # --------------------------------------------------------------------------------------------------------
  1606. # Init host
  1607. if failError:
  1608. # no try
  1609. host = PluginClass() if isPlugin else CarlaHostDLL(os.path.join(pathBinaries, libname))
  1610. else:
  1611. try:
  1612. host = PluginClass() if isPlugin else CarlaHostDLL(os.path.join(pathBinaries, libname))
  1613. except:
  1614. host = CarlaHostNull()
  1615. host.isControl = isControl
  1616. host.isPlugin = isPlugin
  1617. host.set_engine_callback(lambda h,a,p,v1,v2,v3,vs: engineCallback(host,a,p,v1,v2,v3,vs))
  1618. host.set_file_callback(fileCallback)
  1619. # If it's a plugin the paths are already set
  1620. if not isPlugin:
  1621. host.pathBinaries = pathBinaries
  1622. host.pathResources = pathResources
  1623. host.set_engine_option(ENGINE_OPTION_PATH_BINARIES, 0, pathBinaries)
  1624. host.set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources)
  1625. if not isControl:
  1626. host.set_engine_option(ENGINE_OPTION_NSM_INIT, os.getpid(), initName)
  1627. return host
  1628. # ------------------------------------------------------------------------------------------------------------
  1629. # Load host settings
  1630. def loadHostSettings(host):
  1631. # kdevelop likes this :)
  1632. if False: host = CarlaHostMeta()
  1633. settings = QSettings("falkTX", "Carla2")
  1634. # bool values
  1635. try:
  1636. host.forceStereo = settings.value(CARLA_KEY_ENGINE_FORCE_STEREO, CARLA_DEFAULT_FORCE_STEREO, type=bool)
  1637. except:
  1638. host.forceStereo = CARLA_DEFAULT_FORCE_STEREO
  1639. try:
  1640. host.preferPluginBridges = settings.value(CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES, CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, type=bool)
  1641. except:
  1642. host.preferPluginBridges = CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES
  1643. try:
  1644. host.preferUIBridges = settings.value(CARLA_KEY_ENGINE_PREFER_UI_BRIDGES, CARLA_DEFAULT_PREFER_UI_BRIDGES, type=bool)
  1645. except:
  1646. host.preferUIBridges = CARLA_DEFAULT_PREFER_UI_BRIDGES
  1647. try:
  1648. host.uisAlwaysOnTop = settings.value(CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP, CARLA_DEFAULT_UIS_ALWAYS_ON_TOP, type=bool)
  1649. except:
  1650. host.uisAlwaysOnTop = CARLA_DEFAULT_UIS_ALWAYS_ON_TOP
  1651. # int values
  1652. try:
  1653. host.maxParameters = settings.value(CARLA_KEY_ENGINE_MAX_PARAMETERS, CARLA_DEFAULT_MAX_PARAMETERS, type=int)
  1654. except:
  1655. host.maxParameters = CARLA_DEFAULT_MAX_PARAMETERS
  1656. try:
  1657. host.uiBridgesTimeout = settings.value(CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT, CARLA_DEFAULT_UI_BRIDGES_TIMEOUT, type=int)
  1658. except:
  1659. host.uiBridgesTimeout = CARLA_DEFAULT_UI_BRIDGES_TIMEOUT
  1660. if host.isPlugin:
  1661. return
  1662. # enums
  1663. try:
  1664. host.transportMode = settings.value(CARLA_KEY_ENGINE_TRANSPORT_MODE, CARLA_DEFAULT_TRANSPORT_MODE, type=int)
  1665. except:
  1666. host.transportMode = CARLA_DEFAULT_TRANSPORT_MODE
  1667. if not host.processModeForced:
  1668. try:
  1669. host.processMode = settings.value(CARLA_KEY_ENGINE_PROCESS_MODE, CARLA_DEFAULT_PROCESS_MODE, type=int)
  1670. except:
  1671. host.processMode = CARLA_DEFAULT_PROCESS_MODE
  1672. # --------------------------------------------------------------------------------------------------------
  1673. # fix things if needed
  1674. if host.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS and LADISH_APP_NAME:
  1675. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  1676. host.processMode = ENGINE_PROCESS_MODE_SINGLE_CLIENT
  1677. # ------------------------------------------------------------------------------------------------------------
  1678. # Set host settings
  1679. def setHostSettings(host):
  1680. # kdevelop likes this :)
  1681. if False: host = CarlaHostMeta()
  1682. # TEST
  1683. host.preventBadBehaviour = True
  1684. host.set_engine_option(ENGINE_OPTION_FORCE_STEREO, host.forceStereo, "")
  1685. host.set_engine_option(ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, host.preferPluginBridges, "")
  1686. host.set_engine_option(ENGINE_OPTION_PREFER_UI_BRIDGES, host.preferUIBridges, "")
  1687. host.set_engine_option(ENGINE_OPTION_UIS_ALWAYS_ON_TOP, host.uisAlwaysOnTop, "")
  1688. host.set_engine_option(ENGINE_OPTION_MAX_PARAMETERS, host.maxParameters, "")
  1689. host.set_engine_option(ENGINE_OPTION_UI_BRIDGES_TIMEOUT, host.uiBridgesTimeout, "")
  1690. host.set_engine_option(ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR, host.preventBadBehaviour, "")
  1691. if host.isPlugin:
  1692. return
  1693. host.set_engine_option(ENGINE_OPTION_PROCESS_MODE, host.processMode, "")
  1694. host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, "")
  1695. # ------------------------------------------------------------------------------------------------------------