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.

carla_patchbay.py 35KB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964
  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)