Browse Source

Start moving files around

pull/1689/head
falkTX 2 years ago
parent
commit
5db10df8be
31 changed files with 3565 additions and 2852 deletions
  1. +6
    -1
      .gitignore
  2. +1
    -0
      source/Makefile.deps.mk
  3. +0
    -188
      source/frontend/C++/carla_database.cpp
  4. +0
    -118
      source/frontend/C++/carla_shared.cpp
  5. +0
    -48
      source/frontend/C++/carla_shared.hpp
  6. +55
    -21
      source/frontend/Makefile
  7. +15
    -2
      source/frontend/carla
  8. +2
    -1
      source/frontend/carla_app.py
  9. +0
    -2454
      source/frontend/carla_database.py
  10. +3
    -1
      source/frontend/carla_host.py
  11. +2
    -1
      source/frontend/carla_settings.py
  12. +0
    -13
      source/frontend/carla_shared.py
  13. +1
    -1
      source/frontend/patchcanvas/patchcanvas.py
  14. +20
    -0
      source/frontend/pluginlist/__init__.py
  15. +390
    -0
      source/frontend/pluginlist/discovery.py
  16. +772
    -0
      source/frontend/pluginlist/discoverythread.py
  17. +264
    -0
      source/frontend/pluginlist/jackappdialog.cpp
  18. +76
    -0
      source/frontend/pluginlist/jackappdialog.hpp
  19. +221
    -0
      source/frontend/pluginlist/jackappdialog.py
  20. +0
    -0
      source/frontend/pluginlist/jackappdialog.ui
  21. +971
    -0
      source/frontend/pluginlist/pluginlistdialog.py
  22. +0
    -0
      source/frontend/pluginlist/pluginlistdialog.ui
  23. +471
    -0
      source/frontend/pluginlist/pluginlistrefreshdialog.py
  24. +0
    -0
      source/frontend/pluginlist/pluginlistrefreshdialog.ui
  25. +19
    -0
      source/frontend/utils/__init__.py
  26. +64
    -0
      source/frontend/utils/qcarlastring.hpp
  27. +113
    -0
      source/frontend/utils/qsafesettings.cpp
  28. +60
    -0
      source/frontend/utils/qsafesettings.hpp
  29. +37
    -0
      source/frontend/utils/qsafesettings.py
  30. +2
    -2
      source/frontend/widgets/pixmapkeyboard.py
  31. +0
    -1
      source/includes/CarlaNativePrograms.hpp

+ 6
- 1
.gitignore View File

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


+ 1
- 0
source/Makefile.deps.mk View File

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


+ 0
- 188
source/frontend/C++/carla_database.cpp View File

@@ -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();
}

// --------------------------------------------------------------------------------------------------------------------

+ 0
- 118
source/frontend/C++/carla_shared.cpp View File

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




+ 0
- 48
source/frontend/C++/carla_shared.hpp View File

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




+ 55
- 21
source/frontend/Makefile View File

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


+ 15
- 2
source/frontend/carla View File

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


+ 2
- 1
source/frontend/carla_app.py View File

@@ -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():


+ 0
- 2454
source/frontend/carla_database.py
File diff suppressed because it is too large
View File


+ 3
- 1
source/frontend/carla_host.py View File

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




+ 2
- 1
source/frontend/carla_settings.py View File

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

# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
# ... # ...




+ 0
- 13
source/frontend/carla_shared.py View File

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




+ 1
- 1
source/frontend/patchcanvas/patchcanvas.py View File

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


# ------------------------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------------------------




+ 20
- 0
source/frontend/pluginlist/__init__.py View File

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

+ 390
- 0
source/frontend/pluginlist/discovery.py View File

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

+ 772
- 0
source/frontend/pluginlist/discoverythread.py View File

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

# ---------------------------------------------------------------------------------------------------------------------

+ 264
- 0
source/frontend/pluginlist/jackappdialog.cpp View File

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

// --------------------------------------------------------------------------------------------------------------------

+ 76
- 0
source/frontend/pluginlist/jackappdialog.hpp View File

@@ -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();
};

// --------------------------------------------------------------------------------------------------------------------

+ 221
- 0
source/frontend/pluginlist/jackappdialog.py View File

@@ -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}")

# ---------------------------------------------------------------------------------------------------------------------

resources/ui/carla_add_jack.ui → source/frontend/pluginlist/jackappdialog.ui View File


+ 971
- 0
source/frontend/pluginlist/pluginlistdialog.py View File

@@ -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}")

# ---------------------------------------------------------------------------------------------------------------------

resources/ui/carla_database.ui → source/frontend/pluginlist/pluginlistdialog.ui View File


+ 471
- 0
source/frontend/pluginlist/pluginlistrefreshdialog.py View File

@@ -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_()

# ---------------------------------------------------------------------------------------------------------------------

resources/ui/carla_refresh.ui → source/frontend/pluginlist/pluginlistrefreshdialog.ui View File


+ 19
- 0
source/frontend/utils/__init__.py View File

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

+ 64
- 0
source/frontend/utils/qcarlastring.hpp View File

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

//---------------------------------------------------------------------------------------------------------------------

+ 113
- 0
source/frontend/utils/qsafesettings.cpp View File

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

//---------------------------------------------------------------------------------------------------------------------

+ 60
- 0
source/frontend/utils/qsafesettings.hpp View File

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

//---------------------------------------------------------------------------------------------------------------------

+ 37
- 0
source/frontend/utils/qsafesettings.py View File

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

# ---------------------------------------------------------------------------------------------------------------------

+ 2
- 2
source/frontend/widgets/pixmapkeyboard.py View File

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


# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------




+ 0
- 1
source/includes/CarlaNativePrograms.hpp View File

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


Loading…
Cancel
Save