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.

515 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. from PyQt4.QtCore import QSize, QTimer
  20. from PyQt4.QtGui import QApplication, QListWidget, QListWidgetItem
  21. # ------------------------------------------------------------------------------------------------------------
  22. # Imports (Custom Stuff)
  23. from carla_widgets import *
  24. # ------------------------------------------------------------------------------------------------------------
  25. # Rack widget item
  26. class CarlaRackItem(QListWidgetItem):
  27. kRackItemType = QListWidgetItem.UserType + 1
  28. kStaticHeight = 32
  29. def __init__(self, parent, pluginId):
  30. QListWidgetItem.__init__(self, parent, self.kRackItemType)
  31. self.fWidget = PluginWidget(parent, pluginId)
  32. self.fWidget.setFixedHeight(self.kStaticHeight)
  33. self.setSizeHint(QSize(300, self.kStaticHeight))
  34. parent.setItemWidget(self, self.fWidget)
  35. # -----------------------------------------------------------------
  36. def close(self):
  37. self.fWidget.ui.edit_dialog.close()
  38. def setId(self, idx):
  39. self.fWidget.setId(idx)
  40. # ------------------------------------------------------------------------------------------------------------
  41. # Rack widget
  42. class CarlaRackW(QListWidget):
  43. def __init__(self, parent, doSetup = True):
  44. QListWidget.__init__(self, parent)
  45. # -------------------------------------------------------------
  46. # Internal stuff
  47. self.fParent = parent
  48. self.fPluginCount = 0
  49. self.fPluginList = []
  50. # -------------------------------------------------------------
  51. # Set-up GUI stuff
  52. #self.setMnimumWidth(800)
  53. self.setSortingEnabled(False)
  54. #app = QApplication.instance()
  55. #pal1 = app.palette().base().color()
  56. #pal2 = app.palette().button().color()
  57. #col1 = "stop:0 rgb(%i, %i, %i)" % (pal1.red(), pal1.green(), pal1.blue())
  58. #col2 = "stop:1 rgb(%i, %i, %i)" % (pal2.red(), pal2.green(), pal2.blue())
  59. #self.setStyleSheet("""
  60. #QListWidget {
  61. #background-color: qlineargradient(spread:pad,
  62. #x1:0.0, y1:0.0,
  63. #x2:0.2, y2:1.0,
  64. #%s,
  65. #%s
  66. #);
  67. #}
  68. #""" % (col1, col2))
  69. # -------------------------------------------------------------
  70. # Connect actions to functions
  71. if not doSetup: return
  72. parent.ui.menu_Canvas.hide()
  73. parent.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
  74. parent.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
  75. parent.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
  76. parent.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
  77. parent.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
  78. parent.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
  79. parent.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
  80. parent.ui.act_plugins_panic.triggered.connect(self.slot_pluginsDisable)
  81. parent.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
  82. parent.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
  83. parent.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
  84. parent.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
  85. parent.ParameterMidiCcChangedCallback.connect(self.slot_handleParameterMidiCcChangedCallback)
  86. parent.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
  87. parent.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
  88. parent.UiStateChangedCallback.connect(self.slot_handleUiStateChangedCallback)
  89. parent.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
  90. parent.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
  91. parent.UpdateCallback.connect(self.slot_handleUpdateCallback)
  92. parent.ReloadInfoCallback.connect(self.slot_handleReloadInfoCallback)
  93. parent.ReloadParametersCallback.connect(self.slot_handleReloadParametersCallback)
  94. parent.ReloadProgramsCallback.connect(self.slot_handleReloadProgramsCallback)
  95. parent.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
  96. # -----------------------------------------------------------------
  97. def getPluginCount(self):
  98. return self.fPluginCount
  99. # -----------------------------------------------------------------
  100. def addPlugin(self, pluginId, isProjectLoading):
  101. pitem = CarlaRackItem(self, pluginId)
  102. self.fPluginList.append(pitem)
  103. self.fPluginCount += 1
  104. if not isProjectLoading:
  105. pitem.fWidget.setActive(True, True, True)
  106. def removePlugin(self, pluginId):
  107. if pluginId >= self.fPluginCount:
  108. return
  109. pitem = self.fPluginList[pluginId]
  110. if pitem is None:
  111. return
  112. self.fPluginCount -= 1
  113. self.fPluginList.pop(pluginId)
  114. self.takeItem(pluginId)
  115. pitem.close()
  116. del pitem
  117. # push all plugins 1 slot back
  118. for i in range(pluginId, self.fPluginCount):
  119. self.fPluginList[i].setId(i)
  120. def renamePlugin(self, pluginId, newName):
  121. if pluginId >= self.fPluginCount:
  122. return
  123. pitem = self.fPluginList[pluginId]
  124. if pitem is None:
  125. return
  126. pitem.fWidget.ui.label_name.setText(newName)
  127. pitem.fWidget.ui.edit_dialog.setName(newName)
  128. def removeAllPlugins(self):
  129. while (self.takeItem(0)):
  130. pass
  131. for i in range(self.fPluginCount):
  132. pitem = self.fPluginList[i]
  133. if pitem is None:
  134. break
  135. pitem.close()
  136. del pitem
  137. self.fPluginCount = 0
  138. self.fPluginList = []
  139. # -----------------------------------------------------------------
  140. def engineStarted(self):
  141. pass
  142. def engineStopped(self):
  143. pass
  144. def engineChanged(self):
  145. pass
  146. # -----------------------------------------------------------------
  147. def idleFast(self):
  148. for i in range(self.fPluginCount):
  149. pitem = self.fPluginList[i]
  150. if pitem is None:
  151. break
  152. pitem.fWidget.idleFast()
  153. def idleSlow(self):
  154. for i in range(self.fPluginCount):
  155. pitem = self.fPluginList[i]
  156. if pitem is None:
  157. break
  158. pitem.fWidget.idleSlow()
  159. # -----------------------------------------------------------------
  160. def saveSettings(self, settings):
  161. pass
  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.openSettingsWindow(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_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  266. if pluginId >= self.fPluginCount:
  267. return
  268. pitem = self.fPluginList[pluginId]
  269. if pitem is None:
  270. return
  271. pitem.fWidget.setParameterMidiControl(index, cc)
  272. @pyqtSlot(int, int, int)
  273. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  274. if pluginId >= self.fPluginCount:
  275. return
  276. pitem = self.fPluginList[pluginId]
  277. if pitem is None:
  278. return
  279. pitem.fWidget.setParameterMidiChannel(index, channel)
  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)
  299. def slot_handleUiStateChangedCallback(self, pluginId, state):
  300. if pluginId >= self.fPluginCount:
  301. return
  302. pitem = self.fPluginList[pluginId]
  303. if pitem is None:
  304. return
  305. if state == 0:
  306. pitem.fWidget.ui.b_gui.setChecked(False)
  307. pitem.fWidget.ui.b_gui.setEnabled(True)
  308. elif state == 1:
  309. pitem.fWidget.ui.b_gui.setChecked(True)
  310. pitem.fWidget.ui.b_gui.setEnabled(True)
  311. elif state == -1:
  312. pitem.fWidget.ui.b_gui.setChecked(False)
  313. pitem.fWidget.ui.b_gui.setEnabled(False)
  314. # -----------------------------------------------------------------
  315. @pyqtSlot(int, int, int, int)
  316. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  317. if pluginId >= self.fPluginCount:
  318. return
  319. pitem = self.fPluginList[pluginId]
  320. if pitem is None:
  321. return
  322. pitem.fWidget.sendNoteOn(channel, note)
  323. @pyqtSlot(int, int, int)
  324. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  325. if pluginId >= self.fPluginCount:
  326. return
  327. pitem = self.fPluginList[pluginId]
  328. if pitem is None:
  329. return
  330. pitem.fWidget.sendNoteOff(channel, note)
  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_()