diff options
Diffstat (limited to 'source/plugin/PluginVst2xHostCallback.cpp')
| -rw-r--r-- | source/plugin/PluginVst2xHostCallback.cpp | 433 |
1 files changed, 433 insertions, 0 deletions
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" |
