UserPresetHandler
The UserPresetHandler
object can be used to customize the data handling of your project. You can attach callbacks to certain events (eg. loading user presets), define a custom data object that will replace the default XML structure of a user preset and add custom automation parameters. In order to use it, create it with Engine.createUserPresetHandler()
and then use one of its methods.
const var UserPresetHandler = Engine.createUserPresetHandler();
It's considered best practice to only have one of these objects around.
Class methods
attachAutomationCallback
Attaches a callback to automation changes. Pass a non-function as updateCallback to remove the callback for the given automation ID.
UserPresetHandler.attachAutomationCallback(String automationId, var updateCallback, var isSynchronous)
This attaches a script callback to an automation parameter that was previously registered using
Userpresethandler.setCustomAutomation()
.
The automationId
argument must match the ID
property of one of the existing automation parameters. The function you pass in as updateFunction
must have a single parameter that will contain the parameter value when the parameter is changed. You can decide whether the execution of the parameter should be done synchronously (in the audio thread) or deferred on the UI thread depenending on your use case.
Unlike the setControlCallback()
function, registering an additional callback will not
override the connections defined with the connections
property so it can be used to perform additional tasks.
Use clearAttachedCallbacks()
in order to remove previously registered functions.
clearAttachedCallbacks
Clears all attached callbacks. Edit on GitHub
UserPresetHandler.clearAttachedCallbacks()
createObjectForAutomationValues
Creates an object containing the values for every automation ID. Edit on GitHub
UserPresetHandler.createObjectForAutomationValues()
createObjectForSaveInPresetComponents
Creates an object containing all values of components with the saveInPreset
flag. Edit on GitHub
UserPresetHandler.createObjectForSaveInPresetComponents()
getAutomationIndex
Returns the automation index. Edit on GitHub
UserPresetHandler.getAutomationIndex(String automationID)
getSecondsSinceLastPresetLoad
Returns the amount of seconds since the last preset has been loaded. Edit on GitHub
UserPresetHandler.getSecondsSinceLastPresetLoad()
isCurrentlyLoadingPreset
Returns true if this is called somewhere inside a preset load. This takes the thread ID into account to avoid false positives when calling this on another thread. Edit on GitHub
UserPresetHandler.isCurrentlyLoadingPreset()
isInternalPresetLoad
Returns true if the user preset that is about to be loaded is a DAW state (or initial state). This function is only useful during the pre / post load callbacks. Edit on GitHub
UserPresetHandler.isInternalPresetLoad()
isOldVersion
Checks if the given version string is a older version than the current project version number. Edit on GitHub
UserPresetHandler.isOldVersion( String version)
resetToDefaultUserPreset
Loads the default user preset (if it's defined in the project). Edit on GitHub
UserPresetHandler.resetToDefaultUserPreset()
runTest
Runs a few tests that catches data persistency issues. Edit on GitHub
UserPresetHandler.runTest()
sendParameterGesture
Sends a parameter gesture change message to the host. Returns true if the parameter exists. Edit on GitHub
UserPresetHandler.sendParameterGesture(int automationType, int indexWithinType, bool gestureActive)
setAutomationValue
Sends an automation value change for the given index. Edit on GitHub
UserPresetHandler.setAutomationValue(int automationIndex, float newValue)
setCustomAutomation
Enables host / MIDI automation with the custom user preset model.
UserPresetHandler.setCustomAutomation(var automationData)
This method allows to define automation parameters that can be attached / detached to UI elements dynamically. If you use this mode, all MIDI CC automation and macro assignments will operate on the automation data model instead of the actual UI element (so assigning a MIDI CC to a UI element via drop down or learn mode will connect the automation data with the source).
This is useful when you're using an UI element that can be assigned to multiple targets dynamically. In order to use it, just call this method with a JSON object that describes all automatable parameters. Then use the automationID
property of a UI element to assign the control to one of the automation targets. This can be done dynamically.
Be aware that this function only works in combination with a custom user preset model, so you need to call UserPresetHandler.setUseCustomDataModel()
before using this method.
The JSON object you need to pass in here must be an array of objects where each array element describes one automation parameter. The automation object can / must have these properties:
Property | Type | Default | Description |
ID
|
String | - | a unique identifier for the automation parameter |
min
|
double | 0.0 | he minimum value |
max
|
double | 1.0 | the maximum value |
middlePosition
|
double | mid | the middle position |
stepSize
|
double | 0.0 | the step size |
allowMidiAutomation
|
bool | true
|
whether this parameter can be automated using MIDI CC messages |
allowHostAutomation
|
bool | true
|
whether this parameter can be automated using host automation (plugin parameters) |
connections
|
Array | []
|
A list of parameter targets that are changed when the parameter is changed. Each element of this array must be a object with a processorId
and parameterId
property (just like the default UI element connections). |
Example object:
const var automationObject =
[
{
"ID": "First Parameter",
"min": 0.5,
"max": 2.0,
"middlePosition": 1.0,
"stepSize": 0.0,
"allowMidiAutomation": true,
"allowHostAutomation": false,
"connections": [
{
"processorId": "SimpleGain1",
"parameterId": "Gain"
},
{
"processorId": "SimpleGain2",
"parameterId": "Gain"
},
]
}
]
The connections
property is not the only way to add logic to a automation parameter, you can alse use Userpresethandler.attachAutomationCallback()
in order to add a scripted callback (this is equivalent to ScriptComponent.setControlCallback()
vs. using the processorId
/ parameterId
connections).
setEnableUserPresetPreprocessing
Enables a preprocessing of every user preset that is being loaded. Edit on GitHub
UserPresetHandler.setEnableUserPresetPreprocessing(bool processBeforeLoading, bool shouldUnpackComplexData)
setParameterGestureCallback
Attaches a callback to the begin and end of parameter gestures. Edit on GitHub
UserPresetHandler.setParameterGestureCallback(var callbackFunction)
setPluginParameterGroupNames
Sets the available group names for plugin parameter groups. Edit on GitHub
UserPresetHandler.setPluginParameterGroupNames(var pluginParameterGroupNames)
setPluginParameterSortFunction
Sets a custom sort function for the plugin parameter order.
UserPresetHandler.setPluginParameterSortFunction(var customSortFunction)
This function is used if you need to maintain backwards compatibility with older versions: the order how plugin parameters are registered at the host might affect old DAW projects so if you change that order between updates, there is the chance of breaking user projects.
In order to prevent that problem, you can use this function to ensure that all plugin parameters that you've added in your update are put at the end of the list so that the old plugin parameters retain their index.
If you are free of the burden of backwards-compatibility, you can still use this function to prettify your plugin parameter list, which otherwise is created in a default sorting logic that may or may not align with your expected order.
By default the plugin parameters are sorted by type, and then within that type in order of definition. The type order is:
- All dynamic plugin parameters (if
HISE_MACROS_ARE_PLUGIN_PARAMETERS=1
) in ascending order - All custom automation parameters defined with UserPresetHandler.setCustomAutomation()
- All script components that have the
isPluginParameter
flag set.
So if you have 3 macros, 2 custom automation slots and 4 script controls, the order would be:
Macro 1
Macro 2
Macro 3
Custom 1
Custom 2
Component 1
Component 2
Component 3
Component 4
If you need to change that default order, just pass in a function into this method. This function will be executed whenever the plugin parameters are rebuilt (so in HISE itself after each compilation and in your compiled plugin once at initialisation). It expects two parameters p1
and p2
which will be filled with two JSON objects with the following properties:;
Property | Description |
type
|
the plugin parameter type. This is a magic number and will be 0
for macro controls, 1
for custom automation slots and 2
for UI components |
parameterIndex
|
This is the index in the default sorting order. |
typeIndex
|
This is the index within the type. So for the first custom automation slot it will be 0, no matter how many other parameters of a different type come before that. |
group
|
If you have assigned this parameter to a group, it will contain the string with the group name, otherwise it will be an empty string. |
name
|
the plugin parameter name as it will be shown in the host. |
Using the example list above, this would be the JSON object for two of the elements:
{
"Macro 2": {
type: 0, // type is macro
parameterIndex: 1, // index in full list
typeIndex: 1 // second macro
group: "", // no group
name: "Macro 2" // macro name
},
"Component 1": {
type: 2, // type is UI component
parameterIndex: 5, // index in full list
typeIndex: 0, // first UI component
group: "", // no parameterGroupName set
name: "Component 1" // pluginParameterName property
}
}
You will now have to implement the sorting logic by writing a function that compares the two objects and returns one of the given values:
-1
if the first parameter should come before the second1
if the second parameter should come before the first0
if the parameters are supposed to be equalundefined
if you want to resort to the default sorting logic between the two parameters.
Note that if you use parameter groups it will override this sorting mechanism and always put parameters without a group ID first followed by all parameters of a group (as this is how it's required by the hosts), so be cautious when adding parameter group IDs to an existing project.
Examples
Here is an example function that will keep the normal sorting logic but move all custom automation data slots at the beginning of the list.
You would use this function if you have added the ability of assigning dynamic plugin parameters in an update and want to ensure that the original order of the custom automation slots are not changed (because by default the macro parameters would be put at the beginning of the list).
const var CUSTOM_TYPE = 1;
// This function moves all custom automation parameters at the beginning (so they appear before the macros)
uph.setPluginParameterSortFunction(function(p1, p2)
{
// If one of the parameters is a custom type, put it before
// the other element.
if(p1.type == CUSTOM_TYPE && p2.type != CUSTOM_TYPE)
return -1;
else if (p2.type == CUSTOM_TYPE && p1.type != CUSTOM_TYPE)
return 1;
// otherwise return undefined which uses the default sorting
return undefined;
});
You can always check the order of the parameters in the Plugin Parameter Simulator which will be rebuilt after each compilation and takes the sorting mechanism into account.
Another example that will put the plugin parameters from a given name list at the end can be used if your update contains new controls that you want to be put at the end of the list:
// These are the new controls in your update that you want to put at the end:
const var NEW_CONTROLS = [ "Close 2", "Far 1"];
// This function moves all custom automation parameters at the beginning (so they appear before the macros)
uph.setPluginParameterSortFunction(function(p1, p2)
{
var c1 = NEW_CONTROLS.contains(p1.name);
var c2 = NEW_CONTROLS.contains(p2.name);
if(c1 && !c2) // p1 is a new control and p2 isn't
return 1;
else if (c2 && !c1) // p2 is a new control and p1 isn't
return -1;
// otherwise return undefined which uses the default sorting
return undefined;
});
setPostCallback
Sets a callback that will be executed after the preset has been loaded. Edit on GitHub
UserPresetHandler.setPostCallback(var presetPostCallback)
setPostSaveCallback
Sets a callback that will be executed after a preset has been saved. Edit on GitHub
UserPresetHandler.setPostSaveCallback(var presetPostSaveCallback)
setPreCallback
Sets a callback that will be executed synchronously before the preset was loaded Edit on GitHub
UserPresetHandler.setPreCallback(var presetPreCallback)
setUseCustomUserPresetModel
Disables the default user preset data model and allows a manual data handling.
UserPresetHandler.setUseCustomUserPresetModel(var loadCallback, var saveCallback, bool usePersistentObject)
This function can be used to bypass the default HISE data model (one value per UI element) and roll your own data model with dedicated callbacks for loading and saving the state of your projects (eg. in user presets, DAW save files, init state, etc).
In order to use this function, you need to supply two methods for loading and saving with these signatures:
const var uph = Engine.createUserPresetHandler();
inline function onPresetLoad(var obj)
{
// do something with `obj`
}
inline function onPresetSave()
{
return { "MyObject": someContent };
}
uph.setUseCustomUserPresetModel(onPresetLoad, onPresetSave, false);
The load function contains a single parameter with a JSON object describing your plugin state. What the content of this object is is defined by your save function, which expects you to return a object that fully describes your plugin state.
Usually this means that you have one big JSON object that you return in your save function and restore in your load function.
During development, the usePersistentObject
flag might be helpful - if this is true, it will call the save function before recompiling and call the load function with the previous object after compiling, so that the values of your data object to not reset each time you compile. However this might overwrite the object you define, so at the beginning, you must use this function at least once without this flag. In the compiled project it won't make much of a difference as the recompilation does not happen after the plugin was instantiated.
Also be aware that if you want to use the custom automation model, you will need to enable this mode too.
setUseUndoForPresetLoading
Enables Engine.undo() to restore the previous user preset (default is disabled). Edit on GitHub
UserPresetHandler.setUseUndoForPresetLoading(bool shouldUseUndoManager)
updateAutomationValues
Updates the given automation values and optionally sends out a message. Edit on GitHub
UserPresetHandler.updateAutomationValues(var data, var sendMessage, bool useUndoManager)
updateConnectedComponentsFromModuleState
Restores the values for all UI elements that are connected to a processor with the processorID
/ parameterId
properties. Edit on GitHub
UserPresetHandler.updateConnectedComponentsFromModuleState()
updateSaveInPresetComponents
Restores all values of components with the saveInPreset
flag. Edit on GitHub
UserPresetHandler.updateSaveInPresetComponents(var obj)