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.

1694 lines
67KB

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