Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

928 lines
34KB

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