Initial release

This commit is contained in:
civ
2026-08-16 18:24:52 +07:00
commit 876886a39a
13244 changed files with 2353959 additions and 0 deletions
@@ -0,0 +1,471 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/again.cpp
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST SDK 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "again.h"
#include "againcids.h" // for class ids
#include "againparamids.h"
#include "againprocess.h"
#include "public.sdk/source/vst/vstaudioprocessoralgo.h"
#include "public.sdk/source/vst/vsthelpers.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/vst/vstpresetkeys.h" // for use of IStreamAttributes
#include "base/source/fstreamer.h"
#include <cstdio>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// AGain Implementation
//------------------------------------------------------------------------
AGain::AGain ()
{
// register its editor class (the same than used in againentry.cpp)
setControllerClass (AGainControllerUID);
}
//------------------------------------------------------------------------
AGain::~AGain ()
{
// nothing to do here yet..
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::initialize (FUnknown* context)
{
//---always initialize the parent-------
tresult result = AudioEffect::initialize (context);
// if everything Ok, continue
if (result != kResultOk)
{
return result;
}
//---create Audio In/Out busses------
// we want a stereo Input and a Stereo Output
addAudioInput (STR16 ("Stereo In"), SpeakerArr::kStereo);
addAudioOutput (STR16 ("Stereo Out"), SpeakerArr::kStereo);
//---create Event In/Out busses (1 bus with only 1 channel)------
addEventInput (STR16 ("Event In"), 1);
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::terminate ()
{
// nothing to do here yet...except calling our parent terminate
return AudioEffect::terminate ();
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::setActive (TBool state)
{
if (state)
{
sendTextMessage ("AGain::setActive (true)");
}
else
{
sendTextMessage ("AGain::setActive (false)");
}
// reset the VuMeter value
fVuPPMOld = 0.f;
// call our parent setActive
return AudioEffect::setActive (state);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::process (ProcessData& data)
{
// finally the process function
// In this example there are 4 steps:
// 1) Read inputs parameters coming from host (in order to adapt our model values)
// 2) Read inputs events coming from host (we apply a gain reduction depending of the velocity
// of pressed key) 3) Process the gain of the input buffer to the output buffer 4) Write the new
// VUmeter value to the output Parameters queue
//---1) Read inputs parameter changes-----------
if (IParameterChanges* paramChanges = data.inputParameterChanges)
{
int32 numParamsChanged = paramChanges->getParameterCount ();
// for each parameter which are some changes in this audio block:
for (int32 i = 0; i < numParamsChanged; i++)
{
if (IParamValueQueue* paramQueue = paramChanges->getParameterData (i))
{
ParamValue value;
int32 sampleOffset;
int32 numPoints = paramQueue->getPointCount ();
switch (paramQueue->getParameterId ())
{
case kGainId:
// we use in this example only the last point of the queue.
// in some wanted case for specific kind of parameter it makes sense to
// retrieve all points and process the whole audio block in small blocks.
if (paramQueue->getPoint (numPoints - 1, sampleOffset, value) ==
kResultTrue)
{
fGain = (float)value;
}
break;
case kBypassId:
if (paramQueue->getPoint (numPoints - 1, sampleOffset, value) ==
kResultTrue)
{
bBypass = (value > 0.5f);
}
break;
}
}
}
}
//---2) Read input events-------------
if (IEventList* eventList = data.inputEvents)
{
int32 numEvent = eventList->getEventCount ();
for (int32 i = 0; i < numEvent; i++)
{
Event event {};
if (eventList->getEvent (i, event) == kResultOk)
{
switch (event.type)
{
//--- -------------------
case Event::kNoteOnEvent:
// use the velocity as gain modifier
fGainReduction = event.noteOn.velocity;
break;
//--- -------------------
case Event::kNoteOffEvent:
// noteOff reset the reduction
fGainReduction = 0.f;
break;
}
}
}
}
//--- ----------------------------------
//---3) Process Audio---------------------
//--- ----------------------------------
if (data.numInputs == 0 || data.numOutputs == 0)
{
// nothing to do
return kResultOk;
}
// (simplification) we suppose in this example that we have the same input channel count than
// the output
int32 numChannels = data.inputs[0].numChannels;
//---get audio buffers----------------
uint32 sampleFramesSize = getSampleFramesSizeInBytes (processSetup, data.numSamples);
void** in = getChannelBuffersPointer (processSetup, data.inputs[0]);
void** out = getChannelBuffersPointer (processSetup, data.outputs[0]);
float fVuPPM = 0.f;
//---check if silence---------------
// check if all channel are silent then process silent
if (data.inputs[0].silenceFlags == getChannelMask (data.inputs[0].numChannels))
{
// mark output silence too (it will help the host to propagate the silence)
data.outputs[0].silenceFlags = data.inputs[0].silenceFlags;
// the plug-in has to be sure that if it sets the flags silence that the output buffer are
// clear
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be cleared if the buffers are the same (in this case input buffer are
// already cleared by the host)
if (in[i] != out[i])
{
memset (out[i], 0, sampleFramesSize);
}
}
fVuPPM = 0.f;
}
else // we have to process (no silence)
{
// mark our outputs has not silent
data.outputs[0].silenceFlags = 0;
//---in bypass mode outputs should be like inputs-----
if (bBypass)
{
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be copied if the buffers are the same
if (in[i] != out[i])
{
memcpy (out[i], in[i], sampleFramesSize);
}
}
if (data.symbolicSampleSize == kSample32)
fVuPPM = processVuPPM<Sample32> ((Sample32**)in, numChannels, data.numSamples);
else
fVuPPM = static_cast<float> (
processVuPPM<Sample64> ((Sample64**)(in), numChannels, data.numSamples));
}
else
{
//---apply gain factor----------
float gain = (fGain - fGainReduction);
if (bHalfGain)
{
gain = gain * 0.5f;
}
// if the applied gain is nearly zero, we could say that the outputs are zeroed and we set
// the silence flags.
if (gain < 0.0000001)
{
for (int32 i = 0; i < numChannels; i++)
{
memset (out[i], 0, sampleFramesSize);
}
// this will set to 1 all channels
data.outputs[0].silenceFlags = getChannelMask (data.outputs[0].numChannels);
}
else
{
if (data.symbolicSampleSize == kSample32)
fVuPPM = processAudio<Sample32> ((Sample32**)in, (Sample32**)out, numChannels,
data.numSamples, gain);
else
fVuPPM = static_cast<float> (processAudio<Sample64> (
(Sample64**)in, (Sample64**)out, numChannels, data.numSamples, gain));
}
}
}
//---3) Write outputs parameter changes-----------
IParameterChanges* outParamChanges = data.outputParameterChanges;
// a new value of VuMeter will be send to the host
// (the host will send it back in sync to our controller for updating our editor)
if (outParamChanges && fVuPPMOld != fVuPPM)
{
int32 index = 0;
IParamValueQueue* paramQueue = outParamChanges->addParameterData (kVuPPMId, index);
if (paramQueue)
{
int32 index2 = 0;
paramQueue->addPoint (0, fVuPPM, index2);
}
}
fVuPPMOld = fVuPPM;
return kResultOk;
}
//------------------------------------------------------------------------
tresult AGain::receiveText (const char* text)
{
// received from Controller
fprintf (stderr, "[AGain] received: ");
fprintf (stderr, "%s", text);
fprintf (stderr, "\n");
bHalfGain = !bHalfGain;
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::setState (IBStream* state)
{
// called when we load a preset, the model has to be reloaded
IBStreamer streamer (state, kLittleEndian);
float savedGain = 0.f;
if (streamer.readFloat (savedGain) == false)
return kResultFalse;
float savedGainReduction = 0.f;
if (streamer.readFloat (savedGainReduction) == false)
return kResultFalse;
int32 savedBypass = 0;
if (streamer.readInt32 (savedBypass) == false)
return kResultFalse;
fGain = savedGain;
fGainReduction = savedGainReduction;
bBypass = savedBypass > 0;
if (Helpers::isProjectState (state) == kResultTrue)
{
// we are in project loading context...
// Example of using the IStreamAttributes interface
if (auto stream = U::cast<IStreamAttributes> (state))
{
if (IAttributeList* list = stream->getAttributes ())
{
// get the full file path of this state
TChar fullPath[1024];
memset (fullPath, 0, 1024 * sizeof (TChar));
if (list->getString (PresetAttributes::kFilePathStringType, fullPath,
1024 * sizeof (TChar)) == kResultTrue)
{
// here we have the full path ...
}
}
}
}
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::getState (IBStream* state)
{
// here we need to save the model
IBStreamer streamer (state, kLittleEndian);
streamer.writeFloat (fGain);
streamer.writeFloat (fGainReduction);
streamer.writeInt32 (bBypass ? 1 : 0);
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::setupProcessing (ProcessSetup& newSetup)
{
// called before the process call, always in a disable state (not active)
// here we keep a trace of the processing mode (offline,...) for example.
currentProcessMode = newSetup.processMode;
return AudioEffect::setupProcessing (newSetup);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs, int32 numOuts)
{
if (numIns == 1 && numOuts == 1)
{
// the host wants Mono => Mono (or 1 channel -> 1 channel)
if (SpeakerArr::getChannelCount (inputs[0]) == 1 &&
SpeakerArr::getChannelCount (outputs[0]) == 1)
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
// check if we are Mono => Mono, if not we need to recreate the busses
if (bus->getArrangement () != inputs[0])
{
getAudioInput (0)->setArrangement (inputs[0]);
getAudioInput (0)->setName (STR16 ("Mono In"));
getAudioOutput (0)->setArrangement (outputs[0]);
getAudioOutput (0)->setName (STR16 ("Mono Out"));
}
return kResultOk;
}
}
// the host wants something else than Mono => Mono,
// in this case we are always Stereo => Stereo
else
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
tresult result = kResultFalse;
// the host wants 2->2 (could be LsRs -> LsRs)
if (SpeakerArr::getChannelCount (inputs[0]) == 2 &&
SpeakerArr::getChannelCount (outputs[0]) == 2)
{
getAudioInput (0)->setArrangement (inputs[0]);
getAudioInput (0)->setName (STR16 ("Stereo In"));
getAudioOutput (0)->setArrangement (outputs[0]);
getAudioOutput (0)->setName (STR16 ("Stereo Out"));
result = kResultTrue;
}
// the host want something different than 1->1 or 2->2 : in this case we want stereo
else if (bus->getArrangement () != SpeakerArr::kStereo)
{
getAudioInput (0)->setArrangement (SpeakerArr::kStereo);
getAudioInput (0)->setName (STR16 ("Stereo In"));
getAudioOutput (0)->setArrangement (SpeakerArr::kStereo);
getAudioOutput (0)->setName (STR16 ("Stereo Out"));
result = kResultFalse;
}
return result;
}
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::canProcessSampleSize (int32 symbolicSampleSize)
{
if (symbolicSampleSize == kSample32)
return kResultTrue;
// we support double processing
if (symbolicSampleSize == kSample64)
return kResultTrue;
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGain::notify (IMessage* message)
{
if (!message)
return kInvalidArgument;
if (strcmp (message->getMessageID (), "BinaryMessage") == 0)
{
const void* data;
uint32 size;
if (message->getAttributes ()->getBinary ("MyData", data, size) == kResultOk)
{
// we are in UI thread
// size should be 100
if (size == 100 && ((char*)data)[1] == 1) // yeah...
{
fprintf (stderr, "[AGain] received the binary message!\n");
}
return kResultOk;
}
}
return AudioEffect::notify (message);
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,101 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/again.h
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST SDK 3.0
// Simple gain plug-in with gain, bypass values and 1 midi input
// and the same plug-in with sidechain
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/vstaudioeffect.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// AGain: directly derived from the helper class AudioEffect
//------------------------------------------------------------------------
class AGain : public AudioEffect
{
public:
AGain ();
~AGain () override;
//--- ---------------------------------------------------------------------
// create function required for plug-in factory,
// it will be called to create new instances of this plug-in
//--- ---------------------------------------------------------------------
static FUnknown* createInstance (void* /*context*/) { return (IAudioProcessor*)new AGain; }
//--- ---------------------------------------------------------------------
// AudioEffect overrides:
//--- ---------------------------------------------------------------------
/** Called at first after constructor */
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
/** Called at the end before destructor */
tresult PLUGIN_API terminate () SMTG_OVERRIDE;
/** Switch the plug-in on/off */
tresult PLUGIN_API setActive (TBool state) SMTG_OVERRIDE;
/** Here we go...the process call */
tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE;
/** Test of a communication channel between controller and component */
tresult receiveText (const char* text) SMTG_OVERRIDE;
/** For persistence */
tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE;
/** Will be called before any process call */
tresult PLUGIN_API setupProcessing (ProcessSetup& newSetup) SMTG_OVERRIDE;
/** Bus arrangement managing: in this example the 'again' will be mono for mono input/output and
* stereo for other arrangements. */
tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs,
int32 numOuts) SMTG_OVERRIDE;
/** Asks if a given sample size is supported see \ref SymbolicSampleSizes. */
tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE;
/** We want to receive message. */
tresult PLUGIN_API notify (IMessage* message) SMTG_OVERRIDE;
//------------------------------------------------------------------------
protected:
//==============================================================================
template <typename SampleType>
SampleType processAudio (SampleType** input, SampleType** output, int32 numChannels,
int32 sampleFrames, float gain);
template <typename SampleType>
SampleType processVuPPM (SampleType** input, int32 numChannels, int32 sampleFrames);
// our model values
float fGain {1.f};
float fGainReduction {0.f};
float fVuPPMOld {0.f};
int32 currentProcessMode {-1}; // -1 means not initialized
bool bHalfGain {false};
bool bBypass {false};
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,32 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againcids.h
// Created by : Steinberg, 12/2007
// Description : define the class IDs for AGain
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
namespace Steinberg {
namespace Vst {
// Here are defined the UIDs for the 2 processors (2 plug-ins) and 1 controller (shared by the 2 plug-ins)
static const FUID AGainProcessorUID (0x84E8DE5F, 0x92554F53, 0x96FAE413, 0x3C935A18);
static const FUID AGainWithSideChainProcessorUID (0x41347FD6, 0xFED64094, 0xAFBB12B7, 0xDBA1D441);
static const FUID AGainControllerUID (0xD39D5B65, 0xD7AF42FA, 0x843F4AC8, 0x41EB04F0);
#define AGainVST3Category "Fx"
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,393 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againcontroller.cpp
// Created by : Steinberg, 04/2005
// Description : AGain Controller Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "againcontroller.h"
#include "againparamids.h"
#include "againuimessagecontroller.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/base/ustring.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "base/source/fstreamer.h"
#include "base/source/fstring.h"
#include "vstgui/uidescription/delegationcontroller.h"
#include <cmath>
#include <cstdio>
using namespace VSTGUI;
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// GainParameter Declaration
// example of custom parameter (overwriting to and fromString)
//------------------------------------------------------------------------
class GainParameter : public Parameter
{
public:
GainParameter (int32 flags, int32 id);
void toString (ParamValue normValue, String128 string) const SMTG_OVERRIDE;
bool fromString (const TChar* string, ParamValue& normValue) const SMTG_OVERRIDE;
};
//------------------------------------------------------------------------
// GainParameter Implementation
//------------------------------------------------------------------------
GainParameter::GainParameter (int32 flags, int32 id)
{
Steinberg::UString (info.title, USTRINGSIZE (info.title)).assign (USTRING ("Gain"));
Steinberg::UString (info.units, USTRINGSIZE (info.units)).assign (USTRING ("dB"));
info.flags = flags;
info.id = id;
info.stepCount = 0;
info.defaultNormalizedValue = 0.5f;
info.unitId = kRootUnitId;
setNormalized (1.f);
}
//------------------------------------------------------------------------
void GainParameter::toString (ParamValue normValue, String128 string) const
{
char text[32];
if (normValue > 0.0001)
{
snprintf (text, 32, "%.2f", 20 * log10f ((float)normValue));
}
else
{
strcpy (text, "-oo");
}
Steinberg::UString (string, 128).fromAscii (text);
}
//------------------------------------------------------------------------
bool GainParameter::fromString (const TChar* string, ParamValue& normValue) const
{
String wrapper ((TChar*)string); // don't know buffer size here!
double tmp = 0.0;
if (wrapper.scanFloat (tmp))
{
// allow only values between -oo and 0dB
if (tmp > 0.0)
{
tmp = -tmp;
}
normValue = expf (logf (10.f) * (float)tmp / 20.f);
return true;
}
return false;
}
//------------------------------------------------------------------------
// AGainController Implementation
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::initialize (FUnknown* context)
{
tresult result = EditControllerEx1::initialize (context);
if (result != kResultOk)
{
return result;
}
//--- Create Units-------------
UnitInfo unitInfo {};
Unit* unit;
// create root only if you want to use the programListId
/* unitInfo.id = kRootUnitId; // always for Root Unit
unitInfo.parentUnitId = kNoParentUnitId; // always for Root Unit
Steinberg::UString (unitInfo.name, USTRINGSIZE (unitInfo.name)).assign (USTRING ("Root"));
unitInfo.programListId = kNoProgramListId;
unit = new Unit (unitInfo);
addUnitInfo (unit);*/
// create a unit1 for the gain
unitInfo.id = 1;
unitInfo.parentUnitId = kRootUnitId; // attached to the root unit
Steinberg::UString (unitInfo.name, USTRINGSIZE (unitInfo.name)).assign (USTRING ("Unit1"));
unitInfo.programListId = kNoProgramListId;
unit = new Unit (unitInfo);
addUnit (unit);
//---Create Parameters------------
//---Gain parameter--
auto* gainParam = new GainParameter (ParameterInfo::kCanAutomate, kGainId);
parameters.addParameter (gainParam);
gainParam->setUnitID (1);
//---VuMeter parameter---
int32 stepCount = 0;
ParamValue defaultVal = 0;
int32 flags = ParameterInfo::kIsReadOnly;
int32 tag = kVuPPMId;
parameters.addParameter (STR16 ("VuPPM"), nullptr, stepCount, defaultVal, flags, tag);
//---Bypass parameter---
stepCount = 1;
defaultVal = 0;
flags = ParameterInfo::kCanAutomate | ParameterInfo::kIsBypass;
tag = kBypassId;
parameters.addParameter (STR16 ("Bypass"), nullptr, stepCount, defaultVal, flags, tag);
//---Custom state init------------
String str ("Hello World!");
str.copyTo16 (defaultMessageText, 0, 127);
return result;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::terminate ()
{
return EditControllerEx1::terminate ();
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::setComponentState (IBStream* state)
{
// we receive the current state of the component (processor part)
// we read only the gain and bypass value...
if (!state)
return kResultFalse;
IBStreamer streamer (state, kLittleEndian);
float savedGain = 0.f;
if (streamer.readFloat (savedGain) == false)
return kResultFalse;
setParamNormalized (kGainId, savedGain);
// jump the GainReduction
streamer.seek (sizeof (float), kSeekCurrent);
int32 bypassState = 0;
if (streamer.readInt32 (bypassState) == false)
return kResultFalse;
setParamNormalized (kBypassId, bypassState ? 1 : 0);
return kResultOk;
}
//------------------------------------------------------------------------
IPlugView* PLUGIN_API AGainController::createView (const char* _name)
{
// someone wants my editor
ConstString name (_name);
if (name == ViewType::kEditor)
{
auto* view = new VST3Editor (this, "view", "again.uidesc");
return view;
}
return nullptr;
}
//------------------------------------------------------------------------
IController* AGainController::createSubController (UTF8StringPtr name,
const IUIDescription* /*description*/,
VST3Editor* /*editor*/)
{
if (UTF8StringView (name) == "MessageController")
{
auto* controller = new UIMessageController (this);
addUIMessageController (controller);
return controller;
}
return nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::setState (IBStream* state)
{
IBStreamer streamer (state, kLittleEndian);
int8 byteOrder;
if (streamer.readInt8 (byteOrder) == false)
return kResultFalse;
if (streamer.readRaw (defaultMessageText, 128 * sizeof (TChar)) == false)
return kResultFalse;
// if the byteorder doesn't match, byte swap the text array ...
if (byteOrder != BYTEORDER)
{
for (int32 i = 0; i < 128; i++)
{
SWAP_16 (defaultMessageText[i])
}
}
// update our editors
for (auto& uiMessageController : uiMessageControllers)
uiMessageController->setMessageText (defaultMessageText);
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::getState (IBStream* state)
{
// here we can save UI settings for example
// as we save a Unicode string, we must know the byteorder when setState is called
IBStreamer streamer (state, kLittleEndian);
int8 byteOrder = BYTEORDER;
if (streamer.writeInt8 (byteOrder) == false)
return kResultFalse;
if (streamer.writeRaw (defaultMessageText, 128 * sizeof (TChar)) == false)
return kResultFalse;
return kResultTrue;
}
//------------------------------------------------------------------------
tresult AGainController::receiveText (const char* text)
{
// received from Component
if (text)
{
fprintf (stderr, "[AGainController] received: ");
fprintf (stderr, "%s", text);
fprintf (stderr, "\n");
}
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::setParamNormalized (ParamID tag, ParamValue value)
{
// called from host to update our parameters state
tresult result = EditControllerEx1::setParamNormalized (tag, value);
return result;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::getParamStringByValue (ParamID tag, ParamValue valueNormalized,
String128 string)
{
/* example, but better to use a custom Parameter as seen in GainParameter
switch (tag)
{
case kGainId:
{
char text[32];
if (valueNormalized > 0.0001)
{
sprintf (text, "%.2f", 20 * log10f ((float)valueNormalized));
}
else
strcpy (text, "-oo");
Steinberg::UString (string, 128).fromAscii (text);
return kResultTrue;
}
}*/
return EditControllerEx1::getParamStringByValue (tag, valueNormalized, string);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::getParamValueByString (ParamID tag, TChar* string,
ParamValue& valueNormalized)
{
/* example, but better to use a custom Parameter as seen in GainParameter
switch (tag)
{
case kGainId:
{
Steinberg::UString wrapper ((TChar*)string, -1); // don't know buffer size here!
double tmp = 0.0;
if (wrapper.scanFloat (tmp))
{
valueNormalized = expf (logf (10.f) * (float)tmp / 20.f);
return kResultTrue;
}
return kResultFalse;
}
}*/
return EditControllerEx1::getParamValueByString (tag, string, valueNormalized);
}
//------------------------------------------------------------------------
void AGainController::addUIMessageController (UIMessageController* controller)
{
uiMessageControllers.push_back (controller);
}
//------------------------------------------------------------------------
void AGainController::removeUIMessageController (UIMessageController* controller)
{
UIMessageControllerList::const_iterator it =
std::find (uiMessageControllers.begin (), uiMessageControllers.end (), controller);
if (it != uiMessageControllers.end ())
uiMessageControllers.erase (it);
}
//------------------------------------------------------------------------
void AGainController::setDefaultMessageText (String128 text)
{
String tmp (text);
tmp.copyTo16 (defaultMessageText, 0, 127);
}
//------------------------------------------------------------------------
TChar* AGainController::getDefaultMessageText ()
{
return defaultMessageText;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::queryInterface (const char* iid, void** obj)
{
QUERY_INTERFACE (iid, obj, IMidiMapping::iid, IMidiMapping)
return EditControllerEx1::queryInterface (iid, obj);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainController::getMidiControllerAssignment (int32 busIndex,
int16 /*midiChannel*/,
CtrlNumber midiControllerNumber,
ParamID& tag)
{
// we support for the Gain parameter all MIDI Channel but only first bus (there is only one!)
if (busIndex == 0 && midiControllerNumber == kCtrlVolume)
{
tag = kGainId;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,98 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againcontroller.h
// Created by : Steinberg, 04/2005
// Description : AGain Editor Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "vstgui/plugin-bindings/vst3editor.h"
#include "public.sdk/source/vst/vsteditcontroller.h"
#include <vector>
namespace Steinberg {
namespace Vst {
template <typename T>
class AGainUIMessageController;
//------------------------------------------------------------------------
// AGainController
//------------------------------------------------------------------------
class AGainController : public EditControllerEx1, public IMidiMapping, public VSTGUI::VST3EditorDelegate
{
public:
using UIMessageController = AGainUIMessageController<AGainController>;
using UTF8StringPtr = VSTGUI::UTF8StringPtr;
using IUIDescription = VSTGUI::IUIDescription;
using IController = VSTGUI::IController;
using VST3Editor = VSTGUI::VST3Editor;
//--- ---------------------------------------------------------------------
// create function required for plug-in factory,
// it will be called to create new instances of this controller
//--- ---------------------------------------------------------------------
static FUnknown* createInstance (void* /*context*/)
{
return (IEditController*)new AGainController;
}
//---from IPluginBase--------
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
tresult PLUGIN_API terminate () SMTG_OVERRIDE;
//---from EditController-----
tresult PLUGIN_API setComponentState (IBStream* state) SMTG_OVERRIDE;
IPlugView* PLUGIN_API createView (const char* name) SMTG_OVERRIDE;
tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API setParamNormalized (ParamID tag, ParamValue value) SMTG_OVERRIDE;
tresult PLUGIN_API getParamStringByValue (ParamID tag, ParamValue valueNormalized,
String128 string) SMTG_OVERRIDE;
tresult PLUGIN_API getParamValueByString (ParamID tag, TChar* string,
ParamValue& valueNormalized) SMTG_OVERRIDE;
//---from ComponentBase-----
tresult receiveText (const char* text) SMTG_OVERRIDE;
//---from IMidiMapping-----------------
tresult PLUGIN_API getMidiControllerAssignment (int32 busIndex, int16 channel,
CtrlNumber midiControllerNumber,
ParamID& tag) SMTG_OVERRIDE;
//---from VST3EditorDelegate-----------
IController* createSubController (UTF8StringPtr name, const IUIDescription* description,
VST3Editor* editor) SMTG_OVERRIDE;
DELEGATE_REFCOUNT (EditController)
tresult PLUGIN_API queryInterface (const char* iid, void** obj) SMTG_OVERRIDE;
//---Internal functions-------
void addUIMessageController (UIMessageController* controller);
void removeUIMessageController (UIMessageController* controller);
void setDefaultMessageText (String128 text);
TChar* getDefaultMessageText ();
//------------------------------------------------------------------------
private:
using UIMessageControllerList = std::vector<UIMessageController*>;
UIMessageControllerList uiMessageControllers;
String128 defaultMessageText {};
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,80 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againentry.cpp
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "again.h" // for AGain
#include "againsidechain.h" // for AGain SideChain
#include "againcontroller.h" // for AGainController
#include "againcids.h" // for class ids and category
#include "version.h" // for versioning
#include "public.sdk/source/main/pluginfactory.h"
#define stringPluginName "AGain VST3"
#define stringPluginSideChainName "AGain SideChain VST3"
#if TARGET_OS_IPHONE
#include "public.sdk/source/vst/vstguieditor.h"
extern void* moduleHandle;
#endif
using namespace Steinberg::Vst;
//------------------------------------------------------------------------
// VST Plug-in Entry
//------------------------------------------------------------------------
// Windows: do not forget to include a .def file in your project to export
// GetPluginFactory function!
//------------------------------------------------------------------------
BEGIN_FACTORY_DEF (stringCompanyName, stringCompanyWeb, stringCompanyEmail)
//---First plug-in included in this factory-------
// its kVstAudioEffectClass component
DEF_CLASS2 (INLINE_UID_FROM_FUID(AGainProcessorUID),
PClassInfo::kManyInstances, // cardinality
kVstAudioEffectClass, // the component category (do not change this)
stringPluginName, // here the plug-in name (to be changed)
Vst::kDistributable, // means that component and controller could be distributed on different computers
AGainVST3Category, // Subcategory for this plug-in (to be changed)
FULL_VERSION_STR, // Plug-in version (to be changed)
kVstVersionString, // the VST 3 SDK version (do not change this, always use this define)
Steinberg::Vst::AGain::createInstance) // function pointer called when this component should be instantiated
// its kVstComponentControllerClass component
DEF_CLASS2 (INLINE_UID_FROM_FUID (AGainControllerUID),
PClassInfo::kManyInstances, // cardinality
kVstComponentControllerClass,// the Controller category (do not change this)
stringPluginName "Controller", // controller name (can be the same as the component name)
0, // not used here
"", // not used here
FULL_VERSION_STR, // Plug-in version (to be changed)
kVstVersionString, // the VST 3 SDK version (do not change this, always use this define)
Steinberg::Vst::AGainController::createInstance)// function pointer called when this component should be instantiated
//---Second plug-in (AGain with sidechain (only component, use the same controller) included in this factory-------
DEF_CLASS2 (INLINE_UID_FROM_FUID(AGainWithSideChainProcessorUID),
PClassInfo::kManyInstances, // cardinality
kVstAudioEffectClass, // the component category (do not change this)
stringPluginSideChainName, // here the plug-in name (to be changed)
Vst::kDistributable, // means that component and controller could be distributed on different computers
AGainVST3Category, // Subcategory for this plug-in (to be changed)
FULL_VERSION_STR, // Plug-in version (to be changed)
kVstVersionString, // the VST 3 SDK version (do not change this, always use this define)
Steinberg::Vst::AGainWithSideChain::createInstance) // function pointer called when this component should be instantiated
//----for others plug-ins contained in this factory, put like for the first plug-in different DEF_CLASS2---
END_FACTORY
@@ -0,0 +1,25 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againparamids.h
// Created by : Steinberg, 12/2007
// Description : define the parameter IDs used by AGain
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
enum
{
/** parameter ID */
kGainId = 0, ///< for the gain value (is automatable)
kVuPPMId, ///< for the Vu value return to host (ReadOnly parameter for our UI)
kBypassId ///< Bypass value (we will handle the bypass process) (is automatable)
};
@@ -0,0 +1,81 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againprocess.h
// Created by : Steinberg, 11/2016
// Description : AGain Example for VST SDK 3.0
// Simple gain plug-in with gain, bypass values and 1 midi input
// and the same plug-in with sidechain
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
template <typename SampleType>
SampleType AGain::processAudio (SampleType** in, SampleType** out, int32 numChannels,
int32 sampleFrames, float gain)
{
SampleType vuPPM = 0;
// in real Plug-in it would be better to do dezippering to avoid jump (click) in gain value
for (int32 i = 0; i < numChannels; i++)
{
int32 samples = sampleFrames;
SampleType* ptrIn = (SampleType*)in[i];
SampleType* ptrOut = (SampleType*)out[i];
SampleType tmp;
while (--samples >= 0)
{
// apply gain
tmp = (*ptrIn++) * gain;
(*ptrOut++) = tmp;
// check only positive values
if (tmp > vuPPM)
{
vuPPM = tmp;
}
}
}
return vuPPM;
}
//------------------------------------------------------------------------
template <typename SampleType>
SampleType AGain::processVuPPM (SampleType** in, int32 numChannels, int32 sampleFrames)
{
SampleType vuPPM = 0;
for (int32 i = 0; i < numChannels; i++)
{
int32 samples = sampleFrames;
SampleType* ptrIn = (SampleType*)in[i];
SampleType tmp;
while (--samples >= 0)
{
tmp = (*ptrIn++);
// check only positive values
if (tmp > vuPPM)
{
vuPPM = tmp;
}
}
}
return vuPPM;
}
} // Vst
} // Steinberg
@@ -0,0 +1,365 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againsidechain.cpp
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST SDK 3
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "againsidechain.h"
#include "againcids.h" // for class ids
#include "againparamids.h"
#include "againprocess.h"
#include "public.sdk/source/vst/vstaudioprocessoralgo.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include <cstdio>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// AGainWithSideChain Implementation
//------------------------------------------------------------------------
//------------------------------------------------------------------------
tresult PLUGIN_API AGainWithSideChain::initialize (FUnknown* context)
{
//---always initialize the parent-------
tresult result = AGain::initialize (context);
// if everything Ok, continue
if (result != kResultOk)
{
return result;
}
// create a Mono SideChain input bus (this will be the 2cd input)
addAudioInput (STR16 ("Mono Aux In"), SpeakerArr::kMono, kAux, 0);
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainWithSideChain::process (ProcessData& data)
{
// finally the process function
// In this example there are 4 steps:
// 1) Read inputs parameters coming from host (in order to adapt our model values)
// 2) Read inputs events coming from host (we apply a gain reduction depending of the velocity
// of pressed key) 3) Process the gain of the input buffer to the output buffer 4) Write the new
// VUmeter value to the output Parameters queue
//---1) Read inputs parameter changes-----------
if (IParameterChanges* paramChanges = data.inputParameterChanges)
{
int32 numParamsChanged = paramChanges->getParameterCount ();
// for each parameter which are some changes in this audio block:
for (int32 i = 0; i < numParamsChanged; i++)
{
if (IParamValueQueue* paramQueue = paramChanges->getParameterData (i))
{
int32 offsetSamples;
double value;
int32 numPoints = paramQueue->getPointCount ();
switch (paramQueue->getParameterId ())
{
case kGainId:
// we use in this example only the last point of the queue.
// in some wanted case for specific kind of parameter it makes sense to
// retrieve all points and process the whole audio block in small blocks.
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) ==
kResultTrue)
{
fGain = (float)value;
}
break;
case kBypassId:
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) ==
kResultTrue)
{
bBypass = (value > 0.5f);
}
break;
}
}
}
}
//---2) Read input events-------------
if (IEventList* eventList = data.inputEvents)
{
int32 numEvent = eventList->getEventCount ();
for (int32 i = 0; i < numEvent; i++)
{
Event event {};
if (eventList->getEvent (i, event) == kResultOk)
{
switch (event.type)
{
//--- -------------------
case Event::kNoteOnEvent:
// use the velocity as gain modifier
fGainReduction = event.noteOn.velocity;
break;
//--- -------------------
case Event::kNoteOffEvent:
// noteOff reset the reduction
fGainReduction = 0.f;
break;
}
}
}
}
//--- ----------------------------------
//---3) Process Audio---------------------
//--- ----------------------------------
if (data.numInputs == 0 || data.numOutputs == 0)
{
// nothing to do
return kResultOk;
}
// (simplification) we suppose in this example that we have the same input channel count than
// the output
int32 numChannels = data.inputs[0].numChannels;
//---get audio buffers----------------
uint32 sampleFramesSize = getSampleFramesSizeInBytes (processSetup, data.numSamples);
void** in = getChannelBuffersPointer (processSetup, data.inputs[0]);
void** out = getChannelBuffersPointer (processSetup, data.outputs[0]);
void** auxIn = nullptr;
bool auxActive = false;
// check if our sidechain input is active (here our sidechain is the 2cd input)
if (getAudioInput (1)->isActive ())
{
auxIn = getChannelBuffersPointer (processSetup, data.inputs[1]);
auxActive = true;
}
float fVuPPM = 0.f;
//---check if silence---------------
// check if all channel are silent then process silent
if (data.inputs[0].silenceFlags == getChannelMask (data.inputs[0].numChannels))
{
// mark output silence too
data.outputs[0].silenceFlags = data.inputs[0].silenceFlags;
// the plug-in has to be sure that if it sets the flags silence that the output buffer are
// clear
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be cleared if the buffers are the same (in this case input buffer are
// already cleared by the host)
if (in[i] != out[i])
{
memset (out[i], 0, sampleFramesSize);
}
}
fVuPPM = 0.f;
}
else // we have to process (no silence)
{
// mark our outputs has not silent
data.outputs[0].silenceFlags = 0;
//---in bypass mode outputs should be like inputs-----
if (bBypass)
{
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be copied if the buffers are the same
if (in[i] != out[i])
{
memcpy (out[i], in[i], sampleFramesSize);
}
}
// in this example we do not update the VuMeter in Bypass
}
else
{
//---apply gain factor----------
float gain = (fGain - fGainReduction);
if (bHalfGain)
{
gain = gain * 0.5f;
}
// if the applied gain is nearly zero, we could say that the outputs are zeroed and we set
// the silence flags.
if (gain < 0.0000001)
{
for (int32 i = 0; i < numChannels; i++)
{
memset (out[i], 0, sampleFramesSize);
}
// this will set to 1 all channels
data.outputs[0].silenceFlags = getChannelMask (data.outputs[0].numChannels);
fVuPPM = 0.f;
}
else
{
if (auxActive)
{
if (data.symbolicSampleSize == kSample32)
fVuPPM = processAudioWithSideChain<Sample32> (
(Sample32**)in, (Sample32**)out, (Sample32**)auxIn, numChannels,
data.numSamples, gain);
else
fVuPPM = static_cast<float> (processAudioWithSideChain<Sample64> (
(Sample64**)in, (Sample64**)out, (Sample64**)auxIn, numChannels,
data.numSamples, gain));
}
else
{
if (data.symbolicSampleSize == kSample32)
fVuPPM = processAudio<Sample32> ((Sample32**)in, (Sample32**)out,
numChannels, data.numSamples, gain);
else
fVuPPM = static_cast<float> (processAudio<Sample64> (
(Sample64**)in, (Sample64**)out, numChannels, data.numSamples, gain));
}
}
}
}
//---3) Write <outputs parameter changes-----------
IParameterChanges* outParamChanges = data.outputParameterChanges;
// a new value of VuMeter will be send to the host
// (the host will send it back in sync to our controller for updating our editor)
if (outParamChanges && fVuPPMOld != fVuPPM)
{
int32 index = 0;
IParamValueQueue* paramQueue = outParamChanges->addParameterData (kVuPPMId, index);
if (paramQueue)
{
int32 index2 = 0;
paramQueue->addPoint (0, fVuPPM, index2);
}
}
fVuPPMOld = fVuPPM;
return kResultOk;
}
//------------------------------------------------------------------------
template <typename SampleType>
SampleType AGainWithSideChain::processAudioWithSideChain (SampleType** in, SampleType** out,
SampleType** aux, int32 numChannels,
int32 sampleFrames, float gain)
{
SampleType vuPPM = 0;
// we add the sidechain to the input signal
for (int32 i = 0; i < numChannels; i++)
{
int32 samples = sampleFrames;
auto* ptrIn = (SampleType*)in[i];
auto* ptrAuxIn = (SampleType*)aux[0];
auto* ptrOut = (SampleType*)out[i];
SampleType tmp;
while (--samples >= 0)
{
// apply gain
tmp = (*ptrIn++ + *ptrAuxIn++) * gain;
(*ptrOut++) = tmp;
// check only positive values
if (tmp > vuPPM)
{
vuPPM = tmp;
}
}
}
return vuPPM;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainWithSideChain::setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs,
int32 numOuts)
{
// the first input is the Main Input and the second is the SideChain Input
if (numIns == 2 && numOuts == 1)
{
// the host wants Mono => Mono (or 1 channel -> 1 channel)
if (SpeakerArr::getChannelCount (inputs[0]) == 1 &&
SpeakerArr::getChannelCount (outputs[0]) == 1)
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
// check if we are Mono => Mono, if not we need to recreate the busses
if (bus->getArrangement () != inputs[0])
{
getAudioInput (0)->setArrangement (inputs[0]);
getAudioInput (0)->setName (STR16 ("Mono In"));
getAudioOutput (0)->setArrangement (outputs[0]);
getAudioOutput (0)->setName (STR16 ("Mono Out"));
}
// check if sidechain is mono
if (SpeakerArr::getChannelCount (inputs[1]) != 1)
return kResultFalse;
return kResultOk;
}
}
// the host wants something else than Mono => Mono, in this case we are always Stereo =>
// Stereo
else
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
tresult result = kResultFalse;
// the host wants 2->2 (could be LsRs -> LsRs)
if (SpeakerArr::getChannelCount (inputs[0]) == 2 &&
SpeakerArr::getChannelCount (outputs[0]) == 2)
{
getAudioInput (0)->setArrangement (inputs[0]);
getAudioInput (0)->setName (STR16 ("Stereo In"));
getAudioOutput (0)->setArrangement (outputs[0]);
getAudioOutput (0)->setName (STR16 ("Stereo Out"));
// check if sidechain is mono
if (SpeakerArr::getChannelCount (inputs[1]) != 1)
result = kResultFalse;
else
result = kResultTrue;
}
// the host want something different than 1->1 or 2->2 : in this case we want stereo
else if (bus->getArrangement () != SpeakerArr::kStereo)
{
getAudioInput (0)->setArrangement (SpeakerArr::kStereo);
getAudioInput (0)->setName (STR16 ("Stereo In"));
getAudioOutput (0)->setArrangement (SpeakerArr::kStereo);
getAudioOutput (0)->setName (STR16 ("Stereo Out"));
result = kResultFalse;
}
return result;
}
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,53 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againsidechain.h
// Created by : Steinberg, 04/2016
// Description : AGain Example for VST SDK 3.0
// Simple gain plug-in with gain, bypass values and 1 midi input
// and a sidechain
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "again.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// AGainWithSideChain: directly derived from AGain
//------------------------------------------------------------------------
class AGainWithSideChain : public AGain
{
public:
// just overwrite some functions
static FUnknown* createInstance (void* /*context*/)
{
return (IAudioProcessor*)new AGainWithSideChain;
}
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE;
tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs,
int32 numOuts) SMTG_OVERRIDE;
protected:
//==============================================================================
template <typename SampleType>
SampleType processAudioWithSideChain (SampleType** in, SampleType** out, SampleType** aux,
int32 numChannels, int32 sampleFrames, float gain);
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,702 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againsimple.cpp
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST SDK 3.0
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#include "againsimple.h"
#include "againparamids.h"
#include "againuimessagecontroller.h"
#include "version.h" // for versioning
#include "public.sdk/source/main/pluginfactory.h"
#include "public.sdk/source/vst/vstaudioprocessoralgo.h"
#include "pluginterfaces/base/funknownimpl.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/base/ustring.h" // for UString128
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/vst/vstpresetkeys.h" // for use of IStreamAttributes
#include "base/source/fstreamer.h"
#include <cmath>
#include <cstdio>
// this allows to enable the communication example between again and its controller
#define AGAIN_TEST 1
using namespace VSTGUI;
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// GainParameter Declaration
// example of custom parameter (overwriting to and fromString)
//------------------------------------------------------------------------
class GainParameter : public Parameter
{
public:
GainParameter (int32 flags, int32 id);
void toString (ParamValue normValue, String128 string) const SMTG_OVERRIDE;
bool fromString (const TChar* string, ParamValue& normValue) const SMTG_OVERRIDE;
};
//------------------------------------------------------------------------
// GainParameter Implementation
//------------------------------------------------------------------------
GainParameter::GainParameter (int32 flags, int32 id)
{
Steinberg::UString (info.title, USTRINGSIZE (info.title)).assign (USTRING ("Gain"));
Steinberg::UString (info.units, USTRINGSIZE (info.units)).assign (USTRING ("dB"));
info.flags = flags;
info.id = id;
info.stepCount = 0;
info.defaultNormalizedValue = 0.5f;
info.unitId = kRootUnitId;
setNormalized (1.f);
}
//------------------------------------------------------------------------
void GainParameter::toString (ParamValue normValue, String128 string) const
{
char text[32];
if (normValue > 0.0001)
snprintf (text, 32, "%.2f", 20 * log10f ((float)normValue));
else
strcpy (text, "-oo");
Steinberg::UString (string, 128).fromAscii (text);
}
//------------------------------------------------------------------------
bool GainParameter::fromString (const TChar* string, ParamValue& normValue) const
{
Steinberg::UString wrapper ((TChar*)string, -1); // don't know buffer size here!
double tmp = 0.0;
if (wrapper.scanFloat (tmp))
{
// allow only values between -oo and 0dB
if (tmp > 0.0)
tmp = -tmp;
normValue = expf (logf (10.f) * (float)tmp / 20.f);
return true;
}
return false;
}
//------------------------------------------------------------------------
// AGain Implementation
//------------------------------------------------------------------------
AGainSimple::AGainSimple ()
: fGain (1.f)
, fGainReduction (0.f)
, fVuPPMOld (0.f)
, currentProcessMode (-1) // -1 means not initialized
, bHalfGain (false)
, bBypass (false)
{
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::initialize (FUnknown* context)
{
tresult result = SingleComponentEffect::initialize (context);
if (result != kResultOk)
return result;
//---create Audio In/Out busses------
// we want a stereo Input and a Stereo Output
addAudioInput (STR16 ("Stereo In"), SpeakerArr::kStereo);
addAudioOutput (STR16 ("Stereo Out"), SpeakerArr::kStereo);
//---create Event In/Out busses (1 bus with only 1 channel)------
addEventInput (STR16 ("Event In"), 1);
//---Create Parameters------------
//---Gain parameter--
auto* gainParam = new GainParameter (ParameterInfo::kCanAutomate, kGainId);
parameters.addParameter (gainParam);
//---VuMeter parameter---
int32 stepCount = 0;
ParamValue defaultVal = 0;
int32 flags = ParameterInfo::kIsReadOnly;
int32 tag = kVuPPMId;
parameters.addParameter (USTRING ("VuPPM"), nullptr, stepCount, defaultVal, flags, tag);
//---Bypass parameter---
stepCount = 1;
defaultVal = 0;
flags = ParameterInfo::kCanAutomate | ParameterInfo::kIsBypass;
tag = kBypassId;
parameters.addParameter (USTRING ("Bypass"), nullptr, stepCount, defaultVal, flags, tag);
//---Custom state init------------
UString str (defaultMessageText, 128);
str.fromAscii ("Hello World!");
return result;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::terminate ()
{
return SingleComponentEffect::terminate ();
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setActive (TBool state)
{
#if AGAIN_TEST
if (state)
fprintf (stderr, "[AGainSimple] Activated \n");
else
fprintf (stderr, "[AGainSimple] Deactivated \n");
#endif
// reset the VuMeter value
fVuPPMOld = 0.f;
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::process (ProcessData& data)
{
// finally the process function
// In this example there are 4 steps:
// 1) Read inputs parameters coming from host (in order to adapt our model values)
// 2) Read inputs events coming from host (we apply a gain reduction depending of the velocity of pressed key)
// 3) Process the gain of the input buffer to the output buffer
// 4) Write the new VUmeter value to the output Parameters queue
//---1) Read inputs parameter changes-----------
if (IParameterChanges* paramChanges = data.inputParameterChanges)
{
int32 numParamsChanged = paramChanges->getParameterCount ();
// for each parameter which are some changes in this audio block:
for (int32 i = 0; i < numParamsChanged; i++)
{
if (IParamValueQueue* paramQueue = paramChanges->getParameterData (i))
{
ParamValue value;
int32 sampleOffset;
int32 numPoints = paramQueue->getPointCount ();
switch (paramQueue->getParameterId ())
{
case kGainId:
// we use in this example only the last point of the queue.
// in some wanted case for specific kind of parameter it makes sense to retrieve all points
// and process the whole audio block in small blocks.
if (paramQueue->getPoint (numPoints - 1, sampleOffset, value) == kResultTrue)
{
fGain = (float)value;
}
break;
case kBypassId:
if (paramQueue->getPoint (numPoints - 1, sampleOffset, value) == kResultTrue)
{
bBypass = (value > 0.5f);
}
break;
}
}
}
}
//---2) Read input events-------------
if (IEventList* eventList = data.inputEvents)
{
int32 numEvent = eventList->getEventCount ();
for (int32 i = 0; i < numEvent; i++)
{
Event event {};
if (eventList->getEvent (i, event) == kResultOk)
{
switch (event.type)
{
//--- -------------------
case Event::kNoteOnEvent:
// use the velocity as gain modifier
fGainReduction = event.noteOn.velocity;
break;
//--- -------------------
case Event::kNoteOffEvent:
// noteOff reset the reduction
fGainReduction = 0.f;
break;
}
}
}
}
//--- ----------------------------------
//---3) Process Audio---------------------
//--- ----------------------------------
if (data.numInputs == 0 || data.numOutputs == 0)
{
// nothing to do
return kResultOk;
}
// (simplification) we suppose in this example that we have the same input channel count than
// the output
int32 numChannels = data.inputs[0].numChannels;
//---get audio buffers----------------
uint32 sampleFramesSize = getSampleFramesSizeInBytes (processSetup, data.numSamples);
void** in = getChannelBuffersPointer (processSetup, data.inputs[0]);
void** out = getChannelBuffersPointer (processSetup, data.outputs[0]);
float fVuPPM = 0.f;
//---check if silence---------------
// check if all channel are silent then process silent
if (data.inputs[0].silenceFlags == getChannelMask (data.inputs[0].numChannels))
{
// mark output silence too
data.outputs[0].silenceFlags = data.inputs[0].silenceFlags;
// the plug-in has to be sure that if it sets the flags silence that the output buffer are
// clear
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be cleared if the buffers are the same (in this case input buffer are
// already cleared by the host)
if (in[i] != out[i])
{
memset (out[i], 0, sampleFramesSize);
}
}
fVuPPM = 0.f;
}
else
{
// mark our outputs has not silent
data.outputs[0].silenceFlags = 0;
//---in bypass mode outputs should be like inputs-----
if (bBypass)
{
for (int32 i = 0; i < numChannels; i++)
{
// do not need to be copied if the buffers are the same
if (in[i] != out[i])
{
memcpy (out[i], in[i], sampleFramesSize);
}
}
// in this example we do not update the VuMeter in Bypass
}
else
{
//---apply gain factor----------
float gain = (fGain - fGainReduction);
if (bHalfGain)
{
gain = gain * 0.5f;
}
// if the applied gain is nearly zero, we could say that the outputs are zeroed and we set
// the silence flags.
if (gain < 0.0000001)
{
for (int32 i = 0; i < numChannels; i++)
{
memset (out[i], 0, sampleFramesSize);
}
// this will set to 1 all channels
data.outputs[0].silenceFlags = getChannelMask (data.outputs[0].numChannels);
fVuPPM = 0.f;
}
else
{
if (data.symbolicSampleSize == kSample32)
fVuPPM = processAudio<Sample32> ((Sample32**)in, (Sample32**)out, numChannels,
data.numSamples, gain);
else
fVuPPM = static_cast<float> (processAudio<Sample64> (
(Sample64**)in, (Sample64**)out, numChannels, data.numSamples, gain));
}
}
}
//---3) Write outputs parameter changes-----------
IParameterChanges* outParamChanges = data.outputParameterChanges;
// a new value of VuMeter will be send to the host
// (the host will send it back in sync to our controller for updating our editor)
if (outParamChanges && fVuPPMOld != fVuPPM)
{
int32 index = 0;
IParamValueQueue* paramQueue = outParamChanges->addParameterData (kVuPPMId, index);
if (paramQueue)
{
int32 index2 = 0;
paramQueue->addPoint (0, fVuPPM, index2);
}
}
fVuPPMOld = fVuPPM;
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setState (IBStream* state)
{
// we receive the current (processor part)
// called when we load a preset, the model has to be reloaded
IBStreamer streamer (state, kLittleEndian);
float savedGain = 0.f;
if (streamer.readFloat (savedGain) == false)
return kResultFalse;
float savedGainReduction = 0.f;
if (streamer.readFloat (savedGainReduction) == false)
return kResultFalse;
int32 savedBypass = 0;
if (streamer.readInt32 (savedBypass) == false)
return kResultFalse;
fGain = savedGain;
fGainReduction = savedGainReduction;
bBypass = savedBypass > 0;
setParamNormalized (kGainId, savedGain);
setParamNormalized (kBypassId, bBypass);
// Example of using the IStreamAttributes interface
if (auto stream = U::cast<IStreamAttributes> (state))
{
if (IAttributeList* list = stream->getAttributes ())
{
// get the current type (project/Default..) of this state
String128 string = {0};
if (list->getString (PresetAttributes::kStateType, string, 128 * sizeof (TChar)) ==
kResultTrue)
{
UString128 tmp (string);
char ascii[128];
tmp.toAscii (ascii, 128);
if (strncmp (ascii, StateType::kProject, strlen (StateType::kProject)) == 0)
{
// we are in project loading context...
}
}
// get the full file path of this state
TChar fullPath[1024];
memset (fullPath, 0, 1024 * sizeof (TChar));
if (list->getString (PresetAttributes::kFilePathStringType, fullPath,
1024 * sizeof (TChar)) == kResultTrue)
{
// here we have the full path ...
}
}
}
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::getState (IBStream* state)
{
// here we need to save the model
IBStreamer streamer (state, kLittleEndian);
streamer.writeFloat (fGain);
streamer.writeFloat (fGainReduction);
streamer.writeInt32 (bBypass ? 1 : 0);
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setupProcessing (ProcessSetup& newSetup)
{
// called before the process call, always in a disable state (not active)
// here we keep a trace of the processing mode (offline,...) for example.
currentProcessMode = newSetup.processMode;
return SingleComponentEffect::setupProcessing (newSetup);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs, int32 numOuts)
{
if (numIns == 1 && numOuts == 1)
{
// the host wants Mono => Mono (or 1 channel -> 1 channel)
if (SpeakerArr::getChannelCount (inputs[0]) == 1 &&
SpeakerArr::getChannelCount (outputs[0]) == 1)
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
// check if we are Mono => Mono, if not we need to recreate the busses
if (bus->getArrangement () != inputs[0])
{
bus->setArrangement (inputs[0]);
bus->setName (STR16 ("Mono In"));
if (auto* busOut = FCast<AudioBus> (audioOutputs.at (0)))
{
busOut->setArrangement (outputs[0]);
busOut->setName (STR16 ("Mono Out"));
}
}
return kResultOk;
}
}
// the host wants something else than Mono => Mono, in this case we are always Stereo =>
// Stereo
else
{
auto* bus = FCast<AudioBus> (audioInputs.at (0));
if (bus)
{
tresult result = kResultFalse;
// the host wants 2->2 (could be LsRs -> LsRs)
if (SpeakerArr::getChannelCount (inputs[0]) == 2 &&
SpeakerArr::getChannelCount (outputs[0]) == 2)
{
bus->setArrangement (inputs[0]);
bus->setName (STR16 ("Stereo In"));
if (auto* busOut = FCast<AudioBus> (audioOutputs.at (0)))
{
busOut->setArrangement (outputs[0]);
busOut->setName (STR16 ("Stereo Out"));
}
result = kResultTrue;
}
// the host want something different than 1->1 or 2->2 : in this case we want stereo
else if (bus->getArrangement () != SpeakerArr::kStereo)
{
bus->setArrangement (SpeakerArr::kStereo);
bus->setName (STR16 ("Stereo In"));
if (auto* busOut = FCast<AudioBus> (audioOutputs.at (0)))
{
busOut->setArrangement (SpeakerArr::kStereo);
busOut->setName (STR16 ("Stereo Out"));
}
result = kResultFalse;
}
return result;
}
}
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::canProcessSampleSize (int32 symbolicSampleSize)
{
if (symbolicSampleSize == kSample32)
return kResultTrue;
// we support double processing
if (symbolicSampleSize == kSample64)
return kResultTrue;
return kResultFalse;
}
//------------------------------------------------------------------------
IPlugView* PLUGIN_API AGainSimple::createView (const char* name)
{
// someone wants my editor
if (name && FIDStringsEqual (name, ViewType::kEditor))
{
auto* view = new VST3Editor (this, "view", "again.uidesc");
return view;
}
return nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::getMidiControllerAssignment (int32 busIndex, int16 /*midiChannel*/,
CtrlNumber midiControllerNumber,
ParamID& tag)
{
// we support for the Gain parameter all MIDI Channel but only first bus (there is only one!)
if (busIndex == 0 && midiControllerNumber == kCtrlVolume)
{
tag = kGainId;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
IController* AGainSimple::createSubController (UTF8StringPtr name,
const IUIDescription* /*description*/,
VST3Editor* /*editor*/)
{
if (UTF8StringView (name) == "MessageController")
{
auto* controller = new UIMessageController (this);
addUIMessageController (controller);
return controller;
}
return nullptr;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setEditorState (IBStream* state)
{
tresult result = kResultFalse;
int8 byteOrder;
if ((result = state->read (&byteOrder, sizeof (int8))) != kResultTrue)
return result;
if ((result = state->read (defaultMessageText, 128 * sizeof (TChar))) != kResultTrue)
return result;
// if the byteorder doesn't match, byte swap the text array ...
if (byteOrder != BYTEORDER)
{
for (int32 i = 0; i < 128; i++)
SWAP_16 (defaultMessageText[i])
}
for (auto& uiMessageController : uiMessageControllers)
uiMessageController->setMessageText (defaultMessageText);
return result;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::getEditorState (IBStream* state)
{
// here we can save UI settings for example
IBStreamer streamer (state, kLittleEndian);
// as we save a Unicode string, we must know the byteorder when setState is called
int8 byteOrder = BYTEORDER;
if (streamer.writeInt8 (byteOrder) == false)
return kResultFalse;
if (streamer.writeRaw (defaultMessageText, 128 * sizeof (TChar)) == false)
return kResultFalse;
return kResultTrue;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::setParamNormalized (ParamID tag, ParamValue value)
{
// called from host to update our parameters state
tresult result = SingleComponentEffect::setParamNormalized (tag, value);
return result;
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::getParamStringByValue (ParamID tag, ParamValue valueNormalized,
String128 string)
{
return SingleComponentEffect::getParamStringByValue (tag, valueNormalized, string);
}
//------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::getParamValueByString (ParamID tag, TChar* string,
ParamValue& valueNormalized)
{
return SingleComponentEffect::getParamValueByString (tag, string, valueNormalized);
}
//------------------------------------------------------------------------
void AGainSimple::addUIMessageController (UIMessageController* controller)
{
uiMessageControllers.push_back (controller);
}
//------------------------------------------------------------------------
void AGainSimple::removeUIMessageController (UIMessageController* controller)
{
UIMessageControllerList::const_iterator it =
std::find (uiMessageControllers.begin (), uiMessageControllers.end (), controller);
if (it != uiMessageControllers.end ())
uiMessageControllers.erase (it);
}
//------------------------------------------------------------------------
void AGainSimple::setDefaultMessageText (String128 text)
{
UString str (defaultMessageText, 128);
str.assign (text, -1);
}
//------------------------------------------------------------------------
TChar* AGainSimple::getDefaultMessageText ()
{
return defaultMessageText;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API AGainSimple::queryInterface (const TUID iid, void** obj)
{
DEF_INTERFACE (IMidiMapping)
return SingleComponentEffect::queryInterface (iid, obj);
}
//------------------------------------------------------------------------
enum
{
// UI size
kEditorWidth = 350,
kEditorHeight = 120
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
//------------------------------------------------------------------------
BEGIN_FACTORY_DEF (stringCompanyName, stringCompanyWeb, stringCompanyEmail)
//---First plug-in included in this factory-------
// its kVstAudioEffectClass component
DEF_CLASS2 (INLINE_UID (0xB9F9ADE1, 0xCD9C4B6D, 0xA57E61E3, 0x123535FD),
PClassInfo::kManyInstances, // cardinality
kVstAudioEffectClass, // the component category (do not change this)
"AGainSimple VST3", // here the plug-in name (to be changed)
0, // single component effects cannot be distributed so this is zero
"Fx", // Subcategory for this plug-in (to be changed)
FULL_VERSION_STR, // Plug-in version (to be changed)
kVstVersionString, // the VST 3 SDK version (do not change this, always use this define)
Steinberg::Vst::AGainSimple::createInstance)// function pointer called when this component should be instantiated
END_FACTORY
@@ -0,0 +1,148 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againsimple.h
// Created by : Steinberg, 04/2005
// Description : AGain Example for VST SDK 3.0
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
// must always come first
#include "public.sdk/source/vst/vstsinglecomponenteffect.h"
//------------------------------------------------------------------------
#include "public.sdk/source/vst/vstguieditor.h"
#include "pluginterfaces/vst/ivstcontextmenu.h"
#include "pluginterfaces/vst/ivstplugview.h"
#include "vstgui/plugin-bindings/vst3editor.h"
namespace Steinberg {
namespace Vst {
template <typename T>
class AGainUIMessageController;
//------------------------------------------------------------------------
// AGain as combined processor and controller
//------------------------------------------------------------------------
class AGainSimple : public SingleComponentEffect,
public VSTGUI::VST3EditorDelegate,
public IMidiMapping
{
public:
//------------------------------------------------------------------------
using UIMessageController = AGainUIMessageController<AGainSimple>;
using UTF8StringPtr = VSTGUI::UTF8StringPtr;
using IUIDescription = VSTGUI::IUIDescription;
using IController = VSTGUI::IController;
using VST3Editor = VSTGUI::VST3Editor;
AGainSimple ();
static FUnknown* createInstance (void* /*context*/) { return (IAudioProcessor*)new AGainSimple; }
//---from IComponent-----------------------
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
tresult PLUGIN_API terminate () SMTG_OVERRIDE;
tresult PLUGIN_API setActive (TBool state) SMTG_OVERRIDE;
tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE;
tresult PLUGIN_API canProcessSampleSize (int32 symbolicSampleSize) SMTG_OVERRIDE;
tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API setupProcessing (ProcessSetup& newSetup) SMTG_OVERRIDE;
tresult PLUGIN_API setBusArrangements (SpeakerArrangement* inputs, int32 numIns,
SpeakerArrangement* outputs,
int32 numOuts) SMTG_OVERRIDE;
//---from IEditController-------
IPlugView* PLUGIN_API createView (const char* name) SMTG_OVERRIDE;
tresult PLUGIN_API setEditorState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getEditorState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API setParamNormalized (ParamID tag, ParamValue value) SMTG_OVERRIDE;
tresult PLUGIN_API getParamStringByValue (ParamID tag, ParamValue valueNormalized,
String128 string) SMTG_OVERRIDE;
tresult PLUGIN_API getParamValueByString (ParamID tag, TChar* string,
ParamValue& valueNormalized) SMTG_OVERRIDE;
//---from IMidiMapping-----------------
tresult PLUGIN_API getMidiControllerAssignment (int32 busIndex, int16 channel,
CtrlNumber midiControllerNumber,
ParamID& tag) SMTG_OVERRIDE;
//---from VST3EditorDelegate-----------
IController* createSubController (UTF8StringPtr name, const IUIDescription* description,
VST3Editor* editor) SMTG_OVERRIDE;
//---Interface---------
OBJ_METHODS (AGainSimple, SingleComponentEffect)
tresult PLUGIN_API queryInterface (const TUID iid, void** obj) SMTG_OVERRIDE;
REFCOUNT_METHODS (SingleComponentEffect)
//---Internal functions-------
void addUIMessageController (UIMessageController* controller);
void removeUIMessageController (UIMessageController* controller);
void setDefaultMessageText (String128 text);
TChar* getDefaultMessageText ();
//------------------------------------------------------------------------
template <typename SampleType>
SampleType processAudio (SampleType** in, SampleType** out, int32 numChannels,
int32 sampleFrames, float gain)
{
SampleType vuPPM = 0;
// in real plug-in it would be better to do dezippering to avoid jump (click) in gain value
for (int32 i = 0; i < numChannels; i++)
{
int32 samples = sampleFrames;
auto* ptrIn = (SampleType*)in[i];
auto* ptrOut = (SampleType*)out[i];
SampleType tmp;
while (--samples >= 0)
{
// apply gain
tmp = (*ptrIn++) * gain;
(*ptrOut++) = tmp;
// check only positive values
if (tmp > vuPPM)
{
vuPPM = tmp;
}
}
}
return vuPPM;
}
//------------------------------------------------------------------------
private:
// our model values
float fGain;
float fGainReduction;
float fVuPPMOld;
int32 currentProcessMode;
bool bHalfGain;
bool bBypass;
using UIMessageControllerList = std::vector<UIMessageController*>;
UIMessageControllerList uiMessageControllers;
String128 defaultMessageText;
};
//------------------------------------------------------------------------
} // namespace Vst
} // namespace Steinberg
@@ -0,0 +1,159 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/againuimessagecontroller.h
// Created by : Steinberg, 04/2005
// Description : AGain UI Message Controller
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, 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.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// 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 OWNER 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.
//-----------------------------------------------------------------------------
#pragma once
#include "vstgui/lib/iviewlistener.h"
#include "vstgui/uidescription/icontroller.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// AGainUIMessageController
//------------------------------------------------------------------------
template <typename ControllerType>
class AGainUIMessageController : public VSTGUI::IController, public VSTGUI::ViewListenerAdapter
{
public:
enum Tags
{
kSendMessageTag = 1000
};
AGainUIMessageController (ControllerType* againController) : againController (againController), textEdit (nullptr)
{
}
~AGainUIMessageController () override
{
if (textEdit)
viewWillDelete (textEdit);
againController->removeUIMessageController (this);
}
void setMessageText (String128 msgText)
{
if (!textEdit)
return;
textEdit->setText (StringConvert::convert (msgText));
}
private:
using CControl = VSTGUI::CControl;
using CView = VSTGUI::CView;
using CTextEdit = VSTGUI::CTextEdit;
using UTF8String = VSTGUI::UTF8String;
using UIAttributes = VSTGUI::UIAttributes;
using IUIDescription = VSTGUI::IUIDescription;
//--- from IControlListener ----------------------
void valueChanged (CControl* /*pControl*/) override {}
void controlBeginEdit (CControl* /*pControl*/) override {}
void controlEndEdit (CControl* pControl) override
{
if (pControl->getTag () == kSendMessageTag)
{
if (pControl->getValueNormalized () > 0.5f)
{
againController->sendTextMessage (textEdit->getText ().data ());
pControl->setValue (0.f);
pControl->invalid ();
//---send a binary message
if (IPtr<IMessage> message = owned (againController->allocateMessage ()))
{
message->setMessageID ("BinaryMessage");
uint32 size = 100;
char8 data[100];
memset (data, 0, size * sizeof (char));
// fill my data with dummy stuff
for (uint32 i = 0; i < size; i++)
data[i] = i;
message->getAttributes ()->setBinary ("MyData", data, size);
againController->sendMessage (message);
}
}
}
}
//--- from IControlListener ----------------------
//--- is called when a view is created -----
CView* verifyView (CView* view, const UIAttributes& /*attributes*/,
const IUIDescription* /*description*/) override
{
if (CTextEdit* te = dynamic_cast<CTextEdit*> (view))
{
// this allows us to keep a pointer of the text edit view
textEdit = te;
// add this as listener in order to get viewWillDelete and viewLostFocus calls
textEdit->registerViewListener (this);
// initialize it content
textEdit->setText (
StringConvert::convert (againController->getDefaultMessageText ()));
}
return view;
}
//--- from IViewListenerAdapter ----------------------
//--- is called when a view will be deleted: the editor is closed -----
void viewWillDelete (CView* view) override
{
if (dynamic_cast<CTextEdit*> (view) == textEdit)
{
textEdit->unregisterViewListener (this);
textEdit = nullptr;
}
}
//--- is called when the view is loosing the focus -----------------
void viewLostFocus (CView* view) override
{
if (dynamic_cast<CTextEdit*> (view) == textEdit)
{
// save the last content of the text edit view
const auto& text = textEdit->getText ();
auto utf16Text = StringConvert::convert (text.getString ());
againController->setDefaultMessageText (utf16Text.data ());
}
}
ControllerType* againController;
CTextEdit* textEdit;
};
//------------------------------------------------------------------------
} // Vst
} // Steinberg
@@ -0,0 +1,35 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/again/source/version.h
// Created by : Steinberg, 01/2008
// Description : Example of handle the versioning and copyright info of again plug-in
// used for the resources (RC file for example)
//
//-----------------------------------------------------------------------------
// This file is part of a Steinberg SDK. It is subject to the license terms
// in the LICENSE file found in the top-level directory of this distribution
// and at www.steinberg.net/sdklicenses.
// No part of the SDK, including this file, may be copied, modified, propagated,
// or distributed except according to the terms contained in the LICENSE file.
//-----------------------------------------------------------------------------
#pragma once
#include "pluginterfaces/base/fplatform.h"
// Plain project version file generated by cmake
#include "projectversion.h"
#define stringOriginalFilename "again.vst3"
#if SMTG_PLATFORM_64
#define stringFileDescription "AGain VST3-SDK (64Bit)"
#else
#define stringFileDescription "AGain VST3-SDK"
#endif
#define stringCompanyWeb "http://www.steinberg.net"
#define stringCompanyEmail "mailto:info@steinberg.de"
#define stringCompanyName "Steinberg Media Technologies"
#define stringLegalCopyright "© 2025 Steinberg Media Technologies"
#define stringLegalTrademarks "VST is a trademark of Steinberg Media Technologies GmbH"