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.

965 lines
35KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla patchbay widget 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 QPointF, QTimer
  24. from PyQt5.QtGui import QImage
  25. from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
  26. from PyQt5.QtWidgets import QFrame, QGraphicsView, QGridLayout
  27. else:
  28. from PyQt4.QtCore import QPointF, QTimer
  29. from PyQt4.QtGui import QFrame, QGraphicsView, QGridLayout, QImage, QPrinter, QPrintDialog
  30. # ------------------------------------------------------------------------------------------------------------
  31. # Imports (Custom Stuff)
  32. import patchcanvas
  33. from carla_host import *
  34. from digitalpeakmeter import DigitalPeakMeter
  35. from pixmapkeyboard import PixmapKeyboardHArea
  36. # ------------------------------------------------------------------------------------------------------------
  37. # Try Import OpenGL
  38. try:
  39. if config_UseQt5:
  40. from PyQt5.QtOpenGL import QGLWidget
  41. else:
  42. from PyQt4.QtOpenGL import QGLWidget
  43. hasGL = True
  44. except:
  45. hasGL = False
  46. # ------------------------------------------------------------------------------------------------------------
  47. # Carla Canvas defaults
  48. CARLA_DEFAULT_CANVAS_SIZE_WIDTH = 3100
  49. CARLA_DEFAULT_CANVAS_SIZE_HEIGHT = 2400
  50. # ------------------------------------------------------------------------------------------------
  51. # Dummy class used in main carla as replacement for PluginEdit
  52. class DummyPluginEdit(object):
  53. def __init__(self, parent, pluginId):
  54. object.__init__(self)
  55. self.fPluginId = pluginId
  56. #------------------------------------------------------------------
  57. def clearNotes(self):
  58. pass
  59. def noteOn(self, channel, note, velocity):
  60. pass
  61. def noteOff(self, channel, note):
  62. pass
  63. #------------------------------------------------------------------
  64. def setPluginId(self, idx):
  65. self.fPluginId = idx
  66. #------------------------------------------------------------------
  67. def close(self):
  68. pass
  69. def setName(self, name):
  70. pass
  71. def setParameterValue(self, parameterId, value):
  72. pass
  73. def reloadAll(self):
  74. pass
  75. def idleSlow(self):
  76. pass
  77. # ------------------------------------------------------------------------------------------------
  78. # Patchbay widget
  79. class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta):
  80. #class CarlaPatchbayW(QFrame, PluginEditParentMeta, HostWidgetMeta, metaclass=PyQtMetaClass):
  81. def __init__(self, parent, host, doSetup = True, onlyPatchbay = True, is3D = False):
  82. QFrame.__init__(self, parent)
  83. self.host = host
  84. if False:
  85. # kdevelop likes this :)
  86. host = CarlaHostMeta()
  87. self.host = host
  88. # -------------------------------------------------------------
  89. self.fLayout = QGridLayout(self)
  90. self.fLayout.setContentsMargins(0, 0, 0, 0)
  91. self.fLayout.setSpacing(1)
  92. self.setLayout(self.fLayout)
  93. self.fView = QGraphicsView(self)
  94. self.fKeys = PixmapKeyboardHArea(self)
  95. self.fPeaksIn = DigitalPeakMeter(self)
  96. self.fPeaksOut = DigitalPeakMeter(self)
  97. self.fPeaksCleared = True
  98. self.fPeaksIn.setColor(DigitalPeakMeter.BLUE)
  99. self.fPeaksIn.setChannels(2)
  100. self.fPeaksIn.setOrientation(DigitalPeakMeter.VERTICAL)
  101. self.fPeaksIn.setFixedWidth(25)
  102. self.fPeaksOut.setColor(DigitalPeakMeter.GREEN)
  103. self.fPeaksOut.setChannels(2)
  104. self.fPeaksOut.setOrientation(DigitalPeakMeter.VERTICAL)
  105. self.fPeaksOut.setFixedWidth(25)
  106. self.fLayout.addWidget(self.fPeaksIn, 0, 0)
  107. self.fLayout.addWidget(self.fView, 0, 1) # self.fViewWidget if is3D else
  108. self.fLayout.addWidget(self.fPeaksOut, 0, 2)
  109. self.fLayout.addWidget(self.fKeys, 1, 0, 1, 0)
  110. # -------------------------------------------------------------
  111. # Internal stuff
  112. self.fParent = parent
  113. self.fPluginCount = 0
  114. self.fPluginList = []
  115. self.fExternalPatchbay = False
  116. self.fIsOnlyPatchbay = onlyPatchbay
  117. self.fSelectedPlugins = []
  118. self.fCanvasWidth = 0
  119. self.fCanvasHeight = 0
  120. # -------------------------------------------------------------
  121. # Set-up Canvas Preview
  122. self.fMiniCanvasPreview = self.fParent.ui.miniCanvasPreview
  123. self.fMiniCanvasPreview.setRealParent(self)
  124. self.fMovingViaMiniCanvas = False
  125. # -------------------------------------------------------------
  126. # Set-up Canvas
  127. self.scene = patchcanvas.PatchScene(self, self.fView)
  128. self.fView.setScene(self.scene)
  129. self.fView.setRenderHint(QPainter.Antialiasing, bool(parent.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL))
  130. if parent.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL: # and not is3D:
  131. self.fViewWidget = QGLWidget(self)
  132. self.fView.setViewport(self.fViewWidget)
  133. self.fView.setRenderHint(QPainter.HighQualityAntialiasing, parent.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING])
  134. self.setupCanvas()
  135. QTimer.singleShot(100, self.slot_restoreScrollbarValues)
  136. # -------------------------------------------------------------
  137. # Connect actions to functions
  138. parent.ui.act_settings_show_meters.toggled.connect(self.slot_showCanvasMeters)
  139. parent.ui.act_settings_show_keyboard.toggled.connect(self.slot_showCanvasKeyboard)
  140. self.fView.horizontalScrollBar().valueChanged.connect(self.slot_horizontalScrollBarChanged)
  141. self.fView.verticalScrollBar().valueChanged.connect(self.slot_verticalScrollBarChanged)
  142. self.scene.scaleChanged.connect(self.slot_canvasScaleChanged)
  143. self.scene.sceneGroupMoved.connect(self.slot_canvasItemMoved)
  144. self.scene.pluginSelected.connect(self.slot_canvasPluginSelected)
  145. self.fMiniCanvasPreview.miniCanvasMoved.connect(self.slot_miniCanvasMoved)
  146. self.fKeys.keyboard.noteOn.connect(self.slot_noteOn)
  147. self.fKeys.keyboard.noteOff.connect(self.slot_noteOff)
  148. # -------------------------------------------------------------
  149. # Load Settings
  150. settings = QSettings()
  151. showMeters = settings.value("ShowMeters", False, type=bool)
  152. self.fParent.ui.act_settings_show_meters.setChecked(showMeters)
  153. self.fPeaksIn.setVisible(showMeters)
  154. self.fPeaksOut.setVisible(showMeters)
  155. showKeyboard = settings.value("ShowKeyboard", not(MACOS or WINDOWS), type=bool)
  156. self.fParent.ui.act_settings_show_keyboard.setChecked(showKeyboard)
  157. self.fKeys.setVisible(showKeyboard)
  158. # -------------------------------------------------------------
  159. # Connect actions to functions (part 2)
  160. host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback)
  161. host.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback)
  162. host.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  163. host.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  164. host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback)
  165. host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback)
  166. host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback)
  167. host.PatchbayClientDataChangedCallback.connect(self.slot_handlePatchbayClientDataChangedCallback)
  168. host.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback)
  169. host.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback)
  170. host.PatchbayPortRenamedCallback.connect(self.slot_handlePatchbayPortRenamedCallback)
  171. host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
  172. host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)
  173. if not doSetup: return
  174. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  175. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  176. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  177. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  178. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  179. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  180. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  181. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  182. parent.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal)
  183. parent.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal)
  184. parent.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  185. parent.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange)
  186. parent.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh)
  187. parent.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit)
  188. parent.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn)
  189. parent.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut)
  190. parent.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset)
  191. parent.ui.act_canvas_print.triggered.connect(self.slot_canvasPrint)
  192. parent.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage)
  193. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  194. # -----------------------------------------------------------------
  195. def getPluginEditDialog(self, pluginId):
  196. if pluginId >= self.fPluginCount:
  197. return None
  198. pedit = self.fPluginList[pluginId]
  199. if pedit is None:
  200. return None
  201. if False:
  202. pedit = DummyPluginEdit(self, 0)
  203. return pedit
  204. # -----------------------------------------------------------------
  205. @pyqtSlot(int, str)
  206. def slot_handlePluginAddedCallback(self, pluginId, pluginName):
  207. if self.fIsOnlyPatchbay:
  208. pedit = PluginEdit(self, self.host, pluginId)
  209. else:
  210. pedit = DummyPluginEdit(self, pluginId)
  211. self.fPluginList.append(pedit)
  212. self.fPluginCount += 1
  213. if self.fIsOnlyPatchbay and not self.fParent.isProjectLoading():
  214. self.host.set_active(pluginId, True)
  215. @pyqtSlot(int)
  216. def slot_handlePluginRemovedCallback(self, pluginId):
  217. patchcanvas.handlePluginRemoved(pluginId)
  218. if pluginId in self.fSelectedPlugins:
  219. self.clearSideStuff()
  220. pedit = self.getPluginEditDialog(pluginId)
  221. self.fPluginCount -= 1
  222. self.fPluginList.pop(pluginId)
  223. if pedit is not None:
  224. pedit.close()
  225. del pedit
  226. # push all plugins 1 slot back
  227. for i in range(pluginId, self.fPluginCount):
  228. pedit = self.fPluginList[i]
  229. pedit.setPluginId(i)
  230. @pyqtSlot(int, int, int, int)
  231. def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity):
  232. if pluginId in self.fSelectedPlugins:
  233. self.fKeys.keyboard.sendNoteOn(note, False)
  234. @pyqtSlot(int, int, int)
  235. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  236. if pluginId in self.fSelectedPlugins:
  237. self.fKeys.keyboard.sendNoteOff(note, False)
  238. # -----------------------------------------------------------------
  239. # HostWidgetMeta methods
  240. def removeAllPlugins(self):
  241. patchcanvas.handleAllPluginsRemoved()
  242. for pedit in self.fPluginList:
  243. if pedit is None:
  244. break
  245. pedit.close()
  246. del pedit
  247. self.fPluginCount = 0
  248. self.fPluginList = []
  249. self.clearSideStuff()
  250. def engineStarted(self):
  251. pass
  252. def engineStopped(self):
  253. patchcanvas.clear()
  254. def idleFast(self):
  255. if self.fPluginCount == 0:
  256. return
  257. for pluginId in self.fSelectedPlugins:
  258. self.fPeaksCleared = False
  259. if self.fPeaksIn.isVisible():
  260. self.fPeaksIn.displayMeter(1, self.host.get_input_peak_value(pluginId, True))
  261. self.fPeaksIn.displayMeter(2, self.host.get_input_peak_value(pluginId, False))
  262. if self.fPeaksOut.isVisible():
  263. self.fPeaksOut.displayMeter(1, self.host.get_output_peak_value(pluginId, True))
  264. self.fPeaksOut.displayMeter(2, self.host.get_output_peak_value(pluginId, False))
  265. return
  266. if self.fPeaksCleared:
  267. return
  268. self.fPeaksCleared = True
  269. self.fPeaksIn.displayMeter(1, 0.0, True)
  270. self.fPeaksIn.displayMeter(2, 0.0, True)
  271. self.fPeaksOut.displayMeter(1, 0.0, True)
  272. self.fPeaksOut.displayMeter(2, 0.0, True)
  273. def idleSlow(self):
  274. for pedit in self.fPluginList:
  275. if pedit is None:
  276. break
  277. pedit.idleSlow()
  278. def projectLoadingStarted(self):
  279. self.fView.setEnabled(False)
  280. def projectLoadingFinished(self):
  281. self.fView.setEnabled(True)
  282. QTimer.singleShot(1000, self.slot_canvasRefresh)
  283. def saveSettings(self, settings):
  284. settings.setValue("ShowMeters", self.fParent.ui.act_settings_show_meters.isChecked())
  285. settings.setValue("ShowKeyboard", self.fParent.ui.act_settings_show_keyboard.isChecked())
  286. settings.setValue("HorizontalScrollBarValue", self.fView.horizontalScrollBar().value())
  287. settings.setValue("VerticalScrollBarValue", self.fView.verticalScrollBar().value())
  288. def showEditDialog(self, pluginId):
  289. pedit = self.getPluginEditDialog(pluginId)
  290. if pedit:
  291. pedit.show()
  292. # -----------------------------------------------------------------
  293. # PluginEdit callbacks
  294. def editDialogVisibilityChanged(self, pluginId, visible):
  295. pass
  296. def editDialogPluginHintsChanged(self, pluginId, hints):
  297. pass
  298. def editDialogParameterValueChanged(self, pluginId, parameterId, value):
  299. pass
  300. def editDialogProgramChanged(self, pluginId, index):
  301. pass
  302. def editDialogMidiProgramChanged(self, pluginId, index):
  303. pass
  304. def editDialogNotePressed(self, pluginId, note):
  305. if pluginId in self.fSelectedPlugins:
  306. self.fKeys.keyboard.sendNoteOn(note, False)
  307. def editDialogNoteReleased(self, pluginId, note):
  308. if pluginId in self.fSelectedPlugins:
  309. self.fKeys.keyboard.sendNoteOff(note, False)
  310. def editDialogMidiActivityChanged(self, pluginId, onOff):
  311. pass
  312. # -----------------------------------------------------------------
  313. def clearSideStuff(self):
  314. self.scene.clearSelection()
  315. self.fSelectedPlugins = []
  316. self.fKeys.keyboard.allNotesOff(False)
  317. self.fKeys.setEnabled(False)
  318. self.fPeaksCleared = True
  319. self.fPeaksIn.displayMeter(1, 0.0, True)
  320. self.fPeaksIn.displayMeter(2, 0.0, True)
  321. self.fPeaksOut.displayMeter(1, 0.0, True)
  322. self.fPeaksOut.displayMeter(2, 0.0, True)
  323. def setupCanvas(self):
  324. pOptions = patchcanvas.options_t()
  325. pOptions.theme_name = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_THEME]
  326. pOptions.auto_hide_groups = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS]
  327. pOptions.use_bezier_lines = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_USE_BEZIER_LINES]
  328. pOptions.antialiasing = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING]
  329. pOptions.eyecandy = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY]
  330. pFeatures = patchcanvas.features_t()
  331. pFeatures.group_info = False
  332. pFeatures.group_rename = False
  333. pFeatures.port_info = False
  334. pFeatures.port_rename = False
  335. pFeatures.handle_group_pos = True
  336. patchcanvas.setOptions(pOptions)
  337. patchcanvas.setFeatures(pFeatures)
  338. patchcanvas.init("Carla2", self.scene, canvasCallback, False)
  339. tryCanvasSize = self.fParent.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x")
  340. if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit() and tryCanvasSize[1].isdigit():
  341. self.fCanvasWidth = int(tryCanvasSize[0])
  342. self.fCanvasHeight = int(tryCanvasSize[1])
  343. else:
  344. self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH
  345. self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT
  346. patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight)
  347. patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2)
  348. self.fView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight)
  349. self.themeData = [self.fCanvasWidth, self.fCanvasHeight, patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color()]
  350. def updateCanvasInitialPos(self):
  351. x = self.fView.horizontalScrollBar().value() + self.width()/4
  352. y = self.fView.verticalScrollBar().value() + self.height()/4
  353. patchcanvas.setInitialPos(x, y)
  354. # -----------------------------------------------------------------
  355. @pyqtSlot(bool)
  356. def slot_showCanvasMeters(self, yesNo):
  357. self.fPeaksIn.setVisible(yesNo)
  358. self.fPeaksOut.setVisible(yesNo)
  359. @pyqtSlot(bool)
  360. def slot_showCanvasKeyboard(self, yesNo):
  361. self.fKeys.setVisible(yesNo)
  362. # -----------------------------------------------------------------
  363. @pyqtSlot()
  364. def slot_miniCanvasCheckAll(self):
  365. self.slot_miniCanvasCheckSize()
  366. self.slot_horizontalScrollBarChanged(self.fView.horizontalScrollBar().value())
  367. self.slot_verticalScrollBarChanged(self.fView.verticalScrollBar().value())
  368. @pyqtSlot()
  369. def slot_miniCanvasCheckSize(self):
  370. self.fMiniCanvasPreview.setViewSize(float(self.width()) / self.fCanvasWidth, float(self.height()) / self.fCanvasHeight)
  371. @pyqtSlot(int)
  372. def slot_horizontalScrollBarChanged(self, value):
  373. if self.fMovingViaMiniCanvas: return
  374. maximum = self.fView.horizontalScrollBar().maximum()
  375. if maximum == 0:
  376. xp = 0
  377. else:
  378. xp = float(value) / maximum
  379. self.fMiniCanvasPreview.setViewPosX(xp)
  380. self.updateCanvasInitialPos()
  381. @pyqtSlot(int)
  382. def slot_verticalScrollBarChanged(self, value):
  383. if self.fMovingViaMiniCanvas: return
  384. maximum = self.fView.verticalScrollBar().maximum()
  385. if maximum == 0:
  386. yp = 0
  387. else:
  388. yp = float(value) / maximum
  389. self.fMiniCanvasPreview.setViewPosY(yp)
  390. self.updateCanvasInitialPos()
  391. @pyqtSlot()
  392. def slot_restoreScrollbarValues(self):
  393. settings = QSettings()
  394. self.fView.horizontalScrollBar().setValue(settings.value("HorizontalScrollBarValue", self.fView.horizontalScrollBar().maximum()/2, type=int))
  395. self.fView.verticalScrollBar().setValue(settings.value("VerticalScrollBarValue", self.fView.verticalScrollBar().maximum()/2, type=int))
  396. # -----------------------------------------------------------------
  397. @pyqtSlot(float)
  398. def slot_canvasScaleChanged(self, scale):
  399. self.fMiniCanvasPreview.setViewScale(scale)
  400. @pyqtSlot(int, int, QPointF)
  401. def slot_canvasItemMoved(self, group_id, split_mode, pos):
  402. self.fMiniCanvasPreview.update()
  403. @pyqtSlot(list)
  404. def slot_canvasPluginSelected(self, pluginList):
  405. self.fKeys.keyboard.allNotesOff(False)
  406. self.fKeys.setEnabled(len(pluginList) != 0) # and self.fPluginCount > 0
  407. self.fSelectedPlugins = pluginList
  408. @pyqtSlot(float, float)
  409. def slot_miniCanvasMoved(self, xp, yp):
  410. self.fMovingViaMiniCanvas = True
  411. self.fView.horizontalScrollBar().setValue(xp * self.fView.horizontalScrollBar().maximum())
  412. self.fView.verticalScrollBar().setValue(yp * self.fView.verticalScrollBar().maximum())
  413. self.fMovingViaMiniCanvas = False
  414. self.updateCanvasInitialPos()
  415. # -----------------------------------------------------------------
  416. @pyqtSlot(int)
  417. def slot_noteOn(self, note):
  418. for pluginId in self.fSelectedPlugins:
  419. self.host.send_midi_note(pluginId, 0, note, 100)
  420. pedit = self.getPluginEditDialog(pluginId)
  421. pedit.noteOn(0, note, 100)
  422. @pyqtSlot(int)
  423. def slot_noteOff(self, note):
  424. for pluginId in self.fSelectedPlugins:
  425. self.host.send_midi_note(pluginId, 0, note, 0)
  426. pedit = self.getPluginEditDialog(pluginId)
  427. pedit.noteOff(0, note)
  428. # -----------------------------------------------------------------
  429. @pyqtSlot()
  430. def slot_pluginsEnable(self):
  431. if not self.host.is_engine_running():
  432. return
  433. for i in range(self.fPluginCount):
  434. self.host.set_active(i, True)
  435. @pyqtSlot()
  436. def slot_pluginsDisable(self):
  437. if not self.host.is_engine_running():
  438. return
  439. for i in range(self.fPluginCount):
  440. self.host.set_active(i, False)
  441. @pyqtSlot()
  442. def slot_pluginsVolume100(self):
  443. if not self.host.is_engine_running():
  444. return
  445. for i in range(self.fPluginCount):
  446. pedit = self.fPluginList[i]
  447. if pedit is None:
  448. break
  449. if pedit.getHints() & PLUGIN_CAN_VOLUME:
  450. pedit.setParameterValue(PARAMETER_VOLUME, 1.0)
  451. self.host.set_volume(i, 1.0)
  452. @pyqtSlot()
  453. def slot_pluginsMute(self):
  454. if not self.host.is_engine_running():
  455. return
  456. for i in range(self.fPluginCount):
  457. pedit = self.fPluginList[i]
  458. if pedit is None:
  459. break
  460. if pedit.getHints() & PLUGIN_CAN_VOLUME:
  461. pedit.setParameterValue(PARAMETER_VOLUME, 0.0)
  462. self.host.set_volume(i, 0.0)
  463. @pyqtSlot()
  464. def slot_pluginsWet100(self):
  465. if not self.host.is_engine_running():
  466. return
  467. for i in range(self.fPluginCount):
  468. pedit = self.fPluginList[i]
  469. if pedit is None:
  470. break
  471. if pedit.getHints() & PLUGIN_CAN_DRYWET:
  472. pedit.setParameterValue(PARAMETER_DRYWET, 1.0)
  473. self.host.set_drywet(i, 1.0)
  474. @pyqtSlot()
  475. def slot_pluginsBypass(self):
  476. if not self.host.is_engine_running():
  477. return
  478. for i in range(self.fPluginCount):
  479. pedit = self.fPluginList[i]
  480. if pedit is None:
  481. break
  482. if pedit.getHints() & PLUGIN_CAN_DRYWET:
  483. pedit.setParameterValue(PARAMETER_DRYWET, 0.0)
  484. self.host.set_drywet(i, 0.0)
  485. @pyqtSlot()
  486. def slot_pluginsCenter(self):
  487. if not self.host.is_engine_running():
  488. return
  489. for i in range(self.fPluginCount):
  490. pedit = self.fPluginList[i]
  491. if pedit is None:
  492. break
  493. if pedit.getHints() & PLUGIN_CAN_BALANCE:
  494. pedit.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  495. pedit.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  496. self.host.set_balance_left(i, -1.0)
  497. self.host.set_balance_right(i, 1.0)
  498. if pedit.getHints() & PLUGIN_CAN_PANNING:
  499. pedit.setParameterValue(PARAMETER_PANNING, 0.0)
  500. self.host.set_panning(i, 0.0)
  501. # -----------------------------------------------------------------
  502. @pyqtSlot()
  503. def slot_configureCarla(self):
  504. dialog = CarlaSettingsW(self, self.host, True, hasGL)
  505. if not dialog.exec_():
  506. return
  507. self.fParent.loadSettings(False)
  508. patchcanvas.clear()
  509. self.setupCanvas()
  510. self.fParent.updateContainer(self.themeData)
  511. self.slot_miniCanvasCheckAll()
  512. if self.host.is_engine_running():
  513. self.host.patchbay_refresh(self.fExternalPatchbay)
  514. # -----------------------------------------------------------------
  515. @pyqtSlot(int, int, int, str)
  516. def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName):
  517. pcSplit = patchcanvas.SPLIT_UNDEF
  518. pcIcon = patchcanvas.ICON_APPLICATION
  519. if clientIcon == PATCHBAY_ICON_PLUGIN:
  520. pcIcon = patchcanvas.ICON_PLUGIN
  521. if clientIcon == PATCHBAY_ICON_HARDWARE:
  522. pcIcon = patchcanvas.ICON_HARDWARE
  523. elif clientIcon == PATCHBAY_ICON_CARLA:
  524. pass
  525. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  526. pcIcon = patchcanvas.ICON_DISTRHO
  527. elif clientIcon == PATCHBAY_ICON_FILE:
  528. pcIcon = patchcanvas.ICON_FILE
  529. patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
  530. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  531. if pluginId < 0:
  532. return
  533. if pluginId >= self.fPluginCount:
  534. print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
  535. return
  536. patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI))
  537. @pyqtSlot(int)
  538. def slot_handlePatchbayClientRemovedCallback(self, clientId):
  539. #if not self.fEngineStarted: return
  540. patchcanvas.removeGroup(clientId)
  541. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  542. @pyqtSlot(int, str)
  543. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  544. patchcanvas.renameGroup(clientId, newClientName)
  545. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  546. @pyqtSlot(int, int, int)
  547. def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId):
  548. pcIcon = patchcanvas.ICON_APPLICATION
  549. if clientIcon == PATCHBAY_ICON_PLUGIN:
  550. pcIcon = patchcanvas.ICON_PLUGIN
  551. if clientIcon == PATCHBAY_ICON_HARDWARE:
  552. pcIcon = patchcanvas.ICON_HARDWARE
  553. elif clientIcon == PATCHBAY_ICON_CARLA:
  554. pass
  555. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  556. pcIcon = patchcanvas.ICON_DISTRHO
  557. elif clientIcon == PATCHBAY_ICON_FILE:
  558. pcIcon = patchcanvas.ICON_FILE
  559. patchcanvas.setGroupIcon(clientId, pcIcon)
  560. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  561. if pluginId < 0:
  562. return
  563. if pluginId >= self.fPluginCount:
  564. print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
  565. return
  566. patchcanvas.setGroupAsPlugin(clientId, pluginId, bool(self.host.get_plugin_info(pluginId)['hints'] & PLUGIN_HAS_CUSTOM_UI))
  567. @pyqtSlot(int, int, int, str)
  568. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  569. isAlternate = False
  570. if (portFlags & PATCHBAY_PORT_IS_INPUT):
  571. portMode = patchcanvas.PORT_MODE_INPUT
  572. else:
  573. portMode = patchcanvas.PORT_MODE_OUTPUT
  574. if (portFlags & PATCHBAY_PORT_TYPE_AUDIO):
  575. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  576. elif (portFlags & PATCHBAY_PORT_TYPE_CV):
  577. isAlternate = True
  578. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  579. elif (portFlags & PATCHBAY_PORT_TYPE_MIDI):
  580. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  581. else:
  582. portType = patchcanvas.PORT_TYPE_NULL
  583. patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate)
  584. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  585. @pyqtSlot(int, int)
  586. def slot_handlePatchbayPortRemovedCallback(self, groupId, portId):
  587. #if not self.fEngineStarted: return
  588. patchcanvas.removePort(groupId, portId)
  589. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  590. @pyqtSlot(int, int, str)
  591. def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName):
  592. patchcanvas.renamePort(groupId, portId, newPortName)
  593. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  594. @pyqtSlot(int, int, int, int, int)
  595. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId):
  596. patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId)
  597. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  598. @pyqtSlot(int, int, int)
  599. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId):
  600. #if not self.fEngineStarted: return
  601. patchcanvas.disconnectPorts(connectionId)
  602. QTimer.singleShot(0, self.fMiniCanvasPreview.update)
  603. # -----------------------------------------------------------------
  604. @pyqtSlot()
  605. def slot_canvasArrange(self):
  606. patchcanvas.arrange()
  607. @pyqtSlot()
  608. def slot_canvasShowInternal(self):
  609. self.fExternalPatchbay = False
  610. self.fParent.ui.act_canvas_show_internal.blockSignals(True)
  611. self.fParent.ui.act_canvas_show_external.blockSignals(True)
  612. self.fParent.ui.act_canvas_show_internal.setChecked(True)
  613. self.fParent.ui.act_canvas_show_external.setChecked(False)
  614. self.fParent.ui.act_canvas_show_internal.blockSignals(False)
  615. self.fParent.ui.act_canvas_show_external.blockSignals(False)
  616. self.slot_canvasRefresh()
  617. @pyqtSlot()
  618. def slot_canvasShowExternal(self):
  619. self.fExternalPatchbay = True
  620. self.fParent.ui.act_canvas_show_internal.blockSignals(True)
  621. self.fParent.ui.act_canvas_show_external.blockSignals(True)
  622. self.fParent.ui.act_canvas_show_internal.setChecked(False)
  623. self.fParent.ui.act_canvas_show_external.setChecked(True)
  624. self.fParent.ui.act_canvas_show_internal.blockSignals(False)
  625. self.fParent.ui.act_canvas_show_external.blockSignals(False)
  626. self.slot_canvasRefresh()
  627. @pyqtSlot()
  628. def slot_canvasRefresh(self):
  629. patchcanvas.clear()
  630. if self.host.is_engine_running():
  631. self.host.patchbay_refresh(self.fExternalPatchbay)
  632. for pedit in self.fPluginList:
  633. if pedit is None:
  634. break
  635. pedit.reloadAll()
  636. QTimer.singleShot(1000 if self.fParent.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY] else 0, self.fMiniCanvasPreview.update)
  637. @pyqtSlot()
  638. def slot_canvasZoomFit(self):
  639. self.scene.zoom_fit()
  640. @pyqtSlot()
  641. def slot_canvasZoomIn(self):
  642. self.scene.zoom_in()
  643. @pyqtSlot()
  644. def slot_canvasZoomOut(self):
  645. self.scene.zoom_out()
  646. @pyqtSlot()
  647. def slot_canvasZoomReset(self):
  648. self.scene.zoom_reset()
  649. @pyqtSlot()
  650. def slot_canvasPrint(self):
  651. self.scene.clearSelection()
  652. self.fExportPrinter = QPrinter()
  653. dialog = QPrintDialog(self.fExportPrinter, self)
  654. if dialog.exec_():
  655. painter = QPainter(self.fExportPrinter)
  656. painter.save()
  657. painter.setRenderHint(QPainter.Antialiasing)
  658. painter.setRenderHint(QPainter.TextAntialiasing)
  659. self.scene.render(painter)
  660. painter.restore()
  661. @pyqtSlot()
  662. def slot_canvasSaveImage(self):
  663. newPath = QFileDialog.getSaveFileName(self, self.tr("Save Image"), filter=self.tr("PNG Image (*.png);;JPEG Image (*.jpg)"))
  664. if config_UseQt5:
  665. newPath = newPath[0]
  666. if not newPath:
  667. return
  668. self.scene.clearSelection()
  669. if newPath.lower().endswith((".jpg",)):
  670. imgFormat = "JPG"
  671. elif newPath.lower().endswith((".png",)):
  672. imgFormat = "PNG"
  673. else:
  674. # File-dialog may not auto-add the extension
  675. imgFormat = "PNG"
  676. newPath += ".png"
  677. self.fExportImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_RGB32)
  678. painter = QPainter(self.fExportImage)
  679. painter.save()
  680. painter.setRenderHint(QPainter.Antialiasing) # TODO - set true, cleanup this
  681. painter.setRenderHint(QPainter.TextAntialiasing)
  682. self.scene.render(painter)
  683. self.fExportImage.save(newPath, imgFormat, 100)
  684. painter.restore()
  685. # -----------------------------------------------------------------
  686. def resizeEvent(self, event):
  687. QFrame.resizeEvent(self, event)
  688. self.slot_miniCanvasCheckSize()
  689. # ------------------------------------------------------------------------------------------------
  690. # Canvas callback
  691. def canvasCallback(action, value1, value2, valueStr):
  692. host = gCarla.gui.host
  693. if action == patchcanvas.ACTION_GROUP_INFO:
  694. pass
  695. elif action == patchcanvas.ACTION_GROUP_RENAME:
  696. pass
  697. elif action == patchcanvas.ACTION_GROUP_SPLIT:
  698. groupId = value1
  699. patchcanvas.splitGroup(groupId)
  700. gCarla.gui.ui.miniCanvasPreview.update()
  701. elif action == patchcanvas.ACTION_GROUP_JOIN:
  702. groupId = value1
  703. patchcanvas.joinGroup(groupId)
  704. gCarla.gui.ui.miniCanvasPreview.update()
  705. elif action == patchcanvas.ACTION_PORT_INFO:
  706. pass
  707. elif action == patchcanvas.ACTION_PORT_RENAME:
  708. pass
  709. elif action == patchcanvas.ACTION_PORTS_CONNECT:
  710. gOut, pOut, gIn, pIn = [int(i) for i in valueStr.split(":")]
  711. if not host.patchbay_connect(gOut, pOut, gIn, pIn):
  712. print("Connection failed:", host.get_last_error())
  713. elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
  714. connectionId = value1
  715. if not host.patchbay_disconnect(connectionId):
  716. print("Disconnect failed:", host.get_last_error())
  717. elif action == patchcanvas.ACTION_PLUGIN_CLONE:
  718. pluginId = value1
  719. host.clone_plugin(pluginId)
  720. elif action == patchcanvas.ACTION_PLUGIN_EDIT:
  721. pluginId = value1
  722. gCarla.gui.fContainer.showEditDialog(pluginId)
  723. elif action == patchcanvas.ACTION_PLUGIN_RENAME:
  724. pluginId = value1
  725. newName = valueStr
  726. host.rename_plugin(pluginId, newName)
  727. elif action == patchcanvas.ACTION_PLUGIN_REMOVE:
  728. pluginId = value1
  729. host.remove_plugin(pluginId)
  730. elif action == patchcanvas.ACTION_PLUGIN_SHOW_UI:
  731. pluginId = value1
  732. host.show_custom_ui(pluginId, True)