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.

2026 lines
74KB

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