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,20 @@
cmake_minimum_required(VERSION 3.25.0)
project(smtg-vst3-programchange
VERSION ${vstsdk_VERSION}.0
DESCRIPTION "Steinberg VST 3 Programchange example"
)
smtg_add_vst3plugin(program-change
source/plug.cpp
source/plug.h
source/plugcids.h
source/plugcontroller.cpp
source/plugcontroller.h
source/plugentry.cpp
source/plugparamids.h
source/version.h
)
smtg_target_setup_as_vst3_example(program-change)
@@ -0,0 +1,18 @@
# Test Program Change
## Introduction
**Test Program Change** is simple FX plug-in showing how to support multiple ProgramChange parameters: 16 slots with one associated program change parameter and a program list for each slot.
> See also: [Online Documentation](https://steinbergmedia.github.io/vst3_dev_portal/pages/What+is+the+VST+3+SDK/Plug-in+Examples.html#test-multiple-program-changes).
## Getting Started
This plug-in is part of the VST 3 SDK package. It is created with the VST 3 SDK root project.
> See the top-level README of the VST 3 SDK: https://github.com/steinbergmedia/vst3sdk.git
## Getting Help
* Read through the SDK documentation on the **[VST 3 Developer Portal](https://steinbergmedia.github.io/vst3_dev_portal/pages/index.html)**
* Ask some real people in the official **[VST 3 Developer Forum](https://forums.steinberg.net/c/developer/103)**
@@ -0,0 +1,45 @@
#include <windows.h>
#include "../source/version.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// Version
/////////////////////////////////////////////////////////////////////////////
VS_VERSION_INFO VERSIONINFO
FILEVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT
PRODUCTVERSION MAJOR_VERSION_INT,SUB_VERSION_INT,RELEASE_NUMBER_INT,BUILD_NUMBER_INT
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040004e4"
BEGIN
VALUE "FileVersion", FULL_VERSION_STR
VALUE "ProductVersion", FULL_VERSION_STR
VALUE "OriginalFilename", stringOriginalFilename
VALUE "FileDescription", stringFileDescription
VALUE "InternalName", stringFileDescription
VALUE "ProductName", stringFileDescription
VALUE "CompanyName", stringCompanyName
VALUE "LegalCopyright", stringLegalCopyright
VALUE "LegalTrademarks", stringLegalTrademarks
//VALUE "PrivateBuild", " \0"
//VALUE "SpecialBuild", " \0"
//VALUE "Comments", " \0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x400, 1252
END
END
@@ -0,0 +1,241 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plug.cpp
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 "plug.h"
#include "plugparamids.h"
#include "plugcids.h" // for class ids
#include "public.sdk/source/vst/vstaudioprocessoralgo.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/vst/ivstparameterchanges.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/base/futils.h"
#include "base/source/fstreamer.h"
#include <cstdio>
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// Plug Implementation
//------------------------------------------------------------------------
Plug::Plug () : bBypass (false), currentProgram (0), currentGainValue (0.f)
{
// register its editor class (the same than used in plugentry.cpp)
setControllerClass (PlugControllerUID);
}
//------------------------------------------------------------------------
tresult PLUGIN_API Plug::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);
addEventInput (STR16 ("Event In"), 1);
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API Plug::process (ProcessData& data)
{
//---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 kBypassId:
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) ==
kResultTrue)
{
bBypass = (value > 0.5f);
}
break;
case kProgramId:
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) ==
kResultTrue)
{
// here we get the last set program
currentProgram = FromNormalized<ParamValue> (value, kNumProgs - 1);
}
break;
case kGainId:
if (paramQueue->getPoint (numPoints - 1, offsetSamples, value) ==
kResultTrue)
{
currentGainValue = static_cast<float> (value);
}
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----------------
float** in = data.inputs[0].channelBuffers32;
float** out = data.outputs[0].channelBuffers32;
// 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;
int32 sampleFrames = data.numSamples;
// 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, sampleFrames * sizeof (float));
}
}
// nothing to do at this point
return kResultOk;
}
// mark our outputs has not silent
data.outputs[0].silenceFlags = 0;
//---in bypass mode outputs should be like inputs-----
if (bBypass)
{
int32 sampleFrames = data.numSamples;
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], sampleFrames * sizeof (float));
}
}
}
else
{
// here we use the current program as gain value...
float gain = currentGainValue;
// 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 sampleFrames = data.numSamples;
float* ptrIn = in[i];
float* ptrOut = out[i];
float tmp;
while (--sampleFrames >= 0)
{
// apply gain
tmp = (*ptrIn++) * gain;
(*ptrOut++) = tmp;
}
}
}
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API Plug::setState (IBStream* state)
{
// called when we load a preset, the model has to be reloaded
if (!state)
return kResultFalse;
IBStreamer streamer (state, kLittleEndian);
// read the bypass
int32 savedBypass = 0;
if (streamer.readInt32 (savedBypass) == false)
return kResultFalse;
// read the program
int32 savedProgram = 0;
if (streamer.readInt32 (savedProgram) == false)
return kResultFalse;
// read the Gain param
float savedGain = 0.f;
if (streamer.readFloat (savedGain) == false)
return kResultFalse;
bBypass = savedBypass > 0;
currentProgram = savedProgram;
currentGainValue = savedGain;
return kResultOk;
}
//------------------------------------------------------------------------
tresult PLUGIN_API Plug::getState (IBStream* state)
{
// here we need to save the model
if (!state)
return kResultFalse;
IBStreamer streamer (state, kLittleEndian);
streamer.writeInt32 (bBypass ? 1 : 0);
streamer.writeInt32 (currentProgram);
streamer.writeFloat (currentGainValue);
return kResultOk;
}
}} // namespaces
@@ -0,0 +1,58 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plug.h
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 {
//------------------------------------------------------------------------
// Plug: directly derived from the helper class AudioEffect
//------------------------------------------------------------------------
class Plug : public AudioEffect
{
public:
Plug ();
//------------------------------------------------------------------------
// 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 Plug; }
//------------------------------------------------------------------------
// AudioEffect overrides:
//------------------------------------------------------------------------
/** Called at first after constructor */
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
/** Here we go...the process call */
tresult PLUGIN_API process (ProcessData& data) SMTG_OVERRIDE;
/** For persistence */
tresult PLUGIN_API setState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getState (IBStream* state) SMTG_OVERRIDE;
//------------------------------------------------------------------------
protected:
bool bBypass;
int32 currentProgram;
float currentGainValue;
};
}
} // namespaces
@@ -0,0 +1,24 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plugcids.h
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 {
static const FUID PlugProcessorUID (0xC09CDA28, 0x346B4318, 0x9906B54E, 0x8A7023E2);
static const FUID PlugControllerUID (0x86152E37, 0x6F094BDF, 0x9929F219, 0xBA3BBED7);
}
} // namespaces
@@ -0,0 +1,137 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plugcontroller.cpp
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 "plugcontroller.h"
#include "plugparamids.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "base/source/fstreamer.h"
#include "pluginterfaces/base/ibstream.h"
#include "pluginterfaces/base/futils.h"
#include "pluginterfaces/vst/ivstcomponent.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// PlugController Implementation
//------------------------------------------------------------------------
tresult PLUGIN_API PlugController::initialize (FUnknown* context)
{
tresult result = EditControllerEx1::initialize (context);
if (result != kResultOk)
{
return result;
}
//---Create Parameters------------
//---Bypass parameter---
int32 stepCount = 1;
ParamValue defaultVal = 0;
int32 flags = ParameterInfo::kCanAutomate | ParameterInfo::kIsBypass;
int32 tag = kBypassId;
parameters.addParameter (STR ("Bypass"), nullptr, stepCount, defaultVal, flags, tag);
// create top root unit with kProgramId as id for the programList
addUnit (new Unit (STR ("Root"), kRootUnitId, kNoParentUnitId, kProgramId));
// create the program list: here kNumProgs entries
auto* prgList = new ProgramList (STR ("Bank"), kProgramId, kRootUnitId);
addProgramList (prgList);
for (int32 i = 0; i < kNumProgs; i++)
{
std::u16string title = STR ("Prog ");
title += Vst::toString (i + 1);
prgList->addProgram (title.data ());
}
//---Program Change parameter---
Parameter* prgParam = prgList->getParameter ();
// by default this program change parameter if automatable we can overwrite this:
prgParam->getInfo ().flags &= ~ParameterInfo::kCanAutomate;
parameters.addParameter (prgParam);
//---Gain parameter---
parameters.addParameter (STR16 ("Gain"), nullptr, 0, 1.f, ParameterInfo::kCanAutomate, kGainId);
return result;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API PlugController::setParamNormalized (ParamID tag, ParamValue value)
{
tresult res = EditControllerEx1::setParamNormalized (tag, value);
if (res == kResultOk && tag == kProgramId) // program change
{
// here we use the 1-program as gain...just an example
EditControllerEx1::setParamNormalized (kGainId, value);
if (componentHandler)
componentHandler->restartComponent (kParamValuesChanged);
}
return res;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugController::getUnitByBus (MediaType type, BusDirection dir, int32 busIndex,
int32 channel, UnitID& unitId)
{
if (type == kEvent && dir == kInput && busIndex == 0 && channel == 0)
{
unitId = kRootUnitId;
return kResultTrue;
}
return kResultFalse;
}
//------------------------------------------------------------------------
tresult PLUGIN_API PlugController::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);
// read the bypass
int32 bypassState = 0;
if (streamer.readInt32 (bypassState) == false)
return kResultFalse;
setParamNormalized (kBypassId, bypassState ? 1 : 0);
// read the program
int32 programState = 0;
if (streamer.readInt32 (programState) == false)
return kResultFalse;
EditControllerEx1::setParamNormalized (kProgramId,
ToNormalized<ParamValue> (programState, kNumProgs - 1));
// read the Gain param
float savedGain = 0.f;
if (streamer.readFloat (savedGain) == false)
return kResultFalse;
setParamNormalized (kGainId, savedGain);
return kResultOk;
}
}} // namespaces
@@ -0,0 +1,56 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plugcontroller.h
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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/vsteditcontroller.h"
namespace Steinberg {
namespace Vst {
//------------------------------------------------------------------------
// PlugController
//------------------------------------------------------------------------
class PlugController: public EditControllerEx1
{
public:
//------------------------------------------------------------------------
// 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 PlugController;
}
//---from IPluginBase--------
tresult PLUGIN_API initialize (FUnknown* context) SMTG_OVERRIDE;
//---from EditControllerEx1-----
tresult PLUGIN_API setParamNormalized (ParamID tag, ParamValue value) SMTG_OVERRIDE;
tresult PLUGIN_API setComponentState (IBStream* state) SMTG_OVERRIDE;
tresult PLUGIN_API getUnitByBus (MediaType /*type*/, BusDirection /*dir*/, int32 /*busIndex*/,
int32 /*channel*/, UnitID& /*unitId*/ /*out*/) SMTG_OVERRIDE;
DELEGATE_REFCOUNT (EditControllerEx1)
//------------------------------------------------------------------------
private:
};
}} // namespaces
@@ -0,0 +1,61 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plugentry.cpp
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 "plug.h"
#include "plugcontroller.h"
#include "plugcids.h" // for class ids
#include "version.h" // for versioning
#include "public.sdk/source/main/pluginfactory.h"
#define stringPluginName "Test Program Change"
using namespace Steinberg;
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(PlugProcessorUID),
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
"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::Plug::createInstance) // function pointer called when this component should be instantiated
// its kVstComponentControllerClass component
DEF_CLASS2 (INLINE_UID_FROM_FUID (PlugControllerUID),
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::PlugController::createInstance)// function pointer called when this component should be instantiated
END_FACTORY
@@ -0,0 +1,28 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/plugparamids.h
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 */
kBypassId = 0, ///< Bypass value (we will handle the bypass process) (is automatable)
kProgramId,
kGainId,
kNumProgs = 100
};
@@ -0,0 +1,34 @@
//------------------------------------------------------------------------
// Project : VST SDK
//
// Category : Examples
// Filename : public.sdk/samples/vst/programchange/source/version.h
// Created by : Steinberg, 02/2016
// Description : Plug-in Example for VST SDK 3.x using ProgramChange parameter
//
//-----------------------------------------------------------------------------
// 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 "TestProgramChange.vst3"
#if SMTG_PLATFORM_64
#define stringFileDescription "TestProgramChange VST3-SDK (64Bit)"
#else
#define stringFileDescription "TestProgramChange 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"