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.

512 lines
16KB

  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):
  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 parent is None:
  76. return
  77. parent.ui.menu_Canvas.hide()
  78. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  79. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  80. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  81. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  82. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  83. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  84. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  85. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  86. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  87. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  88. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  89. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  90. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  91. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  92. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  93. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  94. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  95. parent.ShowGuiCallback.connect(self.slot_handleShowGuiCallback)
  96. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  97. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  98. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  99. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  100. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  101. # -----------------------------------------------------------------
  102. def getPluginCount(self):
  103. return self.fPluginCount
  104. # -----------------------------------------------------------------
  105. def addPlugin(self, pluginId, isProjectLoading):
  106. pitem = CarlaRackItem(self, pluginId)
  107. self.fPluginList.append(pitem)
  108. self.fPluginCount += 1
  109. if not isProjectLoading:
  110. pitem.fWidget.setActive(True, True, True)
  111. def removePlugin(self, pluginId):
  112. if pluginId >= self.fPluginCount:
  113. return
  114. pitem = self.fPluginList[pluginId]
  115. if pitem is None:
  116. return
  117. self.fPluginCount -= 1
  118. self.fPluginList.pop(pluginId)
  119. self.takeItem(pluginId)
  120. pitem.close()
  121. del pitem
  122. # push all plugins 1 slot back
  123. for i in range(pluginId, self.fPluginCount):
  124. self.fPluginList[i].setId(i)
  125. def renamePlugin(self, pluginId, newName):
  126. if pluginId >= self.fPluginCount:
  127. return
  128. pitem = self.fPluginList[pluginId]
  129. if pitem is None:
  130. return
  131. pitem.fWidget.ui.label_name.setText(newName)
  132. pitem.fWidget.ui.edit_dialog.setName(newName)
  133. def removeAllPlugins(self):
  134. while (self.takeItem(0)):
  135. pass
  136. for i in range(self.fPluginCount):
  137. pitem = self.fPluginList[i]
  138. if pitem is None:
  139. break
  140. pitem.close()
  141. del pitem
  142. self.fPluginCount = 0
  143. self.fPluginList = []
  144. # -----------------------------------------------------------------
  145. def engineStarted(self):
  146. pass
  147. def engineStopped(self):
  148. pass
  149. # -----------------------------------------------------------------
  150. def idleFast(self):
  151. for i in range(self.fPluginCount):
  152. pitem = self.fPluginList[i]
  153. if pitem is None:
  154. break
  155. pitem.fWidget.idleFast()
  156. def idleSlow(self):
  157. for i in range(self.fPluginCount):
  158. pitem = self.fPluginList[i]
  159. if pitem is None:
  160. break
  161. pitem.fWidget.idleSlow()
  162. # -----------------------------------------------------------------
  163. @pyqtSlot()
  164. def slot_pluginsEnable(self):
  165. if not Carla.host.is_engine_running():
  166. return
  167. for i in range(self.fPluginCount):
  168. pitem = self.fPluginList[i]
  169. if pitem is None:
  170. break
  171. pitem.fWidget.setActive(True, True, True)
  172. @pyqtSlot()
  173. def slot_pluginsDisable(self):
  174. if not Carla.host.is_engine_running():
  175. return
  176. for i in range(self.fPluginCount):
  177. pitem = self.fPluginList[i]
  178. if pitem is None:
  179. break
  180. pitem.fWidget.setActive(False, True, True)
  181. @pyqtSlot()
  182. def slot_pluginsVolume100(self):
  183. if not Carla.host.is_engine_running():
  184. return
  185. for i in range(self.fPluginCount):
  186. pitem = self.fPluginList[i]
  187. if pitem is None:
  188. break
  189. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  190. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 1.0)
  191. Carla.host.set_volume(i, 1.0)
  192. @pyqtSlot()
  193. def slot_pluginsMute(self):
  194. if not Carla.host.is_engine_running():
  195. return
  196. for i in range(self.fPluginCount):
  197. pitem = self.fPluginList[i]
  198. if pitem is None:
  199. break
  200. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  201. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 0.0)
  202. Carla.host.set_volume(i, 0.0)
  203. @pyqtSlot()
  204. def slot_pluginsWet100(self):
  205. if not Carla.host.is_engine_running():
  206. return
  207. for i in range(self.fPluginCount):
  208. pitem = self.fPluginList[i]
  209. if pitem is None:
  210. break
  211. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  212. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 1.0)
  213. Carla.host.set_drywet(i, 1.0)
  214. @pyqtSlot()
  215. def slot_pluginsBypass(self):
  216. if not Carla.host.is_engine_running():
  217. return
  218. for i in range(self.fPluginCount):
  219. pitem = self.fPluginList[i]
  220. if pitem is None:
  221. break
  222. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  223. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 0.0)
  224. Carla.host.set_drywet(i, 0.0)
  225. @pyqtSlot()
  226. def slot_pluginsCenter(self):
  227. if not Carla.host.is_engine_running():
  228. return
  229. for i in range(self.fPluginCount):
  230. pitem = self.fPluginList[i]
  231. if pitem is None:
  232. break
  233. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  234. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  235. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  236. Carla.host.set_balance_left(i, -1.0)
  237. Carla.host.set_balance_right(i, 1.0)
  238. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_PANNING:
  239. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_PANNING, 1.0)
  240. Carla.host.set_panning(i, 1.0)
  241. # -----------------------------------------------------------------
  242. @pyqtSlot()
  243. def slot_configureCarla(self):
  244. if self.fParent is None or not self.fParent.openSettings(False, False):
  245. return
  246. #self.loadSettings(False)
  247. # -----------------------------------------------------------------
  248. @pyqtSlot(int, int, float)
  249. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  250. if pluginId >= self.fPluginCount:
  251. return
  252. pitem = self.fPluginList[pluginId]
  253. if pitem is None:
  254. return
  255. pitem.fWidget.setParameterValue(index, value)
  256. @pyqtSlot(int, int, float)
  257. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  258. if pluginId >= self.fPluginCount:
  259. return
  260. pitem = self.fPluginList[pluginId]
  261. if pitem is None:
  262. return
  263. pitem.fWidget.setParameterDefault(index, value)
  264. @pyqtSlot(int, int, int)
  265. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  266. if pluginId >= self.fPluginCount:
  267. return
  268. pitem = self.fPluginList[pluginId]
  269. if pitem is None:
  270. return
  271. pitem.fWidget.setParameterMidiChannel(index, channel)
  272. @pyqtSlot(int, int, int)
  273. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  274. if pluginId >= self.fPluginCount:
  275. return
  276. pitem = self.fPluginList[pluginId]
  277. if pitem is None:
  278. return
  279. pitem.fWidget.setParameterMidiControl(index, cc)
  280. # -----------------------------------------------------------------
  281. @pyqtSlot(int, int)
  282. def slot_handleProgramChangedCallback(self, pluginId, index):
  283. if pluginId >= self.fPluginCount:
  284. return
  285. pitem = self.fPluginList[pluginId]
  286. if pitem is None:
  287. return
  288. pitem.fWidget.setProgram(index)
  289. @pyqtSlot(int, int)
  290. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  291. if pluginId >= self.fPluginCount:
  292. return
  293. pitem = self.fPluginList[pluginId]
  294. if pitem is None:
  295. return
  296. pitem.fWidget.setMidiProgram(index)
  297. # -----------------------------------------------------------------
  298. @pyqtSlot(int, int, int, int)
  299. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  300. if pluginId >= self.fPluginCount:
  301. return
  302. pitem = self.fPluginList[pluginId]
  303. if pitem is None:
  304. return
  305. pitem.fWidget.sendNoteOn(channel, note)
  306. @pyqtSlot(int, int, int)
  307. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  308. if pluginId >= self.fPluginCount:
  309. return
  310. pitem = self.fPluginList[pluginId]
  311. if pitem is None:
  312. return
  313. pitem.fWidget.sendNoteOff(channel, note)
  314. # -----------------------------------------------------------------
  315. @pyqtSlot(int, int)
  316. def slot_handleShowGuiCallback(self, pluginId, state):
  317. if pluginId >= self.fPluginCount:
  318. return
  319. pitem = self.fPluginList[pluginId]
  320. if pitem is None:
  321. return
  322. if state == 0:
  323. pitem.fWidget.ui.b_gui.setChecked(False)
  324. pitem.fWidget.ui.b_gui.setEnabled(True)
  325. elif state == 1:
  326. pitem.fWidget.ui.b_gui.setChecked(True)
  327. pitem.fWidget.ui.b_gui.setEnabled(True)
  328. elif state == -1:
  329. pitem.fWidget.ui.b_gui.setChecked(False)
  330. pitem.fWidget.ui.b_gui.setEnabled(False)
  331. # -----------------------------------------------------------------
  332. @pyqtSlot(int)
  333. def slot_handleUpdateCallback(self, pluginId):
  334. if pluginId >= self.fPluginCount:
  335. return
  336. pitem = self.fPluginList[pluginId]
  337. if pitem is None:
  338. return
  339. pitem.ui.edit_dialog.updateInfo()
  340. @pyqtSlot(int)
  341. def slot_handleReloadInfoCallback(self, pluginId):
  342. if pluginId >= self.fPluginCount:
  343. return
  344. pitem = self.fPluginList[pluginId]
  345. if pitem is None:
  346. return
  347. pitem.ui.edit_dialog.reloadInfo()
  348. @pyqtSlot(int)
  349. def slot_handleReloadParametersCallback(self, pluginId):
  350. if pluginId >= self.fPluginCount:
  351. return
  352. pitem = self.fPluginList[pluginId]
  353. if pitem is None:
  354. return
  355. pitem.ui.edit_dialog.reloadParameters()
  356. @pyqtSlot(int)
  357. def slot_handleReloadProgramsCallback(self, pluginId):
  358. if pluginId >= self.fPluginCount:
  359. return
  360. pitem = self.fPluginList[pluginId]
  361. if pitem is None:
  362. return
  363. pitem.ui.edit_dialog.reloadPrograms()
  364. @pyqtSlot(int)
  365. def slot_handleReloadAllCallback(self, pluginId):
  366. if pluginId >= self.fPluginCount:
  367. return
  368. pitem = self.fPluginList[pluginId]
  369. if pitem is None:
  370. return
  371. pitem.ui.edit_dialog.reloadAll()
  372. # ------------------------------------------------------------------------------------------------------------
  373. # TESTING
  374. #from PyQt5.QtWidgets import QApplication
  375. #app = QApplication(sys.argv)
  376. #gui = CarlaRackW(None)
  377. #gui.show()
  378. #app.exec_()