How to make your own VST host
2012-03-14 17:55
435 查看
Unlike the other software from Teragon Audio, MissWatson is not open source on account of an external licensing agreement. I get lots of emails asking about the source code for MissWatson, to which I must unfortunately reply with this exact statement. However,
it is not so difficult to write your own VST host in C++, and this tutorial will teach you how to do just that.
The most difficult part of writing a VST host is loading in the plugin and returning the correct data from your host callback function. Loading the actual plugin from disk is a bit tricky, and documentation on this front is a bit sparse. However, the VST
communication protocol itself is well-documented and not so difficult to figure out. After you have established basic communication from your host to the plugin, there will be other hurdles to overcome, but the architecture of these challenges will vary greatly
depending on your requirements and platform.
This guide will only cover basic plugin loading and communication, and the language of choice here is C++. C# programmers should consider using theVST.NET framework, and I'm not sure what frameworks exist for
other languages.
Step 1: Download Requirements:
Steinberg's VST SDK, which requires you tomake a free
Steinberg Developer account. This tutorial assumes you are working with the VST 2.4 SDK.
Microsoft's Visual C++ 2008 Express (version 9), if you wish to support Microsoft Windows
Microsoft's Platform SDK. This tutorial assumes you are using the Windows Server 2003 R2 SDK.
Xcode 3.x, if you wish to support Mac OSX.
Step 2: Project Configuration
Aside from your project files, you need only to add the VST SDK headers into your project's include path. This includes the following files, which are located under the
aeffect.h
aeffectx.h
vsfxstore.h
In the course of your development, you will probably require logging, error handling, etc. To simplify the code in this tutorial, I have simply written "return -1" or "return NULL" statements, but you should consider expanding this to log some info or handle
the error.
Step 3: Loading the VST plugin
Afte
4000
r your host performs its own internal initialization routines, it is time to load the VST plugin from source. This procedure varies a bit depending on the platform, but the algorithm is fundamentally the same: find the plugin, load the dynamic library
into memory, acquire the plugin's main address, and create a VST callback connection. These callbacks are defined function pointers which you should define in one of your project's header files, and are as follows:
On Windows, VST plugins are simply dynamically linked libraries (DLL's). The code for opening a DLL library in Windows is fairly simple:
On Mac OSX, VST plugins are also dynamic libraries, but they are packaged as bundles. Your host can open these bundles through the Carbon API. On Mac OS9, VST plugins were packaged as CFM files, which has long since been deprecated, and it is highly unlikely
that any modern VST host should need to support this format.
The procedure for opening a plugin under OSX is a bit more complex, but the code should be fairly straightforward. Keep in mind that although a VST plugin can be loaded from any location on disk, they are usually stored in either
or
Note: Apple is working to deprecate Carbon, so the examples below may be a bit out of date.
You need to keep the bundle pointer around until the host is ready to unload the plugin. At this point, you call
Step 4: Setting up plugin callbacks
At this point, you should now have successfully loaded the plugin into memory, and you can now establish the plugin dispatcher callbacks:
Step 4: Plugin initialization
At this point, the plugin should be ready to go, so you can initialize it through the dispatcher handle created in the previous step:
Calling the plugin's suspend and resume methods are a bit counter-intuitive, and are done like this:
The VST protocol uses "canDo" strings to define plugin capabilities, the most common of which are defined in
following dispatcher call:
The plugin can also ask the host if it supports a given capability, which is done through the hostCallback() function defined above. The implementation of this file looks something like this:
figure out which ones are the most important through trial and error. Depending on the nature of the opcall, you will either be required to return a given integer value, call a method in the plugin's dispatcher, or fill the
type of data. The VST SDK header files have fairly good documentation specifying what you need to do depending on the opcode.
Unfortunately, I am unable to post my complete implementation of this function for MissWatson, but the actual nature of this function will vary greatly depending on the architecture of your host.
Step 8: Processing audio
In the VST SDK 2.4, processReplacing() became the new standard call; you may have to add in support to your host for the old style of process() plugins, though there aren't so many plugins out there which still do this. To have the plugin process some audio:
data, with the left channel being the first one. You should also take care to properly initialize the data in both the inputs and outputs array to zero, or else you can get static or other random noise in the processed signal.
Step 9: Sending MIDI messages
Likewise, processing MIDI events is very similar to processing audio:
The above events array should be allocated and properly initialized by the host to contain the MIDI events which the plugin will receive. The VstEvent structure is defined in
all of which are deprecated except for
Note that the plugin must support the "receiveVstMidiEvent" canDo in order to process MIDI.
Conclusion
At this point, you should have a basic working host capable of loading and communicating with a VST plugin. As you continue your development, take care to thoroughly read the VST SDK header files and other associated documentation, as they will provide you
with further hints as to the correct implementation. Also, you should take time to create good logging facilities in your host, particularly in the hostCallback() method, as most plugin incompatibilities are usually triggered from some miscommunication there.
it is not so difficult to write your own VST host in C++, and this tutorial will teach you how to do just that.
The most difficult part of writing a VST host is loading in the plugin and returning the correct data from your host callback function. Loading the actual plugin from disk is a bit tricky, and documentation on this front is a bit sparse. However, the VST
communication protocol itself is well-documented and not so difficult to figure out. After you have established basic communication from your host to the plugin, there will be other hurdles to overcome, but the architecture of these challenges will vary greatly
depending on your requirements and platform.
This guide will only cover basic plugin loading and communication, and the language of choice here is C++. C# programmers should consider using theVST.NET framework, and I'm not sure what frameworks exist for
other languages.
Step 1: Download Requirements:
Steinberg's VST SDK, which requires you tomake a free
Steinberg Developer account. This tutorial assumes you are working with the VST 2.4 SDK.
Microsoft's Visual C++ 2008 Express (version 9), if you wish to support Microsoft Windows
Microsoft's Platform SDK. This tutorial assumes you are using the Windows Server 2003 R2 SDK.
Xcode 3.x, if you wish to support Mac OSX.
Step 2: Project Configuration
Aside from your project files, you need only to add the VST SDK headers into your project's include path. This includes the following files, which are located under the
vstsdk2.4/pluginterfaces/vst2.xdirectory:
aeffect.h
aeffectx.h
vsfxstore.h
In the course of your development, you will probably require logging, error handling, etc. To simplify the code in this tutorial, I have simply written "return -1" or "return NULL" statements, but you should consider expanding this to log some info or handle
the error.
Step 3: Loading the VST plugin
Afte
4000
r your host performs its own internal initialization routines, it is time to load the VST plugin from source. This procedure varies a bit depending on the platform, but the algorithm is fundamentally the same: find the plugin, load the dynamic library
into memory, acquire the plugin's main address, and create a VST callback connection. These callbacks are defined function pointers which you should define in one of your project's header files, and are as follows:
#include "aeffectx.h" // C callbacks extern "C" { // Main host callback VstIntPtr VSTCALLBACK hostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); } // Plugin's entry point typedef AEffect *(*vstPluginFuncPtr)(audioMasterCallback host); // Plugin's dispatcher function typedef VstIntPtr (*dispatcherFuncPtr)(AEffect *effect, VstInt32 opCode, VstInt32 index, VstInt32 value, void *ptr, float opt); // Plugin's getParameter() method typedef float (*getParameterFuncPtr)(AEffect *effect, VstInt32 index); // Plugin's setParameter() method typedef void (*setParameterFuncPtr)(AEffect *effect, VstInt32 index, float value); // Plugin's processEvents() method typedef VstInt32 (*processEventsFuncPtr)(VstEvents *events); // Plugin's process() method typedef void (*processFuncPtr)(AEffect *effect, float **inputs, float **outputs, VstInt32 sampleFrames);Step 3a: Windows
On Windows, VST plugins are simply dynamically linked libraries (DLL's). The code for opening a DLL library in Windows is fairly simple:
AEffect* loadPlugin() { AEffect *plugin = NULL; char *vstPath = "c:\\wherever\\the\\plugin\\is\\located.vst"; modulePtr = LoadLibrary(vstPath); if(modulePtr == NULL) { printf("Failed trying to load VST from '%s', error %d\n", vstPath, GetLastError()); return NULL; } vstPluginFuncPtr mainEntryPoint = (vstPluginFuncPtr)GetProcAddress(modulePtr, "VSTPluginMain"); // Instantiate the plugin plugin = mainEntryPoint(hostCallback); return plugin; }Step 3b: Loading the VST on Mac OSX
On Mac OSX, VST plugins are also dynamic libraries, but they are packaged as bundles. Your host can open these bundles through the Carbon API. On Mac OS9, VST plugins were packaged as CFM files, which has long since been deprecated, and it is highly unlikely
that any modern VST host should need to support this format.
The procedure for opening a plugin under OSX is a bit more complex, but the code should be fairly straightforward. Keep in mind that although a VST plugin can be loaded from any location on disk, they are usually stored in either
/Library/Audio/Plug-Ins/VST
or
$HOME/Library/Audio/Plug-Ins/VST, where
$HOMErefers to the user's home directory.
Note: Apple is working to deprecate Carbon, so the examples below may be a bit out of date.
AEffect* loadPlugin() { AEffect *plugin = NULL; audioMasterCallback hostCallbackFuncPtr = hostCallback; char *pluginPath = "/wherever/the/plugin/is/located.vst"; // Create a path to the bundle CFStringRef pluginPathStringRef = CFStringCreateWithCString(NULL, pluginPath, kCFStringEncodingASCII); CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pluginPathStringRef, kCFURLPOSIXPathStyle, true); if(bundleUrl == NULL) { printf("Couldn't make URL reference for plugin\n"); return NULL; } // Open the bundle CFBundleRef bundle; bundle = CFBundleCreate(kCFAllocatorDefault, bundleUrl); if(bundle == NULL) { printf("Couldn't create bundle reference\n"); CFRelease(pluginPathStringRef); CFRelease(bundleUrl); return NULL; } vstPluginFuncPtr mainEntryPoint = NULL; mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("VSTPluginMain")); // VST plugins previous to the 2.4 SDK used main_macho for the entry point name if(mainEntryPoint == NULL) { mainEntryPoint = (vstPluginFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("main_macho")); } if(mainEntryPoint == NULL) { printf("Couldn't get a pointer to plugin's main()\n"); CFBundleUnloadExecutable(bundle); CFRelease(bundle); return NULL; } plugin = mainEntryPoint(hostCallback); if(plugin == NULL) { printf("Plugin's main() returns null\n"); CFBundleUnloadExecutable(bundle); CFRelease(bundle); return NULL; } // Clean up CFRelease(pluginPathStringRef); CFRelease(bundleUrl); return plugin; }
You need to keep the bundle pointer around until the host is ready to unload the plugin. At this point, you call
CFBundleUnloadExecutableand then
CFReleaseon the bundle's reference.
Step 4: Setting up plugin callbacks
At this point, you should now have successfully loaded the plugin into memory, and you can now establish the plugin dispatcher callbacks:
int initPlugin(AEffect *plugin) { // Check plugin's magic number // If incorrect, then the file either was not loaded properly, is not a real // VST plugin, or is otherwise corrupt. if(plugin->magic != kEffectMagic) { printf("Plugin's magic number is bad\n"); return -1; } // Create dispatcher handle dispatcherFuncPtr dispatcher = (dispatcherFuncPtr)(plugin->dispatcher); // Set up plugin callback functions plugin->getParameter = (getParameterFuncPtr)plugin->getParameter; plugin->processReplacing = (processFuncPtr)plugin->processReplacing; plugin->setParameter = (setParameterFuncPtr)plugin->setParameter; return plugin; }
Step 4: Plugin initialization
At this point, the plugin should be ready to go, so you can initialize it through the dispatcher handle created in the previous step:
void initPlugin(AEffect *plugin) { dispatcher(plugin, effOpen, 0, 0, NULL, 0.0f); // Set some default properties float sampleRate = 44100.0f; dispatcher(plugin, effSetSampleRate, 0, 0, NULL, sampleRate); int blocksize = 512; dispatcher(plugin, effSetBlockSize, 0, blocksize, NULL, 0.0f); resume(); }Step 5: Suspending and resuming
Calling the plugin's suspend and resume methods are a bit counter-intuitive, and are done like this:
void resumePlugin(AEffect *plugin) { dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f); } void suspendPlugin(AEffect *plugin) { dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0.0f); }Step 6: Plugin capabilities
The VST protocol uses "canDo" strings to define plugin capabilities, the most common of which are defined in
audioeffectx.cppin the PlugCanDos namespace near the top of the file. To ask a plugin if it supports one of these capabilities, make the
following dispatcher call:
bool canPluginDo(char *canDoString) { return (dispatcher(plugin, effCanDo, 0, 0, (void*)canDoString, 0.0f) > 0); }Step 7: Host capabilities
The plugin can also ask the host if it supports a given capability, which is done through the hostCallback() function defined above. The implementation of this file looks something like this:
extern "C" { VstIntPtr VSTCALLBACK hostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) { switch(opcode) { case audioMasterVersion: return 2400; case audioMasterIdle: effect->dispatcher(effect, effEditIdle, 0, 0, 0, 0); // Handle other opcodes here... there will be lots of them default: printf("Plugin requested value of opcode %d\n", opcode); break; } } }The full list of opcodes is defined in
aeffect.h(for the VST 1.x protocol) and
aeffectx.h(for VST 2.x protocol). There are a lot of opcodes, and your application doesn't need to support them all, but you will soon
figure out which ones are the most important through trial and error. Depending on the nature of the opcall, you will either be required to return a given integer value, call a method in the plugin's dispatcher, or fill the
*ptrpointer with some
type of data. The VST SDK header files have fairly good documentation specifying what you need to do depending on the opcode.
Unfortunately, I am unable to post my complete implementation of this function for MissWatson, but the actual nature of this function will vary greatly depending on the architecture of your host.
Step 8: Processing audio
In the VST SDK 2.4, processReplacing() became the new standard call; you may have to add in support to your host for the old style of process() plugins, though there aren't so many plugins out there which still do this. To have the plugin process some audio:
void processAudio(AEffect *plugin, float **inputs, float **outputs, long numFrames) { // Note: If you are processing an instrument, you should probably zero out the input // channels first to avoid any accidental noise. If you are processing an effect, you // should probably zero the values in the output channels. See the silenceChannel() // method below. plugin->processReplacing(plugin, inputs, outputs, numFrames); } void silenceChannel(float **channelData, int numChannels, long numFrames) { for(int channel = 0; channels < numChannels; ++channel) { for(long frame = 0; frame < numFrames; ++frame) { channelData[channel][frame] = 0.0f; } } }Note that you need to properly allocate the arrays for the audio inputs and outputs depending on your blocksize and channel count. Like a regular VST plugin, this structure is simply a de-interlaced array ordered by channel of the sample block
data, with the left channel being the first one. You should also take care to properly initialize the data in both the inputs and outputs array to zero, or else you can get static or other random noise in the processed signal.
Step 9: Sending MIDI messages
Likewise, processing MIDI events is very similar to processing audio:
void processMidi(AEffect *plugin, VstEvents *events) { dispatcher(plugin, effProcessEvents, 0, 0, events, 0.0f); }
The above events array should be allocated and properly initialized by the host to contain the MIDI events which the plugin will receive. The VstEvent structure is defined in
aeffectx.h, and there you will also find the respective VstEvent types,
all of which are deprecated except for
kVstMidiTypeand
kVstSysExType.
Note that the plugin must support the "receiveVstMidiEvent" canDo in order to process MIDI.
Conclusion
At this point, you should have a basic working host capable of loading and communicating with a VST plugin. As you continue your development, take care to thoroughly read the VST SDK header files and other associated documentation, as they will provide you
with further hints as to the correct implementation. Also, you should take time to create good logging facilities in your host, particularly in the hostCallback() method, as most plugin incompatibilities are usually triggered from some miscommunication there.
相关文章推荐
- 源自人脑的神奇算法 -- 读《How to make your own neural network》有感
- How to make your own maps/tiles
- c++ how to make your own class a valid key type for std::map?
- How To MakeUp With Your Girlfriend After A Fight
- How to register your own or the third ActiveX Control to the target PC using InstallShield 11.5 Pre Edition?
- How To Create Your Own Smarty Function
- SAP CRM How to Create your own BOL Object for webclient
- [GUIDE] How to make a nandroid backup directly to your computer without using sdcard
- how to write your annotation types and make use of built-in annotations to control their behavior
- How to Build Your Own Blockchain Part 3 — Writing Nodes that Mine and Talk
- How to get your very own RStudio Server and Shiny Server with DigitalOcean
- How To Make Your Websites Faster On Mobile Devices
- How to install your own zImage
- How To Build Your Own IPTV-VoD System
- How to Build Your Own Blockchain Part 4.1 — Bitcoin Proof of Work Difficulty Explained
- 如何调整Dreamhost主机PHP上传尺寸的限制/How to change the maximal size of uploading in your Dreamhost
- How to Build Your Own Blockchain Part 4.2 — Ethereum Proof of Work Difficulty Explained
- unity kinect2 怎么使用姿势或者使用你自己的How to Use Gestures or Create Your Own Ones
- The Defining Decade-Why Your Twenties Matter--And How to Make the Most of Them Now
- How to create your own api with ACL in Magento