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.

838 lines
29KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Common Carla code
  4. # Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # 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 COPYING file
  17. # ------------------------------------------------------------------------------------------------------------
  18. # Imports (Global)
  19. import os
  20. #import platform
  21. import sys
  22. from codecs import open as codecopen
  23. from copy import deepcopy
  24. #from decimal import Decimal
  25. from PyQt4.QtCore import pyqtSlot, qWarning, SIGNAL, SLOT
  26. #pyqtSlot, qFatal, Qt, QSettings, QTimer
  27. from PyQt4.QtGui import QDialog, QWidget
  28. #from PyQt4.QtGui import QColor, QCursor, QFontMetrics, QFrame, QGraphicsScene, QInputDialog, QLinearGradient, QMenu, QPainter, QPainterPath, QVBoxLayout
  29. #from PyQt4.QtXml import QDomDocument
  30. # ------------------------------------------------------------------------------------------------------------
  31. # Imports (Custom)
  32. import ui_carla_about
  33. #import ui_carla_edit
  34. import ui_carla_parameter
  35. #import ui_carla_plugin
  36. # ------------------------------------------------------------------------------------------------------------
  37. # Try Import Signal
  38. try:
  39. from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2
  40. haveSignal = True
  41. except:
  42. haveSignal = False
  43. # ------------------------------------------------------------------------------------------------------------
  44. # Set Platform
  45. if sys.platform == "darwin":
  46. from PyQt4.QtGui import qt_mac_set_menubar_icons
  47. qt_mac_set_menubar_icons(False)
  48. HAIKU = False
  49. LINUX = False
  50. MACOS = True
  51. WINDOWS = False
  52. elif "haiku" in sys.platform:
  53. HAIKU = True
  54. LINUX = False
  55. MACOS = False
  56. WINDOWS = False
  57. elif "linux" in sys.platform:
  58. HAIKU = False
  59. LINUX = True
  60. MACOS = False
  61. WINDOWS = False
  62. elif sys.platform in ("win32", "win64", "cygwin"):
  63. WINDIR = os.getenv("WINDIR")
  64. HAIKU = False
  65. LINUX = False
  66. MACOS = False
  67. WINDOWS = True
  68. else:
  69. HAIKU = False
  70. LINUX = False
  71. MACOS = False
  72. WINDOWS = False
  73. # ------------------------------------------------------------------------------------------------------------
  74. # Set Version
  75. VERSION = "0.5.0"
  76. # ------------------------------------------------------------------------------------------------------------
  77. # Set TMP
  78. TMP = os.getenv("TMP")
  79. if TMP is None:
  80. if WINDOWS:
  81. qWarning("TMP variable not set")
  82. TMP = os.path.join(WINDIR, "temp")
  83. else:
  84. TMP = "/tmp"
  85. # ------------------------------------------------------------------------------------------------------------
  86. # Set HOME
  87. HOME = os.getenv("HOME")
  88. if HOME is None:
  89. HOME = os.path.expanduser("~")
  90. if LINUX or MACOS:
  91. qWarning("HOME variable not set")
  92. if not os.path.exists(HOME):
  93. qWarning("HOME does not exist")
  94. HOME = TMP
  95. # ------------------------------------------------------------------------------------------------------------
  96. # Set PATH
  97. PATH = os.getenv("PATH")
  98. if PATH is None:
  99. qWarning("PATH variable not set")
  100. if MACOS:
  101. PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin")
  102. elif WINDOWS:
  103. PATH = (os.path.join(WINDIR, "system32"), WINDIR)
  104. else:
  105. PATH = ("/usr/local/bin", "/usr/bin", "/bin")
  106. else:
  107. PATH = PATH.split(os.pathsep)
  108. # ------------------------------------------------------------------------------------------------------------
  109. # 64bit check
  110. is64bit = False #bool(platform.architecture()[0] == "64bit" and sys.maxsize > 2**32)
  111. # ------------------------------------------------------------------------------------------------------------
  112. # Convert a ctypes c_char_p into a python string
  113. def cString(value):
  114. if not value:
  115. return ""
  116. if isinstance(value, str):
  117. return value
  118. return value.decode("utf-8", errors="ignore")
  119. # ------------------------------------------------------------------------------------------------------------
  120. # Check if a value is a number (float support)
  121. def isNumber(value):
  122. try:
  123. float(value)
  124. return True
  125. except:
  126. return False
  127. # ------------------------------------------------------------------------------------------------------------
  128. # Unicode open
  129. def uopen(filename, mode="r"):
  130. return codecopen(filename, encoding="utf-8", mode=mode)
  131. # ------------------------------------------------------------------------------------------------
  132. # Backend defines
  133. MAX_DEFAULT_PLUGINS = 99
  134. MAX_RACK_PLUGINS = 16
  135. MAX_PATCHBAY_PLUGINS = 999
  136. MAX_DEFAULT_PARAMETERS = 200
  137. # Plugin Hints
  138. PLUGIN_IS_BRIDGE = 0x001
  139. PLUGIN_IS_RTSAFE = 0x002
  140. PLUGIN_IS_SYNTH = 0x004
  141. PLUGIN_HAS_GUI = 0x010
  142. PLUGIN_USES_CHUNKS = 0x020
  143. PLUGIN_USES_SINGLE_THREAD = 0x040
  144. PLUGIN_CAN_DRYWET = 0x100
  145. PLUGIN_CAN_VOLUME = 0x200
  146. PLUGIN_CAN_BALANCE = 0x400
  147. PLUGIN_CAN_FORCE_STEREO = 0x800
  148. # Parameter Hints
  149. PARAMETER_IS_BOOLEAN = 0x01
  150. PARAMETER_IS_INTEGER = 0x02
  151. PARAMETER_IS_LOGARITHMIC = 0x04
  152. PARAMETER_IS_ENABLED = 0x08
  153. PARAMETER_IS_AUTOMABLE = 0x10
  154. PARAMETER_USES_SAMPLERATE = 0x20
  155. PARAMETER_USES_SCALEPOINTS = 0x40
  156. PARAMETER_USES_CUSTOM_TEXT = 0x80
  157. # FIXME
  158. # Custom Data types
  159. CUSTOM_DATA_INVALID = None
  160. CUSTOM_DATA_CHUNK = "http://kxstudio.sf.net/ns/carla/chunk"
  161. CUSTOM_DATA_STRING = "http://kxstudio.sf.net/ns/carla/string"
  162. # Binary Type
  163. BINARY_NONE = 0
  164. BINARY_POSIX32 = 1
  165. BINARY_POSIX64 = 2
  166. BINARY_WIN32 = 3
  167. BINARY_WIN64 = 4
  168. BINARY_OTHER = 5
  169. # Plugin Type
  170. PLUGIN_NONE = 0
  171. PLUGIN_INTERNAL = 1
  172. PLUGIN_LADSPA = 2
  173. PLUGIN_DSSI = 3
  174. PLUGIN_LV2 = 4
  175. PLUGIN_VST = 5
  176. PLUGIN_GIG = 6
  177. PLUGIN_SF2 = 7
  178. PLUGIN_SFZ = 8
  179. # Plugin Category
  180. PLUGIN_CATEGORY_NONE = 0
  181. PLUGIN_CATEGORY_SYNTH = 1
  182. PLUGIN_CATEGORY_DELAY = 2 # also Reverb
  183. PLUGIN_CATEGORY_EQ = 3
  184. PLUGIN_CATEGORY_FILTER = 4
  185. PLUGIN_CATEGORY_DYNAMICS = 5 # Amplifier, Compressor, Gate
  186. PLUGIN_CATEGORY_MODULATOR = 6 # Chorus, Flanger, Phaser
  187. PLUGIN_CATEGORY_UTILITY = 7 # Analyzer, Converter, Mixer
  188. PLUGIN_CATEGORY_OTHER = 8 # used to check if a plugin has a category
  189. # Parameter Type
  190. PARAMETER_UNKNOWN = 0
  191. PARAMETER_INPUT = 1
  192. PARAMETER_OUTPUT = 2
  193. PARAMETER_LATENCY = 3
  194. PARAMETER_SAMPLE_RATE = 4
  195. PARAMETER_LV2_FREEWHEEL = 5
  196. PARAMETER_LV2_TIME = 6
  197. # Internal Parameters Index
  198. PARAMETER_NULL = -1
  199. PARAMETER_ACTIVE = -2
  200. PARAMETER_DRYWET = -3
  201. PARAMETER_VOLUME = -4
  202. PARAMETER_BALANCE_LEFT = -5
  203. PARAMETER_BALANCE_RIGHT = -6
  204. PARAMETER_PANNING = -7
  205. # Options Type
  206. OPTION_PROCESS_NAME = 0
  207. OPTION_PROCESS_MODE = 1
  208. OPTION_PROCESS_HIGH_PRECISION = 2
  209. OPTION_FORCE_STEREO = 3
  210. OPTION_PREFER_PLUGIN_BRIDGES = 4
  211. OPTION_PREFER_UI_BRIDGES = 5
  212. OPTION_USE_DSSI_VST_CHUNKS = 6
  213. OPTION_MAX_PARAMETERS = 7
  214. OPTION_OSC_UI_TIMEOUT = 8
  215. OPTION_PREFERRED_BUFFER_SIZE = 9
  216. OPTION_PREFERRED_SAMPLE_RATE = 10
  217. OPTION_PATH_BRIDGE_NATIVE = 11
  218. OPTION_PATH_BRIDGE_POSIX32 = 12
  219. OPTION_PATH_BRIDGE_POSIX64 = 13
  220. OPTION_PATH_BRIDGE_WIN32 = 14
  221. OPTION_PATH_BRIDGE_WIN64 = 15
  222. OPTION_PATH_BRIDGE_LV2_GTK2 = 16
  223. OPTION_PATH_BRIDGE_LV2_GTK3 = 17
  224. OPTION_PATH_BRIDGE_LV2_QT4 = 18
  225. OPTION_PATH_BRIDGE_LV2_QT5 = 19
  226. OPTION_PATH_BRIDGE_LV2_COCOA = 20
  227. OPTION_PATH_BRIDGE_LV2_WINDOWS = 21
  228. OPTION_PATH_BRIDGE_LV2_X11 = 22
  229. OPTION_PATH_BRIDGE_VST_COCOA = 23
  230. OPTION_PATH_BRIDGE_VST_HWND = 24
  231. OPTION_PATH_BRIDGE_VST_X11 = 25
  232. # Callback Type
  233. CALLBACK_DEBUG = 0
  234. CALLBACK_PARAMETER_VALUE_CHANGED = 1
  235. CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED = 2
  236. CALLBACK_PARAMETER_MIDI_CC_CHANGED = 3
  237. CALLBACK_PROGRAM_CHANGED = 4
  238. CALLBACK_MIDI_PROGRAM_CHANGED = 5
  239. CALLBACK_NOTE_ON = 6
  240. CALLBACK_NOTE_OFF = 7
  241. CALLBACK_SHOW_GUI = 8
  242. CALLBACK_UPDATE = 9
  243. CALLBACK_RELOAD_INFO = 10
  244. CALLBACK_RELOAD_PARAMETERS = 11
  245. CALLBACK_RELOAD_PROGRAMS = 12
  246. CALLBACK_RELOAD_ALL = 13
  247. CALLBACK_NSM_ANNOUNCE = 14
  248. CALLBACK_NSM_OPEN1 = 15
  249. CALLBACK_NSM_OPEN2 = 16
  250. CALLBACK_NSM_SAVE = 17
  251. CALLBACK_ERROR = 18
  252. CALLBACK_QUIT = 19
  253. # Process Mode Type
  254. PROCESS_MODE_SINGLE_CLIENT = 0
  255. PROCESS_MODE_MULTIPLE_CLIENTS = 1
  256. PROCESS_MODE_CONTINUOUS_RACK = 2
  257. PROCESS_MODE_PATCHBAY = 3
  258. # Set BINARY_NATIVE
  259. if HAIKU or LINUX or MACOS:
  260. BINARY_NATIVE = BINARY_POSIX64 if is64bit else BINARY_POSIX32
  261. elif WINDOWS:
  262. BINARY_NATIVE = BINARY_WIN64 if is64bit else BINARY_WIN32
  263. else:
  264. BINARY_NATIVE = BINARY_OTHER
  265. # ------------------------------------------------------------------------------------------------------------
  266. # Carla Host object
  267. class CarlaHostObject(object):
  268. __slots__ = [
  269. 'host',
  270. 'gui',
  271. 'isControl',
  272. 'processMode',
  273. 'maxParameters'
  274. ]
  275. Carla = CarlaHostObject()
  276. Carla.host = None
  277. Carla.gui = None
  278. Carla.isControl = False
  279. Carla.processMode = PROCESS_MODE_CONTINUOUS_RACK
  280. Carla.maxParameters = MAX_RACK_PLUGINS
  281. # ------------------------------------------------------------------------------------------------------------
  282. # Carla GUI stuff
  283. ICON_STATE_NULL = 0
  284. ICON_STATE_WAIT = 1
  285. ICON_STATE_OFF = 2
  286. ICON_STATE_ON = 3
  287. PALETTE_COLOR_NONE = 0
  288. PALETTE_COLOR_WHITE = 1
  289. PALETTE_COLOR_RED = 2
  290. PALETTE_COLOR_GREEN = 3
  291. PALETTE_COLOR_BLUE = 4
  292. PALETTE_COLOR_YELLOW = 5
  293. PALETTE_COLOR_ORANGE = 6
  294. PALETTE_COLOR_BROWN = 7
  295. PALETTE_COLOR_PINK = 8
  296. CarlaStateParameter = {
  297. 'index': 0,
  298. 'name': "",
  299. 'symbol': "",
  300. 'value': 0.0,
  301. 'midiChannel': 1,
  302. 'midiCC': -1
  303. }
  304. CarlaStateCustomData = {
  305. 'type': CUSTOM_DATA_INVALID,
  306. 'key': "",
  307. 'value': ""
  308. }
  309. CarlaSaveState = {
  310. 'type': "",
  311. 'name': "",
  312. 'label': "",
  313. 'binary': "",
  314. 'uniqueId': 0,
  315. 'active': False,
  316. 'dryWet': 1.0,
  317. 'volume': 1.0,
  318. 'balanceLeft': -1.0,
  319. 'balanceRight': 1.0,
  320. 'pannning': 0.0,
  321. 'parameterList': [],
  322. 'currentProgramIndex': -1,
  323. 'currentProgramName': "",
  324. 'currentMidiBank': -1,
  325. 'currentMidiProgram': -1,
  326. 'customDataList': [],
  327. 'chunk': None
  328. }
  329. # ------------------------------------------------------------------------------------------------------------
  330. # Static MIDI CC list
  331. MIDI_CC_LIST = (
  332. #"0x00 Bank Select",
  333. "0x01 Modulation",
  334. "0x02 Breath",
  335. "0x03 (Undefined)",
  336. "0x04 Foot",
  337. "0x05 Portamento",
  338. #"0x06 (Data Entry MSB)",
  339. "0x07 Volume",
  340. "0x08 Balance",
  341. "0x09 (Undefined)",
  342. "0x0A Pan",
  343. "0x0B Expression",
  344. "0x0C FX Control 1",
  345. "0x0D FX Control 2",
  346. "0x0E (Undefined)",
  347. "0x0F (Undefined)",
  348. "0x10 General Purpose 1",
  349. "0x11 General Purpose 2",
  350. "0x12 General Purpose 3",
  351. "0x13 General Purpose 4",
  352. "0x14 (Undefined)",
  353. "0x15 (Undefined)",
  354. "0x16 (Undefined)",
  355. "0x17 (Undefined)",
  356. "0x18 (Undefined)",
  357. "0x19 (Undefined)",
  358. "0x1A (Undefined)",
  359. "0x1B (Undefined)",
  360. "0x1C (Undefined)",
  361. "0x1D (Undefined)",
  362. "0x1E (Undefined)",
  363. "0x1F (Undefined)",
  364. #"0x20 *Bank Select",
  365. #"0x21 *Modulation",
  366. #"0x22 *Breath",
  367. #"0x23 *(Undefined)",
  368. #"0x24 *Foot",
  369. #"0x25 *Portamento",
  370. #"0x26 *(Data Entry MSB)",
  371. #"0x27 *Volume",
  372. #"0x28 *Balance",
  373. #"0x29 *(Undefined)",
  374. #"0x2A *Pan",
  375. #"0x2B *Expression",
  376. #"0x2C *FX *Control 1",
  377. #"0x2D *FX *Control 2",
  378. #"0x2E *(Undefined)",
  379. #"0x2F *(Undefined)",
  380. #"0x30 *General Purpose 1",
  381. #"0x31 *General Purpose 2",
  382. #"0x32 *General Purpose 3",
  383. #"0x33 *General Purpose 4",
  384. #"0x34 *(Undefined)",
  385. #"0x35 *(Undefined)",
  386. #"0x36 *(Undefined)",
  387. #"0x37 *(Undefined)",
  388. #"0x38 *(Undefined)",
  389. #"0x39 *(Undefined)",
  390. #"0x3A *(Undefined)",
  391. #"0x3B *(Undefined)",
  392. #"0x3C *(Undefined)",
  393. #"0x3D *(Undefined)",
  394. #"0x3E *(Undefined)",
  395. #"0x3F *(Undefined)",
  396. #"0x40 Damper On/Off", # <63 off, >64 on
  397. #"0x41 Portamento On/Off", # <63 off, >64 on
  398. #"0x42 Sostenuto On/Off", # <63 off, >64 on
  399. #"0x43 Soft Pedal On/Off", # <63 off, >64 on
  400. #"0x44 Legato Footswitch", # <63 Normal, >64 Legato
  401. #"0x45 Hold 2", # <63 off, >64 on
  402. "0x46 Control 1 [Variation]",
  403. "0x47 Control 2 [Timbre]",
  404. "0x48 Control 3 [Release]",
  405. "0x49 Control 4 [Attack]",
  406. "0x4A Control 5 [Brightness]",
  407. "0x4B Control 6 [Decay]",
  408. "0x4C Control 7 [Vib Rate]",
  409. "0x4D Control 8 [Vib Depth]",
  410. "0x4E Control 9 [Vib Delay]",
  411. "0x4F Control 10 [Undefined]",
  412. "0x50 General Purpose 5",
  413. "0x51 General Purpose 6",
  414. "0x52 General Purpose 7",
  415. "0x53 General Purpose 8",
  416. "0x54 Portamento Control",
  417. "0x5B FX 1 Depth [Reverb]",
  418. "0x5C FX 2 Depth [Tremolo]",
  419. "0x5D FX 3 Depth [Chorus]",
  420. "0x5E FX 4 Depth [Detune]",
  421. "0x5F FX 5 Depth [Phaser]"
  422. )
  423. # ------------------------------------------------------------------------------------------------------------
  424. # Carla XML helpers
  425. def getSaveStateDictFromXML(xmlNode):
  426. saveState = deepcopy(CarlaSaveState)
  427. node = xmlNode.firstChild()
  428. while not node.isNull():
  429. # ------------------------------------------------------
  430. # Info
  431. if node.toElement().tagName() == "Info":
  432. xmlInfo = node.toElement().firstChild()
  433. while not xmlInfo.isNull():
  434. tag = xmlInfo.toElement().tagName()
  435. text = xmlInfo.toElement().text().strip()
  436. if tag == "Type":
  437. saveState["type"] = text
  438. elif tag == "Name":
  439. saveState["name"] = xmlSafeString(text, False)
  440. elif tag in ("Label", "URI"):
  441. saveState["label"] = xmlSafeString(text, False)
  442. elif tag == "Binary":
  443. saveState["binary"] = xmlSafeString(text, False)
  444. elif tag == "UniqueID":
  445. if text.isdigit(): saveState["uniqueId"] = int(text)
  446. xmlInfo = xmlInfo.nextSibling()
  447. # ------------------------------------------------------
  448. # Data
  449. elif node.toElement().tagName() == "Data":
  450. xmlData = node.toElement().firstChild()
  451. while not xmlData.isNull():
  452. tag = xmlData.toElement().tagName()
  453. text = xmlData.toElement().text().strip()
  454. # ----------------------------------------------
  455. # Internal Data
  456. if tag == "Active":
  457. saveState['active'] = bool(text == "Yes")
  458. elif tag == "DryWet":
  459. if isNumber(text): saveState["dryWet"] = float(text)
  460. elif tag == "Volume":
  461. if isNumber(text): saveState["volume"] = float(text)
  462. elif tag == "Balance-Left":
  463. if isNumber(text): saveState["balanceLeft"] = float(text)
  464. elif tag == "Balance-Right":
  465. if isNumber(text): saveState["balanceRight"] = float(text)
  466. elif tag == "Panning":
  467. if isNumber(text): saveState["pannning"] = float(text)
  468. # ----------------------------------------------
  469. # Program (current)
  470. elif tag == "CurrentProgramIndex":
  471. if text.isdigit(): saveState["currentProgramIndex"] = int(text)
  472. elif tag == "CurrentProgramName":
  473. saveState["currentProgramName"] = xmlSafeString(text, False)
  474. # ----------------------------------------------
  475. # Midi Program (current)
  476. elif tag == "CurrentMidiBank":
  477. if text.isdigit(): saveState["currentMidiBank"] = int(text)
  478. elif tag == "CurrentMidiProgram":
  479. if text.isdigit(): saveState["currentMidiProgram"] = int(text)
  480. # ----------------------------------------------
  481. # Parameters
  482. elif tag == "Parameter":
  483. stateParameter = deepcopy(CarlaStateParameter)
  484. xmlSubData = xmlData.toElement().firstChild()
  485. while not xmlSubData.isNull():
  486. pTag = xmlSubData.toElement().tagName()
  487. pText = xmlSubData.toElement().text().strip()
  488. if pTag == "Index":
  489. if pText.isdigit(): stateParameter["index"] = int(pText)
  490. elif pTag == "Name":
  491. stateParameter["name"] = xmlSafeString(pText, False)
  492. elif pTag == "Symbol":
  493. stateParameter["symbol"] = xmlSafeString(pText, False)
  494. elif pTag == "Value":
  495. if isNumber(pText): stateParameter["value"] = float(pText)
  496. elif pTag == "MidiChannel":
  497. if pText.isdigit(): stateParameter["midiChannel"] = int(pText)
  498. elif pTag == "MidiCC":
  499. if pText.isdigit(): stateParameter["midiCC"] = int(pText)
  500. xmlSubData = xmlSubData.nextSibling()
  501. saveState["parameterList"].append(stateParameter)
  502. # ----------------------------------------------
  503. # Custom Data
  504. elif tag == "CustomData":
  505. stateCustomData = deepcopy(CarlaStateCustomData)
  506. xmlSubData = xmlData.toElement().firstChild()
  507. while not xmlSubData.isNull():
  508. cTag = xmlSubData.toElement().tagName()
  509. cText = xmlSubData.toElement().text().strip()
  510. if cTag == "Type":
  511. stateCustomData["type"] = xmlSafeString(cText, False)
  512. elif cTag == "Key":
  513. stateCustomData["key"] = xmlSafeString(cText, False)
  514. elif cTag == "Value":
  515. stateCustomData["value"] = xmlSafeString(cText, False)
  516. xmlSubData = xmlSubData.nextSibling()
  517. saveState["customDataList"].append(stateCustomData)
  518. # ----------------------------------------------
  519. # Chunk
  520. elif tag == "Chunk":
  521. saveState["chunk"] = xmlSafeString(text, False)
  522. # ----------------------------------------------
  523. xmlData = xmlData.nextSibling()
  524. # ------------------------------------------------------
  525. node = node.nextSibling()
  526. return saveState
  527. def xmlSafeString(string, toXml):
  528. if toXml:
  529. return string.replace("&", "&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;")
  530. else:
  531. return string.replace("&amp;", "&").replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"")
  532. # ------------------------------------------------------------------------------------------------------------
  533. # Carla About dialog
  534. class CarlaAboutW(QDialog, ui_carla_about.Ui_CarlaAboutW):
  535. def __init__(self, parent):
  536. QDialog.__init__(self, parent)
  537. self.setupUi(self)
  538. oscTxt = self.tr(" - <b>OSC Bridge Version</b>") if Carla.isControl else ""
  539. self.l_about.setText(self.tr(""
  540. "<br>Version %s"
  541. "<br>Carla is a Multi-Plugin Host for JACK%s.<br>"
  542. "<br>Copyright (C) 2011-2012 falkTX<br>"
  543. "" % (VERSION, oscTxt)))
  544. if Carla.isControl:
  545. self.l_extended.setVisible(False) # TODO - write about this special OSC version
  546. self.tabWidget.removeTab(1)
  547. self.tabWidget.removeTab(1)
  548. else:
  549. self.l_extended.setText(cString(Carla.host.get_extended_license_text()))
  550. self.le_osc_url.setText(cString(Carla.host.get_host_osc_url()) if Carla.host.is_engine_running() else self.tr("(Engine not running)"))
  551. self.l_osc_cmds.setText(
  552. " /set_active <i-value>\n"
  553. " /set_drywet <f-value>\n"
  554. " /set_volume <f-value>\n"
  555. " /set_balance_left <f-value>\n"
  556. " /set_balance_right <f-value>\n"
  557. " /set_panning <f-value>\n"
  558. " /set_parameter_value <i-index> <f-value>\n"
  559. " /set_parameter_midi_cc <i-index> <i-cc>\n"
  560. " /set_parameter_midi_channel <i-index> <i-channel>\n"
  561. " /set_program <i-index>\n"
  562. " /set_midi_program <i-index>\n"
  563. " /note_on <i-note> <i-velo>\n"
  564. " /note_off <i-note>\n"
  565. )
  566. self.l_example.setText("/Carla/2/set_parameter_value 5 1.0")
  567. self.l_example_help.setText("<i>(as in this example, \"2\" is the plugin number and \"5\" the parameter)</i>")
  568. self.l_ladspa.setText(self.tr("Everything! (Including LRDF)"))
  569. self.l_dssi.setText(self.tr("Everything! (Including CustomData/Chunks)"))
  570. self.l_lv2.setText(self.tr("About 95&#37; complete (using custom extensions).<br/>"
  571. "Implemented Feature/Extensions:"
  572. "<ul>"
  573. "<li>http://lv2plug.in/ns/ext/atom</li>"
  574. "<li>http://lv2plug.in/ns/ext/buf-size</li>"
  575. "<li>http://lv2plug.in/ns/ext/data-access</li>"
  576. #"<li>http://lv2plug.in/ns/ext/dynmanifest</li>"
  577. "<li>http://lv2plug.in/ns/ext/event</li>"
  578. "<li>http://lv2plug.in/ns/ext/instance-access</li>"
  579. "<li>http://lv2plug.in/ns/ext/log</li>"
  580. "<li>http://lv2plug.in/ns/ext/midi</li>"
  581. "<li>http://lv2plug.in/ns/ext/options</li>"
  582. #"<li>http://lv2plug.in/ns/ext/parameters</li>"
  583. "<li>http://lv2plug.in/ns/ext/patch</li>"
  584. #"<li>http://lv2plug.in/ns/ext/port-groups</li>"
  585. "<li>http://lv2plug.in/ns/ext/port-props</li>"
  586. #"<li>http://lv2plug.in/ns/ext/presets</li>"
  587. "<li>http://lv2plug.in/ns/ext/state</li>"
  588. "<li>http://lv2plug.in/ns/ext/time</li>"
  589. "<li>http://lv2plug.in/ns/ext/uri-map</li>"
  590. "<li>http://lv2plug.in/ns/ext/urid</li>"
  591. "<li>http://lv2plug.in/ns/ext/worker</li>"
  592. "<li>http://lv2plug.in/ns/extensions/ui</li>"
  593. "<li>http://lv2plug.in/ns/extensions/units</li>"
  594. "<li>http://kxstudio.sf.net/ns/lv2ext/external-ui</li>"
  595. "<li>http://kxstudio.sf.net/ns/lv2ext/programs</li>"
  596. "<li>http://kxstudio.sf.net/ns/lv2ext/rtmempool</li>"
  597. "<li>http://ll-plugins.nongnu.org/lv2/ext/midimap</li>"
  598. "<li>http://ll-plugins.nongnu.org/lv2/ext/miditype</li>"
  599. "</ul>"))
  600. self.l_vst.setText(self.tr("<p>About 85&#37; complete (missing vst bank/presets and some minor stuff)</p>"))
  601. def done(self, r):
  602. QDialog.done(self, r)
  603. self.close()
  604. # ------------------------------------------------------------------------------------------------------------
  605. # Plugin Parameter
  606. class PluginParameter(QWidget, ui_carla_parameter.Ui_PluginParameter):
  607. def __init__(self, parent, pInfo, pluginId, tabIndex):
  608. QWidget.__init__(self, parent)
  609. self.setupUi(self)
  610. pType = pInfo['type']
  611. pHints = pInfo['hints']
  612. self.m_midiCC = -1
  613. self.m_midiChannel = 1
  614. self.m_pluginId = pluginId
  615. self.m_parameterId = pInfo['index']
  616. self.m_tabIndex = tabIndex
  617. self.label.setText(pInfo['name'])
  618. for MIDI_CC in MIDI_CC_LIST:
  619. self.combo.addItem(MIDI_CC)
  620. if pType == PARAMETER_INPUT:
  621. self.widget.set_minimum(pInfo['minimum'])
  622. self.widget.set_maximum(pInfo['maximum'])
  623. self.widget.set_default(pInfo['default'])
  624. self.widget.set_value(pInfo['current'], False)
  625. self.widget.set_label(pInfo['unit'])
  626. self.widget.set_step(pInfo['step'])
  627. self.widget.set_step_small(pInfo['stepSmall'])
  628. self.widget.set_step_large(pInfo['stepLarge'])
  629. self.widget.set_scalepoints(pInfo['scalepoints'], bool(pHints & PARAMETER_USES_SCALEPOINTS))
  630. if not pHints & PARAMETER_IS_ENABLED:
  631. self.widget.set_read_only(True)
  632. self.combo.setEnabled(False)
  633. self.sb_channel.setEnabled(False)
  634. elif not pHints & PARAMETER_IS_AUTOMABLE:
  635. self.combo.setEnabled(False)
  636. self.sb_channel.setEnabled(False)
  637. elif pType == PARAMETER_OUTPUT:
  638. self.widget.set_minimum(pInfo['minimum'])
  639. self.widget.set_maximum(pInfo['maximum'])
  640. self.widget.set_value(pInfo['current'], False)
  641. self.widget.set_label(pInfo['unit'])
  642. self.widget.set_read_only(True)
  643. if not pHints & PARAMETER_IS_AUTOMABLE:
  644. self.combo.setEnabled(False)
  645. self.sb_channel.setEnabled(False)
  646. else:
  647. self.widget.setVisible(False)
  648. self.combo.setVisible(False)
  649. self.sb_channel.setVisible(False)
  650. self.set_parameter_midi_cc(pInfo['midiCC'])
  651. self.set_parameter_midi_channel(pInfo['midiChannel'])
  652. self.connect(self.widget, SIGNAL("valueChanged(double)"), SLOT("slot_valueChanged(double)"))
  653. self.connect(self.sb_channel, SIGNAL("valueChanged(int)"), SLOT("slot_midiChannelChanged(int)"))
  654. self.connect(self.combo, SIGNAL("currentIndexChanged(int)"), SLOT("slot_midiCcChanged(int)"))
  655. #if force_parameters_style:
  656. #self.widget.force_plastique_style()
  657. if pHints & PARAMETER_USES_CUSTOM_TEXT:
  658. self.widget.set_text_call(self.textCallBack)
  659. self.widget.updateAll()
  660. def setDefaultValue(self, value):
  661. self.widget.set_default(value)
  662. def set_parameter_value(self, value, send=True):
  663. self.widget.set_value(value, send)
  664. def set_parameter_midi_cc(self, cc):
  665. self.m_midiCC = cc
  666. self.set_MIDI_CC_in_ComboBox(cc)
  667. def set_parameter_midi_channel(self, channel):
  668. self.m_midiChannel = channel+1
  669. self.sb_channel.setValue(channel+1)
  670. def set_MIDI_CC_in_ComboBox(self, cc):
  671. for i in range(len(MIDI_CC_LIST)):
  672. ccText = MIDI_CC_LIST[i].split(" ")[0]
  673. if int(ccText, 16) == cc:
  674. ccIndex = i
  675. break
  676. else:
  677. ccIndex = -1
  678. self.combo.setCurrentIndex(ccIndex+1)
  679. def tabIndex(self):
  680. return self.m_tabIndex
  681. def textCallBack(self):
  682. return cString(Carla.host.get_parameter_text(self.m_pluginId, self.m_parameterId))
  683. @pyqtSlot(float)
  684. def slot_valueChanged(self, value):
  685. self.emit(SIGNAL("valueChanged(int, double)"), self.m_parameterId, value)
  686. @pyqtSlot(int)
  687. def slot_midiCcChanged(self, ccIndex):
  688. if ccIndex <= 0:
  689. cc = -1
  690. else:
  691. ccText = MIDI_CC_LIST[ccIndex - 1].split(" ")[0]
  692. cc = int(ccText, 16)
  693. if self.m_midiCC != cc:
  694. self.emit(SIGNAL("midiCcChanged(int, int)"), self.m_parameterId, cc)
  695. self.m_midiCC = cc
  696. @pyqtSlot(int)
  697. def slot_midiChannelChanged(self, channel):
  698. if self.m_midiChannel != channel:
  699. self.emit(SIGNAL("midiChannelChanged(int, int)"), self.m_parameterId, channel)
  700. self.m_midiChannel = channel