The <modulators> element ============================== Version 1.6.0 of Decent Sampler officially introduces the new `` section into the `.dspreset` format. This section lives below the top-level `` element and it is where all modulators for the entire sample library live. ```xml ``` ## The <lfo> element Underneath the `` section, you can have any number of different LFOs, which are defined using an `` element, for example: ```xml ``` For an LFO with a 500ms delay before starting: ```xml ``` This element has the following attributes: - **`shape`**: controls the oscillator shape. Possible values are `sine`, `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 are `global` (default for LFOs) and `voice`. If `voice` is 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 are `add`, `multiply`, and `set`. If `add` is chosen, the LFO will add its value to the parameter it is targeting. If `multiply` is chosen, the LFO will multiply its value by the parameter it is targeting. If `set` is 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 `` element: This element has the following attributes: - **`attack`**: The length in seconds of the attack portion of the ADSR envelope - **`decay`**: The length in seconds of the decay portion of the ADSR envelope - **`sustain`**: 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 envelope - **`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 LFO exists for all notes or whether each keypress gets its own LFO. Possible values are `global` and `voice` (default for envelopes). If `voice` is 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 are `add`, `multiply`, and `set`. If `add` is chosen, the envelope will add its value to the parameter it is targeting. If `multiply` is chosen, the envelope will multiply its value by the parameter it is targeting. If `set` is 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), and `100` (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), and `100` (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), and `100` (exponential). Default value: `100` (exponential). ## The <midiCC> element The `` 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. ```xml ``` 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: ```xml ``` Example of a global CC modulator that always reads from channel 1: ```xml ``` **Important distinction:** The `` 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 `` element (documented in [the MIDI section](the-midi-element.md)) allows MIDI continuous controllers to make _permanent_ value changes to parameters, similar to how UI knobs work. Use `` modulators when you want per-note modulation behavior (like using the mod wheel to add vibrato to individual notes), and use `` 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 `` 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. ```xml ``` 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 are `global` and `voice` (default for midiVelocity). If `voice` is 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: ```xml ``` 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: ```xml ``` ## The <mpeTimbre> element The `` 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 `` 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 are `global` and `voice` (default for MPE timbre). If `voice` is 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 `` element: ```xml ``` 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 `` 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 `` 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 are `global` and `voice` (default for MPE pressure). If `voice` is 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 `` element: ```xml ``` 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 `` element allows you to generate random values for modulation. This can be useful for adding unpredictability and variation to your sounds. The `` 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 either `note_on` or `periodic`. - **`frequency`**: How often the random value is generated. This is only relevant if the `mode` is set to `periodic`. - **`trigger`**: Two possible values: `attack` means that the random value generator is reset on note-on events, `none` means 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 are `global` and `voice`. If `voice` is chosen, a new random modulator is started each time a new note is pressed. Here's an example of how to use the `` element: ```xml ``` ## 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](https://www.decentsamples.com/wp-content/uploads/2020/06/format-documentation.html#appendix-b-the-binding-element) then return here. Bindings tell the engine which parameters the LFO should be affecting and how. Here is an example: ```xml ``` ### 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 **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | | `FREQUENCY` | LFO rate in Hz | | `SHAPE` | Waveform shape (`"sine"`, `"saw"`, `"square"`, `"triangle"`) | | `MOD_DELAY_TIME` | Delay before the LFO begins (seconds) | | `TRIGGER` | Reset behavior (`"attack"` or `"none"`) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | | `ENV_ATTACK` | Attack time (seconds) | | `ENV_ATTACK_CURVE` | Attack curve shape | | `ENV_DECAY` | Decay time (seconds) | | `ENV_DECAY_CURVE` | Decay curve shape | | `ENV_SUSTAIN` | Sustain level (0.0–1.0) | | `ENV_RELEASE` | Release time (seconds) | | `ENV_RELEASE_CURVE` | Release curve shape | | `MOD_DELAY_TIME` | Delay before the envelope begins (seconds) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | | `FREQUENCY` | Generation rate in Hz (only relevant when `mode="periodic"`) | | `TRIGGER` | Reset behavior (`"attack"` or `"none"`) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | **``** | `parameter` value | Description | |---|---| | `MOD_AMOUNT` | Modulation depth (0.0–1.0) | #### Example: controlling LFO parameters from UI knobs ```xml ``` You can also control modulator parameters via MIDI CC: ```xml ``` 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 `` or `` 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: ```xml ``` 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](https://www.decentsamples.com/2022/08/19/how-to-add-lfos-and-extra-envelopes-to-your-decent-sampler-instruments).