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.

1851 lines
72KB

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