Initial release
This commit is contained in:
+89
@@ -0,0 +1,89 @@
|
||||
|
||||
if(SMTG_ENABLE_VST3_HOSTING_EXAMPLES)
|
||||
|
||||
include(SMTG_FindJack)
|
||||
if(${LIBJACK_FOUND})
|
||||
set(audiohost_sources
|
||||
"${SDK_ROOT}/public.sdk/samples/vst-hosting/editorhost/source/platform/iapplication.h"
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/plugprovider.cpp"
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/plugprovider.h"
|
||||
source/audiohost.cpp
|
||||
source/audiohost.h
|
||||
source/media/audioclient.cpp
|
||||
source/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
|
||||
)
|
||||
|
||||
if(SMTG_MAC)
|
||||
set(audiohost_sources
|
||||
${audiohost_sources}
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm"
|
||||
)
|
||||
set_source_files_properties(
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_mac.mm" PROPERTIES
|
||||
COMPILE_FLAGS "-fobjc-arc"
|
||||
)
|
||||
set(audiohost_PLATFORM_LIBS "-framework CoreFoundation")
|
||||
get_filename_component(InfoPlistFile "resource/Info.plist" ABSOLUTE)
|
||||
set(APP_PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST ${InfoPlistFile}
|
||||
)
|
||||
elseif(SMTG_LINUX)
|
||||
set(audiohost_sources
|
||||
${audiohost_sources}
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_linux.cpp"
|
||||
)
|
||||
set(audiohost_PLATFORM_LIBS)
|
||||
set(APP_PROPERTIES)
|
||||
elseif(SMTG_WIN)
|
||||
set(audiohost_sources
|
||||
${audiohost_sources}
|
||||
"${SDK_ROOT}/public.sdk/source/vst/hosting/module_win32.cpp"
|
||||
)
|
||||
set(audiohost_PLATFORM_LIBS)
|
||||
if(MSVC)
|
||||
set(APP_PROPERTIES
|
||||
LINK_FLAGS
|
||||
"/SUBSYSTEM:console"
|
||||
)
|
||||
endif(MSVC)
|
||||
endif(SMTG_MAC)
|
||||
|
||||
set(target audiohost)
|
||||
|
||||
add_executable(${target} ${audiohost_sources})
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
${SDK_IDE_HOSTING_EXAMPLES_FOLDER}
|
||||
)
|
||||
target_compile_features(${target}
|
||||
PUBLIC
|
||||
cxx_std_17
|
||||
)
|
||||
target_link_libraries(${target}
|
||||
PRIVATE
|
||||
sdk_hosting
|
||||
${audiohost_PLATFORM_LIBS}
|
||||
${LIBJACK_LIBRARY}
|
||||
)
|
||||
target_include_directories(${target}
|
||||
PRIVATE
|
||||
${LIBJACK_INCLUDE_DIR}
|
||||
)
|
||||
if (APP_PROPERTIES)
|
||||
set_target_properties(${target}
|
||||
PROPERTIES
|
||||
${APP_PROPERTIES}
|
||||
)
|
||||
endif(APP_PROPERTIES)
|
||||
else()
|
||||
message(STATUS "[SMTG] LIBJACK_FOUND is not set.")
|
||||
endif(${LIBJACK_FOUND})
|
||||
|
||||
endif(SMTG_ENABLE_VST3_HOSTING_EXAMPLES)
|
||||
@@ -0,0 +1,18 @@
|
||||
# AudioHost Application
|
||||
|
||||
## Introduction
|
||||
|
||||
As Cross-platform source code: Simple cross-platform (only tested on Linux) host application allowing you to register a **VST 3** plug-in with Jack Server.
|
||||
|
||||
> See also: [Online Documentation](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/AudioHost.html#audiohost-application).
|
||||
|
||||
## Getting Started
|
||||
|
||||
This application is part of the VST 3 SDK package. It is created with the VST 3 SDK root project.
|
||||
|
||||
> See the top-level README of the VST 3 SDK: https://github.com/steinbergmedia/vst3sdk.git
|
||||
|
||||
## Getting Help
|
||||
|
||||
* Read through the SDK documentation on the **[VST 3 Developer Portal](https://steinbergmedia.github.io/vst3_dev_portal/pages/index.html)**
|
||||
* Ask some real people in the official **[VST 3 Developer Forum](https://forums.steinberg.net/c/developer/103)**
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>audiohost</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.steinberg.vstsdk.audiohost</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>audiohost</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2024 Steinberg. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>vst3</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>VST3 Plug-In</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Audio</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>UTImportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
</array>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>VST3 Plug-In</string>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>com.steinberg.vst3.plugin</string>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>vst3</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+158
@@ -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
|
||||
+54
@@ -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
|
||||
Vendored
+440
@@ -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
|
||||
+108
@@ -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
|
||||
+98
@@ -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
|
||||
Vendored
+39
@@ -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
|
||||
Vendored
+405
@@ -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
|
||||
+139
@@ -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
|
||||
+38
@@ -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
|
||||
+24
@@ -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
|
||||
Reference in New Issue
Block a user