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.

2203 lines
84KB

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