Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Lv2 Worker #6484

Merged
merged 8 commits into from
Sep 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class LMMS_EXPORT AudioEngine : public QObject

// audio-device-stuff

bool renderOnly() const { return m_renderOnly; }
// Returns the current audio device's name. This is not necessarily
// the user's preferred audio device, in case you were thinking that.
inline const QString & audioDevName() const
Expand Down
93 changes: 93 additions & 0 deletions include/LmmsSemaphore.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Semaphore.h - Semaphore declaration
*
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
*
* 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.
*
*/

/*
* This code has been copied and adapted from https://github.com/drobilla/jalv
* File src/zix/sem.h
*/

#ifndef LMMS_SEMAPHORE_H
#define LMMS_SEMAPHORE_H

#include "lmmsconfig.h"

#ifdef LMMS_BUILD_APPLE
# include <mach/mach.h>
#elif defined(LMMS_BUILD_WIN32)
# include <windows.h>
#else
# include <semaphore.h>
#endif

#include <system_error>

namespace lmms {

/**
A counting semaphore.

This is an integer that is always positive, and has two main operations:
increment (post) and decrement (wait). If a decrement can not be performed
(i.e. the value is 0) the caller will be blocked until another thread posts
and the operation can succeed.

Semaphores can be created with any starting value, but typically this will
be 0 so the semaphore can be used as a simple signal where each post
corresponds to one wait.

Semaphores are very efficient (much moreso than a mutex/cond pair). In
particular, at least on Linux, post is async-signal-safe, which means it
does not block and will not be interrupted. If you need to signal from
a realtime thread, this is the most appropriate primitive to use.

@note Likely outdated with C++20's std::counting_semaphore
(though we have to check that this will be RT conforming on all platforms)
*/
class Semaphore
{
public:
Semaphore(unsigned initial);
Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
Semaphore(Semaphore&&) = delete;
Semaphore& operator=(Semaphore&&) = delete;
~Semaphore();

void post();
void wait();
bool tryWait();

private:
#ifdef LMMS_BUILD_APPLE
semaphore_t m_sem;
#elif defined(LMMS_BUILD_WIN32)
HANDLE m_sem;
#else
sem_t m_sem;
#endif
};

} // namespace lmms

#endif // LMMS_SEMAPHORE_H
3 changes: 2 additions & 1 deletion include/LocklessRingBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,14 @@ class LocklessRingBuffer
std::size_t capacity() const {return m_buffer.maximum_eventual_write_space();}
std::size_t free() const {return m_buffer.write_space();}
void wakeAll() {m_notifier.wakeAll();}
std::size_t write(const sampleFrame *src, std::size_t cnt, bool notify = false)
std::size_t write(const T *src, std::size_t cnt, bool notify = false)
{
std::size_t written = LocklessRingBuffer<T>::m_buffer.write(src, cnt);
// Let all waiting readers know new data are available.
if (notify) {LocklessRingBuffer<T>::m_notifier.wakeAll();}
return written;
}
void mlock() { m_buffer.mlock(); }

protected:
ringbuffer_t<T> m_buffer;
Expand Down
6 changes: 6 additions & 0 deletions include/Lv2Basics.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,14 @@ struct LilvNodesDeleter
void operator()(LilvNodes* n) { lilv_nodes_free(n); }
};

struct LilvScalePointsDeleter
{
void operator()(LilvScalePoints* s) { lilv_scale_points_free(s); }
};

using AutoLilvNode = std::unique_ptr<LilvNode, LilvNodeDeleter>;
using AutoLilvNodes = std::unique_ptr<LilvNodes, LilvNodesDeleter>;
using AutoLilvScalePoints = std::unique_ptr<LilvScalePoints, LilvScalePointsDeleter>;

/**
Return QString from a plugin's node, everything will be freed automatically
Expand Down
3 changes: 2 additions & 1 deletion include/Lv2Features.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#ifdef LMMS_HAVE_LV2

#include <map>
#include <string_view>
#include <vector>
#include "Lv2Manager.h"

Expand Down Expand Up @@ -78,7 +79,7 @@ class Lv2Features
//! pointers to m_features, required for lilv_plugin_instantiate
std::vector<const LV2_Feature*> m_featurePointers;
//! features + data, ordered by URI
std::map<const char*, void*, Lv2Manager::CmpStr> m_featureByUri;
std::map<std::string_view, void*> m_featureByUri;
};


Expand Down
20 changes: 10 additions & 10 deletions include/Lv2Manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include <map>
#include <set>
#include <string_view>
#include <lilv/lilv.h>

#include "Lv2Basics.h"
Expand Down Expand Up @@ -120,33 +121,31 @@ class Lv2Manager
Iterator begin() { return m_lv2InfoMap.begin(); }
Iterator end() { return m_lv2InfoMap.end(); }

//! strcmp based key comparator for std::set and std::map
struct CmpStr
{
bool operator()(char const *a, char const *b) const;
};

UridMap& uridMap() { return m_uridMap; }
const Lv2UridCache& uridCache() const { return m_uridCache; }
const std::set<const char*, CmpStr>& supportedFeatureURIs() const
const std::set<std::string_view>& supportedFeatureURIs() const
{
return m_supportedFeatureURIs;
}
bool isFeatureSupported(const char* featName) const;
AutoLilvNodes findNodes(const LilvNode *subject,
const LilvNode *predicate, const LilvNode *object);

static const std::set<const char*, Lv2Manager::CmpStr>& getPluginBlacklist()
static const std::set<std::string_view>& getPluginBlacklist()
{
return pluginBlacklist;
}
static const std::set<std::string_view>& getPluginBlacklistBuffersizeLessThan32()
{
return pluginBlacklistBuffersizeLessThan32;
}

private:
// general data
bool m_debug; //!< if set, debug output will be printed
LilvWorld* m_world;
Lv2InfoMap m_lv2InfoMap;
std::set<const char*, CmpStr> m_supportedFeatureURIs;
std::set<std::string_view> m_supportedFeatureURIs;

// feature data that are common for all Lv2Proc
UridMap m_uridMap;
Expand All @@ -155,7 +154,8 @@ class Lv2Manager
Lv2UridCache m_uridCache;

// static
static const std::set<const char*, Lv2Manager::CmpStr> pluginBlacklist;
static const std::set<std::string_view>
pluginBlacklist, pluginBlacklistBuffersizeLessThan32;

// functions
bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr);
Expand Down
13 changes: 11 additions & 2 deletions include/Lv2Proc.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* Lv2Proc.h - Lv2 processor class
*
* Copyright (c) 2019-2020 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
* Copyright (c) 2019-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
*
* This file is part of LMMS - https://lmms.io
*
Expand Down Expand Up @@ -31,11 +31,14 @@

#include <lilv/lilv.h>
#include <memory>
#include <optional>

#include "LinkedModelGroups.h"
#include "LmmsSemaphore.h"
#include "Lv2Basics.h"
#include "Lv2Features.h"
#include "Lv2Options.h"
#include "LinkedModelGroups.h"
#include "Lv2Worker.h"
#include "Plugin.h"
#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h"
#include "TimePos.h"
Expand Down Expand Up @@ -174,8 +177,14 @@ class Lv2Proc : public LinkedModelGroup
const LilvPlugin* m_plugin;
LilvInstance* m_instance;
Lv2Features m_features;

// options
Lv2Options m_options;

// worker
std::optional<Lv2Worker> m_worker;
Semaphore m_workLock; // this must be shared by different workers
JohannesLorenz marked this conversation as resolved.
Show resolved Hide resolved

// full list of ports
std::vector<std::unique_ptr<Lv2Ports::PortBase>> m_ports;
// quick reference to specific, unique ports
Expand Down
2 changes: 1 addition & 1 deletion include/Lv2ViewBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Lv2ViewProc : public LinkedModelGroupView
{
public:
//! @param colNum numbers of columns for the controls
Lv2ViewProc(QWidget *parent, Lv2Proc *ctrlBase, int colNum);
Lv2ViewProc(QWidget *parent, Lv2Proc *proc, int colNum);
~Lv2ViewProc() override = default;

private:
Expand Down
93 changes: 93 additions & 0 deletions include/Lv2Worker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Lv2Worker.h - Lv2Worker class
*
* Copyright (c) 2022-2022 Johannes Lorenz <jlsf2013$users.sourceforge.net, $=@>
*
* 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 LV2WORKER_H
#define LV2WORKER_H

#include "lmmsconfig.h"

#ifdef LMMS_HAVE_LV2

#include <lilv/lilv.h>
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
#include <thread>
#include <vector>

#include "LocklessRingBuffer.h"
#include "LmmsSemaphore.h"

namespace lmms
{

/**
Worker container
*/
class Lv2Worker
{
public:
// CTOR/DTOR/feature access
Lv2Worker(const LV2_Worker_Interface* iface, Semaphore* common_work_lock, bool threaded);
~Lv2Worker();
void setHandle(LV2_Handle handle) { m_handle = handle; }
LV2_Worker_Schedule* feature() { return &m_scheduleFeature; }

// public API
void emitResponses();
void notifyPluginThatRunFinished()
{
if(m_iface->end_run) { m_iface->end_run(m_scheduleFeature.handle); }
}

// to be called only by static functions
LV2_Worker_Status scheduleWork(uint32_t size, const void* data);
LV2_Worker_Status respond(uint32_t size, const void* data);

private:
// functions
void workerFunc();
std::size_t bufferSize() const; //!< size of internal buffers

// parameters
const LV2_Worker_Interface* m_iface;
bool m_threaded;
LV2_Handle m_handle;
LV2_Worker_Schedule m_scheduleFeature;

// threading/synchronization
std::thread m_thread;
std::vector<char> m_response; //!< buffer where single requests from m_requests are unpacked
LocklessRingBuffer<char> m_requests, m_responses; //!< ringbuffer to queue multiple requests
LocklessRingBufferReader<char> m_requestsReader, m_responsesReader;
std::atomic<bool> m_exit = false; //!< Whether the worker function should keep looping
Semaphore m_sem;
Semaphore* m_workLock;
};


} // namespace lmms

#endif // LMMS_HAVE_LV2

#endif // LV2WORKER_H

2 changes: 2 additions & 0 deletions include/SetupDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ private slots:
// Audio settings widget.
void audioInterfaceChanged(const QString & driver);
void toggleHQAudioDev(bool enabled);
void updateBufferSizeWarning(int value);
void setBufferSize(int value);
void resetBufferSize();

Expand Down Expand Up @@ -179,6 +180,7 @@ private slots:
int m_bufferSize;
QSlider * m_bufferSizeSlider;
QLabel * m_bufferSizeLbl;
QLabel * m_bufferSizeWarnLbl;

// MIDI settings widgets.
QComboBox * m_midiInterfaces;
Expand Down
Loading
Loading