Worker thread per plugin instance.

This commit is contained in:
Robin E. R. Davies
2025-07-22 16:02:37 -04:00
parent 7ffbee16cf
commit ea134b46a8
8 changed files with 56 additions and 29 deletions
+1 -1
View File
@@ -163,7 +163,7 @@
"cSpell.ignoreWords": [ "cSpell.ignoreWords": [
"nammodel" "nammodel"
], ],
"cSpell.enabled": true, "cSpell.enabled": false,
// Disable all automatic completion suggestions - only show when Ctrl+Space is pressed // Disable all automatic completion suggestions - only show when Ctrl+Space is pressed
"editor.quickSuggestions": { "editor.quickSuggestions": {
-1
View File
@@ -46,7 +46,6 @@ namespace pipedal {
virtual int GetNumberOfInputAudioChannels() const = 0; virtual int GetNumberOfInputAudioChannels() const = 0;
virtual int GetNumberOfOutputAudioChannels() const = 0; virtual int GetNumberOfOutputAudioChannels() const = 0;
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0; virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread() = 0;
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0; virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
+8 -4
View File
@@ -41,6 +41,7 @@
#include "AudioHost.hpp" #include "AudioHost.hpp"
#include <exception> #include <exception>
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
#include "Worker.hpp"
using namespace pipedal; using namespace pipedal;
namespace fs = std::filesystem; namespace fs = std::filesystem;
@@ -70,6 +71,11 @@ Lv2Effect::Lv2Effect(
this->bypass = pedalboardItem.isEnabled(); this->bypass = pedalboardItem.isEnabled();
this->workerThread = std::make_unique<HostWorkerThread>();
if (info_->WantsWorkerThread())
{
workerThread->StartThread();
}
// stash a list of known file properties that we want to keep synced. // stash a list of known file properties that we want to keep synced.
if (info->piPedalUI()) if (info->piPedalUI())
{ {
@@ -179,10 +185,8 @@ Lv2Effect::Lv2Effect(
const LV2_Worker_Interface *worker_interface = const LV2_Worker_Interface *worker_interface =
(const LV2_Worker_Interface *)lilv_instance_get_extension_data(pInstance, (const LV2_Worker_Interface *)lilv_instance_get_extension_data(pInstance,
LV2_WORKER__interface); LV2_WORKER__interface);
if (worker_interface) this->worker = std::make_unique<Worker>(workerThread, pInstance, worker_interface);
{
this->worker = std::make_unique<Worker>(pHost->GetHostWorkerThread(), pInstance, worker_interface);
}
const LV2_State_Interface *state_interface = const LV2_State_Interface *state_interface =
(const LV2_State_Interface *)lilv_instance_get_extension_data(pInstance, (const LV2_State_Interface *)lilv_instance_get_extension_data(pInstance,
LV2_STATE__interface); LV2_STATE__interface);
+5 -2
View File
@@ -40,12 +40,12 @@
#include "StateInterface.hpp" #include "StateInterface.hpp"
#include "LogFeature.hpp" #include "LogFeature.hpp"
namespace pipedal namespace pipedal
{ {
class RealtimeRingBufferWriter; class RealtimeRingBufferWriter;
class IPatchWriterCallback; class IPatchWriterCallback;
class HostWorkerThread;
class Lv2Effect : public IEffect, private LogFeature::LogMessageListener class Lv2Effect : public IEffect, private LogFeature::LogMessageListener
{ {
@@ -57,6 +57,10 @@ namespace pipedal
private: private:
std::shared_ptr<HostWorkerThread> workerThread;
std::unique_ptr<Worker> worker;
std::unordered_map<std::string,int> controlIndex; std::unordered_map<std::string,int> controlIndex;
FileBrowserFilesFeature fileBrowserFilesFeature; FileBrowserFilesFeature fileBrowserFilesFeature;
@@ -70,7 +74,6 @@ namespace pipedal
int numberOfOutputs = 0; int numberOfOutputs = 0;
int numberOfMidiInputs = 0; int numberOfMidiInputs = 0;
std::unique_ptr<Worker> worker;
LilvInstance *pInstance; LilvInstance *pInstance;
std::shared_ptr<Lv2PluginInfo> info; std::shared_ptr<Lv2PluginInfo> info;
std::vector<float> controlValues; std::vector<float> controlValues;
+8 -5
View File
@@ -47,6 +47,7 @@
#include "StdErrorCapture.hpp" #include "StdErrorCapture.hpp"
#include "util.hpp" #include "util.hpp"
#include "ModFileTypes.hpp" #include "ModFileTypes.hpp"
#include <algorithm>
#include "Locale.hpp" #include "Locale.hpp"
@@ -301,7 +302,6 @@ PluginHost::PluginHost()
this->urids = new Urids(mapFeature); this->urids = new Urids(mapFeature);
pHostWorkerThread = std::make_shared<HostWorkerThread>();
} }
void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings) void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
@@ -1668,10 +1668,6 @@ bool Lv2PluginInfo::isSplit() const
{ {
return uri_ == SPLIT_PEDALBOARD_ITEM_URI; return uri_ == SPLIT_PEDALBOARD_ITEM_URI;
} }
std::shared_ptr<HostWorkerThread> PluginHost::GetHostWorkerThread()
{
return pHostWorkerThread;
}
class ResourceInfo class ResourceInfo
{ {
@@ -1883,6 +1879,13 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
} }
} }
// ffs.
static inline bool contains(const std::vector<std::string> &vec, const std::string &value) {
return std::find(vec.begin(),vec.end(),value) != vec.end();
}
bool Lv2PluginInfo::WantsWorkerThread() const {
return contains(this->required_features_,LV2_WORKER__schedule) || contains(this->supported_features_,LV2_WORKER__schedule);
}
// void PiPedalHostLogError(const std::string &error) // void PiPedalHostLogError(const std::string &error)
// { // {
// Lv2Log::error("%s",error.c_str()); // Lv2Log::error("%s",error.c_str());
+2 -2
View File
@@ -454,6 +454,8 @@ namespace pipedal
LV2_PROPERTY_GETSET(patchProperties) LV2_PROPERTY_GETSET(patchProperties)
LV2_PROPERTY_GETSET(hasDefaultState) LV2_PROPERTY_GETSET(hasDefaultState)
bool WantsWorkerThread() const;
const Lv2PortInfo &getPort(const std::string &symbol) const Lv2PortInfo &getPort(const std::string &symbol)
{ {
for (size_t i = 0; i < ports_.size(); ++i) for (size_t i = 0; i < ports_.size(); ++i)
@@ -913,7 +915,6 @@ namespace pipedal
} }
private: private:
std::shared_ptr<HostWorkerThread> pHostWorkerThread;
// IHost implementation. // IHost implementation.
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; } virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; } virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
@@ -922,7 +923,6 @@ namespace pipedal
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; } virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; } virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); } virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); }
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread();
public: public:
virtual MapFeature &GetMapFeature() override { return this->mapFeature; } virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
+24 -8
View File
@@ -15,7 +15,7 @@
*/ */
// (Borrows heavily from worker.c by David Robillard.) // (Borrows heavily from worker.c by David Robillard.)
// Copyright (c) 2022 Robin Davies // Copyright (c) 2024 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
@@ -242,8 +242,19 @@ void HostWorkerThread::ThreadProc() noexcept
HostWorkerThread::HostWorkerThread() HostWorkerThread::HostWorkerThread()
{ {
this->dataBuffer.resize(16*1024); this->dataBuffer.resize(16*1024);
pThread = std::make_unique<std::thread>([this]() }
{ this->ThreadProc(); });
bool HostWorkerThread::StartThread()
{
if (!pThread)
{
if (closed) {
return false;
}
pThread = std::make_unique<std::thread>([this]()
{ this->ThreadProc(); });
}
return true;
} }
void HostWorkerThread::Close() void HostWorkerThread::Close()
@@ -252,10 +263,12 @@ void HostWorkerThread::Close()
bool sendClose = false; bool sendClose = false;
{ {
std::lock_guard lock{submitMutex}; std::lock_guard lock{submitMutex};
if (!closed) if (pThread) {
{ if (!closed)
closed = true; {
sendClose = true; closed = true;
sendClose = true;
}
} }
} }
if (sendClose) if (sendClose)
@@ -278,7 +291,10 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
{ {
std::lock_guard lock(submitMutex); std::lock_guard lock(submitMutex);
if (exiting) if (!StartThread()) { // ensures thread is running, when plugins haven't declared they want a worker thread.
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
}
if (exiting )
{ {
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE; return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
} }
+8 -6
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies // Copyright (c) 2025 Robin Davies
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy of // Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in // this software and associated documentation files (the "Software"), to deal in
@@ -32,6 +32,7 @@
#include "lv2/atom/atom.h" #include "lv2/atom/atom.h"
#include "lv2/worker/worker.h" #include "lv2/worker/worker.h"
#include "condition_variable" #include "condition_variable"
#include <atomic>
#include <map> #include <map>
#include <string> #include <string>
@@ -51,10 +52,12 @@ namespace pipedal {
HostWorkerThread(); HostWorkerThread();
~HostWorkerThread(); ~HostWorkerThread();
bool StartThread();
void Close(); void Close();
bool Closed() const { return closed || pThread == nullptr; }
LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data); LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data);
private: private:
bool closed = false; std::atomic<bool> closed = false;
std::unique_ptr<std::thread> pThread; std::unique_ptr<std::thread> pThread;
void ThreadProc() noexcept; void ThreadProc() noexcept;
@@ -64,8 +67,6 @@ namespace pipedal {
std::vector<uint8_t> dataBuffer; std::vector<uint8_t> dataBuffer;
}; };
class Worker { class Worker {
@@ -75,8 +76,8 @@ namespace pipedal {
LilvInstance*lilvInstance; LilvInstance*lilvInstance;
const LV2_Worker_Interface*workerInterface; const LV2_Worker_Interface*workerInterface;
bool closed = false; std::atomic<bool> closed = false;
bool exiting = false; std::atomic<bool> exiting = false;
RingBuffer<true,false> responseRingBuffer; RingBuffer<true,false> responseRingBuffer;
std::vector<uint8_t> responseBuffer; std::vector<uint8_t> responseBuffer;
@@ -94,6 +95,7 @@ namespace pipedal {
public: public:
Worker(const std::shared_ptr<HostWorkerThread>& pHostWorker,LilvInstance *instance, const LV2_Worker_Interface *iface); Worker(const std::shared_ptr<HostWorkerThread>& pHostWorker,LilvInstance *instance, const LV2_Worker_Interface *iface);
~Worker(); ~Worker();
void Close(); void Close();
LV2_Worker_Status ScheduleWork( LV2_Worker_Status ScheduleWork(