The <modulators> element
Version 1.6.0 of Decent Sampler officially introduces the new <modulators> section into the .dspreset format. This section lives below the top-level <DecentSampler> element and it is where all modulators for the entire sample library live.
<DecentSampler>
<modulators>
<!-- Your modulators go here. -->
</modulators>
</DecentSampler>
The <lfo> element
Underneath the <modulators> section, you can have any number of different LFOs, which are defined using an <lfo> element, for example:
<modulators>
<lfo shape="sine" frequency="2" modAmount="1.0"></lfo>
</modulators>
For an LFO with a 500ms delay before starting:
<modulators>
<lfo shape="sine" frequency="2" modAmount="1.0" delayTime="0.500"></lfo>
</modulators>
This element has the following attributes:
shape: controls the oscillator shape. Possible values aresine,square,saw.frequency: The speed of the LFO in cycles per second. For example, a value of 10 would mean that the waveform repeats ten times per second.modAmount: This value between 0 and 1 controls how much the modulation affects the things it is targeting. In conventional terms, this is like the modulation depth. Default value: 1.0.delayTime: The time in seconds to wait before the LFO starts outputting signal. During this delay period, the LFO outputs zero. Default value: 0.0 (no delay).scope: Whether or not this LFO exists for all notes or whether each keypress gets its own LFO. Possible values areglobal(default for LFOs) andvoice. Ifvoiceis chosen, a new LFO is started each time a new note is pressed.modBehavior: This attribute controls how the LFO affects the parameter it is targeting. Possible values areadd,multiply, andset. Ifaddis chosen, the LFO will add its value to the parameter it is targeting. Ifmultiplyis chosen, the LFO will multiply its value by the parameter it is targeting. Ifsetis chosen, the LFO will set the parameter it is targeting to its value. Default value:set.
The <envelope> element
In addition to LFOs, you can also have additional ADSR envelopes. These can be useful for controlling group-level effects, such as low-pass filters. If this is what you wish to achieve, make sure you check out the section on group-level effects below.
To create an envelope, use an <envelope> element:
This element has the following attributes:
attack: The length in seconds of the attack portion of the ADSR envelopedecay: The length in seconds of the decay portion of the ADSR envelopesustain: The height of the sustain portion of the ADSR envelope. This is expressed as a value between 0 and 1.release: The length in seconds of the release portion of the ADSR envelopemodAmount: This value between 0 and 1 controls how much the modulation affects the things it is targeting. In conventional terms, this is like the modulation depth. Default value: 1.0.scope: Whether or not this LFO exists for all notes or whether each keypress gets its own LFO. Possible values areglobalandvoice(default for envelopes). Ifvoiceis chosen, a new LFO is started each time a new note is pressed.modBehavior: This attribute controls how the envelope affects the parameter it is targeting. Possible values areadd,multiply, andset. Ifaddis chosen, the envelope will add its value to the parameter it is targeting. Ifmultiplyis chosen, the envelope will multiply its value by the parameter it is targeting. Ifsetis chosen, the envelope will set the parameter it is targeting to its value. Default value:set.attackCurve: A numeric value from -100 to 100 that determines the shape of the attack portion of the ADSR envelope. Common values are-100(logarithmic),0(linear), and100(exponential). Default value:-100(logarithmic).decayCurve: A numeric value from -100 to 100 that determines the shape of the decay portion of the ADSR envelope. Common values are-100(logarithmic),0(linear), and100(exponential). Default value:100(exponential).releaseCurve: A numeric value from -100 to 100 that determines the shape of the release portion of the ADSR envelope. Common values are-100(logarithmic),0(linear), and100(exponential). Default value:100(exponential).
The <midiCC> element
The <midiCC> element allows you to use MIDI Continuous Controller (CC) messages as modulation sources. This makes it possible to control various parameters of your instrument in response to incoming MIDI CC data, such as the mod wheel, expression pedal, or any other MIDI controller.
<modulators>
<midiCC number="1" modAmount="1.0" channel="voice">
<!-- Bindings go here -->
</midiCC>
</modulators>
This element has the following attributes:
number: The MIDI CC number to respond to (0-127). Common CC numbers include 1 (mod wheel), 7 (volume), 10 (pan), 11 (expression), and 74 (filter cutoff). This attribute is required.modAmount: This value between 0 and 1 controls how much the modulation affects the things it is targeting. In conventional terms, this is like the modulation depth. Default value: 1.0.channel: Determines which MIDI channel’s CC values to read. Options are:"voice"(default): Uses the MIDI channel of the note that triggered the voice. This is essential for MPE and multi-channel setups where each voice may be on a different channel.1-16: A specific channel number. The modulator will always read CC values from this channel, regardless of which channel triggered the voice. Useful for global control in non-MPE setups.
For voice-scope modulators, the default is
"voice". For global-scope modulators, the default is"1".
Here’s a practical example that uses the mod wheel (CC#1) to control filter cutoff with per-voice channel tracking:
<modulators>
<midiCC number="1" modAmount="1.0" channel="voice" scope="voice">
<binding type="effect" level="group" groupIndex="0" effectIndex="0" parameter="FX_FILTER_FREQUENCY"
modBehavior="add"
translation="table"
translationTable="0,33;0.3,150;0.4,450;0.5,1100;0.7,4100;0.9,11000;1.0001,22000" />
</midiCC>
</modulators>
Example of a global CC modulator that always reads from channel 1:
<modulators>
<midiCC number="11" modAmount="1.0" channel="1" scope="global">
<binding type="amp" level="instrument" parameter="AMP_VOLUME"
modBehavior="set"
translation="linear"
translationOutputMin="0"
translationOutputMax="1" />
</midiCC>
</modulators>
Important distinction: The <midiCC> modulator creates temporary modulation that only lasts for the life of each note. This means the modulation values are computed per-note and do not permanently change the underlying parameter values. In contrast, the <midi><cc> element (documented in the MIDI section) allows MIDI continuous controllers to make permanent value changes to parameters, similar to how UI knobs work. Use <midiCC> modulators when you want per-note modulation behavior (like using the mod wheel to add vibrato to individual notes), and use <midi><cc> bindings when you want global parameter changes (like using a CC to control the overall cutoff frequency of a filter).
MPE and Multi-Channel Considerations: In MPE mode or multi-channel setups, CC messages are sent per-channel. The channel attribute determines how the modulator handles this:
With
channel="voice", each note reads CC values from its own MIDI channel, allowing independent control per note (essential for MPE)With
channel="1"(or any specific channel), all voices read from that channel’s CC values, providing global control
The <midiVelocity> element
The <midiVelocity> element allows you to use note-on velocity as a modulation source. This makes it possible to control various parameters based on how hard a key is pressed, providing dynamic expression in your instrument.
<modulators>
<midiVelocity modAmount="1.0" scope="voice">
<!-- Bindings go here -->
</midiVelocity>
</modulators>
This element has the following attributes:
modAmount: This value between 0 and 1 controls how much the modulation affects the things it is targeting. In conventional terms, this is like the modulation depth. Default value: 1.0.scope: Whether or not this modulator exists for all notes or whether each keypress gets its own modulator. Possible values areglobalandvoice(default for midiVelocity). Ifvoiceis chosen, each note retains its own velocity value for modulation.
Here’s a practical example that uses velocity to control the brightness of a sound:
<modulators>
<midiVelocity scope="voice" modAmount="1.0">
<binding type="effect" level="group" groupIndex="0" effectIndex="0" parameter="FX_FILTER_FREQUENCY"
modBehavior="add"
translation="linear"
translationOutputMin="0"
translationOutputMax="5000" />
</midiVelocity>
</modulators>
This example uses velocity to modulate the cutoff frequency of a low-pass filter, making softer notes sound darker and harder notes sound brighter. This is a common technique for adding expressiveness to sampled instruments.
Another example showing velocity controlling reverb amount:
<modulators>
<midiVelocity scope="voice" modAmount="1.0">
<binding type="effect" level="group" groupIndex="0" effectIndex="1" parameter="FX_REVERB_WET_LEVEL"
modBehavior="set"
translation="linear"
translationOutputMin="0.0"
translationOutputMax="0.5" />
</midiVelocity>
</modulators>
The <mpeTimbre> element
The <mpeTimbre> element allows users to control the timbre of an instrument in response to MPE messages. NOTE: In order for this to work, the plugin must be in MPE mode. This can be turned on by going into the File > MIDI Input Settings.. dialog box. The <mpeTimbre> element has the following attributes:
scope: Whether or not this MPE timbre exists for all notes or whether each keypress gets its own MPE timbre. Possible values areglobalandvoice(default for MPE timbre). Ifvoiceis chosen, a new MPE timbre is started each time a new note is pressed.risingSmoothingTime: The time in milliseconds it takes for the MPE timbre to rise to its target value. Default value: 0 milliseconds.fallingSmoothingTime: The time in milliseconds it takes for the MPE timbre to fall to its target value. Default value: 0 milliseconds.
Here’s a practical example of how to use the <mpeTimbre> element:
<modulators>
<mpeTimbre scope="voice" fallingSmoothingTime="20" risingSmoothingTime="20">
<!-- This binding modifies the frequency of a low-pass filter -->
<binding type="effect" level="group" groupIndex="0" effectIndex="0" parameter="FX_FILTER_FREQUENCY"
modBehavior="add"
translation="table"
translationTable="0,33;0.3,150;0.4,450;0.5,1100;0.7,4100;0.9,11000;1.0001,22000" />
</mpeTimbre>
</modulators>
This example shows an MPE timbre modulator that modifies the frequency of a low-pass filter based on the MPE timbre value. This example uses a translation table to create a non-linear response curve, making the filter frequency changes more musical and intuitive.
The <mpePressure> element
The <mpePressure> element allows users to control an instrument in response to MPE Pressure messages. NOTE: In order for this to work, the plugin must be in MPE mode. This can be turned on by going into the File > MIDI Input Settings.. dialog box. The <mpePressure> element has the following attributes:
scope: Whether or not this MPE pressure exists for all notes or whether each keypress gets its own MPE pressure. Possible values areglobalandvoice(default for MPE pressure). Ifvoiceis chosen, a new MPE pressure is started each time a new note is pressed.risingSmoothingTime: The time in milliseconds it takes for the MPE pressure to rise to its target value. Default value: 0 milliseconds.fallingSmoothingTime: The time in milliseconds it takes for the MPE pressure to fall to its target value. Default value: 0 milliseconds.
Here’s a practical example of how to use the <mpePressure> element:
<modulators>
<mpePressure scope="voice" fallingSmoothingTime="20" risingSmoothingTime="20">
<binding type="amp" level="group" groupIndex="0" effectIndex="0" parameter="AMP_VOLUME"
modBehavior="set"
translation="linear"
translationOutputMin="0.5"
translationOutputMax="1" />
</mpePressure>
</modulators>
This example shows an MPE pressure modulator that modifies the volume of a group based on the MPE pressure value. The translation is linear, meaning that the pressure value directly scales the volume between 0.5 and 1.
The <random> element
The <random> element allows you to generate random values for modulation. This can be useful for adding unpredictability and variation to your sounds. The <random> element always produces a value between -1 and 1. It has the following attributes:
mode: The event that triggers the random value generation. This can be eithernote_onorperiodic.frequency: How often the random value is generated. This is only relevant if themodeis set toperiodic.trigger: Two possible values:attackmeans that the random value generator is reset on note-on events,nonemeans that it is not. This is really only help for periodic random value generators.seed: An integer seed value for the random number generator. This can be useful for creating reproducible results.scope: Whether or not this modulator exists for all notes or whether each keypress gets its own modulator. Possible values areglobalandvoice. Ifvoiceis chosen, a new random modulator is started each time a new note is pressed.
Here’s an example of how to use the <random> element:
<modulators>
<random mode="note_on" seed="12345" scope="voice">
<binding type="amp" level="group" groupIndex="0" effectIndex="0" parameter="AMP_VOLUME"
modBehavior="set"
translation="linear"
translationOutputMin="0.5"
translationOutputMax="1" />
</random>
</modulators>
How to use <binding>s in conjunction with modulators
In order to actually have your LFOs and envelopes do anything, you need to have bindings under them. If you are not familiar with the concept of bindings, you may want to read this section then return here. Bindings tell the engine which parameters the LFO should be affecting and how. Here is an example:
<modulators>
<lfo shape="sine" frequency="2" modAmount="1.0">
<!-- This binding modifies the frequency of a low-pass filter -->
<binding type="effect" level="instrument" effectIndex="0" parameter="FX_FILTER_FREQUENCY" modBehavior="add" translation="linear" translationOutputMin="0" translationOutputMax="2000.0" />
</lfo>
</modulators>
Controlling Modulator Parameters with Bindings
You can bind to modulator parameters themselves to control them in real-time from UI controls, MIDI CC, or other sources. The binding uses type="modulator" and level="instrument", with a modulatorIndex attribute pointing to the modulator (0-based) and a parameter attribute specifying which property to change.
Bindable parameters by modulator type
<lfo>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
|
LFO rate in Hz |
|
Waveform shape ( |
|
Delay before the LFO begins (seconds) |
|
Reset behavior ( |
<envelope>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
|
Attack time (seconds) |
|
Attack curve shape |
|
Decay time (seconds) |
|
Decay curve shape |
|
Sustain level (0.0–1.0) |
|
Release time (seconds) |
|
Release curve shape |
|
Delay before the envelope begins (seconds) |
<random>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
|
Generation rate in Hz (only relevant when |
|
Reset behavior ( |
<midiCC>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
<midiVelocity>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
<mpeTimbre>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
<mpePressure>
|
Description |
|---|---|
|
Modulation depth (0.0–1.0) |
Example: controlling LFO parameters from UI knobs
<DecentSampler>
<ui>
<tab>
<!-- Controls LFO depth -->
<labeled_knob x="10" y="30" width="90" textSize="16" textColor="AA000000"
parameterName="Depth" minValue="0" maxValue="1" value="1">
<binding type="modulator" level="instrument" parameter="MOD_AMOUNT"
modulatorIndex="0" translation="linear" />
</labeled_knob>
<!-- Controls LFO rate -->
<labeled_knob x="110" y="30" width="90" textSize="16" textColor="AA000000"
parameterName="Rate" minValue="0.1" maxValue="20" value="2">
<binding type="modulator" level="instrument" parameter="FREQUENCY"
modulatorIndex="0" translation="linear" />
</labeled_knob>
<!-- Controls LFO delay time -->
<labeled_knob x="210" y="30" width="90" textSize="16" textColor="AA000000"
parameterName="Delay" minValue="0" maxValue="2" value="0.5">
<binding type="modulator" level="instrument" parameter="MOD_DELAY_TIME"
modulatorIndex="0" translation="linear" />
</labeled_knob>
</tab>
</ui>
<modulators>
<lfo shape="sine" frequency="2" modAmount="1.0" delayTime="0.500">
<binding type="effect" level="instrument" effectIndex="0" parameter="FX_FILTER_FREQUENCY"
modBehavior="add" translation="linear" translationOutputMin="0" translationOutputMax="2000.0" />
</lfo>
</modulators>
</DecentSampler>
You can also control modulator parameters via MIDI CC:
<DecentSampler>
<midi>
<cc number="74">
<binding type="modulator" level="instrument" parameter="MOD_AMOUNT"
modulatorIndex="0" translation="linear" translationOutputMin="0" translationOutputMax="1" />
</cc>
</midi>
<modulators>
<lfo shape="sine" frequency="2" modAmount="1.0" delayTime="0.500">
<binding type="effect" level="instrument" effectIndex="0" parameter="FX_FILTER_FREQUENCY"
modBehavior="add" translation="linear" translationOutputMin="0" translationOutputMax="2000.0" />
</lfo>
</modulators>
</DecentSampler>
There are a few differences between bindings as they are used by knobs and the ones used by modulators. Specifically, when you move a UI control that has a binding attached, the engine actually goes out and changes the value of the parameter that is targeted by that binding. For example, if you have a knob that controls a lowpass filter’s cutoff frequency, moving that knob will cause that actual frequency of that filter to change. In other words, the changes that the knob is making on the underlying sample library are permanent. The same is also true for bindings associated with MIDI continuous controllers.
Modulators, on the other hand, are temporary. If a modulator (such as an LFO) changes its value, the engine looks at the bindings associated with that LFO and then makes a list of temporary changes to the underlying data. When it comes time to render out the effect, it consults both the permanent value and the temporary modulation values. As a result of this difference in the way bindings are handled, only some parameters are “modulatable.” At time of press, the following parameters are modulatable:
Almost all effect parameters
Group Volume
Global Volume
Group Pan
Global Pan
Group Tuning
Global Tuning
Modulator scope: global or voice-level
By default, all modulators will be created at the global level. This means that there will be exactly one modulator that is shared by all voices. In many situations, such as an LFO modulating a single low-pass filter which is shared by all of voices, this is often what we want.
But there are other situations where we don’t want our modulator to be global. For example, what if we want to have a unique envelope for each key-press? Well, for that we use the scope attribute of the <lfo> or <envelope> element. This attribute can be set to voice, which means that each time a new note is pressed, a new modulator will be created for that note. This is particularly useful for envelopes, which are often used to control parameters that are unique to each note, such as the volume or filter cutoff of a note:
<envelope attack="2" decay="0" sustain="1" release="0.5" modAmount="1.0" scope="voice">
For certain parameters, such as Group Tuning or Group Pan, it may always make sense to have scope="voice" set. For others, such as Global Volume or Global Pan, it may make more sense to have scope="global" set.
For a full discussion of how to use modulators, check out this article on the Decent Samples blog.