Gradually decrease/increase gain on song state change

Hi all,
I’d like to know if it is possible to gradually increase and/or decrease a plugin gain getting the same behaviour as if using an expression pedal. It would be great to do that on a song state change event, I would like avoiding use an expression pedal since it’s not precise.
Thanks in advance to all those who will help me to manage this.

1 Like

Hi Marco,

first: welcome to the community and a happy new year!

Unfortunately, it is not possible to do what you want natively within Cantabile, but there is - as almost always - a way around this. Unfortunately, this is a bit complex, but it works nicely for me. This solution will require three things:

  • a ReaJS script that allows you to “ramp” controller changes
  • bindings in your instrument rack that will react to cc commands and route them to the ReaJS plugin and from there to the rack output volume
  • Song State bindings in your song that will trigger these volume changes

Let me show you how I do this: Here is one of my Hammond racks:

It contains two “Smoother” plugins - essentially just renamed ReaJS plugins running my custom script:

This script reads CC commands (in this case CC7=volume) and gradually steps from the current value to the new value, with every step being delayed by the “ms per step” parameter. These current settings make for nice smooth volume ramps, but of course this is tuneable.

There’s a nice little twist to the script: if you send CC7 on channel 2, the change is instant, only CC7 on channel 1 get ramped. So if you want quick jumps in volume, you can do this as well.

Now to the bindings:

I have created them in my organ rack so that I send CC7 for “smoothed” volume changes and CC8 for “instant” changes. The first two bindings simply translate this to CC7 on channel 1 and 2 respectively and send them to the plugin with the script.

The third binding processes the output from the plugin and controls the output gain of the whole rack. I use a MIDI control curve that sets an output value of 0 dB at a MIDI CC value of 64 (“Cantabile (0 dB Center)”); this way I can best use my control pots - they have a little “stop” in the middle - for volume control.

Now for setting the levels in my song: I use bindings from Song States to send CC7 commands to my organ rack:

You need to set the “Target” state behavior to ON, then you can customize the CC7 values for every state:

That’s it - works nicely, but is a bit complex, I’ll admit.

Now if @brad could give us a specific feature, this would be a lot easier, so here’s my 2021 feature request:

can we have a right-click setting for gain sliders that lets us set gain change speeds? If we can set this with a numerical parameter (0…100, with 0 being “instant” and 100 “very slow”, whatever that means), then Cantabile could smooth out any volume changes automatically, and we could simply set gain levels directly in our songs.

also maybe a “smoothing” parameter for bindings, so that Cantabile fades smoothly between current value and target value on state changes (if no “current value” exists, change can be instant)

@brad: pretty pleeeeease?




Here is the script for ReaJS:

desc: MIDI CC Smoother
//tags: MIDI processing 

slider1:1<0,127,1{0 Bank Sel M,1 Mod Wheel M,2 Breath M,3,4 Foot P M,5 Porta M,6 Data Entry M,7 Vol M,8 Balance M,9,10 Pan M,11 Expression M,12 Ctrl 1 M,13 Ctrl 2 M,14,15,16 GP Slider 1,17 GP Slider 2,18 GP Slider 3,19 GP Slider 4,20,21,22,23,24,25,26,27,28,29,30,31,32 Bank Sel L,33 Mod Wheel L,34 Breath L,35,36 Foot P L,37 Porta L,38 Data Entry L,39 Vol L,40 Balance L,41,42 Pan L,43 Expression L,44 Ctrl 1 L,45 Ctrl 2 L,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64 Hold P sw,65 Porta sw,66 Sustenuto sw,67 Soft P sw,68 Legato P sw,69 Hold 2 P sw,70 S.Variation,71 S.Timbre,72 S.Release,73 S.Attack,74 S.Brightness,75 S.Ctrl 6,76 S.Ctrl 7,77 S.Ctrl 8,78 S.Ctrl 9,79 S.Ctrl 10,80 GP B.1 sw,81 GP B.2 sw,82 GP B.3 sw,83 GP B.4 sw,84,85,86,87,88,89,90,91 Effects Lv,92 Trem Lv,93 Chorus Lv,94 Celeste Lv,95 Phaser Lv,96 Data B. Inc,97 Data B. Dec,98 NRP L,99 NRP M,100 RP L,101 RP M,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127}>Controller
slider2:0<0,100,1>ms per Step
slider3:1<1,5,1>Step increment


CC_MSG = 11;
positions = 1000;
values = 2000;
channels = 3000;
maxvalues = 1000;

// calculate samples per interval
interval = (srate*slider2/1000)|0;

stepsize = slider3;
ccsrc = slider1;

// initialize values
currval = 0;
targval = 0;
elapsed = 0;
increment = stepsize;

arraypos = 0;

(interval == 0) ? ( // no smoothing at all
    // simply forward all input
    while (
        midirecv(mpos, msg1, msg23) ? (
            midisend(mpos, msg1, msg23);

) : (  //else, we really need to smooth

    while ( 
        midirecv(mpos, msg1, msg23) ? (
            status = msg1;
            statusHi = (msg1/16)|0;
            statusLo = msg1-(statusHi*16);
            msg3 = (msg23/256)|0;
            msg2 = msg23-(msg3*256);
            (statusHi == CC_MSG && msg2 == ccsrc) ? ( // we have a controller to smooth
                // now collect it into the arrays
                positions[arraypos] = mpos;
                values[arraypos] = msg3;
                channels[arraypos] = statusLo;
            ) : ( // not my controller - simply pass thru
                midisend(mpos, msg1, msg23);
        ); // end if received
    ); // end while
    // now we create the smoothing data, looping through the sample buffer
    currsamp = 0;
    currcont = 0; //pointer into controller array
    while (currsamp < samplesblock) (
        while (currcont < arraypos && currsamp == positions[currcont]) ( // we have a valid controller at the current sample position
            (channels[currcont]==1)? ( // hard control
                targval = values[currcont];
                currval = targval;
                elapsed = 0;
                midisend(currsamp,176,ccsrc, currval);
            ) : ( // not a hard control
                targval = values[currcont];
                (targval >= currval)? increment = stepsize : increment = -stepsize;
            currcont += 1;
        ); // loop through valid controllers at current position
        // now compare currval with target and step towards it if necessary and right time
        (currval != targval && elapsed >= interval) ? ( // we need to send a new value now
            newval = currval + increment;
            // check for overshoots here
            (increment < 0) ? ( // stepping down
                newval < targval ? newval = targval;
            ) : ( //stepping up
                newval > targval ? newval = targval;
            currval = newval;
            // now send new value and reset elapsed
            midisend(currsamp,176,ccsrc, currval);
            elapsed = 0;
        currsamp += 1;
        elapsed += 1;
    ); // loop thru samples 

1 Like

I developed a (free) plugin that does just that (and more). Check out Topiary Presetz - you will want to make sure that whatever triggers your state change, triggers a Variation change in Presetz. Presetz can then ‘gradually’ fade up to 16 virtual sliders (which you can assign to any CC) from one level to another. You can even link the speed of the transition to cantabile’s BPM (as in, take a beat, a measure, whatever) :slight_smile:
Feel free to DM me if you want more info.


Sounds interesting! I’ll have to take a look at this - certainly a super-valuable tool to automate setups!

1 Like

Sounds reasonable I think but will need to look into it.

So this would apply when a gain setting changes in response to a state change?




1 Like

Plus, for my second half of the request: Let’s say I have a binding that sends CC values to a plugin or changes a VST parameter on song state changes (e.g. automates the filter cutoff of a synth). It would be nice to have a similar smoothing option for such bindings, so that the filter cutoff doesn’t jump from one value to the other abruptly, but fades smoothly up or down when the state changes.

Not sure about the complexity of that one, though, since with a “target” state behavior, a whole lot can change, not only the CC value or VST parameter value… So this might be more difficult to implement, with a lot more potential to go wrong… Maybe better to start out simple with smoothing gain levels :wink:



1 Like

Thanks a lot Torsten, I’ll try in the next days and update you about results. Marco

Hi Tom, sounds great :smiley:

Understood. Yep, it could get complex. Leave it with me and I’ll see what I can work out…

1 Like

Yeah, but don’t spend too much time on that one - there are workarounds for this (@dave_dore’s fader rack, @Tom_Tollenaere’s plugin, or my ReaJS script).

The gain slider smoothing should be easier and more controllable, so that would be my priority.

1 Like

Hi Tom,

do I understand this correctly: Presetz will only do smooth transitions when transport is running? If so, it’s a pity - I don’t want to need to have transport running - usually, I use the current VST tempo alone, which is enough for most time-based effects like delays etc.

But I like the idea of having tempo-based transitions with defined lengths like bar, quarter or half note - guess I’ll build an additional version of my smoothing script :wink: - I’ll build it on the VST tempo, though, no need to have the transport running for that…



1 Like

Torsten, not really: if you tick the box “Override host” then you don’t need the Cantabile transport to run. In that case you set the BPM in Presetz (and it will ignore the tempo Cantabile is in).

Oh @Torsten maybe the above still won’t address your need - with the above, you’d still have to “start” the plugin transport (be it independent from the Cantabile transport). I can change behavior such that even when the plugin (or host) is NOT running transport, it still does the transition according to the dropdown box for Transition Time (and according to the BPM set in the plugin transport). If that is what you need, let me know and I’ll program that over the weekend.

1 Like

Hmmm, could Presetz still use the bpm tempo from Cantabile for transition time calculation, even when transport is not running? Host tempo should be available to plugins, independent of transport status.

That would be my preferred approach: set the transition time in beats/ bars, get the tempo from Cantabile, program the variations (8 should definitely be enough for most songs) and use them to automatically fade layers in or out, move filter cutoff or organ expression pedals per song section. Currently, I use bindings that send CC values to my instrument racks, combined with the smoothing script within the racks, but having it all centrally in one spot would make things a lot easier to manage.



Yes, that is possible. If you do NOT override the host transport, Presetz stays in sync with Cantabile’s BPM settings. If you override you have to set BPM yourself.

I’ll ping back when that change has been done - I do have some spare time this weekend :slight_smile:

1 Like

OK, a new version is available that does what Torsten wanted. The manual has been updated to reflect the changes. For technical reasons (don’t ask) I had to change the product code, so if you have an old version in a Cantabile file, you’ll see it but it won’t work and the editor won’t open. Just start fresh I would suggest :slight_smile:

(I have the plugin in dozens of Cantabile songs and didn’t feel like pathing all that so for now I keep the old version side by side with the new one (which you can do - just give the .dll a different name and Cantabile will sort it out!)

Changes have been tested, but my personal use cases are different, so it’s not impossible there are still bugs or unexpected behaviors (the manual does warn for seemingly-unexpected-but-perfectly-normal behavior :slight_smile: ).

Download here -
Manual here -

Reactions welcome!


Hi Tom,

first: thanx for implementing this so quickly - very nice!!

It looks like everything is working nicely - just two things I am noticing:

  • the “stepping” of the transitions seems a bit rough - if you have a transition from 0 to 127 over the course of a measure, you have the time to go for more than 10 intermediary values, don’t you? Not sure what logic you are using to determine the number of steps, but it could take the transition time into account.
  • when “instant” is selected, value smoothing is still applied (just in a very fast transition) - not sure if this is necessary - could just be a simple stepped value change

These are just the things I found when playing around with the new version for a bit - will report back once I’ve tried using it in earnest :wink:



Glad to be of assistance.

  1. Stepping of transitions I can increase. Right now I think it’s 10 steps but that was pretty arbitrary - I’ll up that tonight.
  2. Instant does indeed smooth and that is intentional and I won’t change that. Reason is that the signal Presetz sends out can be used to literally anything, not just volumes and I have had a case where it cause crackling. FYI “instant” is 1/64 of a beat so should be plenty fast :slight_smile:
1 Like