Initial VST3 Support (disabled)

Disabled until Steinberg licensing compliance  can be sorted out.
This commit is contained in:
Robin Davies
2022-08-15 18:14:49 -04:00
parent 351d1542e1
commit 553ae2a3f7
56 changed files with 4840 additions and 637 deletions
+2 -1
View File
@@ -29,6 +29,7 @@
#include "AlsaDriver.hpp"
#include "JackServerSettings.hpp"
#include <thread>
#include "RtInversionGuard.hpp"
#include "CpuUse.hpp"
@@ -967,7 +968,7 @@ namespace pipedal
struct sched_param param;
memset(&param, 0, sizeof(param));
param.sched_priority = 79;
param.sched_priority = RT_THREAD_PRIORITY;
int result = sched_setscheduler(0, SCHED_RR, &param);
if (result == 0)
+279 -8
View File
@@ -7,6 +7,37 @@ set (USE_PCH 1)
include(FindPkgConfig)
#################################################################
# ENABLE/DISABLE VST3 Support.
# Disabled, pending Steinberg licensing agreement implementation
# and approval..
# Do not enable unless you have non-GPL3 access to the Steinberg
# SDK.
#################################################################
set (ENABLE_VST3 0)
if (ENABLE_VST3)
set (VST3PATH "../../vst3sdk")
message(STATUS " VST3 Support Enabled")
set (VST3_INCLUDES "${VST3PATH}")
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_definitions("DEVELOPMENT")
else()
add_compile_definitions("RELEASE")
endif()
add_compile_definitions("ENABLE_VST3" )
#add_compile_definitions("_GLIBCXX_USE_CXX11_ABI")
else()
message(STATUS " VST3 Support Disabled")
set (VST3_INCLUDES "")
endif()
########### VST 3 SUPPORT #############################################3
# Can't get the pkg_check to work.
@@ -60,19 +91,27 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set (USE_SANITIZE False)
if (!ENABLE_VST3)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-psabi")
else()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-psabi")
endif()
if (CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "Debug build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -O0 -DDEBUG -Werror" )
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG" )
if (USE_SANITIZE)
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fsanitize=address -static-libasan " )
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " )
endif()
elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "RelWithgDebInfo build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" )
#set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
else()
message(STATUS "Release build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" )
#set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
endif()
@@ -84,8 +123,19 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING)
if (VST3PATH)
set (VST3_SOURCES
vst3/Vst3Effect.hpp Vst3Effect.cpp
vst3/Vst3Host.hpp Vst3Host.cpp
Vst3MidiToEvent.hpp
vst3/Vst3RtStream.hpp Vst3RtStream.cpp
)
else()
set (VST3_SOURCES)
endif()
set (PIPEDAL_SOURCES
RtInversionGuard.hpp
CpuUse.hpp CpuUse.cpp
P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp
@@ -104,7 +154,7 @@ set (PIPEDAL_SOURCES
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
ConfigUtil.hpp ConfigUtil.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp PiPedalHost.hpp PiPedalHost.cpp
PluginType.hpp PluginType.cpp
Lv2Log.hpp Lv2Log.cpp
PiPedalSocket.hpp PiPedalSocket.cpp
@@ -152,6 +202,7 @@ set (PIPEDAL_SOURCES
AudioDriver.hpp
AudioConfig.hpp
${VST3_SOURCES}
)
@@ -161,16 +212,25 @@ include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
set (PIPEDAL_INCLUDES
${LV2DEV_INCLUDE_DIRS}
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS}
${VST3_INCLUDES}
.
)
set(PIPEDAL_LIBS libpipedald pthread atomic stdc++fs asound avahi-common avahi-client systemd
set(PIPEDAL_LIBS libpipedald
pthread atomic stdc++fs asound avahi-common avahi-client systemd
${LILV_0_LIBRARIES}
# ${JACK_LIBRARIES}
${LIBNL3_LIBRARIES} )
${LIBNL3_LIBRARIES}
)
if (VST3_ENABLED)
set (PIPEDAL_LIBS vst3_lib ${PIPEDAL_LIBS})
endif()
##########################
add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
target_include_directories(libpipedald PRIVATE ${PIPEDAL_INCLUDES}
@@ -226,6 +286,217 @@ target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS}
)
#########################################################
# VST3 Source.
#
# VST3 support is optional, because it poses unfortunate
# licensing problems.
# There's no easy way to get the VST SDK to build without
# GCC c++11 linkage, which is not compatible with pipedal's
# use of C++20 features (strictly a link time breakage).
#
# So pipedal rebuilds the VST3 SDK using pipedal-compatible
# compile flags.
#
# The current build supports only Linux builds. Refer to the
# original VST3 SDK build for details on how to build
# for other platforms.
#
#########################################################
if(ENABLE_VST3)
set (VST3_SRCPATH "${VST3PATH}/public.sdk/source")
set (VST3_BASEPATH "${VST3PATH}/base")
set (VST3_PLUGININTERFACES_BASE_PATH "${VST3PATH}/pluginterfaces/base")
set(vst3_sources
${VST3_SRCPATH}/vst/vstpresetfile.cpp
${VST3_SRCPATH}/vst/vstpresetfile.h
# base files.
${VST3_BASEPATH}/source/baseiids.cpp
${VST3_BASEPATH}/source/classfactoryhelpers.h
${VST3_BASEPATH}/source/fbuffer.cpp
${VST3_BASEPATH}/source/fbuffer.h
${VST3_BASEPATH}/source/fcleanup.h
${VST3_BASEPATH}/source/fcommandline.h
${VST3_BASEPATH}/source/fdebug.cpp
${VST3_BASEPATH}/source/fdebug.h
${VST3_BASEPATH}/source/fdynlib.cpp
${VST3_BASEPATH}/source/fdynlib.h
${VST3_BASEPATH}/source/fobject.cpp
${VST3_BASEPATH}/source/fobject.h
${VST3_BASEPATH}/source/fstreamer.cpp
${VST3_BASEPATH}/source/fstreamer.h
${VST3_BASEPATH}/source/fstring.cpp
${VST3_BASEPATH}/source/fstring.h
${VST3_BASEPATH}/source/timer.cpp
${VST3_BASEPATH}/source/timer.h
${VST3_BASEPATH}/source/updatehandler.cpp
${VST3_BASEPATH}/source/updatehandler.h
${VST3_BASEPATH}/thread/include/fcondition.h
${VST3_BASEPATH}/thread/include/flock.h
${VST3_BASEPATH}/thread/source/fcondition.cpp
${VST3_BASEPATH}/thread/source/flock.cpp
# sdk_common files.
${VST3_SRCPATH}/common/commoniids.cpp
${VST3_SRCPATH}/common/openurl.cpp
${VST3_SRCPATH}/common/openurl.h
${VST3_SRCPATH}/common/systemclipboard.h
${VST3_SRCPATH}/common/systemclipboard_win32.cpp
${VST3_SRCPATH}/common/threadchecker_linux.cpp
${VST3_SRCPATH}/common/threadchecker_win32.cpp
${VST3_SRCPATH}/common/threadchecker.h
${VST3_SRCPATH}/common/memorystream.cpp
${VST3_SRCPATH}/common/memorystream.h
# pluginterfaces
${VST3_PLUGININTERFACES_BASE_PATH}/conststringtable.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/conststringtable.h
${VST3_PLUGININTERFACES_BASE_PATH}/coreiids.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/falignpop.h
${VST3_PLUGININTERFACES_BASE_PATH}/falignpush.h
${VST3_PLUGININTERFACES_BASE_PATH}/fplatform.h
${VST3_PLUGININTERFACES_BASE_PATH}/fstrdefs.h
${VST3_PLUGININTERFACES_BASE_PATH}/ftypes.h
${VST3_PLUGININTERFACES_BASE_PATH}/funknown.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/funknown.h
${VST3_PLUGININTERFACES_BASE_PATH}/funknownimpl.h
${VST3_PLUGININTERFACES_BASE_PATH}/futils.h
${VST3_PLUGININTERFACES_BASE_PATH}/fvariant.h
${VST3_PLUGININTERFACES_BASE_PATH}/geoconstants.h
${VST3_PLUGININTERFACES_BASE_PATH}/ibstream.h
${VST3_PLUGININTERFACES_BASE_PATH}/icloneable.h
${VST3_PLUGININTERFACES_BASE_PATH}/ierrorcontext.h
${VST3_PLUGININTERFACES_BASE_PATH}/ipersistent.h
${VST3_PLUGININTERFACES_BASE_PATH}/ipluginbase.h
${VST3_PLUGININTERFACES_BASE_PATH}/istringresult.h
${VST3_PLUGININTERFACES_BASE_PATH}/iupdatehandler.h
${VST3_PLUGININTERFACES_BASE_PATH}/keycodes.h
${VST3_PLUGININTERFACES_BASE_PATH}/pluginbasefwd.h
${VST3_PLUGININTERFACES_BASE_PATH}/smartpointer.h
${VST3_PLUGININTERFACES_BASE_PATH}/typesizecheck.h
${VST3_PLUGININTERFACES_BASE_PATH}/ucolorspec.h
${VST3_PLUGININTERFACES_BASE_PATH}/ustring.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/ustring.h
# sdk files.
# ${VST3_SRCPATH}/main/moduleinit.cpp
# ${VST3_SRCPATH}/main/moduleinit.h
${VST3_SRCPATH}/vst/utility/audiobuffers.h
${VST3_SRCPATH}/vst/utility/processcontextrequirements.h
${VST3_SRCPATH}/vst/utility/processdataslicer.h
${VST3_SRCPATH}/vst/utility/ringbuffer.h
${VST3_SRCPATH}/vst/utility/rttransfer.h
${VST3_SRCPATH}/vst/utility/sampleaccurate.h
${VST3_SRCPATH}/vst/utility/stringconvert.cpp
${VST3_SRCPATH}/vst/utility/stringconvert.h
${VST3_SRCPATH}/vst/utility/testing.cpp
${VST3_SRCPATH}/vst/utility/testing.h
${VST3_SRCPATH}/vst/utility/vst2persistence.h
${VST3_SRCPATH}/vst/utility/vst2persistence.cpp
${VST3_SRCPATH}/vst/vstaudioeffect.cpp
${VST3_SRCPATH}/vst/vstaudioeffect.h
${VST3_SRCPATH}/vst/vstbus.cpp
${VST3_SRCPATH}/vst/vstbus.h
${VST3_SRCPATH}/vst/vstbypassprocessor.h
${VST3_SRCPATH}/vst/vstcomponent.cpp
${VST3_SRCPATH}/vst/vstcomponent.h
${VST3_SRCPATH}/vst/vstcomponentbase.cpp
${VST3_SRCPATH}/vst/vstcomponentbase.h
${VST3_SRCPATH}/vst/vsteditcontroller.cpp
${VST3_SRCPATH}/vst/vsteditcontroller.h
${VST3_SRCPATH}/vst/vsteventshelper.h
${VST3_SRCPATH}/vst/vsthelpers.h
${VST3_SRCPATH}/vst/vstinitiids.cpp
${VST3_SRCPATH}/vst/vstnoteexpressiontypes.cpp
${VST3_SRCPATH}/vst/vstnoteexpressiontypes.h
${VST3_SRCPATH}/vst/vstparameters.cpp
${VST3_SRCPATH}/vst/vstparameters.h
${VST3_SRCPATH}/vst/vstrepresentation.cpp
${VST3_SRCPATH}/vst/vstrepresentation.h
# sdk_host.a files.
${VST3_SRCPATH}/vst/hosting/connectionproxy.cpp
${VST3_SRCPATH}/vst/hosting/connectionproxy.h
${VST3_SRCPATH}/vst/hosting/eventlist.cpp
${VST3_SRCPATH}/vst/hosting/eventlist.h
${VST3_SRCPATH}/vst/hosting/hostclasses.cpp
${VST3_SRCPATH}/vst/hosting/hostclasses.h
${VST3_SRCPATH}/vst/hosting/module.cpp
${VST3_SRCPATH}/vst/hosting/module.h
${VST3_SRCPATH}/vst/hosting/parameterchanges.cpp
${VST3_SRCPATH}/vst/hosting/parameterchanges.h
${VST3_SRCPATH}/vst/hosting/pluginterfacesupport.cpp
${VST3_SRCPATH}/vst/hosting/pluginterfacesupport.h
${VST3_SRCPATH}/vst/hosting/plugprovider.cpp
${VST3_SRCPATH}/vst/hosting/plugprovider.h
${VST3_SRCPATH}/vst/hosting/processdata.cpp
${VST3_SRCPATH}/vst/hosting/processdata.h
${VST3_SRCPATH}/vst/utility/optional.h
${VST3_SRCPATH}/vst/utility/stringconvert.cpp
${VST3_SRCPATH}/vst/utility/stringconvert.h
${VST3_SRCPATH}/vst/utility/uid.h
${VST3_SRCPATH}/vst/vstinitiids.cpp
${VST3_SRCPATH}/vst/hosting/module_linux.cpp
# source/audiohost.cpp
# source/audiohost.h
# source/media/audioclient.cpp
# ource/media/audioclient.h
# source/media/imediaserver.h
# source/media/iparameterclient.h
# #source/media/jack/jackclient.cpp
# source/media/miditovst.h
# source/platform/appinit.h
# source/usediids.cpp
)
add_library(vst3_lib STATIC ${vst3_sources})
target_include_directories(vst3_lib PRIVATE ${VST3_INCLUDES}
)
add_executable(vst3test Vst3test.cpp
MemDebug.cpp
MemDebug.hpp
Vst3SdkRepro.cpp
${VST3_FILES}
#vst3/Vst3PresetFile.hpp Vst3PresetFile.cpp
asan_options.cpp
)
target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES}
)
target_link_libraries(vst3test PRIVATE ${PIPEDAL_LIBS}
vst3_lib dl pthread
)
endif(ENABLE_VST3)
# Build machine tests. Run tests that are not dependent on hardware.
add_test(NAME BuildTest COMMAND pipedaltest "[Build]")
+11 -3
View File
@@ -23,11 +23,12 @@
namespace pipedal {
class RealtimeParameterRequest;
class RealtimeRingBufferWriter;
class IEffect {
public:
virtual ~IEffect() {}
virtual long GetInstanceId() const = 0;
virtual uint64_t GetInstanceId() const = 0;
virtual int GetControlIndex(const std::string&symbol) const = 0;
virtual void SetControl(int index, float value) = 0;
virtual float GetControlValue(int index) const = 0;
@@ -35,6 +36,7 @@ namespace pipedal {
virtual float GetOutputControlValue(int controlIndex) const = 0;
virtual int GetNumberOfInputAudioPorts() const = 0;
virtual int GetNumberOfOutputAudioPorts() const = 0;
virtual float *GetAudioInputBuffer(int index) const = 0;
@@ -43,13 +45,19 @@ namespace pipedal {
virtual void RequestParameter(LV2_URID uridUri) = 0;
virtual void GatherParameter(RealtimeParameterRequest*pRequest) = 0;
virtual std::string AtomToJson(uint8_t*pAtom) = 0;
virtual std::string GetAtomObjectType(uint8_t*pData) = 0;
//virtual std::string AtomToJson(uint8_t*pAtom) = 0;
//virtual std::string GetAtomObjectType(uint8_t*pData) = 0;
virtual void SetAudioInputBuffer(int index, float *buffer) = 0;
virtual void SetAudioOutputBuffer(int index, float*buffer) = 0;
virtual void Activate() = 0;
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter) = 0;
virtual void Deactivate() = 0;
virtual bool IsVst3() const = 0;
};
} //namespace
+239 -5
View File
@@ -106,6 +106,73 @@ static std::string GetGovernor()
class JackHostImpl : public JackHost, private AudioDriverHost
{
private:
IHost *pHost = nullptr;
class Uris
{
public:
Uris(IHost *pHost)
{
atom_Blank = pHost->GetLv2Urid(LV2_ATOM__Blank);
atom_Path = pHost->GetLv2Urid(LV2_ATOM__Path);
atom_float = pHost->GetLv2Urid(LV2_ATOM__Float);
atom_Double = pHost->GetLv2Urid(LV2_ATOM__Double);
atom_Int = pHost->GetLv2Urid(LV2_ATOM__Int);
atom_Long = pHost->GetLv2Urid(LV2_ATOM__Long);
atom_Bool = pHost->GetLv2Urid(LV2_ATOM__Bool);
atom_String = pHost->GetLv2Urid(LV2_ATOM__String);
atom_Vector = pHost->GetLv2Urid(LV2_ATOM__Vector);
atom_Object = pHost->GetLv2Urid(LV2_ATOM__Object);
atom_Sequence = pHost->GetLv2Urid(LV2_ATOM__Sequence);
atom_Chunk = pHost->GetLv2Urid(LV2_ATOM__Chunk);
atom_URID = pHost->GetLv2Urid(LV2_ATOM__URID);
atom_eventTransfer = pHost->GetLv2Urid(LV2_ATOM__eventTransfer);
patch_Get = pHost->GetLv2Urid(LV2_PATCH__Get);
patch_Set = pHost->GetLv2Urid(LV2_PATCH__Set);
patch_Put = pHost->GetLv2Urid(LV2_PATCH__Put);
patch_body = pHost->GetLv2Urid(LV2_PATCH__body);
patch_subject = pHost->GetLv2Urid(LV2_PATCH__subject);
patch_property = pHost->GetLv2Urid(LV2_PATCH__property);
//patch_accept = pHost->GetLv2Urid(LV2_PATCH__accept);
patch_value = pHost->GetLv2Urid(LV2_PATCH__value);
unitsFrame = pHost->GetLv2Urid(LV2_UNITS__frame);
}
//LV2_URID patch_accept;
LV2_URID unitsFrame;
LV2_URID pluginUri;
LV2_URID atom_Blank;
LV2_URID atom_Bool;
LV2_URID atom_float;
LV2_URID atom_Double;
LV2_URID atom_Int;
LV2_URID atom_Long;
LV2_URID atom_String;
LV2_URID atom_Object;
LV2_URID atom_Vector;
LV2_URID atom_Path;
LV2_URID atom_Sequence;
LV2_URID atom_Chunk;
LV2_URID atom_URID;
LV2_URID atom_eventTransfer;
LV2_URID midi_Event;
LV2_URID patch_Get;
LV2_URID patch_Set;
LV2_URID patch_Put;
LV2_URID patch_body;
LV2_URID patch_subject;
LV2_URID patch_property;
LV2_URID patch_value;
LV2_URID param_uiState;
};
Uris uris;
AudioDriver*audioDriver = nullptr;
inherit_priority_recursive_mutex mutex;
@@ -146,6 +213,30 @@ private:
std::atomic<std::chrono::system_clock::time_point> lastUnderrunTime =
std::chrono::system_clock::from_time_t(0);
std::string GetAtomObjectType(uint8_t*pData)
{
LV2_Atom_Object *pAtom = (LV2_Atom_Object*)pData;
if (pAtom->atom.type != uris.atom_Object)
{
throw std::invalid_argument("Not an Lv2 Object");
}
return pHost->Lv2UriudToString(pAtom->body.otype);
}
void WriteAtom(json_writer &writer, LV2_Atom*pAtom);
std::string AtomToJson(uint8_t*pData)
{
std::stringstream s;
json_writer writer(s);
LV2_Atom *pAtom = (LV2_Atom*)pData;
WriteAtom(writer,pAtom);
return s.str();
}
virtual void OnUnderrun()
{
++this->underruns;
@@ -582,7 +673,6 @@ private:
}
}
public:
JackHostImpl(IHost *pHost)
@@ -592,7 +682,9 @@ public:
realtimeWriter(&this->outputRingBuffer),
hostReader(&this->outputRingBuffer),
hostWriter(&this->inputRingBuffer),
eventBufferUrids(pHost)
eventBufferUrids(pHost),
pHost(pHost),
uris(pHost)
{
#if JACK_HOST
@@ -758,7 +850,7 @@ public:
}
else
{
pRequest->jsonResponse = pEffect->AtomToJson(pRequest->response);
pRequest->jsonResponse = AtomToJson(pRequest->response);
}
}
pRequest->onJackRequestComplete(pRequest);
@@ -802,8 +894,8 @@ public:
IEffect *pEffect = currentPedalBoard->GetEffect(instanceId);
if (pEffect != nullptr &&this->pNotifyCallbacks && listenForAtomOutput)
{
std::string atomType = pEffect->GetAtomObjectType(&atomBuffer[0]);
auto json = pEffect->AtomToJson(&(atomBuffer[0]));
std::string atomType = GetAtomObjectType(&atomBuffer[0]);
auto json = AtomToJson(&(atomBuffer[0]));
this->pNotifyCallbacks->OnNotifyAtomOutput(instanceId,atomType,json);
}
}
@@ -1275,6 +1367,148 @@ public:
}
};
static std::string UriToFieldName(const std::string&uri){
int pos;
for (pos = uri.length(); pos >= 0; --pos)
{
char c = uri[pos];
if (c == '#' || c == '/' || c == ':')
{
break;
}
}
return uri.substr(pos+1);
}
void JackHostImpl::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
{
if (pAtom->type == uris.atom_Blank)
{
writer.write_raw("null");
}
else if (pAtom->type == uris.atom_float)
{
writer.write(
((LV2_Atom_Float*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Int)
{
writer.write(
((LV2_Atom_Int*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Long)
{
writer.write(
((LV2_Atom_Long*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Double)
{
writer.write(
((LV2_Atom_Double*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Bool)
{
writer.write(
((LV2_Atom_Bool*)pAtom)->body
);
} else if (pAtom->type == uris.atom_String)
{
const char *p = (((const char*) pAtom) + sizeof(LV2_Atom_String));
writer.write(
p
);
} else if (pAtom->type == uris.atom_Vector)
{
LV2_Atom_Vector *pVector = (LV2_Atom_Vector*)pAtom;
writer.start_array();
{
size_t n = (pAtom->size-sizeof(pVector->body))/pVector->body.child_size;
char *pItems = ((char*)pAtom) + sizeof(LV2_Atom_Vector);
if (pVector->body.child_type == uris.atom_float)
{
float *p = (float*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Int)
{
int32_t *p = (int32_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Long)
{
int64_t *p = (int64_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Double)
{
double *p = (double*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Bool)
{
bool *p = (bool*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
}
}
writer.end_array();
} else if (pAtom->type == uris.atom_Object)
{
writer.start_object();
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)pAtom;
bool firstMember = true;
if (obj->body.id != 0)
{
std::string id = pHost->Lv2UriudToString(obj->body.id);
writer.write_member("id",id.c_str());
firstMember = false;
}
if (obj->body.otype != 0)
{
std::string type = pHost->Lv2UriudToString(obj->body.otype);
writer.write_member("lv2Type",type.c_str());
if (!firstMember)
{
writer.write_raw(",");
}
firstMember = false;
}
LV2_ATOM_OBJECT_FOREACH (obj, prop) {
if (!firstMember) {
writer.write_raw(",");
}
firstMember = false;
std::string key = pHost->Lv2UriudToString(prop->key);
key = UriToFieldName(key);
writer.write(key);
writer.write_raw(": ");
LV2_Atom *value = &(prop->value);
WriteAtom(writer,value);
}
writer.end_object();
}
}
JackHost *JackHost::CreateInstance(IHost *pHost)
{
+74
View File
@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LiteralVersion.hpp"
#include <sstream>
#include <cctype>
using namespace pipedal;
LiteralVersion::LiteralVersion()
{
}
LiteralVersion::LiteralVersion(const std::string &version)
{
Parse(version);
}
bool LiteralVersion::Parse(const std::string &version)
{
int ix = 0;
while (ix < version.length())
{
char c = version[ix];
int n = 0;
if (std::isdigit(c))
{
while (std::isdigit(c))
{
c = version[ix++];
n = c -'0' + n;
}
this->versionNumbers.push_back(n);
if (version[ix] == '.')
{
++ix;
}
} else {
std::stringstream t;
while (ix < version.length())
{
c = version[ix++];
t << c;
}
suffix = t.str();
}
}
return true;
}
+45
View File
@@ -0,0 +1,45 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <vector>
#include <string>
namespace pipedal {
class LiteralVersion {
public:
LiteralVersion();
LiteralVersion(const std::string&version);
bool Parse(const std::string&version);
const std::vector<int> &getVersionNumbers() const { return versionNumbers; }
const std::string&getSuffix() const { return suffix; }
int compare(const LiteralVersion&other);
private:
std::vector<int> versionNumbers;
std::string suffix;
};
};
+4 -163
View File
@@ -216,7 +216,7 @@ void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
void Lv2Effect::SetAudioInputBuffer(float *left)
{
if (GetNumberOfInputs() > 1)
if (GetNumberOfInputAudioPorts() > 1)
{
SetAudioInputBuffer(0, left);
SetAudioInputBuffer(1, left);
@@ -229,7 +229,7 @@ void Lv2Effect::SetAudioInputBuffer(float *left)
void Lv2Effect::SetAudioInputBuffers(float *left, float *right)
{
if (GetNumberOfInputs() == 1)
if (GetNumberOfInputAudioPorts() == 1)
{
SetAudioInputBuffer(0, left);
}
@@ -343,7 +343,7 @@ static inline void CopyBuffer(float *input, float *output, uint32_t frames)
}
}
void Lv2Effect::Run(uint32_t samples,uint64_t instanceId, RealtimeRingBufferWriter *realtimeRingBufferWriter)
void Lv2Effect::Run(uint32_t samples,RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
// close off the atom input frame.
if (this->inputAtomBuffers.size() != 0)
@@ -441,7 +441,7 @@ void Lv2Effect::Run(uint32_t samples,uint64_t instanceId, RealtimeRingBufferWrit
}
}
RelayOutputMessages(instanceId,realtimeRingBufferWriter);
RelayOutputMessages(this->instanceId,realtimeRingBufferWriter);
}
LV2_Worker_Status Lv2Effect::worker_schedule_fn(LV2_Worker_Schedule_Handle handle,
@@ -600,164 +600,5 @@ void Lv2Effect::GatherParameter(RealtimeParameterRequest *pRequest)
}
}
std::string Lv2Effect::GetAtomObjectType(uint8_t*pData)
{
LV2_Atom_Object *pAtom = (LV2_Atom_Object*)pData;
if (pAtom->atom.type != uris.atom_Object)
{
throw std::invalid_argument("Not an Lv2 Object");
}
return pHost->Lv2UriudToString(pAtom->body.otype);
}
std::string Lv2Effect::AtomToJson(uint8_t*pData)
{
std::stringstream s;
json_writer writer(s);
LV2_Atom *pAtom = (LV2_Atom*)pData;
WriteAtom(writer,pAtom);
return s.str();
}
static std::string UriToFieldName(const std::string&uri){
int pos;
for (pos = uri.length(); pos >= 0; --pos)
{
char c = uri[pos];
if (c == '#' || c == '/' || c == ':')
{
break;
}
}
return uri.substr(pos+1);
}
void Lv2Effect::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
{
if (pAtom->type == uris.atom_Blank)
{
writer.write_raw("null");
}
else if (pAtom->type == uris.atom_float)
{
writer.write(
((LV2_Atom_Float*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Int)
{
writer.write(
((LV2_Atom_Int*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Long)
{
writer.write(
((LV2_Atom_Long*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Double)
{
writer.write(
((LV2_Atom_Double*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Bool)
{
writer.write(
((LV2_Atom_Bool*)pAtom)->body
);
} else if (pAtom->type == uris.atom_String)
{
const char *p = (((const char*) pAtom) + sizeof(LV2_Atom_String));
writer.write(
p
);
} else if (pAtom->type == uris.atom_Vector)
{
LV2_Atom_Vector *pVector = (LV2_Atom_Vector*)pAtom;
writer.start_array();
{
size_t n = (pAtom->size-sizeof(pVector->body))/pVector->body.child_size;
char *pItems = ((char*)pAtom) + sizeof(LV2_Atom_Vector);
if (pVector->body.child_type == uris.atom_float)
{
float *p = (float*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Int)
{
int32_t *p = (int32_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Long)
{
int64_t *p = (int64_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Double)
{
double *p = (double*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Bool)
{
bool *p = (bool*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
}
}
writer.end_array();
} else if (pAtom->type == uris.atom_Object)
{
writer.start_object();
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)pAtom;
bool firstMember = true;
if (obj->body.id != 0)
{
std::string id = pHost->Lv2UriudToString(obj->body.id);
writer.write_member("id",id.c_str());
firstMember = false;
}
if (obj->body.otype != 0)
{
std::string type = pHost->Lv2UriudToString(obj->body.otype);
writer.write_member("lv2Type",type.c_str());
if (!firstMember)
{
writer.write_raw(",");
}
firstMember = false;
}
LV2_ATOM_OBJECT_FOREACH (obj, prop) {
if (!firstMember) {
writer.write_raw(",");
}
firstMember = false;
std::string key = pHost->Lv2UriudToString(prop->key);
key = UriToFieldName(key);
writer.write(key);
writer.write_raw(": ");
LV2_Atom *value = &(prop->value);
WriteAtom(writer,value);
}
writer.end_object();
}
}
+20 -35
View File
@@ -20,7 +20,7 @@
#pragma once
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "PedalBoard.hpp"
#include <lilv/lilv.h>
#include "BufferPool.hpp"
@@ -82,7 +82,6 @@ namespace pipedal
LV2_Atom_Forge outputForgeRt;
void WriteAtom(json_writer &writer, LV2_Atom*pAtom);
class Uris
{
@@ -167,9 +166,10 @@ namespace pipedal
void BypassTo(float value);
virtual void RequestParameter(LV2_URID uridUri);
virtual void GatherParameter(RealtimeParameterRequest*pRequest);
virtual bool IsVst3() const { return false; }
virtual void RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
virtual uint8_t*GetAtomInputBuffer() {
@@ -182,10 +182,6 @@ namespace pipedal
return (uint8_t*)this->outputAtomBuffers[0];
}
virtual std::string AtomToJson(uint8_t*pAtom);
virtual std::string GetAtomObjectType(uint8_t*pData);
public:
@@ -196,29 +192,30 @@ namespace pipedal
~Lv2Effect();
virtual void ResetAtomBuffers();
virtual long GetInstanceId() const { return instanceId; }
virtual uint64_t GetInstanceId() const { return instanceId; }
virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); }
virtual int GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); }
int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual void SetAudioInputBuffer(int index, float *buffer);
float *GetAudioInputBuffer(int index) const { return this->inputAudioBuffers[index]; }
virtual float *GetAudioInputBuffer(int index) const { return this->inputAudioBuffers[index]; }
void SetAudioInputBuffer(float *buffer);
void SetAudioInputBuffers(float *left, float *right);
virtual void SetAudioInputBuffer(float *buffer);
virtual void SetAudioInputBuffers(float *left, float *right);
virtual void SetAudioOutputBuffer(int index, float *buffer);
float *GetAudioOutputBuffer(int index) const { return this->outputAudioBuffers[index]; }
virtual float *GetAudioOutputBuffer(int index) const { return this->outputAudioBuffers[index]; }
void SetAtomInputBuffer(int index, void *buffer);
void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; }
virtual void SetAtomInputBuffer(int index, void *buffer) { this->inputAtomBuffers[index] = (char*)buffer;}
virtual void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; }
void SetAtomOutputBuffer(int index, void *buffer);
void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; }
virtual void SetAtomOutputBuffer(int index, void *buffer) { this->outputAtomBuffers[index] = (char*)buffer; }
virtual void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; }
virtual int GetControlIndex(const std::string &symbol) const;
@@ -254,22 +251,10 @@ namespace pipedal
}
}
int GetNumberOfInputs() const
{
return this->inputAudioPortIndices.size();
}
int GetNumberOfOutputs() const
{
return this->outputAudioPortIndices.size();
}
int GetNumberOfMidiInputs() const
{
return this->inputMidiPortIndices.size();
}
void Activate();
void Run(uint32_t samples, uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
void Deactivate();
virtual void Activate();
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter);
virtual void Deactivate();
};
} // namespace
+1 -1
View File
@@ -19,7 +19,7 @@
#include "pch.h"
#include "Lv2EventBufferWriter.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
using namespace pipedal;
+3 -3
View File
@@ -24,17 +24,17 @@
#include <string>
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "MemDebug.hpp"
using namespace pipedal;
TEST_CASE( "Lv2Host memory leak", "[lv2host_leak][Build][Dev]" ) {
TEST_CASE( "PiPedalHost memory leak", "[lv2host_leak][Build][Dev]" ) {
MemStats initialMemory = GetMemStats();
{
Lv2Host host;
PiPedalHost host;
host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2");
}
+5 -4
View File
@@ -21,6 +21,7 @@
#include "Lv2PedalBoard.hpp"
#include "Lv2Effect.hpp"
#include "SplitEffect.hpp"
#include "RingBufferReader.hpp"
#include "VuUpdate.hpp"
@@ -57,7 +58,7 @@ int Lv2PedalBoard::GetControlIndex(uint64_t instanceId, const std::string &symbo
return -1;
}
std::vector<float *> Lv2PedalBoard::PrepareItems(
const std::vector<PedalBoardItem> &items,
std::vector<PedalBoardItem> &items,
std::vector<float *> inputBuffers)
{
for (int i = 0; i < items.size(); ++i)
@@ -142,9 +143,9 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
}
this->processActions.push_back(
[pLv2Effect,this,instanceId](uint32_t frames)
[pLv2Effect,this](uint32_t frames)
{
pLv2Effect->Run(frames,instanceId,this->ringBufferWriter);
pLv2Effect->Run(frames,this->ringBufferWriter);
});
}
}
@@ -197,7 +198,7 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
return inputBuffers;
}
void Lv2PedalBoard::Prepare(IHost *pHost, const PedalBoard &pedalBoard)
void Lv2PedalBoard::Prepare(IHost *pHost, PedalBoard &pedalBoard)
{
this->pHost = pHost;
+5 -3
View File
@@ -19,7 +19,7 @@
#pragma once
#include "PedalBoard.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "Lv2Effect.hpp"
#include "BufferPool.hpp"
#include <functional>
@@ -81,7 +81,7 @@ class Lv2PedalBoard {
std::vector<float*> PrepareItems(
const std::vector<PedalBoardItem> & items,
std::vector<PedalBoardItem> & items,
std::vector<float*> inputBuffers
);
@@ -95,7 +95,9 @@ public:
Lv2PedalBoard() { }
~Lv2PedalBoard() { }
void Prepare(IHost *pHost,const PedalBoard&pedalBoard);
void Prepare(IHost *pHost,PedalBoard&pedalBoard);
std::vector<IEffect* > GetEffects() { return realtimeEffects; }
int GetIndexOfInstanceId(uint64_t instanceId) {
for (int i = 0; i < this->realtimeEffects.size(); ++i)
+2
View File
@@ -82,10 +82,12 @@ class PedalBoardItem: public JsonWritable {
std::vector<PedalBoardItem> topChain_;
std::vector<PedalBoardItem> bottomChain_;
std::vector<MidiBinding> midiBindings_;
std::string vstState_;
public:
ControlValue*GetControlValue(const std::string&symbol);
GETTER_SETTER(instanceId)
GETTER_SETTER_REF(uri)
GETTER_SETTER_REF(vstState);
GETTER_SETTER_REF(pluginName)
GETTER_SETTER(isEnabled)
GETTER_SETTER_VEC(controlValues)
+1
View File
@@ -36,4 +36,5 @@ JSON_MAP_REFERENCE(PiPedalConfiguration, logHttpRequests)
JSON_MAP_REFERENCE(PiPedalConfiguration, maxUploadSize)
JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointGateway)
JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointServerAddress)
JSON_MAP_REFERENCE(PiPedalConfiguration, isVst3Enabled)
JSON_MAP_END()
+2 -1
View File
@@ -47,9 +47,10 @@ private:
uint64_t maxUploadSize_ = 1024*1024;
std::string accessPointGateway_;
std::string accessPointServerAddress_;
bool isVst3Enabled_ = true;
public:
bool IsVst3Enabled() const { return isVst3Enabled_; }
std::filesystem::path GetConfigFilePath() const {
return docRoot_ / "config.jason";
}
+173 -112
View File
@@ -18,7 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include <lilv/lilv.h>
#include <stdexcept>
#include <locale>
@@ -85,7 +85,7 @@ namespace pipedal
static const char *LV2_MIDI_EVENT = "http://lv2plug.in/ns/ext/midi#MidiEvent";
static const char *LV2_DESIGNATION = "http://lv2plug.in/ns/lv2core#Designation";
class Lv2Host::Urids
class PiPedalHost::Urids
{
public:
Urids(MapFeature &mapFeature)
@@ -96,9 +96,17 @@ namespace pipedal
};
}
void Lv2Host::LilvUris::Initialize(LilvWorld *pWorld)
void PiPedalHost::SetConfiguration(const PiPedalConfiguration &configuration)
{
rdfsComment = lilv_new_uri(pWorld, Lv2Host::RDFS_COMMENT_URI);
this->vst3CachePath =
std::filesystem::path(configuration.GetLocalStoragePath()) / "vst3cache.json";
this->vst3Enabled = configuration.IsVst3Enabled();
}
void PiPedalHost::LilvUris::Initialize(LilvWorld *pWorld)
{
rdfsComment = lilv_new_uri(pWorld, PiPedalHost::RDFS_COMMENT_URI);
logarithic_uri = lilv_new_uri(pWorld, LV2_PORT_LOGARITHMIC);
display_priority_uri = lilv_new_uri(pWorld, LV2_PORT_DISPLAY_PRIORITY);
range_steps_uri = lilv_new_uri(pWorld, LV2_PORT_RANGE_STEPS);
@@ -124,7 +132,7 @@ void Lv2Host::LilvUris::Initialize(LilvWorld *pWorld)
appliesTo = lilv_new_uri(pWorld, LV2_CORE__appliesTo);
}
void Lv2Host::LilvUris::Free()
void PiPedalHost::LilvUris::Free()
{
rdfsComment.Free();
logarithic_uri.Free();
@@ -242,7 +250,7 @@ public:
}
};
Lv2Host::Lv2Host()
PiPedalHost::PiPedalHost()
{
pWorld = nullptr;
@@ -260,7 +268,7 @@ Lv2Host::Lv2Host()
this->urids = new Urids(mapFeature);
}
void Lv2Host::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
void PiPedalHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
{
this->sampleRate = configuration.GetSampleRate();
if (configuration.isValid())
@@ -272,7 +280,7 @@ void Lv2Host::OnConfigurationChanged(const JackConfiguration &configuration, con
}
}
Lv2Host::~Lv2Host()
PiPedalHost::~PiPedalHost()
{
delete[] lv2Features;
lilvUris.Free();
@@ -280,7 +288,7 @@ Lv2Host::~Lv2Host()
delete urids;
}
void Lv2Host::free_world()
void PiPedalHost::free_world()
{
if (pWorld)
{
@@ -288,7 +296,7 @@ void Lv2Host::free_world()
pWorld = nullptr;
}
}
std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const std::string &uri) const
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetPluginClass(const std::string &uri) const
{
auto it = this->classesMap.find(uri);
if (it == this->classesMap.end())
@@ -296,7 +304,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const std::string &uri)
return (*it).second;
}
void Lv2Host::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
void PiPedalHost::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
{
this->classesMap[pluginClass->uri()] = pluginClass;
for (int i = 0; i < pluginClass->children_.size(); ++i)
@@ -306,7 +314,7 @@ void Lv2Host::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
child->parent_ = pluginClass.get();
}
}
void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
void PiPedalHost::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
{
std::ifstream f(jsonFile);
json_reader reader(f);
@@ -320,7 +328,7 @@ void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
MAP_CHECK();
}
std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *pClass)
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetPluginClass(const LilvPluginClass *pClass)
{
MAP_CHECK();
@@ -328,7 +336,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *p
std::shared_ptr<Lv2PluginClass> pResult = this->classesMap[uri];
return pResult;
}
std::shared_ptr<Lv2PluginClass> Lv2Host::MakePluginClass(const LilvPluginClass *pClass)
std::shared_ptr<Lv2PluginClass> PiPedalHost::MakePluginClass(const LilvPluginClass *pClass)
{
std::string uri = nodeAsString(lilv_plugin_class_get_uri(pClass));
auto t = this->classesMap.find(uri);
@@ -348,7 +356,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::MakePluginClass(const LilvPluginClass *
return result;
}
void Lv2Host::LoadPluginClassesFromLilv()
void PiPedalHost::LoadPluginClassesFromLilv()
{
const auto classes = lilv_world_get_plugin_classes(pWorld);
@@ -378,7 +386,7 @@ void Lv2Host::LoadPluginClassesFromLilv()
}
}
void Lv2Host::Load(const char *lv2Path)
void PiPedalHost::Load(const char *lv2Path)
{
this->plugins_.clear();
@@ -390,13 +398,14 @@ void Lv2Host::Load(const char *lv2Path)
free_world();
Lv2Log::info("Scanning for LV2 Plugins");
setenv("LV2_PATH", lv2Path, true);
StdErrorCapture stdoutCapture; // captures lilv messages written to STDOUT.
StdErrorCapture stdoutCapture; // captures lilv messages written to STDOUT.
pWorld = lilv_world_new();
{
lilv_world_load_all(pWorld);
}
lilvUris.Initialize(pWorld);
@@ -443,7 +452,7 @@ void Lv2Host::Load(const char *lv2Path)
}
auto messages = stdoutCapture.GetOutputLines();
for (const std::string & s: messages)
for (const std::string &s : messages)
{
Lv2Log::info("lilv: " + s);
}
@@ -491,8 +500,35 @@ void Lv2Host::Load(const char *lv2Path)
}
}
}
}
;
#if ENABLE_VST3
if (vst3Enabled)
{
Lv2Log::info("Scanning for VST3 Plugins");
this->vst3Host = Vst3Host::CreateInstance(
this->vst3CachePath);
this->vst3Host->RefreshPlugins();
const auto &vst3PluginList = this->vst3Host->getPluginList();
for (const auto &vst3Plugin : vst3PluginList)
{
// copy not move!
ui_plugins_.push_back(vst3Plugin->pluginInfo_);
}
auto ui_compare = [&collation](
Lv2PluginUiInfo &left,
Lv2PluginUiInfo &right)
{
const char *pb1 = left.name().c_str();
const char *pb2 = right.name().c_str();
return collation.compare(
pb1, pb1 + left.name().size(),
pb2, pb2 + right.name().size()) < 0;
};
std::sort(this->ui_plugins_.begin(), this->ui_plugins_.end(), ui_compare);
}
#endif
};
class NodesAutoFree
{
@@ -532,10 +568,10 @@ static std::vector<std::string> nodeAsStringArray(const LilvNodes *nodes)
return result;
}
const char *Lv2Host::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#"
"comment";
const char *PiPedalHost::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#"
"comment";
LilvNode *Lv2Host::get_comment(const std::string &uri)
LilvNode *PiPedalHost::get_comment(const std::string &uri)
{
NodeAutoFree uriNode = lilv_new_uri(pWorld, uri.c_str());
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfsComment, nullptr);
@@ -547,7 +583,7 @@ static bool ports_sort_compare(std::shared_ptr<Lv2PortInfo> &p1, const std::shar
return p1->index() < p2->index();
}
bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin)
bool Lv2PluginInfo::HasFactoryPresets(PiPedalHost *lv2Host, const LilvPlugin *plugin)
{
NodesAutoFree nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris.pset_Preset);
bool result = false;
@@ -559,7 +595,7 @@ bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin
return result;
}
Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin)
Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin)
{
const LilvNode *pluginUri = lilv_plugin_get_uri(pPlugin);
@@ -664,7 +700,7 @@ Lv2PluginInfo::~Lv2PluginInfo()
{
}
bool Lv2PortInfo::is_a(Lv2Host *lv2Plugins, const char *classUri)
bool Lv2PortInfo::is_a(PiPedalHost *lv2Plugins, const char *classUri)
{
return classes_.is_a(lv2Plugins, classUri);
}
@@ -686,7 +722,7 @@ static bool scale_points_sort_compare(const Lv2ScalePoint &v1, const Lv2ScalePoi
{
return v1.value() < v2.value();
}
Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort *pPort)
Lv2PortInfo::Lv2PortInfo(PiPedalHost *host, const LilvPlugin *plugin, const LilvPort *pPort)
{
auto pWorld = host->pWorld;
index_ = lilv_port_get_index(plugin, pPort);
@@ -785,7 +821,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
NodeAutoFree commentNode = lilv_port_get(plugin,pPort,host->lilvUris.rdfsComment);
NodeAutoFree commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfsComment);
this->comment_ = nodeAsString(commentNode);
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
@@ -800,7 +836,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
((is_input_ || is_output_) && (is_audio_port_ || is_atom_port_ || is_cv_port_));
}
Lv2PluginClasses Lv2Host::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort)
Lv2PluginClasses PiPedalHost::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort)
{
std::vector<std::string> result;
const LilvNodes *nodes = lilv_port_get_classes(lilvPlugin, lilvPort);
@@ -832,7 +868,7 @@ bool Lv2PluginClass::is_a(const std::string &classUri) const
}
return false;
}
bool Lv2PluginClasses::is_a(Lv2Host *lv2Plugins, const char *classUri) const
bool Lv2PluginClasses::is_a(PiPedalHost *lv2Plugins, const char *classUri) const
{
std::string classUri_(classUri);
for (auto i : classes_)
@@ -845,7 +881,7 @@ bool Lv2PluginClasses::is_a(Lv2Host *lv2Plugins, const char *classUri) const
return false;
}
bool Lv2Host::is_a(const std::string &class_, const std::string &target_class)
bool PiPedalHost::is_a(const std::string &class_, const std::string &target_class)
{
if (class_ == target_class)
return true;
@@ -857,7 +893,7 @@ bool Lv2Host::is_a(const std::string &class_, const std::string &target_class)
return false;
}
Lv2PluginUiInfo::Lv2PluginUiInfo(Lv2Host *pHost, const Lv2PluginInfo *plugin)
Lv2PluginUiInfo::Lv2PluginUiInfo(PiPedalHost *pHost, const Lv2PluginInfo *plugin)
: uri_(plugin->uri()),
name_(plugin->name()),
author_name_(plugin->author_name()),
@@ -919,12 +955,12 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(Lv2Host *pHost, const Lv2PluginInfo *plugin)
}
}
std::shared_ptr<Lv2PluginClass> Lv2Host::GetLv2PluginClass() const
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetLv2PluginClass() const
{
return this->GetPluginClass(LV2_PLUGIN);
}
std::shared_ptr<Lv2PluginInfo> Lv2Host::GetPluginInfo(const std::string &uri) const
std::shared_ptr<Lv2PluginInfo> PiPedalHost::GetPluginInfo(const std::string &uri) const
{
for (auto i = this->plugins_.begin(); i != this->plugins_.end(); ++i)
{
@@ -936,7 +972,7 @@ std::shared_ptr<Lv2PluginInfo> Lv2Host::GetPluginInfo(const std::string &uri) co
return nullptr;
}
Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard)
Lv2PedalBoard *PiPedalHost::CreateLv2PedalBoard(PedalBoard &pedalBoard)
{
Lv2PedalBoard *pPedalBoard = new Lv2PedalBoard();
try
@@ -953,18 +989,18 @@ Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard)
struct StateCallbackData
{
Lv2Host *pHost;
PiPedalHost *pHost;
std::vector<ControlValue> *pResult;
};
void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol,
void *user_data,
const void *value,
uint32_t size,
uint32_t type)
void PiPedalHost::fn_LilvSetPortValueFunc(const char *port_symbol,
void *user_data,
const void *value,
uint32_t size,
uint32_t type)
{
StateCallbackData *pData = (StateCallbackData *)user_data;
Lv2Host *pHost = pData->pHost;
PiPedalHost *pHost = pData->pHost;
std::vector<ControlValue> *pResult = pData->pResult;
LV2_URID valueType = (LV2_URID)type;
if (valueType != pHost->urids->atom_Float)
@@ -974,7 +1010,7 @@ void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol,
pResult->push_back(ControlValue(port_symbol, *(float *)value));
}
std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
std::vector<ControlValue> PiPedalHost::LoadFactoryPluginPreset(
PedalBoardItem *pedalBoardItem, const std::string &presetUri)
{
@@ -1032,15 +1068,15 @@ std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
struct PresetCallbackState
{
Lv2Host *pHost;
PiPedalHost *pHost;
std::map<std::string, float> *values;
bool failed = false;
};
void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type)
void PiPedalHost::PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type)
{
PresetCallbackState *pState = static_cast<PresetCallbackState *>(user_data);
Lv2Host *pHost = pState->pHost;
PiPedalHost *pHost = pState->pHost;
if (type == pHost->urids->atom_Float)
{
@@ -1051,7 +1087,7 @@ void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void
pState->failed = true;
}
}
PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri)
PluginPresets PiPedalHost::GetFactoryPluginPresets(const std::string &pluginUri)
{
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
@@ -1093,17 +1129,38 @@ PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri)
return result;
}
Lv2Effect *Lv2Host::CreateEffect(const PedalBoardItem &pedalBoardItem)
IEffect *PiPedalHost::CreateEffect(PedalBoardItem &pedalBoardItem)
{
if (pedalBoardItem.uri().starts_with("vst3:"))
{
#if ENABLE_VST3
try
{
Vst3Effect::Ptr vst3Plugin = this->vst3Host->CreatePlugin(pedalBoardItem, this);
return vst3Plugin.release();
}
catch (const std::exception &e)
{
Lv2Log::error(std::string("Failed to create VST3 plugin: ") + e.what());
throw;
}
#else
Lv2Log::error(std::string("VST3 support not enabled at compile time."));
throw PiPedalException("VST3 support not enabled at compile time, SEE ENABLE_VST3 in CMakeList.txt.");
auto info = this->GetPluginInfo(pedalBoardItem.uri());
if (!info)
return nullptr;
#endif
}
else
{
auto info = this->GetPluginInfo(pedalBoardItem.uri());
if (!info)
return nullptr;
return new Lv2Effect(this, info, pedalBoardItem);
return new Lv2Effect(this, info, pedalBoardItem);
}
}
Lv2PortGroup::Lv2PortGroup(Lv2Host *lv2Host, const std::string &groupUri)
Lv2PortGroup::Lv2PortGroup(PiPedalHost *lv2Host, const std::string &groupUri)
{
LilvWorld *pWorld = lv2Host->pWorld;
@@ -1131,47 +1188,45 @@ json_map::storage_type<Lv2ScalePoint> Lv2ScalePoint::jmap{{
json_map::reference("label", &Lv2ScalePoint::label_),
}};
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("symbol", &Lv2PortInfo::symbol_),
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("name", &Lv2PortInfo::name_),
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("symbol", &Lv2PortInfo::symbol_),
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("name", &Lv2PortInfo::name_),
json_map::reference("min_value", &Lv2PortInfo::min_value_),
json_map::reference("max_value", &Lv2PortInfo::max_value_),
json_map::reference("default_value", &Lv2PortInfo::default_value_),
json_map::reference("classes", &Lv2PortInfo::classes_),
json_map::reference("min_value", &Lv2PortInfo::min_value_),
json_map::reference("max_value", &Lv2PortInfo::max_value_),
json_map::reference("default_value", &Lv2PortInfo::default_value_),
json_map::reference("classes", &Lv2PortInfo::classes_),
json_map::reference("scale_points", &Lv2PortInfo::scale_points_),
json_map::reference("scale_points", &Lv2PortInfo::scale_points_),
json_map::reference("is_input", &Lv2PortInfo::is_input_),
json_map::reference("is_output", &Lv2PortInfo::is_output_),
json_map::reference("is_input", &Lv2PortInfo::is_input_),
json_map::reference("is_output", &Lv2PortInfo::is_output_),
json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_),
json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_atom_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_cv_port", &Lv2PortInfo::is_cv_port_),
json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_),
json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_atom_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_cv_port", &Lv2PortInfo::is_cv_port_),
json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
json_map::reference("port_group", &Lv2PortInfo::port_group_),
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
json_map::reference("port_group", &Lv2PortInfo::port_group_),
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
MAP_REF(Lv2PortInfo, is_logarithmic),
MAP_REF(Lv2PortInfo, display_priority),
MAP_REF(Lv2PortInfo, range_steps),
MAP_REF(Lv2PortInfo, trigger),
MAP_REF(Lv2PortInfo, integer_property),
MAP_REF(Lv2PortInfo, enumeration_property),
MAP_REF(Lv2PortInfo, toggled_property),
MAP_REF(Lv2PortInfo, not_on_gui),
MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)
}};
MAP_REF(Lv2PortInfo, is_logarithmic),
MAP_REF(Lv2PortInfo, display_priority),
MAP_REF(Lv2PortInfo, range_steps),
MAP_REF(Lv2PortInfo, trigger),
MAP_REF(Lv2PortInfo, integer_property),
MAP_REF(Lv2PortInfo, enumeration_property),
MAP_REF(Lv2PortInfo, toggled_property),
MAP_REF(Lv2PortInfo, not_on_gui),
MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)}};
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
MAP_REF(Lv2PortGroup, uri),
@@ -1197,21 +1252,19 @@ json_map::storage_type<Lv2PluginInfo> Lv2PluginInfo::jmap{{
json_map::reference("has_factory_presets", &Lv2PluginInfo::has_factory_presets_),
}};
json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
json_map::reference("author_name", &Lv2PluginUiInfo::author_name_),
json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_),
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
json_map::reference("description", &Lv2PluginUiInfo::description_),
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{
MAP_REF(Lv2PluginClass, uri),
MAP_REF(Lv2PluginClass, display_name),
MAP_REF(Lv2PluginClass, parent_uri),
json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()),
MAP_REF(Lv2PluginClass, children),
}};
json_map::storage_type<Lv2PluginUiPortGroup> Lv2PluginUiPortGroup::jmap{{
MAP_REF(Lv2PluginUiPortGroup, symbol),
MAP_REF(Lv2PluginUiPortGroup, name),
MAP_REF(Lv2PluginUiPortGroup, parent_group),
MAP_REF(Lv2PluginUiPortGroup, program_list_id),
}};
json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
@@ -1234,18 +1287,26 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
MAP_REF(Lv2PluginUiControlPort, comment),
MAP_REF(Lv2PluginUiControlPort, is_bypass),
MAP_REF(Lv2PluginUiControlPort, is_program_controller),
MAP_REF(Lv2PluginUiControlPort, custom_units),
}};
json_map::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{
MAP_REF(Lv2PluginClass, uri),
MAP_REF(Lv2PluginClass, display_name),
MAP_REF(Lv2PluginClass, parent_uri),
json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()),
MAP_REF(Lv2PluginClass, children),
}};
json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
json_map::reference("author_name", &Lv2PluginUiInfo::author_name_),
json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_),
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
json_map::reference("description", &Lv2PluginUiInfo::description_),
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
json_map::storage_type<Lv2PluginUiPortGroup> Lv2PluginUiPortGroup::jmap{{
MAP_REF(Lv2PluginUiPortGroup, symbol),
MAP_REF(Lv2PluginUiPortGroup, name),
}};
+181 -138
View File
@@ -30,11 +30,15 @@
#include "OptionsFeature.hpp"
#include <filesystem>
#include <cmath>
#include <string>
#include "lv2.h"
#include "Units.hpp"
#include "PluginPreset.hpp"
#include "IEffect.hpp"
#include "PiPedalConfiguration.hpp"
namespace pipedal {
@@ -42,19 +46,23 @@ namespace pipedal {
// forward declarations
class Lv2Effect;
class Lv2PedalBoard;
class Lv2Host;
class PiPedalHost;
class JackConfiguration;
class JackChannelSelection;
#ifndef LV2_PROPERTY_GETSET
#define LV2_PROPERTY_GETSET(name) \
const decltype(name##_) & name() const { return name##_; }; \
decltype(name##_) & name() { return name##_; }; \
void name(const decltype(name##_) &value) { name##_ = value; };
#endif
#ifndef LV2_PROPERTY_GETSET_SCALAR
#define LV2_PROPERTY_GETSET_SCALAR(name) \
decltype(name##_) name() const { return name##_;}; \
void name(decltype(name##_) value) { name##_ = value; };
#endif
@@ -62,7 +70,7 @@ class JackChannelSelection;
class Lv2PluginClass {
public:
friend class Lv2Host;
friend class PiPedalHost;
private:
Lv2PluginClass* parent_ = nullptr; // NOT SERIALIZED!
std::string parent_uri_;
@@ -72,7 +80,7 @@ private:
std::vector<std::shared_ptr<Lv2PluginClass>> children_;
friend class ::pipedal::Lv2Host;
friend class ::pipedal::PiPedalHost;
// hide copy constructor.
Lv2PluginClass(const Lv2PluginClass &other)
{
@@ -135,7 +143,7 @@ public:
{
return classes_;
}
bool is_a(Lv2Host*lv2Plugins,const char*classUri) const;
bool is_a(PiPedalHost*lv2Plugins,const char*classUri) const;
static json_map::storage_type<Lv2PluginClasses> jmap;
@@ -171,7 +179,7 @@ enum class Lv2BufferType {
class Lv2PortInfo {
public:
Lv2PortInfo(Lv2Host*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort);
Lv2PortInfo(PiPedalHost*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort);
private:
friend class Lv2PluginInfo;
@@ -276,7 +284,7 @@ public:
public:
Lv2PortInfo() { }
~Lv2PortInfo() = default;
bool is_a(Lv2Host*lv2Plugins,const char*classUri);
bool is_a(PiPedalHost*lv2Plugins,const char*classUri);
static json_map::storage_type<Lv2PortInfo> jmap;
@@ -294,19 +302,19 @@ public:
LV2_PROPERTY_GETSET(name);
Lv2PortGroup() { }
Lv2PortGroup(Lv2Host*lv2Host,const std::string &groupUri);
Lv2PortGroup(PiPedalHost*lv2Host,const std::string &groupUri);
static json_map::storage_type<Lv2PortGroup> jmap;
};
class Lv2PluginInfo {
private:
friend class Lv2Host;
friend class PiPedalHost;
public:
Lv2PluginInfo(Lv2Host*lv2Host,const LilvPlugin*);
Lv2PluginInfo(PiPedalHost*lv2Host,const LilvPlugin*);
Lv2PluginInfo() { }
private:
bool HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin);
bool HasFactoryPresets(PiPedalHost*lv2Host, const LilvPlugin*plugin);
std::string uri_;
std::string name_;
std::string plugin_class_;
@@ -399,139 +407,151 @@ public:
};
class Lv2PluginUiControlPort {
public:
Lv2PluginUiControlPort() {
}
Lv2PluginUiControlPort(const Lv2PluginInfo *pPlugin,const Lv2PortInfo*pPort)
: symbol_(pPort->symbol())
, index_(pPort->index())
, name_(pPort->name())
, min_value_(pPort->min_value())
, max_value_(pPort->max_value())
, default_value_(pPort->default_value())
, range_steps_(pPort->range_steps())
, display_priority_(pPort->display_priority())
, is_logarithmic_(pPort->is_logarithmic())
, integer_property_(pPort->integer_property())
, enumeration_property_(pPort->enumeration_property())
, toggled_property_(pPort->toggled_property())
, not_on_gui_(pPort->not_on_gui())
, scale_points_(pPort->scale_points())
, comment_(pPort->comment())
, units_(pPort->units())
class Lv2PluginUiPortGroup
{
// Use symbols to index port groups, instead of uris.
// symbols are guaranteed to be unique.
auto &portGroup = pPort->port_group();
for (int i = 0; i < pPlugin->port_groups().size(); ++i)
private:
std::string symbol_;
std::string name_;
std::string parent_group_;
int32_t program_list_id_ = -1; // used by VST3.
public:
LV2_PROPERTY_GETSET(symbol)
LV2_PROPERTY_GETSET(name)
LV2_PROPERTY_GETSET(parent_group)
LV2_PROPERTY_GETSET_SCALAR(program_list_id)
public:
Lv2PluginUiPortGroup() {}
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
: symbol_(pPortGroup->symbol()), name_(pPortGroup->name())
{
}
Lv2PluginUiPortGroup(
const std::string &symbol, const std::string &name,
const std::string &parent_group, int32_t programListId)
: symbol_(symbol),name_(name),parent_group_(parent_group),program_list_id_(programListId)
{
auto &p = pPlugin->port_groups()[i];
if (p->uri() == portGroup)
{
this->port_group_ = p->symbol();
break;
}
}
}
}
private:
std::string symbol_;
int index_;
std::string name_;
float min_value_ = 0, max_value_ = 1,default_value_ = 0;
int range_steps_ = 0;
int display_priority_ = -1;
bool is_logarithmic_ = false;
bool integer_property_ = false;
bool enumeration_property_ = false;
bool toggled_property_ = false;
bool not_on_gui_ = false;
std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_;
Units units_ = Units::none;
std::string comment_;
public:
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index);
LV2_PROPERTY_GETSET(name);
LV2_PROPERTY_GETSET_SCALAR(min_value);
LV2_PROPERTY_GETSET_SCALAR(max_value);
LV2_PROPERTY_GETSET_SCALAR(default_value);
LV2_PROPERTY_GETSET_SCALAR(range_steps);
LV2_PROPERTY_GETSET_SCALAR(display_priority);
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
LV2_PROPERTY_GETSET_SCALAR(integer_property);
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
public:
static json_map::storage_type<Lv2PluginUiPortGroup> jmap;
};
public:
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
};
class Lv2PluginUiPortGroup {
private:
std::string symbol_;
std::string name_;
public:
Lv2PluginUiPortGroup() { }
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
: symbol_(pPortGroup->symbol())
, name_(pPortGroup->name())
class Lv2PluginUiControlPort
{
}
public:
static json_map::storage_type<Lv2PluginUiPortGroup> jmap;
public:
Lv2PluginUiControlPort()
{
}
Lv2PluginUiControlPort(const Lv2PluginInfo *pPlugin, const Lv2PortInfo *pPort)
: symbol_(pPort->symbol()), index_(pPort->index()), name_(pPort->name()), min_value_(pPort->min_value()), max_value_(pPort->max_value()), default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()), is_logarithmic_(pPort->is_logarithmic()), integer_property_(pPort->integer_property()), enumeration_property_(pPort->enumeration_property()), toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()), comment_(pPort->comment()), units_(pPort->units())
{
// Use symbols to index port groups, instead of uris.
// symbols are guaranteed to be unique.
auto &portGroup = pPort->port_group();
for (int i = 0; i < pPlugin->port_groups().size(); ++i)
{
};
auto &p = pPlugin->port_groups()[i];
if (p->uri() == portGroup)
{
this->port_group_ = p->symbol();
break;
}
}
}
class Lv2PluginUiInfo {
public:
Lv2PluginUiInfo() { }
Lv2PluginUiInfo(Lv2Host *pPlugins,const Lv2PluginInfo *plugin);
private:
std::string symbol_;
int index_;
std::string name_;
float min_value_ = 0, max_value_ = 1, default_value_ = 0;
int range_steps_ = 0;
int display_priority_ = -1;
bool is_logarithmic_ = false;
bool integer_property_ = false;
bool enumeration_property_ = false;
bool toggled_property_ = false;
bool not_on_gui_ = false;
std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_;
Units units_ = Units::none;
std::string comment_;
bool is_bypass_ = false;
bool is_program_controller_ = false;
std::string custom_units_;
private:
std::string uri_;
std::string name_;
std::string author_name_;
std::string author_homepage_;
PluginType plugin_type_;
std::string plugin_display_type_;
int audio_inputs_ = 0;
int audio_outputs_ = 0;
int has_midi_input_ = false;
int has_midi_output_ = false;
std::string description_;
public:
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index);
LV2_PROPERTY_GETSET(name);
LV2_PROPERTY_GETSET(port_group);
LV2_PROPERTY_GETSET_SCALAR(min_value);
LV2_PROPERTY_GETSET_SCALAR(max_value);
LV2_PROPERTY_GETSET_SCALAR(default_value);
LV2_PROPERTY_GETSET_SCALAR(range_steps);
LV2_PROPERTY_GETSET_SCALAR(display_priority);
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
LV2_PROPERTY_GETSET_SCALAR(integer_property);
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
LV2_PROPERTY_GETSET_SCALAR(is_bypass);
LV2_PROPERTY_GETSET_SCALAR(is_program_controller);
LV2_PROPERTY_GETSET(custom_units);
std::vector<Lv2PluginUiControlPort> controls_;
std::vector<Lv2PluginUiPortGroup> port_groups_;
public:
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
};
public:
LV2_PROPERTY_GETSET(uri)
LV2_PROPERTY_GETSET(name)
LV2_PROPERTY_GETSET(author_name)
LV2_PROPERTY_GETSET(author_homepage)
LV2_PROPERTY_GETSET_SCALAR(plugin_type)
LV2_PROPERTY_GETSET(plugin_display_type)
LV2_PROPERTY_GETSET_SCALAR(audio_inputs)
LV2_PROPERTY_GETSET_SCALAR(audio_outputs)
LV2_PROPERTY_GETSET_SCALAR(has_midi_input)
LV2_PROPERTY_GETSET_SCALAR(has_midi_output)
LV2_PROPERTY_GETSET_SCALAR(description)
LV2_PROPERTY_GETSET(port_groups)
class Lv2PluginUiInfo
{
public:
Lv2PluginUiInfo() {}
Lv2PluginUiInfo(PiPedalHost *pPlugins, const Lv2PluginInfo *plugin);
private:
std::string uri_;
std::string name_;
std::string author_name_;
std::string author_homepage_;
PluginType plugin_type_;
std::string plugin_display_type_;
int audio_inputs_ = 0;
int audio_outputs_ = 0;
int has_midi_input_ = false;
int has_midi_output_ = false;
std::string description_;
bool is_vst3_ = false;
std::vector<Lv2PluginUiControlPort> controls_;
std::vector<Lv2PluginUiPortGroup> port_groups_;
public:
LV2_PROPERTY_GETSET(uri)
LV2_PROPERTY_GETSET(name)
LV2_PROPERTY_GETSET(author_name)
LV2_PROPERTY_GETSET(author_homepage)
LV2_PROPERTY_GETSET_SCALAR(plugin_type)
LV2_PROPERTY_GETSET(plugin_display_type)
LV2_PROPERTY_GETSET_SCALAR(audio_inputs)
LV2_PROPERTY_GETSET_SCALAR(audio_outputs)
LV2_PROPERTY_GETSET_SCALAR(has_midi_input)
LV2_PROPERTY_GETSET_SCALAR(has_midi_output)
LV2_PROPERTY_GETSET_SCALAR(description)
LV2_PROPERTY_GETSET(controls)
LV2_PROPERTY_GETSET(port_groups)
LV2_PROPERTY_GETSET_SCALAR(is_vst3)
static json_map::storage_type<Lv2PluginUiInfo> jmap;
};
static json_map::storage_type<Lv2PluginUiInfo> jmap;
};
class IHost {
public:
@@ -552,7 +572,7 @@ public:
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string&uri) const = 0;
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoard) = 0;
virtual IEffect*CreateEffect(PedalBoardItem &pedalBoard) = 0;
};
@@ -583,8 +603,24 @@ public:
LilvNodePtr&operator=(LilvNode*node) { Free(); this->node = node; return *this;}
};
class Lv2Host: private IHost {
}
#if ENABLE_VST3
#include "vst3/Vst3Host.hpp"
#endif
namespace pipedal{
class PiPedalHost: private IHost {
private:
#if ENABLE_VST3
Vst3Host::Ptr vst3Host;
#endif
static const char *RDFS_COMMENT_URI;
class LilvUris {
public:
@@ -619,6 +655,8 @@ private:
};
bool vst3Enabled = true;
LilvUris lilvUris;
LilvNode *get_comment(const std::string &uri);
@@ -631,6 +669,8 @@ private:
int numberOfAudioOutputChannels = 2;
double sampleRate = 0;
std::string vst3CachePath;
LV2_Feature*const*lv2Features = nullptr;
MapFeature mapFeature;
LogFeature logFeature;
@@ -678,17 +718,20 @@ private:
}
static void PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type);
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoardItem);
virtual IEffect*CreateEffect(PedalBoardItem &pedalBoardItem);
void LoadPluginClassesFromLilv();
void AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass);
public:
Lv2Host();
virtual ~Lv2Host();
PiPedalHost();
void SetConfiguration(const PiPedalConfiguration&configuration);
virtual ~PiPedalHost();
IHost* asIHost() { return this;}
virtual Lv2PedalBoard * CreateLv2PedalBoard(const PedalBoard& pedalBoard);
virtual Lv2PedalBoard * CreateLv2PedalBoard(PedalBoard& pedalBoard);
void setSampleRate(double sampleRate)
{
@@ -719,7 +762,7 @@ public:
void LoadPluginClassesFromJson(std::filesystem::path jsonFile);
void Load(const char*lv2Path = Lv2Host::DEFAULT_LV2_PATH);
void Load(const char*lv2Path = PiPedalHost::DEFAULT_LV2_PATH);
virtual LV2_URID GetLv2Urid(const char*uri) {
return this->mapFeature.GetUrid(uri);
@@ -736,5 +779,5 @@ public:
};
#undef LV2_PROPERTY_GETSET
#undef LV2_PROPERTY_GETSET_SCALAR
} // namespace pipedal.
+86 -24
View File
@@ -43,9 +43,18 @@ T &constMutex(const T &mutex)
return const_cast<T &>(mutex);
}
std::string AtomToJson(int length, uint8_t *pData)
static const char *hexChars = "0123456789ABCDEF";
static std::string BytesToHex(const std::vector<uint8_t> &bytes)
{
return "";
std::stringstream s;
for (size_t i = 0; i < bytes.size(); ++i)
{
uint8_t b = bytes[i];
s << hexChars[(b >> 4) & 0x0F] << hexChars[b & 0x0F];
}
return s.str();
}
PiPedalModel::PiPedalModel()
@@ -119,6 +128,7 @@ PiPedalModel::~PiPedalModel()
void PiPedalModel::Init(const PiPedalConfiguration &configuration)
{
this->configuration = configuration;
lv2Host.SetConfiguration(configuration);
storage.SetConfigRoot(configuration.GetDocRoot());
storage.SetDataRoot(configuration.GetLocalStoragePath());
storage.Initialize();
@@ -282,7 +292,17 @@ void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubsc
void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
{
jackHost->SetControlValue(pedalItemId, symbol, value);
IEffect* effect = lv2PedalBoard->GetEffect(pedalItemId);
if (effect->IsVst3())
{
int index = lv2PedalBoard->GetControlIndex(pedalItemId, symbol);
if (index != -1)
{
effect->SetControl(index,value);;
}
} else {
jackHost->SetControlValue(pedalItemId, symbol, value);
}
}
void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
@@ -291,6 +311,8 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
std::lock_guard<std::recursive_mutex> guard(mutex);
PreviewControl(clientId, pedalItemId, symbol, value);
this->pedalBoard.SetControlValue(pedalItemId, symbol, value);
IEffect *pEffect = this->lv2PedalBoard->GetEffect(pedalItemId);
{
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
@@ -300,9 +322,11 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{
t[i]->OnControlChanged(clientId, pedalItemId, symbol, value);
for (size_t i = 0; i < n; ++i)
{
t[i]->OnControlChanged(clientId, pedalItemId, symbol, value);
}
}
delete[] t;
@@ -379,6 +403,23 @@ void PiPedalModel::SetPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
this->SetPresetChanged(clientId, true);
}
}
void PiPedalModel::UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
// update vst3 presets if neccessary.
// the pedalboard must be a manipualted instance of the current Lv2Pedalboard.
UpdateVst3Settings(pedalBoard);
this->pedalBoard = pedalBoard;
UpdateDefaults(&this->pedalBoard);
this->FirePedalBoardChanged(clientId);
this->SetPresetChanged(clientId, true);
}
}
void PiPedalModel::SetPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
{
@@ -475,10 +516,35 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
}
}
void PiPedalModel::UpdateVst3Settings(PedalBoard &pedalBoard)
{
// get the vst3 state bundle from lv2Pedalboard for the current pedalBoard.
#if ENABLE_VST3
PedalBoard pb;
for (IEffect *effect : lv2PedalBoard->GetEffects())
{
if (effect->IsVst3())
{
PedalBoardItem *item = pedalBoard.GetItem(effect->GetInstanceId());
if (item)
{
Vst3Effect *vst3Effect = (Vst3Effect *)effect;
std::vector<uint8_t> state;
if (vst3Effect->GetState(&state))
{
item->vstState(BytesToHex(state));
}
}
}
}
#endif
}
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
UpdateVst3Settings(this->pedalBoard);
storage.SaveCurrentPreset(this->pedalBoard);
this->SetPresetChanged(clientId, false);
}
@@ -517,6 +583,7 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
std::lock_guard<std::recursive_mutex> guard{mutex};
auto pedalboard = this->pedalBoard;
UpdateVst3Settings(pedalBoard);
pedalboard.name(name);
int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId);
FirePresetsChanged(clientId);
@@ -697,14 +764,13 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
}
}
static std::string GetP2pdName()
{
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
std::string result;
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result))
if (ConfigUtil::GetConfigLine(name, "p2p_device_name", &result))
{
return result;
}
@@ -718,17 +784,15 @@ void PiPedalModel::UpdateDnsSd()
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
std::string p2pdName = GetP2pdName();
if (p2pdName != "") {
if (p2pdName != "")
{
deviceIdFile.deviceName = p2pdName;
}
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
{
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
}
else
{
@@ -820,16 +884,16 @@ void PiPedalModel::RestartAudio()
this->jackHost->Close();
}
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread.
// Still bugs wrt/ restarting the circular buffers for the audio thread.
// for the meantime, just rely on the fact that the admin service will restart
// the process.
//...
// for the meantime, just rely on the fact that the admin service will restart
// the process.
//...
// do a complete reload.
// do a complete reload.
this->jackHost->SetPedalBoard(nullptr);
@@ -839,14 +903,14 @@ void PiPedalModel::RestartAudio()
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
} else {
}
else
{
jackConfiguration.SetErrorStatus("Error");
}
FireJackConfigurationChanged(jackConfiguration);
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{
Lv2Log::error("Audio configuration not valid.");
@@ -1223,12 +1287,11 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
}
delete[] t;
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
FireJackConfigurationChanged(this->jackConfiguration);
guard.unlock();
RestartAudio();
@@ -1280,7 +1343,6 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
#endif
}
});
}
#endif
}
+7 -3
View File
@@ -19,7 +19,7 @@
#pragma once
#include <mutex>
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "GovernorSettings.hpp"
#include "PedalBoard.hpp"
#include "Storage.hpp"
@@ -52,6 +52,7 @@ public:
virtual int64_t GetClientId() = 0;
virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnVst3ControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value, const std::string&state) = 0;
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0;
virtual void OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0;
virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0;
@@ -104,7 +105,7 @@ private:
std::vector<AtomOutputListener> atomOutputListeners;
JackServerSettings jackServerSettings;
Lv2Host lv2Host;
PiPedalHost lv2Host;
PedalBoard pedalBoard;
Storage storage;
bool hasPresetChanged = false;
@@ -155,6 +156,7 @@ private: // IJackHostCallbacks
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson);
void UpdateVst3Settings(PedalBoard&pedalBoard);
PiPedalConfiguration configuration;
public:
@@ -173,7 +175,7 @@ public:
void LoadLv2PluginInfo();
void Load();
const Lv2Host& GetLv2Host() const { return lv2Host; }
const PiPedalHost& GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy()
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -191,6 +193,8 @@ public:
void SetControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void GetPresets(PresetIndex*pResult);
PedalBoard GetPreset(int64_t instanceId);
+40 -9
View File
@@ -277,18 +277,18 @@ JSON_MAP_REFERENCE(PedalBoardItemEnabledBody, instanceId)
JSON_MAP_REFERENCE(PedalBoardItemEnabledBody, enabled)
JSON_MAP_END()
class SetCurrentPedalBoardBody
class UpdateCurrentPedalBoardBody
{
public:
int64_t clientId_ = -1;
PedalBoard pedalBoard_;
DECLARE_JSON_MAP(SetCurrentPedalBoardBody);
DECLARE_JSON_MAP(UpdateCurrentPedalBoardBody);
};
JSON_MAP_BEGIN(SetCurrentPedalBoardBody)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, clientId)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, pedalBoard)
JSON_MAP_BEGIN(UpdateCurrentPedalBoardBody)
JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, clientId)
JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, pedalBoard)
JSON_MAP_END()
class ChannelSelectionChangedBody
@@ -335,6 +335,26 @@ JSON_MAP_REFERENCE(ControlChangedBody, symbol)
JSON_MAP_REFERENCE(ControlChangedBody, value)
JSON_MAP_END()
class Vst3ControlChangedBody
{
public:
int64_t clientId_;
int64_t instanceId_;
std::string symbol_;
float value_;
std::string state_;
DECLARE_JSON_MAP(Vst3ControlChangedBody);
};
JSON_MAP_BEGIN(Vst3ControlChangedBody)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, clientId)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, instanceId)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, symbol)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, value)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, state)
JSON_MAP_END()
class PiPedalSocketHandler : public SocketHandler, public IPiPedalModelSubscriber
{
private:
@@ -940,13 +960,13 @@ public:
pReader->read(&body);
model.SetPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
}
else if (message == "setCurrentPedalBoard")
else if (message == "updateCurrentPedalBoard")
{
{
SetCurrentPedalBoardBody body;
UpdateCurrentPedalBoardBody body;
pReader->read(&body);
this->model.SetPedalBoard(body.clientId_, body.pedalBoard_);
this->model.UpdateCurrentPedalBoard(body.clientId_, body.pedalBoard_);
}
}
else if (message == "currentPedalBoard")
@@ -1377,6 +1397,17 @@ public:
}
}
virtual void OnVst3ControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value, const std::string &state)
{
Vst3ControlChangedBody body;
body.clientId_ = clientId;
body.instanceId_ = instanceId;
body.symbol_ = key;
body.value_ = value;
body.state_ = state;
Send("onVst3ControlChanged", body);
}
virtual void OnControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value)
{
ControlChangedBody body;
@@ -1544,7 +1575,7 @@ public:
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{
SetCurrentPedalBoardBody body;
UpdateCurrentPedalBoardBody body;
body.clientId_ = clientId;
body.pedalBoard_ = pedalBoard;
Send("onPedalBoardChanged", body);
+69
View File
@@ -0,0 +1,69 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "ss.hpp"
#include "Lv2Log.hpp"
namespace pipedal
{
constexpr int RT_THREAD_PRIORITY = 80;
/**
* @brief RAII Priority boost to avoid Rt-thread priority inversion
* Boosts current thread to realtime priority for the duration of the current scope.
* Do this before acquiring a mutex that the RT thread might block on.
*
*/
class RtInversionGuard
{
private:
int currentScheduler;
struct sched_param currentPriority;
;
public:
RtInversionGuard()
{
currentScheduler = sched_getscheduler(0);
sched_getparam(0, &currentPriority);
struct sched_param param;
memset(&param, 0, sizeof(param));
param.sched_priority = RT_THREAD_PRIORITY;
int result = sched_setscheduler(0, SCHED_RR, &param);
if (result != 0)
{
Lv2Log::error(SS("Failed to set ALSA AudioThread priority. (" << strerror(result) << ")"));
}
}
~RtInversionGuard()
{
sched_setscheduler(0, currentScheduler, &currentPriority);
}
};
}
+1 -1
View File
@@ -20,7 +20,7 @@
#include "pch.h"
#include "SplitEffect.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "PedalBoard.hpp"
using namespace pipedal;
+16 -1
View File
@@ -22,6 +22,7 @@
#include "IEffect.hpp"
#include "PiPedalException.hpp"
#include "PiPedalMath.hpp"
#include <assert.h>
namespace pipedal
{
@@ -60,6 +61,18 @@ namespace pipedal
class SplitEffect : public IEffect
{
private:
virtual void SetAudioInputBuffer(int index, float *buffer)
{
assert(false); // not used.
}
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
assert(false); // not used.
}
const double MIX_TRANSITION_TIME_S = 0.1;
double sampleRate;
std::vector<float *> inputs;
@@ -283,6 +296,8 @@ namespace pipedal
virtual std::string AtomToJson(uint8_t *pAtom) { return ""; }
virtual std::string GetAtomObjectType(uint8_t*pData) { return "not implemented";}
virtual bool IsVst3() const { return false; }
public:
SplitEffect(
uint64_t instanceId,
@@ -297,7 +312,7 @@ namespace pipedal
}
virtual long GetInstanceId() const
virtual uint64_t GetInstanceId() const
{
return instanceId;
}
+1
View File
@@ -44,6 +44,7 @@ enum class Units {
pc,
s,
semitone12TET,
custom
};
+885
View File
@@ -0,0 +1,885 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "ss.hpp"
#include <assert.h>
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include "Lv2Log.hpp"
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <filesystem>
#include "RingBufferReader.hpp"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/common/memorystream.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include <array>
#include <sstream>
#include "LiteralVersion.hpp"
#include "Vst3MidiToEvent.hpp"
#include "vst3/Vst3EffectImpl.hpp"
#include "PiPedalHost.hpp"
using namespace pipedal;
std::unique_ptr<Vst3Effect> Vst3Effect::CreateInstance(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost)
{
std::unique_ptr<Vst3EffectImpl> result = std::make_unique<Vst3EffectImpl>();
result->Load(instanceId, info, pHost);
return std::move(result);
}
static double NaNGuard(double value)
{
if (std::isnan(value)) return 0;
if (std::isinf(value)) return 0;
return value;
}
static uint32_t GetRefCount(FUnknown *p)
{
p->addRef();
return p->release();
}
//------------------------------------------------------------------------
// From Vst2Wrapper
static MidiCCMapping initMidiCtrlerAssignment(IComponent *component, IMidiMapping *midiMapping)
{
MidiCCMapping midiCCMapping{};
if (!midiMapping || !component)
return midiCCMapping;
int32 busses = std::min<int32>(component->getBusCount(kEvent, kInput), kMaxMidiMappingBusses);
if (midiCCMapping[0][0].empty())
{
for (int32 b = 0; b < busses; b++)
for (int32 i = 0; i < kMaxMidiChannels; i++)
midiCCMapping[b][i].resize(Vst::kCountCtrlNumber);
}
ParamID paramID;
for (int32 b = 0; b < busses; b++)
{
for (int16 ch = 0; ch < kMaxMidiChannels; ch++)
{
for (int32 i = 0; i < Vst::kCountCtrlNumber; i++)
{
paramID = kNoParamId;
if (midiMapping->getMidiControllerAssignment(b, ch, (CtrlNumber)i, paramID) ==
kResultTrue)
{
// TODO check if tag is associated to a parameter
midiCCMapping[b][ch][i] = paramID;
}
else
midiCCMapping[b][ch][i] = kNoParamId;
}
}
}
return midiCCMapping;
}
//------------------------------------------------------------------------
static void assignBusBuffers(const IAudioClient::Buffers &buffers, HostProcessData &processData,
bool unassign = false)
{
// Set outputs
auto bufferIndex = 0;
for (auto busIndex = 0; busIndex < processData.numOutputs; busIndex++)
{
auto channelCount = processData.outputs[busIndex].numChannels;
for (auto chanIndex = 0; chanIndex < channelCount; chanIndex++)
{
if (bufferIndex < buffers.numOutputs)
{
processData.setChannelBuffer(BusDirections::kOutput, busIndex, chanIndex,
unassign ? nullptr : buffers.outputs[bufferIndex]);
bufferIndex++;
}
}
}
// Set inputs
bufferIndex = 0;
for (auto busIndex = 0; busIndex < processData.numInputs; busIndex++)
{
auto channelCount = processData.inputs[busIndex].numChannels;
for (auto chanIndex = 0; chanIndex < channelCount; chanIndex++)
{
if (bufferIndex < buffers.numInputs)
{
processData.setChannelBuffer(BusDirections::kInput, busIndex, chanIndex,
unassign ? nullptr : buffers.inputs[bufferIndex]);
bufferIndex++;
}
}
}
}
//------------------------------------------------------------------------
static void unassignBusBuffers(const IAudioClient::Buffers &buffers, HostProcessData &processData)
{
assignBusBuffers(buffers, processData, true);
}
//------------------------------------------------------------------------
// Vst3Processor
//------------------------------------------------------------------------
Vst3EffectImpl::Vst3EffectImpl()
{
buffers.inputs = nullptr;
buffers.outputs = nullptr;
componentHandler.setEffect(this);
}
//------------------------------------------------------------------------
Vst3EffectImpl::~Vst3EffectImpl()
{
delete[] buffers.inputs;
delete[] buffers.outputs;
terminate();
}
//------------------------------------------------------------------------
void Vst3EffectImpl::SetControl(int index, float value)
{
this->parameterValues[index] = value;
ParamID paramId = lv2ToVstParam[index];
double normalizedValue = controller->plainParamToNormalized(paramId, value);
this->controller->setParamNormalized(paramId, normalizedValue);
{
RtInversionGuard inversionGuard; // boost priority to prevent priority inversion.
std::lock_guard guard{parameterMutex};
paramTransferrer.addChange(paramId, normalizedValue, 0);
}
if (!isProcessing)
{
Run(0, nullptr); // pump parameter changes on UI thread.
}
}
void Vst3EffectImpl::Load(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost)
{
processContext = {};
processContext.tempo = 120;
this->pHost = pHost;
this->instanceId = instanceId;
this->info = info;
size_t nControls = info.pluginInfo_.controls().size();
inputParameterChanges.setMaxParameters(nControls);
outputParameterChanges.setMaxParameters(nControls);
lv2ToVstParam.resize(nControls);
parameterValues.resize(nControls);
for (size_t i = 0; i < nControls; ++i)
{
const auto &control = info.pluginInfo_.controls()[i];
ParamID paramId;
std::stringstream ss(control.symbol());
ss >> paramId;
assert(i == control.index());
lv2ToVstParam[control.index()] = paramId;
parameterValues[control.index()] = control.default_value();
if (control.is_bypass())
{
bypassControl = i;
}
}
std::string error;
this->module = Module::create(info.filePath_, error);
if (!module)
{
throw Vst3Exception(SS("Vst3 load failed: " << error << "(" << info.filePath_ << ")"));
}
const PluginFactory &factory = module->getFactory();
ClassInfo myClassInfo;
bool foundClassInfo = false;
VST3::Optional<VST3::UID> myUid = VST3::UID::fromString(info.uid_);
if (!myUid)
{
throw Vst3Exception(SS("Invalid UID"));
}
for (const ClassInfo &classInfo : factory.classInfos())
{
if (classInfo.category() == kVstAudioEffectClass &&
classInfo.ID() == *myUid)
{
myClassInfo = classInfo;
foundClassInfo = true;
}
}
if (!foundClassInfo)
{
throw Vst3Exception(SS("Vst3 load failed: effect class not found in " << info.filePath_));
}
plugProvider = owned(NEW PlugProvider(factory, myClassInfo, true));
this->component = plugProvider->getComponent();
this->processor = component;
this->controller = plugProvider->getController();
controller->queryInterface(IMidiMapping::iid, (void **)&midiMapping);
paramTransferrer.setMaxParameters(1000);
if (midiMapping)
midiCCMapping = initMidiCtrlerAssignment(component, midiMapping);
setBlockSize((Steinberg::int32)pHost->GetMaxAudioBufferSize());
setSamplerate((SampleRate)pHost->GetSampleRate());
ProcessSetup setup{kRealtime, kSample32, blockSize, sampleRate};
initProcessData();
if (processor->setupProcessing(setup) != kResultOk)
{
throw Vst3Exception(SS(info.pluginInfo_.name() << ": setupProcessing() failed."));
}
if (component->setActive(true) != kResultOk)
throw Vst3Exception(SS(info.pluginInfo_.name() << ": setActive() failed."));
OPtr<IBStream> state = new MemoryStream();
assert(GetRefCount(state.get()) == 1);
this->supportsState = false;
if (component->getState(state) == kResultOk)
{
this->supportsState = true;
state->seek(0, IBStream::kIBSeekSet);
controller->setComponentState(state);
refreshControlValues();
}
IComponentHandler *handler;
controller->setComponentHandler(&componentHandler);
paramTransferrer.setMaxParameters(1000);
if (midiMapping)
midiCCMapping = initMidiCtrlerAssignment(component, midiMapping);
component->setActive(true);
for (size_t i = 0; i < this->lv2ToVstParam.size(); ++i)
{
fireControlChanged(i, (float)(controller->getParamNormalized(lv2ToVstParam[i])));
}
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// void Vst3PluginImpl::createLocalMediaServer (const Name& name)
// {
// mediaServer = createMediaServer (name);
// mediaServer->registerAudioClient (this);
// mediaServer->registerMidiClient (this);
// }
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void Vst3EffectImpl::terminate()
{
// mediaServer = nullptr;
if (!processor)
return;
processor->setProcessing(false);
component->setActive(false);
}
//------------------------------------------------------------------------
void Vst3EffectImpl::initProcessData()
{
// processData.prepare will be done in setBlockSize
buffers.numInputs = info.pluginInfo_.audio_inputs();
buffers.numOutputs = info.pluginInfo_.audio_outputs();
buffers.numSamples = 0;
buffers.inputs = new float *[buffers.numInputs + 1];
buffers.outputs = new float *[buffers.numOutputs + 1];
for (size_t i = 0; i < buffers.numInputs + 1; ++i)
{
buffers.inputs[i] = nullptr;
}
for (size_t i = 0; i < buffers.numOutputs + 1; ++i)
{
buffers.outputs[i] = nullptr;
}
processData.inputEvents = &eventList;
processData.inputParameterChanges = &inputParameterChanges;
processData.outputParameterChanges = &outputParameterChanges;
processData.processContext = &processContext;
setSamplerate(pHost->GetSampleRate());
setBlockSize((Steinberg::int32)pHost->GetMaxAudioBufferSize());
}
//------------------------------------------------------------------------
IMidiClient::IOSetup Vst3EffectImpl::getMidiIOSetup() const
{
IMidiClient::IOSetup iosetup;
auto count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kEvent, BusDirections::kInput, i, info) != kResultOk)
continue;
auto busName = VST3::StringConvert::convert(info.name, 128);
iosetup.inputs.push_back(busName);
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kEvent, BusDirections::kOutput, i, info) !=
kResultOk)
continue;
auto busName = VST3::StringConvert::convert(info.name, 128);
iosetup.outputs.push_back(busName);
}
return iosetup;
}
//------------------------------------------------------------------------
IAudioClient::IOSetup Vst3EffectImpl::getIOSetup() const
{
IAudioClient::IOSetup iosetup;
auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, info) !=
kResultOk)
continue;
for (int32_t j = 0; j < info.channelCount; j++)
{
auto channelName = VST3::StringConvert::convert(info.name, 128);
iosetup.outputs.push_back(channelName + " " + std::to_string(j));
}
}
count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, info) != kResultOk)
continue;
for (int32_t j = 0; j < info.channelCount; j++)
{
auto channelName = VST3::StringConvert::convert(info.name, 128);
iosetup.inputs.push_back(channelName + " " + std::to_string(j));
}
}
return iosetup;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::preprocess(Buffers &buffers, int64_t continousFrames)
{
processData.numSamples = buffers.numSamples;
processContext.continousTimeSamples = continousFrames;
assignBusBuffers(buffers, processData);
{
std::lock_guard guard{parameterMutex};
paramTransferrer.transferChangesTo(inputParameterChanges);
}
outputParameterChanges.clearQueue();
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::process(Buffers &buffers, int64_t continousFrames)
{
if (!processor || !isProcessing)
return false;
buffers.numSamples = continousFrames;
preprocess(buffers, continousFrames);
if (processor->process(processData) != kResultOk)
return false;
postprocess(buffers);
return true;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::postprocess(Buffers &buffers)
{
eventList.clear();
inputParameterChanges.clearQueue();
unassignBusBuffers(buffers, processData);
}
void Vst3EffectImpl::SendControlChanges(RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
for (auto i = 0; i < outputParameterChanges.getParameterCount(); ++i)
{
IParamValueQueue *queue = outputParameterChanges.getParameterData(i);
auto points = queue->getPointCount();
if (points != 0)
{
Steinberg::Vst::ParamValue value;
Steinberg::int32 sampleOffset;
queue->getPoint(points - 1, sampleOffset, value);
auto paramId = queue->getParameterId();
int thisControlId = -1;
for (size_t lv2Id = 0; lv2Id < this->lv2ToVstParam.size(); ++lv2Id)
{
if (lv2ToVstParam[lv2Id] == paramId)
{
thisControlId = (int)lv2Id;
break;
}
}
if (thisControlId != -1)
{
// realtimeRingBufferWriter->NotifyVst3ControlValue(this->instanceId,thisControlId,(float)value);
}
}
}
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::setSamplerate(SampleRate value)
{
if (sampleRate == value)
return true;
sampleRate = value;
processContext.sampleRate = sampleRate;
if (blockSize == 0)
return true;
return true;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::setBlockSize(int32 value)
{
blockSize = value;
if (sampleRate == 0)
return true;
processData.prepare(*component, blockSize, kSample32);
return true;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
bool Vst3EffectImpl::isPortInRange(int32 port, int32 channel) const
{
return port < kMaxMidiMappingBusses && !midiCCMapping[port][channel].empty();
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::processVstEvent(const IMidiClient::Event &event, int32 port)
{
auto vstEvent = midiToEvent(event.type, event.channel, event.data0, event.data1);
if (vstEvent)
{
vstEvent->busIndex = port;
if (eventList.addEvent(*vstEvent) != kResultOk)
{
assert(false && "Event was not added to EventList!");
}
return true;
}
return false;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::processParamChange(const IMidiClient::Event &event, int32 port)
{
auto paramMapping = [port, this](int32 channel, MidiData data1) -> ParamID
{
if (!isPortInRange(port, channel))
return kNoParamId;
return midiCCMapping[port][channel][data1];
};
auto paramChange =
midiToParameter(event.type, event.channel, event.data0, event.data1, paramMapping);
if (paramChange)
{
int32 index = 0;
IParamValueQueue *queue =
inputParameterChanges.addParameterData((*paramChange).first, index);
if (queue)
{
if (queue->addPoint(event.timestamp, (*paramChange).second, index) != kResultOk)
{
assert(false && "Parameter point was not added to ParamValueQueue!");
}
}
return true;
}
return false;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::onEvent(const IMidiClient::Event &event, int32_t port)
{
// Try to create Event first.
if (processVstEvent(event, port))
return true;
// In case this is no event it must be a parameter.
if (processParamChange(event, port))
return true;
// TODO: Something else???
return true;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::Activate()
{
processor->setProcessing(true); // != kResultOk
this->isProcessing = true;
}
void Vst3EffectImpl::Deactivate()
{
if (isProcessing)
{
Run(0, nullptr); // make sure all pending events have been processed.
isProcessing = false;
}
}
void Vst3EffectImpl::Prepare(int32_t sampleRate, size_t maxBufferSize, int inputChannels, int outputChannels)
{
}
void Vst3EffectImpl::Unprepare()
{
if (isProcessing)
{
Deactivate();
}
}
ssize_t Vst3EffectImpl::ParamIdToLv2Id(ParamID id) const
{
for (size_t i = 0; i < this->lv2ToVstParam.size(); ++i)
{
if (lv2ToVstParam[i] == id)
{
return (ssize_t)i;
}
}
return -1;
}
tresult Vst3EffectImpl::beginEdit(ParamID id)
{
return kResultOk;
}
tresult Vst3EffectImpl::performEdit(ParamID id, ParamValue valueNormalized)
{
int ix = ParamIdToLv2Id(id);
if (ix != -1)
{
fireControlChanged(ix, (float)valueNormalized);
}
return kResultOk;
}
tresult Vst3EffectImpl::endEdit(ParamID id)
{
return kResultOk;
}
void Vst3EffectImpl::transferControllerStateToComponent()
{
OPtr<IRtStream> bStream = this->streamPool.AllocateBStream();
assert(GetRefCount(bStream.get()) == 1);
RtInversionGuard inversionGuard; // boost priority to prevent priority inversion.
std::lock_guard guard{parameterMutex};
paramTransferrer.removeChanges();
// assume that parameters are straightforward and uncomplicated if they
// didn't provide BStream-based state management
// Vst2 used to do this. It's not clear that this is still legal in Vst3.
for (int index = 0; index < this->lv2ToVstParam.size(); ++index)
{
ParamID paramId = lv2ToVstParam[index];
paramTransferrer.addChange(paramId,
controller->plainParamToNormalized(paramId, parameterValues[index]),
0);
}
if (!this->isProcessing)
{
Run(0,nullptr);
}
}
tresult Vst3EffectImpl::restartComponent(int32 flags)
{
if (flags & (RestartFlags::kParamValuesChanged))
{
refreshControlValues();
transferControllerStateToComponent();
}
return kResultOk;
}
static std::vector<uint8_t> StreamToVec(IBStream *stream)
{
Steinberg::int64 length;
stream->seek(0, IBStream::kIBSeekEnd, &length);
stream->seek(0, IBStream::kIBSeekSet);
std::vector<uint8_t> result;
result.resize(length);
Steinberg::int32 numRead = 0;
stream->read(&result[0], length, &numRead);
if (numRead != length)
{
throw Vst3Exception("Failed to read.");
}
return result;
}
void Vst3EffectImpl::CheckSync()
{
OPtr<IBStream> stream{new MemoryStream()};
if (this->controller->getState(stream) == kResultOk)
{
std::vector<uint8_t> initialState = StreamToVec(stream);
OPtr<IBStream> stream2{new MemoryStream()};
this->component->getState(stream2);
stream2->seek(0, IBStream::kIBSeekSet);
this->controller->setState(stream2);
OPtr<IBStream> stream3{new MemoryStream()};
this->controller->getState(stream3);
std::vector<uint8_t> newState = StreamToVec(stream3);
if (initialState.size() != newState.size())
{
throw Vst3Exception("State changed.");
}
for (size_t i = 0; i < newState.size(); ++i)
{
if (initialState[i] != newState[i])
{
throw Vst3Exception("State changed.");
}
}
}
}
bool Vst3EffectImpl::GetState(std::vector<uint8_t>*state)
{
OPtr<IBStream> stream{new MemoryStream()};
if (component->getState(stream) != kResultOk)
{
return false;
}
Steinberg::int64 length = 0;
if (stream->seek(0, IBStream::kIBSeekEnd, &length) != kResultOk)
{
return false;
}
stream->seek(0, IBStream::kIBSeekSet);
std::vector<uint8_t> result;
result.resize(length);
int64 ix = 0;
while (length != 0)
{
int32 thisTime;
if (length > 0x10000000)
{
thisTime = 0x10000000;
}
else
{
thisTime = (int32)length;
}
if (stream->read((void *)(&result[ix]), thisTime, &thisTime) != kResultOk)
{
throw Vst3Exception(this->info.pluginInfo_.name() + ": Failed to read state.");
}
if (thisTime == 0)
{
throw Vst3Exception(this->info.pluginInfo_.name() + ": Unexpected end of file while reading state.");
}
ix += thisTime;
length -= thisTime;
}
*state = std::move(result);
return true;
}
void Vst3EffectImpl::SetState(const std::vector<uint8_t> state)
{
OPtr<IBStream> stream{new MemoryStream((void *)(&state[0]), state.size())};
if (controller->setComponentState(stream) != kResultOk)
{
throw Vst3Exception(this->info.pluginInfo_.name() + " Failed to restore state.");
}
refreshControlValues();
stream->seek(0,IBStream::kIBSeekSet);
if (component->setState(stream) != kResultOk)
{
transferControllerStateToComponent();
}
}
void Vst3EffectImpl::refreshControlValues()
{
for (size_t i = 0; i < lv2ToVstParam.size(); ++i)
{
fireControlChanged(i, controller->getParamNormalized(lv2ToVstParam[i]));
}
}
std::vector<Vst3ProgramList> Vst3EffectImpl::GetProgramList(int32_t programListId)
{
std::vector<Vst3ProgramList> result;
FUnknownPtr<IUnitInfo> iUnitInfo(controller);
if (!iUnitInfo)
throw Vst3Exception("Invalid unit info");
int count = iUnitInfo->getProgramListCount();
for (int i = 0; i < count; ++i)
{
ProgramListInfo programListInfo;
if (iUnitInfo->getProgramListInfo(i, programListInfo) != kResultOk)
{
break;
}
if (programListInfo.id == programListId)
{
Vst3ProgramList list;
list.name = VST3::StringConvert::convert(programListInfo.name);
for (int prog = 0; prog < programListInfo.programCount; ++prog)
{
Vst::String128 progName;
if (iUnitInfo->getProgramName(programListInfo.id, prog, progName) == kResultOk)
{
Vst3ProgramListEntry entry;
entry.id = prog;
entry.name = VST3::StringConvert::convert(progName);
list.programs.push_back(std::move(entry));
}
}
result.push_back(std::move(list));
}
}
return result;
}
void Vst3EffectImpl::fireControlChanged(int control, float normalizedValue)
{
normalizedValue = NaNGuard(normalizedValue);
float plainValue = NaNGuard(this->controller->normalizedParamToPlain(this->lv2ToVstParam[control], normalizedValue));
if (parameterValues[control] != plainValue)
{
parameterValues[control] = plainValue;
controlChangedHandler(control,plainValue);
}
}
JSON_MAP_BEGIN(Vst3PluginInfo)
JSON_MAP_REFERENCE(Vst3PluginInfo, filePath)
JSON_MAP_REFERENCE(Vst3PluginInfo, version)
JSON_MAP_REFERENCE(Vst3PluginInfo, uid)
JSON_MAP_REFERENCE(Vst3PluginInfo, pluginInfo)
JSON_MAP_END()
+689
View File
@@ -0,0 +1,689 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "ss.hpp"
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include "Lv2Log.hpp"
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <filesystem>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include <array>
#include <sstream>
#include "LiteralVersion.hpp"
#include "json.hpp"
using namespace std;
using namespace pipedal;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace pipedal
{
class Vst3HostApplication : public HostApplication
{
public:
Vst3HostApplication() {}
//--- IHostApplication ---------------
tresult PLUGIN_API getName(String128 name)
{
return VST3::StringConvert::convert("PiPedal", name) ? kResultTrue : kInternalError;
}
};
class Vst3HostImpl : public Vst3Host
{
private:
OPtr<Vst3HostApplication> hostApplication;
std::string cacheFilePath;
public:
Vst3HostImpl(const std::string&cacheFilePath);
const Vst3Host::PluginList &RescanPlugins() override;
const Vst3Host::PluginList &RefreshPlugins() override;
const Vst3Host::PluginList &getPluginList() override { return pluginList; }
std::unique_ptr<Vst3Effect> CreatePlugin(long instanceId, const std::string &url, IHost *pHost) override;
std::unique_ptr<Vst3Effect> CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost) override;
private:
Lv2PluginUiInfo *GetPluginInfo(const std::string&uri);
void LoadPluginCache();
void SavePluginCache();
bool havePluginsChanged() const;
PluginList pluginList;
void EnsureContext();
std::unique_ptr<Vst3PluginInfo> MakeDetailedPluginInfo(const PluginFactory &factory, const ClassInfo &classInfo, const std::string &path);
void MakeVst3Info(const std::filesystem::path &path, Vst3Host::PluginList &list);
};
Vst3Host::Ptr Vst3Host::CreateInstance(const std::string&cacheFilePath)
{
return std::make_unique<Vst3HostImpl>(cacheFilePath);
}
//--------------------------------------------------------------
} // namespace pipedal
Vst3HostImpl::Vst3HostImpl(const std::string&cacheFilePath)
:cacheFilePath(cacheFilePath)
{
this->hostApplication = new Vst3HostApplication();
EnsureContext();
}
static bool hasSubCategory(const ClassInfo &classInfo, const std::string &subcategory)
{
for (auto &category : classInfo.subCategories())
{
if (category == subcategory)
{
return true;
}
}
return false;
}
static PluginType getVst3PluginType(const ClassInfo &classInfo)
{
if (hasSubCategory(classInfo, "Instrument"))
{
return PluginType::InstrumentPlugin;
}
if (!hasSubCategory(classInfo, "Fx"))
{
return PluginType::InvalidPlugin;
}
// kFxAnalyzer = "Fx|Analyzer"; ///< Scope, FFT-Display, Loudness Processing...
if (hasSubCategory(classInfo, "Analyzer"))
{
return PluginType::AnalyserPlugin;
}
// kFxDelay = "Fx|Delay"; ///< Delay, Multi-tap Delay, Ping-Pong Delay...
if (hasSubCategory(classInfo, "Delay"))
{
return PluginType::DelayPlugin;
}
// kFxDistortion = "Fx|Distortion"; ///< Amp Simulator, Sub-Harmonic, SoftClipper...
if (hasSubCategory(classInfo, "Distortion"))
{
return PluginType::DistortionPlugin;
}
// kFxDynamics = "Fx|Dynamics"; ///< Compressor, Expander, Gate, Limiter, Maximizer, Tape Simulator, EnvelopeShaper...
if (hasSubCategory(classInfo, "Dynamics"))
{
return PluginType::DynamicsPlugin;
}
// kFxEQ = "Fx|EQ"; ///< Equalization, Graphical EQ...
if (hasSubCategory(classInfo, "EQ"))
{
return PluginType::EQPlugin;
}
// kFxFilter = "Fx|Filter"; ///< WahWah, ToneBooster, Specific Filter,...
if (hasSubCategory(classInfo, "Filter"))
{
return PluginType::FilterPlugin;
}
// kFx = "Fx"; ///< others type (not categorized)
// kFxSpatial = "Fx|Spatial"; ///< MonoToStereo, StereoEnhancer,...
if (hasSubCategory(classInfo, "Spatial"))
{
return PluginType::SpatialPlugin;
}
// kFxGenerator = "Fx|Generator"; ///< Tone Generator, Noise Generator...
if (hasSubCategory(classInfo, "Generator"))
{
return PluginType::GeneratorPlugin;
}
// kFxReverb = "Fx|Reverb"; ///< Reverberation, Room Simulation, Convolution Reverb...
if (hasSubCategory(classInfo, "Reverb"))
{
return PluginType::ReverbPlugin;
}
// kFxPitchShift = "Fx|Pitch Shift"; ///< Pitch Processing, Pitch Correction, Vocal Tuning...
if (hasSubCategory(classInfo, "Pitch Shift"))
{
return PluginType::PitchPlugin;
}
// kFxModulation = "Fx|Modulation"; ///< Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan, Rotary, Cloner...
if (hasSubCategory(classInfo, "Modulation"))
{
return PluginType::ModulatorPlugin;
}
// kFxRestoration = "Fx|Restoration"; ///< Denoiser, Declicker,...
if (hasSubCategory(classInfo, "Restoration"))
{
return PluginType::UtilityPlugin;
}
// kFxSurround = "Fx|Surround"; ///< dedicated to surround processing: LFE Splitter, Bass Manager...
if (hasSubCategory(classInfo, "Surround"))
{
return PluginType::UtilityPlugin;
}
// kFxTools = "Fx|Tools"; ///< Volume, Mixer, Tuner...
if (hasSubCategory(classInfo, "Tools"))
{
return PluginType::UtilityPlugin;
}
// kFxMastering = "Fx|Mastering"; ///< Dither, Noise Shaping,...
if (hasSubCategory(classInfo, "Mastering"))
{
return PluginType::UtilityPlugin;
}
// kFxNetwork = "Fx|Network"; ///< using Network
if (hasSubCategory(classInfo, "Network"))
{
return PluginType::UtilityPlugin;
}
// kFxInstrument = "Fx|Instrument"; ///< Fx which could be loaded as Instrument too
// kFxInstrumentExternal = "Fx|Instrument|External"; ///< Fx which could be loaded as Instrument too and is external (wrapped Hardware)
if (hasSubCategory(classInfo, "Instrument"))
{
return PluginType::InstrumentPlugin;
}
return PluginType::Plugin;
}
void Vst3HostImpl::EnsureContext()
{
PluginContextFactory::instance().setPluginContext(this->hostApplication);
}
unique_ptr<Vst3PluginInfo> Vst3HostImpl::MakeDetailedPluginInfo(const PluginFactory &factory, const ClassInfo &classInfo, const std::string &path)
{
IPtr<PlugProvider> plugProvider = owned(NEW PlugProvider(factory, classInfo, true));
{
OPtr<IComponent> component = plugProvider->getComponent();
OPtr<IEditController> controller = plugProvider->getController();
FUnknownPtr<IMidiMapping> midiMapping(controller);
FUnknownPtr<IUnitInfo> iUnitInfo(controller);
unique_ptr<Vst3PluginInfo> info = make_unique<Vst3PluginInfo>();
info->filePath_ = path;
info->version_ = classInfo.version();
info->uid_ = classInfo.ID().toString();
Lv2PluginUiInfo &pluginInfo = info->pluginInfo_;
pluginInfo.uri(SS("vst3:" << classInfo.ID().toString()));
pluginInfo.is_vst3(true);
pluginInfo.name(classInfo.name());
pluginInfo.author_name(classInfo.vendor());
pluginInfo.plugin_type(
getVst3PluginType(classInfo));
pluginInfo.plugin_display_type(""); // fill this in later. VSTHost has list of name translations; we don;'t.
if (iUnitInfo)
{
auto nUnits = iUnitInfo->getUnitCount();
for (int i = 0; i < nUnits; ++i)
{
Steinberg::Vst::UnitInfo unitInfo;
iUnitInfo->getUnitInfo(i, unitInfo);
Lv2PluginUiPortGroup portGroup(
SS(unitInfo.id), VST3::StringConvert::convert(unitInfo.name), SS(unitInfo.parentUnitId), unitInfo.programListId);
pluginInfo.port_groups().push_back(portGroup);
}
}
auto nParams = controller->getParameterCount();
for (int32 param = 0; param < nParams; ++param)
{
Steinberg::Vst::ParameterInfo info;
tresult tErr = controller->getParameterInfo(param, info);
Lv2PluginUiControlPort port;
port.index(param); // used for LV2 purposes.
port.symbol(SS(info.id)); // Used for VST3 purposes. convert to int to specifiy a vst control id.
std::string shortTitle = VST3::StringConvert::convert(info.shortTitle);
std::string title = VST3::StringConvert::convert(info.title);
if (shortTitle.length() == 0)
{
shortTitle = title;
}
port.name(shortTitle);
port.comment(title);
// guard against NaN value (mda Talkbox)
port.display_priority(param);
bool isList = (info.flags & ParameterInfo::kIsList) != ParameterInfo::kNoFlags;
bool canAutomate = (info.flags & ParameterInfo::kCanAutomate) != ParameterInfo::kNoFlags;
bool isProgramChange = (info.flags & ParameterInfo::kIsProgramChange) != ParameterInfo::kNoFlags;
bool isReadOnly = (info.flags & ParameterInfo::kIsReadOnly) != ParameterInfo::kNoFlags;
bool notOnGui = (info.flags & ParameterInfo::kIsHidden) != ParameterInfo::kNoFlags;
bool isBypass = (info.flags & ParameterInfo::kIsBypass) != ParameterInfo::kNoFlags;
port.is_bypass(isBypass);
port.not_on_gui(isReadOnly | notOnGui);
port.min_value(controller->normalizedParamToPlain(info.id,0));
port.max_value(controller->normalizedParamToPlain(info.id,1));
double t = controller->normalizedParamToPlain(info.id,info.defaultNormalizedValue);
if (std::isnan(t) || std::isinf(t))
{
t = 0;
}
port.default_value(t);
port.range_steps(info.stepCount == 0 ? 0 : info.stepCount + 1);
if (isList && !isProgramChange)
{
port.enumeration_property(true);
for (int i = 0; i <= info.stepCount; ++i)
{
double normalizedValue;
if (info.stepCount == 0)
{
normalizedValue = 0;
}
else
{
normalizedValue = i * 1.0 / (info.stepCount);
}
String128 strValue;
if (controller->getParamStringByValue(info.id, normalizedValue, strValue) == kResultOk)
{
Lv2ScalePoint scalePoint((float)controller->normalizedParamToPlain(info.id,normalizedValue), VST3::StringConvert::convert(strValue));
port.scale_points().push_back(scalePoint);
}
}
}
if (info.unitId != 0)
{
port.port_group(SS(info.unitId));
}
std::string units = VST3::StringConvert::convert(info.units);
if (units.size() != 0)
{
if (units == "dB")
{
port.units(Units::db);
} else if (units == "%")
{
port.units(Units::pc);
} else if (units == "Hz")
{
port.units(Units::hz);
} else if (units == "kHz")
{
port.units(Units::khz);
} else if (units == "sec" || units == "s")
{
port.units(Units::s);
} else if (units == "ms")
{
port.units(Units::ms);
} else if (units == "cent")
{
port.units(Units::cent);
} else {
port.units(Units::custom);
port.custom_units(units);
}
}
pluginInfo.controls().push_back(port);
}
auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo busInfo;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, busInfo) ==
kResultOk)
{
auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
{
pluginInfo.audio_outputs(pluginInfo.audio_outputs() + busInfo.channelCount);
}
}
}
count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo busInfo;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, busInfo) ==
kResultOk)
{
auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
{
pluginInfo.audio_inputs(pluginInfo.audio_inputs() + busInfo.channelCount);
}
}
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
if (count >= 1)
{
pluginInfo.has_midi_input(1);
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
if (count >= 1)
{
pluginInfo.has_midi_output(1);
}
return info;
}
}
void Vst3HostImpl::MakeVst3Info(const std::filesystem::path &path, Vst3Host::PluginList &list)
{
EnsureContext();
unique_ptr<Vst3PluginInfo> result = std::make_unique<Vst3PluginInfo>();
result->filePath_ = path;
string error;
Module::Ptr module = Module::create(path.string(), error);
if (!module)
{
Lv2Log::error(SS("Vst3 load failed: " << error << "(" << path.string() << ")"));
return;
}
const PluginFactory &factory = module->getFactory();
for (const ClassInfo &classInfo : factory.classInfos())
{
if (classInfo.category() == kVstAudioEffectClass)
{
bool found = false;
std::string newUid = classInfo.ID().toString();
for (const auto &existingPlugin : list)
{
if (existingPlugin->uid_ == newUid)
{
found = true;
break; // first instance takes precedence (e.g. ~/.vst3 takes precedence over /usr/lib/vst3 )
}
}
if (!found)
{
std::unique_ptr<Vst3PluginInfo> pluginInfo = MakeDetailedPluginInfo(factory, classInfo, path);
list.push_back(std::move(pluginInfo));
}
}
}
}
bool Vst3HostImpl::havePluginsChanged() const
{
std::unordered_set<std::string> modules;
for (const auto &plugin : pluginList)
{
if (!std::filesystem::exists(plugin->filePath_))
{
return true;
}
modules.insert(plugin->filePath_);
}
Module::PathList pathList = Module::getModulePaths();
for (const auto &path : pathList)
{
if (!modules.contains(path))
{
return true;
}
}
return false;
}
const Vst3Host::PluginList &Vst3HostImpl::RefreshPlugins()
{
if (cacheFilePath.length() != 0)
{
LoadPluginCache();
}
if (havePluginsChanged())
{
RescanPlugins();
}
return pluginList;
}
const Vst3Host::PluginList &Vst3HostImpl::RescanPlugins()
{
Module::PathList pathList = Module::getModulePaths();
PluginList list;
//pathList.insert(pathList.begin(),"/home/pi/.vst3/mda-vst3.vst3");
for (auto &path : pathList)
{
if (path == std::string("/home/pi/.vst3/mda-vst3.vst3"))
{
Lv2Log::debug("Processing MDA plugins");
}
filesystem::path p{path};
MakeVst3Info(p, list);
}
this->pluginList = std::move(list);
SavePluginCache();
return pluginList;
}
static std::string ParseVst3Url(const std::string &url)
{
if (!url.starts_with("vst3:"))
{
throw Vst3Exception("Not a valid vst3 url.: " + url);
}
std::string strUrl = url.substr(5);
return strUrl;
}
std::unique_ptr<Vst3Effect> Vst3HostImpl::CreatePlugin(long instanceId, const std::string &url, IHost *pHost)
{
EnsureContext();
auto uuid = ParseVst3Url(url);
std::unique_ptr<Vst3Effect> result;
for (const auto &plugin : pluginList)
{
if (plugin->uid_ == uuid)
{
return Vst3Effect::CreateInstance(instanceId, *(plugin.get()), pHost);
}
}
throw Vst3Exception("Plugin not found.");
}
static inline uint8_t Hex(char c)
{
if (c >= '0' && c <= '9') return c-'0';
if (c >= 'a' && c <= 'z') return c-'a'+10;
if (c >= 'A' && c <= 'Z') return c-'A'+10;
throw Vst3Exception("Invalid state bundle.");
}
static std::vector<uint8_t> HexToByteArray(const std::string&hexState)
{
std::vector<uint8_t> result;
result.resize(hexState.length()/2);
for (int i = 0; i < result.size(); ++i)
{
result[i] = Hex(hexState[i*2])*16 + Hex(hexState[i*2+1]);
}
return result;
}
std::unique_ptr<Vst3Effect> Vst3HostImpl::CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost)
{
std::unique_ptr<Vst3Effect> result = CreatePlugin(pedalBoardItem.instanceId(),pedalBoardItem.uri(),pHost);
auto pluginInfo = this->GetPluginInfo(pedalBoardItem.uri());
if (!pluginInfo)
{
throw Vst3Exception(SS("Plugin " << pedalBoardItem.pluginName() << " not found."));
}
if (pedalBoardItem.vstState().length() != 0)
{
std::vector<uint8_t> state = HexToByteArray(pedalBoardItem.vstState());
result->SetState(state);
for (ControlValue &controlValue: pedalBoardItem.controlValues())
{
int32_t index = -1;
for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
{
if (pluginInfo->controls()[i].symbol() == controlValue.key())
{
index = (int32_t)(pluginInfo->controls()[i].index());
break;
}
}
if (index != -1)
{
float value = result->GetControlValue(index);
controlValue.value(value);
}
}
} else {
for (const ControlValue &controlValue: pedalBoardItem.controlValues())
{
int32_t index = -1;
for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
{
if (pluginInfo->controls()[i].symbol() == controlValue.key())
{
index = (int32_t)(pluginInfo->controls()[i].index());
break;
}
}
if (index != -1)
{
result->SetControl(index,controlValue.value());
}
}
}
return result;
}
#include <fstream>
void Vst3HostImpl::LoadPluginCache()
{
if (cacheFilePath.length() != 0)
{
std::ifstream f(cacheFilePath);
if (f.is_open())
{
json_reader reader(f);
reader.read(&(this->pluginList));
}
}
}
void Vst3HostImpl::SavePluginCache()
{
if (cacheFilePath.length() != 0)
{
std::ofstream f(cacheFilePath);
if (f.is_open())
{
json_writer writer(f);
writer.write(this->pluginList);
}
}
}
Lv2PluginUiInfo *Vst3HostImpl::GetPluginInfo(const std::string&uri)
{
for (const auto &pluginInfo: pluginList)
{
if (pluginInfo->pluginInfo_.uri() == uri)
{
return &(pluginInfo->pluginInfo_);
}
}
return nullptr;
}
+159
View File
@@ -0,0 +1,159 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/miditovst.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/utility/optional.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include <functional>
#include <limits>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
const uint8_t kNoteOff = 0x80; ///< note, off velocity
const uint8_t kNoteOn = 0x90; ///< note, on velocity
const uint8_t kPolyPressure = 0xA0; ///< note, pressure
const uint8_t kController = 0xB0; ///< controller, value
const uint8_t kProgramChangeStatus = 0xC0; ///< program change
const uint8_t kAfterTouchStatus = 0xD0; ///< channel pressure
const uint8_t kPitchBendStatus = 0xE0; ///< lsb, msb
static const uint32 kDataMask = 0x7F;
const float kMidiScaler = 1.f / 127.f;
using MidiData = uint8_t;
float toNormalized (const MidiData& data)
{
return (float)data * kMidiScaler;
}
using OptionalEvent = VST3::Optional<Event>;
using ParameterChange = std::pair<ParamID, ParamValue>;
using OptionParamChange = VST3::Optional<ParameterChange>;
OptionalEvent midiToEvent (MidiData status, MidiData channel, MidiData midiData0,
MidiData midiData1)
{
Event new_event = {};
if (status == kNoteOn || status == kNoteOff)
{
if (status == kNoteOff) // note off
{
new_event.noteOff.noteId = -1;
new_event.type = Event::kNoteOffEvent;
new_event.noteOff.channel = channel;
new_event.noteOff.pitch = midiData0;
new_event.noteOff.velocity = toNormalized (midiData1);
return std::move (new_event);
}
else if (status == kNoteOn) // note on
{
new_event.noteOn.noteId = -1;
new_event.type = Event::kNoteOnEvent;
new_event.noteOn.channel = channel;
new_event.noteOn.pitch = midiData0;
new_event.noteOn.velocity = toNormalized (midiData1);
return std::move (new_event);
}
}
//--- -----------------------------
else if (status == kPolyPressure)
{
new_event.type = Vst::Event::kPolyPressureEvent;
new_event.polyPressure.channel = channel;
new_event.polyPressure.pitch = midiData0;
new_event.polyPressure.pressure = toNormalized (midiData1);
return std::move (new_event);
}
return {};
}
//------------------------------------------------------------------------
using ToParameterIdFunc = std::function<ParamID (int32, MidiData)>;
OptionParamChange midiToParameter (MidiData status, MidiData channel, MidiData midiData1,
MidiData midiData2, const ToParameterIdFunc& toParamID)
{
if (!toParamID)
return {};
ParameterChange paramChange;
if (status == kController) // controller
{
paramChange.first = toParamID (channel, midiData1);
if (paramChange.first != kNoParamId)
{
paramChange.second = (double)midiData2 * kMidiScaler;
return std::move (paramChange);
}
}
else if (status == kPitchBendStatus)
{
paramChange.first = toParamID (channel, Vst::kPitchBend);
if (paramChange.first != kNoParamId)
{
const double kPitchWheelScaler = 1. / (double)0x3FFF;
const int32 ctrl = (midiData1 & kDataMask) | (midiData2 & kDataMask) << 7;
paramChange.second = kPitchWheelScaler * (double)ctrl;
return std::move (paramChange);
};
}
else if (status == kAfterTouchStatus)
{
paramChange.first = toParamID (channel, Vst::kAfterTouch);
if (paramChange.first != kNoParamId)
{
paramChange.second = (ParamValue) (midiData1 & kDataMask) * kMidiScaler;
return std::move (paramChange);
};
}
else if (status == kProgramChangeStatus)
{
// TODO
}
return {};
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
+86
View File
@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "vst3/Vst3PresetFile.hpp"
#include "public.sdk/source/vst/vstpresetfile.h"
using namespace pipedal;
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace pipedal {
class Vst3PresetFileImpl : public Vst3PresetFile {
public:
virtual ~Vst3PresetFileImpl() { }
virtual void Load(const std::string&path);
virtual std::vector<Vst3PresetInfo> GetPresets();
virtual std::vector<uint8_t> GetPresetChunk(uint32_t index);
private:
std::unique_ptr<PresetFile> presetFile;
};
void Vst3PresetFileImpl::Load(const std::string&path)
{
OPtr<IBStream> stream = FileStream::open(path.c_str(),"r");
presetFile = std::make_unique<PresetFile>(stream);
}
std::vector<Vst3PresetInfo> Vst3PresetFileImpl::GetPresets()
{
std::vector<Vst3PresetInfo> result;
auto count = presetFile->getEntryCount();
result.reserve(count);
for (int i = 0; i < count; ++i)
{
const PresetFile::Entry& entry = presetFile->at(i);
}
return result;
}
std::vector<uint8_t> Vst3PresetFileImpl::GetPresetChunk(uint32_t index)
{
return std::vector<uint8_t>(); // STUB
}
std::unique_ptr<Vst3PresetFile> Vst3PresetFile::CreateInstance()
{
return std::make_unique<Vst3PresetFileImpl>();
}
} // namespace pipedal
+358
View File
@@ -0,0 +1,358 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Classes
// Filename : public.sdk/source/common/memorystream.cpp
// Created by : Steinberg, 03/2008
// Description : IBStream Implementation for memory blocks
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#include "vst3/Vst3RtStream.hpp"
#include "pluginterfaces/base/futils.h"
#include <stdlib.h>
namespace pipedal {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (RtStream, IBStream, IBStream::iid)
static const TSize kMemGrowAmount = 4096;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
RtStream::RtStream(RtStreamPool*pool)
: pool(pool)
, memory (nullptr)
, memorySize (0)
, size (0)
, cursor (0)
, ownMemory (true)
, allocationError (false)
{
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
RtStream::~RtStream ()
{
if (ownMemory && memory)
::free (memory);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::read (void* data, int32 numBytes, int32* numBytesRead)
{
if (memory == nullptr)
{
if (allocationError)
return kOutOfMemory;
numBytes = 0;
}
else
{
// Does read exceed size ?
if (cursor + numBytes > size)
{
int32 maxBytes = int32 (size - cursor);
// Has length become zero or negative ?
if (maxBytes <= 0)
{
cursor = size;
numBytes = 0;
}
else
numBytes = maxBytes;
}
if (numBytes)
{
memcpy (data, &memory[cursor], static_cast<size_t> (numBytes));
cursor += numBytes;
}
}
if (numBytesRead)
*numBytesRead = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
if (allocationError)
return kOutOfMemory;
if (buffer == nullptr)
return kInvalidArgument;
// Does write exceed size ?
TSize requiredSize = cursor + numBytes;
if (requiredSize > size)
{
if (requiredSize > memorySize)
setSize (requiredSize);
else
size = requiredSize;
}
// Copy data
if (memory && cursor >= 0 && numBytes > 0)
{
memcpy (&memory[cursor], buffer, static_cast<size_t> (numBytes));
// Update cursor
cursor += numBytes;
}
else
numBytes = 0;
if (numBytesWritten)
*numBytesWritten = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::seek (int64 pos, int32 mode, int64* result)
{
switch (mode)
{
case kIBSeekSet:
cursor = pos;
break;
case kIBSeekCur:
cursor = cursor + pos;
break;
case kIBSeekEnd:
cursor = size + pos;
break;
}
if (ownMemory == false)
if (cursor > memorySize)
cursor = memorySize;
if (result)
*result = cursor;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::tell (int64* pos)
{
if (!pos)
return kInvalidArgument;
*pos = cursor;
return kResultTrue;
}
//------------------------------------------------------------------------
TSize RtStream::getSize () const
{
return size;
}
//------------------------------------------------------------------------
void RtStream::setSize (TSize s)
{
if (s <= 0)
{
if (ownMemory && memory)
free (memory);
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return;
}
TSize newMemorySize = (((Max (memorySize, s) - 1) / kMemGrowAmount) + 1) * kMemGrowAmount;
if (newMemorySize == memorySize)
{
size = s;
return;
}
if (memory && ownMemory == false)
{
allocationError = true;
return;
}
ownMemory = true;
char* newMemory = nullptr;
if (memory)
{
newMemory = (char*)realloc (memory, (size_t)newMemorySize);
if (newMemory == nullptr && newMemorySize > 0)
{
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory)
{
memcpy (newMemory, memory, (size_t)Min (newMemorySize, memorySize));
free (memory);
}
}
}
else
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory == nullptr)
{
if (newMemorySize > 0)
allocationError = true;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
}
else
{
memory = newMemory;
memorySize = newMemorySize;
size = s;
}
}
//------------------------------------------------------------------------
char* RtStream::getData () const
{
return memory;
}
//------------------------------------------------------------------------
char* RtStream::detachData ()
{
if (ownMemory)
{
char* result = memory;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return result;
}
return nullptr;
}
//------------------------------------------------------------------------
bool RtStream::truncate ()
{
if (ownMemory == false)
return false;
if (memorySize == size)
return true;
memorySize = size;
if (memorySize == 0)
{
if (memory)
{
free (memory);
memory = nullptr;
}
}
else
{
if (memory)
{
char* newMemory = (char*)realloc (memory, (size_t)memorySize);
if (newMemory)
memory = newMemory;
}
}
return true;
}
//------------------------------------------------------------------------
bool RtStream::truncateToCursor ()
{
size = cursor;
return truncate ();
}
}
// namespace
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "vst3/Vst3RtStream.hpp"
using namespace Steinberg;
using namespace pipedal;
void RtStreamPool::ReleaseStreams()
{
RtStream *freeStreamList;
{
freeStreamList = this->freeStreamList;
this->freeStreamList = nullptr;
}
while (freeStreamList)
{
RtStream *next = freeStreamList->next;
freeStreamList->release();
freeStreamList = next;
}
}
+49
View File
@@ -0,0 +1,49 @@
#include <string>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
using namespace std;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
PluginFactory::ClassInfos GetVst3ClassInfos(const std::string& pluginPath)
{
std::string error;
Module::Ptr module = Module::create(pluginPath.c_str(), error);
if (!module)
{
assert(false && "Vst3 load failed.");
}
const PluginFactory &factory = module->getFactory();
return factory.classInfos();
}
static std::string HomePath() { return getenv("HOME"); }
void BugReportTest()
{
auto _ = GetVst3ClassInfos(HomePath() + "/.vst3/adelay.vst3");
PluginFactory::ClassInfos classInfos = GetVst3ClassInfos(HomePath() + "/.vst3/mda-vst3.vst3");
// right size.
assert(classInfos.size() == 68); // succeeds
// wrong classInfo (from adelay.vst3)
assert(classInfos[0].name() == "mda Ambience"); // actual result "ADelay"
}
+149
View File
@@ -0,0 +1,149 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include <iostream>
using namespace pipedal;
using namespace std;
void EnumerateVst3Plugins()
{
cout << "--- Enumeration Test --------" << endl;
Vst3Host::Ptr vst3Host = Vst3Host::CreateInstance();
vst3Host->RescanPlugins();
vst3Host->RefreshPlugins();
}
void CheckSync(IEffect*effect)
{
Vst3Effect *vst3Effect = (Vst3Effect*)effect;
vst3Effect->CheckSync();
}
void TestPrograms(const Vst3PluginInfo &info, Vst3Effect *effect)
{
for (size_t i = 0; i < info.pluginInfo_.port_groups().size(); ++i)
{
const auto &port_group = info.pluginInfo_.port_groups()[i];
if (port_group.program_list_id() != -1)
{
auto programList = effect->GetProgramList(port_group.program_list_id());
for (const auto& programGroup: programList)
{
cout <<" " << port_group.program_list_id() << " " << programGroup.name << " (" << programGroup.programs.size() << " programs)" << std::endl;
// for (const auto&program: programGroup.programs)
// {
// cout << " " << program.id << " " << program.name << endl;
// }
}
}
}
}
void RunVsts()
{
cout << "--- RunVsts ----------------" << endl;
Vst3Host::Ptr vst3Host = Vst3Host::CreateInstance();
cout << "Scanning" << endl;
const auto & plugins = vst3Host->RescanPlugins();
PiPedalHost host;
IHost *pHost = host.asIHost();
host.setSampleRate(44100);
for (const auto & info : plugins)
{
cout << " Running " << info->pluginInfo_.name() << endl;
auto plugin = vst3Host->CreatePlugin(0,info->pluginInfo_.uri(),pHost);
int nInputs = info->pluginInfo_.audio_inputs();
int nOutputs = info->pluginInfo_.audio_outputs();
plugin->Prepare(host.GetSampleRate(),pHost->GetMaxAudioBufferSize(),info->pluginInfo_.audio_inputs(), info->pluginInfo_.audio_outputs());
plugin->Activate();
std::vector<float*> inputs;
std::vector<float*> outputs;
inputs.resize(nInputs);
for (int i = 0; i < nInputs; ++i)
{
inputs[i] = new float[512];
plugin->SetAudioInputBuffer(i,inputs[i]);
for (int j = 0; j < 512; ++j)
{
inputs[i][j] = j / 512.0;
}
}
outputs.resize(nOutputs);
for (int i = 0; i < nOutputs; ++i)
{
outputs[i] = new float[512];
plugin->SetAudioOutputBuffer(i,outputs[i]);
}
plugin->Run(512,nullptr);
for (int i = 0; i < info->pluginInfo_.controls().size(); ++i)
{
float v = plugin->GetControlValue(i);
plugin->SetControl(i,v);
plugin->Run(0,nullptr);
plugin->CheckSync();
}
TestPrograms(*info,plugin.get());
plugin->Deactivate();
}
}
void BugReportTest();
int main(int, char**)
{
BugReportTest();
RunVsts();
EnumerateVst3Plugins();
return 0;
}
+1
View File
@@ -25,6 +25,7 @@ extern "C" {
"detect_leaks=0" // undesirable behavior on abnormal termination.
":alloc_dealloc_mismatch=0" // Guitarix components trigger this. It's not actually a problem on GCC.
":new_delete_type_mismatch=0" //GxTuner
":detect_odr_violation=0"
":print_stacktrace=1"
":halt_on_error=1"
;
+1 -1
View File
@@ -27,7 +27,7 @@
#include "AvahiService.hpp"
#include "PiPedalSocket.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include <boost/system/error_code.hpp>
#include <filesystem>
#include "PiPedalConfiguration.hpp"
+83
View File
@@ -0,0 +1,83 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "PiPedalHost.hpp"
#include <functional>
#include "json.hpp"
namespace pipedal {
struct Vst3PluginInfo {
public:
std::string filePath_;
std::string version_;
std::string uid_;
Lv2PluginUiInfo pluginInfo_;
DECLARE_JSON_MAP(Vst3PluginInfo);
};
struct Vst3ProgramListEntry {
int32_t id;
std::string name;
};
struct Vst3ProgramList {
std::string name;
std::vector<Vst3ProgramListEntry> programs;
};
class Vst3Effect: public IEffect {
public:
using Ptr = std::unique_ptr<Vst3Effect>;
virtual void Prepare(int32_t sampleRate, size_t maxBufferSize,int inputChannels, int outputChannels) = 0;
virtual void Activate() = 0;
virtual void Deactivate() = 0;
virtual void Unprepare() = 0;
virtual void CheckSync() = 0; // throw if audioProcessor and controller are not sync (test only)
virtual bool SupportsState() const = 0;
virtual bool GetState(std::vector<uint8_t> *state) = 0;
virtual void SetState(const std::vector<uint8_t> state) = 0;
using ControlChangedHandler = std::function<void(int control,float value)>;
virtual void SetControlChangedHandler(const ControlChangedHandler& handler) = 0;
virtual void RemoveControlChangedHandler() = 0;
virtual std::vector<Vst3ProgramList> GetProgramList(int32_t programListId) = 0;
public:
static std::unique_ptr<Vst3Effect> CreateInstance(uint64_t instanceId,const Vst3PluginInfo& info, IHost *pHost);
};
}
+286
View File
@@ -0,0 +1,286 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "Vst3Effect.hpp"
#include <vector>
#include "RtInversionGuard.hpp"
#include "Vst3RtStream.hpp"
#pragma once
namespace pipedal
{
using namespace std;
using namespace pipedal;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
enum
{
kMaxMidiMappingBusses = 4,
kMaxMidiChannels = 16
};
using Controllers = std::vector<int32>;
using Channels = std::array<Controllers, kMaxMidiChannels>;
using Busses = std::array<Channels, kMaxMidiMappingBusses>;
using MidiCCMapping = Busses;
class Vst3EffectImpl : public Vst3Effect,
public IAudioClient,
public IMidiClient
{
public:
//--------------------------------------------------------------------
using Name = std::string;
Vst3EffectImpl();
virtual ~Vst3EffectImpl();
void Load(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost);
// IEffect
virtual bool IsVst3() const { return true; }
virtual uint64_t GetInstanceId() const { return instanceId; }
virtual int GetControlIndex(const std::string &symbol) const
{
for (size_t i = 0; i < info.pluginInfo_.controls().size(); ++i)
{
if (info.pluginInfo_.controls()[i].symbol() == symbol)
{
return (int)i;
}
}
return -1;
}
bool SupportsState() const { return supportsState; }
bool GetState(std::vector<uint8_t> *state);
void SetState(const std::vector<uint8_t> state);
virtual void CheckSync();
//- PiPedalHost Interfaces.
virtual void SetControl(int index, float value);
virtual float GetControlValue(int index) const
{
return this->parameterValues[index];
}
virtual std::vector<Vst3ProgramList> GetProgramList(int32_t programListId);
int bypassControl = -1;
bool bypassEnable = false;
virtual void SetBypass(bool enable)
{
if (bypassControl != -1)
{
SetControl(bypassControl, enable ? 1 : 0);
}
else
{
bypassEnable = enable;
}
}
virtual void Activate();
virtual void Deactivate();
virtual float GetOutputControlValue(int controlIndex) const
{
return this->parameterValues[controlIndex];
}
virtual int GetNumberOfInputAudioPorts() const
{
return info.pluginInfo_.audio_inputs();
}
virtual int GetNumberOfOutputAudioPorts() const
{
return info.pluginInfo_.audio_outputs();
}
virtual float *GetAudioInputBuffer(int index) const { return buffers.inputs[index]; }
virtual float *GetAudioOutputBuffer(int index) const { return buffers.outputs[index]; }
virtual void ResetAtomBuffers() {}
virtual void RequestParameter(LV2_URID uridUri) {} // no vst equivalent.
virtual void GatherParameter(RealtimeParameterRequest *pRequest) {} // no vst equivalent.
virtual void SetAudioInputBuffer(int index, float *buffer)
{
buffers.inputs[index] = buffer;
}
virtual void SetAudioOutputBuffer(int index, float *buffer)
{
buffers.outputs[index] = buffer;
}
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
process(buffers, samples);
SendControlChanges(realtimeRingBufferWriter);
}
// Vst3Effect interfaces.
virtual void Prepare(int32_t sampleRate, size_t maxBufferSize, int inputChannels, int outputChannels);
virtual void Unprepare();
// IAudioClient
bool process(Buffers &buffers, int64_t continousFrames) override;
bool setSamplerate(SampleRate value) override;
bool setBlockSize(int32 value) override;
IAudioClient::IOSetup getIOSetup() const override;
// IMidiClient
bool onEvent(const Event &event, int32_t port) override;
IMidiClient::IOSetup getMidiIOSetup() const override;
// IParameterClient
//--------------------------------------------------------------------
private:
bool supportsState = false;
IHost *pHost;
Module::Ptr module;
IPtr<IMidiMapping> midiMapping;
IPtr<PlugProvider> plugProvider;
RtStreamPool streamPool;
void SendControlChanges(RealtimeRingBufferWriter *realtimeRingBufferWriter);
//--------------------------------------------------------------------
private:
std::mutex parameterMutex;
Buffers buffers;
std::vector<ParamID> lv2ToVstParam;
std::vector<float> parameterValues;
// void createLocalMediaServer(const Name &name);
uint64_t instanceId;
Vst3PluginInfo info;
void terminate();
void initProcessData();
void updateBusBuffers(Buffers &buffers, HostProcessData &processData);
void preprocess(Buffers &buffers, int64_t continousFrames);
void postprocess(Buffers &buffers);
bool isPortInRange(int32 port, int32 channel) const;
bool processVstEvent(const IMidiClient::Event &event, int32 port);
bool processParamChange(const IMidiClient::Event &event, int32 port);
SampleRate sampleRate = 0;
int32 blockSize = 0;
HostProcessData processData;
ProcessContext processContext;
EventList eventList;
ParameterChanges inputParameterChanges;
ParameterChanges outputParameterChanges;
IComponent *component = nullptr;
IEditController *controller = nullptr;
FUnknownPtr<IAudioProcessor> processor;
ParameterChangeTransfer paramTransferrer;
MidiCCMapping midiCCMapping;
// IMediaServerPtr mediaServer;
bool isProcessing = false;
Name name;
private:
ssize_t ParamIdToLv2Id(ParamID id) const;
class ComponentHandler : public IComponentHandler
{
private:
Vst3EffectImpl *pEffect = nullptr;
public:
ComponentHandler() { }
void setEffect(Vst3EffectImpl*effect)
{
pEffect = effect;
}
tresult PLUGIN_API beginEdit(ParamID id) override
{
return pEffect->beginEdit(id);
}
tresult PLUGIN_API performEdit(ParamID id, ParamValue valueNormalized) override
{
return pEffect->performEdit(id, valueNormalized);
}
tresult PLUGIN_API endEdit(ParamID id) override
{
return pEffect->endEdit(id);
}
tresult PLUGIN_API restartComponent(int32 flags) override
{
return pEffect->restartComponent(flags);
}
private:
tresult PLUGIN_API queryInterface(const TUID _iid, void ** obj) override
{
if (_iid == IComponentHandler_iid)
{
addRef();
*((FUnknown**)obj) = this;
return kResultOk;
}
return kNoInterface;
}
uint32 PLUGIN_API addRef() override { return 1000; }
uint32 PLUGIN_API release() override { return 1000; }
};
ComponentHandler componentHandler;
void transferControllerStateToComponent();
void refreshControlValues();
virtual tresult beginEdit(ParamID id);
virtual tresult performEdit(ParamID id, ParamValue valueNormalized);
virtual tresult endEdit(ParamID id);
virtual tresult restartComponent(int32 flags);
private:
ControlChangedHandler controlChangedHandler = [] (int control,float value) {};
void fireControlChanged(int control, float normalizedValue);
public:
virtual void SetControlChangedHandler(const ControlChangedHandler& handler)
{
controlChangedHandler = handler;
}
virtual void RemoveControlChangedHandler() {
controlChangedHandler = [] (int,float){};
}
};
}
+70
View File
@@ -0,0 +1,70 @@
/*
/* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <vector>
#include <stdint.h>
#include <string>
#include <exception>
#include <stdexcept>
#include "public.sdk/source/vst/utility/uid.h"
#include "vst3/Vst3Effect.hpp"
#include "PedalBoard.hpp"
namespace pipedal {
class Vst3Exception: public std::logic_error {
public:
Vst3Exception(const std::string&error)
:logic_error(error)
{
}
};
class Vst3Host {
public:
using Ptr = std::unique_ptr<Vst3Host>;
using PluginList = std::vector<std::unique_ptr<Vst3PluginInfo>>;
virtual ~Vst3Host() {}
virtual const PluginList &RescanPlugins() = 0;
virtual const PluginList &RefreshPlugins() = 0;
virtual const PluginList &getPluginList() = 0;
virtual std::unique_ptr<Vst3Effect> CreatePlugin(long instanceId,const std::string&url, IHost*pHost) = 0;
virtual std::unique_ptr<Vst3Effect> CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost) = 0;
static Ptr CreateInstance(const std::string &cacheFilePath = "");
};
};
+53
View File
@@ -0,0 +1,53 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <cstddef>
#include <string>
#include <vector>
#include <memory>
namespace pipedal {
struct Vst3PresetInfo {
uint32_t index;
std::string name;
};
class Vst3PresetFile {
protected:
Vst3PresetFile() { }
public:
virtual ~Vst3PresetFile() { }
virtual void Load(const std::string&path) = 0;
virtual std::vector<Vst3PresetInfo> GetPresets() = 0;
virtual std::vector<uint8_t> GetPresetChunk(uint32_t index) = 0;
std::unique_ptr<Vst3PresetFile> CreateInstance();
};
}
+109
View File
@@ -0,0 +1,109 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* 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
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* 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 AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "public.sdk/source/common/memorystream.h"
#include <mutex>
#include "Vst3RtStream.hpp"
namespace pipedal
{
using namespace Steinberg;
class IRtStream : public IBStream
{
public:
virtual void QueueForRelease() = 0;
};
class RtStreamPool;
class RtStream : public IRtStream
{
friend class RtStreamPool;
virtual void PLUGIN_API QueueForRelease();
RtStream(RtStreamPool *pool);
public:
virtual ~RtStream();
//---IBStream---------------------------------------
tresult PLUGIN_API read(void *buffer, int32 numBytes, int32 *numBytesRead) SMTG_OVERRIDE;
tresult PLUGIN_API write(void *buffer, int32 numBytes, int32 *numBytesWritten) SMTG_OVERRIDE;
tresult PLUGIN_API seek(int64 pos, int32 mode, int64 *result) SMTG_OVERRIDE;
tresult PLUGIN_API tell(int64 *pos) SMTG_OVERRIDE;
TSize getSize() const; ///< returns the current memory size
void setSize(TSize size); ///< set the memory size, a realloc will occur if memory already used
char *getData() const; ///< returns the memory pointer
char *detachData(); ///< returns the memory pointer and give up ownership
bool truncate(); ///< realloc to the current use memory size if needed
bool truncateToCursor(); ///< truncate memory at current cursor position
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
RtStreamPool *pool = nullptr;
RtStream *next = nullptr;
char *memory; // memory block
TSize memorySize; // size of the memory block
TSize size; // size of the stream
int64 cursor; // stream pointer
bool ownMemory; // stream has allocated memory itself
bool allocationError; // stream invalid
};
class RtStreamPool
{
public:
~RtStreamPool()
{
ReleaseStreams();
}
IRtStream *AllocateBStream()
{
ReleaseStreams();
return new RtStream(this);
}
void ReleaseStreams();
private:
friend class RtStream;
RtStream *freeStreamList = nullptr;
void QueueForRelease(RtStream *pStream)
{
pStream->next = freeStreamList;
freeStreamList = pStream;
}
};
inline void RtStream::QueueForRelease()
{
pool->QueueForRelease(this);
}
};