From 93884a2f92bbd6a81ad60700253f44ca9eb466bc Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 8 May 2024 03:14:58 -0400 Subject: [PATCH 1/4] Add PluginPortConfig class --- include/PluginPortConfig.h | 154 +++++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/PluginPortConfig.cpp | 252 ++++++++++++++++++++++++++++++++++ 3 files changed, 407 insertions(+) create mode 100644 include/PluginPortConfig.h create mode 100644 src/core/PluginPortConfig.cpp diff --git a/include/PluginPortConfig.h b/include/PluginPortConfig.h new file mode 100644 index 00000000000..68d68c338b7 --- /dev/null +++ b/include/PluginPortConfig.h @@ -0,0 +1,154 @@ +/* + * PluginPortConfig.h - Specifies how to route audio channels + * in and out of a plugin. + * + * Copyright (c) 2024 Dalton Messmer + * + * This file is part of LMMS - https://lmms.io + * + * 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 (at your option) 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. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_PLUGIN_PORT_CONFIG_H +#define LMMS_PLUGIN_PORT_CONFIG_H + +#include + +#include "ComboBoxModel.h" +#include "lmms_export.h" +#include "SerializingObject.h" + +class QWidget; + +namespace lmms +{ + +namespace gui +{ + +class ComboBox; + +} // namespace gui + +//! Configure channel routing for a plugin's mono/stereo in/out ports +class LMMS_EXPORT PluginPortConfig + : public QObject + , public SerializingObject +{ + Q_OBJECT + +public: + enum class PortType + { + None, + Mono, + Stereo + }; + + enum class Config + { + None = -1, + MonoMix, // mono ports only + LeftOnly, // mono ports only + RightOnly, // mono ports only + Stereo + }; + + enum class MonoPluginType + { + None, + Input, + Output, + Both + }; + + PluginPortConfig(Model* parent = nullptr); + PluginPortConfig(PortType in, PortType out, Model* parent = nullptr); + + /** + * Getters + */ + auto inputPortType() const { return m_inPort; } + auto outputPortType() const { return m_outPort; } + + template + auto portConfig() const -> Config + { + if constexpr (isInput) + { + switch (m_inPort) + { + default: [[fallthrough]]; + case PortType::None: + return Config::None; + case PortType::Mono: + return static_cast(m_config.value()); + case PortType::Stereo: + return Config::Stereo; + } + } + else + { + switch (m_outPort) + { + default: [[fallthrough]]; + case PortType::None: + return Config::None; + case PortType::Mono: + return static_cast(m_config.value()); + case PortType::Stereo: + return Config::Stereo; + } + } + } + + auto hasMonoPort() const -> bool; + auto monoPluginType() const -> MonoPluginType; + auto model() -> ComboBoxModel* { return &m_config; } + + /** + * Setters + */ + void setPortType(unsigned inCount, unsigned outCount); + void setPortType(PortType in, PortType out); + auto setPortConfig(Config config) -> bool; + + /** + * SerializingObject implementation + */ + void saveSettings(QDomDocument& doc, QDomElement& elem) override; + void loadSettings(const QDomElement& elem) override; + auto nodeName() const -> QString override { return "port_config"; } + + auto instantiateView(QWidget* parent = nullptr) -> gui::ComboBox*; + +signals: + void portsChanged(); + +private: + void updateOptions(); + + PortType m_inPort = PortType::None; + PortType m_outPort = PortType::None; + + //! Value is 0..2, which represents { MonoMix, LeftOnly, RightOnly } for non-Stereo plugins + ComboBoxModel m_config; +}; + +} // namespace lmms + +#endif // LMMS_PLUGIN_PORT_CONFIG_H diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a4871fc72b..e170d0583da 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -58,6 +58,7 @@ set(LMMS_SRCS core/Plugin.cpp core/PluginIssue.cpp core/PluginFactory.cpp + core/PluginPortConfig.cpp core/PresetPreviewPlayHandle.cpp core/ProjectJournal.cpp core/ProjectRenderer.cpp diff --git a/src/core/PluginPortConfig.cpp b/src/core/PluginPortConfig.cpp new file mode 100644 index 00000000000..d51dd15b8ab --- /dev/null +++ b/src/core/PluginPortConfig.cpp @@ -0,0 +1,252 @@ +/* + * PluginPortConfig.cpp - Specifies how to route audio channels + * in and out of a plugin. + * + * Copyright (c) 2024 Dalton Messmer + * + * This file is part of LMMS - https://lmms.io + * + * 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 (at your option) 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. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "PluginPortConfig.h" + +#include +#include +#include +#include + +#include "ComboBox.h" +#include "Model.h" + +namespace lmms +{ + +PluginPortConfig::PluginPortConfig(Model* parent) + : QObject{parent} + , m_config{parent, tr("L/R channel configuration")} +{ +} + +PluginPortConfig::PluginPortConfig(PortType in, PortType out, Model* parent) + : QObject{parent} + , m_inPort{in} + , m_outPort{out} + , m_config{parent, tr("L/R channel configuration")} +{ +} + +auto PluginPortConfig::hasMonoPort() const -> bool +{ + return m_inPort == PortType::Mono || m_outPort == PortType::Mono; +} + +auto PluginPortConfig::monoPluginType() const -> MonoPluginType +{ + if (m_inPort == PortType::Mono) + { + if (m_outPort == PortType::Mono) + { + return MonoPluginType::Both; + } + return MonoPluginType::Input; + } + else if (m_outPort == PortType::Mono) + { + return MonoPluginType::Output; + } + return MonoPluginType::None; +} + +void PluginPortConfig::setPortType(unsigned inCount, unsigned outCount) +{ + PortType in; + PortType out; + switch (inCount) + { + case 0: in = PortType::None; break; + case 1: in = PortType::Mono; break; + case 2: in = PortType::Stereo; break; + default: throw std::invalid_argument{"Invalid input count"}; + } + switch (outCount) + { + case 0: out = PortType::None; break; + case 1: out = PortType::Mono; break; + case 2: out = PortType::Stereo; break; + default: throw std::invalid_argument{"Invalid output count"}; + } + setPortType(in, out); +} + +void PluginPortConfig::setPortType(PortType in, PortType out) +{ + if (in == PortType::None && out == PortType::None) { return; } + if (m_inPort == in && m_outPort == out) { return; } + + m_inPort = in; + m_outPort = out; + + updateOptions(); + + emit portsChanged(); +} + +auto PluginPortConfig::setPortConfig(Config config) -> bool +{ + assert(config != Config::None); + if (m_inPort != PortType::Mono && m_outPort != PortType::Mono) + { + if (config != Config::Stereo) { return false; } + m_config.setValue(0); + } + else + { + if (config == Config::Stereo) { return false; } + m_config.setValue(static_cast(config)); + } + + return true; +} + +void PluginPortConfig::saveSettings(QDomDocument& doc, QDomElement& elem) +{ + // Only plugins with a mono in/out need to be saved + //if (m_inPort != PortType::Mono && m_outPort != PortType::Mono) { return; } + + elem.setAttribute("in", static_cast(m_inPort)); // probably not needed, but just in case + elem.setAttribute("out", static_cast(m_outPort)); // ditto + m_config.saveSettings(doc, elem, "config"); +} + +void PluginPortConfig::loadSettings(const QDomElement& elem) +{ + //const auto inPort = static_cast(elem.attribute("in", "0").toInt()); + //const auto outPort = static_cast(elem.attribute("out", "0").toInt()); + m_config.loadSettings(elem, "config"); +} + +auto PluginPortConfig::instantiateView(QWidget* parent) -> gui::ComboBox* +{ + auto view = new gui::ComboBox{parent}; + view->setFixedSize(96, gui::ComboBox::DEFAULT_HEIGHT); + + QString inputType; + switch (inputPortType()) + { + case PluginPortConfig::PortType::None: break; + case PluginPortConfig::PortType::Mono: + inputType += QObject::tr("mono in"); break; + case PluginPortConfig::PortType::Stereo: + inputType += QObject::tr("stereo in"); break; + default: break; + } + + QString outputType; + switch (outputPortType()) + { + case PluginPortConfig::PortType::None: break; + case PluginPortConfig::PortType::Mono: + outputType += QObject::tr("mono out"); break; + case PluginPortConfig::PortType::Stereo: + outputType += QObject::tr("stereo out"); break; + default: break; + } + + QString pluginType; + if (inputType.isEmpty()) { pluginType = outputType; } + else if (outputType.isEmpty()) { pluginType = inputType; } + else { pluginType = QString{"%1, %2"}.arg(inputType, outputType); } + + view->setToolTip(QObject::tr("L/R channel config for %1 plugin").arg(pluginType)); + view->setModel(model()); + + return view; +} + +void PluginPortConfig::updateOptions() +{ + m_config.clear(); + + const auto monoType = monoPluginType(); + if (monoType == PluginPortConfig::MonoPluginType::None) + { + m_config.addItem(tr("Stereo")); + return; + } + + const auto hasInputPort = inputPortType() != PluginPortConfig::PortType::None; + const auto hasOutputPort = outputPortType() != PluginPortConfig::PortType::None; + + // 1. Mono mix + QString itemText; + switch (monoType) + { + case PluginPortConfig::MonoPluginType::Input: + itemText = tr("Downmix to mono"); break; + case PluginPortConfig::MonoPluginType::Output: + itemText = tr("Upmix to stereo"); break; + case PluginPortConfig::MonoPluginType::Both: + itemText = tr("Mono mix"); break; + default: break; + } + m_config.addItem(itemText); + + // 2. Left only + itemText = QString{}; + switch (monoType) + { + case PluginPortConfig::MonoPluginType::Input: + itemText = hasOutputPort + ? tr("L in (R bypass)") + : tr("Left in"); + break; + case PluginPortConfig::MonoPluginType::Output: + itemText = hasInputPort + ? tr("L out (R bypass)") + : tr("Left only"); + break; + case PluginPortConfig::MonoPluginType::Both: + itemText = tr("L only (R bypass)"); + break; + default: break; + } + m_config.addItem(itemText); + + // 3. Right only + itemText = QString{}; + switch (monoType) + { + case PluginPortConfig::MonoPluginType::Input: + itemText = hasOutputPort + ? tr("R in (L bypass)") + : tr("Right in"); + break; + case PluginPortConfig::MonoPluginType::Output: + itemText = hasInputPort + ? tr("R out (L bypass)") + : tr("Right only"); + break; + case PluginPortConfig::MonoPluginType::Both: + itemText = tr("R only (L bypass)"); + break; + default: break; + } + m_config.addItem(itemText); +} + +} // namespace lmms From f1f1010f0d770d807d88f44b34b5bb46b629d5f7 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 8 May 2024 03:24:41 -0400 Subject: [PATCH 2/4] Avoid misleading types and weird casts --- include/RemotePluginClient.h | 8 +++----- plugins/VstBase/RemoteVstPlugin.cpp | 9 +++++---- plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/include/RemotePluginClient.h b/include/RemotePluginClient.h index 77eef68f035..71056725b5c 100644 --- a/include/RemotePluginClient.h +++ b/include/RemotePluginClient.h @@ -58,8 +58,7 @@ class RemotePluginClient : public RemotePluginBase bool processMessage( const message & _m ) override; - virtual void process( const sampleFrame * _in_buf, - sampleFrame * _out_buf ) = 0; + virtual void process(float* _in_buf, float* _out_buf) = 0; virtual void processMidiEvent( const MidiEvent&, const f_cnt_t /* _offset */ ) { @@ -342,9 +341,8 @@ void RemotePluginClient::doProcessing() { if (m_audioBuffer) { - process( (sampleFrame *)( m_inputCount > 0 ? m_audioBuffer.get() : nullptr ), - (sampleFrame *)( m_audioBuffer.get() + - ( m_inputCount*m_bufferSize ) ) ); + process(m_inputCount > 0 ? m_audioBuffer.get() : nullptr, + m_audioBuffer.get() + m_inputCount * m_bufferSize); } else { diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index 0ec60bea4fe..a5733643b35 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -191,7 +191,7 @@ class RemoteVstPlugin : public RemotePluginClient void hideEditor(); void destroyEditor(); - virtual void process( const sampleFrame * _in, sampleFrame * _out ); + void process(float* _in, float* _out) override; virtual void processMidiEvent( const MidiEvent& event, const f_cnt_t offset ); @@ -1027,7 +1027,7 @@ bool RemoteVstPlugin::load( const std::string & _plugin_file ) -void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) +void RemoteVstPlugin::process(float* _in, float* _out) { // first we gonna post all MIDI-events we enqueued so far if( m_midiEvents.size() ) @@ -1076,14 +1076,15 @@ void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) return; } + // NOTE: VST in/out channels are always provided split: in[0..frames] (left), in[frames..2*frames] (right) for( int i = 0; i < inputCount(); ++i ) { - m_inputs[i] = &((float *) _in)[i * bufferSize()]; + m_inputs[i] = &_in[i * bufferSize()]; } for( int i = 0; i < outputCount(); ++i ) { - m_outputs[i] = &((float *) _out)[i * bufferSize()]; + m_outputs[i] = &_out[i * bufferSize()]; memset( m_outputs[i], 0, bufferSize() * sizeof( float ) ); } diff --git a/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp b/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp index c4d6b71a16b..c3c467910a7 100644 --- a/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp @@ -141,9 +141,9 @@ class RemoteZynAddSubFx : public RemotePluginClient, public LocalZynAddSubFx } - void process( const sampleFrame * _in, sampleFrame * _out ) override + void process(float* _in, float* _out) override { - LocalZynAddSubFx::processAudio( _out ); + LocalZynAddSubFx::processAudio( (sampleFrame*)_out ); } void guiLoop(); From c78c17f0f51862ab2c43aca5771269c73be30bc9 Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Wed, 8 May 2024 03:26:13 -0400 Subject: [PATCH 3/4] Use PluginPortConfig for mono VSTs --- include/RemotePlugin.h | 9 ++ plugins/Vestige/Vestige.cpp | 33 +++++--- plugins/Vestige/Vestige.h | 12 ++- plugins/VstBase/VstPlugin.cpp | 3 + plugins/VstEffect/VstEffectControlDialog.cpp | 14 ++-- plugins/VstEffect/VstEffectControls.cpp | 21 +++-- plugins/VstEffect/VstEffectControls.h | 6 +- src/core/PluginPortConfig.cpp | 1 - src/core/RemotePlugin.cpp | 86 +++++++++++++++++--- 9 files changed, 139 insertions(+), 46 deletions(-) diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index c5fcd7dd2c3..b3068250ee1 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -26,6 +26,8 @@ #define LMMS_REMOTE_PLUGIN_H #include "RemotePluginBase.h" + +#include "PluginPortConfig.h" #include "SharedMemory.h" #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) @@ -140,6 +142,11 @@ class LMMS_EXPORT RemotePlugin : public QObject, public RemotePluginBase m_commMutex.unlock(); } + PluginPortConfig& portConfig() + { + return m_portConfig; + } + public slots: virtual void showUI(); virtual void hideUI(); @@ -175,6 +182,8 @@ public slots: int m_inputCount; int m_outputCount; + PluginPortConfig m_portConfig; + #ifndef SYNC_WITH_SHM_FIFO int m_server; QString m_socketFile; diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index 1de713960ba..b443519741a 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -41,6 +41,7 @@ #include "AudioEngine.h" +#include "ComboBox.h" #include "ConfigManager.h" #include "CustomTextKnob.h" #include "Engine.h" @@ -482,10 +483,17 @@ gui::PluginView * VestigeInstrument::instantiateView( QWidget * _parent ) } +PluginPortConfig* VestigeInstrument::portConfig() +{ + if (!m_plugin) { return nullptr; } + return &m_plugin->portConfig(); +} + + namespace gui { -VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, +VestigeInstrumentView::VestigeInstrumentView( VestigeInstrument * _instrument, QWidget * _parent ) : InstrumentViewFixedSize( _instrument, _parent ), lastPosInMenu (0) @@ -602,7 +610,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, SLOT( noteOffAll() ) ); setAcceptDrops( true ); - _instrument2 = _instrument; + m_instrument2 = _instrument; _parent2 = _parent; } @@ -610,7 +618,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, void VestigeInstrumentView::managePlugin( void ) { if ( m_vi->m_plugin != nullptr && m_vi->m_subWindow == nullptr ) { - m_vi->p_subWindow = new ManageVestigeInstrumentView( _instrument2, _parent2, m_vi); + m_vi->p_subWindow = new ManageVestigeInstrumentView( m_instrument2, _parent2, m_vi); } else if (m_vi->m_subWindow != nullptr) { if (m_vi->m_subWindow->widget()->isVisible() == false ) { m_vi->m_scrollArea->show(); @@ -912,8 +920,8 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) -ManageVestigeInstrumentView::ManageVestigeInstrumentView( Instrument * _instrument, - QWidget * _parent, VestigeInstrument * m_vi2 ) : +ManageVestigeInstrumentView::ManageVestigeInstrumentView( VestigeInstrument * _instrument, + QWidget * _parent, VestigeInstrument * _vi2 ) : InstrumentViewFixedSize( _instrument, _parent ) { #if QT_VERSION < 0x50C00 @@ -927,7 +935,7 @@ ManageVestigeInstrumentView::ManageVestigeInstrumentView( Instrument * _instrume #endif - m_vi = m_vi2; + m_vi = _vi2; m_vi->m_scrollArea = new QScrollArea( this ); widget = new QWidget(this); l = new QGridLayout( this ); @@ -960,13 +968,12 @@ ManageVestigeInstrumentView::ManageVestigeInstrumentView( Instrument * _instrume l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); - - m_closeButton = new QPushButton( tr( " Close " ), widget ); - connect( m_closeButton, SIGNAL( clicked() ), this, - SLOT( closeWindow() ) ); - - l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); - + if (m_vi->portConfig()->hasMonoPort()) + { + m_portConfig = m_vi->portConfig()->instantiateView(this); + m_portConfig->setFixedSize(108, gui::ComboBox::DEFAULT_HEIGHT); + l->addWidget(m_portConfig, 0, 2, 1, 3, Qt::AlignLeft); + } for( int i = 0; i < 10; i++ ) { diff --git a/plugins/Vestige/Vestige.h b/plugins/Vestige/Vestige.h index 529893ba087..30ef1cc1458 100644 --- a/plugins/Vestige/Vestige.h +++ b/plugins/Vestige/Vestige.h @@ -42,7 +42,9 @@ class QGridLayout; namespace lmms { +class ComboBox; class FloatModel; +class PluginPortConfig; class VstPlugin; namespace gui @@ -74,6 +76,8 @@ class VestigeInstrument : public Instrument virtual gui::PluginView* instantiateView( QWidget * _parent ); + PluginPortConfig* portConfig(); + protected slots: void setParameter( lmms::Model * action ); void handleConfigChange( QString cls, QString attr, QString value ); @@ -107,7 +111,7 @@ class ManageVestigeInstrumentView : public InstrumentViewFixedSize { Q_OBJECT public: - ManageVestigeInstrumentView( Instrument * _instrument, QWidget * _parent, VestigeInstrument * m_vi2 ); + ManageVestigeInstrumentView( VestigeInstrument * _instrument, QWidget * _parent, VestigeInstrument * _vi2 ); virtual ~ManageVestigeInstrumentView(); @@ -132,7 +136,7 @@ protected slots: QGridLayout * l; QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; - QPushButton * m_closeButton; + ComboBox* m_portConfig; CustomTextKnob ** vstKnobs; } ; @@ -142,7 +146,7 @@ class VestigeInstrumentView : public InstrumentViewFixedSize { Q_OBJECT public: - VestigeInstrumentView( Instrument * _instrument, QWidget * _parent ); + VestigeInstrumentView( VestigeInstrument * _instrument, QWidget * _parent ); virtual ~VestigeInstrumentView() = default; @@ -182,7 +186,7 @@ protected slots: PixmapButton * m_managePluginButton; PixmapButton * m_savePresetButton; - Instrument * _instrument2; + VestigeInstrument* m_instrument2; QWidget * _parent2; } ; diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index 0361d4c25d0..a2a015f651f 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -264,6 +264,8 @@ void VstPlugin::loadSettings( const QDomElement & _this ) } setParameterDump( dump ); } + + portConfig().loadSettings(_this); } @@ -307,6 +309,7 @@ void VstPlugin::saveSettings( QDomDocument & _doc, QDomElement & _this ) } _this.setAttribute( "program", currentProgram() ); + portConfig().saveSettings(_doc, _this); } void VstPlugin::toggleUI() diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 0fb4913a338..81576ed80c9 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -22,20 +22,20 @@ * */ +#include "VstEffectControlDialog.h" + +#include #include #include #include +#include -#include "VstEffectControlDialog.h" +#include "embed.h" +#include "gui_templates.h" +#include "PixmapButton.h" #include "VstEffect.h" #include "VstPlugin.h" -#include "PixmapButton.h" -#include "embed.h" - -#include "gui_templates.h" -#include -#include namespace lmms::gui { diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index c9eb4923451..3e29c6bd8fe 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -30,6 +30,7 @@ #include #include "embed.h" +#include "ComboBox.h" #include "CustomTextKnob.h" #include "VstEffectControls.h" #include "VstEffectControlDialog.h" @@ -165,6 +166,13 @@ gui::EffectControlDialog* VstEffectControls::createView() +PluginPortConfig* VstEffectControls::portConfig() +{ + if (!m_effect->m_plugin) { return nullptr; } + return &m_effect->m_plugin->portConfig(); +} + + void VstEffectControls::managePlugin() { @@ -356,13 +364,12 @@ ManageVSTEffectView::ManageVSTEffectView( VstEffect * _eff, VstEffectControls * l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft ); - - m_closeButton = new QPushButton( tr( " Close " ), widget ); - connect( m_closeButton, SIGNAL( clicked() ), this, - SLOT( closeWindow() ) ); - - l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft ); - + if (m_vi->portConfig()->hasMonoPort()) + { + m_portConfig = m_vi->portConfig()->instantiateView(widget); + m_portConfig->setFixedSize(108, gui::ComboBox::DEFAULT_HEIGHT); + l->addWidget(m_portConfig, 0, 2, 1, 3, Qt::AlignLeft); + } for( int i = 0; i < 10; i++ ) { diff --git a/plugins/VstEffect/VstEffectControls.h b/plugins/VstEffect/VstEffectControls.h index e2bea36e88c..6f21fa102dd 100644 --- a/plugins/VstEffect/VstEffectControls.h +++ b/plugins/VstEffect/VstEffectControls.h @@ -39,11 +39,12 @@ class QScrollArea; namespace lmms { - +class PluginPortConfig; class VstEffect; namespace gui { +class ComboBox; class CustomTextKnob; class ManageVSTEffectView; class VstEffectControlDialog; @@ -68,6 +69,7 @@ class VstEffectControls : public EffectControls gui::EffectControlDialog* createView() override; + PluginPortConfig* portConfig(); protected slots: void updateMenu(); @@ -138,7 +140,7 @@ protected slots: QPushButton * m_syncButton; QPushButton * m_displayAutomatedOnly; - QPushButton * m_closeButton; + ComboBox* m_portConfig; CustomTextKnob ** vstKnobs; } ; diff --git a/src/core/PluginPortConfig.cpp b/src/core/PluginPortConfig.cpp index d51dd15b8ab..2b735b8a208 100644 --- a/src/core/PluginPortConfig.cpp +++ b/src/core/PluginPortConfig.cpp @@ -143,7 +143,6 @@ void PluginPortConfig::loadSettings(const QDomElement& elem) auto PluginPortConfig::instantiateView(QWidget* parent) -> gui::ComboBox* { auto view = new gui::ComboBox{parent}; - view->setFixedSize(96, gui::ComboBox::DEFAULT_HEIGHT); QString inputType; switch (inputPortType()) diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index b46c547da62..cd3d2c87644 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -365,13 +365,44 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf { if( m_splitChannels ) { - for( ch_cnt_t ch = 0; ch < inputs; ++ch ) + // NOTE: VST plugins always use split channels + switch (m_portConfig.portConfig()) { - for( fpp_t frame = 0; frame < frames; ++frame ) - { - m_audioBuffer[ch * frames + frame] = - _in_buf[frame][ch]; - } + case PluginPortConfig::Config::MonoMix: + assert(m_inputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + // mix stereo to mono for mono plugin input + m_audioBuffer[frame] = (_in_buf[frame][0] + _in_buf[frame][1]) / 2; + } + break; + case PluginPortConfig::Config::LeftOnly: + assert(m_inputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + m_audioBuffer[frame] = _in_buf[frame][0]; + _out_buf[frame][1] = _in_buf[frame][1]; // right bypass + } + break; + case PluginPortConfig::Config::RightOnly: + assert(m_inputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + _out_buf[frame][0] = _in_buf[frame][0]; // left bypass + m_audioBuffer[frame] = _in_buf[frame][1]; + } + break; + case PluginPortConfig::Config::Stereo: + assert(m_inputCount == 2); + for (ch_cnt_t ch = 0; ch < inputs; ++ch) + { + for (fpp_t frame = 0; frame < frames; ++frame) + { + m_audioBuffer[ch * frames + frame] = _in_buf[frame][ch]; + } + } + break; + default: throw std::runtime_error{"Invalid input port config"}; } } else if( inputs == DEFAULT_CHANNELS ) @@ -407,13 +438,41 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf DEFAULT_CHANNELS); if( m_splitChannels ) { - for( ch_cnt_t ch = 0; ch < outputs; ++ch ) + // NOTE: VST plugins always use split channels + switch (m_portConfig.portConfig()) { - for( fpp_t frame = 0; frame < frames; ++frame ) - { - _out_buf[frame][ch] = m_audioBuffer[( m_inputCount+ch )* - frames + frame]; - } + case PluginPortConfig::Config::MonoMix: + assert(m_outputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + _out_buf[frame][0] = _out_buf[frame][1] = m_audioBuffer[m_inputCount * frames + frame]; + } + break; + case PluginPortConfig::Config::LeftOnly: + assert(m_outputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + _out_buf[frame][0] = m_audioBuffer[m_inputCount * frames + frame]; + } + break; + case PluginPortConfig::Config::RightOnly: + assert(m_outputCount == 1); + for (fpp_t frame = 0; frame < frames; ++frame) + { + _out_buf[frame][1] = m_audioBuffer[m_inputCount * frames + frame]; + } + break; + case PluginPortConfig::Config::Stereo: + assert(m_outputCount == 2); + for (ch_cnt_t ch = 0; ch < outputs; ++ch) + { + for (fpp_t frame = 0; frame < frames; ++frame) + { + _out_buf[frame][ch] = m_audioBuffer[(m_inputCount + ch) * frames + frame]; + } + } + break; + default: throw std::runtime_error{"Invalid output port config"}; } } else if( outputs == DEFAULT_CHANNELS ) @@ -545,17 +604,20 @@ bool RemotePlugin::processMessage( const message & _m ) case IdChangeInputCount: m_inputCount = _m.getInt( 0 ); + m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); resizeSharedProcessingMemory(); break; case IdChangeOutputCount: m_outputCount = _m.getInt( 0 ); + m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); resizeSharedProcessingMemory(); break; case IdChangeInputOutputCount: m_inputCount = _m.getInt( 0 ); m_outputCount = _m.getInt( 1 ); + m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); resizeSharedProcessingMemory(); break; From 4da60af67a8f2bae8c7d36b5522791a0e8e3c04e Mon Sep 17 00:00:00 2001 From: Dalton Messmer Date: Sat, 15 Jun 2024 21:53:21 -0400 Subject: [PATCH 4/4] Refactor; Fix automation clip tooltip --- include/PluginPortConfig.h | 71 ++++++++---------- include/RemotePlugin.h | 7 +- plugins/Vestige/Vestige.cpp | 2 +- plugins/VstBase/VstPlugin.cpp | 17 ++--- plugins/VstBase/VstPlugin.h | 2 +- plugins/VstEffect/VstEffect.cpp | 2 +- src/core/PluginPortConfig.cpp | 124 ++++++++++++++++---------------- src/core/RemotePlugin.cpp | 94 +++++++++++------------- 8 files changed, 148 insertions(+), 171 deletions(-) diff --git a/include/PluginPortConfig.h b/include/PluginPortConfig.h index 68d68c338b7..c6fcc1d0195 100644 --- a/include/PluginPortConfig.h +++ b/include/PluginPortConfig.h @@ -52,20 +52,13 @@ class LMMS_EXPORT PluginPortConfig Q_OBJECT public: - enum class PortType - { - None, - Mono, - Stereo - }; - enum class Config { - None = -1, - MonoMix, // mono ports only - LeftOnly, // mono ports only - RightOnly, // mono ports only - Stereo + None = -1, + MonoMix = 0, // mono ports only + LeftOnly = 1, // mono ports only + RightOnly = 2, // mono ports only + Stereo = 3 }; enum class MonoPluginType @@ -77,42 +70,33 @@ class LMMS_EXPORT PluginPortConfig }; PluginPortConfig(Model* parent = nullptr); - PluginPortConfig(PortType in, PortType out, Model* parent = nullptr); + PluginPortConfig(int inCount, int outCount, Model* parent = nullptr); /** * Getters */ - auto inputPortType() const { return m_inPort; } - auto outputPortType() const { return m_outPort; } + auto portCountIn() const -> int { return m_portCountIn; } + auto portCountOut() const -> int { return m_portCountOut; } - template - auto portConfig() const -> Config + auto portConfigIn() const -> Config { - if constexpr (isInput) + switch (m_portCountIn) { - switch (m_inPort) - { - default: [[fallthrough]]; - case PortType::None: - return Config::None; - case PortType::Mono: - return static_cast(m_config.value()); - case PortType::Stereo: - return Config::Stereo; - } + default: [[fallthrough]]; + case 0: return Config::None; + case 1: return static_cast(m_config.value()); + case 2: return Config::Stereo; } - else + } + + auto portConfigOut() const -> Config + { + switch (m_portCountOut) { - switch (m_outPort) - { - default: [[fallthrough]]; - case PortType::None: - return Config::None; - case PortType::Mono: - return static_cast(m_config.value()); - case PortType::Stereo: - return Config::Stereo; - } + default: [[fallthrough]]; + case 0: return Config::None; + case 1: return static_cast(m_config.value()); + case 2: return Config::Stereo; } } @@ -123,8 +107,9 @@ class LMMS_EXPORT PluginPortConfig /** * Setters */ - void setPortType(unsigned inCount, unsigned outCount); - void setPortType(PortType in, PortType out); + void setPortCounts(int inCount, int outCount); + void setPortCountIn(int inCount); + void setPortCountOut(int outCount); auto setPortConfig(Config config) -> bool; /** @@ -142,8 +127,8 @@ class LMMS_EXPORT PluginPortConfig private: void updateOptions(); - PortType m_inPort = PortType::None; - PortType m_outPort = PortType::None; + int m_portCountIn = DEFAULT_CHANNELS; + int m_portCountOut = DEFAULT_CHANNELS; //! Value is 0..2, which represents { MonoMix, LeftOnly, RightOnly } for non-Stereo plugins ComboBoxModel m_config; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index b3068250ee1..2d3d0b1f992 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -44,7 +44,7 @@ class ProcessWatcher : public QThread { Q_OBJECT public: - ProcessWatcher( RemotePlugin * ); + explicit ProcessWatcher(RemotePlugin* plugin); ~ProcessWatcher() override = default; void stop() @@ -71,7 +71,7 @@ class LMMS_EXPORT RemotePlugin : public QObject, public RemotePluginBase { Q_OBJECT public: - RemotePlugin(); + explicit RemotePlugin(Model* parent = nullptr); ~RemotePlugin() override; inline bool isRunning() @@ -179,9 +179,6 @@ public slots: SharedMemory m_audioBuffer; std::size_t m_audioBufferSize; - int m_inputCount; - int m_outputCount; - PluginPortConfig m_portConfig; #ifndef SYNC_WITH_SHM_FIFO diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index b443519741a..9377cbed81e 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -364,7 +364,7 @@ void VestigeInstrument::loadFile( const QString & _file ) } m_pluginMutex.lock(); - m_plugin = new VstInstrumentPlugin( m_pluginDLL ); + m_plugin = new VstInstrumentPlugin{m_pluginDLL, this}; if( m_plugin->failed() ) { m_pluginMutex.unlock(); diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index a2a015f651f..abd22de34af 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -121,14 +121,15 @@ enum class ExecutableType Unknown, Win32, Win64, Linux64, }; -VstPlugin::VstPlugin( const QString & _plugin ) : - m_plugin( PathUtil::toAbsolute(_plugin) ), - m_pluginWindowID( 0 ), - m_embedMethod( (gui::getGUI() != nullptr) - ? ConfigManager::inst()->vstEmbedMethod() - : "headless" ), - m_version( 0 ), - m_currentProgram() +VstPlugin::VstPlugin(const QString& plugin, Model* parent) + : RemotePlugin{parent} + , m_plugin{PathUtil::toAbsolute(plugin)} + , m_pluginWindowID{0} + , m_embedMethod{(gui::getGUI() != nullptr) + ? ConfigManager::inst()->vstEmbedMethod() + : "headless"} + , m_version{0} + , m_currentProgram{-1} { setSplittedChannels( true ); diff --git a/plugins/VstBase/VstPlugin.h b/plugins/VstBase/VstPlugin.h index 03e732970e2..04a044b1da9 100644 --- a/plugins/VstBase/VstPlugin.h +++ b/plugins/VstBase/VstPlugin.h @@ -44,7 +44,7 @@ class VSTBASE_EXPORT VstPlugin : public RemotePlugin, public JournallingObject { Q_OBJECT public: - VstPlugin( const QString & _plugin ); + explicit VstPlugin(const QString& plugin, Model* parent = nullptr); ~VstPlugin() override; void tryLoad( const QString &remoteVstPluginExecutable ); diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index bdbdea8060c..f61623f9987 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -133,7 +133,7 @@ void VstEffect::openPlugin( const QString & _plugin ) } QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml ); - m_plugin = QSharedPointer(new VstPlugin( _plugin )); + m_plugin = QSharedPointer(new VstPlugin{_plugin, this}); if( m_plugin->failed() ) { m_plugin.clear(); diff --git a/src/core/PluginPortConfig.cpp b/src/core/PluginPortConfig.cpp index 2b735b8a208..83ed9a2ffc3 100644 --- a/src/core/PluginPortConfig.cpp +++ b/src/core/PluginPortConfig.cpp @@ -42,74 +42,81 @@ PluginPortConfig::PluginPortConfig(Model* parent) { } -PluginPortConfig::PluginPortConfig(PortType in, PortType out, Model* parent) +PluginPortConfig::PluginPortConfig(int inCount, int outCount, Model* parent) : QObject{parent} - , m_inPort{in} - , m_outPort{out} + , m_portCountIn{inCount} + , m_portCountOut{outCount} , m_config{parent, tr("L/R channel configuration")} { } auto PluginPortConfig::hasMonoPort() const -> bool { - return m_inPort == PortType::Mono || m_outPort == PortType::Mono; + return m_portCountIn == 1 || m_portCountOut == 1; } auto PluginPortConfig::monoPluginType() const -> MonoPluginType { - if (m_inPort == PortType::Mono) + if (m_portCountIn == 1) { - if (m_outPort == PortType::Mono) + if (m_portCountOut == 1) { return MonoPluginType::Both; } return MonoPluginType::Input; } - else if (m_outPort == PortType::Mono) + else if (m_portCountOut == 1) { return MonoPluginType::Output; } return MonoPluginType::None; } -void PluginPortConfig::setPortType(unsigned inCount, unsigned outCount) +void PluginPortConfig::setPortCounts(int inCount, int outCount) { - PortType in; - PortType out; - switch (inCount) + if (inCount < 0 || inCount > DEFAULT_CHANNELS) { - case 0: in = PortType::None; break; - case 1: in = PortType::Mono; break; - case 2: in = PortType::Stereo; break; - default: throw std::invalid_argument{"Invalid input count"}; + throw std::invalid_argument{"Invalid input count"}; } - switch (outCount) + + if (outCount < 0 || outCount > DEFAULT_CHANNELS) { - case 0: out = PortType::None; break; - case 1: out = PortType::Mono; break; - case 2: out = PortType::Stereo; break; - default: throw std::invalid_argument{"Invalid output count"}; + throw std::invalid_argument{"Invalid output count"}; } - setPortType(in, out); -} -void PluginPortConfig::setPortType(PortType in, PortType out) -{ - if (in == PortType::None && out == PortType::None) { return; } - if (m_inPort == in && m_outPort == out) { return; } + if (inCount == 0 && outCount == 0) + { + throw std::invalid_argument{"At least one port count must be non-zero"}; + } - m_inPort = in; - m_outPort = out; + if (m_portCountIn == inCount && m_portCountOut == outCount) + { + // No action needed + return; + } + + m_portCountIn = inCount; + m_portCountOut = outCount; updateOptions(); emit portsChanged(); } +void PluginPortConfig::setPortCountIn(int inCount) +{ + setPortCounts(inCount, m_portCountOut); +} + +void PluginPortConfig::setPortCountOut(int outCount) +{ + setPortCounts(m_portCountIn, outCount); +} + auto PluginPortConfig::setPortConfig(Config config) -> bool { assert(config != Config::None); - if (m_inPort != PortType::Mono && m_outPort != PortType::Mono) + if (m_portCountIn != 1 && m_portCountOut != 1) { if (config != Config::Stereo) { return false; } m_config.setValue(0); @@ -126,17 +133,18 @@ auto PluginPortConfig::setPortConfig(Config config) -> bool void PluginPortConfig::saveSettings(QDomDocument& doc, QDomElement& elem) { // Only plugins with a mono in/out need to be saved - //if (m_inPort != PortType::Mono && m_outPort != PortType::Mono) { return; } + //if (m_portCountIn != 1 && m_portCountOut != 1) { return; } - elem.setAttribute("in", static_cast(m_inPort)); // probably not needed, but just in case - elem.setAttribute("out", static_cast(m_outPort)); // ditto + elem.setAttribute("in", m_portCountIn); // probably not needed, but just in case + elem.setAttribute("out", m_portCountOut); // ditto m_config.saveSettings(doc, elem, "config"); } void PluginPortConfig::loadSettings(const QDomElement& elem) { - //const auto inPort = static_cast(elem.attribute("in", "0").toInt()); - //const auto outPort = static_cast(elem.attribute("out", "0").toInt()); + // TODO: Assert port counts are what was expected? + //const auto portCountIn = elem.attribute("in", "0").toInt(); + //const auto portCountOut = elem.attribute("out", "0").toInt(); m_config.loadSettings(elem, "config"); } @@ -145,33 +153,29 @@ auto PluginPortConfig::instantiateView(QWidget* parent) -> gui::ComboBox* auto view = new gui::ComboBox{parent}; QString inputType; - switch (inputPortType()) + switch (m_portCountIn) { - case PluginPortConfig::PortType::None: break; - case PluginPortConfig::PortType::Mono: - inputType += QObject::tr("mono in"); break; - case PluginPortConfig::PortType::Stereo: - inputType += QObject::tr("stereo in"); break; + case 0: break; + case 1: inputType += tr("mono in"); break; + case 2: inputType += tr("stereo in"); break; default: break; } QString outputType; - switch (outputPortType()) + switch (m_portCountOut) { - case PluginPortConfig::PortType::None: break; - case PluginPortConfig::PortType::Mono: - outputType += QObject::tr("mono out"); break; - case PluginPortConfig::PortType::Stereo: - outputType += QObject::tr("stereo out"); break; + case 0: break; + case 1: outputType += tr("mono out"); break; + case 2: outputType += tr("stereo out"); break; default: break; } QString pluginType; if (inputType.isEmpty()) { pluginType = outputType; } else if (outputType.isEmpty()) { pluginType = inputType; } - else { pluginType = QString{"%1, %2"}.arg(inputType, outputType); } + else { pluginType = tr("%1, %2").arg(inputType, outputType); } - view->setToolTip(QObject::tr("L/R channel config for %1 plugin").arg(pluginType)); + view->setToolTip(tr("L/R channel config for %1 plugin").arg(pluginType)); view->setModel(model()); return view; @@ -182,24 +186,24 @@ void PluginPortConfig::updateOptions() m_config.clear(); const auto monoType = monoPluginType(); - if (monoType == PluginPortConfig::MonoPluginType::None) + if (monoType == MonoPluginType::None) { m_config.addItem(tr("Stereo")); return; } - const auto hasInputPort = inputPortType() != PluginPortConfig::PortType::None; - const auto hasOutputPort = outputPortType() != PluginPortConfig::PortType::None; + const auto hasInputPort = m_portCountIn != 0; + const auto hasOutputPort = m_portCountOut != 0; // 1. Mono mix QString itemText; switch (monoType) { - case PluginPortConfig::MonoPluginType::Input: + case MonoPluginType::Input: itemText = tr("Downmix to mono"); break; - case PluginPortConfig::MonoPluginType::Output: + case MonoPluginType::Output: itemText = tr("Upmix to stereo"); break; - case PluginPortConfig::MonoPluginType::Both: + case MonoPluginType::Both: itemText = tr("Mono mix"); break; default: break; } @@ -209,17 +213,17 @@ void PluginPortConfig::updateOptions() itemText = QString{}; switch (monoType) { - case PluginPortConfig::MonoPluginType::Input: + case MonoPluginType::Input: itemText = hasOutputPort ? tr("L in (R bypass)") : tr("Left in"); break; - case PluginPortConfig::MonoPluginType::Output: + case MonoPluginType::Output: itemText = hasInputPort ? tr("L out (R bypass)") : tr("Left only"); break; - case PluginPortConfig::MonoPluginType::Both: + case MonoPluginType::Both: itemText = tr("L only (R bypass)"); break; default: break; @@ -230,17 +234,17 @@ void PluginPortConfig::updateOptions() itemText = QString{}; switch (monoType) { - case PluginPortConfig::MonoPluginType::Input: + case MonoPluginType::Input: itemText = hasOutputPort ? tr("R in (L bypass)") : tr("Right in"); break; - case PluginPortConfig::MonoPluginType::Output: + case MonoPluginType::Output: itemText = hasInputPort ? tr("R out (L bypass)") : tr("Right only"); break; - case PluginPortConfig::MonoPluginType::Both: + case MonoPluginType::Both: itemText = tr("R only (L bypass)"); break; default: break; diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index cd3d2c87644..8cc954d495a 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -77,10 +77,10 @@ namespace lmms // simple helper thread monitoring our RemotePlugin - if process terminates // unexpectedly invalidate plugin so LMMS doesn't lock up -ProcessWatcher::ProcessWatcher( RemotePlugin * _p ) : - QThread(), - m_plugin( _p ), - m_quit( false ) +ProcessWatcher::ProcessWatcher(RemotePlugin* plugin) + : QThread{} + , m_plugin{plugin} + , m_quit{false} { } @@ -130,22 +130,21 @@ void ProcessWatcher::run() -RemotePlugin::RemotePlugin() : - QObject(), +RemotePlugin::RemotePlugin(Model* parent) + : QObject{} #ifdef SYNC_WITH_SHM_FIFO - RemotePluginBase( new shmFifo(), new shmFifo() ), + , RemotePluginBase{new shmFifo(), new shmFifo()} #else - RemotePluginBase(), + , RemotePluginBase{} #endif - m_failed( true ), - m_watcher( this ), + , m_failed{true} + , m_watcher{this} #if (QT_VERSION < QT_VERSION_CHECK(5,14,0)) - m_commMutex(QMutex::Recursive), + , m_commMutex{QMutex::Recursive} #endif - m_splitChannels( false ), - m_audioBufferSize( 0 ), - m_inputCount( DEFAULT_CHANNELS ), - m_outputCount( DEFAULT_CHANNELS ) + , m_splitChannels{false} + , m_audioBufferSize{0} + , m_portConfig{parent} { #ifndef SYNC_WITH_SHM_FIFO struct sockaddr_un sa; @@ -359,17 +358,17 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf memset( m_audioBuffer.get(), 0, m_audioBufferSize ); - ch_cnt_t inputs = std::min(m_inputCount, DEFAULT_CHANNELS); + const ch_cnt_t inputsReal = m_portConfig.portCountIn(); + const ch_cnt_t inputsClamped = std::min(inputsReal, DEFAULT_CHANNELS); - if( _in_buf != nullptr && inputs > 0 ) + if( _in_buf != nullptr && inputsClamped > 0 ) { if( m_splitChannels ) { // NOTE: VST plugins always use split channels - switch (m_portConfig.portConfig()) + switch (m_portConfig.portConfigIn()) { case PluginPortConfig::Config::MonoMix: - assert(m_inputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { // mix stereo to mono for mono plugin input @@ -377,7 +376,6 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } break; case PluginPortConfig::Config::LeftOnly: - assert(m_inputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { m_audioBuffer[frame] = _in_buf[frame][0]; @@ -385,7 +383,6 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } break; case PluginPortConfig::Config::RightOnly: - assert(m_inputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { _out_buf[frame][0] = _in_buf[frame][0]; // left bypass @@ -393,8 +390,8 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } break; case PluginPortConfig::Config::Stereo: - assert(m_inputCount == 2); - for (ch_cnt_t ch = 0; ch < inputs; ++ch) + assert(inputsReal == 2); + for (ch_cnt_t ch = 0; ch < inputsClamped; ++ch) { for (fpp_t frame = 0; frame < frames; ++frame) { @@ -405,14 +402,14 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf default: throw std::runtime_error{"Invalid input port config"}; } } - else if( inputs == DEFAULT_CHANNELS ) + else if (inputsClamped == DEFAULT_CHANNELS) { memcpy( m_audioBuffer.get(), _in_buf, frames * BYTES_PER_FRAME ); } else { - auto o = (sampleFrame*)m_audioBuffer.get(); - for( ch_cnt_t ch = 0; ch < inputs; ++ch ) + auto o = reinterpret_cast(m_audioBuffer.get()); + for( ch_cnt_t ch = 0; ch < inputsClamped; ++ch ) { for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -425,7 +422,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf lock(); sendMessage( IdStartProcessing ); - if( m_failed || _out_buf == nullptr || m_outputCount == 0 ) + if (m_failed || _out_buf == nullptr || m_portConfig.portCountOut() == 0) { unlock(); return false; @@ -434,60 +431,56 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf waitForMessage( IdProcessingDone ); unlock(); - const ch_cnt_t outputs = std::min(m_outputCount, - DEFAULT_CHANNELS); + const ch_cnt_t outputsReal = m_portConfig.portCountOut(); + const ch_cnt_t outputsClamped = std::min(outputsReal, DEFAULT_CHANNELS); + if( m_splitChannels ) { // NOTE: VST plugins always use split channels - switch (m_portConfig.portConfig()) + switch (m_portConfig.portConfigOut()) { case PluginPortConfig::Config::MonoMix: - assert(m_outputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { - _out_buf[frame][0] = _out_buf[frame][1] = m_audioBuffer[m_inputCount * frames + frame]; + _out_buf[frame][0] = _out_buf[frame][1] = m_audioBuffer[inputsReal * frames + frame]; } break; case PluginPortConfig::Config::LeftOnly: - assert(m_outputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { - _out_buf[frame][0] = m_audioBuffer[m_inputCount * frames + frame]; + _out_buf[frame][0] = m_audioBuffer[inputsReal * frames + frame]; } break; case PluginPortConfig::Config::RightOnly: - assert(m_outputCount == 1); for (fpp_t frame = 0; frame < frames; ++frame) { - _out_buf[frame][1] = m_audioBuffer[m_inputCount * frames + frame]; + _out_buf[frame][1] = m_audioBuffer[inputsReal * frames + frame]; } break; case PluginPortConfig::Config::Stereo: - assert(m_outputCount == 2); - for (ch_cnt_t ch = 0; ch < outputs; ++ch) + for (ch_cnt_t ch = 0; ch < outputsClamped; ++ch) { for (fpp_t frame = 0; frame < frames; ++frame) { - _out_buf[frame][ch] = m_audioBuffer[(m_inputCount + ch) * frames + frame]; + _out_buf[frame][ch] = m_audioBuffer[(inputsReal + ch) * frames + frame]; } } break; default: throw std::runtime_error{"Invalid output port config"}; } } - else if( outputs == DEFAULT_CHANNELS ) + else if (outputsClamped == DEFAULT_CHANNELS) { - memcpy( _out_buf, m_audioBuffer.get() + m_inputCount * frames, + memcpy( _out_buf, m_audioBuffer.get() + inputsReal * frames, frames * BYTES_PER_FRAME ); } else { - auto o = (sampleFrame*)(m_audioBuffer.get() + m_inputCount * frames); + auto o = reinterpret_cast(m_audioBuffer.get() + inputsReal * frames); // clear buffer, if plugin didn't fill up both channels BufferManager::clear( _out_buf, frames ); - for (ch_cnt_t ch = 0; ch < - std::min(DEFAULT_CHANNELS, outputs); ++ch) + for (ch_cnt_t ch = 0; ch < outputsClamped; ++ch) { for( fpp_t frame = 0; frame < frames; ++frame ) { @@ -535,7 +528,8 @@ void RemotePlugin::hideUI() void RemotePlugin::resizeSharedProcessingMemory() { - const size_t s = (m_inputCount + m_outputCount) * Engine::audioEngine()->framesPerPeriod(); + const size_t s = (m_portConfig.portCountIn() + m_portConfig.portCountOut()) + * Engine::audioEngine()->framesPerPeriod(); try { m_audioBuffer.create(QUuid::createUuid().toString().toStdString(), s); @@ -603,21 +597,17 @@ bool RemotePlugin::processMessage( const message & _m ) break; case IdChangeInputCount: - m_inputCount = _m.getInt( 0 ); - m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); + m_portConfig.setPortCountIn(_m.getInt(0)); resizeSharedProcessingMemory(); break; case IdChangeOutputCount: - m_outputCount = _m.getInt( 0 ); - m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); + m_portConfig.setPortCountOut(_m.getInt(0)); resizeSharedProcessingMemory(); break; case IdChangeInputOutputCount: - m_inputCount = _m.getInt( 0 ); - m_outputCount = _m.getInt( 1 ); - m_portConfig.setPortType(static_cast(m_inputCount), static_cast(m_outputCount)); + m_portConfig.setPortCounts(_m.getInt(0), _m.getInt(1)); resizeSharedProcessingMemory(); break;