Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,158 @@
//-----------------------------------------------------------------------------
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/audiohost.cpp
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/samples/vst-hosting/audiohost/source/audiohost.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/platform/appinit.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "base/source/fcommandline.h"
#include "pluginterfaces/base/funknown.h"
#include "pluginterfaces/base/fstrdefs.h"
#include "pluginterfaces/gui/iplugview.h"
#include "pluginterfaces/gui/iplugviewcontentscalesupport.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/vsttypes.h"
#include <cstdio>
#include <iostream>
#if WIN32
#include "windows.h"
#include <wtypes.h>
#endif
//------------------------------------------------------------------------
namespace Steinberg {
FUnknown* gStandardPluginContext = new Vst::HostApplication ();
namespace Vst {
namespace AudioHost {
static AudioHost::AppInit gInit (std::make_unique<App> ());
//------------------------------------------------------------------------
App::~App () noexcept
{
}
//------------------------------------------------------------------------
void App::startAudioClient (const std::string& path, VST3::Optional<VST3::UID> effectID,
uint32 flags)
{
std::string error;
module = VST3::Hosting::Module::create (path, error);
if (!module)
{
std::string reason = "Could not create Module for file:";
reason += path;
reason += "\nError: ";
reason += error;
// EditorHost::IPlatform::instance ().kill (-1, reason);
return;
}
auto factory = module->getFactory ();
for (auto& classInfo : factory.classInfos ())
{
if (classInfo.category () == kVstAudioEffectClass)
{
if (effectID)
{
if (*effectID != classInfo.ID ())
continue;
}
plugProvider = owned (new PlugProvider (factory, classInfo, true));
break;
}
}
if (!plugProvider)
{
std::string error;
if (effectID)
error =
"No VST3 Audio Module Class with UID " + effectID->toString () + " found in file ";
else
error = "No VST3 Audio Module Class found in file ";
error += path;
// EditorHost::IPlatform::instance ().kill (-1, error);
return;
}
OPtr<IComponent> component = plugProvider->getComponent ();
OPtr<IEditController> controller = plugProvider->getController ();
auto midiMapping = U::cast<IMidiMapping> (controller);
//! TODO: Query the plugProvider for a proper name which gets displayed in JACK.
vst3Processor = AudioClient::create ("VST 3 SDK", component, midiMapping);
}
//------------------------------------------------------------------------
void App::init (const std::vector<std::string>& cmdArgs)
{
if (cmdArgs.empty ())
{
/*auto helpText = R"(
usage: AudioHost pluginPath
)";
*/
return;
}
VST3::Optional<VST3::UID> uid;
uint32 flags {};
startAudioClient (cmdArgs.back (), std::move (uid), flags);
}
//------------------------------------------------------------------------
void App::terminate ()
{
}
//------------------------------------------------------------------------
} // EditorHost
} // Vst
} // Steinberg
//------------------------------------------------------------------------
#if WIN32
int wmain (int argc, wchar_t* argv[])
{
std::vector<std::string> cmdArgs;
for (int i = 1; i < argc; ++i)
cmdArgs.push_back (Steinberg::Vst::StringConvert::convert (Steinberg::wscast (argv[i])));
Steinberg::Vst::AudioHost::gInit.app->init (cmdArgs);
std::cout << "Press <enter> to continue . . .";
std::getchar ();
return 0;
}
#else
int main (int argc, char* argv[])
{
std::vector<std::string> cmdArgs;
for (int i = 1; i < argc; ++i)
cmdArgs.push_back (argv[i]);
Steinberg::Vst::AudioHost::gInit.app->init (cmdArgs);
std::cout << "Press <enter> to continue . . .";
std::getchar ();
return 0;
}
#endif
@@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/audiohost.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/samples/vst-hosting/editorhost/source/platform/iapplication.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/audioclient.h"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/vst/utility/optional.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace AudioHost {
//------------------------------------------------------------------------
class App : public EditorHost::IApplication
{
public:
~App () noexcept override;
void init (const std::vector<std::string>& cmdArgs) override;
void terminate () override;
private:
enum OpenFlags
{
};
void startAudioClient (const std::string& path, VST3::Optional<VST3::UID> effectID,
uint32 flags);
VST3::Hosting::Module::Ptr module {nullptr};
IPtr<PlugProvider> plugProvider {nullptr};
AudioClientPtr vst3Processor;
};
//------------------------------------------------------------------------
} // AudioHost
} // Vst
} // Steinberg
@@ -0,0 +1,440 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/audioclient.cpp
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "audioclient.h"
#include "miditovst.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include <functional>
#include <cassert>
#include <limits>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// 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
//------------------------------------------------------------------------
AudioClient::AudioClient ()
{
}
//------------------------------------------------------------------------
AudioClient::~AudioClient ()
{
terminate ();
}
//------------------------------------------------------------------------
AudioClientPtr AudioClient::create (const Name& name, IComponent* component,
IMidiMapping* midiMapping)
{
auto newProcessor = std::make_shared<AudioClient> ();
newProcessor->initialize (name, component, midiMapping);
return newProcessor;
}
//------------------------------------------------------------------------
void AudioClient::initProcessContext ()
{
processContext = {};
processContext.tempo = 120;
}
//------------------------------------------------------------------------
void AudioClient::createLocalMediaServer (const Name& name)
{
mediaServer = createMediaServer (name);
mediaServer->registerAudioClient (this);
mediaServer->registerMidiClient (this);
}
//------------------------------------------------------------------------
bool AudioClient::initialize (const Name& name, IComponent* _component, IMidiMapping* midiMapping)
{
component = _component;
if (!component)
return false;
initProcessData ();
paramTransferrer.setMaxParameters (1000);
if (midiMapping)
midiCCMapping = initMidiCtrlerAssignment (component, midiMapping);
createLocalMediaServer (name);
return true;
}
//------------------------------------------------------------------------
void AudioClient::terminate ()
{
mediaServer = nullptr;
FUnknownPtr<IAudioProcessor> processor = component;
if (!processor)
return;
processor->setProcessing (false);
component->setActive (false);
}
//------------------------------------------------------------------------
void AudioClient::initProcessData ()
{
// processData.prepare will be done in setBlockSize
processData.inputEvents = &eventList;
processData.inputParameterChanges = &inputParameterChanges;
processData.processContext = &processContext;
initProcessContext ();
}
//------------------------------------------------------------------------
IMidiClient::IOSetup AudioClient::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 = 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 = StringConvert::convert (info.name, 128);
iosetup.outputs.push_back (busName);
}
return iosetup;
}
//------------------------------------------------------------------------
IAudioClient::IOSetup AudioClient::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 = 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 = StringConvert::convert (info.name, 128);
iosetup.inputs.push_back (channelName + " " + std::to_string (j));
}
}
return iosetup;
}
//------------------------------------------------------------------------
void AudioClient::preprocess (Buffers& buffers, int64_t continousFrames)
{
processData.numSamples = buffers.numSamples;
processContext.continousTimeSamples = continousFrames;
assignBusBuffers (buffers, processData);
paramTransferrer.transferChangesTo (inputParameterChanges);
}
//------------------------------------------------------------------------
bool AudioClient::process (Buffers& buffers, int64_t continousFrames)
{
FUnknownPtr<IAudioProcessor> processor = component;
if (!processor || !isProcessing)
return false;
preprocess (buffers, continousFrames);
if (processor->process (processData) != kResultOk)
return false;
postprocess (buffers);
return true;
}
//------------------------------------------------------------------------
void AudioClient::postprocess (Buffers& buffers)
{
eventList.clear ();
inputParameterChanges.clearQueue ();
unassignBusBuffers (buffers, processData);
}
//------------------------------------------------------------------------
bool AudioClient::setSamplerate (SampleRate value)
{
if (sampleRate == value)
return true;
sampleRate = value;
processContext.sampleRate = sampleRate;
if (blockSize == 0)
return true;
return updateProcessSetup ();
}
//------------------------------------------------------------------------
bool AudioClient::setBlockSize (int32 value)
{
if (blockSize == value)
return true;
blockSize = value;
if (sampleRate == 0)
return true;
processData.prepare (*component, blockSize, kSample32);
return updateProcessSetup ();
}
//------------------------------------------------------------------------
bool AudioClient::updateProcessSetup ()
{
FUnknownPtr<IAudioProcessor> processor = component;
if (!processor)
return false;
if (isProcessing)
{
if (processor->setProcessing (false) != kResultOk)
return false;
if (component->setActive (false) != kResultOk)
return false;
}
ProcessSetup setup {kRealtime, kSample32, blockSize, sampleRate};
if (processor->setupProcessing (setup) != kResultOk)
return false;
if (component->setActive (true) != kResultOk)
return false;
processor->setProcessing (true); // != kResultOk
/*
if (processor->setProcessing(true) != kResultOk)
return false;*/
isProcessing = true;
return isProcessing;
}
//------------------------------------------------------------------------
bool AudioClient::isPortInRange (int32 port, int32 channel) const
{
return port < kMaxMidiMappingBusses && !midiCCMapping[port][channel].empty ();
}
//------------------------------------------------------------------------
bool AudioClient::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 AudioClient::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 (static_cast<int32> (event.timestamp), (*paramChange).second,
index) != kResultOk)
{
assert (false && "Parameter point was not added to ParamValueQueue!");
}
}
return true;
}
return false;
}
//------------------------------------------------------------------------
bool AudioClient::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 AudioClient::setParameter (ParamID id, ParamValue value, int32 sampleOffset)
{
paramTransferrer.addChange (id, value, sampleOffset);
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/audioclient.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.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 <array>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
class IMidiMapping;
class IComponent;
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;
//------------------------------------------------------------------------
using AudioClientPtr = std::shared_ptr<class AudioClient>;
//------------------------------------------------------------------------
class AudioClient : public IAudioClient, public IMidiClient, public IParameterClient
{
public:
//--------------------------------------------------------------------
using Name = std::string;
AudioClient ();
~AudioClient () override;
static AudioClientPtr create (const Name& name, IComponent* component,
IMidiMapping* midiMapping);
// 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
void setParameter (ParamID id, ParamValue value, int32 sampleOffset) override;
bool initialize (const Name& name, IComponent* component, IMidiMapping* midiMapping);
//--------------------------------------------------------------------
private:
void createLocalMediaServer (const Name& name);
void terminate ();
void updateBusBuffers (Buffers& buffers, HostProcessData& processData);
void initProcessData ();
void initProcessContext ();
bool updateProcessSetup ();
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;
IComponent* component = nullptr;
ParameterChangeTransfer paramTransferrer;
MidiCCMapping midiCCMapping;
IMediaServerPtr mediaServer;
bool isProcessing = false;
Name name;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,98 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <memory>
#include <string>
#include <vector>
#include <pluginterfaces/vst/vsttypes.h>
//----------------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
using IOName = std::string;
using IONames = std::vector<IOName>;
using AudioClientName = std::string;
struct IAudioClient
{
struct Buffers
{
float** inputs;
int32_t numInputs;
float** outputs;
int32_t numOutputs;
int32_t numSamples;
};
struct IOSetup
{
IONames inputs;
IONames outputs;
};
virtual bool process (Buffers& buffers, int64_t continousFrames) = 0;
virtual bool setSamplerate (SampleRate value) = 0;
virtual bool setBlockSize (int32 value) = 0;
virtual IOSetup getIOSetup () const = 0;
virtual ~IAudioClient () {}
};
//----------------------------------------------------------------------------------
struct IMidiClient
{
using MidiData = uint8_t;
struct Event
{
MidiData type;
MidiData channel;
MidiData data0;
MidiData data1;
int64_t timestamp;
};
struct IOSetup
{
IONames inputs;
IONames outputs;
};
virtual bool onEvent (const Event& event, int32_t port) = 0;
virtual IOSetup getMidiIOSetup () const = 0;
virtual ~IMidiClient () {}
};
//----------------------------------------------------------------------------------
struct IMediaServer
{
virtual bool registerAudioClient (IAudioClient* client) = 0;
virtual bool registerMidiClient (IMidiClient* client) = 0;
virtual ~IMediaServer () {}
};
//----------------------------------------------------------------------------------
using IMediaServerPtr = std::shared_ptr<IMediaServer>;
IMediaServerPtr createMediaServer (const AudioClientName& name);
//----------------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,39 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include <memory>
#include <pluginterfaces/vst/vsttypes.h>
//----------------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//----------------------------------------------------------------------------------
struct IParameterClient
{
virtual void setParameter (ParamID id, ParamValue value, int32 sampleOffset) = 0;
virtual ~IParameterClient () {}
};
//----------------------------------------------------------------------------------
using IParameterClientPtr = std::weak_ptr<IParameterClient>;
//----------------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,405 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/jack/jackclient.cpp
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3 using Jack
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include <cassert>
//! Workaround for Jack on Windows
#if defined(SMTG_OS_WINDOWS) && defined(_STDINT)
#define _STDINT_H
#endif
#include <jack/jack.h>
#include <jack/midiport.h>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
static const int kJackSuccess = 0;
//------------------------------------------------------------------------
// jack Client
//------------------------------------------------------------------------
class JackClient : public IMediaServer
{
public:
//--------------------------------------------------------------------
using JackPorts = std::vector<jack_port_t*>;
using JackName = std::string;
JackClient () = default;
~JackClient () override;
// IMediaServer interface
bool registerAudioClient (IAudioClient* client) override;
bool registerMidiClient (IMidiClient* client) override;
bool initialize (JackName name);
// jack process callback
int process (jack_nframes_t nframes);
//--------------------------------------------------------------------
private:
jack_client_t* registerClient (JackName name);
bool registerAudioPorts (IAudioClient* processor);
bool registerMidiPorts (IMidiClient* processor);
bool addAudioOutputPort (JackName name);
bool addAudioInputPort (JackName name);
bool addMidiInputPort (JackName name);
int processMidi (jack_nframes_t nframes);
bool setupJackProcessCallbacks (jack_client_t* client);
bool autoConnectAudioPorts (jack_client_t* client);
bool autoConnectMidiPorts (jack_client_t* client);
void updateAudioBuffers (jack_nframes_t nframes);
// Jack objects
jack_client_t* jackClient = nullptr;
JackPorts audioOutputPorts;
JackPorts audioInputPorts;
JackPorts midiInputPorts;
IAudioClient* audioClient = nullptr;
IMidiClient* midiClient = nullptr;
using BufferPointers = std::vector<jack_default_audio_sample_t*>;
BufferPointers audioOutputPointers;
BufferPointers audioInputPointers;
IAudioClient::Buffers buffers {nullptr};
};
//------------------------------------------------------------------------
int jack_on_process (jack_nframes_t nframes, void* arg)
{
auto client = reinterpret_cast<JackClient*> (arg);
return client->process (nframes);
}
//------------------------------------------------------------------------
int jack_on_set_sample_rate (jack_nframes_t nframes, void* arg)
{
auto client = reinterpret_cast<IAudioClient*> (arg);
client->setSamplerate (static_cast<SampleRate> (nframes));
return kJackSuccess;
}
//------------------------------------------------------------------------
int jack_on_set_block_size (jack_nframes_t nframes, void* arg)
{
auto client = reinterpret_cast<IAudioClient*> (arg);
client->setBlockSize (static_cast<int32> (nframes));
return kJackSuccess;
}
//------------------------------------------------------------------------
IMediaServerPtr createMediaServer (const AudioClientName& name)
{
auto client = std::make_shared<JackClient> ();
client->initialize (name);
return client;
}
//------------------------------------------------------------------------
JackClient::~JackClient ()
{
//! We do not need to "unregister" ports. It is done automatically with "jack_client_close"
jack_deactivate (jackClient); // Stops calls of process
jack_client_close (jackClient); // Remove client from process graph and remove all ports
}
//------------------------------------------------------------------------
bool JackClient::registerAudioClient (IAudioClient* client)
{
if (audioClient)
return false;
audioClient = client;
//! First thing to do: register the audio ports.
if (!registerAudioPorts (audioClient))
return false;
//! Setup all the callbacks like setSampleRate and process etc.
if (!setupJackProcessCallbacks (jackClient))
return false;
//! Activate after defining the callbacks. It is said in the documentation.
if (jack_activate (jackClient) != kJackSuccess)
return false;
//! AFTER activation, register the ports.
if (!autoConnectAudioPorts (jackClient))
return false;
return true;
}
//------------------------------------------------------------------------
bool JackClient::registerMidiClient (IMidiClient* client)
{
if (midiClient)
return false;
midiClient = client;
//! Register the midi ports.
if (!registerMidiPorts (midiClient))
return false;
//! Afterwards auto-connect them.
if (!autoConnectMidiPorts (jackClient))
return false;
return true;
}
//------------------------------------------------------------------------
bool JackClient::initialize (JackClient::JackName name)
{
jackClient = registerClient (name);
if (!jackClient)
return false;
return true;
}
//------------------------------------------------------------------------
void JackClient::updateAudioBuffers (jack_nframes_t nframes)
{
int outputIndex = 0;
for (auto audioOutputPort : audioOutputPorts)
{
auto* portBuffer = jack_port_get_buffer (audioOutputPort, nframes);
if (!portBuffer)
continue;
buffers.outputs[outputIndex++] = static_cast<jack_default_audio_sample_t*> (portBuffer);
}
int inputIndex = 0;
for (auto audioInputPort : audioInputPorts)
{
auto* portBuffer = jack_port_get_buffer (audioInputPort, nframes);
if (!portBuffer)
continue;
buffers.inputs[inputIndex++] = static_cast<jack_default_audio_sample_t*> (portBuffer);
}
}
//------------------------------------------------------------------------
int JackClient::process (jack_nframes_t nframes)
{
processMidi (nframes);
buffers.numSamples = nframes;
updateAudioBuffers (nframes);
if (!audioClient)
return 0;
if (audioClient->process (buffers, jack_last_frame_time (jackClient)) == false)
{
assert (false);
}
return kJackSuccess;
}
//------------------------------------------------------------------------
bool JackClient::registerAudioPorts (IAudioClient* processor)
{
auto ioSetup = processor->getIOSetup ();
for (const auto& output : ioSetup.outputs)
addAudioOutputPort (output);
for (const auto& input : ioSetup.inputs)
addAudioInputPort (input);
buffers.inputs = audioInputPointers.data ();
buffers.numInputs = (int32_t)audioInputPointers.size ();
buffers.numOutputs = (int32_t)audioOutputPointers.size ();
buffers.outputs = audioOutputPointers.data ();
return true;
}
//------------------------------------------------------------------------
bool JackClient::registerMidiPorts (IMidiClient* processor)
{
const auto ioSetup = processor->getMidiIOSetup ();
for (const auto& input : ioSetup.inputs)
addMidiInputPort (input);
return true;
}
//------------------------------------------------------------------------
bool JackClient::addAudioOutputPort (JackClient::JackName name)
{
auto port =
jack_port_register (jackClient, name.data (), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
if (!port)
return false;
audioOutputPorts.push_back (port);
audioOutputPointers.resize (audioOutputPorts.size ());
return true;
}
//------------------------------------------------------------------------
bool JackClient::addAudioInputPort (JackClient::JackName name)
{
auto port =
jack_port_register (jackClient, name.data (), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
if (!port)
return false;
audioInputPorts.push_back (port);
audioInputPointers.resize (audioOutputPorts.size ());
return true;
}
//------------------------------------------------------------------------
bool JackClient::addMidiInputPort (JackClient::JackName name)
{
auto port =
jack_port_register (jackClient, name.data (), JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
if (!port)
return false;
midiInputPorts.push_back (port);
return true;
}
//------------------------------------------------------------------------
int JackClient::processMidi (jack_nframes_t nframes)
{
static const uint8_t kChannelMask = 0x0F;
static const uint8_t kStatusMask = 0xF0;
static const uint32_t kDataMask = 0x7F;
for (int32_t portIndex = 0, count = static_cast<int32_t> (midiInputPorts.size ());
portIndex < count; ++portIndex)
{
auto midiInputPort = midiInputPorts.at (portIndex);
auto* portBuffer = jack_port_get_buffer (midiInputPort, nframes);
if (!portBuffer)
continue;
jack_midi_event_t in_event;
auto event_count = jack_midi_get_event_count (portBuffer);
for (uint32_t i = 0; i < event_count; i++)
{
jack_midi_event_get (&in_event, portBuffer, i);
if (in_event.size == 0)
continue;
auto midiData = in_event.buffer;
Steinberg::Vst::IMidiClient::MidiData channel = midiData[0] & kChannelMask;
Steinberg::Vst::IMidiClient::MidiData status = midiData[0] & kStatusMask;
Steinberg::Vst::IMidiClient::MidiData data0 = midiData[1];
Steinberg::Vst::IMidiClient::MidiData data1 = midiData[2];
midiClient->onEvent ({status, channel, data0, data1, in_event.time}, portIndex);
}
}
return kJackSuccess;
}
//------------------------------------------------------------------------
jack_client_t* JackClient::registerClient (JackClient::JackName name)
{
jack_options_t options = JackNullOption;
jack_status_t status;
jackClient = jack_client_open (name.data (), options, &status, nullptr);
return jackClient;
/* Use the status to check for errors:
if (status & JackServerFailed)
{
fprintf (stderr, "Unable to connect to JACK server\n");
}*/
}
//------------------------------------------------------------------------
bool JackClient::setupJackProcessCallbacks (jack_client_t* client)
{
if (jack_set_process_callback (client, jack_on_process, this) != kJackSuccess)
return false;
if (jack_set_sample_rate_callback (client, jack_on_set_sample_rate, audioClient) !=
kJackSuccess)
return false;
if (jack_set_buffer_size_callback (client, jack_on_set_block_size, audioClient) != kJackSuccess)
return false;
return true;
}
//------------------------------------------------------------------------
bool JackClient::autoConnectAudioPorts (jack_client_t* client)
{
int portIndex = 0;
//! Connect Audio Outputs
auto ports = jack_get_ports (client, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput);
for (auto& port : audioOutputPorts)
{
if (!ports[portIndex])
break;
auto output__port_name = ports[portIndex++];
auto res = jack_connect (client, jack_port_name (port), output__port_name);
if (res != 0)
break;
}
jack_free (ports);
return true;
}
//------------------------------------------------------------------------
bool JackClient::autoConnectMidiPorts (jack_client_t* client)
{
int portIndex = 1;
//! Connect MIDI Inputs
auto ports = jack_get_ports (client, nullptr, "midi", JackPortIsPhysical | JackPortIsOutput);
if (!ports)
return false;
for (auto& port : midiInputPorts)
{
if (!ports[portIndex])
break;
auto inputPortName = ports[portIndex++];
auto res = jack_connect (client, inputPortName, jack_port_name (port));
if (res != 0)
continue;
}
jack_free (ports);
return true;
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,139 @@
//-----------------------------------------------------------------------------
// 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
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#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 new_event;
}
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 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 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 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 paramChange;
};
}
else if (status == kAfterTouchStatus)
{
paramChange.first = toParamID (channel, Vst::kAfterTouch);
if (paramChange.first != kNoParamId)
{
paramChange.second = (ParamValue) (midiData1 & kDataMask) * kMidiScaler;
return paramChange;
};
}
else if (status == kProgramChangeStatus)
{
// TODO
}
return {};
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,38 @@
//------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/platform/appinit.h
// Created by : Steinberg, 04/2005
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/samples/vst-hosting/editorhost/source/platform/iapplication.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
namespace AudioHost {
//------------------------------------------------------------------------
struct AppInit
{
explicit AppInit (EditorHost::ApplicationPtr&& _app) { app = std::move (_app); }
EditorHost::ApplicationPtr app;
};
//------------------------------------------------------------------------
} // EditorHost
} // Vst
} // Steinberg
@@ -0,0 +1,24 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/usediids.cpp
// Created by : Steinberg 09.2008
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
//#define INIT_CLASS_IID
// This macro definition modifies the behavior of DECLARE_CLASS_IID (funknown.h)
// and produces the actual symbols for all interface identifiers.
// It must be defined before including the interface headers and
// in only one source file!
//------------------------------------------------------------------------
//#define INIT_CLASS_IID