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.

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