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.

2072 lines
75KB

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