Skip to content

Commit

Permalink
Fixes LMMS#6401: Reload Lv2 plugin on SR change
Browse files Browse the repository at this point in the history
This also includes banning blop's wavedata plugins, because they crash
on reloading. Reference: https://gitlab.com/drobilla/blop-lv2/-/issues/3
  • Loading branch information
JohannesLorenz committed Feb 4, 2023
1 parent 7d4157c commit c41a254
Show file tree
Hide file tree
Showing 13 changed files with 170 additions and 41 deletions.
6 changes: 4 additions & 2 deletions include/Lv2ControlBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups
static Plugin::PluginTypes check(const LilvPlugin* m_plugin,
std::vector<PluginIssue> &issues);

void shutdown();
void init(Model* meAsModel);

const LilvPlugin* getPlugin() const { return m_plugin; }

Lv2Proc *control(std::size_t idx) { return m_procs[idx].get(); }
Expand All @@ -95,6 +98,7 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups
Lv2ControlBase(class Model *that, const QString& uri);
Lv2ControlBase(const Lv2ControlBase&) = delete;
~Lv2ControlBase() override;
void reload();

Lv2ControlBase& operator=(const Lv2ControlBase&) = delete;

Expand Down Expand Up @@ -129,8 +133,6 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups
void saveSettings(QDomDocument &doc, QDomElement &that);
void loadSettings(const QDomElement &that);
void loadFile(const QString &file);
//! TODO: not implemented
void reloadPlugin();

/*
more functions that must be called from virtuals
Expand Down
2 changes: 2 additions & 0 deletions include/Lv2Features.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ class Lv2Features
{
return m_featurePointers.data();
}
//! Clear everything
void clear();

private:
//! feature storage
Expand Down
4 changes: 3 additions & 1 deletion include/Lv2Proc.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ class Lv2Proc : public LinkedModelGroup
std::vector<PluginIssue> &issues);

/*
ctor/dtor
ctor/dtor/reload
*/
Lv2Proc(const LilvPlugin* plugin, Model *parent);
~Lv2Proc() override;
void reload();
void onSampleRateChanged();
//! Must be checked after ctor or reload
bool isValid() const { return m_valid; }

Expand Down
6 changes: 4 additions & 2 deletions plugins/Lv2Effect/Lv2FxControlDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@ Lv2FxControlDialog::Lv2FxControlDialog(Lv2FxControls *controls) :
EffectControlDialog(controls),
Lv2ViewBase(this, controls)
{
connect(controls, &Lv2FxControls::modelChanged,
this, [this](){ this->modelChanged();} );
if (m_reloadPluginButton) {
connect(m_reloadPluginButton, &QPushButton::clicked,
this, [this](){ lv2Controls()->reloadPlugin(); });
this, [this](){ lv2Controls()->reload(); });
}
if (m_toggleUIButton) {
connect(m_toggleUIButton, &QPushButton::toggled,
Expand Down Expand Up @@ -70,4 +72,4 @@ void Lv2FxControlDialog::modelChanged()
}


} // namespace lmms::gui
} // namespace lmms::gui
2 changes: 1 addition & 1 deletion plugins/Lv2Effect/Lv2FxControlDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Lv2FxControlDialog : public EffectControlDialog, public Lv2ViewBase

private:
Lv2FxControls *lv2Controls();
void modelChanged() override;
void modelChanged() final;
};


Expand Down
22 changes: 21 additions & 1 deletion plugins/Lv2Effect/Lv2FxControls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,33 @@ Lv2FxControls::Lv2FxControls(class Lv2Effect *effect, const QString& uri) :
if (isValid())
{
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, [this](){Lv2ControlBase::reloadPlugin();});
this, [this](){onSampleRateChanged();});
}
}




void Lv2FxControls::reload()
{
Lv2ControlBase::reload();
emit modelChanged();
}




void Lv2FxControls::onSampleRateChanged()
{
// TODO: once lv2 options are implemented,
// plugins that support it might allow changing their samplerate
// through it instead of reloading
reload();
}




void Lv2FxControls::saveSettings(QDomDocument &doc, QDomElement &that)
{
Lv2ControlBase::saveSettings(doc, that);
Expand Down
5 changes: 5 additions & 0 deletions plugins/Lv2Effect/Lv2FxControls.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ class Lv2FxControlDialog;
class Lv2FxControls : public EffectControls, public Lv2ControlBase
{
Q_OBJECT
signals:
void modelChanged();
public:
Lv2FxControls(Lv2Effect *effect, const QString &uri);
void reload();

void saveSettings(QDomDocument &_doc, QDomElement &_parent) override;
void loadSettings(const QDomElement &that) override;
Expand All @@ -60,6 +63,8 @@ private slots:
void changeControl();

private:
void onSampleRateChanged();

friend class gui::Lv2FxControlDialog;
friend class Lv2Effect;
};
Expand Down
41 changes: 39 additions & 2 deletions plugins/Lv2Instrument/Lv2Instrument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,12 @@ Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg,
{
if (Lv2ControlBase::isValid())
{
clearRunningNotes();

connect(instrumentTrack()->pitchRangeModel(), SIGNAL(dataChanged()),
this, SLOT(updatePitchRange()), Qt::DirectConnection);
connect(Engine::audioEngine(), &AudioEngine::sampleRateChanged,
this, [this](){Lv2ControlBase::reloadPlugin();});
this, [this](){onSampleRateChanged();});

// now we need a play-handle which cares for calling play()
auto iph = new InstrumentPlayHandle(this, instrumentTrackArg);
Expand All @@ -101,6 +103,38 @@ Lv2Instrument::~Lv2Instrument()



void Lv2Instrument::reload()
{
qDebug() << "Lv2Instrument::reload";
Lv2ControlBase::reload();
clearRunningNotes();
emit modelChanged();
}




void Lv2Instrument::clearRunningNotes()
{
#ifdef LV2_INSTRUMENT_USE_MIDI
for (int i = 0; i < NumKeys; ++i) { m_runningNotes[i] = 0; }
#endif
}




void Lv2Instrument::onSampleRateChanged()
{
// TODO: once lv2 options are implemented,
// plugins that support it might allow changing their samplerate
// through it instead of reloading
reload();
}




bool Lv2Instrument::isValid() const { return Lv2ControlBase::isValid(); }


Expand Down Expand Up @@ -209,9 +243,11 @@ Lv2InsView::Lv2InsView(Lv2Instrument *_instrument, QWidget *_parent) :
Lv2ViewBase(this, _instrument)
{
setAutoFillBackground(true);
connect(_instrument, &Lv2Instrument::modelChanged,
this, [this](){ this->modelChanged();} );
if (m_reloadPluginButton) {
connect(m_reloadPluginButton, &QPushButton::clicked,
this, [this](){ this->castModel<Lv2Instrument>()->reloadPlugin();} );
this, [this](){ this->castModel<Lv2Instrument>()->reload();} );
}
if (m_toggleUIButton) {
connect(m_toggleUIButton, &QPushButton::toggled,
Expand Down Expand Up @@ -266,6 +302,7 @@ void Lv2InsView::dropEvent(QDropEvent *_de)

void Lv2InsView::modelChanged()
{
qDebug() << "Lv2insView::modelCh";
Lv2ViewBase::modelChanged(castModel<Lv2Instrument>());
}

Expand Down
5 changes: 5 additions & 0 deletions plugins/Lv2Instrument/Lv2Instrument.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ class Lv2InsView;
class Lv2Instrument : public Instrument, public Lv2ControlBase
{
Q_OBJECT
signals:
void modelChanged();
public:
/*
initialization
*/
Lv2Instrument(InstrumentTrack *instrumentTrackArg,
Descriptor::SubPluginFeatures::Key* key);
~Lv2Instrument() override;
void reload();
void onSampleRateChanged();
//! Must be checked after ctor or reload
bool isValid() const;

Expand Down Expand Up @@ -101,6 +105,7 @@ private slots:
#ifdef LV2_INSTRUMENT_USE_MIDI
std::array<int, NumKeys> m_runningNotes = {};
#endif
void clearRunningNotes();

friend class gui::Lv2InsView;
};
Expand Down
80 changes: 48 additions & 32 deletions src/core/lv2/Lv2ControlBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,30 +54,7 @@ Lv2ControlBase::Lv2ControlBase(Model* that, const QString &uri) :
{
if (m_plugin)
{
int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo
while (channelsLeft > 0)
{
std::unique_ptr<Lv2Proc> newOne = std::make_unique<Lv2Proc>(m_plugin, that);
if (newOne->isValid())
{
channelsLeft -= std::max(
1 + static_cast<bool>(newOne->inPorts().m_right),
1 + static_cast<bool>(newOne->outPorts().m_right));
Q_ASSERT(channelsLeft >= 0);
m_procs.push_back(std::move(newOne));
}
else
{
qCritical() << "Failed instantiating LV2 processor";
m_valid = false;
channelsLeft = 0;
}
}
if (m_valid)
{
m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size();
linkAllModels();
}
init(that);
}
else
{
Expand All @@ -94,6 +71,53 @@ Lv2ControlBase::~Lv2ControlBase() = default;



void Lv2ControlBase::init(Model* meAsModel)
{
int channelsLeft = DEFAULT_CHANNELS; // LMMS plugins are stereo
while (channelsLeft > 0)
{
std::unique_ptr<Lv2Proc> newOne = std::make_unique<Lv2Proc>(m_plugin, meAsModel);
if (newOne->isValid())
{
channelsLeft -= std::max(
1 + static_cast<bool>(newOne->inPorts().m_right),
1 + static_cast<bool>(newOne->outPorts().m_right));
Q_ASSERT(channelsLeft >= 0);
m_procs.push_back(std::move(newOne));
}
else
{
qCritical() << "Failed instantiating LV2 processor";
m_valid = false;
channelsLeft = 0;
}
}
if (m_valid)
{
m_channelsPerProc = DEFAULT_CHANNELS / m_procs.size();
linkAllModels();
}
}




void Lv2ControlBase::shutdown()
{
// currently nothing to do here
}




void Lv2ControlBase::reload()
{
for (const auto& c : m_procs) { c->reload(); }
}




LinkedModelGroup *Lv2ControlBase::getGroup(std::size_t idx)
{
return (m_procs.size() > idx) ? m_procs[idx].get() : nullptr;
Expand Down Expand Up @@ -183,14 +207,6 @@ void Lv2ControlBase::loadFile(const QString &file)



void Lv2ControlBase::reloadPlugin()
{
// TODO
}




std::size_t Lv2ControlBase::controlCount() const {
std::size_t res = 0;
for (const auto& c : m_procs) { res += c->controlCount(); }
Expand Down
8 changes: 8 additions & 0 deletions src/core/lv2/Lv2Features.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ void *&Lv2Features::operator[](const char *featName)
}




void Lv2Features::clear()
{
m_featureByUri.clear();
}


} // namespace lmms

#endif // LMMS_HAVE_LV2
Expand Down
6 changes: 6 additions & 0 deletions src/core/lv2/Lv2Manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ const std::set<const char*, Lv2Manager::CmpStr> Lv2Manager::pluginBlacklist =
"http://calf.sourceforge.net/plugins/TransientDesigner",
"http://calf.sourceforge.net/plugins/Vinyl",

// https://gitlab.com/drobilla/blop-lv2/-/issues/3
"http://drobilla.net/plugins/blop/pulse",
"http://drobilla.net/plugins/blop/sawtooth",
"http://drobilla.net/plugins/blop/square",
"http://drobilla.net/plugins/blop/triangle",

// Visualization, meters, and scopes etc., won't work until we have gui support
"http://distrho.sf.net/plugins/ProM",
"http://distrho.sf.net/plugins/glBars",
Expand Down
Loading

0 comments on commit c41a254

Please sign in to comment.