@@ -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; | ||||