diff --git a/Makefile b/Makefile index 22c9400..0f428f1 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ all: build build: UI RES LANG -UI: catarina catia claudia tools +UI: catarina catia claudia carla tools catarina: src/ui_catarina.py \ src/ui_catarina_addgroup.py src/ui_catarina_removegroup.py src/ui_catarina_renamegroup.py \ @@ -26,6 +26,21 @@ claudia: src/ui_claudia.py \ src/ui_claudia_runcustom.py # src/ui_claudia_addnew.py src/ui_claudia_addnew_klaudia.py +carla: carla_backend carla_bridges carla_discovery carla_gui + +carla_backend: +# $(MAKE) -C src/carla + +carla_bridges: +# $(MAKE) native$(_arch_n) -C src/carla-bridges + +carla_discovery: +# $(MAKE) native$(_arch_n) -C src/carla-discovery + +carla_gui: src/ui_carla.py src/ui_carla_control.py \ + src/ui_carla_about.py src/ui_carla_database.py src/ui_carla_edit.py src/ui_carla_parameter.py src/ui_carla_plugin.py src/ui_carla_refresh.py \ + src/ui_inputdialog_value.py + tools: \ src/ui_logs.py src/ui_render.py src/ui_xycontroller.py \ src/ui_settings_app.py src/ui_settings_jack.py @@ -81,6 +96,30 @@ src/ui_claudia_projectproperties.py: src/ui/claudia_projectproperties.ui src/ui_claudia_runcustom.py: src/ui/claudia_runcustom.ui $(PYUIC) -o src/ui_claudia_runcustom.py $< +src/ui_carla.py: src/ui/carla.ui + $(PYUIC) -o src/ui_carla.py $< + +src/ui_carla_control.py: src/ui/carla_control.ui + $(PYUIC) -o src/ui_carla_control.py $< + +src/ui_carla_about.py: src/ui/carla_about.ui + $(PYUIC) -o src/ui_carla_about.py $< + +src/ui_carla_database.py: src/ui/carla_database.ui + $(PYUIC) -o src/ui_carla_database.py $< + +src/ui_carla_edit.py: src/ui/carla_edit.ui + $(PYUIC) -o src/ui_carla_edit.py $< + +src/ui_carla_parameter.py: src/ui/carla_parameter.ui + $(PYUIC) -o src/ui_carla_parameter.py $< + +src/ui_carla_plugin.py: src/ui/carla_plugin.ui + $(PYUIC) -o src/ui_carla_plugin.py $< + +src/ui_carla_refresh.py: src/ui/carla_refresh.ui + $(PYUIC) -o src/ui_carla_refresh.py $< + src/ui_logs.py: src/ui/logs.ui $(PYUIC) -o src/ui_logs.py $< @@ -96,6 +135,9 @@ src/ui_settings_app.py: src/ui/settings_app.ui src/ui_settings_jack.py: src/ui/settings_jack.ui $(PYUIC) -o src/ui_settings_jack.py $< +src/ui_inputdialog_value.py: src/ui/inputdialog_value.ui + $(PYUIC) -o src/ui_inputdialog_value.py $< + RES: src/icons_rc.py src/icons_rc.py: src/icons/icons.qrc diff --git a/src/carla.py b/src/carla.py new file mode 100755 index 0000000..6a8d526 --- /dev/null +++ b/src/carla.py @@ -0,0 +1,3154 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Carla Backend code +# Copyright (C) 2011-2012 Filipe Coelho FIXME +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the COPYING file + +# Imports (Global) +import json, os, sys +#from PyQt4.QtCore import Qt, QSettings, QThread, QTimer, QVariant, SIGNAL, SLOT +#from PyQt4.QtGui import QApplication, QColor, QCursor, QFileDialog, QFontMetrics, QInputDialog, QMenu, QPainter, QPixmap, QTableWidgetItem, QVBoxLayout +#from PyQt4.QtXml import QDomDocument +from time import sleep +#from sip import unwrapinstance +from PyQt4.QtCore import pyqtSlot, Qt, QSettings, QThread +from PyQt4.QtGui import QApplication, QDialog, QFrame, QMainWindow, QWidget + +# Imports (Custom Stuff) +import ui_carla, ui_carla_about, ui_carla_database, ui_carla_edit, ui_carla_parameter, ui_carla_plugin, ui_carla_refresh +from carla_backend import * +from shared import * + +ICON_STATE_WAIT = 0 +ICON_STATE_OFF = 1 +ICON_STATE_ON = 2 + +PALETTE_COLOR_NONE = 0 +PALETTE_COLOR_WHITE = 1 +PALETTE_COLOR_RED = 2 +PALETTE_COLOR_GREEN = 3 +PALETTE_COLOR_BLUE = 4 +PALETTE_COLOR_YELLOW = 5 +PALETTE_COLOR_ORANGE = 6 +PALETTE_COLOR_BROWN = 7 +PALETTE_COLOR_PINK = 8 + +# Save support +save_state_dict = { + 'Type': "", + 'Name': "", + 'Label': "", + 'Binary': "", + 'UniqueID': 0, + 'Active': False, + 'DryWet': 1.0, + 'Vol': 1.0, + 'Balance-Left': -1.0, + 'Balance-Right': 1.0, + 'Parameters': [], + 'ProgramIndex': -1, + 'ProgramName': "", + 'MidiBank': -1, + 'MidiProgram': -1, + 'CustomData': [], + 'Chunk': None +} + +save_state_parameter = { + 'index': 0, + 'rindex': 0, + 'name': "", + 'symbol': "", + 'value': 0.0, + 'midi_channel': 1, + 'midi_cc': -1 +} + +save_state_custom_data = { + 'type': CUSTOM_DATA_INVALID, + 'key': "", + 'value': "" +} + +def is_number(value): + string = str(value) + if (string != ""): + if (string[0] == "-"): + string = string.replace("-","",1) + sstring = string.split(".") + if (len(sstring) == 1 and sstring[0].isdigit()): + return True + elif (len(sstring) == 2 and sstring[0].isdigit() and sstring[1].isdigit()): + return True + else: + return False + else: + return False + +def getStateSaveDict(xml_node): + x_save_state_dict = deepcopy(save_state_dict) + + node = xml_node.firstChild() + while not node.isNull(): + if (node.toElement().tagName() == "Info"): + xml_info = node.toElement().firstChild() + + while not xml_info.isNull(): + tag = QStringStr(xml_info.toElement().tagName()) + text = QStringStr(xml_info.toElement().text()) + if (tag == "Type"): + x_save_state_dict['Type'] = text + elif (tag == "Name"): + x_save_state_dict['Name'] = text + elif (tag == "Label"): + x_save_state_dict['Label'] = text + elif (tag == "Binary"): + x_save_state_dict['Binary'] = text + elif (tag == "UniqueID"): + if (text.isdigit()): + x_save_state_dict['UniqueID'] = long(text) + xml_info = xml_info.nextSibling() + + elif (node.toElement().tagName() == "Data"): + xml_data = node.toElement().firstChild() + + while not xml_data.isNull(): + tag = QStringStr(xml_data.toElement().tagName()) + text = QStringStr(xml_data.toElement().text()) + + if (tag == "Active"): + x_save_state_dict['Active'] = bool(text == "Yes") + elif (tag == "DryWet"): + if (is_number(text)): + x_save_state_dict['DryWet'] = float(text) + elif (tag == "Vol"): + if (is_number(text)): + x_save_state_dict['Vol'] = float(text) + elif (tag == "Balance-Left"): + if (is_number(text)): + x_save_state_dict['Balance-Left'] = float(text) + elif (tag == "Balance-Right"): + if (is_number(text)): + x_save_state_dict['Balance-Right'] = float(text) + elif (tag == "ProgramIndex"): + if (is_number(text)): + x_save_state_dict['ProgramIndex'] = int(text) + elif (tag == "ProgramName"): + x_save_state_dict['ProgramName'] = text + elif (tag == "MidiBank"): + if (is_number(text)): + x_save_state_dict['MidiBank'] = int(text) + elif (tag == "MidiProgram"): + if (is_number(text)): + x_save_state_dict['MidiProgram'] = int(text) + elif (tag == "Chunk"): + if (text): + x_save_state_dict['Chunk'] = text.strip() + + elif (tag == "Parameter"): + x_save_state_parameter = deepcopy(save_state_parameter) + + xml_subdata = xml_data.toElement().firstChild() + while not xml_subdata.isNull(): + ptag = QStringStr(xml_subdata.toElement().tagName()) + ptext = QStringStr(xml_subdata.toElement().text()) + + if (ptag == "index"): + if (is_number(ptext)): + x_save_state_parameter['index'] = int(ptext) + elif (ptag == "rindex"): + if (is_number(ptext)): + x_save_state_parameter['rindex'] = int(ptext) + elif (ptag == "name"): + x_save_state_parameter['name'] = ptext + elif (ptag == "symbol"): + x_save_state_parameter['symbol'] = ptext + elif (ptag == "value"): + if (is_number(ptext)): + x_save_state_parameter['value'] = float(ptext) + elif (ptag == "midi_channel"): + if (is_number(ptext)): + x_save_state_parameter['midi_channel'] = int(ptext) + elif (ptag == "midi_cc"): + if (is_number(ptext)): + x_save_state_parameter['midi_cc'] = int(ptext) + + xml_subdata = xml_subdata.nextSibling() + + x_save_state_dict['Parameters'].append(x_save_state_parameter) + + elif (tag == "CustomData"): + x_save_state_custom_data = deepcopy(save_state_custom_data) + + xml_subdata = xml_data.toElement().firstChild() + while not xml_subdata.isNull(): + ctag = QStringStr(xml_subdata.toElement().tagName()) + ctext = QStringStr(xml_subdata.toElement().text()) + + if (ctag == "type"): + if (is_number(ctext)): + x_save_state_custom_data['type'] = int(ctext) + elif (ctag == "key"): + x_save_state_custom_data['key'] = ctext + elif (ctag == "value"): + if (ctext): + x_value = ctext.strip() + x_save_state_custom_data['value'] = x_value.replace("<","<").replace(">",">").replace(""","\"") + + xml_subdata = xml_subdata.nextSibling() + + x_save_state_dict['CustomData'].append(x_save_state_custom_data) + + xml_data = xml_data.nextSibling() + + node = node.nextSibling() + + return x_save_state_dict + +def strPyPluginInfo(qt_pinfo): + pinfo = deepcopy(PyPluginInfo) + pinfo['type'] = qt_pinfo[QString('type')] + pinfo['category'] = qt_pinfo[QString('category')] + pinfo['hints'] = qt_pinfo[QString('hints')] + pinfo['binary'] = QStringStr(qt_pinfo[QString('binary')]) + pinfo['name'] = QStringStr(qt_pinfo[QString('name')]) + pinfo['label'] = QStringStr(qt_pinfo[QString('label')]) + pinfo['maker'] = QStringStr(qt_pinfo[QString('maker')]) + pinfo['copyright'] = QStringStr(qt_pinfo[QString('copyright')]) + pinfo['id'] = QStringStr(qt_pinfo[QString('id')]) + pinfo['audio.ins'] = qt_pinfo[QString('audio.ins')] + pinfo['audio.outs'] = qt_pinfo[QString('audio.outs')] + pinfo['audio.total'] = qt_pinfo[QString('audio.total')] + pinfo['midi.ins'] = qt_pinfo[QString('midi.ins')] + pinfo['midi.outs'] = qt_pinfo[QString('midi.outs')] + pinfo['midi.total'] = qt_pinfo[QString('midi.total')] + pinfo['parameters.ins'] = qt_pinfo[QString('parameters.ins')] + pinfo['parameters.outs'] = qt_pinfo[QString('parameters.outs')] + pinfo['parameters.total'] = qt_pinfo[QString('parameters.total')] + pinfo['programs.total'] = qt_pinfo[QString('programs.total')] + return pinfo + +# Separate Thread for Plugin Search +class SearchPluginsThread(QThread): + def __init__(self, parent): + QThread.__init__(self, parent) + + self.settings_db = self.parent().settings_db + #self.disccover_skip_kill = "" + + self.check_ladspa = True + self.check_dssi = True + self.check_lv2 = True + self.check_vst = True + self.check_sf2 = True + + self.check_native = None + self.check_bins = [] + + def skipPlugin(self): + #if (self.disccover_skip_kill): + os.system("killall -KILL carla-discovery carla-discovery-unix32 carla-discovery-unix64 carla-discovery-win32.exe carla-discovery-win64.exe") + + def pluginLook(self, percent, plugin): + print("pluginLook %.1f - %s" % (percent, plugin)) + self.emit(SIGNAL("PluginLook(int, QString)"), percent, plugin) + + def setSearchBins(self, bins): + self.check_bins = bins + + def setSearchNative(self, native): + self.check_native = native + + def setSearchTypes(self, ladspa, dssi, lv2, vst, sf2): + self.check_ladspa = ladspa + self.check_dssi = dssi + self.check_lv2 = lv2 + self.check_vst = vst + self.check_sf2 = sf2 + + def setLastLoadedBinary(self, binary): + self.settings_db.setValue("Plugins/LastLoadedBinary", binary) + + def run(self): + blacklist = toList(self.settings_db.value("Plugins/Blacklisted", [])) + bins = [] + bins_w = [] + + m_count = type_count = 0 + if (self.check_ladspa): m_count += 1 + if (self.check_dssi): m_count += 1 + if (self.check_vst): m_count += 1 + + check_native = check_wine = False + + if (LINUX): + OS = "LINUX" + elif (MACOS): + OS = "MACOS" + elif (WINDOWS): + OS = "WINDOWS" + else: + OS = "UNKNOWN" + + if (LINUX or MACOS): + if (carla_discovery_unix32 in self.check_bins or carla_discovery_unix64 in self.check_bins): + type_count += m_count + check_native = True + if (carla_discovery_unix32 in self.check_bins): + bins.append(carla_discovery_unix32) + if (carla_discovery_unix64 in self.check_bins): + bins.append(carla_discovery_unix64) + + if (carla_discovery_win32 in self.check_bins or carla_discovery_win64 in self.check_bins): + type_count += m_count + check_wine = True + if (carla_discovery_win32 in self.check_bins): + bins_w.append(carla_discovery_win32) + if (carla_discovery_win64 in self.check_bins): + bins_w.append(carla_discovery_win64) + + elif (WINDOWS): + if (carla_discovery_win32 in self.check_bins or carla_discovery_win64 in self.check_bins): + type_count += m_count + check_native = True + if (carla_discovery_win32 in self.check_bins): + bins.append(carla_discovery_win32) + if (carla_discovery_win64 in self.check_bins): + bins.append(carla_discovery_win64) + + if (self.check_lv2): type_count += 1 + if (self.check_sf2): type_count += 1 + + if (type_count == 0): + return + + ladspa_plugins = [] + dssi_plugins = [] + lv2_plugins = [] + vst_plugins = [] + soundfonts = [] + + ladspa_rdf_info = [] + lv2_rdf_info = [] + + last_value = 0 + percent_value = 100/type_count + + # ----- LADSPA + if (self.check_ladspa): + + if (check_native): + ladspa_binaries = [] + + for PATH in LADSPA_PATH: + binaries = findBinaries(PATH, OS) + for binary in binaries: + if (binary not in ladspa_binaries): + ladspa_binaries.append(binary) + + ladspa_binaries.sort() + + for i in range(len(ladspa_binaries)): + ladspa = ladspa_binaries[i] + if (getShortFileName(ladspa) in blacklist): + print("plugin %s is blacklisted, skip it" % (ladspa)) + continue + else: + percent = ( float(i) / len(ladspa_binaries) ) * percent_value + self.pluginLook((last_value + percent)*0.9, ladspa) + self.setLastLoadedBinary(ladspa) + for bin_ in bins: + plugins = checkPluginLADSPA(ladspa, bin_) + if (plugins != None): + ladspa_plugins.append(plugins) + + last_value += percent_value + + if (check_wine): + ladspa_binaries_w = [] + + for PATH in LADSPA_PATH: + binaries = findBinaries(PATH, "WINDOWS") + for binary in binaries: + if (binary not in ladspa_binaries_w): + ladspa_binaries_w.append(binary) + + ladspa_binaries_w.sort() + + # Check binaries, wine + for i in range(len(ladspa_binaries_w)): + ladspa_w = ladspa_binaries_w[i] + if (getShortFileName(ladspa_w) in blacklist): + print("plugin %s is blacklisted, skip it" % (ladspa_w)) + continue + else: + percent = ( float(i) / len(ladspa_binaries_w) ) * percent_value + self.pluginLook((last_value + percent)*0.9, ladspa_w) + self.setLastLoadedBinary(ladspa_w) + for bin_w in bins_w: + plugins_w = checkPluginLADSPA(ladspa_w, bin_w, True) + if (plugins_w != None): + ladspa_plugins.append(plugins_w) + + last_value += percent_value + + if (haveRDF): + m_value = 0 + if (check_native): m_value += 0.1 + if (check_wine): m_value += 0.1 + + if (m_value > 0): + start_value = last_value - (percent_value * m_value) + + self.pluginLook(start_value, "LADSPA RDFs...") + ladspa_rdf_info = ladspa_rdf.recheck_all_plugins(self, start_value, percent_value, m_value) + + ## ----- DSSI + #if (self.check_dssi): + ## Check PATH + #dssi_binaries = [] + #dssi_binaries_w = [] + + #for PATH in DSSI_PATH: + #binaries = findBinaries(PATH, OS) + #for binary in binaries: + #if (binary not in dssi_binaries): + #dssi_binaries.append(binary) + + #if (len(bins_w) > 0): + #for PATH in DSSI_PATH: + #binaries = findBinaries(PATH, "WINDOWS") + #for binary in binaries: + #if (binary not in dssi_binaries_w): + #dssi_binaries_w.append(binary) + + #dssi_binaries.sort() + #dssi_binaries_w.sort() + + #for i in range(len(dssi_binaries)): + #dssi = dssi_binaries[i] + #if (getShortFileName(dssi) in blacklist): + #print("plugin %s is blacklisted, skip it" % (dssi)) + #continue + #else: + #percent = (( float(i) / len(dssi_binaries) ) * percent_value) + #self.pluginLook(percent + last_value, dssi) + #self.setLastLoadedBinary(dssi) + #for bin_ in bins: + #plugins = checkPluginDSSI(dssi, bin_) + #if (plugins != None): + #dssi_plugins.append(plugins) + + #last_value += percent_value + + ## ----- LV2 + #if (self.check_lv2 and haveRDF): + #self.disccover_skip_kill = "" + #self.pluginLook(self.last_value, "LV2 bundles...") + #lv2_rdf_info = lv2_rdf.recheck_all_plugins(self) + #for info in lv2_rdf_info: + #plugins = checkPluginLV2(info) + #if (plugins != None): + #lv2_plugins.append(plugins) + + #self.last_value += self.percent_value + + ## ----- VST + #if (self.check_vst): + ## Check PATH + #vst_binaries = [] + #for i in range(len(VST_PATH)): + #binaries = findBinaries(VST_PATH[i]) + #for j in range(len(binaries)): + #if (binaries[j] not in vst_binaries): + #vst_binaries.append(binaries[j]) + + #self.disccover_skip_kill = "carla-discovery carla-discovery-native" + #for i in range(len(vst_binaries)): + #if (getShortFileName(vst_binaries[i]) in blacklist): + #print("plugin %s is blacklisted, skip it" % (vst_binaries[i])) + #continue + #else: + #percent = (( float(i) / len(vst_binaries) ) * self.percent_value) + #self.pluginLook(percent + self.last_value, vst_binaries[i]) + #self.setLastLoadedBinary(vst_binaries[i]) + #plugins = checkPluginVST(vst_binaries[i]) + #if (plugins != None): + #vst_plugins.append(plugins) + + #self.last_value += self.percent_value + + ## ----- SF2 + #if (self.check_sf2): + ## Check PATH + #sf2_files = [] + #for i in range(len(SF2_PATH)): + #files = findSoundFonts(SF2_PATH[i]) + #for j in range(len(files)): + #if (files[j] not in sf2_files): + #sf2_files.append(files[j]) + + #self.disccover_skip_kill = "carla-discovery carla-discovery-native" + #for i in range(len(sf2_files)): + #if (getShortFileName(sf2_files[i]) in blacklist): + #print("soundfont %s is blacklisted, skip it" % (sf2_files[i])) + #continue + #else: + #percent = (( float(i) / len(sf2_files) ) * self.percent_value) + #self.pluginLook(percent + self.last_value, sf2_files[i]) + #self.setLastLoadedBinary(sf2_files[i]) + #soundfont_ = checkPluginSF2(sf2_files[i]) + #if (soundfont_): + #soundfonts.append(soundfont_[0]) + + #self.last_value += self.percent_value + + #self.disccover_skip_kill = "" + self.setLastLoadedBinary("") + + # Save plugins to database + self.pluginLook(100, "Database...") + + #if (self.check_ladspa): + #self.settings_db.setValue("Plugins/LADSPA", QVariant(ladspa_plugins)) + + #if (self.check_dssi): + #self.settings_db.setValue("Plugins/DSSI", QVariant(dssi_plugins)) + + #if (self.check_lv2): + #self.settings_db.setValue("Plugins/LV2", QVariant(lv2_plugins)) + + #if (self.check_vst): + #self.settings_db.setValue("Plugins/VST", QVariant(vst_plugins)) + + #if (self.check_winvst): + #self.settings_db.setValue("Plugins/WinVST", QVariant(winvst_plugins)) + + #if (self.check_sf2): + #self.settings_db.setValue("Plugins/SF2", QVariant(soundfonts)) + + #self.settings_db.sync() + + #if (haveRDF): + #SettingsDir = os.path.join(HOME, ".config", "Cadence") + + #if (self.check_ladspa): + #f_ladspa = open(os.path.join(SettingsDir, "ladspa_rdf.db"), 'w') + #if (f_ladspa): + #json.dump(ladspa_rdf_info, f_ladspa) + #f_ladspa.close() + + #if (self.check_lv2): + #f_lv2 = open(os.path.join(SettingsDir, "lv2_rdf.db"), 'w') + #if (f_lv2): + #json.dump(lv2_rdf_info, f_lv2) + #f_lv2.close() + +# Plugin Refresh Dialog +class PluginRefreshW(QDialog, ui_carla_refresh.Ui_PluginRefreshW): + def __init__(self, parent): + QDialog.__init__(self, parent) + self.setupUi(self) + + self.b_skip.setVisible(False) + self.settings = self.parent().settings + self.settings_db = self.parent().settings_db + self.loadSettings() + + self.pThread = SearchPluginsThread(self) + + if (carla_discovery_unix32): + self.ico_unix32.setPixmap(getIcon("dialog-ok-apply").pixmap(16, 16)) + else: + self.ico_unix32.setPixmap(getIcon("dialog-error").pixmap(16, 16)) + self.ch_unix32.setChecked(False) + self.ch_unix32.setEnabled(False) + + if (carla_discovery_unix64): + self.ico_unix64.setPixmap(getIcon("dialog-ok-apply").pixmap(16, 16)) + else: + self.ico_unix64.setPixmap(getIcon("dialog-error").pixmap(16, 16)) + self.ch_unix64.setChecked(False) + self.ch_unix64.setEnabled(False) + + if (carla_discovery_win32): + self.ico_win32.setPixmap(getIcon("dialog-ok-apply").pixmap(16, 16)) + else: + self.ico_win32.setPixmap(getIcon("dialog-error").pixmap(16, 16)) + self.ch_win32.setChecked(False) + self.ch_win32.setEnabled(False) + + if (carla_discovery_win64): + self.ico_win64.setPixmap(getIcon("dialog-ok-apply").pixmap(16, 16)) + else: + self.ico_win64.setPixmap(getIcon("dialog-error").pixmap(16, 16)) + self.ch_win64.setChecked(False) + self.ch_win64.setEnabled(False) + + if (haveRDF): + self.ico_rdflib.setPixmap(getIcon("dialog-ok-apply").pixmap(16, 16)) + else: + self.ico_rdflib.setPixmap(getIcon("dialog-error").pixmap(16, 16)) + self.ch_lv2.setChecked(False) + self.ch_lv2.setEnabled(False) + + if (LINUX or MACOS): + if (is64bit): + hasNative = bool(carla_discovery_unix64) + hasNonNative = bool(carla_discovery_unix32 or carla_discovery_win32 or carla_discovery_win64) + self.pThread.setSearchNative(carla_discovery_unix64) + else: + hasNative = bool(carla_discovery_unix32) + hasNonNative = bool(carla_discovery_unix64 or carla_discovery_win32 or carla_discovery_win64) + self.pThread.setSearchNative(carla_discovery_unix32) + elif (WINDOWS): + if (is64bit): + hasNative = bool(carla_discovery_win64) + hasNonNative = bool(carla_discovery_win32) + self.pThread.setSearchNative(carla_discovery_win64) + else: + hasNative = bool(carla_discovery_win32) + hasNonNative = bool(carla_discovery_win64) + self.pThread.setSearchNative(carla_discovery_win32) + else: + hasNative = False + + if (not hasNative): + self.ch_sf2.setChecked(False) + self.ch_sf2.setEnabled(False) + + if (not hasNonNative): + self.ch_ladspa.setChecked(False) + self.ch_ladspa.setEnabled(False) + self.ch_dssi.setChecked(False) + self.ch_dssi.setEnabled(False) + self.ch_vst.setChecked(False) + self.ch_vst.setEnabled(False) + + if (not haveRDF): + self.b_refresh.setEnabled(False) + + self.connect(self.b_refresh, SIGNAL("clicked()"), SLOT("slot_refresh_plugins()")) + self.connect(self.b_skip, SIGNAL("clicked()"), SLOT("slot_skip()")) + self.connect(self.pThread, SIGNAL("PluginLook(int, QString)"), SLOT("slot_handlePluginLook(int, QString)")) + self.connect(self.pThread, SIGNAL("finished()"), SLOT("slot_handlePluginThreadFinished()")) + + @pyqtSlot() + def slot_refresh_plugins(self): + self.progressBar.setMinimum(0) + self.progressBar.setMaximum(100) + self.progressBar.setValue(0) + self.b_refresh.setEnabled(False) + self.b_skip.setVisible(True) + self.b_close.setVisible(False) + + bins = [] + if (self.ch_unix32.isChecked()): + bins.append(carla_discovery_unix32) + if (self.ch_unix64.isChecked()): + bins.append(carla_discovery_unix64) + if (self.ch_win32.isChecked()): + bins.append(carla_discovery_win32) + if (self.ch_win64.isChecked()): + bins.append(carla_discovery_win64) + + self.pThread.setSearchBins(bins) + self.pThread.setSearchTypes(self.ch_ladspa.isChecked(), self.ch_dssi.isChecked(), self.ch_lv2.isChecked(), self.ch_vst.isChecked(), self.ch_sf2.isChecked()) + self.pThread.start() + + @pyqtSlot() + def slot_skip(self): + self.pThread.skipPlugin() + + @pyqtSlot(int, str) + def slot_handlePluginLook(self, percent, plugin): + self.progressBar.setFormat("%s" % (plugin)) + self.progressBar.setValue(percent) + + @pyqtSlot() + def slot_handlePluginThreadFinished(self): + self.progressBar.setMinimum(0) + self.progressBar.setMaximum(1) + self.progressBar.setValue(1) + self.progressBar.setFormat(self.tr("Done")) + self.b_refresh.setEnabled(True) + self.b_skip.setVisible(False) + self.b_close.setVisible(True) + + def saveSettings(self): + self.settings.setValue("PluginDatabase/SearchLADSPA", self.ch_ladspa.isChecked()) + self.settings.setValue("PluginDatabase/SearchDSSI", self.ch_dssi.isChecked()) + self.settings.setValue("PluginDatabase/SearchLV2", self.ch_lv2.isChecked()) + self.settings.setValue("PluginDatabase/SearchVST", self.ch_vst.isChecked()) + self.settings.setValue("PluginDatabase/SearchSF2", self.ch_sf2.isChecked()) + self.settings.setValue("PluginDatabase/SearchUnix32", self.ch_unix32.isChecked()) + self.settings.setValue("PluginDatabase/SearchUnix64", self.ch_unix64.isChecked()) + self.settings.setValue("PluginDatabase/SearchWin32", self.ch_win32.isChecked()) + self.settings.setValue("PluginDatabase/SearchWin64", self.ch_win64.isChecked()) + self.settings_db.setValue("Plugins/LastLoadedBinary", "") + + def loadSettings(self): + self.ch_ladspa.setChecked(self.settings.value("PluginDatabase/SearchLADSPA", True, type=bool)) + self.ch_dssi.setChecked(self.settings.value("PluginDatabase/SearchDSSI", True, type=bool)) + self.ch_lv2.setChecked(self.settings.value("PluginDatabase/SearchLV2", True, type=bool)) + self.ch_vst.setChecked(self.settings.value("PluginDatabase/SearchVST", True, type=bool)) + self.ch_sf2.setChecked(self.settings.value("PluginDatabase/SearchSF2", True, type=bool)) + self.ch_unix32.setChecked(self.settings.value("PluginDatabase/SearchUnix32", True, type=bool)) + self.ch_unix64.setChecked(self.settings.value("PluginDatabase/SearchUnix64", True, type=bool)) + self.ch_win32.setChecked(self.settings.value("PluginDatabase/SearchWin32", True, type=bool)) + self.ch_win64.setChecked(self.settings.value("PluginDatabase/SearchWin64", True, type=bool)) + + def closeEvent(self, event): + if (self.pThread.isRunning()): + self.pThread.terminate() + self.pThread.wait() + self.saveSettings() + QDialog.closeEvent(self, event) + +# Plugin Database Dialog +class PluginDatabaseW(QDialog, ui_carla_database.Ui_PluginDatabaseW): + def __init__(self, parent): + QDialog.__init__(self, parent) + self.setupUi(self) + + self.b_add.setEnabled(False) + + self.settings = self.parent().settings + self.settings_db = self.parent().settings_db + self.loadSettings() + + # Blacklist plugins + if not self.settings_db.contains("Plugins/Blacklisted"): + blacklist = [] + # Broken or useless plugins + #blacklist.append("dssi-vst.so") + blacklist.append("liteon_biquad-vst.so") + blacklist.append("liteon_biquad-vst_64bit.so") + blacklist.append("fx_blur-vst.so") + blacklist.append("fx_blur-vst_64bit.so") + blacklist.append("Scrubby_64bit.so") + blacklist.append("Skidder_64bit.so") + blacklist.append("libwormhole2_64bit.so") + blacklist.append("vexvst.so") + #blacklist.append("deckadance.dll") + self.settings_db.setValue("Plugins/Blacklisted", blacklist) + + self.connect(self.b_add, SIGNAL("clicked()"), SLOT("slot_add_plugin()")) + self.connect(self.b_refresh, SIGNAL("clicked()"), SLOT("slot_refresh_plugins()")) + self.connect(self.tb_filters, SIGNAL("clicked()"), SLOT("slot_maybe_show_filters()")) + self.connect(self.tableWidget, SIGNAL("currentCellChanged(int, int, int, int)"), SLOT("slot_checkPlugin(int)")) + self.connect(self.tableWidget, SIGNAL("cellDoubleClicked(int, int)"), SLOT("slot_add_plugin()")) + + self.connect(self.lineEdit, SIGNAL("textChanged(QString)"), SLOT("slot_checkFilters()")) + self.connect(self.ch_effects, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_instruments, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_midi, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_other, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_sf2, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_ladspa, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_dssi, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_lv2, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_vst, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_gui, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + self.connect(self.ch_stereo, SIGNAL("clicked()"), SLOT("slot_checkFilters()")) + + self.ret_plugin = None + + def showFilters(self, yesno): + if (yesno): + arrow = Qt.UpArrow + else: + arrow = Qt.DownArrow + + self.tb_filters.setArrowType(arrow) + self.frame.setVisible(yesno) + + #def reAddPlugins(self): + #self.last_table_index = 0 + #self.tableWidget.setSortingEnabled(False) + + #row_count = self.tableWidget.rowCount() + #for i in range(row_count): + #self.tableWidget.removeRow(0) + + #ladspa_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/LADSPA").toList()) + #dssi_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/DSSI").toList()) + #lv2_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/LV2").toList()) + #vst_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/VST").toList()) + #winvst_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/WinVST").toList()) + #soundfonts = QVariantPyObjectList(self.settings_db.value("Plugins/SF2").toList()) + + #ladspa_count = 0 + #dssi_count = 0 + #lv2_count = 0 + #vst_count = 0 + #winvst_count = 0 + #sf2_count = 0 + + #for plugins in ladspa_plugins: + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + #self.addPluginToTable(plugin, "LADSPA") + #ladspa_count += 1 + + #for plugins in dssi_plugins: + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + #self.addPluginToTable(plugin, "DSSI") + #dssi_count += 1 + + #for plugins in lv2_plugins: + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + #self.addPluginToTable(plugin, "LV2") + #lv2_count += 1 + + #for plugins in vst_plugins: + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + #self.addPluginToTable(plugin, "VST") + #vst_count += 1 + + #for plugins in winvst_plugins: + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + #self.addPluginToTable(plugin, "WinVST") + #winvst_count += 1 + + #for soundfont in soundfonts: + #plugin = strPyPluginInfo(soundfont) + #self.addPluginToTable(plugin, "SF2") + #sf2_count += 1 + + #self.checkFilters() + #self.tableWidget.setSortingEnabled(True) + #self.tableWidget.sortByColumn(0, Qt.AscendingOrder) + + #self.label.setText(self.tr("Have %1 LADSPA, %2 DSSI, %3 LV2, %4 VST, %5 Windows VST and %6 SoundFonts" + #).arg(ladspa_count).arg(dssi_count).arg(lv2_count).arg(vst_count).arg(winvst_count).arg(sf2_count)) + + #def addPluginToTable(self, plugin, ptype): + #index = self.last_table_index + #self.tableWidget.insertRow(index) + #self.tableWidget.setItem(index, 0, QTableWidgetItem(plugin['name'])) + #self.tableWidget.setItem(index, 1, QTableWidgetItem(plugin['label'])) + #self.tableWidget.setItem(index, 2, QTableWidgetItem(plugin['maker'])) + #self.tableWidget.setItem(index, 3, QTableWidgetItem(plugin['id'])) + #self.tableWidget.setItem(index, 4, QTableWidgetItem(str(plugin['audio.ins']))) + #self.tableWidget.setItem(index, 5, QTableWidgetItem(str(plugin['audio.outs']))) + #self.tableWidget.setItem(index, 6, QTableWidgetItem(str(plugin['midi.ins']))) + #self.tableWidget.setItem(index, 7, QTableWidgetItem(str(plugin['midi.outs']))) + #self.tableWidget.setItem(index, 8, QTableWidgetItem(str(plugin['parameters.ins']))) + #self.tableWidget.setItem(index, 9, QTableWidgetItem(str(plugin['parameters.outs']))) + #self.tableWidget.setItem(index, 10, QTableWidgetItem(str(plugin['programs.total']))) + #self.tableWidget.setItem(index, 11, QTableWidgetItem(self.tr("Yes") if (plugin['hints'] & PLUGIN_HAS_GUI) else self.tr("No"))) + #self.tableWidget.setItem(index, 12, QTableWidgetItem(self.tr("Yes") if (plugin['hints'] & PLUGIN_IS_SYNTH) else self.tr("No"))) + #self.tableWidget.setItem(index, 13, QTableWidgetItem(ptype)) + #self.tableWidget.setItem(index, 14, QTableWidgetItem(plugin['binary'])) + #self.tableWidget.item(self.last_table_index, 0).plugin = plugin + #self.last_table_index += 1 + + @pyqtSlot() + def slot_add_plugin(self): + if (self.tableWidget.currentRow() >= 0): + self.plugin_to_add = self.tableWidget.item(self.tableWidget.currentRow(), 0).plugin + self.accept() + else: + self.reject() + + @pyqtSlot() + def slot_refresh_plugins(self): + lastLoadedPlugin = self.settings_db.value("Plugins/LastLoadedBinary", "", type=str) + if (lastLoadedPlugin): + lastLoadedPlugin = getShortFileName(lastLoadedPlugin) + ask = QMessageBox.question(self, self.tr("Warning"), self.tr("" + "There was an error while checking the plugin %s.\n" + "Do you want to blacklist it?" % (lastLoadedPlugin)), QMessageBox.Yes|QMessageBox.No, QMessageBox.Yes) + + if (ask == QMessageBox.Yes): + blacklist = toList(self.settings_db.value("Plugins/Blacklisted", [])) + blacklist.append(lastLoadedPlugin) + self.settings_db.setValue("Plugins/Blacklisted", blacklist) + + self.label.setText(self.tr("Looking for plugins...")) + PluginRefreshW(self).exec_() + + #self.reAddPlugins() + #self.parent().loadRDFs() + + @pyqtSlot() + def slot_maybe_show_filters(self): + self.showFilters(not self.frame.isVisible()) + + @pyqtSlot(int) + def slot_checkPlugin(self, row): + self.b_add.setEnabled(row >= 0) + + @pyqtSlot() + def slot_checkFilters(self): + text = self.lineEdit.text().lower() + hide_effects = not self.ch_effects.isChecked() + hide_instruments = not self.ch_instruments.isChecked() + hide_midi = not self.ch_midi.isChecked() + hide_other = not self.ch_other.isChecked() + hide_ladspa = not self.ch_ladspa.isChecked() + hide_dssi = not self.ch_dssi.isChecked() + hide_lv2 = not self.ch_lv2.isChecked() + hide_vst = not self.ch_vst.isChecked() + hide_sf2 = not self.ch_sf2.isChecked() + hide_non_gui = self.ch_gui.isChecked() + hide_non_stereo = self.ch_stereo.isChecked() + + #row_count = self.tableWidget.rowCount() + #for i in range(row_count): + #self.tableWidget.showRow(i) + #ains = self.tableWidget.item(i, 4).text().toInt()[0] + #aouts = self.tableWidget.item(i, 5).text().toInt()[0] + #mins = self.tableWidget.item(i, 6).text().toInt()[0] + #mouts = self.tableWidget.item(i, 7).text().toInt()[0] + #ptype = self.tableWidget.item(i, 13).text() + #is_synth = (self.tableWidget.item(i, 12).text() == self.tr("Yes")) + #is_effect = (ains > 0 and aouts > 0 and not is_synth) + #is_midi = (ains == 0 and aouts == 0 and mins > 0 and mouts > 0) + #is_sf2 = (ptype == "SF2") + #is_outro = (not (is_effect or is_synth or is_midi or is_sf2)) + #is_stereo = (ains == 2 and aouts == 2) or (is_synth and aouts == 2) + #has_gui = (self.tableWidget.item(i, 11).text() == self.tr("Yes")) + + #if (hide_effects and is_effect): + #self.tableWidget.hideRow(i) + #elif (hide_instruments and is_synth): + #self.tableWidget.hideRow(i) + #elif (hide_midi and is_midi): + #self.tableWidget.hideRow(i) + #elif (hide_outro and is_outro): + #self.tableWidget.hideRow(i) + #elif (hide_sf2 and is_sf2): + #self.tableWidget.hideRow(i) + #elif (hide_ladspa and ptype == "LADSPA"): + #self.tableWidget.hideRow(i) + #elif (hide_dssi and ptype == "DSSI"): + #self.tableWidget.hideRow(i) + #elif (hide_lv2 and ptype == "LV2"): + #self.tableWidget.hideRow(i) + #elif (hide_vst and ptype == "VST"): + #self.tableWidget.hideRow(i) + #elif (hide_winvst and ptype == "WinVST"): + #self.tableWidget.hideRow(i) + #elif (hide_non_gui and not has_gui): + #self.tableWidget.hideRow(i) + #elif (hide_non_stereo and not is_stereo): + #self.tableWidget.hideRow(i) + #elif (not text.isEmpty() and not ( + #text in self.tableWidget.item(i, 0).text().toLower() or + #text in self.tableWidget.item(i, 1).text().toLower() or + #text in self.tableWidget.item(i, 2).text().toLower() or + #text in self.tableWidget.item(i, 3).text().toLower() or + #text in self.tableWidget.item(i, 14).text().toLower()) + #): + #self.tableWidget.hideRow(i) + + def saveSettings(self): + self.settings.setValue("PluginDatabase/Geometry", self.saveGeometry()) + self.settings.setValue("PluginDatabase/TableGeometry", self.tableWidget.horizontalHeader().saveState()) + self.settings.setValue("PluginDatabase/ShowFilters", (self.tb_filters.arrowType() == Qt.UpArrow)) + self.settings.setValue("PluginDatabase/ShowEffects", self.ch_effects.isChecked()) + self.settings.setValue("PluginDatabase/ShowInstruments", self.ch_instruments.isChecked()) + self.settings.setValue("PluginDatabase/ShowMIDI", self.ch_midi.isChecked()) + self.settings.setValue("PluginDatabase/ShowOther", self.ch_other.isChecked()) + self.settings.setValue("PluginDatabase/ShowLADSPA", self.ch_ladspa.isChecked()) + self.settings.setValue("PluginDatabase/ShowDSSI", self.ch_dssi.isChecked()) + self.settings.setValue("PluginDatabase/ShowLV2", self.ch_lv2.isChecked()) + self.settings.setValue("PluginDatabase/ShowVST", self.ch_vst.isChecked()) + self.settings.setValue("PluginDatabase/ShowSF2", self.ch_sf2.isChecked()) + self.settings.setValue("PluginDatabase/ShowHasGUI", self.ch_gui.isChecked()) + self.settings.setValue("PluginDatabase/ShowStereoOnly", self.ch_stereo.isChecked()) + + def loadSettings(self): + self.restoreGeometry(self.settings.value("PluginDatabase/Geometry", "")) + self.tableWidget.horizontalHeader().restoreState(self.settings.value("PluginDatabase/TableGeometry", "")) + self.showFilters(self.settings.value("PluginDatabase/ShowFilters", False, type=bool)) + self.ch_effects.setChecked(self.settings.value("PluginDatabase/ShowEffects", True, type=bool)) + self.ch_instruments.setChecked(self.settings.value("PluginDatabase/ShowInstruments", True, type=bool)) + self.ch_midi.setChecked(self.settings.value("PluginDatabase/ShowMIDI", True, type=bool)) + self.ch_other.setChecked(self.settings.value("PluginDatabase/ShowOther", True, type=bool)) + self.ch_ladspa.setChecked(self.settings.value("PluginDatabase/ShowLADSPA", True, type=bool)) + self.ch_dssi.setChecked(self.settings.value("PluginDatabase/ShowDSSI", True, type=bool)) + self.ch_lv2.setChecked(self.settings.value("PluginDatabase/ShowLV2", True, type=bool)) + self.ch_vst.setChecked(self.settings.value("PluginDatabase/ShowVST", True, type=bool)) + self.ch_sf2.setChecked(self.settings.value("PluginDatabase/ShowSF2", True, type=bool)) + self.ch_gui.setChecked(self.settings.value("PluginDatabase/ShowHasGUI", False, type=bool)) + self.ch_stereo.setChecked(self.settings.value("PluginDatabase/ShowStereoOnly", False, type=bool)) + #self.reAddPlugins() + + def closeEvent(self, event): + self.saveSettings() + QDialog.closeEvent(self, event) + +# About Carla Dialog +class AboutW(QDialog, ui_carla_about.Ui_AboutW): + def __init__(self, parent=None): + super(AboutW, self).__init__(parent) + self.setupUi(self) + + self.l_about.setText(self.tr("" + "
Version %s" + "
Carla is a Multi-Plugin Host for JACK.
" + "
Copyright (C) 2011 falkTX
" + "
VST is a trademark of Steinberg Media Technologies GmbH." + "" % (VERSION))) + + host_osc_url = NativeHost.get_host_osc_url() + if (not host_osc_url): host_osc_url = "" + + self.le_osc_url.setText(host_osc_url) + + self.l_osc_cmds.setText("" + " /set_active \n" + " /set_drywet \n" + " /set_vol \n" + " /set_balance_left \n" + " /set_balance_right \n" + " /set_parameter \n" + " /set_program \n" + " /set_midi_program \n" + " /note_on \n" + " /note_off \n" + ) + + self.l_example.setText("/Carla/2/set_parameter_value 2 0.5") + self.l_example_help.setText("(as in this example, \"2\" is the plugin number)") + + self.l_ladspa.setText(self.tr("Everything! (Including LRDF)")) + self.l_dssi.setText(self.tr("Everything! (Including CustomData/Chunks)")) + self.l_lv2.setText(self.tr("About 95% complete (missing state files and other minor features).
" + "Implemented Feature/Extensions:" + "
    " + #"
  • http://lv2plug.in/ns/ext/cv-port
  • " + #"
  • http://lv2plug.in/ns/ext/data-access
  • " + #"
  • http://lv2plug.in/ns/ext/event
  • " + #"
  • http://lv2plug.in/ns/ext/host-info
  • " + #"
  • http://lv2plug.in/ns/ext/instance-access
  • " + #"
  • http://lv2plug.in/ns/ext/midi
  • " + #"
  • http://lv2plug.in/ns/ext/port-props
  • " + #"
  • http://lv2plug.in/ns/ext/presets
  • " + #"
  • http://lv2plug.in/ns/ext/state (not files)
  • " + #"
  • http://lv2plug.in/ns/ext/time
  • " + #"
  • http://lv2plug.in/ns/ext/ui-resize
  • " + #"
  • http://lv2plug.in/ns/ext/uri-map
  • " + #"
  • http://lv2plug.in/ns/ext/urid
  • " + #"
  • http://lv2plug.in/ns/extensions/units
  • " + #"
  • http://lv2plug.in/ns/extensions/ui
  • " + #"
  • http://ll-plugins.nongnu.org/lv2/ext/midimap
  • " + #"
  • http://home.gna.org/lv2dynparam/rtmempool/v1
  • " + #"
  • http://nedko.arnaudov.name/lv2/external_ui/
  • " + "
" + "(Note that Gtk2 UIs with instance-access will not work, such as IR.lv2)")) + self.l_vst.setText(self.tr("

About 75% complete (missing MIDI-Output and some minor stuff)

")) + +# Single Plugin Parameter +class PluginParameter(QWidget, ui_carla_parameter.Ui_PluginParameter): + def __init__(self, parent=None, pinfo=None, plugin_id=-1): + super(PluginParameter, self).__init__(parent) + self.setupUi(self) + + self.ptype = pinfo['type'] + self.parameter_id = pinfo['index'] + self.hints = pinfo['hints'] + + self.midi_cc = -1 + self.midi_channel = 1 + self.plugin_id = plugin_id + + self.add_MIDI_CCs_to_ComboBox() + + self.label.setText(pinfo['name']) + + if (self.ptype == PARAMETER_INPUT): + self.widget.set_minimum(pinfo['minimum']) + self.widget.set_maximum(pinfo['maximum']) + self.widget.set_default(pinfo['default']) + self.widget.set_value(pinfo['current'], False) + self.widget.set_label(pinfo['label']) + self.widget.set_step(pinfo['step']) + self.widget.set_step_small(pinfo['step_small']) + self.widget.set_step_large(pinfo['step_large']) + self.widget.set_scalepoints(pinfo['scalepoints'], (pinfo['hints'] & PARAMETER_USES_SCALEPOINTS)) + + if (not self.hints & PARAMETER_IS_AUTOMABLE): + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + if (not self.hints & PARAMETER_IS_ENABLED): + self.widget.set_read_only(True) + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + elif (self.ptype == PARAMETER_OUTPUT): + self.widget.set_minimum(pinfo['minimum']) + self.widget.set_maximum(pinfo['maximum']) + self.widget.set_value(pinfo['current'], False) + self.widget.set_label(pinfo['label']) + self.widget.set_read_only(True) + + if (not self.hints & PARAMETER_IS_AUTOMABLE): + self.combo.setEnabled(False) + self.sb_channel.setEnabled(False) + + else: + self.widget.setVisible(False) + self.combo.setVisible(False) + self.sb_channel.setVisible(False) + + self.set_parameter_midi_channel(pinfo['midi_channel']) + self.set_parameter_midi_cc(pinfo['midi_cc']) + + self.connect(self.widget, SIGNAL("valueChanged(float)"), self.handleValueChanged) + self.connect(self.sb_channel, SIGNAL("valueChanged(int)"), self.handleMidiChannelChanged) + self.connect(self.combo, SIGNAL("currentIndexChanged(int)"), self.handleMidiCcChanged) + + if force_parameters_style: + self.widget.force_plastique_style() + + self.widget.updateAll() + + def set_default_value(self, value): + self.widget.set_default(value) + + def set_parameter_value(self, value, send=True): + self.widget.set_value(value, send) + + def set_parameter_midi_channel(self, channel): + self.midi_channel = channel + self.sb_channel.setValue(channel-1) + + def set_parameter_midi_cc(self, cc_index): + self.midi_cc = cc_index + self.set_MIDI_CC_in_ComboBox(cc_index) + + def handleValueChanged(self, value): + self.emit(SIGNAL("valueChanged(int, float)"), self.parameter_id, value) + + def handleMidiChannelChanged(self, channel): + if (self.midi_channel != channel): + self.emit(SIGNAL("midiChannelChanged(int, int)"), self.parameter_id, channel) + self.midi_channel = channel + + def handleMidiCcChanged(self, cc_index): + if (cc_index <= 0): + midi_cc = -1 + else: + midi_cc_text = QStringStr(MIDI_CC_LIST[cc_index-1]).split(" ")[0] + midi_cc = int(midi_cc_text, 16) + + if (self.midi_cc != midi_cc): + self.emit(SIGNAL("midiCcChanged(int, int)"), self.parameter_id, midi_cc) + self.midi_cc = midi_cc + + def add_MIDI_CCs_to_ComboBox(self): + for MIDI_CC in MIDI_CC_LIST: + self.combo.addItem(MIDI_CC) + + def set_MIDI_CC_in_ComboBox(self, midi_cc): + for i in range(len(MIDI_CC_LIST)): + midi_cc_text = QStringStr(MIDI_CC_LIST[i]).split(" ")[0] + if (int(midi_cc_text, 16) == midi_cc): + cc_index = i + break + else: + cc_index = -1 + + cc_index += 1 + self.combo.setCurrentIndex(cc_index) + +# Plugin GUI +class PluginGUI(QDialog): + def __init__(self, parent, plugin_name, gui_data): + super(PluginGUI, self).__init__(parent) + + self.myLayout = QVBoxLayout(self) + self.myLayout.setContentsMargins(0, 0, 0, 0) + self.setLayout(self.myLayout) + + self.resizable = gui_data['resizable'] + self.set_new_size(gui_data['width'], gui_data['height']) + + if (not plugin_name): + plugin_name = "Plugin" + + self.setWindowTitle(plugin_name+" (GUI)") + + def set_new_size(self, width, height): + if (width < 30): + width = 30 + if (height < 30): + height = 30 + + if (self.resizable): + self.resize(width, height) + else: + self.setFixedSize(width, height) + + def hideEvent(self, event): + event.accept() + self.close() + +# Plugin Editor (Built-in) +class PluginEdit(QDialog, ui_carla_edit.Ui_PluginEdit): + def __init__(self, parent, plugin_id): + super(PluginEdit, self).__init__(parent) + self.setupUi(self) + + self.pinfo = None + self.ptype = PLUGIN_NONE + self.plugin_id = plugin_id + + self.parameter_count = 0 + self.parameter_list = [] + self.parameter_list_to_update = [] + + self.state_filename = None + self.cur_program_index = -1 + self.cur_midi_program_index = -1 + + self.tab_icon_off = QIcon(":/bitmaps/led_off.png") + self.tab_icon_on = QIcon(":/bitmaps/led_yellow.png") + self.tab_icon_count = 0 + self.tab_icon_timers = [] + + self.connect(self.b_save_state, SIGNAL("clicked()"), self.save_state) + self.connect(self.b_load_state, SIGNAL("clicked()"), self.load_state) + + self.connect(self.keyboard, SIGNAL("noteOn(int)"), self.handleNoteOn) + self.connect(self.keyboard, SIGNAL("noteOff(int)"), self.handleNoteOff) + + self.connect(self.keyboard, SIGNAL("notesOn()"), self.handleNotesOn) + self.connect(self.keyboard, SIGNAL("notesOff()"), self.handleNotesOff) + + self.connect(self.cb_programs, SIGNAL("currentIndexChanged(int)"), self.handleProgramIndexChanged) + self.connect(self.cb_midi_programs, SIGNAL("currentIndexChanged(int)"), self.handleMidiProgramIndexChanged) + + self.keyboard.setMode(self.keyboard.HORIZONTAL) + self.keyboard.setOctaves(6) + self.scrollArea.ensureVisible(self.keyboard.width()*1/5, 0) + self.scrollArea.setVisible(False) + + # TODO - not implemented yet + self.b_reload_program.setEnabled(False) + self.b_reload_midi_program.setEnabled(False) + + self.do_reload_all() + + self.TIMER_GUI_STUFF = self.startTimer(100) + + def do_update(self): + self.checkInputControlParameters() + self.checkOutputControlParameters() + + # Update current program text + if (self.cb_programs.count() > 0): + pindex = self.cb_programs.currentIndex() + pname = NativeHost.get_program_name(self.plugin_id, pindex) + if (not pname): pname = "" + self.cb_programs.setItemText(pindex, pname) + + # Update current midi program text + if (self.cb_midi_programs.count() > 0): + mpindex = self.cb_midi_programs.currentIndex() + mpname = NativeHost.get_midi_program_name(self.plugin_id, pindex) + if (not mpname): mpname = "" + self.cb_midi_programs.setItemText(pindex, mpname) + # FIXME - leave 001:001 alone + + def do_reload_all(self): + self.pinfo = NativeHost.get_plugin_info(self.plugin_id) + if (not self.pinfo['valid']): + self.pinfo["type"] = PLUGIN_NONE + self.pinfo["category"] = PLUGIN_CATEGORY_NONE + self.pinfo["hints"] = 0 + self.pinfo["binary"] = "" + self.pinfo["name"] = "(Unknown)" + self.pinfo["label"] = "" + self.pinfo["maker"] = "" + self.pinfo["copyright"] = "" + self.pinfo["unique_id"] = 0 + + # Save from null values + if not self.pinfo['name']: self.pinfo['name'] = "" + if not self.pinfo['label']: self.pinfo['label'] = "" + if not self.pinfo['maker']: self.pinfo['maker'] = "" + if not self.pinfo['copyright']: self.pinfo['copyright'] = "" + + self.do_reload_info() + self.do_reload_parameters() + self.do_reload_programs() + + def do_reload_info(self): + if (self.ptype == PLUGIN_NONE and self.pinfo['type'] in (PLUGIN_DSSI, PLUGIN_SF2)): + self.tab_programs.setCurrentIndex(1) + + self.ptype = self.pinfo['type'] + + real_plugin_name = NativeHost.get_real_plugin_name(self.plugin_id) + if not real_plugin_name: real_plugin_name = "" + + self.le_name.setText(real_plugin_name) + self.le_name.setToolTip(real_plugin_name) + self.le_label.setText(self.pinfo['label']) + self.le_label.setToolTip(self.pinfo['label']) + self.le_maker.setText(self.pinfo['maker']) + self.le_maker.setToolTip(self.pinfo['maker']) + self.le_copyright.setText(self.pinfo['copyright']) + self.le_copyright.setToolTip(self.pinfo['copyright']) + self.le_unique_id.setText(str(self.pinfo['unique_id'])) + self.le_unique_id.setToolTip(str(self.pinfo['unique_id'])) + + self.label_plugin.setText("\n%s\n" % (self.pinfo['name'])) + self.setWindowTitle(self.pinfo['name']) + + if (self.ptype == PLUGIN_LADSPA): + self.le_type.setText("LADSPA") + elif (self.ptype == PLUGIN_DSSI): + self.le_type.setText("DSSI") + elif (self.ptype == PLUGIN_LV2): + self.le_type.setText("LV2") + elif (self.ptype == PLUGIN_VST): + self.le_type.setText("VST") + elif (self.ptype == PLUGIN_WINVST): + self.le_type.setText("Windows VST") + elif (self.ptype == PLUGIN_SF2): + self.le_type.setText("SoundFont") + else: + self.le_type.setText(self.tr("Unknown")) + + audio_count = NativeHost.get_audio_port_count_info(self.plugin_id) + if (not audio_count['valid']): + audio_count['ins'] = 0 + audio_count['outs'] = 0 + audio_count['total'] = 0 + + midi_count = NativeHost.get_midi_port_count_info(self.plugin_id) + if (not midi_count['valid']): + midi_count['ins'] = 0 + midi_count['outs'] = 0 + midi_count['total'] = 0 + + param_count = NativeHost.get_parameter_count_info(self.plugin_id) + if (not param_count['valid']): + param_count['ins'] = 0 + param_count['outs'] = 0 + param_count['total'] = 0 + + self.le_ains.setText(str(audio_count['ins'])) + self.le_aouts.setText(str(audio_count['outs'])) + self.le_params.setText(str(param_count['ins'])) + self.le_couts.setText(str(param_count['outs'])) + self.le_is_synth.setText(self.tr("Yes") if (self.pinfo['hints'] & PLUGIN_IS_SYNTH) else (self.tr("No"))) + self.le_has_gui.setText(self.tr("Yes") if (self.pinfo['hints'] & PLUGIN_HAS_GUI) else (self.tr("No"))) + + self.scrollArea.setVisible(self.pinfo['hints'] & PLUGIN_IS_SYNTH or (midi_count['ins'] > 0 and midi_count['outs'] > 0)) + self.parent().recheck_hints(self.pinfo['hints']) + + def do_reload_parameters(self): + parameters_count = NativeHost.get_parameter_count(self.plugin_id) + + self.parameter_list = [] + self.parameter_list_to_update = [] + + self.tab_icon_count = 0 + self.tab_icon_timers = [] + + for i in range(self.tabWidget.count()): + if (i == 0): continue + self.tabWidget.widget(1).deleteLater() + self.tabWidget.removeTab(1) + + if (parameters_count <= 0): + pass + + elif (parameters_count <= MAX_PARAMETERS): + p_in = [] + p_in_tmp = [] + p_in_index = 0 + p_in_width = 0 + + p_out = [] + p_out_tmp = [] + p_out_index = 0 + p_out_width = 0 + + for i in range(parameters_count): + param_info = NativeHost.get_parameter_info(self.plugin_id, i) + param_data = NativeHost.get_parameter_data(self.plugin_id, i) + param_ranges = NativeHost.get_parameter_ranges(self.plugin_id, i) + + if not param_info['valid']: + continue + + # Save from null values + if not param_info['name']: param_info['name'] = "" + if not param_info['symbol']: param_info['symbol'] = "" + if not param_info['label']: param_info['label'] = "" + + parameter = { + 'type': param_data['type'], + 'hints': param_data['hints'], + 'name': param_info['name'], + 'label': param_info['label'], + 'scalepoints': [], + + 'index': param_data['index'], + 'default': param_ranges['def'], + 'minimum': param_ranges['min'], + 'maximum': param_ranges['max'], + 'step': param_ranges['step'], + 'step_small': param_ranges['step_small'], + 'step_large': param_ranges['step_large'], + 'midi_channel': param_data['midi_channel'], + 'midi_cc': param_data['midi_cc'], + + 'current': NativeHost.get_current_parameter_value(self.plugin_id, i) + } + + for j in range(param_info['scalepoint_count']): + scalepoint = NativeHost.get_scalepoint_info(self.plugin_id, i, j) + parameter['scalepoints'].append(scalepoint) + + # ----------------------------------------------------------------- + # Get width values, in packs of 10 + + if (parameter['type'] == PARAMETER_INPUT): + p_in_tmp.append(parameter) + p_in_width_tmp = QFontMetrics(self.font()).width(parameter['name']) + + if (p_in_width_tmp > p_in_width): + p_in_width = p_in_width_tmp + + if (len(p_in_tmp) == 10): + p_in.append((p_in_tmp, p_in_width)) + p_in_tmp = [] + p_in_index = 0 + p_in_width = 0 + else: + p_in_index += 1 + + elif (parameter['type'] == PARAMETER_OUTPUT): + p_out_tmp.append(parameter) + p_out_width_tmp = QFontMetrics(self.font()).width(parameter['name']) + + if (p_out_width_tmp > p_out_width): + p_out_width = p_out_width_tmp + + if (len(p_out_tmp) == 10): + p_out.append((p_out_tmp, p_out_width)) + p_out_tmp = [] + p_out_index = 0 + p_out_width = 0 + else: + p_out_index += 1 + + else: + # Final page width values + if (len(p_in_tmp) > 0 and len(p_in_tmp) < 10): + p_in.append((p_in_tmp, p_in_width)) + + if (len(p_out_tmp) > 0 and len(p_out_tmp) < 10): + p_out.append((p_out_tmp, p_out_width)) + + # ----------------------------------------------------------------- + # Create parameter widgets + + if (len(p_in) > 0): + self.createParameterWidgets(p_in, self.tr("Parameters"), PARAMETER_INPUT) + + if (len(p_out) > 0): + self.createParameterWidgets(p_out, self.tr("Outputs"), PARAMETER_OUTPUT) + + else: # > MAX_PARAMETERS + fake_name = "This plugin has too many parameters to display here!" + + p_fake = [] + p_fake_tmp = [] + p_fake_width = QFontMetrics(self.font()).width(fake_name) + + parameter = { + 'type': PARAMETER_UNKNOWN, + 'hints': 0, + 'name': fake_name, + 'label': "", + 'scalepoints': [], + + 'index': 0, + 'default': 0, + 'minimum': 0, + 'maximum': 0, + 'step': 0, + 'step_small': 0, + 'step_large': 0, + 'midi_channel': 0, + 'midi_cc': -1, + + 'current': 0.0 + } + + p_fake_tmp.append(parameter) + p_fake.append((p_fake_tmp, p_fake_width)) + + self.createParameterWidgets(p_fake, self.tr("Information"), PARAMETER_UNKNOWN) + + def do_reload_programs(self): + # Programs + old_current = self.cur_program_index + program_count = NativeHost.get_program_count(self.plugin_id) + + if (self.cb_programs.count() > 0): + self.cur_program_index = -1 + self.set_program(-1) + self.cb_programs.clear() + + if (program_count > 0): + self.cb_programs.setEnabled(True) + self.cur_program_index = 0 + + for i in range(program_count): + pname = NativeHost.get_program_name(self.plugin_id, i) + if (not pname): pname = "" + self.cb_programs.addItem(pname) + + if (old_current < 0): + old_current = 0 + + self.cur_program_index = old_current + self.set_program(old_current) + + else: + self.cb_programs.setEnabled(False) + + # MIDI Programs + old_midi_current = self.cur_midi_program_index + midi_program_count = NativeHost.get_midi_program_count(self.plugin_id) + + if (self.cb_midi_programs.count() > 0): + self.cur_midi_program_index = -1 + self.set_midi_program(-1) + self.cb_midi_programs.clear() + + if (midi_program_count > 0): + self.cb_midi_programs.setEnabled(True) + self.cur_midi_program_index = 0 + + for i in range(midi_program_count): + midip = NativeHost.get_midi_program_info(self.plugin_id, i) + if (not midip['label']): midip['label'] = "" + + bank = midip['bank'] + prog = midip['program'] + + if (bank < 10): + bank_str = "00%i" % (bank) + elif (bank < 100): + bank_str = "0%i" % (bank) + else: + bank_str = "%i" % (bank) + + if (prog < 10): + prog_str = "00%i" % (prog) + elif (prog < 100): + prog_str = "0%i" % (prog) + else: + prog_str = "%i" % (prog) + + self.cb_midi_programs.addItem("%s:%s - %s" % (bank_str, prog_str, midip['label'])) + + if (old_midi_current < 0): + old_midi_current = 0 + + self.cur_midi_program_index = old_midi_current + self.set_midi_program(old_midi_current) + + else: + self.cb_midi_programs.setEnabled(False) + + def set_parameter_value(self, parameter_id, value): + if (parameter_id not in self.parameter_list_to_update): + self.parameter_list_to_update.append(parameter_id) + + def set_parameter_midi_channel(self, parameter_id, channel): + for parameter in self.parameter_list: + ptype, pid, pwidget = parameter + if (parameter_id == pid): + pwidget.set_parameter_midi_channel(channel) + break + + def set_parameter_midi_cc(self, parameter_id, midi_cc): + for parameter in self.parameter_list: + ptype, pid, pwidget = parameter + if (parameter_id == pid): + pwidget.set_parameter_midi_cc(midi_cc) + break + + def set_program(self, program_id): + self.cur_program_index = program_id + self.cb_programs.setCurrentIndex(program_id) + QTimer.singleShot(0, self.checkInputControlParameters) + + def set_midi_program(self, midi_program_id): + self.cur_midi_program_index = midi_program_id + self.cb_midi_programs.setCurrentIndex(midi_program_id) + QTimer.singleShot(0, self.checkInputControlParameters) + + def handleParameterValueChanged(self, parameter_id, value): + NativeHost.set_parameter_value(self.plugin_id, parameter_id, value) + + def handleParameterMidiChannelChanged(self, parameter_id, channel): + NativeHost.set_parameter_midi_channel(self.plugin_id, parameter_id, channel-1) + + def handleParameterMidiCcChanged(self, parameter_id, cc_index): + NativeHost.set_parameter_midi_cc(self.plugin_id, parameter_id, cc_index) + + def handleProgramIndexChanged(self, index): + if (self.cur_program_index != index): + NativeHost.set_program(self.plugin_id, index) + QTimer.singleShot(0, self.checkInputControlParameters) + self.cur_program_index = index + + def handleMidiProgramIndexChanged(self, index): + if (self.cur_midi_program_index != index): + NativeHost.set_midi_program(self.plugin_id, index) + QTimer.singleShot(0, self.checkInputControlParameters) + self.cur_midi_program_index = index + + def handleNoteOn(self, note): + NativeHost.send_midi_note(self.plugin_id, True, note, 100) + + def handleNoteOff(self, note): + NativeHost.send_midi_note(self.plugin_id, False, note, 100) + + def handleNotesOn(self): + self.parent().led_midi.setChecked(True) + + def handleNotesOff(self): + self.parent().led_midi.setChecked(False) + + def save_state(self): + # TODO - LV2 and VST native formats + if (self.state_filename == None): + file_filter = self.tr("Carla State File (*.carxs)") + filename_try = QFileDialog.getSaveFileName(self, self.tr("Save Carla State File"), filter=file_filter) + + if (not filename_try.isEmpty()): + self.state_filename = QStringStr(filename_try) + self.save_state_InternalFormat() + else: + self.state_filename = None + + else: + ask_try = QMessageBox.question(self, self.tr("Overwrite?"), self.tr("Overwrite previously created file?"), QMessageBox.Ok|QMessageBox.Cancel) + + if (ask_try == QMessageBox.Ok): + self.save_state_InternalFormat() + else: + self.state_filename = None + self.saveState() + + def load_state(self): + # TODO - LV2 and VST native formats + file_filter = self.tr("Carla State File (*.carxs)") + filename_try = QFileDialog.getOpenFileName(self, self.tr("Open Carla State File"), filter=file_filter) + + if (not filename_try.isEmpty()): + self.state_filename = QStringStr(filename_try) + self.load_state_InternalFormat() + + def save_state_InternalFormat(self): + content = ("\n" + "\n" + "\n") % (VERSION) + + content += self.parent().getSaveXMLContent() + + content += "\n" + + if (open(self.state_filename, "w").write(content)): + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to save state file")) + + def load_state_InternalFormat(self): + state_read = open(self.state_filename, "r").read() + + if not state_read: + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to load state file")) + return + + xml = QDomDocument() + xml.setContent(state_read) + + xml_node = xml.documentElement() + if (xml_node.tagName() != "CARLA-PRESET"): + QMessageBox.critical(self, self.tr("Error"), self.tr("Not a valid Carla state file")) + return + + x_save_state_dict = getStateSaveDict(xml_node) + + # TODO - verify plugin + + self.parent().load_save_state_dict(x_save_state_dict) + + def createParameterWidgets(self, p_list_full, tab_name, ptype): + for i in range(len(p_list_full)): + p_list = p_list_full[i][0] + width = p_list_full[i][1] + + if (len(p_list) > 0): + container = QWidget(self.tabWidget) + layout = QVBoxLayout(container) + container.setLayout(layout) + + for j in range(len(p_list)): + pwidget = PluginParameter(container, p_list[j], self.plugin_id) + pwidget.label.setMinimumWidth(width) + pwidget.label.setMaximumWidth(width) + pwidget.tab_index = self.tabWidget.count() + layout.addWidget(pwidget) + + self.parameter_list.append((ptype, p_list[j]['index'], pwidget)) + + if (ptype == PARAMETER_INPUT): + self.connect(pwidget, SIGNAL("valueChanged(int, float)"), self.handleParameterValueChanged) + + self.connect(pwidget, SIGNAL("midiChannelChanged(int, int)"), self.handleParameterMidiChannelChanged) + self.connect(pwidget, SIGNAL("midiCcChanged(int, int)"), self.handleParameterMidiCcChanged) + + layout.addStretch() + + self.tabWidget.addTab(container, "%s (%i)" % (tab_name, i+1)) + + if (ptype == PARAMETER_INPUT): + self.tabWidget.setTabIcon(pwidget.tab_index, self.tab_icon_off) + self.tab_icon_timers.append(ICON_STATE_OFF) + else: + self.tab_icon_timers.append(None) + + def animateTab(self, index): + if (self.tab_icon_timers[index-1] == None): + self.tabWidget.setTabIcon(index, self.tab_icon_on) + self.tab_icon_timers[index-1] = ICON_STATE_ON + + def checkInputControlParameters(self): + for parameter in self.parameter_list: + parameter_type = parameter[0] + if (parameter_type == PARAMETER_INPUT): + parameter_id = parameter[1] + parameter_widget = parameter[2] + parameter_widget.set_default_value(NativeHost.get_default_parameter_value(self.plugin_id, parameter_id)) + parameter_widget.set_parameter_value(NativeHost.get_current_parameter_value(self.plugin_id, parameter_id), False) + + def checkOutputControlParameters(self): + for parameter in self.parameter_list: + parameter_type = parameter[0] + if (parameter_type == PARAMETER_OUTPUT): + parameter_id = parameter[1] + parameter_widget = parameter[2] + parameter_widget.set_parameter_value(NativeHost.get_current_parameter_value(self.plugin_id, parameter_id), False) + + def checkTabIcons(self): + for i in range(len(self.tab_icon_timers)): + if (self.tab_icon_timers[i] == ICON_STATE_ON): + self.tab_icon_timers[i] = ICON_STATE_WAIT + elif (self.tab_icon_timers[i] == ICON_STATE_WAIT): + self.tab_icon_timers[i] = ICON_STATE_OFF + elif (self.tab_icon_timers[i] == ICON_STATE_OFF): + self.tabWidget.setTabIcon(i+1, self.tab_icon_off) + self.tab_icon_timers[i] = None + + def checkUpdatedParameters(self): + for parameter_id in self.parameter_list_to_update: + value = NativeHost.get_current_parameter_value(self.plugin_id, parameter_id) + + for parameter in self.parameter_list: + x_parameter_type = parameter[0] + x_parameter_id = parameter[1] + x_parameter_widget = parameter[2] + + if (x_parameter_id == parameter_id): + x_parameter_widget.set_parameter_value(value, False) + + if (x_parameter_type == PARAMETER_INPUT): + self.animateTab(x_parameter_widget.tab_index) + + break + + for i in self.parameter_list_to_update: + self.parameter_list_to_update.pop(0) + + def timerEvent(self, event): + if (event.timerId() == self.TIMER_GUI_STUFF): + self.checkOutputControlParameters() + self.checkTabIcons() + self.checkUpdatedParameters() + return QDialog.timerEvent(self, event) + +# (New) Plugin Widget +class PluginWidget(QFrame, ui_carla_plugin.Ui_PluginWidget): + def __init__(self, parent, plugin_id): + super(PluginWidget, self).__init__(parent) + self.setupUi(self) + + self.plugin_id = plugin_id + + self.params_total = 0 + self.parameter_activity_timer = None + + # Fake effect + self.color_1 = QColor( 0, 0, 0, 220) + self.color_2 = QColor( 0, 0, 0, 170) + self.color_3 = QColor( 7, 7, 7, 250) + self.color_4 = QColor(14, 14, 14, 255) + + self.led_enable.setColor(self.led_enable.BIG_RED) + self.led_enable.setChecked(False) + + self.led_control.setColor(self.led_control.YELLOW) + self.led_control.setEnabled(False) + + self.led_midi.setColor(self.led_midi.RED) + self.led_midi.setEnabled(False) + + self.led_audio_in.setColor(self.led_audio_in.GREEN) + self.led_audio_in.setEnabled(False) + + self.led_audio_out.setColor(self.led_audio_out.BLUE) + self.led_audio_out.setEnabled(False) + + self.dial_drywet.setPixmap(1) + self.dial_vol.setPixmap(2) + self.dial_b_left.setPixmap(1) + self.dial_b_right.setPixmap(1) + + self.dial_drywet.setLabel("Wet") + self.dial_vol.setLabel("Vol") + self.dial_b_left.setLabel("L") + self.dial_b_right.setLabel("R") + + self.peak_in.setColor(self.peak_in.GREEN) + self.peak_in.setOrientation(self.peak_in.HORIZONTAL) + + self.peak_out.setColor(self.peak_in.BLUE) + self.peak_out.setOrientation(self.peak_out.HORIZONTAL) + + audio_count = NativeHost.get_audio_port_count_info(self.plugin_id) + if (not audio_count['valid']): + audio_count['ins'] = 0 + audio_count['outs'] = 0 + audio_count['total'] = 0 + + self.peaks_in = audio_count['ins'] + self.peaks_out = audio_count['outs'] + + if (self.peaks_in > 2): + self.peaks_in = 2 + + if (self.peaks_out > 2): + self.peaks_out = 2 + + self.peak_in.setChannels(self.peaks_in) + self.peak_out.setChannels(self.peaks_out) + + self.pinfo = NativeHost.get_plugin_info(self.plugin_id) + if (not self.pinfo['valid']): + self.pinfo["type"] = PLUGIN_NONE + self.pinfo["category"] = PLUGIN_CATEGORY_NONE + self.pinfo["hints"] = 0 + self.pinfo["binary"] = "" + self.pinfo["name"] = "(Unknown)" + self.pinfo["label"] = "" + self.pinfo["maker"] = "" + self.pinfo["copyright"] = "" + self.pinfo["unique_id"] = 0 + + # Save from null values + if not self.pinfo['name']: self.pinfo['name'] = "" + if not self.pinfo['label']: self.pinfo['label'] = "" + if not self.pinfo['maker']: self.pinfo['maker'] = "" + if not self.pinfo['copyright']: self.pinfo['copyright'] = "" + + # Set widget page + if (self.pinfo['type'] == PLUGIN_NONE or audio_count['total'] == 0): + self.stackedWidget.setCurrentIndex(1) + + self.label_name.setText(self.pinfo['name']) + + self.dial_drywet.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_DRYWET) + self.dial_vol.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_VOL) + self.dial_b_left.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_BALANCE) + self.dial_b_right.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_BALANCE) + self.b_gui.setEnabled(self.pinfo['hints'] & PLUGIN_HAS_GUI) + + # Colorify + if (self.pinfo['category'] == PLUGIN_CATEGORY_SYNTH): + self.set_plugin_widget_color(PALETTE_COLOR_WHITE) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_DELAY): + self.set_plugin_widget_color(PALETTE_COLOR_ORANGE) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_EQ): + self.set_plugin_widget_color(PALETTE_COLOR_GREEN) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_FILTER): + self.set_plugin_widget_color(PALETTE_COLOR_BLUE) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_DYNAMICS): + self.set_plugin_widget_color(PALETTE_COLOR_PINK) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_MODULATOR): + self.set_plugin_widget_color(PALETTE_COLOR_RED) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_UTILITY): + self.set_plugin_widget_color(PALETTE_COLOR_YELLOW) + elif (self.pinfo['category'] == PLUGIN_CATEGORY_OUTRO): + self.set_plugin_widget_color(PALETTE_COLOR_BROWN) + else: + self.set_plugin_widget_color(PALETTE_COLOR_NONE) + + if (self.pinfo['hints'] & PLUGIN_IS_SYNTH): + self.led_audio_in.setVisible(False) + else: + self.led_midi.setVisible(False) + + self.edit_dialog = PluginEdit(self, self.plugin_id) + self.edit_dialog.hide() + self.edit_dialog_geometry = QVariant(self.edit_dialog.saveGeometry()) + + if (self.pinfo['hints'] & PLUGIN_HAS_GUI): + gui_data = NativeHost.get_gui_data(self.plugin_id) + self.gui_dialog_type = gui_data['type'] + + if (self.gui_dialog_type in (GUI_INTERNAL_QT4, GUI_INTERNAL_X11)): + self.gui_dialog = PluginGUI(self, self.pinfo['name'], gui_data) + self.gui_dialog.hide() + self.gui_dialog_geometry = QVariant(self.gui_dialog.saveGeometry()) + self.connect(self.gui_dialog, SIGNAL("finished(int)"), self.gui_dialog_closed) + + NativeHost.set_gui_data(self.plugin_id, Display, unwrapinstance(self.gui_dialog)) + + elif (self.gui_dialog_type in (GUI_EXTERNAL_OSC, GUI_EXTERNAL_LV2)): + self.gui_dialog = None + + else: + self.gui_dialog = None + self.b_gui.setEnabled(False) + + else: + self.gui_dialog = None + self.gui_dialog_type = GUI_NONE + + self.connect(self.led_enable, SIGNAL("clicked(bool)"), self.set_active) + self.connect(self.dial_drywet, SIGNAL("sliderMoved(int)"), self.set_drywet) + self.connect(self.dial_vol, SIGNAL("sliderMoved(int)"), self.set_vol) + self.connect(self.dial_b_left, SIGNAL("sliderMoved(int)"), self.set_balance_left) + self.connect(self.dial_b_right, SIGNAL("sliderMoved(int)"), self.set_balance_right) + self.connect(self.b_gui, SIGNAL("clicked(bool)"), self.handleShowGUI) + self.connect(self.b_edit, SIGNAL("clicked(bool)"), self.handleEdit) + self.connect(self.b_remove, SIGNAL("clicked()"), self.handleRemove) + + self.connect(self.dial_drywet, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomDialMenu) + self.connect(self.dial_vol, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomDialMenu) + self.connect(self.dial_b_left, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomDialMenu) + self.connect(self.dial_b_right, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomDialMenu) + + self.connect(self.edit_dialog, SIGNAL("finished(int)"), self.edit_dialog_closed) + + #self.check_gui_stuff() + self.TIMER_GUI_STUFF = self.startTimer(50) + + def recheck_hints(self, hints): + self.pinfo['hints'] = hints + self.dial_drywet.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_DRYWET) + self.dial_vol.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_VOL) + self.dial_b_left.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_BALANCE) + self.dial_b_right.setEnabled(self.pinfo['hints'] & PLUGIN_CAN_BALANCE) + self.b_gui.setEnabled(self.pinfo['hints'] & PLUGIN_HAS_GUI) + + def set_active(self, active, gui_send=False, callback_send=True): + if (gui_send): self.led_enable.setChecked(active) + if (callback_send): NativeHost.set_active(self.plugin_id, active) + + def set_drywet(self, value, gui_send=False, callback_send=True): + if (gui_send): self.dial_drywet.setValue(value) + if (callback_send): NativeHost.set_drywet(self.plugin_id, float(value)/1000) + + message = self.tr("Output dry/wet (%1%)").arg(value/10) + self.dial_drywet.setStatusTip(message) + gui.statusBar().showMessage(message) + + def set_vol(self, value, gui_send=False, callback_send=True): + if (gui_send): self.dial_vol.setValue(value) + if (callback_send): NativeHost.set_vol(self.plugin_id, float(value)/1000) + + message = self.tr("Output volume (%1%)").arg(value/10) + self.dial_vol.setStatusTip(message) + gui.statusBar().showMessage(message) + + def set_balance_left(self, value, gui_send=False, callback_send=True): + if (gui_send): self.dial_b_left.setValue(value) + if (callback_send): NativeHost.set_balance_left(self.plugin_id, float(value)/1000) + + if (value == 0): + message = self.tr("Left Panning (Center)") + elif (value < 0): + message = self.tr("Left Panning (%1% Left)").arg(-value/10) + else: + message = self.tr("Left Panning (%1% Right)").arg(value/10) + + self.dial_b_left.setStatusTip(message) + gui.statusBar().showMessage(message) + + def set_balance_right(self, value, gui_send=False, callback_send=True): + if (gui_send): self.dial_b_right.setValue(value) + if (callback_send): NativeHost.set_balance_right(self.plugin_id, float(value)/1000) + + if (value == 0): + message = self.tr("Right Panning (Center)") + elif (value < 0): + message = self.tr("Right Panning (%1%) Left").arg(-value/10) + else: + message = self.tr("Right Panning (%1% Right)").arg(value/10) + + self.dial_b_right.setStatusTip(message) + gui.statusBar().showMessage(message) + + def gui_dialog_closed(self): + self.b_gui.setChecked(False) + + def edit_dialog_closed(self): + self.b_edit.setChecked(False) + + def check_gui_stuff(self): + # Input peaks + if (self.peaks_in > 0): + if (self.peaks_in > 1): + peak1 = NativeHost.get_input_peak_value(self.plugin_id, 1) + peak2 = NativeHost.get_input_peak_value(self.plugin_id, 2) + self.peak_in.displayMeter(1, peak1) + self.peak_in.displayMeter(2, peak2) + self.led_audio_in.setChecked((peak1 != 0.0 or peak2 != 0.0)) + + else: + peak = NativeHost.get_input_peak_value(self.plugin_id, 1) + self.peak_in.displayMeter(1, peak) + self.led_audio_in.setChecked((peak != 0.0)) + + # Output peaks + if (self.peaks_out > 0): + if (self.peaks_out > 1): + peak1 = NativeHost.get_output_peak_value(self.plugin_id, 1) + peak2 = NativeHost.get_output_peak_value(self.plugin_id, 2) + self.peak_out.displayMeter(1, peak1) + self.peak_out.displayMeter(2, peak2) + self.led_audio_out.setChecked((peak1 != 0.0 or peak2 != 0.0)) + + else: + peak = NativeHost.get_output_peak_value(self.plugin_id, 1) + self.peak_out.displayMeter(1, peak) + self.led_audio_out.setChecked((peak != 0.0)) + + # Parameter Activity LED + if (self.parameter_activity_timer == ICON_STATE_ON): + self.led_control.setChecked(True) + self.parameter_activity_timer = ICON_STATE_WAIT + elif (self.parameter_activity_timer == ICON_STATE_WAIT): + self.parameter_activity_timer = ICON_STATE_OFF + elif (self.parameter_activity_timer == ICON_STATE_OFF): + self.led_control.setChecked(False) + self.parameter_activity_timer = None + + def set_plugin_widget_color(self, color): + if (color == PALETTE_COLOR_WHITE): + r = 110 + g = 110 + b = 110 + texture = 7 + elif (color == PALETTE_COLOR_RED): + r = 110 + g = 15 + b = 15 + texture = 3 + elif (color == PALETTE_COLOR_GREEN): + r = 15 + g = 110 + b = 15 + w = 15 + texture = 6 + elif (color == PALETTE_COLOR_BLUE): + r = 15 + g = 15 + b = 110 + texture = 4 + elif (color == PALETTE_COLOR_YELLOW): + r = 110 + g = 110 + b = 15 + texture = 2 + elif (color == PALETTE_COLOR_ORANGE): + r = 180 + g = 110 + b = 15 + texture = 5 + elif (color == PALETTE_COLOR_BROWN): + r = 110 + g = 35 + b = 15 + texture = 1 + elif (color == PALETTE_COLOR_PINK): + r = 110 + g = 15 + b = 110 + texture = 8 + else: + r = 35 + g = 35 + b = 35 + texture = 4 + + self.setStyleSheet(""" + QFrame#PluginWidget { + background-image: url(:/bitmaps/textures/metal_%i-512px.jpg); + background-repeat: repeat-x; + background-position: top left; + } + QLabel#label_name { + color: white; + } + QFrame#frame_name { + background-image: url(:/bitmaps/glass.png); + background-color: rgba(%i, %i, %i); + border: 2px outset; + border-color: rgb(%i, %i, %i); + } + QFrame#frame_controls { + background-image: url(:/bitmaps/glass2.png); + background-color: rgb(30, 30, 30); + border: 2px outset; + border-color: rgb(30, 30, 30); + } + QFrame#frame_peaks { + background-color: rgba(30, 30, 30, 200); + border: 2px inset; + border-color: rgba(30, 30, 30, 255); + } + """ % (texture, r, g, b, r, g, b)) + + def handleShowGUI(self, show): + if (self.gui_dialog_type in (GUI_INTERNAL_QT4, GUI_INTERNAL_X11)): + if (show): + self.gui_dialog.restoreGeometry(self.gui_dialog_geometry.toByteArray()) + else: + self.gui_dialog_geometry = QVariant(self.gui_dialog.saveGeometry()) + self.gui_dialog.setVisible(show) + NativeHost.show_gui(self.plugin_id, show) + + def handleEdit(self, show): + if (show): + self.edit_dialog.restoreGeometry(self.edit_dialog_geometry.toByteArray()) + else: + self.edit_dialog_geometry = QVariant(self.edit_dialog.saveGeometry()) + self.edit_dialog.setVisible(show) + + def handleRemove(self): + gui.func_remove_plugin(self.plugin_id, True) + + def showCustomDialMenu(self, pos): + dial_name = QStringStr(self.sender().objectName()) + if (dial_name == "dial_drywet"): + minimum = 0 + maximum = 100 + default = 100 + label = "Dry/Wet" + elif (dial_name == "dial_vol"): + minimum = 0 + maximum = 127 + default = 100 + label = "Volume" + elif (dial_name == "dial_b_left"): + minimum = -100 + maximum = 100 + default = -100 + label = "Balance-Left" + elif (dial_name == "dial_b_right"): + minimum = -100 + maximum = 100 + default = 100 + label = "Balance-Right" + else: + minimum = 0 + maximum = 100 + default = 100 + label = "Unknown" + + current = self.sender().value()/10 + + menu = QMenu(self) + act_x_reset = menu.addAction(self.tr("Reset (%1%)").arg(default)) + menu.addSeparator() + act_x_min = menu.addAction(self.tr("Set to Minimum (%1%)").arg(minimum)) + act_x_cen = menu.addAction(self.tr("Set to Center")) + act_x_max = menu.addAction(self.tr("Set to Maximum (%1%)").arg(maximum)) + menu.addSeparator() + act_x_set = menu.addAction(self.tr("Set value...")) + + if (label not in ("Balance-Left", "Balance-Right")): + menu.removeAction(act_x_cen) + + act_x_sel = menu.exec_(QCursor.pos()) + + if (act_x_sel == act_x_set): + value_try = QInputDialog.getInteger(self, self.tr("Set value"), label, current, minimum, maximum, 1) + if (value_try[1]): + value = value_try[0]*10 + else: + value = None + + elif (act_x_sel == act_x_min): + value = minimum*10 + elif (act_x_sel == act_x_max): + value = maximum*10 + elif (act_x_sel == act_x_reset): + value = default*10 + elif (act_x_sel == act_x_cen): + value = 0 + else: + value = None + + if (value != None): + if (label == "Dry/Wet"): + self.set_drywet(value, True, True) + elif (label == "Volume"): + self.set_vol(value, True, True) + elif (label == "Balance-Left"): + self.set_balance_left(value, True, True) + elif (label == "Balance-Right"): + self.set_balance_right(value, True, True) + + def getSaveXMLContent(self): + NativeHost.prepare_for_save(self.plugin_id) + + if (self.pinfo['type'] == PLUGIN_LADSPA): + type_str = "LADSPA" + elif (self.pinfo['type'] == PLUGIN_DSSI): + type_str = "DSSI" + elif (self.pinfo['type'] == PLUGIN_LV2): + type_str = "LV2" + elif (self.pinfo['type'] == PLUGIN_VST): + type_str = "VST" + elif (self.pinfo['type'] == PLUGIN_WINVST): + type_str = "Windows VST" + elif (self.pinfo['type'] == PLUGIN_SF2): + type_str = "SoundFont" + else: + type_str = "Unknown" + + real_plugin_name = NativeHost.get_real_plugin_name(self.plugin_id) + if not real_plugin_name: real_plugin_name = "" + + x_save_state_dict = deepcopy(save_state_dict) + + # ---------------------------- + # Basic info + + x_save_state_dict['Type'] = type_str + x_save_state_dict['Name'] = real_plugin_name + x_save_state_dict['Label'] = self.pinfo['label'] + x_save_state_dict['Binary'] = self.pinfo['binary'] + x_save_state_dict['UniqueID'] = self.pinfo['unique_id'] + + # ---------------------------- + # Internals + + x_save_state_dict['Active'] = self.led_enable.isChecked() + x_save_state_dict['DryWet'] = float(self.dial_drywet.value())/1000 + x_save_state_dict['Vol'] = float(self.dial_vol.value())/1000 + x_save_state_dict['Balance-Left'] = float(self.dial_b_left.value())/1000 + x_save_state_dict['Balance-Right'] = float(self.dial_b_right.value())/1000 + + # ---------------------------- + # Programs + + if (self.edit_dialog.cb_programs.currentIndex() >= 0): + x_save_state_dict['ProgramIndex'] = self.edit_dialog.cb_programs.currentIndex() + x_save_state_dict['ProgramName'] = QStringStr(self.edit_dialog.cb_programs.currentText()) + + # ---------------------------- + # MIDI Programs + + if (self.edit_dialog.cb_midi_programs.currentIndex() >= 0): + midi_program_info = NativeHost.get_midi_program_info(self.plugin_id, self.edit_dialog.cb_midi_programs.currentIndex()) + x_save_state_dict['MidiBank'] = midi_program_info['bank'] + x_save_state_dict['MidiProgram'] = midi_program_info['program'] + + # ---------------------------- + # Parameters + + parameter_count = NativeHost.get_parameter_count(self.plugin_id) + + for i in range(parameter_count): + parameter_info = NativeHost.get_parameter_info(self.plugin_id, i) + parameter_data = NativeHost.get_parameter_data(self.plugin_id, i) + + if (not parameter_info['valid'] or parameter_data['type'] != PARAMETER_INPUT): + continue + + # Save from null values + if not parameter_info['name']: parameter_info['name'] = "" + if not parameter_info['symbol']: parameter_info['symbol'] = "" + if not parameter_info['label']: parameter_info['label'] = "" + + x_save_state_parameter = deepcopy(save_state_parameter) + + x_save_state_parameter['index'] = parameter_data['index'] + x_save_state_parameter['rindex'] = parameter_data['rindex'] + x_save_state_parameter['name'] = parameter_info['name'] + x_save_state_parameter['symbol'] = parameter_info['symbol'] + x_save_state_parameter['value'] = NativeHost.get_current_parameter_value(self.plugin_id, parameter_data['index']) + x_save_state_parameter['midi_channel'] = parameter_data['midi_channel']+1 + x_save_state_parameter['midi_cc'] = parameter_data['midi_cc'] + + if (parameter_data['hints'] & PARAMETER_USES_SAMPLERATE): + x_save_state_parameter['value'] /= NativeHost.get_sample_rate() + + x_save_state_dict['Parameters'].append(x_save_state_parameter) + + # ---------------------------- + # Custom Data + + custom_data_count = NativeHost.get_custom_data_count(self.plugin_id) + + for i in range(custom_data_count): + custom_data = NativeHost.get_custom_data(self.plugin_id, i) + + if (custom_data['type'] == CUSTOM_DATA_INVALID): + continue + + # Save from null values + if not custom_data['key']: custom_data['key'] = "" + if not custom_data['value']: custom_data['value'] = "" + + x_save_state_custom_data = deepcopy(save_state_custom_data) + + x_save_state_custom_data['type'] = custom_data['type'] + x_save_state_custom_data['key'] = custom_data['key'] + x_save_state_custom_data['value'] = custom_data['value'] + + x_save_state_dict['CustomData'].append(x_save_state_custom_data) + + # ---------------------------- + # Chunk + + if (self.pinfo['hints'] & PLUGIN_USES_CHUNKS): + chunk_data = NativeHost.get_chunk_data(self.plugin_id) + if chunk_data: + x_save_state_dict['Chunk'] = chunk_data + + # ---------------------------- + # Generate XML for this plugin + + content = "" + + content += " \n" + content += " %s\n" % (x_save_state_dict['Type']) + content += " %s\n" % (x_save_state_dict['Name']) + content += " \n" % (x_save_state_dict['Label']) + content += " %s\n" % (x_save_state_dict['Binary']) + content += " %li\n" % (x_save_state_dict['UniqueID']) + content += " \n" + + content += "\n" + content += " \n" + content += " %s\n" % ("Yes" if x_save_state_dict['Active'] else "No") + content += " %f\n" % (x_save_state_dict['DryWet']) + content += " %f\n" % (x_save_state_dict['Vol']) + content += " %f\n" % (x_save_state_dict['Balance-Left']) + content += " %f\n" % (x_save_state_dict['Balance-Right']) + + for parameter in x_save_state_dict['Parameters']: + content += "\n" + content += " \n" + content += " %i\n" % (parameter['index']) + content += " %i\n" % (parameter['rindex']) + content += " %s\n" % (parameter['name']) + content += " %s\n" % (parameter['symbol']) + content += " %f\n" % (parameter['value']) + content += " %i\n" % (parameter['midi_channel']) + content += " %i\n" % (parameter['midi_cc']) + content += " \n" + + if (x_save_state_dict['ProgramIndex'] >= 0): + content += "\n" + content += " %i\n" % (x_save_state_dict['ProgramIndex']) + content += " %s\n" % (x_save_state_dict['ProgramName']) + + if (x_save_state_dict['MidiBank'] >= 0 and x_save_state_dict['MidiProgram'] >= 0): + content += "\n" + content += " %i\n" % (x_save_state_dict['MidiBank']) + content += " %i\n" % (x_save_state_dict['MidiProgram']) + + for custom_data in x_save_state_dict['CustomData']: + if (not custom_data['value'].endswith("\n")): + custom_data['value'] += "\n" + content += "\n" + content += " \n" + content += " %i\n" % (custom_data['type']) + content += " %s\n" % (custom_data['key']) + content += " \n" + content += "%s" % (Qt.escape(custom_data['value'])) + content += " \n" + content += " \n" + + if (x_save_state_dict['Chunk']): + if (not x_save_state_dict['Chunk'].endswith("\n")): + x_save_state_dict['Chunk'] += "\n" + content += "\n" + content += " \n" + content += "%s" % (x_save_state_dict['Chunk']) + content += " \n" + + content += " \n" + + return content + + def load_save_state_dict(self, content): + + # Part 1 - set custom data + for custom_data in content['CustomData']: + NativeHost.set_custom_data(self.plugin_id, custom_data['type'], custom_data['key'], custom_data['value']) + + # Part 2 - set program (carefully) + program_id = -1 + program_count = NativeHost.get_program_count(self.plugin_id) + + if (content['ProgramName']): + test_pname = NativeHost.get_program_name(self.plugin_id, content['ProgramIndex']) + + if (content['ProgramName'] == test_pname): + program_id = content['ProgramIndex'] + else: + for i in range(program_count): + new_test_pname = NativeHost.get_program_name(self.plugin_id, i) + if (content['ProgramName'] == new_test_pname): + program_id = i + break + else: + if (content['ProgramIndex'] < program_count): + program_id = content['ProgramIndex'] + else: + if (content['ProgramIndex'] < program_count): + program_id = content['ProgramIndex'] + + if (program_id >= 0): + NativeHost.set_program(self.plugin_id, program_id) + self.edit_dialog.set_program(program_id) + + # Part 3 - set midi program + if (content['MidiBank'] >= 0 and content['MidiProgram'] >= 0): + midi_program_count = NativeHost.get_midi_program_count(self.plugin_id) + + for i in range(midi_program_count): + program_info = NativeHost.get_midi_program_info(self.plugin_id, i) + if (program_info['bank'] == content['MidiBank'] and program_info['program'] == content['MidiProgram']): + NativeHost.set_midi_program(self.plugin_id, i) + self.edit_dialog.set_midi_program(i) + break + + # Part 4a - store symbol values, for ladspa and lv2 + param_symbols = [] # (index, symbol) + + for parameter in content['Parameters']: + if (parameter['symbol']): + param_info = NativeHost.get_parameter_info(self.plugin_id, parameter['index']) + + if (param_info['valid'] and param_info['symbol']): + param_symbols.append((parameter['index'], param_info['symbol'])) + + # Part 4b - set parameter values (carefully) + for parameter in content['Parameters']: + index = -1 + + if (content['Type'] == "LADSPA"): + # Try to set by symbol, otherwise use index + if (parameter['symbol'] != None and parameter['symbol'] != ""): + for param_symbol in param_symbols: + if (param_symbol[1] == parameter['symbol']): + index = param_symbol[0] + break + else: + index = parameter['index'] + else: + index = parameter['index'] + + elif (content['Type'] == "LV2"): + # Symbol only + if (parameter['symbol'] != None and parameter['symbol'] != ""): + for param_symbol in param_symbols: + if (param_symbol[1] == parameter['symbol']): + index = param_symbol[0] + break + else: + print("Failed to find LV2 parameter symbol for", parameter['index'], "->", parameter['name']) + else: + print("LV2 Plugin parameter", parameter['index'], "has no symbol ->", parameter['name']) + + else: + # Index only + index = parameter['index'] + + if (index >= 0): + param_data = NativeHost.get_parameter_data(self.plugin_id, parameter['index']) + if (param_data['hints'] & PARAMETER_USES_SAMPLERATE): + parameter['value'] *= NativeHost.get_sample_rate() + + NativeHost.set_parameter_value(self.plugin_id, index, parameter['value']) + NativeHost.set_parameter_midi_channel(self.plugin_id, index, parameter['midi_channel']-1) + NativeHost.set_parameter_midi_cc(self.plugin_id, index, parameter['midi_cc']) + else: + print("Could not set parameter data for", parameter['index'], "->", parameter['name']) + + # Part 5 - set chunk data + if (content['Chunk']): + NativeHost.set_chunk_data(self.plugin_id, content['Chunk']) + + # Part 6 - set internal stuff + self.set_drywet(content['DryWet']*1000, True, True) + self.set_vol(content['Vol']*1000, True, True) + self.set_balance_left(content['Balance-Left']*1000, True, True) + self.set_balance_right(content['Balance-Right']*1000, True, True) + self.edit_dialog.do_reload_all() + + self.set_active(content['Active'], True, True) + + # Done! + gui.statusBar().showMessage("State File Loaded Sucessfully!") + + def timerEvent(self, event): + if (event.timerId() == self.TIMER_GUI_STUFF): + self.check_gui_stuff() + return QFrame.timerEvent(self, event) + + def paintEvent(self, event): + painter = QPainter(self) + painter.setPen(self.color_1) + painter.drawLine(0, 3, self.width(), 3) + painter.drawLine(0, self.height()-4, self.width(), self.height()-4) + painter.setPen(self.color_2) + painter.drawLine(0, 2, self.width(), 2) + painter.drawLine(0, self.height()-3, self.width(), self.height()-3) + painter.setPen(self.color_3) + painter.drawLine(0, 1, self.width(), 1) + painter.drawLine(0, self.height()-2, self.width(), self.height()-2) + painter.setPen(self.color_4) + painter.drawLine(0, 0, self.width(), 0) + painter.drawLine(0, self.height()-1, self.width(), self.height()-1) + QFrame.paintEvent(self, event) + +# Main Window +class CarlaMainW(QMainWindow, ui_carla.Ui_CarlaMainW): + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + self.setupUi(self) + + # ------------------------------------------------------------- + # Load Settings + + self.settings = QSettings("Cadence", "Carla") + self.settings_db = QSettings("Cadence", "Carla-Database") + self.loadSettings() + + #self.loadRDFs() + + self.setStyleSheet(""" + QWidget#centralwidget { + background-color: qlineargradient(spread:pad, + x1:0.0, y1:0.0, + x2:0.2, y2:1.0, + stop:0 rgb( 7, 7, 7), + stop:1 rgb(28, 28, 28) + ); + } + """) + + # ------------------------------------------------------------- + # Internal stuff + + self.m_plugin_list = [] + for x in range(MAX_PLUGINS): + self.m_plugin_list.append(None) + + self.m_project_filename = None + + NativeHost.set_callback_function(self.callback_function) + + # ------------------------------------------------------------- + # Set-up GUI stuff + + self.act_plugin_remove_all.setEnabled(False) + self.resize(self.width(), 0) + + # ------------------------------------------------------------- + # Connect actions to functions + + self.connect(self.act_file_new, SIGNAL("triggered()"), SLOT("slot_file_new()")) + self.connect(self.act_file_open, SIGNAL("triggered()"), SLOT("slot_file_open()")) + self.connect(self.act_file_save, SIGNAL("triggered()"), SLOT("slot_file_save()")) + self.connect(self.act_file_save_as, SIGNAL("triggered()"), SLOT("slot_file_save_as()")) + + self.connect(self.act_plugin_add, SIGNAL("triggered()"), SLOT("slot_plugin_add()")) + self.connect(self.act_plugin_remove_all, SIGNAL("triggered()"), SLOT("slot_remove_all()")) + + self.connect(self.act_help_about, SIGNAL("triggered()"), SLOT("slot_aboutCarla()")) + self.connect(self.act_help_about_qt, SIGNAL("triggered()"), app, SLOT("aboutQt()")) + + #self.connect(self, SIGNAL("SIGUSR1()"), self.handleSIGUSR1) + #self.connect(self, SIGNAL("DebugCallback(int, int, int, double)"), self.handleDebugCallback) + #self.connect(self, SIGNAL("ParameterCallback(int, int, double)"), self.handleParameterCallback) + #self.connect(self, SIGNAL("ProgramCallback(int, int)"), self.handleProgramCallback) + #self.connect(self, SIGNAL("MidiProgramCallback(int, int)"), self.handleMidiProgramCallback) + #self.connect(self, SIGNAL("NoteOnCallback(int, int, int)"), self.handleNoteOnCallback) + #self.connect(self, SIGNAL("NoteOffCallback(int, int, int)"), self.handleNoteOffCallback) + #self.connect(self, SIGNAL("ShowGuiCallback(int, int)"), self.handleShowGuiCallback) + #self.connect(self, SIGNAL("ResizeGuiCallback(int, int, int)"), self.handleResizeGuiCallback) + #self.connect(self, SIGNAL("UpdateCallback(int)"), self.handleUpdateCallback) + #self.connect(self, SIGNAL("ReloadInfoCallback(int)"), self.handleReloadInfoCallback) + #self.connect(self, SIGNAL("ReloadParametersCallback(int)"), self.handleReloadParametersCallback) + #self.connect(self, SIGNAL("ReloadProgramsCallback(int)"), self.handleReloadProgramsCallback) + #self.connect(self, SIGNAL("ReloadAllCallback(int)"), self.handleReloadAllCallback) + #self.connect(self, SIGNAL("QuitCallback()"), self.handleQuitCallback) + + def callback_function(self, action, plugin_id, value1, value2, value3): + if (plugin_id < 0 or plugin_id >= MAX_PLUGINS): + return + + #if (action == CALLBACK_DEBUG): + #self.emit(SIGNAL("DebugCallback(int, int, int, double)"), plugin_id, value1, value2, value3) + #elif (action == CALLBACK_PARAMETER_CHANGED): + #self.emit(SIGNAL("ParameterCallback(int, int, double)"), plugin_id, value1, value3) + #elif (action == CALLBACK_PROGRAM_CHANGED): + #self.emit(SIGNAL("ProgramCallback(int, int)"), plugin_id, value1) + #elif (action == CALLBACK_MIDI_PROGRAM_CHANGED): + #self.emit(SIGNAL("MidiProgramCallback(int, int)"), plugin_id, value1) + #elif (action == CALLBACK_NOTE_ON): + #self.emit(SIGNAL("NoteOnCallback(int, int, int)"), plugin_id, value1, value2) + #elif (action == CALLBACK_NOTE_OFF): + #self.emit(SIGNAL("NoteOffCallback(int, int, int)"), plugin_id, value1, value2) + #elif (action == CALLBACK_SHOW_GUI): + #self.emit(SIGNAL("ShowGuiCallback(int, int)"), plugin_id, value1) + #elif (action == CALLBACK_RESIZE_GUI): + #self.emit(SIGNAL("ResizeGuiCallback(int, int, int)"), plugin_id, value1, value2) + #elif (action == CALLBACK_UPDATE): + #self.emit(SIGNAL("UpdateCallback(int)"), plugin_id) + #elif (action == CALLBACK_RELOAD_INFO): + #self.emit(SIGNAL("ReloadInfoCallback(int)"), plugin_id) + #elif (action == CALLBACK_RELOAD_PARAMETERS): + #self.emit(SIGNAL("ReloadParametersCallback(int)"), plugin_id) + #elif (action == CALLBACK_RELOAD_PROGRAMS): + #self.emit(SIGNAL("ReloadProgramsCallback(int)"), plugin_id) + #elif (action == CALLBACK_RELOAD_ALL): + #self.emit(SIGNAL("ReloadAllCallback(int)"), plugin_id) + #elif (action == CALLBACK_QUIT): + #self.emit(SIGNAL("QuitCallback()")) + + #return 0 + + #def handleSIGUSR1(self): + #print "Got SIGUSR1 -> Saving project now" + #self.func_file_save() + + #def handleDebugCallback(self, plugin_id, value1, value2, value3): + #print "DEBUG ::", plugin_id, value1, value2, value3 + + #def handleParameterCallback(self, plugin_id, parameter_id, value): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.parameter_activity_timer = ICON_STATE_ON + + #if (parameter_id == PARAMETER_ACTIVE): + #pwidget.set_active((value > 0.0), True, False) + #elif (parameter_id == PARAMETER_DRYWET): + #pwidget.set_drywet(value*1000, True, False) + #elif (parameter_id == PARAMETER_VOLUME): + #pwidget.set_vol(value*1000, True, False) + #elif (parameter_id == PARAMETER_BALANCE_LEFT): + #pwidget.set_balance_left(value*1000, True, False) + #elif (parameter_id == PARAMETER_BALANCE_RIGHT): + #pwidget.set_balance_right(value*1000, True, False) + #elif (parameter_id >= 0): + #pwidget.edit_dialog.set_parameter_value(parameter_id, value) + + #def handleProgramCallback(self, plugin_id, program_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.set_program(program_id) + + #def handleMidiProgramCallback(self, plugin_id, midi_program_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.set_midi_program(midi_program_id) + + #def handleNoteOnCallback(self, plugin_id, note, velo): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.keyboard.noteOn(note, False) + + #def handleNoteOffCallback(self, plugin_id, note, velo): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.keyboard.noteOff(note, False) + + #def handleShowGuiCallback(self, plugin_id, show): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #if (show == 0): + #pwidget.b_gui.setChecked(False) + #pwidget.b_gui.setEnabled(True) + #elif (show == 1): + #pwidget.b_gui.setChecked(True) + #pwidget.b_gui.setEnabled(True) + #elif (show == -1): + #pwidget.b_gui.setChecked(False) + #pwidget.b_gui.setEnabled(False) + + #def handleResizeGuiCallback(self, plugin_id, width, height): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #gui_dialog = pwidget.gui_dialog + #if (gui_dialog): + #gui_dialog.set_new_size(width, height) + + #def handleUpdateCallback(self, plugin_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.do_update() + + #def handleReloadInfoCallback(self, plugin_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.do_reload_info() + + #def handleReloadParametersCallback(self, plugin_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.do_reload_parameters() + + #def handleReloadProgramsCallback(self, plugin_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.do_reload_programs() + + #def handleReloadAllCallback(self, plugin_id): + #pwidget = self.plugin_list[plugin_id] + #if (pwidget): + #pwidget.edit_dialog.do_reload_all() + + #def handleQuitCallback(self): + #CustomMessageBox(self, QMessageBox.Warning, self.tr("Warning"), self.tr("JACK has been stopped or crashed.\nPlease start JACK and restart Carla"), + #"You may want to save your session now...", QMessageBox.Ok, QMessageBox.Ok) + + #def func_add_plugin(self, ptype, filename, label, extra_stuff, activate): + #new_plugin_id = NativeHost.add_plugin(ptype, filename, label, extra_stuff) + + #if (new_plugin_id < 0): + #CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to load plugin"), + #NativeHost.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) + #else: + #pwidget = PluginWidget(self, new_plugin_id) + #self.w_plugins.layout().addWidget(pwidget) + #self.plugin_list[new_plugin_id] = pwidget + #self.act_plugin_remove_all.setEnabled(True) + + #if (activate): + #pwidget.set_active(True, True) + + #return new_plugin_id + + def remove_plugin(self, plugin_id, showError): + pwidget = self.m_plugin_list[plugin_id] + #pwidget.edit_dialog.close() + + #if (pwidget.gui_dialog): + #pwidget.gui_dialog.close() + + #if (NativeHost.remove_plugin(plugin_id)): + #pwidget.close() + #pwidget.deleteLater() + #self.w_plugins.layout().removeWidget(pwidget) + #self.plugin_list[plugin_id] = None + + #else: + #if (showError): + #CustomMessageBox(self, QMessageBox.Critical, self.tr("Error"), self.tr("Failed to remove plugin"), + #NativeHost.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) + + for i in range(MAX_PLUGINS): + if (self.m_plugin_list[i] != None): + self.act_plugin_remove_all.setEnabled(True) + break + else: + self.act_plugin_remove_all.setEnabled(False) + + #def get_extra_stuff(self, plugin): + #ptype = plugin['type'] + + #if (ptype == PLUGIN_LADSPA): + #p_id = long(plugin['id']) + #for rdf_item in self.ladspa_rdf_list: + #if (rdf_item.UniqueID == p_id): + #return pointer(rdf_item) + #else: + #return c_nullptr + + #elif (ptype == PLUGIN_DSSI): + #if (plugin['hints'] & PLUGIN_HAS_GUI): + #return findDSSIGUI(plugin['binary'], plugin['name'], plugin['label']) + #else: + #return c_nullptr + + #elif (ptype == PLUGIN_LV2): + #p_uri = plugin['label'] + #for rdf_item in self.lv2_rdf_list: + #if (rdf_item.URI == p_uri): + #return pointer(rdf_item) + #else: + #return c_nullptr + + #elif (ptype == PLUGIN_WINVST): + ## Store object so we can return a pointer + #if (self.winvst_info == None): + #self.winvst_info = WinVstBaseInfo() + #self.winvst_info.category = plugin['category'] + #self.winvst_info.hints = plugin['hints'] + #self.winvst_info.name = plugin['name'] + #self.winvst_info.maker = plugin['maker'] + #self.winvst_info.unique_id = long(plugin['id']) + #self.winvst_info.ains = plugin['audio.ins'] + #self.winvst_info.aouts = plugin['audio.outs'] + #return pointer(self.winvst_info) + + #else: + #return c_nullptr + + def save_project(self): + content = ("\n" + "\n" + "\n") % (VERSION) + + first_plugin = True + + for pwidget in self.m_plugin_list: + if (pwidget != None): + if (first_plugin == False): + content += "\n" + + content += " \n" + #content += pwidget.getSaveXMLContent() + content += " \n" + + first_plugin = False + + content += "\n" + + try: + open(self.m_project_filename, "w").write(content) + except: + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to save project file")) + + def load_project(self): + try: + project_read = open(self.m_project_filename, "r").read() + except: + project_read = None + + if not project_read: + QMessageBox.critical(self, self.tr("Error"), self.tr("Failed to load project file")) + return + + #xml = QDomDocument() + #xml.setContent(project_read) + + #xml_node = xml.documentElement() + #if (xml_node.tagName() != "CARLA-PROJECT"): + #QMessageBox.critical(self, self.tr("Error"), self.tr("Not a valid Carla project file")) + #return + + #failed_plugins = [] + #x_save_state_dicts = [] + + #node = xml_node.firstChild() + #while not node.isNull(): + #if (node.toElement().tagName() == "Plugin"): + #x_save_state_dict = getStateSaveDict(node) + #x_save_state_dicts.append(x_save_state_dict) + #node = node.nextSibling() + + #for x_save_state_dict in x_save_state_dicts: + #ptype = x_save_state_dict['Type'] + #label = x_save_state_dict['Label'] + #binary = x_save_state_dict['Binary'] + #unique_id = str(x_save_state_dict['UniqueID']) + #plugin = None + + #if (ptype == "LADSPA"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/LADSPA").toList()) + #elif (ptype == "DSSI"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/DSSI").toList()) + #elif (ptype == "LV2"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/LV2").toList()) + #elif (ptype == "VST"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/VST").toList()) + #elif (ptype == "Windows VST"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/WINVST").toList()) + #elif (ptype == "SoundFont"): + #x_plugins = QVariantPyObjectList(self.settings_db.value("Plugins/SF2").toList()) + #else: + #failed_plugins.append(x_save_state_dict['Name']) + #continue + + ## Try UniqueID -> Label -> Binary + #plugin_ulb = None + #plugin_ul = None + #plugin_ub = None + #plugin_lb = None + #plugin_u = None + #plugin_l = None + #plugin_b = None + + #for plugins in x_plugins: + #if (ptype == "SoundFont"): + #plugins = (plugins,) + + #for plugin_ in plugins: + #plugin = strPyPluginInfo(plugin_) + + #if (unique_id == plugin['id'] and label == plugin['label'] and binary == plugin['binary']): + #plugin_ulb = plugin + #break + #elif (unique_id == plugin['id'] and label == plugin['label']): + #plugin_ul = plugin + #elif (unique_id == plugin['id'] and binary == plugin['binary']): + #plugin_ub = plugin + #elif (label == plugin['label'] and binary == plugin['binary']): + #plugin_lb = plugin + #elif (unique_id == plugin['id']): + #plugin_u = plugin + #elif (label == plugin['label']): + #plugin_l = plugin + #elif (binary == plugin['binary']): + #plugin_b = plugin + + ## LV2 only uses URIs (label in this case) + #if (ptype != "LV2"): + #plugin_ub = None + #plugin_u = None + #plugin_b = None + + ## SoundFonts only uses Binaries + #if (ptype != "LV2"): + #plugin_ul = None + #plugin_u = None + #plugin_l = None + + #if (plugin_ulb): + #plugin = plugin_ulb + #elif (plugin_ul): + #plugin = plugin_ul + #elif (plugin_ub): + #plugin = plugin_ub + #elif (plugin_lb): + #plugin = plugin_lb + #elif (plugin_u): + #plugin = plugin_u + #elif (plugin_l): + #plugin = plugin_l + #elif (plugin_b): + #plugin = plugin_b + #else: + #plugin = None + + #if (plugin != None): + #extra_stuff = self.get_extra_stuff(plugin) + #new_plugin_id = self.func_add_plugin(plugin['type'], binary, label, extra_stuff, False) + + #if (new_plugin_id >= 0): + #pwidget = self.plugin_list[new_plugin_id] + #pwidget.load_save_state_dict(x_save_state_dict) + + #else: + #failed_plugins.append(x_save_state_dict['Name']) + + #else: + #failed_plugins.append(x_save_state_dict['Name']) + + #if (len(failed_plugins) > 0): + #print "----------- FAILED TO LOAD!! ->", failed_plugins + ## TODO - display error + + #def loadRDFs(self): + ## Save RDF info for later + #if (haveRDF): + #SettingsDir = os.path.join(HOME, ".config", "Cadence") + + #fr_ladspa_file = os.path.join(SettingsDir, "ladspa_rdf.db") + #if (os.path.exists(fr_ladspa_file)): + #fr_ladspa = open(fr_ladspa_file, 'r') + #if (fr_ladspa): + #try: + #self.ladspa_rdf_list = ladspa_rdf.get_c_ladspa_rdfs(json.load(fr_ladspa)) + #except: + #self.ladspa_rdf_list = [] + #fr_ladspa.close() + + #fr_lv2_file = os.path.join(SettingsDir, "lv2_rdf.db") + #if (os.path.exists(fr_lv2_file)): + #fr_lv2 = open(fr_lv2_file, 'r') + #if (fr_lv2): + #try: + #self.lv2_rdf_list = lv2_rdf.get_c_lv2_rdfs(json.load(fr_lv2)) + #except: + #self.lv2_rdf_list = [] + #fr_lv2.close() + + #else: + #self.ladspa_rdf_list = [] + #self.lv2_rdf_list = [] + + @pyqtSlot() + def slot_file_new(self): + self.slot_remove_all() + self.m_project_filename = None + self.setWindowTitle("Carla") + + @pyqtSlot() + def slot_file_open(self): + file_filter = self.tr("Carla Project File (*.carxp)") + filename = QFileDialog.getOpenFileName(self, self.tr("Open Carla Project File"), filter=file_filter) + + if (filename): + self.m_project_filename = filename + self.slot_remove_all() + self.load_project() + self.setWindowTitle("Carla - %s" % (getShortFileName(self.m_project_filename))) + + @pyqtSlot() + def slot_file_save(self, saveAs=False): + if (self.m_project_filename == None or saveAs): + file_filter = self.tr("Carla Project File (*.carxp)") + filename = QFileDialog.getSaveFileName(self, self.tr("Save Carla Project File"), filter=file_filter) + + if (filename): + self.m_project_filename = filename + self.save_project() + self.setWindowTitle("Carla - %s" % (getShortFileName(self.m_project_filename))) + + else: + self.save_project() + + @pyqtSlot() + def slot_file_save_as(self): + self.slot_file_save(True) + + @pyqtSlot() + def slot_plugin_add(self): + dialog = PluginDatabaseW(self) + if (dialog.exec_()): + pass + #plugin = dialog.plugin_to_add + #fname = plugin['binary'] + #label = plugin['label'] + #ptype = plugin['type'] + #extra_stuff = self.get_extra_stuff(plugin) + #self.func_add_plugin(ptype, fname, label, extra_stuff, True) + + @pyqtSlot() + def slot_remove_all(self): + for i in range(MAX_PLUGINS): + if (self.m_plugin_list[i] != None): + self.remove_plugin(i, False) + + @pyqtSlot() + def slot_aboutCarla(self): + AboutW(self).exec_() + + def saveSettings(self): + self.settings.setValue("Geometry", self.saveGeometry()) + + def loadSettings(self): + self.restoreGeometry(self.settings.value("Geometry", "")) + + def closeEvent(self, event): + self.saveSettings() + self.slot_remove_all() + QMainWindow.closeEvent(self, event) + +#--------------- main ------------------ +if __name__ == '__main__': + + # App initialization + app = QApplication(sys.argv) + app.setApplicationName("Carla") + app.setApplicationVersion(VERSION) + app.setOrganizationName("falkTX") + app.setWindowIcon(QIcon(":/scalable/carla.svg")) + + #style = app.style().metaObject().className() + #force_parameters_style = (style in ("Bespin::Style",)) + + PWD = sys.path[0] + NativeHost = Host(PWD) + #NativeHost.set_option(OPTION_GLOBAL_JACK_CLIENT, 0, "") + #NativeHost.set_option(OPTION_BRIDGE_PATH_LV2_GTK2, 0, carla_bridge_lv2_gtk2) + #NativeHost.set_option(OPTION_BRIDGE_PATH_LV2_QT4, 0, carla_bridge_lv2_qt4) + #NativeHost.set_option(OPTION_BRIDGE_PATH_LV2_X11, 0, carla_bridge_lv2_x11) + #NativeHost.set_option(OPTION_BRIDGE_PATH_VST_QT4, 0, carla_bridge_vst_qt4) + #NativeHost.set_option(OPTION_BRIDGE_PATH_WINVST, 0, carla_bridge_winvst) + + if (not NativeHost.carla_init("Carla")): + CustomMessageBox(None, QMessageBox.Critical, "Error", "Could not connect to JACK", + NativeHost.get_last_error(), QMessageBox.Ok, QMessageBox.Ok) + sys.exit(1) + + ## X11 Display + #Display = 0 + #if (LINUX): + #Display_env = os.getenv("DISPLAY") + #if (Display_env != None): + #try: + #Display = int(float(Display_env.replace(":",""))) + #except: + #Display = 0 + + # Show GUI + gui = CarlaMainW() + gui.show() + + # Set-up custom signal handling + set_up_signals(gui) + + for i in range(len(app.arguments())): + if (i == 0): continue + try_path = app.arguments()[i] + if (os.path.exists(try_path)): + gui.m_project_filename = try_path + gui.load_project() + gui.setWindowTitle("Carla - %s" % (getShortFileName(try_path))) + + # App-Loop + ret = app.exec_() + + # Close Host + if (NativeHost.carla_is_engine_running()): + if (not NativeHost.carla_close()): + print(NativeHost.get_last_error()) + + # Exit properly + sys.exit(ret) diff --git a/src/carla_backend.py b/src/carla_backend.py new file mode 100644 index 0000000..c9ee02f --- /dev/null +++ b/src/carla_backend.py @@ -0,0 +1,1126 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Carla Backend code +# Copyright (C) 2011-2012 Filipe Coelho FIXME +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the COPYING file + +# Imports (Global) +import os, sys +from ctypes import * +from copy import deepcopy +from subprocess import getoutput + +# Imports (Custom) +try: + import ladspa_rdf + haveRDF = True +except: + print("RDF Support not available (LADSPA-RDF and LV2 will be disabled)") + haveRDF = False + +# Set Platform and Architecture +is64bit = False +LINUX = False +MACOS = False +WINDOWS = False + +if (sys.platform == "darwin"): + MACOS = True +elif (sys.platform.startswith("linux")): + LINUX = True + if (len(os.uname()) > 4 and os.uname()[4] in ('x86_64',)): #FIXME - need more checks + is64bit = True +elif (sys.platform.startswith("win")): + WINDOWS = True + if (sys.platform == "win64"): + is64bit = True + +if (is64bit): + c_intptr = c_int64 +else: + c_intptr = c_int32 + +# Get short filename from full filename (/a/b.c -> b.c) +def getShortFileName(filename): + short = filename + if (os.sep in filename): + short = filename.rsplit(os.sep, 1)[1] + return short + +# Convert a ctypes struct into a dict +def struct_to_dict(struct): + return dict((attr, getattr(struct, attr)) for attr, value in struct._fields_) + +# ------------------------------------------------------------------------------------------------ +# Default Plugin Folders + +if (WINDOWS): + splitter = ";" + APPDATA = os.getenv("APPDATA") + PROGRAMFILES = os.getenv("PROGRAMFILES") + COMMONPROGRAMFILES = os.getenv("COMMONPROGRAMFILES") + + # Small integrity tests + if not APPDATA: + print("APPDATA variable not set, cannot continue") + sys.exit(1) + + if not PROGRAMFILES: + print("PROGRAMFILES variable not set, cannot continue") + sys.exit(1) + + if not COMMONPROGRAMFILES: + print("COMMONPROGRAMFILES variable not set, cannot continue") + sys.exit(1) + + DEFAULT_LADSPA_PATH = [ + os.path.join(APPDATA, "LADSPA"), + os.path.join(PROGRAMFILES, "LADSPA") + ] + + DEFAULT_DSSI_PATH = [ + os.path.join(APPDATA, "DSSI"), + os.path.join(PROGRAMFILES, "DSSI") + ] + + DEFAULT_LV2_PATH = [ + os.path.join(APPDATA, "LV2"), + os.path.join(COMMONPROGRAMFILES, "LV2") + ] + + DEFAULT_VST_PATH = [ + os.path.join(PROGRAMFILES, "VstPlugins"), + os.path.join(PROGRAMFILES, "Steinberg", "VstPlugins") + ] + + DEFAULT_SF2_PATH = [ + os.path.join(APPDATA, "SF2") + ] + + #if (is64bit): + # TODO + +elif (MACOS): + splitter = ":" + HOME = os.getenv("HOME") + + # Small integrity tests + if not HOME: + print("HOME variable not set, cannot continue") + sys.exit(1) + + DEFAULT_LADSPA_PATH = [ + os.path.join(HOME, "Library", "Audio", "Plug-Ins", "LADSPA"), + os.path.join("/", "Library", "Audio", "Plug-Ins", "LADSPA") + ] + + DEFAULT_DSSI_PATH = [ + os.path.join(HOME, "Library", "Audio", "Plug-Ins", "DSSI"), + os.path.join("/", "Library", "Audio", "Plug-Ins", "DSSI") + ] + + DEFAULT_LV2_PATH = [ + os.path.join(HOME, "Library", "Audio", "Plug-Ins", "LV2"), + os.path.join("/", "Library", "Audio", "Plug-Ins", "LV2") + ] + + DEFAULT_VST_PATH = [ + os.path.join(HOME, "Library", "Audio", "Plug-Ins", "VST"), + os.path.join("/", "Library", "Audio", "Plug-Ins", "VST") + ] + + DEFAULT_SF2_PATH = [ + # TODO + ] + +else: + splitter = ":" + HOME = os.getenv("HOME") + + # Small integrity tests + if not HOME: + print("HOME variable not set, cannot continue") + sys.exit(1) + + DEFAULT_LADSPA_PATH = [ + os.path.join(HOME, ".ladspa"), + os.path.join("/", "usr", "lib", "ladspa"), + os.path.join("/", "usr", "local", "lib", "ladspa") + ] + + DEFAULT_DSSI_PATH = [ + os.path.join(HOME, ".dssi"), + os.path.join("/", "usr", "lib", "dssi"), + os.path.join("/", "usr", "local", "lib", "dssi") + ] + + DEFAULT_LV2_PATH = [ + os.path.join(HOME, ".lv2"), + os.path.join("/", "usr", "lib", "lv2"), + os.path.join("/", "usr", "local", "lib", "lv2") + ] + + DEFAULT_VST_PATH = [ + os.path.join(HOME, ".vst"), + os.path.join("/", "usr", "lib", "vst"), + os.path.join("/", "usr", "local", "lib", "vst") + ] + + DEFAULT_SF2_PATH = [ + os.path.join(HOME, ".sf2"), + os.path.join("/", "usr", "share", "sounds", "sf2") + ] + +# ------------------------------------------------------------------------------------------------ +# Carla command-line tools + +carla_discovery_unix32 = "" +carla_discovery_unix64 = "" +carla_discovery_win32 = "" +carla_discovery_win64 = "" + +#carla_bridge_lv2_gtk2 = "" +#carla_bridge_lv2_qt4 = "" +#carla_bridge_lv2_x11 = "" +#carla_bridge_vst_qt4 = "" +#carla_bridge_winvst = "" + +if (WINDOWS): + PATH = (os.path.join(PROGRAMFILES, "Cadence", "carla"),) + +else: + PATH_env = os.getenv("PATH") + + if (PATH_env != None): + PATH = PATH_env.split(":") + else: + PATH = ("/usr/bin", "/usr/local/bin") + +CWD = sys.path[0] + +# discovery-unix32 +if (os.path.exists(os.path.join(CWD, "carla-discovery", "carla-discovery-unix32"))): + carla_discovery_unix32 = os.path.join(CWD, "carla-discovery", "carla-discovery-unix32") +else: + for p in PATH: + if (os.path.exists(os.path.join(p, "carla-discovery-unix32"))): + carla_discovery_unix32 = os.path.join(p, "carla-discovery-unix32") + break + +# discovery-unix64 +if (os.path.exists(os.path.join(CWD, "carla-discovery", "carla-discovery-unix64"))): + carla_discovery_unix64 = os.path.join(CWD, "carla-discovery", "carla-discovery-unix64") +else: + for p in PATH: + if (os.path.exists(os.path.join(p, "carla-discovery-unix64"))): + carla_discovery_unix64 = os.path.join(p, "carla-discovery-unix64") + break + +# discovery-win32 +if (os.path.exists(os.path.join(CWD, "carla-discovery", "carla-discovery-win32.exe"))): + carla_discovery_win32 = os.path.join(CWD, "carla-discovery", "carla-discovery-win32.exe") +else: + for p in PATH: + if (os.path.exists(os.path.join(p, "carla-discovery-wine32.exe"))): + carla_discovery_win32 = os.path.join(p, "carla-discovery-win32.exe") + break + +# discovery-win64 +if (os.path.exists(os.path.join(CWD, "carla-discovery", "carla-discovery-win64.exe"))): + carla_discovery_win64 = os.path.join(CWD, "carla-discovery", "carla-discovery-win64.exe") +else: + for p in PATH: + if (os.path.exists(os.path.join(p, "carla-discovery-win64.exe"))): + carla_discovery_win64 = os.path.join(p, "carla-discovery-win64.exe") + break + + ## lv2-gtk2 + #if (os.path.exists(os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-gtk2"))): + #carla_bridge_lv2_gtk2 = os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-gtk2") + #else: + #for p in PATH: + #if (os.path.exists(os.path.join(p, "carla-bridge-lv2-gtk2"))): + #carla_bridge_lv2_gtk2 = os.path.join(p, "carla-bridge-lv2-gtk2") + #break + + ## lv2-qt4 + #if (os.path.exists(os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-qt4"))): + #carla_bridge_lv2_qt4 = os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-qt4") + #else: + #for p in PATH: + #if (os.path.exists(os.path.join(p, "carla-bridge-lv2-qt4"))): + #carla_bridge_lv2_qt4 = os.path.join(p, "carla-bridge-lv2-qt4") + #break + + ## lv2-x11 + #if (os.path.exists(os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-x11"))): + #carla_bridge_lv2_x11 = os.path.join(CWD, "carla-bridges", "carla-bridge-lv2-x11") + #else: + #for p in PATH: + #if (os.path.exists(os.path.join(p, "carla-bridge-lv2-x11"))): + #carla_bridge_lv2_x11 = os.path.join(p, "carla-bridge-lv2-x11") + #break + + ## vst-qt4 + #if (os.path.exists(os.path.join(CWD, "carla-bridges", "carla-bridge-vst-qt4"))): + #carla_bridge_vst_qt4 = os.path.join(CWD, "carla-bridges", "carla-bridge-vst-qt4") + #else: + #for p in PATH: + #if (os.path.exists(os.path.join(p, "carla-bridge-vst-qt4"))): + #carla_bridge_vst_qt4 = os.path.join(p, "carla-bridge-vst-qt4") + #break + + ## winvst + #if (os.path.exists(os.path.join(CWD, "carla-bridges", "carla-bridge-winvst.exe"))): + #carla_bridge_winvst = os.path.join(CWD, "carla-bridges", "carla-bridge-winvst.exe") + #else: + #for p in PATH: + #if (os.path.exists(os.path.join(p, "carla-bridge-winvst.exe"))): + #carla_bridge_winvst = os.path.join(p, "carla-bridge-winvst.exe") + #break + +print("carla_discovery_unix32 ->", carla_discovery_unix32) +print("carla_discovery_unix64 ->", carla_discovery_unix64) +print("carla_discovery_win32 ->", carla_discovery_win32) +print("carla_discovery_win64 ->", carla_discovery_win64) + +print("LADSPA ->", DEFAULT_LADSPA_PATH) +print("DSSI ->", DEFAULT_DSSI_PATH) +print("LV2 ->", DEFAULT_LV2_PATH) +print("VST ->", DEFAULT_VST_PATH) +print("SF2 ->", DEFAULT_SF2_PATH) + +# ------------------------------------------------------------------------------------------------ +# Plugin Query (helper functions) + +def findBinaries(PATH, OS): + binaries = [] + + if (OS == "WINDOWS"): + extensions = (".dll", ".dlL", ".dLL", ".DLL", "DLl", "Dll") + elif (OS == "MACOS"): + extensions = (".dylib", ".so") + else: + extensions = (".so", ".sO", ".SO", ".So") + + for root, dirs, files in os.walk(PATH): + for name in [name for name in files if name.endswith(extensions)]: + binaries.append(os.path.join(root, name)) + + return binaries + +#def findSoundFonts(PATH): + #soundfonts = [] + + #extensions = (".sf2", ".sF2", ".SF2", ".Sf2") + + #for root, dirs, files in os.walk(PATH): + #for name in [name for name in files if name.endswith(extensions)]: + #soundfonts.append(os.path.join(root, name)) + + #return soundfonts + +#def findLV2Bundles(PATH): + #bundles = [] + #extensions = (".lv2", ".lV2", ".LV2", ".Lv2") + + #for root, dirs, files in os.walk(PATH): + #for dir_ in [dir_ for dir_ in dirs if dir_.endswith(extensions)]: + #bundles.append(os.path.join(root, dir_)) + + #return bundles + +#def findDSSIGUI(filename, name, label): + #gui_filename = "" + #plugin_dir = filename.rsplit(".", 1)[0] + #short_name = getShortFileName(filename).rsplit(".", 1)[0] + + #check_name = name.replace(" ","_") + #check_label = label + #check_sname = short_name + + #if (check_name[-1] != "_"): check_name += "_" + #if (check_label[-1] != "_"): check_label += "_" + #if (check_sname[-1] != "_"): check_sname += "_" + + #for root, dirs, files in os.walk(plugin_dir): + #plugin_files = files + #break + #else: + #plugin_files = [] + + #for i in range(len(plugin_files)): + #if (check_name in files[i] or check_label in files[i] or check_sname in files[i]): + #gui_filename = os.path.join(plugin_dir, files[i]) + #break + + #return gui_filename + +# ------------------------------------------------------------------------------------------------ +# Plugin Query + +#PyPluginInfo = { + #'type': 0, + #'category': 0, + #'hints': 0, + #'binary': "", + #'name': "", + #'label': "", + #'maker': "", + #'copyright': "", + #'id': "", + #'audio.ins': 0, + #'audio.outs': 0, + #'audio.totals': 0, + #'midi.ins': 0, + #'midi.outs': 0, + #'midi.totals': 0, + #'parameters.ins': 0, + #'parameters.outs': 0, + #'parameters.total': 0, + #'programs.total': 0 +#} + +def runCarlaDiscovery(itype, stype, filename, tool, isWine=False): + short_name = getShortFileName(filename).rsplit(".", 1)[0] + plugins = [] + pinfo = None + + command = "" + + if (LINUX or MACOS): + command += "env LANG=C " + + if (isWine): + command += "WINEDEBUG=-all " + + command += "%s %s \"%s\"" % (tool, stype, filename) + + print(command) + + try: + output = getoutput(command).split("\n") + except: + output = [] + + #for line in output: + #print(line) + #if (line == "carla-discovery::init::-----------"): + #pinfo = deepcopy(PyPluginInfo) + #pinfo['type'] = itype + #pinfo['category'] = PLUGIN_CATEGORY_NONE + #pinfo['hints'] = 0 + #pinfo['binary'] = filename + #pinfo['name'] = "" + #pinfo['label'] = "" + #pinfo['maker'] = "" + #pinfo['copyright'] = "" + #pinfo['id'] = "" + #pinfo['audio.ins'] = 0 + #pinfo['audio.outs'] = 0 + #pinfo['audio.total'] = 0 + #pinfo['midi.ins'] = 0 + #pinfo['midi.outs'] = 0 + #pinfo['midi.total'] = 0 + #pinfo['parameters.ins'] = 0 + #pinfo['parameters.outs'] = 0 + #pinfo['parameters.total'] = 0 + #pinfo['programs.total'] = 0 + + #elif (line == "carla-discovery::end::------------"): + #if (pinfo != None): + #plugins.append(pinfo) + + #elif (line == "Segmentation fault"): + #print "carla-discovery::crash::%s crashed during discovery" % (filename) + + #elif line.startswith("err:module:import_dll Library"): + #print line + + #elif line.startswith("carla-discovery::error::"): + #print line, "-", filename + + #elif line.startswith("carla-discovery::"): + #if (pinfo == None): continue + #prop, value = line.replace("carla-discovery::","").split("::", 1) + + #if (prop == "category"): + #if value.isdigit(): + #pinfo['category'] = int(value) + #elif (prop == "name"): + #if (value): + #pinfo['name'] = value + #else: + #pinfo['name'] = short_name + #elif (prop == "label"): + #if (value): + #pinfo['label'] = value + #else: + #pinfo['label'] = short_name + #elif (prop == "maker"): + #pinfo['maker'] = value + #elif (prop == "copyright"): + #pinfo['copyright'] = value + #elif (prop == "id"): + #pinfo['id'] = value + #elif (prop == "hints"): + #if value.isdigit(): + #pinfo['hints'] = int(value) + #elif (prop == "audio.ins"): + #if value.isdigit(): + #pinfo['audio.ins'] = int(value) + #elif (prop == "audio.outs"): + #if value.isdigit(): + #pinfo['audio.outs'] = int(value) + #elif (prop == "audio.total"): + #if value.isdigit(): + #pinfo['audio.total'] = int(value) + #elif (prop == "midi.ins"): + #if value.isdigit(): + #pinfo['midi.ins'] = int(value) + #elif (prop == "midi.outs"): + #if value.isdigit(): + #pinfo['midi.outs'] = int(value) + #elif (prop == "midi.total"): + #if value.isdigit(): + #pinfo['midi.total'] = int(value) + #elif (prop == "parameters.ins"): + #if value.isdigit(): + #pinfo['parameters.ins'] = int(value) + #elif (prop == "parameters.outs"): + #if value.isdigit(): + #pinfo['parameters.outs'] = int(value) + #elif (prop == "parameters.total"): + #if value.isdigit(): + #pinfo['parameters.total'] = int(value) + #elif (prop == "programs.total"): + #if value.isdigit(): + #pinfo['programs.total'] = int(value) + + ## Additional checks + #for pinfo in plugins: + #if (itype == PLUGIN_LADSPA): + ## TODO - find category using ladspa_rdf + #pass + + #elif (itype == PLUGIN_DSSI): + #if (findDSSIGUI(pinfo['binary'], pinfo['name'], pinfo['label'])): + #pinfo['hints'] |= PLUGIN_HAS_GUI + + return plugins + +def checkPluginLADSPA(filename, tool, isWine=False): + return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, isWine) + +def checkPluginDSSI(filename, tool, isWine=False): + return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, isWine) + +def checkPluginVST(filename, tool, isWine=False): + return runCarlaDiscovery(PLUGIN_VST, "VST", filename, tool, isWine) + +def checkPluginSF2(filename, tool): + return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) + +#def checkPluginLV2(rdf_info): + #plugins = [] + + #pinfo = deepcopy(PyPluginInfo) + #pinfo['type'] = PLUGIN_LV2 + #pinfo['category'] = PLUGIN_CATEGORY_NONE # TODO + #pinfo['hints'] = 0 + #pinfo['binary'] = rdf_info['Binary'] + #pinfo['name'] = rdf_info['Name'] + #pinfo['label'] = rdf_info['URI'] + #pinfo['maker'] = rdf_info['Author'] + #pinfo['copyright'] = rdf_info['License'] + #pinfo['id'] = str(rdf_info['UniqueID']) + #pinfo['audio.ins'] = 0 + #pinfo['audio.outs'] = 0 + #pinfo['audio.total'] = 0 + #pinfo['midi.ins'] = 0 + #pinfo['midi.outs'] = 0 + #pinfo['midi.total'] = 0 + #pinfo['parameters.ins'] = 0 + #pinfo['parameters.outs'] = 0 + #pinfo['parameters.total'] = 0 + #pinfo['programs.total'] = rdf_info['PresetCount'] + + #if (not rdf_info['Bundle'] or pinfo['binary'] == "" or pinfo['name'] == ""): + #return None + + #for i in range(rdf_info['PortCount']): + #PortType = rdf_info['Ports'][i]['Type'] + #PortProps = rdf_info['Ports'][i]['Properties'] + #if (PortType & lv2_rdf.LV2_PORT_AUDIO): + #pinfo['audio.total'] += 1 + #if (PortType & lv2_rdf.LV2_PORT_INPUT): + #pinfo['audio.ins'] += 1 + #elif (PortType & lv2_rdf.LV2_PORT_OUTPUT): + #pinfo['audio.outs'] += 1 + #elif (PortType & lv2_rdf.LV2_PORT_EVENT_MIDI): + #pinfo['midi.total'] += 1 + #if (PortType & lv2_rdf.LV2_PORT_INPUT): + #pinfo['midi.ins'] += 1 + #elif (PortType & lv2_rdf.LV2_PORT_OUTPUT): + #pinfo['midi.outs'] += 1 + #elif (PortType & lv2_rdf.LV2_PORT_CONTROL): + #pinfo['parameters.total'] += 1 + #if (PortType & lv2_rdf.LV2_PORT_INPUT): + #pinfo['parameters.ins'] += 1 + #elif (PortType & lv2_rdf.LV2_PORT_OUTPUT): + #if (not PortProps & lv2_rdf.LV2_PORT_LATENCY): + #pinfo['parameters.outs'] += 1 + + #if (rdf_info['Type'] & lv2_rdf.LV2_GROUP_GENERATOR): + #pinfo['hints'] |= PLUGIN_IS_SYNTH + + #if (rdf_info['UICount'] > 0): + #pinfo['hints'] |= PLUGIN_HAS_GUI + + #plugins.append(pinfo) + + #return plugins + +# ------------------------------------------------------------------------------------------------ +# Backend C++ -> Python variables + +global Callback + +c_enum = c_int +c_nullptr = None + +# static max values +MAX_PLUGINS = 99 +MAX_PARAMETERS = 200 +MAX_MIDI_EVENTS = 512 + +# plugin hints +PLUGIN_HAS_GUI = 0x01 +PLUGIN_IS_BRIDGE = 0x02 +PLUGIN_IS_SYNTH = 0x04 +PLUGIN_USES_CHUNKS = 0x08 +PLUGIN_CAN_DRYWET = 0x10 +PLUGIN_CAN_VOL = 0x20 +PLUGIN_CAN_BALANCE = 0x40 + +# parameter hints +PARAMETER_IS_ENABLED = 0x01 +PARAMETER_IS_AUTOMABLE = 0x02 +PARAMETER_HAS_STRICT_BOUNDS = 0x04 +PARAMETER_USES_SCALEPOINTS = 0x08 +PARAMETER_USES_SAMPLERATE = 0x10 + +# enum PluginType +PLUGIN_NONE = 0 +PLUGIN_LADSPA = 1 +PLUGIN_DSSI = 2 +PLUGIN_LV2 = 3 +PLUGIN_VST = 4 +PLUGIN_WINVST = 5 +PLUGIN_SF2 = 6 + +# enum PluginCategory +PLUGIN_CATEGORY_NONE = 0 +PLUGIN_CATEGORY_SYNTH = 1 +PLUGIN_CATEGORY_DELAY = 2 # also Reverb +PLUGIN_CATEGORY_EQ = 3 +PLUGIN_CATEGORY_FILTER = 4 +PLUGIN_CATEGORY_DYNAMICS = 5 # Amplifier, Compressor, Gate +PLUGIN_CATEGORY_MODULATOR = 6 # Chorus, Flanger, Phaser +PLUGIN_CATEGORY_UTILITY = 7 # Analyzer, Converter, Mixer +PLUGIN_CATEGORY_OUTRO = 8 # used to check if a plugin has a category + +# enum ParameterType +PARAMETER_UNKNOWN = 0 +PARAMETER_INPUT = 1 +PARAMETER_OUTPUT = 2 + +# enum InternalParametersIndex +PARAMETER_ACTIVE = -1 +PARAMETER_DRYWET = -2 +PARAMETER_VOLUME = -3 +PARAMETER_BALANCE_LEFT = -4 +PARAMETER_BALANCE_RIGHT = -5 + +# enum CustomDataType +CUSTOM_DATA_INVALID = 0 +CUSTOM_DATA_STRING = 1 +CUSTOM_DATA_BINARY = 2 + +# enum GuiType +GUI_NONE = 0 +GUI_INTERNAL_QT4 = 1 +GUI_INTERNAL_X11 = 2 +GUI_EXTERNAL_OSC = 3 +GUI_EXTERNAL_LV2 = 4 + +# enum OptionsType +OPTION_GLOBAL_JACK_CLIENT = 1 +OPTION_BRIDGE_PATH_LV2_GTK2 = 2 +OPTION_BRIDGE_PATH_LV2_QT4 = 3 +OPTION_BRIDGE_PATH_LV2_X11 = 4 +OPTION_BRIDGE_PATH_VST_QT4 = 5 +OPTION_BRIDGE_PATH_WINVST = 6 + +# enum CallbackType +CALLBACK_DEBUG = 0 +CALLBACK_PARAMETER_CHANGED = 1 # parameter_id, 0, value +CALLBACK_PROGRAM_CHANGED = 2 # program_id, 0, 0 +CALLBACK_MIDI_PROGRAM_CHANGED = 3 # midi_program_id, 0, 0 +CALLBACK_NOTE_ON = 4 # key, velocity, 0 +CALLBACK_NOTE_OFF = 5 # key, velocity, 0 +CALLBACK_SHOW_GUI = 6 # show? (0|1, -1=quit), 0, 0 +CALLBACK_RESIZE_GUI = 7 # width, height, 0 +CALLBACK_UPDATE = 8 +CALLBACK_RELOAD_INFO = 9 +CALLBACK_RELOAD_PARAMETERS = 10 +CALLBACK_RELOAD_PROGRAMS = 11 +CALLBACK_RELOAD_ALL = 12 +CALLBACK_QUIT = 13 + +class ParameterData(Structure): + _fields_ = [ + ("type", c_enum), + ("index", c_int32), + ("rindex", c_int32), + ("hints", c_int32), + ("midi_channel", c_uint8), + ("midi_cc", c_int16) + ] + +class ParameterRanges(Structure): + _fields_ = [ + ("def", c_double), + ("min", c_double), + ("max", c_double), + ("step", c_double), + ("step_small", c_double), + ("step_large", c_double) + ] + +class CustomData(Structure): + _fields_ = [ + ("type", c_enum), + ("key", c_char_p), + ("value", c_char_p) + ] + +class GuiData(Structure): + _fields_ = [ + ("type", c_enum), + ("visible", c_bool), + ("resizable", c_bool), + ("width", c_uint), + ("height", c_uint), + ("name", c_char_p), + ("show_now", c_bool) + ] + +class PluginInfo(Structure): + _fields_ = [ + ("valid", c_bool), + ("type", c_enum), + ("category", c_enum), + ("hints", c_uint), + ("binary", c_char_p), + ("name", c_char_p), + ("label", c_char_p), + ("maker", c_char_p), + ("copyright", c_char_p), + ("unique_id", c_long) + ] + +class PortCountInfo(Structure): + _fields_ = [ + ("valid", c_bool), + ("ins", c_uint32), + ("outs", c_uint32), + ("total", c_uint32) + ] + +class ParameterInfo(Structure): + _fields_ = [ + ("valid", c_bool), + ("name", c_char_p), + ("symbol", c_char_p), + ("label", c_char_p), + ("scalepoint_count", c_uint32) + ] + +class ScalePointInfo(Structure): + _fields_ = [ + ("valid", c_bool), + ("value", c_double), + ("label", c_char_p) + ] + +class MidiProgramInfo(Structure): + _fields_ = [ + ("valid", c_bool), + ("bank", c_uint32), + ("program", c_uint32), + ("label", c_char_p) + ] + +class WinVstBaseInfo(Structure): + _fields_ = [ + ("category", c_enum), + ("hints", c_uint), + ("name", c_char_p), + ("maker", c_char_p), + ("unique_id", c_long), + ("ains", c_uint32), + ("aouts", c_uint32) + ] + +# ------------------------------------------------------------------------------------------------ +# Backend C++ -> Python object + +class Host(object): + def __init__(self, cwd): + super(Host, self).__init__() + + if (WINDOWS): + libname = "carla_backend.dll" + elif (MACOS): + libname = "carla_backend.dylib" + else: + libname = "carla_backend.so" + + self.lib = None #cdll.LoadLibrary(os.path.join(cwd, "carla", libname)) + + def carla_init(self, client_name): + return True + #self.lib.carla_init.argtypes = [c_char_p] + #self.lib.carla_init.restype = c_bool + #return self.lib.carla_init(client_name) + + def carla_close(self): + return True + #self.lib.carla_close.argtypes = None + #self.lib.carla_close.restype = c_bool + #return self.lib.carla_close() + + def carla_is_engine_running(self): + return False + #self.lib.carla_is_engine_running.argtypes = None + #self.lib.carla_is_engine_running.restype = c_bool + #return self.lib.carla_is_engine_running() + + def add_plugin(self, ptype, filename, label, extra_stuff): + self.lib.add_plugin.argtypes = [c_enum, c_char_p, c_char_p, c_void_p] + self.lib.add_plugin.restype = c_short + return self.lib.add_plugin(ptype, filename, label, cast(extra_stuff, c_void_p)) + + def remove_plugin(self, plugin_id): + self.lib.remove_plugin.argtypes = [c_ushort] + self.lib.remove_plugin.restype = c_bool + return self.lib.remove_plugin(plugin_id) + + def get_plugin_info(self, plugin_id): + self.lib.get_plugin_info.argtypes = [c_ushort] + self.lib.get_plugin_info.restype = POINTER(PluginInfo) + return struct_to_dict(self.lib.get_plugin_info(plugin_id).contents) + + def get_audio_port_count_info(self, plugin_id): + self.lib.get_audio_port_count_info.argtypes = [c_ushort] + self.lib.get_audio_port_count_info.restype = POINTER(PortCountInfo) + return struct_to_dict(self.lib.get_audio_port_count_info(plugin_id).contents) + + def get_midi_port_count_info(self, plugin_id): + self.lib.get_midi_port_count_info.argtypes = [c_ushort] + self.lib.get_midi_port_count_info.restype = POINTER(PortCountInfo) + return struct_to_dict(self.lib.get_midi_port_count_info(plugin_id).contents) + + def get_parameter_count_info(self, plugin_id): + self.lib.get_parameter_count_info.argtypes = [c_ushort] + self.lib.get_parameter_count_info.restype = POINTER(PortCountInfo) + return struct_to_dict(self.lib.get_parameter_count_info(plugin_id).contents) + + def get_parameter_info(self, plugin_id, parameter_id): + self.lib.get_parameter_info.argtypes = [c_ushort, c_uint32] + self.lib.get_parameter_info.restype = POINTER(ParameterInfo) + return struct_to_dict(self.lib.get_parameter_info(plugin_id, parameter_id).contents) + + def get_scalepoint_info(self, plugin_id, parameter_id, scalepoint_id): + self.lib.get_scalepoint_info.argtypes = [c_ushort, c_uint32, c_uint32] + self.lib.get_scalepoint_info.restype = POINTER(ScalePointInfo) + return struct_to_dict(self.lib.get_scalepoint_info(plugin_id, parameter_id, scalepoint_id).contents) + + def get_midi_program_info(self, plugin_id, midi_program_id): + self.lib.get_midi_program_info.argtypes = [c_ushort, c_uint32] + self.lib.get_midi_program_info.restype = POINTER(MidiProgramInfo) + return struct_to_dict(self.lib.get_midi_program_info(plugin_id, midi_program_id).contents) + + def get_parameter_data(self, plugin_id, parameter_id): + self.lib.get_parameter_data.argtypes = [c_ushort, c_uint32] + self.lib.get_parameter_data.restype = POINTER(ParameterData) + return struct_to_dict(self.lib.get_parameter_data(plugin_id, parameter_id).contents) + + def get_parameter_ranges(self, plugin_id, parameter_id): + self.lib.get_parameter_ranges.argtypes = [c_ushort, c_uint32] + self.lib.get_parameter_ranges.restype = POINTER(ParameterRanges) + return struct_to_dict(self.lib.get_parameter_ranges(plugin_id, parameter_id).contents) + + def get_custom_data(self, plugin_id, custom_data_id): + self.lib.get_custom_data.argtypes = [c_ushort, c_uint32] + self.lib.get_custom_data.restype = POINTER(CustomData) + return struct_to_dict(self.lib.get_custom_data(plugin_id, custom_data_id).contents) + + def get_chunk_data(self, plugin_id): + self.lib.get_chunk_data.argtypes = [c_ushort] + self.lib.get_chunk_data.restype = c_char_p + return self.lib.get_chunk_data(plugin_id) + + def get_gui_data(self, plugin_id): + self.lib.get_gui_data.argtypes = [c_ushort] + self.lib.get_gui_data.restype = POINTER(GuiData) + return struct_to_dict(self.lib.get_gui_data(plugin_id).contents) + + def get_parameter_count(self, plugin_id): + self.lib.get_parameter_count.argtypes = [c_ushort] + self.lib.get_parameter_count.restype = c_uint32 + return self.lib.get_parameter_count(plugin_id) + + def get_program_count(self, plugin_id): + self.lib.get_program_count.argtypes = [c_ushort] + self.lib.get_program_count.restype = c_uint32 + return self.lib.get_program_count(plugin_id) + + def get_midi_program_count(self, plugin_id): + self.lib.get_midi_program_count.argtypes = [c_ushort] + self.lib.get_midi_program_count.restype = c_uint32 + return self.lib.get_midi_program_count(plugin_id) + + def get_custom_data_count(self, plugin_id): + self.lib.get_custom_data_count.argtypes = [c_ushort] + self.lib.get_custom_data_count.restype = c_uint32 + return self.lib.get_custom_data_count(plugin_id) + + def get_program_name(self, plugin_id, program_id): + self.lib.get_program_name.argtypes = [c_ushort, c_uint32] + self.lib.get_program_name.restype = c_char_p + return self.lib.get_program_name(plugin_id, program_id) + + def get_midi_program_name(self, plugin_id, midi_program_id): + self.lib.get_midi_program_name.argtypes = [c_ushort, c_uint32] + self.lib.get_midi_program_name.restype = c_char_p + return self.lib.get_midi_program_name(plugin_id, midi_program_id) + + def get_real_plugin_name(self, plugin_id): + self.lib.get_real_plugin_name.argtypes = [c_ushort] + self.lib.get_real_plugin_name.restype = c_char_p + return self.lib.get_real_plugin_name(plugin_id) + + def get_current_program_index(self, plugin_id): + self.lib.get_current_program_index.argtypes = [c_ushort] + self.lib.get_current_program_index.restype = c_int32 + return self.lib.get_current_program_index(plugin_id) + + def get_current_midi_program_index(self, plugin_id): + self.lib.get_current_midi_program_index.argtypes = [c_ushort] + self.lib.get_current_midi_program_index.restype = c_int32 + return self.lib.get_current_midi_program_index(plugin_id) + + def get_default_parameter_value(self, plugin_id, parameter_id): + self.lib.get_default_parameter_value.argtypes = [c_ushort, c_uint32] + self.lib.get_default_parameter_value.restype = c_double + return self.lib.get_default_parameter_value(plugin_id, parameter_id) + + def get_current_parameter_value(self, plugin_id, parameter_id): + self.lib.get_current_parameter_value.argtypes = [c_ushort, c_uint32] + self.lib.get_current_parameter_value.restype = c_double + return self.lib.get_current_parameter_value(plugin_id, parameter_id) + + def get_input_peak_value(self, plugin_id, port_id): + self.lib.get_input_peak_value.argtypes = [c_ushort, c_ushort] + self.lib.get_input_peak_value.restype = c_double + return self.lib.get_input_peak_value(plugin_id, port_id) + + def get_output_peak_value(self, plugin_id, port_id): + self.lib.get_output_peak_value.argtypes = [c_ushort, c_ushort] + self.lib.get_output_peak_value.restype = c_double + return self.lib.get_output_peak_value(plugin_id, port_id) + + def set_active(self, plugin_id, onoff): + self.lib.set_active.argtypes = [c_ushort, c_bool] + self.lib.set_active.restype = None + self.lib.set_active(plugin_id, onoff) + + def set_drywet(self, plugin_id, value): + self.lib.set_drywet.argtypes = [c_ushort, c_double] + self.lib.set_drywet.restype = None + self.lib.set_drywet(plugin_id, value) + + def set_vol(self, plugin_id, value): + self.lib.set_vol.argtypes = [c_ushort, c_double] + self.lib.set_vol.restype = None + self.lib.set_vol(plugin_id, value) + + def set_balance_left(self, plugin_id, value): + self.lib.set_balance_left.argtypes = [c_ushort, c_double] + self.lib.set_balance_left.restype = None + self.lib.set_balance_left(plugin_id, value) + + def set_balance_right(self, plugin_id, value): + self.lib.set_balance_right.argtypes = [c_ushort, c_double] + self.lib.set_balance_right.restype = None + self.lib.set_balance_right(plugin_id, value) + + def set_parameter_value(self, plugin_id, parameter_id, value): + self.lib.set_parameter_value.argtypes = [c_ushort, c_uint32, c_double] + self.lib.set_parameter_value.restype = None + self.lib.set_parameter_value(plugin_id, parameter_id, value) + + def set_parameter_midi_channel(self, plugin_id, parameter_id, channel): + self.lib.set_parameter_midi_channel.argtypes = [c_ushort, c_uint32, c_uint8] + self.lib.set_parameter_midi_channel.restype = None + self.lib.set_parameter_midi_channel(plugin_id, parameter_id, channel) + + def set_parameter_midi_cc(self, plugin_id, parameter_id, cc): + self.lib.set_parameter_midi_cc.argtypes = [c_ushort, c_uint32, c_int16] + self.lib.set_parameter_midi_cc.restype = None + self.lib.set_parameter_midi_cc(plugin_id, parameter_id, cc) + + def set_program(self, plugin_id, program_id): + self.lib.set_program.argtypes = [c_ushort, c_uint32] + self.lib.set_program.restype = None + self.lib.set_program(plugin_id, program_id) + + def set_midi_program(self, plugin_id, midi_program_id): + self.lib.set_midi_program.argtypes = [c_ushort, c_uint32] + self.lib.set_midi_program.restype = None + self.lib.set_midi_program(plugin_id, midi_program_id) + + def set_custom_data(self, plugin_id, dtype, key, value): + self.lib.set_custom_data.argtypes = [c_ushort, c_enum, c_char_p, c_char_p] + self.lib.set_custom_data.restype = None + return self.lib.set_custom_data(plugin_id, dtype, key, value) + + def set_chunk_data(self, plugin_id, chunk_data): + self.lib.set_chunk_data.argtypes = [c_ushort, c_char_p] + self.lib.set_chunk_data.restype = None + self.lib.set_chunk_data(plugin_id, chunk_data) + + def set_gui_data(self, plugin_id, data, gui_addr): + self.lib.set_gui_data.argtypes = [c_ushort, c_int, c_intptr] + self.lib.set_gui_data.restype = None + self.lib.set_gui_data(plugin_id, data, gui_addr) + + def show_gui(self, plugin_id, yesno): + self.lib.show_gui.argtypes = [c_ushort, c_bool] + self.lib.show_gui.restype = None + self.lib.show_gui(plugin_id, yesno) + + def idle_gui(self, plugin_id): + self.lib.idle_gui.argtypes = [c_ushort] + self.lib.idle_gui.restype = None + self.lib.idle_gui(plugin_id) + + def send_midi_note(self, plugin_id, onoff, note, velocity): + self.lib.send_midi_note.argtypes = [c_ushort, c_bool, c_uint8, c_uint8] + self.lib.send_midi_note.restype = None + self.lib.send_midi_note(plugin_id, onoff, note, velocity) + + def prepare_for_save(self, plugin_id): + self.lib.prepare_for_save.argtypes = [c_ushort] + self.lib.prepare_for_save.restype = None + self.lib.prepare_for_save(plugin_id) + + def set_callback_function(self, func): + global Callback + #Callback = CFUNCTYPE(None, c_enum, c_ushort, c_int, c_int, c_double)(func) + #self.lib.set_callback_function.argtypes = [] # TODO + #self.lib.set_callback_function.restype = None + #self.lib.set_callback_function(Callback) + + def set_option(self, option, value, value_str): + self.lib.set_option.argtypes = [c_enum, c_int, c_char_p] + self.lib.set_option.restype = None + self.lib.set_option(option, value, value_str) + + def get_last_error(self): + return "" + #self.lib.get_last_error.argtypes = None + #self.lib.get_last_error.restype = c_char_p + #return self.lib.get_last_error() + + def get_host_osc_url(self): + return "" + #self.lib.get_host_osc_url.argtypes = None + #self.lib.get_host_osc_url.restype = c_char_p + #return self.lib.get_host_osc_url() + + def get_host_client_name(self): + return "" + #self.lib.get_host_client_name.argtypes = None + #self.lib.get_host_client_name.restype = c_char_p + #return self.lib.get_host_client_name() + + def get_buffer_size(self): + self.lib.get_buffer_size.argtypes = None + self.lib.get_buffer_size.restype = c_uint32 + return self.lib.get_buffer_size() + + def get_sample_rate(self): + self.lib.get_sample_rate.argtypes = None + self.lib.get_sample_rate.restype = c_double + return self.lib.get_sample_rate() + + def get_latency(self): + self.lib.get_latency.argtypes = None + self.lib.get_latency.restype = c_double + return self.lib.get_latency() + +# ------------------------------------------------------------------------------------------------ +# Default Plugin Folders (set) + +LADSPA_PATH_env = os.getenv("LADSPA_PATH") +DSSI_PATH_env = os.getenv("DSSI_PATH") +LV2_PATH_env = os.getenv("LV2_PATH") +VST_PATH_env = os.getenv("VST_PATH") +SF2_PATH_env = os.getenv("SF2_PATH") + +if (LADSPA_PATH_env): + LADSPA_PATH = LADSPA_PATH_env.split(splitter) +else: + LADSPA_PATH = DEFAULT_LADSPA_PATH + +if (DSSI_PATH_env): + DSSI_PATH = DSSI_PATH_env.split(splitter) +else: + DSSI_PATH = DEFAULT_DSSI_PATH + +if (LV2_PATH_env): + LV2_PATH = LV2_PATH_env.split(splitter) +else: + LV2_PATH = DEFAULT_LV2_PATH + +if (VST_PATH_env): + VST_PATH = VST_PATH_env.split(splitter) +else: + VST_PATH = DEFAULT_VST_PATH + +if (SF2_PATH_env): + SF2_PATH = SF2_PATH_env.split(splitter) +else: + SF2_PATH = DEFAULT_SF2_PATH + +if (haveRDF): + LADSPA_RDF_PATH_env = os.getenv("LADSPA_RDF_PATH") + if (LADSPA_RDF_PATH_env): + ladspa_rdf.set_rdf_path(LADSPA_RDF_PATH_env.split(splitter)) + + #lv2_rdf.set_rdf_path(LV2_PATH) diff --git a/src/icons/16x16/dialog-error.png b/src/icons/16x16/dialog-error.png new file mode 100644 index 0000000..f7fc68f Binary files /dev/null and b/src/icons/16x16/dialog-error.png differ diff --git a/src/icons/16x16/dialog-ok-apply.png b/src/icons/16x16/dialog-ok-apply.png new file mode 100644 index 0000000..1abcaf2 Binary files /dev/null and b/src/icons/16x16/dialog-ok-apply.png differ diff --git a/src/icons/48x48/carla.png b/src/icons/48x48/carla.png new file mode 100644 index 0000000..353d5d7 Binary files /dev/null and b/src/icons/48x48/carla.png differ diff --git a/src/icons/bitmaps/carla_about.png b/src/icons/bitmaps/carla_about.png new file mode 100644 index 0000000..162b48c Binary files /dev/null and b/src/icons/bitmaps/carla_about.png differ diff --git a/src/icons/bitmaps/carla_about.xcf b/src/icons/bitmaps/carla_about.xcf new file mode 100644 index 0000000..e6e1f13 Binary files /dev/null and b/src/icons/bitmaps/carla_about.xcf differ diff --git a/src/icons/icons.qrc b/src/icons/icons.qrc index 7cecc64..b1e41cd 100644 --- a/src/icons/icons.qrc +++ b/src/icons/icons.qrc @@ -2,6 +2,8 @@ 16x16/application-exit.png 16x16/configure.png + 16x16/dialog-error.png + 16x16/dialog-ok-apply.png 16x16/document-new.png 16x16/document-open.png 16x16/document-print.png @@ -33,6 +35,7 @@ 48x48/catarina.png 48x48/catia.png 48x48/claudia.png + 48x48/carla.png 48x48/canvas.png 48x48/exec.png 48x48/jack.png @@ -42,6 +45,7 @@ scalable/catarina.svg scalable/catia.svg scalable/claudia.svg + scalable/carla.svg scalable/jack.svg scalable/pb_generic.svg scalable/pb_hardware.svg @@ -66,5 +70,6 @@ bitmaps/led_yellow.png bitmaps/led-big_on.png bitmaps/led-big_off.png + bitmaps/carla_about.png diff --git a/src/icons/scalable/carla.svg b/src/icons/scalable/carla.svg new file mode 100644 index 0000000..e388e34 --- /dev/null +++ b/src/icons/scalable/carla.svg @@ -0,0 +1,853 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + Carla + + + + + + + + + + + + + + + + + + + + + + Carla + + + + + + + + + + + + + + + + + + + diff --git a/src/ladspa_rdf.py b/src/ladspa_rdf.py index 9b4b33a..c3eca7b 100644 --- a/src/ladspa_rdf.py +++ b/src/ladspa_rdf.py @@ -679,7 +679,7 @@ def parse_rdf_file(filename): import os # Main function - check all rdfs for information about ladspa plugins -def recheck_all_plugins(qobject=None): +def recheck_all_plugins(qobject, start_value, percent_value, m_value): global LADSPA_RDF_PATH, LADSPA_Plugins LADSPA_Plugins = [] @@ -698,8 +698,8 @@ def recheck_all_plugins(qobject=None): # Tell GUI we're parsing this bundle if (qobject): - percent = (qobject.percent_value * 0.80) + ( (float(i) / len(rdf_files) ) * qobject.percent_value * 0.20 ) - qobject.pluginLook(percent, rdf_file) + percent = (float(i) / len(rdf_files) ) * percent_value + qobject.pluginLook(start_value + (percent * m_value), rdf_file) # Parse RDF parse_rdf_file(rdf_file) @@ -781,7 +781,7 @@ def get_c_ladspa_rdfs(PyPluginList): #if __name__ == '__main__': #set_rdf_path(["/home/falktx/Personal/FOSS/GIT/Cadence/lrdf/"]) - #plugins = recheck_all_plugins() + #plugins = recheck_all_plugins(None, 0) #for plugin in LADSPA_Plugins: #print("----------------------") #print("Type: 0x%X" % (plugin["Type"])) diff --git a/src/paramspinbox.py b/src/paramspinbox.py new file mode 100644 index 0000000..6823a06 --- /dev/null +++ b/src/paramspinbox.py @@ -0,0 +1,355 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Imports (Global) +from PyQt4.QtCore import Qt, QTimer, SIGNAL +from PyQt4.QtGui import QAbstractSpinBox, QComboBox, QCursor, QDialog, QInputDialog, QMenu, QPainter, QProgressBar, QValidator +from PyQt4.QtGui import QStyleFactory +from math import isnan + +# Imports (Custom) +import ui_inputdialog_value + +def fix_value(value, minimum, maximum): + if (isnan(value)): + print("Parameter is NaN! - %f" % (value)) + return minimum + elif (value < minimum): + print("Parameter too low! - %f/%f" % (value, minimum)) + return minimum + elif (value > maximum): + print("Parameter too high! - %f/%f" % (value, maximum)) + return maximum + else: + return value + +QPlastiqueStyle = QStyleFactory.create("Plastique") + +# Custom InputDialog with Scale Points support +class CustomInputDialog(QDialog, ui_inputdialog_value.Ui_Dialog): + def __init__(self, parent=None, label="", current=0, minimum=0, maximum=0, step=0, scalepoints=None): + super(CustomInputDialog, self).__init__(parent) + self.setupUi(self) + + self.label.setText(label) + self.doubleSpinBox.setMinimum(minimum) + self.doubleSpinBox.setMaximum(maximum) + self.doubleSpinBox.setValue(current) + self.doubleSpinBox.setSingleStep(step) + + self.ret_value = current + + if (scalepoints == None): + self.groupBox.setVisible(False) + self.resize(200, 0) + else: + text = "" + for scalepoint in scalepoints: + text += "" % (str(scalepoint['value']), scalepoint['label']) + text += "
%s - %s
" + self.textBrowser.setText(text) + self.resize(200, 300) + + self.connect(self, SIGNAL("accepted()"), self.setReturnValue) + + def setReturnValue(self): + self.ret_value = self.doubleSpinBox.value() + +# Progress-Bar used for ParamSpinBox +class ParamProgressBar(QProgressBar): + def __init__(self, parent=None): + super(ParamProgressBar, self).__init__(parent) + + self._minimum = 0.0 + self._maximum = 1.0 + self._step_small = 0.0 + self._rvalue = 0.0 + + self._label = "" + self._pre_label = " " + self._left_click_down = False + + self.setMinimum(0) + self.setMaximum(1000) + self.setValue(0) + self.setFormat("(none)") + + def set_minimum(self, value): + self._minimum = value + + def set_maximum(self, value): + self._maximum = value + + def set_value(self, value): + self._rvalue = value + vper = (value-self._minimum)/(self._maximum-self._minimum) + self.setValue(vper*1000) + + def set_label(self, label): + self._label = label.strip() + + if (self._label == "(coef)"): + self._label = "" + self._pre_label = "*" + + self.update() + + def handleMouseEventPos(self, pos): + xper = float(pos.x())/self.width() + value = xper*(self._maximum-self._minimum)+self._minimum + + if (value < self._minimum): + value = self._minimum + elif (value > self._maximum): + value = self._maximum + + self.emit(SIGNAL("valueChangedFromBar(float)"), value) + + def mousePressEvent(self, event): + if (event.button() == Qt.LeftButton): + self.handleMouseEventPos(event.pos()) + self._left_click_down = True + else: + self._left_click_down = False + return QProgressBar.mousePressEvent(self, event) + + def mouseMoveEvent(self, event): + if (self._left_click_down): + self.handleMouseEventPos(event.pos()) + return QProgressBar.mouseMoveEvent(self, event) + + def mouseReleaseEvent(self, event): + self._left_click_down = False + return QProgressBar.mouseReleaseEvent(self, event) + + def paintEvent(self, event): + self.setFormat(QString("%1 %2 %3").arg(self._pre_label).arg(self._rvalue).arg(self._label)) + return QProgressBar.paintEvent(self, event) + +# Special SpinBox used for parameters +class ParamSpinBox(QAbstractSpinBox): + def __init__(self, parent=None): + super(ParamSpinBox, self).__init__(parent) + + self._minimum = 0.0 + self._maximum = 1.0 + self._default = 0.0 + self._value = None + self._step = 0.0 + self._step_small = 0.0 + self._step_large = 0.0 + + self._read_only = False + self._scalepoints = None + self._have_scalepoints = False + + self.bar = ParamProgressBar(self) + self.bar.setContextMenuPolicy(Qt.NoContextMenu) + self.bar.show() + + self.lineEdit().setVisible(False) + + self.connect(self.bar, SIGNAL("valueChangedFromBar(float)"), self.handleValueChangedFromBar) + self.connect(self, SIGNAL("customContextMenuRequested(QPoint)"), self.showCustomMenu) + + QTimer.singleShot(0, self.updateBarGeometry) + + def force_plastique_style(self): + self.setStyle(QPlastiqueStyle) + + def set_minimum(self, value): + self._minimum = value + self.bar.set_minimum(value) + + def set_maximum(self, value): + self._maximum = value + self.bar.set_maximum(value) + + def set_default(self, value): + value = fix_value(value, self._minimum, self._maximum) + self._default = value + + def set_value(self, value, send=True): + value = fix_value(value, self._minimum, self._maximum) + if (self._value != value): + self._value = value + self.bar.set_value(value) + + if (self._have_scalepoints): + self.set_scalepoint_value(value) + + if (send): + self.emit(SIGNAL("valueChanged(float)"), value) + + self.update() + + return True + + else: + return False + + def set_step(self, value): + if (value == 0.0): + self._step = 0.01 + else: + self._step = value + + def set_step_small(self, value): + if (value == 0.0): + self._step_small = 0.001 + else: + self._step_small = value + + def set_step_large(self, value): + if (value == 0.0): + self._step_large = 0.1 + else: + self._step_large = value + + def set_label(self, label): + self.bar.set_label(label) + + def set_read_only(self, yesno): + self.setButtonSymbols(QAbstractSpinBox.UpDownArrows if (yesno) else QAbstractSpinBox.NoButtons) + self._read_only = yesno + self.setReadOnly(yesno) + + def set_scalepoints(self, scalepoints, use_scalepoints): + if (len(scalepoints) > 0): + self._scalepoints = scalepoints + self._have_scalepoints = use_scalepoints + + if (use_scalepoints): + # Hide ProgressBar and create a ComboBox + self.bar.close() + self.box = QComboBox(self) + self.box.setContextMenuPolicy(Qt.NoContextMenu) + self.box.show() + self.updateBarGeometry() + + for scalepoint in scalepoints: + self.box.addItem(QString("%1 - %2").arg(scalepoint['value']).arg(scalepoint['label'])) + + self.set_scalepoint_value(self._value) + + self.connect(self.box, SIGNAL("currentIndexChanged(QString)"), self.handleValueChangedFromBox) + + else: + self._scalepoints = None + + def set_scalepoint_value(self, value): + value = self.get_nearest_scalepoint(value) + for i in range(self.box.count()): + if (self.box.itemText(i).split(" - ")[0].toFloat()[0] == value): + self.box.setCurrentIndex(i) + break + + def get_nearest_scalepoint(self, real_value): + final_value = 0.0 + for i in range(len(self._scalepoints)): + scale_value = self._scalepoints[i]['value'] + if (i == 0): + final_value = scale_value + else: + srange1 = abs(real_value-scale_value) + srange2 = abs(real_value-final_value) + + if (srange2 > srange1): + final_value = scale_value + + return final_value + + def handleValueChangedFromBar(self, value): + if (self._read_only): return + + step = int(0.5+((value-self._minimum)/self._step)) + real_value = self._minimum+(step*self._step) + + self.set_value(real_value) + + def handleValueChangedFromBox(self, box_text): + if (self._read_only): return + + value = box_text.split(" - ")[0].toFloat()[0] + last_scale_value = self._scalepoints[len(self._scalepoints)-1]['value'] + + if (value == last_scale_value): + value = self._maximum + + self.set_value(value) + + def showCustomMenu(self, pos): + menu = QMenu(self) + act_x_reset = menu.addAction(self.tr("Reset (%1)").arg(self._default)) + menu.addSeparator() + act_x_copy = menu.addAction(self.tr("Copy (%1)").arg(self._value)) + if (False and not self._read_only): + act_x_paste = menu.addAction(self.tr("Paste (%1)").arg("TODO")) + else: + act_x_paste = menu.addAction(self.tr("Paste")) + act_x_paste.setEnabled(False) + menu.addSeparator() + act_x_set = menu.addAction(self.tr("Set value...")) + + if (self._read_only): + act_x_reset.setEnabled(False) + act_x_paste.setEnabled(False) + act_x_set.setEnabled(False) + + # TODO - NOT IMPLEMENTED YET + act_x_copy.setEnabled(False) + + act_x_sel = menu.exec_(QCursor.pos()) + + if (act_x_sel == act_x_set): + dialog = CustomInputDialog(self, self.parent().label.text(), self._value, self._minimum, self._maximum, self._step, self._scalepoints) + if (dialog.exec_()): + value = dialog.ret_value + self.set_value(value) + + elif (act_x_sel == act_x_copy): + pass + + elif (act_x_sel == act_x_paste): + pass + + elif (act_x_sel == act_x_reset): + self.set_value(self._default) + + def stepBy(self, steps): + if (steps == 0): + return + + value = self._value+(steps*self._step) + + if (value < self._minimum): + value = self._minimum + elif (value > self._maximum): + value = self._maximum + + self.set_value(value) + + def stepEnabled(self): + if (self._read_only): + return QAbstractSpinBox.StepNone + elif (self._value <= self._minimum): + return QAbstractSpinBox.StepUpEnabled + elif (self._value >= self._maximum): + return QAbstractSpinBox.StepDownEnabled + else: + return QAbstractSpinBox.StepUpEnabled|QAbstractSpinBox.StepDownEnabled + + def updateAll(self): + self.update() + self.bar.update() + if (self._have_scalepoints): + self.box.update() + + def updateBarGeometry(self): + self.bar.setGeometry(self.lineEdit().geometry()) + if (self._have_scalepoints): + self.box.setGeometry(self.lineEdit().geometry()) + + def resizeEvent(self, event): + QTimer.singleShot(0, self.updateBarGeometry) + return QAbstractSpinBox.resizeEvent(self, event) diff --git a/src/shared.py b/src/shared.py index aa9c04c..f52806e 100644 --- a/src/shared.py +++ b/src/shared.py @@ -213,6 +213,9 @@ def CustomMessageBox(self, icon, title, text, extraText="", buttons=QMessageBox. # signal handler for unix systems def set_up_signals(_gui): + if (WINDOWS): + return + global x_gui x_gui = _gui signal(SIGINT, signal_handler) diff --git a/src/ui/carla.ui b/src/ui/carla.ui new file mode 100644 index 0000000..82fea19 --- /dev/null +++ b/src/ui/carla.ui @@ -0,0 +1,210 @@ + + + CarlaMainW + + + + 0 + 0 + 710 + 109 + + + + Carla + + + + + 0 + + + + + + 1 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 710 + 20 + + + + + &File + + + + + + + + + + + &Plugin + + + + + + + + &Help + + + + + + + + + + + + toolBar + + + false + + + Qt::NoToolBarArea + + + Qt::ToolButtonTextBesideIcon + + + TopToolBarArea + + + false + + + + + + + + + + + + + :/16x16/document-save.png:/16x16/document-save.png + + + &Save + + + + + + :/16x16/document-save-as.png:/16x16/document-save-as.png + + + Save &As... + + + + + + :/16x16/document-new.png:/16x16/document-new.png + + + &New + + + + + + :/16x16/document-open.png:/16x16/document-open.png + + + &Open... + + + + + + :/16x16/application-exit.png:/16x16/application-exit.png + + + &Quit + + + + + + :/16x16/edit-delete.png:/16x16/edit-delete.png + + + &Remove All + + + + + &About + + + + + About &Qt + + + + + + :/16x16/list-add.png:/16x16/list-add.png + + + &Add New... + + + + + + + + + act_file_quit + triggered() + CarlaMainW + close() + + + -1 + -1 + + + 189 + 34 + + + + + diff --git a/src/ui/carla_about.ui b/src/ui/carla_about.ui new file mode 100644 index 0000000..1f8bd2b --- /dev/null +++ b/src/ui/carla_about.ui @@ -0,0 +1,357 @@ + + + AboutW + + + + 0 + 0 + 484 + 254 + + + + About Carla + + + + + + 0 + + + + About + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 100 + 20 + + + + + + + + about text here + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + :/bitmaps/carla_about.png + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Features + + + + + + LADSPA: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + DSSI: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + LV2: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + VST: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Horizontal + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + OSC + + + + + + Host URL: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Valid commands: + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + + DejaVu Sans Mono + + + + valid osc commands here + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Example: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + DejaVu Sans Mono + + + + TextLabel + + + + + + + TextLabel + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + + 456 + 35 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AboutW + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AboutW + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/ui/carla_control.ui b/src/ui/carla_control.ui new file mode 100644 index 0000000..f6be56d --- /dev/null +++ b/src/ui/carla_control.ui @@ -0,0 +1,160 @@ + + + CarlaControlW + + + + 0 + 0 + 648 + 155 + + + + Carla Control + + + + + 0 + + + + + + 3 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + 0 + 648 + 17 + + + + + &File + + + + + + + + + &Help + + + + + + + + + + + toolBar + + + false + + + Qt::NoToolBarArea + + + Qt::ToolButtonTextBesideIcon + + + TopToolBarArea + + + false + + + + + + + + + + :/16x16/network-connect.png:/16x16/network-connect.png + + + &Connect... + + + + + + :/16x16/application-exit.png:/16x16/application-exit.png + + + &Quit + + + + + &About + + + + + About &Qt + + + + + + :/16x16/view-refresh.png:/16x16/view-refresh.png + + + &Refresh + + + + + + + + + act_file_quit + triggered() + CarlaControlW + close() + + + -1 + -1 + + + 189 + 34 + + + + + diff --git a/src/ui/carla_database.ui b/src/ui/carla_database.ui new file mode 100644 index 0000000..bd51613 --- /dev/null +++ b/src/ui/carla_database.ui @@ -0,0 +1,348 @@ + + + PluginDatabaseW + + + + 0 + 0 + 779 + 465 + + + + Carla - Add New + + + + + + + + + + + Special Filters: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + Qt::UpArrow + + + + + + + Refresh + + + + :/16x16/view-refresh.png:/16x16/view-refresh.png + + + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + Effects + + + + + + + Instruments + + + + + + + MIDI Plugins + + + + + + + Other/Misc + + + + + + + SoundFonts + + + + + + + LV2 + + + + + + + VST + + + + + + + LADSPA + + + + + + + DSSI + + + + + + + Native + + + + + + + Bridged (32bit) + + + + + + + Bridged (Wine) + + + + + + + Stereo only + + + + + + + With GUI Only + + + + + + + + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + Qt::NoPen + + + true + + + true + + + false + + + 22 + + + 12 + + + + Name + + + + + Label + + + + + Maker + + + + + ID + + + + + Audio Ins + + + + + Audio Outs + + + + + MIDI Ins + + + + + MIDI Outs + + + + + Param Ins + + + + + Param Outs + + + + + Programs + + + + + Has GUI + + + + + Is Synth + + + + + Type + + + + + Binary + + + + + + + + + + (Number of Plugins go here) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Add Plugin + + + + :/16x16/list-add.png:/16x16/list-add.png + + + + + + + + + lineEdit + b_add + tb_filters + b_refresh + tableWidget + + + + + + + tb_filters + toggled(bool) + frame + setVisible(bool) + + + 723 + 21 + + + 736 + 39 + + + + + diff --git a/src/ui/carla_edit.ui b/src/ui/carla_edit.ui new file mode 100644 index 0000000..36d38d8 --- /dev/null +++ b/src/ui/carla_edit.ui @@ -0,0 +1,497 @@ + + + PluginEdit + + + + 0 + 0 + 726 + 476 + + + + Plugin Editor + + + + + + QTabWidget::South + + + 0 + + + + Information + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 11 + 75 + true + + + + +Plugin Name + + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Save State + + + + :/16x16/document-save.png:/16x16/document-save.png + + + + + + + Load State + + + + :/16x16/document-open.png:/16x16/document-open.png + + + + + + + + + + 0 + 0 + + + + Meta-Data + + + + + + Name: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Label/Product: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Maker: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Copyright: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Unique ID: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + + + + + 0 + 0 + + + + Processing Data + + + + + + Audio Inputs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Audio Outputs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Parameters: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Is Synth: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + Has External GUI: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + + + + + true + + + + + + + Control Outputs: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + 0 + 0 + + + + 0 + + + + Programs + + + + + + + + + + + + + :/16x16/document-open.png:/16x16/document-open.png + + + + + group_programs + cb_programs + b_reload_program + + + + MIDI Programs + + + + + + + + + + + + + :/16x16/document-open.png:/16x16/document-open.png + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 84 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOn + + + true + + + Qt::AlignCenter + + + + + 0 + 0 + 864 + 64 + + + + + 0 + 0 + + + + + 864 + 64 + + + + + 864 + 64 + + + + + 0 + + + + + + 0 + 0 + + + + + 864 + 64 + + + + + 864 + 64 + + + + + + + + + + + + + PixmapKeyboard + QWidget +
pixmapkeyboard.h
+ 1 +
+
+ + + + +
diff --git a/src/ui/carla_parameter.ui b/src/ui/carla_parameter.ui new file mode 100644 index 0000000..7f39486 --- /dev/null +++ b/src/ui/carla_parameter.ui @@ -0,0 +1,77 @@ + + + PluginParameter + + + + 0 + 0 + 452 + 35 + + + + Form + + + + + + Parameter Name + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Qt::CustomContextMenu + + + + + + + + (none) + + + + + + + + ch + + + 1 + + + 16 + + + 1 + + + + + + + + ParamSpinBox + QWidget +
paramspinbox.h
+ 1 +
+
+ + +
diff --git a/src/ui/carla_plugin.ui b/src/ui/carla_plugin.ui new file mode 100644 index 0000000..559199d --- /dev/null +++ b/src/ui/carla_plugin.ui @@ -0,0 +1,384 @@ + + + PluginWidget + + + + 0 + 0 + 936 + 136 + + + + + 0 + 0 + + + + Form + + + + + + + + + Activate/Deactivate plugin + + + + + + + + 300 + 0 + + + + + 300 + 16777215 + + + + Plugin name + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 14 + 14 + + + + Parameter Activity LED + + + + + + + + 14 + 14 + + + + MIDI Activity LED + + + + + + + + + + + 14 + 14 + + + + Audio Input Activity LED + + + + + + + + 14 + 14 + + + + Audio Output Activity LED + + + + + + + + + + + 75 + true + + + + Plugin name + + + Plugin Name + + + true + + + + + + + + + Show external plugin's GUI + + + Show GUI + + + true + + + + + + + Edit parameters + + + Edit + + + + :/16x16/edit-rename.png:/16x16/edit-rename.png + + + true + + + + + + + Remove this plugin + + + Remove + + + + :/16x16/list-remove.png:/16x16/list-remove.png + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + Qt::CustomContextMenu + + + Balance Right (0%) + + + -1000 + + + 1000 + + + 1000 + + + + + + + Qt::CustomContextMenu + + + Output volume (100%) + + + 1270 + + + 1000 + + + + + + + Qt::CustomContextMenu + + + Output dry/wet (100%) + + + 1000 + + + 1000 + + + + + + + Qt::CustomContextMenu + + + Balance Left (0%) + + + -1000 + + + 1000 + + + -1000 + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + + + + 0 + 0 + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + + + + + + + 0 + + + + + + 20 + true + true + false + + + + + + + :/bitmaps/carla_about.png + + + Qt::AlignCenter + + + + + + + + + + + + + + + PixmapDial + QDial +
pixmapdial.h
+
+ + DigitalPeakMeter + QWidget +
digitalpeakmeter.h
+ 1 +
+ + LEDButton + QPushButton +
ledbutton.h
+
+
+ + + + +
diff --git a/src/ui/carla_refresh.ui b/src/ui/carla_refresh.ui new file mode 100644 index 0000000..3d32df0 --- /dev/null +++ b/src/ui/carla_refresh.ui @@ -0,0 +1,373 @@ + + + PluginRefreshW + + + + 0 + 0 + 700 + 195 + + + + Carla - Refresh + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 30 + 20 + + + + + + + + Search for new... + + + Qt::AlignCenter + + + + + + LV2 + + + + + + + LADSPA + + + + + + + DSSI + + + + + + + SoundFonts + + + + + + + VST + + + + + + + Unix 32bit + + + + + + + Unix 64bit + + + + + + + Windows 32bit + + + + + + + Qt::Vertical + + + + + + + Windows 64bit + + + + + + + Qt::Horizontal + + + + 40 + 5 + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 30 + 20 + + + + + + + + Qt::Horizontal + + + + + + + + + + 0 + 0 + + + + 0 + + + -1 + + + + + + + + + + Refresh + + + + :/16x16/view-refresh.png:/16x16/view-refresh.png + + + + + + + >> Skip + + + + + + + Close + + + + :/16x16/window-close.png:/16x16/window-close.png + + + + + + + + + Available tools: + + + Qt::AlignCenter + + + true + + + + + + + 22 + 16777215 + + + + + + + :/16x16/dialog-ok-apply.png + + + + + + + carla-discovery-unix32 + + + + + + + + 22 + 16777215 + + + + + + + :/16x16/dialog-ok-apply.png + + + + + + + carla-discovery-win32 + + + + + + + + 22 + 16777215 + + + + + + + :/16x16/dialog-ok-apply.png + + + + + + + carla-discovery-unix64 + + + + + + + python-rdflib (LADSPA RDF and LV2 support) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + carla-discovery-win64 + + + + + + + + + + :/16x16/dialog-ok-apply.png + + + + + + + + + + :/16x16/dialog-ok-apply.png + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + + + + + b_close + clicked() + PluginRefreshW + close() + + + 426 + 231 + + + 236 + 125 + + + + + diff --git a/src/ui/inputdialog_value.ui b/src/ui/inputdialog_value.ui new file mode 100644 index 0000000..f3f3b26 --- /dev/null +++ b/src/ui/inputdialog_value.ui @@ -0,0 +1,86 @@ + + + Dialog + + + + 0 + 0 + 269 + 183 + + + + Set value + + + + + + TextLabel + + + + + + + + + + Scale Points + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +