@@ -4,6 +4,7 @@ | |||
.*.kate-swp | |||
.libmagic-tmp | |||
.libmagic-tmp.bc | |||
.cache/ | |||
.kdev4/ | |||
.DS_Store | |||
@@ -17,6 +18,7 @@ | |||
*.7z | |||
*.bz2 | |||
*.a | |||
*.d | |||
*.o | |||
*.dll | |||
*.dll.def | |||
@@ -58,6 +60,8 @@ qrc_resources.cpp | |||
*.pyc | |||
# Qt files | |||
*_ui.hpp | |||
*_ui.py | |||
*_rc.cpp | |||
*_rc.py | |||
ui_*.hpp | |||
@@ -104,6 +108,7 @@ carla-native-plugin | |||
carla-rest-server | |||
zynaddsubfx-ui | |||
compile_commands.json | |||
stoat-output.png | |||
source/tests/ansi-pedantic-test_* | |||
@@ -143,7 +148,7 @@ bin/resources/widgets | |||
source/native-plugins/resources/*.py | |||
# Other | |||
source/frontend/carla_config.py | |||
source/frontend/pluginlist/jackappdialog | |||
source/includes/asio/ | |||
source/includes/rewire/ | |||
source/includes/vst2 | |||
@@ -294,6 +294,7 @@ ifeq ($(HAVE_QT5),true) | |||
QT5_HOSTBINS = $(shell $(PKG_CONFIG) --variable=host_bins Qt5Core) | |||
MOC_QT5 ?= $(QT5_HOSTBINS)/moc | |||
RCC_QT5 ?= $(QT5_HOSTBINS)/rcc | |||
UIC_QT5 ?= $(QT5_HOSTBINS)/uic | |||
ifeq (,$(wildcard $(MOC_QT5))) | |||
HAVE_QT5 = false | |||
endif | |||
@@ -315,191 +315,3 @@ void PluginDatabaseW::slot_saveSettings() | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Jack Application Dialog | |||
// NOTE: index matches the one in the UI | |||
enum UiSessionManager { | |||
UI_SESSION_NONE, | |||
UI_SESSION_LADISH, | |||
UI_SESSION_NSM | |||
}; | |||
struct JackApplicationW::PrivateData { | |||
Ui::Dialog ui; | |||
const QString fProjectFilename; | |||
PrivateData(JackApplicationW* const dialog, const QString& projectFilename) | |||
: ui(), | |||
fProjectFilename(projectFilename) | |||
{ | |||
ui.setupUi(dialog); | |||
ui.group_error->setVisible(false); | |||
// ------------------------------------------------------------------------------------------------------------ | |||
// Load settings | |||
loadSettings(); | |||
} | |||
void checkIfButtonBoxShouldBeEnabled(int index, const QString text) | |||
{ | |||
static QList<QChar> badFirstChars = { '.', '/' }; | |||
bool enabled = text.length() > 0; | |||
QCarlaString showErr; | |||
// NSM applications must not be abstract or absolute paths, and must not contain arguments | |||
if (enabled && index == UI_SESSION_NSM) | |||
{ | |||
if (badFirstChars.contains(text[0])) | |||
showErr = tr("NSM applications cannot use abstract or absolute paths"); | |||
else if (text.contains(' ') || text.contains(';') || text.contains('&')) | |||
showErr = tr("NSM applications cannot use CLI arguments"); | |||
else if (fProjectFilename.isEmpty()) | |||
showErr = tr("You need to save the current Carla project before NSM can be used"); | |||
} | |||
if (showErr.isNotEmpty()) | |||
{ | |||
enabled = false; | |||
ui.l_error->setText(showErr); | |||
ui.group_error->setVisible(true); | |||
} | |||
else | |||
{ | |||
ui.group_error->setVisible(false); | |||
} | |||
if (QPushButton* const button = ui.buttonBox->button(QDialogButtonBox::Ok)) | |||
button->setEnabled(enabled); | |||
} | |||
void loadSettings() | |||
{ | |||
const QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||
const QString smName = settings.valueString("SessionManager", ""); | |||
if (smName == "LADISH (SIGUSR1)") | |||
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH); | |||
else if (smName == "NSM") | |||
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM); | |||
else | |||
ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE); | |||
ui.le_command->setText(settings.valueString("Command", "")); | |||
ui.le_name->setText(settings.valueString("Name", "")); | |||
ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||
ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||
ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2)); | |||
ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0)); | |||
ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0)); | |||
ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true)); | |||
ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false)); | |||
ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false)); | |||
checkIfButtonBoxShouldBeEnabled(ui.cb_session_mgr->currentIndex(), ui.le_command->text()); | |||
} | |||
void saveSettings() | |||
{ | |||
QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||
settings.setValue("Command", ui.le_command->text()); | |||
settings.setValue("Name", ui.le_name->text()); | |||
settings.setValue("SessionManager", ui.cb_session_mgr->currentText()); | |||
settings.setValue("NumAudioIns", ui.sb_audio_ins->value()); | |||
settings.setValue("NumAudioOuts", ui.sb_audio_outs->value()); | |||
settings.setValue("NumMidiIns", ui.sb_midi_ins->value()); | |||
settings.setValue("NumMidiOuts", ui.sb_midi_outs->value()); | |||
settings.setValue("ManageWindow", ui.cb_manage_window->isChecked()); | |||
settings.setValue("CaptureFirstWindow", ui.cb_capture_first_window->isChecked()); | |||
settings.setValue("MidiOutMixdown", ui.cb_out_midi_mixdown->isChecked()); | |||
} | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) | |||
}; | |||
JackApplicationW::JackApplicationW(QWidget* parent, const QString& projectFilename) | |||
: QDialog(parent), | |||
self(new PrivateData(this, projectFilename)) | |||
{ | |||
adjustSize(); | |||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | |||
// ---------------------------------------------------------------------------------------------------------------- | |||
// Set-up connections | |||
connect(this, SIGNAL(finished(int)), SLOT(slot_saveSettings())); | |||
connect(self->ui.cb_session_mgr, SIGNAL(currentIndexChanged(int)), SLOT(slot_sessionManagerChanged(int))); | |||
connect(self->ui.le_command, SIGNAL(textChanged(QString)), SLOT(slot_commandChanged(QString))); | |||
} | |||
JackApplicationW::~JackApplicationW() | |||
{ | |||
delete self; | |||
} | |||
void JackApplicationW::getCommandAndFlags(QString& command, QString& name, QString& labelSetup) | |||
{ | |||
name = self->ui.le_name->text(); | |||
command = self->ui.le_command->text(); | |||
if (name.isEmpty()) | |||
{ | |||
name = QFileInfo(command.split(' ').first()).baseName(); | |||
// FIXME | |||
name[0] = name[0].toTitleCase(); | |||
} | |||
SessionManager smgr; | |||
switch (self->ui.cb_session_mgr->currentIndex()) | |||
{ | |||
case UI_SESSION_LADISH: | |||
smgr = LIBJACK_SESSION_MANAGER_LADISH; | |||
break; | |||
case UI_SESSION_NSM: | |||
smgr = LIBJACK_SESSION_MANAGER_NSM; | |||
break; | |||
default: | |||
smgr = LIBJACK_SESSION_MANAGER_NONE; | |||
break; | |||
} | |||
uint flags = 0x0; | |||
if (self->ui.cb_manage_window->isChecked()) | |||
flags |= LIBJACK_FLAG_CONTROL_WINDOW; | |||
if (self->ui.cb_capture_first_window->isChecked()) | |||
flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW; | |||
if (self->ui.cb_buffers_addition_mode->isChecked()) | |||
flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION; | |||
if (self->ui.cb_out_midi_mixdown->isChecked()) | |||
flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN; | |||
if (self->ui.cb_external_start->isChecked()) | |||
flags |= LIBJACK_FLAG_EXTERNAL_START; | |||
labelSetup = QString("%1%2%3%4%5%6").arg(QChar('0' + self->ui.sb_audio_ins->value())) | |||
.arg(QChar('0' + self->ui.sb_audio_outs->value())) | |||
.arg(QChar('0' + self->ui.sb_midi_ins->value())) | |||
.arg(QChar('0' + self->ui.sb_midi_outs->value())) | |||
.arg(QChar('0' + smgr)) | |||
.arg(QChar('0' + flags)); | |||
} | |||
void JackApplicationW::slot_commandChanged(const QString text) | |||
{ | |||
self->checkIfButtonBoxShouldBeEnabled(self->ui.cb_session_mgr->currentIndex(), text); | |||
} | |||
void JackApplicationW::slot_sessionManagerChanged(const int index) | |||
{ | |||
self->checkIfButtonBoxShouldBeEnabled(index, self->ui.le_command->text()); | |||
} | |||
void JackApplicationW::slot_saveSettings() | |||
{ | |||
self->saveSettings(); | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- |
@@ -370,124 +370,6 @@ void QMessageBoxWithBetterWidth::showEvent(QShowEvent* const event) | |||
QMessageBox::showEvent(event); | |||
} | |||
//--------------------------------------------------------------------------------------------------------------------- | |||
// 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 | |||
@@ -437,32 +437,6 @@ int getIndexOfQDoubleListValue(const QList<double>& list, const double value); | |||
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 | |||
@@ -476,28 +450,6 @@ protected: | |||
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 | |||
@@ -12,6 +12,24 @@ include $(CWD)/Makefile.mk | |||
BINDIR := $(CWD)/../bin | |||
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 | |||
@@ -77,26 +95,29 @@ endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# 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 $@ | |||
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: | |||
pylint \ | |||
--extension-pkg-whitelist=PyQt5 \ | |||
@@ -2,7 +2,7 @@ | |||
# -*- coding: utf-8 -*- | |||
# 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 | |||
# modify it under the terms of the GNU General Public License as | |||
@@ -19,7 +19,20 @@ | |||
# ---------------------------------------------------------------------------------------------------------------------- | |||
# 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 | |||
@@ -46,10 +46,11 @@ from carla_shared import ( | |||
CARLA_DEFAULT_MAIN_PRO_THEME_COLOR, | |||
CWD, VERSION, | |||
getPaths, | |||
QSafeSettings, | |||
gCarla | |||
) | |||
from utils import QSafeSettings | |||
# ------------------------------------------------------------------------------------------------------------ | |||
class CarlaApplication(): | |||
@@ -53,13 +53,15 @@ from PyQt5.QtWidgets import ( | |||
import ui_carla_host | |||
from carla_app import * | |||
from carla_backend import * | |||
from carla_backend_qt import CarlaHostQtDLL, CarlaHostQtNull | |||
from carla_database import * | |||
from carla_shared import * | |||
from carla_settings import * | |||
from carla_utils import * | |||
from carla_widgets import * | |||
from patchcanvas import patchcanvas | |||
from pluginlist import PluginDatabaseW | |||
from widgets.digitalpeakmeter import DigitalPeakMeter | |||
from widgets.pixmapkeyboard import PixmapKeyboardHArea | |||
@@ -178,11 +178,12 @@ from carla_shared import ( | |||
getIcon, | |||
fontMetricsHorizontalAdvance, | |||
splitter, | |||
QSafeSettings | |||
) | |||
from patchcanvas.theme import Theme, getThemeName | |||
from utils import QSafeSettings | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# ... | |||
@@ -870,19 +870,6 @@ class QMessageBoxWithBetterWidth(QMessageBox): | |||
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 | |||
@@ -63,7 +63,7 @@ from .utils import CanvasCallback, CanvasGetNewGroupPos, CanvasItemFX, CanvasRem | |||
from . import * | |||
from .scene import PatchScene | |||
from carla_shared import QSafeSettings | |||
from utils import QSafeSettings | |||
# ------------------------------------------------------------------------------------------------------------ | |||
@@ -0,0 +1,20 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# Carla plugin host | |||
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License as | |||
# published by the Free Software Foundation; either version 2 of | |||
# the License, or any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
from .jackappdialog import JackApplicationW | |||
from .pluginlistdialog import PluginDatabaseW |
@@ -0,0 +1,390 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# Carla plugin list code | |||
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License as | |||
# published by the Free Software Foundation; either version 2 of | |||
# the License, or any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Imports (Global) | |||
import os | |||
from copy import deepcopy | |||
from subprocess import Popen, PIPE | |||
from PyQt5.QtCore import qWarning | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Imports (Carla) | |||
from carla_backend import ( | |||
BINARY_NATIVE, | |||
BINARY_NONE, | |||
PLUGIN_AU, | |||
PLUGIN_DSSI, | |||
PLUGIN_LADSPA, | |||
PLUGIN_LV2, | |||
PLUGIN_NONE, | |||
PLUGIN_SF2, | |||
PLUGIN_SFZ, | |||
PLUGIN_VST2, | |||
PLUGIN_VST3, | |||
) | |||
from carla_shared import ( | |||
LINUX, | |||
MACOS, | |||
WINDOWS, | |||
) | |||
from carla_utils import getPluginCategoryAsString | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Plugin Query (helper functions) | |||
def findBinaries(binPath, pluginType, OS): | |||
binaries = [] | |||
if OS == "HAIKU": | |||
extensions = ("") if pluginType == PLUGIN_VST2 else (".so",) | |||
elif OS == "MACOS": | |||
extensions = (".dylib", ".so") | |||
elif OS == "WINDOWS": | |||
extensions = (".dll",) | |||
else: | |||
extensions = (".so",) | |||
for root, _, files in os.walk(binPath): | |||
for name in tuple(name for name in files if name.lower().endswith(extensions)): | |||
binaries.append(os.path.join(root, name)) | |||
return binaries | |||
def findVST3Binaries(binPath): | |||
binaries = [] | |||
for root, dirs, files in os.walk(binPath): | |||
for name in tuple(name for name in (files+dirs) if name.lower().endswith(".vst3")): | |||
binaries.append(os.path.join(root, name)) | |||
return binaries | |||
def findLV2Bundles(bundlePath): | |||
bundles = [] | |||
for root, _, _2 in os.walk(bundlePath, followlinks=True): | |||
if root == bundlePath: | |||
continue | |||
if os.path.exists(os.path.join(root, "manifest.ttl")): | |||
bundles.append(root) | |||
return bundles | |||
def findMacVSTBundles(bundlePath, isVST3): | |||
bundles = [] | |||
extension = ".vst3" if isVST3 else ".vst" | |||
for root, dirs, _ in os.walk(bundlePath, followlinks=True): | |||
for name in tuple(name for name in dirs if name.lower().endswith(extension)): | |||
bundles.append(os.path.join(root, name)) | |||
return bundles | |||
def findFilenames(filePath, stype): | |||
filenames = [] | |||
if stype == "sf2": | |||
extensions = (".sf2",".sf3",) | |||
else: | |||
return [] | |||
for root, _, files in os.walk(filePath): | |||
for name in tuple(name for name in files if name.lower().endswith(extensions)): | |||
filenames.append(os.path.join(root, name)) | |||
return filenames | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Plugin Query | |||
# NOTE: this code is ugly, it is meant to be replaced, so let it be as-is for now | |||
PLUGIN_QUERY_API_VERSION = 12 | |||
PyPluginInfo = { | |||
'API': PLUGIN_QUERY_API_VERSION, | |||
'valid': False, | |||
'build': BINARY_NONE, | |||
'type': PLUGIN_NONE, | |||
'hints': 0x0, | |||
'category': "", | |||
'filename': "", | |||
'name': "", | |||
'label': "", | |||
'maker': "", | |||
'uniqueId': 0, | |||
'audio.ins': 0, | |||
'audio.outs': 0, | |||
'cv.ins': 0, | |||
'cv.outs': 0, | |||
'midi.ins': 0, | |||
'midi.outs': 0, | |||
'parameters.ins': 0, | |||
'parameters.outs': 0 | |||
} | |||
gDiscoveryProcess = None | |||
def findWinePrefix(filename, recursionLimit = 10): | |||
if recursionLimit == 0 or len(filename) < 5 or "/" not in filename: | |||
return "" | |||
path = filename[:filename.rfind("/")] | |||
if os.path.isdir(path + "/dosdevices"): | |||
return path | |||
return findWinePrefix(path, recursionLimit-1) | |||
def runCarlaDiscovery(itype, stype, filename, tool, wineSettings=None): | |||
if not os.path.exists(tool): | |||
qWarning(f"runCarlaDiscovery() - tool '{tool}' does not exist") | |||
return [] | |||
command = [] | |||
if LINUX or MACOS: | |||
command.append("env") | |||
command.append("LANG=C") | |||
command.append("LD_PRELOAD=") | |||
if wineSettings is not None: | |||
command.append("WINEDEBUG=-all") | |||
if wineSettings['autoPrefix']: | |||
winePrefix = findWinePrefix(filename) | |||
else: | |||
winePrefix = "" | |||
if not winePrefix: | |||
envWinePrefix = os.getenv("WINEPREFIX") | |||
if envWinePrefix: | |||
winePrefix = envWinePrefix | |||
elif wineSettings['fallbackPrefix']: | |||
winePrefix = os.path.expanduser(wineSettings['fallbackPrefix']) | |||
else: | |||
winePrefix = os.path.expanduser("~/.wine") | |||
wineCMD = wineSettings['executable'] if wineSettings['executable'] else "wine" | |||
if tool.endswith("64.exe") and os.path.exists(wineCMD + "64"): | |||
wineCMD += "64" | |||
command.append("WINEPREFIX=" + winePrefix) | |||
command.append(wineCMD) | |||
command.append(tool) | |||
command.append(stype) | |||
command.append(filename) | |||
# pylint: disable=global-statement | |||
global gDiscoveryProcess | |||
# pylint: enable=global-statement | |||
# pylint: disable=consider-using-with | |||
gDiscoveryProcess = Popen(command, stdout=PIPE) | |||
# pylint: enable=consider-using-with | |||
pinfo = None | |||
plugins = [] | |||
fakeLabel = os.path.basename(filename).rsplit(".", 1)[0] | |||
while True: | |||
try: | |||
line = gDiscoveryProcess.stdout.readline().decode("utf-8", errors="ignore") | |||
except: | |||
print("ERROR: discovery readline failed") | |||
break | |||
# line is valid, strip it | |||
if line: | |||
line = line.strip() | |||
# line is invalid, try poll() again | |||
elif gDiscoveryProcess.poll() is None: | |||
continue | |||
# line is invalid and poll() failed, stop here | |||
else: | |||
break | |||
if line == "carla-discovery::init::-----------": | |||
pinfo = deepcopy(PyPluginInfo) | |||
pinfo['type'] = itype | |||
pinfo['filename'] = filename if filename != ":all" else "" | |||
elif line == "carla-discovery::end::------------": | |||
if pinfo is not None: | |||
plugins.append(pinfo) | |||
del pinfo | |||
pinfo = None | |||
elif line == "Segmentation fault": | |||
print(f"carla-discovery::crash::{filename} crashed during discovery") | |||
elif line.startswith("err:module:import_dll Library"): | |||
print(line) | |||
elif line.startswith("carla-discovery::info::"): | |||
print(f"{line} - {filename}") | |||
elif line.startswith("carla-discovery::warning::"): | |||
print(f"{line} - {filename}") | |||
elif line.startswith("carla-discovery::error::"): | |||
print(f"{line} - {filename}") | |||
elif line.startswith("carla-discovery::"): | |||
if pinfo is None: | |||
continue | |||
try: | |||
prop, value = line.replace("carla-discovery::", "").split("::", 1) | |||
except: | |||
continue | |||
# pylint: disable=unsupported-assignment-operation | |||
if prop == "build": | |||
if value.isdigit(): | |||
pinfo['build'] = int(value) | |||
elif prop == "name": | |||
pinfo['name'] = value if value else fakeLabel | |||
elif prop == "label": | |||
pinfo['label'] = value if value else fakeLabel | |||
elif prop == "filename": | |||
pinfo['filename'] = value | |||
elif prop == "maker": | |||
pinfo['maker'] = value | |||
elif prop == "category": | |||
pinfo['category'] = value | |||
elif prop == "uniqueId": | |||
if value.isdigit(): | |||
pinfo['uniqueId'] = int(value) | |||
elif prop == "hints": | |||
if value.isdigit(): | |||
pinfo['hints'] = int(value) | |||
elif prop == "audio.ins": | |||
if value.isdigit(): | |||
pinfo['audio.ins'] = int(value) | |||
elif prop == "audio.outs": | |||
if value.isdigit(): | |||
pinfo['audio.outs'] = int(value) | |||
elif prop == "cv.ins": | |||
if value.isdigit(): | |||
pinfo['cv.ins'] = int(value) | |||
elif prop == "cv.outs": | |||
if value.isdigit(): | |||
pinfo['cv.outs'] = int(value) | |||
elif prop == "midi.ins": | |||
if value.isdigit(): | |||
pinfo['midi.ins'] = int(value) | |||
elif prop == "midi.outs": | |||
if value.isdigit(): | |||
pinfo['midi.outs'] = int(value) | |||
elif prop == "parameters.ins": | |||
if value.isdigit(): | |||
pinfo['parameters.ins'] = int(value) | |||
elif prop == "parameters.outs": | |||
if value.isdigit(): | |||
pinfo['parameters.outs'] = int(value) | |||
elif prop == "uri": | |||
if value: | |||
pinfo['label'] = value | |||
else: | |||
# cannot use empty URIs | |||
del pinfo | |||
pinfo = None | |||
continue | |||
else: | |||
print(f"{line} - {filename} (unknown property)") | |||
# pylint: enable=unsupported-assignment-operation | |||
tmp = gDiscoveryProcess | |||
gDiscoveryProcess = None | |||
del tmp | |||
return plugins | |||
def killDiscovery(): | |||
# pylint: disable=global-variable-not-assigned | |||
global gDiscoveryProcess | |||
# pylint: enable=global-variable-not-assigned | |||
if gDiscoveryProcess is not None: | |||
gDiscoveryProcess.kill() | |||
def checkPluginCached(desc, ptype): | |||
pinfo = deepcopy(PyPluginInfo) | |||
pinfo['build'] = BINARY_NATIVE | |||
pinfo['type'] = ptype | |||
pinfo['hints'] = desc['hints'] | |||
pinfo['name'] = desc['name'] | |||
pinfo['label'] = desc['label'] | |||
pinfo['maker'] = desc['maker'] | |||
pinfo['category'] = getPluginCategoryAsString(desc['category']) | |||
pinfo['audio.ins'] = desc['audioIns'] | |||
pinfo['audio.outs'] = desc['audioOuts'] | |||
pinfo['cv.ins'] = desc['cvIns'] | |||
pinfo['cv.outs'] = desc['cvOuts'] | |||
pinfo['midi.ins'] = desc['midiIns'] | |||
pinfo['midi.outs'] = desc['midiOuts'] | |||
pinfo['parameters.ins'] = desc['parameterIns'] | |||
pinfo['parameters.outs'] = desc['parameterOuts'] | |||
if ptype == PLUGIN_LV2: | |||
pinfo['filename'], pinfo['label'] = pinfo['label'].split('\\' if WINDOWS else '/',1) | |||
elif ptype == PLUGIN_SFZ: | |||
pinfo['filename'] = pinfo['label'] | |||
pinfo['label'] = pinfo['name'] | |||
return pinfo | |||
def checkPluginLADSPA(filename, tool, wineSettings=None): | |||
return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, wineSettings) | |||
def checkPluginDSSI(filename, tool, wineSettings=None): | |||
return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, wineSettings) | |||
def checkPluginLV2(filename, tool, wineSettings=None): | |||
return runCarlaDiscovery(PLUGIN_LV2, "LV2", filename, tool, wineSettings) | |||
def checkPluginVST2(filename, tool, wineSettings=None): | |||
return runCarlaDiscovery(PLUGIN_VST2, "VST2", filename, tool, wineSettings) | |||
def checkPluginVST3(filename, tool, wineSettings=None): | |||
return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) | |||
def checkFileSF2(filename, tool): | |||
return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) | |||
def checkFileSFZ(filename, tool): | |||
return runCarlaDiscovery(PLUGIN_SFZ, "SFZ", filename, tool) | |||
def checkAllPluginsAU(tool): | |||
return runCarlaDiscovery(PLUGIN_AU, "AU", ":all", tool) |
@@ -0,0 +1,772 @@ | |||
#!/usr/bin/env python3 | |||
# -*- coding: utf-8 -*- | |||
# Carla plugin host | |||
# Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||
# | |||
# This program is free software; you can redistribute it and/or | |||
# modify it under the terms of the GNU General Public License as | |||
# published by the Free Software Foundation; either version 2 of | |||
# the License, or any later version. | |||
# | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# | |||
# For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Imports (Global) | |||
import os | |||
from PyQt5.QtCore import pyqtSignal, QThread | |||
from PyQt5.QtWidgets import QWidget | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Imports (Custom) | |||
from carla_backend import ( | |||
PLUGIN_AU, | |||
PLUGIN_DSSI, | |||
PLUGIN_JSFX, | |||
PLUGIN_LADSPA, | |||
PLUGIN_LV2, | |||
PLUGIN_SFZ, | |||
PLUGIN_VST2, | |||
) | |||
from carla_shared import ( | |||
CARLA_DEFAULT_DSSI_PATH, | |||
CARLA_DEFAULT_JSFX_PATH, | |||
CARLA_DEFAULT_LADSPA_PATH, | |||
CARLA_DEFAULT_LV2_PATH, | |||
CARLA_DEFAULT_SF2_PATH, | |||
CARLA_DEFAULT_SFZ_PATH, | |||
CARLA_DEFAULT_VST2_PATH, | |||
CARLA_DEFAULT_VST3_PATH, | |||
CARLA_DEFAULT_WINE_AUTO_PREFIX, | |||
CARLA_DEFAULT_WINE_EXECUTABLE, | |||
CARLA_DEFAULT_WINE_FALLBACK_PREFIX, | |||
CARLA_KEY_PATHS_DSSI, | |||
CARLA_KEY_PATHS_JSFX, | |||
CARLA_KEY_PATHS_LADSPA, | |||
CARLA_KEY_PATHS_LV2, | |||
CARLA_KEY_PATHS_SF2, | |||
CARLA_KEY_PATHS_SFZ, | |||
CARLA_KEY_PATHS_VST2, | |||
CARLA_KEY_PATHS_VST3, | |||
CARLA_KEY_WINE_AUTO_PREFIX, | |||
CARLA_KEY_WINE_EXECUTABLE, | |||
CARLA_KEY_WINE_FALLBACK_PREFIX, | |||
HAIKU, | |||
LINUX, | |||
MACOS, | |||
WINDOWS, | |||
gCarla, | |||
splitter, | |||
) | |||
from utils import QSafeSettings | |||
from .discovery import ( | |||
checkAllPluginsAU, | |||
checkFileSF2, | |||
checkPluginCached, | |||
checkPluginDSSI, | |||
checkPluginLADSPA, | |||
checkPluginVST2, | |||
checkPluginVST3, | |||
findBinaries, | |||
findFilenames, | |||
findMacVSTBundles, | |||
findVST3Binaries | |||
) | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
# Separate Thread for Plugin Search | |||
class SearchPluginsThread(QThread): | |||
pluginLook = pyqtSignal(float, str) | |||
def __init__(self, parent: QWidget, pathBinaries: str): | |||
QThread.__init__(self, parent) | |||
self.fContinueChecking = False | |||
self.fPathBinaries = pathBinaries | |||
self.fCheckNative = False | |||
self.fCheckPosix32 = False | |||
self.fCheckPosix64 = False | |||
self.fCheckWin32 = False | |||
self.fCheckWin64 = False | |||
self.fCheckLADSPA = False | |||
self.fCheckDSSI = False | |||
self.fCheckLV2 = False | |||
self.fCheckVST2 = False | |||
self.fCheckVST3 = False | |||
self.fCheckAU = False | |||
self.fCheckSF2 = False | |||
self.fCheckSFZ = False | |||
self.fCheckJSFX = False | |||
if WINDOWS: | |||
toolNative = "carla-discovery-native.exe" | |||
self.fWineSettings = None | |||
else: | |||
toolNative = "carla-discovery-native" | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
self.fWineSettings = { | |||
'executable' : settings.value(CARLA_KEY_WINE_EXECUTABLE, | |||
CARLA_DEFAULT_WINE_EXECUTABLE, str), | |||
'autoPrefix' : settings.value(CARLA_KEY_WINE_AUTO_PREFIX, | |||
CARLA_DEFAULT_WINE_AUTO_PREFIX, bool), | |||
'fallbackPrefix': settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, | |||
CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str), | |||
} | |||
del settings | |||
self.fToolNative = os.path.join(pathBinaries, toolNative) | |||
if not os.path.exists(self.fToolNative): | |||
self.fToolNative = "" | |||
self.fCurCount = 0 | |||
self.fCurPercentValue = 0 | |||
self.fLastCheckValue = 0 | |||
self.fSomethingChanged = False | |||
# ----------------------------------------------------------------------------------------------------------------- | |||
# public methods | |||
def hasSomethingChanged(self): | |||
return self.fSomethingChanged | |||
def setSearchBinaryTypes(self, native: bool, posix32: bool, posix64: bool, win32: bool, win64: bool): | |||
self.fCheckNative = native | |||
self.fCheckPosix32 = posix32 | |||
self.fCheckPosix64 = posix64 | |||
self.fCheckWin32 = win32 | |||
self.fCheckWin64 = win64 | |||
def setSearchPluginTypes(self, ladspa: bool, dssi: bool, lv2: bool, vst2: bool, vst3: bool, au: bool, | |||
sf2: bool, sfz: bool, jsfx: bool): | |||
self.fCheckLADSPA = ladspa | |||
self.fCheckDSSI = dssi | |||
self.fCheckLV2 = lv2 | |||
self.fCheckVST2 = vst2 | |||
self.fCheckVST3 = vst3 and (LINUX or MACOS or WINDOWS) | |||
self.fCheckAU = au and MACOS | |||
self.fCheckSF2 = sf2 | |||
self.fCheckSFZ = sfz | |||
self.fCheckJSFX = jsfx | |||
def stop(self): | |||
self.fContinueChecking = False | |||
# ----------------------------------------------------------------------------------------------------------------- | |||
# protected reimplemented methods | |||
def run(self): | |||
settingsDB = QSafeSettings("falkTX", "CarlaPlugins5") | |||
self.fContinueChecking = True | |||
self.fCurCount = 0 | |||
if self.fCheckNative and not self.fToolNative: | |||
self.fCheckNative = False | |||
# looking for plugins via external discovery | |||
pluginCount = 0 | |||
if self.fCheckLADSPA: | |||
pluginCount += 1 | |||
if self.fCheckDSSI: | |||
pluginCount += 1 | |||
if self.fCheckVST2: | |||
pluginCount += 1 | |||
if self.fCheckVST3: | |||
pluginCount += 1 | |||
# Increase count by the number of externally discoverable plugin types | |||
if self.fCheckNative: | |||
self.fCurCount += pluginCount | |||
# Linux, MacOS and Windows are the only VST3 supported OSes | |||
if self.fCheckVST3 and not (LINUX or MACOS or WINDOWS): | |||
self.fCurCount -= 1 | |||
if self.fCheckPosix32: | |||
self.fCurCount += pluginCount | |||
if self.fCheckVST3 and not (LINUX or MACOS): | |||
self.fCurCount -= 1 | |||
if self.fCheckPosix64: | |||
self.fCurCount += pluginCount | |||
if self.fCheckVST3 and not (LINUX or MACOS): | |||
self.fCurCount -= 1 | |||
if self.fCheckWin32: | |||
self.fCurCount += pluginCount | |||
if self.fCheckWin64: | |||
self.fCurCount += pluginCount | |||
if self.fCheckLV2: | |||
if self.fCheckNative: | |||
self.fCurCount += 1 | |||
else: | |||
self.fCheckLV2 = False | |||
if self.fCheckAU: | |||
if self.fCheckNative or self.fCheckPosix32: | |||
self.fCurCount += int(self.fCheckNative) + int(self.fCheckPosix32) | |||
else: | |||
self.fCheckAU = False | |||
if self.fCheckSF2: | |||
if self.fCheckNative: | |||
self.fCurCount += 1 | |||
else: | |||
self.fCheckSF2 = False | |||
if self.fCheckSFZ: | |||
if self.fCheckNative: | |||
self.fCurCount += 1 | |||
else: | |||
self.fCheckSFZ = False | |||
if self.fCheckJSFX: | |||
if self.fCheckNative: | |||
self.fCurCount += 1 | |||
else: | |||
self.fCheckJSFX = False | |||
if self.fCurCount == 0: | |||
return | |||
self.fCurPercentValue = 100.0 / self.fCurCount | |||
self.fLastCheckValue = 0.0 | |||
del pluginCount | |||
if HAIKU: | |||
OS = "HAIKU" | |||
elif LINUX: | |||
OS = "LINUX" | |||
elif MACOS: | |||
OS = "MACOS" | |||
elif WINDOWS: | |||
OS = "WINDOWS" | |||
else: | |||
OS = "UNKNOWN" | |||
if not self.fContinueChecking: | |||
return | |||
self.fSomethingChanged = True | |||
if self.fCheckLADSPA: | |||
if self.fCheckNative: | |||
plugins = self._checkLADSPA(OS, self.fToolNative) | |||
settingsDB.setValue("Plugins/LADSPA_native", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix32: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-posix32") | |||
plugins = self._checkLADSPA(OS, tool) | |||
settingsDB.setValue("Plugins/LADSPA_posix32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix64: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-posix64") | |||
plugins = self._checkLADSPA(OS, tool) | |||
settingsDB.setValue("Plugins/LADSPA_posix64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin32: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||
plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/LADSPA_win32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin64: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||
plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/LADSPA_win64", plugins) | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckDSSI: | |||
if self.fCheckNative: | |||
plugins = self._checkDSSI(OS, self.fToolNative) | |||
settingsDB.setValue("Plugins/DSSI_native", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix32: | |||
plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||
settingsDB.setValue("Plugins/DSSI_posix32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix64: | |||
plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||
settingsDB.setValue("Plugins/DSSI_posix64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin32: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||
plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/DSSI_win32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin64: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||
plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/DSSI_win64", plugins) | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckLV2: | |||
plugins = self._checkCached(True) | |||
settingsDB.setValue("Plugins/LV2", plugins) | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckVST2: | |||
if self.fCheckNative: | |||
plugins = self._checkVST2(OS, self.fToolNative) | |||
settingsDB.setValue("Plugins/VST2_native", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix32: | |||
plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||
settingsDB.setValue("Plugins/VST2_posix32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix64: | |||
plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||
settingsDB.setValue("Plugins/VST2_posix64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin32: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||
plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/VST2_win32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin64: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||
plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/VST2_win64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckVST3: | |||
if self.fCheckNative and (LINUX or MACOS or WINDOWS): | |||
plugins = self._checkVST3(self.fToolNative) | |||
settingsDB.setValue("Plugins/VST3_native", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix32: | |||
plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||
settingsDB.setValue("Plugins/VST3_posix32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix64: | |||
plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix64")) | |||
settingsDB.setValue("Plugins/VST3_posix64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin32: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") | |||
plugins = self._checkVST3(tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/VST3_win32", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckWin64: | |||
tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") | |||
plugins = self._checkVST3(tool, not WINDOWS) | |||
settingsDB.setValue("Plugins/VST3_win64", plugins) | |||
if not self.fContinueChecking: | |||
return | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckAU: | |||
if self.fCheckNative: | |||
plugins = self._checkCached(False) | |||
settingsDB.setValue("Plugins/AU", plugins) | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckPosix32: | |||
plugins = self._checkAU(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) | |||
settingsDB.setValue("Plugins/AU_posix32", self.fAuPlugins) | |||
if not self.fContinueChecking: | |||
return | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckSF2: | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
SF2_PATH = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list) | |||
del settings | |||
kits = self._checkKIT(SF2_PATH, "sf2") | |||
settingsDB.setValue("Plugins/SF2", kits) | |||
settingsDB.sync() | |||
if not self.fContinueChecking: | |||
return | |||
if self.fCheckSFZ: | |||
kits = self._checkSfzCached() | |||
settingsDB.setValue("Plugins/SFZ", kits) | |||
settingsDB.sync() | |||
if self.fCheckJSFX: | |||
kits = self._checkJsfxCached() | |||
settingsDB.setValue("Plugins/JSFX", kits) | |||
settingsDB.sync() | |||
# ----------------------------------------------------------------------------------------------------------------- | |||
# private methods | |||
def _checkLADSPA(self, OS, tool, isWine=False): | |||
ladspaBinaries = [] | |||
ladspaPlugins = [] | |||
self._pluginLook(self.fLastCheckValue, "LADSPA plugins...") | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
LADSPA_PATH = settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list) | |||
del settings | |||
for iPATH in LADSPA_PATH: | |||
binaries = findBinaries(iPATH, PLUGIN_LADSPA, OS) | |||
for binary in binaries: | |||
if binary not in ladspaBinaries: | |||
ladspaBinaries.append(binary) | |||
ladspaBinaries.sort() | |||
if not self.fContinueChecking: | |||
return ladspaPlugins | |||
for i in range(len(ladspaBinaries)): | |||
ladspa = ladspaBinaries[i] | |||
percent = ( float(i) / len(ladspaBinaries) ) * self.fCurPercentValue | |||
self._pluginLook((self.fLastCheckValue + percent) * 0.9, ladspa) | |||
plugins = checkPluginLADSPA(ladspa, tool, self.fWineSettings if isWine else None) | |||
if plugins: | |||
ladspaPlugins.append(plugins) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return ladspaPlugins | |||
def _checkDSSI(self, OS, tool, isWine=False): | |||
dssiBinaries = [] | |||
dssiPlugins = [] | |||
self._pluginLook(self.fLastCheckValue, "DSSI plugins...") | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
DSSI_PATH = settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list) | |||
del settings | |||
for iPATH in DSSI_PATH: | |||
binaries = findBinaries(iPATH, PLUGIN_DSSI, OS) | |||
for binary in binaries: | |||
if binary not in dssiBinaries: | |||
dssiBinaries.append(binary) | |||
dssiBinaries.sort() | |||
if not self.fContinueChecking: | |||
return dssiPlugins | |||
for i in range(len(dssiBinaries)): | |||
dssi = dssiBinaries[i] | |||
percent = ( float(i) / len(dssiBinaries) ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, dssi) | |||
plugins = checkPluginDSSI(dssi, tool, self.fWineSettings if isWine else None) | |||
if plugins: | |||
dssiPlugins.append(plugins) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return dssiPlugins | |||
def _checkVST2(self, OS, tool, isWine=False): | |||
vst2Binaries = [] | |||
vst2Plugins = [] | |||
if MACOS and not isWine: | |||
self._pluginLook(self.fLastCheckValue, "VST2 bundles...") | |||
else: | |||
self._pluginLook(self.fLastCheckValue, "VST2 plugins...") | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
VST2_PATH = settings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list) | |||
del settings | |||
for iPATH in VST2_PATH: | |||
if MACOS and not isWine: | |||
binaries = findMacVSTBundles(iPATH, False) | |||
else: | |||
binaries = findBinaries(iPATH, PLUGIN_VST2, OS) | |||
for binary in binaries: | |||
if binary not in vst2Binaries: | |||
vst2Binaries.append(binary) | |||
vst2Binaries.sort() | |||
if not self.fContinueChecking: | |||
return vst2Plugins | |||
for i in range(len(vst2Binaries)): | |||
vst2 = vst2Binaries[i] | |||
percent = ( float(i) / len(vst2Binaries) ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, vst2) | |||
plugins = checkPluginVST2(vst2, tool, self.fWineSettings if isWine else None) | |||
if plugins: | |||
vst2Plugins.append(plugins) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return vst2Plugins | |||
def _checkVST3(self, tool, isWine=False): | |||
vst3Binaries = [] | |||
vst3Plugins = [] | |||
if MACOS and not isWine: | |||
self._pluginLook(self.fLastCheckValue, "VST3 bundles...") | |||
else: | |||
self._pluginLook(self.fLastCheckValue, "VST3 plugins...") | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
VST3_PATH = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list) | |||
del settings | |||
for iPATH in VST3_PATH: | |||
if MACOS and not isWine: | |||
binaries = findMacVSTBundles(iPATH, True) | |||
else: | |||
binaries = findVST3Binaries(iPATH) | |||
for binary in binaries: | |||
if binary not in vst3Binaries: | |||
vst3Binaries.append(binary) | |||
vst3Binaries.sort() | |||
if not self.fContinueChecking: | |||
return vst3Plugins | |||
for i in range(len(vst3Binaries)): | |||
vst3 = vst3Binaries[i] | |||
percent = ( float(i) / len(vst3Binaries) ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, vst3) | |||
plugins = checkPluginVST3(vst3, tool, self.fWineSettings if isWine else None) | |||
if plugins: | |||
vst3Plugins.append(plugins) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return vst3Plugins | |||
def _checkAU(self, tool): | |||
auPlugins = [] | |||
plugins = checkAllPluginsAU(tool) | |||
if plugins: | |||
auPlugins.append(plugins) | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return auPlugins | |||
def _checkKIT(self, kitPATH, kitExtension): | |||
kitFiles = [] | |||
kitPlugins = [] | |||
for iPATH in kitPATH: | |||
files = findFilenames(iPATH, kitExtension) | |||
for file_ in files: | |||
if file_ not in kitFiles: | |||
kitFiles.append(file_) | |||
kitFiles.sort() | |||
if not self.fContinueChecking: | |||
return kitPlugins | |||
for i in range(len(kitFiles)): | |||
kit = kitFiles[i] | |||
percent = ( float(i) / len(kitFiles) ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, kit) | |||
if kitExtension == "sf2": | |||
plugins = checkFileSF2(kit, self.fToolNative) | |||
else: | |||
plugins = None | |||
if plugins: | |||
kitPlugins.append(plugins) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return kitPlugins | |||
def _checkCached(self, isLV2): | |||
if isLV2: | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)) | |||
del settings | |||
PLUG_TEXT = "LV2" | |||
PLUG_TYPE = PLUGIN_LV2 | |||
else: # AU | |||
PLUG_PATH = "" | |||
PLUG_TEXT = "AU" | |||
PLUG_TYPE = PLUGIN_AU | |||
plugins = [] | |||
self._pluginLook(self.fLastCheckValue, f"{PLUG_TEXT} plugins...") | |||
if not isLV2: | |||
gCarla.utils.juce_init() | |||
count = gCarla.utils.get_cached_plugin_count(PLUG_TYPE, PLUG_PATH) | |||
if not self.fContinueChecking: | |||
return plugins | |||
for i in range(count): | |||
descInfo = gCarla.utils.get_cached_plugin_info(PLUG_TYPE, i) | |||
percent = ( float(i) / count ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||
if not descInfo['valid']: | |||
continue | |||
plugins.append(checkPluginCached(descInfo, PLUG_TYPE)) | |||
if not self.fContinueChecking: | |||
break | |||
if not isLV2: | |||
gCarla.utils.juce_cleanup() | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return plugins | |||
def _checkSfzCached(self): | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)) | |||
del settings | |||
sfzKits = [] | |||
self._pluginLook(self.fLastCheckValue, "SFZ kits...") | |||
count = gCarla.utils.get_cached_plugin_count(PLUGIN_SFZ, PLUG_PATH) | |||
if not self.fContinueChecking: | |||
return sfzKits | |||
for i in range(count): | |||
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_SFZ, i) | |||
percent = ( float(i) / count ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||
if not descInfo['valid']: | |||
continue | |||
sfzKits.append(checkPluginCached(descInfo, PLUGIN_SFZ)) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return sfzKits | |||
def _checkJsfxCached(self): | |||
settings = QSafeSettings("falkTX", "Carla2") | |||
PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)) | |||
del settings | |||
jsfxPlugins = [] | |||
self._pluginLook(self.fLastCheckValue, "JSFX plugins...") | |||
count = gCarla.utils.get_cached_plugin_count(PLUGIN_JSFX, PLUG_PATH) | |||
if not self.fContinueChecking: | |||
return jsfxPlugins | |||
for i in range(count): | |||
descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_JSFX, i) | |||
percent = ( float(i) / count ) * self.fCurPercentValue | |||
self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) | |||
if not descInfo['valid']: | |||
continue | |||
jsfxPlugins.append(checkPluginCached(descInfo, PLUGIN_JSFX)) | |||
if not self.fContinueChecking: | |||
break | |||
self.fLastCheckValue += self.fCurPercentValue | |||
return jsfxPlugins | |||
def _pluginLook(self, percent, plugin): | |||
self.pluginLook.emit(percent, plugin) | |||
# --------------------------------------------------------------------------------------------------------------------- |
@@ -0,0 +1,264 @@ | |||
/* | |||
* Carla plugin host | |||
* Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* This program is free software; you can redistribute it and/or | |||
* modify it under the terms of the GNU General Public License as | |||
* published by the Free Software Foundation; either version 2 of | |||
* the License, or any later version. | |||
* | |||
* This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
*/ | |||
#include "jackappdialog.hpp" | |||
#ifdef __clang__ | |||
# pragma clang diagnostic push | |||
# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy" | |||
# pragma clang diagnostic ignored "-Wdeprecated-register" | |||
#elif defined(__GNUC__) && __GNUC__ >= 8 | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wclass-memaccess" | |||
# pragma GCC diagnostic ignored "-Wdeprecated-copy" | |||
#endif | |||
#include "jackappdialog_ui.hpp" | |||
#include <QtCore/QFileInfo> | |||
#include <QtCore/QVector> | |||
#include <QtWidgets/QPushButton> | |||
#ifdef __clang__ | |||
# pragma clang diagnostic pop | |||
#elif defined(__GNUC__) && __GNUC__ >= 8 | |||
# pragma GCC diagnostic pop | |||
#endif | |||
#include "../utils/qsafesettings.hpp" | |||
#include "../../includes/CarlaLibJackHints.h" | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Jack Application Dialog | |||
enum { | |||
UI_SESSION_NONE = 0, | |||
UI_SESSION_LADISH = 1, | |||
UI_SESSION_NSM = 2, | |||
}; | |||
struct JackApplicationW::Self { | |||
Ui_Dialog ui; | |||
const QString fProjectFilename; | |||
Self(const char* const projectFilename) | |||
: fProjectFilename(projectFilename) {} | |||
static Self& create(const char* const projectFilename) | |||
{ | |||
Self* const self = new Self(projectFilename); | |||
return *self; | |||
} | |||
}; | |||
JackApplicationW::JackApplicationW(QWidget* const parent, const char* const projectFilename) | |||
: QDialog(parent), | |||
self(Self::create(projectFilename)) | |||
{ | |||
self.ui.setupUi(this); | |||
// ------------------------------------------------------------------------------------------------------------- | |||
// UI setup | |||
self.ui.group_error->setVisible(false); | |||
adjustSize(); | |||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | |||
// ------------------------------------------------------------------------------------------------------------- | |||
// Load settings | |||
loadSettings(); | |||
// ------------------------------------------------------------------------------------------------------------- | |||
// Set-up connections | |||
connect(this, &QDialog::finished, | |||
this, &JackApplicationW::slot_saveSettings); | |||
connect(self.ui.cb_session_mgr, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), | |||
this, &JackApplicationW::slot_sessionManagerChanged); | |||
connect(self.ui.le_command, &QLineEdit::textChanged, | |||
this, &JackApplicationW::slot_commandChanged); | |||
} | |||
JackApplicationW::~JackApplicationW() | |||
{ | |||
delete &self; | |||
} | |||
// ----------------------------------------------------------------------------------------------------------------- | |||
// public methods | |||
JackApplicationW::CommandAndFlags JackApplicationW::getCommandAndFlags() const | |||
{ | |||
const QString command = self.ui.le_command->text(); | |||
QString name = self.ui.le_name->text(); | |||
if (name.isEmpty()) | |||
{ | |||
name = QFileInfo(command.split(' ').first()).baseName(); | |||
name[0] = name[0].toTitleCase(); | |||
} | |||
SessionManager smgr; | |||
switch(self.ui.cb_session_mgr->currentIndex()) | |||
{ | |||
case UI_SESSION_LADISH: | |||
smgr = LIBJACK_SESSION_MANAGER_LADISH; | |||
break; | |||
case UI_SESSION_NSM: | |||
smgr = LIBJACK_SESSION_MANAGER_NSM; | |||
break; | |||
default: | |||
smgr = LIBJACK_SESSION_MANAGER_NONE; | |||
break; | |||
} | |||
uint flags = 0x0; | |||
if (self.ui.cb_manage_window->isChecked()) | |||
flags |= LIBJACK_FLAG_CONTROL_WINDOW; | |||
if (self.ui.cb_capture_first_window->isChecked()) | |||
flags |= LIBJACK_FLAG_CAPTURE_FIRST_WINDOW; | |||
if (self.ui.cb_buffers_addition_mode->isChecked()) | |||
flags |= LIBJACK_FLAG_AUDIO_BUFFERS_ADDITION; | |||
if (self.ui.cb_out_midi_mixdown->isChecked()) | |||
flags |= LIBJACK_FLAG_MIDI_OUTPUT_CHANNEL_MIXDOWN; | |||
if (self.ui.cb_external_start->isChecked()) | |||
flags |= LIBJACK_FLAG_EXTERNAL_START; | |||
const QString labelSetup(QString("%1%2%3%4%5%6").arg(QChar('0' + self.ui.sb_audio_ins->value())) | |||
.arg(QChar('0' + self.ui.sb_audio_outs->value())) | |||
.arg(QChar('0' + self.ui.sb_midi_ins->value())) | |||
.arg(QChar('0' + self.ui.sb_midi_outs->value())) | |||
.arg(QChar('0' + smgr)) | |||
.arg(QChar('0' + flags))); | |||
return {command, name, labelSetup}; | |||
} | |||
// ----------------------------------------------------------------------------------------------------------------- | |||
// private methods | |||
void JackApplicationW::checkIfButtonBoxShouldBeEnabled(const int index, const QCarlaString& command) | |||
{ | |||
bool enabled = command.isNotEmpty(); | |||
QCarlaString showErr; | |||
// NSM applications must not be abstract or absolute paths, and must not contain arguments | |||
if (enabled and index == UI_SESSION_NSM) | |||
{ | |||
if (QVector<QChar>{'.', '/'}.contains(command[0])) | |||
showErr = tr("NSM applications cannot use abstract or absolute paths"); | |||
else if (command.contains(' ') or command.contains(';') or command.contains('&')) | |||
showErr = tr("NSM applications cannot use CLI arguments"); | |||
else if (self.fProjectFilename.isEmpty()) | |||
showErr = tr("You need to save the current Carla project before NSM can be used"); | |||
} | |||
if (showErr.isNotEmpty()) | |||
{ | |||
enabled = false; | |||
self.ui.l_error->setText(showErr); | |||
self.ui.group_error->setVisible(true); | |||
} | |||
else | |||
{ | |||
self.ui.group_error->setVisible(false); | |||
} | |||
if (QPushButton* const button = self.ui.buttonBox->button(QDialogButtonBox::Ok)) | |||
button->setEnabled(enabled); | |||
} | |||
void JackApplicationW::loadSettings() | |||
{ | |||
const QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||
const QString smName = settings.valueString("SessionManager", ""); | |||
if (smName == "LADISH (SIGUSR1)") | |||
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_LADISH); | |||
else if (smName == "NSM") | |||
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NSM); | |||
else | |||
self.ui.cb_session_mgr->setCurrentIndex(UI_SESSION_NONE); | |||
self.ui.le_command->setText(settings.valueString("Command", "")); | |||
self.ui.le_name->setText(settings.valueString("Name", "")); | |||
self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||
self.ui.sb_audio_ins->setValue(settings.valueIntPositive("NumAudioIns", 2)); | |||
self.ui.sb_audio_outs->setValue(settings.valueIntPositive("NumAudioOuts", 2)); | |||
self.ui.sb_midi_ins->setValue(settings.valueIntPositive("NumMidiIns", 0)); | |||
self.ui.sb_midi_outs->setValue(settings.valueIntPositive("NumMidiOuts", 0)); | |||
self.ui.cb_manage_window->setChecked(settings.valueBool("ManageWindow", true)); | |||
self.ui.cb_capture_first_window->setChecked(settings.valueBool("CaptureFirstWindow", false)); | |||
self.ui.cb_out_midi_mixdown->setChecked(settings.valueBool("MidiOutMixdown", false)); | |||
checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), | |||
self.ui.le_command->text()); | |||
} | |||
// ----------------------------------------------------------------------------------------------------------------- | |||
// private slots | |||
void JackApplicationW::slot_commandChanged(const QString& command) | |||
{ | |||
checkIfButtonBoxShouldBeEnabled(self.ui.cb_session_mgr->currentIndex(), command); | |||
} | |||
void JackApplicationW::slot_sessionManagerChanged(const int index) | |||
{ | |||
checkIfButtonBoxShouldBeEnabled(index, self.ui.le_command->text()); | |||
} | |||
void JackApplicationW::slot_saveSettings() | |||
{ | |||
QSafeSettings settings("falkTX", "CarlaAddJackApp"); | |||