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.

2074 lines
76KB

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