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.

carla_shared.py 33KB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
7 years ago
7 years ago
10 years ago
7 years ago
7 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  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_PROCESS_MODE = "Engine/ProcessMode" # enum
  168. CARLA_KEY_ENGINE_TRANSPORT_MODE = "Engine/TransportMode" # enum
  169. CARLA_KEY_ENGINE_TRANSPORT_EXTRA = "Engine/TransportExtra" # str
  170. CARLA_KEY_ENGINE_FORCE_STEREO = "Engine/ForceStereo" # bool
  171. CARLA_KEY_ENGINE_PREFER_PLUGIN_BRIDGES = "Engine/PreferPluginBridges" # bool
  172. CARLA_KEY_ENGINE_PREFER_UI_BRIDGES = "Engine/PreferUiBridges" # bool
  173. CARLA_KEY_ENGINE_MANAGE_UIS = "Engine/ManageUIs" # bool
  174. CARLA_KEY_ENGINE_UIS_ALWAYS_ON_TOP = "Engine/UIsAlwaysOnTop" # bool
  175. CARLA_KEY_ENGINE_MAX_PARAMETERS = "Engine/MaxParameters" # int
  176. CARLA_KEY_ENGINE_RESET_XRUNS = "Engine/ResetXruns" # bool
  177. CARLA_KEY_ENGINE_UI_BRIDGES_TIMEOUT = "Engine/UiBridgesTimeout" # int
  178. CARLA_KEY_OSC_ENABLED = "OSC/Enabled"
  179. CARLA_KEY_OSC_TCP_PORT_ENABLED = "OSC/TCPEnabled"
  180. CARLA_KEY_OSC_TCP_PORT_NUMBER = "OSC/TCPNumber"
  181. CARLA_KEY_OSC_TCP_PORT_RANDOM = "OSC/TCPRandom"
  182. CARLA_KEY_OSC_UDP_PORT_ENABLED = "OSC/UDPEnabled"
  183. CARLA_KEY_OSC_UDP_PORT_NUMBER = "OSC/UDPNumber"
  184. CARLA_KEY_OSC_UDP_PORT_RANDOM = "OSC/UDPRandom"
  185. CARLA_KEY_PATHS_AUDIO = "Paths/Audio"
  186. CARLA_KEY_PATHS_MIDI = "Paths/MIDI"
  187. CARLA_KEY_PATHS_LADSPA = "Paths/LADSPA"
  188. CARLA_KEY_PATHS_DSSI = "Paths/DSSI"
  189. CARLA_KEY_PATHS_LV2 = "Paths/LV2"
  190. CARLA_KEY_PATHS_VST2 = "Paths/VST2"
  191. CARLA_KEY_PATHS_VST3 = "Paths/VST3"
  192. CARLA_KEY_PATHS_CLAP = "Paths/CLAP"
  193. CARLA_KEY_PATHS_SF2 = "Paths/SF2"
  194. CARLA_KEY_PATHS_SFZ = "Paths/SFZ"
  195. CARLA_KEY_PATHS_JSFX = "Paths/JSFX"
  196. CARLA_KEY_WINE_EXECUTABLE = "Wine/Executable" # str
  197. CARLA_KEY_WINE_AUTO_PREFIX = "Wine/AutoPrefix" # bool
  198. CARLA_KEY_WINE_FALLBACK_PREFIX = "Wine/FallbackPrefix" # str
  199. CARLA_KEY_WINE_RT_PRIO_ENABLED = "Wine/RtPrioEnabled" # bool
  200. CARLA_KEY_WINE_BASE_RT_PRIO = "Wine/BaseRtPrio" # int
  201. CARLA_KEY_WINE_SERVER_RT_PRIO = "Wine/ServerRtPrio" # int
  202. CARLA_KEY_EXPERIMENTAL_PLUGIN_BRIDGES = "Experimental/PluginBridges" # bool
  203. CARLA_KEY_EXPERIMENTAL_WINE_BRIDGES = "Experimental/WineBridges" # bool
  204. CARLA_KEY_EXPERIMENTAL_JACK_APPS = "Experimental/JackApplications" # bool
  205. CARLA_KEY_EXPERIMENTAL_EXPORT_LV2 = "Experimental/ExportLV2" # bool
  206. CARLA_KEY_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = "Experimental/PreventBadBehaviour" # bool
  207. CARLA_KEY_EXPERIMENTAL_LOAD_LIB_GLOBAL = "Experimental/LoadLibGlobal" # bool
  208. # if pro theme is on and color is black
  209. CARLA_KEY_CUSTOM_PAINTING = "UseCustomPainting" # bool
  210. # ------------------------------------------------------------------------------------------------------------
  211. # Carla Settings defaults
  212. # Main
  213. CARLA_DEFAULT_MAIN_PROJECT_FOLDER = HOME
  214. CARLA_DEFAULT_MAIN_USE_PRO_THEME = True
  215. CARLA_DEFAULT_MAIN_PRO_THEME_COLOR = "Black"
  216. CARLA_DEFAULT_MAIN_REFRESH_INTERVAL = 20
  217. CARLA_DEFAULT_MAIN_CONFIRM_EXIT = True
  218. CARLA_DEFAULT_MAIN_CLASSIC_SKIN = False
  219. CARLA_DEFAULT_MAIN_SHOW_LOGS = bool(not WINDOWS)
  220. CARLA_DEFAULT_MAIN_SYSTEM_ICONS = False
  221. CARLA_DEFAULT_MAIN_EXPERIMENTAL = False
  222. # Canvas
  223. CARLA_DEFAULT_CANVAS_THEME = "Modern Dark"
  224. CARLA_DEFAULT_CANVAS_SIZE = "3100x2400"
  225. CARLA_DEFAULT_CANVAS_SIZE_WIDTH = 3100
  226. CARLA_DEFAULT_CANVAS_SIZE_HEIGHT = 2400
  227. CARLA_DEFAULT_CANVAS_USE_BEZIER_LINES = True
  228. CARLA_DEFAULT_CANVAS_AUTO_HIDE_GROUPS = True
  229. CARLA_DEFAULT_CANVAS_AUTO_SELECT_ITEMS = False
  230. CARLA_DEFAULT_CANVAS_EYE_CANDY = True
  231. CARLA_DEFAULT_CANVAS_FANCY_EYE_CANDY = False
  232. CARLA_DEFAULT_CANVAS_USE_OPENGL = False
  233. CARLA_DEFAULT_CANVAS_ANTIALIASING = CANVAS_ANTIALIASING_SMALL
  234. CARLA_DEFAULT_CANVAS_HQ_ANTIALIASING = False
  235. CARLA_DEFAULT_CANVAS_INLINE_DISPLAYS = False
  236. CARLA_DEFAULT_CANVAS_FULL_REPAINTS = False
  237. # Engine
  238. CARLA_DEFAULT_FORCE_STEREO = False
  239. CARLA_DEFAULT_PREFER_PLUGIN_BRIDGES = False
  240. CARLA_DEFAULT_PREFER_UI_BRIDGES = True
  241. CARLA_DEFAULT_MANAGE_UIS = True
  242. CARLA_DEFAULT_UIS_ALWAYS_ON_TOP = False
  243. CARLA_DEFAULT_MAX_PARAMETERS = MAX_DEFAULT_PARAMETERS
  244. CARLA_DEFAULT_RESET_XRUNS = False
  245. CARLA_DEFAULT_UI_BRIDGES_TIMEOUT = 4000
  246. CARLA_DEFAULT_AUDIO_BUFFER_SIZE = 512
  247. CARLA_DEFAULT_AUDIO_SAMPLE_RATE = 44100
  248. CARLA_DEFAULT_AUDIO_TRIPLE_BUFFER = False
  249. if HAIKU:
  250. CARLA_DEFAULT_AUDIO_DRIVER = "SDL"
  251. elif MACOS:
  252. CARLA_DEFAULT_AUDIO_DRIVER = "CoreAudio"
  253. elif WINDOWS:
  254. CARLA_DEFAULT_AUDIO_DRIVER = "Windows Audio"
  255. elif os.path.exists("/usr/bin/jackd") or os.path.exists("/usr/bin/jackdbus") or os.path.exists("/usr/bin/pw-jack"):
  256. CARLA_DEFAULT_AUDIO_DRIVER = "JACK"
  257. else:
  258. CARLA_DEFAULT_AUDIO_DRIVER = "PulseAudio"
  259. if CARLA_DEFAULT_AUDIO_DRIVER == "JACK":
  260. CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS
  261. CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_JACK
  262. else:
  263. CARLA_DEFAULT_PROCESS_MODE = ENGINE_PROCESS_MODE_PATCHBAY
  264. CARLA_DEFAULT_TRANSPORT_MODE = ENGINE_TRANSPORT_MODE_INTERNAL
  265. # OSC
  266. CARLA_DEFAULT_OSC_ENABLED = not (MACOS or WINDOWS)
  267. CARLA_DEFAULT_OSC_TCP_PORT_ENABLED = True
  268. CARLA_DEFAULT_OSC_TCP_PORT_NUMBER = 22752
  269. CARLA_DEFAULT_OSC_TCP_PORT_RANDOM = False
  270. CARLA_DEFAULT_OSC_UDP_PORT_ENABLED = True
  271. CARLA_DEFAULT_OSC_UDP_PORT_NUMBER = 22752
  272. CARLA_DEFAULT_OSC_UDP_PORT_RANDOM = False
  273. # Wine
  274. CARLA_DEFAULT_WINE_EXECUTABLE = "wine"
  275. CARLA_DEFAULT_WINE_AUTO_PREFIX = True
  276. CARLA_DEFAULT_WINE_FALLBACK_PREFIX = os.path.expanduser("~/.wine")
  277. CARLA_DEFAULT_WINE_RT_PRIO_ENABLED = True
  278. CARLA_DEFAULT_WINE_BASE_RT_PRIO = 15
  279. CARLA_DEFAULT_WINE_SERVER_RT_PRIO = 10
  280. # Experimental
  281. CARLA_DEFAULT_EXPERIMENTAL_PLUGIN_BRIDGES = False
  282. CARLA_DEFAULT_EXPERIMENTAL_WINE_BRIDGES = False
  283. CARLA_DEFAULT_EXPERIMENTAL_JACK_APPS = False
  284. CARLA_DEFAULT_EXPERIMENTAL_LV2_EXPORT = False
  285. CARLA_DEFAULT_EXPERIMENTAL_PREVENT_BAD_BEHAVIOUR = False
  286. CARLA_DEFAULT_EXPERIMENTAL_LOAD_LIB_GLOBAL = False
  287. # ------------------------------------------------------------------------------------------------------------
  288. # Default File Folders
  289. CARLA_DEFAULT_FILE_PATH_AUDIO = []
  290. CARLA_DEFAULT_FILE_PATH_MIDI = []
  291. # ------------------------------------------------------------------------------------------------------------
  292. # Default Plugin Folders (get)
  293. DEFAULT_LADSPA_PATH = ""
  294. DEFAULT_DSSI_PATH = ""
  295. DEFAULT_LV2_PATH = ""
  296. DEFAULT_VST2_PATH = ""
  297. DEFAULT_VST3_PATH = ""
  298. DEFAULT_CLAP_PATH = ""
  299. DEFAULT_SF2_PATH = ""
  300. DEFAULT_SFZ_PATH = ""
  301. DEFAULT_JSFX_PATH = ""
  302. if WINDOWS:
  303. splitter = ";"
  304. APPDATA = os.getenv("APPDATA")
  305. LOCALAPPDATA = os.getenv("LOCALAPPDATA", APPDATA)
  306. PROGRAMFILES = os.getenv("PROGRAMFILES")
  307. PROGRAMFILESx86 = os.getenv("PROGRAMFILES(x86)")
  308. COMMONPROGRAMFILES = os.getenv("COMMONPROGRAMFILES")
  309. COMMONPROGRAMFILESx86 = os.getenv("COMMONPROGRAMFILES(x86)")
  310. # Small integrity tests
  311. if not APPDATA:
  312. qFatal("APPDATA variable not set, cannot continue")
  313. sys.exit(1)
  314. if not PROGRAMFILES:
  315. qFatal("PROGRAMFILES variable not set, cannot continue")
  316. sys.exit(1)
  317. if not COMMONPROGRAMFILES:
  318. qFatal("COMMONPROGRAMFILES variable not set, cannot continue")
  319. sys.exit(1)
  320. DEFAULT_LADSPA_PATH = APPDATA + "\\LADSPA"
  321. DEFAULT_LADSPA_PATH += ";" + PROGRAMFILES + "\\LADSPA"
  322. DEFAULT_DSSI_PATH = APPDATA + "\\DSSI"
  323. DEFAULT_DSSI_PATH += ";" + PROGRAMFILES + "\\DSSI"
  324. DEFAULT_LV2_PATH = APPDATA + "\\LV2"
  325. DEFAULT_LV2_PATH += ";" + COMMONPROGRAMFILES + "\\LV2"
  326. DEFAULT_VST2_PATH = PROGRAMFILES + "\\VstPlugins"
  327. DEFAULT_VST2_PATH += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins"
  328. DEFAULT_JSFX_PATH = APPDATA + "\\REAPER\\Effects"
  329. #DEFAULT_JSFX_PATH += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects"
  330. if kIs64bit:
  331. DEFAULT_VST2_PATH += ";" + COMMONPROGRAMFILES + "\\VST2"
  332. DEFAULT_VST3_PATH = COMMONPROGRAMFILES + "\\VST3"
  333. DEFAULT_VST3_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3"
  334. DEFAULT_CLAP_PATH = COMMONPROGRAMFILES + "\\CLAP"
  335. DEFAULT_CLAP_PATH += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP"
  336. DEFAULT_SF2_PATH = APPDATA + "\\SF2"
  337. DEFAULT_SFZ_PATH = APPDATA + "\\SFZ"
  338. if PROGRAMFILESx86:
  339. DEFAULT_LADSPA_PATH += ";" + PROGRAMFILESx86 + "\\LADSPA"
  340. DEFAULT_DSSI_PATH += ";" + PROGRAMFILESx86 + "\\DSSI"
  341. DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\VstPlugins"
  342. DEFAULT_VST2_PATH += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins"
  343. #DEFAULT_JSFX_PATH += ";" + PROGRAMFILESx86 + "\\REAPER\\InstallData\\Effects"
  344. if COMMONPROGRAMFILESx86:
  345. DEFAULT_VST3_PATH += COMMONPROGRAMFILESx86 + "\\VST3"
  346. DEFAULT_CLAP_PATH += COMMONPROGRAMFILESx86 + "\\CLAP"
  347. elif HAIKU:
  348. splitter = ":"
  349. DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
  350. DEFAULT_LADSPA_PATH += ":/system/add-ons/media/ladspaplugins"
  351. DEFAULT_LADSPA_PATH += ":/system/lib/ladspa"
  352. DEFAULT_DSSI_PATH = HOME + "/.dssi"
  353. DEFAULT_DSSI_PATH += ":/system/add-ons/media/dssiplugins"
  354. DEFAULT_DSSI_PATH += ":/system/lib/dssi"
  355. DEFAULT_LV2_PATH = HOME + "/.lv2"
  356. DEFAULT_LV2_PATH += ":/system/add-ons/media/lv2plugins"
  357. DEFAULT_VST2_PATH = HOME + "/.vst"
  358. DEFAULT_VST2_PATH += ":/system/add-ons/media/vstplugins"
  359. DEFAULT_VST3_PATH = HOME + "/.vst3"
  360. DEFAULT_VST3_PATH += ":/system/add-ons/media/vst3plugins"
  361. DEFAULT_CLAP_PATH = HOME + "/.clap"
  362. DEFAULT_CLAP_PATH += ":/system/add-ons/media/clapplugins"
  363. elif MACOS:
  364. splitter = ":"
  365. DEFAULT_LADSPA_PATH = HOME + "/Library/Audio/Plug-Ins/LADSPA"
  366. DEFAULT_LADSPA_PATH += ":/Library/Audio/Plug-Ins/LADSPA"
  367. DEFAULT_DSSI_PATH = HOME + "/Library/Audio/Plug-Ins/DSSI"
  368. DEFAULT_DSSI_PATH += ":/Library/Audio/Plug-Ins/DSSI"
  369. DEFAULT_LV2_PATH = HOME + "/Library/Audio/Plug-Ins/LV2"
  370. DEFAULT_LV2_PATH += ":/Library/Audio/Plug-Ins/LV2"
  371. DEFAULT_VST2_PATH = HOME + "/Library/Audio/Plug-Ins/VST"
  372. DEFAULT_VST2_PATH += ":/Library/Audio/Plug-Ins/VST"
  373. DEFAULT_VST3_PATH = HOME + "/Library/Audio/Plug-Ins/VST3"
  374. DEFAULT_VST3_PATH += ":/Library/Audio/Plug-Ins/VST3"
  375. DEFAULT_CLAP_PATH = HOME + "/Library/Audio/Plug-Ins/CLAP"
  376. DEFAULT_CLAP_PATH += ":/Library/Audio/Plug-Ins/CLAP"
  377. DEFAULT_JSFX_PATH = HOME + "/Library/Application Support/REAPER/Effects"
  378. #DEFAULT_JSFX_PATH += ":/Applications/REAPER.app/Contents/InstallFiles/Effects"
  379. else:
  380. splitter = ":"
  381. CONFIG_HOME = os.getenv("XDG_CONFIG_HOME", HOME + "/.config")
  382. DEFAULT_LADSPA_PATH = HOME + "/.ladspa"
  383. DEFAULT_LADSPA_PATH += ":/usr/lib/ladspa"
  384. DEFAULT_LADSPA_PATH += ":/usr/local/lib/ladspa"
  385. DEFAULT_DSSI_PATH = HOME + "/.dssi"
  386. DEFAULT_DSSI_PATH += ":/usr/lib/dssi"
  387. DEFAULT_DSSI_PATH += ":/usr/local/lib/dssi"
  388. DEFAULT_LV2_PATH = HOME + "/.lv2"
  389. DEFAULT_LV2_PATH += ":/usr/lib/lv2"
  390. DEFAULT_LV2_PATH += ":/usr/local/lib/lv2"
  391. DEFAULT_VST2_PATH = HOME + "/.vst"
  392. DEFAULT_VST2_PATH += ":/usr/lib/vst"
  393. DEFAULT_VST2_PATH += ":/usr/local/lib/vst"
  394. DEFAULT_VST2_PATH += HOME + "/.lxvst"
  395. DEFAULT_VST2_PATH += ":/usr/lib/lxvst"
  396. DEFAULT_VST2_PATH += ":/usr/local/lib/lxvst"
  397. DEFAULT_VST3_PATH = HOME + "/.vst3"
  398. DEFAULT_VST3_PATH += ":/usr/lib/vst3"
  399. DEFAULT_VST3_PATH += ":/usr/local/lib/vst3"
  400. DEFAULT_CLAP_PATH = HOME + "/.clap"
  401. DEFAULT_CLAP_PATH += ":/usr/lib/clap"
  402. DEFAULT_CLAP_PATH += ":/usr/local/lib/clap"
  403. DEFAULT_SF2_PATH = HOME + "/.sounds/sf2"
  404. DEFAULT_SF2_PATH += ":" + HOME + "/.sounds/sf3"
  405. DEFAULT_SF2_PATH += ":/usr/share/sounds/sf2"
  406. DEFAULT_SF2_PATH += ":/usr/share/sounds/sf3"
  407. DEFAULT_SF2_PATH += ":/usr/share/soundfonts"
  408. DEFAULT_SFZ_PATH = HOME + "/.sounds/sfz"
  409. DEFAULT_SFZ_PATH += ":/usr/share/sounds/sfz"
  410. DEFAULT_JSFX_PATH = CONFIG_HOME + "/REAPER/Effects"
  411. #DEFAULT_JSFX_PATH += ":" + "/opt/REAPER/InstallData/Effects"
  412. if not WINDOWS:
  413. winePrefix = os.getenv("WINEPREFIX")
  414. if not winePrefix:
  415. winePrefix = HOME + "/.wine"
  416. if os.path.exists(winePrefix):
  417. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files/VstPlugins"
  418. DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3"
  419. DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP"
  420. if kIs64bit and os.path.exists(winePrefix + "/drive_c/Program Files (x86)"):
  421. DEFAULT_VST2_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins"
  422. DEFAULT_VST3_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3"
  423. DEFAULT_CLAP_PATH += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP"
  424. del winePrefix
  425. # ------------------------------------------------------------------------------------------------------------
  426. # Default Plugin Folders (set)
  427. readEnvVars = True
  428. if WINDOWS:
  429. # Check if running Wine. If yes, ignore env vars
  430. # pylint: disable=import-error
  431. from winreg import ConnectRegistry, OpenKey, CloseKey, HKEY_CURRENT_USER
  432. # pylint: enable=import-error
  433. _reg = ConnectRegistry(None, HKEY_CURRENT_USER)
  434. try:
  435. _key = OpenKey(_reg, r"SOFTWARE\Wine")
  436. CloseKey(_key)
  437. del _key
  438. readEnvVars = False
  439. except:
  440. pass
  441. CloseKey(_reg)
  442. del _reg
  443. if readEnvVars:
  444. CARLA_DEFAULT_LADSPA_PATH = os.getenv("LADSPA_PATH", DEFAULT_LADSPA_PATH).split(splitter)
  445. CARLA_DEFAULT_DSSI_PATH = os.getenv("DSSI_PATH", DEFAULT_DSSI_PATH).split(splitter)
  446. CARLA_DEFAULT_LV2_PATH = os.getenv("LV2_PATH", DEFAULT_LV2_PATH).split(splitter)
  447. CARLA_DEFAULT_VST2_PATH = os.getenv("VST_PATH", DEFAULT_VST2_PATH).split(splitter)
  448. CARLA_DEFAULT_VST3_PATH = os.getenv("VST3_PATH", DEFAULT_VST3_PATH).split(splitter)
  449. CARLA_DEFAULT_CLAP_PATH = os.getenv("CLAP_PATH", DEFAULT_CLAP_PATH).split(splitter)
  450. CARLA_DEFAULT_SF2_PATH = os.getenv("SF2_PATH", DEFAULT_SF2_PATH).split(splitter)
  451. CARLA_DEFAULT_SFZ_PATH = os.getenv("SFZ_PATH", DEFAULT_SFZ_PATH).split(splitter)
  452. CARLA_DEFAULT_JSFX_PATH = os.getenv("JSFX_PATH", DEFAULT_JSFX_PATH).split(splitter)
  453. else:
  454. CARLA_DEFAULT_LADSPA_PATH = DEFAULT_LADSPA_PATH.split(splitter)
  455. CARLA_DEFAULT_DSSI_PATH = DEFAULT_DSSI_PATH.split(splitter)
  456. CARLA_DEFAULT_LV2_PATH = DEFAULT_LV2_PATH.split(splitter)
  457. CARLA_DEFAULT_VST2_PATH = DEFAULT_VST2_PATH.split(splitter)
  458. CARLA_DEFAULT_VST3_PATH = DEFAULT_VST3_PATH.split(splitter)
  459. CARLA_DEFAULT_CLAP_PATH = DEFAULT_CLAP_PATH.split(splitter)
  460. CARLA_DEFAULT_SF2_PATH = DEFAULT_SF2_PATH.split(splitter)
  461. CARLA_DEFAULT_SFZ_PATH = DEFAULT_SFZ_PATH.split(splitter)
  462. CARLA_DEFAULT_JSFX_PATH = DEFAULT_JSFX_PATH.split(splitter)
  463. # ------------------------------------------------------------------------------------------------------------
  464. # Default Plugin Folders (cleanup)
  465. del DEFAULT_LADSPA_PATH
  466. del DEFAULT_DSSI_PATH
  467. del DEFAULT_LV2_PATH
  468. del DEFAULT_VST2_PATH
  469. del DEFAULT_VST3_PATH
  470. del DEFAULT_CLAP_PATH
  471. del DEFAULT_SF2_PATH
  472. del DEFAULT_SFZ_PATH
  473. # ------------------------------------------------------------------------------------------------------------
  474. # Global Carla object
  475. class CarlaObject():
  476. def __init__(self):
  477. self.cnprefix = "" # Client name prefix
  478. self.gui = None # Host Window
  479. self.nogui = False # Skip UI
  480. self.term = False # Terminated by OS signal
  481. self.felib = None # Frontend lib object
  482. self.utils = None # Utils object
  483. gCarla = CarlaObject()
  484. # ------------------------------------------------------------------------------------------------------------
  485. # Set CWD
  486. CWD = sys.path[0]
  487. if not CWD:
  488. CWD = os.path.dirname(sys.argv[0])
  489. # make it work with cxfreeze
  490. if os.path.isfile(CWD):
  491. CWD = os.path.dirname(CWD)
  492. if CWD.endswith("/lib"):
  493. CWD = CWD.rsplit("/lib",1)[0]
  494. CXFREEZE = True
  495. if not WINDOWS:
  496. os.environ['CARLA_MAGIC_FILE'] = os.path.join(CWD, "magic.mgc")
  497. else:
  498. CXFREEZE = False
  499. # ------------------------------------------------------------------------------------------------------------
  500. # Set DLL_EXTENSION
  501. if WINDOWS:
  502. DLL_EXTENSION = "dll"
  503. elif MACOS:
  504. DLL_EXTENSION = "dylib"
  505. else:
  506. DLL_EXTENSION = "so"
  507. # ------------------------------------------------------------------------------------------------------------
  508. # Find decimal points for a parameter, using step and stepSmall
  509. def countDecimalPoints(step, stepSmall):
  510. if stepSmall >= 1.0:
  511. return 0
  512. if step >= 1.0:
  513. return 2
  514. count = 0
  515. value = fmod(abs(step), 1)
  516. while 0.0001 < value < 0.999 and count < 6:
  517. value = fmod(value*10, 1)
  518. count += 1
  519. return count
  520. # ------------------------------------------------------------------------------------------------------------
  521. # Check if a value is a number (float support)
  522. def isNumber(value):
  523. try:
  524. float(value)
  525. return True
  526. except:
  527. return False
  528. # ------------------------------------------------------------------------------------------------------------
  529. # Convert a value to a list
  530. def toList(value):
  531. if value is None:
  532. return []
  533. if not isinstance(value, list):
  534. return [value]
  535. return value
  536. # ------------------------------------------------------------------------------------------------------------
  537. # Get Icon from user theme, using our own as backup (Oxygen)
  538. def getIcon(icon, size, qrcformat):
  539. return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.%s" % (size, size, icon, qrcformat)))
  540. # ------------------------------------------------------------------------------------------------------------
  541. # Handle some basic command-line arguments shared between all carla variants
  542. def handleInitialCommandLineArguments(file):
  543. initName = os.path.basename(file) if (file is not None and os.path.dirname(file) in PATH) else sys.argv[0]
  544. libPrefix = None
  545. readPrefixNext = False
  546. for arg in sys.argv[1:]:
  547. if arg.startswith("--with-appname="):
  548. initName = os.path.basename(arg.replace("--with-appname=", ""))
  549. elif arg.startswith("--with-libprefix="):
  550. libPrefix = arg.replace("--with-libprefix=", "")
  551. elif arg.startswith("--osc-gui="):
  552. gCarla.nogui = int(arg.replace("--osc-gui=", ""))
  553. elif arg.startswith("--cnprefix="):
  554. gCarla.cnprefix = arg.replace("--cnprefix=", "")
  555. elif arg == "--cnprefix":
  556. readPrefixNext = True
  557. elif arg == "--gdb":
  558. pass
  559. elif arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui"):
  560. gCarla.nogui = True
  561. elif MACOS and arg.startswith("-psn_"):
  562. pass
  563. elif arg in ("-h", "--h", "-help", "--help"):
  564. print("Usage: %s [OPTION]... [FILE|URL]" % initName)
  565. print("")
  566. print(" where FILE can be a Carla project or preset file to be loaded, or URL if using Carla-Control")
  567. print("")
  568. print(" and OPTION can be one or more of the following:")
  569. print("")
  570. print(" --cnprefix\t Set a prefix for client names in multi-client mode.")
  571. if isinstance(gCarla.nogui, bool):
  572. if X_LIBDIR_X is not None:
  573. print(" --gdb \t Run Carla inside gdb.")
  574. print(" -n,--no-gui \t Run Carla headless, don't show UI.")
  575. print("")
  576. print(" -h,--help \t Print this help text and exit.")
  577. print(" -v,--version \t Print version information and exit.")
  578. print("")
  579. if not isinstance(gCarla.nogui, bool):
  580. print("NOTE: when using %s the FILE is only valid the first time the backend is started" % initName)
  581. sys.exit(1)
  582. sys.exit(0)
  583. elif arg in ("-v", "--v", "-version", "--version"):
  584. pathBinaries, pathResources = getPaths(libPrefix)
  585. print("Using Carla version %s" % VERSION)
  586. print(" Python version: %s" % sys.version.split(" ",1)[0])
  587. print(" Qt version: %s" % QT_VERSION_STR)
  588. print(" PyQt version: %s" % PYQT_VERSION_STR)
  589. print(" Binary dir: %s" % pathBinaries)
  590. print(" Resources dir: %s" % pathResources)
  591. sys.exit(1 if gCarla.nogui else 0)
  592. elif readPrefixNext:
  593. readPrefixNext = False
  594. gCarla.cnprefix = arg
  595. if gCarla.nogui and not isinstance(gCarla.nogui, bool):
  596. if os.fork():
  597. # pylint: disable=protected-access
  598. os._exit(0)
  599. # pylint: enable=protected-access
  600. else:
  601. os.setsid()
  602. return (initName, libPrefix)
  603. # ------------------------------------------------------------------------------------------------------------
  604. # Get initial project file (as passed in the command-line parameters)
  605. def getInitialProjectFile(skipExistCheck = False):
  606. # NOTE: PyQt mishandles unicode characters, we directly use sys.argv instead of qApp->arguments()
  607. # see https://riverbankcomputing.com/pipermail/pyqt/2015-January/035395.html
  608. args = sys.argv[1:]
  609. readPrefixNext = False
  610. for arg in args:
  611. if readPrefixNext:
  612. readPrefixNext = False
  613. continue
  614. if arg.startswith("--cnprefix="):
  615. continue
  616. if arg.startswith("--osc-gui="):
  617. continue
  618. if arg.startswith("--with-appname="):
  619. continue
  620. if arg.startswith("--with-libprefix="):
  621. continue
  622. if arg == "--cnprefix":
  623. readPrefixNext = True
  624. continue
  625. if arg in ("-n", "--n", "-no-gui", "--no-gui", "-nogui", "--nogui", "--gdb"):
  626. continue
  627. if MACOS and arg.startswith("-psn_"):
  628. continue
  629. arg = os.path.expanduser(arg)
  630. if skipExistCheck or os.path.exists(arg):
  631. return arg
  632. return None
  633. # ------------------------------------------------------------------------------------------------------------
  634. # Get paths (binaries, resources)
  635. def getPaths(libPrefix = None):
  636. CWDl = CWD.lower()
  637. # adjust for special distros
  638. libdir = os.path.basename(os.path.normpath(X_LIBDIR_X)) if X_LIBDIR_X else "lib"
  639. datadir = os.path.basename(os.path.normpath(X_DATADIR_X)) if X_DATADIR_X else "share"
  640. # standalone, installed system-wide linux
  641. if libPrefix is not None:
  642. pathBinaries = os.path.join(libPrefix, libdir, "carla")
  643. pathResources = os.path.join(libPrefix, datadir, "carla", "resources")
  644. # standalone, local source
  645. elif CWDl.endswith("frontend"):
  646. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "bin"))
  647. pathResources = os.path.join(pathBinaries, "resources")
  648. # plugin
  649. elif CWDl.endswith("resources"):
  650. # installed system-wide linux
  651. if CWDl.endswith("/share/carla/resources"):
  652. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", libdir, "carla"))
  653. pathResources = CWD
  654. # local source
  655. elif CWDl.endswith("native-plugins%sresources" % os.sep):
  656. pathBinaries = os.path.abspath(os.path.join(CWD, "..", "..", "..", "bin"))
  657. pathResources = CWD
  658. # other
  659. else:
  660. pathBinaries = os.path.abspath(os.path.join(CWD, ".."))
  661. pathResources = CWD
  662. # everything else
  663. else:
  664. pathBinaries = CWD
  665. pathResources = os.path.join(pathBinaries, "resources")
  666. return (pathBinaries, pathResources)
  667. # ------------------------------------------------------------------------------------------------------------
  668. # Signal handler
  669. # TODO move to carla_host.py or something
  670. def signalHandler(sig, frame):
  671. if sig in (SIGINT, SIGTERM):
  672. gCarla.term = True
  673. if gCarla.gui is not None:
  674. gCarla.gui.SIGTERM.emit()
  675. elif haveSIGUSR1 and sig == SIGUSR1:
  676. if gCarla.gui is not None:
  677. gCarla.gui.SIGUSR1.emit()
  678. def setUpSignals():
  679. signal(SIGINT, signalHandler)
  680. signal(SIGTERM, signalHandler)
  681. if not haveSIGUSR1:
  682. return
  683. signal(SIGUSR1, signalHandler)
  684. # ------------------------------------------------------------------------------------------------------------
  685. # QLineEdit and QPushButton combo
  686. def getAndSetPath(parent, lineEdit):
  687. newPath = QFileDialog.getExistingDirectory(parent, parent.tr("Set Path"), lineEdit.text(), QFileDialog.ShowDirsOnly)
  688. if newPath:
  689. lineEdit.setText(newPath)
  690. return newPath
  691. # ------------------------------------------------------------------------------------------------------------
  692. # Backwards-compatible horizontalAdvance/width call, depending on Qt version
  693. def fontMetricsHorizontalAdvance(fontMetrics, string):
  694. if QT_VERSION >= 0x50b00:
  695. return fontMetrics.horizontalAdvance(string)
  696. return fontMetrics.width(string)
  697. # ------------------------------------------------------------------------------------------------------------
  698. # Custom QMessageBox which resizes itself to fit text
  699. class QMessageBoxWithBetterWidth(QMessageBox):
  700. def __init__(self, parent):
  701. QMessageBox.__init__(self, parent)
  702. def showEvent(self, event):
  703. fontMetrics = self.fontMetrics()
  704. lines = self.text().strip().split("\n") + self.informativeText().strip().split("\n")
  705. if lines:
  706. width = 0
  707. for line in lines:
  708. width = max(fontMetricsHorizontalAdvance(fontMetrics, line), width)
  709. self.layout().setColumnMinimumWidth(2, width + 12)
  710. QMessageBox.showEvent(self, event)
  711. # ------------------------------------------------------------------------------------------------------------
  712. # Custom MessageBox
  713. # pylint: disable=too-many-arguments
  714. def CustomMessageBox(parent, icon, title, text,
  715. extraText="",
  716. buttons=QMessageBox.Yes|QMessageBox.No,
  717. defButton=QMessageBox.No):
  718. msgBox = QMessageBoxWithBetterWidth(parent)
  719. msgBox.setIcon(icon)
  720. msgBox.setWindowTitle(title)
  721. msgBox.setText(text)
  722. msgBox.setInformativeText(extraText)
  723. msgBox.setStandardButtons(buttons)
  724. msgBox.setDefaultButton(defButton)
  725. return msgBox.exec_()
  726. # pylint: enable=too-many-arguments
  727. # ------------------------------------------------------------------------------------------------------------