Merge branch 'master' into refactor-samplebuffer
This commit is contained in:
@@ -197,6 +197,7 @@ public:
|
||||
|
||||
// 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
|
||||
|
||||
93
include/LmmsSemaphore.h
Normal file
93
include/LmmsSemaphore.h
Normal file
@@ -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
|
||||
@@ -51,13 +51,14 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifdef LMMS_HAVE_LV2
|
||||
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "Lv2Manager.h"
|
||||
|
||||
@@ -78,7 +79,7 @@ private:
|
||||
//! 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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string_view>
|
||||
#include <lilv/lilv.h>
|
||||
|
||||
#include "Lv2Basics.h"
|
||||
@@ -120,15 +121,9 @@ public:
|
||||
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;
|
||||
}
|
||||
@@ -136,17 +131,21 @@ public:
|
||||
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;
|
||||
@@ -155,7 +154,8 @@ private:
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -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"
|
||||
@@ -174,8 +177,14 @@ private:
|
||||
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
|
||||
|
||||
// full list of ports
|
||||
std::vector<std::unique_ptr<Lv2Ports::PortBase>> m_ports;
|
||||
// quick reference to specific, unique ports
|
||||
|
||||
@@ -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:
|
||||
|
||||
93
include/Lv2Worker.h
Normal file
93
include/Lv2Worker.h
Normal file
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -179,6 +180,7 @@ private:
|
||||
int m_bufferSize;
|
||||
QSlider * m_bufferSizeSlider;
|
||||
QLabel * m_bufferSizeLbl;
|
||||
QLabel * m_bufferSizeWarnLbl;
|
||||
|
||||
// MIDI settings widgets.
|
||||
QComboBox * m_midiInterfaces;
|
||||
|
||||
@@ -325,6 +325,32 @@ static inline T absMin( T a, T b )
|
||||
return std::abs(a) < std::abs(b) ? a : b;
|
||||
}
|
||||
|
||||
// @brief Calculate number of digits which LcdSpinBox would show for a given number
|
||||
// @note Once we upgrade to C++20, we could probably use std::formatted_size
|
||||
static inline int numDigitsAsInt(float f)
|
||||
{
|
||||
// use rounding:
|
||||
// LcdSpinBox sometimes uses roundf(), sometimes cast rounding
|
||||
// we use rounding to be on the "safe side"
|
||||
const float rounded = roundf(f);
|
||||
int asInt = static_cast<int>(rounded);
|
||||
int digits = 1; // always at least 1
|
||||
if(asInt < 0)
|
||||
{
|
||||
++digits;
|
||||
asInt = -asInt;
|
||||
}
|
||||
// "asInt" is positive from now
|
||||
int32_t power = 1;
|
||||
for(int32_t i = 1; i<10; ++i)
|
||||
{
|
||||
power *= 10;
|
||||
if(static_cast<int32_t>(asInt) >= power) { ++digits; } // 2 digits for >=10, 3 for >=100
|
||||
else { break; }
|
||||
}
|
||||
return digits;
|
||||
}
|
||||
|
||||
|
||||
} // namespace lmms
|
||||
|
||||
|
||||
Reference in New Issue
Block a user