Update CI version
[carla.git] / source / frontend / carla_host.py
blob5c7f46b98e7a51530c184e50f18689999a119422
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Carla host code
5 # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of
10 # the License, or any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # For a full copy of the GNU General Public License see the doc/GPL.txt file.
19 # ------------------------------------------------------------------------------------------------------------
20 # Imports (Global)
22 import json
24 # ------------------------------------------------------------------------------------------------------------
25 # Imports (ctypes)
27 from ctypes import (
28 byref, pointer
31 # ------------------------------------------------------------------------------------------------------------
32 # Imports (PyQt5)
34 # This fails in some configurations, assume >= 5.6.0 in that case
35 try:
36 from PyQt5.Qt import PYQT_VERSION
37 except ImportError:
38 PYQT_VERSION = 0x50600
40 from PyQt5.QtCore import (
41 QT_VERSION, qCritical, QBuffer, QEventLoop, QFileInfo, QIODevice, QMimeData, QModelIndex, QPointF, QTimer, QEvent
43 from PyQt5.QtGui import (
44 QImage, QImageWriter, QPainter, QPalette, QBrush
46 from PyQt5.QtWidgets import (
47 QAction, QApplication, QInputDialog, QFileSystemModel, QListWidgetItem, QGraphicsView, QMainWindow
50 # ------------------------------------------------------------------------------------------------------------
51 # Imports (Custom)
53 import ui_carla_host
55 from carla_app import *
56 from carla_backend import *
57 from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull
58 from carla_frontend import CarlaFrontendLib
59 from carla_shared import *
60 from carla_settings import *
61 from carla_utils import *
62 from carla_widgets import *
64 from patchcanvas import patchcanvas
65 from widgets.digitalpeakmeter import DigitalPeakMeter
66 from widgets.pixmapkeyboard import PixmapKeyboardHArea
68 # ------------------------------------------------------------------------------------------------------------
69 # Try Import OpenGL
71 try:
72 from PyQt5.QtOpenGL import QGLWidget
73 hasGL = True
74 except:
75 hasGL = False
77 # ------------------------------------------------------------------------------------------------------------
78 # Safe exception hook, needed for PyQt5
80 def sys_excepthook(typ, value, tback):
81 return sys.__excepthook__(typ, value, tback)
83 # ------------------------------------------------------------------------------------------------------------
84 # Session Management support
86 CARLA_CLIENT_NAME = os.getenv("CARLA_CLIENT_NAME")
87 LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
88 NSM_URL = os.getenv("NSM_URL")
90 # ------------------------------------------------------------------------------------------------------------
91 # Small print helper
93 def processMode2Str(processMode):
94 if processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT:
95 return "Single client"
96 if processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS:
97 return "Multi client"
98 if processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
99 return "Continuous Rack"
100 if processMode == ENGINE_PROCESS_MODE_PATCHBAY:
101 return "Patchbay"
102 if processMode == ENGINE_PROCESS_MODE_BRIDGE:
103 return "Bridge"
104 return "Unknown"
106 # ------------------------------------------------------------------------------------------------------------
107 # Carla Print class
109 class CarlaPrint:
110 def __init__(self, err):
111 self.err = err
113 def flush(self):
114 gCarla.utils.fflush(self.err)
116 def write(self, string):
117 gCarla.utils.fputs(self.err, string)
119 # ------------------------------------------------------------------------------------------------------------
120 # Host Window
122 class HostWindow(QMainWindow):
123 #class HostWindow(QMainWindow, PluginEditParentMeta, metaclass=PyQtMetaClass):
124 # signals
125 SIGTERM = pyqtSignal()
126 SIGUSR1 = pyqtSignal()
128 # CustomActions
129 CUSTOM_ACTION_NONE = 0
130 CUSTOM_ACTION_APP_CLOSE = 1
131 CUSTOM_ACTION_PROJECT_LOAD = 2
133 # --------------------------------------------------------------------------------------------------------
135 def __init__(self, host, withCanvas, parent=None):
136 QMainWindow.__init__(self, parent)
137 self.host = host
138 self.ui = ui_carla_host.Ui_CarlaHostW()
139 self.ui.setupUi(self)
140 gCarla.gui = self
142 if False:
143 # kdevelop likes this :)
144 host = CarlaHostNull()
145 self.host = host
147 self._true = c_char_p("true".encode("utf-8"))
149 self.fParentOrSelf = parent or self
151 # ----------------------------------------------------------------------------------------------------
152 # Internal stuff
154 self.fIdleTimerNull = self.startTimer(1000) # keep application signals alive
155 self.fIdleTimerFast = 0
156 self.fIdleTimerSlow = 0
158 self.fLadspaRdfNeedsUpdate = True
159 self.fLadspaRdfList = []
161 self.fPluginCount = 0
162 self.fPluginList = []
164 self.fPluginListDialog = None
165 self.fFavoritePlugins = []
167 self.fProjectFilename = ""
168 self.fIsProjectLoading = False
169 self.fCurrentlyRemovingAllPlugins = False
170 self.fHasLoadedLv2Plugins = False
172 self.fLastTransportBPM = 0.0
173 self.fLastTransportFrame = 0
174 self.fLastTransportState = False
175 self.fBufferSize = 0
176 self.fSampleRate = 0.0
177 self.fOscAddressTCP = ""
178 self.fOscAddressUDP = ""
180 if MACOS:
181 self.fMacClosingHelper = True
183 # CancelableActionCallback Box
184 self.fCancelableActionBox = None
186 # run a custom action after engine is properly closed
187 self.fCustomStopAction = self.CUSTOM_ACTION_NONE
189 # first attempt of auto-start engine doesn't show an error
190 self.fFirstEngineInit = True
192 # to be filled with key-value pairs of current settings
193 self.fSavedSettings = {}
195 # true if NSM server handles our window management
196 self.fWindowCloseHideGui = False
198 if host.isControl:
199 self.fClientName = "Carla-Control"
200 self.fSessionManagerName = "Control"
201 elif host.isPlugin:
202 self.fClientName = "Carla-Plugin"
203 self.fSessionManagerName = "Plugin"
204 elif LADISH_APP_NAME:
205 self.fClientName = LADISH_APP_NAME
206 self.fSessionManagerName = ""
207 elif NSM_URL and host.nsmOK:
208 self.fClientName = "Carla.tmp"
209 self.fSessionManagerName = "Non Session Manager TMP"
210 self.fWindowCloseHideGui = True
211 else:
212 self.fClientName = CARLA_CLIENT_NAME or "Carla"
213 self.fSessionManagerName = ""
215 # ----------------------------------------------------------------------------------------------------
216 # Internal stuff (patchbay)
218 self.fPeaksCleared = True
220 self.fExternalPatchbay = False
221 self.fSelectedPlugins = []
223 self.fCanvasWidth = 0
224 self.fCanvasHeight = 0
225 self.fMiniCanvasUpdateTimeout = 0
227 self.fWithCanvas = withCanvas
229 # ----------------------------------------------------------------------------------------------------
230 # Internal stuff (logs)
232 self.autoscrollOnNewLog = True
233 self.lastLogSliderPos = 0
235 # ----------------------------------------------------------------------------------------------------
236 # Set up GUI (engine stopped)
238 if self.host.isPlugin or self.host.isControl:
239 self.ui.act_file_save.setVisible(False)
240 self.ui.act_engine_start.setEnabled(False)
241 self.ui.act_engine_start.setVisible(False)
242 self.ui.act_engine_stop.setEnabled(False)
243 self.ui.act_engine_stop.setVisible(False)
244 self.ui.menu_Engine.setEnabled(False)
245 self.ui.menu_Engine.setVisible(False)
246 self.ui.menu_Engine.menuAction().setVisible(False)
247 self.ui.tabWidget.removeTab(2)
249 if self.host.isControl:
250 self.ui.act_file_new.setVisible(False)
251 self.ui.act_file_open.setVisible(False)
252 self.ui.act_file_save_as.setVisible(False)
253 self.ui.tabUtils.removeTab(0)
254 else:
255 self.ui.act_file_save_as.setText(self.tr("Export as..."))
257 if not withCanvas:
258 self.ui.tabWidget.tabBar().hide()
260 else:
261 self.ui.act_engine_start.setEnabled(True)
263 if WINDOWS:
264 self.ui.tabWidget.removeTab(2)
266 if self.host.isControl:
267 self.ui.act_file_refresh.setEnabled(False)
268 else:
269 self.ui.act_file_connect.setEnabled(False)
270 self.ui.act_file_connect.setVisible(False)
271 self.ui.act_file_refresh.setEnabled(False)
272 self.ui.act_file_refresh.setVisible(False)
274 if self.fSessionManagerName and not self.host.isPlugin:
275 self.ui.act_file_new.setEnabled(False)
277 self.ui.act_file_open.setEnabled(False)
278 self.ui.act_file_save.setEnabled(False)
279 self.ui.act_file_save_as.setEnabled(False)
280 self.ui.act_engine_stop.setEnabled(False)
281 self.ui.act_plugin_remove_all.setEnabled(False)
283 self.ui.act_canvas_show_internal.setChecked(False)
284 self.ui.act_canvas_show_internal.setVisible(False)
285 self.ui.act_canvas_show_external.setChecked(False)
286 self.ui.act_canvas_show_external.setVisible(False)
288 self.ui.menu_PluginMacros.setEnabled(False)
289 self.ui.menu_Canvas.setEnabled(False)
291 self.ui.dockWidgetTitleBar = QWidget(self)
292 self.ui.dockWidget.setTitleBarWidget(self.ui.dockWidgetTitleBar)
294 if not withCanvas:
295 self.ui.act_canvas_show_internal.setVisible(False)
296 self.ui.act_canvas_show_external.setVisible(False)
297 self.ui.act_canvas_arrange.setVisible(False)
298 self.ui.act_canvas_refresh.setVisible(False)
299 self.ui.act_canvas_save_image.setVisible(False)
300 self.ui.act_canvas_zoom_100.setVisible(False)
301 self.ui.act_canvas_zoom_fit.setVisible(False)
302 self.ui.act_canvas_zoom_in.setVisible(False)
303 self.ui.act_canvas_zoom_out.setVisible(False)
304 self.ui.act_settings_show_meters.setVisible(False)
305 self.ui.act_settings_show_keyboard.setVisible(False)
306 self.ui.menu_Canvas_Zoom.setEnabled(False)
307 self.ui.menu_Canvas_Zoom.setVisible(False)
308 self.ui.menu_Canvas_Zoom.menuAction().setVisible(False)
309 self.ui.menu_Canvas.setEnabled(False)
310 self.ui.menu_Canvas.setVisible(False)
311 self.ui.menu_Canvas.menuAction().setVisible(False)
312 self.ui.tw_miniCanvas.hide()
313 self.ui.tabWidget.removeTab(1)
314 if WINDOWS:
315 self.ui.tabWidget.tabBar().hide()
317 # ----------------------------------------------------------------------------------------------------
318 # Set up GUI (disk)
320 exts = gCarla.utils.get_supported_file_extensions()
322 self.fDirModel = QFileSystemModel(self)
323 self.fDirModel.setRootPath(HOME)
324 self.fDirModel.setNameFilters(tuple(("*." + i) for i in exts))
326 self.ui.fileTreeView.setModel(self.fDirModel)
327 self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
328 self.ui.fileTreeView.setColumnHidden(1, True)
329 self.ui.fileTreeView.setColumnHidden(2, True)
330 self.ui.fileTreeView.setColumnHidden(3, True)
331 self.ui.fileTreeView.setHeaderHidden(True)
333 # ----------------------------------------------------------------------------------------------------
334 # Set up GUI (transport)
336 fontMetrics = self.ui.l_transport_bbt.fontMetrics()
337 minValueWidth = fontMetricsHorizontalAdvance(fontMetrics, "000|00|0000")
338 minLabelWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_frame.text())
340 labelTimeWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_time.text())
341 labelBBTWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_bbt.text())
343 if minLabelWidth < labelTimeWidth:
344 minLabelWidth = labelTimeWidth
345 if minLabelWidth < labelBBTWidth:
346 minLabelWidth = labelBBTWidth
348 self.ui.label_transport_frame.setMinimumWidth(minLabelWidth + 3)
349 self.ui.label_transport_time.setMinimumWidth(minLabelWidth + 3)
350 self.ui.label_transport_bbt.setMinimumWidth(minLabelWidth + 3)
352 self.ui.l_transport_bbt.setMinimumWidth(minValueWidth + 3)
353 self.ui.l_transport_frame.setMinimumWidth(minValueWidth + 3)
354 self.ui.l_transport_time.setMinimumWidth(minValueWidth + 3)
356 if host.isPlugin:
357 self.ui.b_transport_play.setEnabled(False)
358 self.ui.b_transport_stop.setEnabled(False)
359 self.ui.b_transport_backwards.setEnabled(False)
360 self.ui.b_transport_forwards.setEnabled(False)
361 self.ui.group_transport_controls.setEnabled(False)
362 self.ui.group_transport_controls.setVisible(False)
363 self.ui.cb_transport_link.setEnabled(False)
364 self.ui.cb_transport_link.setVisible(False)
365 self.ui.cb_transport_jack.setEnabled(False)
366 self.ui.cb_transport_jack.setVisible(False)
367 self.ui.dsb_transport_bpm.setEnabled(False)
368 self.ui.dsb_transport_bpm.setReadOnly(True)
370 self.ui.w_transport.setEnabled(False)
372 # ----------------------------------------------------------------------------------------------------
373 # Set up GUI (rack)
375 self.ui.listWidget.setHostAndParent(self.host, self)
377 sb = self.ui.listWidget.verticalScrollBar()
378 self.ui.rackScrollBar.setMinimum(sb.minimum())
379 self.ui.rackScrollBar.setMaximum(sb.maximum())
380 self.ui.rackScrollBar.setValue(sb.value())
382 sb.rangeChanged.connect(self.ui.rackScrollBar.setRange)
383 sb.valueChanged.connect(self.ui.rackScrollBar.setValue)
384 self.ui.rackScrollBar.rangeChanged.connect(sb.setRange)
385 self.ui.rackScrollBar.valueChanged.connect(sb.setValue)
387 self.updateStyle()
389 self.ui.rack.setStyleSheet("""
390 CarlaRackList#CarlaRackList {
391 background-color: black;
393 """)
395 # ----------------------------------------------------------------------------------------------------
396 # Set up GUI (patchbay)
398 self.ui.peak_in.setChannelCount(2)
399 self.ui.peak_in.setMeterColor(DigitalPeakMeter.COLOR_BLUE)
400 self.ui.peak_in.setMeterOrientation(DigitalPeakMeter.VERTICAL)
401 self.ui.peak_in.setFixedWidth(25)
403 self.ui.peak_out.setChannelCount(2)
404 self.ui.peak_out.setMeterColor(DigitalPeakMeter.COLOR_GREEN)
405 self.ui.peak_out.setMeterOrientation(DigitalPeakMeter.VERTICAL)
406 self.ui.peak_out.setFixedWidth(25)
408 self.ui.scrollArea = PixmapKeyboardHArea(self.ui.patchbay)
409 self.ui.keyboard = self.ui.scrollArea.keyboard
410 self.ui.patchbay.layout().addWidget(self.ui.scrollArea, 1, 0, 1, 0)
412 self.ui.scrollArea.setEnabled(False)
414 self.ui.miniCanvasPreview.setRealParent(self)
415 self.ui.tw_miniCanvas.tabBar().hide()
417 # ----------------------------------------------------------------------------------------------------
418 # Set up GUI (logs)
420 self.ui.text_logs.textChanged.connect(self.slot_logButtonsState)
421 self.ui.logs_clear.clicked.connect(self.slot_logClear)
422 self.ui.logs_save.clicked.connect(self.slot_logSave)
423 self.ui.logs_autoscroll.stateChanged.connect(self.slot_toggleLogAutoscroll)
424 self.ui.text_logs.verticalScrollBar().valueChanged.connect(self.slot_logSliderMoved)
426 # ----------------------------------------------------------------------------------------------------
427 # Set up GUI (special stuff for Mac OS)
429 if MACOS:
430 self.ui.act_file_quit.setMenuRole(QAction.QuitRole)
431 self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole)
432 self.ui.act_help_about.setMenuRole(QAction.AboutRole)
433 self.ui.act_help_about_juce.setMenuRole(QAction.ApplicationSpecificRole)
434 self.ui.act_help_about_qt.setMenuRole(QAction.AboutQtRole)
435 self.ui.menu_Settings.setTitle("Panels")
437 # ----------------------------------------------------------------------------------------------------
438 # Load Settings
440 self.loadSettings(True)
442 # ----------------------------------------------------------------------------------------------------
443 # Set-up Canvas
445 if withCanvas:
446 self.scene = patchcanvas.PatchScene(self, self.ui.graphicsView)
447 self.ui.graphicsView.setScene(self.scene)
449 if self.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL:
450 self.ui.glView = QGLWidget(self)
451 self.ui.graphicsView.setViewport(self.ui.glView)
453 self.setupCanvas()
455 # ----------------------------------------------------------------------------------------------------
456 # Set-up Icons
458 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
459 self.ui.act_file_connect.setIcon(getIcon('network-connect', 16, 'svgz'))
460 self.ui.act_file_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
461 self.ui.act_file_new.setIcon(getIcon('document-new', 16, 'svgz'))
462 self.ui.act_file_open.setIcon(getIcon('document-open', 16, 'svgz'))
463 self.ui.act_file_save.setIcon(getIcon('document-save', 16, 'svgz'))
464 self.ui.act_file_save_as.setIcon(getIcon('document-save-as', 16, 'svgz'))
465 self.ui.act_file_quit.setIcon(getIcon('application-exit', 16, 'svgz'))
466 self.ui.act_engine_start.setIcon(getIcon('media-playback-start', 16, 'svgz'))
467 self.ui.act_engine_stop.setIcon(getIcon('media-playback-stop', 16, 'svgz'))
468 self.ui.act_engine_panic.setIcon(getIcon('dialog-warning', 16, 'svgz'))
469 self.ui.act_engine_config.setIcon(getIcon('configure', 16, 'svgz'))
470 self.ui.act_plugin_add.setIcon(getIcon('list-add', 16, 'svgz'))
471 self.ui.act_plugin_add_jack.setIcon(getIcon('list-add', 16, 'svgz'))
472 self.ui.act_plugin_remove_all.setIcon(getIcon('edit-delete', 16, 'svgz'))
473 self.ui.act_canvas_arrange.setIcon(getIcon('view-sort-ascending', 16, 'svgz'))
474 self.ui.act_canvas_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
475 self.ui.act_canvas_zoom_fit.setIcon(getIcon('zoom-fit-best', 16, 'svgz'))
476 self.ui.act_canvas_zoom_in.setIcon(getIcon('zoom-in', 16, 'svgz'))
477 self.ui.act_canvas_zoom_out.setIcon(getIcon('zoom-out', 16, 'svgz'))
478 self.ui.act_canvas_zoom_100.setIcon(getIcon('zoom-original', 16, 'svgz'))
479 self.ui.act_settings_configure.setIcon(getIcon('configure', 16, 'svgz'))
480 self.ui.b_disk_add.setIcon(getIcon('list-add', 16, 'svgz'))
481 self.ui.b_disk_remove.setIcon(getIcon('list-remove', 16, 'svgz'))
482 self.ui.b_transport_play.setIcon(getIcon('media-playback-start', 16, 'svgz'))
483 self.ui.b_transport_stop.setIcon(getIcon('media-playback-stop', 16, 'svgz'))
484 self.ui.b_transport_backwards.setIcon(getIcon('media-seek-backward', 16, 'svgz'))
485 self.ui.b_transport_forwards.setIcon(getIcon('media-seek-forward', 16, 'svgz'))
486 self.ui.logs_clear.setIcon(getIcon('edit-clear', 16, 'svgz'))
487 self.ui.logs_save.setIcon(getIcon('document-save', 16, 'svgz'))
489 # ----------------------------------------------------------------------------------------------------
490 # Connect actions to functions
492 self.ui.act_file_new.triggered.connect(self.slot_fileNew)
493 self.ui.act_file_open.triggered.connect(self.slot_fileOpen)
494 self.ui.act_file_save.triggered.connect(self.slot_fileSave)
495 self.ui.act_file_save_as.triggered.connect(self.slot_fileSaveAs)
497 self.ui.act_engine_start.triggered.connect(self.slot_engineStart)
498 self.ui.act_engine_stop.triggered.connect(self.slot_engineStop)
499 self.ui.act_engine_panic.triggered.connect(self.slot_pluginsDisable)
500 self.ui.act_engine_config.triggered.connect(self.slot_engineConfig)
502 self.ui.act_plugin_add.triggered.connect(self.slot_pluginAdd)
503 self.ui.act_plugin_add_jack.triggered.connect(self.slot_jackAppAdd)
504 self.ui.act_plugin_remove_all.triggered.connect(self.slot_confirmRemoveAll)
506 self.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
507 self.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
508 self.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
509 self.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
510 self.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
511 self.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
512 self.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
513 self.ui.act_plugins_compact.triggered.connect(self.slot_pluginsCompact)
514 self.ui.act_plugins_expand.triggered.connect(self.slot_pluginsExpand)
516 self.ui.act_settings_show_toolbar.toggled.connect(self.slot_showToolbar)
517 self.ui.act_settings_show_meters.toggled.connect(self.slot_showCanvasMeters)
518 self.ui.act_settings_show_keyboard.toggled.connect(self.slot_showCanvasKeyboard)
519 self.ui.act_settings_show_side_panel.toggled.connect(self.slot_showSidePanel)
520 self.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
522 self.ui.act_help_about.triggered.connect(self.slot_aboutCarla)
523 self.ui.act_help_about_juce.triggered.connect(self.slot_aboutJuce)
524 self.ui.act_help_about_qt.triggered.connect(self.slot_aboutQt)
526 self.ui.cb_disk.currentIndexChanged.connect(self.slot_diskFolderChanged)
527 self.ui.b_disk_add.clicked.connect(self.slot_diskFolderAdd)
528 self.ui.b_disk_remove.clicked.connect(self.slot_diskFolderRemove)
529 self.ui.fileTreeView.doubleClicked.connect(self.slot_fileTreeDoubleClicked)
531 self.ui.b_transport_play.clicked.connect(self.slot_transportPlayPause)
532 self.ui.b_transport_stop.clicked.connect(self.slot_transportStop)
533 self.ui.b_transport_backwards.clicked.connect(self.slot_transportBackwards)
534 self.ui.b_transport_forwards.clicked.connect(self.slot_transportForwards)
535 self.ui.dsb_transport_bpm.valueChanged.connect(self.slot_transportBpmChanged)
536 self.ui.cb_transport_jack.clicked.connect(self.slot_transportJackEnabled)
537 self.ui.cb_transport_link.clicked.connect(self.slot_transportLinkEnabled)
539 self.ui.b_xruns.clicked.connect(self.slot_xrunClear)
541 self.ui.listWidget.customContextMenuRequested.connect(self.slot_showPluginActionsMenu)
543 self.ui.keyboard.noteOn.connect(self.slot_noteOn)
544 self.ui.keyboard.noteOff.connect(self.slot_noteOff)
546 self.ui.tabWidget.currentChanged.connect(self.slot_tabChanged)
547 self.ui.toolBar.visibilityChanged.connect(self.slot_toolbarVisibilityChanged)
549 if withCanvas:
550 self.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal)
551 self.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal)
552 self.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange)
553 self.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh)
554 self.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit)
555 self.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn)
556 self.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut)
557 self.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset)
558 self.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage)
559 self.ui.act_canvas_save_image_2x.triggered.connect(self.slot_canvasSaveImage)
560 self.ui.act_canvas_save_image_4x.triggered.connect(self.slot_canvasSaveImage)
561 self.ui.act_canvas_copy_clipboard.triggered.connect(self.slot_canvasCopyToClipboard)
562 self.ui.act_canvas_arrange.setEnabled(False) # TODO, later
563 self.ui.graphicsView.horizontalScrollBar().valueChanged.connect(self.slot_horizontalScrollBarChanged)
564 self.ui.graphicsView.verticalScrollBar().valueChanged.connect(self.slot_verticalScrollBarChanged)
565 self.ui.miniCanvasPreview.miniCanvasMoved.connect(self.slot_miniCanvasMoved)
566 self.scene.scaleChanged.connect(self.slot_canvasScaleChanged)
567 self.scene.pluginSelected.connect(self.slot_canvasPluginSelected)
568 self.scene.selectionChanged.connect(self.slot_canvasSelectionChanged)
570 self.SIGUSR1.connect(self.slot_handleSIGUSR1)
571 self.SIGTERM.connect(self.slot_handleSIGTERM)
573 host.EngineStartedCallback.connect(self.slot_handleEngineStartedCallback)
574 host.EngineStoppedCallback.connect(self.slot_handleEngineStoppedCallback)
575 host.TransportModeChangedCallback.connect(self.slot_handleTransportModeChangedCallback)
576 host.BufferSizeChangedCallback.connect(self.slot_handleBufferSizeChangedCallback)
577 host.SampleRateChangedCallback.connect(self.slot_handleSampleRateChangedCallback)
578 host.CancelableActionCallback.connect(self.slot_handleCancelableActionCallback)
579 host.ProjectLoadFinishedCallback.connect(self.slot_handleProjectLoadFinishedCallback)
581 host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback)
582 host.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback)
583 host.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
585 host.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
586 host.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
588 host.UpdateCallback.connect(self.slot_handleUpdateCallback)
590 if withCanvas:
591 host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback)
592 host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback)
593 host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback)
594 host.PatchbayClientDataChangedCallback.connect(self.slot_handlePatchbayClientDataChangedCallback)
595 host.PatchbayClientPositionChangedCallback.connect(self.slot_handlePatchbayClientPositionChangedCallback)
596 host.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback)
597 host.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback)
598 host.PatchbayPortChangedCallback.connect(self.slot_handlePatchbayPortChangedCallback)
599 host.PatchbayPortGroupAddedCallback.connect(self.slot_handlePatchbayPortGroupAddedCallback)
600 host.PatchbayPortGroupRemovedCallback.connect(self.slot_handlePatchbayPortGroupRemovedCallback)
601 host.PatchbayPortGroupChangedCallback.connect(self.slot_handlePatchbayPortGroupChangedCallback)
602 host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
603 host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)
605 host.NSMCallback.connect(self.slot_handleNSMCallback)
607 host.DebugCallback.connect(self.slot_handleDebugCallback)
608 host.InfoCallback.connect(self.slot_handleInfoCallback)
609 host.ErrorCallback.connect(self.slot_handleErrorCallback)
610 host.QuitCallback.connect(self.slot_handleQuitCallback)
611 host.InlineDisplayRedrawCallback.connect(self.slot_handleInlineDisplayRedrawCallback)
613 # ----------------------------------------------------------------------------------------------------
614 # Final setup
616 self.ui.text_logs.clear()
617 self.slot_logButtonsState(False)
618 self.setProperWindowTitle()
620 # Disable non-supported features
621 features = gCarla.utils.get_supported_features()
623 if "link" not in features:
624 self.ui.cb_transport_link.setEnabled(False)
625 self.ui.cb_transport_link.setVisible(False)
627 if "juce" not in features:
628 self.ui.act_help_about_juce.setEnabled(False)
629 self.ui.act_help_about_juce.setVisible(False)
631 # Plugin needs to have timers always running so it receives messages
632 if self.host.isPlugin or self.host.isRemote:
633 self.startTimers()
635 # Qt needs this so it properly creates & resizes the canvas
636 self.ui.tabWidget.blockSignals(True)
637 self.ui.tabWidget.setCurrentIndex(1)
638 self.ui.tabWidget.setCurrentIndex(0)
639 self.ui.tabWidget.blockSignals(False)
641 # Start in patchbay tab if using forced patchbay mode
642 if host.processModeForced and host.processMode == ENGINE_PROCESS_MODE_PATCHBAY:
643 self.ui.tabWidget.setCurrentIndex(1)
645 # Load initial project file if set
646 if not (self.host.isControl or self.host.isPlugin):
647 projectFile = getInitialProjectFile(QApplication.instance())
649 if projectFile:
650 self.loadProjectLater(projectFile)
652 # For NSM we wait for the open message
653 if NSM_URL and host.nsmOK:
654 host.nsm_ready(NSM_CALLBACK_INIT)
655 return
657 if not host.isControl:
658 QTimer.singleShot(0, self.slot_engineStart)
660 # --------------------------------------------------------------------------------------------------------
661 # Manage visibility state, needed for NSM
663 def hideForNSM(self):
664 for pitem in reversed(self.fPluginList):
665 if pitem is None:
666 continue
667 pitem.getWidget().hideCustomUI()
668 self.hide()
670 def showIfNeeded(self):
671 if self.host.nsmOK:
672 self.ui.act_file_quit.setText(self.tr("Hide"))
673 QApplication.instance().setQuitOnLastWindowClosed(False)
674 else:
675 self.show()
677 # --------------------------------------------------------------------------------------------------------
678 # Setup
680 def compactPlugin(self, pluginId):
681 if pluginId > self.fPluginCount:
682 return
684 pitem = self.fPluginList[pluginId]
686 if pitem is None:
687 return
689 pitem.recreateWidget(True)
691 def changePluginColor(self, pluginId, color, colorStr):
692 if pluginId > self.fPluginCount:
693 return
695 pitem = self.fPluginList[pluginId]
697 if pitem is None:
698 return
700 self.host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaColor", colorStr)
701 pitem.recreateWidget(newColor = color)
703 def changePluginSkin(self, pluginId, skin):
704 if pluginId > self.fPluginCount:
705 return
707 pitem = self.fPluginList[pluginId]
709 if pitem is None:
710 return
712 self.host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkin", skin)
713 if skin not in ("default","rncbc","presets","mpresets"):
714 pitem.recreateWidget(newSkin = skin, newColor = (255,255,255))
715 else:
716 pitem.recreateWidget(newSkin = skin)
718 def findPluginInPatchbay(self, pluginId):
719 if pluginId > self.fPluginCount:
720 return
722 if self.fExternalPatchbay:
723 self.slot_canvasShowInternal()
725 if not patchcanvas.focusGroupUsingPluginId(pluginId):
726 name = self.host.get_plugin_info(pluginId)['name']
727 if not patchcanvas.focusGroupUsingGroupName(name):
728 return
730 self.ui.tabWidget.setCurrentIndex(1)
732 def switchPlugins(self, pluginIdA, pluginIdB):
733 if pluginIdA == pluginIdB:
734 return
735 if pluginIdA < 0 or pluginIdB < 0:
736 return
737 if pluginIdA >= self.fPluginCount or pluginIdB >= self.fPluginCount:
738 return
740 self.host.switch_plugins(pluginIdA, pluginIdB)
742 itemA = self.fPluginList[pluginIdA]
743 compactA = itemA.isCompacted()
744 guiShownA = itemA.isGuiShown()
746 itemB = self.fPluginList[pluginIdB]
747 compactB = itemB.isCompacted()
748 guiShownB = itemB.isGuiShown()
750 itemA.setPluginId(pluginIdA)
751 itemA.recreateWidget2(compactB, guiShownB)
753 itemB.setPluginId(pluginIdB)
754 itemB.recreateWidget2(compactA, guiShownA)
756 if self.fWithCanvas:
757 self.slot_canvasRefresh()
759 def setLoadRDFsNeeded(self):
760 self.fLadspaRdfNeedsUpdate = True
762 def setProperWindowTitle(self):
763 title = self.fClientName
765 if self.fProjectFilename and not self.host.nsmOK:
766 title += " - %s" % os.path.basename(self.fProjectFilename)
767 if self.fSessionManagerName:
768 title += " (%s)" % self.fSessionManagerName
770 self.setWindowTitle(title)
772 def updateBufferSize(self, newBufferSize):
773 if self.fBufferSize == newBufferSize:
774 return
775 self.fBufferSize = newBufferSize
776 self.ui.cb_buffer_size.clear()
777 self.ui.cb_buffer_size.addItem(str(newBufferSize))
778 self.ui.cb_buffer_size.setCurrentIndex(0)
780 def updateSampleRate(self, newSampleRate):
781 if self.fSampleRate == newSampleRate:
782 return
783 self.fSampleRate = newSampleRate
784 self.ui.cb_sample_rate.clear()
785 self.ui.cb_sample_rate.addItem(str(newSampleRate))
786 self.ui.cb_sample_rate.setCurrentIndex(0)
787 self.refreshTransport(True)
789 # --------------------------------------------------------------------------------------------------------
790 # Files
792 def loadProjectNow(self):
793 if not self.fProjectFilename:
794 return qCritical("ERROR: loading project without filename set")
795 if self.host.nsmOK and not os.path.exists(self.fProjectFilename):
796 return
798 self.projectLoadingStarted()
799 self.fIsProjectLoading = True
801 if not self.host.load_project(self.fProjectFilename):
802 self.fIsProjectLoading = False
803 self.projectLoadingFinished(True)
805 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load project"),
806 self.host.get_last_error(),
807 QMessageBox.Ok, QMessageBox.Ok)
809 def loadProjectLater(self, filename):
810 self.fProjectFilename = QFileInfo(filename).absoluteFilePath()
811 self.setProperWindowTitle()
812 QTimer.singleShot(1, self.slot_loadProjectNow)
814 def saveProjectNow(self):
815 if not self.fProjectFilename:
816 return qCritical("ERROR: saving project without filename set")
818 if not self.host.save_project(self.fProjectFilename):
819 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to save project"),
820 self.host.get_last_error(),
821 QMessageBox.Ok, QMessageBox.Ok)
822 return
824 def projectLoadingStarted(self):
825 self.ui.rack.setEnabled(False)
826 self.ui.graphicsView.setEnabled(False)
828 def projectLoadingFinished(self, refreshCanvas):
829 self.ui.rack.setEnabled(True)
830 self.ui.graphicsView.setEnabled(True)
832 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE or not self.fWithCanvas:
833 return
835 if refreshCanvas and not self.loadExternalCanvasGroupPositionsIfNeeded(self.fProjectFilename):
836 QTimer.singleShot(1, self.slot_canvasRefresh)
838 def loadExternalCanvasGroupPositionsIfNeeded(self, filename):
839 extrafile = filename.rsplit(".",1)[0]+".json"
840 if not os.path.exists(extrafile):
841 return False
843 with open(filename, "r") as fh:
844 if "".join(fh.readlines(90)).find("<CARLA-PROJECT VERSION='2.0'>") < 0:
845 return False
847 with open(extrafile, "r") as fh:
848 try:
849 canvasdata = json.load(fh)['canvas']
850 except:
851 return False
853 print("NOTICE: loading old-style canvas group positions via legacy json file")
854 patchcanvas.restoreGroupPositions(canvasdata)
855 QTimer.singleShot(1, self.slot_canvasRefresh)
856 return True
858 # --------------------------------------------------------------------------------------------------------
859 # Files (menu actions)
861 @pyqtSlot()
862 def slot_fileNew(self):
863 if self.fPluginCount > 0 and QMessageBox.question(self, self.tr("New File"),
864 self.tr("Plugins that are currently loaded will be removed. Are you sure?"),
865 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
866 return
868 self.pluginRemoveAll()
869 self.fProjectFilename = ""
870 self.setProperWindowTitle()
871 self.host.clear_project_filename()
873 @pyqtSlot()
874 def slot_fileOpen(self):
875 fileFilter = self.tr("Carla Project File (*.carxp);;Carla Preset File (*.carxs)")
876 filename, ok = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
878 # FIXME use ok value, test if it works as expected
879 if not filename:
880 return
882 newFile = True
884 if self.fPluginCount > 0:
885 ask = QMessageBox.question(self, self.tr("Question"), self.tr("There are some plugins loaded, do you want to remove them now?"),
886 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
887 newFile = (ask == QMessageBox.Yes)
889 if newFile:
890 self.pluginRemoveAll()
891 self.fProjectFilename = filename
892 self.setProperWindowTitle()
893 self.loadProjectNow()
894 else:
895 filenameOld = self.fProjectFilename
896 self.fProjectFilename = filename
897 self.loadProjectNow()
898 self.fProjectFilename = filenameOld
900 @pyqtSlot()
901 def slot_fileSave(self, saveAs=False):
902 if self.fProjectFilename and not saveAs:
903 return self.saveProjectNow()
905 fileFilter = self.tr("Carla Project File (*.carxp)")
906 filename, ok = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
908 # FIXME use ok value, test if it works as expected
909 if not filename:
910 return
912 if not filename.lower().endswith(".carxp"):
913 filename += ".carxp"
915 if self.fProjectFilename != filename:
916 self.fProjectFilename = filename
917 self.setProperWindowTitle()
919 self.saveProjectNow()
921 @pyqtSlot()
922 def slot_fileSaveAs(self):
923 self.slot_fileSave(True)
925 @pyqtSlot()
926 def slot_loadProjectNow(self):
927 self.loadProjectNow()
929 # --------------------------------------------------------------------------------------------------------
930 # Engine (menu actions)
932 @pyqtSlot()
933 def slot_engineStart(self):
934 audioDriver = setEngineSettings(self.host, self.fSavedSettings)
936 firstInit = self.fFirstEngineInit
937 self.fFirstEngineInit = False
939 self.ui.text_logs.appendPlainText("======= Starting engine =======")
941 if self.host.engine_init(audioDriver, self.fClientName):
942 if firstInit and not (self.host.isControl or self.host.isPlugin):
943 settings = QSafeSettings()
944 lastBpm = settings.value("LastBPM", 120.0, float)
945 del settings
946 if lastBpm >= 20.0:
947 self.host.transport_bpm(lastBpm)
948 return
950 elif firstInit:
951 self.ui.text_logs.appendPlainText("Failed to start engine on first try, ignored")
952 return
954 audioError = self.host.get_last_error()
956 if audioError:
957 QMessageBox.critical(self, self.tr("Error"),
958 self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
959 else:
960 QMessageBox.critical(self, self.tr("Error"),
961 self.tr("Could not connect to Audio backend '%s'" % audioDriver))
963 @pyqtSlot()
964 def slot_engineStop(self, forced = False):
965 self.ui.text_logs.appendPlainText("======= Stopping engine =======")
967 if self.fPluginCount == 0 or not self.host.is_engine_running():
968 self.engineStopFinal()
969 return True
971 if not forced:
972 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"
973 "Do you want to do this now?"),
974 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
975 if ask != QMessageBox.Yes:
976 return False
978 return self.slot_engineStopTryAgain()
980 @pyqtSlot()
981 def slot_engineConfig(self):
982 engineRunning = self.host.is_engine_running()
984 if engineRunning:
985 dialog = RuntimeDriverSettingsW(self.fParentOrSelf, self.host)
987 else:
988 if self.host.isPlugin:
989 driverName = "Plugin"
990 driverIndex = 0
991 elif self.host.audioDriverForced:
992 driverName = self.host.audioDriverForced
993 driverIndex = 0
994 else:
995 settings = QSafeSettings()
996 driverName = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
997 for i in range(self.host.get_engine_driver_count()):
998 if self.host.get_engine_driver_name(i) == driverName:
999 driverIndex = i
1000 break
1001 else:
1002 driverIndex = -1
1003 del settings
1004 dialog = DriverSettingsW(self.fParentOrSelf, self.host, driverIndex, driverName)
1005 dialog.ui.ico_restart.hide()
1006 dialog.ui.label_restart.hide()
1007 dialog.adjustSize()
1009 if not dialog.exec_():
1010 return
1012 audioDevice, bufferSize, sampleRate = dialog.getValues()
1014 if engineRunning:
1015 self.host.set_engine_buffer_size_and_sample_rate(bufferSize, sampleRate)
1016 else:
1017 self.host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
1018 self.host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, bufferSize, "")
1019 self.host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, sampleRate, "")
1021 @pyqtSlot()
1022 def slot_engineStopTryAgain(self):
1023 if self.host.is_engine_running() and not self.host.set_engine_about_to_close():
1024 QTimer.singleShot(0, self.slot_engineStopTryAgain)
1025 return False
1027 self.engineStopFinal()
1028 return True
1030 def engineStopFinal(self):
1031 patchcanvas.handleAllPluginsRemoved()
1032 self.killTimers()
1034 if self.fCustomStopAction == self.CUSTOM_ACTION_PROJECT_LOAD:
1035 self.removeAllPlugins()
1036 else:
1037 self.fProjectFilename = ""
1038 self.setProperWindowTitle()
1039 if self.fPluginCount != 0:
1040 self.fCurrentlyRemovingAllPlugins = True
1041 self.projectLoadingStarted()
1043 if self.host.is_engine_running() and not self.host.remove_all_plugins():
1044 self.ui.text_logs.appendPlainText("Failed to remove all plugins, error was:")
1045 self.ui.text_logs.appendPlainText(self.host.get_last_error())
1047 if not self.host.engine_close():
1048 self.ui.text_logs.appendPlainText("Failed to stop engine, error was:")
1049 self.ui.text_logs.appendPlainText(self.host.get_last_error())
1051 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE:
1052 self.close()
1053 elif self.fCustomStopAction == self.CUSTOM_ACTION_PROJECT_LOAD:
1054 self.slot_engineStart()
1055 self.loadProjectNow()
1056 self.host.nsm_ready(NSM_CALLBACK_OPEN)
1058 self.fCustomStopAction = self.CUSTOM_ACTION_NONE
1060 # --------------------------------------------------------------------------------------------------------
1061 # Engine (host callbacks)
1063 @pyqtSlot(int, int, int, int, float, str)
1064 def slot_handleEngineStartedCallback(self, pluginCount, processMode, transportMode, bufferSize, sampleRate, driverName):
1065 self.ui.menu_PluginMacros.setEnabled(True)
1066 self.ui.menu_Canvas.setEnabled(True)
1067 self.ui.w_transport.setEnabled(True)
1069 self.ui.act_canvas_show_internal.blockSignals(True)
1070 self.ui.act_canvas_show_external.blockSignals(True)
1072 if processMode == ENGINE_PROCESS_MODE_PATCHBAY and not self.host.isPlugin:
1073 self.ui.act_canvas_show_internal.setChecked(True)
1074 self.ui.act_canvas_show_internal.setVisible(True)
1075 self.ui.act_canvas_show_external.setChecked(False)
1076 self.ui.act_canvas_show_external.setVisible(True)
1077 self.fExternalPatchbay = False
1078 else:
1079 self.ui.act_canvas_show_internal.setChecked(False)
1080 self.ui.act_canvas_show_internal.setVisible(False)
1081 self.ui.act_canvas_show_external.setChecked(True)
1082 self.ui.act_canvas_show_external.setVisible(False)
1083 self.fExternalPatchbay = not self.host.isPlugin
1085 self.ui.act_canvas_show_internal.blockSignals(False)
1086 self.ui.act_canvas_show_external.blockSignals(False)
1088 if not (self.host.isControl or self.host.isPlugin):
1089 canSave = (self.fProjectFilename and os.path.exists(self.fProjectFilename)) or not self.fSessionManagerName
1090 self.ui.act_file_save.setEnabled(canSave)
1091 self.ui.act_engine_start.setEnabled(False)
1092 self.ui.act_engine_stop.setEnabled(True)
1094 if not self.host.isPlugin:
1095 self.enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED)
1097 if self.host.isPlugin or not self.fSessionManagerName:
1098 self.ui.act_file_open.setEnabled(True)
1099 self.ui.act_file_save_as.setEnabled(True)
1101 self.ui.cb_transport_jack.setChecked(transportMode == ENGINE_TRANSPORT_MODE_JACK)
1102 self.ui.cb_transport_jack.setEnabled(driverName == "JACK" and processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS)
1104 if self.ui.cb_transport_link.isEnabled():
1105 self.ui.cb_transport_link.setChecked(":link:" in self.host.transportExtra)
1107 self.updateBufferSize(bufferSize)
1108 self.updateSampleRate(int(sampleRate))
1109 self.refreshRuntimeInfo(0.0, 0)
1110 self.startTimers()
1112 self.ui.text_logs.appendPlainText("======= Engine started ========")
1113 self.ui.text_logs.appendPlainText("Carla engine started, details:")
1114 self.ui.text_logs.appendPlainText(" Driver name: %s" % driverName)
1115 self.ui.text_logs.appendPlainText(" Sample rate: %i" % int(sampleRate))
1116 self.ui.text_logs.appendPlainText(" Process mode: %s" % processMode2Str(processMode))
1118 @pyqtSlot()
1119 def slot_handleEngineStoppedCallback(self):
1120 self.ui.text_logs.appendPlainText("======= Engine stopped ========")
1122 if self.fWithCanvas:
1123 patchcanvas.clear()
1125 self.killTimers()
1127 # just in case
1128 self.removeAllPlugins()
1129 self.refreshRuntimeInfo(0.0, 0)
1131 self.ui.menu_PluginMacros.setEnabled(False)
1132 self.ui.menu_Canvas.setEnabled(False)
1133 self.ui.w_transport.setEnabled(False)
1135 if not (self.host.isControl or self.host.isPlugin):
1136 self.ui.act_file_save.setEnabled(False)
1137 self.ui.act_engine_start.setEnabled(True)
1138 self.ui.act_engine_stop.setEnabled(False)
1140 if self.host.isPlugin or not self.fSessionManagerName:
1141 self.ui.act_file_open.setEnabled(False)
1142 self.ui.act_file_save_as.setEnabled(False)
1144 @pyqtSlot(int, str)
1145 def slot_handleTransportModeChangedCallback(self, transportMode, transportExtra):
1146 self.enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED)
1148 self.ui.cb_transport_jack.setChecked(transportMode == ENGINE_TRANSPORT_MODE_JACK)
1149 self.ui.cb_transport_link.setChecked(":link:" in transportExtra)
1151 @pyqtSlot(int)
1152 def slot_handleBufferSizeChangedCallback(self, newBufferSize):
1153 self.updateBufferSize(newBufferSize)
1155 @pyqtSlot(float)
1156 def slot_handleSampleRateChangedCallback(self, newSampleRate):
1157 self.updateSampleRate(int(newSampleRate))
1159 @pyqtSlot(int, bool, str)
1160 def slot_handleCancelableActionCallback(self, pluginId, started, action):
1161 if self.fCancelableActionBox is not None:
1162 self.fCancelableActionBox.close()
1164 if started:
1165 self.fCancelableActionBox = QMessageBox(self)
1166 self.fCancelableActionBox.setIcon(QMessageBox.Information)
1167 self.fCancelableActionBox.setWindowTitle(self.tr("Action in progress"))
1168 self.fCancelableActionBox.setText(action)
1169 self.fCancelableActionBox.setInformativeText(self.tr("An action is in progress, please wait..."))
1170 self.fCancelableActionBox.setStandardButtons(QMessageBox.Cancel)
1171 self.fCancelableActionBox.setDefaultButton(QMessageBox.Cancel)
1172 self.fCancelableActionBox.buttonClicked.connect(self.slot_canlableActionBoxClicked)
1173 self.fCancelableActionBox.show()
1175 else:
1176 self.fCancelableActionBox = None
1178 @pyqtSlot()
1179 def slot_canlableActionBoxClicked(self):
1180 self.host.cancel_engine_action()
1182 @pyqtSlot()
1183 def slot_handleProjectLoadFinishedCallback(self):
1184 self.fIsProjectLoading = False
1185 self.projectLoadingFinished(False)
1187 # --------------------------------------------------------------------------------------------------------
1188 # Plugins
1190 def removeAllPlugins(self):
1191 self.ui.act_plugin_remove_all.setEnabled(False)
1192 patchcanvas.handleAllPluginsRemoved()
1194 while self.ui.listWidget.takeItem(0):
1195 pass
1197 self.clearSideStuff()
1199 for pitem in self.fPluginList:
1200 if pitem is None:
1201 continue
1203 pitem.close()
1204 del pitem
1206 self.fPluginCount = 0
1207 self.fPluginList = []
1209 # --------------------------------------------------------------------------------------------------------
1210 # Plugins (menu actions)
1212 def showAddPluginDialog(self):
1213 # TODO self.fHasLoadedLv2Plugins
1214 if self.fPluginListDialog is None:
1215 hostSettings = {
1216 'showPluginBridges': self.fSavedSettings[CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES],
1217 'showWineBridges': self.fSavedSettings[CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES],
1218 'useSystemIcons': self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS],
1219 'wineAutoPrefix': self.fSavedSettings[CARLA_KEY_WINE_AUTO_PREFIX],
1220 'wineExecutable': self.fSavedSettings[CARLA_KEY_WINE_EXECUTABLE],
1221 'wineFallbackPrefix': self.fSavedSettings[CARLA_KEY_WINE_FALLBACK_PREFIX],
1223 self.fPluginListDialog = d = gCarla.felib.createPluginListDialog(self.fParentOrSelf, hostSettings)
1225 gCarla.felib.setPluginListDialogPath(d, PLUGIN_LADSPA, self.fSavedSettings[CARLA_KEY_PATHS_LADSPA])
1226 gCarla.felib.setPluginListDialogPath(d, PLUGIN_DSSI, self.fSavedSettings[CARLA_KEY_PATHS_DSSI])
1227 gCarla.felib.setPluginListDialogPath(d, PLUGIN_LV2, self.fSavedSettings[CARLA_KEY_PATHS_LV2])
1228 gCarla.felib.setPluginListDialogPath(d, PLUGIN_VST2, self.fSavedSettings[CARLA_KEY_PATHS_VST2])
1229 gCarla.felib.setPluginListDialogPath(d, PLUGIN_VST3, self.fSavedSettings[CARLA_KEY_PATHS_VST3])
1230 gCarla.felib.setPluginListDialogPath(d, PLUGIN_SF2, self.fSavedSettings[CARLA_KEY_PATHS_SF2])
1231 gCarla.felib.setPluginListDialogPath(d, PLUGIN_SFZ, self.fSavedSettings[CARLA_KEY_PATHS_SFZ])
1232 gCarla.felib.setPluginListDialogPath(d, PLUGIN_JSFX, self.fSavedSettings[CARLA_KEY_PATHS_JSFX])
1233 gCarla.felib.setPluginListDialogPath(d, PLUGIN_CLAP, self.fSavedSettings[CARLA_KEY_PATHS_CLAP])
1235 ret = gCarla.felib.execPluginListDialog(self.fPluginListDialog)
1237 # TODO
1238 #if dialog.fFavoritePluginsChanged:
1239 #self.fFavoritePlugins = dialog.fFavoritePlugins
1241 if not ret:
1242 return
1244 if not self.host.is_engine_running():
1245 QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
1246 return
1248 btype = ret['build']
1249 ptype = ret['type']
1250 filename = ret['filename']
1251 label = ret['label']
1252 uniqueId = ret['uniqueId']
1253 extraPtr = None
1255 return (btype, ptype, filename, label, uniqueId, extraPtr)
1257 def showAddJackAppDialog(self):
1258 ret = gCarla.felib.createAndExecJackAppDialog(self.fParentOrSelf, self.fProjectFilename)
1260 if not ret:
1261 return
1263 if not self.host.is_engine_running():
1264 QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
1265 return
1267 return ret
1269 @pyqtSlot()
1270 def slot_favoritePluginAdd(self):
1271 plugin = self.sender().data()
1273 if plugin is None:
1274 return
1276 if not self.host.add_plugin(plugin['build'], plugin['type'], plugin['filename'], None,
1277 plugin['label'], plugin['uniqueId'], None, PLUGIN_OPTIONS_NULL):
1278 # remove plugin from favorites
1279 try:
1280 self.fFavoritePlugins.remove(plugin)
1281 except ValueError:
1282 pass
1283 else:
1284 settingsDBf = QSafeSettings("falkTX", "CarlaDatabase2")
1285 settingsDBf.setValue("PluginDatabase/Favorites", self.fFavoritePlugins)
1286 settingsDBf.sync()
1287 del settingsDBf
1289 CustomMessageBox(self,
1290 QMessageBox.Critical,
1291 self.tr("Error"),
1292 self.tr("Failed to load plugin"),
1293 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1295 @pyqtSlot()
1296 def slot_showPluginActionsMenu(self):
1297 menu = QMenu(self)
1299 menu.addSection("Plugins")
1300 menu.addAction(self.ui.act_plugin_add)
1302 if len(self.fFavoritePlugins) != 0:
1303 fmenu = QMenu("Add from favorites", self)
1304 for p in self.fFavoritePlugins:
1305 act = fmenu.addAction(p['name'])
1306 act.setData(p)
1307 act.triggered.connect(self.slot_favoritePluginAdd)
1308 menu.addMenu(fmenu)
1310 menu.addAction(self.ui.act_plugin_remove_all)
1312 menu.addSection("All plugins (macros)")
1313 menu.addAction(self.ui.act_plugins_enable)
1314 menu.addAction(self.ui.act_plugins_disable)
1315 menu.addSeparator()
1316 menu.addAction(self.ui.act_plugins_volume100)
1317 menu.addAction(self.ui.act_plugins_mute)
1318 menu.addSeparator()
1319 menu.addAction(self.ui.act_plugins_wet100)
1320 menu.addAction(self.ui.act_plugins_bypass)
1321 menu.addSeparator()
1322 menu.addAction(self.ui.act_plugins_center)
1323 menu.addSeparator()
1324 menu.addAction(self.ui.act_plugins_compact)
1325 menu.addAction(self.ui.act_plugins_expand)
1327 menu.exec_(QCursor.pos())
1329 @pyqtSlot()
1330 def slot_pluginAdd(self):
1331 data = self.showAddPluginDialog()
1333 if data is None:
1334 return
1336 btype, ptype, filename, label, uniqueId, extraPtr = data
1338 if not self.host.add_plugin(btype, ptype, filename, None, label, uniqueId, extraPtr, PLUGIN_OPTIONS_NULL):
1339 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"),
1340 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1342 @pyqtSlot()
1343 def slot_confirmRemoveAll(self):
1344 if self.fPluginCount == 0:
1345 return
1347 if QMessageBox.question(self, self.tr("Remove All"),
1348 self.tr("Are you sure you want to remove all plugins?"),
1349 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
1350 return
1352 self.pluginRemoveAll()
1354 def pluginRemoveAll(self):
1355 if self.fPluginCount == 0:
1356 return
1358 self.fCurrentlyRemovingAllPlugins = True
1359 self.projectLoadingStarted()
1361 if not self.host.remove_all_plugins():
1362 self.projectLoadingFinished(True)
1363 self.fCurrentlyRemovingAllPlugins = False
1364 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1365 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1367 @pyqtSlot()
1368 def slot_jackAppAdd(self):
1369 data = self.showAddJackAppDialog()
1371 if data is None:
1372 return
1374 if not data['command']:
1375 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Cannot add jack application"),
1376 self.tr("command is empty"), QMessageBox.Ok, QMessageBox.Ok)
1377 return
1379 if not self.host.add_plugin(BINARY_NATIVE, PLUGIN_JACK,
1380 data['command'], data['name'], data['labelSetup'],
1381 0, None, PLUGIN_OPTIONS_NULL):
1382 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"),
1383 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1385 # --------------------------------------------------------------------------------------------------------
1386 # Plugins (macros)
1388 @pyqtSlot()
1389 def slot_pluginsEnable(self):
1390 if not self.host.is_engine_running():
1391 return
1393 for pitem in self.fPluginList:
1394 if pitem is None:
1395 break
1397 pitem.getWidget().setActive(True, True, True)
1399 @pyqtSlot()
1400 def slot_pluginsDisable(self):
1401 if not self.host.is_engine_running():
1402 return
1404 for pitem in self.fPluginList:
1405 if pitem is None:
1406 break
1408 pitem.getWidget().setActive(False, True, True)
1410 @pyqtSlot()
1411 def slot_pluginsVolume100(self):
1412 if not self.host.is_engine_running():
1413 return
1415 for pitem in self.fPluginList:
1416 if pitem is None:
1417 break
1419 pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 1.0)
1421 @pyqtSlot()
1422 def slot_pluginsMute(self):
1423 if not self.host.is_engine_running():
1424 return
1426 for pitem in self.fPluginList:
1427 if pitem is None:
1428 break
1430 pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 0.0)
1432 @pyqtSlot()
1433 def slot_pluginsWet100(self):
1434 if not self.host.is_engine_running():
1435 return
1437 for pitem in self.fPluginList:
1438 if pitem is None:
1439 break
1441 pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 1.0)
1443 @pyqtSlot()
1444 def slot_pluginsBypass(self):
1445 if not self.host.is_engine_running():
1446 return
1448 for pitem in self.fPluginList:
1449 if pitem is None:
1450 break
1452 pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 0.0)
1454 @pyqtSlot()
1455 def slot_pluginsCenter(self):
1456 if not self.host.is_engine_running():
1457 return
1459 for pitem in self.fPluginList:
1460 if pitem is None:
1461 break
1463 pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_LEFT, -1.0)
1464 pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_RIGHT, 1.0)
1465 pitem.getWidget().setInternalParameter(PARAMETER_PANNING, 0.0)
1467 @pyqtSlot()
1468 def slot_pluginsCompact(self):
1469 for pitem in self.fPluginList:
1470 if pitem is None:
1471 break
1472 pitem.compact()
1474 @pyqtSlot()
1475 def slot_pluginsExpand(self):
1476 for pitem in self.fPluginList:
1477 if pitem is None:
1478 break
1479 pitem.expand()
1481 # --------------------------------------------------------------------------------------------------------
1482 # Plugins (host callbacks)
1484 @pyqtSlot(int, int, str)
1485 def slot_handlePluginAddedCallback(self, pluginId, pluginType, pluginName):
1486 if pluginId != self.fPluginCount:
1487 print("ERROR: pluginAdded mismatch Id:", pluginId, self.fPluginCount)
1488 pitem = self.getPluginItem(pluginId)
1489 pitem.recreateWidget()
1490 return
1492 pitem = self.ui.listWidget.createItem(pluginId, self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN])
1493 self.fPluginList.append(pitem)
1494 self.fPluginCount += 1
1496 self.ui.act_plugin_remove_all.setEnabled(self.fPluginCount > 0)
1498 if pluginType == PLUGIN_LV2:
1499 self.fHasLoadedLv2Plugins = True
1501 @pyqtSlot(int)
1502 def slot_handlePluginRemovedCallback(self, pluginId):
1503 if self.fWithCanvas:
1504 patchcanvas.handlePluginRemoved(pluginId)
1506 if pluginId in self.fSelectedPlugins:
1507 self.clearSideStuff()
1509 if self.fPluginCount == 0:
1510 return
1512 pitem = self.getPluginItem(pluginId)
1514 self.fPluginCount -= 1
1515 self.fPluginList.pop(pluginId)
1516 self.ui.listWidget.takeItem(pluginId)
1518 if pitem is not None:
1519 pitem.close()
1520 del pitem
1522 if self.fPluginCount == 0:
1523 self.ui.act_plugin_remove_all.setEnabled(False)
1524 if self.fCurrentlyRemovingAllPlugins:
1525 self.fCurrentlyRemovingAllPlugins = False
1526 self.projectLoadingFinished(False)
1527 return
1529 # push all plugins 1 slot back
1530 for i in range(pluginId, self.fPluginCount):
1531 pitem = self.fPluginList[i]
1532 pitem.setPluginId(i)
1534 self.ui.act_plugin_remove_all.setEnabled(True)
1536 # --------------------------------------------------------------------------------------------------------
1537 # Canvas
1539 def clearSideStuff(self):
1540 if self.fWithCanvas:
1541 self.scene.clearSelection()
1543 self.fSelectedPlugins = []
1545 self.ui.keyboard.allNotesOff(False)
1546 self.ui.scrollArea.setEnabled(False)
1548 self.fPeaksCleared = True
1549 self.ui.peak_in.displayMeter(1, 0.0, True)
1550 self.ui.peak_in.displayMeter(2, 0.0, True)
1551 self.ui.peak_out.displayMeter(1, 0.0, True)
1552 self.ui.peak_out.displayMeter(2, 0.0, True)
1554 def setupCanvas(self):
1555 pOptions = patchcanvas.options_t()
1556 pOptions.theme_name = self.fSavedSettings[CARLA_KEY_CANVAS_THEME]
1557 pOptions.auto_hide_groups = self.fSavedSettings[CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS]
1558 pOptions.auto_select_items = self.fSavedSettings[CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS]
1559 pOptions.use_bezier_lines = self.fSavedSettings[CARLA_KEY_CANVAS_USE_BEZIER_LINES]
1560 pOptions.antialiasing = self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING]
1561 pOptions.inline_displays = self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS]
1563 if self.fSavedSettings[CARLA_KEY_CANVAS_FANCY_EYE_CANDY]:
1564 pOptions.eyecandy = patchcanvas.EYECANDY_FULL
1565 elif self.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY]:
1566 pOptions.eyecandy = patchcanvas.EYECANDY_SMALL
1567 else:
1568 pOptions.eyecandy = patchcanvas.EYECANDY_NONE
1570 pFeatures = patchcanvas.features_t()
1571 pFeatures.group_info = False
1572 pFeatures.group_rename = False
1573 pFeatures.port_info = False
1574 pFeatures.port_rename = False
1575 pFeatures.handle_group_pos = False
1577 patchcanvas.setOptions(pOptions)
1578 patchcanvas.setFeatures(pFeatures)
1579 patchcanvas.init("Carla2", self.scene, canvasCallback, False)
1581 tryCanvasSize = self.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x")
1583 if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit() and tryCanvasSize[1].isdigit():
1584 self.fCanvasWidth = int(tryCanvasSize[0])
1585 self.fCanvasHeight = int(tryCanvasSize[1])
1586 else:
1587 self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH
1588 self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT
1590 patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight)
1591 patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2)
1592 self.ui.graphicsView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight)
1594 self.ui.miniCanvasPreview.setViewTheme(patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color())
1595 self.ui.miniCanvasPreview.init(self.scene, self.fCanvasWidth, self.fCanvasHeight, self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING])
1597 if self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] != patchcanvas.ANTIALIASING_NONE:
1598 self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, True)
1600 fullAA = self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL
1601 self.ui.graphicsView.setRenderHint(QPainter.SmoothPixmapTransform, fullAA)
1602 self.ui.graphicsView.setRenderHint(QPainter.TextAntialiasing, fullAA)
1604 if self.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL:
1605 self.ui.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING])
1607 else:
1608 self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, False)
1610 if self.fSavedSettings[CARLA_KEY_CANVAS_FULL_REPAINTS]:
1611 self.ui.graphicsView.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
1612 else:
1613 self.ui.graphicsView.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
1615 def updateCanvasInitialPos(self):
1616 x = self.ui.graphicsView.horizontalScrollBar().value() + self.width()/4
1617 y = self.ui.graphicsView.verticalScrollBar().value() + self.height()/4
1618 patchcanvas.setInitialPos(x, y)
1620 def updateMiniCanvasLater(self):
1621 QTimer.singleShot(self.fMiniCanvasUpdateTimeout, self.ui.miniCanvasPreview.update)
1623 # --------------------------------------------------------------------------------------------------------
1624 # Canvas (menu actions)
1626 @pyqtSlot()
1627 def slot_canvasShowInternal(self):
1628 self.fExternalPatchbay = False
1629 self.ui.act_canvas_show_internal.blockSignals(True)
1630 self.ui.act_canvas_show_external.blockSignals(True)
1631 self.ui.act_canvas_show_internal.setChecked(True)
1632 self.ui.act_canvas_show_external.setChecked(False)
1633 self.ui.act_canvas_show_internal.blockSignals(False)
1634 self.ui.act_canvas_show_external.blockSignals(False)
1635 self.slot_canvasRefresh()
1637 @pyqtSlot()
1638 def slot_canvasShowExternal(self):
1639 self.fExternalPatchbay = True
1640 self.ui.act_canvas_show_internal.blockSignals(True)
1641 self.ui.act_canvas_show_external.blockSignals(True)
1642 self.ui.act_canvas_show_internal.setChecked(False)
1643 self.ui.act_canvas_show_external.setChecked(True)
1644 self.ui.act_canvas_show_internal.blockSignals(False)
1645 self.ui.act_canvas_show_external.blockSignals(False)
1646 self.slot_canvasRefresh()
1648 @pyqtSlot()
1649 def slot_canvasArrange(self):
1650 patchcanvas.arrange()
1652 @pyqtSlot()
1653 def slot_canvasRefresh(self):
1654 patchcanvas.clear()
1656 if self.host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and self.host.isPlugin:
1657 return
1659 if self.host.is_engine_running():
1660 self.host.patchbay_refresh(self.fExternalPatchbay)
1662 self.updateMiniCanvasLater()
1664 @pyqtSlot()
1665 def slot_canvasZoomFit(self):
1666 self.scene.zoom_fit()
1668 @pyqtSlot()
1669 def slot_canvasZoomIn(self):
1670 self.scene.zoom_in()
1672 @pyqtSlot()
1673 def slot_canvasZoomOut(self):
1674 self.scene.zoom_out()
1676 @pyqtSlot()
1677 def slot_canvasZoomReset(self):
1678 self.scene.zoom_reset()
1680 def _canvasImageRender(self, zoom = 1.0):
1681 image = QImage(self.scene.width()*zoom, self.scene.height()*zoom, QImage.Format_RGB32)
1682 painter = QPainter(image)
1683 painter.save()
1684 painter.setRenderHints(painter.renderHints() | QPainter.Antialiasing | QPainter.TextAntialiasing)
1685 self.scene.clearSelection()
1686 self.scene.render(painter)
1687 painter.restore()
1688 del painter
1689 return image
1691 def _canvasImageWrite(self, iw: QImageWriter, imgFormat: bytes, image: QImage):
1692 iw.setFormat(imgFormat)
1693 iw.setCompression(-1)
1694 if QT_VERSION >= 0x50500:
1695 iw.setOptimizedWrite(True)
1696 iw.write(image)
1698 @pyqtSlot()
1699 def slot_canvasSaveImage(self):
1700 if self.fProjectFilename:
1701 dir = QFileInfo(self.fProjectFilename).absoluteDir().absolutePath()
1702 else:
1703 dir = self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER]
1705 fileDialog = QFileDialog(self)
1706 fileDialog.setAcceptMode(QFileDialog.AcceptSave)
1707 fileDialog.setDirectory(dir)
1708 fileDialog.setFileMode(QFileDialog.AnyFile)
1709 fileDialog.setMimeTypeFilters(("image/png", "image/jpeg"))
1710 fileDialog.setNameFilter(self.tr("Images (*.png *.jpg)"))
1711 fileDialog.setOptions(QFileDialog.DontUseCustomDirectoryIcons)
1712 fileDialog.setWindowTitle(self.tr("Save Image"))
1714 ok = fileDialog.exec_()
1716 if not ok:
1717 return
1719 newPath = fileDialog.selectedFiles()
1721 if len(newPath) != 1:
1722 return
1724 newPath = newPath[0]
1726 if QT_VERSION >= 0x50900:
1727 if fileDialog.selectedMimeTypeFilter() == "image/jpeg":
1728 imgFormat = b"JPG"
1729 else:
1730 imgFormat = b"PNG"
1731 else:
1732 if newPath.lower().endswith((".jpg", ".jpeg")):
1733 imgFormat = b"JPG"
1734 else:
1735 imgFormat = b"PNG"
1737 sender = self.sender()
1738 if sender == self.ui.act_canvas_save_image_2x:
1739 zoom = 2.0
1740 elif sender == self.ui.act_canvas_save_image_4x:
1741 zoom = 4.0
1742 else:
1743 zoom = 1.0
1745 image = self._canvasImageRender(zoom)
1746 iw = QImageWriter(newPath)
1747 self._canvasImageWrite(iw, imgFormat, image)
1749 @pyqtSlot()
1750 def slot_canvasCopyToClipboard(self):
1751 buffer = QBuffer()
1752 buffer.open(QIODevice.WriteOnly)
1754 image = self._canvasImageRender()
1755 iw = QImageWriter(buffer, b"PNG")
1756 self._canvasImageWrite(iw, b"PNG", image)
1758 buffer.close()
1760 mimeData = QMimeData()
1761 mimeData.setData("image/png", buffer.buffer());
1763 QApplication.clipboard().setMimeData(mimeData)
1765 # --------------------------------------------------------------------------------------------------------
1766 # Canvas (canvas callbacks)
1768 @pyqtSlot()
1769 def slot_canvasSelectionChanged(self):
1770 self.updateMiniCanvasLater()
1772 @pyqtSlot(float)
1773 def slot_canvasScaleChanged(self, scale):
1774 self.ui.miniCanvasPreview.setViewScale(scale)
1776 @pyqtSlot(list)
1777 def slot_canvasPluginSelected(self, pluginList):
1778 self.ui.keyboard.allNotesOff(False)
1779 self.ui.scrollArea.setEnabled(len(pluginList) != 0) # and self.fPluginCount > 0
1780 self.fSelectedPlugins = pluginList
1782 # --------------------------------------------------------------------------------------------------------
1783 # Canvas (host callbacks)
1785 @pyqtSlot(int, int, int, str)
1786 def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName):
1787 pcSplit = patchcanvas.SPLIT_UNDEF
1788 pcIcon = patchcanvas.ICON_APPLICATION
1790 if clientIcon == PATCHBAY_ICON_PLUGIN:
1791 pcIcon = patchcanvas.ICON_PLUGIN
1792 if clientIcon == PATCHBAY_ICON_HARDWARE:
1793 pcIcon = patchcanvas.ICON_HARDWARE
1794 elif clientIcon == PATCHBAY_ICON_CARLA:
1795 pass
1796 elif clientIcon == PATCHBAY_ICON_DISTRHO:
1797 pcIcon = patchcanvas.ICON_DISTRHO
1798 elif clientIcon == PATCHBAY_ICON_FILE:
1799 pcIcon = patchcanvas.ICON_FILE
1801 patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
1803 self.updateMiniCanvasLater()
1805 if pluginId < 0:
1806 return
1807 if pluginId >= self.fPluginCount and pluginId != MAIN_CARLA_PLUGIN_ID:
1808 print("Error mapping plugin to canvas client:", clientName)
1809 return
1811 if pluginId == MAIN_CARLA_PLUGIN_ID:
1812 hasCustomUI = False
1813 hasInlineDisplay = False
1814 else:
1815 hints = self.host.get_plugin_info(pluginId)['hints']
1816 hasCustomUI = bool(hints & PLUGIN_HAS_CUSTOM_UI)
1817 hasInlineDisplay = bool(hints & PLUGIN_HAS_INLINE_DISPLAY)
1819 patchcanvas.setGroupAsPlugin(clientId, pluginId, hasCustomUI, hasInlineDisplay)
1821 @pyqtSlot(int)
1822 def slot_handlePatchbayClientRemovedCallback(self, clientId):
1823 patchcanvas.removeGroup(clientId)
1824 self.updateMiniCanvasLater()
1826 @pyqtSlot(int, str)
1827 def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
1828 patchcanvas.renameGroup(clientId, newClientName)
1829 self.updateMiniCanvasLater()
1831 @pyqtSlot(int, int, int)
1832 def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId):
1833 pcIcon = patchcanvas.ICON_APPLICATION
1835 if clientIcon == PATCHBAY_ICON_PLUGIN:
1836 pcIcon = patchcanvas.ICON_PLUGIN
1837 if clientIcon == PATCHBAY_ICON_HARDWARE:
1838 pcIcon = patchcanvas.ICON_HARDWARE
1839 elif clientIcon == PATCHBAY_ICON_CARLA:
1840 pass
1841 elif clientIcon == PATCHBAY_ICON_DISTRHO:
1842 pcIcon = patchcanvas.ICON_DISTRHO
1843 elif clientIcon == PATCHBAY_ICON_FILE:
1844 pcIcon = patchcanvas.ICON_FILE
1846 patchcanvas.setGroupIcon(clientId, pcIcon)
1847 self.updateMiniCanvasLater()
1849 if pluginId < 0:
1850 return
1851 if pluginId >= self.fPluginCount and pluginId != MAIN_CARLA_PLUGIN_ID:
1852 print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
1853 return
1855 if pluginId == MAIN_CARLA_PLUGIN_ID:
1856 hasCustomUI = False
1857 hasInlineDisplay = False
1858 else:
1859 hints = self.host.get_plugin_info(pluginId)['hints']
1860 hasCustomUI = bool(hints & PLUGIN_HAS_CUSTOM_UI)
1861 hasInlineDisplay = bool(hints & PLUGIN_HAS_INLINE_DISPLAY)
1863 patchcanvas.setGroupAsPlugin(clientId, pluginId, hasCustomUI, hasInlineDisplay)
1865 @pyqtSlot(int, int, int, int, int)
1866 def slot_handlePatchbayClientPositionChangedCallback(self, clientId, x1, y1, x2, y2):
1867 if (x1 != 0 and x2 != 0) or (y1 != 0 and y2 != 0):
1868 patchcanvas.splitGroup(clientId)
1869 else:
1870 patchcanvas.joinGroup(clientId)
1871 patchcanvas.setGroupPosFull(clientId, x1, y1, x2, y2)
1872 self.updateMiniCanvasLater()
1874 @pyqtSlot(int, int, int, int, str)
1875 def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portGroupId, portName):
1876 if portFlags & PATCHBAY_PORT_IS_INPUT:
1877 portMode = patchcanvas.PORT_MODE_INPUT
1878 else:
1879 portMode = patchcanvas.PORT_MODE_OUTPUT
1881 if portFlags & PATCHBAY_PORT_TYPE_AUDIO:
1882 portType = patchcanvas.PORT_TYPE_AUDIO_JACK
1883 isAlternate = False
1884 elif portFlags & PATCHBAY_PORT_TYPE_CV:
1885 portType = patchcanvas.PORT_TYPE_PARAMETER
1886 isAlternate = False
1887 elif portFlags & PATCHBAY_PORT_TYPE_MIDI:
1888 portType = patchcanvas.PORT_TYPE_MIDI_JACK
1889 isAlternate = False
1890 else:
1891 portType = patchcanvas.PORT_TYPE_NULL
1892 isAlternate = False
1894 patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate)
1895 self.updateMiniCanvasLater()
1897 @pyqtSlot(int, int)
1898 def slot_handlePatchbayPortRemovedCallback(self, groupId, portId):
1899 patchcanvas.removePort(groupId, portId)
1900 self.updateMiniCanvasLater()
1902 @pyqtSlot(int, int, int, int, str)
1903 def slot_handlePatchbayPortChangedCallback(self, groupId, portId, portFlags, portGroupId, newPortName):
1904 patchcanvas.renamePort(groupId, portId, newPortName)
1905 self.updateMiniCanvasLater()
1907 @pyqtSlot(int, int, int, str)
1908 def slot_handlePatchbayPortGroupAddedCallback(self, groupId, portId, portGroupId, newPortName):
1909 # TODO
1910 pass
1912 @pyqtSlot(int, int)
1913 def slot_handlePatchbayPortGroupRemovedCallback(self, groupId, portId):
1914 # TODO
1915 pass
1917 @pyqtSlot(int, int, int, str)
1918 def slot_handlePatchbayPortGroupChangedCallback(self, groupId, portId, portGroupId, newPortName):
1919 # TODO
1920 pass
1922 @pyqtSlot(int, int, int, int, int)
1923 def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId):
1924 patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId)
1925 self.updateMiniCanvasLater()
1927 @pyqtSlot(int, int, int)
1928 def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId):
1929 patchcanvas.disconnectPorts(connectionId)
1930 self.updateMiniCanvasLater()
1932 # --------------------------------------------------------------------------------------------------------
1933 # Settings
1935 def saveSettings(self):
1936 settings = QSafeSettings()
1938 settings.setValue("Geometry", self.saveGeometry())
1939 settings.setValue("ShowToolbar", self.ui.toolBar.isEnabled())
1940 settings.setValue("ShowSidePanel", self.ui.dockWidget.isEnabled())
1942 diskFolders = []
1944 for i in range(self.ui.cb_disk.count()):
1945 diskFolders.append(self.ui.cb_disk.itemData(i))
1947 settings.setValue("DiskFolders", diskFolders)
1948 settings.setValue("LastBPM", self.fLastTransportBPM)
1950 settings.setValue("ShowMeters", self.ui.act_settings_show_meters.isChecked())
1951 settings.setValue("ShowKeyboard", self.ui.act_settings_show_keyboard.isChecked())
1952 settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
1953 settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())
1955 settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_MODE, self.host.transportMode)
1956 settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_EXTRA, self.host.transportExtra)
1958 return settings
1960 def loadSettings(self, firstTime):
1961 settings = QSafeSettings()
1963 if self.fPluginListDialog is not None:
1964 gCarla.felib.destroyPluginListDialog(self.fPluginListDialog)
1965 self.fPluginListDialog = None
1967 if firstTime:
1968 geometry = settings.value("Geometry", QByteArray(), QByteArray)
1969 if not geometry.isNull():
1970 self.restoreGeometry(geometry)
1972 showToolbar = settings.value("ShowToolbar", True, bool)
1973 self.ui.act_settings_show_toolbar.setChecked(showToolbar)
1974 self.ui.toolBar.blockSignals(True)
1975 self.ui.toolBar.setEnabled(showToolbar)
1976 self.ui.toolBar.setVisible(showToolbar)
1977 self.ui.toolBar.blockSignals(False)
1979 #if settings.contains("SplitterState"):
1980 #self.ui.splitter.restoreState(settings.value("SplitterState", b""))
1981 #else:
1982 #self.ui.splitter.setSizes([210, 99999])
1984 showSidePanel = settings.value("ShowSidePanel", True, bool)
1985 self.ui.act_settings_show_side_panel.setChecked(showSidePanel)
1986 self.slot_showSidePanel(showSidePanel)
1988 diskFolders = settings.value("DiskFolders", [HOME], list)
1990 self.ui.cb_disk.setItemData(0, HOME)
1992 for i in range(len(diskFolders)):
1993 if i == 0: continue
1994 folder = diskFolders[i]
1995 self.ui.cb_disk.addItem(os.path.basename(folder), folder)
1997 #if MACOS and not settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, bool):
1998 # self.setUnifiedTitleAndToolBarOnMac(True)
2000 showMeters = settings.value("ShowMeters", True, bool)
2001 self.ui.act_settings_show_meters.setChecked(showMeters)
2002 self.ui.peak_in.setVisible(showMeters)
2003 self.ui.peak_out.setVisible(showMeters)
2005 showKeyboard = settings.value("ShowKeyboard", True, bool)
2006 self.ui.act_settings_show_keyboard.setChecked(showKeyboard)
2007 self.ui.scrollArea.setVisible(showKeyboard)
2009 settingsDBf = QSafeSettings("falkTX", "CarlaDatabase2")
2010 self.fFavoritePlugins = settingsDBf.value("PluginDatabase/Favorites", [], list)
2012 QTimer.singleShot(100, self.slot_restoreCanvasScrollbarValues)
2014 # TODO - complete this
2015 oldSettings = self.fSavedSettings
2017 if self.host.audioDriverForced is not None:
2018 audioDriver = self.host.audioDriverForced
2019 else:
2020 audioDriver = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
2022 audioDriverPrefix = CARLA_KEY_ENGINE_DRIVER_PREFIX + audioDriver
2024 self.fSavedSettings = {
2025 CARLA_KEY_MAIN_PROJECT_FOLDER: settings.value(CARLA_KEY_MAIN_PROJECT_FOLDER, CARLA_DEFAULT_MAIN_PROJECT_FOLDER, str),
2026 CARLA_KEY_MAIN_CONFIRM_EXIT: settings.value(CARLA_KEY_MAIN_CONFIRM_EXIT, CARLA_DEFAULT_MAIN_CONFIRM_EXIT, bool),
2027 CARLA_KEY_MAIN_CLASSIC_SKIN: settings.value(CARLA_KEY_MAIN_CLASSIC_SKIN, CARLA_DEFAULT_MAIN_CLASSIC_SKIN, bool),
2028 CARLA_KEY_MAIN_REFRESH_INTERVAL: settings.value(CARLA_KEY_MAIN_REFRESH_INTERVAL, CARLA_DEFAULT_MAIN_REFRESH_INTERVAL, int),
2029 CARLA_KEY_MAIN_SYSTEM_ICONS: settings.value(CARLA_KEY_MAIN_SYSTEM_ICONS, CARLA_DEFAULT_MAIN_SYSTEM_ICONS, bool),
2030 CARLA_KEY_MAIN_EXPERIMENTAL: settings.value(CARLA_KEY_MAIN_EXPERIMENTAL, CARLA_DEFAULT_MAIN_EXPERIMENTAL, bool),
2031 CARLA_KEY_CANVAS_THEME: settings.value(CARLA_KEY_CANVAS_THEME, CARLA_DEFAULT_CANVAS_THEME, str),
2032 CARLA_KEY_CANVAS_SIZE: settings.value(CARLA_KEY_CANVAS_SIZE, CARLA_DEFAULT_CANVAS_SIZE, str),
2033 CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS: settings.value(CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS, CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS, bool),
2034 CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS: settings.value(CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS, CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS, bool),
2035 CARLA_KEY_CANVAS_USE_BEZIER_LINES: settings.value(CARLA_KEY_CANVAS_USE_BEZIER_LINES, CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES, bool),
2036 CARLA_KEY_CANVAS_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_EYE_CANDY, CARLA_DEFAULT_CANVAS_EYE_CANDY, bool),
2037 CARLA_KEY_CANVAS_FANCY_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_FANCY_EYE_CANDY, CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY, bool),
2038 CARLA_KEY_CANVAS_USE_OPENGL: settings.value(CARLA_KEY_CANVAS_USE_OPENGL, CARLA_DEFAULT_CANVAS_USE_OPENGL, bool),
2039 CARLA_KEY_CANVAS_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_ANTIALIASING, CARLA_DEFAULT_CANVAS_ANTIALIASING, int),
2040 CARLA_KEY_CANVAS_HQ_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_HQ_ANTIALIASING, CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING, bool),
2041 CARLA_KEY_CANVAS_FULL_REPAINTS: settings.value(CARLA_KEY_CANVAS_FULL_REPAINTS, CARLA_DEFAULT_CANVAS_FULL_REPAINTS, bool),
2042 CARLA_KEY_CUSTOM_PAINTING: (settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, bool) and
2043 settings.value(CARLA_KEY_MAIN_PRO_THEME_COLOR, "Black", str).lower() == "black"),
2045 # engine
2046 CARLA_KEY_ENGINE_AUDIO_DRIVER: audioDriver,
2047 CARLA_KEY_ENGINE_AUDIO_DEVICE: settings.value(audioDriverPrefix+"/Device", "", str),
2048 CARLA_KEY_ENGINE_BUFFER_SIZE: settings.value(audioDriverPrefix+"/BufferSize", CARLA_DEFAULT_AUDIO_BUFFER_SIZE, int),
2049 CARLA_KEY_ENGINE_SAMPLE_RATE: settings.value(audioDriverPrefix+"/SampleRate", CARLA_DEFAULT_AUDIO_SAMPLE_RATE, int),
2050 CARLA_KEY_ENGINE_TRIPLE_BUFFER: settings.value(audioDriverPrefix+"/TripleBuffer", CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER, bool),
2052 # file paths
2053 CARLA_KEY_PATHS_AUDIO: splitter.join(settings.value(CARLA_KEY_PATHS_AUDIO, CARLA_DEFAULT_FILE_PATH_AUDIO, list)),
2054 CARLA_KEY_PATHS_MIDI: splitter.join(settings.value(CARLA_KEY_PATHS_MIDI, CARLA_DEFAULT_FILE_PATH_MIDI, list)),
2056 # plugin paths
2057 CARLA_KEY_PATHS_LADSPA: splitter.join(settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list)),
2058 CARLA_KEY_PATHS_DSSI: splitter.join(settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list)),
2059 CARLA_KEY_PATHS_LV2: splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)),
2060 CARLA_KEY_PATHS_VST2: splitter.join(settings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list)),
2061 CARLA_KEY_PATHS_VST3: splitter.join(settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)),
2062 CARLA_KEY_PATHS_SF2: splitter.join(settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)),
2063 CARLA_KEY_PATHS_SFZ: splitter.join(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)),
2064 CARLA_KEY_PATHS_JSFX: splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)),
2065 CARLA_KEY_PATHS_CLAP: splitter.join(settings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list)),
2067 # osc
2068 CARLA_KEY_OSC_ENABLED: settings.value(CARLA_KEY_OSC_ENABLED, CARLA_DEFAULT_OSC_ENABLED, bool),
2069 CARLA_KEY_OSC_TCP_PORT_ENABLED: settings.value(CARLA_KEY_OSC_TCP_PORT_ENABLED, CARLA_DEFAULT_OSC_TCP_PORT_ENABLED, bool),
2070 CARLA_KEY_OSC_TCP_PORT_RANDOM: settings.value(CARLA_KEY_OSC_TCP_PORT_RANDOM, CARLA_DEFAULT_OSC_TCP_PORT_RANDOM, bool),
2071 CARLA_KEY_OSC_TCP_PORT_NUMBER: settings.value(CARLA_KEY_OSC_TCP_PORT_NUMBER, CARLA_DEFAULT_OSC_TCP_PORT_NUMBER, int),
2072 CARLA_KEY_OSC_UDP_PORT_ENABLED: settings.value(CARLA_KEY_OSC_UDP_PORT_ENABLED, CARLA_DEFAULT_OSC_UDP_PORT_ENABLED, bool),
2073 CARLA_KEY_OSC_UDP_PORT_RANDOM: settings.value(CARLA_KEY_OSC_UDP_PORT_RANDOM, CARLA_DEFAULT_OSC_UDP_PORT_RANDOM, bool),
2074 CARLA_KEY_OSC_UDP_PORT_NUMBER: settings.value(CARLA_KEY_OSC_UDP_PORT_NUMBER, CARLA_DEFAULT_OSC_UDP_PORT_NUMBER, int),
2076 # wine
2077 CARLA_KEY_WINE_EXECUTABLE: settings.value(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE, str),
2078 CARLA_KEY_WINE_AUTO_PREFIX: settings.value(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX, bool),
2079 CARLA_KEY_WINE_FALLBACK_PREFIX: settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str),
2080 CARLA_KEY_WINE_RT_PRIO_ENABLED: settings.value(CARLA_KEY_WINE_RT_PRIO_ENABLED, CARLA_DEFAULT_WINE_RT_PRIO_ENABLED, bool),
2081 CARLA_KEY_WINE_BASE_RT_PRIO: settings.value(CARLA_KEY_WINE_BASE_RT_PRIO, CARLA_DEFAULT_WINE_BASE_RT_PRIO, int),
2082 CARLA_KEY_WINE_SERVER_RT_PRIO: settings.value(CARLA_KEY_WINE_SERVER_RT_PRIO, CARLA_DEFAULT_WINE_SERVER_RT_PRIO, int),
2084 # experimental switches
2085 CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES:
2086 settings.value(CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES, bool),
2087 CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES:
2088 settings.value(CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES, bool),
2091 if not self.host.isControl:
2092 self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS] = settings.value(CARLA_KEY_CANVAS_INLINE_DISPLAYS, CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS, bool)
2093 else:
2094 self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS] = False
2096 settings2 = QSafeSettings("falkTX", "Carla2")
2098 if self.host.experimental:
2099 visible = settings2.value(CARLA_KEY_EXPERIMENTAL_JACK_APPS, CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS, bool)
2100 self.ui.act_plugin_add_jack.setVisible(visible)
2101 else:
2102 self.ui.act_plugin_add_jack.setVisible(False)
2104 self.fMiniCanvasUpdateTimeout = 1000 if self.fSavedSettings[CARLA_KEY_CANVAS_FANCY_EYE_CANDY] else 0
2106 setEngineSettings(self.host, self.fSavedSettings)
2107 self.restartTimersIfNeeded()
2109 if oldSettings.get(CARLA_KEY_MAIN_CLASSIC_SKIN, None) not in (self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN], None):
2110 newSkin = "classic" if self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN] else None
2112 for pitem in self.fPluginList:
2113 if pitem is None:
2114 continue
2115 pitem.recreateWidget(newSkin = newSkin)
2117 return settings
2119 # --------------------------------------------------------------------------------------------------------
2120 # Settings (helpers)
2122 def enableTransport(self, enabled):
2123 self.ui.group_transport_controls.setEnabled(enabled)
2124 self.ui.group_transport_settings.setEnabled(enabled)
2126 @pyqtSlot()
2127 def slot_restoreCanvasScrollbarValues(self):
2128 settings = QSafeSettings()
2129 horiz = settings.value("HorizontalScrollBarValue", int(self.ui.graphicsView.horizontalScrollBar().maximum()/2), int)
2130 vertc = settings.value("VerticalScrollBarValue", int(self.ui.graphicsView.verticalScrollBar().maximum()/2), int)
2131 self.ui.graphicsView.horizontalScrollBar().setValue(horiz)
2132 self.ui.graphicsView.verticalScrollBar().setValue(vertc)
2134 # --------------------------------------------------------------------------------------------------------
2135 # Settings (menu actions)
2137 @pyqtSlot(bool)
2138 def slot_showSidePanel(self, yesNo):
2139 self.ui.dockWidget.setEnabled(yesNo)
2140 self.ui.dockWidget.setVisible(yesNo)
2142 @pyqtSlot(bool)
2143 def slot_showToolbar(self, yesNo):
2144 self.ui.toolBar.blockSignals(True)
2145 self.ui.toolBar.setEnabled(yesNo)
2146 self.ui.toolBar.setVisible(yesNo)
2147 self.ui.toolBar.blockSignals(False)
2149 @pyqtSlot(bool)
2150 def slot_showCanvasMeters(self, yesNo):
2151 self.ui.peak_in.setVisible(yesNo)
2152 self.ui.peak_out.setVisible(yesNo)
2153 QTimer.singleShot(0, self.slot_miniCanvasCheckAll)
2155 @pyqtSlot(bool)
2156 def slot_showCanvasKeyboard(self, yesNo):
2157 self.ui.scrollArea.setVisible(yesNo)
2158 QTimer.singleShot(0, self.slot_miniCanvasCheckAll)
2160 @pyqtSlot()
2161 def slot_configureCarla(self):
2162 dialog = CarlaSettingsW(self.fParentOrSelf, self.host, True, hasGL)
2163 if not dialog.exec_():
2164 return
2166 self.loadSettings(False)
2168 if self.fWithCanvas:
2169 patchcanvas.clear()
2170 self.setupCanvas()
2171 self.slot_miniCanvasCheckAll()
2173 if self.host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and self.host.isPlugin:
2174 pass
2175 elif self.host.is_engine_running():
2176 self.host.patchbay_refresh(self.fExternalPatchbay)
2178 # --------------------------------------------------------------------------------------------------------
2179 # About (menu actions)
2181 @pyqtSlot()
2182 def slot_aboutCarla(self):
2183 CarlaAboutW(self.fParentOrSelf, self.host).exec_()
2185 @pyqtSlot()
2186 def slot_aboutJuce(self):
2187 gCarla.felib.createAndExecAboutJuceDialog(self.fParentOrSelf)
2189 @pyqtSlot()
2190 def slot_aboutQt(self):
2191 QApplication.instance().aboutQt()
2193 # --------------------------------------------------------------------------------------------------------
2194 # Disk (menu actions)
2196 @pyqtSlot(int)
2197 def slot_diskFolderChanged(self, index):
2198 if index < 0:
2199 return
2200 elif index == 0:
2201 filename = HOME
2202 self.ui.b_disk_remove.setEnabled(False)
2203 else:
2204 filename = self.ui.cb_disk.itemData(index)
2205 self.ui.b_disk_remove.setEnabled(True)
2207 self.fDirModel.setRootPath(filename)
2208 self.ui.fileTreeView.setRootIndex(self.fDirModel.index(filename))
2210 @pyqtSlot()
2211 def slot_diskFolderAdd(self):
2212 newPath = QFileDialog.getExistingDirectory(self, self.tr("New Folder"), "", QFileDialog.ShowDirsOnly)
2214 if newPath:
2215 if newPath[-1] == os.sep:
2216 newPath = newPath[:-1]
2217 self.ui.cb_disk.addItem(os.path.basename(newPath), newPath)
2218 self.ui.cb_disk.setCurrentIndex(self.ui.cb_disk.count()-1)
2219 self.ui.b_disk_remove.setEnabled(True)
2221 @pyqtSlot()
2222 def slot_diskFolderRemove(self):
2223 index = self.ui.cb_disk.currentIndex()
2225 if index <= 0:
2226 return
2228 self.ui.cb_disk.removeItem(index)
2230 if self.ui.cb_disk.currentIndex() == 0:
2231 self.ui.b_disk_remove.setEnabled(False)
2233 @pyqtSlot(QModelIndex)
2234 def slot_fileTreeDoubleClicked(self, modelIndex):
2235 filename = self.fDirModel.filePath(modelIndex)
2237 if not self.ui.listWidget.isDragUrlValid(filename):
2238 return
2240 if not self.host.load_file(filename):
2241 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
2242 self.tr("Failed to load file"),
2243 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
2244 return
2246 if filename.endswith(".carxp"):
2247 self.loadExternalCanvasGroupPositionsIfNeeded(filename)
2249 # --------------------------------------------------------------------------------------------------------
2250 # Transport
2252 def refreshTransport(self, forced = False):
2253 if not self.ui.l_transport_time.isVisible():
2254 return
2255 if self.fSampleRate == 0.0 or not self.host.is_engine_running():
2256 return
2258 timeInfo = self.host.get_transport_info()
2259 playing = timeInfo['playing']
2260 frame = timeInfo['frame']
2261 bpm = timeInfo['bpm']
2263 if playing != self.fLastTransportState or forced:
2264 if playing:
2265 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
2266 icon = getIcon('media-playback-pause', 16, 'svgz')
2267 else:
2268 icon = QIcon(":/16x16/media-playback-pause.svgz")
2269 self.ui.b_transport_play.setChecked(True)
2270 self.ui.b_transport_play.setIcon(icon)
2271 #self.ui.b_transport_play.setText(self.tr("&Pause"))
2272 else:
2273 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
2274 icon = getIcon('media-playback-start', 16, 'svgz')
2275 else:
2276 icon = QIcon(":/16x16/media-playback-start.svgz")
2277 self.ui.b_transport_play.setChecked(False)
2278 self.ui.b_transport_play.setIcon(icon)
2279 #self.ui.b_play.setText(self.tr("&Play"))
2281 self.fLastTransportState = playing
2283 if frame != self.fLastTransportFrame or forced:
2284 self.fLastTransportFrame = frame
2286 time = frame / self.fSampleRate
2287 secs = time % 60
2288 mins = (time / 60) % 60
2289 hrs = (time / 3600) % 60
2290 self.ui.l_transport_time.setText("%02i:%02i:%02i" % (hrs, mins, secs))
2292 frame1 = frame % 1000
2293 frame2 = (frame / 1000) % 1000
2294 frame3 = (frame / 1000000) % 1000
2295 self.ui.l_transport_frame.setText("%03i'%03i'%03i" % (frame3, frame2, frame1))
2297 bar = timeInfo['bar']
2298 beat = timeInfo['beat']
2299 tick = timeInfo['tick']
2300 self.ui.l_transport_bbt.setText("%03i|%02i|%04i" % (bar, beat, tick))
2302 if bpm != self.fLastTransportBPM or forced:
2303 self.fLastTransportBPM = bpm
2305 if bpm > 0.0:
2306 self.ui.dsb_transport_bpm.blockSignals(True)
2307 self.ui.dsb_transport_bpm.setValue(bpm)
2308 self.ui.dsb_transport_bpm.blockSignals(False)
2309 self.ui.dsb_transport_bpm.setStyleSheet("")
2310 else:
2311 self.ui.dsb_transport_bpm.setStyleSheet("QDoubleSpinBox { color: palette(mid); }")
2313 # --------------------------------------------------------------------------------------------------------
2314 # Transport (menu actions)
2316 @pyqtSlot(bool)
2317 def slot_transportPlayPause(self, toggled):
2318 if self.host.isPlugin or not self.host.is_engine_running():
2319 return
2321 if toggled:
2322 self.host.transport_play()
2323 else:
2324 self.host.transport_pause()
2326 self.refreshTransport()
2328 @pyqtSlot()
2329 def slot_transportStop(self):
2330 if self.host.isPlugin or not self.host.is_engine_running():
2331 return
2333 self.host.transport_pause()
2334 self.host.transport_relocate(0)
2336 self.refreshTransport()
2338 @pyqtSlot()
2339 def slot_transportBackwards(self):
2340 if self.host.isPlugin or not self.host.is_engine_running():
2341 return
2343 newFrame = self.host.get_current_transport_frame() - 100000
2345 if newFrame < 0:
2346 newFrame = 0
2348 self.host.transport_relocate(newFrame)
2350 @pyqtSlot(float)
2351 def slot_transportBpmChanged(self, newValue):
2352 self.host.transport_bpm(newValue)
2354 @pyqtSlot()
2355 def slot_transportForwards(self):
2356 if self.fSampleRate == 0.0 or self.host.isPlugin or not self.host.is_engine_running():
2357 return
2359 newFrame = self.host.get_current_transport_frame() + int(self.fSampleRate*2.5)
2360 self.host.transport_relocate(newFrame)
2362 @pyqtSlot(bool)
2363 def slot_transportJackEnabled(self, clicked):
2364 if not self.host.is_engine_running():
2365 return
2366 self.host.transportMode = ENGINE_TRANSPORT_MODE_JACK if clicked else ENGINE_TRANSPORT_MODE_INTERNAL
2367 self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE,
2368 self.host.transportMode,
2369 self.host.transportExtra)
2371 @pyqtSlot(bool)
2372 def slot_transportLinkEnabled(self, clicked):
2373 if not self.host.is_engine_running():
2374 return
2375 extra = ":link:" if clicked else ""
2376 self.host.transportExtra = extra
2377 self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE,
2378 self.host.transportMode,
2379 self.host.transportExtra)
2381 # --------------------------------------------------------------------------------------------------------
2382 # Other
2384 @pyqtSlot(bool)
2385 def slot_xrunClear(self):
2386 self.host.clear_engine_xruns()
2388 # --------------------------------------------------------------------------------------------------------
2389 # Canvas scrollbars
2391 @pyqtSlot(int)
2392 def slot_horizontalScrollBarChanged(self, value):
2393 maximum = self.ui.graphicsView.horizontalScrollBar().maximum()
2394 if maximum == 0:
2395 xp = 0
2396 else:
2397 xp = float(value) / maximum
2398 self.ui.miniCanvasPreview.setViewPosX(xp)
2399 self.updateCanvasInitialPos()
2401 @pyqtSlot(int)
2402 def slot_verticalScrollBarChanged(self, value):
2403 maximum = self.ui.graphicsView.verticalScrollBar().maximum()
2404 if maximum == 0:
2405 yp = 0
2406 else:
2407 yp = float(value) / maximum
2408 self.ui.miniCanvasPreview.setViewPosY(yp)
2409 self.updateCanvasInitialPos()
2411 # --------------------------------------------------------------------------------------------------------
2412 # Canvas keyboard
2414 @pyqtSlot(int)
2415 def slot_noteOn(self, note):
2416 if self.fPluginCount == 0:
2417 return
2419 for pluginId in self.fSelectedPlugins:
2420 self.host.send_midi_note(pluginId, 0, note, 100)
2422 pedit = self.getPluginEditDialog(pluginId)
2423 pedit.noteOn(0, note, 100)
2425 @pyqtSlot(int)
2426 def slot_noteOff(self, note):
2427 if self.fPluginCount == 0:
2428 return
2430 for pluginId in self.fSelectedPlugins:
2431 self.host.send_midi_note(pluginId, 0, note, 0)
2433 pedit = self.getPluginEditDialog(pluginId)
2434 pedit.noteOff(0, note)
2436 # --------------------------------------------------------------------------------------------------------
2437 # Canvas keyboard (host callbacks)
2439 @pyqtSlot(int, int, int, int)
2440 def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity):
2441 if pluginId in self.fSelectedPlugins:
2442 self.ui.keyboard.sendNoteOn(note, False)
2444 @pyqtSlot(int, int, int)
2445 def slot_handleNoteOffCallback(self, pluginId, channel, note):
2446 if pluginId in self.fSelectedPlugins:
2447 self.ui.keyboard.sendNoteOff(note, False)
2449 # --------------------------------------------------------------------------------------------------------
2451 @pyqtSlot(int)
2452 def slot_handleUpdateCallback(self, pluginId):
2453 pitem = self.getPluginItem(pluginId)
2455 if pitem is None:
2456 return
2458 wasCompacted = pitem.isCompacted()
2459 isCompacted = wasCompacted
2461 check = self.host.get_custom_data_value(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkinIsCompacted")
2462 if not check:
2463 return
2464 isCompacted = bool(check == "true")
2466 if wasCompacted == isCompacted:
2467 return
2469 pitem.recreateWidget(True)
2471 # --------------------------------------------------------------------------------------------------------
2472 # MiniCanvas stuff
2474 @pyqtSlot()
2475 def slot_miniCanvasCheckAll(self):
2476 self.slot_miniCanvasCheckSize()
2477 self.slot_horizontalScrollBarChanged(self.ui.graphicsView.horizontalScrollBar().value())
2478 self.slot_verticalScrollBarChanged(self.ui.graphicsView.verticalScrollBar().value())
2480 @pyqtSlot()
2481 def slot_miniCanvasCheckSize(self):
2482 if self.fCanvasWidth == 0 or self.fCanvasHeight == 0:
2483 return
2485 currentIndex = self.ui.tabWidget.currentIndex()
2487 if currentIndex == 1:
2488 width = self.ui.graphicsView.width()
2489 height = self.ui.graphicsView.height()
2490 else:
2491 self.ui.tabWidget.blockSignals(True)
2492 self.ui.tabWidget.setCurrentIndex(1)
2493 width = self.ui.graphicsView.width()
2494 height = self.ui.graphicsView.height()
2495 self.ui.tabWidget.setCurrentIndex(currentIndex)
2496 self.ui.tabWidget.blockSignals(False)
2498 self.scene.updateLimits()
2500 self.ui.miniCanvasPreview.setViewSize(float(width)/self.fCanvasWidth, float(height)/self.fCanvasHeight)
2502 @pyqtSlot(float, float)
2503 def slot_miniCanvasMoved(self, xp, yp):
2504 hsb = self.ui.graphicsView.horizontalScrollBar()
2505 vsb = self.ui.graphicsView.verticalScrollBar()
2506 hsb.setValue(xp * hsb.maximum())
2507 vsb.setValue(yp * vsb.maximum())
2508 self.updateCanvasInitialPos()
2510 # --------------------------------------------------------------------------------------------------------
2511 # Logs autoscroll, save and clear
2513 @pyqtSlot(int)
2514 def slot_toggleLogAutoscroll(self, checkState):
2515 self.autoscrollOnNewLog = checkState == Qt.Checked
2516 if self.autoscrollOnNewLog:
2517 self.ui.text_logs.verticalScrollBar().setValue(self.ui.text_logs.verticalScrollBar().maximum())
2519 @pyqtSlot(int)
2520 def slot_logSliderMoved(self, slider_pos):
2521 if self.ui.text_logs.verticalScrollBar().hasTracking() or self.autoscrollOnNewLog:
2522 self.lastLogSliderPos = slider_pos
2523 else:
2524 self.ui.text_logs.verticalScrollBar().setValue(self.lastLogSliderPos)
2526 @pyqtSlot()
2527 def slot_logButtonsState(self, enabled=True):
2528 self.ui.logs_clear.setEnabled(enabled)
2529 self.ui.logs_save.setEnabled(enabled)
2531 @pyqtSlot()
2532 def slot_logSave(self):
2533 filename = os.path.join(self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], 'carla_log.txt')
2534 filename, _ = QFileDialog.getSaveFileName(self, self.tr("Save Logs"), filename)
2536 if not filename:
2537 return
2539 try:
2540 with open(filename, "w") as logfile:
2541 logfile.write(self.ui.text_logs.toPlainText())
2542 logfile.close()
2543 except:
2544 return
2546 @pyqtSlot()
2547 def slot_logClear(self):
2548 self.ui.text_logs.clear()
2549 self.ui.text_logs.appendPlainText("======= Logs cleared ========")
2550 self.slot_logButtonsState(False)
2552 # --------------------------------------------------------------------------------------------------------
2553 # Timers
2555 def startTimers(self):
2556 if self.fIdleTimerFast == 0:
2557 self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
2559 if self.fIdleTimerSlow == 0:
2560 self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
2562 def restartTimersIfNeeded(self):
2563 if self.fIdleTimerFast != 0:
2564 self.killTimer(self.fIdleTimerFast)
2565 self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
2567 if self.fIdleTimerSlow != 0:
2568 self.killTimer(self.fIdleTimerSlow)
2569 self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
2571 def killTimers(self):
2572 if self.fIdleTimerFast != 0:
2573 self.killTimer(self.fIdleTimerFast)
2574 self.fIdleTimerFast = 0
2576 if self.fIdleTimerSlow != 0:
2577 self.killTimer(self.fIdleTimerSlow)
2578 self.fIdleTimerSlow = 0
2580 # --------------------------------------------------------------------------------------------------------
2581 # Misc
2583 @pyqtSlot(bool)
2584 def slot_toolbarVisibilityChanged(self, visible):
2585 self.ui.toolBar.blockSignals(True)
2586 self.ui.toolBar.setEnabled(visible)
2587 self.ui.toolBar.blockSignals(False)
2588 self.ui.act_settings_show_toolbar.setChecked(visible)
2590 @pyqtSlot(int)
2591 def slot_tabChanged(self, index):
2592 if index != 1:
2593 return
2595 self.ui.graphicsView.setFocus()
2597 @pyqtSlot(int)
2598 def slot_handleReloadAllCallback(self, pluginId):
2599 if pluginId >= self.fPluginCount:
2600 return
2602 pitem = self.fPluginList[pluginId]
2603 if pitem is None:
2604 return
2606 pitem.recreateWidget()
2608 # --------------------------------------------------------------------------------------------------------
2610 @pyqtSlot(int, int, str)
2611 def slot_handleNSMCallback(self, opcode, valueInt, valueStr):
2612 if opcode == NSM_CALLBACK_INIT:
2613 return
2615 # Error
2616 elif opcode == NSM_CALLBACK_ERROR:
2617 pass
2619 # Reply
2620 elif opcode == NSM_CALLBACK_ANNOUNCE:
2621 self.fFirstEngineInit = False
2622 self.fSessionManagerName = valueStr
2623 self.setProperWindowTitle()
2625 # If NSM server does not support optional-gui, revert our initial assumptions that it does
2626 if (valueInt & (1 << 1)) == 0x0:
2627 self.fWindowCloseHideGui = False
2628 self.ui.act_file_quit.setText(self.tr("&Quit"))
2629 QApplication.instance().setQuitOnLastWindowClosed(True)
2630 self.show()
2632 # Open
2633 elif opcode == NSM_CALLBACK_OPEN:
2634 self.fProjectFilename = QFileInfo(valueStr+".carxp").absoluteFilePath()
2635 self.setProperWindowTitle()
2637 self.fCustomStopAction = self.CUSTOM_ACTION_PROJECT_LOAD
2638 self.slot_engineStop(True)
2639 return
2641 # Save
2642 elif opcode == NSM_CALLBACK_SAVE:
2643 self.saveProjectNow()
2645 # Session is Loaded
2646 elif opcode == NSM_CALLBACK_SESSION_IS_LOADED:
2647 pass
2649 # Show Optional Gui
2650 elif opcode == NSM_CALLBACK_SHOW_OPTIONAL_GUI:
2651 self.show()
2653 # Hide Optional Gui
2654 elif opcode == NSM_CALLBACK_HIDE_OPTIONAL_GUI:
2655 self.hideForNSM()
2657 # Set client name
2658 elif opcode == NSM_CALLBACK_SET_CLIENT_NAME_ID:
2659 self.fClientName = valueStr
2660 return
2662 self.host.nsm_ready(opcode)
2664 # --------------------------------------------------------------------------------------------------------
2666 def fixLogText(self, text):
2667 return text.replace("\x1b[30;1m", "").replace("\x1b[31m", "").replace("\x1b[0m", "")
2669 @pyqtSlot(int, int, int, int, float, str)
2670 def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valuef, valueStr):
2671 self.ui.text_logs.appendPlainText(self.fixLogText(valueStr))
2673 @pyqtSlot(str)
2674 def slot_handleInfoCallback(self, info):
2675 QMessageBox.information(self, "Information", info)
2677 @pyqtSlot(str)
2678 def slot_handleErrorCallback(self, error):
2679 QMessageBox.critical(self, "Error", error)
2681 @pyqtSlot()
2682 def slot_handleQuitCallback(self):
2683 self.fIsProjectLoading = False
2684 self.killTimers()
2685 self.removeAllPlugins()
2686 self.projectLoadingFinished(False)
2688 @pyqtSlot(int)
2689 def slot_handleInlineDisplayRedrawCallback(self, pluginId):
2690 # FIXME
2691 if self.fIdleTimerSlow != 0 and self.fIdleTimerFast != 0 and pluginId < self.fPluginCount and not self.fIsProjectLoading:
2692 patchcanvas.redrawPluginGroup(pluginId)
2694 # --------------------------------------------------------------------------------------------------------
2696 @pyqtSlot()
2697 def slot_handleSIGUSR1(self):
2698 print("Got SIGUSR1 -> Saving project now")
2699 self.slot_fileSave()
2701 @pyqtSlot()
2702 def slot_handleSIGTERM(self):
2703 print("Got SIGTERM -> Closing now")
2704 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
2705 self.close()
2707 # --------------------------------------------------------------------------------------------------------
2708 # Internal stuff
2710 def getExtraPtr(self, plugin):
2711 ptype = plugin['type']
2713 if ptype == PLUGIN_LADSPA:
2714 uniqueId = plugin['uniqueId']
2716 self.maybeLoadRDFs()
2718 for rdfItem in self.fLadspaRdfList:
2719 if rdfItem.UniqueID == uniqueId:
2720 return pointer(rdfItem)
2722 elif ptype == PLUGIN_SF2:
2723 if plugin['name'].lower().endswith(" (16 outputs)"):
2724 return self._true
2726 return None
2728 def maybeLoadRDFs(self):
2729 if not self.fLadspaRdfNeedsUpdate:
2730 return
2732 self.fLadspaRdfNeedsUpdate = False
2733 self.fLadspaRdfList = []
2735 if not haveLRDF:
2736 return
2738 settingsDir = os.path.join(HOME, ".config", "falkTX")
2739 frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
2741 if os.path.exists(frLadspaFile):
2742 frLadspa = open(frLadspaFile, 'r')
2744 try:
2745 self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
2746 except:
2747 pass
2749 frLadspa.close()
2751 # --------------------------------------------------------------------------------------------------------
2753 def getPluginCount(self):
2754 return self.fPluginCount
2756 def getPluginItem(self, pluginId):
2757 if pluginId >= self.fPluginCount:
2758 return None
2760 pitem = self.fPluginList[pluginId]
2761 if pitem is None:
2762 return None
2763 #if False:
2764 #return CarlaRackItem(self, 0, False)
2766 return pitem
2768 def getPluginEditDialog(self, pluginId):
2769 if pluginId >= self.fPluginCount:
2770 return None
2772 pitem = self.fPluginList[pluginId]
2773 if pitem is None:
2774 return None
2775 if False:
2776 return PluginEdit(self, self.host, 0)
2778 return pitem.getEditDialog()
2780 def getPluginSlotWidget(self, pluginId):
2781 if pluginId >= self.fPluginCount:
2782 return None
2784 pitem = self.fPluginList[pluginId]
2785 if pitem is None:
2786 return None
2787 #if False:
2788 #return AbstractPluginSlot()
2790 return pitem.getWidget()
2792 # --------------------------------------------------------------------------------------------------------
2794 def waitForPendingEvents(self):
2795 pass
2797 # --------------------------------------------------------------------------------------------------------
2798 # show/hide event
2800 def showEvent(self, event):
2801 self.getAndRefreshRuntimeInfo()
2802 self.refreshTransport(True)
2803 QMainWindow.showEvent(self, event)
2805 if QT_VERSION >= 0x50600:
2806 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_UI_SCALE, int(self.devicePixelRatioF() * 1000), "")
2807 print("Frontend pixel ratio is", self.devicePixelRatioF())
2809 # set our gui as parent for all plugins UIs
2810 if self.host.manageUIs and not self.host.isControl:
2811 if MACOS:
2812 nsViewPtr = int(self.winId())
2813 winIdStr = "%x" % gCarla.utils.cocoa_get_window(nsViewPtr)
2814 else:
2815 winIdStr = "%x" % int(self.winId())
2816 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr)
2818 def hideEvent(self, event):
2819 # disable parent
2820 if not self.host.isControl:
2821 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")
2823 QMainWindow.hideEvent(self, event)
2825 # --------------------------------------------------------------------------------------------------------
2826 # resize event
2828 def resizeEvent(self, event):
2829 QMainWindow.resizeEvent(self, event)
2831 if self.fWithCanvas:
2832 self.slot_miniCanvasCheckSize()
2834 # --------------------------------------------------------------------------------------------------------
2835 # timer event
2837 def refreshRuntimeInfo(self, load, xruns):
2838 txt1 = str(xruns) if (xruns >= 0) else "--"
2839 txt2 = "" if (xruns == 1) else "s"
2840 self.ui.b_xruns.setText("%s Xrun%s" % (txt1, txt2))
2841 self.ui.pb_dsp_load.setValue(int(load))
2843 def getAndRefreshRuntimeInfo(self):
2844 if not self.ui.pb_dsp_load.isVisible():
2845 return
2846 if not self.host.is_engine_running():
2847 return
2848 info = self.host.get_runtime_engine_info()
2849 self.refreshRuntimeInfo(info['load'], info['xruns'])
2851 def idleFast(self):
2852 self.host.engine_idle()
2853 self.refreshTransport()
2855 if self.fPluginCount == 0 or self.fCurrentlyRemovingAllPlugins:
2856 return
2858 for pitem in self.fPluginList:
2859 if pitem is None:
2860 break
2862 pitem.getWidget().idleFast()
2864 for pluginId in self.fSelectedPlugins:
2865 self.fPeaksCleared = False
2866 if self.ui.peak_in.isVisible():
2867 self.ui.peak_in.displayMeter(1, self.host.get_input_peak_value(pluginId, True))
2868 self.ui.peak_in.displayMeter(2, self.host.get_input_peak_value(pluginId, False))
2869 if self.ui.peak_out.isVisible():
2870 self.ui.peak_out.displayMeter(1, self.host.get_output_peak_value(pluginId, True))
2871 self.ui.peak_out.displayMeter(2, self.host.get_output_peak_value(pluginId, False))
2872 return
2874 if self.fPeaksCleared:
2875 return
2877 self.fPeaksCleared = True
2878 self.ui.peak_in.displayMeter(1, 0.0, True)
2879 self.ui.peak_in.displayMeter(2, 0.0, True)
2880 self.ui.peak_out.displayMeter(1, 0.0, True)
2881 self.ui.peak_out.displayMeter(2, 0.0, True)
2883 def idleSlow(self):
2884 self.getAndRefreshRuntimeInfo()
2886 if self.fPluginCount == 0 or self.fCurrentlyRemovingAllPlugins:
2887 return
2889 for pitem in self.fPluginList:
2890 if pitem is None:
2891 break
2893 pitem.getWidget().idleSlow()
2895 def timerEvent(self, event):
2896 if event.timerId() == self.fIdleTimerFast:
2897 self.idleFast()
2899 elif event.timerId() == self.fIdleTimerSlow:
2900 self.idleSlow()
2902 QMainWindow.timerEvent(self, event)
2904 # --------------------------------------------------------------------------------------------------------
2905 # color/style change event
2907 def changeEvent(self, event):
2908 if event.type() in (QEvent.PaletteChange, QEvent.StyleChange):
2909 self.updateStyle()
2910 QMainWindow.changeEvent(self, event)
2912 def updateStyle(self):
2913 # Rack padding images setup
2914 rack_imgL = QImage(":/bitmaps/rack_padding_left.png")
2915 rack_imgR = QImage(":/bitmaps/rack_padding_right.png")
2917 min_value = 0.07
2919 if PYQT_VERSION >= 0x50600:
2920 value_fix = 1.0/(1.0-rack_imgL.scaled(1, 1, Qt.IgnoreAspectRatio, Qt.SmoothTransformation).pixelColor(0,0).blackF())
2921 else:
2922 value_fix = 1.5
2924 rack_pal = self.ui.rack.palette()
2925 bg_color = rack_pal.window().color()
2926 fg_color = rack_pal.text().color()
2927 bg_value = 1.0 - bg_color.blackF()
2928 if bg_value != 0.0 and bg_value < min_value:
2929 pad_color = bg_color.lighter(int(100*min_value/bg_value*value_fix))
2930 else:
2931 pad_color = QColor.fromHsvF(0.0, 0.0, min_value*value_fix)
2933 painter = QPainter()
2934 fillRect = rack_imgL.rect().adjusted(-1,-1,1,1)
2936 painter.begin(rack_imgL)
2937 painter.setCompositionMode(QPainter.CompositionMode_Multiply)
2938 painter.setBrush(pad_color)
2939 painter.drawRect(fillRect)
2940 painter.end()
2941 rack_pixmapL = QPixmap(rack_imgL)
2942 self.imgL_palette = QPalette()
2943 self.imgL_palette.setBrush(QPalette.Window, QBrush(rack_pixmapL))
2944 self.ui.pad_left.setPalette(self.imgL_palette)
2945 self.ui.pad_left.setAutoFillBackground(True)
2947 painter.begin(rack_imgR)
2948 painter.setCompositionMode(QPainter.CompositionMode_Multiply)
2949 painter.setBrush(pad_color)
2950 painter.drawRect(fillRect)
2951 painter.end()
2952 rack_pixmapR = QPixmap(rack_imgR)
2953 self.imgR_palette = QPalette()
2954 self.imgR_palette.setBrush(QPalette.Window, QBrush(rack_pixmapR))
2955 self.ui.pad_right.setPalette(self.imgR_palette)
2956 self.ui.pad_right.setAutoFillBackground(True)
2958 # qt's rgba is actually argb, so convert that
2959 bg_color_value = bg_color.rgba()
2960 bg_color_value = ((bg_color_value & 0xffffff) << 8) | (bg_color_value >> 24)
2962 fg_color_value = fg_color.rgba()
2963 fg_color_value = ((fg_color_value & 0xffffff) << 8) | (fg_color_value >> 24)
2965 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_BACKGROUND_COLOR, bg_color_value, "")
2966 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_FOREGROUND_COLOR, fg_color_value, "")
2968 # --------------------------------------------------------------------------------------------------------
2969 # paint event
2971 #def paintEvent(self, event):
2972 #QMainWindow.paintEvent(self, event)
2974 #if MACOS or not self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING]:
2975 #return
2977 #painter = QPainter(self)
2978 #painter.setBrush(QColor(36, 36, 36))
2979 #painter.setPen(QColor(62, 62, 62))
2980 #painter.drawRect(1, self.height()/2, self.width()-3, self.height()-self.height()/2-1)
2982 # --------------------------------------------------------------------------------------------------------
2983 # close event
2985 def shouldIgnoreClose(self):
2986 if self.host.isControl or self.host.isPlugin:
2987 return False
2988 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE:
2989 return False
2990 if self.fWindowCloseHideGui:
2991 return False
2992 if self.fSavedSettings[CARLA_KEY_MAIN_CONFIRM_EXIT]:
2993 return QMessageBox.question(self, self.tr("Quit"),
2994 self.tr("Are you sure you want to quit Carla?"),
2995 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No
2996 return False
2998 def closeEvent(self, event):
2999 if self.shouldIgnoreClose():
3000 event.ignore()
3001 return
3003 if self.fWindowCloseHideGui and self.fCustomStopAction != self.CUSTOM_ACTION_APP_CLOSE:
3004 self.hideForNSM()
3005 self.host.nsm_ready(NSM_CALLBACK_HIDE_OPTIONAL_GUI)
3006 return
3008 patchcanvas.handleAllPluginsRemoved()
3010 if MACOS and self.fMacClosingHelper and not (self.host.isControl or self.host.isPlugin):
3011 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
3012 self.fMacClosingHelper = False
3013 event.ignore()
3015 for i in reversed(range(self.fPluginCount)):
3016 self.host.show_custom_ui(i, False)
3018 QTimer.singleShot(100, self.close)
3019 return
3021 self.killTimers()
3022 self.saveSettings()
3024 if self.host.is_engine_running() and not (self.host.isControl or self.host.isPlugin):
3025 if not self.slot_engineStop(True):
3026 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
3027 event.ignore()
3028 return
3030 QMainWindow.closeEvent(self, event)
3032 # if we reach this point, fully close ourselves
3033 gCarla.gui = None
3034 QApplication.instance().quit()
3036 # ------------------------------------------------------------------------------------------------
3037 # Canvas callback
3039 def canvasCallback(action, value1, value2, valueStr):
3040 host = gCarla.gui.host
3042 if gCarla.gui.fCustomStopAction == HostWindow.CUSTOM_ACTION_APP_CLOSE:
3043 return
3045 if action == patchcanvas.ACTION_GROUP_INFO:
3046 pass
3048 elif action == patchcanvas.ACTION_GROUP_RENAME:
3049 pass
3051 elif action == patchcanvas.ACTION_GROUP_SPLIT:
3052 groupId = value1
3053 patchcanvas.splitGroup(groupId)
3054 gCarla.gui.updateMiniCanvasLater()
3056 elif action == patchcanvas.ACTION_GROUP_JOIN:
3057 groupId = value1
3058 patchcanvas.joinGroup(groupId)
3059 gCarla.gui.updateMiniCanvasLater()
3061 elif action == patchcanvas.ACTION_GROUP_POSITION:
3062 if gCarla.gui.fIsProjectLoading:
3063 return
3064 if not host.is_engine_running():
3065 return
3066 groupId = value1
3067 x1, y1, x2, y2 = tuple(int(i) for i in valueStr.split(":"))
3068 host.patchbay_set_group_pos(gCarla.gui.fExternalPatchbay, groupId, x1, y1, x2, y2)
3069 gCarla.gui.updateMiniCanvasLater()
3071 elif action == patchcanvas.ACTION_PORT_INFO:
3072 pass
3074 elif action == patchcanvas.ACTION_PORT_RENAME:
3075 pass
3077 elif action == patchcanvas.ACTION_PORTS_CONNECT:
3078 gOut, pOut, gIn, pIn = tuple(int(i) for i in valueStr.split(":"))
3080 if not host.patchbay_connect(gCarla.gui.fExternalPatchbay, gOut, pOut, gIn, pIn):
3081 print("Connection failed:", host.get_last_error())
3083 elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
3084 connectionId = value1
3086 if not host.patchbay_disconnect(gCarla.gui.fExternalPatchbay, connectionId):
3087 print("Disconnect failed:", host.get_last_error())
3089 elif action == patchcanvas.ACTION_PLUGIN_CLONE:
3090 pluginId = value1
3092 if not host.clone_plugin(pluginId):
3093 CustomMessageBox(gCarla.gui, QMessageBox.Warning, gCarla.gui.tr("Error"), gCarla.gui.tr("Operation failed"),
3094 host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
3096 elif action == patchcanvas.ACTION_PLUGIN_EDIT:
3097 pluginId = value1
3098 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3100 if pwidget is not None:
3101 pwidget.showEditDialog()
3103 elif action == patchcanvas.ACTION_PLUGIN_RENAME:
3104 pluginId = value1
3105 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3107 if pwidget is not None:
3108 pwidget.showRenameDialog()
3110 elif action == patchcanvas.ACTION_PLUGIN_REPLACE:
3111 pluginId = value1
3112 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3114 if pwidget is not None:
3115 pwidget.showReplaceDialog()
3117 elif action == patchcanvas.ACTION_PLUGIN_REMOVE:
3118 pluginId = value1
3120 if not host.remove_plugin(pluginId):
3121 CustomMessageBox(gCarla.gui, QMessageBox.Warning, gCarla.gui.tr("Error"), gCarla.gui.tr("Operation failed"),
3122 host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
3124 elif action == patchcanvas.ACTION_PLUGIN_SHOW_UI:
3125 pluginId = value1
3126 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3128 if pwidget is not None:
3129 pwidget.showCustomUI()
3131 elif action == patchcanvas.ACTION_BG_RIGHT_CLICK:
3132 gCarla.gui.slot_showPluginActionsMenu()
3134 elif action == patchcanvas.ACTION_INLINE_DISPLAY:
3135 if gCarla.gui.fIsProjectLoading:
3136 return
3137 if not host.is_engine_running():
3138 return
3139 pluginId = value1
3140 width, height = [int(v) for v in valueStr.split(":")]
3141 return host.render_inline_display(pluginId, width, height)
3143 # ------------------------------------------------------------------------------------------------------------
3144 # Engine callback
3146 def engineCallback(host, action, pluginId, value1, value2, value3, valuef, valueStr):
3147 # kdevelop likes this :)
3148 if False: host = CarlaHostNull()
3150 valueStr = charPtrToString(valueStr)
3152 if action == ENGINE_CALLBACK_ENGINE_STARTED:
3153 host.processMode = value1
3154 host.transportMode = value2
3155 elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
3156 host.processMode = value1
3157 elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
3158 host.transportMode = value1
3159 host.transportExtra = valueStr
3161 if action == ENGINE_CALLBACK_DEBUG:
3162 host.DebugCallback.emit(pluginId, value1, value2, value3, valuef, valueStr)
3163 elif action == ENGINE_CALLBACK_PLUGIN_ADDED:
3164 host.PluginAddedCallback.emit(pluginId, value1, valueStr)
3165 elif action == ENGINE_CALLBACK_PLUGIN_REMOVED:
3166 host.PluginRemovedCallback.emit(pluginId)
3167 elif action == ENGINE_CALLBACK_PLUGIN_RENAMED:
3168 host.PluginRenamedCallback.emit(pluginId, valueStr)
3169 elif action == ENGINE_CALLBACK_PLUGIN_UNAVAILABLE:
3170 host.PluginUnavailableCallback.emit(pluginId, valueStr)
3171 elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
3172 host.ParameterValueChangedCallback.emit(pluginId, value1, valuef)
3173 elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
3174 host.ParameterDefaultChangedCallback.emit(pluginId, value1, valuef)
3175 elif action == ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED:
3176 host.ParameterMappedControlIndexChangedCallback.emit(pluginId, value1, value2)
3177 elif action == ENGINE_CALLBACK_PARAMETER_MAPPED_RANGE_CHANGED:
3178 minimum, maximum = (float(v) for v in valueStr.split(":", 2))
3179 host.ParameterMappedRangeChangedCallback.emit(pluginId, value1, minimum, maximum)
3180 elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
3181 host.ParameterMidiChannelChangedCallback.emit(pluginId, value1, value2)
3182 elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
3183 host.ProgramChangedCallback.emit(pluginId, value1)
3184 elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
3185 host.MidiProgramChangedCallback.emit(pluginId, value1)
3186 elif action == ENGINE_CALLBACK_OPTION_CHANGED:
3187 host.OptionChangedCallback.emit(pluginId, value1, bool(value2))
3188 elif action == ENGINE_CALLBACK_UI_STATE_CHANGED:
3189 host.UiStateChangedCallback.emit(pluginId, value1)
3190 elif action == ENGINE_CALLBACK_NOTE_ON:
3191 host.NoteOnCallback.emit(pluginId, value1, value2, value3)
3192 elif action == ENGINE_CALLBACK_NOTE_OFF:
3193 host.NoteOffCallback.emit(pluginId, value1, value2)
3194 elif action == ENGINE_CALLBACK_UPDATE:
3195 host.UpdateCallback.emit(pluginId)
3196 elif action == ENGINE_CALLBACK_RELOAD_INFO:
3197 host.ReloadInfoCallback.emit(pluginId)
3198 elif action == ENGINE_CALLBACK_RELOAD_PARAMETERS:
3199 host.ReloadParametersCallback.emit(pluginId)
3200 elif action == ENGINE_CALLBACK_RELOAD_PROGRAMS:
3201 host.ReloadProgramsCallback.emit(pluginId)
3202 elif action == ENGINE_CALLBACK_RELOAD_ALL:
3203 host.ReloadAllCallback.emit(pluginId)
3204 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
3205 host.PatchbayClientAddedCallback.emit(pluginId, value1, value2, valueStr)
3206 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED:
3207 host.PatchbayClientRemovedCallback.emit(pluginId)
3208 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED:
3209 host.PatchbayClientRenamedCallback.emit(pluginId, valueStr)
3210 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_DATA_CHANGED:
3211 host.PatchbayClientDataChangedCallback.emit(pluginId, value1, value2)
3212 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED:
3213 host.PatchbayClientPositionChangedCallback.emit(pluginId, value1, value2, value3, int(round(valuef)))
3214 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
3215 host.PatchbayPortAddedCallback.emit(pluginId, value1, value2, value3, valueStr)
3216 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED:
3217 host.PatchbayPortRemovedCallback.emit(pluginId, value1)
3218 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_CHANGED:
3219 host.PatchbayPortChangedCallback.emit(pluginId, value1, value2, value3, valueStr)
3220 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_ADDED:
3221 host.PatchbayPortGroupAddedCallback.emit(pluginId, value1, value2, valueStr)
3222 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_REMOVED:
3223 host.PatchbayPortGroupRemovedCallback.emit(pluginId, value1)
3224 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_CHANGED:
3225 host.PatchbayPortGroupChangedCallback.emit(pluginId, value1, value2, valueStr)
3226 elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED:
3227 gOut, pOut, gIn, pIn = [int(i) for i in valueStr.split(":")] # FIXME
3228 host.PatchbayConnectionAddedCallback.emit(pluginId, gOut, pOut, gIn, pIn)
3229 elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED:
3230 host.PatchbayConnectionRemovedCallback.emit(pluginId, value1, value2)
3231 elif action == ENGINE_CALLBACK_ENGINE_STARTED:
3232 host.EngineStartedCallback.emit(pluginId, value1, value2, value3, valuef, valueStr)
3233 elif action == ENGINE_CALLBACK_ENGINE_STOPPED:
3234 host.EngineStoppedCallback.emit()
3235 elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
3236 host.ProcessModeChangedCallback.emit(value1)
3237 elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
3238 host.TransportModeChangedCallback.emit(value1, valueStr)
3239 elif action == ENGINE_CALLBACK_BUFFER_SIZE_CHANGED:
3240 host.BufferSizeChangedCallback.emit(value1)
3241 elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
3242 host.SampleRateChangedCallback.emit(valuef)
3243 elif action == ENGINE_CALLBACK_CANCELABLE_ACTION:
3244 host.CancelableActionCallback.emit(pluginId, bool(value1 != 0), valueStr)
3245 elif action == ENGINE_CALLBACK_PROJECT_LOAD_FINISHED:
3246 host.ProjectLoadFinishedCallback.emit()
3247 elif action == ENGINE_CALLBACK_NSM:
3248 host.NSMCallback.emit(value1, value2, valueStr)
3249 elif action == ENGINE_CALLBACK_IDLE:
3250 QApplication.processEvents()
3251 elif action == ENGINE_CALLBACK_INFO:
3252 host.InfoCallback.emit(valueStr)
3253 elif action == ENGINE_CALLBACK_ERROR:
3254 host.ErrorCallback.emit(valueStr)
3255 elif action == ENGINE_CALLBACK_QUIT:
3256 host.QuitCallback.emit()
3257 elif action == ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW:
3258 host.InlineDisplayRedrawCallback.emit(pluginId)
3259 else:
3260 print("unhandled action", action)
3262 # ------------------------------------------------------------------------------------------------------------
3263 # File callback
3265 def fileCallback(ptr, action, isDir, title, filter):
3266 title = charPtrToString(title)
3267 filter = charPtrToString(filter)
3269 if action == FILE_CALLBACK_OPEN:
3270 ret, ok = QFileDialog.getOpenFileName(gCarla.gui, title, "", filter) #, QFileDialog.ShowDirsOnly if isDir else 0x0)
3271 elif action == FILE_CALLBACK_SAVE:
3272 ret, ok = QFileDialog.getSaveFileName(gCarla.gui, title, "", filter, QFileDialog.ShowDirsOnly if isDir else 0x0)
3273 else:
3274 ret, ok = ("", "")
3276 # FIXME use ok value, test if it works as expected
3277 if not ret:
3278 return None
3280 # FIXME
3281 global fileRet
3282 fileRet = c_char_p(ret.encode("utf-8"))
3283 retval = cast(byref(fileRet), POINTER(c_uintptr))
3284 return retval.contents.value
3286 # ------------------------------------------------------------------------------------------------------------
3287 # Init host
3289 def initHost(initName, libPrefix, isControl, isPlugin, failError, HostClass = None):
3290 pathBinaries, pathResources = getPaths(libPrefix)
3292 # --------------------------------------------------------------------------------------------------------
3293 # Fail if binary dir is not found
3295 if not os.path.exists(pathBinaries):
3296 if failError:
3297 QMessageBox.critical(None, "Error", "Failed to find the carla binaries, cannot continue")
3298 sys.exit(1)
3299 return
3301 # --------------------------------------------------------------------------------------------------------
3302 # Check if we should open main lib as local or global
3304 settings = QSafeSettings("falkTX", "Carla2")
3306 loadGlobal = settings.value(CARLA_KEY_EXPERIMENTAL_LOAD_LIB_GLOBAL, CARLA_DEFAULT_EXPERIMENTAL_LOAD_LIB_GLOBAL, bool)
3308 # --------------------------------------------------------------------------------------------------------
3309 # Set Carla library name
3311 libname = "libcarla_%s2.%s" % ("control" if isControl else "standalone", DLL_EXTENSION)
3312 libname = os.path.join(pathBinaries, libname)
3313 felibname = os.path.join(pathBinaries, "libcarla_frontend.%s" % (DLL_EXTENSION))
3314 utilsname = os.path.join(pathBinaries, "libcarla_utils.%s" % (DLL_EXTENSION))
3316 # --------------------------------------------------------------------------------------------------------
3317 # Print info
3319 if not (gCarla.nogui and isinstance(gCarla.nogui, int)):
3320 print("Carla %s started, status:" % VERSION)
3321 print(" Python version: %s" % sys.version.split(" ",1)[0])
3322 print(" Qt version: %s" % QT_VERSION_STR)
3323 print(" PyQt version: %s" % PYQT_VERSION_STR)
3324 print(" Binary dir: %s" % pathBinaries)
3325 print(" Resources dir: %s" % pathResources)
3327 # --------------------------------------------------------------------------------------------------------
3328 # Init host
3330 if failError:
3331 # no try
3332 host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
3333 else:
3334 try:
3335 host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
3336 except:
3337 host = CarlaHostQtNull()
3339 host.isControl = isControl
3340 host.isPlugin = isPlugin
3342 host.set_engine_callback(lambda h,a,p,v1,v2,v3,vf,vs: engineCallback(host,a,p,v1,v2,v3,vf,vs))
3343 host.set_file_callback(fileCallback)
3345 # If it's a plugin the paths are already set
3346 if not isPlugin:
3347 host.pathBinaries = pathBinaries
3348 host.pathResources = pathResources
3349 host.set_engine_option(ENGINE_OPTION_PATH_BINARIES, 0, pathBinaries)
3350 host.set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources)
3352 if not isControl:
3353 host.nsmOK = host.nsm_init(os.getpid(), initName)
3355 # --------------------------------------------------------------------------------------------------------
3356 # Init frontend lib
3358 if not gCarla.nogui:
3359 gCarla.felib = CarlaFrontendLib(felibname)
3361 # --------------------------------------------------------------------------------------------------------
3362 # Init utils
3364 gCarla.utils = CarlaUtils(utilsname)
3365 gCarla.utils.set_process_name(os.path.basename(initName))
3367 try:
3368 sys.stdout.flush()
3369 except:
3370 pass
3372 sys.stdout = CarlaPrint(False)
3373 sys.stderr = CarlaPrint(True)
3374 sys.excepthook = sys_excepthook
3376 # --------------------------------------------------------------------------------------------------------
3377 # Done
3379 return host
3381 # ------------------------------------------------------------------------------------------------------------
3382 # Load host settings
3384 def loadHostSettings(host):
3385 # kdevelop likes this :)
3386 if False: host = CarlaHostNull()
3388 settings = QSafeSettings("falkTX", "Carla2")
3390 host.experimental = settings.value(CARLA_KEY_MAIN_EXPERIMENTAL, CARLA_DEFAULT_MAIN_EXPERIMENTAL, bool)
3391 host.exportLV2 = settings.value(CARLA_KEY_EXPERIMENTAL_EXPORT_LV2, CARLA_DEFAULT_EXPERIMENTAL_LV2_EXPORT, bool)
3392 host.manageUIs = settings.value(CARLA_KEY_ENGINE_MANAGE_UIS, CARLA_DEFAULT_MANAGE_UIS, bool)
3393 host.maxParameters = settings.value(CARLA_KEY_ENGINE_MAX_PARAMETERS, CARLA_DEFAULT_MAX_PARAMETERS, int)
3394 host.resetXruns = settings.value(CARLA_KEY_ENGINE_RESET_XRUNS, CARLA_DEFAULT_RESET_XRUNS, bool)
3395 host.forceStereo = settings.value(CARLA_KEY_ENGINE_FORCE_STEREO, CARLA_DEFAULT_FORCE_STEREO, bool)
3396 host.preferPluginBridges = settings.value(CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES, CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, bool)
3397 host.preferUIBridges = settings.value(CARLA_KEY_ENGINE_PREFER_UI_BRIDGES, CARLA_DEFAULT_PREFER_UI_BRIDGES, bool)
3398 host.preventBadBehaviour = settings.value(CARLA_KEY_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR, CARLA_DEFAULT_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR, bool)
3399 host.showLogs = settings.value(CARLA_KEY_MAIN_SHOW_LOGS, CARLA_DEFAULT_MAIN_SHOW_LOGS, bool) and not WINDOWS
3400 host.showPluginBridges = settings.value(CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES, bool)
3401 host.showWineBridges = settings.value(CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES, bool)
3402 host.uiBridgesTimeout = settings.value(CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT, CARLA_DEFAULT_UI_BRIDGES_TIMEOUT, int)
3403 host.uisAlwaysOnTop = settings.value(CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP, CARLA_DEFAULT_UIS_ALWAYS_ON_TOP, bool)
3405 if host.isPlugin:
3406 return
3408 host.transportExtra = settings.value(CARLA_KEY_ENGINE_TRANSPORT_EXTRA, "", str)
3410 # enums
3411 if host.audioDriverForced is None:
3412 host.transportMode = settings.value(CARLA_KEY_ENGINE_TRANSPORT_MODE, CARLA_DEFAULT_TRANSPORT_MODE, int)
3414 if not host.processModeForced:
3415 host.processMode = settings.value(CARLA_KEY_ENGINE_PROCESS_MODE, CARLA_DEFAULT_PROCESS_MODE, int)
3417 host.nextProcessMode = host.processMode
3419 # --------------------------------------------------------------------------------------------------------
3420 # fix things if needed
3422 if host.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS:
3423 if LADISH_APP_NAME:
3424 print("LADISH detected but using multiple clients (not allowed), forcing single client now")
3425 host.nextProcessMode = host.processMode = ENGINE_PROCESS_MODE_SINGLE_CLIENT
3427 else:
3428 host.transportMode = ENGINE_TRANSPORT_MODE_JACK
3430 if gCarla.nogui:
3431 host.showLogs = False
3433 # --------------------------------------------------------------------------------------------------------
3434 # run headless host now if nogui option enabled
3436 if gCarla.nogui:
3437 runHostWithoutUI(host)
3439 # ------------------------------------------------------------------------------------------------------------
3440 # Set host settings
3442 def setHostSettings(host):
3443 # kdevelop likes this :)
3444 if False: host = CarlaHostNull()
3446 host.set_engine_option(ENGINE_OPTION_FORCE_STEREO, host.forceStereo, "")
3447 host.set_engine_option(ENGINE_OPTION_MAX_PARAMETERS, host.maxParameters, "")
3448 host.set_engine_option(ENGINE_OPTION_RESET_XRUNS, host.resetXruns, "")
3449 host.set_engine_option(ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, host.preferPluginBridges, "")
3450 host.set_engine_option(ENGINE_OPTION_PREFER_UI_BRIDGES, host.preferUIBridges, "")
3451 host.set_engine_option(ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR, host.preventBadBehaviour, "")
3452 host.set_engine_option(ENGINE_OPTION_UI_BRIDGES_TIMEOUT, host.uiBridgesTimeout, "")
3453 host.set_engine_option(ENGINE_OPTION_UIS_ALWAYS_ON_TOP, host.uisAlwaysOnTop, "")
3455 if host.isPlugin or host.isRemote or host.is_engine_running():
3456 return
3458 host.set_engine_option(ENGINE_OPTION_PROCESS_MODE, host.nextProcessMode, "")
3459 host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, host.transportExtra)
3460 host.set_engine_option(ENGINE_OPTION_DEBUG_CONSOLE_OUTPUT, host.showLogs, "")
3462 if not (NSM_URL and host.nsmOK):
3463 host.set_engine_option(ENGINE_OPTION_CLIENT_NAME_PREFIX, 0, gCarla.cnprefix)
3465 # ---------------------------------------------------------------------------------------------------------------------
3466 # Set Engine settings according to carla preferences. Returns selected audio driver.
3468 def setEngineSettings(host, settings, oscPort = None):
3469 # kdevelop likes this :)
3470 if False: host = CarlaHostNull()
3472 # -----------------------------------------------------------------------------------------------------------------
3473 # do nothing if control
3475 if host.isControl:
3476 return "Control"
3478 # -----------------------------------------------------------------------------------------------------------------
3479 # fetch settings as needed
3481 if settings is None:
3482 qsettings = QSafeSettings("falkTX", "Carla2")
3484 if host.audioDriverForced is not None:
3485 audioDriver = host.audioDriverForced
3486 else:
3487 audioDriver = qsettings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
3489 audioDriverPrefix = CARLA_KEY_ENGINE_DRIVER_PREFIX + audioDriver
3491 settings = {
3492 # engine
3493 CARLA_KEY_ENGINE_AUDIO_DRIVER: audioDriver,
3494 CARLA_KEY_ENGINE_AUDIO_DEVICE: qsettings.value(audioDriverPrefix+"/Device", "", str),
3495 CARLA_KEY_ENGINE_BUFFER_SIZE: qsettings.value(audioDriverPrefix+"/BufferSize", CARLA_DEFAULT_AUDIO_BUFFER_SIZE, int),
3496 CARLA_KEY_ENGINE_SAMPLE_RATE: qsettings.value(audioDriverPrefix+"/SampleRate", CARLA_DEFAULT_AUDIO_SAMPLE_RATE, int),
3497 CARLA_KEY_ENGINE_TRIPLE_BUFFER: qsettings.value(audioDriverPrefix+"/TripleBuffer", CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER, bool),
3499 # file paths
3500 CARLA_KEY_PATHS_AUDIO: splitter.join(qsettings.value(CARLA_KEY_PATHS_AUDIO, CARLA_DEFAULT_FILE_PATH_AUDIO, list)),
3501 CARLA_KEY_PATHS_MIDI: splitter.join(qsettings.value(CARLA_KEY_PATHS_MIDI, CARLA_DEFAULT_FILE_PATH_MIDI, list)),
3503 # plugin paths
3504 CARLA_KEY_PATHS_LADSPA: splitter.join(qsettings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list)),
3505 CARLA_KEY_PATHS_DSSI: splitter.join(qsettings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list)),
3506 CARLA_KEY_PATHS_LV2: splitter.join(qsettings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)),
3507 CARLA_KEY_PATHS_VST2: splitter.join(qsettings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list)),
3508 CARLA_KEY_PATHS_VST3: splitter.join(qsettings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)),
3509 CARLA_KEY_PATHS_SF2: splitter.join(qsettings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)),
3510 CARLA_KEY_PATHS_SFZ: splitter.join(qsettings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)),
3511 CARLA_KEY_PATHS_JSFX: splitter.join(qsettings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)),
3512 CARLA_KEY_PATHS_CLAP: splitter.join(qsettings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list)),
3514 # osc
3515 CARLA_KEY_OSC_ENABLED: qsettings.value(CARLA_KEY_OSC_ENABLED, CARLA_DEFAULT_OSC_ENABLED, bool),
3516 CARLA_KEY_OSC_TCP_PORT_ENABLED: qsettings.value(CARLA_KEY_OSC_TCP_PORT_ENABLED, CARLA_DEFAULT_OSC_TCP_PORT_ENABLED, bool),
3517 CARLA_KEY_OSC_TCP_PORT_RANDOM: qsettings.value(CARLA_KEY_OSC_TCP_PORT_RANDOM, CARLA_DEFAULT_OSC_TCP_PORT_RANDOM, bool),
3518 CARLA_KEY_OSC_TCP_PORT_NUMBER: qsettings.value(CARLA_KEY_OSC_TCP_PORT_NUMBER, CARLA_DEFAULT_OSC_TCP_PORT_NUMBER, int),
3519 CARLA_KEY_OSC_UDP_PORT_ENABLED: qsettings.value(CARLA_KEY_OSC_UDP_PORT_ENABLED, CARLA_DEFAULT_OSC_UDP_PORT_ENABLED, bool),
3520 CARLA_KEY_OSC_UDP_PORT_RANDOM: qsettings.value(CARLA_KEY_OSC_UDP_PORT_RANDOM, CARLA_DEFAULT_OSC_UDP_PORT_RANDOM, bool),
3521 CARLA_KEY_OSC_UDP_PORT_NUMBER: qsettings.value(CARLA_KEY_OSC_UDP_PORT_NUMBER, CARLA_DEFAULT_OSC_UDP_PORT_NUMBER, int),
3523 # wine
3524 CARLA_KEY_WINE_EXECUTABLE: qsettings.value(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE, str),
3525 CARLA_KEY_WINE_AUTO_PREFIX: qsettings.value(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX, bool),
3526 CARLA_KEY_WINE_FALLBACK_PREFIX: qsettings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str),
3527 CARLA_KEY_WINE_RT_PRIO_ENABLED: qsettings.value(CARLA_KEY_WINE_RT_PRIO_ENABLED, CARLA_DEFAULT_WINE_RT_PRIO_ENABLED, bool),
3528 CARLA_KEY_WINE_BASE_RT_PRIO: qsettings.value(CARLA_KEY_WINE_BASE_RT_PRIO, CARLA_DEFAULT_WINE_BASE_RT_PRIO, int),
3529 CARLA_KEY_WINE_SERVER_RT_PRIO: qsettings.value(CARLA_KEY_WINE_SERVER_RT_PRIO, CARLA_DEFAULT_WINE_SERVER_RT_PRIO, int),
3532 # -----------------------------------------------------------------------------------------------------------------
3533 # main settings
3535 setHostSettings(host)
3537 # -----------------------------------------------------------------------------------------------------------------
3538 # file paths
3540 host.set_engine_option(ENGINE_OPTION_FILE_PATH, FILE_AUDIO, settings[CARLA_KEY_PATHS_AUDIO])
3541 host.set_engine_option(ENGINE_OPTION_FILE_PATH, FILE_MIDI, settings[CARLA_KEY_PATHS_MIDI])
3543 # -----------------------------------------------------------------------------------------------------------------
3544 # plugin paths
3546 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, settings[CARLA_KEY_PATHS_LADSPA])
3547 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, settings[CARLA_KEY_PATHS_DSSI])
3548 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, settings[CARLA_KEY_PATHS_LV2])
3549 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST2, settings[CARLA_KEY_PATHS_VST2])
3550 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, settings[CARLA_KEY_PATHS_VST3])
3551 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, settings[CARLA_KEY_PATHS_SF2])
3552 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, settings[CARLA_KEY_PATHS_SFZ])
3553 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, settings[CARLA_KEY_PATHS_JSFX])
3554 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_CLAP, settings[CARLA_KEY_PATHS_CLAP])
3556 # -----------------------------------------------------------------------------------------------------------------
3557 # don't continue if plugin
3559 if host.isPlugin:
3560 return "Plugin"
3562 # -----------------------------------------------------------------------------------------------------------------
3563 # osc settings
3565 if oscPort is not None and isinstance(oscPort, int):
3566 oscEnabled = True
3567 portNumTCP = portNumUDP = oscPort
3569 else:
3570 oscEnabled = settings[CARLA_KEY_OSC_ENABLED]
3572 if not settings[CARLA_KEY_OSC_TCP_PORT_ENABLED]:
3573 portNumTCP = -1
3574 elif settings[CARLA_KEY_OSC_TCP_PORT_RANDOM]:
3575 portNumTCP = 0
3576 else:
3577 portNumTCP = settings[CARLA_KEY_OSC_TCP_PORT_NUMBER]
3579 if not settings[CARLA_KEY_OSC_UDP_PORT_ENABLED]:
3580 portNumUDP = -1
3581 elif settings[CARLA_KEY_OSC_UDP_PORT_RANDOM]:
3582 portNumUDP = 0
3583 else:
3584 portNumUDP = settings[CARLA_KEY_OSC_UDP_PORT_NUMBER]
3586 host.set_engine_option(ENGINE_OPTION_OSC_ENABLED, 1 if oscEnabled else 0, "")
3587 host.set_engine_option(ENGINE_OPTION_OSC_PORT_TCP, portNumTCP, "")
3588 host.set_engine_option(ENGINE_OPTION_OSC_PORT_UDP, portNumUDP, "")
3590 # -----------------------------------------------------------------------------------------------------------------
3591 # wine settings
3593 host.set_engine_option(ENGINE_OPTION_WINE_EXECUTABLE, 0, settings[CARLA_KEY_WINE_EXECUTABLE])
3594 host.set_engine_option(ENGINE_OPTION_WINE_AUTO_PREFIX, 1 if settings[CARLA_KEY_WINE_AUTO_PREFIX] else 0, "")
3595 host.set_engine_option(ENGINE_OPTION_WINE_FALLBACK_PREFIX, 0, os.path.expanduser(settings[CARLA_KEY_WINE_FALLBACK_PREFIX]))
3596 host.set_engine_option(ENGINE_OPTION_WINE_RT_PRIO_ENABLED, 1 if settings[CARLA_KEY_WINE_RT_PRIO_ENABLED] else 0, "")
3597 host.set_engine_option(ENGINE_OPTION_WINE_BASE_RT_PRIO, settings[CARLA_KEY_WINE_BASE_RT_PRIO], "")
3598 host.set_engine_option(ENGINE_OPTION_WINE_SERVER_RT_PRIO, settings[CARLA_KEY_WINE_SERVER_RT_PRIO], "")
3600 # -----------------------------------------------------------------------------------------------------------------
3601 # driver and device settings
3603 # driver name
3604 audioDriver = settings[CARLA_KEY_ENGINE_AUDIO_DRIVER]
3606 # Only setup audio things if engine is not running
3607 if not host.is_engine_running():
3608 host.set_engine_option(ENGINE_OPTION_AUDIO_DRIVER, 0, audioDriver)
3609 host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, settings[CARLA_KEY_ENGINE_AUDIO_DEVICE])
3611 if not audioDriver.startswith("JACK"):
3612 host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, settings[CARLA_KEY_ENGINE_BUFFER_SIZE], "")
3613 host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, settings[CARLA_KEY_ENGINE_SAMPLE_RATE], "")
3614 host.set_engine_option(ENGINE_OPTION_AUDIO_TRIPLE_BUFFER, 1 if settings[CARLA_KEY_ENGINE_TRIPLE_BUFFER] else 0, "")
3616 # -----------------------------------------------------------------------------------------------------------------
3617 # fix things if needed
3619 if audioDriver != "JACK" and host.transportMode == ENGINE_TRANSPORT_MODE_JACK:
3620 host.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL
3621 host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, ENGINE_TRANSPORT_MODE_INTERNAL, host.transportExtra)
3623 # -----------------------------------------------------------------------------------------------------------------
3624 # return selected driver name
3626 return audioDriver
3628 # ---------------------------------------------------------------------------------------------------------------------
3629 # Run Carla without showing UI
3631 def runHostWithoutUI(host):
3632 # kdevelop likes this :)
3633 if False: host = CarlaHostNull()
3635 # --------------------------------------------------------------------------------------------------------
3636 # Some initial checks
3638 if not gCarla.nogui:
3639 return
3641 projectFile = getInitialProjectFile(True)
3643 if gCarla.nogui is True:
3644 oscPort = None
3646 if not projectFile:
3647 print("Carla no-gui mode can only be used together with a project file.")
3648 sys.exit(1)
3650 else:
3651 oscPort = gCarla.nogui
3653 # --------------------------------------------------------------------------------------------------------
3654 # Additional imports
3656 from time import sleep
3658 # --------------------------------------------------------------------------------------------------------
3659 # Init engine
3661 audioDriver = setEngineSettings(host, None, oscPort)
3662 if not host.engine_init(audioDriver, CARLA_CLIENT_NAME or "Carla"):
3663 print("Engine failed to initialize, possible reasons:\n%s" % host.get_last_error())
3664 sys.exit(1)
3666 if projectFile and not host.load_project(projectFile):
3667 print("Failed to load selected project file, possible reasons:\n%s" % host.get_last_error())
3668 host.engine_close()
3669 sys.exit(1)
3671 # --------------------------------------------------------------------------------------------------------
3672 # Idle
3674 print("Carla ready!")
3676 while host.is_engine_running() and not gCarla.term:
3677 host.engine_idle()
3678 sleep(0.0333) # 30 Hz
3680 # --------------------------------------------------------------------------------------------------------
3681 # Stop
3683 host.engine_close()
3684 sys.exit(0)
3686 # ------------------------------------------------------------------------------------------------------------