summaryrefslogtreecommitdiff
path: root/source/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'source/plugin')
-rw-r--r--source/plugin/Plugin.c205
-rw-r--r--source/plugin/Plugin.h195
-rw-r--r--source/plugin/PluginChain.c420
-rw-r--r--source/plugin/PluginChain.h165
-rw-r--r--source/plugin/PluginGain.c133
-rw-r--r--source/plugin/PluginGain.h47
-rw-r--r--source/plugin/PluginLimiter.c121
-rw-r--r--source/plugin/PluginLimiter.h37
-rw-r--r--source/plugin/PluginPassthru.c105
-rw-r--r--source/plugin/PluginPassthru.h37
-rw-r--r--source/plugin/PluginPreset.c104
-rw-r--r--source/plugin/PluginPreset.h111
-rw-r--r--source/plugin/PluginPresetFxp.c280
-rw-r--r--source/plugin/PluginPresetFxp.h74
-rw-r--r--source/plugin/PluginPresetInternalProgram.c74
-rw-r--r--source/plugin/PluginPresetInternalProgram.h40
-rw-r--r--source/plugin/PluginSilence.c105
-rw-r--r--source/plugin/PluginSilence.h37
-rw-r--r--source/plugin/PluginVst2x.cpp897
-rw-r--r--source/plugin/PluginVst2x.h91
-rw-r--r--source/plugin/PluginVst2xHostCallback.cpp433
-rw-r--r--source/plugin/PluginVst2xHostCallback.h45
-rw-r--r--source/plugin/PluginVst2xId.c101
-rw-r--r--source/plugin/PluginVst2xId.h45
-rw-r--r--source/plugin/PluginVst2xLinux.cpp113
-rw-r--r--source/plugin/PluginVst2xMac.cpp136
-rw-r--r--source/plugin/PluginVst2xWindows.cpp114
27 files changed, 4265 insertions, 0 deletions
diff --git a/source/plugin/Plugin.c b/source/plugin/Plugin.c
new file mode 100644
index 0000000..a307e8e
--- /dev/null
+++ b/source/plugin/Plugin.c
@@ -0,0 +1,205 @@
+//
+// Plugin.c - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "audio/AudioSettings.h"
+#include "logging/EventLogger.h"
+#include "plugin/Plugin.h"
+#include "plugin/PluginGain.h"
+#include "plugin/PluginLimiter.h"
+#include "plugin/PluginPassthru.h"
+#include "plugin/PluginVst2x.h"
+#include "plugin/PluginSilence.h"
+
+static PluginInterfaceType _guessPluginInterfaceType(const CharString pluginName, const CharString pluginSearchRoot)
+{
+ PluginInterfaceType pluginType = PLUGIN_TYPE_INVALID;
+
+ if (pluginName == NULL || charStringIsEmpty(pluginName)) {
+ logError("Attempt to guess plugin with empty name");
+ return pluginType;
+ }
+
+ logDebug("Trying to find plugin '%s'", pluginName->data);
+
+ if (pluginVst2xExists(pluginName, pluginSearchRoot)) {
+ logInfo("Plugin '%s' is of type VST2.x", pluginName->data);
+ pluginType = PLUGIN_TYPE_VST_2X;
+ } else if (!strncmp(INTERNAL_PLUGIN_PREFIX, pluginName->data, strlen(INTERNAL_PLUGIN_PREFIX))) {
+ logInfo("Plugin '%s' is an internal plugin", pluginName->data);
+ pluginType = PLUGIN_TYPE_INTERNAL;
+ } else {
+ logError("Plugin '%s' could not be found", pluginName->data);
+ }
+
+ return pluginType;
+}
+
+static void _logPluginLocation(const CharString location)
+{
+ logInfo("Location (internal):", location->data);
+}
+
+static void _listAvailablePluginsInternal(void)
+{
+ CharString internalLocation = newCharStringWithCString("(Internal)");
+ _logPluginLocation(internalLocation);
+ logInfo(" %s", kInternalPluginPassthruName);
+ logInfo(" %s", kInternalPluginSilenceName);
+ freeCharString(internalLocation);
+}
+
+void listAvailablePlugins(const CharString pluginRoot)
+{
+ listAvailablePluginsVst2x(pluginRoot);
+ _listAvailablePluginsInternal();
+}
+
+/**
+ * Used to check if an internal plugin (ie, starting with "mrs_" matches an
+ * internal plugin name. This function only compares to the length of the
+ * internal name, so that extra parameters can be appended to the end of the
+ * plugin name argument.
+ * @param pluginName Plugin name to check
+ * @param internalName Internal name to compare against
+ * @return True if the plugin is a match
+ */
+static boolByte _internalPluginNameMatches(const CharString pluginName, const char *internalName)
+{
+ return (boolByte)(strncmp(pluginName->data, internalName, strlen(internalName)) == 0);
+}
+
+// Plugin newPlugin(PluginInterfaceType interfaceType, const CharString pluginName, const CharString pluginLocation) {
+Plugin pluginFactory(const CharString pluginName, const CharString pluginRoot)
+{
+ PluginInterfaceType interfaceType = _guessPluginInterfaceType(pluginName, pluginRoot);
+
+ if (interfaceType == PLUGIN_TYPE_INVALID) {
+ return NULL;
+ }
+
+ switch (interfaceType) {
+ case PLUGIN_TYPE_VST_2X:
+ return newPluginVst2x(pluginName, pluginRoot);
+
+ case PLUGIN_TYPE_INTERNAL:
+ if (_internalPluginNameMatches(pluginName, kInternalPluginGainName)) {
+ return newPluginGain(pluginName);
+ } else if (_internalPluginNameMatches(pluginName, kInternalPluginLimiterName)) {
+ return newPluginLimiter(pluginName);
+ } else if (_internalPluginNameMatches(pluginName, kInternalPluginPassthruName)) {
+ return newPluginPassthru(pluginName);
+ } else if (_internalPluginNameMatches(pluginName, kInternalPluginSilenceName)) {
+ return newPluginSilence(pluginName);
+ } else {
+ logError("'%s' is not a recognized internal plugin", pluginName->data);
+ return NULL;
+ }
+
+ default:
+ logError("Could not find plugin type for '%s'", pluginName->data);
+ return NULL;
+ }
+}
+
+boolByte openPlugin(Plugin self)
+{
+ if (self == NULL) {
+ logError("There is no plugin to open");
+ return false;
+ } else if (self->isOpen) {
+ return true;
+ }
+
+ if (!self->openPlugin(self)) {
+ logError("Plugin '%s' could not be opened", self->pluginName->data);
+ return false;
+ } else {
+ self->inputBuffer = newSampleBuffer((ChannelCount)self->getSetting(self, PLUGIN_NUM_INPUTS), getBlocksize());
+ self->outputBuffer = newSampleBuffer((ChannelCount)self->getSetting(self, PLUGIN_NUM_OUTPUTS), getBlocksize());
+ self->isOpen = true;
+ }
+
+ return true;
+}
+
+boolByte closePlugin(Plugin self)
+{
+ if (self == NULL) {
+ logError("There is no plugin to open");
+ return false;
+ }
+
+ self->closePlugin(self);
+ freeSampleBuffer(self->inputBuffer);
+ self->inputBuffer = NULL;
+ freeSampleBuffer(self->outputBuffer);
+ self->outputBuffer = NULL;
+ self->isOpen = false;
+ return true;
+}
+
+Plugin _newPlugin(PluginInterfaceType interfaceType, PluginType pluginType)
+{
+ Plugin plugin = (Plugin)malloc(sizeof(PluginMembers));
+
+ plugin->interfaceType = interfaceType;
+ plugin->pluginType = pluginType;
+ plugin->pluginName = newCharString();
+ plugin->pluginLocation = newCharString();
+ plugin->pluginAbsolutePath = newCharString();
+
+ plugin->inputBuffer = NULL;
+ plugin->outputBuffer = NULL;
+ plugin->isOpen = false;
+
+ return plugin;
+}
+
+void freePlugin(Plugin self)
+{
+ if (self != NULL) {
+ if (self->extraData != NULL) {
+ self->freePluginData(self->extraData);
+ free(self->extraData);
+ }
+
+ if (self->inputBuffer != NULL) {
+ freeSampleBuffer(self->inputBuffer);
+ }
+ if (self->outputBuffer != NULL) {
+ freeSampleBuffer(self->outputBuffer);
+ }
+ freeCharString(self->pluginName);
+ freeCharString(self->pluginLocation);
+ freeCharString(self->pluginAbsolutePath);
+ free(self);
+ }
+}
diff --git a/source/plugin/Plugin.h b/source/plugin/Plugin.h
new file mode 100644
index 0000000..1fb9519
--- /dev/null
+++ b/source/plugin/Plugin.h
@@ -0,0 +1,195 @@
+//
+// Plugin.h - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_Plugin_h
+#define MrsWatson_Plugin_h
+
+#include "audio/SampleBuffer.h"
+#include "base/CharString.h"
+#include "base/LinkedList.h"
+
+// All internal plugins should start with this string
+#define INTERNAL_PLUGIN_PREFIX "mrs_"
+
+typedef enum {
+ PLUGIN_TYPE_INVALID,
+ PLUGIN_TYPE_VST_2X,
+ PLUGIN_TYPE_INTERNAL,
+ NUM_PLUGIN_INTERFACE_TYPES
+} PluginInterfaceType;
+
+typedef enum {
+ PLUGIN_TYPE_UNKNOWN,
+ PLUGIN_TYPE_UNSUPPORTED,
+ PLUGIN_TYPE_EFFECT,
+ PLUGIN_TYPE_INSTRUMENT,
+ NUM_PLUGIN_TYPES
+} PluginType;
+
+typedef enum {
+ PLUGIN_SETTING_TAIL_TIME_IN_MS,
+ PLUGIN_NUM_INPUTS,
+ PLUGIN_NUM_OUTPUTS,
+ PLUGIN_INITIAL_DELAY,
+ NUM_PLUGIN_SETTINGS
+} PluginSetting;
+
+/**
+ * Called when a plugin is to be opened. This includes loading any dynamic
+ * libraries into memory initializing the plugin.
+ * @param pluginPtr self
+ */
+typedef boolByte (*PluginOpenFunc)(void *pluginPtr);
+/**
+ * Called when the plugin should display some generic info about itself. This
+ * may be a list of supported parameters or programs, or any other information
+ * relevant to the user. See the implementation in the PluginVST2x class for an
+ * example.
+ * @param pluginPtr self
+ */
+typedef void (*PluginDisplayInfoFunc)(void *pluginPtr);
+/**
+ * Used to gather information about the plugin, such as the number of inputs and
+ * outputs. See the PluginSetting enum for examples of information which may be
+ * requested.
+ * @param pluginPtr self
+ * @param pluginSetting Setting to query
+ */
+typedef int (*PluginGetSettingFunc)(void *pluginPtr, PluginSetting pluginSetting);
+/**
+ * Called with the host wants to process a block of audio samples.
+ * @param pluginPtr self
+ * @param inputs Block of input samples to process
+ * @param outputs Block where output samples shall be written
+ */
+typedef void (*PluginProcessAudioFunc)(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs);
+/**
+ * Called the host wants to process MIDI events. This will be called directly
+ * before the call to process audio.
+ * @param pluginPtr self
+ * @param midiEvents List of events to process. This should be non-empty, as
+ * this function is not called when there are no events to process.
+ */
+typedef void (*PluginProcessMidiEventsFunc)(void *pluginPtr, LinkedList midiEvents);
+/**
+ * Set a parameter within a plugin
+ * @param pluginPtr self
+ * @param index Parameter index
+ * @param value New value
+ */
+typedef boolByte (*PluginSetParameterFunc)(void *pluginPtr, unsigned int index, float value);
+/**
+ * Called once before audio processing begins. Some interfaces provide hooks for
+ * a plugin to prepare itself before audio blocks are sent to it.
+ * @param pluginPtr self
+ */
+typedef void (*PluginPrepareForProcessingFunc)(void *pluginPtr);
+/**
+ * Called when the plugin is to be uninitialized and closed.
+ * @param pluginPtr self
+ */
+typedef void (*PluginCloseFunc)(void *pluginPtr);
+/**
+ * Pointer to the free routine for the plugin interface
+ * @param pluginPtr self
+ */
+typedef void (*FreePluginDataFunc)(void *pluginDataPtr);
+
+typedef struct {
+ PluginInterfaceType interfaceType;
+ PluginType pluginType;
+ CharString pluginName;
+ CharString pluginLocation;
+ CharString pluginAbsolutePath;
+
+ PluginOpenFunc openPlugin;
+ PluginDisplayInfoFunc displayInfo;
+ PluginGetSettingFunc getSetting;
+ PluginProcessAudioFunc processAudio;
+ PluginProcessMidiEventsFunc processMidiEvents;
+ PluginSetParameterFunc setParameter;
+ PluginPrepareForProcessingFunc prepareForProcessing;
+ PluginCloseFunc closePlugin;
+ FreePluginDataFunc freePluginData;
+ SampleBuffer inputBuffer;
+ SampleBuffer outputBuffer;
+ boolByte isOpen;
+
+ void *extraData;
+} PluginMembers;
+
+/**
+ * One of the base classes of the API, this represents a plugin. Currently only
+ * instrument and effect plugins are supported.
+ */
+typedef PluginMembers *Plugin;
+
+/**
+ * Create a plugin instance. The plugin interface type will be automatically
+ * determined.
+ * @param pluginName Plugin name. For plugins which supports loading directly
+ * from the filesystem, this argument may also be an absolute path.
+ * @param pluginRoot User-provided search root path. May be NULL or empty.
+ * @return Initialized object, or NULL if no matching plugin was found
+ */
+Plugin pluginFactory(const CharString pluginName, const CharString pluginRoot);
+
+/**
+ * List all known plugins of all known types on the system.
+ * @param pluginRoot User-provided search root path
+ */
+void listAvailablePlugins(const CharString pluginRoot);
+
+/**
+ * Open a plugin.
+ * @param self
+ */
+boolByte openPlugin(Plugin self);
+
+/**
+ * Close a plugin.
+ * @param self
+ */
+boolByte closePlugin(Plugin self);
+
+/**
+* Create a new plugin. Considered "protected", only subclasses of Plugin should
+* directly call this.
+* @param interfaceType Plugin interface type
+* @param pluginType Plugin type
+* @return Plugin initialized base Plugin struct
+*/
+Plugin _newPlugin(PluginInterfaceType interfaceType, PluginType pluginType);
+
+/**
+ * Release a plugin and all of its associated resources. Note that the plugin
+ * must be closed before this is called, or else resources will be leaked.
+ * @param self
+ */
+void freePlugin(Plugin self);
+
+#endif
diff --git a/source/plugin/PluginChain.c b/source/plugin/PluginChain.c
new file mode 100644
index 0000000..bbf2f9e
--- /dev/null
+++ b/source/plugin/PluginChain.c
@@ -0,0 +1,420 @@
+//
+// PluginChain.c - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "logging/EventLogger.h"
+#include "plugin/PluginChain.h"
+#include "audio/AudioSettings.h"
+
+PluginChain pluginChainInstance = NULL;
+
+PluginChain getPluginChain(void)
+{
+ return pluginChainInstance;
+}
+
+void initPluginChain(void)
+{
+ pluginChainInstance = (PluginChain)malloc(sizeof(PluginChainMembers));
+
+ pluginChainInstance->numPlugins = 0;
+ pluginChainInstance->plugins = (Plugin *)malloc(sizeof(Plugin) * MAX_PLUGINS);
+ pluginChainInstance->presets = (PluginPreset *)malloc(sizeof(PluginPreset) * MAX_PLUGINS);
+ pluginChainInstance->audioTimers = (TaskTimer *)malloc(sizeof(TaskTimer) * MAX_PLUGINS);
+ pluginChainInstance->midiTimers = (TaskTimer *)malloc(sizeof(TaskTimer) * MAX_PLUGINS);
+
+ pluginChainInstance->_realtime = false;
+ pluginChainInstance->_realtimeTimer = NULL;
+}
+
+boolByte pluginChainAppend(PluginChain self, Plugin plugin, PluginPreset preset)
+{
+ if (plugin == NULL) {
+ return false;
+ } else if (self->numPlugins + 1 >= MAX_PLUGINS) {
+ logError("Could not add plugin '%s', maximum number reached", plugin->pluginName->data);
+ return false;
+ } else if (!openPlugin(plugin)) {
+ return false;
+ } else {
+ self->plugins[self->numPlugins] = plugin;
+ self->presets[self->numPlugins] = preset;
+ self->audioTimers[self->numPlugins] = newTaskTimer(plugin->pluginName, "Audio Processing");
+ self->midiTimers[self->numPlugins] = newTaskTimer(plugin->pluginName, "MIDI Processing");
+ self->numPlugins++;
+ return true;
+ }
+}
+
+boolByte pluginChainAddFromArgumentString(PluginChain pluginChain, const CharString argumentString, const CharString userSearchPath)
+{
+ // Expect a semicolon-separated string of plugins with comma separators for preset names
+ // Example: plugin1,preset1name;plugin2,preset2name
+ char *substringStart;
+ char *pluginSeparator;
+ char *endChar;
+ CharString pluginNameBuffer = NULL;
+ CharString presetNameBuffer = NULL;
+ char *presetSeparator;
+ PluginPreset preset;
+ Plugin plugin;
+ size_t substringLength;
+
+ if (charStringIsEmpty(argumentString)) {
+ logWarn("Plugin chain string is empty");
+ return false;
+ }
+
+ substringStart = argumentString->data;
+ pluginSeparator = strchr(argumentString->data, CHAIN_STRING_PLUGIN_SEPARATOR);
+ endChar = argumentString->data + strlen(argumentString->data);
+
+ do {
+ if (pluginSeparator == NULL) {
+ substringLength = strlen(argumentString->data);
+ } else {
+ substringLength = pluginSeparator - substringStart;
+ }
+
+ pluginNameBuffer = newCharString();
+ strncpy(pluginNameBuffer->data, substringStart, substringLength);
+
+ // Look for the separator for presets to load into these plugins
+ presetNameBuffer = newCharString();
+ presetSeparator = strchr(pluginNameBuffer->data, CHAIN_STRING_PROGRAM_SEPARATOR);
+
+ if (presetSeparator != NULL) {
+ // Null-terminate this string to force it to end, then extract preset name from next char
+ *presetSeparator = '\0';
+ strncpy(presetNameBuffer->data, presetSeparator + 1, strlen(presetSeparator + 1));
+ }
+
+ // Find preset for this plugin (if given)
+ preset = NULL;
+
+ if (strlen(presetNameBuffer->data) > 0) {
+ logInfo("Opening preset '%s' for plugin", presetNameBuffer->data);
+ preset = pluginPresetFactory(presetNameBuffer);
+ }
+
+ // Guess the plugin type from the file extension, search root, etc.
+ plugin = pluginFactory(pluginNameBuffer, userSearchPath);
+
+ if (plugin != NULL) {
+ if (!pluginChainAppend(pluginChain, plugin, preset)) {
+ logError("Plugin '%s' could not be added to the chain", pluginNameBuffer->data);
+ free(pluginNameBuffer);
+ free(presetNameBuffer);
+ return false;
+ }
+ }
+
+ if (pluginSeparator == NULL) {
+ break;
+ } else {
+ substringStart = pluginSeparator + 1;
+ pluginSeparator = strchr(pluginSeparator + 1, CHAIN_STRING_PLUGIN_SEPARATOR);
+ }
+ } while (substringStart < endChar);
+
+ freeCharString(pluginNameBuffer);
+ freeCharString(presetNameBuffer);
+ return true;
+}
+
+static boolByte _loadPresetForPlugin(Plugin plugin, PluginPreset preset)
+{
+ if (pluginPresetIsCompatibleWith(preset, plugin)) {
+ if (!preset->openPreset(preset)) {
+ logError("Could not open preset '%s'", preset->presetName->data);
+ return false;
+ }
+
+ if (!preset->loadPreset(preset, plugin)) {
+ logError("Could not load preset '%s' in plugin '%s'", preset->presetName->data, plugin->pluginName->data);
+ return false;
+ }
+
+ logInfo("Loaded preset '%s' in plugin '%s'", preset->presetName->data, plugin->pluginName->data);
+ return true;
+ } else {
+ logError("Preset '%s' is not a compatible format for plugin", preset->presetName->data);
+ return false;
+ }
+}
+
+ReturnCodes pluginChainInitialize(PluginChain pluginChain)
+{
+ Plugin plugin;
+ PluginPreset preset;
+ unsigned int i;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ plugin = pluginChain->plugins[i];
+
+ if (!openPlugin(plugin)) {
+ return RETURN_CODE_PLUGIN_ERROR;
+ } else {
+ if (i > 0 && plugin->pluginType == PLUGIN_TYPE_INSTRUMENT) {
+ logError("Instrument plugin '%s' must be first in the chain", plugin->pluginName->data);
+ return RETURN_CODE_INVALID_PLUGIN_CHAIN;
+ } else if (plugin->pluginType == PLUGIN_TYPE_UNKNOWN) {
+ logError("Plugin '%s' has unknown type; It was probably not loaded correctly", plugin->pluginName->data);
+ return RETURN_CODE_PLUGIN_ERROR;
+ } else if (plugin->pluginType == PLUGIN_TYPE_UNSUPPORTED) {
+ logError("Plugin '%s' is of unsupported type", plugin->pluginName->data);
+ return RETURN_CODE_PLUGIN_ERROR;
+ }
+
+ preset = pluginChain->presets[i];
+
+ if (preset != NULL) {
+ if (!_loadPresetForPlugin(plugin, preset)) {
+ return RETURN_CODE_INVALID_ARGUMENT;
+ }
+ }
+ }
+ }
+
+ return RETURN_CODE_SUCCESS;
+}
+
+void pluginChainInspect(PluginChain pluginChain)
+{
+ Plugin plugin;
+ unsigned int i;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ plugin = pluginChain->plugins[i];
+ plugin->displayInfo(plugin);
+ }
+}
+
+void pluginChainPrepareForProcessing(PluginChain self)
+{
+ Plugin plugin;
+ unsigned int i;
+
+ for (i = 0; i < self->numPlugins; i++) {
+ plugin = self->plugins[i];
+ plugin->prepareForProcessing(plugin);
+ }
+}
+
+int pluginChainGetMaximumTailTimeInMs(PluginChain pluginChain)
+{
+ Plugin plugin;
+ int tailTime;
+ int maxTailTime = 0;
+ unsigned int i;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ plugin = pluginChain->plugins[i];
+ tailTime = plugin->getSetting(plugin, PLUGIN_SETTING_TAIL_TIME_IN_MS);
+
+ if (tailTime > maxTailTime) {
+ maxTailTime = tailTime;
+ }
+ }
+
+ return maxTailTime;
+}
+
+unsigned long pluginChainGetProcessingDelay(PluginChain self)
+{
+ unsigned long processingDelay = 0;
+ unsigned int i;
+
+ for (i = 0; i < self->numPlugins; i++) {
+ Plugin plugin = self->plugins[i];
+ processingDelay += plugin->getSetting(plugin, PLUGIN_INITIAL_DELAY);
+ }
+
+ return processingDelay;
+}
+
+typedef struct {
+ Plugin plugin;
+ boolByte success;
+} _PluginChainSetParameterPassData;
+
+void _pluginChainSetParameter(void *item, void *userData)
+{
+ // Expect that the linked list contains CharStrings, single that is what is
+ // being given from the command line.
+ char *parameterValue = (char *)item;
+ _PluginChainSetParameterPassData *passData = (_PluginChainSetParameterPassData *)userData;
+ Plugin plugin = passData->plugin;
+ char *comma = NULL;
+ int index;
+ float value;
+
+ // If a previous attempt to set a parameter failed, then return right away
+ // since this method will return false anyways.
+ if (!passData->success) {
+ return;
+ }
+
+ // TODO: Need a "pair" type, this string parsing is done several times in the codebase
+ comma = strchr(parameterValue, ',');
+
+ if (comma == NULL) {
+ logError("Malformed parameter string, see --help parameter for usage");
+ return;
+ }
+
+ *comma = '\0';
+ index = (int)strtod(parameterValue, NULL);
+ value = (float)strtod(comma + 1, NULL);
+ logDebug("Set parameter %d to %f", index, value);
+ passData->success = plugin->setParameter(plugin, (unsigned int)index, value);
+}
+
+boolByte pluginChainSetParameters(PluginChain self, const LinkedList parameters)
+{
+ _PluginChainSetParameterPassData passData;
+ passData.plugin = self->plugins[0];
+ passData.success = true;
+ logDebug("Setting parameters on head plugin in chain");
+ linkedListForeach(parameters, _pluginChainSetParameter, &passData);
+ return passData.success;
+}
+
+void pluginChainSetRealtime(PluginChain self, boolByte realtime)
+{
+ self->_realtime = realtime;
+
+ if (realtime) {
+ self->_realtimeTimer = newTaskTimerWithCString("PluginChain", "Realtime");
+ } else if (self->_realtimeTimer) {
+ freeTaskTimer(self->_realtimeTimer);
+ }
+}
+
+void pluginChainProcessAudio(PluginChain pluginChain, SampleBuffer inBuffer, SampleBuffer outBuffer)
+{
+ Plugin plugin;
+ unsigned int i;
+ double processingTimeInMs;
+ double totalProcessingTimeInMs;
+ const double maxProcessingTimeInMs = inBuffer->blocksize * 1000.0 / getSampleRate();
+
+ if (pluginChain->_realtime) {
+ taskTimerStart(pluginChain->_realtimeTimer);
+ }
+
+ SampleBuffer formerOutputBuffer = inBuffer;
+ SampleBuffer nextInputBuffer = NULL;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ plugin = pluginChain->plugins[i];
+ logDebug("Processing audio with plugin '%s'", plugin->pluginName->data);
+ nextInputBuffer = plugin->inputBuffer;
+ nextInputBuffer->blocksize = formerOutputBuffer->blocksize;
+ sampleBufferCopyAndMapChannels(nextInputBuffer, formerOutputBuffer);
+ plugin->outputBuffer->blocksize = plugin->inputBuffer->blocksize;
+ taskTimerStart(pluginChain->audioTimers[i]);
+ plugin->processAudio(plugin, plugin->inputBuffer, plugin->outputBuffer);
+ processingTimeInMs = taskTimerStop(pluginChain->audioTimers[i]);
+
+ if (processingTimeInMs > maxProcessingTimeInMs && pluginChain->_realtime) {
+ logWarn("Possible dropout! Plugin '%s' spent %dms processing time (%dms max)",
+ plugin->pluginName->data, (int)processingTimeInMs, (int)maxProcessingTimeInMs);
+ } else {
+ logDebug("Plugin '%s' spent %dms processing (%d%% effective CPU usage)",
+ plugin->pluginName->data, (int)processingTimeInMs,
+ (int)(processingTimeInMs / maxProcessingTimeInMs));
+ }
+
+ formerOutputBuffer = plugin->outputBuffer;
+ }
+
+ nextInputBuffer = outBuffer;
+ nextInputBuffer->blocksize = formerOutputBuffer->blocksize;
+ sampleBufferCopyAndMapChannels(nextInputBuffer, formerOutputBuffer);
+
+ if (pluginChain->_realtime) {
+ totalProcessingTimeInMs = taskTimerStop(pluginChain->_realtimeTimer);
+
+ if (totalProcessingTimeInMs < maxProcessingTimeInMs) {
+ taskTimerSleep(maxProcessingTimeInMs - totalProcessingTimeInMs);
+ }
+ }
+}
+
+void pluginChainProcessMidi(PluginChain pluginChain, LinkedList midiEvents)
+{
+ Plugin plugin;
+
+ if (midiEvents->item != NULL) {
+ logDebug("Processing plugin chain MIDI events");
+ // Right now, we only process MIDI in the first plugin in the chain
+ // TODO: Is this really the correct behavior? How do other sequencers do it?
+ plugin = pluginChain->plugins[0];
+ taskTimerStart(pluginChain->midiTimers[0]);
+ plugin->processMidiEvents(plugin, midiEvents);
+ taskTimerStop(pluginChain->midiTimers[0]);
+ }
+}
+
+void pluginChainShutdown(PluginChain pluginChain)
+{
+ Plugin plugin;
+ unsigned int i;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ plugin = pluginChain->plugins[i];
+ logInfo("Closing plugin '%s'", plugin->pluginName->data);
+ closePlugin(plugin);
+ }
+}
+
+void freePluginChain(PluginChain pluginChain)
+{
+ unsigned int i;
+
+ for (i = 0; i < pluginChain->numPlugins; i++) {
+ freePluginPreset(pluginChain->presets[i]);
+ freePlugin(pluginChain->plugins[i]);
+ freeTaskTimer(pluginChain->audioTimers[i]);
+ freeTaskTimer(pluginChain->midiTimers[i]);
+ }
+
+ free(pluginChain->presets);
+ free(pluginChain->plugins);
+ free(pluginChain->audioTimers);
+ free(pluginChain->midiTimers);
+
+ if (pluginChain->_realtime) {
+ freeTaskTimer(pluginChain->_realtimeTimer);
+ }
+
+ free(pluginChain);
+}
diff --git a/source/plugin/PluginChain.h b/source/plugin/PluginChain.h
new file mode 100644
index 0000000..62c58a1
--- /dev/null
+++ b/source/plugin/PluginChain.h
@@ -0,0 +1,165 @@
+//
+// PluginChain.h - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginChain_h
+#define MrsWatson_PluginChain_h
+
+#include "app/ReturnCodes.h"
+#include "base/LinkedList.h"
+#include "plugin/Plugin.h"
+#include "plugin/PluginPreset.h"
+#include "time/TaskTimer.h"
+
+#define MAX_PLUGINS 8
+#define CHAIN_STRING_PLUGIN_SEPARATOR ';'
+#define CHAIN_STRING_PROGRAM_SEPARATOR ','
+
+typedef struct {
+ unsigned int numPlugins;
+ Plugin *plugins;
+ PluginPreset *presets;
+ TaskTimer *audioTimers;
+ TaskTimer *midiTimers;
+
+ // Private fields
+ boolByte _realtime;
+ TaskTimer _realtimeTimer;
+} PluginChainMembers;
+
+/**
+ * Class which holds multiple plugins which process audio in serial. Only one
+ * instrument may be present in a plugin chain.
+ */
+typedef PluginChainMembers *PluginChain;
+
+/**
+ * Get a reference to the global plugin chain instance.
+ * @return Reference to global plugin chain, or NULL if the global instance has
+ * not yet been initialized.
+ */
+PluginChain getPluginChain(void);
+
+/**
+ * Initialize the global plugin chain instance. Should be called fairly
+ * early in the program initialization.
+ */
+void initPluginChain(void);
+
+/**
+ * Append a plugin to the end of the chain
+ * @param self
+ * @param plugin Plugin to add
+ * @param preset Preset to be loaded into the plugin. If no preset is desired,
+ * passed NULL here.
+ * @return True if the plugin could be added to the end of the chain
+ */
+boolByte pluginChainAppend(PluginChain self, Plugin plugin, PluginPreset preset);
+
+// TODO: Deprecate and remove this function
+boolByte pluginChainAddFromArgumentString(PluginChain self, const CharString argumentString, const CharString userSearchPath);
+
+/**
+ * Open and initialize all plugins in the chain.
+ * @param self
+ * @return RETURN_CODE_SUCCESS on success, other code on failure
+ */
+ReturnCodes pluginChainInitialize(PluginChain self);
+
+/**
+ * Inspect each plugin in the chain
+ * @param self
+ */
+void pluginChainInspect(PluginChain self);
+
+/**
+ * Get the maximum amount of tail time (post*processing time with empty input)
+ * needed for the chain. This is essentially the largest tail time value for any
+ * plug-in in the chain.
+ * @param self
+ * @return Maximum tail time, in milliseconds
+ */
+int pluginChainGetMaximumTailTimeInMs(PluginChain self);
+
+/**
+ * Get the total processing delay in frames.
+ * @param self
+ * @return Total processing delay, in frames.
+ */
+unsigned long pluginChainGetProcessingDelay(PluginChain self);
+
+/**
+ * Set parameters on the first plugin in a chain.
+ * @param self
+ * @param parameters List of parameters to be applied
+ * @return True if all parameters were set, false otherwise
+ */
+boolByte pluginChainSetParameters(PluginChain self, const LinkedList parameters);
+
+/**
+ * Set realtime mode for the plugin chain. When set, calls to pluginChainProcessAudio()
+ * will sleep for the additional time required to process the block in realtime.
+ * @param realtime True to enable realtime mode, false to disable (default)
+ * @param self
+ */
+void pluginChainSetRealtime(PluginChain self, boolByte realtime);
+
+/**
+ * Prepare each plugin in the chain for processing. This should be called before
+ * the first block of audio is sent to the chain.
+ * @param self
+ */
+void pluginChainPrepareForProcessing(PluginChain self);
+
+/**
+ * Process a single block of samples through each plugin in the chain.
+ * @param self
+ * @param inBuffer Input sample block
+ * @param outBuffer Output sample block
+ */
+void pluginChainProcessAudio(PluginChain self, SampleBuffer inBuffer, SampleBuffer outBuffer);
+
+/**
+ * Send a list of MIDI events to be processed by the chain. Currently, only the
+ * first plugin in the chain will receive these events.
+ * @param self
+ * @param midiEvents List of events to process
+ */
+void pluginChainProcessMidi(PluginChain self, LinkedList midiEvents);
+
+/**
+ * Close all plugins in the chain
+ * @param self
+ */
+void pluginChainShutdown(PluginChain self);
+
+/**
+ * Free the plugin chain and all associated resources (including all plugins)
+ * @param self
+ */
+void freePluginChain(PluginChain self);
+
+#endif
diff --git a/source/plugin/PluginGain.c b/source/plugin/PluginGain.c
new file mode 100644
index 0000000..d2f21b9
--- /dev/null
+++ b/source/plugin/PluginGain.c
@@ -0,0 +1,133 @@
+//
+// PluginGain.c - MrsWatson
+// Created by Nik Reiman on 26 May 14.
+// Copyright (c) 2014 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "audio/SampleBuffer.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginGain.h"
+
+const char *kInternalPluginGainName = INTERNAL_PLUGIN_PREFIX "gain";
+
+static void _pluginGainEmpty(void *pluginPtr)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginGainOpen(void *pluginPtr)
+{
+ return true;
+}
+
+static void _pluginGainGetAbsolutePath(void *pluginPtr, CharString outPath)
+{
+ // Internal plugins don't have a path, and thus can't be copied. So just copy
+ // an empty string here and let any callers needing the absolute path to check
+ // for this value before doing anything important.
+ charStringClear(outPath);
+}
+
+static void _pluginGainDisplayInfo(void *pluginPtr)
+{
+ logInfo("Information for Internal plugin '%s'", kInternalPluginGainName);
+ logInfo("Type: effect, parameters: none");
+ logInfo("Description: a basic gain effect");
+}
+
+static int _pluginGainGetSetting(void *pluginPtr, PluginSetting pluginSetting)
+{
+ switch (pluginSetting) {
+ case PLUGIN_SETTING_TAIL_TIME_IN_MS:
+ return 0;
+
+ case PLUGIN_NUM_INPUTS:
+ return 2;
+
+ case PLUGIN_NUM_OUTPUTS:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+static void _pluginGainProcessAudio(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs)
+{
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginGainSettings settings = (PluginGainSettings)plugin->extraData;
+ unsigned long channel, sample;
+
+ sampleBufferCopyAndMapChannels(outputs, inputs);
+
+ for (channel = 0; channel < outputs->numChannels; ++channel) {
+ for (sample = 0; sample < outputs->blocksize; ++sample) {
+ outputs->samples[channel][sample] *= settings->gain;
+ }
+ }
+}
+
+static void _pluginGainProcessMidiEvents(void *pluginPtr, LinkedList midiEvents)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginGainSetParameter(void *pluginPtr, unsigned int i, float value)
+{
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginGainSettings settings = (PluginGainSettings)plugin->extraData;
+
+ switch (i) {
+ case PLUGIN_GAIN_SETTINGS_GAIN:
+ settings->gain = value;
+ return true;
+
+ default:
+ logError("Attempt to set invalid parameter %d on internal gain plugin", i);
+ return false;
+ }
+}
+
+Plugin newPluginGain(const CharString pluginName)
+{
+ Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_EFFECT);
+ PluginGainSettings settings = (PluginGainSettings)malloc(sizeof(PluginGainSettingsMembers));
+
+ charStringCopy(plugin->pluginName, pluginName);
+ charStringCopyCString(plugin->pluginLocation, "Internal");
+
+ plugin->openPlugin = _pluginGainOpen;
+ plugin->displayInfo = _pluginGainDisplayInfo;
+ plugin->getSetting = _pluginGainGetSetting;
+ plugin->prepareForProcessing = _pluginGainEmpty;
+ plugin->processAudio = _pluginGainProcessAudio;
+ plugin->processMidiEvents = _pluginGainProcessMidiEvents;
+ plugin->setParameter = _pluginGainSetParameter;
+ plugin->closePlugin = _pluginGainEmpty;
+ plugin->freePluginData = _pluginGainEmpty;
+
+ settings->gain = 1.0f;
+ plugin->extraData = settings;
+ return plugin;
+}
diff --git a/source/plugin/PluginGain.h b/source/plugin/PluginGain.h
new file mode 100644
index 0000000..b1d6b65
--- /dev/null
+++ b/source/plugin/PluginGain.h
@@ -0,0 +1,47 @@
+//
+// PluginGain.h - MrsWatson
+// Created by Nik Reiman on 26 May 14.
+// Copyright (c) 2014 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginGain_h
+#define MrsWatson_PluginGain_h
+
+#include "plugin/Plugin.h"
+
+extern const char *kInternalPluginGainName;
+
+typedef enum {
+ PLUGIN_GAIN_SETTINGS_GAIN,
+ PLUGIN_GAIN_NUM_SETTINGS
+} PluginGainSettingsIndex;
+
+typedef struct {
+ float gain;
+} PluginGainSettingsMembers;
+typedef PluginGainSettingsMembers *PluginGainSettings;
+
+Plugin newPluginGain(const CharString pluginName);
+
+#endif
diff --git a/source/plugin/PluginLimiter.c b/source/plugin/PluginLimiter.c
new file mode 100644
index 0000000..4ae20d2
--- /dev/null
+++ b/source/plugin/PluginLimiter.c
@@ -0,0 +1,121 @@
+//
+// PluginLimiter.c - MrsWatson
+// Created by Nik Reiman on 26 May 14.
+// Copyright (c) 2014 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include "audio/SampleBuffer.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginLimiter.h"
+
+const char *kInternalPluginLimiterName = INTERNAL_PLUGIN_PREFIX "limiter";
+
+static void _pluginLimiterEmpty(void *pluginPtr)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginLimiterOpen(void *pluginPtr)
+{
+ return true;
+}
+
+static void _pluginLimiterGetAbsolutePath(void *pluginPtr, CharString outPath)
+{
+ // Internal plugins don't have a path, and thus can't be copied. So just copy
+ // an empty string here and let any callers needing the absolute path to check
+ // for this value before doing anything important.
+ charStringClear(outPath);
+}
+
+static void _pluginLimiterDisplayInfo(void *pluginPtr)
+{
+ logInfo("Information for Internal plugin '%s'", kInternalPluginLimiterName);
+ logInfo("Type: effect, parameters: none");
+ logInfo("Description: a brickwall limiter effect");
+}
+
+static int _pluginLimiterGetSetting(void *pluginPtr, PluginSetting pluginSetting)
+{
+ switch (pluginSetting) {
+ case PLUGIN_SETTING_TAIL_TIME_IN_MS:
+ return 0;
+
+ case PLUGIN_NUM_INPUTS:
+ return 2;
+
+ case PLUGIN_NUM_OUTPUTS:
+ return 2;
+
+ default:
+ return 0;
+ }
+}
+
+static void _pluginLimiterProcessAudio(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs)
+{
+ unsigned long channel, sample;
+
+ sampleBufferCopyAndMapChannels(outputs, inputs);
+
+ for (channel = 0; channel < outputs->numChannels; ++channel) {
+ for (sample = 0; sample < outputs->blocksize; ++sample) {
+ if (outputs->samples[channel][sample] > 1.0f) {
+ outputs->samples[channel][sample] = 1.0f;
+ } else if (outputs->samples[channel][sample] < -1.0f) {
+ outputs->samples[channel][sample] = -1.0f;
+ }
+ }
+ }
+}
+
+static void _pluginLimiterProcessMidiEvents(void *pluginPtr, LinkedList midiEvents)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginLimiterSetParameter(void *pluginPtr, unsigned int i, float value)
+{
+ return false;
+}
+
+Plugin newPluginLimiter(const CharString pluginName)
+{
+ Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_EFFECT);
+ charStringCopy(plugin->pluginName, pluginName);
+ charStringCopyCString(plugin->pluginLocation, "Internal");
+
+ plugin->openPlugin = _pluginLimiterOpen;
+ plugin->displayInfo = _pluginLimiterDisplayInfo;
+ plugin->getSetting = _pluginLimiterGetSetting;
+ plugin->prepareForProcessing = _pluginLimiterEmpty;
+ plugin->processAudio = _pluginLimiterProcessAudio;
+ plugin->processMidiEvents = _pluginLimiterProcessMidiEvents;
+ plugin->setParameter = _pluginLimiterSetParameter;
+ plugin->closePlugin = _pluginLimiterEmpty;
+ plugin->freePluginData = _pluginLimiterEmpty;
+
+ plugin->extraData = NULL;
+ return plugin;
+}
diff --git a/source/plugin/PluginLimiter.h b/source/plugin/PluginLimiter.h
new file mode 100644
index 0000000..af6ddd7
--- /dev/null
+++ b/source/plugin/PluginLimiter.h
@@ -0,0 +1,37 @@
+//
+// PluginLimiter.h - MrsWatson
+// Created by Nik Reiman on 26 May 14.
+// Copyright (c) 2014 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginLimiter_h
+#define MrsWatson_PluginLimiter_h
+
+#include "plugin/Plugin.h"
+
+extern const char *kInternalPluginLimiterName;
+
+Plugin newPluginLimiter(const CharString pluginName);
+
+#endif
diff --git a/source/plugin/PluginPassthru.c b/source/plugin/PluginPassthru.c
new file mode 100644
index 0000000..353eeed
--- /dev/null
+++ b/source/plugin/PluginPassthru.c
@@ -0,0 +1,105 @@
+//
+// PluginPassthru.c - MrsWatson
+// Created by Nik Reiman on 8/17/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdlib.h>
+
+#include "logging/EventLogger.h"
+#include "plugin/PluginPassthru.h"
+
+const char *kInternalPluginPassthruName = INTERNAL_PLUGIN_PREFIX "passthru";
+
+static void _pluginPassthruEmpty(void *pluginPtr)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginPassthruOpen(void *pluginPtr)
+{
+ return true;
+}
+
+static void _pluginPassthruDisplayInfo(void *pluginPtr)
+{
+ logInfo("Information for Internal plugin '%s'", kInternalPluginPassthruName);
+ logInfo("Type: effect, parameters: none");
+ logInfo("Description: a passthru effect which copies input data to the output");
+}
+
+static int _pluginPassthruGetSetting(void *pluginPtr, PluginSetting pluginSetting)
+{
+ switch (pluginSetting) {
+ case PLUGIN_SETTING_TAIL_TIME_IN_MS:
+ return 0;
+
+ case PLUGIN_NUM_INPUTS:
+ return 2;
+
+ case PLUGIN_NUM_OUTPUTS:
+ return 2;
+
+ case PLUGIN_INITIAL_DELAY:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+static void _pluginPassthruProcessAudio(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs)
+{
+ sampleBufferCopyAndMapChannels(outputs, inputs);
+}
+
+static void _pluginPassthruProcessMidiEvents(void *pluginPtr, LinkedList midiEvents)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginPassthruSetParameter(void *pluginPtr, unsigned int i, float value)
+{
+ return false;
+}
+
+Plugin newPluginPassthru(const CharString pluginName)
+{
+ Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_EFFECT);
+ charStringCopy(plugin->pluginName, pluginName);
+ charStringCopyCString(plugin->pluginLocation, "Internal");
+
+ plugin->openPlugin = _pluginPassthruOpen;
+ plugin->displayInfo = _pluginPassthruDisplayInfo;
+ plugin->getSetting = _pluginPassthruGetSetting;
+ plugin->prepareForProcessing = _pluginPassthruEmpty;
+ plugin->processAudio = _pluginPassthruProcessAudio;
+ plugin->processMidiEvents = _pluginPassthruProcessMidiEvents;
+ plugin->setParameter = _pluginPassthruSetParameter;
+ plugin->closePlugin = _pluginPassthruEmpty;
+ plugin->freePluginData = _pluginPassthruEmpty;
+
+ plugin->extraData = NULL;
+ return plugin;
+}
diff --git a/source/plugin/PluginPassthru.h b/source/plugin/PluginPassthru.h
new file mode 100644
index 0000000..61848ad
--- /dev/null
+++ b/source/plugin/PluginPassthru.h
@@ -0,0 +1,37 @@
+//
+// PluginPassthru.h - MrsWatson
+// Created by Nik Reiman on 8/17/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginPassthru_h
+#define MrsWatson_PluginPassthru_h
+
+#include "plugin/Plugin.h"
+
+extern const char *kInternalPluginPassthruName;
+
+Plugin newPluginPassthru(const CharString pluginName);
+
+#endif
diff --git a/source/plugin/PluginPreset.c b/source/plugin/PluginPreset.c
new file mode 100644
index 0000000..4c22211
--- /dev/null
+++ b/source/plugin/PluginPreset.c
@@ -0,0 +1,104 @@
+//
+// PluginPreset.c - MrsWatson
+// Created by Nik Reiman on 1/13/12.
+// Copyright (c) 2011 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/File.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginPreset.h"
+#include "plugin/PluginPresetFxp.h"
+#include "plugin/PluginPresetInternalProgram.h"
+
+static PluginPresetType _pluginPresetGuessType(const CharString presetName)
+{
+ if (presetName == NULL || charStringIsEmpty(presetName)) {
+ return PRESET_TYPE_INVALID;
+ }
+
+ File presetFile = newFileWithPath(presetName);
+ CharString fileExtension = fileGetExtension(presetFile);
+ freeFile(presetFile);
+
+ if (fileExtension == NULL) {
+ for (size_t i = 0; i < strlen(presetName->data); i++) {
+ if (!charStringIsNumber(presetName, i)) {
+ return PRESET_TYPE_INVALID;
+ }
+ }
+
+ // If the preset name is all numeric, then it's an internal program number
+ return PRESET_TYPE_INTERNAL_PROGRAM;
+ } else if (charStringIsEqualToCString(fileExtension, "fxp", true)) {
+ freeCharString(fileExtension);
+ return PRESET_TYPE_FXP;
+ } else {
+ logCritical("Preset '%s' does not match any supported type", presetName->data);
+ freeCharString(fileExtension);
+ return PRESET_TYPE_INVALID;
+ }
+}
+
+PluginPreset pluginPresetFactory(const CharString presetName)
+{
+ PluginPresetType presetType = _pluginPresetGuessType(presetName);
+
+ switch (presetType) {
+ case PRESET_TYPE_FXP:
+ return newPluginPresetFxp(presetName);
+
+ case PRESET_TYPE_INTERNAL_PROGRAM:
+ return newPluginPresetInternalProgram(presetName);
+
+ default:
+ return NULL;
+ }
+}
+
+void pluginPresetSetCompatibleWith(PluginPreset pluginPreset, PluginInterfaceType interfaceType)
+{
+ pluginPreset->compatiblePluginTypes |= (1 << interfaceType);
+}
+
+boolByte pluginPresetIsCompatibleWith(const PluginPreset pluginPreset, const Plugin plugin)
+{
+ return (pluginPreset->compatiblePluginTypes & (1 << plugin->interfaceType));
+}
+
+void freePluginPreset(PluginPreset pluginPreset)
+{
+ if (pluginPreset != NULL) {
+ if (pluginPreset->extraData != NULL) {
+ pluginPreset->freePresetData(pluginPreset->extraData);
+ free(pluginPreset->extraData);
+ }
+
+ freeCharString(pluginPreset->presetName);
+ free(pluginPreset);
+ }
+}
diff --git a/source/plugin/PluginPreset.h b/source/plugin/PluginPreset.h
new file mode 100644
index 0000000..76e2b79
--- /dev/null
+++ b/source/plugin/PluginPreset.h
@@ -0,0 +1,111 @@
+//
+// PluginPreset.h - MrsWatson
+// Created by Nik Reiman on 1/13/12.
+// Copyright (c) 2011 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginPreset_h
+#define MrsWatson_PluginPreset_h
+
+#include "base/CharString.h"
+#include "plugin/Plugin.h"
+
+typedef enum {
+ PRESET_TYPE_INVALID,
+ PRESET_TYPE_FXP,
+ PRESET_TYPE_INTERNAL_PROGRAM,
+ NUM_PRESET_TYPES
+} PluginPresetType;
+
+/**
+ * Called when the preset is to be loaded from the filesystem
+ * @param pluginPresetPtr self
+ */
+typedef boolByte (*OpenPresetFunc)(void *pluginPresetPtr);
+/**
+ * Called when the preset is to be loaded into a plugin
+ * @param plugin Plugin which will receive the preset. This should check that
+ * the preset is compatible with the given plugging before loading it.
+ * @param pluginPresetPtr self
+ * @return True on success, false on failure
+ */
+typedef boolByte (*LoadPresetFunc)(void *pluginPresetPtr, Plugin plugin);
+/**
+ * Free a preset and it's related data
+ * @param pluginPresetPtr self
+ */
+typedef void (*FreePresetDataFunc)(void *pluginPresetPtr);
+
+typedef struct {
+ PluginPresetType presetType;
+ CharString presetName;
+ unsigned int compatiblePluginTypes;
+
+ OpenPresetFunc openPreset;
+ LoadPresetFunc loadPreset;
+ FreePresetDataFunc freePresetData;
+
+ void *extraData;
+} PluginPresetMembers;
+
+/**
+ * Class which is used to hold preset data which will be loaded into a plugin
+ * before audio processing.
+ */
+typedef PluginPresetMembers *PluginPreset;
+
+/**
+ * Create a new plugin preset from a given name. Usually this function inspects
+ * the name and guesses an appropriate handler based on the file extension.
+ * @param presetName Preset name
+ * @return Initialized PluginPreset or NULL if no preset type found
+ */
+PluginPreset pluginPresetFactory(const CharString presetName);
+
+/**
+ * Check if a preset will be compatible with a plugin. Some plugin interface
+ * types have extra safety checks to make sure that a preset must match the
+ * plugin's ID, this call essentially wraps this functionality.
+ * @param self
+ * @param plugin Plugin to check against
+ * @return True if the preset can be loaded into the plugin
+ */
+boolByte pluginPresetIsCompatibleWith(const PluginPreset self, const Plugin plugin);
+
+/**
+ * Set interface compatibility for a preset type. This function should only be
+ * called from a PluginPreset subclass, you should not have to manually call
+ * this in normal host operations.
+ * @param self
+ * @param interfaceType Interface type to set
+ */
+void pluginPresetSetCompatibleWith(PluginPreset self, PluginInterfaceType interfaceType);
+
+/**
+ * Free a PluginPreset and all associated resources
+ * @param self
+ */
+void freePluginPreset(PluginPreset self);
+
+#endif
diff --git a/source/plugin/PluginPresetFxp.c b/source/plugin/PluginPresetFxp.c
new file mode 100644
index 0000000..200d310
--- /dev/null
+++ b/source/plugin/PluginPresetFxp.c
@@ -0,0 +1,280 @@
+//
+// PluginPresetFxp.c - MrsWatson
+// Created by Nik Reiman on 1/13/12.
+// Copyright (c) 2011 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "base/Endian.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginPresetFxp.h"
+#include "plugin/PluginVst2x.h"
+
+static boolByte _openPluginPresetFxp(void *pluginPresetPtr)
+{
+ PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr;
+ PluginPresetFxpData extraData = (PluginPresetFxpData)(pluginPreset->extraData);
+ extraData->fileHandle = fopen(pluginPreset->presetName->data, "rb");
+
+ if (extraData->fileHandle == NULL) {
+ logError("Preset '%s' could not be opened for reading", pluginPreset->presetName->data);
+ return false;
+ }
+
+ return true;
+}
+
+static boolByte _loadPluginPresetFxp(void *pluginPresetPtr, Plugin plugin)
+{
+ PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr;
+ PluginPresetFxpData extraData = (PluginPresetFxpData)(pluginPreset->extraData);
+ FxpProgram inProgram = NULL;
+ PluginPresetFxpProgramType programType;
+
+ char *chunk;
+ size_t chunkSize;
+ unsigned int valueBuffer;
+ size_t numObjectsRead;
+ float parameterValue;
+ unsigned int i;
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at chunkMagic");
+ return false;
+ }
+
+ inProgram = (FxpProgram)malloc(sizeof(FxpProgramMembers));
+ inProgram->chunkMagic = convertBigEndianIntToPlatform(valueBuffer);
+
+ if (inProgram->chunkMagic != 0x43636E4B) { // 'CcnK'
+ logError("FXP preset file has bad chunk magic");
+ free(inProgram);
+ return false;
+ }
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at byteSize");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->byteSize = convertBigEndianIntToPlatform(valueBuffer);
+ logDebug("FXP program has %d bytes in main chunk", inProgram->byteSize);
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at fxMagic");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->fxMagic = convertBigEndianIntToPlatform(valueBuffer);
+
+ if (inProgram->fxMagic == 0x4678436b) { // 'FxCk'
+ programType = FXP_TYPE_REGULAR;
+ } else if (inProgram->fxMagic == 0x46504368) { // 'FPCh'
+ programType = FXP_TYPE_OPAQUE_CHUNK;
+ } else {
+ logError("FXP preset has invalid fxMagic type");
+ free(inProgram);
+ return false;
+ }
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at version");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->version = convertBigEndianIntToPlatform(valueBuffer);
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at fxID");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->fxID = convertBigEndianIntToPlatform(valueBuffer);
+ logDebug("Preset's fxID is %d", inProgram->fxID);
+
+ if (inProgram->fxID != pluginVst2xGetUniqueId(plugin)) {
+ logError("Preset '%s' is not compatible with plugin '%s'", pluginPreset->presetName->data, plugin->pluginName->data);
+ free(inProgram);
+ return false;
+ }
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at fxVersion");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->fxVersion = convertBigEndianIntToPlatform(valueBuffer);
+
+ if (inProgram->fxVersion != pluginVst2xGetVersion(plugin)) {
+ logWarn("Plugin has version %ld, but preset has version %d. Loading this preset may result in unexpected behavior!",
+ pluginVst2xGetVersion(plugin), inProgram->fxVersion);
+ } else {
+ logDebug("Preset's version is %d", inProgram->fxVersion);
+ }
+
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at numParams");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->numParams = convertBigEndianIntToPlatform(valueBuffer);
+ logDebug("Preset has %d params", inProgram->numParams);
+
+ memset(inProgram->prgName, 0, sizeof(char) * 28);
+ numObjectsRead = fread(inProgram->prgName, sizeof(char), 28, extraData->fileHandle);
+
+ if (numObjectsRead != 28) {
+ logError("Short read of FXP preset file at prgName");
+ free(inProgram);
+ return false;
+ }
+
+ charStringCopyCString(pluginPreset->presetName, inProgram->prgName);
+ logDebug("Preset's name is %s", pluginPreset->presetName->data);
+
+ if (programType == FXP_TYPE_REGULAR) {
+ for (i = 0; i < inProgram->numParams; i++) {
+ float parameterBuffer = 0.0f;
+ numObjectsRead = fread(&parameterBuffer, sizeof(float), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset at parameter data");
+ free(inProgram);
+ return false;
+ }
+
+ parameterValue = convertBigEndianFloatToPlatform(parameterBuffer);
+ plugin->setParameter(plugin, i, parameterValue);
+ }
+ } else if (programType == FXP_TYPE_OPAQUE_CHUNK) {
+ numObjectsRead = fread(&valueBuffer, sizeof(unsigned int), 1, extraData->fileHandle);
+
+ if (numObjectsRead != 1) {
+ logError("Short read of FXP preset file at chunk size");
+ free(inProgram);
+ return false;
+ }
+
+ inProgram->content.data.size = convertBigEndianIntToPlatform(valueBuffer);
+ chunkSize = inProgram->content.data.size;
+
+ if (chunkSize == 0) {
+ logError("FXP preset has chunk of 0 bytes");
+ free(inProgram);
+ return false;
+ } else {
+ logDebug("Plugin has chunk size of %d bytes", chunkSize);
+ }
+
+ chunk = (char *)malloc(sizeof(char) * chunkSize);
+ memset(chunk, 0, sizeof(char) * chunkSize);
+ numObjectsRead = fread(chunk, sizeof(char), chunkSize, extraData->fileHandle);
+
+ if (numObjectsRead != chunkSize) {
+ logError("Short read of FXP preset file at chunk");
+ free(inProgram);
+ free(chunk);
+ return false;
+ }
+
+ // The chunk has been read, set it to the actual plugin
+ if (plugin->interfaceType == PLUGIN_TYPE_VST_2X) {
+ pluginVst2xSetProgramChunk(plugin, chunk, chunkSize);
+ free(inProgram);
+ free(chunk);
+ return true;
+ } else {
+ logInternalError("Load FXP preset to wrong plugin type");
+ free(inProgram);
+ free(chunk);
+ return false;
+ }
+ } else {
+ logInternalError("Invalid FXP program type");
+ free(inProgram);
+ return false;
+ }
+
+ free(inProgram);
+ return true;
+}
+
+static void _freePluginPresetDataFxp(void *extraDataPtr)
+{
+ PluginPresetFxpData extraData = extraDataPtr;
+
+ if (extraData->fileHandle != NULL) {
+ fclose(extraData->fileHandle);
+ }
+
+ if (extraData->chunk != NULL) {
+ free(extraData->chunk);
+ }
+}
+
+PluginPreset newPluginPresetFxp(const CharString presetName)
+{
+ PluginPreset pluginPreset = (PluginPreset)malloc(sizeof(PluginPresetMembers));
+ PluginPresetFxpData extraData = (PluginPresetFxpData)malloc(sizeof(PluginPresetFxpDataMembers));
+
+ pluginPreset->presetType = PRESET_TYPE_FXP;
+ pluginPreset->presetName = newCharString();
+ charStringCopy(pluginPreset->presetName, presetName);
+ pluginPreset->compatiblePluginTypes = 0;
+ pluginPresetSetCompatibleWith(pluginPreset, PLUGIN_TYPE_VST_2X);
+
+ pluginPreset->openPreset = _openPluginPresetFxp;
+ pluginPreset->loadPreset = _loadPluginPresetFxp;
+ pluginPreset->freePresetData = _freePluginPresetDataFxp;
+
+ extraData->fileHandle = NULL;
+ extraData->chunk = NULL;
+ pluginPreset->extraData = extraData;
+
+ return pluginPreset;
+}
+
diff --git a/source/plugin/PluginPresetFxp.h b/source/plugin/PluginPresetFxp.h
new file mode 100644
index 0000000..c951dbb
--- /dev/null
+++ b/source/plugin/PluginPresetFxp.h
@@ -0,0 +1,74 @@
+//
+// PluginPresetFxp.h - MrsWatson
+// Created by Nik Reiman on 1/13/12.
+// Copyright (c) 2011 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginPresetFxp_h
+#define MrsWatson_PluginPresetFxp_h
+
+#include <stdio.h>
+
+#include "plugin/PluginPreset.h"
+
+typedef enum {
+ FXP_TYPE_INVALID,
+ FXP_TYPE_REGULAR,
+ FXP_TYPE_OPAQUE_CHUNK,
+} PluginPresetFxpProgramType;
+
+// Copied from the VST SDK. Yes, this is a bit lame, but otherwise the C++ "virus"
+// starts to leak into the code again, and as vstfxstore.h is pure C, I don't see
+// any reason why all these files must be compiled as C++.
+typedef struct {
+ unsigned int chunkMagic; ///< 'CcnK'
+ unsigned int byteSize; ///< size of this chunk, excl. magic + byteSize
+
+ unsigned int fxMagic; ///< 'FxCk' (regular) or 'FPCh' (opaque chunk)
+ unsigned int version; ///< format version (currently 1)
+ unsigned int fxID; ///< fx unique ID
+ unsigned int fxVersion; ///< fx version
+
+ unsigned int numParams; ///< number of parameters
+ char prgName[28]; ///< program name (null-terminated ASCII string)
+
+ union {
+ float *params; ///< variable sized array with parameter values
+ struct {
+ unsigned int size; ///< size of program data
+ char *chunk; ///< variable sized array with opaque program data
+ } data; ///< program chunk data
+ } content; ///< program content depending on fxMagic
+} FxpProgramMembers;
+typedef FxpProgramMembers *FxpProgram;
+
+typedef struct {
+ FILE *fileHandle;
+ byte *chunk;
+} PluginPresetFxpDataMembers;
+typedef PluginPresetFxpDataMembers *PluginPresetFxpData;
+
+PluginPreset newPluginPresetFxp(const CharString presetName);
+
+#endif
diff --git a/source/plugin/PluginPresetInternalProgram.c b/source/plugin/PluginPresetInternalProgram.c
new file mode 100644
index 0000000..27e5a89
--- /dev/null
+++ b/source/plugin/PluginPresetInternalProgram.c
@@ -0,0 +1,74 @@
+//
+// PluginPresetInternalProgram.c - MrsWatson
+// Created by Nik Reiman on 19 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdlib.h>
+
+#include "base/CharString.h"
+#include "plugin/PluginPresetInternalProgram.h"
+#include "plugin/PluginVst2x.h"
+
+static boolByte _openPluginPresetInternalProgram(void *pluginPresetPtr)
+{
+ PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr;
+ PluginPresetInternalProgramData extraData = (PluginPresetInternalProgramData)pluginPreset->extraData;
+ extraData->programNumber = (unsigned int)strtoul(pluginPreset->presetName->data, NULL, 10);
+ return true;
+}
+
+static boolByte _loadPluginPresetInternalProgram(void *pluginPresetPtr, Plugin plugin)
+{
+ PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr;
+ PluginPresetInternalProgramData extraData = (PluginPresetInternalProgramData)pluginPreset->extraData;
+ return pluginVst2xSetProgram(plugin, extraData->programNumber);
+}
+
+static void _freePluginPresetInternalProgram(void *extraDataPtr)
+{
+ // Nothing needed here
+}
+
+PluginPreset newPluginPresetInternalProgram(const CharString presetName)
+{
+ PluginPreset pluginPreset = (PluginPreset)malloc(sizeof(PluginPresetMembers));
+ PluginPresetInternalProgramData extraData; // Yay for long type names!
+ extraData = (PluginPresetInternalProgramData)malloc(sizeof(PluginPresetInternalProgramDataMembers));
+
+ pluginPreset->presetType = PRESET_TYPE_INTERNAL_PROGRAM;
+ pluginPreset->presetName = newCharString();
+ charStringCopy(pluginPreset->presetName, presetName);
+ pluginPreset->compatiblePluginTypes = 0;
+ pluginPresetSetCompatibleWith(pluginPreset, PLUGIN_TYPE_VST_2X);
+
+ pluginPreset->openPreset = _openPluginPresetInternalProgram;
+ pluginPreset->loadPreset = _loadPluginPresetInternalProgram;
+ pluginPreset->freePresetData = _freePluginPresetInternalProgram;
+
+ extraData->programNumber = 0;
+ pluginPreset->extraData = extraData;
+
+ return pluginPreset;
+}
diff --git a/source/plugin/PluginPresetInternalProgram.h b/source/plugin/PluginPresetInternalProgram.h
new file mode 100644
index 0000000..d93795e
--- /dev/null
+++ b/source/plugin/PluginPresetInternalProgram.h
@@ -0,0 +1,40 @@
+//
+// PluginPresetInternalProgram.h - MrsWatson
+// Created by Nik Reiman on 19 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginPresetInternalProgram_h
+#define MrsWatson_PluginPresetInternalProgram_h
+
+#include "plugin/PluginPreset.h"
+
+typedef struct {
+ unsigned int programNumber;
+} PluginPresetInternalProgramDataMembers;
+typedef PluginPresetInternalProgramDataMembers *PluginPresetInternalProgramData;
+
+PluginPreset newPluginPresetInternalProgram(const CharString presetName);
+
+#endif
diff --git a/source/plugin/PluginSilence.c b/source/plugin/PluginSilence.c
new file mode 100644
index 0000000..f3f79a3
--- /dev/null
+++ b/source/plugin/PluginSilence.c
@@ -0,0 +1,105 @@
+//
+// PluginSilence.c - MrsWatson
+// Created by Nik Reiman on 19 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdlib.h>
+
+#include "logging/EventLogger.h"
+#include "plugin/PluginSilence.h"
+
+const char *kInternalPluginSilenceName = INTERNAL_PLUGIN_PREFIX "silence";
+
+static void _pluginSilenceEmpty(void *pluginPtr)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginSilenceOpen(void *pluginPtr)
+{
+ return true;
+}
+
+static void _pluginSilenceDisplayInfo(void *pluginPtr)
+{
+ logInfo("Information for Internal plugin '%s'", kInternalPluginSilenceName);
+ logInfo("Type: instrument, parameters: none");
+ logInfo("Description: an instrument which generates silence");
+}
+
+static int _pluginSilenceGetSetting(void *pluginPtr, PluginSetting pluginSetting)
+{
+ switch (pluginSetting) {
+ case PLUGIN_SETTING_TAIL_TIME_IN_MS:
+ return 0;
+
+ case PLUGIN_NUM_INPUTS:
+ return 0;
+
+ case PLUGIN_NUM_OUTPUTS:
+ return 2;
+
+ case PLUGIN_INITIAL_DELAY:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+static void _pluginSilenceProcessAudio(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs)
+{
+ sampleBufferClear(outputs);
+}
+
+static void _pluginSilenceProcessMidiEvents(void *pluginPtr, LinkedList midiEvents)
+{
+ // Nothing to do here
+}
+
+static boolByte _pluginSilenceSetParameter(void *pluginPtr, unsigned int i, float value)
+{
+ return false;
+}
+
+Plugin newPluginSilence(const CharString pluginName)
+{
+ Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_INSTRUMENT);
+ charStringCopy(plugin->pluginName, pluginName);
+ charStringCopyCString(plugin->pluginLocation, "Internal");
+
+ plugin->openPlugin = _pluginSilenceOpen;
+ plugin->displayInfo = _pluginSilenceDisplayInfo;
+ plugin->getSetting = _pluginSilenceGetSetting;
+ plugin->prepareForProcessing = _pluginSilenceEmpty;
+ plugin->processAudio = _pluginSilenceProcessAudio;
+ plugin->processMidiEvents = _pluginSilenceProcessMidiEvents;
+ plugin->setParameter = _pluginSilenceSetParameter;
+ plugin->closePlugin = _pluginSilenceEmpty;
+ plugin->freePluginData = _pluginSilenceEmpty;
+
+ plugin->extraData = NULL;
+ return plugin;
+}
diff --git a/source/plugin/PluginSilence.h b/source/plugin/PluginSilence.h
new file mode 100644
index 0000000..bb5891a
--- /dev/null
+++ b/source/plugin/PluginSilence.h
@@ -0,0 +1,37 @@
+//
+// PluginSilence.h - MrsWatson
+// Created by Nik Reiman on 19 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginSilence_h
+#define MrsWatson_PluginSilence_h
+
+#include "plugin/Plugin.h"
+
+extern const char *kInternalPluginSilenceName;
+
+Plugin newPluginSilence(const CharString pluginName);
+
+#endif
diff --git a/source/plugin/PluginVst2x.cpp b/source/plugin/PluginVst2x.cpp
new file mode 100644
index 0000000..e39f3ec
--- /dev/null
+++ b/source/plugin/PluginVst2x.cpp
@@ -0,0 +1,897 @@
+//
+// PluginVst2x.cpp - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+// C++ includes
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+#include "plugin/PluginVst2xHostCallback.h"
+
+// C includes
+extern "C" {
+#include <stdlib.h>
+
+#include "audio/AudioSettings.h"
+#include "base/File.h"
+#include "base/PlatformInfo.h"
+#include "base/Types.h"
+#include "logging/EventLogger.h"
+#include "midi/MidiEvent.h"
+#include "plugin/PluginVst2x.h"
+#include "plugin/PluginVst2xId.h"
+
+ extern LinkedList getVst2xPluginLocations(CharString currentDirectory);
+ extern LibraryHandle getLibraryHandleForPlugin(const CharString pluginAbsolutePath);
+ extern AEffect *loadVst2xPlugin(LibraryHandle libraryHandle);
+ extern void closeLibraryHandle(LibraryHandle libraryHandle);
+}
+
+// Opaque struct must be declared here rather than in the header, otherwise many
+// other files in this project must be compiled as C++ code. =/
+typedef struct {
+ AEffect *pluginHandle;
+ PluginVst2xId pluginId;
+ Vst2xPluginDispatcherFunc dispatcher;
+ LibraryHandle libraryHandle;
+ boolByte isPluginShell;
+ VstInt32 shellPluginId;
+ // Must be retained until processReplacing() is called, so best to keep a
+ // reference in the plugin's data storage.
+ struct VstEvents *vstEvents;
+} PluginVst2xDataMembers;
+typedef PluginVst2xDataMembers *PluginVst2xData;
+
+// Implementation body starts here
+extern "C" {
+// Current plugin ID, which is mostly used by shell plugins during initialization.
+// To support VST shell plugins, we must provide them with a unique ID of a sub-plugin
+// which they will ask for from the host (opcode audioMasterCurrentId). The problem
+// is that this host callback is made when the plugin's main() function is called for
+// the first time, in loadVst2xPlugin(). While the AEffect struct provides a void* user
+// member for storing a pointer to an arbitrary object which could be used to store this
+// value, that struct is not fully constructed when the callback is made to the host
+// (in fact, calling the plugin's main() *returns* the AEffect* which we save in our
+// extraData struct). Therefore it is not possible to have the plugin reach our host
+// callback with some custom data, and we must keep a global variable to the current
+// effect ID.
+// That said, this prevents initializing plugins in multiple threads in the future, as
+// as we must set this to the correct ID before calling the plugin's main() function
+// when setting up the effect chain.
+ VstInt32 currentPluginUniqueId;
+
+ static const char *_getVst2xPlatformExtension(void)
+ {
+ PlatformInfo platform = newPlatformInfo();
+ PlatformType platformType = platform->type;
+ freePlatformInfo(platform);
+
+ switch (platformType) {
+ case PLATFORM_MACOSX:
+ return ".vst";
+
+ case PLATFORM_WINDOWS:
+ return ".dll";
+
+ case PLATFORM_LINUX:
+ return ".so";
+
+ default:
+ return EMPTY_STRING;
+ }
+ }
+
+ static void _logPluginVst2xInLocation(void *item, void *userData)
+ {
+ File itemFile = (File)item;
+ CharString itemPath = newCharStringWithCString(itemFile->absolutePath->data);
+ boolByte *pluginsFound = (boolByte *)userData;
+ char *dot;
+
+ logDebug("Checking item '%s'", itemPath->data);
+ dot = strrchr(itemPath->data, '.');
+
+ if (dot != NULL) {
+ if (!strncmp(dot, _getVst2xPlatformExtension(), 3)) {
+ *dot = '\0';
+ logInfo(" %s", itemPath->data);
+ *pluginsFound = true;
+ }
+ }
+
+ freeCharString(itemPath);
+ }
+
+ static void _logPluginLocation(const CharString location)
+ {
+ logInfo("Location '%s', type VST 2.x:", location->data);
+ }
+
+ static void _listPluginsVst2xInLocation(void *item, void *userData)
+ {
+ CharString locationString;
+ File location = NULL;
+ LinkedList locationItems;
+ boolByte pluginsFound = false;
+
+ locationString = (CharString)item;
+ _logPluginLocation(locationString);
+ location = newFileWithPath(locationString);
+ locationItems = fileListDirectory(location);
+
+ if (linkedListLength(locationItems) == 0) {
+ // Empty or does not exist, return
+ logInfo(" (Empty or non-existent directory)");
+ freeLinkedList(locationItems);
+ freeFile(location);
+ return;
+ }
+
+ linkedListForeach(locationItems, _logPluginVst2xInLocation, &pluginsFound);
+
+ if (!pluginsFound) {
+ logInfo(" (No plugins found)");
+ }
+
+ freeFile(location);
+ freeLinkedListAndItems(locationItems, (LinkedListFreeItemFunc)freeFile);
+ }
+
+ void listAvailablePluginsVst2x(const CharString pluginRoot)
+ {
+ if (!charStringIsEmpty(pluginRoot)) {
+ _listPluginsVst2xInLocation(pluginRoot, NULL);
+ }
+
+ LinkedList pluginLocations = getVst2xPluginLocations(fileGetCurrentDirectory());
+ linkedListForeach(pluginLocations, _listPluginsVst2xInLocation, NULL);
+ freeLinkedListAndItems(pluginLocations, (LinkedListFreeItemFunc)freeCharString);
+ }
+
+ static boolByte _doesVst2xPluginExistAtLocation(const CharString pluginName, const CharString locationName)
+ {
+ boolByte result = false;
+ const char *subpluginSeparator = NULL;
+ CharString pluginSearchName = NULL;
+ CharString pluginSearchExtension = NULL;
+ File location = NULL;
+ File pluginSearchPath = NULL;
+
+ subpluginSeparator = strrchr(pluginName->data, kPluginVst2xSubpluginSeparator);
+
+ if (subpluginSeparator != NULL) {
+ pluginSearchName = newCharString();
+ strncpy(pluginSearchName->data, pluginName->data, subpluginSeparator - pluginName->data);
+ result = _doesVst2xPluginExistAtLocation(pluginSearchName, locationName);
+ freeCharString(pluginSearchName);
+ return result;
+ }
+
+ logDebug("Looking for plugin '%s' in '%s'", pluginName->data, locationName->data);
+
+ location = newFileWithPath(locationName);
+
+ if (location == NULL || !fileExists(location) || location->fileType != kFileTypeDirectory) {
+ logWarn("Location '%s' is not a valid directory", locationName->data);
+ freeFile(location);
+ return result;
+ }
+
+ pluginSearchName = newCharStringWithCString(pluginName->data);
+ pluginSearchPath = newFileWithParent(location, pluginSearchName);
+ pluginSearchExtension = fileGetExtension(pluginSearchPath);
+
+ if (pluginSearchExtension == NULL) {
+ freeFile(pluginSearchPath);
+ charStringAppendCString(pluginSearchName, _getVst2xPlatformExtension());
+ pluginSearchPath = newFileWithParent(location, pluginSearchName);
+ }
+
+ if (fileExists(pluginSearchPath)) {
+ result = true;
+ }
+
+ freeCharString(pluginSearchExtension);
+ freeCharString(pluginSearchName);
+ freeFile(pluginSearchPath);
+ freeFile(location);
+ return result;
+ }
+
+ static CharString _getVst2xPluginLocation(const CharString pluginName, const CharString pluginRoot)
+ {
+ File pluginAbsolutePath = newFileWithPath(pluginName);
+
+ if (fileExists(pluginAbsolutePath)) {
+ File pluginParentDir = fileGetParent(pluginAbsolutePath);
+ CharString result = newCharStringWithCString(pluginParentDir->absolutePath->data);
+ freeFile(pluginParentDir);
+ freeFile(pluginAbsolutePath);
+ return result;
+ } else {
+ freeFile(pluginAbsolutePath);
+ }
+
+ // Then search the path given to --plugin-root, if given
+ if (!charStringIsEmpty(pluginRoot)) {
+ if (_doesVst2xPluginExistAtLocation(pluginName, pluginRoot)) {
+ return newCharStringWithCString(pluginRoot->data);
+ }
+ }
+
+ // If the plugin wasn't found in the user's plugin root, then try searching
+ // the default locations for the platform, starting with the current directory.
+ LinkedList pluginLocations = getVst2xPluginLocations(fileGetCurrentDirectory());
+
+ if (pluginLocations->item == NULL) {
+ freeLinkedListAndItems(pluginLocations, (LinkedListFreeItemFunc)freeCharString);
+ return NULL;
+ }
+
+ LinkedListIterator iterator = pluginLocations;
+
+ while (iterator != NULL) {
+ CharString searchLocation = (CharString)(iterator->item);
+
+ if (_doesVst2xPluginExistAtLocation(pluginName, searchLocation)) {
+ CharString result = newCharStringWithCString(searchLocation->data);
+ freeLinkedListAndItems(pluginLocations, (LinkedListFreeItemFunc)freeCharString);
+ return result;
+ }
+
+ iterator = (LinkedListIterator)iterator->nextItem;
+ }
+
+ freeLinkedListAndItems(pluginLocations, (LinkedListFreeItemFunc)freeCharString);
+ return NULL;
+ }
+
+ boolByte pluginVst2xExists(const CharString pluginName, const CharString pluginRoot)
+ {
+ CharString pluginLocation = _getVst2xPluginLocation(pluginName, pluginRoot);
+ boolByte result = (boolByte)((pluginLocation != NULL) && !charStringIsEmpty(pluginLocation));
+ freeCharString(pluginLocation);
+ return result;
+ }
+
+ static short _canPluginDo(Plugin plugin, const char *canDoString)
+ {
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ VstIntPtr result = data->dispatcher(data->pluginHandle, effCanDo, 0, 0, (void *)canDoString, 0.0f);
+ return (short)result;
+ }
+
+ static void _resumePlugin(Plugin plugin)
+ {
+ logDebug("Resuming plugin '%s'", plugin->pluginName->data);
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+
+ if (data->isPluginShell && data->shellPluginId == 0) {
+ logError("'%s' is a shell plugin, but no sub-plugin ID was given, run with --help plugin", plugin->pluginName->data);
+ }
+
+ data->dispatcher(data->pluginHandle, effMainsChanged, 0, 1, NULL, 0.0f);
+ data->dispatcher(data->pluginHandle, effStartProcess, 0, 0, NULL, 0.0f);
+ }
+
+ static void _suspendPlugin(Plugin plugin)
+ {
+ logDebug("Suspending plugin '%s'", plugin->pluginName->data);
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ data->dispatcher(data->pluginHandle, effMainsChanged, 0, 0, NULL, 0.0f);
+ data->dispatcher(data->pluginHandle, effStopProcess, 0, 0, NULL, 0.0f);
+ }
+
+ static void _setSpeakers(struct VstSpeakerArrangement *speakerArrangement, int channels)
+ {
+ memset(speakerArrangement, 0, sizeof(struct VstSpeakerArrangement));
+ speakerArrangement->numChannels = channels;
+
+ if (channels <= 8) {
+ speakerArrangement->numChannels = channels;
+ } else {
+ logInfo("Number of channels = %d. Will only arrange 8 speakers.", channels);
+ speakerArrangement->numChannels = 8;
+ }
+
+ switch (speakerArrangement->numChannels) {
+ case 0:
+ speakerArrangement->type = kSpeakerArrEmpty;
+ break;
+
+ case 1:
+ speakerArrangement->type = kSpeakerArrMono;
+ break;
+
+ case 2:
+ speakerArrangement->type = kSpeakerArrStereo;
+ break;
+
+ case 3:
+ speakerArrangement->type = kSpeakerArr30Music;
+ break;
+
+ case 4:
+ speakerArrangement->type = kSpeakerArr40Music;
+ break;
+
+ case 5:
+ speakerArrangement->type = kSpeakerArr50;
+ break;
+
+ case 6:
+ speakerArrangement->type = kSpeakerArr60Music;
+ break;
+
+ case 7:
+ speakerArrangement->type = kSpeakerArr70Music;
+ break;
+
+ case 8:
+ speakerArrangement->type = kSpeakerArr80Music;
+ break;
+
+ default:
+ logInternalError("Cannot arrange more than 8 speakers.");//The datastructure does not allow.
+ break;
+ }
+
+ for (int i = 0; i < speakerArrangement->numChannels; i++) {
+ speakerArrangement->speakers[i].azimuth = 0.0f;
+ speakerArrangement->speakers[i].elevation = 0.0f;
+ speakerArrangement->speakers[i].radius = 0.0f;
+ speakerArrangement->speakers[i].reserved = 0.0f;
+ speakerArrangement->speakers[i].name[0] = '\0';
+ speakerArrangement->speakers[i].type = kSpeakerUndefined;
+ }
+ }
+
+ static boolByte _initVst2xPlugin(Plugin plugin)
+ {
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ PluginVst2xId subpluginId;
+
+ logDebug("Initializing VST2.x plugin '%s'", plugin->pluginName->data);
+
+ if (data->pluginHandle->flags & effFlagsIsSynth) {
+ plugin->pluginType = PLUGIN_TYPE_INSTRUMENT;
+ } else {
+ plugin->pluginType = PLUGIN_TYPE_EFFECT;
+ }
+
+ if (data->pluginHandle->dispatcher(data->pluginHandle, effGetPlugCategory, 0, 0, NULL, 0.0f) == kPlugCategShell) {
+ subpluginId = newPluginVst2xIdWithId((unsigned long)data->shellPluginId);
+ logDebug("VST is a shell plugin, sub-plugin ID '%s'", subpluginId->idString->data);
+ freePluginVst2xId(subpluginId);
+ data->isPluginShell = true;
+ }
+
+ data->dispatcher(data->pluginHandle, effOpen, 0, 0, NULL, 0.0f);
+ data->dispatcher(data->pluginHandle, effSetSampleRate, 0, 0, NULL, (float)getSampleRate());
+ data->dispatcher(data->pluginHandle, effSetBlockSize, 0, (VstIntPtr)getBlocksize(), NULL, 0.0f);
+ struct VstSpeakerArrangement inSpeakers;
+ _setSpeakers(&inSpeakers, data->pluginHandle->numInputs);
+ struct VstSpeakerArrangement outSpeakers;
+ _setSpeakers(&outSpeakers, data->pluginHandle->numOutputs);
+ data->dispatcher(data->pluginHandle, effSetSpeakerArrangement, 0, (VstIntPtr)&inSpeakers, &outSpeakers, 0.0f);
+
+ return true;
+ }
+
+ unsigned long pluginVst2xGetUniqueId(const Plugin self)
+ {
+ if (self->interfaceType == PLUGIN_TYPE_VST_2X) {
+ PluginVst2xData data = (PluginVst2xData)self->extraData;
+ return (unsigned long)data->pluginHandle->uniqueID;
+ }
+
+ return 0;
+ }
+
+ unsigned long pluginVst2xGetVersion(const Plugin self)
+ {
+ if (self->interfaceType == PLUGIN_TYPE_VST_2X) {
+ PluginVst2xData data = (PluginVst2xData)self->extraData;
+ return (unsigned long)data->pluginHandle->version;
+ }
+
+ return 0;
+ }
+
+ void pluginVst2xAudioMasterIOChanged(const Plugin self, AEffect const *const newValues)
+ {
+ PluginVst2xData data = (PluginVst2xData)(self->extraData);
+ data->pluginHandle->initialDelay = newValues->initialDelay;
+
+ if (newValues->numInputs != data->pluginHandle->numInputs || newValues->numOutputs != data->pluginHandle->numOutputs) {
+ data->pluginHandle->numInputs = newValues->numInputs;
+ struct VstSpeakerArrangement inSpeakers;
+ _setSpeakers(&inSpeakers, data->pluginHandle->numInputs);
+ data->pluginHandle->numOutputs = newValues->numOutputs;
+ struct VstSpeakerArrangement outSpeakers;
+ _setSpeakers(&outSpeakers, data->pluginHandle->numOutputs);
+ data->dispatcher(data->pluginHandle, effSetSpeakerArrangement, 0, (VstIntPtr)&inSpeakers, &outSpeakers, 0.0f);
+ }
+ }
+
+ static boolByte _openVst2xPlugin(void *pluginPtr)
+ {
+ boolByte result = false;
+ AEffect *pluginHandle;
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ File pluginPath = newFileWithPath(plugin->pluginName);
+ CharString pluginBasename = fileGetBasename(pluginPath);
+ char *subpluginSeparator = strrchr((char *)pluginBasename, kPluginVst2xSubpluginSeparator);
+ CharString subpluginIdString = NULL;
+
+ if (subpluginSeparator != NULL) {
+ *subpluginSeparator = '\0';
+ subpluginIdString = newCharStringWithCapacity(kCharStringLengthShort);
+ strncpy(subpluginIdString->data, subpluginSeparator + 1, 4);
+ PluginVst2xId subpluginId = newPluginVst2xIdWithStringId(subpluginIdString);
+ data->shellPluginId = (VstInt32)subpluginId->id;
+ currentPluginUniqueId = data->shellPluginId;
+ freePluginVst2xId(subpluginId);
+ }
+
+ logInfo("Opening VST2.x plugin '%s'", plugin->pluginName->data);
+
+ if (fileExists(pluginPath)) {
+ charStringCopy(plugin->pluginAbsolutePath, pluginPath->absolutePath);
+ } else {
+ File pluginLocationPath = newFileWithPath(plugin->pluginLocation);
+ CharString pluginNameWithExtension = newCharString();
+ charStringCopy(pluginNameWithExtension, plugin->pluginName);
+ charStringAppendCString(pluginNameWithExtension, _getVst2xPlatformExtension());
+ File pluginAbsolutePath = newFileWithParent(pluginLocationPath, pluginNameWithExtension);
+ charStringCopy(plugin->pluginAbsolutePath, pluginAbsolutePath->absolutePath);
+ freeFile(pluginAbsolutePath);
+ freeFile(pluginLocationPath);
+ freeCharString(pluginNameWithExtension);
+ }
+
+ freeFile(pluginPath);
+ logDebug("Plugin location is '%s'", plugin->pluginAbsolutePath->data);
+
+ data->libraryHandle = getLibraryHandleForPlugin(plugin->pluginAbsolutePath);
+
+ if (data->libraryHandle == NULL) {
+ return false;
+ }
+
+ pluginHandle = loadVst2xPlugin(data->libraryHandle);
+
+ if (pluginHandle == NULL) {
+ logError("Could not load VST2.x plugin '%s'", plugin->pluginAbsolutePath->data);
+ return false;
+ }
+
+ // The plugin name which is passed into this function is basically just used to find the
+ // actual location. Now that the plugin has been loaded, we can set a friendlier name.
+ CharString temp = plugin->pluginName;
+ plugin->pluginName = newCharStringWithCString(pluginBasename->data);
+ freeCharString(pluginBasename);
+ freeCharString(temp);
+
+ if (data->shellPluginId && subpluginIdString != NULL) {
+ charStringAppendCString(plugin->pluginName, " (");
+ charStringAppend(plugin->pluginName, subpluginIdString);
+ charStringAppendCString(plugin->pluginName, ")");
+ }
+
+ // 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 (pluginHandle->magic != kEffectMagic) {
+ logError("Plugin '%s' has bad magic number, possibly corrupt", plugin->pluginName->data);
+ } else {
+ data->dispatcher = (Vst2xPluginDispatcherFunc)(pluginHandle->dispatcher);
+ data->pluginHandle = pluginHandle;
+ result = _initVst2xPlugin(plugin);
+
+ if (result) {
+ data->pluginId = newPluginVst2xIdWithId((unsigned long)data->pluginHandle->uniqueID);
+ }
+ }
+
+ freeCharString(subpluginIdString);
+ return result;
+ }
+
+ static LinkedList _getCommonCanDos(void)
+ {
+ LinkedList result = newLinkedList();
+ linkedListAppend(result, (char *)"sendVstEvents");
+ linkedListAppend(result, (char *)"sendVstMidiEvent");
+ linkedListAppend(result, (char *)"receiveVstEvents");
+ linkedListAppend(result, (char *)"receiveVstMidiEvent");
+ linkedListAppend(result, (char *)"receiveVstTimeInfo");
+ linkedListAppend(result, (char *)"offline");
+ linkedListAppend(result, (char *)"midiProgramNames");
+ linkedListAppend(result, (char *)"bypass");
+ return result;
+ }
+
+ static const char *_prettyTextForCanDoResult(int result)
+ {
+ if (result == -1) {
+ return "No";
+ } else if (result == 0) {
+ return "Don't know";
+ } else if (result == 1) {
+ return "Yes";
+ } else {
+ return "Undefined response";
+ }
+ }
+
+ static void _displayVst2xPluginCanDo(void *item, void *userData)
+ {
+ char *canDoString = (char *)item;
+ short result = _canPluginDo((Plugin)userData, canDoString);
+ logInfo(" %s: %s", canDoString, _prettyTextForCanDoResult(result));
+ }
+
+ static void _displayVst2xPluginInfo(void *pluginPtr)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ CharString nameBuffer = newCharString();
+
+ logInfo("Information for VST2.x plugin '%s'", plugin->pluginName->data);
+ data->dispatcher(data->pluginHandle, effGetVendorString, 0, 0, nameBuffer->data, 0.0f);
+ logInfo("Vendor: %s", nameBuffer->data);
+ VstInt32 vendorVersion = (VstInt32)data->dispatcher(data->pluginHandle, effGetVendorVersion, 0, 0, NULL, 0.0f);
+ logInfo("Version: %d", vendorVersion);
+ charStringClear(nameBuffer);
+
+ logInfo("Unique ID: %s", data->pluginId->idString->data);
+ freeCharString(nameBuffer);
+
+ VstInt32 pluginCategory = (VstInt32)data->dispatcher(data->pluginHandle, effGetPlugCategory, 0, 0, NULL, 0.0f);
+
+ switch (plugin->pluginType) {
+ case PLUGIN_TYPE_EFFECT:
+ logInfo("Plugin type: effect, category %d", pluginCategory);
+ break;
+
+ case PLUGIN_TYPE_INSTRUMENT:
+ logInfo("Plugin type: instrument, category %d", pluginCategory);
+ break;
+
+ default:
+ logInfo("Plugin type: other, category %d", pluginCategory);
+ break;
+ }
+
+ logInfo("Version: %d", data->pluginHandle->version);
+ logInfo("I/O: %d/%d", data->pluginHandle->numInputs, data->pluginHandle->numOutputs);
+ logInfo("InitialDelay: %d frames", data->pluginHandle->initialDelay);
+
+ if (data->isPluginShell && data->shellPluginId == 0) {
+ logInfo("Sub-plugins:");
+ nameBuffer = newCharStringWithCapacity(kCharStringLengthShort);
+
+ while (true) {
+ charStringClear(nameBuffer);
+ VstInt32 shellPluginId = (VstInt32)data->dispatcher(data->pluginHandle, effShellGetNextPlugin, 0, 0, nameBuffer->data, 0.0f);
+
+ if (shellPluginId == 0 || charStringIsEmpty(nameBuffer)) {
+ break;
+ } else {
+ PluginVst2xId subpluginId = newPluginVst2xIdWithId((unsigned long)shellPluginId);
+ logInfo(" '%s' (%s)", subpluginId->idString->data, nameBuffer->data);
+ freePluginVst2xId(subpluginId);
+ }
+ }
+
+ freeCharString(nameBuffer);
+ } else {
+ nameBuffer = newCharStringWithCapacity(kCharStringLengthShort);
+ logInfo("Parameters (%d total):", data->pluginHandle->numParams);
+
+ for (VstInt32 i = 0; i < data->pluginHandle->numParams; i++) {
+ float value = data->pluginHandle->getParameter(data->pluginHandle, i);
+ charStringClear(nameBuffer);
+ data->dispatcher(data->pluginHandle, effGetParamName, i, 0, nameBuffer->data, 0.0f);
+ logInfo(" %d: '%s' (%f)", i, nameBuffer->data, value);
+
+ if (isLogLevelAtLeast(LOG_DEBUG)) {
+ logDebug(" Displaying common values for parameter:");
+
+ for (unsigned int j = 0; j < 128; j++) {
+ const float midiValue = (float)j / 127.0f;
+ // Don't use the other setParameter function, or else that will log like crazy to
+ // a different log level.
+ data->pluginHandle->setParameter(data->pluginHandle, i, midiValue);
+ charStringClear(nameBuffer);
+ data->dispatcher(data->pluginHandle, effGetParamDisplay, i, 0, nameBuffer->data, 0.0f);
+ logDebug(" %0.3f/MIDI value %d (0x%02x): %s", midiValue, j, j, nameBuffer->data);
+ }
+ }
+ }
+
+ logInfo("Programs (%d total):", data->pluginHandle->numPrograms);
+
+ for (int i = 0; i < data->pluginHandle->numPrograms; i++) {
+ charStringClear(nameBuffer);
+ data->dispatcher(data->pluginHandle, effGetProgramNameIndexed, i, 0, nameBuffer->data, 0.0f);
+ logInfo(" %d: '%s'", i, nameBuffer->data);
+ }
+
+ charStringClear(nameBuffer);
+ data->dispatcher(data->pluginHandle, effGetProgramName, 0, 0, nameBuffer->data, 0.0f);
+ logInfo("Current program: '%s'", nameBuffer->data);
+ freeCharString(nameBuffer);
+
+ logInfo("Common canDo's:");
+ LinkedList commonCanDos = _getCommonCanDos();
+ linkedListForeach(commonCanDos, _displayVst2xPluginCanDo, plugin);
+ freeLinkedList(commonCanDos);
+ }
+ }
+
+ static int _getVst2xPluginSetting(void *pluginPtr, PluginSetting pluginSetting)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+
+ switch (pluginSetting) {
+ case PLUGIN_SETTING_TAIL_TIME_IN_MS: {
+ VstInt32 tailSize = (VstInt32)data->dispatcher(data->pluginHandle, effGetTailSize, 0, 0, NULL, 0.0f);
+
+ // For some reason, the VST SDK says that plugins return a 1 here for no tail.
+ if (tailSize == 1 || tailSize == 0) {
+ return 0;
+ } else {
+ // If tailSize is not 0 or 1, then it is assumed to be in samples
+ return (int)((double)tailSize * getSampleRate() / 1000.0f);
+ }
+ }
+
+ case PLUGIN_NUM_INPUTS:
+ return data->pluginHandle->numInputs;
+
+ case PLUGIN_NUM_OUTPUTS:
+ return data->pluginHandle->numOutputs;
+
+ case PLUGIN_INITIAL_DELAY:
+ return data->pluginHandle->initialDelay;
+
+ default:
+ logUnsupportedFeature("Plugin setting for VST2.x");
+ return 0;
+ }
+ }
+
+ void pluginVst2xSetProgramChunk(Plugin plugin, char *chunk, size_t chunkSize)
+ {
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ data->dispatcher(data->pluginHandle, effSetChunk, 1, (VstIntPtr)chunkSize, chunk, 0.0f);
+ }
+
+ static void _processAudioVst2xPlugin(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ data->pluginHandle->processReplacing(data->pluginHandle, inputs->samples, outputs->samples, (VstInt32)outputs->blocksize);
+ }
+
+ static void _fillVstMidiEvent(const MidiEvent midiEvent, VstMidiEvent *vstMidiEvent)
+ {
+ switch (midiEvent->eventType) {
+ case MIDI_TYPE_REGULAR:
+ vstMidiEvent->type = kVstMidiType;
+ vstMidiEvent->byteSize = sizeof(VstMidiEvent);
+ vstMidiEvent->deltaFrames = (VstInt32)midiEvent->deltaFrames;
+ vstMidiEvent->midiData[0] = midiEvent->status;
+ vstMidiEvent->midiData[1] = midiEvent->data1;
+ vstMidiEvent->midiData[2] = midiEvent->data2;
+ vstMidiEvent->flags = 0;
+ vstMidiEvent->reserved1 = 0;
+ vstMidiEvent->reserved2 = 0;
+ break;
+
+ case MIDI_TYPE_SYSEX:
+ logUnsupportedFeature("VST2.x plugin sysex messages");
+ break;
+
+ case MIDI_TYPE_META:
+ // Ignore, don't care
+ break;
+
+ default:
+ logInternalError("Cannot convert MIDI event type '%d' to VstMidiEvent", midiEvent->eventType);
+ break;
+ }
+ }
+
+ static void _processMidiEventsVst2xPlugin(void *pluginPtr, LinkedList midiEvents)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)(plugin->extraData);
+ int numEvents = linkedListLength(midiEvents);
+
+ // Free events from the previous call
+ if (data->vstEvents != NULL) {
+ for (int i = 0; i < data->vstEvents->numEvents; i++) {
+ free(data->vstEvents->events[i]);
+ }
+
+ free(data->vstEvents);
+ }
+
+ data->vstEvents = (struct VstEvents *)malloc(sizeof(struct VstEvent) + (numEvents * sizeof(struct VstEvent *)));
+ data->vstEvents->numEvents = numEvents;
+
+ // Some monophonic instruments have problems dealing with the order of MIDI events,
+ // so send them all note off events *first* followed by any other event types.
+ LinkedListIterator iterator = midiEvents;
+ int outIndex = 0;
+
+ while (iterator != NULL && outIndex < numEvents) {
+ MidiEvent midiEvent = (MidiEvent)(iterator->item);
+
+ if (midiEvent != NULL && (midiEvent->status >> 4) == 0x08) {
+ VstMidiEvent *vstMidiEvent = (VstMidiEvent *)malloc(sizeof(VstMidiEvent));
+ _fillVstMidiEvent(midiEvent, vstMidiEvent);
+ data->vstEvents->events[outIndex] = (VstEvent *)vstMidiEvent;
+ outIndex++;
+ }
+
+ iterator = (LinkedListIterator)(iterator->nextItem);
+ }
+
+ iterator = midiEvents;
+
+ while (iterator != NULL && outIndex < numEvents) {
+ MidiEvent midiEvent = (MidiEvent)(iterator->item);
+
+ if (midiEvent != NULL && (midiEvent->status >> 4) != 0x08) {
+ VstMidiEvent *vstMidiEvent = (VstMidiEvent *)malloc(sizeof(VstMidiEvent));
+ _fillVstMidiEvent(midiEvent, vstMidiEvent);
+ data->vstEvents->events[outIndex] = (VstEvent *)vstMidiEvent;
+ outIndex++;
+ }
+
+ iterator = (LinkedListIterator)(iterator->nextItem);
+ }
+
+ data->dispatcher(data->pluginHandle, effProcessEvents, 0, 0, data->vstEvents, 0.0f);
+ }
+
+ boolByte pluginVst2xSetProgram(Plugin plugin, const int programNumber)
+ {
+ PluginVst2xData data = (PluginVst2xData)plugin->extraData;
+ CharString currentProgram;
+ VstInt32 result;
+
+ if (programNumber < data->pluginHandle->numPrograms) {
+ result = (VstInt32)data->pluginHandle->dispatcher(data->pluginHandle, effSetProgram, 0, programNumber, NULL, 0.0f);
+
+ if (result != 0) {
+ logError("Plugin '%s' failed to load program number %d", plugin->pluginName->data, programNumber);
+ return false;
+ } else {
+ result = (VstInt32)data->pluginHandle->dispatcher(data->pluginHandle, effGetProgram, 0, 0, NULL, 0.0f);
+
+ if (result != programNumber) {
+ logError("Plugin '%s' claimed to load program %d successfully, but current program is %d",
+ plugin->pluginName->data, programNumber, result);
+ return false;
+ } else {
+ currentProgram = newCharStringWithCapacity(kVstMaxProgNameLen + 1);
+ data->dispatcher(data->pluginHandle, effGetProgramName, 0, 0, currentProgram->data, 0.0f);
+ logDebug("Current program is now '%s'", currentProgram->data);
+ freeCharString(currentProgram);
+ return true;
+ }
+ }
+ } else {
+ logError("Cannot load program, plugin '%s' only has %d programs",
+ plugin->pluginName->data, data->pluginHandle->numPrograms - 1);
+ return false;
+ }
+ }
+
+ static boolByte _setParameterVst2xPlugin(void *pluginPtr, unsigned int index, float value)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ PluginVst2xData data = (PluginVst2xData)(plugin->extraData);
+
+ if (index < (unsigned int)data->pluginHandle->numParams) {
+ CharString valueBuffer = newCharStringWithCapacity(kCharStringLengthShort);
+ data->pluginHandle->setParameter(data->pluginHandle, index, value);
+ data->dispatcher(data->pluginHandle, effGetParamDisplay, index, 0, valueBuffer->data, 0.0f);
+ logInfo("Set parameter %d on plugin '%s' to %f (%s)",
+ index, plugin->pluginName->data, value, valueBuffer->data);
+ freeCharString(valueBuffer);
+ return true;
+ } else {
+ logError("Cannot set parameter %d on plugin '%s', invalid index", index, plugin->pluginName->data);
+ return false;
+ }
+ }
+
+ static void _prepareForProcessingVst2xPlugin(void *pluginPtr)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ _resumePlugin(plugin);
+ }
+
+ static void _closeVst2xPlugin(void *pluginPtr)
+ {
+ Plugin plugin = (Plugin)pluginPtr;
+ _suspendPlugin(plugin);
+ }
+
+ static void _freeVst2xPluginData(void *pluginDataPtr)
+ {
+ PluginVst2xData data = (PluginVst2xData)(pluginDataPtr);
+
+ data->dispatcher(data->pluginHandle, effClose, 0, 0, NULL, 0.0f);
+ data->dispatcher = NULL;
+ data->pluginHandle = NULL;
+ freePluginVst2xId(data->pluginId);
+ closeLibraryHandle(data->libraryHandle);
+
+ if (data->vstEvents != NULL) {
+ for (int i = 0; i < data->vstEvents->numEvents; i++) {
+ free(data->vstEvents->events[i]);
+ }
+
+ free(data->vstEvents);
+ }
+ }
+
+ Plugin newPluginVst2x(const CharString pluginName, const CharString pluginRoot)
+ {
+ Plugin plugin = _newPlugin(PLUGIN_TYPE_VST_2X, PLUGIN_TYPE_UNKNOWN);
+ charStringCopy(plugin->pluginName, pluginName);
+ plugin->pluginLocation = _getVst2xPluginLocation(pluginName, pluginRoot);
+
+ plugin->openPlugin = _openVst2xPlugin;
+ plugin->displayInfo = _displayVst2xPluginInfo;
+ plugin->getSetting = _getVst2xPluginSetting;
+ plugin->processAudio = _processAudioVst2xPlugin;
+ plugin->processMidiEvents = _processMidiEventsVst2xPlugin;
+ plugin->setParameter = _setParameterVst2xPlugin;
+ plugin->prepareForProcessing = _prepareForProcessingVst2xPlugin;
+ plugin->closePlugin = _closeVst2xPlugin;
+ plugin->freePluginData = _freeVst2xPluginData;
+
+ PluginVst2xData extraData = (PluginVst2xData)malloc(sizeof(PluginVst2xDataMembers));
+ extraData->pluginHandle = NULL;
+ extraData->pluginId = NULL;
+ extraData->dispatcher = NULL;
+ extraData->libraryHandle = NULL;
+ extraData->isPluginShell = false;
+ extraData->shellPluginId = 0;
+ extraData->vstEvents = NULL;
+ plugin->extraData = extraData;
+
+ return plugin;
+ }
+}
diff --git a/source/plugin/PluginVst2x.h b/source/plugin/PluginVst2x.h
new file mode 100644
index 0000000..8c52d4e
--- /dev/null
+++ b/source/plugin/PluginVst2x.h
@@ -0,0 +1,91 @@
+//
+// PluginVst2x.h - MrsWatson
+// Created by Nik Reiman on 1/3/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginVst2x_h
+#define MrsWatson_PluginVst2x_h
+
+#include "base/CharString.h"
+#include "plugin/Plugin.h"
+
+static const char kPluginVst2xSubpluginSeparator = ':';
+
+/**
+ * List all available VST2.x plugins in common system locations. Note that this
+ * function does not do recursive searches (yet).
+ * @param pluginRoot User-provided plugin root path to search
+ */
+void listAvailablePluginsVst2x(const CharString pluginRoot);
+
+/**
+ * Create a new instance of a VST 2.x plugin
+ * @param pluginName Plugin name
+ * @param pluginRoot User-defined plugin root path
+ * @return Initialized Plugin object, or NULL if no such plugin was found
+ */
+Plugin newPluginVst2x(const CharString pluginName, const CharString pluginRoot);
+
+/**
+ * Get the VST2.x unique ID
+ * @param self
+ * @return Unique ID, or 0 if not known (yes, this can happen)
+ */
+unsigned long pluginVst2xGetUniqueId(const Plugin self);
+
+/**
+ * Get the plugin's version number. Used to determine compatible FXB/FXP patches.
+ * @param self
+ * @return Plugin version, or 0 if an error occurred. Note that some (buggy) plugins
+ * could potentially have a declared version of 0 as well.
+ */
+unsigned long pluginVst2xGetVersion(const Plugin self);
+
+/**
+ * See if a VST2.x plugin exists with the given name. Absolute paths will also
+ * be respected if passed.
+ * @param pluginName Plugin name (short name or absolute path)
+ * @param pluginRoot User-provided plugin root path
+ * @return True if such a plugin exists in any location, false otherwise
+ */
+boolByte pluginVst2xExists(const CharString pluginName, const CharString pluginRoot);
+
+/**
+ * Set an internal program number for a VST2.x plugin.
+ * @param self
+ * @param programNumber Program to set
+ * @return True if the program could be set and verified
+ */
+boolByte pluginVst2xSetProgram(Plugin self, const int programNumber);
+
+/**
+ * Set chuck preset data from an FXP preset to a VST2.x plugin.
+ * @param self
+ * @param chunk Chunk data to set
+ * @param chunkSize Chunk size
+ */
+void pluginVst2xSetProgramChunk(Plugin self, char *chunk, size_t chunkSize);
+
+#endif
diff --git a/source/plugin/PluginVst2xHostCallback.cpp b/source/plugin/PluginVst2xHostCallback.cpp
new file mode 100644
index 0000000..12cc409
--- /dev/null
+++ b/source/plugin/PluginVst2xHostCallback.cpp
@@ -0,0 +1,433 @@
+//
+// Vst2xHostCallback.cpp - MrsWatson
+// Created by Nik Reiman on 1/5/12.
+// Copyright (c) 2012 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+// C++ includes
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+#include "plugin/PluginVst2xHostCallback.h"
+
+// C includes
+extern "C" {
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "app/BuildInfo.h"
+#include "audio/AudioSettings.h"
+#include "base/CharString.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginChain.h"
+#include "plugin/PluginVst2x.h"
+#include "plugin/PluginVst2xId.h"
+#include "time/AudioClock.h"
+
+ void pluginVst2xAudioMasterIOChanged(const Plugin self, AEffect const *const newValues);
+}
+
+// Global variables (sigh, unfortunately yes). When plugins ask for the time, they
+// expect that the host will pass them a pointer to a VstTimeInfo struct, a pointer
+// to which they are not the owner of and are not expected to free afterwards...
+// which is actually the correct thing to do, given that if this were the case, a
+// huge number of plugins would probably fail to do this and leak memory all over
+// the place.
+// Anyways, since we cannot scope this variable intelligently, we instead keep one
+// instance of it as a static variable, so it is always avaible to plugins when they
+// ask for the time.
+static VstTimeInfo vstTimeInfo;
+
+extern "C" {
+// Current plugin ID, which is mostly used by shell plugins during initialization.
+// Instance declared in PluginVst2x.cpp, see explanation for the global-ness and
+// need of this variable there.
+ extern VstInt32 currentPluginUniqueId;
+
+ static int _canHostDo(const char *pluginName, const char *canDoString)
+ {
+ boolByte supported = false;
+
+ logDebug("Plugin '%s' asked if we can do '%s'", pluginName, canDoString);
+
+ if (!strcmp(canDoString, EMPTY_STRING)) {
+ logWarn("Plugin '%s' asked if we can do an empty string. This is probably a bug.", pluginName);
+ } else if (!strcmp(canDoString, "sendVstEvents")) {
+ supported = true;
+ } else if (!strcmp(canDoString, "sendVstMidiEvent")) {
+ supported = true;
+ } else if (!strcmp(canDoString, "sendVstTimeInfo")) {
+ supported = true;
+ } else if (!strcmp(canDoString, "receiveVstEvents")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "receiveVstMidiEvent")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "reportConnectionChanges")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "acceptIOChanges")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "sizeWindow")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "offline")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "openFileSelector")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "closeFileSelector")) {
+ supported = false;
+ } else if (!strcmp(canDoString, "startStopProcess")) {
+ supported = true;
+ } else if (!strcmp(canDoString, "shellCategory")) {
+ supported = true;
+ } else if (!strcmp(canDoString, "sendVstMidiEventFlagIsRealtime")) {
+ supported = false;
+ } else {
+ logInfo("Plugin '%s' asked if host canDo '%s' (unimplemented)", pluginName, canDoString);
+ }
+
+ return supported;
+ }
+
+ VstIntPtr VSTCALLBACK pluginVst2xHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *dataPtr, float opt)
+ {
+ // This string is used in a bunch of logging calls below
+ PluginVst2xId pluginId;
+
+ if (effect != NULL) {
+ pluginId = newPluginVst2xIdWithId((unsigned long)effect->uniqueID);
+ } else {
+ // During plugin initialization, the dispatcher can be called without a
+ // valid plugin instance, as the AEffect* struct is still not fully constructed
+ // at that point.
+ pluginId = newPluginVst2xId();
+ }
+
+ const char *pluginIdString = pluginId->idString->data;
+ VstIntPtr result = 0;
+
+ logDebug("Plugin '%s' called host dispatcher with %d, %d, %d", pluginIdString, opcode, index, value);
+
+ switch (opcode) {
+ case audioMasterAutomate:
+ // The plugin will call this if a parameter has changed via MIDI or the GUI, so the host can update
+ // itself accordingly. We don't care about this (for the time being), and as we don't support either
+ // GUI's or live MIDI, this opcode can be ignored.
+ break;
+
+ case audioMasterVersion:
+ // We are a VST 2.4 compatible host
+ result = 2400;
+ break;
+
+ case audioMasterCurrentId:
+ // Use the current plugin ID, needed by VST shell plugins to determine which sub-plugin to load
+ result = currentPluginUniqueId;
+ break;
+
+ case audioMasterIdle:
+ // Ignore
+ result = 1;
+ break;
+
+ case audioMasterPinConnected:
+ logDeprecated("audioMasterPinConnected", pluginIdString);
+ break;
+
+ case audioMasterWantMidi:
+ // This (deprecated) call is sometimes made by VST2.3 instruments to tell
+ // the host that it is an instrument. We can safely ignore it.
+ result = 1;
+ break;
+
+ case audioMasterGetTime: {
+ AudioClock audioClock = getAudioClock();
+
+ // These values are always valid
+ vstTimeInfo.samplePos = audioClock->currentFrame;
+ vstTimeInfo.sampleRate = getSampleRate();
+
+ // Set flags for transport state
+ vstTimeInfo.flags = 0;
+ vstTimeInfo.flags |= audioClock->transportChanged ? kVstTransportChanged : 0;
+ vstTimeInfo.flags |= audioClock->isPlaying ? kVstTransportPlaying : 0;
+
+ // Fill values based on other flags which may have been requested
+ if (value & kVstNanosValid) {
+ // It doesn't make sense to return this value, as the plugin may try to calculate
+ // something based on the current system time. As we are running offline, anything
+ // the plugin calculates here will probably be wrong given the way we are running.
+ // However, for realtime mode, this flag should be implemented in that case.
+ logWarn("Plugin '%s' asked for time in nanoseconds (unsupported)", pluginIdString);
+ }
+
+ if (value & kVstPpqPosValid) {
+ // TODO: Move calculations to AudioClock
+ double samplesPerBeat = (60.0 / getTempo()) * getSampleRate();
+ // Musical time starts with 1, not 0
+ vstTimeInfo.ppqPos = (vstTimeInfo.samplePos / samplesPerBeat) + 1.0;
+ logDebug("Current PPQ position is %g", vstTimeInfo.ppqPos);
+ vstTimeInfo.flags |= kVstPpqPosValid;
+ }
+
+ if (value & kVstTempoValid) {
+ vstTimeInfo.tempo = getTempo();
+ vstTimeInfo.flags |= kVstTempoValid;
+ }
+
+ if (value & kVstBarsValid) {
+ if (!(value & kVstPpqPosValid)) {
+ logError("Plugin requested position in bars, but not PPQ");
+ }
+
+ // TODO: Move calculations to AudioClock
+ double currentBarPos = floor(vstTimeInfo.ppqPos / (double)getTimeSignatureBeatsPerMeasure());
+ vstTimeInfo.barStartPos = currentBarPos * (double)getTimeSignatureBeatsPerMeasure() + 1.0;
+ logDebug("Current bar is %g", vstTimeInfo.barStartPos);
+ vstTimeInfo.flags |= kVstBarsValid;
+ }
+
+ if (value & kVstCyclePosValid) {
+ // We don't support cycling, so this is always 0
+ }
+
+ if (value & kVstTimeSigValid) {
+ vstTimeInfo.timeSigNumerator = getTimeSignatureBeatsPerMeasure();
+ vstTimeInfo.timeSigDenominator = getTimeSignatureNoteValue();
+ vstTimeInfo.flags |= kVstTimeSigValid;
+ }
+
+ if (value & kVstSmpteValid) {
+ logUnsupportedFeature("Current time in SMPTE format");
+ }
+
+ if (value & kVstClockValid) {
+ logUnsupportedFeature("Sample frames until next clock");
+ }
+
+ result = (VstIntPtr)&vstTimeInfo;
+ break;
+ }
+
+ case audioMasterProcessEvents:
+ logUnsupportedFeature("VST master opcode audioMasterProcessEvents");
+ break;
+
+ case audioMasterSetTime:
+ logDeprecated("audioMasterSetTime", pluginIdString);
+ break;
+
+ case audioMasterTempoAt:
+ logDeprecated("audioMasterTempoAt", pluginIdString);
+ break;
+
+ case audioMasterGetNumAutomatableParameters:
+ logDeprecated("audioMasterGetNumAutomatableParameters", pluginIdString);
+ break;
+
+ case audioMasterGetParameterQuantization:
+ logDeprecated("audioMasterGetParameterQuantization", pluginIdString);
+ break;
+
+ case audioMasterIOChanged: {
+ if (effect != NULL) {
+ PluginChain pluginChain = getPluginChain();
+ logDebug("Number of inputs: %d", effect->numInputs);
+ logDebug("Number of outputs: %d", effect->numOutputs);
+ logDebug("Number of parameters: %d", effect->numParams);
+ logDebug("Initial Delay: %d", effect->initialDelay);
+ result = -1;
+
+ for (unsigned int i = 0; i < pluginChain->numPlugins; ++i) {
+ if ((unsigned long)effect->uniqueID == pluginVst2xGetUniqueId(pluginChain->plugins[i])) {
+ logDebug("Updating plugin");
+ pluginVst2xAudioMasterIOChanged(pluginChain->plugins[i], effect);
+ result = 0;
+ break;//Only one plugin will match anyway.
+ }
+ }
+
+ break;
+ }
+ }
+
+ case audioMasterNeedIdle:
+ logDeprecated("audioMasterNeedIdle", pluginIdString);
+ break;
+
+ case audioMasterSizeWindow:
+ logWarn("Plugin '%s' asked us to resize window (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterGetSampleRate:
+ result = (int)getSampleRate();
+ break;
+
+ case audioMasterGetBlockSize:
+ result = (VstIntPtr)getBlocksize();
+ break;
+
+ case audioMasterGetInputLatency:
+ // Input latency is not used, and is always 0
+ result = 0;
+ break;
+
+ case audioMasterGetOutputLatency:
+ // Output latency is not used, and is always 0
+ result = 0;
+ break;
+
+ case audioMasterGetPreviousPlug:
+ logDeprecated("audioMasterGetPreviousPlug", pluginIdString);
+ break;
+
+ case audioMasterGetNextPlug:
+ logDeprecated("audioMasterGetNextPlug", pluginIdString);
+ break;
+
+ case audioMasterWillReplaceOrAccumulate:
+ logDeprecated("audioMasterWillReplaceOrAccumulate", pluginIdString);
+ break;
+
+ case audioMasterGetCurrentProcessLevel:
+ // We are not a multithreaded app and have no GUI, so this is unsupported.
+ result = kVstProcessLevelUnknown;
+ break;
+
+ case audioMasterGetAutomationState:
+ // Automation is also not supported (for now)
+ result = kVstAutomationUnsupported;
+ break;
+
+ case audioMasterOfflineStart:
+ logWarn("Plugin '%s' asked us to start offline processing (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterOfflineRead:
+ logWarn("Plugin '%s' asked to read offline data (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterOfflineWrite:
+ logWarn("Plugin '%s' asked to write offline data (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterOfflineGetCurrentPass:
+ logWarn("Plugin '%s' asked for current offline pass (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterOfflineGetCurrentMetaPass:
+ logWarn("Plugin '%s' asked for current offline meta pass (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterSetOutputSampleRate:
+ logDeprecated("audioMasterSetOutputSampleRate", pluginIdString);
+ break;
+
+ case audioMasterGetOutputSpeakerArrangement:
+ logDeprecated("audioMasterGetOutputSpeakerArrangement", pluginIdString);
+ break;
+
+ case audioMasterGetVendorString:
+ strncpy((char *)dataPtr, VENDOR_NAME, kVstMaxVendorStrLen);
+ result = 1;
+ break;
+
+ case audioMasterGetProductString:
+ strncpy((char *)dataPtr, PROGRAM_NAME, kVstMaxProductStrLen);
+ result = 1;
+ break;
+
+ case audioMasterGetVendorVersion:
+ // Return our version as a single string, in the form ABCC, which corresponds to version A.B.C
+ // Often times the patch can reach double-digits, so it gets two decimal places.
+ result = VERSION_MAJOR * 1000 + VERSION_MINOR * 100 + VERSION_PATCH;
+ break;
+
+ case audioMasterVendorSpecific:
+ logWarn("Plugin '%s' made a vendor specific call (unsupported). Arguments: %d, %d, %f", pluginIdString, index, value, opt);
+ break;
+
+ case audioMasterCanDo:
+ result = _canHostDo(pluginIdString, (char *)dataPtr);
+ break;
+
+ case audioMasterSetIcon:
+ logDeprecated("audioMasterSetIcon", pluginIdString);
+ break;
+
+ case audioMasterGetLanguage:
+ result = kVstLangEnglish;
+ break;
+
+ case audioMasterOpenWindow:
+ logDeprecated("audioMasterOpenWindow", pluginIdString);
+ break;
+
+ case audioMasterCloseWindow:
+ logDeprecated("audioMasterCloseWindow", pluginIdString);
+ break;
+
+ case audioMasterGetDirectory:
+ logWarn("Plugin '%s' asked for directory pointer (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterUpdateDisplay:
+ // Ignore
+ break;
+
+ case audioMasterBeginEdit:
+ logWarn("Plugin '%s' asked to begin parameter automation (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterEndEdit:
+ logWarn("Plugin '%s' asked to end parameter automation (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterOpenFileSelector:
+ logWarn("Plugin '%s' asked us to open file selector (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterCloseFileSelector:
+ logWarn("Plugin '%s' asked us to close file selector (unsupported)", pluginIdString);
+ break;
+
+ case audioMasterEditFile:
+ logDeprecated("audioMasterEditFile", pluginIdString);
+ break;
+
+ case audioMasterGetChunkFile:
+ logDeprecated("audioMasterGetChunkFile", pluginIdString);
+ break;
+
+ case audioMasterGetInputSpeakerArrangement:
+ logDeprecated("audioMasterGetInputSpeakerArrangement", pluginIdString);
+ break;
+
+ default:
+ logWarn("Plugin '%s' asked if host can do unknown opcode %d", pluginIdString, opcode);
+ break;
+ }
+
+ freePluginVst2xId(pluginId);
+ return result;
+ }
+} // extern "C"
diff --git a/source/plugin/PluginVst2xHostCallback.h b/source/plugin/PluginVst2xHostCallback.h
new file mode 100644
index 0000000..4f4ff46
--- /dev/null
+++ b/source/plugin/PluginVst2xHostCallback.h
@@ -0,0 +1,45 @@
+//
+// PluginVst2xCallbacks.h - MrsWatson
+// Created by Nik Reiman on 13 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginVst2xCallbacks_h
+#define MrsWatson_PluginVst2xCallbacks_h
+
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+
+// Callbacks used by VST2.x plugins
+typedef AEffect *(*Vst2xPluginEntryFunc)(audioMasterCallback host);
+typedef VstIntPtr (*Vst2xPluginDispatcherFunc)(AEffect *effect, VstInt32 opCode, VstInt32 index, VstIntPtr value, void *ptr, float opt);
+typedef float (*Vst2xPluginGetParameterFunc)(AEffect *effect, VstInt32 index);
+typedef void (*Vst2xPluginSetParameterFunc)(AEffect *effect, VstInt32 index, float value);
+typedef void (*Vst2xPluginProcessFunc)(AEffect *effect, float **inputs, float **outputs, VstInt32 sampleFrames);
+
+extern "C" {
+ VstIntPtr VSTCALLBACK pluginVst2xHostCallback(AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *dataPtr, float opt);
+}
+
+#endif
diff --git a/source/plugin/PluginVst2xId.c b/source/plugin/PluginVst2xId.c
new file mode 100644
index 0000000..3fd8038
--- /dev/null
+++ b/source/plugin/PluginVst2xId.c
@@ -0,0 +1,101 @@
+//
+// PluginVst2xId.c - MrsWatson
+// Created by Nik Reiman on 07 Jun 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "base/CharString.h"
+#include "plugin/PluginVst2xId.h"
+
+static CharString _convertIntIdToString(const unsigned long id)
+{
+ CharString result = newCharStringWithCapacity(5);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ result->data[i] = (char)(id >> ((3 - i) * 8) & 0xff);
+ }
+
+ return result;
+}
+
+static unsigned long _convertStringIdToInt(const CharString idString)
+{
+ unsigned long result = 0;
+ int i;
+
+ if (idString != NULL && strlen(idString->data) == 4) {
+ for (i = 0; i < 4; i++) {
+ result |= (unsigned long)(idString->data[i]) << ((3 - i) * 8);
+ }
+ }
+
+ return result;
+}
+
+PluginVst2xId newPluginVst2xId(void)
+{
+ PluginVst2xId pluginVst2xId = (PluginVst2xId)malloc(sizeof(PluginVst2xIdMembers));
+
+ pluginVst2xId->id = 0;
+ pluginVst2xId->idString = newCharStringWithCString(PLUGIN_VST2X_ID_UNKNOWN);
+
+ return pluginVst2xId;
+}
+
+PluginVst2xId newPluginVst2xIdWithId(unsigned long id)
+{
+ PluginVst2xId pluginVst2xId = newPluginVst2xId();
+
+ pluginVst2xId->id = id;
+ freeCharString(pluginVst2xId->idString);
+ pluginVst2xId->idString = _convertIntIdToString(id);
+
+ return pluginVst2xId;
+}
+
+PluginVst2xId newPluginVst2xIdWithStringId(const CharString idString)
+{
+ PluginVst2xId pluginVst2xId = newPluginVst2xId();
+
+ pluginVst2xId->id = _convertStringIdToInt(idString);
+
+ if (idString != NULL && pluginVst2xId->id > 0) {
+ charStringCopy(pluginVst2xId->idString, idString);
+ }
+
+ return pluginVst2xId;
+}
+
+void freePluginVst2xId(PluginVst2xId self)
+{
+ if (self) {
+ freeCharString(self->idString);
+ free(self);
+ }
+}
diff --git a/source/plugin/PluginVst2xId.h b/source/plugin/PluginVst2xId.h
new file mode 100644
index 0000000..185a9c1
--- /dev/null
+++ b/source/plugin/PluginVst2xId.h
@@ -0,0 +1,45 @@
+//
+// PluginVst2xId.h - MrsWatson
+// Created by Nik Reiman on 07 Jun 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#ifndef MrsWatson_PluginVst2xId_h
+#define MrsWatson_PluginVst2xId_h
+
+#define PLUGIN_VST2X_ID_UNKNOWN "????"
+
+typedef struct {
+ unsigned long id;
+ CharString idString;
+} PluginVst2xIdMembers;
+typedef PluginVst2xIdMembers *PluginVst2xId;
+
+PluginVst2xId newPluginVst2xId(void);
+PluginVst2xId newPluginVst2xIdWithId(unsigned long id);
+PluginVst2xId newPluginVst2xIdWithStringId(const CharString stringId);
+
+void freePluginVst2xId(PluginVst2xId self);
+
+#endif
diff --git a/source/plugin/PluginVst2xLinux.cpp b/source/plugin/PluginVst2xLinux.cpp
new file mode 100644
index 0000000..5543344
--- /dev/null
+++ b/source/plugin/PluginVst2xLinux.cpp
@@ -0,0 +1,113 @@
+//
+// PluginVst2xLinux.c - MrsWatson
+// Created by Nik Reiman on 13 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#if LINUX
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+
+extern "C" {
+#include <dlfcn.h>
+#include <stdlib.h>
+#include "base/CharString.h"
+#include "base/LinkedList.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginVst2xHostCallback.h"
+
+ LinkedList getVst2xPluginLocations(CharString currentDirectory)
+ {
+ LinkedList locations = newLinkedList();
+ CharString locationBuffer;
+ char *vstPathEnv;
+
+ linkedListAppend(locations, currentDirectory);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s/.vst", getenv("HOME"));
+ linkedListAppend(locations, locationBuffer);
+
+ locationBuffer = newCharString();
+ vstPathEnv = getenv("VST_PATH");
+
+ if (vstPathEnv != NULL) {
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s", vstPathEnv);
+ linkedListAppend(locations, locationBuffer);
+ } else {
+ freeCharString(locationBuffer);
+ }
+
+ return locations;
+ }
+
+ LibraryHandle getLibraryHandleForPlugin(const CharString pluginAbsolutePath)
+ {
+ void *libraryHandle = dlopen(pluginAbsolutePath->data, RTLD_NOW | RTLD_LOCAL);
+
+ if (libraryHandle == NULL) {
+ logError("Could not open library, %s", dlerror());
+ return NULL;
+ }
+
+ return libraryHandle;
+ }
+
+ AEffect *loadVst2xPlugin(LibraryHandle libraryHandle)
+ {
+ // Somewhat cheap hack to avoid a tricky compiler warning. Casting from void* to a proper function
+ // pointer will cause GCC to warn that "ISO C++ forbids casting between pointer-to-function and
+ // pointer-to-object". Here, we represent both types in a union and use the correct one in the given
+ // context, thus avoiding the need to cast anything.
+ // See also: http://stackoverflow.com/a/2742234/14302
+ union {
+ Vst2xPluginEntryFunc entryPointFuncPtr;
+ void *entryPointVoidPtr;
+ } entryPoint;
+
+ entryPoint.entryPointVoidPtr = dlsym(libraryHandle, "VSTPluginMain");
+
+ if (entryPoint.entryPointVoidPtr == NULL) {
+ entryPoint.entryPointVoidPtr = dlsym(libraryHandle, "main");
+
+ if (entryPoint.entryPointVoidPtr == NULL) {
+ logError("Couldn't get a pointer to plugin's main()");
+ return NULL;
+ }
+ }
+
+ Vst2xPluginEntryFunc mainEntryPoint = entryPoint.entryPointFuncPtr;
+ AEffect *plugin = mainEntryPoint(pluginVst2xHostCallback);
+ return plugin;
+ }
+
+ void closeLibraryHandle(LibraryHandle libraryHandle)
+ {
+ if (dlclose(libraryHandle) != 0) {
+ logWarn("Could not safely close plugin, possible resource leak");
+ }
+ }
+
+} // extern "C"
+#endif
diff --git a/source/plugin/PluginVst2xMac.cpp b/source/plugin/PluginVst2xMac.cpp
new file mode 100644
index 0000000..75369d4
--- /dev/null
+++ b/source/plugin/PluginVst2xMac.cpp
@@ -0,0 +1,136 @@
+//
+// PluginVst2xMac.c - MrsWatson
+// Created by Nik Reiman on 13 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#if MACOSX
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+
+extern "C" {
+#include <stdlib.h>
+#include <CoreFoundation/CFBundle.h>
+#include "base/CharString.h"
+#include "logging/EventLogger.h"
+#include "plugin/PluginVst2xHostCallback.h"
+
+ LinkedList getVst2xPluginLocations(CharString currentDirectory);
+ LinkedList getVst2xPluginLocations(CharString currentDirectory)
+ {
+ LinkedList locations = newLinkedList();
+ CharString locationBuffer;
+
+ linkedListAppend(locations, currentDirectory);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "/Library/Audio/Plug-Ins/VST");
+ linkedListAppend(locations, locationBuffer);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s/Library/Audio/Plug-Ins/VST", getenv("HOME"));
+ linkedListAppend(locations, locationBuffer);
+
+ return locations;
+ }
+
+ LibraryHandle getLibraryHandleForPlugin(const CharString pluginAbsolutePath);
+ LibraryHandle getLibraryHandleForPlugin(const CharString pluginAbsolutePath)
+ {
+ // Create a path to the bundle
+ CFStringRef pluginPathStringRef = CFStringCreateWithCString(NULL, pluginAbsolutePath->data, kCFStringEncodingASCII);
+ CFURLRef bundleUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pluginPathStringRef, kCFURLPOSIXPathStyle, true);
+
+ if (bundleUrl == NULL) {
+ logError("Couldn't make URL reference for plugin");
+ return NULL;
+ }
+
+ // Open the bundle
+ CFBundleRef bundleRef = CFBundleCreate(kCFAllocatorDefault, bundleUrl);
+
+ if (bundleRef == NULL) {
+ logError("Couldn't create bundle reference");
+ CFRelease(pluginPathStringRef);
+ CFRelease(bundleUrl);
+ return NULL;
+ }
+
+ // Clean up
+ CFRelease(pluginPathStringRef);
+ CFRelease(bundleUrl);
+
+ return bundleRef;
+ }
+
+ AEffect *loadVst2xPlugin(LibraryHandle libraryHandle);
+ AEffect *loadVst2xPlugin(LibraryHandle libraryHandle)
+ {
+ // Somewhat cheap hack to avoid a tricky compiler warning. Casting from void* to a proper function
+ // pointer will cause GCC to warn that "ISO C++ forbids casting between pointer-to-function and
+ // pointer-to-object". Here, we represent both types in a union and use the correct one in the given
+ // context, thus avoiding the need to cast anything.
+ // See also: http://stackoverflow.com/a/2742234/14302
+ union {
+ Vst2xPluginEntryFunc entryPointFuncPtr;
+ void *entryPointVoidPtr;
+ } entryPoint;
+
+ entryPoint.entryPointVoidPtr = CFBundleGetFunctionPointerForName(libraryHandle, CFSTR("VSTPluginMain"));
+ Vst2xPluginEntryFunc mainEntryPoint = entryPoint.entryPointFuncPtr;
+
+ // VST plugins previous to the 2.4 SDK used main_macho for the entry point name
+ if (mainEntryPoint == NULL) {
+ entryPoint.entryPointVoidPtr = CFBundleGetFunctionPointerForName(libraryHandle, CFSTR("main_macho"));
+ mainEntryPoint = entryPoint.entryPointFuncPtr;
+ }
+
+ if (mainEntryPoint == NULL) {
+ logError("Couldn't get a pointer to plugin's main()");
+ CFBundleUnloadExecutable(libraryHandle);
+ CFRelease(libraryHandle);
+ return NULL;
+ }
+
+ AEffect *plugin = mainEntryPoint(pluginVst2xHostCallback);
+
+ if (plugin == NULL) {
+ logError("Plugin's main() returns null");
+ CFBundleUnloadExecutable(libraryHandle);
+ CFRelease(libraryHandle);
+ return NULL;
+ }
+
+ return plugin;
+ }
+
+ void closeLibraryHandle(LibraryHandle libraryHandle);
+ void closeLibraryHandle(LibraryHandle libraryHandle)
+ {
+ CFBundleUnloadExecutable(libraryHandle);
+ CFRelease(libraryHandle);
+ }
+
+} // extern "C"
+#endif
diff --git a/source/plugin/PluginVst2xWindows.cpp b/source/plugin/PluginVst2xWindows.cpp
new file mode 100644
index 0000000..bbc2c8c
--- /dev/null
+++ b/source/plugin/PluginVst2xWindows.cpp
@@ -0,0 +1,114 @@
+//
+// PluginVst2xWindows.c - MrsWatson
+// Created by Nik Reiman on 13 May 13.
+// Copyright (c) 2013 Teragon Audio. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+
+#if WINDOWS
+#define VST_FORCE_DEPRECATED 0
+#include "aeffectx.h"
+#include "plugin/PluginVst2xHostCallback.h"
+
+extern "C" {
+#include <stdio.h>
+#include "base/PlatformInfo.h"
+#include "logging/EventLogger.h"
+
+ static const char *kPlatformWindowsProgramFolder = "C:\\Program Files";
+ static const char *kPlatformWindows32BitProgramFolder = "C:\\Program Files (x86)";
+
+ LinkedList getVst2xPluginLocations(CharString currentDirectory)
+ {
+ LinkedList locations = newLinkedList();
+ CharString locationBuffer;
+ const char *programFiles = (!platformInfoIsRuntime64Bit() && platformInfoIsHost64Bit()) ?
+ kPlatformWindows32BitProgramFolder : kPlatformWindowsProgramFolder;
+
+ linkedListAppend(locations, currentDirectory);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "C:\\VstPlugins");
+ linkedListAppend(locations, locationBuffer);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s\\VstPlugIns", programFiles);
+ linkedListAppend(locations, locationBuffer);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s\\Common Files\\VstPlugIns", programFiles);
+ linkedListAppend(locations, locationBuffer);
+
+ locationBuffer = newCharString();
+ snprintf(locationBuffer->data, (size_t)(locationBuffer->capacity), "%s\\Steinberg\\VstPlugIns", programFiles);
+ linkedListAppend(locations, locationBuffer);
+
+ return locations;
+ }
+
+ LibraryHandle getLibraryHandleForPlugin(const CharString pluginAbsolutePath)
+ {
+ HMODULE libraryHandle = LoadLibraryExA((LPCSTR)pluginAbsolutePath->data, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+ DWORD errorCode = GetLastError();
+
+ if (libraryHandle == NULL) {
+ if (errorCode == ERROR_BAD_EXE_FORMAT) {
+ logError("Could not open library, wrong architecture");
+ } else {
+ logError("Could not open library, error code '%s'", stringForLastError(errorCode));
+ }
+
+ return NULL;
+ }
+
+ return libraryHandle;
+ }
+
+ AEffect *loadVst2xPlugin(LibraryHandle libraryHandle)
+ {
+ Vst2xPluginEntryFunc entryPoint = (Vst2xPluginEntryFunc)GetProcAddress(libraryHandle, "VSTPluginMain");
+
+ if (entryPoint == NULL) {
+ entryPoint = (Vst2xPluginEntryFunc)GetProcAddress(libraryHandle, "VstPluginMain()");
+ }
+
+ if (entryPoint == NULL) {
+ entryPoint = (Vst2xPluginEntryFunc)GetProcAddress(libraryHandle, "main");
+ }
+
+ if (entryPoint == NULL) {
+ logError("Couldn't get a pointer to plugin's main()");
+ return NULL;
+ }
+
+ AEffect *plugin = entryPoint(pluginVst2xHostCallback);
+ return plugin;
+ }
+
+ void closeLibraryHandle(LibraryHandle libraryHandle)
+ {
+ FreeLibrary(libraryHandle);
+ }
+
+} // extern "C"
+#endif