Cleanup
[carla.git] / source / frontend / carla_host.py
blob6480a5b95e74c63a5bf6a36e2f721a599754e8c5
1 #!/usr/bin/env python3
2 # SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # ------------------------------------------------------------------------------------------------------------
6 # Imports (Global)
8 import json
10 # ------------------------------------------------------------------------------------------------------------
11 # Imports (ctypes)
13 from ctypes import (
14 byref, pointer
17 # ------------------------------------------------------------------------------------------------------------
18 # Imports (PyQt)
20 from qt_compat import qt_config
22 if qt_config == 5:
23 # This fails in some configurations, assume >= 5.6.0 in that case
24 try:
25 from PyQt5.Qt import PYQT_VERSION
26 except ImportError:
27 PYQT_VERSION = 0x50600
29 from PyQt5.QtCore import (
30 QT_VERSION,
31 qCritical,
32 QBuffer,
33 QEvent,
34 QEventLoop,
35 QFileInfo,
36 QIODevice,
37 QMimeData,
38 QModelIndex,
39 QPointF,
40 QTimer,
42 from PyQt5.QtGui import (
43 QBrush,
44 QImage,
45 QImageWriter,
46 QPainter,
47 QPalette,
49 from PyQt5.QtWidgets import (
50 QAction,
51 QApplication,
52 QInputDialog,
53 QFileSystemModel,
54 QListWidgetItem,
55 QGraphicsView,
56 QMainWindow,
59 elif qt_config == 6:
60 from PyQt6.QtCore import (
61 PYQT_VERSION,
62 QT_VERSION,
63 qCritical,
64 QBuffer,
65 QEvent,
66 QEventLoop,
67 QFileInfo,
68 QIODevice,
69 QMimeData,
70 QModelIndex,
71 QPointF,
72 QTimer,
74 from PyQt6.QtGui import (
75 QAction,
76 QBrush,
77 QFileSystemModel,
78 QImage,
79 QImageWriter,
80 QPainter,
81 QPalette,
83 from PyQt6.QtWidgets import (
84 QApplication,
85 QInputDialog,
86 QListWidgetItem,
87 QGraphicsView,
88 QMainWindow,
91 # ------------------------------------------------------------------------------------------------------------
92 # Imports (Custom)
94 import ui_carla_host
96 from carla_app import *
97 from carla_backend import *
98 from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull
99 from carla_frontend import CarlaFrontendLib
100 from carla_shared import *
101 from carla_settings import *
102 from carla_utils import *
103 from carla_widgets import *
105 from patchcanvas import patchcanvas
106 from widgets.digitalpeakmeter import DigitalPeakMeter
107 from widgets.pixmapkeyboard import PixmapKeyboardHArea
109 # ------------------------------------------------------------------------------------------------------------
110 # Try Import OpenGL
112 try:
113 from PyQt5.QtOpenGL import QGLWidget
114 hasGL = True
115 except:
116 hasGL = False
118 # ------------------------------------------------------------------------------------------------------------
119 # Safe exception hook, needed for PyQt5
121 def sys_excepthook(typ, value, tback):
122 return sys.__excepthook__(typ, value, tback)
124 # ------------------------------------------------------------------------------------------------------------
125 # Session Management support
127 CARLA_CLIENT_NAME = os.getenv("CARLA_CLIENT_NAME")
128 LADISH_APP_NAME = os.getenv("LADISH_APP_NAME")
129 NSM_URL = os.getenv("NSM_URL")
131 # ------------------------------------------------------------------------------------------------------------
132 # Small print helper
134 def processMode2Str(processMode):
135 if processMode == ENGINE_PROCESS_MODE_SINGLE_CLIENT:
136 return "Single client"
137 if processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS:
138 return "Multi client"
139 if processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK:
140 return "Continuous Rack"
141 if processMode == ENGINE_PROCESS_MODE_PATCHBAY:
142 return "Patchbay"
143 if processMode == ENGINE_PROCESS_MODE_BRIDGE:
144 return "Bridge"
145 return "Unknown"
147 # ------------------------------------------------------------------------------------------------------------
148 # Carla Print class
150 class CarlaPrint:
151 def __init__(self, err):
152 self.err = err
154 def flush(self):
155 gCarla.utils.fflush(self.err)
157 def write(self, string):
158 gCarla.utils.fputs(self.err, string)
160 # ------------------------------------------------------------------------------------------------------------
161 # Host Window
163 class HostWindow(QMainWindow):
164 #class HostWindow(QMainWindow, PluginEditParentMeta, metaclass=PyQtMetaClass):
165 # signals
166 SIGTERM = pyqtSignal()
167 SIGUSR1 = pyqtSignal()
169 # CustomActions
170 CUSTOM_ACTION_NONE = 0
171 CUSTOM_ACTION_APP_CLOSE = 1
172 CUSTOM_ACTION_PROJECT_LOAD = 2
174 # --------------------------------------------------------------------------------------------------------
176 def __init__(self, host, withCanvas, parent=None):
177 QMainWindow.__init__(self, parent)
178 self.host = host
179 self.ui = ui_carla_host.Ui_CarlaHostW()
180 self.ui.setupUi(self)
181 gCarla.gui = self
183 if False:
184 # kdevelop likes this :)
185 host = CarlaHostNull()
186 self.host = host
188 self._true = c_char_p("true".encode("utf-8"))
190 self.fParentOrSelf = parent or self
192 # ----------------------------------------------------------------------------------------------------
193 # Internal stuff
195 self.fIdleTimerNull = self.startTimer(1000) # keep application signals alive
196 self.fIdleTimerFast = 0
197 self.fIdleTimerSlow = 0
199 self.fLadspaRdfNeedsUpdate = True
200 self.fLadspaRdfList = []
202 self.fPluginCount = 0
203 self.fPluginList = []
205 self.fPluginListDialog = None
206 self.fFavoritePlugins = []
208 self.fProjectFilename = ""
209 self.fIsProjectLoading = False
210 self.fCurrentlyRemovingAllPlugins = False
211 self.fHasLoadedLv2Plugins = False
213 self.fLastTransportBPM = 0.0
214 self.fLastTransportFrame = 0
215 self.fLastTransportState = False
216 self.fBufferSize = 0
217 self.fSampleRate = 0.0
218 self.fOscAddressTCP = ""
219 self.fOscAddressUDP = ""
221 if MACOS:
222 self.fMacClosingHelper = True
224 # CancelableActionCallback Box
225 self.fCancelableActionBox = None
227 # run a custom action after engine is properly closed
228 self.fCustomStopAction = self.CUSTOM_ACTION_NONE
230 # first attempt of auto-start engine doesn't show an error
231 self.fFirstEngineInit = True
233 # to be filled with key-value pairs of current settings
234 self.fSavedSettings = {}
236 # true if NSM server handles our window management
237 self.fWindowCloseHideGui = False
239 if host.isControl:
240 self.fClientName = "Carla-Control"
241 self.fSessionManagerName = "Control"
242 elif host.isPlugin:
243 self.fClientName = "Carla-Plugin"
244 self.fSessionManagerName = "Plugin"
245 elif LADISH_APP_NAME:
246 self.fClientName = LADISH_APP_NAME
247 self.fSessionManagerName = ""
248 elif NSM_URL and host.nsmOK:
249 self.fClientName = "Carla.tmp"
250 self.fSessionManagerName = "Non Session Manager TMP"
251 self.fWindowCloseHideGui = True
252 else:
253 self.fClientName = CARLA_CLIENT_NAME or "Carla"
254 self.fSessionManagerName = ""
256 # ----------------------------------------------------------------------------------------------------
257 # Internal stuff (patchbay)
259 self.fPeaksCleared = True
261 self.fExternalPatchbay = False
262 self.fSelectedPlugins = []
264 self.fCanvasWidth = 0
265 self.fCanvasHeight = 0
266 self.fMiniCanvasUpdateTimeout = 0
268 self.fWithCanvas = withCanvas
270 # ----------------------------------------------------------------------------------------------------
271 # Internal stuff (logs)
273 self.autoscrollOnNewLog = True
274 self.lastLogSliderPos = 0
276 # ----------------------------------------------------------------------------------------------------
277 # Set up GUI (engine stopped)
279 if self.host.isPlugin or self.host.isControl:
280 self.ui.act_file_save.setVisible(False)
281 self.ui.act_engine_start.setEnabled(False)
282 self.ui.act_engine_start.setVisible(False)
283 self.ui.act_engine_stop.setEnabled(False)
284 self.ui.act_engine_stop.setVisible(False)
285 self.ui.menu_Engine.setEnabled(False)
286 self.ui.menu_Engine.setVisible(False)
287 self.ui.menu_Engine.menuAction().setVisible(False)
288 self.ui.tabWidget.removeTab(2)
290 if self.host.isControl:
291 self.ui.act_file_new.setVisible(False)
292 self.ui.act_file_open.setVisible(False)
293 self.ui.act_file_save_as.setVisible(False)
294 self.ui.tabUtils.removeTab(0)
295 else:
296 self.ui.act_file_save_as.setText(self.tr("Export as..."))
298 if not withCanvas:
299 self.ui.tabWidget.tabBar().hide()
301 else:
302 self.ui.act_engine_start.setEnabled(True)
304 if WINDOWS:
305 self.ui.tabWidget.removeTab(2)
307 if self.host.isControl:
308 self.ui.act_file_refresh.setEnabled(False)
309 else:
310 self.ui.act_file_connect.setEnabled(False)
311 self.ui.act_file_connect.setVisible(False)
312 self.ui.act_file_refresh.setEnabled(False)
313 self.ui.act_file_refresh.setVisible(False)
315 if self.fSessionManagerName and not self.host.isPlugin:
316 self.ui.act_file_new.setEnabled(False)
318 self.ui.act_file_open.setEnabled(False)
319 self.ui.act_file_save.setEnabled(False)
320 self.ui.act_file_save_as.setEnabled(False)
321 self.ui.act_engine_stop.setEnabled(False)
322 self.ui.act_plugin_remove_all.setEnabled(False)
324 self.ui.act_canvas_show_internal.setChecked(False)
325 self.ui.act_canvas_show_internal.setVisible(False)
326 self.ui.act_canvas_show_external.setChecked(False)
327 self.ui.act_canvas_show_external.setVisible(False)
329 self.ui.menu_PluginMacros.setEnabled(False)
330 self.ui.menu_Canvas.setEnabled(False)
332 self.ui.dockWidgetTitleBar = QWidget(self)
333 self.ui.dockWidget.setTitleBarWidget(self.ui.dockWidgetTitleBar)
335 if not withCanvas:
336 self.ui.act_canvas_show_internal.setVisible(False)
337 self.ui.act_canvas_show_external.setVisible(False)
338 self.ui.act_canvas_arrange.setVisible(False)
339 self.ui.act_canvas_refresh.setVisible(False)
340 self.ui.act_canvas_save_image.setVisible(False)
341 self.ui.act_canvas_zoom_100.setVisible(False)
342 self.ui.act_canvas_zoom_fit.setVisible(False)
343 self.ui.act_canvas_zoom_in.setVisible(False)
344 self.ui.act_canvas_zoom_out.setVisible(False)
345 self.ui.act_settings_show_meters.setVisible(False)
346 self.ui.act_settings_show_keyboard.setVisible(False)
347 self.ui.menu_Canvas_Zoom.setEnabled(False)
348 self.ui.menu_Canvas_Zoom.setVisible(False)
349 self.ui.menu_Canvas_Zoom.menuAction().setVisible(False)
350 self.ui.menu_Canvas.setEnabled(False)
351 self.ui.menu_Canvas.setVisible(False)
352 self.ui.menu_Canvas.menuAction().setVisible(False)
353 self.ui.tw_miniCanvas.hide()
354 self.ui.tabWidget.removeTab(1)
355 if WINDOWS:
356 self.ui.tabWidget.tabBar().hide()
358 # ----------------------------------------------------------------------------------------------------
359 # Set up GUI (disk)
361 exts = gCarla.utils.get_supported_file_extensions()
363 self.fDirModel = QFileSystemModel(self)
364 self.fDirModel.setRootPath(HOME)
365 self.fDirModel.setNameFilters(tuple(("*." + i) for i in exts))
367 self.ui.fileTreeView.setModel(self.fDirModel)
368 self.ui.fileTreeView.setRootIndex(self.fDirModel.index(HOME))
369 self.ui.fileTreeView.setColumnHidden(1, True)
370 self.ui.fileTreeView.setColumnHidden(2, True)
371 self.ui.fileTreeView.setColumnHidden(3, True)
372 self.ui.fileTreeView.setHeaderHidden(True)
374 # ----------------------------------------------------------------------------------------------------
375 # Set up GUI (transport)
377 fontMetrics = self.ui.l_transport_bbt.fontMetrics()
378 minValueWidth = fontMetricsHorizontalAdvance(fontMetrics, "000|00|0000")
379 minLabelWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_frame.text())
381 labelTimeWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_time.text())
382 labelBBTWidth = fontMetricsHorizontalAdvance(fontMetrics, self.ui.label_transport_bbt.text())
384 if minLabelWidth < labelTimeWidth:
385 minLabelWidth = labelTimeWidth
386 if minLabelWidth < labelBBTWidth:
387 minLabelWidth = labelBBTWidth
389 self.ui.label_transport_frame.setMinimumWidth(minLabelWidth + 3)
390 self.ui.label_transport_time.setMinimumWidth(minLabelWidth + 3)
391 self.ui.label_transport_bbt.setMinimumWidth(minLabelWidth + 3)
393 self.ui.l_transport_bbt.setMinimumWidth(minValueWidth + 3)
394 self.ui.l_transport_frame.setMinimumWidth(minValueWidth + 3)
395 self.ui.l_transport_time.setMinimumWidth(minValueWidth + 3)
397 if host.isPlugin:
398 self.ui.b_transport_play.setEnabled(False)
399 self.ui.b_transport_stop.setEnabled(False)
400 self.ui.b_transport_backwards.setEnabled(False)
401 self.ui.b_transport_forwards.setEnabled(False)
402 self.ui.group_transport_controls.setEnabled(False)
403 self.ui.group_transport_controls.setVisible(False)
404 self.ui.cb_transport_link.setEnabled(False)
405 self.ui.cb_transport_link.setVisible(False)
406 self.ui.cb_transport_jack.setEnabled(False)
407 self.ui.cb_transport_jack.setVisible(False)
408 self.ui.dsb_transport_bpm.setEnabled(False)
409 self.ui.dsb_transport_bpm.setReadOnly(True)
411 self.ui.w_transport.setEnabled(False)
413 # ----------------------------------------------------------------------------------------------------
414 # Set up GUI (rack)
416 self.ui.listWidget.setHostAndParent(self.host, self)
418 sb = self.ui.listWidget.verticalScrollBar()
419 self.ui.rackScrollBar.setMinimum(sb.minimum())
420 self.ui.rackScrollBar.setMaximum(sb.maximum())
421 self.ui.rackScrollBar.setValue(sb.value())
423 sb.rangeChanged.connect(self.ui.rackScrollBar.setRange)
424 sb.valueChanged.connect(self.ui.rackScrollBar.setValue)
425 self.ui.rackScrollBar.rangeChanged.connect(sb.setRange)
426 self.ui.rackScrollBar.valueChanged.connect(sb.setValue)
428 self.updateStyle()
430 self.ui.rack.setStyleSheet("""
431 CarlaRackList#CarlaRackList {
432 background-color: black;
434 """)
436 # ----------------------------------------------------------------------------------------------------
437 # Set up GUI (patchbay)
439 self.ui.peak_in.setChannelCount(2)
440 self.ui.peak_in.setMeterColor(DigitalPeakMeter.COLOR_BLUE)
441 self.ui.peak_in.setMeterOrientation(DigitalPeakMeter.VERTICAL)
442 self.ui.peak_in.setFixedWidth(25)
444 self.ui.peak_out.setChannelCount(2)
445 self.ui.peak_out.setMeterColor(DigitalPeakMeter.COLOR_GREEN)
446 self.ui.peak_out.setMeterOrientation(DigitalPeakMeter.VERTICAL)
447 self.ui.peak_out.setFixedWidth(25)
449 self.ui.scrollArea = PixmapKeyboardHArea(self.ui.patchbay)
450 self.ui.keyboard = self.ui.scrollArea.keyboard
451 self.ui.patchbay.layout().addWidget(self.ui.scrollArea, 1, 0, 1, 0)
453 self.ui.scrollArea.setEnabled(False)
455 self.ui.miniCanvasPreview.setRealParent(self)
456 self.ui.tw_miniCanvas.tabBar().hide()
458 # ----------------------------------------------------------------------------------------------------
459 # Set up GUI (logs)
461 self.ui.text_logs.textChanged.connect(self.slot_logButtonsState)
462 self.ui.logs_clear.clicked.connect(self.slot_logClear)
463 self.ui.logs_save.clicked.connect(self.slot_logSave)
464 self.ui.logs_autoscroll.stateChanged.connect(self.slot_toggleLogAutoscroll)
465 self.ui.text_logs.verticalScrollBar().valueChanged.connect(self.slot_logSliderMoved)
467 # ----------------------------------------------------------------------------------------------------
468 # Set up GUI (special stuff for Mac OS)
470 if MACOS:
471 self.ui.act_file_quit.setMenuRole(QAction.QuitRole)
472 self.ui.act_settings_configure.setMenuRole(QAction.PreferencesRole)
473 self.ui.act_help_about.setMenuRole(QAction.AboutRole)
474 self.ui.act_help_about_qt.setMenuRole(QAction.AboutQtRole)
475 self.ui.menu_Settings.setTitle("Panels")
477 # ----------------------------------------------------------------------------------------------------
478 # Load Settings
480 self.loadSettings(True)
482 # ----------------------------------------------------------------------------------------------------
483 # Set-up Canvas
485 if withCanvas:
486 self.scene = patchcanvas.PatchScene(self, self.ui.graphicsView)
487 self.ui.graphicsView.setScene(self.scene)
489 if self.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL:
490 self.ui.glView = QGLWidget(self)
491 self.ui.graphicsView.setViewport(self.ui.glView)
493 self.setupCanvas()
495 # ----------------------------------------------------------------------------------------------------
496 # Set-up Icons
498 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
499 self.ui.act_file_connect.setIcon(getIcon('network-connect', 16, 'svgz'))
500 self.ui.act_file_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
501 self.ui.act_file_new.setIcon(getIcon('document-new', 16, 'svgz'))
502 self.ui.act_file_open.setIcon(getIcon('document-open', 16, 'svgz'))
503 self.ui.act_file_save.setIcon(getIcon('document-save', 16, 'svgz'))
504 self.ui.act_file_save_as.setIcon(getIcon('document-save-as', 16, 'svgz'))
505 self.ui.act_file_quit.setIcon(getIcon('application-exit', 16, 'svgz'))
506 self.ui.act_engine_start.setIcon(getIcon('media-playback-start', 16, 'svgz'))
507 self.ui.act_engine_stop.setIcon(getIcon('media-playback-stop', 16, 'svgz'))
508 self.ui.act_engine_panic.setIcon(getIcon('dialog-warning', 16, 'svgz'))
509 self.ui.act_engine_config.setIcon(getIcon('configure', 16, 'svgz'))
510 self.ui.act_plugin_add.setIcon(getIcon('list-add', 16, 'svgz'))
511 self.ui.act_plugin_add_jack.setIcon(getIcon('list-add', 16, 'svgz'))
512 self.ui.act_plugin_remove_all.setIcon(getIcon('edit-delete', 16, 'svgz'))
513 self.ui.act_canvas_arrange.setIcon(getIcon('view-sort-ascending', 16, 'svgz'))
514 self.ui.act_canvas_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
515 self.ui.act_canvas_zoom_fit.setIcon(getIcon('zoom-fit-best', 16, 'svgz'))
516 self.ui.act_canvas_zoom_in.setIcon(getIcon('zoom-in', 16, 'svgz'))
517 self.ui.act_canvas_zoom_out.setIcon(getIcon('zoom-out', 16, 'svgz'))
518 self.ui.act_canvas_zoom_100.setIcon(getIcon('zoom-original', 16, 'svgz'))
519 self.ui.act_settings_configure.setIcon(getIcon('configure', 16, 'svgz'))
520 self.ui.b_disk_add.setIcon(getIcon('list-add', 16, 'svgz'))
521 self.ui.b_disk_remove.setIcon(getIcon('list-remove', 16, 'svgz'))
522 self.ui.b_transport_play.setIcon(getIcon('media-playback-start', 16, 'svgz'))
523 self.ui.b_transport_stop.setIcon(getIcon('media-playback-stop', 16, 'svgz'))
524 self.ui.b_transport_backwards.setIcon(getIcon('media-seek-backward', 16, 'svgz'))
525 self.ui.b_transport_forwards.setIcon(getIcon('media-seek-forward', 16, 'svgz'))
526 self.ui.logs_clear.setIcon(getIcon('edit-clear', 16, 'svgz'))
527 self.ui.logs_save.setIcon(getIcon('document-save', 16, 'svgz'))
529 # ----------------------------------------------------------------------------------------------------
530 # Connect actions to functions
532 self.ui.act_file_new.triggered.connect(self.slot_fileNew)
533 self.ui.act_file_open.triggered.connect(self.slot_fileOpen)
534 self.ui.act_file_save.triggered.connect(self.slot_fileSave)
535 self.ui.act_file_save_as.triggered.connect(self.slot_fileSaveAs)
537 self.ui.act_engine_start.triggered.connect(self.slot_engineStart)
538 self.ui.act_engine_stop.triggered.connect(self.slot_engineStop)
539 self.ui.act_engine_panic.triggered.connect(self.slot_pluginsDisable)
540 self.ui.act_engine_config.triggered.connect(self.slot_engineConfig)
542 self.ui.act_plugin_add.triggered.connect(self.slot_pluginAdd)
543 self.ui.act_plugin_add_jack.triggered.connect(self.slot_jackAppAdd)
544 self.ui.act_plugin_remove_all.triggered.connect(self.slot_confirmRemoveAll)
546 self.ui.act_plugins_enable.triggered.connect(self.slot_pluginsEnable)
547 self.ui.act_plugins_disable.triggered.connect(self.slot_pluginsDisable)
548 self.ui.act_plugins_volume100.triggered.connect(self.slot_pluginsVolume100)
549 self.ui.act_plugins_mute.triggered.connect(self.slot_pluginsMute)
550 self.ui.act_plugins_wet100.triggered.connect(self.slot_pluginsWet100)
551 self.ui.act_plugins_bypass.triggered.connect(self.slot_pluginsBypass)
552 self.ui.act_plugins_center.triggered.connect(self.slot_pluginsCenter)
553 self.ui.act_plugins_compact.triggered.connect(self.slot_pluginsCompact)
554 self.ui.act_plugins_expand.triggered.connect(self.slot_pluginsExpand)
556 self.ui.act_settings_show_toolbar.toggled.connect(self.slot_showToolbar)
557 self.ui.act_settings_show_meters.toggled.connect(self.slot_showCanvasMeters)
558 self.ui.act_settings_show_keyboard.toggled.connect(self.slot_showCanvasKeyboard)
559 self.ui.act_settings_show_side_panel.toggled.connect(self.slot_showSidePanel)
560 self.ui.act_settings_configure.triggered.connect(self.slot_configureCarla)
562 self.ui.act_help_about.triggered.connect(self.slot_aboutCarla)
563 self.ui.act_help_about_qt.triggered.connect(self.slot_aboutQt)
565 self.ui.cb_disk.currentIndexChanged.connect(self.slot_diskFolderChanged)
566 self.ui.b_disk_add.clicked.connect(self.slot_diskFolderAdd)
567 self.ui.b_disk_remove.clicked.connect(self.slot_diskFolderRemove)
568 self.ui.fileTreeView.doubleClicked.connect(self.slot_fileTreeDoubleClicked)
570 self.ui.b_transport_play.clicked.connect(self.slot_transportPlayPause)
571 self.ui.b_transport_stop.clicked.connect(self.slot_transportStop)
572 self.ui.b_transport_backwards.clicked.connect(self.slot_transportBackwards)
573 self.ui.b_transport_forwards.clicked.connect(self.slot_transportForwards)
574 self.ui.dsb_transport_bpm.valueChanged.connect(self.slot_transportBpmChanged)
575 self.ui.cb_transport_jack.clicked.connect(self.slot_transportJackEnabled)
576 self.ui.cb_transport_link.clicked.connect(self.slot_transportLinkEnabled)
578 self.ui.b_xruns.clicked.connect(self.slot_xrunClear)
580 self.ui.listWidget.customContextMenuRequested.connect(self.slot_showPluginActionsMenu)
582 self.ui.keyboard.noteOn.connect(self.slot_noteOn)
583 self.ui.keyboard.noteOff.connect(self.slot_noteOff)
585 self.ui.tabWidget.currentChanged.connect(self.slot_tabChanged)
586 self.ui.toolBar.visibilityChanged.connect(self.slot_toolbarVisibilityChanged)
588 if withCanvas:
589 self.ui.act_canvas_show_internal.triggered.connect(self.slot_canvasShowInternal)
590 self.ui.act_canvas_show_external.triggered.connect(self.slot_canvasShowExternal)
591 self.ui.act_canvas_arrange.triggered.connect(self.slot_canvasArrange)
592 self.ui.act_canvas_refresh.triggered.connect(self.slot_canvasRefresh)
593 self.ui.act_canvas_zoom_fit.triggered.connect(self.slot_canvasZoomFit)
594 self.ui.act_canvas_zoom_in.triggered.connect(self.slot_canvasZoomIn)
595 self.ui.act_canvas_zoom_out.triggered.connect(self.slot_canvasZoomOut)
596 self.ui.act_canvas_zoom_100.triggered.connect(self.slot_canvasZoomReset)
597 self.ui.act_canvas_save_image.triggered.connect(self.slot_canvasSaveImage)
598 self.ui.act_canvas_save_image_2x.triggered.connect(self.slot_canvasSaveImage)
599 self.ui.act_canvas_save_image_4x.triggered.connect(self.slot_canvasSaveImage)
600 self.ui.act_canvas_copy_clipboard.triggered.connect(self.slot_canvasCopyToClipboard)
601 self.ui.act_canvas_arrange.setEnabled(False) # TODO, later
602 self.ui.graphicsView.horizontalScrollBar().valueChanged.connect(self.slot_horizontalScrollBarChanged)
603 self.ui.graphicsView.verticalScrollBar().valueChanged.connect(self.slot_verticalScrollBarChanged)
604 self.ui.miniCanvasPreview.miniCanvasMoved.connect(self.slot_miniCanvasMoved)
605 self.scene.scaleChanged.connect(self.slot_canvasScaleChanged)
606 self.scene.pluginSelected.connect(self.slot_canvasPluginSelected)
607 self.scene.selectionChanged.connect(self.slot_canvasSelectionChanged)
609 self.SIGUSR1.connect(self.slot_handleSIGUSR1)
610 self.SIGTERM.connect(self.slot_handleSIGTERM)
612 host.EngineStartedCallback.connect(self.slot_handleEngineStartedCallback)
613 host.EngineStoppedCallback.connect(self.slot_handleEngineStoppedCallback)
614 host.TransportModeChangedCallback.connect(self.slot_handleTransportModeChangedCallback)
615 host.BufferSizeChangedCallback.connect(self.slot_handleBufferSizeChangedCallback)
616 host.SampleRateChangedCallback.connect(self.slot_handleSampleRateChangedCallback)
617 host.CancelableActionCallback.connect(self.slot_handleCancelableActionCallback)
618 host.ProjectLoadFinishedCallback.connect(self.slot_handleProjectLoadFinishedCallback)
620 host.PluginAddedCallback.connect(self.slot_handlePluginAddedCallback)
621 host.PluginRemovedCallback.connect(self.slot_handlePluginRemovedCallback)
622 host.ReloadAllCallback.connect(self.slot_handleReloadAllCallback)
624 host.NoteOnCallback.connect(self.slot_handleNoteOnCallback)
625 host.NoteOffCallback.connect(self.slot_handleNoteOffCallback)
627 host.UpdateCallback.connect(self.slot_handleUpdateCallback)
629 if withCanvas:
630 host.PatchbayClientAddedCallback.connect(self.slot_handlePatchbayClientAddedCallback)
631 host.PatchbayClientRemovedCallback.connect(self.slot_handlePatchbayClientRemovedCallback)
632 host.PatchbayClientRenamedCallback.connect(self.slot_handlePatchbayClientRenamedCallback)
633 host.PatchbayClientDataChangedCallback.connect(self.slot_handlePatchbayClientDataChangedCallback)
634 host.PatchbayClientPositionChangedCallback.connect(self.slot_handlePatchbayClientPositionChangedCallback)
635 host.PatchbayPortAddedCallback.connect(self.slot_handlePatchbayPortAddedCallback)
636 host.PatchbayPortRemovedCallback.connect(self.slot_handlePatchbayPortRemovedCallback)
637 host.PatchbayPortChangedCallback.connect(self.slot_handlePatchbayPortChangedCallback)
638 host.PatchbayPortGroupAddedCallback.connect(self.slot_handlePatchbayPortGroupAddedCallback)
639 host.PatchbayPortGroupRemovedCallback.connect(self.slot_handlePatchbayPortGroupRemovedCallback)
640 host.PatchbayPortGroupChangedCallback.connect(self.slot_handlePatchbayPortGroupChangedCallback)
641 host.PatchbayConnectionAddedCallback.connect(self.slot_handlePatchbayConnectionAddedCallback)
642 host.PatchbayConnectionRemovedCallback.connect(self.slot_handlePatchbayConnectionRemovedCallback)
644 host.NSMCallback.connect(self.slot_handleNSMCallback)
646 host.DebugCallback.connect(self.slot_handleDebugCallback)
647 host.InfoCallback.connect(self.slot_handleInfoCallback)
648 host.ErrorCallback.connect(self.slot_handleErrorCallback)
649 host.QuitCallback.connect(self.slot_handleQuitCallback)
650 host.InlineDisplayRedrawCallback.connect(self.slot_handleInlineDisplayRedrawCallback)
652 # ----------------------------------------------------------------------------------------------------
653 # Final setup
655 self.ui.text_logs.clear()
656 self.slot_logButtonsState(False)
657 self.setProperWindowTitle()
659 # Disable non-supported features
660 features = gCarla.utils.get_supported_features()
662 if "link" not in features:
663 self.ui.cb_transport_link.setEnabled(False)
664 self.ui.cb_transport_link.setVisible(False)
666 # Plugin needs to have timers always running so it receives messages
667 if self.host.isPlugin or self.host.isRemote:
668 self.startTimers()
670 # Qt needs this so it properly creates & resizes the canvas
671 self.ui.tabWidget.blockSignals(True)
672 self.ui.tabWidget.setCurrentIndex(1)
673 self.ui.tabWidget.setCurrentIndex(0)
674 self.ui.tabWidget.blockSignals(False)
676 # Start in patchbay tab if using forced patchbay mode
677 if host.processModeForced and host.processMode == ENGINE_PROCESS_MODE_PATCHBAY:
678 self.ui.tabWidget.setCurrentIndex(1)
680 # Load initial project file if set
681 if not (self.host.isControl or self.host.isPlugin):
682 projectFile = getInitialProjectFile(QApplication.instance())
684 if projectFile:
685 self.loadProjectLater(projectFile)
687 # For NSM we wait for the open message
688 if NSM_URL and host.nsmOK:
689 host.nsm_ready(NSM_CALLBACK_INIT)
690 return
692 if not host.isControl:
693 QTimer.singleShot(0, self.slot_engineStart)
695 # --------------------------------------------------------------------------------------------------------
696 # Manage visibility state, needed for NSM
698 def hideForNSM(self):
699 for pitem in reversed(self.fPluginList):
700 if pitem is None:
701 continue
702 pitem.getWidget().hideCustomUI()
703 self.hide()
705 def showIfNeeded(self):
706 if self.host.nsmOK:
707 self.ui.act_file_quit.setText(self.tr("Hide"))
708 QApplication.instance().setQuitOnLastWindowClosed(False)
709 else:
710 self.show()
712 # --------------------------------------------------------------------------------------------------------
713 # Setup
715 def compactPlugin(self, pluginId):
716 if pluginId > self.fPluginCount:
717 return
719 pitem = self.fPluginList[pluginId]
721 if pitem is None:
722 return
724 pitem.recreateWidget(True)
726 def changePluginColor(self, pluginId, color, colorStr):
727 if pluginId > self.fPluginCount:
728 return
730 pitem = self.fPluginList[pluginId]
732 if pitem is None:
733 return
735 self.host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaColor", colorStr)
736 pitem.recreateWidget(newColor = color)
738 def changePluginSkin(self, pluginId, skin):
739 if pluginId > self.fPluginCount:
740 return
742 pitem = self.fPluginList[pluginId]
744 if pitem is None:
745 return
747 self.host.set_custom_data(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkin", skin)
748 if skin not in ("default","rncbc","presets","mpresets"):
749 pitem.recreateWidget(newSkin = skin, newColor = (255,255,255))
750 else:
751 pitem.recreateWidget(newSkin = skin)
753 def findPluginInPatchbay(self, pluginId):
754 if pluginId > self.fPluginCount:
755 return
757 if self.fExternalPatchbay:
758 self.slot_canvasShowInternal()
760 if not patchcanvas.focusGroupUsingPluginId(pluginId):
761 name = self.host.get_plugin_info(pluginId)['name']
762 if not patchcanvas.focusGroupUsingGroupName(name):
763 return
765 self.ui.tabWidget.setCurrentIndex(1)
767 def switchPlugins(self, pluginIdA, pluginIdB):
768 if pluginIdA == pluginIdB:
769 return
770 if pluginIdA < 0 or pluginIdB < 0:
771 return
772 if pluginIdA >= self.fPluginCount or pluginIdB >= self.fPluginCount:
773 return
775 self.host.switch_plugins(pluginIdA, pluginIdB)
777 itemA = self.fPluginList[pluginIdA]
778 compactA = itemA.isCompacted()
779 guiShownA = itemA.isGuiShown()
781 itemB = self.fPluginList[pluginIdB]
782 compactB = itemB.isCompacted()
783 guiShownB = itemB.isGuiShown()
785 itemA.setPluginId(pluginIdA)
786 itemA.recreateWidget2(compactB, guiShownB)
788 itemB.setPluginId(pluginIdB)
789 itemB.recreateWidget2(compactA, guiShownA)
791 if self.fWithCanvas:
792 self.slot_canvasRefresh()
794 def setLoadRDFsNeeded(self):
795 self.fLadspaRdfNeedsUpdate = True
797 def setProperWindowTitle(self):
798 title = self.fClientName
800 if self.fProjectFilename and not self.host.nsmOK:
801 title += " - %s" % os.path.basename(self.fProjectFilename)
802 if self.fSessionManagerName:
803 title += " (%s)" % self.fSessionManagerName
805 self.setWindowTitle(title)
807 def updateBufferSize(self, newBufferSize):
808 if self.fBufferSize == newBufferSize:
809 return
810 self.fBufferSize = newBufferSize
811 self.ui.cb_buffer_size.clear()
812 self.ui.cb_buffer_size.addItem(str(newBufferSize))
813 self.ui.cb_buffer_size.setCurrentIndex(0)
815 def updateSampleRate(self, newSampleRate):
816 if self.fSampleRate == newSampleRate:
817 return
818 self.fSampleRate = newSampleRate
819 self.ui.cb_sample_rate.clear()
820 self.ui.cb_sample_rate.addItem(str(newSampleRate))
821 self.ui.cb_sample_rate.setCurrentIndex(0)
822 self.refreshTransport(True)
824 # --------------------------------------------------------------------------------------------------------
825 # Files
827 def loadProjectNow(self):
828 if not self.fProjectFilename:
829 return qCritical("ERROR: loading project without filename set")
830 if self.host.nsmOK and not os.path.exists(self.fProjectFilename):
831 return
833 self.projectLoadingStarted()
834 self.fIsProjectLoading = True
836 if not self.host.load_project(self.fProjectFilename):
837 self.fIsProjectLoading = False
838 self.projectLoadingFinished(True)
840 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load project"),
841 self.host.get_last_error(),
842 QMessageBox.Ok, QMessageBox.Ok)
844 def loadProjectLater(self, filename):
845 self.fProjectFilename = QFileInfo(filename).absoluteFilePath()
846 self.setProperWindowTitle()
847 QTimer.singleShot(1, self.slot_loadProjectNow)
849 def saveProjectNow(self):
850 if not self.fProjectFilename:
851 return qCritical("ERROR: saving project without filename set")
853 if not self.host.save_project(self.fProjectFilename):
854 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to save project"),
855 self.host.get_last_error(),
856 QMessageBox.Ok, QMessageBox.Ok)
857 return
859 def projectLoadingStarted(self):
860 self.ui.rack.setEnabled(False)
861 self.ui.graphicsView.setEnabled(False)
863 def projectLoadingFinished(self, refreshCanvas):
864 self.ui.rack.setEnabled(True)
865 self.ui.graphicsView.setEnabled(True)
867 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE or not self.fWithCanvas:
868 return
870 if refreshCanvas and not self.loadExternalCanvasGroupPositionsIfNeeded(self.fProjectFilename):
871 QTimer.singleShot(1, self.slot_canvasRefresh)
873 def loadExternalCanvasGroupPositionsIfNeeded(self, filename):
874 extrafile = filename.rsplit(".",1)[0]+".json"
875 if not os.path.exists(extrafile):
876 return False
878 with open(filename, "r") as fh:
879 if "".join(fh.readlines(90)).find("<CARLA-PROJECT VERSION='2.0'>") < 0:
880 return False
882 with open(extrafile, "r") as fh:
883 try:
884 canvasdata = json.load(fh)['canvas']
885 except:
886 return False
888 print("NOTICE: loading old-style canvas group positions via legacy json file")
889 patchcanvas.restoreGroupPositions(canvasdata)
890 QTimer.singleShot(1, self.slot_canvasRefresh)
891 return True
893 # --------------------------------------------------------------------------------------------------------
894 # Files (menu actions)
896 @pyqtSlot()
897 def slot_fileNew(self):
898 if self.fPluginCount > 0 and QMessageBox.question(self, self.tr("New File"),
899 self.tr("Plugins that are currently loaded will be removed. Are you sure?"),
900 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
901 return
903 self.pluginRemoveAll()
904 self.fProjectFilename = ""
905 self.setProperWindowTitle()
906 self.host.clear_project_filename()
908 @pyqtSlot()
909 def slot_fileOpen(self):
910 fileFilter = self.tr("Carla Project File (*.carxp);;Carla Preset File (*.carxs)")
911 filename, ok = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
913 # FIXME use ok value, test if it works as expected
914 if not filename:
915 return
917 newFile = True
919 if self.fPluginCount > 0:
920 ask = QMessageBox.question(self, self.tr("Question"), self.tr("There are some plugins loaded, do you want to remove them now?"),
921 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
922 newFile = (ask == QMessageBox.Yes)
924 if newFile:
925 self.pluginRemoveAll()
926 self.fProjectFilename = filename
927 self.setProperWindowTitle()
928 self.loadProjectNow()
929 else:
930 filenameOld = self.fProjectFilename
931 self.fProjectFilename = filename
932 self.loadProjectNow()
933 self.fProjectFilename = filenameOld
935 @pyqtSlot()
936 def slot_fileSave(self, saveAs=False):
937 if self.fProjectFilename and not saveAs:
938 return self.saveProjectNow()
940 fileFilter = self.tr("Carla Project File (*.carxp)")
941 filename, ok = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], filter=fileFilter)
943 # FIXME use ok value, test if it works as expected
944 if not filename:
945 return
947 if not filename.lower().endswith(".carxp"):
948 filename += ".carxp"
950 if self.fProjectFilename != filename:
951 self.fProjectFilename = filename
952 self.setProperWindowTitle()
954 self.saveProjectNow()
956 @pyqtSlot()
957 def slot_fileSaveAs(self):
958 self.slot_fileSave(True)
960 @pyqtSlot()
961 def slot_loadProjectNow(self):
962 self.loadProjectNow()
964 # --------------------------------------------------------------------------------------------------------
965 # Engine (menu actions)
967 @pyqtSlot()
968 def slot_engineStart(self):
969 audioDriver = setEngineSettings(self.host, self.fSavedSettings)
971 firstInit = self.fFirstEngineInit
972 self.fFirstEngineInit = False
974 self.ui.text_logs.appendPlainText("======= Starting engine =======")
976 if self.host.engine_init(audioDriver, self.fClientName):
977 if firstInit and not (self.host.isControl or self.host.isPlugin):
978 settings = QSafeSettings()
979 lastBpm = settings.value("LastBPM", 120.0, float)
980 del settings
981 if lastBpm >= 20.0:
982 self.host.transport_bpm(lastBpm)
983 return
985 elif firstInit:
986 self.ui.text_logs.appendPlainText("Failed to start engine on first try, ignored")
987 return
989 audioError = self.host.get_last_error()
991 if audioError:
992 QMessageBox.critical(self, self.tr("Error"),
993 self.tr("Could not connect to Audio backend '%s', possible reasons:\n%s" % (audioDriver, audioError)))
994 else:
995 QMessageBox.critical(self, self.tr("Error"),
996 self.tr("Could not connect to Audio backend '%s'" % audioDriver))
998 @pyqtSlot()
999 def slot_engineStop(self, forced = False):
1000 self.ui.text_logs.appendPlainText("======= Stopping engine =======")
1002 if self.fPluginCount == 0 or not self.host.is_engine_running():
1003 self.engineStopFinal()
1004 return True
1006 if not forced:
1007 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"
1008 "Do you want to do this now?"),
1009 QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
1010 if ask != QMessageBox.Yes:
1011 return False
1013 return self.slot_engineStopTryAgain()
1015 @pyqtSlot()
1016 def slot_engineConfig(self):
1017 engineRunning = self.host.is_engine_running()
1019 if engineRunning:
1020 dialog = RuntimeDriverSettingsW(self.fParentOrSelf, self.host)
1022 else:
1023 if self.host.isPlugin:
1024 driverName = "Plugin"
1025 driverIndex = 0
1026 elif self.host.audioDriverForced:
1027 driverName = self.host.audioDriverForced
1028 driverIndex = 0
1029 else:
1030 settings = QSafeSettings()
1031 driverName = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
1032 for i in range(self.host.get_engine_driver_count()):
1033 if self.host.get_engine_driver_name(i) == driverName:
1034 driverIndex = i
1035 break
1036 else:
1037 driverIndex = -1
1038 del settings
1039 dialog = DriverSettingsW(self.fParentOrSelf, self.host, driverIndex, driverName)
1040 dialog.ui.ico_restart.hide()
1041 dialog.ui.label_restart.hide()
1042 dialog.adjustSize()
1044 if not dialog.exec_():
1045 return
1047 audioDevice, bufferSize, sampleRate = dialog.getValues()
1049 if engineRunning:
1050 self.host.set_engine_buffer_size_and_sample_rate(bufferSize, sampleRate)
1051 else:
1052 self.host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, audioDevice)
1053 self.host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, bufferSize, "")
1054 self.host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, sampleRate, "")
1056 @pyqtSlot()
1057 def slot_engineStopTryAgain(self):
1058 if self.host.is_engine_running() and not self.host.set_engine_about_to_close():
1059 QTimer.singleShot(0, self.slot_engineStopTryAgain)
1060 return False
1062 self.engineStopFinal()
1063 return True
1065 def engineStopFinal(self):
1066 patchcanvas.handleAllPluginsRemoved()
1067 self.killTimers()
1069 if self.fCustomStopAction == self.CUSTOM_ACTION_PROJECT_LOAD:
1070 self.removeAllPlugins()
1071 else:
1072 self.fProjectFilename = ""
1073 self.setProperWindowTitle()
1074 if self.fPluginCount != 0:
1075 self.fCurrentlyRemovingAllPlugins = True
1076 self.projectLoadingStarted()
1078 if self.host.is_engine_running() and not self.host.remove_all_plugins():
1079 self.ui.text_logs.appendPlainText("Failed to remove all plugins, error was:")
1080 self.ui.text_logs.appendPlainText(self.host.get_last_error())
1082 if not self.host.engine_close():
1083 self.ui.text_logs.appendPlainText("Failed to stop engine, error was:")
1084 self.ui.text_logs.appendPlainText(self.host.get_last_error())
1086 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE:
1087 self.close()
1088 elif self.fCustomStopAction == self.CUSTOM_ACTION_PROJECT_LOAD:
1089 self.slot_engineStart()
1090 self.loadProjectNow()
1091 self.host.nsm_ready(NSM_CALLBACK_OPEN)
1093 self.fCustomStopAction = self.CUSTOM_ACTION_NONE
1095 # --------------------------------------------------------------------------------------------------------
1096 # Engine (host callbacks)
1098 @pyqtSlot(int, int, int, int, float, str)
1099 def slot_handleEngineStartedCallback(self, pluginCount, processMode, transportMode, bufferSize, sampleRate, driverName):
1100 self.ui.menu_PluginMacros.setEnabled(True)
1101 self.ui.menu_Canvas.setEnabled(True)
1102 self.ui.w_transport.setEnabled(True)
1104 self.ui.act_canvas_show_internal.blockSignals(True)
1105 self.ui.act_canvas_show_external.blockSignals(True)
1107 if processMode == ENGINE_PROCESS_MODE_PATCHBAY and not self.host.isPlugin:
1108 self.ui.act_canvas_show_internal.setChecked(True)
1109 self.ui.act_canvas_show_internal.setVisible(True)
1110 self.ui.act_canvas_show_external.setChecked(False)
1111 self.ui.act_canvas_show_external.setVisible(True)
1112 self.fExternalPatchbay = False
1113 else:
1114 self.ui.act_canvas_show_internal.setChecked(False)
1115 self.ui.act_canvas_show_internal.setVisible(False)
1116 self.ui.act_canvas_show_external.setChecked(True)
1117 self.ui.act_canvas_show_external.setVisible(False)
1118 self.fExternalPatchbay = not self.host.isPlugin
1120 self.ui.act_canvas_show_internal.blockSignals(False)
1121 self.ui.act_canvas_show_external.blockSignals(False)
1123 if not (self.host.isControl or self.host.isPlugin):
1124 canSave = (self.fProjectFilename and os.path.exists(self.fProjectFilename)) or not self.fSessionManagerName
1125 self.ui.act_file_save.setEnabled(canSave)
1126 self.ui.act_engine_start.setEnabled(False)
1127 self.ui.act_engine_stop.setEnabled(True)
1129 if not self.host.isPlugin:
1130 self.enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED)
1132 if self.host.isPlugin or not self.fSessionManagerName:
1133 self.ui.act_file_open.setEnabled(True)
1134 self.ui.act_file_save_as.setEnabled(True)
1136 self.ui.cb_transport_jack.setChecked(transportMode == ENGINE_TRANSPORT_MODE_JACK)
1137 self.ui.cb_transport_jack.setEnabled(driverName == "JACK" and processMode != ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS)
1139 if self.ui.cb_transport_link.isEnabled():
1140 self.ui.cb_transport_link.setChecked(":link:" in self.host.transportExtra)
1142 self.updateBufferSize(bufferSize)
1143 self.updateSampleRate(int(sampleRate))
1144 self.refreshRuntimeInfo(0.0, 0)
1145 self.startTimers()
1147 self.ui.text_logs.appendPlainText("======= Engine started ========")
1148 self.ui.text_logs.appendPlainText("Carla engine started, details:")
1149 self.ui.text_logs.appendPlainText(" Driver name: %s" % driverName)
1150 self.ui.text_logs.appendPlainText(" Sample rate: %i" % int(sampleRate))
1151 self.ui.text_logs.appendPlainText(" Process mode: %s" % processMode2Str(processMode))
1153 @pyqtSlot()
1154 def slot_handleEngineStoppedCallback(self):
1155 self.ui.text_logs.appendPlainText("======= Engine stopped ========")
1157 if self.fWithCanvas:
1158 patchcanvas.clear()
1160 self.killTimers()
1162 # just in case
1163 self.removeAllPlugins()
1164 self.refreshRuntimeInfo(0.0, 0)
1166 self.ui.menu_PluginMacros.setEnabled(False)
1167 self.ui.menu_Canvas.setEnabled(False)
1168 self.ui.w_transport.setEnabled(False)
1170 if not (self.host.isControl or self.host.isPlugin):
1171 self.ui.act_file_save.setEnabled(False)
1172 self.ui.act_engine_start.setEnabled(True)
1173 self.ui.act_engine_stop.setEnabled(False)
1175 if self.host.isPlugin or not self.fSessionManagerName:
1176 self.ui.act_file_open.setEnabled(False)
1177 self.ui.act_file_save_as.setEnabled(False)
1179 @pyqtSlot(int, str)
1180 def slot_handleTransportModeChangedCallback(self, transportMode, transportExtra):
1181 self.enableTransport(transportMode != ENGINE_TRANSPORT_MODE_DISABLED)
1183 self.ui.cb_transport_jack.setChecked(transportMode == ENGINE_TRANSPORT_MODE_JACK)
1184 self.ui.cb_transport_link.setChecked(":link:" in transportExtra)
1186 @pyqtSlot(int)
1187 def slot_handleBufferSizeChangedCallback(self, newBufferSize):
1188 self.updateBufferSize(newBufferSize)
1190 @pyqtSlot(float)
1191 def slot_handleSampleRateChangedCallback(self, newSampleRate):
1192 self.updateSampleRate(int(newSampleRate))
1194 @pyqtSlot(int, bool, str)
1195 def slot_handleCancelableActionCallback(self, pluginId, started, action):
1196 if self.fCancelableActionBox is not None:
1197 self.fCancelableActionBox.close()
1199 if started:
1200 self.fCancelableActionBox = QMessageBox(self)
1201 self.fCancelableActionBox.setIcon(QMessageBox.Information)
1202 self.fCancelableActionBox.setWindowTitle(self.tr("Action in progress"))
1203 self.fCancelableActionBox.setText(action)
1204 self.fCancelableActionBox.setInformativeText(self.tr("An action is in progress, please wait..."))
1205 self.fCancelableActionBox.setStandardButtons(QMessageBox.Cancel)
1206 self.fCancelableActionBox.setDefaultButton(QMessageBox.Cancel)
1207 self.fCancelableActionBox.buttonClicked.connect(self.slot_canlableActionBoxClicked)
1208 self.fCancelableActionBox.show()
1210 else:
1211 self.fCancelableActionBox = None
1213 @pyqtSlot()
1214 def slot_canlableActionBoxClicked(self):
1215 self.host.cancel_engine_action()
1217 @pyqtSlot()
1218 def slot_handleProjectLoadFinishedCallback(self):
1219 self.fIsProjectLoading = False
1220 self.projectLoadingFinished(False)
1222 # --------------------------------------------------------------------------------------------------------
1223 # Plugins
1225 def removeAllPlugins(self):
1226 self.ui.act_plugin_remove_all.setEnabled(False)
1227 patchcanvas.handleAllPluginsRemoved()
1229 while self.ui.listWidget.takeItem(0):
1230 pass
1232 self.clearSideStuff()
1234 for pitem in self.fPluginList:
1235 if pitem is None:
1236 continue
1238 pitem.close()
1239 del pitem
1241 self.fPluginCount = 0
1242 self.fPluginList = []
1244 # --------------------------------------------------------------------------------------------------------
1245 # Plugins (menu actions)
1247 def showAddPluginDialog(self):
1248 # TODO self.fHasLoadedLv2Plugins
1249 if self.fPluginListDialog is None:
1250 hostSettings = {
1251 'showPluginBridges': self.fSavedSettings[CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES],
1252 'showWineBridges': self.fSavedSettings[CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES],
1253 'useSystemIcons': self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS],
1254 'wineAutoPrefix': self.fSavedSettings[CARLA_KEY_WINE_AUTO_PREFIX],
1255 'wineExecutable': self.fSavedSettings[CARLA_KEY_WINE_EXECUTABLE],
1256 'wineFallbackPrefix': self.fSavedSettings[CARLA_KEY_WINE_FALLBACK_PREFIX],
1258 self.fPluginListDialog = d = gCarla.felib.createPluginListDialog(self.fParentOrSelf, hostSettings)
1260 gCarla.felib.setPluginListDialogPath(d, PLUGIN_LADSPA, self.fSavedSettings[CARLA_KEY_PATHS_LADSPA])
1261 gCarla.felib.setPluginListDialogPath(d, PLUGIN_DSSI, self.fSavedSettings[CARLA_KEY_PATHS_DSSI])
1262 gCarla.felib.setPluginListDialogPath(d, PLUGIN_LV2, self.fSavedSettings[CARLA_KEY_PATHS_LV2])
1263 gCarla.felib.setPluginListDialogPath(d, PLUGIN_VST2, self.fSavedSettings[CARLA_KEY_PATHS_VST2])
1264 gCarla.felib.setPluginListDialogPath(d, PLUGIN_VST3, self.fSavedSettings[CARLA_KEY_PATHS_VST3])
1265 gCarla.felib.setPluginListDialogPath(d, PLUGIN_SF2, self.fSavedSettings[CARLA_KEY_PATHS_SF2])
1266 gCarla.felib.setPluginListDialogPath(d, PLUGIN_SFZ, self.fSavedSettings[CARLA_KEY_PATHS_SFZ])
1267 gCarla.felib.setPluginListDialogPath(d, PLUGIN_JSFX, self.fSavedSettings[CARLA_KEY_PATHS_JSFX])
1268 gCarla.felib.setPluginListDialogPath(d, PLUGIN_CLAP, self.fSavedSettings[CARLA_KEY_PATHS_CLAP])
1270 ret = gCarla.felib.execPluginListDialog(self.fPluginListDialog)
1272 # TODO
1273 #if dialog.fFavoritePluginsChanged:
1274 #self.fFavoritePlugins = dialog.fFavoritePlugins
1276 if not ret:
1277 return
1279 if not self.host.is_engine_running():
1280 QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
1281 return
1283 btype = ret['build']
1284 ptype = ret['type']
1285 filename = ret['filename']
1286 label = ret['label']
1287 uniqueId = ret['uniqueId']
1288 extraPtr = None
1290 return (btype, ptype, filename, label, uniqueId, extraPtr)
1292 def showAddJackAppDialog(self):
1293 ret = gCarla.felib.createAndExecJackAppDialog(self.fParentOrSelf, self.fProjectFilename)
1295 if not ret:
1296 return
1298 if not self.host.is_engine_running():
1299 QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped"))
1300 return
1302 return ret
1304 @pyqtSlot()
1305 def slot_favoritePluginAdd(self):
1306 plugin = self.sender().data()
1308 if plugin is None:
1309 return
1311 if not self.host.add_plugin(plugin['build'], plugin['type'], plugin['filename'], None,
1312 plugin['label'], plugin['uniqueId'], None, PLUGIN_OPTIONS_NULL):
1313 # remove plugin from favorites
1314 try:
1315 self.fFavoritePlugins.remove(plugin)
1316 except ValueError:
1317 pass
1318 else:
1319 settingsDBf = QSafeSettings("falkTX", "CarlaDatabase2")
1320 settingsDBf.setValue("PluginDatabase/Favorites", self.fFavoritePlugins)
1321 settingsDBf.sync()
1322 del settingsDBf
1324 CustomMessageBox(self,
1325 QMessageBox.Critical,
1326 self.tr("Error"),
1327 self.tr("Failed to load plugin"),
1328 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1330 @pyqtSlot()
1331 def slot_showPluginActionsMenu(self):
1332 menu = QMenu(self)
1334 menu.addSection("Plugins")
1335 menu.addAction(self.ui.act_plugin_add)
1337 if len(self.fFavoritePlugins) != 0:
1338 fmenu = QMenu("Add from favorites", self)
1339 for p in self.fFavoritePlugins:
1340 act = fmenu.addAction(p['name'])
1341 act.setData(p)
1342 act.triggered.connect(self.slot_favoritePluginAdd)
1343 menu.addMenu(fmenu)
1345 menu.addAction(self.ui.act_plugin_remove_all)
1347 menu.addSection("All plugins (macros)")
1348 menu.addAction(self.ui.act_plugins_enable)
1349 menu.addAction(self.ui.act_plugins_disable)
1350 menu.addSeparator()
1351 menu.addAction(self.ui.act_plugins_volume100)
1352 menu.addAction(self.ui.act_plugins_mute)
1353 menu.addSeparator()
1354 menu.addAction(self.ui.act_plugins_wet100)
1355 menu.addAction(self.ui.act_plugins_bypass)
1356 menu.addSeparator()
1357 menu.addAction(self.ui.act_plugins_center)
1358 menu.addSeparator()
1359 menu.addAction(self.ui.act_plugins_compact)
1360 menu.addAction(self.ui.act_plugins_expand)
1362 menu.exec_(QCursor.pos())
1364 @pyqtSlot()
1365 def slot_pluginAdd(self):
1366 data = self.showAddPluginDialog()
1368 if data is None:
1369 return
1371 btype, ptype, filename, label, uniqueId, extraPtr = data
1373 if not self.host.add_plugin(btype, ptype, filename, None, label, uniqueId, extraPtr, PLUGIN_OPTIONS_NULL):
1374 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"),
1375 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1377 @pyqtSlot()
1378 def slot_confirmRemoveAll(self):
1379 if self.fPluginCount == 0:
1380 return
1382 if QMessageBox.question(self, self.tr("Remove All"),
1383 self.tr("Are you sure you want to remove all plugins?"),
1384 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No:
1385 return
1387 self.pluginRemoveAll()
1389 def pluginRemoveAll(self):
1390 if self.fPluginCount == 0:
1391 return
1393 self.fCurrentlyRemovingAllPlugins = True
1394 self.projectLoadingStarted()
1396 if not self.host.remove_all_plugins():
1397 self.projectLoadingFinished(True)
1398 self.fCurrentlyRemovingAllPlugins = False
1399 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1400 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1402 @pyqtSlot()
1403 def slot_jackAppAdd(self):
1404 data = self.showAddJackAppDialog()
1406 if data is None:
1407 return
1409 if not data['command']:
1410 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Cannot add jack application"),
1411 self.tr("command is empty"), QMessageBox.Ok, QMessageBox.Ok)
1412 return
1414 if not self.host.add_plugin(BINARY_NATIVE, PLUGIN_JACK,
1415 data['command'], data['name'], data['labelSetup'],
1416 0, None, PLUGIN_OPTIONS_NULL):
1417 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"),
1418 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1420 # --------------------------------------------------------------------------------------------------------
1421 # Plugins (macros)
1423 @pyqtSlot()
1424 def slot_pluginsEnable(self):
1425 if not self.host.is_engine_running():
1426 return
1428 for pitem in self.fPluginList:
1429 if pitem is None:
1430 break
1432 pitem.getWidget().setActive(True, True, True)
1434 @pyqtSlot()
1435 def slot_pluginsDisable(self):
1436 if not self.host.is_engine_running():
1437 return
1439 for pitem in self.fPluginList:
1440 if pitem is None:
1441 break
1443 pitem.getWidget().setActive(False, True, True)
1445 @pyqtSlot()
1446 def slot_pluginsVolume100(self):
1447 if not self.host.is_engine_running():
1448 return
1450 for pitem in self.fPluginList:
1451 if pitem is None:
1452 break
1454 pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 1.0)
1456 @pyqtSlot()
1457 def slot_pluginsMute(self):
1458 if not self.host.is_engine_running():
1459 return
1461 for pitem in self.fPluginList:
1462 if pitem is None:
1463 break
1465 pitem.getWidget().setInternalParameter(PLUGIN_CAN_VOLUME, 0.0)
1467 @pyqtSlot()
1468 def slot_pluginsWet100(self):
1469 if not self.host.is_engine_running():
1470 return
1472 for pitem in self.fPluginList:
1473 if pitem is None:
1474 break
1476 pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 1.0)
1478 @pyqtSlot()
1479 def slot_pluginsBypass(self):
1480 if not self.host.is_engine_running():
1481 return
1483 for pitem in self.fPluginList:
1484 if pitem is None:
1485 break
1487 pitem.getWidget().setInternalParameter(PLUGIN_CAN_DRYWET, 0.0)
1489 @pyqtSlot()
1490 def slot_pluginsCenter(self):
1491 if not self.host.is_engine_running():
1492 return
1494 for pitem in self.fPluginList:
1495 if pitem is None:
1496 break
1498 pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_LEFT, -1.0)
1499 pitem.getWidget().setInternalParameter(PARAMETER_BALANCE_RIGHT, 1.0)
1500 pitem.getWidget().setInternalParameter(PARAMETER_PANNING, 0.0)
1502 @pyqtSlot()
1503 def slot_pluginsCompact(self):
1504 for pitem in self.fPluginList:
1505 if pitem is None:
1506 break
1507 pitem.compact()
1509 @pyqtSlot()
1510 def slot_pluginsExpand(self):
1511 for pitem in self.fPluginList:
1512 if pitem is None:
1513 break
1514 pitem.expand()
1516 # --------------------------------------------------------------------------------------------------------
1517 # Plugins (host callbacks)
1519 @pyqtSlot(int, int, str)
1520 def slot_handlePluginAddedCallback(self, pluginId, pluginType, pluginName):
1521 if pluginId != self.fPluginCount:
1522 print("ERROR: pluginAdded mismatch Id:", pluginId, self.fPluginCount)
1523 pitem = self.getPluginItem(pluginId)
1524 pitem.recreateWidget()
1525 return
1527 pitem = self.ui.listWidget.createItem(pluginId, self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN])
1528 self.fPluginList.append(pitem)
1529 self.fPluginCount += 1
1531 self.ui.act_plugin_remove_all.setEnabled(self.fPluginCount > 0)
1533 if pluginType == PLUGIN_LV2:
1534 self.fHasLoadedLv2Plugins = True
1536 @pyqtSlot(int)
1537 def slot_handlePluginRemovedCallback(self, pluginId):
1538 if self.fWithCanvas:
1539 patchcanvas.handlePluginRemoved(pluginId)
1541 if pluginId in self.fSelectedPlugins:
1542 self.clearSideStuff()
1544 if self.fPluginCount == 0:
1545 return
1547 pitem = self.getPluginItem(pluginId)
1549 self.fPluginCount -= 1
1550 self.fPluginList.pop(pluginId)
1551 self.ui.listWidget.takeItem(pluginId)
1553 if pitem is not None:
1554 pitem.close()
1555 del pitem
1557 if self.fPluginCount == 0:
1558 self.ui.act_plugin_remove_all.setEnabled(False)
1559 if self.fCurrentlyRemovingAllPlugins:
1560 self.fCurrentlyRemovingAllPlugins = False
1561 self.projectLoadingFinished(False)
1562 return
1564 # push all plugins 1 slot back
1565 for i in range(pluginId, self.fPluginCount):
1566 pitem = self.fPluginList[i]
1567 pitem.setPluginId(i)
1569 self.ui.act_plugin_remove_all.setEnabled(True)
1571 # --------------------------------------------------------------------------------------------------------
1572 # Canvas
1574 def clearSideStuff(self):
1575 if self.fWithCanvas:
1576 self.scene.clearSelection()
1578 self.fSelectedPlugins = []
1580 self.ui.keyboard.allNotesOff(False)
1581 self.ui.scrollArea.setEnabled(False)
1583 self.fPeaksCleared = True
1584 self.ui.peak_in.displayMeter(1, 0.0, True)
1585 self.ui.peak_in.displayMeter(2, 0.0, True)
1586 self.ui.peak_out.displayMeter(1, 0.0, True)
1587 self.ui.peak_out.displayMeter(2, 0.0, True)
1589 def setupCanvas(self):
1590 pOptions = patchcanvas.options_t()
1591 pOptions.theme_name = self.fSavedSettings[CARLA_KEY_CANVAS_THEME]
1592 pOptions.auto_hide_groups = self.fSavedSettings[CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS]
1593 pOptions.auto_select_items = self.fSavedSettings[CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS]
1594 pOptions.use_bezier_lines = self.fSavedSettings[CARLA_KEY_CANVAS_USE_BEZIER_LINES]
1595 pOptions.antialiasing = self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING]
1596 pOptions.inline_displays = self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS]
1598 if self.fSavedSettings[CARLA_KEY_CANVAS_FANCY_EYE_CANDY]:
1599 pOptions.eyecandy = patchcanvas.EYECANDY_FULL
1600 elif self.fSavedSettings[CARLA_KEY_CANVAS_EYE_CANDY]:
1601 pOptions.eyecandy = patchcanvas.EYECANDY_SMALL
1602 else:
1603 pOptions.eyecandy = patchcanvas.EYECANDY_NONE
1605 pFeatures = patchcanvas.features_t()
1606 pFeatures.group_info = False
1607 pFeatures.group_rename = False
1608 pFeatures.port_info = False
1609 pFeatures.port_rename = False
1610 pFeatures.handle_group_pos = False
1612 patchcanvas.setOptions(pOptions)
1613 patchcanvas.setFeatures(pFeatures)
1614 patchcanvas.init("Carla2", self.scene, canvasCallback, False)
1616 tryCanvasSize = self.fSavedSettings[CARLA_KEY_CANVAS_SIZE].split("x")
1618 if len(tryCanvasSize) == 2 and tryCanvasSize[0].isdigit() and tryCanvasSize[1].isdigit():
1619 self.fCanvasWidth = int(tryCanvasSize[0])
1620 self.fCanvasHeight = int(tryCanvasSize[1])
1621 else:
1622 self.fCanvasWidth = CARLA_DEFAULT_CANVAS_SIZE_WIDTH
1623 self.fCanvasHeight = CARLA_DEFAULT_CANVAS_SIZE_HEIGHT
1625 patchcanvas.setCanvasSize(0, 0, self.fCanvasWidth, self.fCanvasHeight)
1626 patchcanvas.setInitialPos(self.fCanvasWidth / 2, self.fCanvasHeight / 2)
1627 self.ui.graphicsView.setSceneRect(0, 0, self.fCanvasWidth, self.fCanvasHeight)
1629 self.ui.miniCanvasPreview.setViewTheme(patchcanvas.canvas.theme.canvas_bg, patchcanvas.canvas.theme.rubberband_brush, patchcanvas.canvas.theme.rubberband_pen.color())
1630 self.ui.miniCanvasPreview.init(self.scene, self.fCanvasWidth, self.fCanvasHeight, self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING])
1632 if self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] != patchcanvas.ANTIALIASING_NONE:
1633 self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, True)
1635 fullAA = self.fSavedSettings[CARLA_KEY_CANVAS_ANTIALIASING] == patchcanvas.ANTIALIASING_FULL
1636 self.ui.graphicsView.setRenderHint(QPainter.SmoothPixmapTransform, fullAA)
1637 self.ui.graphicsView.setRenderHint(QPainter.TextAntialiasing, fullAA)
1639 if self.fSavedSettings[CARLA_KEY_CANVAS_USE_OPENGL] and hasGL and QPainter.HighQualityAntialiasing is not None:
1640 self.ui.graphicsView.setRenderHint(QPainter.HighQualityAntialiasing, self.fSavedSettings[CARLA_KEY_CANVAS_HQ_ANTIALIASING])
1642 else:
1643 self.ui.graphicsView.setRenderHint(QPainter.Antialiasing, False)
1645 if self.fSavedSettings[CARLA_KEY_CANVAS_FULL_REPAINTS]:
1646 self.ui.graphicsView.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
1647 else:
1648 self.ui.graphicsView.setViewportUpdateMode(QGraphicsView.MinimalViewportUpdate)
1650 def updateCanvasInitialPos(self):
1651 x = self.ui.graphicsView.horizontalScrollBar().value() + self.width()/4
1652 y = self.ui.graphicsView.verticalScrollBar().value() + self.height()/4
1653 patchcanvas.setInitialPos(x, y)
1655 def updateMiniCanvasLater(self):
1656 QTimer.singleShot(self.fMiniCanvasUpdateTimeout, self.ui.miniCanvasPreview.update)
1658 # --------------------------------------------------------------------------------------------------------
1659 # Canvas (menu actions)
1661 @pyqtSlot()
1662 def slot_canvasShowInternal(self):
1663 self.fExternalPatchbay = False
1664 self.ui.act_canvas_show_internal.blockSignals(True)
1665 self.ui.act_canvas_show_external.blockSignals(True)
1666 self.ui.act_canvas_show_internal.setChecked(True)
1667 self.ui.act_canvas_show_external.setChecked(False)
1668 self.ui.act_canvas_show_internal.blockSignals(False)
1669 self.ui.act_canvas_show_external.blockSignals(False)
1670 self.slot_canvasRefresh()
1672 @pyqtSlot()
1673 def slot_canvasShowExternal(self):
1674 self.fExternalPatchbay = True
1675 self.ui.act_canvas_show_internal.blockSignals(True)
1676 self.ui.act_canvas_show_external.blockSignals(True)
1677 self.ui.act_canvas_show_internal.setChecked(False)
1678 self.ui.act_canvas_show_external.setChecked(True)
1679 self.ui.act_canvas_show_internal.blockSignals(False)
1680 self.ui.act_canvas_show_external.blockSignals(False)
1681 self.slot_canvasRefresh()
1683 @pyqtSlot()
1684 def slot_canvasArrange(self):
1685 patchcanvas.arrange()
1687 @pyqtSlot()
1688 def slot_canvasRefresh(self):
1689 patchcanvas.clear()
1691 if self.host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and self.host.isPlugin:
1692 return
1694 if self.host.is_engine_running():
1695 self.host.patchbay_refresh(self.fExternalPatchbay)
1697 self.updateMiniCanvasLater()
1699 @pyqtSlot()
1700 def slot_canvasZoomFit(self):
1701 self.scene.zoom_fit()
1703 @pyqtSlot()
1704 def slot_canvasZoomIn(self):
1705 self.scene.zoom_in()
1707 @pyqtSlot()
1708 def slot_canvasZoomOut(self):
1709 self.scene.zoom_out()
1711 @pyqtSlot()
1712 def slot_canvasZoomReset(self):
1713 self.scene.zoom_reset()
1715 def _canvasImageRender(self, zoom = 1.0):
1716 image = QImage(self.scene.width()*zoom, self.scene.height()*zoom, QImage.Format_RGB32)
1717 painter = QPainter(image)
1718 painter.save()
1719 painter.setRenderHints(painter.renderHints() | QPainter.Antialiasing | QPainter.TextAntialiasing)
1720 self.scene.clearSelection()
1721 self.scene.render(painter)
1722 painter.restore()
1723 del painter
1724 return image
1726 def _canvasImageWrite(self, iw: QImageWriter, imgFormat: bytes, image: QImage):
1727 iw.setFormat(imgFormat)
1728 iw.setCompression(-1)
1729 if QT_VERSION >= 0x50500:
1730 iw.setOptimizedWrite(True)
1731 iw.write(image)
1733 @pyqtSlot()
1734 def slot_canvasSaveImage(self):
1735 if self.fProjectFilename:
1736 dir = QFileInfo(self.fProjectFilename).absoluteDir().absolutePath()
1737 else:
1738 dir = self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER]
1740 fileDialog = QFileDialog(self)
1741 fileDialog.setAcceptMode(QFileDialog.AcceptSave)
1742 fileDialog.setDirectory(dir)
1743 fileDialog.setFileMode(QFileDialog.AnyFile)
1744 fileDialog.setMimeTypeFilters(("image/png", "image/jpeg"))
1745 fileDialog.setNameFilter(self.tr("Images (*.png *.jpg)"))
1746 fileDialog.setOptions(QFileDialog.DontUseCustomDirectoryIcons)
1747 fileDialog.setWindowTitle(self.tr("Save Image"))
1749 ok = fileDialog.exec_()
1751 if not ok:
1752 return
1754 newPath = fileDialog.selectedFiles()
1756 if len(newPath) != 1:
1757 return
1759 newPath = newPath[0]
1761 if QT_VERSION >= 0x50900:
1762 if fileDialog.selectedMimeTypeFilter() == "image/jpeg":
1763 imgFormat = b"JPG"
1764 else:
1765 imgFormat = b"PNG"
1766 else:
1767 if newPath.lower().endswith((".jpg", ".jpeg")):
1768 imgFormat = b"JPG"
1769 else:
1770 imgFormat = b"PNG"
1772 sender = self.sender()
1773 if sender == self.ui.act_canvas_save_image_2x:
1774 zoom = 2.0
1775 elif sender == self.ui.act_canvas_save_image_4x:
1776 zoom = 4.0
1777 else:
1778 zoom = 1.0
1780 image = self._canvasImageRender(zoom)
1781 iw = QImageWriter(newPath)
1782 self._canvasImageWrite(iw, imgFormat, image)
1784 @pyqtSlot()
1785 def slot_canvasCopyToClipboard(self):
1786 buffer = QBuffer()
1787 buffer.open(QIODevice.WriteOnly)
1789 image = self._canvasImageRender()
1790 iw = QImageWriter(buffer, b"PNG")
1791 self._canvasImageWrite(iw, b"PNG", image)
1793 buffer.close()
1795 mimeData = QMimeData()
1796 mimeData.setData("image/png", buffer.buffer());
1798 QApplication.clipboard().setMimeData(mimeData)
1800 # --------------------------------------------------------------------------------------------------------
1801 # Canvas (canvas callbacks)
1803 @pyqtSlot()
1804 def slot_canvasSelectionChanged(self):
1805 self.updateMiniCanvasLater()
1807 @pyqtSlot(float)
1808 def slot_canvasScaleChanged(self, scale):
1809 self.ui.miniCanvasPreview.setViewScale(scale)
1811 @pyqtSlot(list)
1812 def slot_canvasPluginSelected(self, pluginList):
1813 self.ui.keyboard.allNotesOff(False)
1814 self.ui.scrollArea.setEnabled(len(pluginList) != 0) # and self.fPluginCount > 0
1815 self.fSelectedPlugins = pluginList
1817 # --------------------------------------------------------------------------------------------------------
1818 # Canvas (host callbacks)
1820 @pyqtSlot(int, int, int, str)
1821 def slot_handlePatchbayClientAddedCallback(self, clientId, clientIcon, pluginId, clientName):
1822 pcSplit = patchcanvas.SPLIT_UNDEF
1823 pcIcon = patchcanvas.ICON_APPLICATION
1825 if clientIcon == PATCHBAY_ICON_PLUGIN:
1826 pcIcon = patchcanvas.ICON_PLUGIN
1827 if clientIcon == PATCHBAY_ICON_HARDWARE:
1828 pcIcon = patchcanvas.ICON_HARDWARE
1829 elif clientIcon == PATCHBAY_ICON_CARLA:
1830 pass
1831 elif clientIcon == PATCHBAY_ICON_DISTRHO:
1832 pcIcon = patchcanvas.ICON_DISTRHO
1833 elif clientIcon == PATCHBAY_ICON_FILE:
1834 pcIcon = patchcanvas.ICON_FILE
1836 patchcanvas.addGroup(clientId, clientName, pcSplit, pcIcon)
1838 self.updateMiniCanvasLater()
1840 if pluginId < 0:
1841 return
1842 if pluginId >= self.fPluginCount and pluginId != MAIN_CARLA_PLUGIN_ID:
1843 print("Error mapping plugin to canvas client:", clientName)
1844 return
1846 if pluginId == MAIN_CARLA_PLUGIN_ID:
1847 hasCustomUI = False
1848 hasInlineDisplay = False
1849 else:
1850 hints = self.host.get_plugin_info(pluginId)['hints']
1851 hasCustomUI = bool(hints & PLUGIN_HAS_CUSTOM_UI)
1852 hasInlineDisplay = bool(hints & PLUGIN_HAS_INLINE_DISPLAY)
1854 patchcanvas.setGroupAsPlugin(clientId, pluginId, hasCustomUI, hasInlineDisplay)
1856 @pyqtSlot(int)
1857 def slot_handlePatchbayClientRemovedCallback(self, clientId):
1858 patchcanvas.removeGroup(clientId)
1859 self.updateMiniCanvasLater()
1861 @pyqtSlot(int, str)
1862 def slot_handlePatchbayClientRenamedCallback(self, clientId, newClientName):
1863 patchcanvas.renameGroup(clientId, newClientName)
1864 self.updateMiniCanvasLater()
1866 @pyqtSlot(int, int, int)
1867 def slot_handlePatchbayClientDataChangedCallback(self, clientId, clientIcon, pluginId):
1868 pcIcon = patchcanvas.ICON_APPLICATION
1870 if clientIcon == PATCHBAY_ICON_PLUGIN:
1871 pcIcon = patchcanvas.ICON_PLUGIN
1872 if clientIcon == PATCHBAY_ICON_HARDWARE:
1873 pcIcon = patchcanvas.ICON_HARDWARE
1874 elif clientIcon == PATCHBAY_ICON_CARLA:
1875 pass
1876 elif clientIcon == PATCHBAY_ICON_DISTRHO:
1877 pcIcon = patchcanvas.ICON_DISTRHO
1878 elif clientIcon == PATCHBAY_ICON_FILE:
1879 pcIcon = patchcanvas.ICON_FILE
1881 patchcanvas.setGroupIcon(clientId, pcIcon)
1882 self.updateMiniCanvasLater()
1884 if pluginId < 0:
1885 return
1886 if pluginId >= self.fPluginCount and pluginId != MAIN_CARLA_PLUGIN_ID:
1887 print("sorry, can't map this plugin to canvas client", pluginId, self.fPluginCount)
1888 return
1890 if pluginId == MAIN_CARLA_PLUGIN_ID:
1891 hasCustomUI = False
1892 hasInlineDisplay = False
1893 else:
1894 hints = self.host.get_plugin_info(pluginId)['hints']
1895 hasCustomUI = bool(hints & PLUGIN_HAS_CUSTOM_UI)
1896 hasInlineDisplay = bool(hints & PLUGIN_HAS_INLINE_DISPLAY)
1898 patchcanvas.setGroupAsPlugin(clientId, pluginId, hasCustomUI, hasInlineDisplay)
1900 @pyqtSlot(int, int, int, int, int)
1901 def slot_handlePatchbayClientPositionChangedCallback(self, clientId, x1, y1, x2, y2):
1902 if (x1 != 0 and x2 != 0) or (y1 != 0 and y2 != 0):
1903 patchcanvas.splitGroup(clientId)
1904 else:
1905 patchcanvas.joinGroup(clientId)
1906 patchcanvas.setGroupPosFull(clientId, x1, y1, x2, y2)
1907 self.updateMiniCanvasLater()
1909 @pyqtSlot(int, int, int, int, str)
1910 def slot_handlePatchbayPortAddedCallback(self, clientId, portId, portFlags, portGroupId, portName):
1911 if portFlags & PATCHBAY_PORT_IS_INPUT:
1912 portMode = patchcanvas.PORT_MODE_INPUT
1913 else:
1914 portMode = patchcanvas.PORT_MODE_OUTPUT
1916 if portFlags & PATCHBAY_PORT_TYPE_AUDIO:
1917 portType = patchcanvas.PORT_TYPE_AUDIO_JACK
1918 isAlternate = False
1919 elif portFlags & PATCHBAY_PORT_TYPE_CV:
1920 portType = patchcanvas.PORT_TYPE_PARAMETER
1921 isAlternate = False
1922 elif portFlags & PATCHBAY_PORT_TYPE_MIDI:
1923 portType = patchcanvas.PORT_TYPE_MIDI_JACK
1924 isAlternate = False
1925 else:
1926 portType = patchcanvas.PORT_TYPE_NULL
1927 isAlternate = False
1929 patchcanvas.addPort(clientId, portId, portName, portMode, portType, isAlternate)
1930 self.updateMiniCanvasLater()
1932 @pyqtSlot(int, int)
1933 def slot_handlePatchbayPortRemovedCallback(self, groupId, portId):
1934 patchcanvas.removePort(groupId, portId)
1935 self.updateMiniCanvasLater()
1937 @pyqtSlot(int, int, int, int, str)
1938 def slot_handlePatchbayPortChangedCallback(self, groupId, portId, portFlags, portGroupId, newPortName):
1939 patchcanvas.renamePort(groupId, portId, newPortName)
1940 self.updateMiniCanvasLater()
1942 @pyqtSlot(int, int, int, str)
1943 def slot_handlePatchbayPortGroupAddedCallback(self, groupId, portId, portGroupId, newPortName):
1944 # TODO
1945 pass
1947 @pyqtSlot(int, int)
1948 def slot_handlePatchbayPortGroupRemovedCallback(self, groupId, portId):
1949 # TODO
1950 pass
1952 @pyqtSlot(int, int, int, str)
1953 def slot_handlePatchbayPortGroupChangedCallback(self, groupId, portId, portGroupId, newPortName):
1954 # TODO
1955 pass
1957 @pyqtSlot(int, int, int, int, int)
1958 def slot_handlePatchbayConnectionAddedCallback(self, connectionId, groupOutId, portOutId, groupInId, portInId):
1959 patchcanvas.connectPorts(connectionId, groupOutId, portOutId, groupInId, portInId)
1960 self.updateMiniCanvasLater()
1962 @pyqtSlot(int, int, int)
1963 def slot_handlePatchbayConnectionRemovedCallback(self, connectionId, portOutId, portInId):
1964 patchcanvas.disconnectPorts(connectionId)
1965 self.updateMiniCanvasLater()
1967 # --------------------------------------------------------------------------------------------------------
1968 # Settings
1970 def saveSettings(self):
1971 settings = QSafeSettings()
1973 settings.setValue("Geometry", self.saveGeometry())
1974 settings.setValue("ShowToolbar", self.ui.toolBar.isEnabled())
1975 settings.setValue("ShowSidePanel", self.ui.dockWidget.isEnabled())
1977 diskFolders = []
1979 for i in range(self.ui.cb_disk.count()):
1980 diskFolders.append(self.ui.cb_disk.itemData(i))
1982 settings.setValue("DiskFolders", diskFolders)
1983 settings.setValue("LastBPM", self.fLastTransportBPM)
1985 settings.setValue("ShowMeters", self.ui.act_settings_show_meters.isChecked())
1986 settings.setValue("ShowKeyboard", self.ui.act_settings_show_keyboard.isChecked())
1987 settings.setValue("HorizontalScrollBarValue", self.ui.graphicsView.horizontalScrollBar().value())
1988 settings.setValue("VerticalScrollBarValue", self.ui.graphicsView.verticalScrollBar().value())
1990 settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_MODE, self.host.transportMode)
1991 settings.setValue(CARLA_KEY_ENGINE_TRANSPORT_EXTRA, self.host.transportExtra)
1993 return settings
1995 def loadSettings(self, firstTime):
1996 settings = QSafeSettings()
1998 if self.fPluginListDialog is not None:
1999 gCarla.felib.destroyPluginListDialog(self.fPluginListDialog)
2000 self.fPluginListDialog = None
2002 if firstTime:
2003 geometry = settings.value("Geometry", QByteArray(), QByteArray)
2004 if not geometry.isNull():
2005 self.restoreGeometry(geometry)
2007 showToolbar = settings.value("ShowToolbar", True, bool)
2008 self.ui.act_settings_show_toolbar.setChecked(showToolbar)
2009 self.ui.toolBar.blockSignals(True)
2010 self.ui.toolBar.setEnabled(showToolbar)
2011 self.ui.toolBar.setVisible(showToolbar)
2012 self.ui.toolBar.blockSignals(False)
2014 #if settings.contains("SplitterState"):
2015 #self.ui.splitter.restoreState(settings.value("SplitterState", b""))
2016 #else:
2017 #self.ui.splitter.setSizes([210, 99999])
2019 showSidePanel = settings.value("ShowSidePanel", True, bool)
2020 self.ui.act_settings_show_side_panel.setChecked(showSidePanel)
2021 self.slot_showSidePanel(showSidePanel)
2023 diskFolders = settings.value("DiskFolders", [HOME], list)
2025 self.ui.cb_disk.setItemData(0, HOME)
2027 for i in range(len(diskFolders)):
2028 if i == 0: continue
2029 folder = diskFolders[i]
2030 self.ui.cb_disk.addItem(os.path.basename(folder), folder)
2032 #if MACOS and not settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, bool):
2033 # self.setUnifiedTitleAndToolBarOnMac(True)
2035 showMeters = settings.value("ShowMeters", True, bool)
2036 self.ui.act_settings_show_meters.setChecked(showMeters)
2037 self.ui.peak_in.setVisible(showMeters)
2038 self.ui.peak_out.setVisible(showMeters)
2040 showKeyboard = settings.value("ShowKeyboard", True, bool)
2041 self.ui.act_settings_show_keyboard.setChecked(showKeyboard)
2042 self.ui.scrollArea.setVisible(showKeyboard)
2044 settingsDBf = QSafeSettings("falkTX", "CarlaDatabase2")
2045 self.fFavoritePlugins = settingsDBf.value("PluginDatabase/Favorites", [], list)
2047 QTimer.singleShot(100, self.slot_restoreCanvasScrollbarValues)
2049 # TODO - complete this
2050 oldSettings = self.fSavedSettings
2052 if self.host.audioDriverForced is not None:
2053 audioDriver = self.host.audioDriverForced
2054 else:
2055 audioDriver = settings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
2057 audioDriverPrefix = CARLA_KEY_ENGINE_DRIVER_PREFIX + audioDriver
2059 self.fSavedSettings = {
2060 CARLA_KEY_MAIN_PROJECT_FOLDER: settings.value(CARLA_KEY_MAIN_PROJECT_FOLDER, CARLA_DEFAULT_MAIN_PROJECT_FOLDER, str),
2061 CARLA_KEY_MAIN_CONFIRM_EXIT: settings.value(CARLA_KEY_MAIN_CONFIRM_EXIT, CARLA_DEFAULT_MAIN_CONFIRM_EXIT, bool),
2062 CARLA_KEY_MAIN_CLASSIC_SKIN: settings.value(CARLA_KEY_MAIN_CLASSIC_SKIN, CARLA_DEFAULT_MAIN_CLASSIC_SKIN, bool),
2063 CARLA_KEY_MAIN_REFRESH_INTERVAL: settings.value(CARLA_KEY_MAIN_REFRESH_INTERVAL, CARLA_DEFAULT_MAIN_REFRESH_INTERVAL, int),
2064 CARLA_KEY_MAIN_SYSTEM_ICONS: settings.value(CARLA_KEY_MAIN_SYSTEM_ICONS, CARLA_DEFAULT_MAIN_SYSTEM_ICONS, bool),
2065 CARLA_KEY_MAIN_EXPERIMENTAL: settings.value(CARLA_KEY_MAIN_EXPERIMENTAL, CARLA_DEFAULT_MAIN_EXPERIMENTAL, bool),
2066 CARLA_KEY_CANVAS_THEME: settings.value(CARLA_KEY_CANVAS_THEME, CARLA_DEFAULT_CANVAS_THEME, str),
2067 CARLA_KEY_CANVAS_SIZE: settings.value(CARLA_KEY_CANVAS_SIZE, CARLA_DEFAULT_CANVAS_SIZE, str),
2068 CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS: settings.value(CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS, CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS, bool),
2069 CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS: settings.value(CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS, CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS, bool),
2070 CARLA_KEY_CANVAS_USE_BEZIER_LINES: settings.value(CARLA_KEY_CANVAS_USE_BEZIER_LINES, CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES, bool),
2071 CARLA_KEY_CANVAS_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_EYE_CANDY, CARLA_DEFAULT_CANVAS_EYE_CANDY, bool),
2072 CARLA_KEY_CANVAS_FANCY_EYE_CANDY: settings.value(CARLA_KEY_CANVAS_FANCY_EYE_CANDY, CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY, bool),
2073 CARLA_KEY_CANVAS_USE_OPENGL: settings.value(CARLA_KEY_CANVAS_USE_OPENGL, CARLA_DEFAULT_CANVAS_USE_OPENGL, bool),
2074 CARLA_KEY_CANVAS_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_ANTIALIASING, CARLA_DEFAULT_CANVAS_ANTIALIASING, int),
2075 CARLA_KEY_CANVAS_HQ_ANTIALIASING: settings.value(CARLA_KEY_CANVAS_HQ_ANTIALIASING, CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING, bool),
2076 CARLA_KEY_CANVAS_FULL_REPAINTS: settings.value(CARLA_KEY_CANVAS_FULL_REPAINTS, CARLA_DEFAULT_CANVAS_FULL_REPAINTS, bool),
2077 CARLA_KEY_CUSTOM_PAINTING: (settings.value(CARLA_KEY_MAIN_USE_PRO_THEME, True, bool) and
2078 settings.value(CARLA_KEY_MAIN_PRO_THEME_COLOR, "Black", str).lower() == "black"),
2080 # engine
2081 CARLA_KEY_ENGINE_AUDIO_DRIVER: audioDriver,
2082 CARLA_KEY_ENGINE_AUDIO_DEVICE: settings.value(audioDriverPrefix+"/Device", "", str),
2083 CARLA_KEY_ENGINE_BUFFER_SIZE: settings.value(audioDriverPrefix+"/BufferSize", CARLA_DEFAULT_AUDIO_BUFFER_SIZE, int),
2084 CARLA_KEY_ENGINE_SAMPLE_RATE: settings.value(audioDriverPrefix+"/SampleRate", CARLA_DEFAULT_AUDIO_SAMPLE_RATE, int),
2085 CARLA_KEY_ENGINE_TRIPLE_BUFFER: settings.value(audioDriverPrefix+"/TripleBuffer", CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER, bool),
2087 # file paths
2088 CARLA_KEY_PATHS_AUDIO: splitter.join(settings.value(CARLA_KEY_PATHS_AUDIO, CARLA_DEFAULT_FILE_PATH_AUDIO, list)),
2089 CARLA_KEY_PATHS_MIDI: splitter.join(settings.value(CARLA_KEY_PATHS_MIDI, CARLA_DEFAULT_FILE_PATH_MIDI, list)),
2091 # plugin paths
2092 CARLA_KEY_PATHS_LADSPA: splitter.join(settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list)),
2093 CARLA_KEY_PATHS_DSSI: splitter.join(settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list)),
2094 CARLA_KEY_PATHS_LV2: splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)),
2095 CARLA_KEY_PATHS_VST2: splitter.join(settings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list)),
2096 CARLA_KEY_PATHS_VST3: splitter.join(settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)),
2097 CARLA_KEY_PATHS_SF2: splitter.join(settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)),
2098 CARLA_KEY_PATHS_SFZ: splitter.join(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)),
2099 CARLA_KEY_PATHS_JSFX: splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)),
2100 CARLA_KEY_PATHS_CLAP: splitter.join(settings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list)),
2102 # osc
2103 CARLA_KEY_OSC_ENABLED: settings.value(CARLA_KEY_OSC_ENABLED, CARLA_DEFAULT_OSC_ENABLED, bool),
2104 CARLA_KEY_OSC_TCP_PORT_ENABLED: settings.value(CARLA_KEY_OSC_TCP_PORT_ENABLED, CARLA_DEFAULT_OSC_TCP_PORT_ENABLED, bool),
2105 CARLA_KEY_OSC_TCP_PORT_RANDOM: settings.value(CARLA_KEY_OSC_TCP_PORT_RANDOM, CARLA_DEFAULT_OSC_TCP_PORT_RANDOM, bool),
2106 CARLA_KEY_OSC_TCP_PORT_NUMBER: settings.value(CARLA_KEY_OSC_TCP_PORT_NUMBER, CARLA_DEFAULT_OSC_TCP_PORT_NUMBER, int),
2107 CARLA_KEY_OSC_UDP_PORT_ENABLED: settings.value(CARLA_KEY_OSC_UDP_PORT_ENABLED, CARLA_DEFAULT_OSC_UDP_PORT_ENABLED, bool),
2108 CARLA_KEY_OSC_UDP_PORT_RANDOM: settings.value(CARLA_KEY_OSC_UDP_PORT_RANDOM, CARLA_DEFAULT_OSC_UDP_PORT_RANDOM, bool),
2109 CARLA_KEY_OSC_UDP_PORT_NUMBER: settings.value(CARLA_KEY_OSC_UDP_PORT_NUMBER, CARLA_DEFAULT_OSC_UDP_PORT_NUMBER, int),
2111 # wine
2112 CARLA_KEY_WINE_EXECUTABLE: settings.value(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE, str),
2113 CARLA_KEY_WINE_AUTO_PREFIX: settings.value(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX, bool),
2114 CARLA_KEY_WINE_FALLBACK_PREFIX: settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str),
2115 CARLA_KEY_WINE_RT_PRIO_ENABLED: settings.value(CARLA_KEY_WINE_RT_PRIO_ENABLED, CARLA_DEFAULT_WINE_RT_PRIO_ENABLED, bool),
2116 CARLA_KEY_WINE_BASE_RT_PRIO: settings.value(CARLA_KEY_WINE_BASE_RT_PRIO, CARLA_DEFAULT_WINE_BASE_RT_PRIO, int),
2117 CARLA_KEY_WINE_SERVER_RT_PRIO: settings.value(CARLA_KEY_WINE_SERVER_RT_PRIO, CARLA_DEFAULT_WINE_SERVER_RT_PRIO, int),
2119 # experimental switches
2120 CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES:
2121 settings.value(CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES, bool),
2122 CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES:
2123 settings.value(CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES, bool),
2126 if not self.host.isControl:
2127 self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS] = settings.value(CARLA_KEY_CANVAS_INLINE_DISPLAYS, CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS, bool)
2128 else:
2129 self.fSavedSettings[CARLA_KEY_CANVAS_INLINE_DISPLAYS] = False
2131 settings2 = QSafeSettings("falkTX", "Carla2")
2133 if self.host.experimental:
2134 visible = settings2.value(CARLA_KEY_EXPERIMENTAL_JACK_APPS, CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS, bool)
2135 self.ui.act_plugin_add_jack.setVisible(visible)
2136 else:
2137 self.ui.act_plugin_add_jack.setVisible(False)
2139 self.fMiniCanvasUpdateTimeout = 1000 if self.fSavedSettings[CARLA_KEY_CANVAS_FANCY_EYE_CANDY] else 0
2141 setEngineSettings(self.host, self.fSavedSettings)
2142 self.restartTimersIfNeeded()
2144 if oldSettings.get(CARLA_KEY_MAIN_CLASSIC_SKIN, None) not in (self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN], None):
2145 newSkin = "classic" if self.fSavedSettings[CARLA_KEY_MAIN_CLASSIC_SKIN] else None
2147 for pitem in self.fPluginList:
2148 if pitem is None:
2149 continue
2150 pitem.recreateWidget(newSkin = newSkin)
2152 return settings
2154 # --------------------------------------------------------------------------------------------------------
2155 # Settings (helpers)
2157 def enableTransport(self, enabled):
2158 self.ui.group_transport_controls.setEnabled(enabled)
2159 self.ui.group_transport_settings.setEnabled(enabled)
2161 @pyqtSlot()
2162 def slot_restoreCanvasScrollbarValues(self):
2163 settings = QSafeSettings()
2164 horiz = settings.value("HorizontalScrollBarValue", int(self.ui.graphicsView.horizontalScrollBar().maximum()/2), int)
2165 vertc = settings.value("VerticalScrollBarValue", int(self.ui.graphicsView.verticalScrollBar().maximum()/2), int)
2166 self.ui.graphicsView.horizontalScrollBar().setValue(horiz)
2167 self.ui.graphicsView.verticalScrollBar().setValue(vertc)
2169 # --------------------------------------------------------------------------------------------------------
2170 # Settings (menu actions)
2172 @pyqtSlot(bool)
2173 def slot_showSidePanel(self, yesNo):
2174 self.ui.dockWidget.setEnabled(yesNo)
2175 self.ui.dockWidget.setVisible(yesNo)
2177 @pyqtSlot(bool)
2178 def slot_showToolbar(self, yesNo):
2179 self.ui.toolBar.blockSignals(True)
2180 self.ui.toolBar.setEnabled(yesNo)
2181 self.ui.toolBar.setVisible(yesNo)
2182 self.ui.toolBar.blockSignals(False)
2184 @pyqtSlot(bool)
2185 def slot_showCanvasMeters(self, yesNo):
2186 self.ui.peak_in.setVisible(yesNo)
2187 self.ui.peak_out.setVisible(yesNo)
2188 QTimer.singleShot(0, self.slot_miniCanvasCheckAll)
2190 @pyqtSlot(bool)
2191 def slot_showCanvasKeyboard(self, yesNo):
2192 self.ui.scrollArea.setVisible(yesNo)
2193 QTimer.singleShot(0, self.slot_miniCanvasCheckAll)
2195 @pyqtSlot()
2196 def slot_configureCarla(self):
2197 dialog = CarlaSettingsW(self.fParentOrSelf, self.host, True, hasGL)
2198 if not dialog.exec_():
2199 return
2201 self.loadSettings(False)
2203 if self.fWithCanvas:
2204 patchcanvas.clear()
2205 self.setupCanvas()
2206 self.slot_miniCanvasCheckAll()
2208 if self.host.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK and self.host.isPlugin:
2209 pass
2210 elif self.host.is_engine_running():
2211 self.host.patchbay_refresh(self.fExternalPatchbay)
2213 # --------------------------------------------------------------------------------------------------------
2214 # About (menu actions)
2216 @pyqtSlot()
2217 def slot_aboutCarla(self):
2218 CarlaAboutW(self.fParentOrSelf, self.host).exec_()
2220 @pyqtSlot()
2221 def slot_aboutQt(self):
2222 QApplication.instance().aboutQt()
2224 # --------------------------------------------------------------------------------------------------------
2225 # Disk (menu actions)
2227 @pyqtSlot(int)
2228 def slot_diskFolderChanged(self, index):
2229 if index < 0:
2230 return
2231 elif index == 0:
2232 filename = HOME
2233 self.ui.b_disk_remove.setEnabled(False)
2234 else:
2235 filename = self.ui.cb_disk.itemData(index)
2236 self.ui.b_disk_remove.setEnabled(True)
2238 self.fDirModel.setRootPath(filename)
2239 self.ui.fileTreeView.setRootIndex(self.fDirModel.index(filename))
2241 @pyqtSlot()
2242 def slot_diskFolderAdd(self):
2243 newPath = QFileDialog.getExistingDirectory(self, self.tr("New Folder"), "", QFileDialog.ShowDirsOnly)
2245 if newPath:
2246 if newPath[-1] == os.sep:
2247 newPath = newPath[:-1]
2248 self.ui.cb_disk.addItem(os.path.basename(newPath), newPath)
2249 self.ui.cb_disk.setCurrentIndex(self.ui.cb_disk.count()-1)
2250 self.ui.b_disk_remove.setEnabled(True)
2252 @pyqtSlot()
2253 def slot_diskFolderRemove(self):
2254 index = self.ui.cb_disk.currentIndex()
2256 if index <= 0:
2257 return
2259 self.ui.cb_disk.removeItem(index)
2261 if self.ui.cb_disk.currentIndex() == 0:
2262 self.ui.b_disk_remove.setEnabled(False)
2264 @pyqtSlot(QModelIndex)
2265 def slot_fileTreeDoubleClicked(self, modelIndex):
2266 filename = self.fDirModel.filePath(modelIndex)
2268 if not self.ui.listWidget.isDragUrlValid(filename):
2269 return
2271 if not self.host.load_file(filename):
2272 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"),
2273 self.tr("Failed to load file"),
2274 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
2275 return
2277 if filename.endswith(".carxp"):
2278 self.loadExternalCanvasGroupPositionsIfNeeded(filename)
2280 # --------------------------------------------------------------------------------------------------------
2281 # Transport
2283 def refreshTransport(self, forced = False):
2284 if not self.ui.l_transport_time.isVisible():
2285 return
2286 if self.fSampleRate == 0.0 or not self.host.is_engine_running():
2287 return
2289 timeInfo = self.host.get_transport_info()
2290 playing = timeInfo['playing']
2291 frame = timeInfo['frame']
2292 bpm = timeInfo['bpm']
2294 if playing != self.fLastTransportState or forced:
2295 if playing:
2296 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
2297 icon = getIcon('media-playback-pause', 16, 'svgz')
2298 else:
2299 icon = QIcon(":/16x16/media-playback-pause.svgz")
2300 self.ui.b_transport_play.setChecked(True)
2301 self.ui.b_transport_play.setIcon(icon)
2302 #self.ui.b_transport_play.setText(self.tr("&Pause"))
2303 else:
2304 if self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]:
2305 icon = getIcon('media-playback-start', 16, 'svgz')
2306 else:
2307 icon = QIcon(":/16x16/media-playback-start.svgz")
2308 self.ui.b_transport_play.setChecked(False)
2309 self.ui.b_transport_play.setIcon(icon)
2310 #self.ui.b_play.setText(self.tr("&Play"))
2312 self.fLastTransportState = playing
2314 if frame != self.fLastTransportFrame or forced:
2315 self.fLastTransportFrame = frame
2317 time = frame / self.fSampleRate
2318 secs = time % 60
2319 mins = (time / 60) % 60
2320 hrs = (time / 3600) % 60
2321 self.ui.l_transport_time.setText("%02i:%02i:%02i" % (hrs, mins, secs))
2323 frame1 = frame % 1000
2324 frame2 = (frame / 1000) % 1000
2325 frame3 = (frame / 1000000) % 1000
2326 self.ui.l_transport_frame.setText("%03i'%03i'%03i" % (frame3, frame2, frame1))
2328 bar = timeInfo['bar']
2329 beat = timeInfo['beat']
2330 tick = timeInfo['tick']
2331 self.ui.l_transport_bbt.setText("%03i|%02i|%04i" % (bar, beat, tick))
2333 if bpm != self.fLastTransportBPM or forced:
2334 self.fLastTransportBPM = bpm
2336 if bpm > 0.0:
2337 self.ui.dsb_transport_bpm.blockSignals(True)
2338 self.ui.dsb_transport_bpm.setValue(bpm)
2339 self.ui.dsb_transport_bpm.blockSignals(False)
2340 self.ui.dsb_transport_bpm.setStyleSheet("")
2341 else:
2342 self.ui.dsb_transport_bpm.setStyleSheet("QDoubleSpinBox { color: palette(mid); }")
2344 # --------------------------------------------------------------------------------------------------------
2345 # Transport (menu actions)
2347 @pyqtSlot(bool)
2348 def slot_transportPlayPause(self, toggled):
2349 if self.host.isPlugin or not self.host.is_engine_running():
2350 return
2352 if toggled:
2353 self.host.transport_play()
2354 else:
2355 self.host.transport_pause()
2357 self.refreshTransport()
2359 @pyqtSlot()
2360 def slot_transportStop(self):
2361 if self.host.isPlugin or not self.host.is_engine_running():
2362 return
2364 self.host.transport_pause()
2365 self.host.transport_relocate(0)
2367 self.refreshTransport()
2369 @pyqtSlot()
2370 def slot_transportBackwards(self):
2371 if self.host.isPlugin or not self.host.is_engine_running():
2372 return
2374 newFrame = self.host.get_current_transport_frame() - 100000
2376 if newFrame < 0:
2377 newFrame = 0
2379 self.host.transport_relocate(newFrame)
2381 @pyqtSlot(float)
2382 def slot_transportBpmChanged(self, newValue):
2383 self.host.transport_bpm(newValue)
2385 @pyqtSlot()
2386 def slot_transportForwards(self):
2387 if self.fSampleRate == 0.0 or self.host.isPlugin or not self.host.is_engine_running():
2388 return
2390 newFrame = self.host.get_current_transport_frame() + int(self.fSampleRate*2.5)
2391 self.host.transport_relocate(newFrame)
2393 @pyqtSlot(bool)
2394 def slot_transportJackEnabled(self, clicked):
2395 if not self.host.is_engine_running():
2396 return
2397 self.host.transportMode = ENGINE_TRANSPORT_MODE_JACK if clicked else ENGINE_TRANSPORT_MODE_INTERNAL
2398 self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE,
2399 self.host.transportMode,
2400 self.host.transportExtra)
2402 @pyqtSlot(bool)
2403 def slot_transportLinkEnabled(self, clicked):
2404 if not self.host.is_engine_running():
2405 return
2406 extra = ":link:" if clicked else ""
2407 self.host.transportExtra = extra
2408 self.host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE,
2409 self.host.transportMode,
2410 self.host.transportExtra)
2412 # --------------------------------------------------------------------------------------------------------
2413 # Other
2415 @pyqtSlot(bool)
2416 def slot_xrunClear(self):
2417 self.host.clear_engine_xruns()
2419 # --------------------------------------------------------------------------------------------------------
2420 # Canvas scrollbars
2422 @pyqtSlot(int)
2423 def slot_horizontalScrollBarChanged(self, value):
2424 maximum = self.ui.graphicsView.horizontalScrollBar().maximum()
2425 if maximum == 0:
2426 xp = 0
2427 else:
2428 xp = float(value) / maximum
2429 self.ui.miniCanvasPreview.setViewPosX(xp)
2430 self.updateCanvasInitialPos()
2432 @pyqtSlot(int)
2433 def slot_verticalScrollBarChanged(self, value):
2434 maximum = self.ui.graphicsView.verticalScrollBar().maximum()
2435 if maximum == 0:
2436 yp = 0
2437 else:
2438 yp = float(value) / maximum
2439 self.ui.miniCanvasPreview.setViewPosY(yp)
2440 self.updateCanvasInitialPos()
2442 # --------------------------------------------------------------------------------------------------------
2443 # Canvas keyboard
2445 @pyqtSlot(int)
2446 def slot_noteOn(self, note):
2447 if self.fPluginCount == 0:
2448 return
2450 for pluginId in self.fSelectedPlugins:
2451 self.host.send_midi_note(pluginId, 0, note, 100)
2453 pedit = self.getPluginEditDialog(pluginId)
2454 pedit.noteOn(0, note, 100)
2456 @pyqtSlot(int)
2457 def slot_noteOff(self, note):
2458 if self.fPluginCount == 0:
2459 return
2461 for pluginId in self.fSelectedPlugins:
2462 self.host.send_midi_note(pluginId, 0, note, 0)
2464 pedit = self.getPluginEditDialog(pluginId)
2465 pedit.noteOff(0, note)
2467 # --------------------------------------------------------------------------------------------------------
2468 # Canvas keyboard (host callbacks)
2470 @pyqtSlot(int, int, int, int)
2471 def slot_handleNoteOnCallback(self, pluginId, channel, note, velocity):
2472 if pluginId in self.fSelectedPlugins:
2473 self.ui.keyboard.sendNoteOn(note, False)
2475 @pyqtSlot(int, int, int)
2476 def slot_handleNoteOffCallback(self, pluginId, channel, note):
2477 if pluginId in self.fSelectedPlugins:
2478 self.ui.keyboard.sendNoteOff(note, False)
2480 # --------------------------------------------------------------------------------------------------------
2482 @pyqtSlot(int)
2483 def slot_handleUpdateCallback(self, pluginId):
2484 pitem = self.getPluginItem(pluginId)
2486 if pitem is None:
2487 return
2489 wasCompacted = pitem.isCompacted()
2490 isCompacted = wasCompacted
2492 check = self.host.get_custom_data_value(pluginId, CUSTOM_DATA_TYPE_PROPERTY, "CarlaSkinIsCompacted")
2493 if not check:
2494 return
2495 isCompacted = bool(check == "true")
2497 if wasCompacted == isCompacted:
2498 return
2500 pitem.recreateWidget(True)
2502 # --------------------------------------------------------------------------------------------------------
2503 # MiniCanvas stuff
2505 @pyqtSlot()
2506 def slot_miniCanvasCheckAll(self):
2507 self.slot_miniCanvasCheckSize()
2508 self.slot_horizontalScrollBarChanged(self.ui.graphicsView.horizontalScrollBar().value())
2509 self.slot_verticalScrollBarChanged(self.ui.graphicsView.verticalScrollBar().value())
2511 @pyqtSlot()
2512 def slot_miniCanvasCheckSize(self):
2513 if self.fCanvasWidth == 0 or self.fCanvasHeight == 0:
2514 return
2516 currentIndex = self.ui.tabWidget.currentIndex()
2518 if currentIndex == 1:
2519 width = self.ui.graphicsView.width()
2520 height = self.ui.graphicsView.height()
2521 else:
2522 self.ui.tabWidget.blockSignals(True)
2523 self.ui.tabWidget.setCurrentIndex(1)
2524 width = self.ui.graphicsView.width()
2525 height = self.ui.graphicsView.height()
2526 self.ui.tabWidget.setCurrentIndex(currentIndex)
2527 self.ui.tabWidget.blockSignals(False)
2529 self.scene.updateLimits()
2531 self.ui.miniCanvasPreview.setViewSize(float(width)/self.fCanvasWidth, float(height)/self.fCanvasHeight)
2533 @pyqtSlot(float, float)
2534 def slot_miniCanvasMoved(self, xp, yp):
2535 hsb = self.ui.graphicsView.horizontalScrollBar()
2536 vsb = self.ui.graphicsView.verticalScrollBar()
2537 hsb.setValue(xp * hsb.maximum())
2538 vsb.setValue(yp * vsb.maximum())
2539 self.updateCanvasInitialPos()
2541 # --------------------------------------------------------------------------------------------------------
2542 # Logs autoscroll, save and clear
2544 @pyqtSlot(int)
2545 def slot_toggleLogAutoscroll(self, checkState):
2546 self.autoscrollOnNewLog = checkState == Qt.Checked
2547 if self.autoscrollOnNewLog:
2548 self.ui.text_logs.verticalScrollBar().setValue(self.ui.text_logs.verticalScrollBar().maximum())
2550 @pyqtSlot(int)
2551 def slot_logSliderMoved(self, slider_pos):
2552 if self.ui.text_logs.verticalScrollBar().hasTracking() or self.autoscrollOnNewLog:
2553 self.lastLogSliderPos = slider_pos
2554 else:
2555 self.ui.text_logs.verticalScrollBar().setValue(self.lastLogSliderPos)
2557 @pyqtSlot()
2558 def slot_logButtonsState(self, enabled=True):
2559 self.ui.logs_clear.setEnabled(enabled)
2560 self.ui.logs_save.setEnabled(enabled)
2562 @pyqtSlot()
2563 def slot_logSave(self):
2564 filename = os.path.join(self.fSavedSettings[CARLA_KEY_MAIN_PROJECT_FOLDER], 'carla_log.txt')
2565 filename, _ = QFileDialog.getSaveFileName(self, self.tr("Save Logs"), filename)
2567 if not filename:
2568 return
2570 try:
2571 with open(filename, "w") as logfile:
2572 logfile.write(self.ui.text_logs.toPlainText())
2573 logfile.close()
2574 except:
2575 return
2577 @pyqtSlot()
2578 def slot_logClear(self):
2579 self.ui.text_logs.clear()
2580 self.ui.text_logs.appendPlainText("======= Logs cleared ========")
2581 self.slot_logButtonsState(False)
2583 # --------------------------------------------------------------------------------------------------------
2584 # Timers
2586 def startTimers(self):
2587 if self.fIdleTimerFast == 0:
2588 self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
2590 if self.fIdleTimerSlow == 0:
2591 self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
2593 def restartTimersIfNeeded(self):
2594 if self.fIdleTimerFast != 0:
2595 self.killTimer(self.fIdleTimerFast)
2596 self.fIdleTimerFast = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL])
2598 if self.fIdleTimerSlow != 0:
2599 self.killTimer(self.fIdleTimerSlow)
2600 self.fIdleTimerSlow = self.startTimer(self.fSavedSettings[CARLA_KEY_MAIN_REFRESH_INTERVAL]*4)
2602 def killTimers(self):
2603 if self.fIdleTimerFast != 0:
2604 self.killTimer(self.fIdleTimerFast)
2605 self.fIdleTimerFast = 0
2607 if self.fIdleTimerSlow != 0:
2608 self.killTimer(self.fIdleTimerSlow)
2609 self.fIdleTimerSlow = 0
2611 # --------------------------------------------------------------------------------------------------------
2612 # Misc
2614 @pyqtSlot(bool)
2615 def slot_toolbarVisibilityChanged(self, visible):
2616 self.ui.toolBar.blockSignals(True)
2617 self.ui.toolBar.setEnabled(visible)
2618 self.ui.toolBar.blockSignals(False)
2619 self.ui.act_settings_show_toolbar.setChecked(visible)
2621 @pyqtSlot(int)
2622 def slot_tabChanged(self, index):
2623 if index != 1:
2624 return
2626 self.ui.graphicsView.setFocus()
2628 @pyqtSlot(int)
2629 def slot_handleReloadAllCallback(self, pluginId):
2630 if pluginId >= self.fPluginCount:
2631 return
2633 pitem = self.fPluginList[pluginId]
2634 if pitem is None:
2635 return
2637 pitem.recreateWidget()
2639 # --------------------------------------------------------------------------------------------------------
2641 @pyqtSlot(int, int, str)
2642 def slot_handleNSMCallback(self, opcode, valueInt, valueStr):
2643 if opcode == NSM_CALLBACK_INIT:
2644 return
2646 # Error
2647 elif opcode == NSM_CALLBACK_ERROR:
2648 pass
2650 # Reply
2651 elif opcode == NSM_CALLBACK_ANNOUNCE:
2652 self.fFirstEngineInit = False
2653 self.fSessionManagerName = valueStr
2654 self.setProperWindowTitle()
2656 # If NSM server does not support optional-gui, revert our initial assumptions that it does
2657 if (valueInt & (1 << 1)) == 0x0:
2658 self.fWindowCloseHideGui = False
2659 self.ui.act_file_quit.setText(self.tr("&Quit"))
2660 QApplication.instance().setQuitOnLastWindowClosed(True)
2661 self.show()
2663 # Open
2664 elif opcode == NSM_CALLBACK_OPEN:
2665 self.fProjectFilename = QFileInfo(valueStr+".carxp").absoluteFilePath()
2666 self.setProperWindowTitle()
2668 self.fCustomStopAction = self.CUSTOM_ACTION_PROJECT_LOAD
2669 self.slot_engineStop(True)
2670 return
2672 # Save
2673 elif opcode == NSM_CALLBACK_SAVE:
2674 self.saveProjectNow()
2676 # Session is Loaded
2677 elif opcode == NSM_CALLBACK_SESSION_IS_LOADED:
2678 pass
2680 # Show Optional Gui
2681 elif opcode == NSM_CALLBACK_SHOW_OPTIONAL_GUI:
2682 self.show()
2684 # Hide Optional Gui
2685 elif opcode == NSM_CALLBACK_HIDE_OPTIONAL_GUI:
2686 self.hideForNSM()
2688 # Set client name
2689 elif opcode == NSM_CALLBACK_SET_CLIENT_NAME_ID:
2690 self.fClientName = valueStr
2691 return
2693 self.host.nsm_ready(opcode)
2695 # --------------------------------------------------------------------------------------------------------
2697 def fixLogText(self, text):
2698 return text.replace("\x1b[30;1m", "").replace("\x1b[31m", "").replace("\x1b[0m", "")
2700 @pyqtSlot(int, int, int, int, float, str)
2701 def slot_handleDebugCallback(self, pluginId, value1, value2, value3, valuef, valueStr):
2702 self.ui.text_logs.appendPlainText(self.fixLogText(valueStr))
2704 @pyqtSlot(str)
2705 def slot_handleInfoCallback(self, info):
2706 QMessageBox.information(self, "Information", info)
2708 @pyqtSlot(str)
2709 def slot_handleErrorCallback(self, error):
2710 QMessageBox.critical(self, "Error", error)
2712 @pyqtSlot()
2713 def slot_handleQuitCallback(self):
2714 self.fIsProjectLoading = False
2715 self.killTimers()
2716 self.removeAllPlugins()
2717 self.projectLoadingFinished(False)
2719 @pyqtSlot(int)
2720 def slot_handleInlineDisplayRedrawCallback(self, pluginId):
2721 # FIXME
2722 if self.fIdleTimerSlow != 0 and self.fIdleTimerFast != 0 and pluginId < self.fPluginCount and not self.fIsProjectLoading:
2723 patchcanvas.redrawPluginGroup(pluginId)
2725 # --------------------------------------------------------------------------------------------------------
2727 @pyqtSlot()
2728 def slot_handleSIGUSR1(self):
2729 print("Got SIGUSR1 -> Saving project now")
2730 self.slot_fileSave()
2732 @pyqtSlot()
2733 def slot_handleSIGTERM(self):
2734 print("Got SIGTERM -> Closing now")
2735 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
2736 self.close()
2738 # --------------------------------------------------------------------------------------------------------
2739 # Internal stuff
2741 def getExtraPtr(self, plugin):
2742 ptype = plugin['type']
2744 if ptype == PLUGIN_LADSPA:
2745 uniqueId = plugin['uniqueId']
2747 self.maybeLoadRDFs()
2749 for rdfItem in self.fLadspaRdfList:
2750 if rdfItem.UniqueID == uniqueId:
2751 return pointer(rdfItem)
2753 elif ptype == PLUGIN_SF2:
2754 if plugin['name'].lower().endswith(" (16 outputs)"):
2755 return self._true
2757 return None
2759 def maybeLoadRDFs(self):
2760 if not self.fLadspaRdfNeedsUpdate:
2761 return
2763 self.fLadspaRdfNeedsUpdate = False
2764 self.fLadspaRdfList = []
2766 if not haveLRDF:
2767 return
2769 settingsDir = os.path.join(HOME, ".config", "falkTX")
2770 frLadspaFile = os.path.join(settingsDir, "ladspa_rdf.db")
2772 if os.path.exists(frLadspaFile):
2773 frLadspa = open(frLadspaFile, 'r')
2775 try:
2776 self.fLadspaRdfList = ladspa_rdf.get_c_ladspa_rdfs(json.load(frLadspa))
2777 except:
2778 pass
2780 frLadspa.close()
2782 # --------------------------------------------------------------------------------------------------------
2784 def getPluginCount(self):
2785 return self.fPluginCount
2787 def getPluginItem(self, pluginId):
2788 if pluginId >= self.fPluginCount:
2789 return None
2791 pitem = self.fPluginList[pluginId]
2792 if pitem is None:
2793 return None
2794 #if False:
2795 #return CarlaRackItem(self, 0, False)
2797 return pitem
2799 def getPluginEditDialog(self, pluginId):
2800 if pluginId >= self.fPluginCount:
2801 return None
2803 pitem = self.fPluginList[pluginId]
2804 if pitem is None:
2805 return None
2806 if False:
2807 return PluginEdit(self, self.host, 0)
2809 return pitem.getEditDialog()
2811 def getPluginSlotWidget(self, pluginId):
2812 if pluginId >= self.fPluginCount:
2813 return None
2815 pitem = self.fPluginList[pluginId]
2816 if pitem is None:
2817 return None
2818 #if False:
2819 #return AbstractPluginSlot()
2821 return pitem.getWidget()
2823 # --------------------------------------------------------------------------------------------------------
2825 def waitForPendingEvents(self):
2826 pass
2828 # --------------------------------------------------------------------------------------------------------
2829 # show/hide event
2831 def showEvent(self, event):
2832 self.getAndRefreshRuntimeInfo()
2833 self.refreshTransport(True)
2834 QMainWindow.showEvent(self, event)
2836 if QT_VERSION >= 0x50600:
2837 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_UI_SCALE, int(self.devicePixelRatioF() * 1000), "")
2838 print("Frontend pixel ratio is", self.devicePixelRatioF())
2840 # set our gui as parent for all plugins UIs
2841 if self.host.manageUIs and not self.host.isControl:
2842 if MACOS:
2843 nsViewPtr = int(self.winId())
2844 winIdStr = "%x" % gCarla.utils.cocoa_get_window(nsViewPtr)
2845 elif WINDOWS or QApplication.platformName() == "xcb":
2846 winIdStr = "%x" % int(self.winId())
2847 else:
2848 winIdStr = "0"
2849 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, winIdStr)
2851 def hideEvent(self, event):
2852 # disable parent
2853 if not self.host.isControl:
2854 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_WIN_ID, 0, "0")
2856 QMainWindow.hideEvent(self, event)
2858 # --------------------------------------------------------------------------------------------------------
2859 # resize event
2861 def resizeEvent(self, event):
2862 QMainWindow.resizeEvent(self, event)
2864 if self.fWithCanvas:
2865 self.slot_miniCanvasCheckSize()
2867 # --------------------------------------------------------------------------------------------------------
2868 # timer event
2870 def refreshRuntimeInfo(self, load, xruns):
2871 txt1 = str(xruns) if (xruns >= 0) else "--"
2872 txt2 = "" if (xruns == 1) else "s"
2873 self.ui.b_xruns.setText("%s Xrun%s" % (txt1, txt2))
2874 self.ui.pb_dsp_load.setValue(int(load))
2876 def getAndRefreshRuntimeInfo(self):
2877 if not self.ui.pb_dsp_load.isVisible():
2878 return
2879 if not self.host.is_engine_running():
2880 return
2881 info = self.host.get_runtime_engine_info()
2882 self.refreshRuntimeInfo(info['load'], info['xruns'])
2884 def idleFast(self):
2885 self.host.engine_idle()
2886 self.refreshTransport()
2888 if self.fPluginCount == 0 or self.fCurrentlyRemovingAllPlugins:
2889 return
2891 for pitem in self.fPluginList:
2892 if pitem is None:
2893 break
2895 pitem.getWidget().idleFast()
2897 for pluginId in self.fSelectedPlugins:
2898 self.fPeaksCleared = False
2899 if self.ui.peak_in.isVisible():
2900 self.ui.peak_in.displayMeter(1, self.host.get_input_peak_value(pluginId, True))
2901 self.ui.peak_in.displayMeter(2, self.host.get_input_peak_value(pluginId, False))
2902 if self.ui.peak_out.isVisible():
2903 self.ui.peak_out.displayMeter(1, self.host.get_output_peak_value(pluginId, True))
2904 self.ui.peak_out.displayMeter(2, self.host.get_output_peak_value(pluginId, False))
2905 return
2907 if self.fPeaksCleared:
2908 return
2910 self.fPeaksCleared = True
2911 self.ui.peak_in.displayMeter(1, 0.0, True)
2912 self.ui.peak_in.displayMeter(2, 0.0, True)
2913 self.ui.peak_out.displayMeter(1, 0.0, True)
2914 self.ui.peak_out.displayMeter(2, 0.0, True)
2916 def idleSlow(self):
2917 self.getAndRefreshRuntimeInfo()
2919 if self.fPluginCount == 0 or self.fCurrentlyRemovingAllPlugins:
2920 return
2922 for pitem in self.fPluginList:
2923 if pitem is None:
2924 break
2926 pitem.getWidget().idleSlow()
2928 def timerEvent(self, event):
2929 if event.timerId() == self.fIdleTimerFast:
2930 self.idleFast()
2932 elif event.timerId() == self.fIdleTimerSlow:
2933 self.idleSlow()
2935 QMainWindow.timerEvent(self, event)
2937 # --------------------------------------------------------------------------------------------------------
2938 # color/style change event
2940 def changeEvent(self, event):
2941 if event.type() in (QEvent.PaletteChange, QEvent.StyleChange):
2942 self.updateStyle()
2943 QMainWindow.changeEvent(self, event)
2945 def updateStyle(self):
2946 # Rack padding images setup
2947 rack_imgL = QImage(":/bitmaps/rack_padding_left.png")
2948 rack_imgR = QImage(":/bitmaps/rack_padding_right.png")
2950 min_value = 0.07
2952 if PYQT_VERSION >= 0x50600:
2953 value_fix = 1.0/(1.0-rack_imgL.scaled(1, 1, Qt.IgnoreAspectRatio, Qt.SmoothTransformation).pixelColor(0,0).blackF())
2954 else:
2955 value_fix = 1.5
2957 rack_pal = self.ui.rack.palette()
2958 bg_color = rack_pal.window().color()
2959 fg_color = rack_pal.text().color()
2960 bg_value = 1.0 - bg_color.blackF()
2961 if bg_value != 0.0 and bg_value < min_value:
2962 pad_color = bg_color.lighter(int(100*min_value/bg_value*value_fix))
2963 else:
2964 pad_color = QColor.fromHsvF(0.0, 0.0, min_value*value_fix)
2966 painter = QPainter()
2967 fillRect = rack_imgL.rect().adjusted(-1,-1,1,1)
2969 painter.begin(rack_imgL)
2970 painter.setCompositionMode(QPainter.CompositionMode_Multiply)
2971 painter.setBrush(pad_color)
2972 painter.drawRect(fillRect)
2973 painter.end()
2974 rack_pixmapL = QPixmap(rack_imgL)
2975 self.imgL_palette = QPalette()
2976 self.imgL_palette.setBrush(QPalette.Window, QBrush(rack_pixmapL))
2977 self.ui.pad_left.setPalette(self.imgL_palette)
2978 self.ui.pad_left.setAutoFillBackground(True)
2980 painter.begin(rack_imgR)
2981 painter.setCompositionMode(QPainter.CompositionMode_Multiply)
2982 painter.setBrush(pad_color)
2983 painter.drawRect(fillRect)
2984 painter.end()
2985 rack_pixmapR = QPixmap(rack_imgR)
2986 self.imgR_palette = QPalette()
2987 self.imgR_palette.setBrush(QPalette.Window, QBrush(rack_pixmapR))
2988 self.ui.pad_right.setPalette(self.imgR_palette)
2989 self.ui.pad_right.setAutoFillBackground(True)
2991 # qt's rgba is actually argb, so convert that
2992 bg_color_value = bg_color.rgba()
2993 bg_color_value = ((bg_color_value & 0xffffff) << 8) | (bg_color_value >> 24)
2995 fg_color_value = fg_color.rgba()
2996 fg_color_value = ((fg_color_value & 0xffffff) << 8) | (fg_color_value >> 24)
2998 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_BACKGROUND_COLOR, bg_color_value, "")
2999 self.host.set_engine_option(ENGINE_OPTION_FRONTEND_FOREGROUND_COLOR, fg_color_value, "")
3001 # --------------------------------------------------------------------------------------------------------
3002 # paint event
3004 #def paintEvent(self, event):
3005 #QMainWindow.paintEvent(self, event)
3007 #if MACOS or not self.fSavedSettings[CARLA_KEY_CUSTOM_PAINTING]:
3008 #return
3010 #painter = QPainter(self)
3011 #painter.setBrush(QColor(36, 36, 36))
3012 #painter.setPen(QColor(62, 62, 62))
3013 #painter.drawRect(1, self.height()/2, self.width()-3, self.height()-self.height()/2-1)
3015 # --------------------------------------------------------------------------------------------------------
3016 # close event
3018 def shouldIgnoreClose(self):
3019 if self.host.isControl or self.host.isPlugin:
3020 return False
3021 if self.fCustomStopAction == self.CUSTOM_ACTION_APP_CLOSE:
3022 return False
3023 if self.fWindowCloseHideGui:
3024 return False
3025 if self.fSavedSettings[CARLA_KEY_MAIN_CONFIRM_EXIT]:
3026 return QMessageBox.question(self, self.tr("Quit"),
3027 self.tr("Are you sure you want to quit Carla?"),
3028 QMessageBox.Yes|QMessageBox.No) == QMessageBox.No
3029 return False
3031 def closeEvent(self, event):
3032 if self.shouldIgnoreClose():
3033 event.ignore()
3034 return
3036 if self.fWindowCloseHideGui and self.fCustomStopAction != self.CUSTOM_ACTION_APP_CLOSE:
3037 self.hideForNSM()
3038 self.host.nsm_ready(NSM_CALLBACK_HIDE_OPTIONAL_GUI)
3039 return
3041 patchcanvas.handleAllPluginsRemoved()
3043 if MACOS and self.fMacClosingHelper and not (self.host.isControl or self.host.isPlugin):
3044 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
3045 self.fMacClosingHelper = False
3046 event.ignore()
3048 for i in reversed(range(self.fPluginCount)):
3049 self.host.show_custom_ui(i, False)
3051 QTimer.singleShot(100, self.close)
3052 return
3054 self.killTimers()
3055 self.saveSettings()
3057 if self.host.is_engine_running() and not (self.host.isControl or self.host.isPlugin):
3058 if not self.slot_engineStop(True):
3059 self.fCustomStopAction = self.CUSTOM_ACTION_APP_CLOSE
3060 event.ignore()
3061 return
3063 QMainWindow.closeEvent(self, event)
3065 # if we reach this point, fully close ourselves
3066 gCarla.gui = None
3067 QApplication.instance().quit()
3069 # ------------------------------------------------------------------------------------------------
3070 # Canvas callback
3072 def canvasCallback(action, value1, value2, valueStr):
3073 host = gCarla.gui.host
3075 if gCarla.gui.fCustomStopAction == HostWindow.CUSTOM_ACTION_APP_CLOSE:
3076 return
3078 if action == patchcanvas.ACTION_GROUP_INFO:
3079 pass
3081 elif action == patchcanvas.ACTION_GROUP_RENAME:
3082 pass
3084 elif action == patchcanvas.ACTION_GROUP_SPLIT:
3085 groupId = value1
3086 patchcanvas.splitGroup(groupId)
3087 gCarla.gui.updateMiniCanvasLater()
3089 elif action == patchcanvas.ACTION_GROUP_JOIN:
3090 groupId = value1
3091 patchcanvas.joinGroup(groupId)
3092 gCarla.gui.updateMiniCanvasLater()
3094 elif action == patchcanvas.ACTION_GROUP_POSITION:
3095 if gCarla.gui.fIsProjectLoading:
3096 return
3097 if not host.is_engine_running():
3098 return
3099 groupId = value1
3100 x1, y1, x2, y2 = tuple(int(i) for i in valueStr.split(":"))
3101 host.patchbay_set_group_pos(gCarla.gui.fExternalPatchbay, groupId, x1, y1, x2, y2)
3102 gCarla.gui.updateMiniCanvasLater()
3104 elif action == patchcanvas.ACTION_PORT_INFO:
3105 pass
3107 elif action == patchcanvas.ACTION_PORT_RENAME:
3108 pass
3110 elif action == patchcanvas.ACTION_PORTS_CONNECT:
3111 gOut, pOut, gIn, pIn = tuple(int(i) for i in valueStr.split(":"))
3113 if not host.patchbay_connect(gCarla.gui.fExternalPatchbay, gOut, pOut, gIn, pIn):
3114 print("Connection failed:", host.get_last_error())
3116 elif action == patchcanvas.ACTION_PORTS_DISCONNECT:
3117 connectionId = value1
3119 if not host.patchbay_disconnect(gCarla.gui.fExternalPatchbay, connectionId):
3120 print("Disconnect failed:", host.get_last_error())
3122 elif action == patchcanvas.ACTION_PLUGIN_CLONE:
3123 pluginId = value1
3125 if not host.clone_plugin(pluginId):
3126 CustomMessageBox(gCarla.gui, QMessageBox.Warning, gCarla.gui.tr("Error"), gCarla.gui.tr("Operation failed"),
3127 host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
3129 elif action == patchcanvas.ACTION_PLUGIN_EDIT:
3130 pluginId = value1
3131 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3133 if pwidget is not None:
3134 pwidget.showEditDialog()
3136 elif action == patchcanvas.ACTION_PLUGIN_RENAME:
3137 pluginId = value1
3138 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3140 if pwidget is not None:
3141 pwidget.showRenameDialog()
3143 elif action == patchcanvas.ACTION_PLUGIN_REPLACE:
3144 pluginId = value1
3145 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3147 if pwidget is not None:
3148 pwidget.showReplaceDialog()
3150 elif action == patchcanvas.ACTION_PLUGIN_REMOVE:
3151 pluginId = value1
3153 if not host.remove_plugin(pluginId):
3154 CustomMessageBox(gCarla.gui, QMessageBox.Warning, gCarla.gui.tr("Error"), gCarla.gui.tr("Operation failed"),
3155 host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
3157 elif action == patchcanvas.ACTION_PLUGIN_SHOW_UI:
3158 pluginId = value1
3159 pwidget = gCarla.gui.getPluginSlotWidget(pluginId)
3161 if pwidget is not None:
3162 pwidget.showCustomUI()
3164 elif action == patchcanvas.ACTION_BG_RIGHT_CLICK:
3165 gCarla.gui.slot_showPluginActionsMenu()
3167 elif action == patchcanvas.ACTION_INLINE_DISPLAY:
3168 if gCarla.gui.fIsProjectLoading:
3169 return
3170 if not host.is_engine_running():
3171 return
3172 pluginId = value1
3173 width, height = [int(v) for v in valueStr.split(":")]
3174 return host.render_inline_display(pluginId, width, height)
3176 # ------------------------------------------------------------------------------------------------------------
3177 # Engine callback
3179 def engineCallback(host, action, pluginId, value1, value2, value3, valuef, valueStr):
3180 # kdevelop likes this :)
3181 if False: host = CarlaHostNull()
3183 valueStr = charPtrToString(valueStr)
3185 if action == ENGINE_CALLBACK_ENGINE_STARTED:
3186 host.processMode = value1
3187 host.transportMode = value2
3188 elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
3189 host.processMode = value1
3190 elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
3191 host.transportMode = value1
3192 host.transportExtra = valueStr
3194 if action == ENGINE_CALLBACK_DEBUG:
3195 host.DebugCallback.emit(pluginId, value1, value2, value3, valuef, valueStr)
3196 elif action == ENGINE_CALLBACK_PLUGIN_ADDED:
3197 host.PluginAddedCallback.emit(pluginId, value1, valueStr)
3198 elif action == ENGINE_CALLBACK_PLUGIN_REMOVED:
3199 host.PluginRemovedCallback.emit(pluginId)
3200 elif action == ENGINE_CALLBACK_PLUGIN_RENAMED:
3201 host.PluginRenamedCallback.emit(pluginId, valueStr)
3202 elif action == ENGINE_CALLBACK_PLUGIN_UNAVAILABLE:
3203 host.PluginUnavailableCallback.emit(pluginId, valueStr)
3204 elif action == ENGINE_CALLBACK_PARAMETER_VALUE_CHANGED:
3205 host.ParameterValueChangedCallback.emit(pluginId, value1, valuef)
3206 elif action == ENGINE_CALLBACK_PARAMETER_DEFAULT_CHANGED:
3207 host.ParameterDefaultChangedCallback.emit(pluginId, value1, valuef)
3208 elif action == ENGINE_CALLBACK_PARAMETER_MAPPED_CONTROL_INDEX_CHANGED:
3209 host.ParameterMappedControlIndexChangedCallback.emit(pluginId, value1, value2)
3210 elif action == ENGINE_CALLBACK_PARAMETER_MAPPED_RANGE_CHANGED:
3211 minimum, maximum = (float(v) for v in valueStr.split(":", 2))
3212 host.ParameterMappedRangeChangedCallback.emit(pluginId, value1, minimum, maximum)
3213 elif action == ENGINE_CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED:
3214 host.ParameterMidiChannelChangedCallback.emit(pluginId, value1, value2)
3215 elif action == ENGINE_CALLBACK_PROGRAM_CHANGED:
3216 host.ProgramChangedCallback.emit(pluginId, value1)
3217 elif action == ENGINE_CALLBACK_MIDI_PROGRAM_CHANGED:
3218 host.MidiProgramChangedCallback.emit(pluginId, value1)
3219 elif action == ENGINE_CALLBACK_OPTION_CHANGED:
3220 host.OptionChangedCallback.emit(pluginId, value1, bool(value2))
3221 elif action == ENGINE_CALLBACK_UI_STATE_CHANGED:
3222 host.UiStateChangedCallback.emit(pluginId, value1)
3223 elif action == ENGINE_CALLBACK_NOTE_ON:
3224 host.NoteOnCallback.emit(pluginId, value1, value2, value3)
3225 elif action == ENGINE_CALLBACK_NOTE_OFF:
3226 host.NoteOffCallback.emit(pluginId, value1, value2)
3227 elif action == ENGINE_CALLBACK_UPDATE:
3228 host.UpdateCallback.emit(pluginId)
3229 elif action == ENGINE_CALLBACK_RELOAD_INFO:
3230 host.ReloadInfoCallback.emit(pluginId)
3231 elif action == ENGINE_CALLBACK_RELOAD_PARAMETERS:
3232 host.ReloadParametersCallback.emit(pluginId)
3233 elif action == ENGINE_CALLBACK_RELOAD_PROGRAMS:
3234 host.ReloadProgramsCallback.emit(pluginId)
3235 elif action == ENGINE_CALLBACK_RELOAD_ALL:
3236 host.ReloadAllCallback.emit(pluginId)
3237 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_ADDED:
3238 host.PatchbayClientAddedCallback.emit(pluginId, value1, value2, valueStr)
3239 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_REMOVED:
3240 host.PatchbayClientRemovedCallback.emit(pluginId)
3241 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_RENAMED:
3242 host.PatchbayClientRenamedCallback.emit(pluginId, valueStr)
3243 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_DATA_CHANGED:
3244 host.PatchbayClientDataChangedCallback.emit(pluginId, value1, value2)
3245 elif action == ENGINE_CALLBACK_PATCHBAY_CLIENT_POSITION_CHANGED:
3246 host.PatchbayClientPositionChangedCallback.emit(pluginId, value1, value2, value3, int(round(valuef)))
3247 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_ADDED:
3248 host.PatchbayPortAddedCallback.emit(pluginId, value1, value2, value3, valueStr)
3249 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_REMOVED:
3250 host.PatchbayPortRemovedCallback.emit(pluginId, value1)
3251 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_CHANGED:
3252 host.PatchbayPortChangedCallback.emit(pluginId, value1, value2, value3, valueStr)
3253 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_ADDED:
3254 host.PatchbayPortGroupAddedCallback.emit(pluginId, value1, value2, valueStr)
3255 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_REMOVED:
3256 host.PatchbayPortGroupRemovedCallback.emit(pluginId, value1)
3257 elif action == ENGINE_CALLBACK_PATCHBAY_PORT_GROUP_CHANGED:
3258 host.PatchbayPortGroupChangedCallback.emit(pluginId, value1, value2, valueStr)
3259 elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED:
3260 gOut, pOut, gIn, pIn = [int(i) for i in valueStr.split(":")] # FIXME
3261 host.PatchbayConnectionAddedCallback.emit(pluginId, gOut, pOut, gIn, pIn)
3262 elif action == ENGINE_CALLBACK_PATCHBAY_CONNECTION_REMOVED:
3263 host.PatchbayConnectionRemovedCallback.emit(pluginId, value1, value2)
3264 elif action == ENGINE_CALLBACK_ENGINE_STARTED:
3265 host.EngineStartedCallback.emit(pluginId, value1, value2, value3, valuef, valueStr)
3266 elif action == ENGINE_CALLBACK_ENGINE_STOPPED:
3267 host.EngineStoppedCallback.emit()
3268 elif action == ENGINE_CALLBACK_PROCESS_MODE_CHANGED:
3269 host.ProcessModeChangedCallback.emit(value1)
3270 elif action == ENGINE_CALLBACK_TRANSPORT_MODE_CHANGED:
3271 host.TransportModeChangedCallback.emit(value1, valueStr)
3272 elif action == ENGINE_CALLBACK_BUFFER_SIZE_CHANGED:
3273 host.BufferSizeChangedCallback.emit(value1)
3274 elif action == ENGINE_CALLBACK_SAMPLE_RATE_CHANGED:
3275 host.SampleRateChangedCallback.emit(valuef)
3276 elif action == ENGINE_CALLBACK_CANCELABLE_ACTION:
3277 host.CancelableActionCallback.emit(pluginId, bool(value1 != 0), valueStr)
3278 elif action == ENGINE_CALLBACK_PROJECT_LOAD_FINISHED:
3279 host.ProjectLoadFinishedCallback.emit()
3280 elif action == ENGINE_CALLBACK_NSM:
3281 host.NSMCallback.emit(value1, value2, valueStr)
3282 elif action == ENGINE_CALLBACK_IDLE:
3283 QApplication.processEvents()
3284 elif action == ENGINE_CALLBACK_INFO:
3285 host.InfoCallback.emit(valueStr)
3286 elif action == ENGINE_CALLBACK_ERROR:
3287 host.ErrorCallback.emit(valueStr)
3288 elif action == ENGINE_CALLBACK_QUIT:
3289 host.QuitCallback.emit()
3290 elif action == ENGINE_CALLBACK_INLINE_DISPLAY_REDRAW:
3291 host.InlineDisplayRedrawCallback.emit(pluginId)
3292 else:
3293 print("unhandled action", action)
3295 # ------------------------------------------------------------------------------------------------------------
3296 # File callback
3298 def fileCallback(ptr, action, isDir, title, filter):
3299 title = charPtrToString(title)
3300 filter = charPtrToString(filter)
3302 if action == FILE_CALLBACK_OPEN:
3303 ret, ok = QFileDialog.getOpenFileName(gCarla.gui, title, "", filter) #, QFileDialog.ShowDirsOnly if isDir else 0x0)
3304 elif action == FILE_CALLBACK_SAVE:
3305 ret, ok = QFileDialog.getSaveFileName(gCarla.gui, title, "", filter, QFileDialog.ShowDirsOnly if isDir else 0x0)
3306 else:
3307 ret, ok = ("", "")
3309 # FIXME use ok value, test if it works as expected
3310 if not ret:
3311 return None
3313 # FIXME
3314 global fileRet
3315 fileRet = c_char_p(ret.encode("utf-8"))
3316 retval = cast(byref(fileRet), POINTER(c_uintptr))
3317 return retval.contents.value
3319 # ------------------------------------------------------------------------------------------------------------
3320 # Init host
3322 def initHost(initName, libPrefix, isControl, isPlugin, failError, HostClass = None):
3323 pathBinaries, pathResources = getPaths(libPrefix)
3325 # --------------------------------------------------------------------------------------------------------
3326 # Fail if binary dir is not found
3328 if not os.path.exists(pathBinaries):
3329 if failError:
3330 QMessageBox.critical(None, "Error", "Failed to find the carla binaries, cannot continue")
3331 sys.exit(1)
3332 return
3334 # --------------------------------------------------------------------------------------------------------
3335 # Check if we should open main lib as local or global
3337 settings = QSafeSettings("falkTX", "Carla2")
3339 loadGlobal = settings.value(CARLA_KEY_EXPERIMENTAL_LOAD_LIB_GLOBAL, CARLA_DEFAULT_EXPERIMENTAL_LOAD_LIB_GLOBAL, bool)
3341 # --------------------------------------------------------------------------------------------------------
3342 # Set Carla library name
3344 libname = "libcarla_%s2.%s" % ("control" if isControl else "standalone", DLL_EXTENSION)
3345 libname = os.path.join(pathBinaries, libname)
3346 felibname = os.path.join(pathBinaries, "libcarla_frontend.%s" % (DLL_EXTENSION))
3347 utilsname = os.path.join(pathBinaries, "libcarla_utils.%s" % (DLL_EXTENSION))
3349 # --------------------------------------------------------------------------------------------------------
3350 # Print info
3352 if not (gCarla.nogui and isinstance(gCarla.nogui, int)):
3353 print("Carla %s started, status:" % VERSION)
3354 print(" Python version: %s" % sys.version.split(" ",1)[0])
3355 print(" Qt version: %s" % QT_VERSION_STR)
3356 print(" PyQt version: %s" % PYQT_VERSION_STR)
3357 print(" Binary dir: %s" % pathBinaries)
3358 print(" Resources dir: %s" % pathResources)
3360 # --------------------------------------------------------------------------------------------------------
3361 # Init host
3363 if failError:
3364 # no try
3365 host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
3366 else:
3367 try:
3368 host = HostClass() if HostClass is not None else CarlaHostQtDLL(libname, loadGlobal)
3369 except:
3370 host = CarlaHostQtNull()
3372 host.isControl = isControl
3373 host.isPlugin = isPlugin
3375 host.set_engine_callback(lambda h,a,p,v1,v2,v3,vf,vs: engineCallback(host,a,p,v1,v2,v3,vf,vs))
3376 host.set_file_callback(fileCallback)
3378 # If it's a plugin the paths are already set
3379 if not isPlugin:
3380 host.pathBinaries = pathBinaries
3381 host.pathResources = pathResources
3382 host.set_engine_option(ENGINE_OPTION_PATH_BINARIES, 0, pathBinaries)
3383 host.set_engine_option(ENGINE_OPTION_PATH_RESOURCES, 0, pathResources)
3385 if not isControl:
3386 host.nsmOK = host.nsm_init(os.getpid(), initName)
3388 # --------------------------------------------------------------------------------------------------------
3389 # Init frontend lib
3391 if not gCarla.nogui:
3392 gCarla.felib = CarlaFrontendLib(felibname)
3394 # --------------------------------------------------------------------------------------------------------
3395 # Init utils
3397 gCarla.utils = CarlaUtils(utilsname)
3398 gCarla.utils.set_process_name(os.path.basename(initName))
3400 try:
3401 sys.stdout.flush()
3402 except:
3403 pass
3405 sys.stdout = CarlaPrint(False)
3406 sys.stderr = CarlaPrint(True)
3407 sys.excepthook = sys_excepthook
3409 # --------------------------------------------------------------------------------------------------------
3410 # Done
3412 return host
3414 # ------------------------------------------------------------------------------------------------------------
3415 # Load host settings
3417 def loadHostSettings(host):
3418 # kdevelop likes this :)
3419 if False: host = CarlaHostNull()
3421 settings = QSafeSettings("falkTX", "Carla2")
3423 host.experimental = settings.value(CARLA_KEY_MAIN_EXPERIMENTAL, CARLA_DEFAULT_MAIN_EXPERIMENTAL, bool)
3424 host.exportLV2 = settings.value(CARLA_KEY_EXPERIMENTAL_EXPORT_LV2, CARLA_DEFAULT_EXPERIMENTAL_LV2_EXPORT, bool)
3425 host.manageUIs = settings.value(CARLA_KEY_ENGINE_MANAGE_UIS, CARLA_DEFAULT_MANAGE_UIS, bool)
3426 host.maxParameters = settings.value(CARLA_KEY_ENGINE_MAX_PARAMETERS, CARLA_DEFAULT_MAX_PARAMETERS, int)
3427 host.resetXruns = settings.value(CARLA_KEY_ENGINE_RESET_XRUNS, CARLA_DEFAULT_RESET_XRUNS, bool)
3428 host.forceStereo = settings.value(CARLA_KEY_ENGINE_FORCE_STEREO, CARLA_DEFAULT_FORCE_STEREO, bool)
3429 host.preferPluginBridges = settings.value(CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES, CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES, bool)
3430 host.preferUIBridges = settings.value(CARLA_KEY_ENGINE_PREFER_UI_BRIDGES, CARLA_DEFAULT_PREFER_UI_BRIDGES, bool)
3431 host.preventBadBehaviour = settings.value(CARLA_KEY_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR, CARLA_DEFAULT_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR, bool)
3432 host.showLogs = settings.value(CARLA_KEY_MAIN_SHOW_LOGS, CARLA_DEFAULT_MAIN_SHOW_LOGS, bool) and not WINDOWS
3433 host.showPluginBridges = settings.value(CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES, bool)
3434 host.showWineBridges = settings.value(CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES, CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES, bool)
3435 host.uiBridgesTimeout = settings.value(CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT, CARLA_DEFAULT_UI_BRIDGES_TIMEOUT, int)
3436 host.uisAlwaysOnTop = settings.value(CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP, CARLA_DEFAULT_UIS_ALWAYS_ON_TOP, bool)
3438 if host.isPlugin:
3439 return
3441 host.transportExtra = settings.value(CARLA_KEY_ENGINE_TRANSPORT_EXTRA, "", str)
3443 # enums
3444 if host.audioDriverForced is None:
3445 host.transportMode = settings.value(CARLA_KEY_ENGINE_TRANSPORT_MODE, CARLA_DEFAULT_TRANSPORT_MODE, int)
3447 if not host.processModeForced:
3448 host.processMode = settings.value(CARLA_KEY_ENGINE_PROCESS_MODE, CARLA_DEFAULT_PROCESS_MODE, int)
3450 host.nextProcessMode = host.processMode
3452 # --------------------------------------------------------------------------------------------------------
3453 # fix things if needed
3455 if host.processMode == ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS:
3456 if LADISH_APP_NAME:
3457 print("LADISH detected but using multiple clients (not allowed), forcing single client now")
3458 host.nextProcessMode = host.processMode = ENGINE_PROCESS_MODE_SINGLE_CLIENT
3460 else:
3461 host.transportMode = ENGINE_TRANSPORT_MODE_JACK
3463 if gCarla.nogui:
3464 host.showLogs = False
3466 # --------------------------------------------------------------------------------------------------------
3467 # run headless host now if nogui option enabled
3469 if gCarla.nogui:
3470 runHostWithoutUI(host)
3472 # ------------------------------------------------------------------------------------------------------------
3473 # Set host settings
3475 def setHostSettings(host):
3476 # kdevelop likes this :)
3477 if False: host = CarlaHostNull()
3479 host.set_engine_option(ENGINE_OPTION_FORCE_STEREO, host.forceStereo, "")
3480 host.set_engine_option(ENGINE_OPTION_MAX_PARAMETERS, host.maxParameters, "")
3481 host.set_engine_option(ENGINE_OPTION_RESET_XRUNS, host.resetXruns, "")
3482 host.set_engine_option(ENGINE_OPTION_PREFER_PLUGIN_BRIDGES, host.preferPluginBridges, "")
3483 host.set_engine_option(ENGINE_OPTION_PREFER_UI_BRIDGES, host.preferUIBridges, "")
3484 host.set_engine_option(ENGINE_OPTION_PREVENT_BAD_BEHAVIOUR, host.preventBadBehaviour, "")
3485 host.set_engine_option(ENGINE_OPTION_UI_BRIDGES_TIMEOUT, host.uiBridgesTimeout, "")
3486 host.set_engine_option(ENGINE_OPTION_UIS_ALWAYS_ON_TOP, host.uisAlwaysOnTop, "")
3488 if host.isPlugin or host.isRemote or host.is_engine_running():
3489 return
3491 host.set_engine_option(ENGINE_OPTION_PROCESS_MODE, host.nextProcessMode, "")
3492 host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, host.transportMode, host.transportExtra)
3493 host.set_engine_option(ENGINE_OPTION_DEBUG_CONSOLE_OUTPUT, host.showLogs, "")
3495 if not (NSM_URL and host.nsmOK):
3496 host.set_engine_option(ENGINE_OPTION_CLIENT_NAME_PREFIX, 0, gCarla.cnprefix)
3498 # ---------------------------------------------------------------------------------------------------------------------
3499 # Set Engine settings according to carla preferences. Returns selected audio driver.
3501 def setEngineSettings(host, settings, oscPort = None):
3502 # kdevelop likes this :)
3503 if False: host = CarlaHostNull()
3505 # -----------------------------------------------------------------------------------------------------------------
3506 # do nothing if control
3508 if host.isControl:
3509 return "Control"
3511 # -----------------------------------------------------------------------------------------------------------------
3512 # fetch settings as needed
3514 if settings is None:
3515 qsettings = QSafeSettings("falkTX", "Carla2")
3517 if host.audioDriverForced is not None:
3518 audioDriver = host.audioDriverForced
3519 else:
3520 audioDriver = qsettings.value(CARLA_KEY_ENGINE_AUDIO_DRIVER, CARLA_DEFAULT_AUDIO_DRIVER, str)
3522 audioDriverPrefix = CARLA_KEY_ENGINE_DRIVER_PREFIX + audioDriver
3524 settings = {
3525 # engine
3526 CARLA_KEY_ENGINE_AUDIO_DRIVER: audioDriver,
3527 CARLA_KEY_ENGINE_AUDIO_DEVICE: qsettings.value(audioDriverPrefix+"/Device", "", str),
3528 CARLA_KEY_ENGINE_BUFFER_SIZE: qsettings.value(audioDriverPrefix+"/BufferSize", CARLA_DEFAULT_AUDIO_BUFFER_SIZE, int),
3529 CARLA_KEY_ENGINE_SAMPLE_RATE: qsettings.value(audioDriverPrefix+"/SampleRate", CARLA_DEFAULT_AUDIO_SAMPLE_RATE, int),
3530 CARLA_KEY_ENGINE_TRIPLE_BUFFER: qsettings.value(audioDriverPrefix+"/TripleBuffer", CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER, bool),
3532 # file paths
3533 CARLA_KEY_PATHS_AUDIO: splitter.join(qsettings.value(CARLA_KEY_PATHS_AUDIO, CARLA_DEFAULT_FILE_PATH_AUDIO, list)),
3534 CARLA_KEY_PATHS_MIDI: splitter.join(qsettings.value(CARLA_KEY_PATHS_MIDI, CARLA_DEFAULT_FILE_PATH_MIDI, list)),
3536 # plugin paths
3537 CARLA_KEY_PATHS_LADSPA: splitter.join(qsettings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list)),
3538 CARLA_KEY_PATHS_DSSI: splitter.join(qsettings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list)),
3539 CARLA_KEY_PATHS_LV2: splitter.join(qsettings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)),
3540 CARLA_KEY_PATHS_VST2: splitter.join(qsettings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list)),
3541 CARLA_KEY_PATHS_VST3: splitter.join(qsettings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list)),
3542 CARLA_KEY_PATHS_SF2: splitter.join(qsettings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list)),
3543 CARLA_KEY_PATHS_SFZ: splitter.join(qsettings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)),
3544 CARLA_KEY_PATHS_JSFX: splitter.join(qsettings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)),
3545 CARLA_KEY_PATHS_CLAP: splitter.join(qsettings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list)),
3547 # osc
3548 CARLA_KEY_OSC_ENABLED: qsettings.value(CARLA_KEY_OSC_ENABLED, CARLA_DEFAULT_OSC_ENABLED, bool),
3549 CARLA_KEY_OSC_TCP_PORT_ENABLED: qsettings.value(CARLA_KEY_OSC_TCP_PORT_ENABLED, CARLA_DEFAULT_OSC_TCP_PORT_ENABLED, bool),
3550 CARLA_KEY_OSC_TCP_PORT_RANDOM: qsettings.value(CARLA_KEY_OSC_TCP_PORT_RANDOM, CARLA_DEFAULT_OSC_TCP_PORT_RANDOM, bool),
3551 CARLA_KEY_OSC_TCP_PORT_NUMBER: qsettings.value(CARLA_KEY_OSC_TCP_PORT_NUMBER, CARLA_DEFAULT_OSC_TCP_PORT_NUMBER, int),
3552 CARLA_KEY_OSC_UDP_PORT_ENABLED: qsettings.value(CARLA_KEY_OSC_UDP_PORT_ENABLED, CARLA_DEFAULT_OSC_UDP_PORT_ENABLED, bool),
3553 CARLA_KEY_OSC_UDP_PORT_RANDOM: qsettings.value(CARLA_KEY_OSC_UDP_PORT_RANDOM, CARLA_DEFAULT_OSC_UDP_PORT_RANDOM, bool),
3554 CARLA_KEY_OSC_UDP_PORT_NUMBER: qsettings.value(CARLA_KEY_OSC_UDP_PORT_NUMBER, CARLA_DEFAULT_OSC_UDP_PORT_NUMBER, int),
3556 # wine
3557 CARLA_KEY_WINE_EXECUTABLE: qsettings.value(CARLA_KEY_WINE_EXECUTABLE, CARLA_DEFAULT_WINE_EXECUTABLE, str),
3558 CARLA_KEY_WINE_AUTO_PREFIX: qsettings.value(CARLA_KEY_WINE_AUTO_PREFIX, CARLA_DEFAULT_WINE_AUTO_PREFIX, bool),
3559 CARLA_KEY_WINE_FALLBACK_PREFIX: qsettings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str),
3560 CARLA_KEY_WINE_RT_PRIO_ENABLED: qsettings.value(CARLA_KEY_WINE_RT_PRIO_ENABLED, CARLA_DEFAULT_WINE_RT_PRIO_ENABLED, bool),
3561 CARLA_KEY_WINE_BASE_RT_PRIO: qsettings.value(CARLA_KEY_WINE_BASE_RT_PRIO, CARLA_DEFAULT_WINE_BASE_RT_PRIO, int),
3562 CARLA_KEY_WINE_SERVER_RT_PRIO: qsettings.value(CARLA_KEY_WINE_SERVER_RT_PRIO, CARLA_DEFAULT_WINE_SERVER_RT_PRIO, int),
3565 # -----------------------------------------------------------------------------------------------------------------
3566 # main settings
3568 setHostSettings(host)
3570 # -----------------------------------------------------------------------------------------------------------------
3571 # file paths
3573 host.set_engine_option(ENGINE_OPTION_FILE_PATH, FILE_AUDIO, settings[CARLA_KEY_PATHS_AUDIO])
3574 host.set_engine_option(ENGINE_OPTION_FILE_PATH, FILE_MIDI, settings[CARLA_KEY_PATHS_MIDI])
3576 # -----------------------------------------------------------------------------------------------------------------
3577 # plugin paths
3579 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, settings[CARLA_KEY_PATHS_LADSPA])
3580 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, settings[CARLA_KEY_PATHS_DSSI])
3581 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, settings[CARLA_KEY_PATHS_LV2])
3582 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST2, settings[CARLA_KEY_PATHS_VST2])
3583 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, settings[CARLA_KEY_PATHS_VST3])
3584 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SF2, settings[CARLA_KEY_PATHS_SF2])
3585 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_SFZ, settings[CARLA_KEY_PATHS_SFZ])
3586 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, settings[CARLA_KEY_PATHS_JSFX])
3587 host.set_engine_option(ENGINE_OPTION_PLUGIN_PATH, PLUGIN_CLAP, settings[CARLA_KEY_PATHS_CLAP])
3589 # -----------------------------------------------------------------------------------------------------------------
3590 # don't continue if plugin
3592 if host.isPlugin:
3593 return "Plugin"
3595 # -----------------------------------------------------------------------------------------------------------------
3596 # osc settings
3598 if oscPort is not None and isinstance(oscPort, int):
3599 oscEnabled = True
3600 portNumTCP = portNumUDP = oscPort
3602 else:
3603 oscEnabled = settings[CARLA_KEY_OSC_ENABLED]
3605 if not settings[CARLA_KEY_OSC_TCP_PORT_ENABLED]:
3606 portNumTCP = -1
3607 elif settings[CARLA_KEY_OSC_TCP_PORT_RANDOM]:
3608 portNumTCP = 0
3609 else:
3610 portNumTCP = settings[CARLA_KEY_OSC_TCP_PORT_NUMBER]
3612 if not settings[CARLA_KEY_OSC_UDP_PORT_ENABLED]:
3613 portNumUDP = -1
3614 elif settings[CARLA_KEY_OSC_UDP_PORT_RANDOM]:
3615 portNumUDP = 0
3616 else:
3617 portNumUDP = settings[CARLA_KEY_OSC_UDP_PORT_NUMBER]
3619 host.set_engine_option(ENGINE_OPTION_OSC_ENABLED, 1 if oscEnabled else 0, "")
3620 host.set_engine_option(ENGINE_OPTION_OSC_PORT_TCP, portNumTCP, "")
3621 host.set_engine_option(ENGINE_OPTION_OSC_PORT_UDP, portNumUDP, "")
3623 # -----------------------------------------------------------------------------------------------------------------
3624 # wine settings
3626 host.set_engine_option(ENGINE_OPTION_WINE_EXECUTABLE, 0, settings[CARLA_KEY_WINE_EXECUTABLE])
3627 host.set_engine_option(ENGINE_OPTION_WINE_AUTO_PREFIX, 1 if settings[CARLA_KEY_WINE_AUTO_PREFIX] else 0, "")
3628 host.set_engine_option(ENGINE_OPTION_WINE_FALLBACK_PREFIX, 0, os.path.expanduser(settings[CARLA_KEY_WINE_FALLBACK_PREFIX]))
3629 host.set_engine_option(ENGINE_OPTION_WINE_RT_PRIO_ENABLED, 1 if settings[CARLA_KEY_WINE_RT_PRIO_ENABLED] else 0, "")
3630 host.set_engine_option(ENGINE_OPTION_WINE_BASE_RT_PRIO, settings[CARLA_KEY_WINE_BASE_RT_PRIO], "")
3631 host.set_engine_option(ENGINE_OPTION_WINE_SERVER_RT_PRIO, settings[CARLA_KEY_WINE_SERVER_RT_PRIO], "")
3633 # -----------------------------------------------------------------------------------------------------------------
3634 # driver and device settings
3636 # driver name
3637 audioDriver = settings[CARLA_KEY_ENGINE_AUDIO_DRIVER]
3639 # Only setup audio things if engine is not running
3640 if not host.is_engine_running():
3641 host.set_engine_option(ENGINE_OPTION_AUDIO_DRIVER, 0, audioDriver)
3642 host.set_engine_option(ENGINE_OPTION_AUDIO_DEVICE, 0, settings[CARLA_KEY_ENGINE_AUDIO_DEVICE])
3644 if not audioDriver.startswith("JACK"):
3645 host.set_engine_option(ENGINE_OPTION_AUDIO_BUFFER_SIZE, settings[CARLA_KEY_ENGINE_BUFFER_SIZE], "")
3646 host.set_engine_option(ENGINE_OPTION_AUDIO_SAMPLE_RATE, settings[CARLA_KEY_ENGINE_SAMPLE_RATE], "")
3647 host.set_engine_option(ENGINE_OPTION_AUDIO_TRIPLE_BUFFER, 1 if settings[CARLA_KEY_ENGINE_TRIPLE_BUFFER] else 0, "")
3649 # -----------------------------------------------------------------------------------------------------------------
3650 # fix things if needed
3652 if audioDriver != "JACK" and host.transportMode == ENGINE_TRANSPORT_MODE_JACK:
3653 host.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL
3654 host.set_engine_option(ENGINE_OPTION_TRANSPORT_MODE, ENGINE_TRANSPORT_MODE_INTERNAL, host.transportExtra)
3656 # -----------------------------------------------------------------------------------------------------------------
3657 # return selected driver name
3659 return audioDriver
3661 # ---------------------------------------------------------------------------------------------------------------------
3662 # Run Carla without showing UI
3664 def runHostWithoutUI(host):
3665 # kdevelop likes this :)
3666 if False: host = CarlaHostNull()
3668 # --------------------------------------------------------------------------------------------------------
3669 # Some initial checks
3671 if not gCarla.nogui:
3672 return
3674 projectFile = getInitialProjectFile(True)
3676 if gCarla.nogui is True:
3677 oscPort = None
3679 if not projectFile:
3680 print("Carla no-gui mode can only be used together with a project file.")
3681 sys.exit(1)
3683 else:
3684 oscPort = gCarla.nogui
3686 # --------------------------------------------------------------------------------------------------------
3687 # Additional imports
3689 from time import sleep
3691 # --------------------------------------------------------------------------------------------------------
3692 # Init engine
3694 audioDriver = setEngineSettings(host, None, oscPort)
3695 if not host.engine_init(audioDriver, CARLA_CLIENT_NAME or "Carla"):
3696 print("Engine failed to initialize, possible reasons:\n%s" % host.get_last_error())
3697 sys.exit(1)
3699 if projectFile and not host.load_project(projectFile):
3700 print("Failed to load selected project file, possible reasons:\n%s" % host.get_last_error())
3701 host.engine_close()
3702 sys.exit(1)
3704 # --------------------------------------------------------------------------------------------------------
3705 # Idle
3707 print("Carla ready!")
3709 while host.is_engine_running() and not gCarla.term:
3710 host.engine_idle()
3711 sleep(0.0333) # 30 Hz
3713 # --------------------------------------------------------------------------------------------------------
3714 # Stop
3716 host.engine_close()
3717 sys.exit(0)
3719 # ------------------------------------------------------------------------------------------------------------