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.

carla 14KB

10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Carla plugin host
  4. # Copyright (C) 2011-2014 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 GPL.txt file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Config)
  19. from carla_config import *
  20. # ------------------------------------------------------------------------------------------------------------
  21. # Imports (Global)
  22. if config_UseQt5:
  23. from PyQt5.QtWidgets import QLabel, QTabWidget
  24. else:
  25. from PyQt4.QtGui import QLabel, QTabWidget
  26. # ------------------------------------------------------------------------------------------------------------
  27. # Imports (Custom Stuff)
  28. from carla_host import *
  29. from carla_patchbay import CarlaPatchbayW
  30. from carla_rack import CarlaRackW
  31. # ------------------------------------------------------------------------------------------------------------
  32. # Tab widget (rack + patchbay)
  33. class CarlaMultiW(QTabWidget):
  34. def __init__(self, parent):
  35. QTabWidget.__init__(self, parent)
  36. self.fRack = CarlaRackW(parent, False)
  37. self.fPatchbay = CarlaPatchbayW(parent, False, False)
  38. self.fParent = parent
  39. self.fUseCustomPaint = False
  40. self.addTab(self.fRack, "Plugins")
  41. self.addTab(self.fPatchbay, "Patchbay")
  42. #self.fPatchbay.hide()
  43. #self.removeTab(1)
  44. #self.fPatchbay.setParent(None)
  45. #self.fPatchbay.show()
  46. self.scene = self.fPatchbay.scene
  47. parent.ui.act_plugins_enable.triggered.connect(self.fRack.slot_pluginsEnable)
  48. parent.ui.act_plugins_disable.triggered.connect(self.fRack.slot_pluginsDisable)
  49. parent.ui.act_plugins_volume100.triggered.connect(self.fRack.slot_pluginsVolume100)
  50. parent.ui.act_plugins_mute.triggered.connect(self.fRack.slot_pluginsMute)
  51. parent.ui.act_plugins_wet100.triggered.connect(self.fRack.slot_pluginsWet100)
  52. parent.ui.act_plugins_bypass.triggered.connect(self.fRack.slot_pluginsBypass)
  53. parent.ui.act_plugins_center.triggered.connect(self.fRack.slot_pluginsCenter)
  54. parent.ui.act_plugins_panic.triggered.connect(self.fRack.slot_pluginsDisable)
  55. parent.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  56. parent.ui.act_canvas_arrange.triggered.connect(self.fPatchbay.slot_canvasArrange)
  57. parent.ui.act_canvas_refresh.triggered.connect(self.fPatchbay.slot_canvasRefresh)
  58. parent.ui.act_canvas_zoom_fit.triggered.connect(self.fPatchbay.slot_canvasZoomFit)
  59. parent.ui.act_canvas_zoom_in.triggered.connect(self.fPatchbay.slot_canvasZoomIn)
  60. parent.ui.act_canvas_zoom_out.triggered.connect(self.fPatchbay.slot_canvasZoomOut)
  61. parent.ui.act_canvas_zoom_100.triggered.connect(self.fPatchbay.slot_canvasZoomReset)
  62. parent.ui.act_canvas_print.triggered.connect(self.fPatchbay.slot_canvasPrint)
  63. parent.ui.act_canvas_save_image.triggered.connect(self.fPatchbay.slot_canvasSaveImage)
  64. parent.ui.act_settings_configure.triggered.connect(self.fPatchbay.slot_configureCarla)
  65. parent.ParameterValueChangedCallback.connect(self.fRack.slot_handleParameterValueChangedCallback)
  66. parent.ParameterValueChangedCallback.connect(self.fPatchbay.slot_handleParameterValueChangedCallback)
  67. parent.ParameterDefaultChangedCallback.connect(self.fRack.slot_handleParameterDefaultChangedCallback)
  68. parent.ParameterMidiChannelChangedCallback.connect(self.fRack.slot_handleParameterMidiChannelChangedCallback)
  69. parent.ParameterMidiCcChangedCallback.connect(self.fRack.slot_handleParameterMidiCcChangedCallback)
  70. parent.ProgramChangedCallback.connect(self.fRack.slot_handleProgramChangedCallback)
  71. parent.MidiProgramChangedCallback.connect(self.fRack.slot_handleMidiProgramChangedCallback)
  72. parent.UiStateChangedCallback.connect(self.fRack.slot_handleUiStateChangedCallback)
  73. parent.NoteOnCallback.connect(self.fRack.slot_handleNoteOnCallback)
  74. parent.NoteOnCallback.connect(self.fPatchbay.slot_handleNoteOnCallback)
  75. parent.NoteOffCallback.connect(self.fRack.slot_handleNoteOffCallback)
  76. parent.NoteOffCallback.connect(self.fPatchbay.slot_handleNoteOffCallback)
  77. parent.UpdateCallback.connect(self.fRack.slot_handleUpdateCallback)
  78. parent.ReloadInfoCallback.connect(self.fRack.slot_handleReloadInfoCallback)
  79. parent.ReloadParametersCallback.connect(self.fRack.slot_handleReloadParametersCallback)
  80. parent.ReloadParametersCallback.connect(self.fPatchbay.slot_handleReloadParametersCallback)
  81. parent.ReloadProgramsCallback.connect(self.fRack.slot_handleReloadProgramsCallback)
  82. parent.ReloadAllCallback.connect(self.fRack.slot_handleReloadAllCallback)
  83. parent.ReloadAllCallback.connect(self.fPatchbay.slot_handleReloadAllCallback)
  84. parent.PatchbayClientAddedCallback.connect(self.fPatchbay.slot_handlePatchbayClientAddedCallback)
  85. parent.PatchbayClientRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayClientRemovedCallback)
  86. parent.PatchbayClientRenamedCallback.connect(self.fPatchbay.slot_handlePatchbayClientRenamedCallback)
  87. parent.PatchbayClientDataChangedCallback.connect(self.fPatchbay.slot_handlePatchbayClientDataChangedCallback)
  88. parent.PatchbayPortAddedCallback.connect(self.fPatchbay.slot_handlePatchbayPortAddedCallback)
  89. parent.PatchbayPortRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayPortRemovedCallback)
  90. parent.PatchbayPortRenamedCallback.connect(self.fPatchbay.slot_handlePatchbayPortRenamedCallback)
  91. parent.PatchbayPortValueChangedCallback.connect(self.fPatchbay.slot_handlePatchbayPortValueChangedCallback)
  92. parent.PatchbayConnectionAddedCallback.connect(self.fPatchbay.slot_handlePatchbayConnectionAddedCallback)
  93. parent.PatchbayConnectionRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayConnectionRemovedCallback)
  94. # -----------------------------------------------------------------
  95. def getPluginCount(self):
  96. return self.fRack.getPluginCount()
  97. # -----------------------------------------------------------------
  98. def addPlugin(self, pluginId, isProjectLoading):
  99. self.fRack.addPlugin(pluginId, isProjectLoading)
  100. self.fPatchbay.addPlugin(pluginId, isProjectLoading)
  101. def removePlugin(self, pluginId):
  102. self.fRack.removePlugin(pluginId)
  103. self.fPatchbay.removePlugin(pluginId)
  104. def renamePlugin(self, pluginId, newName):
  105. self.fRack.renamePlugin(pluginId, newName)
  106. def disablePlugin(self, pluginId, errorMsg):
  107. self.fRack.disablePlugin(pluginId, errorMsg)
  108. def removeAllPlugins(self):
  109. self.fRack.removeAllPlugins()
  110. self.fPatchbay.removeAllPlugins()
  111. # -----------------------------------------------------------------
  112. def engineStarted(self):
  113. #self.fRack.engineStarted()
  114. #self.fPatchbay.engineStarted()
  115. self.fParent.engineChanged()
  116. def engineStopped(self):
  117. #self.fRack.engineStopped()
  118. self.fPatchbay.engineStopped()
  119. self.fParent.engineStopped()
  120. def engineChanged(self):
  121. self.fParent.engineChanged()
  122. # -----------------------------------------------------------------
  123. def idleFast(self):
  124. self.fRack.idleFast()
  125. self.fPatchbay.idleFast()
  126. def idleSlow(self):
  127. self.fRack.idleSlow()
  128. self.fPatchbay.idleSlow()
  129. # -----------------------------------------------------------------
  130. def projectLoadingStarted(self):
  131. self.fRack.projectLoadingStarted()
  132. #self.fPatchbay.projectLoadingStarted()
  133. def projectLoadingFinished(self):
  134. self.fRack.projectLoadingFinished()
  135. self.fPatchbay.projectLoadingFinished()
  136. # -----------------------------------------------------------------
  137. def saveSettings(self, settings):
  138. self.fPatchbay.saveSettings(settings)
  139. def showEditDialog(self, pluginId):
  140. self.fRack.showEditDialog(pluginId)
  141. # -----------------------------------------------------------------
  142. def fixCanvasPreviewSize(self):
  143. self.fPatchbay.resize(self.fRack.size())
  144. self.fPatchbay.slot_miniCanvasCheckSize()
  145. def setUseCustomPaint(self, useCustomPaint):
  146. if self.fUseCustomPaint != useCustomPaint:
  147. self.fUseCustomPaint = useCustomPaint
  148. self.update()
  149. def paintEvent(self, event):
  150. QTabWidget.paintEvent(self, event)
  151. if MACOS or not self.fUseCustomPaint:
  152. return
  153. painter = QPainter(self)
  154. painter.setBrush(QColor(36, 36, 36))
  155. painter.setPen(QColor(62, 62, 62))
  156. painter.drawRect(1, self.height()/2, self.width()-3, self.height()-self.height()/2-1)
  157. def resizeEvent(self, event):
  158. QTabWidget.resizeEvent(self, event)
  159. if self.currentIndex() == 0:
  160. self.fixCanvasPreviewSize()
  161. # ------------------------------------------------------------------------------------------------------------
  162. # Main Window
  163. class CarlaHostW(HostWindow):
  164. def __init__(self, parent=None):
  165. HostWindow.__init__(self, parent)
  166. # -------------------------------------------------------------
  167. # Set-up container
  168. self.fContainer = CarlaMultiW(self)
  169. self.setupContainer(True, self.fContainer.fPatchbay.themeData)
  170. self.fContainer.setUseCustomPaint(self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING])
  171. # -------------------------------------------------------------
  172. # Set-up GUI stuff
  173. self.fInfoText = ""
  174. self.fInfoLabel = QLabel(self)
  175. self.fInfoLabel.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
  176. self.fInfoLabel.setText("Engine stopped")
  177. if MACOS and False: # TODO: check if NOT using pro theme
  178. self.fInfoLabel.hide()
  179. self.setUnifiedTitleAndToolBarOnMac(True)
  180. # -------------------------------------------------------------
  181. self.ui.act_settings_show_toolbar.triggered.connect(self.slot_toolbarShown)
  182. self.ui.splitter.splitterMoved.connect(self.slot_splitterMoved)
  183. QTimer.singleShot(0, self.slot_initWidgets)
  184. # -----------------------------------------------------------------
  185. def engineStopped(self):
  186. self.fInfoText = ""
  187. self.fInfoLabel.setText("Engine stopped")
  188. def engineChanged(self):
  189. self.fInfoText = "Engine running | SampleRate: %g | BufferSize: %i" % (gCarla.sampleRate, gCarla.bufferSize)
  190. self.fInfoLabel.setText("%s | %s" % (self.fInfoText, self.fTextTransport))
  191. # -----------------------------------------------------------------
  192. def updateInfoLabelXandSize(self):
  193. tabBar = self.fContainer.tabBar()
  194. x = tabBar.width() + self.ui.tabUtils.width() + 20
  195. self.fInfoLabel.move(x, self.fInfoLabel.y())
  196. self.fInfoLabel.resize(self.fContainer.width()-tabBar.width()-20, self.fInfoLabel.height())
  197. def updateInfoLabelY(self):
  198. tabBar = self.fContainer.tabBar()
  199. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()
  200. if not self.ui.toolBar.isVisible():
  201. y -= self.ui.toolBar.height()
  202. self.fInfoLabel.move(self.fInfoLabel.x(), y)
  203. # -----------------------------------------------------------------
  204. @pyqtSlot()
  205. def slot_initWidgets(self):
  206. tabBar = self.fContainer.tabBar()
  207. x = tabBar.width() + self.ui.tabUtils.width() + 20
  208. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()
  209. self.fInfoLabel.move(x, y)
  210. self.fInfoLabel.resize(self.fContainer.width()-tabBar.width()-20, tabBar.height())
  211. # FIXME: Qt4 needs this so it properly creates & resizes the canvas
  212. self.fContainer.setCurrentIndex(1)
  213. self.fContainer.setCurrentIndex(0)
  214. self.fContainer.fixCanvasPreviewSize()
  215. @pyqtSlot()
  216. def slot_splitterMoved(self):
  217. self.updateInfoLabelXandSize()
  218. @pyqtSlot()
  219. def slot_toolbarShown(self):
  220. self.updateInfoLabelY()
  221. # -----------------------------------------------------------------
  222. def resizeEvent(self, event):
  223. HostWindow.resizeEvent(self, event)
  224. self.updateInfoLabelXandSize()
  225. def timerEvent(self, event):
  226. HostWindow.timerEvent(self, event)
  227. if event.timerId() == self.fIdleTimerFast:
  228. self.fInfoLabel.setText("%s | %s" % (self.fInfoText, self.fTextTransport))
  229. # ------------------------------------------------------------------------------------------------------------
  230. # Main
  231. if __name__ == '__main__':
  232. # -------------------------------------------------------------
  233. # Read CLI args
  234. initName = os.path.basename(__file__) if ("__file__" in dir() and os.path.dirname(__file__) in PATH) else sys.argv[0]
  235. libPrefix = None
  236. for arg in sys.argv:
  237. if arg.startswith("--with-appname="):
  238. initName = os.path.basename(arg.replace("--with-initname=", ""))
  239. elif arg.startswith("--with-libprefix="):
  240. libPrefix = arg.replace("--with-libprefix=", "")
  241. # -------------------------------------------------------------
  242. # App initialization
  243. app = CarlaApplication("Carla2", libPrefix)
  244. # -------------------------------------------------------------
  245. # Set-up custom signal handling
  246. setUpSignals()
  247. # -------------------------------------------------------------
  248. # Init host backend
  249. gCarla.isControl = False
  250. gCarla.isLocal = True
  251. gCarla.isPlugin = False
  252. initHost(initName, libPrefix)
  253. # -------------------------------------------------------------
  254. # Create GUI
  255. gCarla.gui = CarlaHostW()
  256. # -------------------------------------------------------------
  257. # Load project file if set
  258. args = app.arguments()
  259. if len(args) > 1:
  260. arg = args[-1]
  261. if arg.startswith("--with-appname=") or arg.startswith("--with-libprefix="):
  262. pass
  263. elif os.path.exists(arg):
  264. gCarla.gui.loadProjectLater(arg)
  265. # -------------------------------------------------------------
  266. # Show GUI
  267. gCarla.gui.show()
  268. # -------------------------------------------------------------
  269. # App-Loop
  270. app.exit_exec()