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.

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