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
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  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.ParameterDefaultChangedCallback.connect(self.fRack.slot_handleParameterDefaultChangedCallback)
  67. parent.ParameterMidiChannelChangedCallback.connect(self.fRack.slot_handleParameterMidiChannelChangedCallback)
  68. parent.ParameterMidiCcChangedCallback.connect(self.fRack.slot_handleParameterMidiCcChangedCallback)
  69. parent.ProgramChangedCallback.connect(self.fRack.slot_handleProgramChangedCallback)
  70. parent.MidiProgramChangedCallback.connect(self.fRack.slot_handleMidiProgramChangedCallback)
  71. parent.UiStateChangedCallback.connect(self.fRack.slot_handleUiStateChangedCallback)
  72. parent.NoteOnCallback.connect(self.fRack.slot_handleNoteOnCallback)
  73. parent.NoteOnCallback.connect(self.fPatchbay.slot_handleNoteOnCallback)
  74. parent.NoteOffCallback.connect(self.fRack.slot_handleNoteOffCallback)
  75. parent.NoteOffCallback.connect(self.fPatchbay.slot_handleNoteOffCallback)
  76. parent.UpdateCallback.connect(self.fRack.slot_handleUpdateCallback)
  77. parent.ReloadInfoCallback.connect(self.fRack.slot_handleReloadInfoCallback)
  78. parent.ReloadParametersCallback.connect(self.fRack.slot_handleReloadParametersCallback)
  79. parent.ReloadProgramsCallback.connect(self.fRack.slot_handleReloadProgramsCallback)
  80. parent.ReloadAllCallback.connect(self.fRack.slot_handleReloadAllCallback)
  81. parent.PatchbayClientAddedCallback.connect(self.fPatchbay.slot_handlePatchbayClientAddedCallback)
  82. parent.PatchbayClientRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayClientRemovedCallback)
  83. parent.PatchbayClientRenamedCallback.connect(self.fPatchbay.slot_handlePatchbayClientRenamedCallback)
  84. parent.PatchbayClientDataChangedCallback.connect(self.fPatchbay.slot_handlePatchbayClientDataChangedCallback)
  85. parent.PatchbayPortAddedCallback.connect(self.fPatchbay.slot_handlePatchbayPortAddedCallback)
  86. parent.PatchbayPortRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayPortRemovedCallback)
  87. parent.PatchbayPortRenamedCallback.connect(self.fPatchbay.slot_handlePatchbayPortRenamedCallback)
  88. parent.PatchbayConnectionAddedCallback.connect(self.fPatchbay.slot_handlePatchbayConnectionAddedCallback)
  89. parent.PatchbayConnectionRemovedCallback.connect(self.fPatchbay.slot_handlePatchbayConnectionRemovedCallback)
  90. # -----------------------------------------------------------------
  91. def getPluginCount(self):
  92. return self.fRack.getPluginCount()
  93. # -----------------------------------------------------------------
  94. def addPlugin(self, pluginId, isProjectLoading):
  95. self.fRack.addPlugin(pluginId, isProjectLoading)
  96. self.fPatchbay.addPlugin(pluginId, isProjectLoading)
  97. def removePlugin(self, pluginId):
  98. self.fRack.removePlugin(pluginId)
  99. self.fPatchbay.removePlugin(pluginId)
  100. def renamePlugin(self, pluginId, newName):
  101. self.fRack.renamePlugin(pluginId, newName)
  102. def disablePlugin(self, pluginId, errorMsg):
  103. self.fRack.disablePlugin(pluginId, errorMsg)
  104. def removeAllPlugins(self):
  105. self.fRack.removeAllPlugins()
  106. self.fPatchbay.removeAllPlugins()
  107. # -----------------------------------------------------------------
  108. def engineStarted(self):
  109. #self.fRack.engineStarted()
  110. #self.fPatchbay.engineStarted()
  111. self.fParent.engineChanged()
  112. def engineStopped(self):
  113. #self.fRack.engineStopped()
  114. self.fPatchbay.engineStopped()
  115. self.fParent.engineStopped()
  116. def engineChanged(self):
  117. self.fParent.engineChanged()
  118. # -----------------------------------------------------------------
  119. def idleFast(self):
  120. self.fRack.idleFast()
  121. self.fPatchbay.idleFast()
  122. def idleSlow(self):
  123. self.fRack.idleSlow()
  124. # -----------------------------------------------------------------
  125. def projectLoadingStarted(self):
  126. self.fRack.projectLoadingStarted()
  127. #self.fPatchbay.projectLoadingStarted()
  128. def projectLoadingFinished(self):
  129. self.fRack.projectLoadingFinished()
  130. self.fPatchbay.projectLoadingFinished()
  131. # -----------------------------------------------------------------
  132. def saveSettings(self, settings):
  133. self.fPatchbay.saveSettings(settings)
  134. def showEditDialog(self, pluginId):
  135. self.fRack.showEditDialog(pluginId)
  136. # -----------------------------------------------------------------
  137. def fixCanvasPreviewSize(self):
  138. self.fPatchbay.resize(self.fRack.size())
  139. self.fPatchbay.slot_miniCanvasCheckSize()
  140. def setUseCustomPaint(self, useCustomPaint):
  141. if self.fUseCustomPaint != useCustomPaint:
  142. self.fUseCustomPaint = useCustomPaint
  143. self.update()
  144. def paintEvent(self, event):
  145. QTabWidget.paintEvent(self, event)
  146. if not self.fUseCustomPaint:
  147. return
  148. painter = QPainter(self)
  149. painter.setBrush(QColor(36, 36, 36))
  150. painter.setPen(QColor(62, 62, 62))
  151. painter.drawRect(1, self.height()/2, self.width()-3, self.height()-self.height()/2-1)
  152. def resizeEvent(self, event):
  153. QTabWidget.resizeEvent(self, event)
  154. if self.currentIndex() == 0:
  155. self.fixCanvasPreviewSize()
  156. # ------------------------------------------------------------------------------------------------------------
  157. # Main Window
  158. class CarlaHostW(HostWindow):
  159. def __init__(self, parent=None):
  160. HostWindow.__init__(self, parent)
  161. # -------------------------------------------------------------
  162. # Set-up container
  163. self.fContainer = CarlaMultiW(self)
  164. self.setupContainer(True, self.fContainer.fPatchbay.themeData)
  165. self.fContainer.setUseCustomPaint(self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING])
  166. # -------------------------------------------------------------
  167. # Set-up GUI stuff
  168. self.fInfoText = ""
  169. self.fInfoLabel = QLabel(self)
  170. self.fInfoLabel.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
  171. self.fInfoLabel.setText("Engine stopped")
  172. if MACOS and False: # TODO: check if NOT using pro theme
  173. self.fInfoLabel.hide()
  174. self.setUnifiedTitleAndToolBarOnMac(True)
  175. # -------------------------------------------------------------
  176. self.ui.act_settings_show_toolbar.triggered.connect(self.slot_toolbarShown)
  177. self.ui.splitter.splitterMoved.connect(self.slot_splitterMoved)
  178. QTimer.singleShot(0, self.slot_initWidgets)
  179. # -----------------------------------------------------------------
  180. def engineStopped(self):
  181. self.fInfoText = ""
  182. self.fInfoLabel.setText("Engine stopped")
  183. def engineChanged(self):
  184. self.fInfoText = "Engine running | SampleRate: %g | BufferSize: %i" % (gCarla.sampleRate, gCarla.bufferSize)
  185. self.fInfoLabel.setText("%s | %s" % (self.fInfoText, self.fTextTransport))
  186. # -----------------------------------------------------------------
  187. def updateInfoLabelXandSize(self):
  188. tabBar = self.fContainer.tabBar()
  189. x = tabBar.width() + self.ui.tabUtils.width() + 20
  190. self.fInfoLabel.move(x, self.fInfoLabel.y())
  191. self.fInfoLabel.resize(self.fContainer.width()-tabBar.width()-20, self.fInfoLabel.height())
  192. def updateInfoLabelY(self):
  193. tabBar = self.fContainer.tabBar()
  194. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()
  195. if not self.ui.toolBar.isVisible():
  196. y -= self.ui.toolBar.height()
  197. self.fInfoLabel.move(self.fInfoLabel.x(), y)
  198. # -----------------------------------------------------------------
  199. @pyqtSlot()
  200. def slot_initWidgets(self):
  201. tabBar = self.fContainer.tabBar()
  202. x = tabBar.width() + self.ui.tabUtils.width() + 20
  203. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()
  204. self.fInfoLabel.move(x, y)
  205. self.fInfoLabel.resize(self.fContainer.width()-tabBar.width()-20, tabBar.height())
  206. # FIXME: Qt4 needs this so it properly creates & resizes the canvas
  207. self.fContainer.setCurrentIndex(1)
  208. self.fContainer.setCurrentIndex(0)
  209. self.fContainer.fixCanvasPreviewSize()
  210. @pyqtSlot()
  211. def slot_splitterMoved(self):
  212. self.updateInfoLabelXandSize()
  213. @pyqtSlot()
  214. def slot_toolbarShown(self):
  215. self.updateInfoLabelY()
  216. # -----------------------------------------------------------------
  217. def resizeEvent(self, event):
  218. HostWindow.resizeEvent(self, event)
  219. self.updateInfoLabelXandSize()
  220. def timerEvent(self, event):
  221. HostWindow.timerEvent(self, event)
  222. if event.timerId() == self.fIdleTimerFast:
  223. self.fInfoLabel.setText("%s | %s" % (self.fInfoText, self.fTextTransport))
  224. # ------------------------------------------------------------------------------------------------------------
  225. # Main
  226. if __name__ == '__main__':
  227. # -------------------------------------------------------------
  228. # App initialization
  229. app = CarlaApplication()
  230. # -------------------------------------------------------------
  231. # Set-up custom signal handling
  232. setUpSignals()
  233. # -------------------------------------------------------------
  234. # Read CLI args
  235. appName = os.path.basename(__file__) if ("__file__" in dir() and os.path.dirname(__file__) in PATH) else sys.argv[0]
  236. libPrefix = None
  237. projectFilename = None
  238. argv = app.arguments()
  239. argc = len(argv)
  240. for i in range(argc):
  241. if i == 0: continue
  242. argument = argv[i]
  243. if argument.startswith("--with-appname="):
  244. appName = os.path.basename(argument.replace("--with-appname=", ""))
  245. elif argument.startswith("--with-libprefix="):
  246. libPrefix = argument.replace("--with-libprefix=", "")
  247. elif os.path.exists(argument):
  248. projectFilename = argument
  249. if libPrefix is not None:
  250. app.addLibraryPath(os.path.join(libPrefix, "lib", "carla"))
  251. # -------------------------------------------------------------
  252. # Init host backend
  253. gCarla.isControl = False
  254. gCarla.isLocal = True
  255. gCarla.isPlugin = False
  256. initHost(appName, libPrefix)
  257. # -------------------------------------------------------------
  258. # Create GUI
  259. gCarla.gui = CarlaHostW()
  260. # set our gui as parent for all plugins UIs
  261. gCarla.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, str(gCarla.gui.winId()))
  262. # -------------------------------------------------------------
  263. # Load project file if set
  264. if projectFilename is not None:
  265. gCarla.gui.loadProjectLater(projectFilename)
  266. # -------------------------------------------------------------
  267. # Show GUI
  268. gCarla.gui.show()
  269. # -------------------------------------------------------------
  270. # App-Loop
  271. sys.exit(app.exec_())