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.

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