Collection of tools useful for audio production
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.

2197 lines
82KB

  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # Common Carla code
  4. # Copyright (C) 2011-2012 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 platform
  20. from copy import deepcopy
  21. from decimal import Decimal
  22. from sip import unwrapinstance
  23. from PyQt4.QtCore import pyqtSlot, qFatal, Qt, QSettings, QTimer
  24. from PyQt4.QtGui import QColor, QCursor, QDialog, QFontMetrics, QFrame, QGraphicsScene, QInputDialog, QLinearGradient, QMenu, QPainter, QPainterPath, QVBoxLayout, QWidget
  25. from PyQt4.QtXml import QDomDocument
  26. try:
  27. from PyQt4.QtGui import QX11EmbedContainer
  28. GuiContainer = QX11EmbedContainer
  29. except:
  30. GuiContainer = QWidget
  31. # ------------------------------------------------------------------------------------------------------------
  32. # Imports (Custom)
  33. import ui_carla_about, ui_carla_edit, ui_carla_parameter, ui_carla_plugin
  34. from shared import *
  35. is64bit = bool(platform.architecture()[0] == "64bit" and sys.maxsize > 2**32)
  36. # ------------------------------------------------------------------------------------------------
  37. # Backend defines
  38. # static max values
  39. MAX_PLUGINS = 99
  40. MAX_PARAMETERS = 200
  41. # group plugin hints
  42. PLUGIN_IS_BRIDGE = 0x001
  43. PLUGIN_IS_SYNTH = 0x002
  44. PLUGIN_HAS_GUI = 0x004
  45. PLUGIN_USES_CHUNKS = 0x008
  46. PLUGIN_USES_SINGLE_THREAD = 0x010
  47. PLUGIN_CAN_DRYWET = 0x020
  48. PLUGIN_CAN_VOLUME = 0x040
  49. PLUGIN_CAN_BALANCE = 0x080
  50. PLUGIN_CAN_FORCE_STEREO = 0x100
  51. # group parameter hints
  52. PARAMETER_IS_BOOLEAN = 0x01
  53. PARAMETER_IS_INTEGER = 0x02
  54. PARAMETER_IS_LOGARITHMIC = 0x04
  55. PARAMETER_IS_ENABLED = 0x08
  56. PARAMETER_IS_AUTOMABLE = 0x10
  57. PARAMETER_USES_SAMPLERATE = 0x20
  58. PARAMETER_USES_SCALEPOINTS = 0x40
  59. PARAMETER_USES_CUSTOM_TEXT = 0x80
  60. # group custom data types
  61. CUSTOM_DATA_INVALID = None
  62. CUSTOM_DATA_STRING = "urn:carla:string"
  63. # enum BinaryType
  64. BINARY_NONE = 0
  65. BINARY_POSIX32 = 1
  66. BINARY_POSIX64 = 2
  67. BINARY_WIN32 = 3
  68. BINARY_WIN64 = 4
  69. BINARY_OTHER = 5
  70. # enum PluginType
  71. PLUGIN_NONE = 0
  72. PLUGIN_INTERNAL = 1
  73. PLUGIN_LADSPA = 2
  74. PLUGIN_DSSI = 3
  75. PLUGIN_LV2 = 4
  76. PLUGIN_VST = 5
  77. PLUGIN_GIG = 6
  78. PLUGIN_SF2 = 7
  79. PLUGIN_SFZ = 8
  80. # enum PluginCategory
  81. PLUGIN_CATEGORY_NONE = 0
  82. PLUGIN_CATEGORY_SYNTH = 1
  83. PLUGIN_CATEGORY_DELAY = 2 # also Reverb
  84. PLUGIN_CATEGORY_EQ = 3
  85. PLUGIN_CATEGORY_FILTER = 4
  86. PLUGIN_CATEGORY_DYNAMICS = 5 # Amplifier, Compressor, Gate
  87. PLUGIN_CATEGORY_MODULATOR = 6 # Chorus, Flanger, Phaser
  88. PLUGIN_CATEGORY_UTILITY = 7 # Analyzer, Converter, Mixer
  89. PLUGIN_CATEGORY_OTHER = 8 # used to check if a plugin has a category
  90. # enum ParameterType
  91. PARAMETER_UNKNOWN = 0
  92. PARAMETER_INPUT = 1
  93. PARAMETER_OUTPUT = 2
  94. PARAMETER_LATENCY = 3
  95. PARAMETER_SAMPLE_RATE = 4
  96. PARAMETER_LV2_FREEWHEEL = 5
  97. PARAMETER_LV2_TIME = 6
  98. # enum InternalParametersIndex
  99. PARAMETER_NULL = -1
  100. PARAMETER_ACTIVE = -2
  101. PARAMETER_DRYWET = -3
  102. PARAMETER_VOLUME = -4
  103. PARAMETER_BALANCE_LEFT = -5
  104. PARAMETER_BALANCE_RIGHT = -6
  105. # enum GuiType
  106. GUI_NONE = 0
  107. GUI_INTERNAL_QT4 = 1
  108. GUI_INTERNAL_COCOA = 2
  109. GUI_INTERNAL_HWND = 3
  110. GUI_INTERNAL_X11 = 4
  111. GUI_EXTERNAL_LV2 = 5
  112. GUI_EXTERNAL_SUIL = 6
  113. GUI_EXTERNAL_OSC = 7
  114. # enum OptionsType
  115. OPTION_PROCESS_NAME = 0
  116. OPTION_PROCESS_MODE = 1
  117. OPTION_PROCESS_HIGH_PRECISION = 2
  118. OPTION_MAX_PARAMETERS = 3
  119. OPTION_PREFERRED_BUFFER_SIZE = 4
  120. OPTION_PREFERRED_SAMPLE_RATE = 5
  121. OPTION_FORCE_STEREO = 6
  122. OPTION_USE_DSSI_VST_CHUNKS = 7
  123. OPTION_PREFER_PLUGIN_BRIDGES = 8
  124. OPTION_PREFER_UI_BRIDGES = 9
  125. OPTION_OSC_UI_TIMEOUT = 10
  126. OPTION_PATH_BRIDGE_POSIX32 = 11
  127. OPTION_PATH_BRIDGE_POSIX64 = 12
  128. OPTION_PATH_BRIDGE_WIN32 = 13
  129. OPTION_PATH_BRIDGE_WIN64 = 14
  130. OPTION_PATH_BRIDGE_LV2_GTK2 = 15
  131. OPTION_PATH_BRIDGE_LV2_GTK3 = 16
  132. OPTION_PATH_BRIDGE_LV2_QT4 = 17
  133. OPTION_PATH_BRIDGE_LV2_QT5 = 18
  134. OPTION_PATH_BRIDGE_LV2_COCOA = 19
  135. OPTION_PATH_BRIDGE_LV2_WINDOWS = 20
  136. OPTION_PATH_BRIDGE_LV2_X11 = 21
  137. OPTION_PATH_BRIDGE_VST_COCOA = 22
  138. OPTION_PATH_BRIDGE_VST_HWND = 23
  139. OPTION_PATH_BRIDGE_VST_X11 = 24
  140. # enum CallbackType
  141. CALLBACK_DEBUG = 0
  142. CALLBACK_PARAMETER_VALUE_CHANGED = 1
  143. CALLBACK_PARAMETER_MIDI_CHANNEL_CHANGED = 2
  144. CALLBACK_PARAMETER_MIDI_CC_CHANGED = 3
  145. CALLBACK_PROGRAM_CHANGED = 4
  146. CALLBACK_MIDI_PROGRAM_CHANGED = 5
  147. CALLBACK_NOTE_ON = 6
  148. CALLBACK_NOTE_OFF = 7
  149. CALLBACK_SHOW_GUI = 8
  150. CALLBACK_RESIZE_GUI = 9
  151. CALLBACK_UPDATE = 10
  152. CALLBACK_RELOAD_INFO = 11
  153. CALLBACK_RELOAD_PARAMETERS = 12
  154. CALLBACK_RELOAD_PROGRAMS = 13
  155. CALLBACK_RELOAD_ALL = 14
  156. CALLBACK_NSM_ANNOUNCE = 15
  157. CALLBACK_NSM_OPEN1 = 16
  158. CALLBACK_NSM_OPEN2 = 17
  159. CALLBACK_NSM_SAVE = 18
  160. CALLBACK_ERROR = 19
  161. CALLBACK_QUIT = 20
  162. # enum ProcessModeType
  163. PROCESS_MODE_SINGLE_CLIENT = 0
  164. PROCESS_MODE_MULTIPLE_CLIENTS = 1
  165. PROCESS_MODE_CONTINUOUS_RACK = 2
  166. PROCESS_MODE_PATCHBAY = 3
  167. # Set BINARY_NATIVE
  168. if HAIKU or LINUX or MACOS:
  169. BINARY_NATIVE = BINARY_POSIX64 if is64bit else BINARY_POSIX32
  170. elif WINDOWS:
  171. BINARY_NATIVE = BINARY_WIN64 if is64bit else BINARY_WIN32
  172. else:
  173. BINARY_NATIVE = BINARY_OTHER
  174. # ------------------------------------------------------------------------------------------------------------
  175. # Carla Host object
  176. class CarlaHostObject(object):
  177. __slots__ = [
  178. 'host',
  179. 'gui',
  180. 'isControl',
  181. 'processMode',
  182. 'maxParameters'
  183. ]
  184. Carla = CarlaHostObject()
  185. Carla.host = None
  186. Carla.gui = None
  187. Carla.isControl = False
  188. Carla.processMode = PROCESS_MODE_CONTINUOUS_RACK
  189. Carla.maxParameters = MAX_PARAMETERS
  190. # ------------------------------------------------------------------------------------------------------------
  191. # Carla GUI stuff
  192. ICON_STATE_NULL = 0
  193. ICON_STATE_WAIT = 1
  194. ICON_STATE_OFF = 2
  195. ICON_STATE_ON = 3
  196. PALETTE_COLOR_NONE = 0
  197. PALETTE_COLOR_WHITE = 1
  198. PALETTE_COLOR_RED = 2
  199. PALETTE_COLOR_GREEN = 3
  200. PALETTE_COLOR_BLUE = 4
  201. PALETTE_COLOR_YELLOW = 5
  202. PALETTE_COLOR_ORANGE = 6
  203. PALETTE_COLOR_BROWN = 7
  204. PALETTE_COLOR_PINK = 8
  205. CarlaStateParameter = {
  206. 'index': 0,
  207. 'name': "",
  208. 'symbol': "",
  209. 'value': 0.0,
  210. 'midiChannel': 1,
  211. 'midiCC': -1
  212. }
  213. CarlaStateCustomData = {
  214. 'type': CUSTOM_DATA_INVALID,
  215. 'key': "",
  216. 'value': ""
  217. }
  218. CarlaSaveState = {
  219. 'Type': "",
  220. 'Name': "",
  221. 'Label': "",
  222. 'Binary': "",
  223. 'UniqueID': 0,
  224. 'Active': False,
  225. 'DryWet': 1.0,
  226. 'Volume': 1.0,
  227. 'Balance-Left': -1.0,
  228. 'Balance-Right': 1.0,
  229. 'Parameters': [],
  230. 'CurrentProgramIndex': -1,
  231. 'CurrentProgramName': "",
  232. 'CurrentMidiBank': -1,
  233. 'CurrentMidiProgram': -1,
  234. 'CustomData': [],
  235. 'Chunk': None
  236. }
  237. def getSaveStateDictFromXML(xmlNode):
  238. saveState = deepcopy(CarlaSaveState)
  239. node = xmlNode.firstChild()
  240. while not node.isNull():
  241. # ------------------------------------------------------
  242. # Info
  243. if node.toElement().tagName() == "Info":
  244. xmlInfo = node.toElement().firstChild()
  245. while not xmlInfo.isNull():
  246. tag = xmlInfo.toElement().tagName()
  247. text = xmlInfo.toElement().text().strip()
  248. if tag == "Type":
  249. saveState['Type'] = text
  250. elif tag == "Name":
  251. saveState['Name'] = xmlSafeString(text, False)
  252. elif tag in ("Label", "URI"):
  253. saveState['Label'] = xmlSafeString(text, False)
  254. elif tag == "Binary":
  255. saveState['Binary'] = xmlSafeString(text, False)
  256. elif tag == "UniqueID":
  257. if text.isdigit(): saveState['UniqueID'] = int(text)
  258. xmlInfo = xmlInfo.nextSibling()
  259. # ------------------------------------------------------
  260. # Data
  261. elif node.toElement().tagName() == "Data":
  262. xmlData = node.toElement().firstChild()
  263. while not xmlData.isNull():
  264. tag = xmlData.toElement().tagName()
  265. text = xmlData.toElement().text().strip()
  266. # ----------------------------------------------
  267. # Internal Data
  268. if tag == "Active":
  269. saveState['Active'] = bool(text == "Yes")
  270. elif tag == "DryWet":
  271. if isNumber(text): saveState['DryWet'] = float(text)
  272. elif tag == "Volume":
  273. if isNumber(text): saveState['Volume'] = float(text)
  274. elif tag == "Balance-Left":
  275. if isNumber(text): saveState['Balance-Left'] = float(text)
  276. elif tag == "Balance-Right":
  277. if isNumber(text): saveState['Balance-Right'] = float(text)
  278. # ----------------------------------------------
  279. # Program (current)
  280. elif tag == "CurrentProgramIndex":
  281. if text.isdigit(): saveState['CurrentProgramIndex'] = int(text)
  282. elif tag == "CurrentProgramName":
  283. saveState['CurrentProgramName'] = xmlSafeString(text, False)
  284. # ----------------------------------------------
  285. # Midi Program (current)
  286. elif tag == "CurrentMidiBank":
  287. if text.isdigit(): saveState['CurrentMidiBank'] = int(text)
  288. elif tag == "CurrentMidiProgram":
  289. if text.isdigit(): saveState['CurrentMidiProgram'] = int(text)
  290. # ----------------------------------------------
  291. # Parameters
  292. elif tag == "Parameter":
  293. stateParameter = deepcopy(CarlaStateParameter)
  294. xmlSubData = xmlData.toElement().firstChild()
  295. while not xmlSubData.isNull():
  296. pTag = xmlSubData.toElement().tagName()
  297. pText = xmlSubData.toElement().text().strip()
  298. if pTag == "index":
  299. if pText.isdigit(): stateParameter['index'] = int(pText)
  300. elif pTag == "name":
  301. stateParameter['name'] = xmlSafeString(pText, False)
  302. elif pTag == "symbol":
  303. stateParameter['symbol'] = xmlSafeString(pText, False)
  304. elif pTag == "value":
  305. if isNumber(pText): stateParameter['value'] = float(pText)
  306. elif pTag == "midiChannel":
  307. if pText.isdigit(): stateParameter['midiChannel'] = int(pText)
  308. elif pTag == "midiCC":
  309. if pText.isdigit(): stateParameter['midiCC'] = int(pText)
  310. xmlSubData = xmlSubData.nextSibling()
  311. saveState['Parameters'].append(stateParameter)
  312. # ----------------------------------------------
  313. # Custom Data
  314. elif tag == "CustomData":
  315. stateCustomData = deepcopy(CarlaStateCustomData)
  316. xmlSubData = xmlData.toElement().firstChild()
  317. while not xmlSubData.isNull():
  318. cTag = xmlSubData.toElement().tagName()
  319. cText = xmlSubData.toElement().text().strip()
  320. if cTag == "type":
  321. stateCustomData['type'] = cText
  322. elif cTag == "key":
  323. stateCustomData['key'] = xmlSafeString(cText, False)
  324. elif cTag == "value":
  325. stateCustomData['value'] = xmlSafeString(cText, False)
  326. xmlSubData = xmlSubData.nextSibling()
  327. saveState['CustomData'].append(stateCustomData)
  328. # ----------------------------------------------
  329. # Chunk
  330. elif tag == "Chunk":
  331. saveState['Chunk'] = xmlSafeString(text, False)
  332. # ----------------------------------------------
  333. xmlData = xmlData.nextSibling()
  334. # ------------------------------------------------------
  335. node = node.nextSibling()
  336. return saveState
  337. def xmlSafeString(string, toXml):
  338. if toXml:
  339. return string.replace("&", "&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;")
  340. else:
  341. return string.replace("&amp;", "&").replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"")
  342. # ------------------------------------------------------------------------------------------------------------
  343. # CarlaAbout.cpp
  344. class CarlaAboutW(QDialog, ui_carla_about.Ui_CarlaAboutW):
  345. def __init__(self, parent):
  346. QDialog.__init__(self, parent)
  347. self.setupUi(self)
  348. self.l_about.setText(self.tr(""
  349. "<br>Version %s"
  350. "<br>Carla is a Multi-Plugin Host for JACK%s.<br>"
  351. "<br>Copyright (C) 2011-2012 falkTX<br>"
  352. "" % (VERSION, " - <b>OSC Bridge Version</b>" if Carla.isControl else "")))
  353. if Carla.isControl:
  354. self.l_extended.setVisible(False) # TODO - write about this special OSC version
  355. self.tabWidget.removeTab(1)
  356. self.tabWidget.removeTab(1)
  357. else:
  358. self.l_extended.setText(cString(Carla.host.get_extended_license_text()))
  359. self.le_osc_url.setText(cString(Carla.host.get_host_osc_url()) if Carla.host.is_engine_running() else self.tr("(Engine not running)"))
  360. self.l_osc_cmds.setText(
  361. " /set_active <i-value>\n"
  362. " /set_drywet <f-value>\n"
  363. " /set_volume <f-value>\n"
  364. " /set_balance_left <f-value>\n"
  365. " /set_balance_right <f-value>\n"
  366. " /set_parameter_value <i-index> <f-value>\n"
  367. " /set_parameter_midi_cc <i-index> <i-cc>\n"
  368. " /set_parameter_midi_channel <i-index> <i-channel>\n"
  369. " /set_program <i-index>\n"
  370. " /set_midi_program <i-index>\n"
  371. " /note_on <i-note> <i-velo>\n"
  372. " /note_off <i-note>\n"
  373. )
  374. self.l_example.setText("/Carla/2/set_parameter_value 5 1.0")
  375. self.l_example_help.setText("<i>(as in this example, \"2\" is the plugin number and \"5\" the parameter)</i>")
  376. self.l_ladspa.setText(self.tr("Everything! (Including LRDF)"))
  377. self.l_dssi.setText(self.tr("Everything! (Including CustomData/Chunks)"))
  378. self.l_lv2.setText(self.tr("About 95&#37; complete (using custom extensions).<br/>"
  379. "Implemented Feature/Extensions:"
  380. "<ul>"
  381. "<li>http://lv2plug.in/ns/ext/atom</li>"
  382. "<li>http://lv2plug.in/ns/ext/buf-size</li>"
  383. "<li>http://lv2plug.in/ns/ext/data-access</li>"
  384. #"<li>http://lv2plug.in/ns/ext/dynmanifest</li>"
  385. "<li>http://lv2plug.in/ns/ext/event</li>"
  386. "<li>http://lv2plug.in/ns/ext/instance-access</li>"
  387. "<li>http://lv2plug.in/ns/ext/log</li>"
  388. "<li>http://lv2plug.in/ns/ext/midi</li>"
  389. "<li>http://lv2plug.in/ns/ext/options</li>"
  390. #"<li>http://lv2plug.in/ns/ext/parameters</li>"
  391. "<li>http://lv2plug.in/ns/ext/patch</li>"
  392. #"<li>http://lv2plug.in/ns/ext/port-groups</li>"
  393. "<li>http://lv2plug.in/ns/ext/port-props</li>"
  394. #"<li>http://lv2plug.in/ns/ext/presets</li>"
  395. "<li>http://lv2plug.in/ns/ext/state</li>"
  396. "<li>http://lv2plug.in/ns/ext/time</li>"
  397. "<li>http://lv2plug.in/ns/ext/uri-map</li>"
  398. "<li>http://lv2plug.in/ns/ext/urid</li>"
  399. "<li>http://lv2plug.in/ns/ext/worker</li>"
  400. "<li>http://lv2plug.in/ns/extensions/ui</li>"
  401. "<li>http://lv2plug.in/ns/extensions/units</li>"
  402. "<li>http://kxstudio.sf.net/ns/lv2ext/external-ui</li>"
  403. "<li>http://kxstudio.sf.net/ns/lv2ext/programs</li>"
  404. "<li>http://kxstudio.sf.net/ns/lv2ext/rtmempool</li>"
  405. "<li>http://ll-plugins.nongnu.org/lv2/ext/midimap</li>"
  406. "<li>http://ll-plugins.nongnu.org/lv2/ext/miditype</li>"
  407. "</ul>"))
  408. self.l_vst.setText(self.tr("<p>About 85&#37; complete (missing vst bank/presets and some minor stuff)</p>"))
  409. def done(self, r):
  410. QDialog.done(self, r)
  411. self.close()
  412. # ------------------------------------------------------------------------------------------------------------
  413. # PluginParameter.cpp
  414. # Plugin Parameter
  415. class PluginParameter(QWidget, ui_carla_parameter.Ui_PluginParameter):
  416. def __init__(self, parent, pInfo, pluginId, tabIndex):
  417. QWidget.__init__(self, parent)
  418. self.setupUi(self)
  419. pType = pInfo['type']
  420. pHints = pInfo['hints']
  421. self.m_midiCC = -1
  422. self.m_midiChannel = 1
  423. self.m_pluginId = pluginId
  424. self.m_parameterId = pInfo['index']
  425. self.m_tabIndex = tabIndex
  426. self.label.setText(pInfo['name'])
  427. for MIDI_CC in MIDI_CC_LIST:
  428. self.combo.addItem(MIDI_CC)
  429. if pType == PARAMETER_INPUT:
  430. self.widget.set_minimum(pInfo['minimum'])
  431. self.widget.set_maximum(pInfo['maximum'])
  432. self.widget.set_default(pInfo['default'])
  433. self.widget.set_value(pInfo['current'], False)
  434. self.widget.set_label(pInfo['unit'])
  435. self.widget.set_step(pInfo['step'])
  436. self.widget.set_step_small(pInfo['stepSmall'])
  437. self.widget.set_step_large(pInfo['stepLarge'])
  438. self.widget.set_scalepoints(pInfo['scalepoints'], bool(pHints & PARAMETER_USES_SCALEPOINTS))
  439. if not pHints & PARAMETER_IS_ENABLED:
  440. self.widget.set_read_only(True)
  441. self.combo.setEnabled(False)
  442. self.sb_channel.setEnabled(False)
  443. elif not pHints & PARAMETER_IS_AUTOMABLE:
  444. self.combo.setEnabled(False)
  445. self.sb_channel.setEnabled(False)
  446. elif pType == PARAMETER_OUTPUT:
  447. self.widget.set_minimum(pInfo['minimum'])
  448. self.widget.set_maximum(pInfo['maximum'])
  449. self.widget.set_value(pInfo['current'], False)
  450. self.widget.set_label(pInfo['unit'])
  451. self.widget.set_read_only(True)
  452. if not pHints & PARAMETER_IS_AUTOMABLE:
  453. self.combo.setEnabled(False)
  454. self.sb_channel.setEnabled(False)
  455. else:
  456. self.widget.setVisible(False)
  457. self.combo.setVisible(False)
  458. self.sb_channel.setVisible(False)
  459. self.set_parameter_midi_cc(pInfo['midiCC'])
  460. self.set_parameter_midi_channel(pInfo['midiChannel'])
  461. self.connect(self.widget, SIGNAL("valueChanged(double)"), SLOT("slot_valueChanged(double)"))
  462. self.connect(self.sb_channel, SIGNAL("valueChanged(int)"), SLOT("slot_midiChannelChanged(int)"))
  463. self.connect(self.combo, SIGNAL("currentIndexChanged(int)"), SLOT("slot_midiCcChanged(int)"))
  464. #if force_parameters_style:
  465. #self.widget.force_plastique_style()
  466. if pHints & PARAMETER_USES_CUSTOM_TEXT:
  467. self.widget.set_text_call(self.textCallBack)
  468. self.widget.updateAll()
  469. def setDefaultValue(self, value):
  470. self.widget.set_default(value)
  471. def set_parameter_value(self, value, send=True):
  472. self.widget.set_value(value, send)
  473. def set_parameter_midi_cc(self, cc):
  474. self.m_midiCC = cc
  475. self.set_MIDI_CC_in_ComboBox(cc)
  476. def set_parameter_midi_channel(self, channel):
  477. self.m_midiChannel = channel+1
  478. self.sb_channel.setValue(channel+1)
  479. def set_MIDI_CC_in_ComboBox(self, cc):
  480. for i in range(len(MIDI_CC_LIST)):
  481. ccText = MIDI_CC_LIST[i].split(" ")[0]
  482. if int(ccText, 16) == cc:
  483. ccIndex = i
  484. break
  485. else:
  486. ccIndex = -1
  487. self.combo.setCurrentIndex(ccIndex+1)
  488. def tabIndex(self):
  489. return self.m_tabIndex
  490. def textCallBack(self):
  491. return cString(Carla.host.get_parameter_text(self.m_pluginId, self.m_parameterId))
  492. @pyqtSlot(float)
  493. def slot_valueChanged(self, value):
  494. self.emit(SIGNAL("valueChanged(int, double)"), self.m_parameterId, value)
  495. @pyqtSlot(int)
  496. def slot_midiCcChanged(self, ccIndex):
  497. if ccIndex <= 0:
  498. cc = -1
  499. else:
  500. ccText = MIDI_CC_LIST[ccIndex - 1].split(" ")[0]
  501. cc = int(ccText, 16)
  502. if self.m_midiCC != cc:
  503. self.emit(SIGNAL("midiCcChanged(int, int)"), self.m_parameterId, cc)
  504. self.m_midiCC = cc
  505. @pyqtSlot(int)
  506. def slot_midiChannelChanged(self, channel):
  507. if self.m_midiChannel != channel:
  508. self.emit(SIGNAL("midiChannelChanged(int, int)"), self.m_parameterId, channel)
  509. self.m_midiChannel = channel
  510. # Plugin Editor (Built-in)
  511. class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit):
  512. def __init__(self, parent, pluginId):
  513. QDialog.__init__(self, Carla.gui)
  514. self.setupUi(self)
  515. self.m_firstShow = True
  516. self.m_geometry = None
  517. self.m_pluginId = pluginId
  518. self.m_pluginInfo = None
  519. self.m_realParent = parent
  520. self.m_parameterCount = 0
  521. self.m_parameterList = [] # (type, id, widget)
  522. self.m_parameterIdsToUpdate = [] # id
  523. self.m_currentProgram = -1
  524. self.m_currentMidiProgram = -1
  525. self.m_currentStateFilename = None
  526. self.m_tabIconOff = QIcon(":/bitmaps/led_off.png")
  527. self.m_tabIconOn = QIcon(":/bitmaps/led_yellow.png")
  528. self.m_tabIconCount = 0
  529. self.m_tabIconTimers = []
  530. self.keyboard.setMode(self.keyboard.HORIZONTAL)
  531. self.keyboard.setOctaves(6)
  532. self.scrollArea.ensureVisible(self.keyboard.width() * 1 / 5, 0)
  533. self.scrollArea.setVisible(False)
  534. # TODO - not implemented yet
  535. self.b_reload_program.setEnabled(False)
  536. self.b_reload_midi_program.setEnabled(False)
  537. # Not available for carla-control
  538. if Carla.isControl:
  539. self.b_load_state.setEnabled(False)
  540. self.b_save_state.setEnabled(False)
  541. else:
  542. self.connect(self.b_save_state, SIGNAL("clicked()"), SLOT("slot_saveState()"))
  543. self.connect(self.b_load_state, SIGNAL("clicked()"), SLOT("slot_loadState()"))
  544. self.connect(self.keyboard, SIGNAL("noteOn(int)"), SLOT("slot_noteOn(int)"))
  545. self.connect(self.keyboard, SIGNAL("noteOff(int)"), SLOT("slot_noteOff(int)"))
  546. self.connect(self.cb_programs, SIGNAL("currentIndexChanged(int)"), SLOT("slot_programIndexChanged(int)"))
  547. self.connect(self.cb_midi_programs, SIGNAL("currentIndexChanged(int)"), SLOT("slot_midiProgramIndexChanged(int)"))
  548. self.connect(self, SIGNAL("finished(int)"), SLOT("slot_finished()"))
  549. self.do_reload_all()
  550. def do_reload_all(self):
  551. self.m_pluginInfo = Carla.host.get_plugin_info(self.m_pluginId)
  552. self.m_pluginInfo["binary"] = cString(self.m_pluginInfo["binary"])
  553. self.m_pluginInfo["name"] = cString(self.m_pluginInfo["name"])
  554. self.m_pluginInfo["label"] = cString(self.m_pluginInfo["label"])
  555. self.m_pluginInfo["maker"] = cString(self.m_pluginInfo["maker"])
  556. self.m_pluginInfo["copyright"] = cString(self.m_pluginInfo["copyright"])
  557. self.do_reload_info()
  558. self.do_reload_parameters()
  559. self.do_reload_programs()
  560. def do_reload_info(self):
  561. pluginName = cString(Carla.host.get_real_plugin_name(self.m_pluginId))
  562. pluginType = self.m_pluginInfo['type']
  563. pluginHints = self.m_pluginInfo['hints']
  564. # Automatically change to MidiProgram tab
  565. if pluginType != PLUGIN_VST and not self.le_name.text():
  566. self.tab_programs.setCurrentIndex(1)
  567. # Set Meta-Data
  568. if pluginType == PLUGIN_INTERNAL:
  569. self.le_type.setText(self.tr("Internal"))
  570. elif pluginType == PLUGIN_LADSPA:
  571. self.le_type.setText("LADSPA")
  572. elif pluginType == PLUGIN_DSSI:
  573. self.le_type.setText("DSSI")
  574. elif pluginType == PLUGIN_LV2:
  575. self.le_type.setText("LV2")
  576. elif pluginType == PLUGIN_VST:
  577. self.le_type.setText("VST")
  578. elif pluginType == PLUGIN_GIG:
  579. self.le_type.setText("GIG")
  580. elif pluginType == PLUGIN_SF2:
  581. self.le_type.setText("SF2")
  582. elif pluginType == PLUGIN_SFZ:
  583. self.le_type.setText("SFZ")
  584. else:
  585. self.le_type.setText(self.tr("Unknown"))
  586. self.le_name.setText(pluginName)
  587. self.le_name.setToolTip(pluginName)
  588. self.le_label.setText(self.m_pluginInfo['label'])
  589. self.le_label.setToolTip(self.m_pluginInfo['label'])
  590. self.le_maker.setText(self.m_pluginInfo['maker'])
  591. self.le_maker.setToolTip(self.m_pluginInfo['maker'])
  592. self.le_copyright.setText(self.m_pluginInfo['copyright'])
  593. self.le_copyright.setToolTip(self.m_pluginInfo['copyright'])
  594. self.le_unique_id.setText(str(self.m_pluginInfo['uniqueId']))
  595. self.le_unique_id.setToolTip(str(self.m_pluginInfo['uniqueId']))
  596. self.label_plugin.setText("\n%s\n" % self.m_pluginInfo['name'])
  597. self.setWindowTitle(self.m_pluginInfo['name'])
  598. # Set Processing Data
  599. audioCountInfo = Carla.host.get_audio_port_count_info(self.m_pluginId)
  600. midiCountInfo = Carla.host.get_midi_port_count_info(self.m_pluginId)
  601. paramCountInfo = Carla.host.get_parameter_count_info(self.m_pluginId)
  602. self.le_ains.setText(str(audioCountInfo['ins']))
  603. self.le_aouts.setText(str(audioCountInfo['outs']))
  604. self.le_params.setText(str(paramCountInfo['ins']))
  605. self.le_couts.setText(str(paramCountInfo['outs']))
  606. self.le_is_synth.setText(self.tr("Yes") if bool(pluginHints & PLUGIN_IS_SYNTH) else self.tr("No"))
  607. self.le_has_gui.setText(self.tr("Yes") if bool(pluginHints & PLUGIN_HAS_GUI) else self.tr("No"))
  608. # Show/hide keyboard
  609. self.scrollArea.setVisible((pluginHints & PLUGIN_IS_SYNTH) > 0 or (midiCountInfo['ins'] > 0 < midiCountInfo['outs']))
  610. # Force-Update parent for new hints (knobs)
  611. self.m_realParent.recheck_hints(pluginHints)
  612. def do_reload_parameters(self):
  613. parameterCount = Carla.host.get_parameter_count(self.m_pluginId)
  614. self.m_parameterList = []
  615. self.m_parameterIdsToUpdate = []
  616. self.m_tabIconCount = 0
  617. self.m_tabIconTimers = []
  618. # remove all parameter tabs
  619. for i in range(self.tabWidget.count()):
  620. if i == 0: continue
  621. self.tabWidget.widget(1).deleteLater()
  622. self.tabWidget.removeTab(1)
  623. if parameterCount <= 0:
  624. pass
  625. elif parameterCount <= Carla.maxParameters:
  626. paramInputListFull = []
  627. paramOutputListFull = []
  628. paramInputList = []
  629. paramInputWidth = 0
  630. paramOutputList = []
  631. paramOutputWidth = 0
  632. for i in range(parameterCount):
  633. paramInfo = Carla.host.get_parameter_info(self.m_pluginId, i)
  634. paramData = Carla.host.get_parameter_data(self.m_pluginId, i)
  635. paramRanges = Carla.host.get_parameter_ranges(self.m_pluginId, i)
  636. if paramData['type'] not in (PARAMETER_INPUT, PARAMETER_OUTPUT):
  637. continue
  638. parameter = {
  639. 'type': paramData['type'],
  640. 'hints': paramData['hints'],
  641. 'name': cString(paramInfo['name']),
  642. 'unit': cString(paramInfo['unit']),
  643. 'scalepoints': [],
  644. 'index': paramData['index'],
  645. 'default': paramRanges['def'],
  646. 'minimum': paramRanges['min'],
  647. 'maximum': paramRanges['max'],
  648. 'step': paramRanges['step'],
  649. 'stepSmall': paramRanges['stepSmall'],
  650. 'stepLarge': paramRanges['stepLarge'],
  651. 'midiCC': paramData['midiCC'],
  652. 'midiChannel': paramData['midiChannel'],
  653. 'current': Carla.host.get_current_parameter_value(self.m_pluginId, i)
  654. }
  655. for j in range(paramInfo['scalePointCount']):
  656. scalePointInfo = Carla.host.get_parameter_scalepoint_info(self.m_pluginId, i, j)
  657. parameter['scalepoints'].append(
  658. {
  659. 'value': scalePointInfo['value'],
  660. 'label': cString(scalePointInfo['label'])
  661. })
  662. # -----------------------------------------------------------------
  663. # Get width values, in packs of 10
  664. if parameter['type'] == PARAMETER_INPUT:
  665. paramInputList.append(parameter)
  666. paramInputWidthTMP = QFontMetrics(self.font()).width(parameter['name'])
  667. if paramInputWidthTMP > paramInputWidth:
  668. paramInputWidth = paramInputWidthTMP
  669. if len(paramInputList) == 10:
  670. paramInputListFull.append((paramInputList, paramInputWidth))
  671. paramInputList = []
  672. paramInputWidth = 0
  673. elif parameter['type'] == PARAMETER_OUTPUT:
  674. paramOutputList.append(parameter)
  675. paramOutputWidthTMP = QFontMetrics(self.font()).width(parameter['name'])
  676. if paramOutputWidthTMP > paramOutputWidth:
  677. paramOutputWidth = paramOutputWidthTMP
  678. if len(paramOutputList) == 10:
  679. paramOutputListFull.append((paramOutputList, paramOutputWidth))
  680. paramOutputList = []
  681. paramOutputWidth = 0
  682. else:
  683. # Final page width values
  684. if 0 < len(paramInputList) < 10:
  685. paramInputListFull.append((paramInputList, paramInputWidth))
  686. if 0 < len(paramOutputList) < 10:
  687. paramOutputListFull.append((paramOutputList, paramOutputWidth))
  688. # -----------------------------------------------------------------
  689. # Create parameter widgets
  690. self.createParameterWidgets(PARAMETER_INPUT, paramInputListFull, self.tr("Parameters"))
  691. self.createParameterWidgets(PARAMETER_OUTPUT, paramOutputListFull, self.tr("Outputs"))
  692. else: # > Carla.maxParameters
  693. fakeName = self.tr("This plugin has too many parameters to display here!")
  694. paramFakeListFull = []
  695. paramFakeList = []
  696. paramFakeWidth = QFontMetrics(self.font()).width(fakeName)
  697. parameter = {
  698. 'type': PARAMETER_UNKNOWN,
  699. 'hints': 0,
  700. 'name': fakeName,
  701. 'unit': "",
  702. 'scalepoints': [],
  703. 'index': 0,
  704. 'default': 0,
  705. 'minimum': 0,
  706. 'maximum': 0,
  707. 'step': 0,
  708. 'stepSmall': 0,
  709. 'stepLarge': 0,
  710. 'midiCC': -1,
  711. 'midiChannel': 0,
  712. 'current': 0.0
  713. }
  714. paramFakeList.append(parameter)
  715. paramFakeListFull.append((paramFakeList, paramFakeWidth))
  716. self.createParameterWidgets(PARAMETER_UNKNOWN, paramFakeListFull, self.tr("Information"))
  717. def createParameterWidgets(self, paramType, paramListFull, tabPageName):
  718. i = 1
  719. for paramList, width in paramListFull:
  720. if len(paramList) == 0:
  721. break
  722. tabPageContainer = QWidget(self.tabWidget)
  723. tabPageLayout = QVBoxLayout(tabPageContainer)
  724. tabPageContainer.setLayout(tabPageLayout)
  725. for paramInfo in paramList:
  726. paramWidget = PluginParameter(tabPageContainer, paramInfo, self.m_pluginId, self.tabWidget.count())
  727. paramWidget.label.setMinimumWidth(width)
  728. paramWidget.label.setMaximumWidth(width)
  729. tabPageLayout.addWidget(paramWidget)
  730. self.m_parameterList.append((paramType, paramInfo['index'], paramWidget))
  731. if paramType == PARAMETER_INPUT:
  732. self.connect(paramWidget, SIGNAL("valueChanged(int, double)"), SLOT("slot_parameterValueChanged(int, double)"))
  733. self.connect(paramWidget, SIGNAL("midiChannelChanged(int, int)"), SLOT("slot_parameterMidiChannelChanged(int, int)"))
  734. self.connect(paramWidget, SIGNAL("midiCcChanged(int, int)"), SLOT("slot_parameterMidiCcChanged(int, int)"))
  735. # FIXME
  736. tabPageLayout.addStretch()
  737. self.tabWidget.addTab(tabPageContainer, "%s (%i)" % (tabPageName, i))
  738. i += 1
  739. if paramType == PARAMETER_INPUT:
  740. self.tabWidget.setTabIcon(paramWidget.tabIndex(), self.m_tabIconOff)
  741. self.m_tabIconTimers.append(ICON_STATE_NULL)
  742. def do_reload_programs(self):
  743. # Programs
  744. self.cb_programs.blockSignals(True)
  745. self.cb_programs.clear()
  746. programCount = Carla.host.get_program_count(self.m_pluginId)
  747. if programCount > 0:
  748. self.cb_programs.setEnabled(True)
  749. for i in range(programCount):
  750. pName = cString(Carla.host.get_program_name(self.m_pluginId, i))
  751. self.cb_programs.addItem(pName)
  752. self.m_currentProgram = Carla.host.get_current_program_index(self.m_pluginId)
  753. self.cb_programs.setCurrentIndex(self.m_currentProgram)
  754. else:
  755. self.m_currentProgram = -1
  756. self.cb_programs.setEnabled(False)
  757. self.cb_programs.blockSignals(False)
  758. # MIDI Programs
  759. self.cb_midi_programs.blockSignals(True)
  760. self.cb_midi_programs.clear()
  761. midiProgramCount = Carla.host.get_midi_program_count(self.m_pluginId)
  762. if midiProgramCount > 0:
  763. self.cb_midi_programs.setEnabled(True)
  764. for i in range(midiProgramCount):
  765. mpData = Carla.host.get_midi_program_data(self.m_pluginId, i)
  766. mpBank = int(mpData['bank'])
  767. mpProg = int(mpData['program'])
  768. mpLabel = cString(mpData['label'])
  769. self.cb_midi_programs.addItem("%03i:%03i - %s" % (mpBank, mpProg, mpLabel))
  770. self.m_currentMidiProgram = Carla.host.get_current_midi_program_index(self.m_pluginId)
  771. self.cb_midi_programs.setCurrentIndex(self.m_currentMidiProgram)
  772. else:
  773. self.m_currentMidiProgram = -1
  774. self.cb_midi_programs.setEnabled(False)
  775. self.cb_midi_programs.blockSignals(False)
  776. def do_update(self):
  777. # Update current program text
  778. if self.cb_programs.count() > 0:
  779. pIndex = self.cb_programs.currentIndex()
  780. pName = cString(Carla.host.get_program_name(self.m_pluginId, pIndex))
  781. self.cb_programs.setItemText(pIndex, pName)
  782. # Update current midi program text
  783. if self.cb_midi_programs.count() > 0:
  784. mpIndex = self.cb_midi_programs.currentIndex()
  785. mpData = Carla.host.get_midi_program_data(self.m_pluginId, mpIndex)
  786. mpBank = int(mpData['bank'])
  787. mpProg = int(mpData['program'])
  788. mpLabel = cString(mpData['label'])
  789. self.cb_midi_programs.setItemText(mpIndex, "%03i:%03i - %s" % (mpBank, mpProg, mpLabel))
  790. for paramType, paramId, paramWidget in self.m_parameterList:
  791. paramWidget.set_parameter_value(Carla.host.get_current_parameter_value(self.m_pluginId, paramId), False)
  792. paramWidget.update()
  793. def set_parameter_to_update(self, index):
  794. if index not in self.m_parameterIdsToUpdate:
  795. self.m_parameterIdsToUpdate.append(index)
  796. #def set_parameter_default_value(self, index, value):
  797. #self.m_parameterList[index].setDefaultValue(value)
  798. def set_parameter_midi_channel(self, index, midiChannel, blockSignals = False):
  799. for paramType, paramId, paramWidget in self.m_parameterList:
  800. if paramId == index:
  801. if (blockSignals): paramWidget.blockSignals(True)
  802. paramWidget.set_parameter_midi_channel(midiChannel)
  803. if (blockSignals): paramWidget.blockSignals(False)
  804. break
  805. def set_parameter_midi_cc(self, index, midiCC, blockSignals = False):
  806. for paramType, paramId, paramWidget in self.m_parameterList:
  807. if paramId == index:
  808. if (blockSignals): paramWidget.blockSignals(True)
  809. paramWidget.set_parameter_midi_cc(midiCC)
  810. if (blockSignals): paramWidget.blockSignals(False)
  811. break
  812. def set_program(self, index):
  813. self.m_currentProgram = index
  814. self.cb_programs.setCurrentIndex(index)
  815. QTimer.singleShot(0, self, SLOT("slot_checkInputControlParameters()"))
  816. def set_midi_program(self, index):
  817. self.m_currentMidiProgram = index
  818. self.cb_midi_programs.setCurrentIndex(index)
  819. QTimer.singleShot(0, self, SLOT("slot_checkInputControlParameters()"))
  820. def saveState(self):
  821. content = ""
  822. content += "<?xml version='1.0' encoding='UTF-8'?>\n"
  823. content += "<!DOCTYPE CARLA-PRESET>\n"
  824. content += "<CARLA-PRESET VERSION='%s'>\n" % VERSION
  825. content += self.m_realParent.getSaveXMLContent()
  826. content += "</CARLA-PRESET>\n"
  827. try:
  828. fd = uopen(self.m_currentStateFilename, "w")
  829. fd.write(content)
  830. fd.close()
  831. except:
  832. QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to save state file"))
  833. def saveStateLV2(self):
  834. pass
  835. def saveStateAsVstPreset(self):
  836. pass
  837. def loadState(self):
  838. try:
  839. fd = uopen(self.m_currentStateFilename, "r")
  840. stateRead = fd.read()
  841. fd.close()
  842. except:
  843. QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to load state file"))
  844. return
  845. xml = QDomDocument()
  846. xml.setContent(stateRead.encode("utf-8"))
  847. xmlNode = xml.documentElement()
  848. if xmlNode.tagName() != "CARLA-PRESET":
  849. QMessageBox.critical(self, self.tr("Error"), self.tr("Not a valid Carla state file"))
  850. return
  851. saveState = getSaveStateDictFromXML(xmlNode)
  852. self.m_realParent.loadStateDict(saveState)
  853. def loadStateLV2(self):
  854. pass
  855. def loadStateFromVstPreset(self):
  856. pass
  857. def animateTab(self, index):
  858. if self.m_tabIconTimers[index-1] == ICON_STATE_NULL:
  859. self.tabWidget.setTabIcon(index, self.m_tabIconOn)
  860. self.m_tabIconTimers[index-1] = ICON_STATE_ON
  861. def setVisible(self, yesNo):
  862. if yesNo:
  863. if self.m_firstShow:
  864. self.m_firstShow = False
  865. self.restoreGeometry("")
  866. elif self.m_geometry and not self.m_geometry.isNull():
  867. self.restoreGeometry(self.m_geometry)
  868. else:
  869. self.m_geometry = self.saveGeometry()
  870. QDialog.setVisible(self, yesNo)
  871. def updateParametersDefaultValues(self):
  872. for paramType, paramId, paramWidget in self.m_parameterList:
  873. if self.m_pluginInfo["type"] not in (PLUGIN_GIG, PLUGIN_SF2, PLUGIN_SFZ):
  874. paramWidget.setDefaultValue(Carla.host.get_default_parameter_value(self.m_pluginId, paramId))
  875. def updateParametersInputs(self):
  876. for paramType, paramId, paramWidget in self.m_parameterList:
  877. if paramType == PARAMETER_INPUT:
  878. paramWidget.set_parameter_value(Carla.host.get_current_parameter_value(self.m_pluginId, paramId), False)
  879. def updateParametersOutputs(self):
  880. for paramType, paramId, paramWidget in self.m_parameterList:
  881. if paramType == PARAMETER_OUTPUT:
  882. paramWidget.set_parameter_value(Carla.host.get_current_parameter_value(self.m_pluginId, paramId), False)
  883. def updatePlugin(self):
  884. # Check Tab icons
  885. for i in range(len(self.m_tabIconTimers)):
  886. if self.m_tabIconTimers[i] == ICON_STATE_ON:
  887. self.m_tabIconTimers[i] = ICON_STATE_WAIT
  888. elif self.m_tabIconTimers[i] == ICON_STATE_WAIT:
  889. self.m_tabIconTimers[i] = ICON_STATE_OFF
  890. elif self.m_tabIconTimers[i] == ICON_STATE_OFF:
  891. self.m_tabIconTimers[i] = ICON_STATE_NULL
  892. self.tabWidget.setTabIcon(i+1, self.m_tabIconOff)
  893. # Check parameters needing update
  894. for index in self.m_parameterIdsToUpdate:
  895. value = Carla.host.get_current_parameter_value(self.m_pluginId, index)
  896. for paramType, paramId, paramWidget in self.m_parameterList:
  897. if paramId == index:
  898. paramWidget.set_parameter_value(value, False)
  899. if paramType == PARAMETER_INPUT:
  900. self.animateTab(paramWidget.tabIndex())
  901. break
  902. # Clear all parameters
  903. self.m_parameterIdsToUpdate = []
  904. # Update output parameters
  905. self.updateParametersOutputs()
  906. @pyqtSlot()
  907. def slot_saveState(self):
  908. if self.m_pluginInfo['type'] == PLUGIN_LV2:
  909. # TODO
  910. QMessageBox.warning(self, self.tr("Warning"), self.tr("LV2 Presets is not implemented yet"))
  911. return self.saveStateLV2()
  912. # TODO - VST preset support
  913. if self.m_currentStateFilename:
  914. askTry = QMessageBox.question(self, self.tr("Overwrite?"), self.tr("Overwrite previously created file?"), QMessageBox.Ok|QMessageBox.Cancel)
  915. if askTry == QMessageBox.Ok:
  916. return self.saveState()
  917. self.m_currentStateFilename = None
  918. self.slot_saveState()
  919. else:
  920. fileFilter = self.tr("Carla State File (*.carxs)")
  921. filenameTry = QFileDialog.getSaveFileName(self, self.tr("Save Plugin State File"), filter=fileFilter)
  922. if filenameTry:
  923. if not filenameTry.endswith(".carxs"):
  924. filenameTry += ".carxs"
  925. self.m_currentStateFilename = filenameTry
  926. self.saveState()
  927. @pyqtSlot()
  928. def slot_loadState(self):
  929. if self.m_pluginInfo['type'] == PLUGIN_LV2:
  930. # TODO
  931. QMessageBox.warning(self, self.tr("Warning"), self.tr("LV2 Presets is not implemented yet"))
  932. return self.loadStateLV2()
  933. # TODO - VST preset support
  934. fileFilter = self.tr("Carla State File (*.carxs)")
  935. filenameTry = QFileDialog.getOpenFileName(self, self.tr("Open Plugin State File"), filter=fileFilter)
  936. if filenameTry:
  937. self.m_currentStateFilename = filenameTry
  938. self.loadState()
  939. @pyqtSlot(int, float)
  940. def slot_parameterValueChanged(self, parameter_id, value):
  941. Carla.host.set_parameter_value(self.m_pluginId, parameter_id, value)
  942. @pyqtSlot(int, int)
  943. def slot_parameterMidiChannelChanged(self, parameter_id, channel):
  944. Carla.host.set_parameter_midi_channel(self.m_pluginId, parameter_id, channel-1)
  945. @pyqtSlot(int, int)
  946. def slot_parameterMidiCcChanged(self, parameter_id, cc_index):
  947. Carla.host.set_parameter_midi_cc(self.m_pluginId, parameter_id, cc_index)
  948. @pyqtSlot(int)
  949. def slot_programIndexChanged(self, index):
  950. if self.m_currentProgram != index:
  951. Carla.host.set_program(self.m_pluginId, index)
  952. self.updateParametersDefaultValues()
  953. self.updateParametersInputs()
  954. self.m_currentProgram = index
  955. @pyqtSlot(int)
  956. def slot_midiProgramIndexChanged(self, index):
  957. if self.m_currentMidiProgram != index:
  958. Carla.host.set_midi_program(self.m_pluginId, index)
  959. self.updateParametersDefaultValues()
  960. self.updateParametersInputs()
  961. self.m_currentMidiProgram = index
  962. @pyqtSlot(int)
  963. def slot_noteOn(self, note):
  964. Carla.host.send_midi_note(self.m_pluginId, 0, note, 100)
  965. @pyqtSlot(int)
  966. def slot_noteOff(self, note):
  967. Carla.host.send_midi_note(self.m_pluginId, 0, note, 0)
  968. @pyqtSlot()
  969. def slot_notesOn(self):
  970. self.m_realParent.led_midi.setChecked(True)
  971. @pyqtSlot()
  972. def slot_notesOff(self):
  973. self.m_realParent.led_midi.setChecked(False)
  974. @pyqtSlot()
  975. def slot_checkInputControlParameters(self):
  976. self.updateParametersInputs()
  977. @pyqtSlot()
  978. def slot_finished(self):
  979. self.m_realParent.b_edit.setChecked(False)
  980. def done(self, r):
  981. QDialog.done(self, r)
  982. self.close()
  983. # (New) Plugin Widget
  984. class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget):
  985. def __init__(self, parent, pluginId):
  986. QFrame.__init__(self, parent)
  987. self.setupUi(self)
  988. #self.frame_controls.setVisible(False)
  989. self.m_pluginId = pluginId
  990. self.m_pluginInfo = Carla.host.get_plugin_info(pluginId)
  991. self.m_pluginInfo["binary"] = cString(self.m_pluginInfo["binary"])
  992. self.m_pluginInfo["name"] = cString(self.m_pluginInfo["name"])
  993. self.m_pluginInfo["label"] = cString(self.m_pluginInfo["label"])
  994. self.m_pluginInfo["maker"] = cString(self.m_pluginInfo["maker"])
  995. self.m_pluginInfo["copyright"] = cString(self.m_pluginInfo["copyright"])
  996. self.m_parameterIconTimer = ICON_STATE_NULL
  997. self.m_lastGreenLedState = False
  998. self.m_lastBlueLedState = False
  999. if Carla.processMode == PROCESS_MODE_CONTINUOUS_RACK:
  1000. self.m_peaksInputCount = 2
  1001. self.m_peaksOutputCount = 2
  1002. #self.stackedWidget.setCurrentIndex(0)
  1003. else:
  1004. audioCountInfo = Carla.host.get_audio_port_count_info(self.m_pluginId)
  1005. self.m_peaksInputCount = int(audioCountInfo['ins'])
  1006. self.m_peaksOutputCount = int(audioCountInfo['outs'])
  1007. if self.m_peaksInputCount > 2:
  1008. self.m_peaksInputCount = 2
  1009. if self.m_peaksOutputCount > 2:
  1010. self.m_peaksOutputCount = 2
  1011. #if audioCountInfo['total'] == 0:
  1012. #self.stackedWidget.setCurrentIndex(1)
  1013. #else:
  1014. #self.stackedWidget.setCurrentIndex(0)
  1015. # Background
  1016. self.m_colorTop = QColor(60, 60, 60)
  1017. self.m_colorBottom = QColor(47, 47, 47)
  1018. # Colorify
  1019. if self.m_pluginInfo['category'] == PLUGIN_CATEGORY_SYNTH:
  1020. self.setWidgetColor(PALETTE_COLOR_WHITE)
  1021. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_DELAY:
  1022. self.setWidgetColor(PALETTE_COLOR_ORANGE)
  1023. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_EQ:
  1024. self.setWidgetColor(PALETTE_COLOR_GREEN)
  1025. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_FILTER:
  1026. self.setWidgetColor(PALETTE_COLOR_BLUE)
  1027. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_DYNAMICS:
  1028. self.setWidgetColor(PALETTE_COLOR_PINK)
  1029. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_MODULATOR:
  1030. self.setWidgetColor(PALETTE_COLOR_RED)
  1031. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_UTILITY:
  1032. self.setWidgetColor(PALETTE_COLOR_YELLOW)
  1033. elif self.m_pluginInfo['category'] == PLUGIN_CATEGORY_OTHER:
  1034. self.setWidgetColor(PALETTE_COLOR_BROWN)
  1035. else:
  1036. self.setWidgetColor(PALETTE_COLOR_NONE)
  1037. self.led_enable.setColor(self.led_enable.BIG_RED)
  1038. self.led_enable.setChecked(False)
  1039. self.led_control.setColor(self.led_control.YELLOW)
  1040. self.led_control.setEnabled(False)
  1041. self.led_midi.setColor(self.led_midi.RED)
  1042. self.led_midi.setEnabled(False)
  1043. self.led_audio_in.setColor(self.led_audio_in.GREEN)
  1044. self.led_audio_in.setEnabled(False)
  1045. self.led_audio_out.setColor(self.led_audio_out.BLUE)
  1046. self.led_audio_out.setEnabled(False)
  1047. self.dial_drywet.setPixmap(3)
  1048. self.dial_vol.setPixmap(3)
  1049. self.dial_b_left.setPixmap(4)
  1050. self.dial_b_right.setPixmap(4)
  1051. self.dial_drywet.setCustomPaint(self.dial_drywet.CUSTOM_PAINT_CARLA_WET)
  1052. self.dial_vol.setCustomPaint(self.dial_vol.CUSTOM_PAINT_CARLA_VOL)
  1053. self.dial_b_left.setCustomPaint(self.dial_b_left.CUSTOM_PAINT_CARLA_L)
  1054. self.dial_b_right.setCustomPaint(self.dial_b_right.CUSTOM_PAINT_CARLA_R)
  1055. self.peak_in.setColor(self.peak_in.GREEN)
  1056. self.peak_in.setOrientation(self.peak_in.HORIZONTAL)
  1057. self.peak_out.setColor(self.peak_in.BLUE)
  1058. self.peak_out.setOrientation(self.peak_out.HORIZONTAL)
  1059. self.peak_in.setChannels(self.m_peaksInputCount)
  1060. self.peak_out.setChannels(self.m_peaksOutputCount)
  1061. self.label_name.setText(self.m_pluginInfo['name'])
  1062. self.edit_dialog = PluginEdit(self, self.m_pluginId)
  1063. self.edit_dialog.hide()
  1064. self.gui_dialog = None
  1065. if self.m_pluginInfo['hints'] & PLUGIN_HAS_GUI:
  1066. guiInfo = Carla.host.get_gui_info(self.m_pluginId)
  1067. guiType = guiInfo['type']
  1068. if guiType in (GUI_INTERNAL_QT4, GUI_INTERNAL_COCOA, GUI_INTERNAL_HWND, GUI_INTERNAL_X11):
  1069. self.gui_dialog = PluginGUI(self, self.m_pluginInfo['name'], guiInfo['resizable'])
  1070. self.gui_dialog.hide()
  1071. Carla.host.set_gui_container(self.m_pluginId, unwrapinstance(self.gui_dialog.getContainer()))
  1072. elif guiType in (GUI_EXTERNAL_LV2, GUI_EXTERNAL_SUIL, GUI_EXTERNAL_OSC):
  1073. pass
  1074. else:
  1075. self.b_gui.setEnabled(False)
  1076. self.connect(self.led_enable, SIGNAL("clicked(bool)"), SLOT("slot_setActive(bool)"))
  1077. self.connect(self.dial_drywet, SIGNAL("sliderMoved(int)"), SLOT("slot_setDryWet(int)"))
  1078. self.connect(self.dial_vol, SIGNAL("sliderMoved(int)"), SLOT("slot_setVolume(int)"))
  1079. self.connect(self.dial_b_left, SIGNAL("sliderMoved(int)"), SLOT("slot_setBalanceLeft(int)"))
  1080. self.connect(self.dial_b_right, SIGNAL("sliderMoved(int)"), SLOT("slot_setBalanceRight(int)"))
  1081. self.connect(self.b_gui, SIGNAL("clicked(bool)"), SLOT("slot_guiClicked(bool)"))
  1082. self.connect(self.b_edit, SIGNAL("clicked(bool)"), SLOT("slot_editClicked(bool)"))
  1083. self.connect(self.b_remove, SIGNAL("clicked()"), SLOT("slot_removeClicked()"))
  1084. self.connect(self.dial_drywet, SIGNAL("customContextMenuRequested(QPoint)"), SLOT("slot_showCustomDialMenu()"))
  1085. self.connect(self.dial_vol, SIGNAL("customContextMenuRequested(QPoint)"), SLOT("slot_showCustomDialMenu()"))
  1086. self.connect(self.dial_b_left, SIGNAL("customContextMenuRequested(QPoint)"), SLOT("slot_showCustomDialMenu()"))
  1087. self.connect(self.dial_b_right, SIGNAL("customContextMenuRequested(QPoint)"), SLOT("slot_showCustomDialMenu()"))
  1088. self.check_gui_stuff()
  1089. # FIXME
  1090. #self.setMaximumHeight(40)
  1091. def set_active(self, active, sendGui=False, sendCallback=True):
  1092. if sendGui: self.led_enable.setChecked(active)
  1093. if sendCallback: Carla.host.set_active(self.m_pluginId, active)
  1094. if active:
  1095. self.edit_dialog.keyboard.allNotesOff()
  1096. def set_drywet(self, value, sendGui=False, sendCallback=True):
  1097. if sendGui: self.dial_drywet.setValue(value)
  1098. if sendCallback: Carla.host.set_drywet(self.m_pluginId, float(value)/1000)
  1099. message = self.tr("Output dry/wet (%i%%)" % int(value / 10))
  1100. self.dial_drywet.setStatusTip(message)
  1101. Carla.gui.statusBar().showMessage(message)
  1102. def set_volume(self, value, sendGui=False, sendCallback=True):
  1103. if sendGui: self.dial_vol.setValue(value)
  1104. if sendCallback: Carla.host.set_volume(self.m_pluginId, float(value)/1000)
  1105. message = self.tr("Output volume (%i%%)" % int(value / 10))
  1106. self.dial_vol.setStatusTip(message)
  1107. Carla.gui.statusBar().showMessage(message)
  1108. def set_balance_left(self, value, sendGui=False, sendCallback=True):
  1109. if sendGui: self.dial_b_left.setValue(value)
  1110. if sendCallback: Carla.host.set_balance_left(self.m_pluginId, float(value)/1000)
  1111. if value < 0:
  1112. message = self.tr("Left Panning (%i%% Left)" % int(-value / 10))
  1113. elif value > 0:
  1114. message = self.tr("Left Panning (%i%% Right)" % int(value / 10))
  1115. else:
  1116. message = self.tr("Left Panning (Center)")
  1117. self.dial_b_left.setStatusTip(message)
  1118. Carla.gui.statusBar().showMessage(message)
  1119. def set_balance_right(self, value, sendGui=False, sendCallback=True):
  1120. if sendGui: self.dial_b_right.setValue(value)
  1121. if sendCallback: Carla.host.set_balance_right(self.m_pluginId, float(value)/1000)
  1122. if value < 0:
  1123. message = self.tr("Right Panning (%i%% Left)" % int(-value / 10))
  1124. elif value > 0:
  1125. message = self.tr("Right Panning (%i%% Right)" % int(value / 10))
  1126. else:
  1127. message = self.tr("Right Panning (Center)")
  1128. self.dial_b_right.setStatusTip(message)
  1129. Carla.gui.statusBar().showMessage(message)
  1130. def setId(self, idx):
  1131. self.m_pluginId = idx
  1132. self.edit_dialog.m_pluginId = idx
  1133. def setWidgetColor(self, color):
  1134. if color == PALETTE_COLOR_WHITE:
  1135. r = 110
  1136. g = 110
  1137. b = 110
  1138. borderR = 72
  1139. borderG = 72
  1140. borderB = 72
  1141. elif color == PALETTE_COLOR_RED:
  1142. r = 110
  1143. g = 15
  1144. b = 15
  1145. borderR = 110
  1146. borderG = 45
  1147. borderB = 45
  1148. elif color == PALETTE_COLOR_GREEN:
  1149. r = 15
  1150. g = 110
  1151. b = 15
  1152. borderR = 72
  1153. borderG = 110
  1154. borderB = 72
  1155. elif color == PALETTE_COLOR_BLUE:
  1156. r = 15
  1157. g = 15
  1158. b = 110
  1159. borderR = 45
  1160. borderG = 45
  1161. borderB = 110
  1162. elif color == PALETTE_COLOR_YELLOW:
  1163. r = 110
  1164. g = 110
  1165. b = 15
  1166. borderR = 110
  1167. borderG = 110
  1168. borderB = 110
  1169. elif color == PALETTE_COLOR_ORANGE:
  1170. r = 180
  1171. g = 110
  1172. b = 15
  1173. borderR = 155
  1174. borderG = 110
  1175. borderB = 60
  1176. elif color == PALETTE_COLOR_BROWN:
  1177. r = 110
  1178. g = 35
  1179. b = 15
  1180. borderR = 110
  1181. borderG = 60
  1182. borderB = 45
  1183. elif color == PALETTE_COLOR_PINK:
  1184. r = 110
  1185. g = 15
  1186. b = 110
  1187. borderR = 110
  1188. borderG = 45
  1189. borderB = 110
  1190. else:
  1191. r = 35
  1192. g = 35
  1193. b = 35
  1194. borderR = 60
  1195. borderG = 60
  1196. borderB = 60
  1197. #QFrame#PluginWidget {
  1198. #background-image: url(:/bitmaps/textures/metal_9-512px.jpg);
  1199. #background-repeat: repeat-x;
  1200. #background-position: top left;
  1201. #}
  1202. #QWidget#widget_buttons {
  1203. #background-color: rgb(%i, %i, %i);
  1204. #border-left: 1px solid rgb(%i, %i, %i);
  1205. #border-right: 1px solid rgb(%i, %i, %i);
  1206. #border-bottom: 1px solid rgb(%i, %i, %i);
  1207. #border-bottom-left-radius: 3px;
  1208. #border-bottom-right-radius: 3px;
  1209. #}
  1210. #QPushButton#b_gui:hover, QPushButton#b_edit:hover, QPushButton#b_remove:hover {
  1211. #background-color: rgb(%i, %i, %i);
  1212. #border: 1px solid rgb(%i, %i, %i);
  1213. #border-radius: 3px;
  1214. #}
  1215. #QFrame#frame_name {
  1216. #background-color: rgb(%i, %i, %i);
  1217. #border: 1px solid rgb(%i, %i, %i);
  1218. #border-radius: 4px;
  1219. #}
  1220. #QFrame#frame_peaks {
  1221. #background-color: rgb(35, 35, 35);
  1222. #border: 1px solid rgb(35, 35, 35);
  1223. #border-radius: 4px;
  1224. #}
  1225. ## QWidget#widget_buttons
  1226. #borderR, borderG, borderB,
  1227. #borderR, borderG, borderB,
  1228. #borderR, borderG, borderB,
  1229. #borderR, borderG, borderB,
  1230. ## QPushButton#b_*
  1231. #r, g, b,
  1232. #borderR, borderG, borderB,
  1233. ## QFrame#frame_name
  1234. #r, g, b,
  1235. #borderR, borderG, borderB
  1236. self.setStyleSheet("""
  1237. QLabel#label_name {
  1238. color: white;
  1239. }
  1240. QFrame#frame_controls {
  1241. background-image: url(:/bitmaps/carla_knobs1.png);
  1242. background-color: rgb(35, 35, 35);
  1243. border: 1px solid rgb(35, 35, 35);
  1244. border-radius: 4px;
  1245. }
  1246. """)
  1247. def recheck_hints(self, hints):
  1248. self.m_pluginInfo['hints'] = hints
  1249. self.dial_drywet.setEnabled(hints & PLUGIN_CAN_DRYWET)
  1250. self.dial_vol.setEnabled(hints & PLUGIN_CAN_VOLUME)
  1251. self.dial_b_left.setEnabled(hints & PLUGIN_CAN_BALANCE)
  1252. self.dial_b_right.setEnabled(hints & PLUGIN_CAN_BALANCE)
  1253. self.b_gui.setEnabled(hints & PLUGIN_HAS_GUI)
  1254. def getSaveXMLContent(self):
  1255. Carla.host.prepare_for_save(self.m_pluginId)
  1256. if self.m_pluginInfo['type'] == PLUGIN_INTERNAL:
  1257. typeStr = "Internal"
  1258. elif self.m_pluginInfo['type'] == PLUGIN_LADSPA:
  1259. typeStr = "LADSPA"
  1260. elif self.m_pluginInfo['type'] == PLUGIN_DSSI:
  1261. typeStr = "DSSI"
  1262. elif self.m_pluginInfo['type'] == PLUGIN_LV2:
  1263. typeStr = "LV2"
  1264. elif self.m_pluginInfo['type'] == PLUGIN_VST:
  1265. typeStr = "VST"
  1266. elif self.m_pluginInfo['type'] == PLUGIN_GIG:
  1267. typeStr = "GIG"
  1268. elif self.m_pluginInfo['type'] == PLUGIN_SF2:
  1269. typeStr = "SF2"
  1270. elif self.m_pluginInfo['type'] == PLUGIN_SFZ:
  1271. typeStr = "SFZ"
  1272. else:
  1273. typeStr = "Unknown"
  1274. saveState = deepcopy(CarlaSaveState)
  1275. # ----------------------------
  1276. # Basic info
  1277. saveState['Type'] = typeStr
  1278. saveState['Name'] = self.m_pluginInfo['name']
  1279. saveState['Label'] = self.m_pluginInfo['label']
  1280. saveState['Binary'] = self.m_pluginInfo['binary']
  1281. saveState['UniqueID'] = self.m_pluginInfo['uniqueId']
  1282. # ----------------------------
  1283. # Internals
  1284. saveState['Active'] = self.led_enable.isChecked()
  1285. saveState['DryWet'] = float(self.dial_drywet.value()) / 1000
  1286. saveState['Volume'] = float(self.dial_vol.value()) / 1000
  1287. saveState['Balance-Left'] = float(self.dial_b_left.value()) / 1000
  1288. saveState['Balance-Right'] = float(self.dial_b_right.value()) / 1000
  1289. # ----------------------------
  1290. # Current Program
  1291. if self.edit_dialog.cb_programs.currentIndex() >= 0:
  1292. saveState['CurrentProgramIndex'] = self.edit_dialog.cb_programs.currentIndex()
  1293. saveState['CurrentProgramName'] = self.edit_dialog.cb_programs.currentText()
  1294. # ----------------------------
  1295. # Current MIDI Program
  1296. if self.edit_dialog.cb_midi_programs.currentIndex() >= 0:
  1297. midiProgramData = Carla.host.get_midi_program_data(self.m_pluginId, self.edit_dialog.cb_midi_programs.currentIndex())
  1298. saveState['CurrentMidiBank'] = midiProgramData['bank']
  1299. saveState['CurrentMidiProgram'] = midiProgramData['program']
  1300. # ----------------------------
  1301. # Parameters
  1302. sampleRate = Carla.host.get_sample_rate()
  1303. parameterCount = Carla.host.get_parameter_count(self.m_pluginId)
  1304. for i in range(parameterCount):
  1305. parameterInfo = Carla.host.get_parameter_info(self.m_pluginId, i)
  1306. parameterData = Carla.host.get_parameter_data(self.m_pluginId, i)
  1307. if parameterData['type'] != PARAMETER_INPUT:
  1308. continue
  1309. stateParameter = deepcopy(CarlaStateParameter)
  1310. stateParameter['index'] = parameterData['index']
  1311. stateParameter['name'] = cString(parameterInfo['name'])
  1312. stateParameter['symbol'] = cString(parameterInfo['symbol'])
  1313. stateParameter['value'] = Carla.host.get_current_parameter_value(self.m_pluginId, parameterData['index'])
  1314. stateParameter['midiCC'] = parameterData['midiCC']
  1315. stateParameter['midiChannel'] = parameterData['midiChannel'] + 1
  1316. if parameterData['hints'] & PARAMETER_USES_SAMPLERATE:
  1317. stateParameter['value'] /= sampleRate
  1318. saveState['Parameters'].append(stateParameter)
  1319. # ----------------------------
  1320. # Custom Data
  1321. customDataCount = Carla.host.get_custom_data_count(self.m_pluginId)
  1322. for i in range(customDataCount):
  1323. customData = Carla.host.get_custom_data(self.m_pluginId, i)
  1324. if customData['type'] == CUSTOM_DATA_INVALID:
  1325. continue
  1326. stateCustomData = deepcopy(CarlaStateCustomData)
  1327. stateCustomData['type'] = getCustomDataTypeString(customData['type'])
  1328. stateCustomData['key'] = cString(customData['key'])
  1329. stateCustomData['value'] = cString(customData['value'])
  1330. saveState['CustomData'].append(stateCustomData)
  1331. # ----------------------------
  1332. # Chunk
  1333. if self.m_pluginInfo['hints'] & PLUGIN_USES_CHUNKS:
  1334. saveState['Chunk'] = cString(Carla.host.get_chunk_data(self.m_pluginId))
  1335. # ----------------------------
  1336. # Generate XML for this plugin
  1337. content = ""
  1338. content += " <Info>\n"
  1339. content += " <Type>%s</Type>\n" % saveState['Type']
  1340. content += " <Name>%s</Name>\n" % xmlSafeString(saveState['Name'], True)
  1341. if self.m_pluginInfo['type'] == PLUGIN_LV2:
  1342. content += " <URI>%s</URI>\n" % xmlSafeString(saveState['Label'], True)
  1343. else:
  1344. if saveState['Label'] and self.m_pluginInfo['type'] not in (PLUGIN_GIG, PLUGIN_SF2, PLUGIN_SFZ):
  1345. content += " <Label>%s</Label>\n" % xmlSafeString(saveState['Label'], True)
  1346. content += " <Binary>%s</Binary>\n" % xmlSafeString(saveState['Binary'], True)
  1347. if self.m_pluginInfo['type'] in (PLUGIN_LADSPA, PLUGIN_VST):
  1348. content += " <UniqueID>%li</UniqueID>\n" % saveState['UniqueID']
  1349. content += " </Info>\n"
  1350. content += "\n"
  1351. content += " <Data>\n"
  1352. content += " <Active>%s</Active>\n" % "Yes" if saveState['Active'] else "No"
  1353. content += " <DryWet>%f</DryWet>\n" % saveState['DryWet']
  1354. content += " <Volume>%f</Volume>\n" % saveState['Volume']
  1355. content += " <Balance-Left>%f</Balance-Left>\n" % saveState['Balance-Left']
  1356. content += " <Balance-Right>%f</Balance-Right>\n" % saveState['Balance-Right']
  1357. for parameter in saveState['Parameters']:
  1358. content += "\n"
  1359. content += " <Parameter>\n"
  1360. content += " <index>%i</index>\n" % parameter['index']
  1361. content += " <name>%s</name>\n" % xmlSafeString(parameter['name'], True)
  1362. if parameter['symbol']:
  1363. content += " <symbol>%s</symbol>\n" % xmlSafeString(parameter['symbol'], True)
  1364. strValue = "{0:f}".format(Decimal("%g" % parameter['value']))
  1365. content += " <value>%s</value>\n" % strValue
  1366. if parameter['midiCC'] > 0:
  1367. content += " <midiCC>%i</midiCC>\n" % parameter['midiCC']
  1368. content += " <midiChannel>%i</midiChannel>\n" % parameter['midiChannel']
  1369. content += " </Parameter>\n"
  1370. if saveState['CurrentProgramIndex'] >= 0:
  1371. content += "\n"
  1372. content += " <CurrentProgramIndex>%i</CurrentProgramIndex>\n" % saveState['CurrentProgramIndex']
  1373. content += " <CurrentProgramName>%s</CurrentProgramName>\n" % xmlSafeString(saveState['CurrentProgramName'], True)
  1374. if saveState['CurrentMidiBank'] >= 0 and saveState['CurrentMidiProgram'] >= 0:
  1375. content += "\n"
  1376. content += " <CurrentMidiBank>%i</CurrentMidiBank>\n" % saveState['CurrentMidiBank']
  1377. content += " <CurrentMidiProgram>%i</CurrentMidiProgram>\n" % saveState['CurrentMidiProgram']
  1378. for customData in saveState['CustomData']:
  1379. content += "\n"
  1380. content += " <CustomData>\n"
  1381. content += " <type>%s</type>\n" % customData['type']
  1382. content += " <key>%s</key>\n" % xmlSafeString(customData['key'], True)
  1383. if customData['type'] in ("string", "chunk", "binary"):
  1384. content += " <value>\n"
  1385. content += "%s\n" % xmlSafeString(customData['value'], True)
  1386. content += " </value>\n"
  1387. else:
  1388. content += " <value>%s</value>\n" % xmlSafeString(customData['value'], True)
  1389. content += " </CustomData>\n"
  1390. if saveState['Chunk']:
  1391. content += "\n"
  1392. content += " <Chunk>\n"
  1393. content += "%s\n" % saveState['Chunk']
  1394. content += " </Chunk>\n"
  1395. content += " </Data>\n"
  1396. return content
  1397. def loadStateDict(self, content):
  1398. # ---------------------------------------------------------------------
  1399. # Part 1 - set custom data (except binary/chunks)
  1400. for customData in content['CustomData']:
  1401. if customData['type'] not in (CUSTOM_DATA_BINARY, CUSTOM_DATA_CHUNK):
  1402. Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value'])
  1403. # ---------------------------------------------------------------------
  1404. # Part 2 - set program
  1405. programId = -1
  1406. if content['CurrentProgramName']:
  1407. programCount = Carla.host.get_program_count(self.m_pluginId)
  1408. testProgramName = cString(Carla.host.get_program_name(self.m_pluginId, content['CurrentProgramIndex']))
  1409. # Program index and name matches
  1410. if content['CurrentProgramName'] == testProgramName:
  1411. programId = content['CurrentProgramIndex']
  1412. # index < count
  1413. elif content['CurrentProgramIndex'] < programCount:
  1414. programId = content['CurrentProgramIndex']
  1415. # index not valid, try to find by name
  1416. else:
  1417. for i in range(programCount):
  1418. testProgramName = cString(Carla.host.get_program_name(self.m_pluginId, i))
  1419. if content['CurrentProgramName'] == testProgramName:
  1420. programId = i
  1421. break
  1422. # set program now, if valid
  1423. if programId >= 0:
  1424. Carla.host.set_program(self.m_pluginId, programId)
  1425. self.edit_dialog.set_program(programId)
  1426. # ---------------------------------------------------------------------
  1427. # Part 3 - set midi program
  1428. if content['CurrentMidiBank'] >= 0 and content['CurrentMidiProgram'] >= 0:
  1429. midiProgramCount = Carla.host.get_midi_program_count(self.m_pluginId)
  1430. for i in range(midiProgramCount):
  1431. midiProgramData = Carla.host.get_midi_program_data(self.m_pluginId, i)
  1432. if midiProgramData['bank'] == content['CurrentMidiBank'] and midiProgramData['program'] == content['CurrentMidiProgram']:
  1433. Carla.host.set_midi_program(self.m_pluginId, i)
  1434. self.edit_dialog.set_midi_program(i)
  1435. break
  1436. # ---------------------------------------------------------------------
  1437. # Part 4a - get plugin parameter symbols
  1438. paramSymbols = [] # (index, symbol)
  1439. for parameter in content['Parameters']:
  1440. if parameter['symbol']:
  1441. paramInfo = Carla.host.get_parameter_info(self.m_pluginId, parameter['index'])
  1442. if paramInfo['symbol']:
  1443. paramSymbols.append((parameter['index'], cString(paramInfo['symbol'])))
  1444. # ---------------------------------------------------------------------
  1445. # Part 4b - set parameter values (carefully)
  1446. sampleRate = Carla.host.get_sample_rate()
  1447. for parameter in content['Parameters']:
  1448. index = -1
  1449. if content['Type'] == "LADSPA":
  1450. # Try to set by symbol, otherwise use index
  1451. if parameter['symbol']:
  1452. for paramIndex, paramSymbol in paramSymbols:
  1453. if parameter['symbol'] == paramSymbol:
  1454. index = paramIndex
  1455. break
  1456. else:
  1457. index = parameter['index']
  1458. else:
  1459. index = parameter['index']
  1460. elif content['Type'] == "LV2":
  1461. # Symbol only
  1462. if parameter['symbol']:
  1463. for paramIndex, paramSymbol in paramSymbols:
  1464. if parameter['symbol'] == paramSymbol:
  1465. index = paramIndex
  1466. break
  1467. else:
  1468. print("Failed to find LV2 parameter symbol for '%s')" % parameter['symbol'])
  1469. else:
  1470. print("LV2 Plugin parameter '%s' has no symbol" % parameter['name'])
  1471. else:
  1472. # Index only
  1473. index = parameter['index']
  1474. # Now set parameter
  1475. if index >= 0:
  1476. paramData = Carla.host.get_parameter_data(self.m_pluginId, parameter['index'])
  1477. if paramData['hints'] & PARAMETER_USES_SAMPLERATE:
  1478. parameter['value'] *= sampleRate
  1479. Carla.host.set_parameter_value(self.m_pluginId, index, parameter['value'])
  1480. Carla.host.set_parameter_midi_cc(self.m_pluginId, index, parameter['midiCC'])
  1481. Carla.host.set_parameter_midi_channel(self.m_pluginId, index, parameter['midiChannel']-1)
  1482. else:
  1483. print("Could not set parameter data for '%s')" % parameter['name'])
  1484. # ---------------------------------------------------------------------
  1485. # Part 5 - set chunk data
  1486. for customData in content['CustomData']:
  1487. if customData['type'] in (CUSTOM_DATA_BINARY, CUSTOM_DATA_CHUNK):
  1488. Carla.host.set_custom_data(self.m_pluginId, customData['type'], customData['key'], customData['value'])
  1489. if content['Chunk']:
  1490. Carla.host.set_chunk_data(self.m_pluginId, content['Chunk'])
  1491. # ---------------------------------------------------------------------
  1492. # Part 6 - set internal stuff
  1493. self.set_drywet(content['DryWet'] * 1000, True, True)
  1494. self.set_volume(content['Volume'] * 1000, True, True)
  1495. self.set_balance_left(content['Balance-Left'] * 1000, True, True)
  1496. self.set_balance_right(content['Balance-Right'] * 1000, True, True)
  1497. self.edit_dialog.do_reload_all()
  1498. self.set_active(content['Active'], True, True)
  1499. def check_gui_stuff(self):
  1500. # Input peaks
  1501. if self.m_peaksInputCount > 0:
  1502. if self.m_peaksInputCount > 1:
  1503. peak1 = Carla.host.get_input_peak_value(self.m_pluginId, 1)
  1504. peak2 = Carla.host.get_input_peak_value(self.m_pluginId, 2)
  1505. ledState = bool(peak1 != 0.0 or peak2 != 0.0)
  1506. self.peak_in.displayMeter(1, peak1)
  1507. self.peak_in.displayMeter(2, peak2)
  1508. else:
  1509. peak = Carla.host.get_input_peak_value(self.m_pluginId, 1)
  1510. ledState = bool(peak != 0.0)
  1511. self.peak_in.displayMeter(1, peak)
  1512. if self.m_lastGreenLedState != ledState:
  1513. self.led_audio_in.setChecked(ledState)
  1514. self.m_lastGreenLedState = ledState
  1515. # Output peaks
  1516. if self.m_peaksOutputCount > 0:
  1517. if self.m_peaksOutputCount > 1:
  1518. peak1 = Carla.host.get_output_peak_value(self.m_pluginId, 1)
  1519. peak2 = Carla.host.get_output_peak_value(self.m_pluginId, 2)
  1520. ledState = bool(peak1 != 0.0 or peak2 != 0.0)
  1521. self.peak_out.displayMeter(1, peak1)
  1522. self.peak_out.displayMeter(2, peak2)
  1523. else:
  1524. peak = Carla.host.get_output_peak_value(self.m_pluginId, 1)
  1525. ledState = bool(peak != 0.0)
  1526. self.peak_out.displayMeter(1, peak)
  1527. if self.m_lastBlueLedState != ledState:
  1528. self.led_audio_out.setChecked(ledState)
  1529. self.m_lastBlueLedState = ledState
  1530. def check_gui_stuff2(self):
  1531. # Parameter Activity LED
  1532. if self.m_parameterIconTimer == ICON_STATE_ON:
  1533. self.m_parameterIconTimer = ICON_STATE_WAIT
  1534. self.led_control.setChecked(True)
  1535. elif self.m_parameterIconTimer == ICON_STATE_WAIT:
  1536. self.m_parameterIconTimer = ICON_STATE_OFF
  1537. elif self.m_parameterIconTimer == ICON_STATE_OFF:
  1538. self.m_parameterIconTimer = ICON_STATE_NULL
  1539. self.led_control.setChecked(False)
  1540. # Update edit dialog
  1541. self.edit_dialog.updatePlugin()
  1542. @pyqtSlot(bool)
  1543. def slot_setActive(self, yesno):
  1544. self.set_active(yesno, False, True)
  1545. @pyqtSlot(int)
  1546. def slot_setDryWet(self, value):
  1547. self.set_drywet(value, False, True)
  1548. @pyqtSlot(int)
  1549. def slot_setVolume(self, value):
  1550. self.set_volume(value, False, True)
  1551. @pyqtSlot(int)
  1552. def slot_setBalanceLeft(self, value):
  1553. self.set_balance_left(value, False, True)
  1554. @pyqtSlot(int)
  1555. def slot_setBalanceRight(self, value):
  1556. self.set_balance_right(value, False, True)
  1557. @pyqtSlot(bool)
  1558. def slot_guiClicked(self, show):
  1559. if self.gui_dialog:
  1560. self.gui_dialog.setVisible(show)
  1561. Carla.host.show_gui(self.m_pluginId, show)
  1562. @pyqtSlot(bool)
  1563. def slot_editClicked(self, show):
  1564. self.edit_dialog.setVisible(show)
  1565. @pyqtSlot()
  1566. def slot_removeClicked(self):
  1567. Carla.gui.remove_plugin(self.m_pluginId, True)
  1568. @pyqtSlot()
  1569. def slot_showCustomDialMenu(self):
  1570. dialName = self.sender().objectName()
  1571. if dialName == "dial_drywet":
  1572. minimum = 0
  1573. maximum = 100
  1574. default = 100
  1575. label = "Dry/Wet"
  1576. elif dialName == "dial_vol":
  1577. minimum = 0
  1578. maximum = 127
  1579. default = 100
  1580. label = "Volume"
  1581. elif dialName == "dial_b_left":
  1582. minimum = -100
  1583. maximum = 100
  1584. default = -100
  1585. label = "Balance-Left"
  1586. elif dialName == "dial_b_right":
  1587. minimum = -100
  1588. maximum = 100
  1589. default = 100
  1590. label = "Balance-Right"
  1591. else:
  1592. minimum = 0
  1593. maximum = 100
  1594. default = 100
  1595. label = "Unknown"
  1596. current = self.sender().value() / 10
  1597. menu = QMenu(self)
  1598. actReset = menu.addAction(self.tr("Reset (%i%%)" % default))
  1599. menu.addSeparator()
  1600. actMinimum = menu.addAction(self.tr("Set to Minimum (%i%%)" % minimum))
  1601. actCenter = menu.addAction(self.tr("Set to Center"))
  1602. actMaximum = menu.addAction(self.tr("Set to Maximum (%i%%)" % maximum))
  1603. menu.addSeparator()
  1604. actSet = menu.addAction(self.tr("Set value..."))
  1605. if label not in ("Balance-Left", "Balance-Right"):
  1606. menu.removeAction(actCenter)
  1607. actSelected = menu.exec_(QCursor.pos())
  1608. if actSelected == actSet:
  1609. valueTry = QInputDialog.getInteger(self, self.tr("Set value"), label, current, minimum, maximum, 1)
  1610. if valueTry[1]:
  1611. value = valueTry[0] * 10
  1612. else:
  1613. value = None
  1614. elif actSelected == actMinimum:
  1615. value = minimum * 10
  1616. elif actSelected == actMaximum:
  1617. value = maximum * 10
  1618. elif actSelected == actReset:
  1619. value = default * 10
  1620. elif actSelected == actCenter:
  1621. value = 0
  1622. else:
  1623. return
  1624. if label == "Dry/Wet":
  1625. self.set_drywet(value, True, True)
  1626. elif label == "Volume":
  1627. self.set_volume(value, True, True)
  1628. elif label == "Balance-Left":
  1629. self.set_balance_left(value, True, True)
  1630. elif label == "Balance-Right":
  1631. self.set_balance_right(value, True, True)
  1632. #def mousePressEvent(self, event):
  1633. #Carla.gui.pluginWidgetClicked(self.edit_dialog)
  1634. #QFrame.mousePressEvent(self, event)
  1635. def paintEvent(self, event):
  1636. painter = QPainter(self)
  1637. areaX = self.area_right.x()
  1638. # background
  1639. #painter.setPen(self.m_colorTop)
  1640. #painter.setBrush(self.m_colorTop)
  1641. #painter.drawRect(0, 0, areaX+40, self.height())
  1642. # bottom line
  1643. painter.setPen(self.m_colorBottom)
  1644. painter.setBrush(self.m_colorBottom)
  1645. painter.drawRect(0, self.height()-5, areaX, 5)
  1646. # top line
  1647. painter.drawLine(0, 0, areaX+40, 0)
  1648. # name -> leds arc
  1649. path = QPainterPath()
  1650. path.moveTo(areaX-80, self.height())
  1651. path.cubicTo(areaX+40, self.height()-5, areaX-40, 30, areaX+20, 0)
  1652. path.lineTo(areaX+20, self.height())
  1653. painter.drawPath(path)
  1654. # fill the rest
  1655. painter.drawRect(areaX+20, 0, self.width(), self.height())
  1656. #painter.drawLine(0, 3, self.width(), 3)
  1657. #painter.drawLine(0, self.height() - 4, self.width(), self.height() - 4)
  1658. #painter.setPen(self.m_color2)
  1659. #painter.drawLine(0, 2, self.width(), 2)
  1660. #painter.drawLine(0, self.height() - 3, self.width(), self.height() - 3)
  1661. #painter.setPen(self.m_color3)
  1662. #painter.drawLine(0, 1, self.width(), 1)
  1663. #painter.drawLine(0, self.height() - 2, self.width(), self.height() - 2)
  1664. #painter.setPen(self.m_color4)
  1665. #painter.drawLine(0, 0, self.width(), 0)
  1666. #painter.drawLine(0, self.height() - 1, self.width(), self.height() - 1)
  1667. QFrame.paintEvent(self, event)
  1668. # Carla Scene
  1669. class CarlaScene(QGraphicsScene):
  1670. def __init__(self, parent, view):
  1671. QGraphicsScene.__init__(self, parent)
  1672. self.m_view = view
  1673. if not self.m_view:
  1674. qFatal("CarlaScene() - invalid view")
  1675. self.m_itemList = []
  1676. for x in range(MAX_PLUGINS):
  1677. self.m_itemList.append(None)
  1678. bgGradient = QLinearGradient(0.0, 0.0, 0.2, 1.0)
  1679. bgGradient.setColorAt(0, QColor(7, 7, 7))
  1680. bgGradient.setColorAt(1, QColor(28, 28, 28))
  1681. self.setBackgroundBrush(bgGradient)
  1682. def addWidget(self, idx, widget):
  1683. newItem = QGraphicsScene.addWidget(self, widget)
  1684. newPosY = 0
  1685. for item in self.m_itemList:
  1686. if item:
  1687. newPosY += item.widget().height()
  1688. print(item.widget().height())
  1689. print("fin", newPosY)
  1690. newItem.setPos(0, newPosY)
  1691. newItem.resize(widget.width(), widget.height())
  1692. self.m_itemList[idx] = newItem
  1693. def resize(self):
  1694. width = self.m_view.width()
  1695. height = self.m_view.height()
  1696. print("Resize", 0, 0, width, height)
  1697. self.setSceneRect(0, 0, width, height)
  1698. for item in self.m_itemList:
  1699. if not item:
  1700. continue
  1701. item.resize(width, item.widget().height())
  1702. # Plugin GUI
  1703. class PluginGUI(QDialog):
  1704. def __init__(self, parent, pluginName, resizable):
  1705. QDialog.__init__(self, Carla.gui)
  1706. self.m_firstShow = True
  1707. self.m_resizable = resizable
  1708. self.m_geometry = None
  1709. self.m_realParent = parent
  1710. self.vbLayout = QVBoxLayout(self)
  1711. self.vbLayout.setContentsMargins(0, 0, 0, 0)
  1712. self.setLayout(self.vbLayout)
  1713. self.container = GuiContainer(self)
  1714. self.vbLayout.addWidget(self.container)
  1715. self.setNewSize(50, 50)
  1716. self.setWindowTitle("%s (GUI)" % pluginName)
  1717. if WINDOWS and not resizable:
  1718. self.setWindowFlags(self.windowFlags() | Qt.MSWindowsFixedSizeDialogHint)
  1719. def getContainer(self):
  1720. return self.container
  1721. def setNewSize(self, width, height):
  1722. if width < 30:
  1723. width = 30
  1724. if height < 30:
  1725. height = 30
  1726. if self.m_resizable:
  1727. self.resize(width, height)
  1728. else:
  1729. self.setFixedSize(width, height)
  1730. self.container.setFixedSize(width, height)
  1731. def setVisible(self, yesNo):
  1732. if yesNo:
  1733. if self.m_firstShow:
  1734. self.m_firstShow = False
  1735. self.restoreGeometry("")
  1736. elif self.m_geometry and not self.m_geometry.isNull():
  1737. self.restoreGeometry(self.m_geometry)
  1738. else:
  1739. self.m_geometry = self.saveGeometry()
  1740. QDialog.setVisible(self, yesNo)
  1741. def hideEvent(self, event):
  1742. event.accept()
  1743. self.close()
  1744. def closeEvent(self, event):
  1745. if event.spontaneous():
  1746. self.m_realParent.b_gui.setChecked(False)
  1747. QDialog.closeEvent(self, event)
  1748. def done(self, r):
  1749. QDialog.done(self, r)
  1750. self.close()