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 16KB

11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla rack widget code
  4. # Copyright (C) 2011-2013 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. try:
  20. from PyQt5.QtCore import QSize, QTimer
  21. from PyQt5.QtWidgets import QApplication, QListWidget, QListWidgetItem
  22. except:
  23. from PyQt4.QtCore import QSize, QTimer
  24. from PyQt4.QtGui import QApplication, QListWidget, QListWidgetItem
  25. # ------------------------------------------------------------------------------------------------------------
  26. # Imports (Custom Stuff)
  27. from carla_widgets import *
  28. # ------------------------------------------------------------------------------------------------------------
  29. # Rack widget item
  30. class CarlaRackItem(QListWidgetItem):
  31. RackItemType = QListWidgetItem.UserType + 1
  32. StaticHeight = 32
  33. def __init__(self, parent, pluginId):
  34. QListWidgetItem.__init__(self, parent, self.RackItemType)
  35. self.fWidget = PluginWidget(parent, pluginId)
  36. self.fWidget.setFixedHeight(self.StaticHeight)
  37. self.setSizeHint(QSize(300, self.StaticHeight))
  38. parent.setItemWidget(self, self.fWidget)
  39. # -----------------------------------------------------------------
  40. def close(self):
  41. self.fWidget.ui.edit_dialog.close()
  42. def setId(self, idx):
  43. self.fWidget.setId(idx)
  44. # ------------------------------------------------------------------------------------------------------------
  45. # Rack widget
  46. class CarlaRackW(QListWidget):
  47. def __init__(self, parent, doSetup = True):
  48. QListWidget.__init__(self, parent)
  49. # -------------------------------------------------------------
  50. # Internal stuff
  51. self.fParent = parent
  52. self.fPluginCount = 0
  53. self.fPluginList = []
  54. # -------------------------------------------------------------
  55. # Set-up GUI stuff
  56. #self.setMnimumWidth(800)
  57. self.setSortingEnabled(False)
  58. #app = QApplication.instance()
  59. #pal1 = app.palette().base().color()
  60. #pal2 = app.palette().button().color()
  61. #col1 = "stop:0 rgb(%i, %i, %i)" % (pal1.red(), pal1.green(), pal1.blue())
  62. #col2 = "stop:1 rgb(%i, %i, %i)" % (pal2.red(), pal2.green(), pal2.blue())
  63. #self.setStyleSheet("""
  64. #QListWidget {
  65. #background-color: qlineargradient(spread:pad,
  66. #x1:0.0, y1:0.0,
  67. #x2:0.2, y2:1.0,
  68. #%s,
  69. #%s
  70. #);
  71. #}
  72. #""" % (col1, col2))
  73. # -------------------------------------------------------------
  74. # Connect actions to functions
  75. if not doSetup: return
  76. parent.ui.menu_Canvas.hide()
  77. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  78. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  79. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  80. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  81. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  82. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  83. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  84. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  85. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  86. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  87. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  88. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  89. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  90. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  91. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  92. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  93. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  94. parent.ShowGuiCallback.connect(self.slot_handleShowGuiCallback)
  95. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  96. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  97. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  98. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  99. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  100. # -----------------------------------------------------------------
  101. def getPluginCount(self):
  102. return self.fPluginCount
  103. # -----------------------------------------------------------------
  104. def addPlugin(self, pluginId, isProjectLoading):
  105. pitem = CarlaRackItem(self, pluginId)
  106. self.fPluginList.append(pitem)
  107. self.fPluginCount += 1
  108. if not isProjectLoading:
  109. pitem.fWidget.setActive(True, True, True)
  110. def removePlugin(self, pluginId):
  111. if pluginId >= self.fPluginCount:
  112. return
  113. pitem = self.fPluginList[pluginId]
  114. if pitem is None:
  115. return
  116. self.fPluginCount -= 1
  117. self.fPluginList.pop(pluginId)
  118. self.takeItem(pluginId)
  119. pitem.close()
  120. del pitem
  121. # push all plugins 1 slot back
  122. for i in range(pluginId, self.fPluginCount):
  123. self.fPluginList[i].setId(i)
  124. def renamePlugin(self, pluginId, newName):
  125. if pluginId >= self.fPluginCount:
  126. return
  127. pitem = self.fPluginList[pluginId]
  128. if pitem is None:
  129. return
  130. pitem.fWidget.ui.label_name.setText(newName)
  131. pitem.fWidget.ui.edit_dialog.setName(newName)
  132. def removeAllPlugins(self):
  133. while (self.takeItem(0)):
  134. pass
  135. for i in range(self.fPluginCount):
  136. pitem = self.fPluginList[i]
  137. if pitem is None:
  138. break
  139. pitem.close()
  140. del pitem
  141. self.fPluginCount = 0
  142. self.fPluginList = []
  143. # -----------------------------------------------------------------
  144. def engineStarted(self):
  145. pass
  146. def engineStopped(self):
  147. pass
  148. def engineChanged(self):
  149. pass
  150. # -----------------------------------------------------------------
  151. def idleFast(self):
  152. for i in range(self.fPluginCount):
  153. pitem = self.fPluginList[i]
  154. if pitem is None:
  155. break
  156. pitem.fWidget.idleFast()
  157. def idleSlow(self):
  158. for i in range(self.fPluginCount):
  159. pitem = self.fPluginList[i]
  160. if pitem is None:
  161. break
  162. pitem.fWidget.idleSlow()
  163. # -----------------------------------------------------------------
  164. def saveSettings(self, settings):
  165. pass
  166. # -----------------------------------------------------------------
  167. @pyqtSlot()
  168. def slot_pluginsEnable(self):
  169. if not Carla.host.is_engine_running():
  170. return
  171. for i in range(self.fPluginCount):
  172. pitem = self.fPluginList[i]
  173. if pitem is None:
  174. break
  175. pitem.fWidget.setActive(True, True, True)
  176. @pyqtSlot()
  177. def slot_pluginsDisable(self):
  178. if not Carla.host.is_engine_running():
  179. return
  180. for i in range(self.fPluginCount):
  181. pitem = self.fPluginList[i]
  182. if pitem is None:
  183. break
  184. pitem.fWidget.setActive(False, True, True)
  185. @pyqtSlot()
  186. def slot_pluginsVolume100(self):
  187. if not Carla.host.is_engine_running():
  188. return
  189. for i in range(self.fPluginCount):
  190. pitem = self.fPluginList[i]
  191. if pitem is None:
  192. break
  193. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  194. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 1.0)
  195. Carla.host.set_volume(i, 1.0)
  196. @pyqtSlot()
  197. def slot_pluginsMute(self):
  198. if not Carla.host.is_engine_running():
  199. return
  200. for i in range(self.fPluginCount):
  201. pitem = self.fPluginList[i]
  202. if pitem is None:
  203. break
  204. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  205. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 0.0)
  206. Carla.host.set_volume(i, 0.0)
  207. @pyqtSlot()
  208. def slot_pluginsWet100(self):
  209. if not Carla.host.is_engine_running():
  210. return
  211. for i in range(self.fPluginCount):
  212. pitem = self.fPluginList[i]
  213. if pitem is None:
  214. break
  215. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  216. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 1.0)
  217. Carla.host.set_drywet(i, 1.0)
  218. @pyqtSlot()
  219. def slot_pluginsBypass(self):
  220. if not Carla.host.is_engine_running():
  221. return
  222. for i in range(self.fPluginCount):
  223. pitem = self.fPluginList[i]
  224. if pitem is None:
  225. break
  226. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  227. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 0.0)
  228. Carla.host.set_drywet(i, 0.0)
  229. @pyqtSlot()
  230. def slot_pluginsCenter(self):
  231. if not Carla.host.is_engine_running():
  232. return
  233. for i in range(self.fPluginCount):
  234. pitem = self.fPluginList[i]
  235. if pitem is None:
  236. break
  237. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  238. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  239. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  240. Carla.host.set_balance_left(i, -1.0)
  241. Carla.host.set_balance_right(i, 1.0)
  242. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_PANNING:
  243. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_PANNING, 1.0)
  244. Carla.host.set_panning(i, 1.0)
  245. # -----------------------------------------------------------------
  246. @pyqtSlot()
  247. def slot_configureCarla(self):
  248. if self.fParent is None or not self.fParent.openSettingsWindow(False, False):
  249. return
  250. self.loadSettings(False)
  251. # -----------------------------------------------------------------
  252. @pyqtSlot(int, int, float)
  253. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  254. if pluginId >= self.fPluginCount:
  255. return
  256. pitem = self.fPluginList[pluginId]
  257. if pitem is None:
  258. return
  259. pitem.fWidget.setParameterValue(index, value)
  260. @pyqtSlot(int, int, float)
  261. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  262. if pluginId >= self.fPluginCount:
  263. return
  264. pitem = self.fPluginList[pluginId]
  265. if pitem is None:
  266. return
  267. pitem.fWidget.setParameterDefault(index, value)
  268. @pyqtSlot(int, int, int)
  269. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  270. if pluginId >= self.fPluginCount:
  271. return
  272. pitem = self.fPluginList[pluginId]
  273. if pitem is None:
  274. return
  275. pitem.fWidget.setParameterMidiChannel(index, channel)
  276. @pyqtSlot(int, int, int)
  277. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  278. if pluginId >= self.fPluginCount:
  279. return
  280. pitem = self.fPluginList[pluginId]
  281. if pitem is None:
  282. return
  283. pitem.fWidget.setParameterMidiControl(index, cc)
  284. # -----------------------------------------------------------------
  285. @pyqtSlot(int, int)
  286. def slot_handleProgramChangedCallback(self, pluginId, index):
  287. if pluginId >= self.fPluginCount:
  288. return
  289. pitem = self.fPluginList[pluginId]
  290. if pitem is None:
  291. return
  292. pitem.fWidget.setProgram(index)
  293. @pyqtSlot(int, int)
  294. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  295. if pluginId >= self.fPluginCount:
  296. return
  297. pitem = self.fPluginList[pluginId]
  298. if pitem is None:
  299. return
  300. pitem.fWidget.setMidiProgram(index)
  301. # -----------------------------------------------------------------
  302. @pyqtSlot(int, int, int, int)
  303. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  304. if pluginId >= self.fPluginCount:
  305. return
  306. pitem = self.fPluginList[pluginId]
  307. if pitem is None:
  308. return
  309. pitem.fWidget.sendNoteOn(channel, note)
  310. @pyqtSlot(int, int, int)
  311. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  312. if pluginId >= self.fPluginCount:
  313. return
  314. pitem = self.fPluginList[pluginId]
  315. if pitem is None:
  316. return
  317. pitem.fWidget.sendNoteOff(channel, note)
  318. # -----------------------------------------------------------------
  319. @pyqtSlot(int, int)
  320. def slot_handleShowGuiCallback(self, pluginId, state):
  321. if pluginId >= self.fPluginCount:
  322. return
  323. pitem = self.fPluginList[pluginId]
  324. if pitem is None:
  325. return
  326. if state == 0:
  327. pitem.fWidget.ui.b_gui.setChecked(False)
  328. pitem.fWidget.ui.b_gui.setEnabled(True)
  329. elif state == 1:
  330. pitem.fWidget.ui.b_gui.setChecked(True)
  331. pitem.fWidget.ui.b_gui.setEnabled(True)
  332. elif state == -1:
  333. pitem.fWidget.ui.b_gui.setChecked(False)
  334. pitem.fWidget.ui.b_gui.setEnabled(False)
  335. # -----------------------------------------------------------------
  336. @pyqtSlot(int)
  337. def slot_handleUpdateCallback(self, pluginId):
  338. if pluginId >= self.fPluginCount:
  339. return
  340. pitem = self.fPluginList[pluginId]
  341. if pitem is None:
  342. return
  343. pitem.ui.edit_dialog.updateInfo()
  344. @pyqtSlot(int)
  345. def slot_handleReloadInfoCallback(self, pluginId):
  346. if pluginId >= self.fPluginCount:
  347. return
  348. pitem = self.fPluginList[pluginId]
  349. if pitem is None:
  350. return
  351. pitem.ui.edit_dialog.reloadInfo()
  352. @pyqtSlot(int)
  353. def slot_handleReloadParametersCallback(self, pluginId):
  354. if pluginId >= self.fPluginCount:
  355. return
  356. pitem = self.fPluginList[pluginId]
  357. if pitem is None:
  358. return
  359. pitem.ui.edit_dialog.reloadParameters()
  360. @pyqtSlot(int)
  361. def slot_handleReloadProgramsCallback(self, pluginId):
  362. if pluginId >= self.fPluginCount:
  363. return
  364. pitem = self.fPluginList[pluginId]
  365. if pitem is None:
  366. return
  367. pitem.ui.edit_dialog.reloadPrograms()
  368. @pyqtSlot(int)
  369. def slot_handleReloadAllCallback(self, pluginId):
  370. if pluginId >= self.fPluginCount:
  371. return
  372. pitem = self.fPluginList[pluginId]
  373. if pitem is None:
  374. return
  375. pitem.ui.edit_dialog.reloadAll()
  376. # ------------------------------------------------------------------------------------------------------------
  377. # TESTING
  378. #from PyQt5.QtWidgets import QApplication
  379. #app = QApplication(sys.argv)
  380. #gui = CarlaRackW(None)
  381. #gui.show()
  382. #app.exec_()