Here is the code:
Note: I don’t use the AudioClient and AudioHost classes from the example anymore; rather, I use a “stand-alone” implementation. There was too much going on in the example host, and it didn’t help in identifying the problem. This implementation makes it much clearer to see what’s going on.
However, the results are the same here too. The plugin doesn’t add the effect—it’s only passing a copy of the input buffer.
VstHost class header:
class VstHost {
public:
VstHost();
~VstHost();
bool init(const std::string& path, int sampleRate, int maxBlockSize, int symbolicSampleSize, bool realtime);
void destroy();
Steinberg::Vst::ProcessContext* processContext();
void setProcessing(bool processing);
bool process(int numSamples);
const Steinberg::Vst::BusInfo* busInfo(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection direction, int which);
int numBuses(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection direction);
void setBusActive(Steinberg::Vst::MediaType type, Steinberg::Vst::BusDirection direction, int which, bool active);
Steinberg::Vst::Sample32* channelBuffer32(Steinberg::Vst::BusDirection direction, int which);
Steinberg::Vst::Sample64* channelBuffer64(Steinberg::Vst::BusDirection direction, int which);
Steinberg::Vst::EventList* eventList(Steinberg::Vst::BusDirection direction, int which);
Steinberg::Vst::ParameterChanges* parameterChanges(Steinberg::Vst::BusDirection direction, int which);
const std::string& name();
private:
void _destroy(bool decrementRefCount);
void _printDebug(const std::string& info);
void _printError(const std::string& error);
std::vector _inAudioBusInfos, _outAudioBusInfos;
int _numInAudioBuses = 0, _numOutAudioBuses = 0;
std::vector _inEventBusInfos, _outEventBusInfos;
int _numInEventBuses = 0, _numOutEventBuses = 0;
std::vector _inSpeakerArrs, _outSpeakerArrs;
VST3::Hosting::Module::Ptr _module = nullptr;
Steinberg::IPtr _plugProvider = nullptr;
Steinberg::IPtr _vstPlug = nullptr;
Steinberg::IPtr _audioEffect = nullptr;
Steinberg::IPtr _editController = nullptr;
Steinberg::Vst::HostProcessData _processData = {};
Steinberg::Vst::ProcessSetup _processSetup = {};
Steinberg::Vst::ProcessContext _processContext = {};
Steinberg::IPtr _view = nullptr;
int _sampleRate = 0, _maxBlockSize = 0, _symbolicSampleSize = 0;
bool _realtime = false;
std::string _path;
std::string _name;
static Steinberg::Vst::HostApplication* _standardPluginContext;
static int _standardPluginContextRefCount;
};
For buffer communication between other programms used in main():
#pragma pack(push, 1)
struct VstStream {
int needOutput;
int outputReady;
float inputBuffer[128]; // interleaved
float outputBuffer[128]; // interleaved
};
#pragma pack(pop)
VstStream* connectSharedMemory() {
HANDLE hFile = CreateFile(L"Z:\\tmp\\shared_memory", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cerr << "Could not open shared file. Error: " << GetLastError() << "\n";
return nullptr;
}
HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
if (hMapFile == NULL) {
std::cerr << "Could not create file mapping. Error: " << GetLastError() << "\n";
CloseHandle(hFile);
return nullptr;
}
struct VstStream* vstStream = (struct VstStream*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (!vstStream) {
std::cerr << "Could not map view of file. Error: " << GetLastError() << "\n";
CloseHandle(hMapFile);
CloseHandle(hFile);
return nullptr;
}
std::cout << "Connected to shared file successfully!\n";
return vstStream;
}
The VST host implementation :
Steinberg::Vst::HostApplication* VstHost::_standardPluginContext = nullptr;
int VstHost::_standardPluginContextRefCount = 0;
using namespace Steinberg;
using namespace Steinberg::Vst;
VstHost::VstHost()
{}
VstHost::~VstHost()
{
destroy();
}
bool VstHost::init(const std::string& path, int sampleRate, int maxBlockSize, int symbolicSampleSize, bool realtime)
{
_destroy(false);
++_standardPluginContextRefCount;
if (!_standardPluginContext) {
_standardPluginContext = owned(new HostApplication());
PluginContextFactory::instance().setPluginContext(_standardPluginContext);
}
_path = path;
_sampleRate = sampleRate;
_maxBlockSize = maxBlockSize;
_symbolicSampleSize = symbolicSampleSize;
_realtime = realtime;
_processSetup.processMode = realtime;
_processSetup.symbolicSampleSize = symbolicSampleSize;
_processSetup.sampleRate = _sampleRate;
_processSetup.maxSamplesPerBlock = _maxBlockSize;
_processData.numSamples = 0;
_processData.symbolicSampleSize = _symbolicSampleSize;
_processData.processContext = &_processContext;
std::string error;
_module = VST3::Hosting::Module::create(path, error);
if (!_module) {
_printError(error);
return false;
}
VST3::Hosting::PluginFactory factory = _module->getFactory();
for (auto& classInfo : factory.classInfos())
{
if (classInfo.category() == kVstAudioEffectClass)
{
_plugProvider = owned(new PlugProvider(factory, classInfo, true));
_name = classInfo.name();
break;
}
}
if (!_plugProvider) {
_printError("No PlugProvider found");
return false;
}
_vstPlug = _plugProvider->getComponent();
_audioEffect = FUnknownPtr(_vstPlug);
if (!_audioEffect) {
_printError("Could not get audio processor from VST");
return false;
}
_editController = _plugProvider->getController();
FUnknownPtr contextRequirements(_audioEffect);
if (contextRequirements) {
auto flags = contextRequirements->getProcessContextRequirements();
#define PRINT_FLAG(x) if (flags & IProcessContextRequirements::Flags::x) { _printDebug(#x); }
PRINT_FLAG(kNeedSystemTime)
PRINT_FLAG(kNeedContinousTimeSamples)
PRINT_FLAG(kNeedProjectTimeMusic)
PRINT_FLAG(kNeedBarPositionMusic)
PRINT_FLAG(kNeedCycleMusic)
PRINT_FLAG(kNeedSamplesToNextClock)
PRINT_FLAG(kNeedTempo)
PRINT_FLAG(kNeedTimeSignature)
PRINT_FLAG(kNeedChord)
PRINT_FLAG(kNeedFrameRate)
PRINT_FLAG(kNeedTransportState)
#undef PRINT_FLAG
}
_numInAudioBuses = _vstPlug->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
_numOutAudioBuses = _vstPlug->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
_numInEventBuses = _vstPlug->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
_numOutEventBuses = _vstPlug->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
std::ostringstream debugOss;
debugOss << "Buses: " << _numInAudioBuses << " audio and " << _numInEventBuses << " event inputs; ";
debugOss << _numOutAudioBuses << " audio and " << _numOutEventBuses <getBusInfo(kAudio, kInput, i, info);
_inAudioBusInfos.push_back(info);
setBusActive(kAudio, kInput, i, true);
SpeakerArrangement speakerArr;
_audioEffect->getBusArrangement(kInput, i, speakerArr);
_inSpeakerArrs.push_back(speakerArr);
}
for (int i = 0; i < _numInEventBuses; ++i) {
BusInfo info;
_vstPlug->getBusInfo(kEvent, kInput, i, info);
_inEventBusInfos.push_back(info);
setBusActive(kEvent, kInput, i, false);
}
for (int i = 0; i < _numOutAudioBuses; ++i) {
BusInfo info;
_vstPlug->getBusInfo(kAudio, kOutput, i, info);
_outAudioBusInfos.push_back(info);
setBusActive(kAudio, kOutput, i, true);
SpeakerArrangement speakerArr;
_audioEffect->getBusArrangement(kOutput, i, speakerArr);
_outSpeakerArrs.push_back(speakerArr);
}
for (int i = 0; i < _numOutEventBuses; ++i) {
BusInfo info;
_vstPlug->getBusInfo(kEvent, kOutput, i, info);
_outEventBusInfos.push_back(info);
setBusActive(kEvent, kOutput, i, false);
}
tresult res = _audioEffect->setBusArrangements(_inSpeakerArrs.data(), _numInAudioBuses, _outSpeakerArrs.data(), _numOutAudioBuses);
if (res != kResultTrue) {
_printError("Failed to set bus arrangements");
return false;
}
res = _audioEffect->setupProcessing(_processSetup);
if (res == kResultOk) {
_processData.prepare(*_vstPlug, _maxBlockSize, _processSetup.symbolicSampleSize);
if (_numInEventBuses > 0) {
_processData.inputEvents = new EventList[_numInEventBuses];
}
if (_numOutEventBuses > 0) {
_processData.outputEvents = new EventList[_numOutEventBuses];
}
}
else {
_printError("Failed to setup VST processing");
return false;
}
if (_vstPlug->setActive(true) != kResultTrue) {
_printError("Failed to activate VST component");
return false;
}
return true;
}
void VstHost::destroy()
{
_destroy(true);
}
bool VstHost::process(int numSamples)
{
if (numSamples > _maxBlockSize) {
#ifdef _DEBUG
_printError("numSamples > _maxBlockSize");
#endif
numSamples = _maxBlockSize;
}
_processData.numSamples = numSamples;
tresult result = _audioEffect->process(_processData);
if (result != kResultOk) {
#ifdef _DEBUG
std::cerr << "VST process failed" <activateBus(type, direction, which, active);
}
void VstHost::setProcessing(bool processing)
{
_audioEffect->setProcessing(processing);
}
Steinberg::Vst::ProcessContext* VstHost::processContext()
{
return &_processContext;
}
Steinberg::Vst::Sample32* VstHost::channelBuffer32(BusDirection direction, int which)
{
if (direction == kInput) {
return _processData.inputs->channelBuffers32[which];
}
else if (direction == kOutput) {
return _processData.outputs->channelBuffers32[which];
}
else {
return nullptr;
}
}
Steinberg::Vst::Sample64* VstHost::channelBuffer64(BusDirection direction, int which)
{
if (direction == kInput) {
return _processData.inputs->channelBuffers64[which];
}
else if (direction == kOutput) {
return _processData.outputs->channelBuffers64[which];
}
else {
return nullptr;
}
}
Steinberg::Vst::EventList* VstHost::eventList(Steinberg::Vst::BusDirection direction, int which)
{
if (direction == kInput) {
return static_cast(&_processData.inputEvents[which]);
}
else if (direction == kOutput) {
return static_cast(&_processData.outputEvents[which]);
}
else {
return nullptr;
}
}
Steinberg::Vst::ParameterChanges* VstHost::parameterChanges(Steinberg::Vst::BusDirection direction, int which)
{
if (direction == kInput) {
return static_cast(&_processData.inputParameterChanges[which]);
}
else if (direction == kOutput) {
return static_cast(&_processData.outputParameterChanges[which]);
}
else {
return nullptr;
}
}
const std::string& VstHost::name()
{
return _name;
}
void VstHost::_destroy(bool decrementRefCount)
{
_editController = nullptr;
_audioEffect = nullptr;
_vstPlug = nullptr;
_plugProvider = nullptr;
_module = nullptr;
_inAudioBusInfos.clear();
_outAudioBusInfos.clear();
_numInAudioBuses = 0;
_numOutAudioBuses = 0;
_inEventBusInfos.clear();
_outEventBusInfos.clear();
_numInEventBuses = 0;
_numOutEventBuses = 0;
_inSpeakerArrs.clear();
_outSpeakerArrs.clear();
if (_processData.inputEvents) {
delete[] static_cast(_processData.inputEvents);
}
if (_processData.outputEvents) {
delete[] static_cast(_processData.outputEvents);
}
_processData.unprepare();
_processData = {};
_processSetup = {};
_processContext = {};
_sampleRate = 0;
_maxBlockSize = 0;
_symbolicSampleSize = 0;
_realtime = false;
_path = "";
_name = "";
if (decrementRefCount) {
if (_standardPluginContextRefCount > 0) {
--_standardPluginContextRefCount;
}
if (_standardPluginContext && _standardPluginContextRefCount == 0) {
PluginContextFactory::instance().setPluginContext(nullptr);
_standardPluginContext->release();
delete _standardPluginContext;
_standardPluginContext = nullptr;
}
}
}
void VstHost::_printDebug(const std::string& info)
{
std::cout << "Debug info for VST3 plugin \"" << _path << "\": " << info << std::endl;
}
void VstHost::_printError(const std::string& error)
{
std::cerr << "Error loading VST3 plugin \"" << _path << "\": " << error << std::endl;
}
Initialising and starting processing:
int main()
{
std::string pathToPlugin = "Z:\\home\\patch\\ValhallaVintageVerb.vst3";
int sampleRate = 48000;
int maxBlockSize = 64; // Stands also for block size
VstHost* vst = new VstHost();
vst->init(pathToPlugin, sampleRate, maxBlockSize, kSample32, kRealtime);
VstStream* vstStream = connectSharedMemory();
Sample32* inL = vst->channelBuffer32(kInput, 0);
Sample32* inR = vst->channelBuffer32(kInput, 1);
Sample32* outL = vst->channelBuffer32(kOutput, 0);
Sample32* outR = vst->channelBuffer32(kOutput, 1);
vst->setProcessing(true);
while (true) {
// TODO -> implement better synchronization (this still works without dropouts)
while (true) {
std::this_thread::sleep_for(std::chrono::microseconds(100));
if (vstStream->needOutput) {
vstStream->needOutput = 0;
break;
}
}
for (unsigned int i = 0; i < 64; i++) {
inL[i] = vstStream->inputBuffer[i * 2];
inR[i] = vstStream->inputBuffer[i * 2 + 1];
}
vst->process(64);
for (unsigned int i = 0; i < 64; i++) {
vstStream->outputBuffer[i * 2] = outL[i];
vstStream->outputBuffer[i * 2 + 1] = outR[i];
}
vstStream->outputReady = 1;
}
return 0;
}