Update CI version
[carla.git] / source / frontend / carla_skin.py
blob62e2bd0e598c48154593b619c6db6b383eb2b5ad
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
4 # Carla plugin/slot skin code
5 # Copyright (C) 2013-2020 Filipe Coelho <falktx@falktx.com>
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation; either version 2 of
10 # the License, or any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # For a full copy of the GNU General Public License see the doc/GPL.txt file.
19 # ------------------------------------------------------------------------------------------------------------
20 # Imports (Global)
22 from PyQt5.QtCore import Qt, QRectF, QLineF, QTimer
23 from PyQt5.QtGui import QColor, QFont, QFontDatabase, QPainter, QPainterPath, QPen
24 from PyQt5.QtWidgets import QColorDialog, QFrame, QLineEdit, QPushButton
26 # ------------------------------------------------------------------------------------------------------------
27 # Imports (Custom)
29 import ui_carla_plugin_calf
30 import ui_carla_plugin_classic
31 import ui_carla_plugin_compact
32 import ui_carla_plugin_default
33 import ui_carla_plugin_presets
35 from carla_backend import *
36 from carla_shared import *
37 from carla_widgets import *
38 from widgets.digitalpeakmeter import DigitalPeakMeter
39 from widgets.paramspinbox import CustomInputDialog
40 from widgets.scalabledial import ScalableDial
42 # ------------------------------------------------------------------------------------------------------------
43 # Plugin Skin Rules (WORK IN PROGRESS)
45 # Base is a QFrame (NoFrame, Plain, 0-size lines), with "PluginWidget" as object name.
46 # Spacing of the top-most layout must be 1px.
47 # Top and bottom margins must be 3px (can be split between different Qt layouts).
48 # Left and right margins must be 6px (can be split between different Qt layouts).
49 # If the left or right side has built-in margins, say a transparent svg border,
50 # those margins must be taken into consideration.
52 # There's a top and bottom layout, separated by a horizontal line.
53 # Compacted skins do not have the bottom layout and separating line.
55 # T O P A R E A
57 # -----------------------------------------------------------------
58 # | <> | <> [ WIDGETS ] [ LEDS ] |
59 # | BUTTONS <> | <> PLUGIN NAME < spacer > [ WIDGETS ] [ LEDS ] |
60 # | <> | <> [ WIDGETS ] [ LEDS ] |
61 # -----------------------------------------------------------------
63 # Buttons area has size fixed. (TBA)
64 # Spacers at the left of the plugin name must be 8x1 in size (fixed).
65 # The line before the plugin name must be height-10px (fixed).
66 # WIDGETS area can be extended to the left, if using meters they should have 80px.
67 # WIDGETS margins are 4px for left+right and 2px for top+bottom, with 4px spacing.
69 # ------------------------------------------------------------------------------------------------------------
70 # Try to "shortify" a parameter name
72 def getParameterShortName(paramName):
73 paramName = paramName.split("/",1)[0].split(" (",1)[0].split(" [",1)[0].strip()
74 paramLow = paramName.lower()
76 # Cut useless prefix
77 if paramLow.startswith("compressor "):
78 paramName = paramName.replace("ompressor ", ".", 1)
79 paramLow = paramName.lower()
80 elif paramLow.startswith("room "):
81 paramName = paramName.split(" ",1)[1]
82 paramLow = paramName.lower()
84 # Cut useless suffix
85 if paramLow.endswith(" level"):
86 paramName = paramName.rsplit(" ",1)[0]
87 paramLow = paramName.lower()
88 elif paramLow.endswith(" time"):
89 paramName = paramName.rsplit(" ",1)[0]
90 paramLow = paramName.lower()
92 # Cut generic names
93 if "attack" in paramLow:
94 paramName = paramName.replace("ttack", "tk")
95 elif "bandwidth" in paramLow:
96 paramName = paramName.replace("andwidth", "w")
97 elif "damping" in paramLow:
98 paramName = paramName.replace("amping", "amp")
99 elif "distortion" in paramLow:
100 paramName = paramName.replace("istortion", "ist")
101 elif "feedback" in paramLow:
102 paramName = paramName.replace("eedback", "b")
103 elif "frequency" in paramLow:
104 paramName = paramName.replace("requency", "req")
105 elif "input" in paramLow:
106 paramName = paramName.replace("nput", "n")
107 elif "makeup" in paramLow:
108 paramName = paramName.replace("akeup", "kUp" if "Make" in paramName else "kup")
109 elif "output" in paramLow:
110 paramName = paramName.replace("utput", "ut")
111 elif "random" in paramLow:
112 paramName = paramName.replace("andom", "nd")
113 elif "threshold" in paramLow:
114 paramName = paramName.replace("hreshold", "hres")
116 # remove space if last char from 1st word is lowercase and the first char from the 2nd is uppercase,
117 # or if 2nd is a number
118 if " " in paramName:
119 name1, name2 = paramName.split(" ", 1)
120 if (name1[-1].islower() and name2[0].isupper()) or name2.isdigit():
121 paramName = paramName.replace(" ", "", 1)
123 # cut stuff if too big
124 if len(paramName) > 7:
125 paramName = paramName.replace("a","").replace("e","").replace("i","").replace("o","").replace("u","")
127 if len(paramName) > 7:
128 paramName = paramName[:7]
130 return paramName.strip()
132 # ------------------------------------------------------------------------------------------------------------
133 # Get RGB colors for a plugin category
135 def getColorFromCategory(category):
136 r = 40
137 g = 40
138 b = 40
140 if category == PLUGIN_CATEGORY_MODULATOR:
141 r += 10
142 elif category == PLUGIN_CATEGORY_EQ:
143 g += 10
144 elif category == PLUGIN_CATEGORY_FILTER:
145 b += 10
146 elif category == PLUGIN_CATEGORY_DELAY:
147 r += 15
148 b -= 15
149 elif category == PLUGIN_CATEGORY_DISTORTION:
150 g += 10
151 b += 10
152 elif category == PLUGIN_CATEGORY_DYNAMICS:
153 r += 10
154 b += 10
155 elif category == PLUGIN_CATEGORY_UTILITY:
156 r += 10
157 g += 10
159 return (r, g, b)
161 # ------------------------------------------------------------------------------------------------------------
164 def setScalableDialStyle(widget, parameterId, parameterCount, whiteLabels, skinStyle):
165 if skinStyle.startswith("calf"):
166 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_NO_GRADIENT)
167 widget.setImage(7)
169 elif skinStyle.startswith("openav"):
170 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_NO_GRADIENT)
171 if parameterId == PARAMETER_DRYWET:
172 widget.setImage(13)
173 elif parameterId == PARAMETER_VOLUME:
174 widget.setImage(12)
175 else:
176 widget.setImage(11)
178 else:
179 if parameterId == PARAMETER_DRYWET:
180 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_WET)
182 elif parameterId == PARAMETER_VOLUME:
183 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_VOL)
185 else:
186 _r = 255 - int((float(parameterId)/float(parameterCount))*200.0)
187 _g = 55 + int((float(parameterId)/float(parameterCount))*200.0)
188 _b = 0 #(r-40)*4
189 widget.setCustomPaintColor(QColor(_r, _g, _b))
190 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_COLOR)
192 if whiteLabels:
193 colorEnabled = QColor("#BBB")
194 colorDisabled = QColor("#555")
195 else:
196 colorEnabled = QColor("#111")
197 colorDisabled = QColor("#AAA")
199 widget.setLabelColor(colorEnabled, colorDisabled)
200 widget.setImage(3)
202 # ------------------------------------------------------------------------------------------------------------
203 # Abstract plugin slot
205 class AbstractPluginSlot(QFrame, PluginEditParentMeta):
206 def __init__(self, parent, host, pluginId, skinColor, skinStyle):
207 QFrame.__init__(self, parent)
208 self.host = host
209 self.fParent = parent
211 # -------------------------------------------------------------
212 # Get plugin info
214 self.fPluginId = pluginId
215 self.fPluginInfo = host.get_plugin_info(self.fPluginId)
216 self.fSkinColor = skinColor
217 self.fSkinStyle = skinStyle
218 self.fDarkStyle = QColor(skinColor[0], skinColor[1], skinColor[2]).blackF() > 0.4
220 # -------------------------------------------------------------
221 # Internal stuff
223 self.fIsActive = False
224 self.fIsSelected = False
226 self.fLastGreenLedState = False
227 self.fLastBlueLedState = False
229 self.fParameterIconTimer = ICON_STATE_NULL
230 self.fParameterList = [] # index, widget
232 audioCountInfo = host.get_audio_port_count_info(self.fPluginId)
234 self.fPeaksInputCount = audioCountInfo['ins']
235 self.fPeaksOutputCount = audioCountInfo['outs']
237 if self.fPeaksInputCount > 2:
238 self.fPeaksInputCount = 2
240 if self.fPeaksOutputCount > 2:
241 self.fPeaksOutputCount = 2
243 self.fAdjustViewableKnobCountScheduled = False
245 # used during testing
246 self.fIdleTimerId = 0
248 # -------------------------------------------------------------
249 # Set-up GUI
251 self.fEditDialog = PluginEdit(self, host, self.fPluginId)
253 # -------------------------------------------------------------
254 # Set-up common widgets (as none)
256 self.b_enable = None
257 self.b_gui = None
258 self.b_edit = None
259 self.b_remove = None
261 self.cb_presets = None
263 self.label_name = None
264 self.label_presets = None
265 self.label_type = None
267 self.led_control = None
268 self.led_midi = None
269 self.led_audio_in = None
270 self.led_audio_out = None
272 self.peak_in = None
273 self.peak_out = None
275 self.w_knobs_left = None
276 self.w_knobs_right = None
277 self.spacer_knobs = None
279 # -------------------------------------------------------------
280 # Set-up connections
282 self.customContextMenuRequested.connect(self.slot_showCustomMenu)
283 host.PluginRenamedCallback.connect(self.slot_handlePluginRenamedCallback)
284 host.PluginUnavailableCallback.connect(self.slot_handlePluginUnavailableCallback)
285 host.ParameterValueChangedCallback.connect(self.slot_handleParameterValueChangedCallback)
286 host.ParameterDefaultChangedCallback.connect(self.slot_handleParameterDefaultChangedCallback)
287 host.ParameterMappedControlIndexChangedCallback.connect(self.slot_handleParameterMappedControlIndexChangedCallback)
288 host.ParameterMappedRangeChangedCallback.connect(self.slot_handleParameterMappedRangeChangedCallback)
289 host.ParameterMidiChannelChangedCallback.connect(self.slot_handleParameterMidiChannelChangedCallback)
290 host.ProgramChangedCallback.connect(self.slot_handleProgramChangedCallback)
291 host.MidiProgramChangedCallback.connect(self.slot_handleMidiProgramChangedCallback)
292 host.OptionChangedCallback.connect(self.slot_handleOptionChangedCallback)
293 host.UiStateChangedCallback.connect(self.slot_handleUiStateChangedCallback)
295 # Prepare resources
296 self.sel_pen = QPen(Qt.cyan, 1, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)
297 self.sel_pen.setDashPattern([2.0, 4.0])
298 self.sel_side_pen = QPen(Qt.cyan, 2, Qt.SolidLine, Qt.FlatCap)
299 self.shadow_pen = QPen(Qt.black, 1)
301 # -----------------------------------------------------------------
303 @pyqtSlot(int, str)
304 def slot_handlePluginRenamedCallback(self, pluginId, newName):
305 if self.fEditDialog is not None and self.fPluginId == pluginId:
306 self.setName(newName)
308 @pyqtSlot(int, str)
309 def slot_handlePluginUnavailableCallback(self, pluginId, errorMsg):
310 if self.fEditDialog is not None and self.fPluginId == pluginId:
311 pass
313 @pyqtSlot(int, int, float)
314 def slot_handleParameterValueChangedCallback(self, pluginId, index, value):
315 if self.fEditDialog is not None and self.fPluginId == pluginId:
316 self.setParameterValue(index, value, True)
318 @pyqtSlot(int, int, float)
319 def slot_handleParameterDefaultChangedCallback(self, pluginId, index, value):
320 if self.fEditDialog is not None and self.fPluginId == pluginId:
321 self.setParameterDefault(index, value)
323 @pyqtSlot(int, int, int)
324 def slot_handleParameterMappedControlIndexChangedCallback(self, pluginId, index, ctrl):
325 if self.fEditDialog is not None and self.fPluginId == pluginId:
326 self.setParameterMappedControlIndex(index, ctrl)
328 @pyqtSlot(int, int, float, float)
329 def slot_handleParameterMappedRangeChangedCallback(self, pluginId, index, minimum, maximum):
330 if self.fEditDialog is not None and self.fPluginId == pluginId:
331 self.setParameterMappedRange(index, minimum, maximum)
333 @pyqtSlot(int, int, int)
334 def slot_handleParameterMidiChannelChangedCallback(self, pluginId, index, channel):
335 if self.fEditDialog is not None and self.fPluginId == pluginId:
336 self.setParameterMidiChannel(index, channel)
338 @pyqtSlot(int, int)
339 def slot_handleProgramChangedCallback(self, pluginId, index):
340 if self.fEditDialog is not None and self.fPluginId == pluginId:
341 self.setProgram(index, True)
343 @pyqtSlot(int, int)
344 def slot_handleMidiProgramChangedCallback(self, pluginId, index):
345 if self.fEditDialog is not None and self.fPluginId == pluginId:
346 self.setMidiProgram(index, True)
348 @pyqtSlot(int, int, bool)
349 def slot_handleOptionChangedCallback(self, pluginId, option, yesNo):
350 if self.fEditDialog is not None and self.fPluginId == pluginId:
351 self.setOption(option, yesNo)
353 @pyqtSlot(int, int)
354 def slot_handleUiStateChangedCallback(self, pluginId, state):
355 if self.fEditDialog is not None and self.fPluginId == pluginId:
356 self.customUiStateChanged(state)
358 # ------------------------------------------------------------------
360 def ready(self):
361 self.fIsActive = bool(self.host.get_internal_parameter_value(self.fPluginId, PARAMETER_ACTIVE) >= 0.5)
363 isCalfSkin = self.fSkinStyle.startswith("calf") and not isinstance(self, PluginSlot_Compact)
364 imageSuffix = "white" if self.fDarkStyle else "black"
365 whiteLabels = self.fDarkStyle
367 if self.fSkinStyle.startswith("calf") or self.fSkinStyle.startswith("openav") or self.fSkinStyle in (
368 "3bandeq", "3bandsplitter", "pingpongpan", "nekobi", "calf_black", "zynfx"):
370 imageSuffix = "white"
371 whiteLabels = True
373 if self.b_enable is not None:
374 self.b_enable.setChecked(self.fIsActive)
375 self.b_enable.clicked.connect(self.slot_enableClicked)
377 if isCalfSkin:
378 self.b_enable.setPixmaps(":/bitmaps/button_calf3.png",
379 ":/bitmaps/button_calf3_down.png",
380 ":/bitmaps/button_calf3.png")
381 else:
382 self.b_enable.setSvgs(":/scalable/button_off.svg",
383 ":/scalable/button_on.svg",
384 ":/scalable/button_off.svg")
386 if self.b_gui is not None:
387 self.b_gui.clicked.connect(self.slot_showCustomUi)
388 self.b_gui.setEnabled(bool(self.fPluginInfo['hints'] & PLUGIN_HAS_CUSTOM_UI))
390 if isCalfSkin:
391 self.b_gui.setPixmaps(":/bitmaps/button_calf2.png",
392 ":/bitmaps/button_calf2_down.png",
393 ":/bitmaps/button_calf2_hover.png")
394 elif self.fPluginInfo['iconName'] == "distrho" or self.fSkinStyle in ("3bandeq", "3bandsplitter", "pingpongpan", "nekobi"):
395 self.b_gui.setPixmaps(":/bitmaps/button_distrho-{}.png".format(imageSuffix),
396 ":/bitmaps/button_distrho_down-{}.png".format(imageSuffix),
397 ":/bitmaps/button_distrho_hover-{}.png".format(imageSuffix))
398 elif self.fPluginInfo['iconName'] == "file":
399 self.b_gui.setPixmaps(":/bitmaps/button_file-{}.png".format(imageSuffix),
400 ":/bitmaps/button_file_down-{}.png".format(imageSuffix),
401 ":/bitmaps/button_file_hover-{}.png".format(imageSuffix))
402 else:
403 if imageSuffix == "black": # TODO
404 self.b_gui.setPixmaps(":/bitmaps/button_gui-{}.png".format(imageSuffix),
405 ":/bitmaps/button_gui_down-{}.png".format(imageSuffix),
406 ":/bitmaps/button_gui_hover-{}.png".format(imageSuffix))
407 else:
408 self.b_gui.setSvgs(":/scalable/button_gui-{}.svg".format(imageSuffix),
409 ":/scalable/button_gui_down-{}.svg".format(imageSuffix),
410 ":/scalable/button_gui_hover-{}.svg".format(imageSuffix))
412 if self.b_edit is not None:
413 self.b_edit.clicked.connect(self.slot_showEditDialog)
415 if isCalfSkin:
416 self.b_edit.setPixmaps(":/bitmaps/button_calf2.png",
417 ":/bitmaps/button_calf2_down.png",
418 ":/bitmaps/button_calf2_hover.png")
419 else:
420 self.b_edit.setSvgs(":/scalable/button_edit-{}.svg".format(imageSuffix),
421 ":/scalable/button_edit_down-{}.svg".format(imageSuffix),
422 ":/scalable/button_edit_hover-{}.svg".format(imageSuffix))
424 else:
425 # Edit button *must* be available
426 self.b_edit = QPushButton(self)
427 self.b_edit.setCheckable(True)
428 self.b_edit.hide()
430 if self.b_remove is not None:
431 self.b_remove.clicked.connect(self.slot_removePlugin)
433 if self.label_name is not None:
434 self.label_name.setEnabled(self.fIsActive)
435 self.label_name.setText(self.fPluginInfo['name'])
437 nameFont = self.label_name.font()
439 if self.fSkinStyle.startswith("calf"):
440 nameFont.setBold(True)
441 nameFont.setPixelSize(12)
443 elif self.fSkinStyle.startswith("openav"):
444 QFontDatabase.addApplicationFont(":/fonts/uranium.ttf")
445 nameFont.setFamily("Uranium")
446 nameFont.setPixelSize(15)
447 nameFont.setCapitalization(QFont.AllUppercase)
449 else:
450 nameFont.setBold(True)
451 nameFont.setPixelSize(11)
453 self.label_name.setFont(nameFont)
455 if self.label_presets is not None:
456 presetFont = self.label_presets.font()
457 presetFont.setBold(True)
458 presetFont.setPixelSize(10)
459 self.label_presets.setFont(presetFont)
461 if self.label_type is not None:
462 self.label_type.setText(getPluginTypeAsString(self.fPluginInfo['type']))
464 if self.led_control is not None:
465 self.led_control.setColor(self.led_control.YELLOW)
466 self.led_control.setEnabled(False)
468 if self.led_midi is not None:
469 self.led_midi.setColor(self.led_midi.RED)
470 self.led_midi.setEnabled(False)
472 if self.led_audio_in is not None:
473 self.led_audio_in.setColor(self.led_audio_in.GREEN)
474 self.led_audio_in.setEnabled(False)
476 if self.led_audio_out is not None:
477 self.led_audio_out.setColor(self.led_audio_out.BLUE)
478 self.led_audio_out.setEnabled(False)
480 if self.peak_in is not None:
481 self.peak_in.setChannelCount(self.fPeaksInputCount)
482 self.peak_in.setMeterColor(DigitalPeakMeter.COLOR_GREEN)
483 self.peak_in.setMeterOrientation(DigitalPeakMeter.HORIZONTAL)
485 if self.fSkinStyle.startswith("calf"):
486 self.peak_in.setMeterStyle(DigitalPeakMeter.STYLE_CALF)
487 elif self.fSkinStyle == "rncbc":
488 self.peak_in.setMeterStyle(DigitalPeakMeter.STYLE_RNCBC)
489 elif self.fSkinStyle.startswith("openav") or self.fSkinStyle == "zynfx":
490 self.peak_in.setMeterStyle(DigitalPeakMeter.STYLE_OPENAV)
492 if self.fPeaksInputCount == 0 and not isinstance(self, PluginSlot_Classic):
493 self.peak_in.hide()
495 if self.peak_out is not None:
496 self.peak_out.setChannelCount(self.fPeaksOutputCount)
497 self.peak_out.setMeterColor(DigitalPeakMeter.COLOR_BLUE)
498 self.peak_out.setMeterOrientation(DigitalPeakMeter.HORIZONTAL)
500 if self.fSkinStyle.startswith("calf"):
501 self.peak_out.setMeterStyle(DigitalPeakMeter.STYLE_CALF)
502 elif self.fSkinStyle == "rncbc":
503 self.peak_out.setMeterStyle(DigitalPeakMeter.STYLE_RNCBC)
504 elif self.fSkinStyle.startswith("openav") or self.fSkinStyle == "zynfx":
505 self.peak_out.setMeterStyle(DigitalPeakMeter.STYLE_OPENAV)
507 if self.fPeaksOutputCount == 0 and not isinstance(self, PluginSlot_Classic):
508 self.peak_out.hide()
510 # -------------------------------------------------------------
512 if self.fSkinStyle == "openav":
513 styleSheet = """
514 QFrame#PluginWidget {
515 background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
516 stop: 0 #383838, stop: %f #111111, stop: 1.0 #111111);
518 QLabel#label_name { color: #FFFFFF; }
519 QLabel#label_name:disabled { color: #505050; }
520 """ % (0.95 if isinstance(self, PluginSlot_Compact) else 0.35)
522 elif self.fSkinStyle == "openav-old":
523 styleSheet = """
524 QFrame#PluginWidget {
525 background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
526 stop: 0 #303030, stop: %f #111111, stop: 1.0 #111111);
528 QLabel#label_name { color: #FF5100; }
529 QLabel#label_name:disabled { color: #505050; }
530 """ % (0.95 if isinstance(self, PluginSlot_Compact) else 0.35)
532 else:
533 colorEnabled = "#BBB"
534 colorDisabled = "#555"
536 if self.fSkinStyle in ("3bandeq", "calf_black", "calf_blue", "nekobi", "zynfx"):
537 styleSheet2 = "background-image: url(:/bitmaps/background_%s.png);" % self.fSkinStyle
538 else:
539 styleSheet2 = "background-color: rgb(200, 200, 200);"
540 styleSheet2 += "background-image: url(:/bitmaps/background_noise1.png);"
542 if not self.fDarkStyle:
543 colorEnabled = "#111"
544 colorDisabled = "#AAA"
546 styleSheet = """
547 QFrame#PluginWidget {
549 background-repeat: repeat-xy;
551 QLabel#label_name,
552 QLabel#label_audio_in,
553 QLabel#label_audio_out,
554 QLabel#label_midi,
555 QLabel#label_presets { color: %s; }
556 QLabel#label_name:disabled { color: %s; }
557 """ % (styleSheet2, colorEnabled, colorDisabled)
559 styleSheet += """
560 QComboBox#cb_presets,
561 QLabel#label_audio_in,
562 QLabel#label_audio_out,
563 QLabel#label_midi { font-size: 10px; }
565 self.setStyleSheet(styleSheet)
567 # -------------------------------------------------------------
568 # Set-up parameters
570 if self.w_knobs_left is not None:
571 parameterCount = self.host.get_parameter_count(self.fPluginId)
573 index = 0
574 layout = self.w_knobs_left.layout()
575 for i in range(parameterCount):
576 # 50 should be enough for everybody, right?
577 if index >= 50:
578 break
580 paramInfo = self.host.get_parameter_info(self.fPluginId, i)
581 paramData = self.host.get_parameter_data(self.fPluginId, i)
582 paramRanges = self.host.get_parameter_ranges(self.fPluginId, i)
583 isInteger = (paramData['hints'] & PARAMETER_IS_INTEGER) != 0
585 if paramData['type'] != PARAMETER_INPUT:
586 continue
587 if paramData['hints'] & PARAMETER_IS_BOOLEAN:
588 continue
589 if (paramData['hints'] & PARAMETER_IS_ENABLED) == 0:
590 continue
591 if (paramData['hints'] & PARAMETER_USES_SCALEPOINTS) != 0 and not isInteger:
592 # NOTE: we assume integer scalepoints are continuous
593 continue
594 if isInteger and paramRanges['max']-paramRanges['min'] <= 3:
595 continue
596 if paramInfo['name'].startswith("unused"):
597 continue
599 paramName = getParameterShortName(paramInfo['name'])
601 widget = ScalableDial(self, i)
602 widget.setLabel(paramName)
603 widget.setMinimum(paramRanges['min'])
604 widget.setMaximum(paramRanges['max'])
605 widget.hide()
607 if isInteger:
608 widget.setPrecision(paramRanges['max']-paramRanges['min'], True)
610 setScalableDialStyle(widget, i, parameterCount, whiteLabels, self.fSkinStyle)
612 index += 1
613 self.fParameterList.append([i, widget])
614 layout.addWidget(widget)
616 if self.w_knobs_right is not None and (self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET) != 0:
617 widget = ScalableDial(self, PARAMETER_DRYWET)
618 widget.setLabel("Dry/Wet")
619 widget.setMinimum(0.0)
620 widget.setMaximum(1.0)
621 setScalableDialStyle(widget, PARAMETER_DRYWET, 0, whiteLabels, self.fSkinStyle)
623 self.fParameterList.append([PARAMETER_DRYWET, widget])
624 self.w_knobs_right.layout().addWidget(widget)
626 if self.w_knobs_right is not None and (self.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME) != 0:
627 widget = ScalableDial(self, PARAMETER_VOLUME)
628 widget.setLabel("Volume")
629 widget.setMinimum(0.0)
630 widget.setMaximum(1.27)
631 setScalableDialStyle(widget, PARAMETER_VOLUME, 0, whiteLabels, self.fSkinStyle)
633 self.fParameterList.append([PARAMETER_VOLUME, widget])
634 self.w_knobs_right.layout().addWidget(widget)
636 for paramIndex, paramWidget in self.fParameterList:
637 paramWidget.setContextMenuPolicy(Qt.CustomContextMenu)
638 paramWidget.customContextMenuRequested.connect(self.slot_knobCustomMenu)
639 paramWidget.dragStateChanged.connect(self.slot_parameterDragStateChanged)
640 paramWidget.realValueChanged.connect(self.slot_parameterValueChanged)
641 paramWidget.blockSignals(True)
642 paramWidget.setValue(self.host.get_internal_parameter_value(self.fPluginId, paramIndex))
643 paramWidget.blockSignals(False)
645 # -------------------------------------------------------------
647 self.setWindowTitle(self.fPluginInfo['name'])
649 if not self.fAdjustViewableKnobCountScheduled:
650 self.fAdjustViewableKnobCountScheduled = True
651 QTimer.singleShot(5, self.adjustViewableKnobCount)
653 # -----------------------------------------------------------------
655 def getFixedHeight(self):
656 return 32
658 def getHints(self):
659 return self.fPluginInfo['hints']
661 def getPluginId(self):
662 return self.fPluginId
664 # -----------------------------------------------------------------
666 def setPluginId(self, idx):
667 self.fPluginId = idx
668 self.fEditDialog.setPluginId(idx)
670 def setName(self, name):
671 self.fPluginInfo['name'] = name
672 self.fEditDialog.setName(name)
674 if self.label_name is not None:
675 self.label_name.setText(name)
677 def setSelected(self, yesNo):
678 if self.fIsSelected == yesNo:
679 return
681 self.fIsSelected = yesNo
682 self.update()
684 # -----------------------------------------------------------------
686 def setActive(self, active, sendCallback=False, sendHost=True):
687 self.fIsActive = active
689 if sendCallback:
690 self.fParameterIconTimer = ICON_STATE_ON
691 self.activeChanged(active)
693 if sendHost:
694 self.host.set_active(self.fPluginId, active)
696 if active:
697 self.fEditDialog.clearNotes()
698 self.midiActivityChanged(False)
700 if self.label_name is not None:
701 self.label_name.setEnabled(self.fIsActive)
703 # called from rack, checks if param is possible first
704 def setInternalParameter(self, parameterId, value):
705 if parameterId <= PARAMETER_MAX or parameterId >= PARAMETER_NULL:
706 return
708 elif parameterId == PARAMETER_ACTIVE:
709 return self.setActive(bool(value), True, True)
711 elif parameterId == PARAMETER_DRYWET:
712 if (self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET) == 0: return
713 self.host.set_drywet(self.fPluginId, value)
715 elif parameterId == PARAMETER_VOLUME:
716 if (self.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME) == 0: return
717 self.host.set_volume(self.fPluginId, value)
719 elif parameterId == PARAMETER_BALANCE_LEFT:
720 if (self.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE) == 0: return
721 self.host.set_balance_left(self.fPluginId, value)
723 elif parameterId == PARAMETER_BALANCE_RIGHT:
724 if (self.fPluginInfo['hints'] & PLUGIN_CAN_BALANCE) == 0: return
725 self.host.set_balance_right(self.fPluginId, value)
727 elif parameterId == PARAMETER_PANNING:
728 if (self.fPluginInfo['hints'] & PLUGIN_CAN_PANNING) == 0: return
729 self.host.set_panning(self.fPluginId, value)
731 elif parameterId == PARAMETER_CTRL_CHANNEL:
732 self.host.set_ctrl_channel(self.fPluginId, value)
734 self.fEditDialog.setParameterValue(parameterId, value)
736 # -----------------------------------------------------------------
738 def setParameterValue(self, parameterId, value, sendCallback):
739 if parameterId == PARAMETER_ACTIVE:
740 return self.setActive(bool(value), True, False)
742 self.fEditDialog.setParameterValue(parameterId, value)
744 if sendCallback:
745 self.fParameterIconTimer = ICON_STATE_ON
746 self.editDialogParameterValueChanged(self.fPluginId, parameterId, value)
748 def setParameterDefault(self, parameterId, value):
749 self.fEditDialog.setParameterDefault(parameterId, value)
751 def setParameterMappedControlIndex(self, parameterId, control):
752 self.fEditDialog.setParameterMappedControlIndex(parameterId, control)
754 def setParameterMappedRange(self, parameterId, minimum, maximum):
755 self.fEditDialog.setParameterMappedRange(parameterId, minimum, maximum)
757 def setParameterMidiChannel(self, parameterId, channel):
758 self.fEditDialog.setParameterMidiChannel(parameterId, channel)
760 # -----------------------------------------------------------------
762 def setProgram(self, index, sendCallback):
763 self.fEditDialog.setProgram(index)
765 if sendCallback:
766 self.fParameterIconTimer = ICON_STATE_ON
767 self.editDialogProgramChanged(self.fPluginId, index)
769 self.updateParameterValues()
771 def setMidiProgram(self, index, sendCallback):
772 self.fEditDialog.setMidiProgram(index)
774 if sendCallback:
775 self.fParameterIconTimer = ICON_STATE_ON
776 self.editDialogMidiProgramChanged(self.fPluginId, index)
778 self.updateParameterValues()
780 # -----------------------------------------------------------------
782 def setOption(self, option, yesNo):
783 self.fEditDialog.setOption(option, yesNo)
785 # -----------------------------------------------------------------
787 def showCustomUI(self):
788 self.host.show_custom_ui(self.fPluginId, True)
790 if self.b_gui is not None:
791 self.b_gui.setChecked(True)
793 def hideCustomUI(self):
794 self.host.show_custom_ui(self.fPluginId, False)
796 if self.b_gui is not None:
797 self.b_gui.setChecked(False)
799 def showEditDialog(self):
800 self.fEditDialog.show()
801 self.fEditDialog.activateWindow()
803 if self.b_edit is not None:
804 self.b_edit.setChecked(True)
806 def showRenameDialog(self):
807 oldName = self.fPluginInfo['name']
808 newNameTry = QInputDialog.getText(self, self.tr("Rename Plugin"), self.tr("New plugin name:"), QLineEdit.Normal, oldName)
810 if not (newNameTry[1] and newNameTry[0] and oldName != newNameTry[0]):
811 return
813 newName = newNameTry[0]
815 if not self.host.rename_plugin(self.fPluginId, newName):
816 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
817 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
818 return
820 def showReplaceDialog(self):
821 data = gCarla.gui.showAddPluginDialog()
823 if data is None:
824 return
826 btype, ptype, filename, label, uniqueId, extraPtr = data
828 if not self.host.replace_plugin(self.fPluginId):
829 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to replace plugin"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
830 return
832 ok = self.host.add_plugin(btype, ptype, filename, None, label, uniqueId, extraPtr, PLUGIN_OPTIONS_NULL)
834 self.host.replace_plugin(self.host.get_max_plugin_number())
836 if not ok:
837 CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
839 # -----------------------------------------------------------------
841 def activeChanged(self, onOff):
842 self.fIsActive = onOff
844 if self.b_enable is None:
845 return
847 self.b_enable.blockSignals(True)
848 self.b_enable.setChecked(onOff)
849 self.b_enable.blockSignals(False)
851 def customUiStateChanged(self, state):
852 if self.b_gui is None:
853 return
855 self.b_gui.blockSignals(True)
856 if state == 0:
857 self.b_gui.setChecked(False)
858 self.b_gui.setEnabled(True)
859 elif state == 1:
860 self.b_gui.setChecked(True)
861 self.b_gui.setEnabled(True)
862 elif state == -1:
863 self.b_gui.setChecked(False)
864 self.b_gui.setEnabled(False)
865 self.b_gui.blockSignals(False)
867 def parameterActivityChanged(self, onOff):
868 if self.led_control is None:
869 return
871 self.led_control.setChecked(onOff)
873 def midiActivityChanged(self, onOff):
874 if self.led_midi is None:
875 return
877 self.led_midi.setChecked(onOff)
879 def optionChanged(self, option, yesNo):
880 pass
882 # -----------------------------------------------------------------
883 # PluginEdit callbacks
885 def editDialogVisibilityChanged(self, pluginId, visible):
886 if self.b_edit is None:
887 return
889 self.b_edit.blockSignals(True)
890 self.b_edit.setChecked(visible)
891 self.b_edit.blockSignals(False)
893 def editDialogPluginHintsChanged(self, pluginId, hints):
894 self.fPluginInfo['hints'] = hints
896 for paramIndex, paramWidget in self.fParameterList:
897 if paramIndex == PARAMETER_DRYWET:
898 paramWidget.setVisible(hints & PLUGIN_CAN_DRYWET)
899 elif paramIndex == PARAMETER_VOLUME:
900 paramWidget.setVisible(hints & PLUGIN_CAN_VOLUME)
902 if self.b_gui is not None:
903 self.b_gui.setEnabled(bool(hints & PLUGIN_HAS_CUSTOM_UI))
905 def editDialogParameterValueChanged(self, pluginId, parameterId, value):
906 for paramIndex, paramWidget in self.fParameterList:
907 if paramIndex != parameterId:
908 continue
910 paramWidget.blockSignals(True)
911 paramWidget.setValue(value)
912 paramWidget.blockSignals(False)
913 break
915 def editDialogProgramChanged(self, pluginId, index):
916 if self.cb_presets is None:
917 return
919 self.cb_presets.blockSignals(True)
920 self.cb_presets.setCurrentIndex(index)
921 self.cb_presets.blockSignals(False)
923 # FIXME
924 self.updateParameterValues()
926 def editDialogMidiProgramChanged(self, pluginId, index):
927 if self.cb_presets is None:
928 return
930 self.cb_presets.blockSignals(True)
931 self.cb_presets.setCurrentIndex(index)
932 self.cb_presets.blockSignals(False)
934 # FIXME
935 self.updateParameterValues()
937 def editDialogNotePressed(self, pluginId, note):
938 pass
940 def editDialogNoteReleased(self, pluginId, note):
941 pass
943 def editDialogMidiActivityChanged(self, pluginId, onOff):
944 self.midiActivityChanged(onOff)
946 # -----------------------------------------------------------------
948 def idleFast(self):
949 # Input peaks
950 if self.fPeaksInputCount > 0:
951 if self.fPeaksInputCount > 1:
952 peak1 = self.host.get_input_peak_value(self.fPluginId, True)
953 peak2 = self.host.get_input_peak_value(self.fPluginId, False)
954 ledState = bool(peak1 != 0.0 or peak2 != 0.0)
956 if self.peak_in is not None:
957 self.peak_in.displayMeter(1, peak1)
958 self.peak_in.displayMeter(2, peak2)
960 else:
961 peak = self.host.get_input_peak_value(self.fPluginId, True)
962 ledState = bool(peak != 0.0)
964 if self.peak_in is not None:
965 self.peak_in.displayMeter(1, peak)
967 if self.fLastGreenLedState != ledState and self.led_audio_in is not None:
968 self.fLastGreenLedState = ledState
969 self.led_audio_in.setChecked(ledState)
971 # Output peaks
972 if self.fPeaksOutputCount > 0:
973 if self.fPeaksOutputCount > 1:
974 peak1 = self.host.get_output_peak_value(self.fPluginId, True)
975 peak2 = self.host.get_output_peak_value(self.fPluginId, False)
976 ledState = bool(peak1 != 0.0 or peak2 != 0.0)
978 if self.peak_out is not None:
979 self.peak_out.displayMeter(1, peak1)
980 self.peak_out.displayMeter(2, peak2)
982 else:
983 peak = self.host.get_output_peak_value(self.fPluginId, True)
984 ledState = bool(peak != 0.0)
986 if self.peak_out is not None:
987 self.peak_out.displayMeter(1, peak)
989 if self.fLastBlueLedState != ledState and self.led_audio_out is not None:
990 self.fLastBlueLedState = ledState
991 self.led_audio_out.setChecked(ledState)
993 def idleSlow(self):
994 if self.fParameterIconTimer == ICON_STATE_ON:
995 self.parameterActivityChanged(True)
996 self.fParameterIconTimer = ICON_STATE_WAIT
998 elif self.fParameterIconTimer == ICON_STATE_WAIT:
999 self.fParameterIconTimer = ICON_STATE_OFF
1001 elif self.fParameterIconTimer == ICON_STATE_OFF:
1002 self.parameterActivityChanged(False)
1003 self.fParameterIconTimer = ICON_STATE_NULL
1005 self.fEditDialog.idleSlow()
1007 # -----------------------------------------------------------------
1009 def drawOutline(self, painter):
1010 painter.save()
1011 painter.setBrush(Qt.transparent)
1012 w = float(self.width())
1013 h = float(self.height())
1015 painter.setPen(self.shadow_pen)
1016 painter.drawLine(QLineF(0.5, h-1, w-1, h-1))
1018 if self.fIsSelected:
1019 painter.setCompositionMode(QPainter.CompositionMode_Plus)
1021 painter.setPen(self.sel_pen)
1022 painter.drawRect(QRectF(0.5, 0.5, w-1, h-1))
1023 sidelines = [QLineF(1, 1, 1, h-1), QLineF(w-1, 1, w-1, h-1)]
1024 painter.setPen(self.sel_side_pen)
1025 painter.drawLines(sidelines)
1027 painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
1029 painter.restore()
1031 def updateParameterValues(self):
1032 for paramIndex, paramWidget in self.fParameterList:
1033 if paramIndex < 0:
1034 continue
1036 paramWidget.blockSignals(True)
1037 paramWidget.setValue(self.host.get_current_parameter_value(self.fPluginId, paramIndex))
1038 paramWidget.blockSignals(False)
1040 # -----------------------------------------------------------------
1042 @pyqtSlot(bool)
1043 def slot_enableClicked(self, yesNo):
1044 self.setActive(yesNo, False, True)
1046 @pyqtSlot()
1047 def slot_showCustomMenu(self):
1048 menu = QMenu(self)
1050 # -------------------------------------------------------------
1051 # Expand/Minimize and Tweaks
1053 actCompact = menu.addAction(self.tr("Expand") if isinstance(self, PluginSlot_Compact) else self.tr("Minimize"))
1054 actColor = menu.addAction(self.tr("Change Color..."))
1055 actSkin = menu.addAction(self.tr("Change Skin..."))
1056 menu.addSeparator()
1058 # -------------------------------------------------------------
1059 # Find in patchbay, if possible
1061 if self.host.processMode in (ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS,
1062 ENGINE_PROCESS_MODE_PATCHBAY):
1063 actFindInPatchbay = menu.addAction(self.tr("Find plugin in patchbay"))
1064 menu.addSeparator()
1066 else:
1067 actFindInPatchbay = None
1069 # -------------------------------------------------------------
1070 # Move up and down
1072 actMoveUp = menu.addAction(self.tr("Move Up"))
1073 actMoveDown = menu.addAction(self.tr("Move Down"))
1075 if self.fPluginId == 0:
1076 actMoveUp.setEnabled(False)
1077 if self.fPluginId >= self.fParent.getPluginCount():
1078 actMoveDown.setEnabled(False)
1080 # -------------------------------------------------------------
1081 # Bypass and Enable/Disable
1083 actBypass = menu.addAction(self.tr("Bypass"))
1084 actEnable = menu.addAction(self.tr("Disable") if self.fIsActive else self.tr("Enable"))
1085 menu.addSeparator()
1087 if self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
1088 actBypass.setCheckable(True)
1089 actBypass.setChecked(self.host.get_internal_parameter_value(self.fPluginId, PARAMETER_DRYWET) == 0.0)
1090 else:
1091 actBypass.setVisible(False)
1093 # -------------------------------------------------------------
1094 # Reset and Randomize parameters
1096 actReset = menu.addAction(self.tr("Reset parameters"))
1097 actRandom = menu.addAction(self.tr("Randomize parameters"))
1098 menu.addSeparator()
1100 # -------------------------------------------------------------
1101 # Edit and Show Custom UI
1103 actEdit = menu.addAction(self.tr("Edit"))
1104 actGui = menu.addAction(self.tr("Show Custom UI"))
1105 menu.addSeparator()
1107 if self.b_edit is not None:
1108 actEdit.setCheckable(True)
1109 actEdit.setChecked(self.b_edit.isChecked())
1110 else:
1111 actEdit.setVisible(False)
1113 if self.b_gui is not None:
1114 actGui.setCheckable(True)
1115 actGui.setChecked(self.b_gui.isChecked())
1116 actGui.setEnabled(self.b_gui.isEnabled())
1117 else:
1118 actGui.setVisible(False)
1120 # -------------------------------------------------------------
1121 # Other stuff
1123 actClone = menu.addAction(self.tr("Clone"))
1124 actRename = menu.addAction(self.tr("Rename..."))
1125 actReplace = menu.addAction(self.tr("Replace..."))
1126 actRemove = menu.addAction(self.tr("Remove"))
1128 if self.fIdleTimerId != 0:
1129 actRemove.setVisible(False)
1131 if self.host.exportLV2:
1132 menu.addSeparator()
1133 actExportLV2 = menu.addAction(self.tr("Export LV2..."))
1135 else:
1136 actExportLV2 = None
1138 # -------------------------------------------------------------
1139 # exec
1141 actSel = menu.exec_(QCursor.pos())
1143 if not actSel:
1144 return
1146 # -------------------------------------------------------------
1147 # Expand/Minimize
1149 elif actSel == actCompact:
1150 # FIXME
1151 gCarla.gui.compactPlugin(self.fPluginId)
1153 # -------------------------------------------------------------
1154 # Tweaks
1156 elif actSel == actColor:
1157 initial = QColor(self.fSkinColor[0], self.fSkinColor[1], self.fSkinColor[2])
1158 color = QColorDialog.getColor(initial, self, self.tr("Change Color"), QColorDialog.DontUseNativeDialog)
1160 if not color.isValid():
1161 return
1163 color = color.getRgb()[0:3]
1164 colorStr = "%i;%i;%i" % color
1165 gCarla.gui.changePluginColor(self.fPluginId, color, colorStr)
1167 elif actSel == actSkin:
1168 skinList = [
1169 "default",
1170 "3bandeq",
1171 "rncbc",
1172 "calf_black",
1173 "calf_blue",
1174 "classic",
1175 "openav-old",
1176 "openav",
1177 "zynfx",
1178 "presets",
1179 "mpresets",
1181 try:
1182 index = skinList.index(self.fSkinStyle)
1183 except:
1184 index = 0
1186 skin = QInputDialog.getItem(self, self.tr("Change Skin"),
1187 self.tr("Change Skin to:"),
1188 skinList, index, False)
1189 if not all(skin):
1190 return
1191 gCarla.gui.changePluginSkin(self.fPluginId, skin[0])
1193 # -------------------------------------------------------------
1194 # Find in patchbay
1196 elif actSel == actFindInPatchbay:
1197 gCarla.gui.findPluginInPatchbay(self.fPluginId)
1199 # -------------------------------------------------------------
1200 # Move up and down
1202 elif actSel == actMoveUp:
1203 gCarla.gui.switchPlugins(self.fPluginId, self.fPluginId-1)
1205 elif actSel == actMoveDown:
1206 gCarla.gui.switchPlugins(self.fPluginId, self.fPluginId+1)
1208 # -------------------------------------------------------------
1209 # Bypass and Enable/Disable
1211 elif actSel == actBypass:
1212 value = 0.0 if actBypass.isChecked() else 1.0
1213 self.host.set_drywet(self.fPluginId, value)
1214 self.setParameterValue(PARAMETER_DRYWET, value, True)
1216 elif actSel == actEnable:
1217 self.setActive(not self.fIsActive, True, True)
1219 # -------------------------------------------------------------
1220 # Reset and Randomize parameters
1222 elif actSel == actReset:
1223 self.host.reset_parameters(self.fPluginId)
1225 elif actSel == actRandom:
1226 self.host.randomize_parameters(self.fPluginId)
1228 # -------------------------------------------------------------
1229 # Edit and Show Custom UI
1231 elif actSel == actEdit:
1232 self.b_edit.click()
1234 elif actSel == actGui:
1235 self.b_gui.click()
1237 # -------------------------------------------------------------
1238 # Clone
1240 elif actSel == actClone:
1241 if not self.host.clone_plugin(self.fPluginId):
1242 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1243 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1245 # -------------------------------------------------------------
1246 # Rename
1248 elif actSel == actRename:
1249 self.showRenameDialog()
1251 # -------------------------------------------------------------
1252 # Replace
1254 elif actSel == actReplace:
1255 self.showReplaceDialog()
1257 # -------------------------------------------------------------
1258 # Remove
1260 elif actSel == actRemove:
1261 if not self.host.remove_plugin(self.fPluginId):
1262 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1263 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1265 # -------------------------------------------------------------
1266 # Export LV2
1268 elif actSel == actExportLV2:
1269 filepath = QInputDialog.getItem(self, self.tr("Export LV2 Plugin"),
1270 self.tr("Select LV2 Path where plugin will be exported to:"),
1271 CARLA_DEFAULT_LV2_PATH, editable=False)
1272 if not all(filepath):
1273 return
1275 plugname = self.fPluginInfo['name']
1276 filepath = os.path.join(filepath[0], plugname.replace(" ","_"))
1278 if not filepath.endswith(".lv2"):
1279 filepath += ".lv2"
1281 if os.path.exists(filepath):
1282 if QMessageBox.question(self, self.tr("Export to LV2"),
1283 self.tr("Plugin bundle already exists, overwrite?")) == QMessageBox.Ok:
1284 return
1286 if not self.host.export_plugin_lv2(self.fPluginId, filepath):
1287 return CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1288 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1290 QMessageBox.information(self, self.tr("Plugin exported"),
1291 self.tr("Plugin exported successfully, saved in folder:\n%s" % filepath))
1293 # -------------------------------------------------------------
1295 @pyqtSlot()
1296 def slot_knobCustomMenu(self):
1297 sender = self.sender()
1298 index = sender.fIndex
1299 minimum = sender.fMinimum
1300 maximum = sender.fMaximum
1301 current = sender.fRealValue
1302 label = sender.fLabel
1304 if index in (PARAMETER_NULL, PARAMETER_CTRL_CHANNEL) or index <= PARAMETER_MAX:
1305 return
1306 elif index in (PARAMETER_DRYWET, PARAMETER_VOLUME):
1307 default = 1.0
1308 elif index == PARAMETER_BALANCE_LEFT:
1309 default = -1.0
1310 elif index == PARAMETER_BALANCE_RIGHT:
1311 default = 1.0
1312 elif index == PARAMETER_PANNING:
1313 default = 0.0
1314 else:
1315 default = self.host.get_default_parameter_value(self.fPluginId, index)
1317 if index < PARAMETER_NULL:
1318 # show in integer percentage
1319 textReset = self.tr("Reset (%i%%)" % round(default*100.0))
1320 textMinim = self.tr("Set to Minimum (%i%%)" % round(minimum*100.0))
1321 textMaxim = self.tr("Set to Maximum (%i%%)" % round(maximum*100.0))
1322 else:
1323 # show in full float value
1324 textReset = self.tr("Reset (%f)" % default)
1325 textMinim = self.tr("Set to Minimum (%f)" % minimum)
1326 textMaxim = self.tr("Set to Maximum (%f)" % maximum)
1328 menu = QMenu(self)
1329 actReset = menu.addAction(textReset)
1330 menu.addSeparator()
1331 actMinimum = menu.addAction(textMinim)
1332 actCenter = menu.addAction(self.tr("Set to Center"))
1333 actMaximum = menu.addAction(textMaxim)
1334 menu.addSeparator()
1335 actSet = menu.addAction(self.tr("Set value..."))
1337 if index > PARAMETER_NULL or index not in (PARAMETER_BALANCE_LEFT, PARAMETER_BALANCE_RIGHT, PARAMETER_PANNING):
1338 menu.removeAction(actCenter)
1340 actSelected = menu.exec_(QCursor.pos())
1342 if actSelected == actSet:
1343 if index < PARAMETER_NULL:
1344 value, ok = QInputDialog.getInt(self, self.tr("Set value"), label, round(current*100), round(minimum*100), round(maximum*100), 1)
1346 if not ok:
1347 return
1349 value = float(value)/100.0
1351 else:
1352 paramInfo = self.host.get_parameter_info(self.fPluginId, index)
1353 paramRanges = self.host.get_parameter_ranges(self.fPluginId, index)
1354 scalePoints = []
1356 for i in range(paramInfo['scalePointCount']):
1357 scalePoints.append(self.host.get_parameter_scalepoint_info(self.fPluginId, index, i))
1359 prefix = ""
1360 suffix = paramInfo['unit'].strip()
1362 if suffix == "(coef)":
1363 prefix = "* "
1364 suffix = ""
1365 else:
1366 suffix = " " + suffix
1368 dialog = CustomInputDialog(self, label, current, minimum, maximum,
1369 paramRanges['step'], paramRanges['stepSmall'], scalePoints, prefix, suffix)
1371 if not dialog.exec_():
1372 return
1374 value = dialog.returnValue()
1376 elif actSelected == actMinimum:
1377 value = minimum
1378 elif actSelected == actMaximum:
1379 value = maximum
1380 elif actSelected == actReset:
1381 value = default
1382 elif actSelected == actCenter:
1383 value = 0.0
1384 else:
1385 return
1387 sender.setValue(value, True)
1389 # -----------------------------------------------------------------
1391 @pyqtSlot(bool)
1392 def slot_showCustomUi(self, show):
1393 self.host.show_custom_ui(self.fPluginId, show)
1395 @pyqtSlot(bool)
1396 def slot_showEditDialog(self, show):
1397 self.fEditDialog.setVisible(show)
1399 @pyqtSlot()
1400 def slot_removePlugin(self):
1401 if not self.host.remove_plugin(self.fPluginId):
1402 CustomMessageBox(self, QMessageBox.Warning, self.tr("Error"), self.tr("Operation failed"),
1403 self.host.get_last_error(), QMessageBox.Ok, QMessageBox.Ok)
1405 # -----------------------------------------------------------------
1407 @pyqtSlot(bool)
1408 def slot_parameterDragStateChanged(self, touch):
1409 index = self.sender().getIndex()
1411 if index >= 0:
1412 self.host.set_parameter_touch(self.fPluginId, index, touch)
1414 @pyqtSlot(float)
1415 def slot_parameterValueChanged(self, value):
1416 index = self.sender().getIndex()
1418 if index < 0:
1419 self.setInternalParameter(index, value)
1420 else:
1421 self.host.set_parameter_value(self.fPluginId, index, value)
1422 self.setParameterValue(index, value, False)
1424 @pyqtSlot(int)
1425 def slot_programChanged(self, index):
1426 self.host.set_program(self.fPluginId, index)
1427 self.setProgram(index, False)
1429 @pyqtSlot(int)
1430 def slot_midiProgramChanged(self, index):
1431 self.host.set_midi_program(self.fPluginId, index)
1432 self.setMidiProgram(index, False)
1434 # -----------------------------------------------------------------
1436 def adjustViewableKnobCount(self):
1437 if self.w_knobs_left is None or self.spacer_knobs is None:
1438 return
1440 curWidth = 2
1441 maxWidth = self.w_knobs_left.width() + self.spacer_knobs.geometry().width() + 2
1443 plen = len(self.fParameterList)
1444 for i in range(plen):
1445 index, widget = self.fParameterList[i]
1446 if index < 0:
1447 break
1449 curWidth += widget.width() + 4
1451 if curWidth + widget.width() * 2 + 8 < maxWidth:
1452 #if not widget.isVisible():
1453 widget.show()
1454 continue
1456 for i2 in range(i, plen):
1457 index2, widget2 = self.fParameterList[i2]
1458 if index2 < 0:
1459 break
1460 #if widget2.isVisible():
1461 widget2.hide()
1463 break
1465 self.fAdjustViewableKnobCountScheduled = False
1467 def testTimer(self):
1468 self.fIdleTimerId = self.startTimer(25)
1470 # -----------------------------------------------------------------
1472 def mouseDoubleClickEvent(self, event):
1473 QFrame.mouseDoubleClickEvent(self, event)
1475 # FIXME
1476 gCarla.gui.compactPlugin(self.fPluginId)
1478 def closeEvent(self, event):
1479 if self.fIdleTimerId != 0:
1480 self.killTimer(self.fIdleTimerId)
1481 self.fIdleTimerId = 0
1483 self.host.engine_close()
1485 QFrame.closeEvent(self, event)
1487 def resizeEvent(self, event):
1488 if not self.fAdjustViewableKnobCountScheduled:
1489 self.fAdjustViewableKnobCountScheduled = True
1490 QTimer.singleShot(100, self.adjustViewableKnobCount)
1491 QFrame.resizeEvent(self, event)
1493 def timerEvent(self, event):
1494 if event.timerId() == self.fIdleTimerId:
1495 self.host.engine_idle()
1496 self.idleFast()
1497 self.idleSlow()
1499 QFrame.timerEvent(self, event)
1501 def paintEvent(self, event):
1502 painter = QPainter(self)
1504 # Colorization
1505 if self.fSkinColor != (0,0,0):
1506 painter.setCompositionMode(QPainter.CompositionMode_Multiply)
1507 r,g,b = self.fSkinColor
1508 painter.setBrush(QColor(r,g,b))
1509 painter.setPen(Qt.NoPen)
1510 painter.drawRect(QRectF(0,0,self.width(),self.height()))
1511 painter.setCompositionMode(QPainter.CompositionMode_SourceOver)
1513 self.drawOutline(painter)
1514 QFrame.paintEvent(self, event)
1516 # ------------------------------------------------------------------------------------------------------------
1518 class PluginSlot_Calf(AbstractPluginSlot):
1519 def __init__(self, parent, host, pluginId, skinColor, skinStyle):
1520 AbstractPluginSlot.__init__(self, parent, host, pluginId, skinColor, skinStyle)
1521 self.ui = ui_carla_plugin_calf.Ui_PluginWidget()
1522 self.ui.setupUi(self)
1524 audioCount = self.host.get_audio_port_count_info(self.fPluginId)
1525 midiCount = self.host.get_midi_port_count_info(self.fPluginId)
1527 # -------------------------------------------------------------
1528 # Internal stuff
1530 self.fButtonFont = self.ui.b_gui.font()
1531 self.fButtonFont.setBold(False)
1532 self.fButtonFont.setPixelSize(10)
1534 self.fButtonColorOn = QColor( 18, 41, 87)
1535 self.fButtonColorOff = QColor(150, 150, 150)
1537 # -------------------------------------------------------------
1538 # Set-up GUI
1540 self.ui.label_active.setFont(self.fButtonFont)
1542 self.ui.b_remove.setPixmaps(":/bitmaps/button_calf1.png",
1543 ":/bitmaps/button_calf1_down.png",
1544 ":/bitmaps/button_calf1_hover.png")
1546 self.ui.b_edit.setTopText(self.tr("Edit"), self.fButtonColorOn, self.fButtonFont)
1547 self.ui.b_remove.setTopText(self.tr("Remove"), self.fButtonColorOn, self.fButtonFont)
1549 if self.fPluginInfo['hints'] & PLUGIN_HAS_CUSTOM_UI:
1550 self.ui.b_gui.setTopText(self.tr("GUI"), self.fButtonColorOn, self.fButtonFont)
1551 else:
1552 self.ui.b_gui.setTopText(self.tr("GUI"), self.fButtonColorOff, self.fButtonFont)
1554 if audioCount['ins'] == 0:
1555 self.ui.label_audio_in.hide()
1557 if audioCount['outs'] == 0:
1558 self.ui.label_audio_out.hide()
1560 if midiCount['ins'] == 0:
1561 self.ui.label_midi.hide()
1562 self.ui.led_midi.hide()
1564 if self.fIdleTimerId != 0:
1565 self.ui.b_remove.setEnabled(False)
1566 self.ui.b_remove.setVisible(False)
1568 # -------------------------------------------------------------
1570 self.b_enable = self.ui.b_enable
1571 self.b_gui = self.ui.b_gui
1572 self.b_edit = self.ui.b_edit
1573 self.b_remove = self.ui.b_remove
1575 self.label_name = self.ui.label_name
1576 self.led_midi = self.ui.led_midi
1578 self.peak_in = self.ui.peak_in
1579 self.peak_out = self.ui.peak_out
1581 self.w_knobs_left = self.ui.w_knobs
1582 self.spacer_knobs = self.ui.layout_bottom.itemAt(1).spacerItem()
1584 self.ready()
1586 self.ui.led_midi.setColor(self.ui.led_midi.CALF)
1588 # -----------------------------------------------------------------
1590 def getFixedHeight(self):
1591 return 94 if max(self.peak_in.channelCount(), self.peak_out.channelCount()) < 2 else 106
1593 # -----------------------------------------------------------------
1595 def editDialogPluginHintsChanged(self, pluginId, hints):
1596 if hints & PLUGIN_HAS_CUSTOM_UI:
1597 self.ui.b_gui.setTopText(self.tr("GUI"), self.fButtonColorOn, self.fButtonFont)
1598 else:
1599 self.ui.b_gui.setTopText(self.tr("GUI"), self.fButtonColorOff, self.fButtonFont)
1601 AbstractPluginSlot.editDialogPluginHintsChanged(self, pluginId, hints)
1603 # -----------------------------------------------------------------
1605 def paintEvent(self, event):
1606 isBlack = bool(self.fSkinStyle == "calf_black")
1608 painter = QPainter(self)
1609 painter.setBrush(Qt.transparent)
1611 painter.setPen(QPen(QColor(20, 20, 20) if isBlack else QColor(75, 86, 99), 1))
1612 painter.drawRect(0, 1, self.width()-1, self.height()-3)
1614 painter.setPen(QPen(QColor(45, 45, 45) if isBlack else QColor(86, 99, 114), 1))
1615 painter.drawLine(0, 0, self.width(), 0)
1617 AbstractPluginSlot.paintEvent(self, event)
1619 # ------------------------------------------------------------------------------------------------------------
1621 class PluginSlot_Classic(AbstractPluginSlot):
1622 def __init__(self, parent, host, pluginId):
1623 AbstractPluginSlot.__init__(self, parent, host, pluginId, (0,0,0), "classic")
1624 self.ui = ui_carla_plugin_classic.Ui_PluginWidget()
1625 self.ui.setupUi(self)
1627 # -------------------------------------------------------------
1628 # Internal stuff
1630 self.fColorTop = QColor(60, 60, 60)
1631 self.fColorBottom = QColor(47, 47, 47)
1632 self.fColorSeprtr = QColor(70, 70, 70)
1634 # -------------------------------------------------------------
1636 self.b_enable = self.ui.b_enable
1637 self.b_gui = self.ui.b_gui
1638 self.b_edit = self.ui.b_edit
1640 self.label_name = self.ui.label_name
1641 self.led_control = self.ui.led_control
1642 self.led_midi = self.ui.led_midi
1643 self.led_audio_in = self.ui.led_audio_in
1644 self.led_audio_out = self.ui.led_audio_out
1646 self.peak_in = self.ui.peak_in
1647 self.peak_out = self.ui.peak_out
1649 self.ready()
1651 # -----------------------------------------------------------------
1653 def getFixedHeight(self):
1654 return 36
1656 # -----------------------------------------------------------------
1658 def paintEvent(self, event):
1659 painter = QPainter(self)
1660 painter.save()
1662 areaX = self.ui.area_right.x()+7
1663 width = self.width()
1664 height = self.height()
1666 painter.setPen(QPen(QColor(17, 17, 17), 1))
1667 painter.setBrush(QColor(17, 17, 17))
1668 painter.drawRect(0, 0, width, height)
1670 painter.setPen(self.fColorSeprtr.lighter(110))
1671 painter.setBrush(self.fColorBottom)
1672 painter.setRenderHint(QPainter.Antialiasing, True)
1674 # name -> leds arc
1675 path = QPainterPath()
1676 path.moveTo(areaX-20, height-4)
1677 path.cubicTo(areaX, height-5, areaX-20, 4.75, areaX, 4.75)
1678 path.lineTo(areaX, height-5)
1679 painter.drawPath(path)
1681 painter.setPen(self.fColorSeprtr)
1682 painter.setRenderHint(QPainter.Antialiasing, False)
1684 # separator lines
1685 painter.drawLine(0, height-5, areaX-20, height-5)
1686 painter.drawLine(areaX, 4, width, 4)
1688 painter.setPen(self.fColorBottom)
1689 painter.setBrush(self.fColorBottom)
1691 # top, bottom and left lines
1692 painter.drawLine(0, 0, width, 0)
1693 painter.drawRect(0, height-4, areaX, 4)
1694 painter.drawRoundedRect(areaX-20, height-5, areaX, 5, 22, 22)
1695 painter.drawLine(0, 0, 0, height)
1697 # fill the rest
1698 painter.drawRect(areaX-1, 5, width, height)
1700 # bottom 1px line
1701 painter.setPen(self.fColorSeprtr)
1702 painter.drawLine(0, height-1, width, height-1)
1704 painter.restore()
1705 AbstractPluginSlot.paintEvent(self, event)
1707 # ------------------------------------------------------------------------------------------------------------
1709 class PluginSlot_Compact(AbstractPluginSlot):
1710 def __init__(self, parent, host, pluginId, skinColor, skinStyle):
1711 AbstractPluginSlot.__init__(self, parent, host, pluginId, skinColor, skinStyle)
1712 self.ui = ui_carla_plugin_compact.Ui_PluginWidget()
1713 self.ui.setupUi(self)
1715 self.b_enable = self.ui.b_enable
1716 self.b_gui = self.ui.b_gui
1717 self.b_edit = self.ui.b_edit
1719 self.label_name = self.ui.label_name
1721 self.led_control = self.ui.led_control
1722 self.led_midi = self.ui.led_midi
1723 self.led_audio_in = self.ui.led_audio_in
1724 self.led_audio_out = self.ui.led_audio_out
1726 self.peak_in = self.ui.peak_in
1727 self.peak_out = self.ui.peak_out
1729 self.ready()
1731 # -----------------------------------------------------------------
1733 def getFixedHeight(self):
1734 if self.fSkinStyle == "calf_blue":
1735 return 36
1736 return 30
1738 # ------------------------------------------------------------------------------------------------------------
1740 class PluginSlot_Default(AbstractPluginSlot):
1741 def __init__(self, parent, host, pluginId, skinColor, skinStyle):
1742 AbstractPluginSlot.__init__(self, parent, host, pluginId, skinColor, skinStyle)
1743 self.ui = ui_carla_plugin_default.Ui_PluginWidget()
1744 self.ui.setupUi(self)
1746 # -------------------------------------------------------------
1748 self.b_enable = self.ui.b_enable
1749 self.b_gui = self.ui.b_gui
1750 self.b_edit = self.ui.b_edit
1752 self.label_name = self.ui.label_name
1754 self.led_control = self.ui.led_control
1755 self.led_midi = self.ui.led_midi
1756 self.led_audio_in = self.ui.led_audio_in
1757 self.led_audio_out = self.ui.led_audio_out
1759 self.peak_in = self.ui.peak_in
1760 self.peak_out = self.ui.peak_out
1762 self.w_knobs_left = self.ui.w_knobs_left
1763 self.w_knobs_right = self.ui.w_knobs_right
1764 self.spacer_knobs = self.ui.layout_bottom.itemAt(1).spacerItem()
1766 self.ready()
1768 # -----------------------------------------------------------------
1770 def getFixedHeight(self):
1771 return 80
1773 # -----------------------------------------------------------------
1775 def paintEvent(self, event):
1776 painter = QPainter(self)
1777 painter.setBrush(Qt.transparent)
1779 painter.setPen(QPen(QColor(42, 42, 42), 1))
1780 painter.drawRect(0, 1, self.width()-1, self.getFixedHeight()-3)
1782 painter.setPen(QPen(QColor(60, 60, 60), 1))
1783 painter.drawLine(0, 0, self.width(), 0)
1785 AbstractPluginSlot.paintEvent(self, event)
1787 # ------------------------------------------------------------------------------------------------------------
1789 class PluginSlot_Presets(AbstractPluginSlot):
1790 def __init__(self, parent, host, pluginId, skinColor, skinStyle):
1791 AbstractPluginSlot.__init__(self, parent, host, pluginId, skinColor, skinStyle)
1792 self.ui = ui_carla_plugin_presets.Ui_PluginWidget()
1793 self.ui.setupUi(self)
1795 usingMidiPrograms = bool(skinStyle != "presets")
1797 # -------------------------------------------------------------
1798 # Set-up programs
1800 if usingMidiPrograms:
1801 programCount = self.host.get_midi_program_count(self.fPluginId)
1802 else:
1803 programCount = self.host.get_program_count(self.fPluginId)
1805 if programCount > 0:
1806 self.ui.cb_presets.setEnabled(True)
1807 self.ui.label_presets.setEnabled(True)
1809 for i in range(programCount):
1810 if usingMidiPrograms:
1811 progName = self.host.get_midi_program_data(self.fPluginId, i)['name']
1812 else:
1813 progName = self.host.get_program_name(self.fPluginId, i)
1815 self.ui.cb_presets.addItem(progName)
1817 if usingMidiPrograms:
1818 curProg = self.host.get_current_midi_program_index(self.fPluginId)
1819 else:
1820 curProg = self.host.get_current_program_index(self.fPluginId)
1822 self.ui.cb_presets.setCurrentIndex(curProg)
1824 else:
1825 self.ui.cb_presets.setEnabled(False)
1826 self.ui.cb_presets.setVisible(False)
1827 self.ui.label_presets.setEnabled(False)
1828 self.ui.label_presets.setVisible(False)
1830 # -------------------------------------------------------------
1832 self.b_enable = self.ui.b_enable
1833 self.b_gui = self.ui.b_gui
1834 self.b_edit = self.ui.b_edit
1836 self.cb_presets = self.ui.cb_presets
1838 self.label_name = self.ui.label_name
1839 self.label_presets = self.ui.label_presets
1841 self.led_control = self.ui.led_control
1842 self.led_midi = self.ui.led_midi
1843 self.led_audio_in = self.ui.led_audio_in
1844 self.led_audio_out = self.ui.led_audio_out
1846 self.peak_in = self.ui.peak_in
1847 self.peak_out = self.ui.peak_out
1849 if skinStyle == "zynfx":
1850 self.setupZynFxParams()
1851 else:
1852 self.w_knobs_left = self.ui.w_knobs_left
1853 self.w_knobs_right = self.ui.w_knobs_right
1854 self.spacer_knobs = self.ui.layout_bottom.itemAt(1).spacerItem()
1856 self.ready()
1858 if usingMidiPrograms:
1859 self.ui.cb_presets.currentIndexChanged.connect(self.slot_midiProgramChanged)
1860 else:
1861 self.ui.cb_presets.currentIndexChanged.connect(self.slot_programChanged)
1863 # -------------------------------------------------------------
1865 def setupZynFxParams(self):
1866 parameterCount = min(self.host.get_parameter_count(self.fPluginId), 8)
1868 for i in range(parameterCount):
1869 paramInfo = self.host.get_parameter_info(self.fPluginId, i)
1870 paramData = self.host.get_parameter_data(self.fPluginId, i)
1871 paramRanges = self.host.get_parameter_ranges(self.fPluginId, i)
1873 if paramData['type'] != PARAMETER_INPUT:
1874 continue
1875 if paramData['hints'] & PARAMETER_IS_BOOLEAN:
1876 continue
1877 if (paramData['hints'] & PARAMETER_IS_ENABLED) == 0:
1878 continue
1880 paramName = paramInfo['name']
1882 if paramName.startswith("unused"):
1883 continue
1885 # real zyn fx plugins
1886 if self.fPluginInfo['label'] == "zynalienwah":
1887 if i == 0: paramName = "Freq"
1888 elif i == 1: paramName = "Rnd"
1889 elif i == 2: paramName = "L type" # combobox
1890 elif i == 3: paramName = "St.df"
1891 elif i == 5: paramName = "Fb"
1892 elif i == 7: paramName = "L/R"
1894 elif self.fPluginInfo['label'] == "zynchorus":
1895 if i == 0: paramName = "Freq"
1896 elif i == 1: paramName = "Rnd"
1897 elif i == 2: paramName = "L type" # combobox
1898 elif i == 3: paramName = "St.df"
1899 elif i == 6: paramName = "Fb"
1900 elif i == 7: paramName = "L/R"
1901 elif i == 8: paramName = "Flngr" # button
1902 elif i == 9: paramName = "Subst" # button
1904 elif self.fPluginInfo['label'] == "zyndistortion":
1905 if i == 0: paramName = "LRc."
1906 elif i == 4: paramName = "Neg." # button
1907 elif i == 5: paramName = "LPF"
1908 elif i == 6: paramName = "HPF"
1909 elif i == 7: paramName = "St." # button
1910 elif i == 8: paramName = "PF" # button
1912 elif self.fPluginInfo['label'] == "zyndynamicfilter":
1913 if i == 0: paramName = "Freq"
1914 elif i == 1: paramName = "Rnd"
1915 elif i == 2: paramName = "L type" # combobox
1916 elif i == 3: paramName = "St.df"
1917 elif i == 4: paramName = "LfoD"
1918 elif i == 5: paramName = "A.S."
1919 elif i == 6: paramName = "A.Inv." # button
1920 elif i == 7: paramName = "A.M."
1922 elif self.fPluginInfo['label'] == "zynecho":
1923 if i == 1: paramName = "LRdl."
1924 elif i == 2: paramName = "LRc."
1925 elif i == 3: paramName = "Fb."
1926 elif i == 4: paramName = "Damp"
1928 elif self.fPluginInfo['label'] == "zynphaser":
1929 if i == 0: paramName = "Freq"
1930 elif i == 1: paramName = "Rnd"
1931 elif i == 2: paramName = "L type" # combobox
1932 elif i == 3: paramName = "St.df"
1933 elif i == 5: paramName = "Fb"
1934 elif i == 7: paramName = "L/R"
1935 elif i == 8: paramName = "Subst" # button
1936 elif i == 9: paramName = "Phase"
1937 elif i == 11: paramName = "Dist"
1939 elif self.fPluginInfo['label'] == "zynreverb":
1940 if i == 2: paramName = "I.delfb"
1941 elif i == 5: paramName = "LPF"
1942 elif i == 6: paramName = "HPF"
1943 elif i == 9: paramName = "R.S."
1944 elif i == 10: paramName = "I.del"
1946 else:
1947 paramName = getParameterShortName(paramName)
1949 widget = ScalableDial(self, i)
1951 widget.setLabel(paramName)
1952 widget.setMinimum(paramRanges['min'])
1953 widget.setMaximum(paramRanges['max'])
1954 widget.setImage(3)
1955 widget.setCustomPaintColor(QColor(83, 173, 10))
1956 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_COLOR)
1957 widget.forceWhiteLabelGradientText()
1958 widget.hide()
1960 if (paramData['hints'] & PARAMETER_IS_ENABLED) == 0:
1961 widget.setEnabled(False)
1963 self.fParameterList.append([i, widget])
1964 self.ui.w_knobs_left.layout().addWidget(widget)
1966 if self.fPluginInfo['hints'] & PLUGIN_CAN_DRYWET:
1967 widget = ScalableDial(self, PARAMETER_DRYWET)
1968 widget.setLabel("Wet")
1969 widget.setMinimum(0.0)
1970 widget.setMaximum(1.0)
1971 widget.setImage(3)
1972 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_WET)
1973 widget.forceWhiteLabelGradientText()
1975 self.fParameterList.append([PARAMETER_DRYWET, widget])
1976 self.ui.w_knobs_right.layout().addWidget(widget)
1978 if self.fPluginInfo['hints'] & PLUGIN_CAN_VOLUME:
1979 widget = ScalableDial(self, PARAMETER_VOLUME)
1980 widget.setLabel("Volume")
1981 widget.setMinimum(0.0)
1982 widget.setMaximum(1.27)
1983 widget.setImage(3)
1984 widget.setCustomPaintMode(ScalableDial.CUSTOM_PAINT_MODE_CARLA_VOL)
1985 widget.forceWhiteLabelGradientText()
1987 self.fParameterList.append([PARAMETER_VOLUME, widget])
1988 self.ui.w_knobs_right.layout().addWidget(widget)
1990 # -----------------------------------------------------------------
1992 def getFixedHeight(self):
1993 return 80
1995 # -----------------------------------------------------------------
1997 def paintEvent(self, event):
1998 painter = QPainter(self)
1999 painter.setBrush(Qt.transparent)
2001 painter.setPen(QPen(QColor(50, 50, 50), 1))
2002 painter.drawRect(0, 1, self.width()-1, self.height()-3)
2004 painter.setPen(QPen(QColor(64, 64, 64), 1))
2005 painter.drawLine(0, 0, self.width(), 0)
2007 AbstractPluginSlot.paintEvent(self, event)
2009 # ------------------------------------------------------------------------------------------------------------
2011 def getColorAndSkinStyle(host, pluginId):
2012 pluginInfo = host.get_plugin_info(pluginId)
2013 pluginName = host.get_real_plugin_name(pluginId)
2014 pluginLabel = pluginInfo['label'].lower()
2015 pluginMaker = pluginInfo['maker']
2016 uniqueId = pluginInfo['uniqueId']
2018 if pluginInfo['type'] in (PLUGIN_VST2, PLUGIN_VST3, PLUGIN_AU):
2019 progCount = host.get_program_count(pluginId)
2020 else:
2021 progCount = host.get_midi_program_count(pluginId)
2023 colorCategory = getColorFromCategory(pluginInfo['category'])
2024 colorNone = (0,0,0)
2026 # Samplers
2027 if pluginInfo['type'] == PLUGIN_SF2:
2028 return (colorCategory, "sf2")
2029 if pluginInfo['type'] == PLUGIN_SFZ:
2030 return (colorCategory, "sfz")
2032 # Calf
2033 if pluginName.split(" ", 1)[0].lower() == "calf":
2034 return (colorNone, "calf_black" if "mono" in pluginLabel else "calf_blue")
2036 # OpenAV
2037 if pluginMaker == "OpenAV Productions":
2038 return (colorNone, "openav-old")
2039 if pluginMaker == "OpenAV":
2040 return (colorNone, "openav")
2042 # ZynFX
2043 if pluginInfo['type'] == PLUGIN_INTERNAL:
2044 if pluginLabel.startswith("zyn") and pluginInfo['category'] != PLUGIN_CATEGORY_SYNTH:
2045 return (colorNone, "zynfx")
2047 if pluginInfo['type'] == PLUGIN_LADSPA:
2048 if pluginLabel.startswith("zyn") and pluginMaker.startswith("Josep Andreu"):
2049 return (colorNone, "zynfx")
2051 if pluginInfo['type'] == PLUGIN_LV2:
2052 if pluginLabel.startswith("http://kxstudio.sf.net/carla/plugins/zyn") and pluginName != "ZynAddSubFX":
2053 return (colorNone, "zynfx")
2055 # Presets
2056 if progCount > 1 and (pluginInfo['hints'] & PLUGIN_USES_MULTI_PROGS) == 0:
2057 if pluginInfo['type'] in (PLUGIN_VST2, PLUGIN_VST3, PLUGIN_AU):
2058 return (colorCategory, "presets")
2059 return (colorCategory, "mpresets")
2061 # DISTRHO Plugins (needs to be last)
2062 if pluginMaker.startswith("falkTX, ") or pluginMaker == "DISTRHO" or pluginLabel.startswith("http://distrho.sf.net/plugins/"):
2063 skinStyle = pluginLabel.replace("http://distrho.sf.net/plugins/","")
2064 if skinStyle in ("3bandeq", "nekobi"):
2065 return (colorNone, skinStyle)
2067 return (colorCategory, "default")
2069 def createPluginSlot(parent, host, pluginId, options):
2070 skinColor, skinStyle = getColorAndSkinStyle(host, pluginId)
2072 if options['color'] is not None:
2073 skinColor = options['color']
2075 if options['skin']:
2076 skinStyle = options['skin']
2078 if skinStyle == "classic":
2079 return PluginSlot_Classic(parent, host, pluginId)
2081 if "compact" in skinStyle or options['compact']:
2082 return PluginSlot_Compact(parent, host, pluginId, skinColor, skinStyle)
2084 if skinStyle.startswith("calf"):
2085 return PluginSlot_Calf(parent, host, pluginId, skinColor, skinStyle)
2087 if skinStyle in ("mpresets", "presets", "zynfx"):
2088 return PluginSlot_Presets(parent, host, pluginId, skinColor, skinStyle)
2090 return PluginSlot_Default(parent, host, pluginId, skinColor, skinStyle)
2092 # ------------------------------------------------------------------------------------------------------------
2093 # Main Testing
2095 if __name__ == '__main__':
2096 from carla_app import CarlaApplication
2097 from carla_host import initHost, loadHostSettings
2098 import resources_rc
2100 app = CarlaApplication("Carla-Skins")
2101 host = initHost("Skins", None, False, False, False)
2102 loadHostSettings(host)
2104 host.engine_init("JACK", "Carla-Widgets")
2105 host.add_plugin(BINARY_NATIVE, PLUGIN_INTERNAL, "", "", "zynreverb", 0, None, PLUGIN_OPTIONS_NULL)
2106 #host.add_plugin(BINARY_NATIVE, PLUGIN_DSSI, "/usr/lib/dssi/karplong.so", "karplong", "karplong", 0, None, PLUGIN_OPTIONS_NULL)
2107 #host.add_plugin(BINARY_NATIVE, PLUGIN_LV2, "", "", "http://www.openavproductions.com/sorcer", 0, None, PLUGIN_OPTIONS_NULL)
2108 #host.add_plugin(BINARY_NATIVE, PLUGIN_LV2, "", "", "http://calf.sourceforge.net/plugins/Compressor", 0, None, PLUGIN_OPTIONS_NULL)
2109 host.set_active(0, True)
2111 #gui = createPluginSlot(None, host, 0, True)
2112 gui = PluginSlot_Compact(None, host, 0, (0, 0, 0), "default")
2113 gui.testTimer()
2114 gui.show()
2116 app.exec_()