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.

1628 lines
65KB

  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 time import sleep
  20. from PyQt5.QtCore import Qt, QModelIndex, QPointF, QSize
  21. from PyQt5.QtGui import QImage, QPalette, QResizeEvent, QSyntaxHighlighter
  22. from PyQt5.QtPrintSupport import QPrinter, QPrintDialog
  23. from PyQt5.QtWidgets import QApplication, QDialogButtonBox, QFileSystemModel, QLabel, QMainWindow
  24. # ------------------------------------------------------------------------------------------------------------
  25. # Imports (Custom Stuff)
  26. import ui_carla
  27. from carla_shared import *
  28. # ------------------------------------------------------------------------------------------------------------
  29. # Static Variables
  30. # Tab indexes
  31. TAB_INDEX_MAIN = 0
  32. TAB_INDEX_CANVAS = 1
  33. TAB_INDEX_CARLA_ENGINE = 2
  34. TAB_INDEX_CARLA_PATHS = 3
  35. TAB_INDEX_NONE = 4
  36. # Single and Multiple client mode is only for JACK,
  37. # but we still want to match QComboBox index to defines,
  38. # so add +2 pos padding if driverName != "JACK".
  39. PROCESS_MODE_NON_JACK_PADDING = 2
  40. LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
  41. NSM_URL = os.getenv("NSM_URL")
  42. # ------------------------------------------------------------------------------------------------------------
  43. # Global Variables
  44. appName = os.path.basename(__file__) if "__file__" in dir() and os.path.dirname(__file__) in PATH else sys.argv[0]
  45. libPrefix = None
  46. projectFilename = None
  47. # ------------------------------------------------------------------------------------------------------------
  48. # Log Syntax Highlighter
  49. class LogSyntaxHighlighter(QSyntaxHighlighter):
  50. def __init__(self, parent):
  51. QSyntaxHighlighter.__init__(self, parent)
  52. palette = parent.palette()
  53. self.fColorDebug = palette.color(QPalette.Disabled, QPalette.WindowText)
  54. self.fColorError = Qt.red
  55. # -------------------------------------------------------------
  56. def highlightBlock(self, text):
  57. if text.startswith("DEBUG:"):
  58. self.setFormat(0, len(text), self.fColorDebug)
  59. elif text.startswith("ERROR:"):
  60. self.setFormat(0, len(text), self.fColorError)
  61. # ------------------------------------------------------------------------------------------------------------
  62. # Main Window
  63. class CarlaMainW(QMainWindow):
  64. def __init__(self, parent=None):
  65. QMainWindow.__init__(self, parent)
  66. self.ui = ui_carla.Ui_CarlaMainW()
  67. self.ui.setupUi(self)
  68. # -------------------------------------------------------------
  69. # Internal stuff
  70. self.fBufferSize = 0
  71. self.fSampleRate = 0.0
  72. self.fEngineStarted = False
  73. self.fFirstEngineInit = True
  74. self.fProjectFilename = None
  75. self.fProjectLoading = False
  76. self.fIdleTimerFast = 0
  77. self.fIdleTimerSlow = 0
  78. self.fLastTransportFrame = 0
  79. self.fLastTransportState = False
  80. self.fClientName = "Carla"
  81. self.fSessionManagerName = "LADISH" if LADISH_APP_NAME else ""
  82. # -------------------------------------------------------------
  83. # Load Settings
  84. self.loadSettings(True)
  85. # -------------------------------------------------------------
  86. # Set-up GUI stuff
  87. self.fInfoLabel = QLabel(self)
  88. self.fInfoLabel.setText("")
  89. self.fInfoText = ""
  90. self.fDirModel = QFileSystemModel(self)
  91. self.fDirModel.setNameFilters(cString(Carla.host.get_supported_file_types()).split(";"))
  92. self.fDirModel.setRootPath(HOME)
  93. if not WINDOWS:
  94. self.fSyntaxLog = LogSyntaxHighlighter(self.ui.pte_log)
  95. self.fSyntaxLog.setDocument(self.ui.pte_log.document())
  96. if LADISH_APP_NAME:
  97. self.ui.miniCanvasPreview.setVisible(False)
  98. self.ui.tabMain.removeTab(1)
  99. else:
  100. self.ui.tabMain.removeTab(2)
  101. self.ui.fileTreeView.setModel(self.fDirModel)
  102. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
  103. self.ui.fileTreeView.setColumnHidden(1, True)
  104. self.ui.fileTreeView.setColumnHidden(2, True)
  105. self.ui.fileTreeView.setColumnHidden(3, True)
  106. self.ui.fileTreeView.setHeaderHidden(True)
  107. self.ui.act_engine_start.setEnabled(False)
  108. self.ui.act_engine_stop.setEnabled(False)
  109. self.ui.act_plugin_remove_all.setEnabled(False)
  110. # FIXME: Qt4 needs this so it properly creates & resizes the canvas
  111. self.ui.tabMain.setCurrentIndex(1)
  112. self.ui.tabMain.setCurrentIndex(0)
  113. # -------------------------------------------------------------
  114. # Set-up Canvas Preview
  115. self.ui.miniCanvasPreview.setRealParent(self)
  116. self.ui.miniCanvasPreview.setViewTheme(patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color())
  117. self.ui.miniCanvasPreview.init(self.scene, DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT, self.fSavedSettings["UseCustomMiniCanvasPaint"])
  118. QTimer.singleShot(100, self, SLOT("slot_miniCanvasInit()"))
  119. # -------------------------------------------------------------
  120. # Connect actions to functions
  121. self.connect(self.ui.act_file_new, SIGNAL("triggered()"), SLOT("slot_fileNew()"))
  122. self.connect(self.ui.act_file_open, SIGNAL("triggered()"), SLOT("slot_fileOpen()"))
  123. self.connect(self.ui.act_file_save, SIGNAL("triggered()"), SLOT("slot_fileSave()"))
  124. self.connect(self.ui.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_fileSaveAs()"))
  125. #self.connect(self.ui.act_file_export_lv2, SIGNAL("triggered()"), SLOT("slot_fileExportLv2Preset()"))
  126. self.connect(self.ui.act_engine_start, SIGNAL("triggered()"), SLOT("slot_engineStart()"))
  127. self.connect(self.ui.act_engine_stop, SIGNAL("triggered()"), SLOT("slot_engineStop()"))
  128. self.connect(self.ui.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  129. self.connect(self.ui.act_plugin_add2, SIGNAL("triggered()"), SLOT("slot_pluginAdd()"))
  130. #self.connect(self.ui.act_plugin_refresh, SIGNAL("triggered()"), SLOT("slot_pluginRefresh()"))
  131. self.connect(self.ui.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_pluginRemoveAll()"))
  132. self.connect(self.ui.act_plugins_enable, SIGNAL("triggered()"), SLOT("slot_pluginsEnable()"))
  133. self.connect(self.ui.act_plugins_disable, SIGNAL("triggered()"), SLOT("slot_pluginsDisable()"))
  134. self.connect(self.ui.act_plugins_panic, SIGNAL("triggered()"), SLOT("slot_pluginsDisable()"))
  135. self.connect(self.ui.act_plugins_volume100, SIGNAL("triggered()"), SLOT("slot_pluginsVolume100()"))
  136. self.connect(self.ui.act_plugins_mute, SIGNAL("triggered()"), SLOT("slot_pluginsMute()"))
  137. self.connect(self.ui.act_plugins_wet100, SIGNAL("triggered()"), SLOT("slot_pluginsWet100()"))
  138. self.connect(self.ui.act_plugins_bypass, SIGNAL("triggered()"), SLOT("slot_pluginsBypass()"))
  139. self.connect(self.ui.act_plugins_center, SIGNAL("triggered()"), SLOT("slot_pluginsCenter()"))
  140. self.connect(self.ui.act_transport_play, SIGNAL("triggered(bool)"), SLOT("slot_transportPlayPause(bool)"))
  141. self.connect(self.ui.act_transport_stop, SIGNAL("triggered()"), SLOT("slot_transportStop()"))
  142. self.connect(self.ui.act_transport_backwards, SIGNAL("triggered()"), SLOT("slot_transportBackwards()"))
  143. self.connect(self.ui.act_transport_forwards, SIGNAL("triggered()"), SLOT("slot_transportForwards()"))
  144. self.ui.act_canvas_arrange.setEnabled(False) # TODO, later
  145. self.connect(self.ui.act_canvas_arrange, SIGNAL("triggered()"), SLOT("slot_canvasArrange()"))
  146. self.connect(self.ui.act_canvas_refresh, SIGNAL("triggered()"), SLOT("slot_canvasRefresh()"))
  147. self.connect(self.ui.act_canvas_zoom_fit, SIGNAL("triggered()"), SLOT("slot_canvasZoomFit()"))
  148. self.connect(self.ui.act_canvas_zoom_in, SIGNAL("triggered()"), SLOT("slot_canvasZoomIn()"))
  149. self.connect(self.ui.act_canvas_zoom_out, SIGNAL("triggered()"), SLOT("slot_canvasZoomOut()"))
  150. self.connect(self.ui.act_canvas_zoom_100, SIGNAL("triggered()"), SLOT("slot_canvasZoomReset()"))
  151. self.connect(self.ui.act_canvas_print, SIGNAL("triggered()"), SLOT("slot_canvasPrint()"))
  152. self.connect(self.ui.act_canvas_save_image, SIGNAL("triggered()"), SLOT("slot_canvasSaveImage()"))
  153. self.connect(self.ui.act_settings_show_toolbar, SIGNAL("triggered(bool)"), SLOT("slot_toolbarShown()"))
  154. self.connect(self.ui.act_settings_configure, SIGNAL("triggered()"), SLOT("slot_configureCarla()"))
  155. self.connect(self.ui.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()"))
  156. self.connect(self.ui.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()"))
  157. self.connect(self.ui.splitter, SIGNAL("splitterMoved(int, int)"), SLOT("slot_splitterMoved()"))
  158. self.connect(self.ui.cb_disk, SIGNAL("currentIndexChanged(int)"), SLOT("slot_diskFolderChanged(int)"))
  159. self.connect(self.ui.b_disk_add, SIGNAL("clicked()"), SLOT("slot_diskFolderAdd()"))
  160. self.connect(self.ui.b_disk_remove, SIGNAL("clicked()"), SLOT("slot_diskFolderRemove()"))
  161. self.connect(self.ui.fileTreeView, SIGNAL("doubleClicked(QModelIndex)"), SLOT("slot_fileTreeDoubleClicked(QModelIndex)"))
  162. self.connect(self.ui.miniCanvasPreview, SIGNAL("miniCanvasMoved(double, double)"), SLOT("slot_miniCanvasMoved(double, double)"))
  163. self.connect(self.ui.graphicsView.horizontalScrollBar(), SIGNAL("valueChanged(int)"), SLOT("slot_horizontalScrollBarChanged(int)"))
  164. self.connect(self.ui.graphicsView.verticalScrollBar(), SIGNAL("valueChanged(int)"), SLOT("slot_verticalScrollBarChanged(int)"))
  165. self.connect(self.scene, SIGNAL("sceneGroupMoved(int, int, QPointF)"), SLOT("slot_canvasItemMoved(int, int, QPointF)"))
  166. self.connect(self.scene, SIGNAL("scaleChanged(double)"), SLOT("slot_canvasScaleChanged(double)"))
  167. self.connect(self, SIGNAL("SIGUSR1()"), SLOT("slot_handleSIGUSR1()"))
  168. self.connect(self, SIGNAL("SIGTERM()"), SLOT("slot_handleSIGTERM()"))
  169. self.connect(self, SIGNAL("DebugCallback(int, int, int, double, QString)"), SLOT("slot_handleDebugCallback(int, int, int, double, QString)"))
  170. self.connect(self, SIGNAL("PluginAddedCallback(int)"), SLOT("slot_handlePluginAddedCallback(int)"))
  171. self.connect(self, SIGNAL("PluginRemovedCallback(int)"), SLOT("slot_handlePluginRemovedCallback(int)"))
  172. self.connect(self, SIGNAL("PluginRenamedCallback(int, QString)"), SLOT("slot_handlePluginRenamedCallback(int, QString)"))
  173. self.connect(self, SIGNAL("ParameterValueChangedCallback(int, int, double)"), SLOT("slot_handleParameterValueChangedCallback(int, int, double)"))
  174. self.connect(self, SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), SLOT("slot_handleParameterDefaultChangedCallback(int, int, double)"))
  175. self.connect(self, SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiChannelChangedCallback(int, int, int)"))
  176. self.connect(self, SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), SLOT("slot_handleParameterMidiCcChangedCallback(int, int, int)"))
  177. self.connect(self, SIGNAL("ProgramChangedCallback(int, int)"), SLOT("slot_handleProgramChangedCallback(int, int)"))
  178. self.connect(self, SIGNAL("MidiProgramChangedCallback(int, int)"), SLOT("slot_handleMidiProgramChangedCallback(int, int)"))
  179. self.connect(self, SIGNAL("NoteOnCallback(int, int, int, int)"), SLOT("slot_handleNoteOnCallback(int, int, int, int)"))
  180. self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), SLOT("slot_handleNoteOffCallback(int, int, int)"))
  181. self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), SLOT("slot_handleShowGuiCallback(int, int)"))
  182. self.connect(self, SIGNAL("UpdateCallback(int)"), SLOT("slot_handleUpdateCallback(int)"))
  183. self.connect(self, SIGNAL("ReloadInfoCallback(int)"), SLOT("slot_handleReloadInfoCallback(int)"))
  184. self.connect(self, SIGNAL("ReloadParametersCallback(int)"), SLOT("slot_handleReloadParametersCallback(int)"))
  185. self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), SLOT("slot_handleReloadProgramsCallback(int)"))
  186. self.connect(self, SIGNAL("ReloadAllCallback(int)"), SLOT("slot_handleReloadAllCallback(int)"))
  187. self.connect(self, SIGNAL("PatchbayClientAddedCallback(int, int, QString)"), SLOT("slot_handlePatchbayClientAddedCallback(int, int, QString)"))
  188. self.connect(self, SIGNAL("PatchbayClientRemovedCallback(int, QString)"), SLOT("slot_handlePatchbayClientRemovedCallback(int, QString)"))
  189. self.connect(self, SIGNAL("PatchbayClientRenamedCallback(int, QString)"), SLOT("slot_handlePatchbayClientRenamedCallback(int, QString)"))
  190. self.connect(self, SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), SLOT("slot_handlePatchbayPortAddedCallback(int, int, int, QString)"))
  191. self.connect(self, SIGNAL("PatchbayPortRemovedCallback(int, int, QString)"), SLOT("slot_handlePatchbayPortRemovedCallback(int, int, QString)"))
  192. self.connect(self, SIGNAL("PatchbayPortRenamedCallback(int, int, QString)"), SLOT("slot_handlePatchbayPortRenamedCallback(int, int, QString)"))
  193. self.connect(self, SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), SLOT("slot_handlePatchbayConnectionAddedCallback(int, int, int)"))
  194. self.connect(self, SIGNAL("PatchbayConnectionRemovedCallback(int)"), SLOT("slot_handlePatchbayConnectionRemovedCallback(int)"))
  195. self.connect(self, SIGNAL("PatchbayIconChangedCallback(int, int)"), SLOT("slot_handlePatchbayIconChangedCallback(int, int)"))
  196. self.connect(self, SIGNAL("BufferSizeChangedCallback(int)"), SLOT("slot_handleBufferSizeChangedCallback(int)"))
  197. self.connect(self, SIGNAL("SampleRateChangedCallback(double)"), SLOT("slot_handleSampleRateChangedCallback(double)"))
  198. self.connect(self, SIGNAL("NSM_AnnounceCallback(QString)"), SLOT("slot_handleNSM_AnnounceCallback(QString)"))
  199. self.connect(self, SIGNAL("NSM_OpenCallback(QString)"), SLOT("slot_handleNSM_OpenCallback(QString)"))
  200. self.connect(self, SIGNAL("NSM_SaveCallback()"), SLOT("slot_handleNSM_SaveCallback()"))
  201. self.connect(self, SIGNAL("ErrorCallback(QString)"), SLOT("slot_handleErrorCallback(QString)"))
  202. self.connect(self, SIGNAL("QuitCallback()"), SLOT("slot_handleQuitCallback()"))
  203. self.setProperWindowTitle()
  204. if NSM_URL:
  205. Carla.host.nsm_ready()
  206. else:
  207. QTimer.singleShot(0, self, SLOT("slot_engineStart()"))
  208. def startEngine(self):
  209. # ---------------------------------------------
  210. # Engine settings
  211. settings = QSettings()
  212. audioDriver = settings.value("Engine/AudioDriver", CARLA_DEFAULT_AUDIO_DRIVER, type=str)
  213. transportMode = settings.value("Engine/TransportMode", TRANSPORT_MODE_JACK, type=int)
  214. forceStereo = settings.value("Engine/ForceStereo", CARLA_DEFAULT_FORCE_STEREO, type=bool)
  215. preferPluginBridges = settings.value("Engine/PreferPluginBridges", CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, type=bool)
  216. preferUiBridges = settings.value("Engine/PreferUiBridges", CARLA_DEFAULT_PREFER_UI_BRIDGES, type=bool)
  217. useDssiVstChunks = settings.value("Engine/UseDssiVstChunks", CARLA_DEFAULT_USE_DSSI_VST_CHUNKS, type=bool)
  218. #oscUiTimeout = settings.value("Engine/OscUiTimeout", CARLA_DEFAULT_OSC_UI_TIMEOUT, type=int)
  219. Carla.processMode = settings.value("Engine/ProcessMode", CARLA_DEFAULT_PROCESS_MODE, type=int)
  220. Carla.maxParameters = settings.value("Engine/MaxParameters", CARLA_DEFAULT_MAX_PARAMETERS, type=int)
  221. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  222. forceStereo = True
  223. elif Carla.processMode == PROCESS_MODE_MULTIPLE_CLIENTS and LADISH_APP_NAME:
  224. print("LADISH detected but using multiple clients (not allowed), forcing single client now")
  225. Carla.processMode = PROCESS_MODE_SINGLE_CLIENT
  226. Carla.host.set_engine_option(OPTION_PROCESS_MODE, Carla.processMode, "")
  227. Carla.host.set_engine_option(OPTION_MAX_PARAMETERS, Carla.maxParameters, "")
  228. Carla.host.set_engine_option(OPTION_FORCE_STEREO, forceStereo, "")
  229. Carla.host.set_engine_option(OPTION_PREFER_PLUGIN_BRIDGES, preferPluginBridges, "")
  230. Carla.host.set_engine_option(OPTION_PREFER_UI_BRIDGES, preferUiBridges, "")
  231. Carla.host.set_engine_option(OPTION_USE_DSSI_VST_CHUNKS, useDssiVstChunks, "")
  232. #Carla.host.set_engine_option(OPTION_OSC_UI_TIMEOUT, oscUiTimeout, "")
  233. if audioDriver == "JACK":
  234. pass
  235. #jackAutoConnect = settings.value("Engine/JackAutoConnect", CARLA_DEFAULT_JACK_AUTOCONNECT, type=bool)
  236. #jackTimeMaster = settings.value("Engine/JackTimeMaster", CARLA_DEFAULT_JACK_TIMEMASTER, type=bool)
  237. #if jackAutoConnect and LADISH_APP_NAME:
  238. #print("LADISH detected but using JACK auto-connect (not desired), disabling auto-connect now")
  239. #jackAutoConnect = False
  240. #Carla.host.set_engine_option(OPTION_JACK_AUTOCONNECT, jackAutoConnect, "")
  241. #Carla.host.set_engine_option(OPTION_JACK_TIMEMASTER, jackTimeMaster, "")
  242. else:
  243. rtaudioBufferSize = settings.value("Engine/RtAudioBufferSize", CARLA_DEFAULT_RTAUDIO_BUFFER_SIZE, type=int)
  244. rtaudioSampleRate = settings.value("Engine/RtAudioSampleRate", CARLA_DEFAULT_RTAUDIO_SAMPLE_RATE, type=int)
  245. rtaudioDevice = settings.value("Engine/RtAudioDevice", "", type=str)
  246. Carla.host.set_engine_option(OPTION_RTAUDIO_BUFFER_SIZE, rtaudioBufferSize, "")
  247. Carla.host.set_engine_option(OPTION_RTAUDIO_SAMPLE_RATE, rtaudioSampleRate, "")
  248. Carla.host.set_engine_option(OPTION_RTAUDIO_DEVICE, 0, rtaudioDevice)
  249. # ---------------------------------------------
  250. # Start
  251. if not Carla.host.engine_init(audioDriver, self.fClientName):
  252. if self.fFirstEngineInit:
  253. self.fFirstEngineInit = False
  254. return
  255. audioError = cString(Carla.host.get_last_error())
  256. if audioError:
  257. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
  258. else:
  259. QMessageBox.critical(self, self.tr("Error"), self.tr("Could not connect to Audio backend '%s'" % audioDriver))
  260. return
  261. self.fBufferSize = Carla.host.get_buffer_size()
  262. self.fSampleRate = Carla.host.get_sample_rate()
  263. self.fEngineStarted = True
  264. self.fFirstEngineInit = False
  265. self.fPluginCount = 0
  266. self.fPluginList = []
  267. if transportMode == TRANSPORT_MODE_JACK and audioDriver != "JACK":
  268. transportMode = TRANSPORT_MODE_INTERNAL
  269. Carla.host.set_engine_option(OPTION_TRANSPORT_MODE, transportMode, "")
  270. # Peaks and TimeInfo
  271. self.fIdleTimerFast = self.startTimer(self.fSavedSettings["Main/RefreshInterval"])
  272. # LEDs and edit dialog parameters
  273. self.fIdleTimerSlow = self.startTimer(self.fSavedSettings["Main/RefreshInterval"]*2)
  274. def stopEngine(self):
  275. if self.fPluginCount > 0:
  276. ask = QMessageBox.question(self, self.tr("Warning"), self.tr("There are still some plugins loaded, you need to remove them to stop the engine.\n"
  277. "Do you want to do this now?"),
  278. QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
  279. if ask != QMessageBox.Yes:
  280. return
  281. self.removeAllPlugins()
  282. self.fEngineStarted = False
  283. if Carla.host.is_engine_running() and not Carla.host.engine_close():
  284. print(cString(Carla.host.get_last_error()))
  285. self.fBufferSize = 0
  286. self.fSampleRate = 0.0
  287. self.fPluginCount = 0
  288. self.fPluginList = []
  289. self.killTimer(self.fIdleTimerFast)
  290. self.killTimer(self.fIdleTimerSlow)
  291. patchcanvas.clear()
  292. def loadProject(self, filename):
  293. self.fProjectFilename = filename
  294. self.setProperWindowTitle()
  295. self.fProjectLoading = True
  296. Carla.host.load_project(filename)
  297. self.fProjectLoading = False
  298. def loadProjectLater(self, filename):
  299. self.fProjectFilename = filename
  300. self.setProperWindowTitle()
  301. QTimer.singleShot(0, self, SLOT("slot_loadProjectLater()"))
  302. def saveProject(self, filename):
  303. self.fProjectFilename = filename
  304. self.setProperWindowTitle()
  305. Carla.host.save_project(filename)
  306. def addPlugin(self, btype, ptype, filename, name, label, extraStuff):
  307. if not self.fEngineStarted:
  308. QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
  309. return False
  310. if not Carla.host.add_plugin(btype, ptype, filename, name, label, extraStuff):
  311. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  312. return False
  313. return True
  314. def removeAllPlugins(self):
  315. while (self.ui.w_plugins.layout().takeAt(0)):
  316. pass
  317. self.ui.act_plugin_remove_all.setEnabled(False)
  318. for i in range(self.fPluginCount):
  319. pwidget = self.fPluginList[i]
  320. if pwidget is None:
  321. break
  322. pwidget.ui.edit_dialog.close()
  323. pwidget.close()
  324. pwidget.deleteLater()
  325. del pwidget
  326. self.fPluginCount = 0
  327. self.fPluginList = []
  328. Carla.host.remove_all_plugins()
  329. def menuTransport(self, enabled):
  330. self.ui.act_transport_play.setEnabled(enabled)
  331. self.ui.act_transport_stop.setEnabled(enabled)
  332. self.ui.act_transport_backwards.setEnabled(enabled)
  333. self.ui.act_transport_forwards.setEnabled(enabled)
  334. self.ui.menu_Transport.setEnabled(enabled)
  335. def refreshTransport(self, forced = False):
  336. if not self.fEngineStarted:
  337. return
  338. if self.fSampleRate == 0.0:
  339. return
  340. timeInfo = Carla.host.get_transport_info()
  341. playing = bool(timeInfo['playing'])
  342. frame = int(timeInfo['frame'])
  343. if playing != self.fLastTransportState or forced:
  344. if playing:
  345. icon = getIcon("media-playback-pause")
  346. self.ui.act_transport_play.setChecked(True)
  347. self.ui.act_transport_play.setIcon(icon)
  348. self.ui.act_transport_play.setText(self.tr("&Pause"))
  349. else:
  350. icon = getIcon("media-playback-start")
  351. self.ui.act_transport_play.setChecked(False)
  352. self.ui.act_transport_play.setIcon(icon)
  353. self.ui.act_transport_play.setText(self.tr("&Play"))
  354. self.fLastTransportState = playing
  355. if frame != self.fLastTransportFrame or forced:
  356. time = frame / self.fSampleRate
  357. secs = time % 60
  358. mins = (time / 60) % 60
  359. hrs = (time / 3600) % 60
  360. textTransport = "Transport %s, at %02i:%02i:%02i" % ("playing" if playing else "stopped", hrs, mins, secs)
  361. self.fInfoLabel.setText("%s | %s" % (self.fInfoText, textTransport))
  362. self.fLastTransportFrame = frame
  363. def setProperWindowTitle(self):
  364. title = LADISH_APP_NAME if LADISH_APP_NAME else "Carla"
  365. if self.fProjectFilename:
  366. title += " - %s" % os.path.basename(self.fProjectFilename)
  367. if self.fSessionManagerName:
  368. title += " (%s)" % self.fSessionManagerName
  369. self.setWindowTitle(title)
  370. def updateInfoLabelPos(self):
  371. tabBar = self.ui.tabMain.tabBar()
  372. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()+tabBar.height()/4
  373. if not self.ui.toolBar.isVisible():
  374. y -= self.ui.toolBar.height()
  375. self.fInfoLabel.move(self.fInfoLabel.x(), y)
  376. def updateInfoLabelSize(self):
  377. tabBar = self.ui.tabMain.tabBar()
  378. self.fInfoLabel.resize(self.ui.tabMain.width()-tabBar.width()-20, self.fInfoLabel.height())
  379. @pyqtSlot()
  380. def slot_fileNew(self):
  381. self.removeAllPlugins()
  382. self.fProjectFilename = None
  383. self.fProjectLoading = False
  384. self.setProperWindowTitle()
  385. @pyqtSlot()
  386. def slot_fileOpen(self):
  387. fileFilter = self.tr("Carla Project File (*.carxp)")
  388. filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  389. if filenameTry:
  390. # FIXME - show dialog to user (remove all plugins?)
  391. self.removeAllPlugins()
  392. self.loadProject(filenameTry)
  393. @pyqtSlot()
  394. def slot_fileSave(self, saveAs=False):
  395. if self.fProjectFilename and not saveAs:
  396. return self.saveProject(self.fProjectFilename)
  397. fileFilter = self.tr("Carla Project File (*.carxp)")
  398. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  399. if not filenameTry:
  400. return
  401. if not filenameTry.endswith(".carxp"):
  402. filenameTry += ".carxp"
  403. self.saveProject(filenameTry)
  404. @pyqtSlot()
  405. def slot_fileSaveAs(self):
  406. self.slot_fileSave(True)
  407. @pyqtSlot()
  408. def slot_fileExportLv2Preset(self):
  409. fileFilter = self.tr("LV2 Preset (*.lv2)")
  410. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings["Main/DefaultProjectFolder"], filter=fileFilter)
  411. if not filenameTry:
  412. return
  413. if not filenameTry.endswith(".lv2"):
  414. filenameTry += ".lv2"
  415. if os.path.exists(filenameTry) and not os.path.isdir(filenameTry):
  416. # TODO - error
  417. return
  418. # Save current project to a tmp file, and read it
  419. tmpFile = os.path.join(TMP, "carla-plugin-export.carxp")
  420. if not Carla.host.save_project(tmpFile):
  421. # TODO - error
  422. return
  423. tmpFileFd = open(tmpFile, "r")
  424. presetContents = tmpFileFd.read()
  425. tmpFileFd.close()
  426. os.remove(tmpFile)
  427. # Create LV2 Preset
  428. os.mkdir(filenameTry)
  429. manifestPath = os.path.join(filenameTry, "manifest.ttl")
  430. manifestFd = open(manifestPath, "w")
  431. manifestFd.write("""# LV2 Preset for the Carla LV2 Plugin
  432. @prefix lv2: <http://lv2plug.in/ns/lv2core#> .
  433. @prefix pset: <http://lv2plug.in/ns/ext/presets#> .
  434. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
  435. @prefix state: <http://lv2plug.in/ns/ext/state#> .
  436. <file://%s>
  437. a pset:Preset ;
  438. lv2:appliesTo <http://kxstudio.sf.net/carla> ;
  439. rdfs:label "%s" ;
  440. state:state [
  441. <http://kxstudio.sf.net/ns/carla/string>
  442. \"\"\"
  443. %s
  444. \"\"\"
  445. ] .
  446. """ % (manifestPath, os.path.basename(filenameTry), presetContents))
  447. manifestFd.close()
  448. @pyqtSlot()
  449. def slot_loadProjectLater(self):
  450. self.fProjectLoading = True
  451. Carla.host.load_project(self.fProjectFilename)
  452. self.fProjectLoading = False
  453. @pyqtSlot()
  454. def slot_engineStart(self):
  455. self.startEngine()
  456. check = Carla.host.is_engine_running()
  457. self.ui.act_engine_start.setEnabled(not check)
  458. self.ui.act_engine_stop.setEnabled(check)
  459. if self.fSessionManagerName != "Non Session Manager":
  460. self.ui.act_file_open.setEnabled(check)
  461. if check:
  462. self.fInfoText = "Engine running | SampleRate: %g | BufferSize: %i" % (self.fSampleRate, self.fBufferSize)
  463. self.refreshTransport(True)
  464. self.menuTransport(check)
  465. @pyqtSlot()
  466. def slot_engineStop(self):
  467. self.stopEngine()
  468. check = Carla.host.is_engine_running()
  469. self.ui.act_engine_start.setEnabled(not check)
  470. self.ui.act_engine_stop.setEnabled(check)
  471. if self.fSessionManagerName != "Non Session Manager":
  472. self.ui.act_file_open.setEnabled(check)
  473. if not check:
  474. self.fInfoText = ""
  475. self.fInfoLabel.setText("Engine stopped")
  476. self.menuTransport(check)
  477. @pyqtSlot()
  478. def slot_pluginAdd(self):
  479. dialog = PluginDatabaseW(self)
  480. if dialog.exec_():
  481. btype = dialog.fRetPlugin['build']
  482. ptype = dialog.fRetPlugin['type']
  483. filename = dialog.fRetPlugin['binary']
  484. label = dialog.fRetPlugin['label']
  485. extraStuff = self.getExtraStuff(dialog.fRetPlugin)
  486. self.addPlugin(btype, ptype, filename, None, label, extraStuff)
  487. @pyqtSlot()
  488. def slot_pluginRemoveAll(self):
  489. self.removeAllPlugins()
  490. @pyqtSlot()
  491. def slot_pluginsEnable(self):
  492. if not self.fEngineStarted:
  493. return
  494. for i in range(self.fPluginCount):
  495. pwidget = self.fPluginList[i]
  496. if pwidget is None:
  497. break
  498. pwidget.setActive(True, True, True)
  499. @pyqtSlot()
  500. def slot_pluginsDisable(self):
  501. if not self.fEngineStarted:
  502. return
  503. for i in range(self.fPluginCount):
  504. pwidget = self.fPluginList[i]
  505. if pwidget is None:
  506. break
  507. pwidget.setActive(False, True, True)
  508. @pyqtSlot()
  509. def slot_pluginsVolume100(self):
  510. if not self.fEngineStarted:
  511. return
  512. for i in range(self.fPluginCount):
  513. pwidget = self.fPluginList[i]
  514. if pwidget is None:
  515. break
  516. if pwidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  517. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 1.0)
  518. Carla.host.set_volume(i, 1.0)
  519. @pyqtSlot()
  520. def slot_pluginsMute(self):
  521. if not self.fEngineStarted:
  522. return
  523. for i in range(self.fPluginCount):
  524. pwidget = self.fPluginList[i]
  525. if pwidget is None:
  526. break
  527. if pwidget.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
  528. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_VOLUME, 0.0)
  529. Carla.host.set_volume(i, 0.0)
  530. @pyqtSlot()
  531. def slot_pluginsWet100(self):
  532. if not self.fEngineStarted:
  533. return
  534. for i in range(self.fPluginCount):
  535. pwidget = self.fPluginList[i]
  536. if pwidget is None:
  537. break
  538. if pwidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  539. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 1.0)
  540. Carla.host.set_drywet(i, 1.0)
  541. @pyqtSlot()
  542. def slot_pluginsBypass(self):
  543. if not self.fEngineStarted:
  544. return
  545. for i in range(self.fPluginCount):
  546. pwidget = self.fPluginList[i]
  547. if pwidget is None:
  548. break
  549. if pwidget.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
  550. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_DRYWET, 0.0)
  551. Carla.host.set_drywet(i, 0.0)
  552. @pyqtSlot()
  553. def slot_pluginsCenter(self):
  554. if not self.fEngineStarted:
  555. return
  556. for i in range(self.fPluginCount):
  557. pwidget = self.fPluginList[i]
  558. if pwidget is None:
  559. break
  560. if pwidget.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE:
  561. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_LEFT, -1.0)
  562. pwidget.ui.edit_dialog.setParameterValue(PARAMETER_BALANCE_RIGHT, 1.0)
  563. Carla.host.set_balance_left(i, -1.0)
  564. Carla.host.set_balance_right(i, 1.0)
  565. @pyqtSlot(bool)
  566. def slot_transportPlayPause(self, toggled):
  567. if not self.fEngineStarted:
  568. return
  569. if toggled:
  570. Carla.host.transport_play()
  571. else:
  572. Carla.host.transport_pause()
  573. self.refreshTransport()
  574. @pyqtSlot()
  575. def slot_transportStop(self):
  576. if not self.fEngineStarted:
  577. return
  578. Carla.host.transport_pause()
  579. Carla.host.transport_relocate(0)
  580. self.refreshTransport()
  581. @pyqtSlot()
  582. def slot_transportBackwards(self):
  583. if not self.fEngineStarted:
  584. return
  585. newFrame = Carla.host.get_current_transport_frame() - 100000
  586. if newFrame < 0:
  587. newFrame = 0
  588. Carla.host.transport_relocate(newFrame)
  589. @pyqtSlot()
  590. def slot_transportForwards(self):
  591. if not self.fEngineStarted:
  592. return
  593. newFrame = Carla.host.get_current_transport_frame() + 100000
  594. Carla.host.transport_relocate(newFrame)
  595. @pyqtSlot()
  596. def slot_toolbarShown(self):
  597. self.updateInfoLabelPos()
  598. @pyqtSlot()
  599. def slot_configureCarla(self):
  600. dialog = CarlaSettingsW(self)
  601. if dialog.exec_():
  602. self.loadSettings(False)
  603. patchcanvas.clear()
  604. pOptions = patchcanvas.options_t()
  605. pOptions.theme_name = self.fSavedSettings["Canvas/Theme"]
  606. pOptions.auto_hide_groups = self.fSavedSettings["Canvas/AutoHideGroups"]
  607. pOptions.use_bezier_lines = self.fSavedSettings["Canvas/UseBezierLines"]
  608. pOptions.antialiasing = self.fSavedSettings["Canvas/Antialiasing"]
  609. pOptions.eyecandy = self.fSavedSettings["Canvas/EyeCandy"]
  610. pFeatures = patchcanvas.features_t()
  611. pFeatures.group_info = False
  612. pFeatures.group_rename = False
  613. pFeatures.port_info = False
  614. pFeatures.port_rename = False
  615. pFeatures.handle_group_pos = True
  616. patchcanvas.setOptions(pOptions)
  617. patchcanvas.setFeatures(pFeatures)
  618. patchcanvas.init("Carla", self.scene, canvasCallback, False)
  619. if self.fEngineStarted:
  620. Carla.host.patchbay_refresh()
  621. @pyqtSlot()
  622. def slot_aboutCarla(self):
  623. CarlaAboutW(self).exec_()
  624. @pyqtSlot()
  625. def slot_splitterMoved(self):
  626. self.updateInfoLabelSize()
  627. @pyqtSlot(int)
  628. def slot_diskFolderChanged(self, index):
  629. if index < 0:
  630. return
  631. elif index == 0:
  632. filename = HOME
  633. self.ui.b_disk_remove.setEnabled(False)
  634. else:
  635. filename = self.ui.cb_disk.itemData(index)
  636. self.ui.b_disk_remove.setEnabled(True)
  637. self.fDirModel.setRootPath(filename)
  638. self.ui.fileTreeView.setRootIndex(self.fDirModel.index(filename))
  639. @pyqtSlot()
  640. def slot_diskFolderAdd(self):
  641. newPath = QFileDialog.getExistingDirectory(self, self.tr("New Folder"), "", QFileDialog.ShowDirsOnly)
  642. if newPath:
  643. if newPath[-1] == os.sep:
  644. newPath = newPath[:-1]
  645. self.ui.cb_disk.addItem(os.path.basename(newPath), newPath)
  646. self.ui.cb_disk.setCurrentIndex(self.ui.cb_disk.count()-1)
  647. self.ui.b_disk_remove.setEnabled(True)
  648. @pyqtSlot()
  649. def slot_diskFolderRemove(self):
  650. index = self.ui.cb_disk.currentIndex()
  651. if index <= 0:
  652. return
  653. self.ui.cb_disk.removeItem(index)
  654. if self.ui.cb_disk.currentIndex() == 0:
  655. self.ui.b_disk_remove.setEnabled(False)
  656. @pyqtSlot(QModelIndex)
  657. def slot_fileTreeDoubleClicked(self, modelIndex):
  658. filename = self.fDirModel.filePath(modelIndex)
  659. if not Carla.host.load_filename(filename):
  660. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  661. self.tr("Failed to load file"),
  662. cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  663. @pyqtSlot(float, float)
  664. def slot_miniCanvasMoved(self, xp, yp):
  665. self.ui.graphicsView.horizontalScrollBar().setValue(xp * DEFAULT_CANVAS_WIDTH)
  666. self.ui.graphicsView.verticalScrollBar().setValue(yp * DEFAULT_CANVAS_HEIGHT)
  667. @pyqtSlot(int)
  668. def slot_horizontalScrollBarChanged(self, value):
  669. maximum = self.ui.graphicsView.horizontalScrollBar().maximum()
  670. if maximum == 0:
  671. xp = 0
  672. else:
  673. xp = float(value) / maximum
  674. self.ui.miniCanvasPreview.setViewPosX(xp)
  675. @pyqtSlot(int)
  676. def slot_verticalScrollBarChanged(self, value):
  677. maximum = self.ui.graphicsView.verticalScrollBar().maximum()
  678. if maximum == 0:
  679. yp = 0
  680. else:
  681. yp = float(value) / maximum
  682. self.ui.miniCanvasPreview.setViewPosY(yp)
  683. @pyqtSlot(int, int, QPointF)
  684. def slot_canvasItemMoved(self, group_id, split_mode, pos):
  685. self.ui.miniCanvasPreview.update()
  686. @pyqtSlot(float)
  687. def slot_canvasScaleChanged(self, scale):
  688. self.ui.miniCanvasPreview.setViewScale(scale)
  689. @pyqtSlot()
  690. def slot_handleSIGUSR1(self):
  691. print("Got SIGUSR1 -> Saving project now")
  692. QTimer.singleShot(0, self, SLOT("slot_fileSave()"))
  693. @pyqtSlot()
  694. def slot_handleSIGTERM(self):
  695. print("Got SIGTERM -> Closing now")
  696. self.close()
  697. @pyqtSlot()
  698. def slot_miniCanvasInit(self):
  699. settings = QSettings()
  700. self.ui.graphicsView.horizontalScrollBar().setValue(settings.value("HorizontalScrollBarValue", DEFAULT_CANVAS_WIDTH / 3, type=int))
  701. self.ui.graphicsView.verticalScrollBar().setValue(settings.value("VerticalScrollBarValue", DEFAULT_CANVAS_HEIGHT * 3 / 8, type=int))
  702. tabBar = self.ui.tabMain.tabBar()
  703. x = tabBar.width()+20
  704. y = tabBar.mapFromParent(self.ui.centralwidget.pos()).y()+tabBar.height()/4
  705. self.fInfoLabel.move(x, y)
  706. self.fInfoLabel.resize(self.ui.tabMain.width()-x, tabBar.height())
  707. @pyqtSlot()
  708. def slot_miniCanvasCheckAll(self):
  709. self.slot_miniCanvasCheckSize()
  710. self.slot_horizontalScrollBarChanged(self.ui.graphicsView.horizontalScrollBar().value())
  711. self.slot_verticalScrollBarChanged(self.ui.graphicsView.verticalScrollBar().value())
  712. @pyqtSlot()
  713. def slot_miniCanvasCheckSize(self):
  714. self.ui.miniCanvasPreview.setViewSize(float(self.ui.graphicsView.width()) / DEFAULT_CANVAS_WIDTH, float(self.ui.graphicsView.height()) / DEFAULT_CANVAS_HEIGHT)
  715. @pyqtSlot(int, int, int, float, str)
  716. def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valueStr):
  717. self.ui.pte_log.appendPlainText(valueStr.replace("", "DEBUG: ").replace("", "ERROR: ").replace("", "").replace("\n", ""))
  718. @pyqtSlot(int)
  719. def slot_handlePluginAddedCallback(self, pluginId):
  720. pwidget = PluginWidget(self, pluginId)
  721. self.ui.w_plugins.layout().addWidget(pwidget)
  722. self.fPluginList.append(pwidget)
  723. self.fPluginCount += 1
  724. if not self.fProjectLoading:
  725. pwidget.setActive(True, True, True)
  726. if self.fPluginCount == 1:
  727. self.ui.act_plugin_remove_all.setEnabled(True)
  728. @pyqtSlot(int)
  729. def slot_handlePluginRemovedCallback(self, pluginId):
  730. if pluginId >= self.fPluginCount:
  731. return
  732. pwidget = self.fPluginList[pluginId]
  733. if pwidget is None:
  734. return
  735. self.fPluginCount -= 1
  736. self.fPluginList.pop(pluginId)
  737. self.ui.w_plugins.layout().removeWidget(pwidget)
  738. pwidget.ui.edit_dialog.close()
  739. pwidget.close()
  740. pwidget.deleteLater()
  741. del pwidget
  742. # push all plugins 1 slot back
  743. for i in range(pluginId, self.fPluginCount):
  744. self.fPluginList[i].setId(i)
  745. if self.fPluginCount == 0:
  746. self.ui.act_plugin_remove_all.setEnabled(False)
  747. @pyqtSlot(int, str)
  748. def slot_handlePluginRenamedCallback(self, pluginId, newName):
  749. if pluginId >= self.fPluginCount:
  750. return
  751. pwidget = self.fPluginList[pluginId]
  752. if pwidget is None:
  753. return
  754. pwidget.ui.label_name.setText(newName)
  755. @pyqtSlot(int, int, float)
  756. def slot_handleParameterValueChangedCallback(self, pluginId, parameterId, value):
  757. if pluginId >= self.fPluginCount:
  758. return
  759. pwidget = self.fPluginList[pluginId]
  760. if pwidget is None:
  761. return
  762. pwidget.setParameterValue(parameterId, value)
  763. @pyqtSlot(int, int, float)
  764. def slot_handleParameterDefaultChangedCallback(self, pluginId, parameterId, value):
  765. if pluginId >= self.fPluginCount:
  766. return
  767. pwidget = self.fPluginList[pluginId]
  768. if pwidget is None:
  769. return
  770. pwidget.setParameterDefault(parameterId, value)
  771. @pyqtSlot(int, int, int)
  772. def slot_handleParameterMidiChannelChangedCallback(self, pluginId, parameterId, channel):
  773. if pluginId >= self.fPluginCount:
  774. return
  775. pwidget = self.fPluginList[pluginId]
  776. if pwidget is None:
  777. return
  778. pwidget.setParameterMidiChannel(parameterId, channel)
  779. @pyqtSlot(int, int, int)
  780. def slot_handleParameterMidiCcChangedCallback(self, pluginId, parameterId, cc):
  781. if pluginId >= self.fPluginCount:
  782. return
  783. pwidget = self.fPluginList[pluginId]
  784. if pwidget is None:
  785. return
  786. pwidget.setParameterMidiControl(parameterId, cc)
  787. @pyqtSlot(int, int)
  788. def slot_handleProgramChangedCallback(self, pluginId, programId):
  789. if pluginId >= self.fPluginCount:
  790. return
  791. pwidget = self.fPluginList[pluginId]
  792. if pwidget is None:
  793. return
  794. pwidget.setProgram(programId)
  795. @pyqtSlot(int, int)
  796. def slot_handleMidiProgramChangedCallback(self, pluginId, midiProgramId):
  797. if pluginId >= self.fPluginCount:
  798. return
  799. pwidget = self.fPluginList[pluginId]
  800. if pwidget is None:
  801. return
  802. pwidget.setMidiProgram(midiProgramId)
  803. @pyqtSlot(int, int, int, int)
  804. def slot_handleNoteOnCallback(self, pluginId, channel, note, velo):
  805. if pluginId >= self.fPluginCount:
  806. return
  807. pwidget = self.fPluginList[pluginId]
  808. if pwidget is None:
  809. return
  810. pwidget.sendNoteOn(channel, note)
  811. @pyqtSlot(int, int, int)
  812. def slot_handleNoteOffCallback(self, pluginId, channel, note):
  813. if pluginId >= self.fPluginCount:
  814. return
  815. pwidget = self.fPluginList[pluginId]
  816. if pwidget is None:
  817. return
  818. pwidget.sendNoteOff(channel, note)
  819. @pyqtSlot(int, int)
  820. def slot_handleShowGuiCallback(self, pluginId, show):
  821. if pluginId >= self.fPluginCount:
  822. return
  823. pwidget = self.fPluginList[pluginId]
  824. if pwidget is None:
  825. return
  826. if show == 0:
  827. pwidget.ui.b_gui.setChecked(False)
  828. pwidget.ui.b_gui.setEnabled(True)
  829. elif show == 1:
  830. pwidget.ui.b_gui.setChecked(True)
  831. pwidget.ui.b_gui.setEnabled(True)
  832. elif show == -1:
  833. pwidget.ui.b_gui.setChecked(False)
  834. pwidget.ui.b_gui.setEnabled(False)
  835. @pyqtSlot(int)
  836. def slot_handleUpdateCallback(self, pluginId):
  837. if pluginId >= self.fPluginCount:
  838. return
  839. pwidget = self.fPluginList[pluginId]
  840. if pwidget is None:
  841. return
  842. pwidget.ui.edit_dialog.updateInfo()
  843. @pyqtSlot(int)
  844. def slot_handleReloadInfoCallback(self, pluginId):
  845. if pluginId >= self.fPluginCount:
  846. return
  847. pwidget = self.fPluginList[pluginId]
  848. if pwidget is None:
  849. return
  850. pwidget.ui.edit_dialog.reloadInfo()
  851. @pyqtSlot(int)
  852. def slot_handleReloadParametersCallback(self, pluginId):
  853. if pluginId >= self.fPluginCount:
  854. return
  855. pwidget = self.fPluginList[pluginId]
  856. if pwidget is None:
  857. return
  858. pwidget.ui.edit_dialog.reloadParameters()
  859. @pyqtSlot(int)
  860. def slot_handleReloadProgramsCallback(self, pluginId):
  861. if pluginId >= self.fPluginCount:
  862. return
  863. pwidget = self.fPluginList[pluginId]
  864. if pwidget is None:
  865. return
  866. pwidget.ui.edit_dialog.reloadPrograms()
  867. @pyqtSlot(int)
  868. def slot_handleReloadAllCallback(self, pluginId):
  869. if pluginId >= self.fPluginCount:
  870. return
  871. pwidget = self.fPluginList[pluginId]
  872. if pwidget is None:
  873. return
  874. pwidget.ui.edit_dialog.reloadAll()
  875. @pyqtSlot(int, int, str)
  876. def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, clientName):
  877. pcSplit = patchcanvas.SPLIT_UNDEF
  878. pcIcon = patchcanvas.ICON_APPLICATION
  879. if clientIcon == PATCHBAY_ICON_HARDWARE:
  880. pcSplit = patchcanvas.SPLIT_YES
  881. pcIcon = patchcanvas.ICON_HARDWARE
  882. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  883. pcIcon = patchcanvas.ICON_DISTRHO
  884. elif clientIcon == PATCHBAY_ICON_FILE:
  885. pcIcon = patchcanvas.ICON_FILE
  886. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  887. pcIcon = patchcanvas.ICON_PLUGIN
  888. patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
  889. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  890. @pyqtSlot(int, str)
  891. def slot_handlePatchbayClientRemovedCallback(self, clientId, clientName):
  892. if not self.fEngineStarted: return
  893. patchcanvas.removeGroup(clientId)
  894. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  895. @pyqtSlot(int, str)
  896. def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
  897. patchcanvas.renameGroup(clientId, newClientName)
  898. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  899. @pyqtSlot(int, int, int, str)
  900. def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portName):
  901. if (portFlags & PATCHBAY_PORT_IS_INPUT):
  902. portMode = patchcanvas.PORT_MODE_INPUT
  903. elif (portFlags & PATCHBAY_PORT_IS_OUTPUT):
  904. portMode = patchcanvas.PORT_MODE_OUTPUT
  905. else:
  906. portMode = patchcanvas.PORT_MODE_NULL
  907. if (portFlags & PATCHBAY_PORT_IS_AUDIO):
  908. portType = patchcanvas.PORT_TYPE_AUDIO_JACK
  909. elif (portFlags & PATCHBAY_PORT_IS_MIDI):
  910. portType = patchcanvas.PORT_TYPE_MIDI_JACK
  911. else:
  912. portType = patchcanvas.PORT_TYPE_NULL
  913. patchcanvas.addPort(clientId, portId, portName, portMode, portType)
  914. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  915. @pyqtSlot(int, int, str)
  916. def slot_handlePatchbayPortRemovedCallback(self, groupId, portId, fullPortName):
  917. if not self.fEngineStarted: return
  918. patchcanvas.removePort(portId)
  919. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  920. @pyqtSlot(int, int, str)
  921. def slot_handlePatchbayPortRenamedCallback(self, groupId, portId, newPortName):
  922. patchcanvas.renamePort(portId, newPortName)
  923. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  924. @pyqtSlot(int, int, int)
  925. def slot_handlePatchbayConnectionAddedCallback(self, connectionId, portOutId, portInId):
  926. patchcanvas.connectPorts(connectionId, portOutId, portInId)
  927. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  928. @pyqtSlot(int)
  929. def slot_handlePatchbayConnectionRemovedCallback(self, connectionId):
  930. if not self.fEngineStarted: return
  931. patchcanvas.disconnectPorts(connectionId)
  932. QTimer.singleShot(0, self.ui.miniCanvasPreview, SLOT("update()"))
  933. @pyqtSlot(int, int)
  934. def slot_handlePatchbayIconChangedCallback(self, clientId, clientIcon):
  935. pcIcon = patchcanvas.ICON_APPLICATION
  936. if clientIcon == PATCHBAY_ICON_HARDWARE:
  937. pcIcon = patchcanvas.ICON_HARDWARE
  938. elif clientIcon == PATCHBAY_ICON_DISTRHO:
  939. pcIcon = patchcanvas.ICON_DISTRHO
  940. elif clientIcon == PATCHBAY_ICON_FILE:
  941. pcIcon = patchcanvas.ICON_FILE
  942. elif clientIcon == PATCHBAY_ICON_PLUGIN:
  943. pcIcon = patchcanvas.ICON_PLUGIN
  944. patchcanvas.setGroupIcon(clientId, pcIcon)
  945. @pyqtSlot(int)
  946. def slot_handleBufferSizeChangedCallback(self, newBufferSize):
  947. self.fBufferSize = newBufferSize
  948. self.fInfoText = "Engine running | SampleRate: %g | BufferSize: %i" % (self.fSampleRate, self.fBufferSize)
  949. @pyqtSlot(float)
  950. def slot_handleSampleRateChangedCallback(self, newSampleRate):
  951. self.fSampleRate = newSampleRate
  952. self.fInfoText = "Engine running | SampleRate: %g | BufferSize: %i" % (self.fSampleRate, self.fBufferSize)
  953. @pyqtSlot(str)
  954. def slot_handleNSM_AnnounceCallback(self, smName):
  955. self.fSessionManagerName = smName
  956. self.ui.act_file_new.setEnabled(False)
  957. self.ui.act_file_open.setEnabled(False)
  958. self.ui.act_file_save_as.setEnabled(False)
  959. self.ui.act_engine_start.setEnabled(True)
  960. self.ui.act_engine_stop.setEnabled(False)
  961. @pyqtSlot(str)
  962. def slot_handleNSM_OpenCallback(self, data):
  963. projectPath, clientId = data.rsplit(":", 1)
  964. self.fClientName = clientId
  965. # restart engine
  966. if self.fEngineStarted:
  967. self.stopEngine()
  968. self.slot_engineStart()
  969. if self.fEngineStarted:
  970. self.loadProject(projectPath)
  971. Carla.host.nsm_reply_open()
  972. @pyqtSlot()
  973. def slot_handleNSM_SaveCallback(self):
  974. self.saveProject(self.fProjectFilename)
  975. Carla.host.nsm_reply_save()
  976. @pyqtSlot(str)
  977. def slot_handleErrorCallback(self, error):
  978. QMessageBox.critical(self, self.tr("Error"), error)
  979. @pyqtSlot()
  980. def slot_handleQuitCallback(self):
  981. CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"),
  982. self.tr("Engine has been stopped or crashed.\nPlease restart Carla"),
  983. self.tr("You may want to save your session now..."), QMessageBox.Ok, QMessageBox.Ok)
  984. def loadSettings(self, geometry):
  985. settings = QSettings()
  986. if geometry:
  987. self.restoreGeometry(settings.value("Geometry", ""))
  988. showToolbar = settings.value("ShowToolbar", True, type=bool)
  989. self.ui.act_settings_show_toolbar.setChecked(showToolbar)
  990. self.ui.toolBar.setVisible(showToolbar)
  991. if settings.contains("SplitterState"):
  992. self.ui.splitter.restoreState(settings.value("SplitterState", ""))
  993. else:
  994. self.ui.splitter.setSizes([99999, 210])
  995. diskFolders = toList(settings.value("DiskFolders", [HOME]))
  996. self.ui.cb_disk.setItemData(0, HOME)
  997. for i in range(len(diskFolders)):
  998. if i == 0: continue
  999. folder = diskFolders[i]
  1000. self.ui.cb_disk.addItem(os.path.basename(folder), folder)
  1001. if MACOS and not settings.value("Main/UseProTheme", True, type=bool):
  1002. self.setUnifiedTitleAndToolBarOnMac(True)
  1003. useCustomMiniCanvasPaint = bool(settings.value("Main/UseProTheme", True, type=bool) and
  1004. settings.value("Main/ProThemeColor", "Black", type=str) == "Black")
  1005. self.fSavedSettings = {
  1006. "Main/DefaultProjectFolder": settings.value("Main/DefaultProjectFolder", HOME, type=str),
  1007. "Main/RefreshInterval": settings.value("Main/RefreshInterval", 50, type=int),
  1008. "Canvas/Theme": settings.value("Canvas/Theme", patchcanvas.getDefaultThemeName(), type=str),
  1009. "Canvas/AutoHideGroups": settings.value("Canvas/AutoHideGroups", False, type=bool),
  1010. "Canvas/UseBezierLines": settings.value("Canvas/UseBezierLines", True, type=bool),
  1011. "Canvas/EyeCandy": settings.value("Canvas/EyeCandy", patchcanvas.EYECANDY_SMALL, type=int),
  1012. "Canvas/UseOpenGL": settings.value("Canvas/UseOpenGL", False, type=bool),
  1013. "Canvas/Antialiasing": settings.value("Canvas/Antialiasing", patchcanvas.ANTIALIASING_SMALL, type=int),
  1014. "Canvas/HighQualityAntialiasing": settings.value("Canvas/HighQualityAntialiasing", False, type=bool),
  1015. "UseCustomMiniCanvasPaint": useCustomMiniCanvasPaint
  1016. }
  1017. # ---------------------------------------------
  1018. # plugin checks
  1019. if settings.value("Engine/DisableChecks", False, type=bool):
  1020. os.environ["CARLA_DISCOVERY_NO_PROCESSING_CHECKS"] = "true"
  1021. elif os.getenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS"):
  1022. os.environ.pop("CARLA_DISCOVERY_NO_PROCESSING_CHECKS")
  1023. # ---------------------------------------------
  1024. # plugin paths
  1025. Carla.LADSPA_PATH = toList(settings.value("Paths/LADSPA", Carla.LADSPA_PATH))
  1026. Carla.DSSI_PATH = toList(settings.value("Paths/DSSI", Carla.DSSI_PATH))
  1027. Carla.LV2_PATH = toList(settings.value("Paths/LV2", Carla.LV2_PATH))
  1028. Carla.VST_PATH = toList(settings.value("Paths/VST", Carla.VST_PATH))
  1029. Carla.GIG_PATH = toList(settings.value("Paths/GIG", Carla.GIG_PATH))
  1030. Carla.SF2_PATH = toList(settings.value("Paths/SF2", Carla.SF2_PATH))
  1031. Carla.SFZ_PATH = toList(settings.value("Paths/SFZ", Carla.SFZ_PATH))
  1032. os.environ["LADSPA_PATH"] = splitter.join(Carla.LADSPA_PATH)
  1033. os.environ["DSSI_PATH"] = splitter.join(Carla.DSSI_PATH)
  1034. os.environ["LV2_PATH"] = splitter.join(Carla.LV2_PATH)
  1035. os.environ["VST_PATH"] = splitter.join(Carla.VST_PATH)
  1036. os.environ["GIG_PATH"] = splitter.join(Carla.GIG_PATH)
  1037. os.environ["SF2_PATH"] = splitter.join(Carla.SF2_PATH)
  1038. os.environ["SFZ_PATH"] = splitter.join(Carla.SFZ_PATH)
  1039. def saveSettings(self):
  1040. settings = QSettings()
  1041. settings.setValue("Geometry", self.saveGeometry())
  1042. settings.setValue("SplitterState", self.ui.splitter.saveState())
  1043. settings.setValue("ShowToolbar", self.ui.toolBar.isVisible())
  1044. settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
  1045. settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())
  1046. diskFolders = []
  1047. for i in range(self.ui.cb_disk.count()):
  1048. diskFolders.append(self.ui.cb_disk.itemData(i))
  1049. settings.setValue("DiskFolders", diskFolders)
  1050. def dragEnterEvent(self, event):
  1051. if event.source() == self.ui.fileTreeView:
  1052. event.accept()
  1053. elif self.ui.tabMain.contentsRect().contains(event.pos()):
  1054. event.accept()
  1055. else:
  1056. QMainWindow.dragEnterEvent(self, event)
  1057. def dropEvent(self, event):
  1058. event.accept()
  1059. urls = event.mimeData().urls()
  1060. for url in urls:
  1061. filename = url.toLocalFile()
  1062. if not Carla.host.load_filename(filename):
  1063. CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
  1064. self.tr("Failed to load file"),
  1065. cString(Carla.host.get_last_error()), QMessageBox.Ok, QMessageBox.Ok)
  1066. def resizeEvent(self, event):
  1067. if self.ui.tabMain.currentIndex() == 0:
  1068. # Force update of 2nd tab
  1069. width = self.ui.tab_plugins.width()-4
  1070. height = self.ui.tab_plugins.height()-4
  1071. self.ui.miniCanvasPreview.setViewSize(float(width) / DEFAULT_CANVAS_WIDTH, float(height) / DEFAULT_CANVAS_HEIGHT)
  1072. else:
  1073. QTimer.singleShot(0, self, SLOT("slot_miniCanvasCheckSize()"))
  1074. self.updateInfoLabelSize()
  1075. QMainWindow.resizeEvent(self, event)
  1076. def timerEvent(self, event):
  1077. if event.timerId() == self.fIdleTimerFast:
  1078. if not self.fEngineStarted:
  1079. return
  1080. Carla.host.engine_idle()
  1081. for pwidget in self.fPluginList:
  1082. if pwidget is None:
  1083. break
  1084. pwidget.idleFast()
  1085. self.refreshTransport()
  1086. elif event.timerId() == self.fIdleTimerSlow:
  1087. if not self.fEngineStarted:
  1088. return
  1089. for pwidget in self.fPluginList:
  1090. if pwidget is None:
  1091. break
  1092. pwidget.idleSlow()
  1093. QMainWindow.timerEvent(self, event)
  1094. def closeEvent(self, event):
  1095. self.saveSettings()
  1096. if self.fEngineStarted:
  1097. Carla.host.set_engine_about_to_close()
  1098. self.removeAllPlugins()
  1099. self.stopEngine()
  1100. QMainWindow.closeEvent(self, event)
  1101. def engineCallback(ptr, action, pluginId, value1, value2, value3, valueStr):
  1102. if pluginId < 0 or not Carla.gui:
  1103. return
  1104. if action == CALLBACK_DEBUG:
  1105. Carla.gui.emit(SIGNAL("DebugCallback(int, int, int, double, QString)"), pluginId, value1, value2, value3, cString(valueStr))
  1106. elif action == CALLBACK_PLUGIN_ADDED:
  1107. Carla.gui.emit(SIGNAL("PluginAddedCallback(int)"), pluginId)
  1108. elif action == CALLBACK_PLUGIN_REMOVED:
  1109. Carla.gui.emit(SIGNAL("PluginRemovedCallback(int)"), pluginId)
  1110. elif action == CALLBACK_PLUGIN_RENAMED:
  1111. Carla.gui.emit(SIGNAL("PluginRenamedCallback(int, QString)"), pluginId, valueStr)
  1112. elif action == CALLBACK_PARAMETER_VALUE_CHANGED:
  1113. Carla.gui.emit(SIGNAL("ParameterValueChangedCallback(int, int, double)"), pluginId, value1, value3)
  1114. elif action == CALLBACK_PARAMETER_DEFAULT_CHANGED:
  1115. Carla.gui.emit(SIGNAL("ParameterDefaultChangedCallback(int, int, double)"), pluginId, value1, value3)
  1116. elif action == CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
  1117. Carla.gui.emit(SIGNAL("ParameterMidiChannelChangedCallback(int, int, int)"), pluginId, value1, value2)
  1118. elif action == CALLBACK_PARAMETER_MIDI_CC_CHANGED:
  1119. Carla.gui.emit(SIGNAL("ParameterMidiCcChangedCallback(int, int, int)"), pluginId, value1, value2)
  1120. elif action == CALLBACK_PROGRAM_CHANGED:
  1121. Carla.gui.emit(SIGNAL("ProgramChangedCallback(int, int)"), pluginId, value1)
  1122. elif action == CALLBACK_MIDI_PROGRAM_CHANGED:
  1123. Carla.gui.emit(SIGNAL("MidiProgramChangedCallback(int, int)"), pluginId, value1)
  1124. elif action == CALLBACK_NOTE_ON:
  1125. Carla.gui.emit(SIGNAL("NoteOnCallback(int, int, int, int)"), pluginId, value1, value2, value3)
  1126. elif action == CALLBACK_NOTE_OFF:
  1127. Carla.gui.emit(SIGNAL("NoteOffCallback(int, int, int)"), pluginId, value1, value2)
  1128. elif action == CALLBACK_SHOW_GUI:
  1129. Carla.gui.emit(SIGNAL("ShowGuiCallback(int, int)"), pluginId, value1)
  1130. elif action == CALLBACK_UPDATE:
  1131. Carla.gui.emit(SIGNAL("UpdateCallback(int)"), pluginId)
  1132. elif action == CALLBACK_RELOAD_INFO:
  1133. Carla.gui.emit(SIGNAL("ReloadInfoCallback(int)"), pluginId)
  1134. elif action == CALLBACK_RELOAD_PARAMETERS:
  1135. Carla.gui.emit(SIGNAL("ReloadParametersCallback(int)"), pluginId)
  1136. elif action == CALLBACK_RELOAD_PROGRAMS:
  1137. Carla.gui.emit(SIGNAL("ReloadProgramsCallback(int)"), pluginId)
  1138. elif action == CALLBACK_RELOAD_ALL:
  1139. Carla.gui.emit(SIGNAL("ReloadAllCallback(int)"), pluginId)
  1140. elif action == CALLBACK_PATCHBAY_CLIENT_ADDED:
  1141. Carla.gui.emit(SIGNAL("PatchbayClientAddedCallback(int, int, QString)"), value1, value2, cString(valueStr))
  1142. elif action == CALLBACK_PATCHBAY_CLIENT_REMOVED:
  1143. Carla.gui.emit(SIGNAL("PatchbayClientRemovedCallback(int, QString)"), value1, cString(valueStr))
  1144. elif action == CALLBACK_PATCHBAY_CLIENT_RENAMED:
  1145. Carla.gui.emit(SIGNAL("PatchbayClientRenamedCallback(int, QString)"), value1, cString(valueStr))
  1146. elif action == CALLBACK_PATCHBAY_PORT_ADDED:
  1147. Carla.gui.emit(SIGNAL("PatchbayPortAddedCallback(int, int, int, QString)"), value1, value2, int(value3), cString(valueStr))
  1148. elif action == CALLBACK_PATCHBAY_PORT_REMOVED:
  1149. Carla.gui.emit(SIGNAL("PatchbayPortRemovedCallback(int, int, QString)"), value1, value2, cString(valueStr))
  1150. elif action == CALLBACK_PATCHBAY_PORT_RENAMED:
  1151. Carla.gui.emit(SIGNAL("PatchbayPortRenamedCallback(int, int, QString)"), value1, value2, cString(valueStr))
  1152. elif action == CALLBACK_PATCHBAY_CONNECTION_ADDED:
  1153. Carla.gui.emit(SIGNAL("PatchbayConnectionAddedCallback(int, int, int)"), value1, value2, value3)
  1154. elif action == CALLBACK_PATCHBAY_CONNECTION_REMOVED:
  1155. Carla.gui.emit(SIGNAL("PatchbayConnectionRemovedCallback(int)"), value1)
  1156. elif action == CALLBACK_PATCHBAY_ICON_CHANGED:
  1157. Carla.gui.emit(SIGNAL("PatchbayIconChangedCallback(int, int)"), value1, value2)
  1158. elif action == CALLBACK_BUFFER_SIZE_CHANGED:
  1159. Carla.gui.emit(SIGNAL("BufferSizeChangedCallback(int)"), value1)
  1160. elif action == CALLBACK_SAMPLE_RATE_CHANGED:
  1161. Carla.gui.emit(SIGNAL("SampleRateChangedCallback(double)"), value3)
  1162. elif action == CALLBACK_NSM_ANNOUNCE:
  1163. Carla.gui.emit(SIGNAL("NSM_AnnounceCallback(QString)"), cString(valueStr))
  1164. elif action == CALLBACK_NSM_OPEN:
  1165. Carla.gui.emit(SIGNAL("NSM_OpenCallback(QString)"), cString(valueStr))
  1166. elif action == CALLBACK_NSM_SAVE:
  1167. Carla.gui.emit(SIGNAL("NSM_SaveCallback()"))
  1168. elif action == CALLBACK_ERROR:
  1169. Carla.gui.emit(SIGNAL("ErrorCallback(QString)"), cString(valueStr))
  1170. elif action == CALLBACK_QUIT:
  1171. Carla.gui.emit(SIGNAL("QuitCallback()"))
  1172. #--------------- main ------------------
  1173. if __name__ == '__main__':
  1174. # App initialization
  1175. app = QApplication(sys.argv)
  1176. app.setApplicationName("Carla")
  1177. app.setApplicationVersion(VERSION)
  1178. app.setOrganizationName("falkTX")
  1179. app.setWindowIcon(QIcon(":/scalable/carla.svg"))
  1180. argv = app.arguments()
  1181. argc = len(argv)
  1182. for i in range(argc):
  1183. if i == 0: continue
  1184. argument = argv[i]
  1185. if argument.startswith("--with-appname="):
  1186. appName = os.path.basename(argument.replace("--with-appname=", ""))
  1187. elif argument.startswith("--with-libprefix="):
  1188. libPrefix = argument.replace("--with-libprefix=", "")
  1189. elif os.path.exists(argument):
  1190. projectFilename = argument
  1191. if libPrefix is not None:
  1192. libPath = os.path.join(libPrefix, "lib", "carla")
  1193. libName = os.path.join(libPath, carla_libname)
  1194. else:
  1195. libPath = carla_library_filename.replace(carla_libname, "")
  1196. libName = carla_library_filename
  1197. # Init backend
  1198. Carla.host = Host(libName)
  1199. if NSM_URL:
  1200. Carla.host.nsm_announce(NSM_URL, appName, os.getpid())
  1201. else:
  1202. Carla.host.set_engine_option(OPTION_PROCESS_NAME, 0, "carla")
  1203. Carla.host.set_engine_option(OPTION_PATH_RESOURCES, 0, libPath)
  1204. # Set bridge paths
  1205. if carla_bridge_native:
  1206. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_NATIVE, 0, carla_bridge_native)
  1207. if carla_bridge_posix32:
  1208. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX32, 0, carla_bridge_posix32)
  1209. if carla_bridge_posix64:
  1210. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_POSIX64, 0, carla_bridge_posix64)
  1211. if carla_bridge_win32:
  1212. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN32, 0, carla_bridge_win32)
  1213. if carla_bridge_win64:
  1214. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_WIN64, 0, carla_bridge_win64)
  1215. if WINDOWS:
  1216. if carla_bridge_lv2_windows:
  1217. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_WINDOWS, 0, carla_bridge_lv2_windows)
  1218. if carla_bridge_vst_hwnd:
  1219. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_HWND, 0, carla_bridge_vst_hwnd)
  1220. elif MACOS:
  1221. if carla_bridge_lv2_cocoa:
  1222. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_COCOA, 0, carla_bridge_lv2_cocoa)
  1223. if carla_bridge_vst_mac:
  1224. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_MAC, 0, carla_bridge_vst_mac)
  1225. else:
  1226. if carla_bridge_lv2_gtk2:
  1227. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK2, 0, carla_bridge_lv2_gtk2)
  1228. if carla_bridge_lv2_gtk3:
  1229. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_GTK3, 0, carla_bridge_lv2_gtk3)
  1230. if carla_bridge_lv2_qt4:
  1231. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT4, 0, carla_bridge_lv2_qt4)
  1232. if carla_bridge_lv2_qt5:
  1233. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_QT5, 0, carla_bridge_lv2_qt5)
  1234. if carla_bridge_lv2_x11:
  1235. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_LV2_X11, 0, carla_bridge_lv2_x11)
  1236. if carla_bridge_vst_x11:
  1237. Carla.host.set_engine_option(OPTION_PATH_BRIDGE_VST_X11, 0, carla_bridge_vst_x11)
  1238. # Create GUI and start engine
  1239. Carla.gui = CarlaMainW()
  1240. # Only now we're ready to handle events
  1241. Carla.host.set_engine_callback(engineCallback)
  1242. # Set-up custom signal handling
  1243. setUpSignals()
  1244. # Show GUI
  1245. Carla.gui.show()
  1246. # Load project file if set
  1247. if projectFilename and not NSM_URL:
  1248. Carla.gui.loadProjectLater(projectFilename)
  1249. # App-Loop
  1250. ret = app.exec_()
  1251. # Destroy GUI
  1252. tmp = Carla.gui
  1253. Carla.gui = None
  1254. del tmp
  1255. # Exit properly
  1256. sys.exit(ret)