#include "rack_vst3.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstcomponent.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstprocesscontext.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/vst/ivsthostapplication.h"
#include <vector>
#include <string>
#include <cstring>
#include <mutex>
#include <algorithm>
using namespace VST3;
using namespace Steinberg;
using namespace Steinberg::Vst;
static std::mutex g_vst3_lifecycle_mutex;
static std::string utf16_to_utf8(const char16* utf16_str) {
if (!utf16_str) {
return "";
}
std::string result;
result.reserve(128);
const size_t MAX_STRING_LENGTH = 4096;
size_t processed = 0;
while (*utf16_str && processed < MAX_STRING_LENGTH) {
char16 c = *utf16_str++;
processed++;
if (c < 0x80) {
result.push_back(static_cast<char>(c));
}
else if (c < 0x800) {
result.push_back(static_cast<char>(0xC0 | (c >> 6)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
}
else if (c >= 0xD800 && c <= 0xDBFF) {
if (*utf16_str == 0) {
result.append("\xEF\xBF\xBD"); break;
}
char16 low = *utf16_str;
if (low >= 0xDC00 && low <= 0xDFFF) {
utf16_str++;
processed++;
uint32_t high_bits = (c & 0x3FF); uint32_t low_bits = (low & 0x3FF); uint32_t codepoint = 0x10000 + (high_bits << 10) + low_bits;
if (codepoint > 0x10FFFF) {
result.append("\xEF\xBF\xBD"); } else {
result.push_back(static_cast<char>(0xF0 | (codepoint >> 18)));
result.push_back(static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F)));
result.push_back(static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (codepoint & 0x3F)));
}
} else {
result.append("\xEF\xBF\xBD"); }
}
else if (c >= 0xDC00 && c <= 0xDFFF) {
result.append("\xEF\xBF\xBD"); }
else {
result.push_back(static_cast<char>(0xE0 | (c >> 12)));
result.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
result.push_back(static_cast<char>(0x80 | (c & 0x3F)));
}
}
return result;
}
static bool string_to_uid(const char* str, VST3::UID& uid) {
if (!str) {
return false;
}
auto uid_opt = VST3::UID::fromString(std::string(str));
if (!uid_opt) {
return false;
}
uid = *uid_opt;
return true;
}
class MemoryStream : public IBStream {
public:
MemoryStream() : ref_count_(1), position_(0) {}
MemoryStream(const uint8_t* data, size_t size) : ref_count_(1), position_(0) {
buffer_.assign(data, data + size);
}
virtual ~MemoryStream() = default;
DECLARE_FUNKNOWN_METHODS
tresult PLUGIN_API read(void* buffer, int32 numBytes, int32* numBytesRead) override {
if (!buffer || numBytes < 0) {
return kInvalidArgument;
}
int32 available = static_cast<int32>(buffer_.size()) - position_;
int32 to_read = std::min(numBytes, available);
if (to_read > 0) {
memcpy(buffer, buffer_.data() + position_, to_read);
position_ += to_read;
}
if (numBytesRead) {
*numBytesRead = to_read;
}
return to_read == numBytes ? kResultOk : kResultFalse;
}
tresult PLUGIN_API write(void* buffer, int32 numBytes, int32* numBytesWritten) override {
if (!buffer || numBytes < 0) {
return kInvalidArgument;
}
if (position_ + numBytes > static_cast<int32>(buffer_.size())) {
buffer_.resize(position_ + numBytes);
}
memcpy(buffer_.data() + position_, buffer, numBytes);
position_ += numBytes;
if (numBytesWritten) {
*numBytesWritten = numBytes;
}
return kResultOk;
}
tresult PLUGIN_API seek(int64 pos, int32 mode, int64* result) override {
int64 new_position = position_;
switch (mode) {
case kIBSeekSet:
new_position = pos;
break;
case kIBSeekCur:
new_position = static_cast<int64>(position_) + pos;
break;
case kIBSeekEnd:
new_position = static_cast<int64>(buffer_.size()) + pos;
break;
default:
return kInvalidArgument;
}
if (new_position < 0) {
new_position = 0;
}
int64 buffer_size = static_cast<int64>(buffer_.size());
if (new_position > buffer_size) {
new_position = buffer_size;
}
position_ = static_cast<int32>(new_position);
if (result) {
*result = position_;
}
return kResultOk;
}
tresult PLUGIN_API tell(int64* pos) override {
if (!pos) {
return kInvalidArgument;
}
*pos = position_;
return kResultOk;
}
const std::vector<uint8_t>& getData() const { return buffer_; }
size_t getSize() const { return buffer_.size(); }
void clear() { buffer_.clear(); position_ = 0; }
private:
uint32 ref_count_; std::vector<uint8_t> buffer_;
int32 position_;
};
IMPLEMENT_REFCOUNT(MemoryStream)
tresult PLUGIN_API MemoryStream::queryInterface(const TUID _iid, void** obj) {
QUERY_INTERFACE(_iid, obj, FUnknown::iid, IBStream)
QUERY_INTERFACE(_iid, obj, IBStream::iid, IBStream)
*obj = nullptr;
return kNoInterface;
}
struct RackVST3Plugin {
Hosting::Module::Ptr module;
IPtr<IComponent> component;
IPtr<IAudioProcessor> processor;
IPtr<IEditController> controller;
IPtr<IConnectionPoint> component_cp;
IPtr<IConnectionPoint> controller_cp;
std::string path;
VST3::UID uid;
double sample_rate = 0.0;
uint32_t max_block_size = 0;
bool initialized = false;
int32 num_input_channels = 0;
int32 num_output_channels = 0;
HostProcessData process_data;
ParameterChanges input_param_changes;
ParameterChanges output_param_changes;
EventList input_events;
EventList output_events;
std::vector<float*> input_ptrs;
std::vector<float*> output_ptrs;
struct ParameterInfo {
ParamID id;
std::string title;
std::string units;
ParamValue min_value;
ParamValue max_value;
ParamValue default_value;
};
std::vector<ParameterInfo> parameters;
struct PresetInfo {
int32 program_list_id;
int32 program_index;
std::string name;
};
std::vector<PresetInfo> presets;
};
RackVST3Plugin* rack_vst3_plugin_new(const char* path, const char* uid) {
if (!path || !uid) {
return nullptr;
}
std::lock_guard<std::mutex> lock(g_vst3_lifecycle_mutex);
auto plugin = new(std::nothrow) RackVST3Plugin();
if (!plugin) {
return nullptr;
}
plugin->path = path;
if (!string_to_uid(uid, plugin->uid)) {
delete plugin;
return nullptr;
}
std::string error_description;
plugin->module = Hosting::Module::create(path, error_description);
if (!plugin->module) {
delete plugin;
return nullptr;
}
const auto& factory = plugin->module->getFactory();
plugin->component = factory.createInstance<IComponent>(plugin->uid);
if (!plugin->component) {
delete plugin;
return nullptr;
}
plugin->processor = U::cast<IAudioProcessor>(plugin->component);
if (!plugin->processor) {
delete plugin;
return nullptr;
}
if (plugin->component->initialize(FUnknownPtr<IHostApplication>(new HostApplication())) != kResultOk) {
plugin->component = nullptr;
plugin->processor = nullptr;
plugin->module = nullptr;
delete plugin;
return nullptr;
}
TUID controllerCID;
if (plugin->component->getControllerClassId(controllerCID) == kResultTrue) {
VST3::UID controllerUID = VST3::UID::fromTUID(controllerCID);
plugin->controller = factory.createInstance<IEditController>(controllerUID);
if (plugin->controller) {
if (plugin->controller->initialize(FUnknownPtr<IHostApplication>(new HostApplication())) != kResultOk) {
plugin->component->terminate();
plugin->controller = nullptr;
plugin->component = nullptr;
plugin->processor = nullptr;
plugin->module = nullptr;
delete plugin;
return nullptr;
}
}
} else {
plugin->controller = U::cast<IEditController>(plugin->component);
}
if (plugin->controller && reinterpret_cast<void*>(plugin->controller.get()) != reinterpret_cast<void*>(plugin->component.get())) {
plugin->component_cp = U::cast<IConnectionPoint>(plugin->component);
plugin->controller_cp = U::cast<IConnectionPoint>(plugin->controller);
if (plugin->component_cp && plugin->controller_cp) {
plugin->component_cp->connect(plugin->controller_cp);
plugin->controller_cp->connect(plugin->component_cp);
}
}
return plugin;
}
void rack_vst3_plugin_free(RackVST3Plugin* plugin) {
if (!plugin) {
return;
}
std::lock_guard<std::mutex> lock(g_vst3_lifecycle_mutex);
if (plugin->initialized && plugin->component) {
plugin->component->setActive(false);
}
if (plugin->component_cp && plugin->controller_cp) {
plugin->component_cp->disconnect(plugin->controller_cp);
plugin->controller_cp->disconnect(plugin->component_cp);
}
if (plugin->controller && reinterpret_cast<void*>(plugin->controller.get()) != reinterpret_cast<void*>(plugin->component.get())) {
plugin->controller->terminate();
plugin->controller = nullptr;
}
if (plugin->component) {
plugin->component->terminate();
plugin->component = nullptr;
}
plugin->processor = nullptr;
plugin->module = nullptr;
delete plugin;
}
int rack_vst3_plugin_initialize(RackVST3Plugin* plugin, double sample_rate, uint32_t max_block_size) {
if (!plugin || !plugin->component || !plugin->processor) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
std::lock_guard<std::mutex> lock(g_vst3_lifecycle_mutex);
plugin->sample_rate = sample_rate;
plugin->max_block_size = max_block_size;
ProcessSetup setup;
setup.processMode = kRealtime;
setup.symbolicSampleSize = kSample32;
setup.maxSamplesPerBlock = max_block_size;
setup.sampleRate = sample_rate;
if (plugin->processor->setupProcessing(setup) != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
int32 numInputBuses = plugin->component->getBusCount(kAudio, kInput);
int32 numOutputBuses = plugin->component->getBusCount(kAudio, kOutput);
if (numInputBuses > 0) {
plugin->component->activateBus(kAudio, kInput, 0, true);
BusInfo busInfo;
if (plugin->component->getBusInfo(kAudio, kInput, 0, busInfo) == kResultOk) {
plugin->num_input_channels = busInfo.channelCount;
}
}
if (numOutputBuses > 0) {
plugin->component->activateBus(kAudio, kOutput, 0, true);
BusInfo busInfo;
if (plugin->component->getBusInfo(kAudio, kOutput, 0, busInfo) == kResultOk) {
plugin->num_output_channels = busInfo.channelCount;
}
}
if (plugin->component->setActive(true) != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
if (plugin->processor->setProcessing(true) != kResultOk) {
plugin->component->setActive(false);
return RACK_VST3_ERROR_GENERIC;
}
plugin->process_data.prepare(*plugin->component, max_block_size, kSample32);
if (plugin->controller) {
int32 param_count = plugin->controller->getParameterCount();
plugin->parameters.clear();
plugin->parameters.reserve(param_count);
for (int32 i = 0; i < param_count; ++i) {
ParameterInfo vst3_param_info;
if (plugin->controller->getParameterInfo(i, vst3_param_info) == kResultOk) {
RackVST3Plugin::ParameterInfo info;
info.id = vst3_param_info.id;
info.title = utf16_to_utf8(vst3_param_info.title);
info.units = utf16_to_utf8(vst3_param_info.units);
info.min_value = 0.0;
info.max_value = 1.0;
info.default_value = vst3_param_info.defaultNormalizedValue;
plugin->parameters.push_back(info);
}
}
}
IPtr<IUnitInfo> unit_info = U::cast<IUnitInfo>(plugin->controller);
if (unit_info) {
int32 program_list_count = unit_info->getProgramListCount();
for (int32 i = 0; i < program_list_count; ++i) {
ProgramListInfo list_info;
if (unit_info->getProgramListInfo(i, list_info) == kResultOk) {
for (int32 j = 0; j < list_info.programCount; ++j) {
String128 program_name;
if (unit_info->getProgramName(list_info.id, j, program_name) == kResultOk) {
RackVST3Plugin::PresetInfo preset;
preset.program_list_id = list_info.id;
preset.program_index = j;
preset.name = utf16_to_utf8(program_name);
plugin->presets.push_back(preset);
}
}
}
}
}
plugin->initialized = true;
return RACK_VST3_OK;
}
int rack_vst3_plugin_is_initialized(RackVST3Plugin* plugin) {
return (plugin && plugin->initialized) ? 1 : 0;
}
int rack_vst3_plugin_reset(RackVST3Plugin* plugin) {
if (!plugin) {
return RACK_VST3_ERROR_NOT_INITIALIZED;
}
std::lock_guard<std::mutex> lock(g_vst3_lifecycle_mutex);
if (!plugin->initialized || !plugin->component) {
return RACK_VST3_ERROR_NOT_INITIALIZED;
}
plugin->component->setActive(false);
if (plugin->component->setActive(true) != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
return RACK_VST3_OK;
}
int rack_vst3_plugin_get_input_channels(RackVST3Plugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return plugin->num_input_channels;
}
int rack_vst3_plugin_get_output_channels(RackVST3Plugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return plugin->num_output_channels;
}
int rack_vst3_plugin_process(
RackVST3Plugin* plugin,
const float* const* inputs,
uint32_t num_input_channels,
float* const* outputs,
uint32_t num_output_channels,
uint32_t frames)
{
if (!plugin || !plugin->initialized || !plugin->processor) {
return RACK_VST3_ERROR_NOT_INITIALIZED;
}
if (num_input_channels != static_cast<uint32_t>(plugin->num_input_channels)) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (num_output_channels != static_cast<uint32_t>(plugin->num_output_channels)) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (frames > plugin->max_block_size) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (num_input_channels > 0 && !inputs) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (num_output_channels > 0 && !outputs) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
plugin->process_data.numSamples = frames;
if (num_input_channels > 0) {
AudioBusBuffers& bus = plugin->process_data.inputs[0];
bus.numChannels = num_input_channels;
bus.channelBuffers32 = const_cast<float**>(inputs);
}
if (num_output_channels > 0) {
AudioBusBuffers& bus = plugin->process_data.outputs[0];
bus.numChannels = num_output_channels;
bus.channelBuffers32 = const_cast<float**>(outputs);
}
plugin->process_data.inputParameterChanges = &plugin->input_param_changes;
plugin->process_data.outputParameterChanges = &plugin->output_param_changes;
plugin->process_data.inputEvents = &plugin->input_events;
plugin->process_data.outputEvents = &plugin->output_events;
tresult result = plugin->processor->process(plugin->process_data);
plugin->input_events.clear();
plugin->input_param_changes.clearQueue();
plugin->output_events.clear();
plugin->output_param_changes.clearQueue();
return (result == kResultOk) ? RACK_VST3_OK : RACK_VST3_ERROR_GENERIC;
}
int rack_vst3_plugin_parameter_count(RackVST3Plugin* plugin) {
if (!plugin || !plugin->controller) {
return 0;
}
return static_cast<int>(plugin->parameters.size());
}
int rack_vst3_plugin_get_parameter(RackVST3Plugin* plugin, uint32_t index, float* value) {
if (!plugin || !plugin->controller || !value) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (index >= plugin->parameters.size()) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
ParamID param_id = plugin->parameters[index].id;
ParamValue normalized = plugin->controller->getParamNormalized(param_id);
*value = static_cast<float>(normalized);
return RACK_VST3_OK;
}
int rack_vst3_plugin_set_parameter(RackVST3Plugin* plugin, uint32_t index, float value) {
if (!plugin || !plugin->controller) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (index >= plugin->parameters.size()) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (value < 0.0f) value = 0.0f;
if (value > 1.0f) value = 1.0f;
ParamID param_id = plugin->parameters[index].id;
plugin->controller->setParamNormalized(param_id, value);
int32 queue_index = 0;
IParamValueQueue* queue = plugin->input_param_changes.addParameterData(param_id, queue_index);
if (queue) {
int32 point_index = 0;
queue->addPoint(0, value, point_index);
}
return RACK_VST3_OK;
}
int rack_vst3_plugin_parameter_info(
RackVST3Plugin* plugin,
uint32_t index,
char* name,
size_t name_size,
float* min,
float* max,
float* default_value,
char* unit,
size_t unit_size)
{
if (!plugin || !plugin->controller || !name || name_size == 0) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (index >= plugin->parameters.size()) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
const auto& param_info = plugin->parameters[index];
strncpy(name, param_info.title.c_str(), name_size - 1);
name[name_size - 1] = '\0';
if (min) *min = static_cast<float>(param_info.min_value);
if (max) *max = static_cast<float>(param_info.max_value);
if (default_value) *default_value = static_cast<float>(param_info.default_value);
if (unit && unit_size > 0) {
strncpy(unit, param_info.units.c_str(), unit_size - 1);
unit[unit_size - 1] = '\0';
}
return RACK_VST3_OK;
}
int rack_vst3_plugin_get_preset_count(RackVST3Plugin* plugin) {
if (!plugin || !plugin->initialized) {
return 0;
}
return static_cast<int>(plugin->presets.size());
}
int rack_vst3_plugin_get_preset_info(
RackVST3Plugin* plugin,
uint32_t index,
char* name,
size_t name_size,
int32_t* preset_number)
{
if (!plugin || !plugin->initialized || !name || name_size == 0) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (index >= plugin->presets.size()) {
return RACK_VST3_ERROR_NOT_FOUND;
}
const auto& preset = plugin->presets[index];
strncpy(name, preset.name.c_str(), name_size - 1);
name[name_size - 1] = '\0';
if (preset_number) {
*preset_number = static_cast<int32_t>(index);
}
return RACK_VST3_OK;
}
int rack_vst3_plugin_load_preset(RackVST3Plugin* plugin, int32_t preset_number) {
if (!plugin || !plugin->initialized || !plugin->controller) {
return RACK_VST3_ERROR_NOT_INITIALIZED;
}
if (preset_number < 0 || preset_number >= static_cast<int32_t>(plugin->presets.size())) {
return RACK_VST3_ERROR_NOT_FOUND;
}
const auto& preset = plugin->presets[preset_number];
IPtr<IUnitInfo> unit_info = U::cast<IUnitInfo>(plugin->controller);
if (!unit_info) {
return RACK_VST3_ERROR_GENERIC;
}
IPtr<IProgramListData> program_data = U::cast<IProgramListData>(unit_info);
if (program_data) {
IPtr<MemoryStream> stream(new MemoryStream(), false);
tresult result = program_data->getProgramData(preset.program_list_id, preset.program_index, stream);
if (result == kResultOk) {
stream->seek(0, IBStream::kIBSeekSet, nullptr);
result = program_data->setProgramData(preset.program_list_id, preset.program_index, stream);
if (result == kResultOk) {
return RACK_VST3_OK;
}
}
}
int32 param_count = plugin->controller->getParameterCount();
for (int32 i = 0; i < param_count; i++) {
Vst::ParameterInfo param_info;
if (plugin->controller->getParameterInfo(i, param_info) != kResultOk) {
continue;
}
bool is_discrete = param_info.stepCount > 0;
bool is_program_change = (param_info.flags & ParameterInfo::kIsProgramChange) != 0;
if (!is_discrete && !is_program_change) {
continue; }
std::string param_title = utf16_to_utf8(param_info.title);
std::transform(param_title.begin(), param_title.end(), param_title.begin(),
[](unsigned char c) { return std::tolower(c); });
bool is_program_param = false;
if (is_program_change) {
is_program_param = true;
} else {
const char* keywords[] = {"program", "preset", "patch"};
const size_t keyword_lengths[] = {7, 6, 5};
for (size_t k = 0; k < 3; ++k) {
const char* keyword = keywords[k];
size_t keyword_len = keyword_lengths[k];
size_t pos = 0;
while ((pos = param_title.find(keyword, pos)) != std::string::npos) {
bool start_ok = (pos == 0 || !std::isalnum(param_title[pos - 1]));
bool end_ok = (pos + keyword_len >= param_title.length() ||
!std::isalnum(param_title[pos + keyword_len]));
if (start_ok && end_ok) {
is_program_param = true;
break;
}
pos++;
}
if (is_program_param) {
break; }
}
}
if (is_program_param) {
if (preset.program_index > param_info.stepCount) {
continue; }
float normalized_value = static_cast<float>(preset.program_index) /
static_cast<float>(param_info.stepCount);
normalized_value = std::max(0.0f, std::min(1.0f, normalized_value));
ParamID param_id = param_info.id;
if (plugin->controller->setParamNormalized(param_id, normalized_value) == kResultOk) {
int32 queue_index = 0;
IParamValueQueue* queue = plugin->input_param_changes.addParameterData(param_id, queue_index);
if (queue) {
int32 point_index = 0;
queue->addPoint(0, normalized_value, point_index);
}
return RACK_VST3_OK;
}
}
}
int32 unit_count = unit_info->getUnitCount();
for (int32 i = 0; i < unit_count; i++) {
UnitInfo unit_info_struct;
if (unit_info->getUnitInfo(i, unit_info_struct) == kResultOk) {
if (unit_info_struct.programListId == preset.program_list_id) {
tresult result = unit_info->selectUnit(unit_info_struct.id);
if (result == kResultOk) {
return RACK_VST3_OK;
}
}
}
}
return RACK_VST3_ERROR_NOT_SUPPORTED;
}
int rack_vst3_plugin_get_state_size(RackVST3Plugin* plugin) {
if (!plugin || !plugin->component) {
return 0;
}
IPtr<MemoryStream> stream(new MemoryStream(), false);
uint32_t size_marker_placeholder = 0;
stream->write(&size_marker_placeholder, sizeof(size_marker_placeholder), nullptr);
int64 component_start_pos = 0;
stream->tell(&component_start_pos);
tresult result = plugin->component->getState(stream);
if (result != kResultOk) {
return 1024 * 1024; }
int64 component_end_pos = 0;
stream->tell(&component_end_pos);
if (plugin->controller && reinterpret_cast<void*>(plugin->controller.get()) != reinterpret_cast<void*>(plugin->component.get())) {
result = plugin->controller->getState(stream);
if (result != kResultOk) {
return static_cast<int>(stream->getSize());
}
}
return static_cast<int>(stream->getSize());
}
int rack_vst3_plugin_get_state(RackVST3Plugin* plugin, uint8_t* data, size_t* size) {
if (!plugin || !data || !size || !plugin->component) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (*size == 0) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
IPtr<MemoryStream> stream(new MemoryStream(), false);
uint32_t size_marker_placeholder = 0;
stream->write(&size_marker_placeholder, sizeof(size_marker_placeholder), nullptr);
int64 component_start_pos = 0;
stream->tell(&component_start_pos);
tresult result = plugin->component->getState(stream);
if (result != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
int64 component_end_pos = 0;
stream->tell(&component_end_pos);
uint32_t component_state_size = static_cast<uint32_t>(component_end_pos - component_start_pos);
stream->seek(0, IBStream::kIBSeekSet, nullptr);
stream->write(&component_state_size, sizeof(component_state_size), nullptr);
stream->seek(component_end_pos, IBStream::kIBSeekSet, nullptr);
if (plugin->controller && reinterpret_cast<void*>(plugin->controller.get()) != reinterpret_cast<void*>(plugin->component.get())) {
result = plugin->controller->getState(stream);
if (result != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
}
size_t state_size = stream->getSize();
if (state_size > *size) {
*size = state_size; return RACK_VST3_ERROR_INVALID_PARAM;
}
memcpy(data, stream->getData().data(), state_size);
*size = state_size;
return RACK_VST3_OK;
}
int rack_vst3_plugin_set_state(RackVST3Plugin* plugin, const uint8_t* data, size_t size) {
if (!plugin || !data || size == 0 || !plugin->component) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
IPtr<MemoryStream> stream(new MemoryStream(data, size), false);
uint32_t component_state_size = 0;
int32 bytes_read = 0;
tresult result = stream->read(&component_state_size, sizeof(component_state_size), &bytes_read);
if (result != kResultOk || bytes_read != sizeof(component_state_size)) {
return RACK_VST3_ERROR_GENERIC;
}
result = plugin->component->setState(stream);
if (result != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
if (plugin->controller && reinterpret_cast<void*>(plugin->controller.get()) != reinterpret_cast<void*>(plugin->component.get())) {
result = plugin->controller->setState(stream);
if (result != kResultOk) {
return RACK_VST3_ERROR_GENERIC;
}
}
return RACK_VST3_OK;
}
int rack_vst3_plugin_send_midi(
RackVST3Plugin* plugin,
const RackVST3MidiEvent* events,
uint32_t event_count)
{
if (!plugin || !plugin->initialized) {
return RACK_VST3_ERROR_NOT_INITIALIZED;
}
if (!events && event_count > 0) {
return RACK_VST3_ERROR_INVALID_PARAM;
}
if (event_count == 0) {
return RACK_VST3_OK;
}
for (uint32_t i = 0; i < event_count; ++i) {
const auto& midi_event = events[i];
Event vst3_event;
memset(&vst3_event, 0, sizeof(Event));
vst3_event.sampleOffset = midi_event.sample_offset;
vst3_event.busIndex = 0;
uint8_t status = midi_event.status & 0xF0;
switch (status) {
case 0x90: vst3_event.type = Event::kNoteOnEvent;
vst3_event.noteOn.channel = midi_event.channel;
vst3_event.noteOn.pitch = midi_event.data1;
vst3_event.noteOn.velocity = static_cast<float>(midi_event.data2) / 127.0f;
vst3_event.noteOn.noteId = -1; plugin->input_events.addEvent(vst3_event);
break;
case 0x80: vst3_event.type = Event::kNoteOffEvent;
vst3_event.noteOff.channel = midi_event.channel;
vst3_event.noteOff.pitch = midi_event.data1;
vst3_event.noteOff.velocity = static_cast<float>(midi_event.data2) / 127.0f;
vst3_event.noteOff.noteId = -1; plugin->input_events.addEvent(vst3_event);
break;
case 0xA0: vst3_event.type = Event::kPolyPressureEvent;
vst3_event.polyPressure.channel = midi_event.channel;
vst3_event.polyPressure.pitch = midi_event.data1;
vst3_event.polyPressure.pressure = static_cast<float>(midi_event.data2) / 127.0f;
plugin->input_events.addEvent(vst3_event);
break;
case 0xB0: vst3_event.type = Event::kLegacyMIDICCOutEvent;
vst3_event.midiCCOut.channel = midi_event.channel;
vst3_event.midiCCOut.controlNumber = midi_event.data1;
vst3_event.midiCCOut.value = midi_event.data2;
vst3_event.midiCCOut.value2 = 0;
plugin->input_events.addEvent(vst3_event);
break;
case 0xC0: vst3_event.type = Event::kLegacyMIDICCOutEvent;
vst3_event.midiCCOut.channel = midi_event.channel;
vst3_event.midiCCOut.controlNumber = 0x80; vst3_event.midiCCOut.value = midi_event.data1;
vst3_event.midiCCOut.value2 = 0;
plugin->input_events.addEvent(vst3_event);
break;
case 0xD0: vst3_event.type = Event::kLegacyMIDICCOutEvent;
vst3_event.midiCCOut.channel = midi_event.channel;
vst3_event.midiCCOut.controlNumber = 0x81; vst3_event.midiCCOut.value = midi_event.data1;
vst3_event.midiCCOut.value2 = 0;
plugin->input_events.addEvent(vst3_event);
break;
case 0xE0: { vst3_event.type = Event::kLegacyMIDICCOutEvent;
vst3_event.midiCCOut.channel = midi_event.channel;
vst3_event.midiCCOut.controlNumber = 0x82; vst3_event.midiCCOut.value = midi_event.data1; vst3_event.midiCCOut.value2 = midi_event.data2; plugin->input_events.addEvent(vst3_event);
break;
}
default:
continue;
}
}
return RACK_VST3_OK;
}