commit
f489b88df0
10 changed files with 384 additions and 92 deletions
|
|
@ -30,4 +30,4 @@ clang-tidy \
|
||||||
vst/SfizzVstState.cpp \
|
vst/SfizzVstState.cpp \
|
||||||
-- -Iexternal/abseil-cpp -Isrc/external -Isrc/external/pugixml/src \
|
-- -Iexternal/abseil-cpp -Isrc/external -Isrc/external/pugixml/src \
|
||||||
-Isrc/sfizz -Isrc -Isrc/external/spline \
|
-Isrc/sfizz -Isrc -Isrc/external/spline \
|
||||||
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -DNDEBUG
|
-Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer -DNDEBUG
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,10 @@ set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES
|
||||||
plugin_add_vst3sdk(${VSTPLUGIN_PRJ_NAME})
|
plugin_add_vst3sdk(${VSTPLUGIN_PRJ_NAME})
|
||||||
plugin_add_vstgui(${VSTPLUGIN_PRJ_NAME})
|
plugin_add_vstgui(${VSTPLUGIN_PRJ_NAME})
|
||||||
|
|
||||||
|
# Add the ring buffer
|
||||||
|
target_include_directories(${VSTPLUGIN_PRJ_NAME} PRIVATE "external/ring_buffer")
|
||||||
|
target_sources(${VSTPLUGIN_PRJ_NAME} PRIVATE "external/ring_buffer/ring_buffer/ring_buffer.cpp")
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
target_link_libraries(${VSTPLUGIN_PRJ_NAME} PRIVATE
|
target_link_libraries(${VSTPLUGIN_PRJ_NAME} PRIVATE
|
||||||
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/vst3.version")
|
"-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/vst3.version")
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ void SfizzVstEditor::loadSfzFile(const std::string& filePath)
|
||||||
|
|
||||||
msg->setMessageID("LoadSfz");
|
msg->setMessageID("LoadSfz");
|
||||||
Vst::IAttributeList* attr = msg->getAttributes();
|
Vst::IAttributeList* attr = msg->getAttributes();
|
||||||
attr->setString("File", Steinberg::String(filePath.c_str()).text());
|
attr->setBinary("File", filePath.data(), filePath.size());
|
||||||
ctl->sendMessage(msg);
|
ctl->sendMessage(msg);
|
||||||
msg->release();
|
msg->release();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ constexpr int fastRound(T x)
|
||||||
}
|
}
|
||||||
|
|
||||||
SfizzVstProcessor::SfizzVstProcessor()
|
SfizzVstProcessor::SfizzVstProcessor()
|
||||||
: _fifoToWorker(1024)
|
: _fifoToWorker(64 * 1024)
|
||||||
{
|
{
|
||||||
setControllerClass(SfizzVstController::cid);
|
setControllerClass(SfizzVstController::cid);
|
||||||
}
|
}
|
||||||
|
|
@ -119,14 +119,6 @@ tresult PLUGIN_API SfizzVstProcessor::setActive(TBool state)
|
||||||
|
|
||||||
_fileChangePeriod = static_cast<uint32>(processSetup.sampleRate);
|
_fileChangePeriod = static_cast<uint32>(processSetup.sampleRate);
|
||||||
|
|
||||||
if (!_msgCheckShouldReload) {
|
|
||||||
auto msg = IPtr<Vst::IMessage>::adopt(allocateMessage());
|
|
||||||
if (!msg)
|
|
||||||
return kResultFalse;
|
|
||||||
msg->setMessageID("CheckShouldReload");
|
|
||||||
_msgCheckShouldReload = msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
_workRunning = true;
|
_workRunning = true;
|
||||||
_worker = std::thread([this]() { doBackgroundWork(); });
|
_worker = std::thread([this]() { doBackgroundWork(); });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -183,11 +175,8 @@ tresult PLUGIN_API SfizzVstProcessor::process(Vst::ProcessData& data)
|
||||||
_fileChangeCounter += numFrames;
|
_fileChangeCounter += numFrames;
|
||||||
if (_fileChangeCounter > _fileChangePeriod) {
|
if (_fileChangeCounter > _fileChangePeriod) {
|
||||||
_fileChangeCounter %= _fileChangePeriod;
|
_fileChangeCounter %= _fileChangePeriod;
|
||||||
Vst::IMessage* msg = _msgCheckShouldReload.get();
|
if (writeWorkerMessage("CheckShouldReload", nullptr, 0))
|
||||||
if (_fifoToWorker.push(msg)) {
|
|
||||||
msg->addRef();
|
|
||||||
_semaToWorker.post();
|
_semaToWorker.post();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return kResultTrue;
|
return kResultTrue;
|
||||||
|
|
@ -214,47 +203,26 @@ void SfizzVstProcessor::processParameterChanges(Vst::IParameterChanges& pc)
|
||||||
break;
|
break;
|
||||||
case kPidNumVoices:
|
case kPidNumVoices:
|
||||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||||
Vst::IMessage* msg = allocateMessage();
|
int32 data = static_cast<int32>(kParamNumVoicesRange.denormalize(value));
|
||||||
if (!msg)
|
_state.numVoices = data;
|
||||||
break;
|
if (writeWorkerMessage("SetNumVoices", &data, sizeof(data)))
|
||||||
msg->setMessageID("SetNumVoices");
|
_semaToWorker.post();
|
||||||
Vst::IAttributeList* attr = msg->getAttributes();
|
|
||||||
attr->setInt("NumVoices", static_cast<Steinberg::int64>(kParamNumVoicesRange.denormalize(value)));
|
|
||||||
if (!_fifoToWorker.push(msg)) {
|
|
||||||
msg->release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_semaToWorker.post();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kPidOversampling:
|
case kPidOversampling:
|
||||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||||
Vst::IMessage* msg = allocateMessage();
|
int32 data = static_cast<int32>(kParamOversamplingRange.denormalize(value));
|
||||||
if (!msg)
|
_state.oversamplingLog2 = data;
|
||||||
break;
|
if (writeWorkerMessage("SetOversampling", &data, sizeof(data)))
|
||||||
msg->setMessageID("SetOversampling");
|
_semaToWorker.post();
|
||||||
Vst::IAttributeList* attr = msg->getAttributes();
|
|
||||||
attr->setInt("Oversampling", static_cast<Steinberg::int64>(kParamOversamplingRange.denormalize(value)));
|
|
||||||
if (!_fifoToWorker.push(msg)) {
|
|
||||||
msg->release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_semaToWorker.post();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case kPidPreloadSize:
|
case kPidPreloadSize:
|
||||||
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
if (pointCount > 0 && vq->getPoint(pointCount - 1, sampleOffset, value) == kResultTrue) {
|
||||||
Vst::IMessage* msg = allocateMessage();
|
int32 data = static_cast<int32>(kParamPreloadSizeRange.denormalize(value));
|
||||||
if (!msg)
|
_state.preloadSize = data;
|
||||||
break;
|
if (writeWorkerMessage("SetPreloadSize", &data, sizeof(data)))
|
||||||
msg->setMessageID("SetPreloadSize");
|
_semaToWorker.post();
|
||||||
Vst::IAttributeList* attr = msg->getAttributes();
|
|
||||||
attr->setInt("PreloadSize", static_cast<Steinberg::int64>(kParamPreloadSizeRange.denormalize(value)));
|
|
||||||
if (!_fifoToWorker.push(msg)) {
|
|
||||||
msg->release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_semaToWorker.post();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -335,17 +303,29 @@ int SfizzVstProcessor::convertVelocityFromFloat(float x)
|
||||||
|
|
||||||
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
tresult PLUGIN_API SfizzVstProcessor::notify(Vst::IMessage* message)
|
||||||
{
|
{
|
||||||
|
// Note(jpc) this notification is not necessarily handled by the RT thread
|
||||||
|
|
||||||
tresult result = AudioEffect::notify(message);
|
tresult result = AudioEffect::notify(message);
|
||||||
if (result != kResultFalse)
|
if (result != kResultFalse)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
if (!_fifoToWorker.push(message))
|
const char* id = message->getMessageID();
|
||||||
return kOutOfMemory;
|
Vst::IAttributeList* attr = message->getAttributes();
|
||||||
|
|
||||||
message->addRef();
|
if (!std::strcmp(id, "LoadSfz")) {
|
||||||
_semaToWorker.post();
|
const void* data = nullptr;
|
||||||
|
uint32 size = 0;
|
||||||
|
result = attr->getBinary("File", data, size);
|
||||||
|
|
||||||
return kResultTrue;
|
if (result != kResultTrue)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(_processMutex);
|
||||||
|
_state.sfzFile.assign(static_cast<const char *>(data), size);
|
||||||
|
_synth->loadSfzFile(_state.sfzFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
FUnknown* SfizzVstProcessor::createInstance(void*)
|
FUnknown* SfizzVstProcessor::createInstance(void*)
|
||||||
|
|
@ -355,61 +335,38 @@ FUnknown* SfizzVstProcessor::createInstance(void*)
|
||||||
|
|
||||||
void SfizzVstProcessor::doBackgroundWork()
|
void SfizzVstProcessor::doBackgroundWork()
|
||||||
{
|
{
|
||||||
constexpr uint32 maxPathLen = 32768;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
_semaToWorker.wait();
|
_semaToWorker.wait();
|
||||||
|
|
||||||
if (!_workRunning)
|
if (!_workRunning)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Vst::IMessage* msg;
|
RTMessagePtr msg = readWorkerMessage();
|
||||||
if (!_fifoToWorker.pop(msg)) {
|
if (!msg) {
|
||||||
fprintf(stderr, "[Sfizz] message synchronization error in worker\n");
|
fprintf(stderr, "[Sfizz] message synchronization error in worker\n");
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* id = msg->getMessageID();
|
const char* id = msg->type;
|
||||||
Vst::IAttributeList* attr = msg->getAttributes();
|
|
||||||
|
|
||||||
if (!std::strcmp(id, "LoadSfz")) {
|
if (!std::strcmp(id, "SetNumVoices")) {
|
||||||
std::vector<Vst::TChar> path(maxPathLen + 1);
|
int32 value = *msg->payload<int32>();
|
||||||
if (attr->getString("File", path.data(), maxPathLen) == kResultTrue) {
|
_synth->setNumVoices(value);
|
||||||
std::lock_guard<std::mutex> lock(_processMutex);
|
|
||||||
_state.sfzFile = Steinberg::String(path.data()).text8();
|
|
||||||
_synth->loadSfzFile(_state.sfzFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!std::strcmp(id, "SetNumVoices")) {
|
|
||||||
int64 value;
|
|
||||||
if (attr->getInt("NumVoices", value) == kResultTrue) {
|
|
||||||
_state.numVoices = value;
|
|
||||||
_synth->setNumVoices(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!std::strcmp(id, "SetOversampling")) {
|
else if (!std::strcmp(id, "SetOversampling")) {
|
||||||
int64 value;
|
int32 value = *msg->payload<int32>();
|
||||||
if (attr->getInt("Oversampling", value) == kResultTrue) {
|
_synth->setOversamplingFactor(1 << value);
|
||||||
_state.oversamplingLog2 = value;
|
|
||||||
_synth->setOversamplingFactor(1 << value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!std::strcmp(id, "SetPreloadSize")) {
|
else if (!std::strcmp(id, "SetPreloadSize")) {
|
||||||
int64 value;
|
int32 value = *msg->payload<int32>();
|
||||||
if (attr->getInt("PreloadSize", value) == kResultTrue) {
|
_synth->setPreloadSize(value);
|
||||||
_state.preloadSize = value;
|
|
||||||
_synth->setPreloadSize(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!std::strcmp(id, "CheckShouldReload")) {
|
else if (!std::strcmp(id, "CheckShouldReload")) {
|
||||||
if (_synth->shouldReloadFile()) {
|
if (_synth->shouldReloadFile()) {
|
||||||
fprintf(stderr, "[Sfizz] file has changed, reloading\n");
|
fprintf(stderr, "[Sfizz] file has changed, reloading\n");
|
||||||
std::lock_guard<std::mutex> lock(_processMutex);
|
|
||||||
_synth->loadSfzFile(_state.sfzFile);
|
_synth->loadSfzFile(_state.sfzFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg->release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,15 +380,61 @@ void SfizzVstProcessor::stopBackgroundWork()
|
||||||
_worker.join();
|
_worker.join();
|
||||||
|
|
||||||
while (_semaToWorker.try_wait()) {
|
while (_semaToWorker.try_wait()) {
|
||||||
Vst::IMessage* msg;
|
if (!discardWorkerMessage()) {
|
||||||
if (!_fifoToWorker.pop(msg)) {
|
|
||||||
fprintf(stderr, "[Sfizz] message synchronization error in processor\n");
|
fprintf(stderr, "[Sfizz] message synchronization error in processor\n");
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
msg->release();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SfizzVstProcessor::writeWorkerMessage(const char* type, const void* data, uintptr_t size)
|
||||||
|
{
|
||||||
|
RTMessage header;
|
||||||
|
header.type = type;
|
||||||
|
header.size = size;
|
||||||
|
|
||||||
|
if (_fifoToWorker.size_free() < sizeof(header) + size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_fifoToWorker.put(header);
|
||||||
|
_fifoToWorker.put(static_cast<const uint8*>(data), size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SfizzVstProcessor::RTMessagePtr SfizzVstProcessor::readWorkerMessage()
|
||||||
|
{
|
||||||
|
RTMessage header;
|
||||||
|
|
||||||
|
if (!_fifoToWorker.peek(header))
|
||||||
|
return nullptr;
|
||||||
|
if (_fifoToWorker.size_used() < sizeof(header) + header.size)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
RTMessagePtr msg { reinterpret_cast<RTMessage*>(std::malloc(sizeof(header) + header.size)) };
|
||||||
|
if (!msg)
|
||||||
|
throw std::bad_alloc();
|
||||||
|
|
||||||
|
msg->type = header.type;
|
||||||
|
msg->size = header.size;
|
||||||
|
_fifoToWorker.discard(sizeof(header));
|
||||||
|
_fifoToWorker.get(const_cast<char*>(msg->payload<char>()), header.size);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SfizzVstProcessor::discardWorkerMessage()
|
||||||
|
{
|
||||||
|
RTMessage header;
|
||||||
|
|
||||||
|
if (!_fifoToWorker.peek(header))
|
||||||
|
return false;
|
||||||
|
if (_fifoToWorker.size_used() < sizeof(header) + header.size)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_fifoToWorker.discard(sizeof(header) + header.size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Note(jpc) Generated at random with uuidgen.
|
Note(jpc) Generated at random with uuidgen.
|
||||||
Can't find docs on it... maybe it's to register somewhere?
|
Can't find docs on it... maybe it's to register somewhere?
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "SfizzVstState.h"
|
#include "SfizzVstState.h"
|
||||||
#include "RTSemaphore.h"
|
#include "RTSemaphore.h"
|
||||||
|
#include "ring_buffer/ring_buffer.h"
|
||||||
#include "public.sdk/source/vst/vstaudioeffect.h"
|
#include "public.sdk/source/vst/vstaudioeffect.h"
|
||||||
#include "public.sdk/source/vst/utility/ringbuffer.h"
|
|
||||||
#include <sfizz.hpp>
|
#include <sfizz.hpp>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
using namespace Steinberg;
|
using namespace Steinberg;
|
||||||
|
|
||||||
|
|
@ -51,16 +52,40 @@ private:
|
||||||
// worker and thread sync
|
// worker and thread sync
|
||||||
std::thread _worker;
|
std::thread _worker;
|
||||||
volatile bool _workRunning = false;
|
volatile bool _workRunning = false;
|
||||||
Steinberg::OneReaderOneWriter::RingBuffer<Vst::IMessage*> _fifoToWorker;
|
Ring_Buffer _fifoToWorker;
|
||||||
RTSemaphore _semaToWorker;
|
RTSemaphore _semaToWorker;
|
||||||
std::mutex _processMutex;
|
std::mutex _processMutex;
|
||||||
|
|
||||||
// file modification periodic checker
|
// file modification periodic checker
|
||||||
uint32 _fileChangeCounter = 0;
|
uint32 _fileChangeCounter = 0;
|
||||||
uint32 _fileChangePeriod = 0;
|
uint32 _fileChangePeriod = 0;
|
||||||
IPtr<Vst::IMessage> _msgCheckShouldReload;
|
|
||||||
|
// messaging
|
||||||
|
struct RTMessage {
|
||||||
|
const char* type;
|
||||||
|
uintptr_t size;
|
||||||
|
// 32-bit aligned data after header
|
||||||
|
template <class T> const T* payload() const;
|
||||||
|
};
|
||||||
|
struct RTMessageDelete {
|
||||||
|
void operator()(RTMessage* x) const noexcept { std::free(x); }
|
||||||
|
};
|
||||||
|
typedef std::unique_ptr<RTMessage, RTMessageDelete> RTMessagePtr;
|
||||||
|
|
||||||
// worker
|
// worker
|
||||||
void doBackgroundWork();
|
void doBackgroundWork();
|
||||||
void stopBackgroundWork();
|
void stopBackgroundWork();
|
||||||
|
// writer
|
||||||
|
bool writeWorkerMessage(const char* type, const void* data, uintptr_t size);
|
||||||
|
// reader
|
||||||
|
RTMessagePtr readWorkerMessage();
|
||||||
|
bool discardWorkerMessage();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class T> const T* SfizzVstProcessor::RTMessage::payload() const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const T*>(
|
||||||
|
reinterpret_cast<const uint8*>(this) + sizeof(*this));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ enum {
|
||||||
|
|
||||||
class SfizzVstState {
|
class SfizzVstState {
|
||||||
public:
|
public:
|
||||||
|
SfizzVstState() { sfzFile.reserve(8192); }
|
||||||
|
|
||||||
std::string sfzFile;
|
std::string sfzFile;
|
||||||
float volume = 0;
|
float volume = 0;
|
||||||
int32 numVoices = 64;
|
int32 numVoices = 64;
|
||||||
|
|
|
||||||
23
vst/external/ring_buffer/LICENSE
vendored
Normal file
23
vst/external/ring_buffer/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
105
vst/external/ring_buffer/ring_buffer/ring_buffer.cpp
vendored
Normal file
105
vst/external/ring_buffer/ring_buffer/ring_buffer.cpp
vendored
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright Jean Pierre Cimalando 2018.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#include "ring_buffer.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#if defined(__cpp_if_constexpr)
|
||||||
|
# define if_constexpr if constexpr
|
||||||
|
#else
|
||||||
|
# define if_constexpr if
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
Ring_Buffer_Ex<Atomic>::Ring_Buffer_Ex(size_t capacity)
|
||||||
|
: cap_(capacity + 1),
|
||||||
|
rbdata_(new uint8_t[capacity + 1])
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
Ring_Buffer_Ex<Atomic>::~Ring_Buffer_Ex()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
size_t Ring_Buffer_Ex<Atomic>::size_used() const
|
||||||
|
{
|
||||||
|
const size_t rp = rp_, wp = wp_, cap = cap_;
|
||||||
|
return wp + ((wp < rp) ? cap : 0) - rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
bool Ring_Buffer_Ex<Atomic>::discard(size_t len)
|
||||||
|
{
|
||||||
|
return getbytes_ex_(nullptr, len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
size_t Ring_Buffer_Ex<Atomic>::size_free() const
|
||||||
|
{
|
||||||
|
const size_t rp = rp_, wp = wp_, cap = cap_;
|
||||||
|
return rp + ((rp <= wp) ? cap : 0) - wp - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
bool Ring_Buffer_Ex<Atomic>::getbytes_(void *data, size_t len)
|
||||||
|
{
|
||||||
|
return getbytes_ex_(data, len, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
bool Ring_Buffer_Ex<Atomic>::peekbytes_(void *data, size_t len) const
|
||||||
|
{
|
||||||
|
auto *ncthis = const_cast<Ring_Buffer_Ex<Atomic> *>(this);
|
||||||
|
return ncthis->getbytes_ex_(data, len, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
bool Ring_Buffer_Ex<Atomic>::getbytes_ex_(void *data, size_t len, bool advp)
|
||||||
|
{
|
||||||
|
if (size_used() < len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const size_t rp = rp_, cap = cap_;
|
||||||
|
const uint8_t *src = rbdata_.get();
|
||||||
|
uint8_t *dst = (uint8_t *)data;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
const size_t taillen = std::min(len, cap - rp);
|
||||||
|
if_constexpr (Atomic)
|
||||||
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
|
std::copy_n(&src[rp], taillen, dst);
|
||||||
|
std::copy_n(src, len - taillen, dst + taillen);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (advp)
|
||||||
|
rp_ = (rp + len < cap) ? (rp + len) : (rp + len - cap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
bool Ring_Buffer_Ex<Atomic>::putbytes_(const void *data, size_t len)
|
||||||
|
{
|
||||||
|
if (size_free() < len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const size_t wp = wp_, cap = cap_;
|
||||||
|
const uint8_t *src = (const uint8_t *)data;
|
||||||
|
uint8_t *dst = rbdata_.get();
|
||||||
|
|
||||||
|
const size_t taillen = std::min(len, cap - wp);
|
||||||
|
std::copy_n(src, taillen, &dst[wp]);
|
||||||
|
std::copy_n(src + taillen, len - taillen, dst);
|
||||||
|
if_constexpr (Atomic)
|
||||||
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
|
|
||||||
|
wp_ = (wp + len < cap) ? (wp + len) : (wp + len - cap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template class Ring_Buffer_Ex<true>;
|
||||||
|
template class Ring_Buffer_Ex<false>;
|
||||||
69
vst/external/ring_buffer/ring_buffer/ring_buffer.h
vendored
Normal file
69
vst/external/ring_buffer/ring_buffer/ring_buffer.h
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright Jean Pierre Cimalando 2018.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_atomic_is_always_lock_free)
|
||||||
|
static_assert(
|
||||||
|
std::atomic<size_t>::is_always_lock_free, "atomic<size_t> must be lock free");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <bool> class Ring_Buffer_Ex;
|
||||||
|
typedef Ring_Buffer_Ex<true> Ring_Buffer;
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <class RB>
|
||||||
|
class Basic_Ring_Buffer {
|
||||||
|
public:
|
||||||
|
// read operations
|
||||||
|
template <class T> bool get(T &x);
|
||||||
|
template <class T> bool get(T *x, size_t n);
|
||||||
|
template <class T> bool peek(T &x);
|
||||||
|
template <class T> bool peek(T *x, size_t n);
|
||||||
|
// write operations
|
||||||
|
template <class T> bool put(const T &x);
|
||||||
|
template <class T> bool put(const T *x, size_t n);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <bool Atomic>
|
||||||
|
class Ring_Buffer_Ex final :
|
||||||
|
private Basic_Ring_Buffer<Ring_Buffer_Ex<Atomic>> {
|
||||||
|
private:
|
||||||
|
typedef Basic_Ring_Buffer<Ring_Buffer_Ex<Atomic>> Base;
|
||||||
|
public:
|
||||||
|
// initialization and cleanup
|
||||||
|
explicit Ring_Buffer_Ex(size_t capacity);
|
||||||
|
~Ring_Buffer_Ex();
|
||||||
|
// attributes
|
||||||
|
size_t capacity() const;
|
||||||
|
// read operations
|
||||||
|
size_t size_used() const;
|
||||||
|
bool discard(size_t len);
|
||||||
|
using Base::get;
|
||||||
|
using Base::peek;
|
||||||
|
// write operations
|
||||||
|
size_t size_free() const;
|
||||||
|
using Base::put;
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t cap_{0};
|
||||||
|
typename std::conditional<Atomic, std::atomic<size_t>, size_t>::type rp_{0}, wp_{0};
|
||||||
|
std::unique_ptr<uint8_t[]> rbdata_ {};
|
||||||
|
friend Base;
|
||||||
|
bool getbytes_(void *data, size_t len);
|
||||||
|
bool peekbytes_(void *data, size_t len) const;
|
||||||
|
bool getbytes_ex_(void *data, size_t len, bool advp);
|
||||||
|
bool putbytes_(const void *data, size_t len);
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
#include "ring_buffer.tcc"
|
||||||
61
vst/external/ring_buffer/ring_buffer/ring_buffer.tcc
vendored
Normal file
61
vst/external/ring_buffer/ring_buffer/ring_buffer.tcc
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright Jean Pierre Cimalando 2018.
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE or copy at
|
||||||
|
// http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
#include "ring_buffer.h"
|
||||||
|
|
||||||
|
template <bool Atomic>
|
||||||
|
inline size_t Ring_Buffer_Ex<Atomic>::capacity() const
|
||||||
|
{
|
||||||
|
return cap_ - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::get(T &x)
|
||||||
|
{
|
||||||
|
return get(&x, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::get(T *x, size_t n)
|
||||||
|
{
|
||||||
|
// static_assert(std::is_trivially_copyable<T>::value, "ring_buffer: T must be trivially copyable");
|
||||||
|
RB *self = static_cast<RB *>(this);
|
||||||
|
return self->getbytes_(x, n * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::peek(T &x)
|
||||||
|
{
|
||||||
|
return peek(&x, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::peek(T *x, size_t n)
|
||||||
|
{
|
||||||
|
// static_assert(std::is_trivially_copyable<T>::value, "ring_buffer: T must be trivially copyable");
|
||||||
|
RB *self = static_cast<RB *>(this);
|
||||||
|
return self->peekbytes_(x, n * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::put(const T &x)
|
||||||
|
{
|
||||||
|
return put(&x, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class RB>
|
||||||
|
template <class T>
|
||||||
|
inline bool Basic_Ring_Buffer<RB>::put(const T *x, size_t n)
|
||||||
|
{
|
||||||
|
// static_assert(std::is_trivially_copyable<T>::value, "ring_buffer: T must be trivially copyable");
|
||||||
|
RB *self = static_cast<RB *>(this);
|
||||||
|
return self->putbytes_(x, n * sizeof(T));
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue