ASPiK SDK
Loading...
Searching...
No Matches
Parameter Smoothing

ASPiK implements two types of parameter smoothing:

  • native: you may setup the parameters to smooth themselves on each frame, buffer, or sub-block bounday (or any combination)
  • VST3 Sample Accurate Automation (VST3SAA): for VST3 plugins only, Steinberg defines a method of smoothing parameters arriving from automation playback; if you use the native smoothing properly, you should not need VST3SAA however there may be some specialized plugin for VST3 only, in which you need it

Prior to ASPiK 1.6.8, the native and VST3SAA smoothing operations took place in the same function. For SDK 1.6.8 and beyond, these two smoothing operations have been separated out of a single function, and placed into two functions; you may enable/disable them independently (they have always been independent). Separating the two smoothing methods this way actually saves CPU cycles and parameters may only be smoothed once, with one version or the other. My advice is to implement native smoothing properly, then forget about VSTY3SAA smoothing and just leave it disabled (from the ASPiKreator or in plugindescription.h).

GUI Parameter Update/Cooking with Parameter Smoothing OFF
When parameter smoothing is turned off, the parameter's value will remain fixed and constant during the entire buffer processing cycle. This means that we are wasting CPU cycles by recalculating the cooked volume value on each audio frame. We really only need to calculate it once per buffer cycle and we can make use of the function PluginCore::postUpdatePluginParameter that will be called during the buffer pre-processing phase. Since this function is external to the processAudioFrame function, we will need to declare the cooked volume value as a member variable of the PluginCore object in the plugincore.h file:

// --- members
private:
// --- Continuous Plugin Variables
double volume_dB = 0.0;
// --- cooked volume value PluginCore::postUpdatePluginParameter()
double volumeCooked = 0.0;
// --- Discrete Plugin Variables
int enableMute = 0;
etc . . .
//

Then, in PluginCore::postUpdatePluginParameter we will decode the parameter controlID, and cook the incoming raw data. Because of our clever controlID enumeration naming convention, we don't need to look up the controlID, we simply use the enumeration which is the same as the variable name (controlID::volume_dB):

bool PluginCore::postUpdatePluginParameter(int32_t controlID, double controlValue, ParameterUpdateInfo& paramInfo)
{
// --- decode the controlID
switch(controlID)
{
case controlID::volume_dB:
{
// --- cooked variables
volumeCooked = pow(10.0, volume_dB / 20.0);
return true; // handled
}
default:
return false; // not handled
}
return false;
}
//
virtual bool postUpdatePluginParameter(int32_t controlID, double controlValue, ParameterUpdateInfo &paramInfo)
perform any operations after the plugin parameter has been updated; this is one paradigm for transfer...
Definition: plugincore.cpp:481
Information about a paraemeter being updated. Used when bound variables are updated....
Definition: pluginstructures.h:835

Now, we can remove the cooking calculation from the frame processing function (we'll just comment it out):

// --- convert dB to raw NOW DONE IN postUpdatePluginParameter
// double volumeCooked = pow(10.0, volume_dB / 20.0); <-- commented out!
double volume_L = volumeCooked;
double volume_R = volumeCooked;
//

ParameterUpdateInfo Options
There may be situations where you do not want to perform the post-update cooking function based on whether the parameter was changed during 1) the normal buffer cycle process, 2) a parameter smoothing operation, 3) a VST sample accurate update or 4) the user loading a preset. You might want to experiment with CPU usage rates or have other plugin-specific issues to deal with. For example, if the cooking process is CPU intensive because of some plugin option that the user has chosen, you might opt to disable the cooking in this situation. The third argument in the postUpdatePluginParameter function is a ParameterUpdateInfo structure that delivers information about how and why the function is being called. You can examine the member variables and use them to alter your logic for the cooking process.

{
<SNIP SNIP SNIP>
<SNIP SNIP SNIP>
};
bool bufferProcUpdate
update at top of buffer process
Definition: pluginstructures.h:872
bool boundVariableUpdate
bound variable is being udpated
Definition: pluginstructures.h:871
bool isVSTSampleAccurateUpdate
param updated with VST sample accurate automation
Definition: pluginstructures.h:869
bool isSmoothing
param is being (bulk) smoothed
Definition: pluginstructures.h:868
bool loadingPreset
a preset is being loaded
Definition: pluginstructures.h:870

These boolean flags are fairly straightforward to figure out by their names, but here the logic used.

  • isSmoothing: ordinary parameter smoothing
  • isVST3SampleAccurateUpdate: a VST3 sample accurate automation update
  • loadingPreset: the user loaded a preset in the host DAW
  • boundVariableUpdate: the variable binding operation has occurred
  • bufferProcUpdate: updating at the top of the buffer process cycle

GUI Parameter Update/Cooking with Parameter Smoothing ON
When parameter smoothing is turned on, you have another easy option for cooking the GUI data. In the previous method using PluginCore::postUpdatePluginParameter you decoded each controlID and cooked each parameter separately. A simple alternative is to provide a function that cooks all incoming parameters together (at the same time). In the FX book examples, this function is named updateParameters but of course you may name it how you wish. This function's content will be highly dependent on the algorithm and GUI controls so I won't detail it here, but remember that it converts all GUI parameter values into meaninful data for your algorithm.

For example, when parameter smoothing or VST3SAA is enabled, the value of the volume_dB variable is going to be smoothed and updated on each sample interval at the top of the processAudioFrame function using either method:

  1. PluginCore::postUpdatePluginParameter: here you cook each variable separately based on controlID
  2. updateParameters: in this case you remove the code from postUpdatePluginParameter and place it into a single function that cooks all variables, mine is named updateParameters

Note that the ASPiK parameter smoothing is the constant time variety as opposed to constant rate. The smoothing operation always occurs over the interval that you set; typically 20-100mSec works fine, but for delay time controls, you should increase the parameter smoothing time to match the maximum delay time from the control, to provide a more analog sound as you move the control.

Parameter Smoothing with Frames
The processAudioFrame method already includes the call to the native and VST3SAA parameter smoothers (only one may be active at a time) via the doParameterSmoothing method. You can find the code for this near the top of the function. This is called once per sample interval, and then your cooking code will be called after each smoothing operation. The code here assumes you are using the updateParameters( ) paradigm:

//
{
// --- fire any MIDI events for this sample interval
processFrameInfo.midiEventQueue->fireMidiEvents(processFrameInfo.currentFrame);
// --- do per-frame smoothing
doParameterSmoothing(); // <-- this is the smoother
// --- IF you have your cooking code in one function, you MAY choose to call it here:
updateParameters( ): // <-- your cooking function that you name and write
//
virtual bool fireMidiEvents(uint32_t uSampleOffset)=0
bool doParameterSmoothing()
combines parameter smoothing and VST3 sample accurate updates
Definition: pluginbase.cpp:336
virtual bool processAudioFrame(ProcessFrameInfo &processFrameInfo)
frame-processing method
Definition: plugincore.cpp:151
Information package that arrives with each new audio frame; called internally from the buffer process...
Definition: pluginstructures.h:1177
IMidiEventQueue * midiEventQueue
MIDI event queue.
Definition: pluginstructures.h:1202
uint32_t currentFrame
index of this frame
Definition: pluginstructures.h:1192

Parameter Smoothing with Blocks
When processing blocks, you may perform the parameter smoothing and cooking functions ONCE per block, or interleaved with the block processing, at which point you have the same CPU loading as when processing frames. I prefer to process once per block as long as the blocks are small (64 samples or less). There is a special function for pre-processing prior to each block process, aptly named preProcessAudioBlock and this is where you may smooth and cook the parameters. This code includes a commented-out function call to your parameter cooking function.

{
// --- pre-process the block
processBlockInfo.clearMidiEvents();
// --- sample accurate parameter updates
for (uint32_t sample = processBlockInfo.blockStartIndex;
sample < processBlockInfo.blockStartIndex + processBlockInfo.blockSize;
sample++)
{
// --- the MIDI handler will load up the vector in processBlockInfo
if (midiEventQueue)
midiEventQueue->fireMidiEvents(sample);
}
// --- this will do parameter smoothing ONLY ONCE AT THE TOP OF THE BLOCK PROCESSING
//
// --- to perform per-sample parameter smoothing, move this line of code, AND your updating
// functions (see updateParameters( ) in comment below) into the for( ) loop above
// NOTE: smoothing only once per block usually SAVES CPU cycles
// smoothing once per sample period usually EATS CPU cycles, potentially unnecessarily
// --- call your GUI update/cooking function here, now that smoothing has occurred
//
// NOTE:
// updateParameters is the name used in Will Pirkle's books for the GUI update function
// you may name it what you like - this is where GUI control values are cooked
// for the DSP algorithm at hand
// NOTE: updating (cooking) only once per block usually SAVES CPU cycles
// updating (cooking) once per sample period usually EATS CPU cycles, potentially unnecessarily
// updateParameters();
return true;
}
//
Double buffered queue for MIDI messages.
Definition: pluginstructures.h:1628
virtual bool preProcessAudioBlock(IMidiEventQueue *midiEventQueue=nullptr)
pre-process the audio block
Definition: plugincore.cpp:234
uint32_t blockSize
size of this block
Definition: pluginstructures.h:1077
uint32_t blockStartIndex
start
Definition: pluginstructures.h:1078

Parameter Smoothing with Buffers
Processing a buffer is the same concept as processing a very large block and you have the same options for breaking up the smoothing operation, all the way down to the sample (frame) level if desired. In this case, the processAudioBlock function will be based on entire buffers; or you may alternatively override and implement a PluginCore::processAudioBuffers( ) function. In any event, it will be up to you to figure out how often you want to smooth and then re-cook the GUI parameters.