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.

517 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. def saveSettings(self, settings):
  164. pass
  165. # -----------------------------------------------------------------
  166. @pyqtSlot()
  167. def slot_pluginsEnable(self):
  168. if not Carla.host.is_engine_running():
  169. return
  170. for i in range(self.fPluginCount):
  171. pitem = self.fPluginList[i]
  172. if pitem is None:
  173. break
  174. pitem.fWidget.setActive(True, True, True)
  175. @pyqtSlot()
  176. def slot_pluginsDisable(self):
  177. if not Carla.host.is_engine_running():
  178. return
  179. for i in range(self.fPluginCount):
  180. pitem = self.fPluginList[i]
  181. if pitem is None:
  182. break
  183. pitem.fWidget.setActive(False, True, True)
  184. @pyqtSlot()
  185. def slot_pluginsVolume100(self):
  186. if not Carla.host.is_engine_running():
  187. return
  188. for i in range(self.fPluginCount):
  189. pitem = self.fPluginList[i]
  190. if pitem is None:
  191. break
  192. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  193. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 1.0)
  194. Carla.host.set_volume(i, 1.0)
  195. @pyqtSlot()
  196. def slot_pluginsMute(self):
  197. if not Carla.host.is_engine_running():
  198. return
  199. for i in range(self.fPluginCount):
  200. pitem = self.fPluginList[i]
  201. if pitem is None:
  202. break
  203. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  204. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 0.0)
  205. Carla.host.set_volume(i, 0.0)
  206. @pyqtSlot()
  207. def slot_pluginsWet100(self):
  208. if not Carla.host.is_engine_running():
  209. return
  210. for i in range(self.fPluginCount):
  211. pitem = self.fPluginList[i]
  212. if pitem is None:
  213. break
  214. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  215. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 1.0)
  216. Carla.host.set_drywet(i, 1.0)
  217. @pyqtSlot()
  218. def slot_pluginsBypass(self):
  219. if not Carla.host.is_engine_running():
  220. return
  221. for i in range(self.fPluginCount):
  222. pitem = self.fPluginList[i]
  223. if pitem is None:
  224. break
  225. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  226. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 0.0)
  227. Carla.host.set_drywet(i, 0.0)
  228. @pyqtSlot()
  229. def slot_pluginsCenter(self):
  230. if not Carla.host.is_engine_running():
  231. return
  232. for i in range(self.fPluginCount):
  233. pitem = self.fPluginList[i]
  234. if pitem is None:
  235. break
  236. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  237. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  238. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  239. Carla.host.set_balance_left(i, -1.0)
  240. Carla.host.set_balance_right(i, 1.0)
  241. if pitem.fWidget.fPluginInfo['hints'] & PLUGIN_CAN_PANNING:
  242. pitem.fWidget.ui.edit_dialog.setParameterValue(PARAMETER_PANNING, 1.0)
  243. Carla.host.set_panning(i, 1.0)
  244. # -----------------------------------------------------------------
  245. @pyqtSlot()
  246. def slot_configureCarla(self):
  247. if self.fParent is None or not self.fParent.openSettingsWindow(False, False):
  248. return
  249. self.loadSettings(False)
  250. # -----------------------------------------------------------------
  251. @pyqtSlot(int, int, float)
  252. def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
  253. if pluginId >= self.fPluginCount:
  254. return
  255. pitem = self.fPluginList[pluginId]
  256. if pitem is None:
  257. return
  258. pitem.fWidget.setParameterValue(index, value)
  259. @pyqtSlot(int, int, float)
  260. def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
  261. if pluginId >= self.fPluginCount:
  262. return
  263. pitem = self.fPluginList[pluginId]
  264. if pitem is None:
  265. return
  266. pitem.fWidget.setParameterDefault(index, value)
  267. @pyqtSlot(int, int, int)
  268. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
  269. if pluginId >= self.fPluginCount:
  270. return
  271. pitem = self.fPluginList[pluginId]
  272. if pitem is None:
  273. return
  274. pitem.fWidget.setParameterMidiChannel(index, channel)
  275. @pyqtSlot(int, int, int)
  276. def slot_handleParameterMidiCcChangedCallback(self, pluginId, index, cc):
  277. if pluginId >= self.fPluginCount:
  278. return
  279. pitem = self.fPluginList[pluginId]
  280. if pitem is None:
  281. return
  282. pitem.fWidget.setParameterMidiControl(index, cc)
  283. # -----------------------------------------------------------------
  284. @pyqtSlot(int, int)
  285. def slot_handleProgramChangedCallback(self, pluginId, index):
  286. if pluginId >= self.fPluginCount:
  287. return
  288. pitem = self.fPluginList[pluginId]
  289. if pitem is None:
  290. return
  291. pitem.fWidget.setProgram(index)
  292. @pyqtSlot(int, int)
  293. def slot_handleMidiProgramChangedCallback(self, pluginId, index):
  294. if pluginId >= self.fPluginCount:
  295. return
  296. pitem = self.fPluginList[pluginId]
  297. if pitem is None:
  298. return
  299. pitem.fWidget.setMidiProgram(index)
  300. # -----------------------------------------------------------------
  301. @pyqtSlot(int, int, int, int)
  302. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  303. if pluginId >= self.fPluginCount:
  304. return
  305. pitem = self.fPluginList[pluginId]
  306. if pitem is None:
  307. return
  308. pitem.fWidget.sendNoteOn(channel, note)
  309. @pyqtSlot(int, int, int)
  310. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  311. if pluginId >= self.fPluginCount:
  312. return
  313. pitem = self.fPluginList[pluginId]
  314. if pitem is None:
  315. return
  316. pitem.fWidget.sendNoteOff(channel, note)
  317. # -----------------------------------------------------------------
  318. @pyqtSlot(int, int)
  319. def slot_handleShowGuiCallback(self, pluginId, state):
  320. if pluginId >= self.fPluginCount:
  321. return
  322. pitem = self.fPluginList[pluginId]
  323. if pitem is None:
  324. return
  325. if state == 0:
  326. pitem.fWidget.ui.b_gui.setChecked(False)
  327. pitem.fWidget.ui.b_gui.setEnabled(True)
  328. elif state == 1:
  329. pitem.fWidget.ui.b_gui.setChecked(True)
  330. pitem.fWidget.ui.b_gui.setEnabled(True)
  331. elif state == -1:
  332. pitem.fWidget.ui.b_gui.setChecked(False)
  333. pitem.fWidget.ui.b_gui.setEnabled(False)
  334. # -----------------------------------------------------------------
  335. @pyqtSlot(int)
  336. def slot_handleUpdateCallback(self, pluginId):
  337. if pluginId >= self.fPluginCount:
  338. return
  339. pitem = self.fPluginList[pluginId]
  340. if pitem is None:
  341. return
  342. pitem.ui.edit_dialog.updateInfo()
  343. @pyqtSlot(int)
  344. def slot_handleReloadInfoCallback(self, pluginId):
  345. if pluginId >= self.fPluginCount:
  346. return
  347. pitem = self.fPluginList[pluginId]
  348. if pitem is None:
  349. return
  350. pitem.ui.edit_dialog.reloadInfo()
  351. @pyqtSlot(int)
  352. def slot_handleReloadParametersCallback(self, pluginId):
  353. if pluginId >= self.fPluginCount:
  354. return
  355. pitem = self.fPluginList[pluginId]
  356. if pitem is None:
  357. return
  358. pitem.ui.edit_dialog.reloadParameters()
  359. @pyqtSlot(int)
  360. def slot_handleReloadProgramsCallback(self, pluginId):
  361. if pluginId >= self.fPluginCount:
  362. return
  363. pitem = self.fPluginList[pluginId]
  364. if pitem is None:
  365. return
  366. pitem.ui.edit_dialog.reloadPrograms()
  367. @pyqtSlot(int)
  368. def slot_handleReloadAllCallback(self, pluginId):
  369. if pluginId >= self.fPluginCount:
  370. return
  371. pitem = self.fPluginList[pluginId]
  372. if pitem is None:
  373. return
  374. pitem.ui.edit_dialog.reloadAll()
  375. # ------------------------------------------------------------------------------------------------------------
  376. # TESTING
  377. #from PyQt5.QtWidgets import QApplication
  378. #app = QApplication(sys.argv)
  379. #gui = CarlaRackW(None)
  380. #gui.show()
  381. #app.exec_()