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