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.

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