Cleanup
[carla.git] / source / frontend / carla_shared.py
blobe8a7c66e2e3f14d41596980953f0494e813c74fc
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 os
9 import sys
11 from math import fmod
13 # ------------------------------------------------------------------------------------------------------------
14 # Imports (Signal)
16 from signal import signal, SIGINT, SIGTERM
18 try:
19 from signal import SIGUSR1
20 haveSIGUSR1 = True
21 except:
22 haveSIGUSR1 = False
24 # ------------------------------------------------------------------------------------------------------------
25 # Imports (PyQt)
27 from qt_compat import qt_config
29 if qt_config == 5:
30 # import changed in PyQt 5.15.8, so try both
31 try:
32 from PyQt5.Qt import PYQT_VERSION_STR
33 except ImportError:
34 from PyQt5.QtCore import PYQT_VERSION_STR
36 from PyQt5.QtCore import qFatal, QT_VERSION, QT_VERSION_STR, qWarning, QDir, QSettings
37 from PyQt5.QtGui import QIcon
38 from PyQt5.QtWidgets import QFileDialog, QMessageBox
40 elif qt_config == 6:
41 from PyQt6.QtCore import qFatal, PYQT_VERSION_STR, QT_VERSION, QT_VERSION_STR, qWarning, QDir, QSettings
42 from PyQt6.QtGui import QIcon
43 from PyQt6.QtWidgets import QFileDialog, QMessageBox
45 # ------------------------------------------------------------------------------------------------------------
46 # Imports (Custom)
48 from carla_backend import (
49 MAX_DEFAULT_PARAMETERS,
50 ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS,
51 ENGINE_PROCESS_MODE_PATCHBAY,
52 ENGINE_TRANSPORT_MODE_INTERNAL,
53 ENGINE_TRANSPORT_MODE_JACK
56 from common import kIs64bit, HAIKU, LINUX, MACOS, WINDOWS, VERSION
58 # ------------------------------------------------------------------------------------------------------------
59 # Config
61 # These will be modified during install
62 X_LIBDIR_X = None
63 X_DATADIR_X = None
65 # ------------------------------------------------------------------------------------------------------------
66 # Platform specific stuff
68 if WINDOWS:
69 WINDIR = os.getenv("WINDIR")
71 # ------------------------------------------------------------------------------------------------------------
72 # Set TMP
74 envTMP = os.getenv("TMP")
76 if envTMP is None:
77 if WINDOWS:
78 qWarning("TMP variable not set")
79 TMP = QDir.tempPath()
80 else:
81 TMP = envTMP
83 if not os.path.exists(TMP):
84 qWarning("TMP does not exist")
85 TMP = "/"
87 del envTMP
89 # ------------------------------------------------------------------------------------------------------------
90 # Set HOME
92 envHOME = os.getenv("HOME")
94 if envHOME is None:
95 if not WINDOWS:
96 qWarning("HOME variable not set")
97 HOME = QDir.toNativeSeparators(QDir.homePath())
98 else:
99 HOME = envHOME
101 if not os.path.exists(HOME):
102 qWarning("HOME does not exist")
103 HOME = TMP
105 del envHOME
107 # ------------------------------------------------------------------------------------------------------------
108 # Set PATH
110 envPATH = os.getenv("PATH")
112 if envPATH is None:
113 qWarning("PATH variable not set")
114 if MACOS:
115 PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin")
116 elif WINDOWS:
117 PATH = (os.path.join(WINDIR, "system32"), WINDIR)
118 else:
119 PATH = ("/usr/local/bin", "/usr/bin", "/bin")
120 else:
121 PATH = envPATH.split(os.pathsep)
123 del envPATH
125 # ------------------------------------------------------------------------------------------------------------
126 # Static MIDI CC list
128 MIDI_CC_LIST = (
129 "01 [0x01] Modulation",
130 "02 [0x02] Breath",
131 "04 [0x04] Foot",
132 "05 [0x05] Portamento",
133 "07 [0x07] Volume",
134 "08 [0x08] Balance",
135 "10 [0x0A] Pan",
136 "11 [0x0B] Expression",
137 "12 [0x0C] FX Control 1",
138 "13 [0x0D] FX Control 2",
139 "16 [0x10] General Purpose 1",
140 "17 [0x11] General Purpose 2",
141 "18 [0x12] General Purpose 3",
142 "19 [0x13] General Purpose 4",
143 "70 [0x46] Control 1 [Variation]",
144 "71 [0x47] Control 2 [Timbre]",
145 "72 [0x48] Control 3 [Release]",
146 "73 [0x49] Control 4 [Attack]",
147 "74 [0x4A] Control 5 [Brightness]",
148 "75 [0x4B] Control 6 [Decay]",
149 "76 [0x4C] Control 7 [Vib Rate]",
150 "77 [0x4D] Control 8 [Vib Depth]",
151 "78 [0x4E] Control 9 [Vib Delay]",
152 "79 [0x4F] Control 10 [Undefined]",
153 "80 [0x50] General Purpose 5",
154 "81 [0x51] General Purpose 6",
155 "82 [0x52] General Purpose 7",
156 "83 [0x53] General Purpose 8",
157 "84 [0x54] Portamento Control",
158 "91 [0x5B] FX 1 Depth [Reverb]",
159 "92 [0x5C] FX 2 Depth [Tremolo]",
160 "93 [0x5D] FX 3 Depth [Chorus]",
161 "94 [0x5E] FX 4 Depth [Detune]",
162 "95 [0x5F] FX 5 Depth [Phaser]"
165 MAX_MIDI_CC_LIST_ITEM = 95
167 # ------------------------------------------------------------------------------------------------------------
168 # PatchCanvas defines
170 CANVAS_ANTIALIASING_SMALL = 1
171 CANVAS_EYECANDY_SMALL = 1
173 # ------------------------------------------------------------------------------------------------------------
174 # Carla Settings keys
176 CARLA_KEY_MAIN_PROJECT_FOLDER = "Main/ProjectFolder" # str
177 CARLA_KEY_MAIN_USE_PRO_THEME = "Main/UseProTheme" # bool
178 CARLA_KEY_MAIN_PRO_THEME_COLOR = "Main/ProThemeColor" # str
179 CARLA_KEY_MAIN_REFRESH_INTERVAL = "Main/RefreshInterval" # int
180 CARLA_KEY_MAIN_CONFIRM_EXIT = "Main/ConfirmExit" # bool
181 CARLA_KEY_MAIN_CLASSIC_SKIN = "Main/ClassicSkin" # bool
182 CARLA_KEY_MAIN_SHOW_LOGS = "Main/ShowLogs" # bool
183 CARLA_KEY_MAIN_SYSTEM_ICONS = "Main/SystemIcons" # bool
184 CARLA_KEY_MAIN_EXPERIMENTAL = "Main/Experimental" # bool
186 CARLA_KEY_CANVAS_THEME = "Canvas/Theme" # str
187 CARLA_KEY_CANVAS_SIZE = "Canvas/Size" # str "NxN"
188 CARLA_KEY_CANVAS_USE_BEZIER_LINES = "Canvas/UseBezierLines" # bool
189 CARLA_KEY_CANVAS_AUTO_HIDE_GROUPS = "Canvas/AutoHideGroups" # bool
190 CARLA_KEY_CANVAS_AUTO_SELECT_ITEMS = "Canvas/AutoSelectItems" # bool
191 CARLA_KEY_CANVAS_EYE_CANDY = "Canvas/EyeCandy2" # bool
192 CARLA_KEY_CANVAS_FANCY_EYE_CANDY = "Canvas/FancyEyeCandy" # bool
193 CARLA_KEY_CANVAS_USE_OPENGL = "Canvas/UseOpenGL" # bool
194 CARLA_KEY_CANVAS_ANTIALIASING = "Canvas/Antialiasing" # enum
195 CARLA_KEY_CANVAS_HQ_ANTIALIASING = "Canvas/HQAntialiasing" # bool
196 CARLA_KEY_CANVAS_INLINE_DISPLAYS = "Canvas/InlineDisplays" # bool
197 CARLA_KEY_CANVAS_FULL_REPAINTS = "Canvas/FullRepaints" # bool
199 CARLA_KEY_ENGINE_DRIVER_PREFIX = "Engine/Driver-"
200 CARLA_KEY_ENGINE_AUDIO_DRIVER = "Engine/AudioDriver" # str
201 CARLA_KEY_ENGINE_AUDIO_DEVICE = "Engine/AudioDevice" # str
202 CARLA_KEY_ENGINE_BUFFER_SIZE = "Engine/BufferSize" # int
203 CARLA_KEY_ENGINE_SAMPLE_RATE = "Engine/SampleRate" # int
204 CARLA_KEY_ENGINE_TRIPLE_BUFFER = "Engine/TripleBuffer" # bool
205 CARLA_KEY_ENGINE_PROCESS_MODE = "Engine/ProcessMode" # enum
206 CARLA_KEY_ENGINE_TRANSPORT_MODE = "Engine/TransportMode" # enum
207 CARLA_KEY_ENGINE_TRANSPORT_EXTRA = "Engine/TransportExtra" # str
208 CARLA_KEY_ENGINE_FORCE_STEREO = "Engine/ForceStereo" # bool
209 CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES = "Engine/PreferPluginBridges" # bool
210 CARLA_KEY_ENGINE_PREFER_UI_BRIDGES = "Engine/PreferUiBridges" # bool
211 CARLA_KEY_ENGINE_MANAGE_UIS = "Engine/ManageUIs" # bool
212 CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP = "Engine/UIsAlwaysOnTop" # bool
213 CARLA_KEY_ENGINE_MAX_PARAMETERS = "Engine/MaxParameters" # int
214 CARLA_KEY_ENGINE_RESET_XRUNS = "Engine/ResetXruns" # bool
215 CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT = "Engine/UiBridgesTimeout" # int
217 CARLA_KEY_OSC_ENABLED = "OSC/Enabled"
218 CARLA_KEY_OSC_TCP_PORT_ENABLED = "OSC/TCPEnabled"
219 CARLA_KEY_OSC_TCP_PORT_NUMBER = "OSC/TCPNumber"
220 CARLA_KEY_OSC_TCP_PORT_RANDOM = "OSC/TCPRandom"
221 CARLA_KEY_OSC_UDP_PORT_ENABLED = "OSC/UDPEnabled"
222 CARLA_KEY_OSC_UDP_PORT_NUMBER = "OSC/UDPNumber"
223 CARLA_KEY_OSC_UDP_PORT_RANDOM = "OSC/UDPRandom"
225 CARLA_KEY_PATHS_AUDIO = "Paths/Audio"
226 CARLA_KEY_PATHS_MIDI = "Paths/MIDI"
228 CARLA_KEY_PATHS_LADSPA = "Paths/LADSPA"
229 CARLA_KEY_PATHS_DSSI = "Paths/DSSI"
230 CARLA_KEY_PATHS_LV2 = "Paths/LV2"
231 CARLA_KEY_PATHS_VST2 = "Paths/VST2"
232 CARLA_KEY_PATHS_VST3 = "Paths/VST3"
233 CARLA_KEY_PATHS_CLAP = "Paths/CLAP"
234 CARLA_KEY_PATHS_SF2 = "Paths/SF2"
235 CARLA_KEY_PATHS_SFZ = "Paths/SFZ"
236 CARLA_KEY_PATHS_JSFX = "Paths/JSFX"
238 CARLA_KEY_WINE_EXECUTABLE = "Wine/Executable" # str
239 CARLA_KEY_WINE_AUTO_PREFIX = "Wine/AutoPrefix" # bool
240 CARLA_KEY_WINE_FALLBACK_PREFIX = "Wine/FallbackPrefix" # str
241 CARLA_KEY_WINE_RT_PRIO_ENABLED = "Wine/RtPrioEnabled" # bool
242 CARLA_KEY_WINE_BASE_RT_PRIO = "Wine/BaseRtPrio" # int
243 CARLA_KEY_WINE_SERVER_RT_PRIO = "Wine/ServerRtPrio" # int
245 CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES = "Experimental/PluginBridges" # bool
246 CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES = "Experimental/WineBridges" # bool
247 CARLA_KEY_EXPERIMENTAL_JACK_APPS = "Experimental/JackApplications" # bool
248 CARLA_KEY_EXPERIMENTAL_EXPORT_LV2 = "Experimental/ExportLV2" # bool
249 CARLA_KEY_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = "Experimental/PreventBadBehaviour" # bool
250 CARLA_KEY_EXPERIMENTAL_LOAD_LIB_GLOBAL = "Experimental/LoadLibGlobal" # bool
252 # if pro theme is on and color is black
253 CARLA_KEY_CUSTOM_PAINTING = "UseCustomPainting" # bool
255 # ------------------------------------------------------------------------------------------------------------
256 # Carla Settings defaults
258 # Main
259 CARLA_DEFAULT_MAIN_PROJECT_FOLDER = HOME
260 CARLA_DEFAULT_MAIN_USE_PRO_THEME = True
261 CARLA_DEFAULT_MAIN_PRO_THEME_COLOR = "Black"
262 CARLA_DEFAULT_MAIN_REFRESH_INTERVAL = 20
263 CARLA_DEFAULT_MAIN_CONFIRM_EXIT = True
264 CARLA_DEFAULT_MAIN_CLASSIC_SKIN = False
265 CARLA_DEFAULT_MAIN_SHOW_LOGS = bool(not WINDOWS)
266 CARLA_DEFAULT_MAIN_SYSTEM_ICONS = False
267 CARLA_DEFAULT_MAIN_EXPERIMENTAL = False
269 # Canvas
270 CARLA_DEFAULT_CANVAS_THEME = "Modern Dark"
271 CARLA_DEFAULT_CANVAS_SIZE = "3100x2400"
272 CARLA_DEFAULT_CANVAS_SIZE_WIDTH = 3100
273 CARLA_DEFAULT_CANVAS_SIZE_HEIGHT = 2400
274 CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES = True
275 CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS = True
276 CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS = False
277 CARLA_DEFAULT_CANVAS_EYE_CANDY = True
278 CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY = False
279 CARLA_DEFAULT_CANVAS_USE_OPENGL = False
280 CARLA_DEFAULT_CANVAS_ANTIALIASING = CANVAS_ANTIALIASING_SMALL
281 CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING = False
282 CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS = False
283 CARLA_DEFAULT_CANVAS_FULL_REPAINTS = False
285 # Engine
286 CARLA_DEFAULT_FORCE_STEREO = False
287 CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
288 CARLA_DEFAULT_PREFER_UI_BRIDGES = True
289 CARLA_DEFAULT_MANAGE_UIS = True
290 CARLA_DEFAULT_UIS_ALWAYS_ON_TOP = False
291 CARLA_DEFAULT_MAX_PARAMETERS = MAX_DEFAULT_PARAMETERS
292 CARLA_DEFAULT_RESET_XRUNS = False
293 CARLA_DEFAULT_UI_BRIDGES_TIMEOUT = 4000
295 CARLA_DEFAULT_AUDIO_BUFFER_SIZE = 512
296 CARLA_DEFAULT_AUDIO_SAMPLE_RATE = 44100
297 CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER = False
299 if HAIKU:
300 CARLA_DEFAULT_AUDIO_DRIVER = "SDL"
301 elif MACOS:
302 CARLA_DEFAULT_AUDIO_DRIVER = "CoreAudio"
303 elif WINDOWS:
304 CARLA_DEFAULT_AUDIO_DRIVER = "Windows Audio"
305 elif os.path.exists("/usr/bin/jackd") or os.path.exists("/usr/bin/jackdbus") or os.path.exists("/usr/bin/pw-jack"):
306 CARLA_DEFAULT_AUDIO_DRIVER = "JACK"
307 else:
308 CARLA_DEFAULT_AUDIO_DRIVER = "PulseAudio"
310 if CARLA_DEFAULT_AUDIO_DRIVER == "JACK":
311 CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS
312 CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_JACK
313 else:
314 CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_PATCHBAY
315 CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_INTERNAL
317 # OSC
318 CARLA_DEFAULT_OSC_ENABLED = not (MACOS or WINDOWS)
319 CARLA_DEFAULT_OSC_TCP_PORT_ENABLED = True
320 CARLA_DEFAULT_OSC_TCP_PORT_NUMBER = 22752
321 CARLA_DEFAULT_OSC_TCP_PORT_RANDOM = False
322 CARLA_DEFAULT_OSC_UDP_PORT_ENABLED = True
323 CARLA_DEFAULT_OSC_UDP_PORT_NUMBER = 22752
324 CARLA_DEFAULT_OSC_UDP_PORT_RANDOM = False
326 # Wine
327 CARLA_DEFAULT_WINE_EXECUTABLE = "wine"
328 CARLA_DEFAULT_WINE_AUTO_PREFIX = True
329 CARLA_DEFAULT_WINE_FALLBACK_PREFIX = os.path.expanduser("~/.wine")
330 CARLA_DEFAULT_WINE_RT_PRIO_ENABLED = True
331 CARLA_DEFAULT_WINE_BASE_RT_PRIO = 15
332 CARLA_DEFAULT_WINE_SERVER_RT_PRIO = 10
334 # Experimental
335 CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES = False
336 CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES = False
337 CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS = False
338 CARLA_DEFAULT_EXPERIMENTAL_LV2_EXPORT = False
339 CARLA_DEFAULT_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = False
340 CARLA_DEFAULT_EXPERIMENTAL_LOAD_LIB_GLOBAL = False
342 # ------------------------------------------------------------------------------------------------------------
343 # Default File Folders
345 CARLA_DEFAULT_FILE_PATH_AUDIO = []
346 CARLA_DEFAULT_FILE_PATH_MIDI = []
348 # ------------------------------------------------------------------------------------------------------------
349 # Default Plugin Folders (get)
351 DEFAULT_LADSPA_PATH = ""
352 DEFAULT_DSSI_PATH = ""
353 DEFAULT_LV2_PATH = ""
354 DEFAULT_VST2_PATH = ""
355 DEFAULT_VST3_PATH = ""
356 DEFAULT_CLAP_PATH = ""
357 DEFAULT_SF2_PATH = ""
358 DEFAULT_SFZ_PATH = ""
359 DEFAULT_JSFX_PATH = ""
361 if WINDOWS:
362 splitter = ";"
364 APPDATA = os.getenv("APPDATA")
365 LOCALAPPDATA = os.getenv("LOCALAPPDATA", APPDATA)
366 PROGRAMFILES = os.getenv("PROGRAMFILES")
367 PROGRAMFILESx86 = os.getenv("PROGRAMFILES(x86)")
368 COMMONPROGRAMFILES = os.getenv("COMMONPROGRAMFILES")
369 COMMONPROGRAMFILESx86 = os.getenv("COMMONPROGRAMFILES(x86)")
371 # Small integrity tests
372 if not APPDATA:
373 qFatal("APPDATA variable not set, cannot continue")
374 sys.exit(1)
376 if not PROGRAMFILES:
377 qFatal("PROGRAMFILES variable not set, cannot continue")
378 sys.exit(1)
380 if not COMMONPROGRAMFILES:
381 qFatal("COMMONPROGRAMFILES variable not set, cannot continue")
382 sys.exit(1)
384 DEFAULT_LADSPA_PATH = APPDATA + "\\LADSPA"
385 DEFAULT_LADSPA_PATH += ";" + PROGRAMFILES + "\\LADSPA"
387 DEFAULT_DSSI_PATH = APPDATA + "\\DSSI"
388 DEFAULT_DSSI_PATH += ";" + PROGRAMFILES + "\\DSSI"
390 DEFAULT_LV2_PATH = APPDATA + "\\LV2"
391 DEFAULT_LV2_PATH += ";" + COMMONPROGRAMFILES + "\\LV2"
393 DEFAULT_VST2_PATH = PROGRAMFILES + "\\VstPlugins"
394 DEFAULT_VST2_PATH += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins"
396 DEFAULT_JSFX_PATH = APPDATA + "\\REAPER\\Effects"
397 #DEFAULT_JSFX_PATH += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects"
399 if kIs64bit:
400 DEFAULT_VST2_PATH += ";" + COMMONPROGRAMFILES + "\\VST2"
402 DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\VST3"
403 DEFAULT_VST3_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3"
405 DEFAULT_CLAP_PATH = COMMONPROGRAMFILES + "\\CLAP"
406 DEFAULT_CLAP_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP"
408 DEFAULT_SF2_PATH = APPDATA + "\\SF2"
409 DEFAULT_SFZ_PATH = APPDATA + "\\SFZ"
411 if PROGRAMFILESx86:
412 DEFAULT_LADSPA_PATH += ";" + PROGRAMFILESx86 + "\\LADSPA"
413 DEFAULT_DSSI_PATH += ";" + PROGRAMFILESx86 + "\\DSSI"
414 DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\VstPlugins"
415 DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins"
416 #DEFAULT_JSFX_PATH += ";" + PROGRAMFILESx86 + "\\REAPER\\InstallData\\Effects"
418 if COMMONPROGRAMFILESx86:
419 DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3"
420 DEFAULT_CLAP_PATH += COMMONPROGRAMFILESx86 + "\\CLAP"
422 elif HAIKU:
423 splitter = ":"
425 DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
426 DEFAULT_LADSPA_PATH += ":/system/add-ons/media/ladspaplugins"
427 DEFAULT_LADSPA_PATH += ":/system/lib/ladspa"
429 DEFAULT_DSSI_PATH = HOME + "/.dssi"
430 DEFAULT_DSSI_PATH += ":/system/add-ons/media/dssiplugins"
431 DEFAULT_DSSI_PATH += ":/system/lib/dssi"
433 DEFAULT_LV2_PATH = HOME + "/.lv2"
434 DEFAULT_LV2_PATH += ":/system/add-ons/media/lv2plugins"
436 DEFAULT_VST2_PATH = HOME + "/.vst"
437 DEFAULT_VST2_PATH += ":/system/add-ons/media/vstplugins"
439 DEFAULT_VST3_PATH = HOME + "/.vst3"
440 DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins"
442 DEFAULT_CLAP_PATH = HOME + "/.clap"
443 DEFAULT_CLAP_PATH += ":/system/add-ons/media/clapplugins"
445 elif MACOS:
446 splitter = ":"
448 DEFAULT_LADSPA_PATH = HOME + "/Library/Audio/Plug-Ins/LADSPA"
449 DEFAULT_LADSPA_PATH += ":/Library/Audio/Plug-Ins/LADSPA"
451 DEFAULT_DSSI_PATH = HOME + "/Library/Audio/Plug-Ins/DSSI"
452 DEFAULT_DSSI_PATH += ":/Library/Audio/Plug-Ins/DSSI"
454 DEFAULT_LV2_PATH = HOME + "/Library/Audio/Plug-Ins/LV2"
455 DEFAULT_LV2_PATH += ":/Library/Audio/Plug-Ins/LV2"
457 DEFAULT_VST2_PATH = HOME + "/Library/Audio/Plug-Ins/VST"
458 DEFAULT_VST2_PATH += ":/Library/Audio/Plug-Ins/VST"
460 DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3"
461 DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3"
463 DEFAULT_CLAP_PATH = HOME + "/Library/Audio/Plug-Ins/CLAP"
464 DEFAULT_CLAP_PATH += ":/Library/Audio/Plug-Ins/CLAP"
466 DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects"
467 #DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects"
469 else:
470 splitter = ":"
472 CONFIG_HOME = os.getenv("XDG_CONFIG_HOME", HOME + "/.config")
474 DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
475 DEFAULT_LADSPA_PATH += ":/usr/lib/ladspa"
476 DEFAULT_LADSPA_PATH += ":/usr/local/lib/ladspa"
478 DEFAULT_DSSI_PATH = HOME + "/.dssi"
479 DEFAULT_DSSI_PATH += ":/usr/lib/dssi"
480 DEFAULT_DSSI_PATH += ":/usr/local/lib/dssi"
482 DEFAULT_LV2_PATH = HOME + "/.lv2"
483 DEFAULT_LV2_PATH += ":/usr/lib/lv2"
484 DEFAULT_LV2_PATH += ":/usr/local/lib/lv2"
486 DEFAULT_VST2_PATH = HOME + "/.vst"
487 DEFAULT_VST2_PATH += ":/usr/lib/vst"
488 DEFAULT_VST2_PATH += ":/usr/local/lib/vst"
490 DEFAULT_VST2_PATH += HOME + "/.lxvst"
491 DEFAULT_VST2_PATH += ":/usr/lib/lxvst"
492 DEFAULT_VST2_PATH += ":/usr/local/lib/lxvst"
494 DEFAULT_VST3_PATH = HOME + "/.vst3"
495 DEFAULT_VST3_PATH += ":/usr/lib/vst3"
496 DEFAULT_VST3_PATH += ":/usr/local/lib/vst3"
498 DEFAULT_CLAP_PATH = HOME + "/.clap"
499 DEFAULT_CLAP_PATH += ":/usr/lib/clap"
500 DEFAULT_CLAP_PATH += ":/usr/local/lib/clap"
502 DEFAULT_SF2_PATH = HOME + "/.sounds/sf2"
503 DEFAULT_SF2_PATH += ":" + HOME + "/.sounds/sf3"
504 DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2"
505 DEFAULT_SF2_PATH += ":/usr/share/sounds/sf3"
506 DEFAULT_SF2_PATH += ":/usr/share/soundfonts"
508 DEFAULT_SFZ_PATH = HOME + "/.sounds/sfz"
509 DEFAULT_SFZ_PATH += ":/usr/share/sounds/sfz"
511 DEFAULT_JSFX_PATH = CONFIG_HOME + "/REAPER/Effects"
512 #DEFAULT_JSFX_PATH += ":" + "/opt/REAPER/InstallData/Effects"
514 if not WINDOWS:
515 winePrefix = os.getenv("WINEPREFIX")
517 if not winePrefix:
518 winePrefix = HOME + "/.wine"
520 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins"
521 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VSTPlugins"
522 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Steinberg/VstPlugins"
523 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Steinberg/VSTPlugins"
524 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST2"
525 DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3"
526 DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP"
528 if kIs64bit:
529 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins"
530 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VSTPlugins"
531 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VstPlugins"
532 DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VSTPlugins"
533 DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3"
534 DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP"
536 del winePrefix
538 # ------------------------------------------------------------------------------------------------------------
539 # Default Plugin Folders (set)
541 readEnvVars = True
543 if WINDOWS:
544 # Check if running Wine. If yes, ignore env vars
545 # pylint: disable=import-error
546 from winreg import ConnectRegistry, OpenKey, CloseKey, HKEY_CURRENT_USER
547 # pylint: enable=import-error
548 _reg = ConnectRegistry(None, HKEY_CURRENT_USER)
550 try:
551 _key = OpenKey(_reg, r"SOFTWARE\Wine")
552 CloseKey(_key)
553 del _key
554 readEnvVars = False
555 except:
556 pass
558 CloseKey(_reg)
559 del _reg
561 if readEnvVars:
562 CARLA_DEFAULT_LADSPA_PATH = os.getenv("LADSPA_PATH", DEFAULT_LADSPA_PATH).split(splitter)
563 CARLA_DEFAULT_DSSI_PATH = os.getenv("DSSI_PATH", DEFAULT_DSSI_PATH).split(splitter)
564 CARLA_DEFAULT_LV2_PATH = os.getenv("LV2_PATH", DEFAULT_LV2_PATH).split(splitter)
565 CARLA_DEFAULT_VST2_PATH = os.getenv("VST_PATH", DEFAULT_VST2_PATH).split(splitter)
566 CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter)
567 CARLA_DEFAULT_CLAP_PATH = os.getenv("CLAP_PATH", DEFAULT_CLAP_PATH).split(splitter)
568 CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter)
569 CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter)
570 CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter)
572 else:
573 CARLA_DEFAULT_LADSPA_PATH = DEFAULT_LADSPA_PATH.split(splitter)
574 CARLA_DEFAULT_DSSI_PATH = DEFAULT_DSSI_PATH.split(splitter)
575 CARLA_DEFAULT_LV2_PATH = DEFAULT_LV2_PATH.split(splitter)
576 CARLA_DEFAULT_VST2_PATH = DEFAULT_VST2_PATH.split(splitter)
577 CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter)
578 CARLA_DEFAULT_CLAP_PATH = DEFAULT_CLAP_PATH.split(splitter)
579 CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter)
580 CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter)
581 CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter)
583 # ------------------------------------------------------------------------------------------------------------
584 # Default Plugin Folders (cleanup)
586 del DEFAULT_LADSPA_PATH
587 del DEFAULT_DSSI_PATH
588 del DEFAULT_LV2_PATH
589 del DEFAULT_VST2_PATH
590 del DEFAULT_VST3_PATH
591 del DEFAULT_CLAP_PATH
592 del DEFAULT_SF2_PATH
593 del DEFAULT_SFZ_PATH
595 # ------------------------------------------------------------------------------------------------------------
596 # Global Carla object
598 class CarlaObject():
599 def __init__(self):
600 self.cnprefix = "" # Client name prefix
601 self.gui = None # Host Window
602 self.nogui = False # Skip UI
603 self.term = False # Terminated by OS signal
604 self.felib = None # Frontend lib object
605 self.utils = None # Utils object
607 gCarla = CarlaObject()
609 # ------------------------------------------------------------------------------------------------------------
610 # Set CWD
612 CWD = sys.path[0]
614 if not CWD:
615 CWD = os.path.dirname(sys.argv[0])
617 # make it work with cxfreeze
618 if os.path.isfile(CWD):
619 CWD = os.path.dirname(CWD)
620 if CWD.endswith("/lib"):
621 CWD = CWD.rsplit("/lib",1)[0]
622 CXFREEZE = True
623 if not WINDOWS:
624 os.environ['CARLA_MAGIC_FILE'] = os.path.join(CWD, "magic.mgc")
625 else:
626 CXFREEZE = False
628 # ------------------------------------------------------------------------------------------------------------
629 # Set DLL_EXTENSION
631 if WINDOWS:
632 DLL_EXTENSION = "dll"
633 elif MACOS:
634 DLL_EXTENSION = "dylib"
635 else:
636 DLL_EXTENSION = "so"
638 # ------------------------------------------------------------------------------------------------------------
639 # Find decimal points for a parameter, using step and stepSmall
641 def countDecimalPoints(step, stepSmall):
642 if stepSmall >= 1.0:
643 return 0
644 if step >= 1.0:
645 return 2
647 count = 0
648 value = fmod(abs(step), 1)
649 while 0.0001 < value < 0.999 and count < 6:
650 value = fmod(value*10, 1)
651 count += 1
653 return count
655 # ------------------------------------------------------------------------------------------------------------
656 # Check if a value is a number (float support)
658 def isNumber(value):
659 try:
660 float(value)
661 return True
662 except:
663 return False
665 # ------------------------------------------------------------------------------------------------------------
666 # Convert a value to a list
668 def toList(value):
669 if value is None:
670 return []
671 if not isinstance(value, list):
672 return [value]
673 return value
675 # ------------------------------------------------------------------------------------------------------------
676 # Get Icon from user theme, using our own as backup (Oxygen)
678 def getIcon(icon, size, qrcformat):
679 return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.%s" % (size, size, icon, qrcformat)))
681 # ------------------------------------------------------------------------------------------------------------
682 # Handle some basic command-line arguments shared between all carla variants
684 def handleInitialCommandLineArguments(file):
685 initName = os.path.basename(file) if (file is not None and os.path.dirname(file) in PATH) else sys.argv[0]
686 libPrefix = None
687 readPrefixNext = False
689 for arg in sys.argv[1:]:
690 if arg.startswith("--with-appname="):
691 initName = os.path.basename(arg.replace("--with-appname=", ""))
693 elif arg.startswith("--with-libprefix="):
694 libPrefix = arg.replace("--with-libprefix=", "")
696 elif arg.startswith("--osc-gui="):
697 gCarla.nogui = int(arg.replace("--osc-gui=", ""))
699 elif arg.startswith("--cnprefix="):
700 gCarla.cnprefix = arg.replace("--cnprefix=", "")
702 elif arg == "--cnprefix":
703 readPrefixNext = True
705 elif arg == "--gdb":
706 pass
708 elif arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui"):
709 gCarla.nogui = True
711 elif MACOS and arg.startswith("-psn_"):
712 pass
714 elif arg in ("-h", "--h", "-help", "--help"):
715 print("Usage: %s [OPTION]... [FILE|URL]" % initName)
716 print("")
717 print(" where FILE can be a Carla project or preset file to be loaded, or URL if using Carla-Control")
718 print("")
719 print(" and OPTION can be one or more of the following:")
720 print("")
721 print(" --cnprefix\t Set a prefix for client names in multi-client mode.")
722 if isinstance(gCarla.nogui, bool):
723 if X_LIBDIR_X is not None:
724 print(" --gdb \t Run Carla inside gdb.")
725 print(" -n,--no-gui \t Run Carla headless, don't show UI.")
726 print("")
727 print(" -h,--help \t Print this help text and exit.")
728 print(" -v,--version \t Print version information and exit.")
729 print("")
731 if not isinstance(gCarla.nogui, bool):
732 print("NOTE: when using %s the FILE is only valid the first time the backend is started" % initName)
733 sys.exit(1)
735 sys.exit(0)
737 elif arg in ("-v", "--v", "-version", "--version"):
738 pathBinaries, pathResources = getPaths(libPrefix)
740 print("Using Carla version %s" % VERSION)
741 print(" Python version: %s" % sys.version.split(" ",1)[0])
742 print(" Qt version: %s" % QT_VERSION_STR)
743 print(" PyQt version: %s" % PYQT_VERSION_STR)
744 print(" Binary dir: %s" % pathBinaries)
745 print(" Resources dir: %s" % pathResources)
747 sys.exit(1 if gCarla.nogui else 0)
749 elif readPrefixNext:
750 readPrefixNext = False
751 gCarla.cnprefix = arg
753 if gCarla.nogui and not isinstance(gCarla.nogui, bool):
754 if os.fork():
755 # pylint: disable=protected-access
756 os._exit(0)
757 # pylint: enable=protected-access
758 else:
759 os.setsid()
761 return (initName, libPrefix)
763 # ------------------------------------------------------------------------------------------------------------
764 # Get initial project file (as passed in the command-line parameters)
766 def getInitialProjectFile(skipExistCheck = False):
767 # NOTE: PyQt mishandles unicode characters, we directly use sys.argv instead of qApp->arguments()
768 # see https://riverbankcomputing.com/pipermail/pyqt/2015-January/035395.html
769 args = sys.argv[1:]
770 readPrefixNext = False
771 for arg in args:
772 if readPrefixNext:
773 readPrefixNext = False
774 continue
775 if arg.startswith("--cnprefix="):
776 continue
777 if arg.startswith("--osc-gui="):
778 continue
779 if arg.startswith("--with-appname="):
780 continue
781 if arg.startswith("--with-libprefix="):
782 continue
783 if arg == "--cnprefix":
784 readPrefixNext = True
785 continue
786 if arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui", "--gdb"):
787 continue
788 if MACOS and arg.startswith("-psn_"):
789 continue
790 arg = os.path.expanduser(arg)
791 if skipExistCheck or os.path.exists(arg):
792 return arg
794 return None
796 # ------------------------------------------------------------------------------------------------------------
797 # Get paths (binaries, resources)
799 def getPaths(libPrefix = None):
800 CWDl = CWD.lower()
802 # adjust for special distros
803 libdir = os.path.basename(os.path.normpath(X_LIBDIR_X)) if X_LIBDIR_X else "lib"
804 datadir = os.path.basename(os.path.normpath(X_DATADIR_X)) if X_DATADIR_X else "share"
806 # standalone, installed system-wide linux
807 if libPrefix is not None:
808 pathBinaries = os.path.join(libPrefix, libdir, "carla")
809 pathResources = os.path.join(libPrefix, datadir, "carla", "resources")
811 # standalone, local source
812 elif CWDl.endswith("frontend"):
813 pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "bin"))
814 pathResources = os.path.join(pathBinaries, "resources")
816 # plugin
817 elif CWDl.endswith("resources"):
818 # installed system-wide linux
819 if CWDl.endswith("/share/carla/resources"):
820 pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", libdir, "carla"))
821 pathResources = CWD
823 # local source
824 elif CWDl.endswith("native-plugins%sresources" % os.sep):
825 pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "bin"))
826 pathResources = CWD
828 # other
829 else:
830 pathBinaries = os.path.abspath(os.path.join(CWD, ".."))
831 pathResources = CWD
833 # everything else
834 else:
835 pathBinaries = CWD
836 pathResources = os.path.join(pathBinaries, "resources")
838 return (pathBinaries, pathResources)
840 # ------------------------------------------------------------------------------------------------------------
841 # Signal handler
842 # TODO move to carla_host.py or something
844 def signalHandler(sig, frame):
845 if sig in (SIGINT, SIGTERM):
846 gCarla.term = True
847 if gCarla.gui is not None:
848 gCarla.gui.SIGTERM.emit()
850 elif haveSIGUSR1 and sig == SIGUSR1:
851 if gCarla.gui is not None:
852 gCarla.gui.SIGUSR1.emit()
854 def setUpSignals():
855 signal(SIGINT, signalHandler)
856 signal(SIGTERM, signalHandler)
858 if not haveSIGUSR1:
859 return
861 signal(SIGUSR1, signalHandler)
863 # ------------------------------------------------------------------------------------------------------------
864 # QLineEdit and QPushButton combo
866 def getAndSetPath(parent, lineEdit):
867 newPath = QFileDialog.getExistingDirectory(parent, parent.tr("Set Path"), lineEdit.text(), QFileDialog.ShowDirsOnly)
868 if newPath:
869 lineEdit.setText(newPath)
870 return newPath
872 # ------------------------------------------------------------------------------------------------------------
873 # Backwards-compatible horizontalAdvance/width call, depending on Qt version
875 def fontMetricsHorizontalAdvance(fontMetrics, string):
876 if QT_VERSION >= 0x50b00:
877 return fontMetrics.horizontalAdvance(string)
878 return fontMetrics.width(string)
880 # ------------------------------------------------------------------------------------------------------------
881 # Custom QMessageBox which resizes itself to fit text
883 class QMessageBoxWithBetterWidth(QMessageBox):
884 def __init__(self, parent):
885 QMessageBox.__init__(self, parent)
887 def showEvent(self, event):
888 fontMetrics = self.fontMetrics()
890 lines = self.text().strip().split("\n") + self.informativeText().strip().split("\n")
892 if lines:
893 width = 0
895 for line in lines:
896 width = max(fontMetricsHorizontalAdvance(fontMetrics, line), width)
898 self.layout().setColumnMinimumWidth(2, width + 12)
900 QMessageBox.showEvent(self, event)
902 # ------------------------------------------------------------------------------------------------------------
903 # Custom MessageBox
905 # pylint: disable=too-many-arguments
906 def CustomMessageBox(parent, icon, title, text,
907 extraText="",
908 buttons=QMessageBox.Yes|QMessageBox.No,
909 defButton=QMessageBox.No):
910 msgBox = QMessageBoxWithBetterWidth(parent)
911 msgBox.setIcon(icon)
912 msgBox.setWindowTitle(title)
913 msgBox.setText(text)
914 msgBox.setInformativeText(extraText)
915 msgBox.setStandardButtons(buttons)
916 msgBox.setDefaultButton(defButton)
917 return msgBox.exec_()
918 # pylint: enable=too-many-arguments
920 # ------------------------------------------------------------------------------------------------------------