| @@ -4,6 +4,7 @@ | |||||
| .*.kate-swp | .*.kate-swp | ||||
| .libmagic-tmp | .libmagic-tmp | ||||
| .libmagic-tmp.bc | .libmagic-tmp.bc | ||||
| .cache/ | |||||
| .kdev4/ | .kdev4/ | ||||
| .DS_Store | .DS_Store | ||||
| @@ -17,6 +18,7 @@ | |||||
| *.7z | *.7z | ||||
| *.bz2 | *.bz2 | ||||
| *.a | *.a | ||||
| *.d | |||||
| *.o | *.o | ||||
| *.dll | *.dll | ||||
| *.dll.def | *.dll.def | ||||
| @@ -58,6 +60,8 @@ qrc_resources.cpp | |||||
| *.pyc | *.pyc | ||||
| # Qt files | # Qt files | ||||
| *_ui.hpp | |||||
| *_ui.py | |||||
| *_rc.cpp | *_rc.cpp | ||||
| *_rc.py | *_rc.py | ||||
| ui_*.hpp | ui_*.hpp | ||||
| @@ -104,6 +108,7 @@ carla-native-plugin | |||||
| carla-rest-server | carla-rest-server | ||||
| zynaddsubfx-ui | zynaddsubfx-ui | ||||
| compile_commands.json | |||||
| stoat-output.png | stoat-output.png | ||||
| source/tests/ansi-pedantic-test_* | source/tests/ansi-pedantic-test_* | ||||
| @@ -143,7 +148,7 @@ bin/resources/widgets | |||||
| source/native-plugins/resources/*.py | source/native-plugins/resources/*.py | ||||
| # Other | # Other | ||||
| source/frontend/carla_config.py | |||||
| source/frontend/pluginlist/jackappdialog | |||||
| source/includes/asio/ | source/includes/asio/ | ||||
| source/includes/rewire/ | source/includes/rewire/ | ||||
| source/includes/vst2 | source/includes/vst2 | ||||
| @@ -294,6 +294,7 @@ ifeq ($(HAVE_QT5),true) | |||||
| QT5_HOSTBINS = $(shell $(PKG_CONFIG) --variable=host_bins Qt5Core) | QT5_HOSTBINS = $(shell $(PKG_CONFIG) --variable=host_bins Qt5Core) | ||||
| MOC_QT5 ?= $(QT5_HOSTBINS)/moc | MOC_QT5 ?= $(QT5_HOSTBINS)/moc | ||||
| RCC_QT5 ?= $(QT5_HOSTBINS)/rcc | RCC_QT5 ?= $(QT5_HOSTBINS)/rcc | ||||
| UIC_QT5 ?= $(QT5_HOSTBINS)/uic | |||||
| ifeq (,$(wildcard $(MOC_QT5))) | ifeq (,$(wildcard $(MOC_QT5))) | ||||
| HAVE_QT5 = false | HAVE_QT5 = false | ||||
| endif | endif | ||||
| @@ -315,191 +315,3 @@ void PluginDatabaseW::slot_saveSettings() | |||||
| } | } | ||||
| // -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
| // Jack Application Dialog | |||||
| // NOTE: index matches the one in the UI | |||||
| enum UiSessionManager { | |||||
| UI_SESSION_NONE, | |||||
| UI_SESSION_LADISH, | |||||
| UI_SESSION_NSM | |||||
| }; | |||||
| struct JackApplicationW::PrivateData { | |||||
| Ui::Dialog ui; | |||||
| const QString fProjectFilename; | |||||
| PrivateData(JackApplicationW* const dialog, const QString& projectFilename) | |||||
| : ui(), | |||||
| fProjectFilename(projectFilename) | |||||
| { | |||||
| ui.setupUi(dialog); | |||||
| ui.group_error->setVisible(false); | |||||
| // ------------------------------------------------------------------------------------------------------------ | |||||
| // Load settings | |||||
| loadSettings(); | |||||
| } | |||||
| void checkIfButtonBoxShouldBeEnabled(int index, const QString text) | |||||
| { | |||||
| static QList<QChar> badFirstChars = { '.', '/' }; | |||||
| bool enabled = text.length() > 0; | |||||
| QCarlaString showErr; | |||||
| // NSM applications must not be abstract or absolute paths, and must not contain arguments | |||||
| if (enabled && index == UI_SESSION_NSM) | |||||
| { | |||||
| if (badFirstChars.contains(text[0])) | |||||
| showErr = tr("NSM applications cannot use abstract or absolute paths"); | |||||
| else if (text.contains(' ') || text.contains(';') || text.contains('&')) | |||||
| showErr = tr("NSM applications cannot use CLI arguments"); | |||||
| else if (fProjectFilename.isEmpty()) | |||||
| showErr = tr("You need to save the current Carla project before NSM can be used"); | |||||
| } | |||||
| if (showErr.isNotEmpty()) | |||||
| { | |||||
| enabled = false; | |||||
| ui.l_error->setText(showErr); | |||||
| ui.group_error->setVisible(true); | |||||
| } | |||||
| else | |||||
| { | |||||
| ui.group_error->setVisible(false); | |||||
| } | |||||
| if (QPushButton* const button = ui.buttonBox->button(QDialogButtonBox::Ok)) | |||||
| button->setEnabled(enabled); | |||||
| } | |||||
| void loadSettings() | |||||
| { | |||||
| const QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||||
| const QString smName = settings.valueString("SessionManager", ""); | |||||
| if (smName == "LADISH (SIGUSR1)") | |||||
| ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH); | |||||
| else if (smName == "NSM") | |||||
| ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM); | |||||
| else | |||||
| ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE); | |||||
| ui.le_command->setText(settings.valueString("Command", "")); | |||||
| ui.le_name->setText(settings.valueString("Name", "")); | |||||
| ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||||
| ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||||
| ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2)); | |||||
| ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0)); | |||||
| ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0)); | |||||
| ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true)); | |||||
| ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false)); | |||||
| ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false)); | |||||
| checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(), ui.le_command->text()); | |||||
| } | |||||
| void saveSettings() | |||||
| { | |||||
| QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||||
| settings.setValue("Command", ui.le_command->text()); | |||||
| settings.setValue("Name", ui.le_name->text()); | |||||
| settings.setValue("SessionManager", ui.cb_session_mgr->currentText()); | |||||
| settings.setValue("NumAudioIns", ui.sb_audio_ins->value()); | |||||
| settings.setValue("NumAudioOuts", ui.sb_audio_outs->value()); | |||||
| settings.setValue("NumMidiIns", ui.sb_midi_ins->value()); | |||||
| settings.setValue("NumMidiOuts", ui.sb_midi_outs->value()); | |||||
| settings.setValue("ManageWindow", ui.cb_manage_window->isChecked()); | |||||
| settings.setValue("CaptureFirstWindow", ui.cb_capture_first_window->isChecked()); | |||||
| settings.setValue("MidiOutMixdown", ui.cb_out_midi_mixdown->isChecked()); | |||||
| } | |||||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||||
| }; | |||||
| JackApplicationW::JackApplicationW(QWidget* parent, const QString& projectFilename) | |||||
| : QDialog(parent), | |||||
| self(new PrivateData(this, projectFilename)) | |||||
| { | |||||
| adjustSize(); | |||||
| setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // Set-up connections | |||||
| connect(this, SIGNAL(finished(int)), SLOT(slot_saveSettings())); | |||||
| connect(self->ui.cb_session_mgr, SIGNAL(currentIndexChanged(int)), SLOT(slot_sessionManagerChanged(int))); | |||||
| connect(self->ui.le_command, SIGNAL(textChanged(QString)), SLOT(slot_commandChanged(QString))); | |||||
| } | |||||
| JackApplicationW::~JackApplicationW() | |||||
| { | |||||
| delete self; | |||||
| } | |||||
| void JackApplicationW::getCommandAndFlags(QString& command, QString& name, QString& labelSetup) | |||||
| { | |||||
| name = self->ui.le_name->text(); | |||||
| command = self->ui.le_command->text(); | |||||
| if (name.isEmpty()) | |||||
| { | |||||
| name = QFileInfo(command.split(' ').first()).baseName(); | |||||
| // FIXME | |||||
| name[0] = name[0].toTitleCase(); | |||||
| } | |||||
| SessionManager smgr; | |||||
| switch (self->ui.cb_session_mgr->currentIndex()) | |||||
| { | |||||
| case UI_SESSION_LADISH: | |||||
| smgr = LIBJACK_SESSION_MANAGER_LADISH; | |||||
| break; | |||||
| case UI_SESSION_NSM: | |||||
| smgr = LIBJACK_SESSION_MANAGER_NSM; | |||||
| break; | |||||
| default: | |||||
| smgr = LIBJACK_SESSION_MANAGER_NONE; | |||||
| break; | |||||
| } | |||||
| uint flags = 0x0; | |||||
| if (self->ui.cb_manage_window->isChecked()) | |||||
| flags |= LIBJACK_FLAG_CONTROL_WINDOW; | |||||
| if (self->ui.cb_capture_first_window->isChecked()) | |||||
| flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW; | |||||
| if (self->ui.cb_buffers_addition_mode->isChecked()) | |||||
| flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION; | |||||
| if (self->ui.cb_out_midi_mixdown->isChecked()) | |||||
| flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN; | |||||
| if (self->ui.cb_external_start->isChecked()) | |||||
| flags |= LIBJACK_FLAG_EXTERNAL_START; | |||||
| labelSetup = QString("%1%2%3%4%5%6").arg(QChar('0' + self->ui.sb_audio_ins->value())) | |||||
| .arg(QChar('0' + self->ui.sb_audio_outs->value())) | |||||
| .arg(QChar('0' + self->ui.sb_midi_ins->value())) | |||||
| .arg(QChar('0' + self->ui.sb_midi_outs->value())) | |||||
| .arg(QChar('0' + smgr)) | |||||
| .arg(QChar('0' + flags)); | |||||
| } | |||||
| void JackApplicationW::slot_commandChanged(const QString text) | |||||
| { | |||||
| self->checkIfButtonBoxShouldBeEnabled(self->ui.cb_session_mgr->currentIndex(), text); | |||||
| } | |||||
| void JackApplicationW::slot_sessionManagerChanged(const int index) | |||||
| { | |||||
| self->checkIfButtonBoxShouldBeEnabled(index, self->ui.le_command->text()); | |||||
| } | |||||
| void JackApplicationW::slot_saveSettings() | |||||
| { | |||||
| self->saveSettings(); | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -370,124 +370,6 @@ void QMessageBoxWithBetterWidth::showEvent(QShowEvent* const event) | |||||
| QMessageBox::showEvent(event); | QMessageBox::showEvent(event); | ||||
| } | } | ||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| // Safer QSettings class, which does not throw if type mismatches | |||||
| bool QSafeSettings::valueBool(const QString key, const bool defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::Bool), defaultValue); | |||||
| return var.isValid() ? var.toBool() : defaultValue; | |||||
| } | |||||
| Qt::CheckState QSafeSettings::valueCheckState(const QString key, const Qt::CheckState defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::UInt), defaultValue); | |||||
| if (! var.isValid()) | |||||
| return defaultValue; | |||||
| const uint value = var.toUInt(); | |||||
| switch (value) | |||||
| { | |||||
| case Qt::Unchecked: | |||||
| case Qt::PartiallyChecked: | |||||
| case Qt::Checked: | |||||
| return static_cast<Qt::CheckState>(value); | |||||
| default: | |||||
| return defaultValue; | |||||
| } | |||||
| } | |||||
| int QSafeSettings::valueIntPositive(const QString key, const int defaultValue) const | |||||
| { | |||||
| CARLA_SAFE_ASSERT_INT(defaultValue >= 0, defaultValue); | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::Int), defaultValue); | |||||
| CARLA_SAFE_ASSERT_RETURN(var.isValid(), defaultValue); | |||||
| const int value = var.toInt(); | |||||
| CARLA_SAFE_ASSERT_RETURN(value >= 0, defaultValue); | |||||
| return value; | |||||
| } | |||||
| uint QSafeSettings::valueUInt(const QString key, const uint defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::UInt), defaultValue); | |||||
| return var.isValid() ? var.toUInt() : defaultValue; | |||||
| } | |||||
| double QSafeSettings::valueDouble(const QString key, const double defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::Double), defaultValue); | |||||
| return var.isValid() ? var.toDouble() : defaultValue; | |||||
| } | |||||
| QString QSafeSettings::valueString(const QString key, const QString defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::String), defaultValue); | |||||
| return var.isValid() ? var.toString() : defaultValue; | |||||
| } | |||||
| QByteArray QSafeSettings::valueByteArray(const QString key, const QByteArray defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::ByteArray), defaultValue); | |||||
| return var.isValid() ? var.toByteArray() : defaultValue; | |||||
| } | |||||
| QStringList QSafeSettings::valueStringList(const QString key, const QStringList defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (var.isNull()) | |||||
| return defaultValue; | |||||
| CARLA_SAFE_ASSERT_RETURN(var.convert(QVariant::StringList), defaultValue); | |||||
| return var.isValid() ? var.toStringList() : defaultValue; | |||||
| } | |||||
| //--------------------------------------------------------------------------------------------------------------------- | //--------------------------------------------------------------------------------------------------------------------- | ||||
| // Custom MessageBox | // Custom MessageBox | ||||
| @@ -437,32 +437,6 @@ int getIndexOfQDoubleListValue(const QList<double>& list, const double value); | |||||
| bool isQDoubleListEqual(const QList<double>& list1, const QList<double>& list2); | bool isQDoubleListEqual(const QList<double>& list1, const QList<double>& list2); | ||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| // Custom QString class with a few extra methods | |||||
| class QCarlaString : public QString | |||||
| { | |||||
| public: | |||||
| inline QCarlaString() | |||||
| : QString() {} | |||||
| inline QCarlaString(const char* const ch) | |||||
| : QString(ch) {} | |||||
| inline QCarlaString(const QString& s) | |||||
| : QString(s) {} | |||||
| inline bool isNotEmpty() const | |||||
| { | |||||
| return !isEmpty(); | |||||
| } | |||||
| inline QCarlaString& operator=(const char* const ch) | |||||
| { | |||||
| return (*this = fromUtf8(ch)); | |||||
| } | |||||
| }; | |||||
| //--------------------------------------------------------------------------------------------------------------------- | //--------------------------------------------------------------------------------------------------------------------- | ||||
| // Custom QMessageBox which resizes itself to fit text | // Custom QMessageBox which resizes itself to fit text | ||||
| @@ -476,28 +450,6 @@ protected: | |||||
| void showEvent(QShowEvent* event); | void showEvent(QShowEvent* event); | ||||
| }; | }; | ||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| // Safer QSettings class, which does not throw if type mismatches | |||||
| class QSafeSettings : public QSettings | |||||
| { | |||||
| public: | |||||
| inline QSafeSettings() | |||||
| : QSettings() {} | |||||
| inline QSafeSettings(const QString organizationName, const QString applicationName) | |||||
| : QSettings(organizationName, applicationName) {} | |||||
| bool valueBool(const QString key, const bool defaultValue) const; | |||||
| Qt::CheckState valueCheckState(const QString key, const Qt::CheckState defaultValue) const; | |||||
| int valueIntPositive(const QString key, const int defaultValue) const; | |||||
| uint valueUInt(const QString key, const uint defaultValue) const; | |||||
| double valueDouble(const QString key, const double defaultValue) const; | |||||
| QString valueString(const QString key, const QString defaultValue) const; | |||||
| QByteArray valueByteArray(const QString key, const QByteArray defaultValue = QByteArray()) const; | |||||
| QStringList valueStringList(const QString key, const QStringList defaultValue = QStringList()) const; | |||||
| }; | |||||
| //--------------------------------------------------------------------------------------------------------------------- | //--------------------------------------------------------------------------------------------------------------------- | ||||
| // Custom MessageBox | // Custom MessageBox | ||||
| @@ -12,6 +12,24 @@ include $(CWD)/Makefile.mk | |||||
| BINDIR := $(CWD)/../bin | BINDIR := $(CWD)/../bin | ||||
| RESDIR := $(CWD)/../resources | RESDIR := $(CWD)/../resources | ||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| ifeq ($(WINDOWS),true) | |||||
| QT5_LINK_FLAGS = $(shell echo $(LINK_FLAGS) | awk 'sub(" -static","")') -static-libgcc | |||||
| else | |||||
| QT5_LINK_FLAGS = $(LINK_FLAGS) | |||||
| endif | |||||
| ifeq ($(HAVE_QT5),true) | |||||
| QT5_PREFIX = $(shell pkg-config --variable=prefix Qt5Core) | |||||
| BUILD_CXX_FLAGS += $(shell pkg-config --cflags Qt5Core Qt5Gui Qt5Widgets) | |||||
| QT5_LINK_FLAGS += -Wl,-rpath,$(QT5_PREFIX)/lib $(shell pkg-config --libs Qt5Core Qt5Gui Qt5Widgets) | |||||
| else ifeq ($(HAVE_QT5PKG),true) | |||||
| QT5_PREFIX = $(shell pkg-config --variable=prefix Qt5OpenGLExtensions) | |||||
| BUILD_CXX_FLAGS += -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB -I $(QT5_PREFIX)/include/qt5 | |||||
| QT5_LINK_FLAGS += -Wl,-rpath,$(QT5_PREFIX)/lib -F $(QT5_PREFIX)/lib -framework QtCore -framework QtGui -framework QtWidgets | |||||
| endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Translations | # Translations | ||||
| @@ -77,26 +95,29 @@ endif | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # UI code | # UI code | ||||
| UIs = \ | |||||
| ui_carla_about.py \ | |||||
| ui_carla_about_juce.py \ | |||||
| ui_carla_add_jack.py \ | |||||
| ui_carla_database.py \ | |||||
| ui_carla_edit.py \ | |||||
| ui_carla_host.py \ | |||||
| ui_carla_osc_connect.py \ | |||||
| ui_carla_parameter.py \ | |||||
| ui_carla_plugin_calf.py \ | |||||
| ui_carla_plugin_classic.py \ | |||||
| ui_carla_plugin_compact.py \ | |||||
| ui_carla_plugin_default.py \ | |||||
| ui_carla_plugin_presets.py \ | |||||
| ui_carla_refresh.py \ | |||||
| ui_carla_settings.py \ | |||||
| ui_carla_settings_driver.py \ | |||||
| ui_inputdialog_value.py \ | |||||
| ui_midipattern.py \ | |||||
| ui_xycontroller.py | |||||
| UI_FILES = $(wildcard pluginlist/*.ui) | |||||
| UIs = $(UI_FILES:%.ui=%_ui.hpp) | |||||
| UIs += $(UI_FILES:%.ui=%_ui.py) | |||||
| # ui_carla_about.py \ | |||||
| # ui_carla_about_juce.py \ | |||||
| # ui_carla_database.py \ | |||||
| # ui_carla_edit.py \ | |||||
| # ui_carla_host.py \ | |||||
| # ui_carla_osc_connect.py \ | |||||
| # ui_carla_parameter.py \ | |||||
| # ui_carla_plugin_calf.py \ | |||||
| # ui_carla_plugin_classic.py \ | |||||
| # ui_carla_plugin_compact.py \ | |||||
| # ui_carla_plugin_default.py \ | |||||
| # ui_carla_plugin_presets.py \ | |||||
| # ui_carla_refresh.py \ | |||||
| # ui_carla_settings.py \ | |||||
| # ui_carla_settings_driver.py \ | |||||
| # ui_inputdialog_value.py \ | |||||
| # ui_midipattern.py \ | |||||
| # ui_xycontroller.py | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -104,7 +125,10 @@ all: $(QMs) $(RES) $(UIs) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| ui_%.py: $(RESDIR)/ui/%.ui | |||||
| %_ui.hpp: %.ui | |||||
| $(UIC_QT5) $< -o $@ | |||||
| %_ui.py: %.ui | |||||
| $(PYUIC) $< -o $@ | $(PYUIC) $< -o $@ | ||||
| resources_rc.py: $(RESDIR)/resources.qrc $(RESDIR)/*/*.png $(RESDIR)/*/*.svg $(RESDIR)/*/*.svgz | resources_rc.py: $(RESDIR)/resources.qrc $(RESDIR)/*/*.png $(RESDIR)/*/*.svg $(RESDIR)/*/*.svgz | ||||
| @@ -132,6 +156,16 @@ debug: | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| tests: $(UI_FILES:%.ui=%$(APP_EXT)) | |||||
| pluginlist/jackappdialog$(APP_EXT): pluginlist/jackappdialog.cpp pluginlist/jackappdialog.ui pluginlist/jackappdialog_ui.hpp | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(QT5_LINK_FLAGS) -o $@ | |||||
| pluginlist/pluginlistdialog$(APP_EXT): pluginlist/pluginlistdialog.cpp pluginlist/pluginlistdialog.ui pluginlist/pluginlistdialog_ui.hpp | |||||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(QT5_LINK_FLAGS) -o $@ | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| lint: | lint: | ||||
| pylint \ | pylint \ | ||||
| --extension-pkg-whitelist=PyQt5 \ | --extension-pkg-whitelist=PyQt5 \ | ||||
| @@ -2,7 +2,7 @@ | |||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||
| # Carla plugin host | # Carla plugin host | ||||
| # Copyright (C) 2011-2017 Filipe Coelho <falktx@falktx.com> | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | # | ||||
| # This program is free software; you can redistribute it and/or | # This program is free software; you can redistribute it and/or | ||||
| # modify it under the terms of the GNU General Public License as | # modify it under the terms of the GNU General Public License as | ||||
| @@ -19,7 +19,20 @@ | |||||
| # ---------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------- | ||||
| # Imports (Custom Stuff) | # Imports (Custom Stuff) | ||||
| from carla_host import * | |||||
| from carla_app import ( | |||||
| CarlaApplication, | |||||
| ) | |||||
| from carla_host import ( | |||||
| HostWindow, | |||||
| initHost, | |||||
| loadHostSettings, | |||||
| ) | |||||
| from carla_shared import ( | |||||
| handleInitialCommandLineArguments, | |||||
| setUpSignals, | |||||
| ) | |||||
| # ---------------------------------------------------------------------------------------------------------------------- | # ---------------------------------------------------------------------------------------------------------------------- | ||||
| # Main | # Main | ||||
| @@ -46,10 +46,11 @@ from carla_shared import ( | |||||
| CARLA_DEFAULT_MAIN_PRO_THEME_COLOR, | CARLA_DEFAULT_MAIN_PRO_THEME_COLOR, | ||||
| CWD, VERSION, | CWD, VERSION, | ||||
| getPaths, | getPaths, | ||||
| QSafeSettings, | |||||
| gCarla | gCarla | ||||
| ) | ) | ||||
| from utils import QSafeSettings | |||||
| # ------------------------------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------------------------------ | ||||
| class CarlaApplication(): | class CarlaApplication(): | ||||
| @@ -53,13 +53,15 @@ from PyQt5.QtWidgets import ( | |||||
| import ui_carla_host | import ui_carla_host | ||||
| from carla_app import * | from carla_app import * | ||||
| from carla_backend import * | |||||
| from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull | from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull | ||||
| from carla_database import * | |||||
| from carla_shared import * | |||||
| from carla_settings import * | from carla_settings import * | ||||
| from carla_utils import * | from carla_utils import * | ||||
| from carla_widgets import * | from carla_widgets import * | ||||
| from patchcanvas import patchcanvas | from patchcanvas import patchcanvas | ||||
| from pluginlist import PluginDatabaseW | |||||
| from widgets.digitalpeakmeter import DigitalPeakMeter | from widgets.digitalpeakmeter import DigitalPeakMeter | ||||
| from widgets.pixmapkeyboard import PixmapKeyboardHArea | from widgets.pixmapkeyboard import PixmapKeyboardHArea | ||||
| @@ -178,11 +178,12 @@ from carla_shared import ( | |||||
| getIcon, | getIcon, | ||||
| fontMetricsHorizontalAdvance, | fontMetricsHorizontalAdvance, | ||||
| splitter, | splitter, | ||||
| QSafeSettings | |||||
| ) | ) | ||||
| from patchcanvas.theme import Theme, getThemeName | from patchcanvas.theme import Theme, getThemeName | ||||
| from utils import QSafeSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # ... | # ... | ||||
| @@ -870,19 +870,6 @@ class QMessageBoxWithBetterWidth(QMessageBox): | |||||
| QMessageBox.showEvent(self, event) | QMessageBox.showEvent(self, event) | ||||
| # ------------------------------------------------------------------------------------------------------------ | |||||
| # Safer QSettings class, which does not throw if type mismatches | |||||
| class QSafeSettings(QSettings): | |||||
| def value(self, key, defaultValue, valueType): | |||||
| if not isinstance(defaultValue, valueType): | |||||
| print("QSafeSettings.value() - defaultValue type mismatch for key", key) | |||||
| try: | |||||
| return QSettings.value(self, key, defaultValue, valueType) | |||||
| except: | |||||
| return defaultValue | |||||
| # ------------------------------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------------------------------ | ||||
| # Custom MessageBox | # Custom MessageBox | ||||
| @@ -63,7 +63,7 @@ from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRem | |||||
| from . import * | from . import * | ||||
| from .scene import PatchScene | from .scene import PatchScene | ||||
| from carla_shared import QSafeSettings | |||||
| from utils import QSafeSettings | |||||
| # ------------------------------------------------------------------------------------------------------------ | # ------------------------------------------------------------------------------------------------------------ | ||||
| @@ -0,0 +1,20 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| from .jackappdialog import JackApplicationW | |||||
| from .pluginlistdialog import PluginDatabaseW | |||||
| @@ -0,0 +1,390 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin list code | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| import os | |||||
| from copy import deepcopy | |||||
| from subprocess import Popen, PIPE | |||||
| from PyQt5.QtCore import qWarning | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Carla) | |||||
| from carla_backend import ( | |||||
| BINARY_NATIVE, | |||||
| BINARY_NONE, | |||||
| PLUGIN_AU, | |||||
| PLUGIN_DSSI, | |||||
| PLUGIN_LADSPA, | |||||
| PLUGIN_LV2, | |||||
| PLUGIN_NONE, | |||||
| PLUGIN_SF2, | |||||
| PLUGIN_SFZ, | |||||
| PLUGIN_VST2, | |||||
| PLUGIN_VST3, | |||||
| ) | |||||
| from carla_shared import ( | |||||
| LINUX, | |||||
| MACOS, | |||||
| WINDOWS, | |||||
| ) | |||||
| from carla_utils import getPluginCategoryAsString | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Plugin Query (helper functions) | |||||
| def findBinaries(binPath, pluginType, OS): | |||||
| binaries = [] | |||||
| if OS == "HAIKU": | |||||
| extensions = ("") if pluginType == PLUGIN_VST2 else (".so",) | |||||
| elif OS == "MACOS": | |||||
| extensions = (".dylib", ".so") | |||||
| elif OS == "WINDOWS": | |||||
| extensions = (".dll",) | |||||
| else: | |||||
| extensions = (".so",) | |||||
| for root, _, files in os.walk(binPath): | |||||
| for name in tuple(name for name in files if name.lower().endswith(extensions)): | |||||
| binaries.append(os.path.join(root, name)) | |||||
| return binaries | |||||
| def findVST3Binaries(binPath): | |||||
| binaries = [] | |||||
| for root, dirs, files in os.walk(binPath): | |||||
| for name in tuple(name for name in (files+dirs) if name.lower().endswith(".vst3")): | |||||
| binaries.append(os.path.join(root, name)) | |||||
| return binaries | |||||
| def findLV2Bundles(bundlePath): | |||||
| bundles = [] | |||||
| for root, _, _2 in os.walk(bundlePath, followlinks=True): | |||||
| if root == bundlePath: | |||||
| continue | |||||
| if os.path.exists(os.path.join(root, "manifest.ttl")): | |||||
| bundles.append(root) | |||||
| return bundles | |||||
| def findMacVSTBundles(bundlePath, isVST3): | |||||
| bundles = [] | |||||
| extension = ".vst3" if isVST3 else ".vst" | |||||
| for root, dirs, _ in os.walk(bundlePath, followlinks=True): | |||||
| for name in tuple(name for name in dirs if name.lower().endswith(extension)): | |||||
| bundles.append(os.path.join(root, name)) | |||||
| return bundles | |||||
| def findFilenames(filePath, stype): | |||||
| filenames = [] | |||||
| if stype == "sf2": | |||||
| extensions = (".sf2",".sf3",) | |||||
| else: | |||||
| return [] | |||||
| for root, _, files in os.walk(filePath): | |||||
| for name in tuple(name for name in files if name.lower().endswith(extensions)): | |||||
| filenames.append(os.path.join(root, name)) | |||||
| return filenames | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Plugin Query | |||||
| # NOTE: this code is ugly, it is meant to be replaced, so let it be as-is for now | |||||
| PLUGIN_QUERY_API_VERSION = 12 | |||||
| PyPluginInfo = { | |||||
| 'API': PLUGIN_QUERY_API_VERSION, | |||||
| 'valid': False, | |||||
| 'build': BINARY_NONE, | |||||
| 'type': PLUGIN_NONE, | |||||
| 'hints': 0x0, | |||||
| 'category': "", | |||||
| 'filename': "", | |||||
| 'name': "", | |||||
| 'label': "", | |||||
| 'maker': "", | |||||
| 'uniqueId': 0, | |||||
| 'audio.ins': 0, | |||||
| 'audio.outs': 0, | |||||
| 'cv.ins': 0, | |||||
| 'cv.outs': 0, | |||||
| 'midi.ins': 0, | |||||
| 'midi.outs': 0, | |||||
| 'parameters.ins': 0, | |||||
| 'parameters.outs': 0 | |||||
| } | |||||
| gDiscoveryProcess = None | |||||
| def findWinePrefix(filename, recursionLimit = 10): | |||||
| if recursionLimit == 0 or len(filename) < 5 or "/" not in filename: | |||||
| return "" | |||||
| path = filename[:filename.rfind("/")] | |||||
| if os.path.isdir(path + "/dosdevices"): | |||||
| return path | |||||
| return findWinePrefix(path, recursionLimit-1) | |||||
| def runCarlaDiscovery(itype, stype, filename, tool, wineSettings=None): | |||||
| if not os.path.exists(tool): | |||||
| qWarning(f"runCarlaDiscovery() - tool '{tool}' does not exist") | |||||
| return [] | |||||
| command = [] | |||||
| if LINUX or MACOS: | |||||
| command.append("env") | |||||
| command.append("LANG=C") | |||||
| command.append("LD_PRELOAD=") | |||||
| if wineSettings is not None: | |||||
| command.append("WINEDEBUG=-all") | |||||
| if wineSettings['autoPrefix']: | |||||
| winePrefix = findWinePrefix(filename) | |||||
| else: | |||||
| winePrefix = "" | |||||
| if not winePrefix: | |||||
| envWinePrefix = os.getenv("WINEPREFIX") | |||||
| if envWinePrefix: | |||||
| winePrefix = envWinePrefix | |||||
| elif wineSettings['fallbackPrefix']: | |||||
| winePrefix = os.path.expanduser(wineSettings['fallbackPrefix']) | |||||
| else: | |||||
| winePrefix = os.path.expanduser("~/.wine") | |||||
| wineCMD = wineSettings['executable'] if wineSettings['executable'] else "wine" | |||||
| if tool.endswith("64.exe") and os.path.exists(wineCMD + "64"): | |||||
| wineCMD += "64" | |||||
| command.append("WINEPREFIX=" + winePrefix) | |||||
| command.append(wineCMD) | |||||
| command.append(tool) | |||||
| command.append(stype) | |||||
| command.append(filename) | |||||
| # pylint: disable=global-statement | |||||
| global gDiscoveryProcess | |||||
| # pylint: enable=global-statement | |||||
| # pylint: disable=consider-using-with | |||||
| gDiscoveryProcess = Popen(command, stdout=PIPE) | |||||
| # pylint: enable=consider-using-with | |||||
| pinfo = None | |||||
| plugins = [] | |||||
| fakeLabel = os.path.basename(filename).rsplit(".", 1)[0] | |||||
| while True: | |||||
| try: | |||||
| line = gDiscoveryProcess.stdout.readline().decode("utf-8", errors="ignore") | |||||
| except: | |||||
| print("ERROR: discovery readline failed") | |||||
| break | |||||
| # line is valid, strip it | |||||
| if line: | |||||
| line = line.strip() | |||||
| # line is invalid, try poll() again | |||||
| elif gDiscoveryProcess.poll() is None: | |||||
| continue | |||||
| # line is invalid and poll() failed, stop here | |||||
| else: | |||||
| break | |||||
| if line == "carla-discovery::init::-----------": | |||||
| pinfo = deepcopy(PyPluginInfo) | |||||
| pinfo['type'] = itype | |||||
| pinfo['filename'] = filename if filename != ":all" else "" | |||||
| elif line == "carla-discovery::end::------------": | |||||
| if pinfo is not None: | |||||
| plugins.append(pinfo) | |||||
| del pinfo | |||||
| pinfo = None | |||||
| elif line == "Segmentation fault": | |||||
| print(f"carla-discovery::crash::{filename} crashed during discovery") | |||||
| elif line.startswith("err:module:import_dll Library"): | |||||
| print(line) | |||||
| elif line.startswith("carla-discovery::info::"): | |||||
| print(f"{line} - {filename}") | |||||
| elif line.startswith("carla-discovery::warning::"): | |||||
| print(f"{line} - {filename}") | |||||
| elif line.startswith("carla-discovery::error::"): | |||||
| print(f"{line} - {filename}") | |||||
| elif line.startswith("carla-discovery::"): | |||||
| if pinfo is None: | |||||
| continue | |||||
| try: | |||||
| prop, value = line.replace("carla-discovery::", "").split("::", 1) | |||||
| except: | |||||
| continue | |||||
| # pylint: disable=unsupported-assignment-operation | |||||
| if prop == "build": | |||||
| if value.isdigit(): | |||||
| pinfo['build'] = int(value) | |||||
| elif prop == "name": | |||||
| pinfo['name'] = value if value else fakeLabel | |||||
| elif prop == "label": | |||||
| pinfo['label'] = value if value else fakeLabel | |||||
| elif prop == "filename": | |||||
| pinfo['filename'] = value | |||||
| elif prop == "maker": | |||||
| pinfo['maker'] = value | |||||
| elif prop == "category": | |||||
| pinfo['category'] = value | |||||
| elif prop == "uniqueId": | |||||
| if value.isdigit(): | |||||
| pinfo['uniqueId'] = int(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 == "cv.ins": | |||||
| if value.isdigit(): | |||||
| pinfo['cv.ins'] = int(value) | |||||
| elif prop == "cv.outs": | |||||
| if value.isdigit(): | |||||
| pinfo['cv.outs'] = 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 == "parameters.ins": | |||||
| if value.isdigit(): | |||||
| pinfo['parameters.ins'] = int(value) | |||||
| elif prop == "parameters.outs": | |||||
| if value.isdigit(): | |||||
| pinfo['parameters.outs'] = int(value) | |||||
| elif prop == "uri": | |||||
| if value: | |||||
| pinfo['label'] = value | |||||
| else: | |||||
| # cannot use empty URIs | |||||
| del pinfo | |||||
| pinfo = None | |||||
| continue | |||||
| else: | |||||
| print(f"{line} - {filename} (unknown property)") | |||||
| # pylint: enable=unsupported-assignment-operation | |||||
| tmp = gDiscoveryProcess | |||||
| gDiscoveryProcess = None | |||||
| del tmp | |||||
| return plugins | |||||
| def killDiscovery(): | |||||
| # pylint: disable=global-variable-not-assigned | |||||
| global gDiscoveryProcess | |||||
| # pylint: enable=global-variable-not-assigned | |||||
| if gDiscoveryProcess is not None: | |||||
| gDiscoveryProcess.kill() | |||||
| def checkPluginCached(desc, ptype): | |||||
| pinfo = deepcopy(PyPluginInfo) | |||||
| pinfo['build'] = BINARY_NATIVE | |||||
| pinfo['type'] = ptype | |||||
| pinfo['hints'] = desc['hints'] | |||||
| pinfo['name'] = desc['name'] | |||||
| pinfo['label'] = desc['label'] | |||||
| pinfo['maker'] = desc['maker'] | |||||
| pinfo['category'] = getPluginCategoryAsString(desc['category']) | |||||
| pinfo['audio.ins'] = desc['audioIns'] | |||||
| pinfo['audio.outs'] = desc['audioOuts'] | |||||
| pinfo['cv.ins'] = desc['cvIns'] | |||||
| pinfo['cv.outs'] = desc['cvOuts'] | |||||
| pinfo['midi.ins'] = desc['midiIns'] | |||||
| pinfo['midi.outs'] = desc['midiOuts'] | |||||
| pinfo['parameters.ins'] = desc['parameterIns'] | |||||
| pinfo['parameters.outs'] = desc['parameterOuts'] | |||||
| if ptype == PLUGIN_LV2: | |||||
| pinfo['filename'], pinfo['label'] = pinfo['label'].split('\\' if WINDOWS else '/',1) | |||||
| elif ptype == PLUGIN_SFZ: | |||||
| pinfo['filename'] = pinfo['label'] | |||||
| pinfo['label'] = pinfo['name'] | |||||
| return pinfo | |||||
| def checkPluginLADSPA(filename, tool, wineSettings=None): | |||||
| return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, wineSettings) | |||||
| def checkPluginDSSI(filename, tool, wineSettings=None): | |||||
| return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, wineSettings) | |||||
| def checkPluginLV2(filename, tool, wineSettings=None): | |||||
| return runCarlaDiscovery(PLUGIN_LV2, "LV2", filename, tool, wineSettings) | |||||
| def checkPluginVST2(filename, tool, wineSettings=None): | |||||
| return runCarlaDiscovery(PLUGIN_VST2, "VST2", filename, tool, wineSettings) | |||||
| def checkPluginVST3(filename, tool, wineSettings=None): | |||||
| return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) | |||||
| def checkFileSF2(filename, tool): | |||||
| return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) | |||||
| def checkFileSFZ(filename, tool): | |||||
| return runCarlaDiscovery(PLUGIN_SFZ, "SFZ", filename, tool) | |||||
| def checkAllPluginsAU(tool): | |||||
| return runCarlaDiscovery(PLUGIN_AU, "AU", ":all", tool) | |||||
| @@ -0,0 +1,772 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| import os | |||||
| from PyQt5.QtCore import pyqtSignal, QThread | |||||
| from PyQt5.QtWidgets import QWidget | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Custom) | |||||
| from carla_backend import ( | |||||
| PLUGIN_AU, | |||||
| PLUGIN_DSSI, | |||||
| PLUGIN_JSFX, | |||||
| PLUGIN_LADSPA, | |||||
| PLUGIN_LV2, | |||||
| PLUGIN_SFZ, | |||||
| PLUGIN_VST2, | |||||
| ) | |||||
| from carla_shared import ( | |||||
| CARLA_DEFAULT_DSSI_PATH, | |||||
| CARLA_DEFAULT_JSFX_PATH, | |||||
| CARLA_DEFAULT_LADSPA_PATH, | |||||
| CARLA_DEFAULT_LV2_PATH, | |||||
| CARLA_DEFAULT_SF2_PATH, | |||||
| CARLA_DEFAULT_SFZ_PATH, | |||||
| CARLA_DEFAULT_VST2_PATH, | |||||
| CARLA_DEFAULT_VST3_PATH, | |||||
| CARLA_DEFAULT_WINE_AUTO_PREFIX, | |||||
| CARLA_DEFAULT_WINE_EXECUTABLE, | |||||
| CARLA_DEFAULT_WINE_FALLBACK_PREFIX, | |||||
| CARLA_KEY_PATHS_DSSI, | |||||
| CARLA_KEY_PATHS_JSFX, | |||||
| CARLA_KEY_PATHS_LADSPA, | |||||
| CARLA_KEY_PATHS_LV2, | |||||
| CARLA_KEY_PATHS_SF2, | |||||
| CARLA_KEY_PATHS_SFZ, | |||||
| CARLA_KEY_PATHS_VST2, | |||||
| CARLA_KEY_PATHS_VST3, | |||||
| CARLA_KEY_WINE_AUTO_PREFIX, | |||||
| CARLA_KEY_WINE_EXECUTABLE, | |||||
| CARLA_KEY_WINE_FALLBACK_PREFIX, | |||||
| HAIKU, | |||||
| LINUX, | |||||
| MACOS, | |||||
| WINDOWS, | |||||
| gCarla, | |||||
| splitter, | |||||
| ) | |||||
| from utils import QSafeSettings | |||||
| from .discovery import ( | |||||
| checkAllPluginsAU, | |||||
| checkFileSF2, | |||||
| checkPluginCached, | |||||
| checkPluginDSSI, | |||||
| checkPluginLADSPA, | |||||
| checkPluginVST2, | |||||
| checkPluginVST3, | |||||
| findBinaries, | |||||
| findFilenames, | |||||
| findMacVSTBundles, | |||||
| findVST3Binaries | |||||
| ) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Separate Thread for Plugin Search | |||||
| class SearchPluginsThread(QThread): | |||||
| pluginLook = pyqtSignal(float, str) | |||||
| def __init__(self, parent: QWidget, pathBinaries: str): | |||||
| QThread.__init__(self, parent) | |||||
| self.fContinueChecking = False | |||||
| self.fPathBinaries = pathBinaries | |||||
| self.fCheckNative = False | |||||
| self.fCheckPosix32 = False | |||||
| self.fCheckPosix64 = False | |||||
| self.fCheckWin32 = False | |||||
| self.fCheckWin64 = False | |||||
| self.fCheckLADSPA = False | |||||
| self.fCheckDSSI = False | |||||
| self.fCheckLV2 = False | |||||
| self.fCheckVST2 = False | |||||
| self.fCheckVST3 = False | |||||
| self.fCheckAU = False | |||||
| self.fCheckSF2 = False | |||||
| self.fCheckSFZ = False | |||||
| self.fCheckJSFX = False | |||||
| if WINDOWS: | |||||
| toolNative = "carla-discovery-native.exe" | |||||
| self.fWineSettings = None | |||||
| else: | |||||
| toolNative = "carla-discovery-native" | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| self.fWineSettings = { | |||||
| 'executable' : settings.value(CARLA_KEY_WINE_EXECUTABLE, | |||||
| CARLA_DEFAULT_WINE_EXECUTABLE, str), | |||||
| 'autoPrefix' : settings.value(CARLA_KEY_WINE_AUTO_PREFIX, | |||||
| CARLA_DEFAULT_WINE_AUTO_PREFIX, bool), | |||||
| 'fallbackPrefix': settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, | |||||
| CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str), | |||||
| } | |||||
| del settings | |||||
| self.fToolNative = os.path.join(pathBinaries, toolNative) | |||||
| if not os.path.exists(self.fToolNative): | |||||
| self.fToolNative = "" | |||||
| self.fCurCount = 0 | |||||
| self.fCurPercentValue = 0 | |||||
| self.fLastCheckValue = 0 | |||||
| self.fSomethingChanged = False | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # public methods | |||||
| def hasSomethingChanged(self): | |||||
| return self.fSomethingChanged | |||||
| def setSearchBinaryTypes(self, native: bool, posix32: bool, posix64: bool, win32: bool, win64: bool): | |||||
| self.fCheckNative = native | |||||
| self.fCheckPosix32 = posix32 | |||||
| self.fCheckPosix64 = posix64 | |||||
| self.fCheckWin32 = win32 | |||||
| self.fCheckWin64 = win64 | |||||
| def setSearchPluginTypes(self, ladspa: bool, dssi: bool, lv2: bool, vst2: bool, vst3: bool, au: bool, | |||||
| sf2: bool, sfz: bool, jsfx: bool): | |||||
| self.fCheckLADSPA = ladspa | |||||
| self.fCheckDSSI = dssi | |||||
| self.fCheckLV2 = lv2 | |||||
| self.fCheckVST2 = vst2 | |||||
| self.fCheckVST3 = vst3 and (LINUX or MACOS or WINDOWS) | |||||
| self.fCheckAU = au and MACOS | |||||
| self.fCheckSF2 = sf2 | |||||
| self.fCheckSFZ = sfz | |||||
| self.fCheckJSFX = jsfx | |||||
| def stop(self): | |||||
| self.fContinueChecking = False | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # protected reimplemented methods | |||||
| def run(self): | |||||
| settingsDB = QSafeSettings("falkTX", "CarlaPlugins5") | |||||
| self.fContinueChecking = True | |||||
| self.fCurCount = 0 | |||||
| if self.fCheckNative and not self.fToolNative: | |||||
| self.fCheckNative = False | |||||
| # looking for plugins via external discovery | |||||
| pluginCount = 0 | |||||
| if self.fCheckLADSPA: | |||||
| pluginCount += 1 | |||||
| if self.fCheckDSSI: | |||||
| pluginCount += 1 | |||||
| if self.fCheckVST2: | |||||
| pluginCount += 1 | |||||
| if self.fCheckVST3: | |||||
| pluginCount += 1 | |||||
| # Increase count by the number of externally discoverable plugin types | |||||
| if self.fCheckNative: | |||||
| self.fCurCount += pluginCount | |||||
| # Linux, MacOS and Windows are the only VST3 supported OSes | |||||
| if self.fCheckVST3 and not (LINUX or MACOS or WINDOWS): | |||||
| self.fCurCount -= 1 | |||||
| if self.fCheckPosix32: | |||||
| self.fCurCount += pluginCount | |||||
| if self.fCheckVST3 and not (LINUX or MACOS): | |||||
| self.fCurCount -= 1 | |||||
| if self.fCheckPosix64: | |||||
| self.fCurCount += pluginCount | |||||
| if self.fCheckVST3 and not (LINUX or MACOS): | |||||
| self.fCurCount -= 1 | |||||
| if self.fCheckWin32: | |||||
| self.fCurCount += pluginCount | |||||
| if self.fCheckWin64: | |||||
| self.fCurCount += pluginCount | |||||
| if self.fCheckLV2: | |||||
| if self.fCheckNative: | |||||
| self.fCurCount += 1 | |||||
| else: | |||||
| self.fCheckLV2 = False | |||||
| if self.fCheckAU: | |||||
| if self.fCheckNative or self.fCheckPosix32: | |||||
| self.fCurCount += int(self.fCheckNative) + int(self.fCheckPosix32) | |||||
| else: | |||||
| self.fCheckAU = False | |||||
| if self.fCheckSF2: | |||||
| if self.fCheckNative: | |||||
| self.fCurCount += 1 | |||||
| else: | |||||
| self.fCheckSF2 = False | |||||
| if self.fCheckSFZ: | |||||
| if self.fCheckNative: | |||||
| self.fCurCount += 1 | |||||
| else: | |||||
| self.fCheckSFZ = False | |||||
| if self.fCheckJSFX: | |||||
| if self.fCheckNative: | |||||
| self.fCurCount += 1 | |||||
| else: | |||||
| self.fCheckJSFX = False | |||||
| if self.fCurCount == 0: | |||||
| return | |||||
| self.fCurPercentValue = 100.0 / self.fCurCount | |||||
| self.fLastCheckValue = 0.0 | |||||
| del pluginCount | |||||
| if HAIKU: | |||||
| OS = "HAIKU" | |||||
| elif LINUX: | |||||
| OS = "LINUX" | |||||
| elif MACOS: | |||||
| OS = "MACOS" | |||||
| elif WINDOWS: | |||||
| OS = "WINDOWS" | |||||
| else: | |||||
| OS = "UNKNOWN" | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| self.fSomethingChanged = True | |||||
| if self.fCheckLADSPA: | |||||
| if self.fCheckNative: | |||||
| plugins = self._checkLADSPA(OS, self.fToolNative) | |||||
| settingsDB.setValue("Plugins/LADSPA_native", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix32: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-posix32") | |||||
| plugins = self._checkLADSPA(OS, tool) | |||||
| settingsDB.setValue("Plugins/LADSPA_posix32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix64: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-posix64") | |||||
| plugins = self._checkLADSPA(OS, tool) | |||||
| settingsDB.setValue("Plugins/LADSPA_posix64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin32: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||||
| plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/LADSPA_win32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin64: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||||
| plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/LADSPA_win64", plugins) | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckDSSI: | |||||
| if self.fCheckNative: | |||||
| plugins = self._checkDSSI(OS, self.fToolNative) | |||||
| settingsDB.setValue("Plugins/DSSI_native", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix32: | |||||
| plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||||
| settingsDB.setValue("Plugins/DSSI_posix32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix64: | |||||
| plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||||
| settingsDB.setValue("Plugins/DSSI_posix64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin32: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||||
| plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/DSSI_win32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin64: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||||
| plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/DSSI_win64", plugins) | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckLV2: | |||||
| plugins = self._checkCached(True) | |||||
| settingsDB.setValue("Plugins/LV2", plugins) | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckVST2: | |||||
| if self.fCheckNative: | |||||
| plugins = self._checkVST2(OS, self.fToolNative) | |||||
| settingsDB.setValue("Plugins/VST2_native", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix32: | |||||
| plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||||
| settingsDB.setValue("Plugins/VST2_posix32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix64: | |||||
| plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||||
| settingsDB.setValue("Plugins/VST2_posix64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin32: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||||
| plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/VST2_win32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin64: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||||
| plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/VST2_win64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckVST3: | |||||
| if self.fCheckNative and (LINUX or MACOS or WINDOWS): | |||||
| plugins = self._checkVST3(self.fToolNative) | |||||
| settingsDB.setValue("Plugins/VST3_native", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix32: | |||||
| plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||||
| settingsDB.setValue("Plugins/VST3_posix32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix64: | |||||
| plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||||
| settingsDB.setValue("Plugins/VST3_posix64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin32: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||||
| plugins = self._checkVST3(tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/VST3_win32", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckWin64: | |||||
| tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||||
| plugins = self._checkVST3(tool, not WINDOWS) | |||||
| settingsDB.setValue("Plugins/VST3_win64", plugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckAU: | |||||
| if self.fCheckNative: | |||||
| plugins = self._checkCached(False) | |||||
| settingsDB.setValue("Plugins/AU", plugins) | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckPosix32: | |||||
| plugins = self._checkAU(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||||
| settingsDB.setValue("Plugins/AU_posix32", self.fAuPlugins) | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckSF2: | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| SF2_PATH = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list) | |||||
| del settings | |||||
| kits = self._checkKIT(SF2_PATH, "sf2") | |||||
| settingsDB.setValue("Plugins/SF2", kits) | |||||
| settingsDB.sync() | |||||
| if not self.fContinueChecking: | |||||
| return | |||||
| if self.fCheckSFZ: | |||||
| kits = self._checkSfzCached() | |||||
| settingsDB.setValue("Plugins/SFZ", kits) | |||||
| settingsDB.sync() | |||||
| if self.fCheckJSFX: | |||||
| kits = self._checkJsfxCached() | |||||
| settingsDB.setValue("Plugins/JSFX", kits) | |||||
| settingsDB.sync() | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # private methods | |||||
| def _checkLADSPA(self, OS, tool, isWine=False): | |||||
| ladspaBinaries = [] | |||||
| ladspaPlugins = [] | |||||
| self._pluginLook(self.fLastCheckValue, "LADSPA plugins...") | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| LADSPA_PATH = settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list) | |||||
| del settings | |||||
| for iPATH in LADSPA_PATH: | |||||
| binaries = findBinaries(iPATH, PLUGIN_LADSPA, OS) | |||||
| for binary in binaries: | |||||
| if binary not in ladspaBinaries: | |||||
| ladspaBinaries.append(binary) | |||||
| ladspaBinaries.sort() | |||||
| if not self.fContinueChecking: | |||||
| return ladspaPlugins | |||||
| for i in range(len(ladspaBinaries)): | |||||
| ladspa = ladspaBinaries[i] | |||||
| percent = ( float(i) / len(ladspaBinaries) ) * self.fCurPercentValue | |||||
| self._pluginLook((self.fLastCheckValue + percent) * 0.9, ladspa) | |||||
| plugins = checkPluginLADSPA(ladspa, tool, self.fWineSettings if isWine else None) | |||||
| if plugins: | |||||
| ladspaPlugins.append(plugins) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return ladspaPlugins | |||||
| def _checkDSSI(self, OS, tool, isWine=False): | |||||
| dssiBinaries = [] | |||||
| dssiPlugins = [] | |||||
| self._pluginLook(self.fLastCheckValue, "DSSI plugins...") | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| DSSI_PATH = settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list) | |||||
| del settings | |||||
| for iPATH in DSSI_PATH: | |||||
| binaries = findBinaries(iPATH, PLUGIN_DSSI, OS) | |||||
| for binary in binaries: | |||||
| if binary not in dssiBinaries: | |||||
| dssiBinaries.append(binary) | |||||
| dssiBinaries.sort() | |||||
| if not self.fContinueChecking: | |||||
| return dssiPlugins | |||||
| for i in range(len(dssiBinaries)): | |||||
| dssi = dssiBinaries[i] | |||||
| percent = ( float(i) / len(dssiBinaries) ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, dssi) | |||||
| plugins = checkPluginDSSI(dssi, tool, self.fWineSettings if isWine else None) | |||||
| if plugins: | |||||
| dssiPlugins.append(plugins) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return dssiPlugins | |||||
| def _checkVST2(self, OS, tool, isWine=False): | |||||
| vst2Binaries = [] | |||||
| vst2Plugins = [] | |||||
| if MACOS and not isWine: | |||||
| self._pluginLook(self.fLastCheckValue, "VST2 bundles...") | |||||
| else: | |||||
| self._pluginLook(self.fLastCheckValue, "VST2 plugins...") | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| VST2_PATH = settings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list) | |||||
| del settings | |||||
| for iPATH in VST2_PATH: | |||||
| if MACOS and not isWine: | |||||
| binaries = findMacVSTBundles(iPATH, False) | |||||
| else: | |||||
| binaries = findBinaries(iPATH, PLUGIN_VST2, OS) | |||||
| for binary in binaries: | |||||
| if binary not in vst2Binaries: | |||||
| vst2Binaries.append(binary) | |||||
| vst2Binaries.sort() | |||||
| if not self.fContinueChecking: | |||||
| return vst2Plugins | |||||
| for i in range(len(vst2Binaries)): | |||||
| vst2 = vst2Binaries[i] | |||||
| percent = ( float(i) / len(vst2Binaries) ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, vst2) | |||||
| plugins = checkPluginVST2(vst2, tool, self.fWineSettings if isWine else None) | |||||
| if plugins: | |||||
| vst2Plugins.append(plugins) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return vst2Plugins | |||||
| def _checkVST3(self, tool, isWine=False): | |||||
| vst3Binaries = [] | |||||
| vst3Plugins = [] | |||||
| if MACOS and not isWine: | |||||
| self._pluginLook(self.fLastCheckValue, "VST3 bundles...") | |||||
| else: | |||||
| self._pluginLook(self.fLastCheckValue, "VST3 plugins...") | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| VST3_PATH = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list) | |||||
| del settings | |||||
| for iPATH in VST3_PATH: | |||||
| if MACOS and not isWine: | |||||
| binaries = findMacVSTBundles(iPATH, True) | |||||
| else: | |||||
| binaries = findVST3Binaries(iPATH) | |||||
| for binary in binaries: | |||||
| if binary not in vst3Binaries: | |||||
| vst3Binaries.append(binary) | |||||
| vst3Binaries.sort() | |||||
| if not self.fContinueChecking: | |||||
| return vst3Plugins | |||||
| for i in range(len(vst3Binaries)): | |||||
| vst3 = vst3Binaries[i] | |||||
| percent = ( float(i) / len(vst3Binaries) ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, vst3) | |||||
| plugins = checkPluginVST3(vst3, tool, self.fWineSettings if isWine else None) | |||||
| if plugins: | |||||
| vst3Plugins.append(plugins) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return vst3Plugins | |||||
| def _checkAU(self, tool): | |||||
| auPlugins = [] | |||||
| plugins = checkAllPluginsAU(tool) | |||||
| if plugins: | |||||
| auPlugins.append(plugins) | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return auPlugins | |||||
| def _checkKIT(self, kitPATH, kitExtension): | |||||
| kitFiles = [] | |||||
| kitPlugins = [] | |||||
| for iPATH in kitPATH: | |||||
| files = findFilenames(iPATH, kitExtension) | |||||
| for file_ in files: | |||||
| if file_ not in kitFiles: | |||||
| kitFiles.append(file_) | |||||
| kitFiles.sort() | |||||
| if not self.fContinueChecking: | |||||
| return kitPlugins | |||||
| for i in range(len(kitFiles)): | |||||
| kit = kitFiles[i] | |||||
| percent = ( float(i) / len(kitFiles) ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, kit) | |||||
| if kitExtension == "sf2": | |||||
| plugins = checkFileSF2(kit, self.fToolNative) | |||||
| else: | |||||
| plugins = None | |||||
| if plugins: | |||||
| kitPlugins.append(plugins) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return kitPlugins | |||||
| def _checkCached(self, isLV2): | |||||
| if isLV2: | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)) | |||||
| del settings | |||||
| PLUG_TEXT = "LV2" | |||||
| PLUG_TYPE = PLUGIN_LV2 | |||||
| else: # AU | |||||
| PLUG_PATH = "" | |||||
| PLUG_TEXT = "AU" | |||||
| PLUG_TYPE = PLUGIN_AU | |||||
| plugins = [] | |||||
| self._pluginLook(self.fLastCheckValue, f"{PLUG_TEXT} plugins...") | |||||
| if not isLV2: | |||||
| gCarla.utils.juce_init() | |||||
| count = gCarla.utils.get_cached_plugin_count(PLUG_TYPE, PLUG_PATH) | |||||
| if not self.fContinueChecking: | |||||
| return plugins | |||||
| for i in range(count): | |||||
| descInfo = gCarla.utils.get_cached_plugin_info(PLUG_TYPE, i) | |||||
| percent = ( float(i) / count ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||||
| if not descInfo['valid']: | |||||
| continue | |||||
| plugins.append(checkPluginCached(descInfo, PLUG_TYPE)) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| if not isLV2: | |||||
| gCarla.utils.juce_cleanup() | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return plugins | |||||
| def _checkSfzCached(self): | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)) | |||||
| del settings | |||||
| sfzKits = [] | |||||
| self._pluginLook(self.fLastCheckValue, "SFZ kits...") | |||||
| count = gCarla.utils.get_cached_plugin_count(PLUGIN_SFZ, PLUG_PATH) | |||||
| if not self.fContinueChecking: | |||||
| return sfzKits | |||||
| for i in range(count): | |||||
| descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_SFZ, i) | |||||
| percent = ( float(i) / count ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||||
| if not descInfo['valid']: | |||||
| continue | |||||
| sfzKits.append(checkPluginCached(descInfo, PLUGIN_SFZ)) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return sfzKits | |||||
| def _checkJsfxCached(self): | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)) | |||||
| del settings | |||||
| jsfxPlugins = [] | |||||
| self._pluginLook(self.fLastCheckValue, "JSFX plugins...") | |||||
| count = gCarla.utils.get_cached_plugin_count(PLUGIN_JSFX, PLUG_PATH) | |||||
| if not self.fContinueChecking: | |||||
| return jsfxPlugins | |||||
| for i in range(count): | |||||
| descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_JSFX, i) | |||||
| percent = ( float(i) / count ) * self.fCurPercentValue | |||||
| self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||||
| if not descInfo['valid']: | |||||
| continue | |||||
| jsfxPlugins.append(checkPluginCached(descInfo, PLUGIN_JSFX)) | |||||
| if not self.fContinueChecking: | |||||
| break | |||||
| self.fLastCheckValue += self.fCurPercentValue | |||||
| return jsfxPlugins | |||||
| def _pluginLook(self, percent, plugin): | |||||
| self.pluginLook.emit(percent, plugin) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,264 @@ | |||||
| /* | |||||
| * Carla plugin host | |||||
| * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #include "jackappdialog.hpp" | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-register" | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wclass-memaccess" | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-copy" | |||||
| #endif | |||||
| #include "jackappdialog_ui.hpp" | |||||
| #include <QtCore/QFileInfo> | |||||
| #include <QtCore/QVector> | |||||
| #include <QtWidgets/QPushButton> | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| #include "../utils/qsafesettings.hpp" | |||||
| #include "../../includes/CarlaLibJackHints.h" | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Jack Application Dialog | |||||
| enum { | |||||
| UI_SESSION_NONE = 0, | |||||
| UI_SESSION_LADISH = 1, | |||||
| UI_SESSION_NSM = 2, | |||||
| }; | |||||
| struct JackApplicationW::Self { | |||||
| Ui_Dialog ui; | |||||
| const QString fProjectFilename; | |||||
| Self(const char* const projectFilename) | |||||
| : fProjectFilename(projectFilename) {} | |||||
| static Self& create(const char* const projectFilename) | |||||
| { | |||||
| Self* const self = new Self(projectFilename); | |||||
| return *self; | |||||
| } | |||||
| }; | |||||
| JackApplicationW::JackApplicationW(QWidget* const parent, const char* const projectFilename) | |||||
| : QDialog(parent), | |||||
| self(Self::create(projectFilename)) | |||||
| { | |||||
| self.ui.setupUi(this); | |||||
| // ------------------------------------------------------------------------------------------------------------- | |||||
| // UI setup | |||||
| self.ui.group_error->setVisible(false); | |||||
| adjustSize(); | |||||
| setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | |||||
| // ------------------------------------------------------------------------------------------------------------- | |||||
| // Load settings | |||||
| loadSettings(); | |||||
| // ------------------------------------------------------------------------------------------------------------- | |||||
| // Set-up connections | |||||
| connect(this, &QDialog::finished, | |||||
| this, &JackApplicationW::slot_saveSettings); | |||||
| connect(self.ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), | |||||
| this, &JackApplicationW::slot_sessionManagerChanged); | |||||
| connect(self.ui.le_command, &QLineEdit::textChanged, | |||||
| this, &JackApplicationW::slot_commandChanged); | |||||
| } | |||||
| JackApplicationW::~JackApplicationW() | |||||
| { | |||||
| delete &self; | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // public methods | |||||
| JackApplicationW::CommandAndFlags JackApplicationW::getCommandAndFlags() const | |||||
| { | |||||
| const QString command = self.ui.le_command->text(); | |||||
| QString name = self.ui.le_name->text(); | |||||
| if (name.isEmpty()) | |||||
| { | |||||
| name = QFileInfo(command.split(' ').first()).baseName(); | |||||
| name[0] = name[0].toTitleCase(); | |||||
| } | |||||
| SessionManager smgr; | |||||
| switch(self.ui.cb_session_mgr->currentIndex()) | |||||
| { | |||||
| case UI_SESSION_LADISH: | |||||
| smgr = LIBJACK_SESSION_MANAGER_LADISH; | |||||
| break; | |||||
| case UI_SESSION_NSM: | |||||
| smgr = LIBJACK_SESSION_MANAGER_NSM; | |||||
| break; | |||||
| default: | |||||
| smgr = LIBJACK_SESSION_MANAGER_NONE; | |||||
| break; | |||||
| } | |||||
| uint flags = 0x0; | |||||
| if (self.ui.cb_manage_window->isChecked()) | |||||
| flags |= LIBJACK_FLAG_CONTROL_WINDOW; | |||||
| if (self.ui.cb_capture_first_window->isChecked()) | |||||
| flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW; | |||||
| if (self.ui.cb_buffers_addition_mode->isChecked()) | |||||
| flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION; | |||||
| if (self.ui.cb_out_midi_mixdown->isChecked()) | |||||
| flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN; | |||||
| if (self.ui.cb_external_start->isChecked()) | |||||
| flags |= LIBJACK_FLAG_EXTERNAL_START; | |||||
| const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value())) | |||||
| .arg(QChar('0' + self.ui.sb_audio_outs->value())) | |||||
| .arg(QChar('0' + self.ui.sb_midi_ins->value())) | |||||
| .arg(QChar('0' + self.ui.sb_midi_outs->value())) | |||||
| .arg(QChar('0' + smgr)) | |||||
| .arg(QChar('0' + flags))); | |||||
| return {command, name, labelSetup}; | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // private methods | |||||
| void JackApplicationW::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command) | |||||
| { | |||||
| bool enabled = command.isNotEmpty(); | |||||
| QCarlaString showErr; | |||||
| // NSM applications must not be abstract or absolute paths, and must not contain arguments | |||||
| if (enabled and index == UI_SESSION_NSM) | |||||
| { | |||||
| if (QVector<QChar>{'.', '/'}.contains(command[0])) | |||||
| showErr = tr("NSM applications cannot use abstract or absolute paths"); | |||||
| else if (command.contains(' ') or command.contains(';') or command.contains('&')) | |||||
| showErr = tr("NSM applications cannot use CLI arguments"); | |||||
| else if (self.fProjectFilename.isEmpty()) | |||||
| showErr = tr("You need to save the current Carla project before NSM can be used"); | |||||
| } | |||||
| if (showErr.isNotEmpty()) | |||||
| { | |||||
| enabled = false; | |||||
| self.ui.l_error->setText(showErr); | |||||
| self.ui.group_error->setVisible(true); | |||||
| } | |||||
| else | |||||
| { | |||||
| self.ui.group_error->setVisible(false); | |||||
| } | |||||
| if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok)) | |||||
| button->setEnabled(enabled); | |||||
| } | |||||
| void JackApplicationW::loadSettings() | |||||
| { | |||||
| const QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||||
| const QString smName = settings.valueString("SessionManager", ""); | |||||
| if (smName == "LADISH (SIGUSR1)") | |||||
| self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH); | |||||
| else if (smName == "NSM") | |||||
| self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM); | |||||
| else | |||||
| self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE); | |||||
| self.ui.le_command->setText(settings.valueString("Command", "")); | |||||
| self.ui.le_name->setText(settings.valueString("Name", "")); | |||||
| self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||||
| self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||||
| self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2)); | |||||
| self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0)); | |||||
| self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0)); | |||||
| self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true)); | |||||
| self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false)); | |||||
| self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false)); | |||||
| checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), | |||||
| self.ui.le_command->text()); | |||||
| } | |||||
| // ----------------------------------------------------------------------------------------------------------------- | |||||
| // private slots | |||||
| void JackApplicationW::slot_commandChanged(const QString& command) | |||||
| { | |||||
| checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command); | |||||
| } | |||||
| void JackApplicationW::slot_sessionManagerChanged(const int index) | |||||
| { | |||||
| checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text()); | |||||
| } | |||||
| void JackApplicationW::slot_saveSettings() | |||||
| { | |||||
| QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||||
| settings.setValue("Command", self.ui.le_command->text()); | |||||
| settings.setValue("Name", self.ui.le_name->text()); | |||||
| settings.setValue("SessionManager", self.ui.cb_session_mgr->currentText()); | |||||
| settings.setValue("NumAudioIns", self.ui.sb_audio_ins->value()); | |||||
| settings.setValue("NumAudioOuts", self.ui.sb_audio_outs->value()); | |||||
| settings.setValue("NumMidiIns", self.ui.sb_midi_ins->value()); | |||||
| settings.setValue("NumMidiOuts", self.ui.sb_midi_outs->value()); | |||||
| settings.setValue("ManageWindow", self.ui.cb_manage_window->isChecked()); | |||||
| settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window->isChecked()); | |||||
| settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown->isChecked()); | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Testing | |||||
| #include "../utils/qsafesettings.cpp" | |||||
| int main(int argc, char* argv[]) | |||||
| { | |||||
| QApplication app(argc, argv); | |||||
| JackApplicationW gui(nullptr, ""); | |||||
| gui.show(); | |||||
| if (gui.exec()) | |||||
| { | |||||
| auto cf = gui.getCommandAndFlags(); | |||||
| printf("Results:\n"); | |||||
| printf("\tCommand: %s\n", cf.command.toUtf8().constData()); | |||||
| printf("\tName: %s\n", cf.name.toUtf8().constData()); | |||||
| printf("\tLabelSetup: %s\n", cf.labelSetup.toUtf8().constData()); | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,76 @@ | |||||
| /* | |||||
| * Carla plugin host | |||||
| * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-register" | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wclass-memaccess" | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-copy" | |||||
| #endif | |||||
| #include <QtWidgets/QDialog> | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| #include "../utils/qcarlastring.hpp" | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| // Jack Application Dialog | |||||
| class JackApplicationW : public QDialog | |||||
| { | |||||
| struct Self; | |||||
| Self& self; | |||||
| public: | |||||
| explicit JackApplicationW(QWidget* parent, const char* projectFilename); | |||||
| ~JackApplicationW() override; | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // public methods | |||||
| struct CommandAndFlags { | |||||
| QString command; | |||||
| QString name; | |||||
| QString labelSetup; | |||||
| }; | |||||
| CommandAndFlags getCommandAndFlags() const; | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // private methods | |||||
| private: | |||||
| void checkIfButtonBoxShouldBeEnabled(int index, const QCarlaString& text); | |||||
| void loadSettings(); | |||||
| // ---------------------------------------------------------------------------------------------------------------- | |||||
| // private slots | |||||
| private slots: | |||||
| void slot_commandChanged(const QString& command); | |||||
| void slot_sessionManagerChanged(int); | |||||
| void slot_saveSettings(); | |||||
| }; | |||||
| // -------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,221 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| import os | |||||
| from PyQt5.QtCore import pyqtSlot, Qt | |||||
| from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QWidget | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Carla) | |||||
| from utils import QSafeSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Local) | |||||
| from .jackappdialog_ui import Ui_Dialog | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (API) | |||||
| SESSION_MGR_NONE = 0 | |||||
| SESSION_MGR_AUTO = 1 | |||||
| SESSION_MGR_JACK = 2 | |||||
| SESSION_MGR_LADISH = 3 | |||||
| SESSION_MGR_NSM = 4 | |||||
| FLAG_CONTROL_WINDOW = 0x01 | |||||
| FLAG_CAPTURE_FIRST_WINDOW = 0x02 | |||||
| FLAG_BUFFERS_ADDITION_MODE = 0x10 | |||||
| FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN = 0x20 | |||||
| FLAG_EXTERNAL_START = 0x40 | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Jack Application Dialog | |||||
| UI_SESSION_NONE = 0 | |||||
| UI_SESSION_LADISH = 1 | |||||
| UI_SESSION_NSM = 2 | |||||
| class JackApplicationW(QDialog): | |||||
| def __init__(self, parent: QWidget, projectFilename: str): | |||||
| QDialog.__init__(self, parent) | |||||
| self.ui = Ui_Dialog() | |||||
| self.ui.setupUi(self) | |||||
| self.fProjectFilename = projectFilename | |||||
| # -------------------------------------------------------------------------------------------------------------- | |||||
| # UI setup | |||||
| self.ui.group_error.setVisible(False) | |||||
| self.adjustSize() | |||||
| self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) | |||||
| # -------------------------------------------------------------------------------------------------------------- | |||||
| # Load settings | |||||
| self._loadSettings() | |||||
| # -------------------------------------------------------------------------------------------------------------- | |||||
| # Set-up connections | |||||
| self.finished.connect(self._slot_saveSettings) | |||||
| self.ui.cb_session_mgr.currentIndexChanged.connect(self._slot_sessionManagerChanged) | |||||
| self.ui.le_command.textChanged.connect(self._slot_commandChanged) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # public methods | |||||
| def getCommandAndFlags(self): | |||||
| name = self.ui.le_name.text() | |||||
| command = self.ui.le_command.text() | |||||
| smgr = SESSION_MGR_NONE | |||||
| flags = 0x0 | |||||
| if not name: | |||||
| name = os.path.basename(command.split(" ",1)[0]).title() | |||||
| uiSessionMgrIndex = self.ui.cb_session_mgr.currentIndex() | |||||
| if uiSessionMgrIndex == UI_SESSION_LADISH: | |||||
| smgr = SESSION_MGR_LADISH | |||||
| elif uiSessionMgrIndex == UI_SESSION_NSM: | |||||
| smgr = SESSION_MGR_NSM | |||||
| if self.ui.cb_manage_window.isChecked(): | |||||
| flags |= FLAG_CONTROL_WINDOW | |||||
| if self.ui.cb_capture_first_window.isChecked(): | |||||
| flags |= FLAG_CAPTURE_FIRST_WINDOW | |||||
| if self.ui.cb_buffers_addition_mode.isChecked(): | |||||
| flags |= FLAG_BUFFERS_ADDITION_MODE | |||||
| if self.ui.cb_out_midi_mixdown.isChecked(): | |||||
| flags |= FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN | |||||
| if self.ui.cb_external_start.isChecked(): | |||||
| flags |= FLAG_EXTERNAL_START | |||||
| bv = ord('0') | |||||
| v1 = chr(bv + self.ui.sb_audio_ins.value()) | |||||
| v2 = chr(bv + self.ui.sb_audio_outs.value()) | |||||
| v3 = chr(bv + self.ui.sb_midi_ins.value()) | |||||
| v4 = chr(bv + self.ui.sb_midi_outs.value()) | |||||
| v5 = chr(bv + smgr) | |||||
| v6 = chr(bv + flags) | |||||
| labelSetup = f"{v1}{v2}{v3}{v4}{v5}{v6}" | |||||
| return (command, name, labelSetup) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # private methods | |||||
| def _checkIfButtonBoxShouldBeEnabled(self, index: int, command: str): | |||||
| enabled = len(command) > 0 | |||||
| showErr = "" | |||||
| # NSM applications must not be abstract or absolute paths, and must not contain arguments | |||||
| if enabled and index == UI_SESSION_NSM: | |||||
| if command[0] in (".", "/"): | |||||
| showErr = self.tr("NSM applications cannot use abstract or absolute paths") | |||||
| elif " " in command or ";" in command or "&" in command: | |||||
| showErr = self.tr("NSM applications cannot use CLI arguments") | |||||
| elif not self.fProjectFilename: | |||||
| showErr = self.tr("You need to save the current Carla project before NSM can be used") | |||||
| if showErr: | |||||
| enabled = False | |||||
| self.ui.l_error.setText(showErr) | |||||
| self.ui.group_error.setVisible(True) | |||||
| else: | |||||
| self.ui.group_error.setVisible(False) | |||||
| self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enabled) | |||||
| def _loadSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaAddJackApp") | |||||
| smName = settings.value("SessionManager", "", str) | |||||
| if smName == "LADISH (SIGUSR1)": | |||||
| self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_LADISH) | |||||
| elif smName == "NSM": | |||||
| self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_NSM) | |||||
| else: | |||||
| self.ui.cb_session_mgr.setCurrentIndex(UI_SESSION_NONE) | |||||
| self.ui.le_command.setText(settings.value("Command", "", str)) | |||||
| self.ui.le_name.setText(settings.value("Name", "", str)) | |||||
| self.ui.sb_audio_ins.setValue(settings.value("NumAudioIns", 2, int)) | |||||
| self.ui.sb_audio_ins.setValue(settings.value("NumAudioIns", 2, int)) | |||||
| self.ui.sb_audio_outs.setValue(settings.value("NumAudioOuts", 2, int)) | |||||
| self.ui.sb_midi_ins.setValue(settings.value("NumMidiIns", 0, int)) | |||||
| self.ui.sb_midi_outs.setValue(settings.value("NumMidiOuts", 0, int)) | |||||
| self.ui.cb_manage_window.setChecked(settings.value("ManageWindow", True, bool)) | |||||
| self.ui.cb_capture_first_window.setChecked(settings.value("CaptureFirstWindow", False, bool)) | |||||
| self.ui.cb_out_midi_mixdown.setChecked(settings.value("MidiOutMixdown", False, bool)) | |||||
| self._checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(), | |||||
| self.ui.le_command.text()) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| # private slots | |||||
| @pyqtSlot(str) | |||||
| def _slot_commandChanged(self, command: str): | |||||
| self._checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr.currentIndex(), command) | |||||
| @pyqtSlot(int) | |||||
| def _slot_sessionManagerChanged(self, index: int): | |||||
| self._checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command.text()) | |||||
| @pyqtSlot() | |||||
| def _slot_saveSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaAddJackApp") | |||||
| settings.setValue("Command", self.ui.le_command.text()) | |||||
| settings.setValue("Name", self.ui.le_name.text()) | |||||
| settings.setValue("SessionManager", self.ui.cb_session_mgr.currentText()) | |||||
| settings.setValue("NumAudioIns", self.ui.sb_audio_ins.value()) | |||||
| settings.setValue("NumAudioOuts", self.ui.sb_audio_outs.value()) | |||||
| settings.setValue("NumMidiIns", self.ui.sb_midi_ins.value()) | |||||
| settings.setValue("NumMidiOuts", self.ui.sb_midi_outs.value()) | |||||
| settings.setValue("ManageWindow", self.ui.cb_manage_window.isChecked()) | |||||
| settings.setValue("CaptureFirstWindow", self.ui.cb_capture_first_window.isChecked()) | |||||
| settings.setValue("MidiOutMixdown", self.ui.cb_out_midi_mixdown.isChecked()) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Testing | |||||
| if __name__ == '__main__': | |||||
| import sys | |||||
| # pylint: disable=ungrouped-imports | |||||
| from PyQt5.QtWidgets import QApplication | |||||
| # pylint: enable=ungrouped-imports | |||||
| _app = QApplication(sys.argv) | |||||
| _gui = JackApplicationW(None, "") | |||||
| if _gui.exec_(): | |||||
| _command, _name, _labelSetup = _gui.getCommandAndFlags() | |||||
| print("Results:") | |||||
| print(f"\tCommand: {_command}") | |||||
| print(f"\tName: {_name}") | |||||
| print(f"\tLabelSetup: {_labelSetup}") | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,971 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| import os | |||||
| from PyQt5.QtCore import pyqtSlot, Qt, QByteArray, QEventLoop | |||||
| from PyQt5.QtWidgets import QApplication, QDialog, QHeaderView, QTableWidgetItem, QWidget | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Carla) | |||||
| from carla_backend import ( | |||||
| BINARY_NATIVE, | |||||
| BINARY_OTHER, | |||||
| BINARY_POSIX32, | |||||
| BINARY_POSIX64, | |||||
| BINARY_WIN32, | |||||
| BINARY_WIN64, | |||||
| PLUGIN_AU, | |||||
| PLUGIN_DSSI, | |||||
| PLUGIN_HAS_CUSTOM_UI, | |||||
| PLUGIN_HAS_INLINE_DISPLAY, | |||||
| PLUGIN_INTERNAL, | |||||
| PLUGIN_IS_BRIDGE, | |||||
| PLUGIN_IS_RTSAFE, | |||||
| PLUGIN_IS_SYNTH, | |||||
| PLUGIN_JSFX, | |||||
| PLUGIN_LADSPA, | |||||
| PLUGIN_LV2, | |||||
| PLUGIN_SF2, | |||||
| PLUGIN_SFZ, | |||||
| PLUGIN_VST2, | |||||
| PLUGIN_VST3, | |||||
| ) | |||||
| from carla_shared import ( | |||||
| CARLA_DEFAULT_LV2_PATH, | |||||
| CARLA_KEY_PATHS_LV2, | |||||
| HAIKU, | |||||
| LINUX, | |||||
| MACOS, | |||||
| WINDOWS, | |||||
| fontMetricsHorizontalAdvance, | |||||
| gCarla, | |||||
| getIcon, | |||||
| kIs64bit, | |||||
| splitter, | |||||
| ) | |||||
| from carla_utils import getPluginTypeAsString | |||||
| from utils import QSafeSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Local) | |||||
| from .discovery import PLUGIN_QUERY_API_VERSION, checkPluginCached | |||||
| from .pluginlistdialog_ui import Ui_PluginDatabaseW | |||||
| from .pluginlistrefreshdialog import PluginRefreshW | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Plugin Database Dialog | |||||
| class PluginDatabaseW(QDialog): | |||||
| TABLEWIDGET_ITEM_FAVORITE = 0 | |||||
| TABLEWIDGET_ITEM_NAME = 1 | |||||
| TABLEWIDGET_ITEM_LABEL = 2 | |||||
| TABLEWIDGET_ITEM_MAKER = 3 | |||||
| TABLEWIDGET_ITEM_BINARY = 4 | |||||
| def __init__(self, parent: QWidget, host, useSystemIcons: bool): | |||||
| QDialog.__init__(self, parent) | |||||
| self.host = host | |||||
| self.ui = Ui_PluginDatabaseW() | |||||
| self.ui.setupUi(self) | |||||
| # To be changed by parent | |||||
| self.hasLoadedLv2Plugins = False | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Internal stuff | |||||
| self.fLastTableIndex = 0 | |||||
| self.fRetPlugin = None | |||||
| self.fRealParent = parent | |||||
| self.fFavoritePlugins = [] | |||||
| self.fFavoritePluginsChanged = False | |||||
| self.fUseSystemIcons = useSystemIcons | |||||
| self.fTrYes = self.tr("Yes") | |||||
| self.fTrNo = self.tr("No") | |||||
| self.fTrNative = self.tr("Native") | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Set-up GUI | |||||
| self.ui.b_add.setEnabled(False) | |||||
| self.addAction(self.ui.act_focus_search) | |||||
| self.ui.act_focus_search.triggered.connect(self.slot_focusSearchFieldAndSelectAll) | |||||
| if BINARY_NATIVE in (BINARY_POSIX32, BINARY_WIN32): | |||||
| self.ui.ch_bridged.setText(self.tr("Bridged (64bit)")) | |||||
| else: | |||||
| self.ui.ch_bridged.setText(self.tr("Bridged (32bit)")) | |||||
| if not (LINUX or MACOS): | |||||
| self.ui.ch_bridged_wine.setChecked(False) | |||||
| self.ui.ch_bridged_wine.setEnabled(False) | |||||
| if MACOS: | |||||
| self.setWindowModality(Qt.WindowModal) | |||||
| else: | |||||
| self.ui.ch_au.setChecked(False) | |||||
| self.ui.ch_au.setEnabled(False) | |||||
| self.ui.ch_au.setVisible(False) | |||||
| self.ui.tab_info.tabBar().hide() | |||||
| self.ui.tab_reqs.tabBar().hide() | |||||
| # FIXME, why /2 needed? | |||||
| self.ui.tab_info.setMinimumWidth(int(self.ui.la_id.width()/2) + | |||||
| fontMetricsHorizontalAdvance(self.ui.l_id.fontMetrics(), "9999999999") + 6*3) | |||||
| self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Load settings | |||||
| self.loadSettings() | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Disable bridges if not enabled in settings | |||||
| # NOTE: We Assume win32 carla build will not run win64 plugins | |||||
| if (WINDOWS and not kIs64bit) or not host.showPluginBridges: | |||||
| self.ui.ch_native.setChecked(True) | |||||
| self.ui.ch_native.setEnabled(False) | |||||
| self.ui.ch_native.setVisible(True) | |||||
| self.ui.ch_bridged.setChecked(False) | |||||
| self.ui.ch_bridged.setEnabled(False) | |||||
| self.ui.ch_bridged.setVisible(False) | |||||
| self.ui.ch_bridged_wine.setChecked(False) | |||||
| self.ui.ch_bridged_wine.setEnabled(False) | |||||
| self.ui.ch_bridged_wine.setVisible(False) | |||||
| self.ui.l_arch.setVisible(False) | |||||
| elif not host.showWineBridges: | |||||
| self.ui.ch_bridged_wine.setChecked(False) | |||||
| self.ui.ch_bridged_wine.setEnabled(False) | |||||
| self.ui.ch_bridged_wine.setVisible(False) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Set-up Icons | |||||
| if useSystemIcons: | |||||
| self.ui.b_add.setIcon(getIcon('list-add', 16, 'svgz')) | |||||
| self.ui.b_cancel.setIcon(getIcon('dialog-cancel', 16, 'svgz')) | |||||
| self.ui.b_clear_filters.setIcon(getIcon('edit-clear', 16, 'svgz')) | |||||
| self.ui.b_refresh.setIcon(getIcon('view-refresh', 16, 'svgz')) | |||||
| hhi = self.ui.tableWidget.horizontalHeaderItem(self.TABLEWIDGET_ITEM_FAVORITE) | |||||
| hhi.setIcon(getIcon('bookmarks', 16, 'svgz')) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Set-up connections | |||||
| self.finished.connect(self.slot_saveSettings) | |||||
| self.ui.b_add.clicked.connect(self.slot_addPlugin) | |||||
| self.ui.b_cancel.clicked.connect(self.reject) | |||||
| self.ui.b_refresh.clicked.connect(self.slot_refreshPlugins) | |||||
| self.ui.b_clear_filters.clicked.connect(self.slot_clearFilters) | |||||
| self.ui.lineEdit.textChanged.connect(self.slot_checkFilters) | |||||
| self.ui.tableWidget.currentCellChanged.connect(self.slot_checkPlugin) | |||||
| self.ui.tableWidget.cellClicked.connect(self.slot_cellClicked) | |||||
| self.ui.tableWidget.cellDoubleClicked.connect(self.slot_cellDoubleClicked) | |||||
| self.ui.ch_internal.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_ladspa.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_dssi.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_lv2.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_vst.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_vst3.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_au.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_jsfx.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_kits.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_effects.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_instruments.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_midi.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_other.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_native.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_bridged.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_bridged_wine.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_favorites.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_rtsafe.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_cv.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_gui.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_inline_display.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_stereo.clicked.connect(self.slot_checkFilters) | |||||
| self.ui.ch_cat_all.clicked.connect(self.slot_checkFiltersCategoryAll) | |||||
| self.ui.ch_cat_delay.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_distortion.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_dynamics.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_eq.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_filter.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_modulator.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_synth.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_utility.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| self.ui.ch_cat_other.clicked.connect(self.slot_checkFiltersCategorySpecific) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Post-connect setup | |||||
| self._reAddPlugins() | |||||
| self.slot_focusSearchFieldAndSelectAll() | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot(int, int) | |||||
| def slot_cellClicked(self, row, column): | |||||
| if column == self.TABLEWIDGET_ITEM_FAVORITE: | |||||
| widget = self.ui.tableWidget.item(row, self.TABLEWIDGET_ITEM_FAVORITE) | |||||
| plugin = self.ui.tableWidget.item(row, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) | |||||
| plugin = self._createFavoritePluginDict(plugin) | |||||
| if widget.checkState() == Qt.Checked: | |||||
| if not plugin in self.fFavoritePlugins: | |||||
| self.fFavoritePlugins.append(plugin) | |||||
| self.fFavoritePluginsChanged = True | |||||
| else: | |||||
| try: | |||||
| self.fFavoritePlugins.remove(plugin) | |||||
| self.fFavoritePluginsChanged = True | |||||
| except ValueError: | |||||
| pass | |||||
| @pyqtSlot(int, int) | |||||
| def slot_cellDoubleClicked(self, _, column): | |||||
| if column != self.TABLEWIDGET_ITEM_FAVORITE: | |||||
| self.slot_addPlugin() | |||||
| @pyqtSlot() | |||||
| def slot_focusSearchFieldAndSelectAll(self): | |||||
| self.ui.lineEdit.setFocus() | |||||
| self.ui.lineEdit.selectAll() | |||||
| @pyqtSlot() | |||||
| def slot_addPlugin(self): | |||||
| if self.ui.tableWidget.currentRow() >= 0: | |||||
| self.fRetPlugin = self.ui.tableWidget.item(self.ui.tableWidget.currentRow(), | |||||
| self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) | |||||
| self.accept() | |||||
| else: | |||||
| self.reject() | |||||
| @pyqtSlot(int) | |||||
| def slot_checkPlugin(self, row): | |||||
| if row >= 0: | |||||
| self.ui.b_add.setEnabled(True) | |||||
| plugin = self.ui.tableWidget.item(self.ui.tableWidget.currentRow(), | |||||
| self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) | |||||
| isSynth = bool(plugin['hints'] & PLUGIN_IS_SYNTH) | |||||
| isEffect = bool(plugin['audio.ins'] > 0 < plugin['audio.outs'] and not isSynth) | |||||
| isMidi = bool(plugin['audio.ins'] == 0 and | |||||
| plugin['audio.outs'] == 0 and | |||||
| plugin['midi.ins'] > 0 < plugin['midi.outs']) | |||||
| # isKit = bool(plugin['type'] in (PLUGIN_SF2, PLUGIN_SFZ)) | |||||
| # isOther = bool(not (isEffect or isSynth or isMidi or isKit)) | |||||
| if isSynth: | |||||
| ptype = "Instrument" | |||||
| elif isEffect: | |||||
| ptype = "Effect" | |||||
| elif isMidi: | |||||
| ptype = "MIDI Plugin" | |||||
| else: | |||||
| ptype = "Other" | |||||
| if plugin['build'] == BINARY_NATIVE: | |||||
| parch = self.fTrNative | |||||
| elif plugin['build'] == BINARY_POSIX32: | |||||
| parch = "posix32" | |||||
| elif plugin['build'] == BINARY_POSIX64: | |||||
| parch = "posix64" | |||||
| elif plugin['build'] == BINARY_WIN32: | |||||
| parch = "win32" | |||||
| elif plugin['build'] == BINARY_WIN64: | |||||
| parch = "win64" | |||||
| elif plugin['build'] == BINARY_OTHER: | |||||
| parch = self.tr("Other") | |||||
| elif plugin['build'] == BINARY_WIN32: | |||||
| parch = self.tr("Unknown") | |||||
| self.ui.l_format.setText(getPluginTypeAsString(plugin['type'])) | |||||
| self.ui.l_type.setText(ptype) | |||||
| self.ui.l_arch.setText(parch) | |||||
| self.ui.l_id.setText(str(plugin['uniqueId'])) | |||||
| self.ui.l_ains.setText(str(plugin['audio.ins'])) | |||||
| self.ui.l_aouts.setText(str(plugin['audio.outs'])) | |||||
| self.ui.l_cvins.setText(str(plugin['cv.ins'])) | |||||
| self.ui.l_cvouts.setText(str(plugin['cv.outs'])) | |||||
| self.ui.l_mins.setText(str(plugin['midi.ins'])) | |||||
| self.ui.l_mouts.setText(str(plugin['midi.outs'])) | |||||
| self.ui.l_pins.setText(str(plugin['parameters.ins'])) | |||||
| self.ui.l_pouts.setText(str(plugin['parameters.outs'])) | |||||
| self.ui.l_gui.setText(self.fTrYes if plugin['hints'] & PLUGIN_HAS_CUSTOM_UI else self.fTrNo) | |||||
| self.ui.l_idisp.setText(self.fTrYes if plugin['hints'] & PLUGIN_HAS_INLINE_DISPLAY else self.fTrNo) | |||||
| self.ui.l_bridged.setText(self.fTrYes if plugin['hints'] & PLUGIN_IS_BRIDGE else self.fTrNo) | |||||
| self.ui.l_synth.setText(self.fTrYes if isSynth else self.fTrNo) | |||||
| else: | |||||
| self.ui.b_add.setEnabled(False) | |||||
| self.ui.l_format.setText("---") | |||||
| self.ui.l_type.setText("---") | |||||
| self.ui.l_arch.setText("---") | |||||
| self.ui.l_id.setText("---") | |||||
| self.ui.l_ains.setText("---") | |||||
| self.ui.l_aouts.setText("---") | |||||
| self.ui.l_cvins.setText("---") | |||||
| self.ui.l_cvouts.setText("---") | |||||
| self.ui.l_mins.setText("---") | |||||
| self.ui.l_mouts.setText("---") | |||||
| self.ui.l_pins.setText("---") | |||||
| self.ui.l_pouts.setText("---") | |||||
| self.ui.l_gui.setText("---") | |||||
| self.ui.l_idisp.setText("---") | |||||
| self.ui.l_bridged.setText("---") | |||||
| self.ui.l_synth.setText("---") | |||||
| @pyqtSlot() | |||||
| def slot_checkFilters(self): | |||||
| self._checkFilters() | |||||
| @pyqtSlot(bool) | |||||
| def slot_checkFiltersCategoryAll(self, clicked): | |||||
| self.ui.ch_cat_delay.setChecked(not clicked) | |||||
| self.ui.ch_cat_distortion.setChecked(not clicked) | |||||
| self.ui.ch_cat_dynamics.setChecked(not clicked) | |||||
| self.ui.ch_cat_eq.setChecked(not clicked) | |||||
| self.ui.ch_cat_filter.setChecked(not clicked) | |||||
| self.ui.ch_cat_modulator.setChecked(not clicked) | |||||
| self.ui.ch_cat_synth.setChecked(not clicked) | |||||
| self.ui.ch_cat_utility.setChecked(not clicked) | |||||
| self.ui.ch_cat_other.setChecked(not clicked) | |||||
| self._checkFilters() | |||||
| @pyqtSlot(bool) | |||||
| def slot_checkFiltersCategorySpecific(self, clicked): | |||||
| if clicked: | |||||
| self.ui.ch_cat_all.setChecked(False) | |||||
| elif not (self.ui.ch_cat_delay.isChecked() or | |||||
| self.ui.ch_cat_distortion.isChecked() or | |||||
| self.ui.ch_cat_dynamics.isChecked() or | |||||
| self.ui.ch_cat_eq.isChecked() or | |||||
| self.ui.ch_cat_filter.isChecked() or | |||||
| self.ui.ch_cat_modulator.isChecked() or | |||||
| self.ui.ch_cat_synth.isChecked() or | |||||
| self.ui.ch_cat_utility.isChecked() or | |||||
| self.ui.ch_cat_other.isChecked()): | |||||
| self.ui.ch_cat_all.setChecked(True) | |||||
| self._checkFilters() | |||||
| @pyqtSlot() | |||||
| def slot_refreshPlugins(self): | |||||
| if PluginRefreshW(self, self.host, self.fUseSystemIcons, self.hasLoadedLv2Plugins).exec_(): | |||||
| self._reAddPlugins() | |||||
| if self.fRealParent: | |||||
| self.fRealParent.setLoadRDFsNeeded() | |||||
| @pyqtSlot() | |||||
| def slot_clearFilters(self): | |||||
| self.blockSignals(True) | |||||
| self.ui.ch_internal.setChecked(True) | |||||
| self.ui.ch_ladspa.setChecked(True) | |||||
| self.ui.ch_dssi.setChecked(True) | |||||
| self.ui.ch_lv2.setChecked(True) | |||||
| self.ui.ch_vst.setChecked(True) | |||||
| self.ui.ch_jsfx.setChecked(True) | |||||
| self.ui.ch_kits.setChecked(True) | |||||
| self.ui.ch_instruments.setChecked(True) | |||||
| self.ui.ch_effects.setChecked(True) | |||||
| self.ui.ch_midi.setChecked(True) | |||||
| self.ui.ch_other.setChecked(True) | |||||
| self.ui.ch_native.setChecked(True) | |||||
| self.ui.ch_bridged.setChecked(False) | |||||
| self.ui.ch_bridged_wine.setChecked(False) | |||||
| self.ui.ch_favorites.setChecked(False) | |||||
| self.ui.ch_rtsafe.setChecked(False) | |||||
| self.ui.ch_stereo.setChecked(False) | |||||
| self.ui.ch_cv.setChecked(False) | |||||
| self.ui.ch_gui.setChecked(False) | |||||
| self.ui.ch_inline_display.setChecked(False) | |||||
| if self.ui.ch_vst3.isEnabled(): | |||||
| self.ui.ch_vst3.setChecked(True) | |||||
| if self.ui.ch_au.isEnabled(): | |||||
| self.ui.ch_au.setChecked(True) | |||||
| self.ui.ch_cat_all.setChecked(True) | |||||
| self.ui.ch_cat_delay.setChecked(False) | |||||
| self.ui.ch_cat_distortion.setChecked(False) | |||||
| self.ui.ch_cat_dynamics.setChecked(False) | |||||
| self.ui.ch_cat_eq.setChecked(False) | |||||
| self.ui.ch_cat_filter.setChecked(False) | |||||
| self.ui.ch_cat_modulator.setChecked(False) | |||||
| self.ui.ch_cat_synth.setChecked(False) | |||||
| self.ui.ch_cat_utility.setChecked(False) | |||||
| self.ui.ch_cat_other.setChecked(False) | |||||
| self.ui.lineEdit.clear() | |||||
| self.blockSignals(False) | |||||
| self._checkFilters() | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_saveSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaDatabase2") | |||||
| settings.setValue("PluginDatabase/Geometry", self.saveGeometry()) | |||||
| settings.setValue("PluginDatabase/TableGeometry_6", self.ui.tableWidget.horizontalHeader().saveState()) | |||||
| settings.setValue("PluginDatabase/ShowEffects", self.ui.ch_effects.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowInstruments", self.ui.ch_instruments.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowMIDI", self.ui.ch_midi.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowOther", self.ui.ch_other.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowInternal", self.ui.ch_internal.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowLADSPA", self.ui.ch_ladspa.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowDSSI", self.ui.ch_dssi.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowLV2", self.ui.ch_lv2.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowVST2", self.ui.ch_vst.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowVST3", self.ui.ch_vst3.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowAU", self.ui.ch_au.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowJSFX", self.ui.ch_jsfx.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowKits", self.ui.ch_kits.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowNative", self.ui.ch_native.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowBridged", self.ui.ch_bridged.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowBridgedWine", self.ui.ch_bridged_wine.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowFavorites", self.ui.ch_favorites.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowRtSafe", self.ui.ch_rtsafe.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowHasCV", self.ui.ch_cv.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowHasGUI", self.ui.ch_gui.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowHasInlineDisplay", self.ui.ch_inline_display.isChecked()) | |||||
| settings.setValue("PluginDatabase/ShowStereoOnly", self.ui.ch_stereo.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchText", self.ui.lineEdit.text()) | |||||
| if self.ui.ch_cat_all.isChecked(): | |||||
| settings.setValue("PluginDatabase/ShowCategory", "all") | |||||
| else: | |||||
| categoryhash = "" | |||||
| if self.ui.ch_cat_delay.isChecked(): | |||||
| categoryhash += ":delay" | |||||
| if self.ui.ch_cat_distortion.isChecked(): | |||||
| categoryhash += ":distortion" | |||||
| if self.ui.ch_cat_dynamics.isChecked(): | |||||
| categoryhash += ":dynamics" | |||||
| if self.ui.ch_cat_eq.isChecked(): | |||||
| categoryhash += ":eq" | |||||
| if self.ui.ch_cat_filter.isChecked(): | |||||
| categoryhash += ":filter" | |||||
| if self.ui.ch_cat_modulator.isChecked(): | |||||
| categoryhash += ":modulator" | |||||
| if self.ui.ch_cat_synth.isChecked(): | |||||
| categoryhash += ":synth" | |||||
| if self.ui.ch_cat_utility.isChecked(): | |||||
| categoryhash += ":utility" | |||||
| if self.ui.ch_cat_other.isChecked(): | |||||
| categoryhash += ":other" | |||||
| if categoryhash: | |||||
| categoryhash += ":" | |||||
| settings.setValue("PluginDatabase/ShowCategory", categoryhash) | |||||
| if self.fFavoritePluginsChanged: | |||||
| settings.setValue("PluginDatabase/Favorites", self.fFavoritePlugins) | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| def loadSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaDatabase2") | |||||
| self.fFavoritePlugins = settings.value("PluginDatabase/Favorites", [], list) | |||||
| self.fFavoritePluginsChanged = False | |||||
| self.restoreGeometry(settings.value("PluginDatabase/Geometry", QByteArray(), QByteArray)) | |||||
| self.ui.ch_effects.setChecked(settings.value("PluginDatabase/ShowEffects", True, bool)) | |||||
| self.ui.ch_instruments.setChecked(settings.value("PluginDatabase/ShowInstruments", True, bool)) | |||||
| self.ui.ch_midi.setChecked(settings.value("PluginDatabase/ShowMIDI", True, bool)) | |||||
| self.ui.ch_other.setChecked(settings.value("PluginDatabase/ShowOther", True, bool)) | |||||
| self.ui.ch_internal.setChecked(settings.value("PluginDatabase/ShowInternal", True, bool)) | |||||
| self.ui.ch_ladspa.setChecked(settings.value("PluginDatabase/ShowLADSPA", True, bool)) | |||||
| self.ui.ch_dssi.setChecked(settings.value("PluginDatabase/ShowDSSI", True, bool)) | |||||
| self.ui.ch_lv2.setChecked(settings.value("PluginDatabase/ShowLV2", True, bool)) | |||||
| self.ui.ch_vst.setChecked(settings.value("PluginDatabase/ShowVST2", True, bool)) | |||||
| self.ui.ch_vst3.setChecked(settings.value("PluginDatabase/ShowVST3", (MACOS or WINDOWS), bool)) | |||||
| self.ui.ch_au.setChecked(settings.value("PluginDatabase/ShowAU", MACOS, bool)) | |||||
| self.ui.ch_jsfx.setChecked(settings.value("PluginDatabase/ShowJSFX", True, bool)) | |||||
| self.ui.ch_kits.setChecked(settings.value("PluginDatabase/ShowKits", True, bool)) | |||||
| self.ui.ch_native.setChecked(settings.value("PluginDatabase/ShowNative", True, bool)) | |||||
| self.ui.ch_bridged.setChecked(settings.value("PluginDatabase/ShowBridged", True, bool)) | |||||
| self.ui.ch_bridged_wine.setChecked(settings.value("PluginDatabase/ShowBridgedWine", True, bool)) | |||||
| self.ui.ch_favorites.setChecked(settings.value("PluginDatabase/ShowFavorites", False, bool)) | |||||
| self.ui.ch_rtsafe.setChecked(settings.value("PluginDatabase/ShowRtSafe", False, bool)) | |||||
| self.ui.ch_cv.setChecked(settings.value("PluginDatabase/ShowHasCV", False, bool)) | |||||
| self.ui.ch_gui.setChecked(settings.value("PluginDatabase/ShowHasGUI", False, bool)) | |||||
| self.ui.ch_inline_display.setChecked(settings.value("PluginDatabase/ShowHasInlineDisplay", False, bool)) | |||||
| self.ui.ch_stereo.setChecked(settings.value("PluginDatabase/ShowStereoOnly", False, bool)) | |||||
| self.ui.lineEdit.setText(settings.value("PluginDatabase/SearchText", "", str)) | |||||
| categoryhash = settings.value("PluginDatabase/ShowCategory", "all", str) | |||||
| if categoryhash == "all" or len(categoryhash) < 2: | |||||
| self.ui.ch_cat_all.setChecked(True) | |||||
| self.ui.ch_cat_delay.setChecked(False) | |||||
| self.ui.ch_cat_distortion.setChecked(False) | |||||
| self.ui.ch_cat_dynamics.setChecked(False) | |||||
| self.ui.ch_cat_eq.setChecked(False) | |||||
| self.ui.ch_cat_filter.setChecked(False) | |||||
| self.ui.ch_cat_modulator.setChecked(False) | |||||
| self.ui.ch_cat_synth.setChecked(False) | |||||
| self.ui.ch_cat_utility.setChecked(False) | |||||
| self.ui.ch_cat_other.setChecked(False) | |||||
| else: | |||||
| self.ui.ch_cat_all.setChecked(False) | |||||
| self.ui.ch_cat_delay.setChecked(":delay:" in categoryhash) | |||||
| self.ui.ch_cat_distortion.setChecked(":distortion:" in categoryhash) | |||||
| self.ui.ch_cat_dynamics.setChecked(":dynamics:" in categoryhash) | |||||
| self.ui.ch_cat_eq.setChecked(":eq:" in categoryhash) | |||||
| self.ui.ch_cat_filter.setChecked(":filter:" in categoryhash) | |||||
| self.ui.ch_cat_modulator.setChecked(":modulator:" in categoryhash) | |||||
| self.ui.ch_cat_synth.setChecked(":synth:" in categoryhash) | |||||
| self.ui.ch_cat_utility.setChecked(":utility:" in categoryhash) | |||||
| self.ui.ch_cat_other.setChecked(":other:" in categoryhash) | |||||
| tableGeometry = settings.value("PluginDatabase/TableGeometry_6", QByteArray(), QByteArray) | |||||
| horizontalHeader = self.ui.tableWidget.horizontalHeader() | |||||
| if not tableGeometry.isNull(): | |||||
| horizontalHeader.restoreState(tableGeometry) | |||||
| else: | |||||
| horizontalHeader.setSectionResizeMode(self.TABLEWIDGET_ITEM_FAVORITE, QHeaderView.Fixed) | |||||
| self.ui.tableWidget.setColumnWidth(self.TABLEWIDGET_ITEM_FAVORITE, 24) | |||||
| self.ui.tableWidget.setColumnWidth(self.TABLEWIDGET_ITEM_NAME, 250) | |||||
| self.ui.tableWidget.setColumnWidth(self.TABLEWIDGET_ITEM_LABEL, 200) | |||||
| self.ui.tableWidget.setColumnWidth(self.TABLEWIDGET_ITEM_MAKER, 150) | |||||
| self.ui.tableWidget.sortByColumn(self.TABLEWIDGET_ITEM_NAME, Qt.AscendingOrder) | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| def _createFavoritePluginDict(self, plugin): | |||||
| return { | |||||
| 'name' : plugin['name'], | |||||
| 'build' : plugin['build'], | |||||
| 'type' : plugin['type'], | |||||
| 'filename': plugin['filename'], | |||||
| 'label' : plugin['label'], | |||||
| 'uniqueId': plugin['uniqueId'], | |||||
| } | |||||
| def _checkFilters(self): | |||||
| text = self.ui.lineEdit.text().lower() | |||||
| hideEffects = not self.ui.ch_effects.isChecked() | |||||
| hideInstruments = not self.ui.ch_instruments.isChecked() | |||||
| hideMidi = not self.ui.ch_midi.isChecked() | |||||
| hideOther = not self.ui.ch_other.isChecked() | |||||
| hideInternal = not self.ui.ch_internal.isChecked() | |||||
| hideLadspa = not self.ui.ch_ladspa.isChecked() | |||||
| hideDssi = not self.ui.ch_dssi.isChecked() | |||||
| hideLV2 = not self.ui.ch_lv2.isChecked() | |||||
| hideVST2 = not self.ui.ch_vst.isChecked() | |||||
| hideVST3 = not self.ui.ch_vst3.isChecked() | |||||
| hideAU = not self.ui.ch_au.isChecked() | |||||
| hideJSFX = not self.ui.ch_jsfx.isChecked() | |||||
| hideKits = not self.ui.ch_kits.isChecked() | |||||
| hideNative = not self.ui.ch_native.isChecked() | |||||
| hideBridged = not self.ui.ch_bridged.isChecked() | |||||
| hideBridgedWine = not self.ui.ch_bridged_wine.isChecked() | |||||
| hideNonFavs = self.ui.ch_favorites.isChecked() | |||||
| hideNonRtSafe = self.ui.ch_rtsafe.isChecked() | |||||
| hideNonCV = self.ui.ch_cv.isChecked() | |||||
| hideNonGui = self.ui.ch_gui.isChecked() | |||||
| hideNonIDisp = self.ui.ch_inline_display.isChecked() | |||||
| hideNonStereo = self.ui.ch_stereo.isChecked() | |||||
| if HAIKU or LINUX or MACOS: | |||||
| nativeBins = [BINARY_POSIX32, BINARY_POSIX64] | |||||
| wineBins = [BINARY_WIN32, BINARY_WIN64] | |||||
| elif WINDOWS: | |||||
| nativeBins = [BINARY_WIN32, BINARY_WIN64] | |||||
| wineBins = [] | |||||
| else: | |||||
| nativeBins = [] | |||||
| wineBins = [] | |||||
| self.ui.tableWidget.setRowCount(self.fLastTableIndex) | |||||
| for i in range(self.fLastTableIndex): | |||||
| plugin = self.ui.tableWidget.item(i, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) | |||||
| ptext = self.ui.tableWidget.item(i, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+2) | |||||
| aIns = plugin['audio.ins'] | |||||
| aOuts = plugin['audio.outs'] | |||||
| cvIns = plugin['cv.ins'] | |||||
| cvOuts = plugin['cv.outs'] | |||||
| mIns = plugin['midi.ins'] | |||||
| mOuts = plugin['midi.outs'] | |||||
| phints = plugin['hints'] | |||||
| ptype = plugin['type'] | |||||
| categ = plugin['category'] | |||||
| isSynth = bool(phints & PLUGIN_IS_SYNTH) | |||||
| isEffect = bool(aIns > 0 < aOuts and not isSynth) | |||||
| isMidi = bool(aIns == 0 and aOuts == 0 and mIns > 0 < mOuts) | |||||
| isKit = bool(ptype in (PLUGIN_SF2, PLUGIN_SFZ)) | |||||
| isOther = bool(not (isEffect or isSynth or isMidi or isKit)) | |||||
| isNative = bool(plugin['build'] == BINARY_NATIVE) | |||||
| isRtSafe = bool(phints & PLUGIN_IS_RTSAFE) | |||||
| isStereo = bool(aIns == 2 and aOuts == 2) or (isSynth and aOuts == 2) | |||||
| hasCV = bool(cvIns + cvOuts > 0) | |||||
| hasGui = bool(phints & PLUGIN_HAS_CUSTOM_UI) | |||||
| hasIDisp = bool(phints & PLUGIN_HAS_INLINE_DISPLAY) | |||||
| isBridged = bool(not isNative and plugin['build'] in nativeBins) | |||||
| isBridgedWine = bool(not isNative and plugin['build'] in wineBins) | |||||
| if hideEffects and isEffect: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideInstruments and isSynth: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideMidi and isMidi: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideOther and isOther: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideKits and isKit: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideInternal and ptype == PLUGIN_INTERNAL: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideLadspa and ptype == PLUGIN_LADSPA: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideDssi and ptype == PLUGIN_DSSI: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideLV2 and ptype == PLUGIN_LV2: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideVST2 and ptype == PLUGIN_VST2: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideVST3 and ptype == PLUGIN_VST3: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideAU and ptype == PLUGIN_AU: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideJSFX and ptype == PLUGIN_JSFX: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNative and isNative: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideBridged and isBridged: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideBridgedWine and isBridgedWine: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonRtSafe and not isRtSafe: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonCV and not hasCV: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonGui and not hasGui: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonIDisp and not hasIDisp: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonStereo and not isStereo: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif text and not all(t in ptext for t in text.strip().split(' ')): | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif hideNonFavs and self._createFavoritePluginDict(plugin) not in self.fFavoritePlugins: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| elif (self.ui.ch_cat_all.isChecked() or | |||||
| (self.ui.ch_cat_delay.isChecked() and categ == "delay") or | |||||
| (self.ui.ch_cat_distortion.isChecked() and categ == "distortion") or | |||||
| (self.ui.ch_cat_dynamics.isChecked() and categ == "dynamics") or | |||||
| (self.ui.ch_cat_eq.isChecked() and categ == "eq") or | |||||
| (self.ui.ch_cat_filter.isChecked() and categ == "filter") or | |||||
| (self.ui.ch_cat_modulator.isChecked() and categ == "modulator") or | |||||
| (self.ui.ch_cat_synth.isChecked() and categ == "synth") or | |||||
| (self.ui.ch_cat_utility.isChecked() and categ == "utility") or | |||||
| (self.ui.ch_cat_other.isChecked() and categ == "other")): | |||||
| self.ui.tableWidget.showRow(i) | |||||
| else: | |||||
| self.ui.tableWidget.hideRow(i) | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| def _addPluginToTable(self, plugin, ptype): | |||||
| if plugin['API'] != PLUGIN_QUERY_API_VERSION: | |||||
| return | |||||
| if ptype in (self.tr("Internal"), "LV2", "SF2", "SFZ", "JSFX"): | |||||
| plugin['build'] = BINARY_NATIVE | |||||
| index = self.fLastTableIndex | |||||
| isFav = bool(self._createFavoritePluginDict(plugin) in self.fFavoritePlugins) | |||||
| favItem = QTableWidgetItem() | |||||
| favItem.setCheckState(Qt.Checked if isFav else Qt.Unchecked) | |||||
| favItem.setText(" " if isFav else " ") | |||||
| pluginText = (plugin['name']+plugin['label']+plugin['maker']+plugin['filename']).lower() | |||||
| self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_FAVORITE, favItem) | |||||
| self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_NAME, QTableWidgetItem(plugin['name'])) | |||||
| self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_LABEL, QTableWidgetItem(plugin['label'])) | |||||
| self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_MAKER, QTableWidgetItem(plugin['maker'])) | |||||
| self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_BINARY, QTableWidgetItem(os.path.basename(plugin['filename']))) | |||||
| self.ui.tableWidget.item(index, self.TABLEWIDGET_ITEM_NAME).setData(Qt.UserRole+1, plugin) | |||||
| self.ui.tableWidget.item(index, self.TABLEWIDGET_ITEM_NAME).setData(Qt.UserRole+2, pluginText) | |||||
| self.fLastTableIndex += 1 | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| def _reAddInternalHelper(self, settingsDB, ptype, path): | |||||
| if ptype == PLUGIN_INTERNAL: | |||||
| ptypeStr = "Internal" | |||||
| ptypeStrTr = self.tr("Internal") | |||||
| elif ptype == PLUGIN_LV2: | |||||
| ptypeStr = "LV2" | |||||
| ptypeStrTr = ptypeStr | |||||
| elif ptype == PLUGIN_AU: | |||||
| ptypeStr = "AU" | |||||
| ptypeStrTr = ptypeStr | |||||
| #elif ptype == PLUGIN_SFZ: | |||||
| #ptypeStr = "SFZ" | |||||
| #ptypeStrTr = ptypeStr | |||||
| # TODO(jsfx) what to do here? | |||||
| else: | |||||
| return 0 | |||||
| plugins = settingsDB.value("Plugins/" + ptypeStr, [], list) | |||||
| pluginCount = settingsDB.value("PluginCount/" + ptypeStr, 0, int) | |||||
| if ptype == PLUGIN_AU: | |||||
| gCarla.utils.juce_init() | |||||
| pluginCountNew = gCarla.utils.get_cached_plugin_count(ptype, path) | |||||
| if pluginCountNew != pluginCount or len(plugins) != pluginCount or (len(plugins) > 0 and plugins[0]['API'] != PLUGIN_QUERY_API_VERSION): | |||||
| plugins = [] | |||||
| pluginCount = pluginCountNew | |||||
| QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50) | |||||
| if ptype == PLUGIN_AU: | |||||
| gCarla.utils.juce_idle() | |||||
| for i in range(pluginCountNew): | |||||
| descInfo = gCarla.utils.get_cached_plugin_info(ptype, i) | |||||
| if not descInfo['valid']: | |||||
| continue | |||||
| info = checkPluginCached(descInfo, ptype) | |||||
| plugins.append(info) | |||||
| if i % 50 == 0: | |||||
| QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50) | |||||
| if ptype == PLUGIN_AU: | |||||
| gCarla.utils.juce_idle() | |||||
| settingsDB.setValue("Plugins/" + ptypeStr, plugins) | |||||
| settingsDB.setValue("PluginCount/" + ptypeStr, pluginCount) | |||||
| if ptype == PLUGIN_AU: | |||||
| gCarla.utils.juce_cleanup() | |||||
| # prepare rows in advance | |||||
| self.ui.tableWidget.setRowCount(self.fLastTableIndex + len(plugins)) | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, ptypeStrTr) | |||||
| return pluginCount | |||||
| def _reAddPlugins(self): | |||||
| settingsDB = QSafeSettings("falkTX", "CarlaPlugins5") | |||||
| self.fLastTableIndex = 0 | |||||
| self.ui.tableWidget.setSortingEnabled(False) | |||||
| self.ui.tableWidget.clearContents() | |||||
| settings = QSafeSettings("falkTX", "Carla2") | |||||
| LV2_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)) | |||||
| del settings | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # plugins handled through backend | |||||
| internalCount = self._reAddInternalHelper(settingsDB, PLUGIN_INTERNAL, "") | |||||
| lv2Count = self._reAddInternalHelper(settingsDB, PLUGIN_LV2, LV2_PATH) | |||||
| auCount = self._reAddInternalHelper(settingsDB, PLUGIN_AU, "") if MACOS else 0 | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # LADSPA | |||||
| ladspaPlugins = [] | |||||
| ladspaPlugins += settingsDB.value("Plugins/LADSPA_native", [], list) | |||||
| ladspaPlugins += settingsDB.value("Plugins/LADSPA_posix32", [], list) | |||||
| ladspaPlugins += settingsDB.value("Plugins/LADSPA_posix64", [], list) | |||||
| ladspaPlugins += settingsDB.value("Plugins/LADSPA_win32", [], list) | |||||
| ladspaPlugins += settingsDB.value("Plugins/LADSPA_win64", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # DSSI | |||||
| dssiPlugins = [] | |||||
| dssiPlugins += settingsDB.value("Plugins/DSSI_native", [], list) | |||||
| dssiPlugins += settingsDB.value("Plugins/DSSI_posix32", [], list) | |||||
| dssiPlugins += settingsDB.value("Plugins/DSSI_posix64", [], list) | |||||
| dssiPlugins += settingsDB.value("Plugins/DSSI_win32", [], list) | |||||
| dssiPlugins += settingsDB.value("Plugins/DSSI_win64", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # VST2 | |||||
| vst2Plugins = [] | |||||
| vst2Plugins += settingsDB.value("Plugins/VST2_native", [], list) | |||||
| vst2Plugins += settingsDB.value("Plugins/VST2_posix32", [], list) | |||||
| vst2Plugins += settingsDB.value("Plugins/VST2_posix64", [], list) | |||||
| vst2Plugins += settingsDB.value("Plugins/VST2_win32", [], list) | |||||
| vst2Plugins += settingsDB.value("Plugins/VST2_win64", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # VST3 | |||||
| vst3Plugins = [] | |||||
| vst3Plugins += settingsDB.value("Plugins/VST3_native", [], list) | |||||
| vst3Plugins += settingsDB.value("Plugins/VST3_posix32", [], list) | |||||
| vst3Plugins += settingsDB.value("Plugins/VST3_posix64", [], list) | |||||
| vst3Plugins += settingsDB.value("Plugins/VST3_win32", [], list) | |||||
| vst3Plugins += settingsDB.value("Plugins/VST3_win64", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # AU (extra non-cached) | |||||
| auPlugins32 = settingsDB.value("Plugins/AU_posix32", [], list) if MACOS else [] | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # JSFX | |||||
| jsfxPlugins = settingsDB.value("Plugins/JSFX", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # Kits | |||||
| sf2s = settingsDB.value("Plugins/SF2", [], list) | |||||
| sfzs = settingsDB.value("Plugins/SFZ", [], list) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # count plugins first, so we can create rows in advance | |||||
| ladspaCount = 0 | |||||
| dssiCount = 0 | |||||
| vstCount = 0 | |||||
| vst3Count = 0 | |||||
| au32Count = 0 | |||||
| jsfxCount = len(jsfxPlugins) | |||||
| sf2Count = 0 | |||||
| sfzCount = len(sfzs) | |||||
| for plugins in ladspaPlugins: | |||||
| ladspaCount += len(plugins) | |||||
| for plugins in dssiPlugins: | |||||
| dssiCount += len(plugins) | |||||
| for plugins in vst2Plugins: | |||||
| vstCount += len(plugins) | |||||
| for plugins in vst3Plugins: | |||||
| vst3Count += len(plugins) | |||||
| for plugins in auPlugins32: | |||||
| au32Count += len(plugins) | |||||
| for plugins in sf2s: | |||||
| sf2Count += len(plugins) | |||||
| self.ui.tableWidget.setRowCount(self.fLastTableIndex + | |||||
| ladspaCount + dssiCount + vstCount + vst3Count + au32Count + jsfxCount + | |||||
| sf2Count + sfzCount) | |||||
| if MACOS: | |||||
| self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3, %i AudioUnit plugins and %i JSFX plugins, plus %i Sound Kits" % ( | |||||
| internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, auCount+au32Count, jsfxCount, sf2Count+sfzCount))) | |||||
| else: | |||||
| self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3 plugins and %i JSFX plugins, plus %i Sound Kits" % ( | |||||
| internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, jsfxCount, sf2Count+sfzCount))) | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| # now add all plugins to the table | |||||
| for plugins in ladspaPlugins: | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, "LADSPA") | |||||
| for plugins in dssiPlugins: | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, "DSSI") | |||||
| for plugins in vst2Plugins: | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, "VST2") | |||||
| for plugins in vst3Plugins: | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, "VST3") | |||||
| for plugins in auPlugins32: | |||||
| for plugin in plugins: | |||||
| self._addPluginToTable(plugin, "AU") | |||||
| for plugin in jsfxPlugins: | |||||
| self._addPluginToTable(plugin, "JSFX") | |||||
| for sf2 in sf2s: | |||||
| for sf2_i in sf2: | |||||
| self._addPluginToTable(sf2_i, "SF2") | |||||
| for sfz in sfzs: | |||||
| self._addPluginToTable(sfz, "SFZ") | |||||
| # ---------------------------------------------------------------------------------------------------- | |||||
| self.ui.tableWidget.setSortingEnabled(True) | |||||
| self._checkFilters() | |||||
| self.slot_checkPlugin(self.ui.tableWidget.currentRow()) | |||||
| # -------------------------------------------------------------------------------------------------------- | |||||
| def showEvent(self, event): | |||||
| self.slot_focusSearchFieldAndSelectAll() | |||||
| QDialog.showEvent(self, event) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Testing | |||||
| if __name__ == '__main__': | |||||
| import sys | |||||
| class _host: | |||||
| pathBinaries = "" | |||||
| showPluginBridges = False | |||||
| showWineBridges = False | |||||
| _host = _host() | |||||
| _app = QApplication(sys.argv) | |||||
| _gui = PluginDatabaseW(None, _host, True) | |||||
| if _gui.exec_(): | |||||
| print(f"Result: {_gui.fRetPlugin}") | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,471 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| import os | |||||
| from PyQt5.QtCore import pyqtSlot, Qt, QT_VERSION | |||||
| from PyQt5.QtGui import QPixmap | |||||
| from PyQt5.QtWidgets import QDialog, QWidget | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Carla) | |||||
| from carla_shared import ( | |||||
| HAIKU, | |||||
| LINUX, | |||||
| MACOS, | |||||
| WINDOWS, | |||||
| gCarla, | |||||
| getIcon, | |||||
| kIs64bit, | |||||
| ) | |||||
| from utils import QSafeSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Local) | |||||
| from .discovery import killDiscovery | |||||
| from .discoverythread import SearchPluginsThread | |||||
| from .pluginlistrefreshdialog_ui import Ui_PluginRefreshW | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Plugin Refresh Dialog | |||||
| class PluginRefreshW(QDialog): | |||||
| def __init__(self, parent: QWidget, host, useSystemIcons: bool, hasLoadedLv2Plugins: bool): | |||||
| QDialog.__init__(self, parent) | |||||
| self.host = host | |||||
| self.ui = Ui_PluginRefreshW() | |||||
| self.ui.setupUi(self) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Internal stuff | |||||
| toolNative = "carla-discovery-native.exe" if WINDOWS else "carla-discovery-native" | |||||
| hasNative = os.path.exists(os.path.join(host.pathBinaries, toolNative)) | |||||
| hasPosix32 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-posix32")) | |||||
| hasPosix64 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-posix64")) | |||||
| hasWin32 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-win32.exe")) | |||||
| hasWin64 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-win64.exe")) | |||||
| self.fThread = SearchPluginsThread(self, host.pathBinaries) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Set-up Icons | |||||
| if useSystemIcons: | |||||
| self.ui.b_start.setIcon(getIcon('arrow-right', 16, 'svgz')) | |||||
| self.ui.b_close.setIcon(getIcon('window-close', 16, 'svgz')) | |||||
| if QT_VERSION >= 0x50600: | |||||
| size = int(16 * self.devicePixelRatioF()) | |||||
| else: | |||||
| size = 16 | |||||
| self.fIconYes = QPixmap(getIcon('dialog-ok-apply', 16, 'svgz').pixmap(size)) | |||||
| self.fIconNo = QPixmap(getIcon('dialog-error', 16, 'svgz').pixmap(size)) | |||||
| else: | |||||
| self.fIconYes = QPixmap(":/16x16/dialog-ok-apply.svgz") | |||||
| self.fIconNo = QPixmap(":/16x16/dialog-error.svgz") | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Set-up GUI | |||||
| # FIXME remove LRDF | |||||
| self.ui.ico_rdflib.setPixmap(self.fIconNo) | |||||
| self.ui.b_skip.setVisible(False) | |||||
| if HAIKU: | |||||
| self.ui.ch_posix32.setText("Haiku 32bit") | |||||
| self.ui.ch_posix64.setText("Haiku 64bit") | |||||
| elif LINUX: | |||||
| self.ui.ch_posix32.setText("Linux 32bit") | |||||
| self.ui.ch_posix64.setText("Linux 64bit") | |||||
| elif MACOS: | |||||
| self.ui.ch_posix32.setText("MacOS 32bit") | |||||
| self.ui.ch_posix64.setText("MacOS 64bit") | |||||
| if hasPosix32 and not WINDOWS: | |||||
| self.ui.ico_posix32.setPixmap(self.fIconYes) | |||||
| else: | |||||
| self.ui.ico_posix32.setPixmap(self.fIconNo) | |||||
| self.ui.ch_posix32.setEnabled(False) | |||||
| if hasPosix64 and not WINDOWS: | |||||
| self.ui.ico_posix64.setPixmap(self.fIconYes) | |||||
| else: | |||||
| self.ui.ico_posix64.setPixmap(self.fIconNo) | |||||
| self.ui.ch_posix64.setEnabled(False) | |||||
| if hasWin32: | |||||
| self.ui.ico_win32.setPixmap(self.fIconYes) | |||||
| else: | |||||
| self.ui.ico_win32.setPixmap(self.fIconNo) | |||||
| self.ui.ch_win32.setEnabled(False) | |||||
| if hasWin64: | |||||
| self.ui.ico_win64.setPixmap(self.fIconYes) | |||||
| else: | |||||
| self.ui.ico_win64.setPixmap(self.fIconNo) | |||||
| self.ui.ch_win64.setEnabled(False) | |||||
| if WINDOWS: | |||||
| if kIs64bit: | |||||
| hasNonNative = hasWin32 | |||||
| self.ui.ch_win64.setEnabled(False) | |||||
| self.ui.ch_win64.setVisible(False) | |||||
| self.ui.ico_win64.setVisible(False) | |||||
| self.ui.label_win64.setVisible(False) | |||||
| else: | |||||
| hasNonNative = hasWin64 | |||||
| self.ui.ch_win32.setEnabled(False) | |||||
| self.ui.ch_win32.setVisible(False) | |||||
| self.ui.ico_win32.setVisible(False) | |||||
| self.ui.label_win32.setVisible(False) | |||||
| self.ui.ch_posix32.setEnabled(False) | |||||
| self.ui.ch_posix32.setVisible(False) | |||||
| self.ui.ch_posix64.setEnabled(False) | |||||
| self.ui.ch_posix64.setVisible(False) | |||||
| self.ui.ico_posix32.hide() | |||||
| self.ui.ico_posix64.hide() | |||||
| self.ui.label_posix32.hide() | |||||
| self.ui.label_posix64.hide() | |||||
| self.ui.ico_rdflib.hide() | |||||
| self.ui.label_rdflib.hide() | |||||
| else: | |||||
| if kIs64bit: | |||||
| hasNonNative = bool(hasPosix32 or hasWin32 or hasWin64) | |||||
| self.ui.ch_posix64.setEnabled(False) | |||||
| self.ui.ch_posix64.setVisible(False) | |||||
| self.ui.ico_posix64.setVisible(False) | |||||
| self.ui.label_posix64.setVisible(False) | |||||
| else: | |||||
| hasNonNative = bool(hasPosix64 or hasWin32 or hasWin64) | |||||
| self.ui.ch_posix32.setEnabled(False) | |||||
| self.ui.ch_posix32.setVisible(False) | |||||
| self.ui.ico_posix32.setVisible(False) | |||||
| self.ui.label_posix32.setVisible(False) | |||||
| if not (LINUX or hasWin32 or hasWin64): | |||||
| self.ui.ch_vst3.setEnabled(False) | |||||
| self.ui.ch_vst3.setVisible(False) | |||||
| if MACOS: | |||||
| self.setWindowModality(Qt.WindowModal) | |||||
| else: | |||||
| self.ui.ch_au.setEnabled(False) | |||||
| self.ui.ch_au.setVisible(False) | |||||
| if hasNative: | |||||
| self.ui.ico_native.setPixmap(self.fIconYes) | |||||
| else: | |||||
| self.ui.ico_native.setPixmap(self.fIconNo) | |||||
| self.ui.ch_native.setEnabled(False) | |||||
| self.ui.ch_sf2.setEnabled(False) | |||||
| if not hasNonNative: | |||||
| self.ui.ch_ladspa.setEnabled(False) | |||||
| self.ui.ch_dssi.setEnabled(False) | |||||
| self.ui.ch_vst.setEnabled(False) | |||||
| self.ui.ch_vst3.setEnabled(False) | |||||
| if not hasLoadedLv2Plugins: | |||||
| self.ui.lv2_restart_notice.hide() | |||||
| self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Load settings | |||||
| self.loadSettings() | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Hide bridges if disabled | |||||
| # NOTE: We Assume win32 carla build will not run win64 plugins | |||||
| if (WINDOWS and not kIs64bit) or not host.showPluginBridges: | |||||
| self.ui.ch_native.setChecked(True) | |||||
| self.ui.ch_native.setEnabled(False) | |||||
| self.ui.ch_native.setVisible(False) | |||||
| self.ui.ch_posix32.setChecked(False) | |||||
| self.ui.ch_posix32.setEnabled(False) | |||||
| self.ui.ch_posix32.setVisible(False) | |||||
| self.ui.ch_posix64.setChecked(False) | |||||
| self.ui.ch_posix64.setEnabled(False) | |||||
| self.ui.ch_posix64.setVisible(False) | |||||
| self.ui.ch_win32.setChecked(False) | |||||
| self.ui.ch_win32.setEnabled(False) | |||||
| self.ui.ch_win32.setVisible(False) | |||||
| self.ui.ch_win64.setChecked(False) | |||||
| self.ui.ch_win64.setEnabled(False) | |||||
| self.ui.ch_win64.setVisible(False) | |||||
| self.ui.ico_posix32.hide() | |||||
| self.ui.ico_posix64.hide() | |||||
| self.ui.ico_win32.hide() | |||||
| self.ui.ico_win64.hide() | |||||
| self.ui.label_posix32.hide() | |||||
| self.ui.label_posix64.hide() | |||||
| self.ui.label_win32.hide() | |||||
| self.ui.label_win64.hide() | |||||
| self.ui.sep_format.hide() | |||||
| elif not (WINDOWS or host.showWineBridges): | |||||
| self.ui.ch_win32.setChecked(False) | |||||
| self.ui.ch_win32.setEnabled(False) | |||||
| self.ui.ch_win32.setVisible(False) | |||||
| self.ui.ch_win64.setChecked(False) | |||||
| self.ui.ch_win64.setEnabled(False) | |||||
| self.ui.ch_win64.setVisible(False) | |||||
| self.ui.ico_win32.hide() | |||||
| self.ui.ico_win64.hide() | |||||
| self.ui.label_win32.hide() | |||||
| self.ui.label_win64.hide() | |||||
| # Disable non-supported features | |||||
| features = gCarla.utils.get_supported_features() if gCarla.utils else () | |||||
| if "sf2" not in features: | |||||
| self.ui.ch_sf2.setChecked(False) | |||||
| self.ui.ch_sf2.setEnabled(False) | |||||
| if MACOS and "juce" not in features: | |||||
| self.ui.ch_au.setChecked(False) | |||||
| self.ui.ch_au.setEnabled(False) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Resize to minimum size, as it's very likely UI stuff was hidden | |||||
| self.resize(self.minimumSize()) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Set-up connections | |||||
| self.finished.connect(self.slot_saveSettings) | |||||
| self.ui.b_start.clicked.connect(self.slot_start) | |||||
| self.ui.b_skip.clicked.connect(self.slot_skip) | |||||
| self.ui.ch_native.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_posix32.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_posix64.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_win32.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_win64.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_ladspa.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_dssi.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_lv2.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_vst.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_vst3.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_au.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_sf2.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_sfz.clicked.connect(self.slot_checkTools) | |||||
| self.ui.ch_jsfx.clicked.connect(self.slot_checkTools) | |||||
| self.fThread.pluginLook.connect(self.slot_handlePluginLook) | |||||
| self.fThread.finished.connect(self.slot_handlePluginThreadFinished) | |||||
| # ------------------------------------------------------------------------------------------------------------- | |||||
| # Post-connect setup | |||||
| self.slot_checkTools() | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| def loadSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaRefresh2") | |||||
| check = settings.value("PluginDatabase/SearchLADSPA", True, bool) and self.ui.ch_ladspa.isEnabled() | |||||
| self.ui.ch_ladspa.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchDSSI", True, bool) and self.ui.ch_dssi.isEnabled() | |||||
| self.ui.ch_dssi.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchLV2", True, bool) and self.ui.ch_lv2.isEnabled() | |||||
| self.ui.ch_lv2.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchVST2", True, bool) and self.ui.ch_vst.isEnabled() | |||||
| self.ui.ch_vst.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchVST3", True, bool) and self.ui.ch_vst3.isEnabled() | |||||
| self.ui.ch_vst3.setChecked(check) | |||||
| if MACOS: | |||||
| check = settings.value("PluginDatabase/SearchAU", True, bool) and self.ui.ch_au.isEnabled() | |||||
| else: | |||||
| check = False | |||||
| self.ui.ch_au.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchSF2", False, bool) and self.ui.ch_sf2.isEnabled() | |||||
| self.ui.ch_sf2.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchSFZ", False, bool) and self.ui.ch_sfz.isEnabled() | |||||
| self.ui.ch_sfz.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchJSFX", True, bool) and self.ui.ch_jsfx.isEnabled() | |||||
| self.ui.ch_jsfx.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchNative", True, bool) and self.ui.ch_native.isEnabled() | |||||
| self.ui.ch_native.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchPOSIX32", False, bool) and self.ui.ch_posix32.isEnabled() | |||||
| self.ui.ch_posix32.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchPOSIX64", False, bool) and self.ui.ch_posix64.isEnabled() | |||||
| self.ui.ch_posix64.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchWin32", False, bool) and self.ui.ch_win32.isEnabled() | |||||
| self.ui.ch_win32.setChecked(check) | |||||
| check = settings.value("PluginDatabase/SearchWin64", False, bool) and self.ui.ch_win64.isEnabled() | |||||
| self.ui.ch_win64.setChecked(check) | |||||
| self.ui.ch_do_checks.setChecked(settings.value("PluginDatabase/DoChecks", False, bool)) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_saveSettings(self): | |||||
| settings = QSafeSettings("falkTX", "CarlaRefresh2") | |||||
| settings.setValue("PluginDatabase/SearchLADSPA", self.ui.ch_ladspa.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchDSSI", self.ui.ch_dssi.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchVST3", self.ui.ch_vst3.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchJSFX", self.ui.ch_jsfx.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchNative", self.ui.ch_native.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchPOSIX32", self.ui.ch_posix32.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchPOSIX64", self.ui.ch_posix64.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchWin32", self.ui.ch_win32.isChecked()) | |||||
| settings.setValue("PluginDatabase/SearchWin64", self.ui.ch_win64.isChecked()) | |||||
| settings.setValue("PluginDatabase/DoChecks", self.ui.ch_do_checks.isChecked()) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_start(self): | |||||
| self.ui.progressBar.setMinimum(0) | |||||
| self.ui.progressBar.setMaximum(100) | |||||
| self.ui.progressBar.setValue(0) | |||||
| self.ui.b_start.setEnabled(False) | |||||
| self.ui.b_skip.setVisible(True) | |||||
| self.ui.b_close.setVisible(False) | |||||
| self.ui.group_types.setEnabled(False) | |||||
| self.ui.group_options.setEnabled(False) | |||||
| if gCarla.utils: | |||||
| if self.ui.ch_do_checks.isChecked(): | |||||
| gCarla.utils.unsetenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS") | |||||
| else: | |||||
| gCarla.utils.setenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS", "true") | |||||
| native, posix32, posix64, win32, win64 = (self.ui.ch_native.isChecked(), | |||||
| self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), | |||||
| self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) | |||||
| ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(), | |||||
| self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(), | |||||
| self.ui.ch_vst3.isChecked(), self.ui.ch_au.isChecked(), | |||||
| self.ui.ch_sf2.isChecked(), self.ui.ch_sfz.isChecked(), | |||||
| self.ui.ch_jsfx.isChecked()) | |||||
| self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) | |||||
| self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, au, sf2, sfz, jsfx) | |||||
| self.fThread.start() | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_skip(self): | |||||
| killDiscovery() | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_checkTools(self): | |||||
| enabled1 = bool(self.ui.ch_native.isChecked() or | |||||
| self.ui.ch_posix32.isChecked() or self.ui.ch_posix64.isChecked() or | |||||
| self.ui.ch_win32.isChecked() or self.ui.ch_win64.isChecked()) | |||||
| enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or | |||||
| self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or | |||||
| self.ui.ch_vst3.isChecked() or self.ui.ch_au.isChecked() or | |||||
| self.ui.ch_sf2.isChecked() or self.ui.ch_sfz.isChecked() or | |||||
| self.ui.ch_jsfx.isChecked()) | |||||
| self.ui.b_start.setEnabled(enabled1 and enabled2) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot(float, str) | |||||
| def slot_handlePluginLook(self, percent, plugin): | |||||
| self.ui.progressBar.setFormat(plugin) | |||||
| self.ui.progressBar.setValue(int(percent)) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| @pyqtSlot() | |||||
| def slot_handlePluginThreadFinished(self): | |||||
| self.ui.progressBar.setMinimum(0) | |||||
| self.ui.progressBar.setMaximum(1) | |||||
| self.ui.progressBar.setValue(1) | |||||
| self.ui.progressBar.setFormat(self.tr("Done")) | |||||
| self.ui.b_start.setEnabled(True) | |||||
| self.ui.b_skip.setVisible(False) | |||||
| self.ui.b_close.setVisible(True) | |||||
| self.ui.group_types.setEnabled(True) | |||||
| self.ui.group_options.setEnabled(True) | |||||
| # ----------------------------------------------------------------------------------------------------------------- | |||||
| def closeEvent(self, event): | |||||
| if self.fThread.isRunning(): | |||||
| self.fThread.stop() | |||||
| killDiscovery() | |||||
| #self.fThread.terminate() | |||||
| self.fThread.wait() | |||||
| if self.fThread.hasSomethingChanged(): | |||||
| self.accept() | |||||
| else: | |||||
| self.reject() | |||||
| QDialog.closeEvent(self, event) | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Testing | |||||
| if __name__ == '__main__': | |||||
| import sys | |||||
| # pylint: disable=ungrouped-imports | |||||
| from PyQt5.QtWidgets import QApplication | |||||
| # pylint: enable=ungrouped-imports | |||||
| class _host: | |||||
| pathBinaries = "" | |||||
| showPluginBridges = False | |||||
| showWineBridges = False | |||||
| _host = _host() | |||||
| _app = QApplication(sys.argv) | |||||
| _gui = PluginRefreshW(None, _host, True, False) | |||||
| _app.exec_() | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,19 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin list code | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| from .qsafesettings import QSafeSettings | |||||
| @@ -0,0 +1,64 @@ | |||||
| /* | |||||
| * Carla plugin host | |||||
| * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #pragma once | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-register" | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wclass-memaccess" | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-copy" | |||||
| #endif | |||||
| #include <QtCore/QString> | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| // Custom QString class with a few extra methods | |||||
| class QCarlaString : public QString | |||||
| { | |||||
| public: | |||||
| explicit inline QCarlaString() | |||||
| : QString() {} | |||||
| explicit inline QCarlaString(const char* const str) | |||||
| : QString(fromUtf8(str)) {} | |||||
| inline QCarlaString(const QString& s) | |||||
| : QString(s) {} | |||||
| inline bool isNotEmpty() const | |||||
| { | |||||
| return !isEmpty(); | |||||
| } | |||||
| inline QCarlaString& operator=(const char* const str) | |||||
| { | |||||
| return (*this = fromUtf8(str)); | |||||
| } | |||||
| }; | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,113 @@ | |||||
| /* | |||||
| * Carla plugin host | |||||
| * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #include "qsafesettings.hpp" | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| bool QSafeSettings::valueBool(const QString& key, const bool defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::Bool) && var.isValid()) | |||||
| return var.toBool(); | |||||
| return defaultValue; | |||||
| } | |||||
| Qt::CheckState QSafeSettings::valueCheckState(const QString& key, const Qt::CheckState defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::UInt) && var.isValid()) | |||||
| { | |||||
| const uint value = var.toUInt(); | |||||
| switch (value) | |||||
| { | |||||
| case Qt::Unchecked: | |||||
| case Qt::PartiallyChecked: | |||||
| case Qt::Checked: | |||||
| return static_cast<Qt::CheckState>(value); | |||||
| } | |||||
| } | |||||
| return defaultValue; | |||||
| } | |||||
| int QSafeSettings::valueIntPositive(const QString& key, const int defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::Int) && var.isValid()) | |||||
| return defaultValue; | |||||
| const int value = var.toInt(); | |||||
| return value >= 0 ? value : defaultValue; | |||||
| } | |||||
| uint QSafeSettings::valueUInt(const QString& key, const uint defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::UInt) && var.isValid()) | |||||
| return var.toUInt(); | |||||
| return defaultValue; | |||||
| } | |||||
| double QSafeSettings::valueDouble(const QString& key, const double defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::Double) && var.isValid()) | |||||
| return var.toDouble(); | |||||
| return defaultValue; | |||||
| } | |||||
| QString QSafeSettings::valueString(const QString& key, const QString& defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::String) && var.isValid()) | |||||
| return var.toString(); | |||||
| return defaultValue; | |||||
| } | |||||
| QByteArray QSafeSettings::valueByteArray(const QString& key, const QByteArray defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::ByteArray) && var.isValid()) | |||||
| return var.toByteArray(); | |||||
| return defaultValue; | |||||
| } | |||||
| QStringList QSafeSettings::valueStringList(const QString& key, const QStringList defaultValue) const | |||||
| { | |||||
| QVariant var(value(key, defaultValue)); | |||||
| if (!var.isNull() && var.convert(QVariant::StringList) && var.isValid()) | |||||
| return var.toStringList(); | |||||
| return defaultValue; | |||||
| } | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,60 @@ | |||||
| /* | |||||
| * Carla plugin host | |||||
| * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| * | |||||
| * 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 doc/GPL.txt file. | |||||
| */ | |||||
| #pragma once | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic push | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" | |||||
| # pragma clang diagnostic ignored "-Wdeprecated-register" | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic push | |||||
| # pragma GCC diagnostic ignored "-Wclass-memaccess" | |||||
| # pragma GCC diagnostic ignored "-Wdeprecated-copy" | |||||
| #endif | |||||
| #include <QtCore/QSettings> | |||||
| #ifdef __clang__ | |||||
| # pragma clang diagnostic pop | |||||
| #elif defined(__GNUC__) && __GNUC__ >= 8 | |||||
| # pragma GCC diagnostic pop | |||||
| #endif | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| // Safer QSettings class, which does not throw if type mismatches | |||||
| class QSafeSettings : public QSettings | |||||
| { | |||||
| public: | |||||
| inline QSafeSettings() | |||||
| : QSettings() {} | |||||
| inline QSafeSettings(const QString& organization, const QString& application) | |||||
| : QSettings(organization, application) {} | |||||
| bool valueBool(const QString& key, bool defaultValue) const; | |||||
| Qt::CheckState valueCheckState(const QString& key, Qt::CheckState defaultValue) const; | |||||
| int valueIntPositive(const QString& key, int defaultValue) const; | |||||
| uint valueUInt(const QString& key, uint defaultValue) const; | |||||
| double valueDouble(const QString& key, double defaultValue) const; | |||||
| QString valueString(const QString& key, const QString& defaultValue) const; | |||||
| QByteArray valueByteArray(const QString& key, QByteArray defaultValue = {}) const; | |||||
| QStringList valueStringList(const QString& key, QStringList defaultValue = {}) const; | |||||
| }; | |||||
| //--------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -0,0 +1,37 @@ | |||||
| #!/usr/bin/env python3 | |||||
| # -*- coding: utf-8 -*- | |||||
| # Carla plugin host | |||||
| # Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||||
| # | |||||
| # 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 doc/GPL.txt file. | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Imports (Global) | |||||
| from PyQt5.QtCore import QSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| # Safer QSettings class, which does not throw if type mismatches | |||||
| class QSafeSettings(QSettings): | |||||
| def value(self, key, defaultValue, valueType): | |||||
| if not isinstance(defaultValue, valueType): | |||||
| print("QSafeSettings.value() - defaultValue type mismatch for key", key) | |||||
| try: | |||||
| return QSettings.value(self, key, defaultValue, valueType) | |||||
| except: | |||||
| return defaultValue | |||||
| # --------------------------------------------------------------------------------------------------------------------- | |||||
| @@ -24,9 +24,9 @@ from PyQt5.QtGui import QColor, QPainter, QPixmap | |||||
| from PyQt5.QtWidgets import QActionGroup, QMenu, QScrollArea, QWidget | from PyQt5.QtWidgets import QActionGroup, QMenu, QScrollArea, QWidget | ||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| # Imports (Custom) | |||||
| # Imports (Carla) | |||||
| from carla_shared import QSafeSettings | |||||
| from utils import QSafeSettings | |||||
| # --------------------------------------------------------------------------------------------------------------------- | # --------------------------------------------------------------------------------------------------------------------- | ||||
| @@ -27,7 +27,6 @@ | |||||
| #include "water/memory/SharedResourcePointer.h" | #include "water/memory/SharedResourcePointer.h" | ||||
| #include "water/text/StringArray.h" | #include "water/text/StringArray.h" | ||||
| using water::Array; | |||||
| using water::File; | using water::File; | ||||
| using water::SharedResourcePointer; | using water::SharedResourcePointer; | ||||
| using water::String; | using water::String; | ||||