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_rack.py 19KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla rack 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 (Global)
  19. from PyQt4.QtCore import Qt, QSize, QTimer
  20. from PyQt4.QtGui import QApplication, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QPixmap, QScrollBar
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom Stuff)
  23. from carla_skin import *
  24. # ------------------------------------------------------------------------------------------------------------
  25. # Rack widget item
  26. class CarlaRackItem(QListWidgetItem):
  27. kRackItemType = QListWidgetItem.UserType + 1
  28. def __init__(self, parent, pluginId):
  29. QListWidgetItem.__init__(self, parent, self.kRackItemType)
  30. self.widget = createPluginSlot(parent, pluginId)
  31. self.widget.setFixedHeight(self.widget.getFixedHeight())
  32. self.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled) # Qt.ItemIsDragEnabled|Qt.ItemIsDropEnabled
  33. self.setSizeHint(QSize(300, self.widget.getFixedHeight()))
  34. parent.setItemWidget(self, self.widget)
  35. # -----------------------------------------------------------------
  36. def close(self):
  37. self.widget.fEditDialog.close()
  38. #def setId(self, idx):
  39. #self.widget.setId(idx)
  40. #def setName(self, newName):
  41. #self.widget.ui.label_name.setText(newName)
  42. #self.widget.ui.edit_dialog.setName(newName)
  43. # -----------------------------------------------------------------
  44. #def paintEvent(self, event):
  45. #painter = QPainter(self)
  46. #painter.save()
  47. #painter.setPen(QPen(Qt.black, 3))
  48. #painter.setBrush(Qt.black)
  49. #painter.drawRect(0, 0, self.width(), self.height())
  50. #painter.drawLine(0, self.height()-4, self.width(), self.height()-4)
  51. #painter.restore()
  52. #QListWidgetItem.paintEvent(self, event)
  53. # ------------------------------------------------------------------------------------------------------------
  54. # Rack widget list
  55. class CarlaRackList(QListWidget):
  56. def __init__(self, parent):
  57. QListWidget.__init__(self, parent)
  58. self.fPixmapL = QPixmap(":/bitmaps/rack_interior_left.png")
  59. self.fPixmapR = QPixmap(":/bitmaps/rack_interior_right.png")
  60. self.fPixmapWidth = self.fPixmapL.width()
  61. def paintEvent(self, event):
  62. painter = QPainter(self.viewport())
  63. painter.drawTiledPixmap(0, 0, self.fPixmapWidth, self.height(), self.fPixmapL)
  64. painter.drawTiledPixmap(self.width()-self.fPixmapWidth-2, 0, self.fPixmapWidth, self.height(), self.fPixmapR)
  65. QListWidget.paintEvent(self, event)
  66. # ------------------------------------------------------------------------------------------------------------
  67. # Rack widget
  68. class CarlaRackW(QFrame):
  69. def __init__(self, parent, doSetup = True):
  70. QFrame.__init__(self, parent)
  71. self.fLayout = QHBoxLayout(self)
  72. self.fLayout.setContentsMargins(0, 0, 0, 0)
  73. self.fLayout.setSpacing(0)
  74. self.setLayout(self.fLayout)
  75. self.fPadLeft = QLabel(self)
  76. self.fPadLeft.setFixedWidth(25)
  77. self.fPadLeft.setObjectName("PadLeft")
  78. self.fPadLeft.setText("")
  79. self.fPadRight = QLabel(self)
  80. self.fPadRight.setFixedWidth(25)
  81. self.fPadRight.setObjectName("PadRight")
  82. self.fPadRight.setText("")
  83. self.fRack = CarlaRackList(self)
  84. self.fRack.setMinimumWidth(640+20) # required by zita, 591 was old value
  85. self.fRack.setSortingEnabled(False)
  86. self.fRack.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
  87. self.fRack.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
  88. self.fRack.currentRowChanged.connect(self.slot_currentRowChanged)
  89. sb = self.fRack.verticalScrollBar()
  90. self.fScrollBar = QScrollBar(Qt.Vertical, self)
  91. self.fScrollBar.setMinimum(sb.minimum())
  92. self.fScrollBar.setMaximum(sb.maximum())
  93. self.fScrollBar.setValue(sb.value())
  94. #sb.actionTriggered.connect(self.fScrollBar.triggerAction)
  95. #sb.sliderMoved.connect(self.fScrollBar.)
  96. #sb.sliderPressed.connect(self.fScrollBar.)
  97. #sb.sliderReleased.connect(self.fScrollBar.)
  98. sb.rangeChanged.connect(self.fScrollBar.setRange)
  99. sb.valueChanged.connect(self.fScrollBar.setValue)
  100. self.fScrollBar.rangeChanged.connect(sb.setRange)
  101. self.fScrollBar.valueChanged.connect(sb.setValue)
  102. self.fLayout.addWidget(self.fPadLeft)
  103. self.fLayout.addWidget(self.fRack)
  104. self.fLayout.addWidget(self.fPadRight)
  105. self.fLayout.addWidget(self.fScrollBar)
  106. # -------------------------------------------------------------
  107. # Internal stuff
  108. self.fParent = parent
  109. self.fPluginCount = 0
  110. self.fPluginList = []
  111. self.fCurrentRow = -1
  112. self.fLastSelectedItem = None
  113. # -------------------------------------------------------------
  114. # Set-up GUI stuff
  115. #app = QApplication.instance()
  116. #pal1 = app.palette().base().color()
  117. #pal2 = app.palette().button().color()
  118. #col1 = "stop:0 rgb(%i, %i, %i)" % (pal1.red(), pal1.green(), pal1.blue())
  119. #col2 = "stop:1 rgb(%i, %i, %i)" % (pal2.red(), pal2.green(), pal2.blue())
  120. self.setStyleSheet("""
  121. QLabel#PadLeft {
  122. background-image: url(:/bitmaps/rack_padding_left.png);
  123. background-repeat: repeat-y;
  124. }
  125. QLabel#PadRight {
  126. background-image: url(:/bitmaps/rack_padding_right.png);
  127. background-repeat: repeat-y;
  128. }
  129. QListWidget {
  130. background-color: black;
  131. }
  132. """)
  133. # -------------------------------------------------------------
  134. # Connect actions to functions
  135. if not doSetup: return
  136. parent.ui.menu_Canvas.hide()
  137. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  138. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  139. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  140. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  141. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  142. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  143. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  144. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  145. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  146. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  147. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  148. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  149. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  150. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  151. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  152. parent.UiStateChangedCallback.connect(self.slot_handleUiStateChangedCallback)
  153. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  154. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  155. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  156. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  157. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  158. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  159. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  160. # -----------------------------------------------------------------
  161. def getPluginCount(self):
  162. return self.fPluginCount
  163. # -----------------------------------------------------------------
  164. def addPlugin(self, pluginId, isProjectLoading):
  165. pitem = CarlaRackItem(self.fRack, pluginId)
  166. self.fPluginList.append(pitem)
  167. self.fPluginCount += 1
  168. if not isProjectLoading:
  169. pitem.widget.setActive(True, True, True)
  170. def removePlugin(self, pluginId):
  171. if pluginId >= self.fPluginCount:
  172. return
  173. pitem = self.fPluginList[pluginId]
  174. if pitem is None:
  175. return
  176. self.fPluginCount -= 1
  177. self.fPluginList.pop(pluginId)
  178. self.fRack.takeItem(pluginId)
  179. pitem.close()
  180. del pitem
  181. # push all plugins 1 slot back
  182. for i in range(pluginId, self.fPluginCount):
  183. pitem = self.fPluginList[i]
  184. pitem.widget.setId(i)
  185. def renamePlugin(self, pluginId, newName):
  186. if pluginId >= self.fPluginCount:
  187. return
  188. pitem = self.fPluginList[pluginId]
  189. if pitem is None:
  190. return
  191. pitem.widget.setName(newName)
  192. def disablePlugin(self, pluginId, errorMsg):
  193. if pluginId >= self.fPluginCount:
  194. return
  195. pitem = self.fPluginList[pluginId]
  196. if pitem is None:
  197. return
  198. def removeAllPlugins(self):
  199. while self.fRack.takeItem(0):
  200. pass
  201. for i in range(self.fPluginCount):
  202. pitem = self.fPluginList[i]
  203. if pitem is None:
  204. break
  205. pitem.close()
  206. del pitem
  207. self.fPluginCount = 0
  208. self.fPluginList = []
  209. # -----------------------------------------------------------------
  210. def engineStarted(self):
  211. pass
  212. def engineStopped(self):
  213. pass
  214. def engineChanged(self):
  215. pass
  216. # -----------------------------------------------------------------
  217. def idleFast(self):
  218. for i in range(self.fPluginCount):
  219. pitem = self.fPluginList[i]
  220. if pitem is None:
  221. break
  222. pitem.widget.idleFast()
  223. def idleSlow(self):
  224. for i in range(self.fPluginCount):
  225. pitem = self.fPluginList[i]
  226. if pitem is None:
  227. break
  228. pitem.widget.idleSlow()
  229. # -----------------------------------------------------------------
  230. def projectLoaded(self):
  231. pass
  232. def saveSettings(self, settings):
  233. pass
  234. def showEditDialog(self, pluginId):
  235. if pluginId >= self.fPluginCount:
  236. return
  237. pitem = self.fPluginList[pluginId]
  238. if pitem is None:
  239. return
  240. pitem.widget.slot_showEditDialog(True)
  241. # -----------------------------------------------------------------
  242. @pyqtSlot()
  243. def slot_pluginsEnable(self):
  244. if not gCarla.host.is_engine_running():
  245. return
  246. for i in range(self.fPluginCount):
  247. pitem = self.fPluginList[i]
  248. if pitem is None:
  249. break
  250. pitem.widget.setActive(True, True, True)
  251. @pyqtSlot()
  252. def slot_pluginsDisable(self):
  253. if not gCarla.host.is_engine_running():
  254. return
  255. for i in range(self.fPluginCount):
  256. pitem = self.fPluginList[i]
  257. if pitem is None:
  258. break
  259. pitem.widget.setActive(False, True, True)
  260. @pyqtSlot()
  261. def slot_pluginsVolume100(self):
  262. if not gCarla.host.is_engine_running():
  263. return
  264. for i in range(self.fPluginCount):
  265. pitem = self.fPluginList[i]
  266. if pitem is None:
  267. break
  268. pitem.widget.setInternalParameter(PLUGIN_CAN_VOLUME, 1.0)
  269. @pyqtSlot()
  270. def slot_pluginsMute(self):
  271. if not gCarla.host.is_engine_running():
  272. return
  273. for i in range(self.fPluginCount):
  274. pitem = self.fPluginList[i]
  275. if pitem is None:
  276. break
  277. pitem.widget.setInternalParameter(PLUGIN_CAN_VOLUME, 0.0)
  278. @pyqtSlot()
  279. def slot_pluginsWet100(self):
  280. if not gCarla.host.is_engine_running():
  281. return
  282. for i in range(self.fPluginCount):
  283. pitem = self.fPluginList[i]
  284. if pitem is None:
  285. break
  286. pitem.widget.setInternalParameter(PLUGIN_CAN_DRYWET, 1.0)
  287. @pyqtSlot()
  288. def slot_pluginsBypass(self):
  289. if not gCarla.host.is_engine_running():
  290. return
  291. for i in range(self.fPluginCount):
  292. pitem = self.fPluginList[i]
  293. if pitem is None:
  294. break
  295. pitem.widget.setInternalParameter(PLUGIN_CAN_DRYWET, 0.0)
  296. @pyqtSlot()
  297. def slot_pluginsCenter(self):
  298. if not gCarla.host.is_engine_running():
  299. return
  300. for i in range(self.fPluginCount):
  301. pitem = self.fPluginList[i]
  302. if pitem is None:
  303. break
  304. pitem.widget.setInternalParameter(PARAMETER_BALANCE_LEFT, -1.0)
  305. pitem.widget.setInternalParameter(PARAMETER_BALANCE_RIGHT, 1.0)
  306. pitem.widget.setInternalParameter(PARAMETER_PANNING, 0.0)
  307. # -----------------------------------------------------------------
  308. @pyqtSlot()
  309. def slot_configureCarla(self):
  310. if self.fParent is None or not self.fParent.openSettingsWindow(False, False):
  311. return
  312. self.fParent.loadSettings(False)
  313. # -----------------------------------------------------------------
  314. @pyqtSlot(int, int, float)
  315. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  316. if pluginId >= self.fPluginCount:
  317. return
  318. pitem = self.fPluginList[pluginId]
  319. if pitem is None:
  320. return
  321. pitem.widget.setParameterValue(index, value, True)
  322. @pyqtSlot(int, int, float)
  323. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  324. if pluginId >= self.fPluginCount:
  325. return
  326. pitem = self.fPluginList[pluginId]
  327. if pitem is None:
  328. return
  329. pitem.widget.setParameterDefault(index, value)
  330. @pyqtSlot(int, int, int)
  331. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  332. if pluginId >= self.fPluginCount:
  333. return
  334. pitem = self.fPluginList[pluginId]
  335. if pitem is None:
  336. return
  337. pitem.widget.setParameterMidiControl(index, cc)
  338. @pyqtSlot(int, int, int)
  339. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  340. if pluginId >= self.fPluginCount:
  341. return
  342. pitem = self.fPluginList[pluginId]
  343. if pitem is None:
  344. return
  345. pitem.widget.setParameterMidiChannel(index, channel)
  346. # -----------------------------------------------------------------
  347. @pyqtSlot(int, int)
  348. def slot_handleProgramChangedCallback(self, pluginId, index):
  349. if pluginId >= self.fPluginCount:
  350. return
  351. pitem = self.fPluginList[pluginId]
  352. if pitem is None:
  353. return
  354. pitem.widget.setProgram(index, True)
  355. @pyqtSlot(int, int)
  356. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  357. if pluginId >= self.fPluginCount:
  358. return
  359. pitem = self.fPluginList[pluginId]
  360. if pitem is None:
  361. return
  362. pitem.widget.setMidiProgram(index, True)
  363. # -----------------------------------------------------------------
  364. @pyqtSlot(int, int)
  365. def slot_handleUiStateChangedCallback(self, pluginId, state):
  366. if pluginId >= self.fPluginCount:
  367. return
  368. pitem = self.fPluginList[pluginId]
  369. if pitem is None:
  370. return
  371. pitem.widget.customUiStateChanged(state)
  372. # -----------------------------------------------------------------
  373. @pyqtSlot(int, int, int, int)
  374. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  375. if pluginId >= self.fPluginCount:
  376. return
  377. pitem = self.fPluginList[pluginId]
  378. if pitem is None:
  379. return
  380. pitem.widget.sendNoteOn(channel, note)
  381. @pyqtSlot(int, int, int)
  382. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  383. if pluginId >= self.fPluginCount:
  384. return
  385. pitem = self.fPluginList[pluginId]
  386. if pitem is None:
  387. return
  388. pitem.widget.sendNoteOff(channel, note)
  389. # -----------------------------------------------------------------
  390. @pyqtSlot(int)
  391. def slot_handleUpdateCallback(self, pluginId):
  392. if pluginId >= self.fPluginCount:
  393. return
  394. pitem = self.fPluginList[pluginId]
  395. if pitem is None:
  396. return
  397. pitem.widget.fEditDialog.updateInfo()
  398. @pyqtSlot(int)
  399. def slot_handleReloadInfoCallback(self, pluginId):
  400. if pluginId >= self.fPluginCount:
  401. return
  402. pitem = self.fPluginList[pluginId]
  403. if pitem is None:
  404. return
  405. pitem.widget.fEditDialog.reloadInfo()
  406. @pyqtSlot(int)
  407. def slot_handleReloadParametersCallback(self, pluginId):
  408. if pluginId >= self.fPluginCount:
  409. return
  410. pitem = self.fPluginList[pluginId]
  411. if pitem is None:
  412. return
  413. pitem.widget.fEditDialog.reloadParameters()
  414. @pyqtSlot(int)
  415. def slot_handleReloadProgramsCallback(self, pluginId):
  416. if pluginId >= self.fPluginCount:
  417. return
  418. pitem = self.fPluginList[pluginId]
  419. if pitem is None:
  420. return
  421. pitem.widget.fEditDialog.reloadPrograms()
  422. @pyqtSlot(int)
  423. def slot_handleReloadAllCallback(self, pluginId):
  424. if pluginId >= self.fPluginCount:
  425. return
  426. pitem = self.fPluginList[pluginId]
  427. if pitem is None:
  428. return
  429. pitem.widget.fEditDialog.reloadAll()
  430. # -----------------------------------------------------------------
  431. def slot_currentRowChanged(self, row):
  432. self.fCurrentRow = row
  433. if self.fLastSelectedItem is not None:
  434. self.fLastSelectedItem.setSelected(False)
  435. if row < 0 or row >= self.fPluginCount or self.fPluginList[row] is None:
  436. self.fLastSelectedItem = None
  437. return
  438. pitem = self.fPluginList[row]
  439. pitem.widget.setSelected(True)
  440. self.fLastSelectedItem = pitem.widget
  441. # -----------------------------------------------------------------