Morph between states?

I know you can morph between plugin presets, but I don’t believe there is there a way to morph between states, is there?

Often, my state changes consist of nothing more than plugin or rack gain changes. What I would like to be able to do in certain situations is have these gain changes be gradual instead of instantaneous. For example, have a pad sound fade in from 0 to full volume over 2 bars, or maybe 10 seconds. That way, I can press a pedal to trigger the state change, then have the pad fade in without me needing to take a hand off the keyboard.

I know there are some other ways to achieve this, but ideally, I would like it to be a state change so I can use the same trigger (my pedal) that I use for other state changes. Should I submit this as a feature request?

This has been on my extra-special wish list for a while but I’ve not suggested it as I have a feeling it’s a big undertaking for Brad.
And like you, Tom, the most useful thing is for it to handle gain changes.

As Tom said in his opening post, there are actually other ways of achieving this now.
It’s not, strictly speaking, a ‘morph’ - more of a crossfade.
A couple of midi files - one for up, one for down, tempo adjustable from Cantabile, could do an acceptable job, with bindings to the required destinations.

Thanks for the comments, Neil and Ade. The midi file approach was one thing I was consideriAre.

I know that in programming, nothing is ever as simple as you think it’s going to be, but given the fact that Brad already has code to morph from one plugin preset to another, I don’t think this would be terribly difficult. The only thing left is the variable mix parameters within Cantabile itself. This is mostly gain and pan.

Hey Tom,
Whether it’s integrated or achieved in the way I’ve suggested, there’s always parameters to tweak - that’s why I think you may be surprised at how simply this could be achieved with existing methodology.
But to be absolutely clear, what you’re after is a way of defining the transition time of a given rack or plugin from its current fader/pan setting to that of the next called state?

what you’re after is a way of defining the transition time of a given rack or plugin from its current fader/pan setting to that of the next called state?

You are correct.

I wondered if this could be achieved using a MIDI Plugin that tracks selected MIDI controller messages, and when they change, it emits controller messages to smoothly “glide” up/down to the new value, at some predefined rate. I thought something like that surely must already exist, and checked the Piz MIDI plugins etc., but so far I haven’t found one…

Check midiConverter3 in PizMIDI:

  • set its input to CC 1
  • leave input and output ranges as they are
  • set output to CC 1
  • set Param2 to Inertia: 100

Now midiConverter will smooth out any sudden parameter jumps. Unfortunately the “smoothing” time is still pretty short; no way to set it to, say, 2 seconds or so. It does help avoid nasty jumps in volume, though.

If I find some time to explore the VST SDK, I may take a stab at building a simple “smoother” plugin…

Cheers,

Torsten

1 Like

I’ve used media player to fade in (for example) string pads. Using Reaper, I created a MIDI file of control 7 moving from 0 to 90 over 2 measures. State change triggers the media player. This worked, but it was cumbersome. I abandoned it for a volume pedal. It would be nice to be built-in as a crossfade function.

1 Like

I finally got around to building a script for ReaJS to do exactly this - it allows you to select a controller, then tracks it and smoothes it out. It has three parameters:

  • Controller - this is the CC number to be smoothed
  • ms per step - milliseconds between each cc increment / decrement
  • step increment - how much will the current value be incremented / decremented per step

It will smooth all CC values that come in on MIDI channel 1; send CC values on MIDI channel 2 to send “hard” CCs (no smooting). All output will be redirected to MIDI channel 1.

When loaded, the plugin will initialize the current controller value to zero, so to avoid any nasty jumps when using for the first time, you should initialize it by sending your starting value before playing anything (Song->OnLoad or SongState->OnLoad)

I use it to smoothly fade some rack volumes from my current position (wherever I have currently set it via a MIDI fader) to a fixed state at the next song state: I have one binding from my controller to the CC Smoother rack and another from SongState->OnLoad. So wherever I have moved the volume level in the previous song state, it will move smoothly to the pre-defined initial value for the next song state.

The script is a quick, sloppy one I just created in a couple of hours, but it should do the trick. Simply create a new MIDI effect file in ReaJS, then paste the script below there and give it a try. You should be able to create all kinds of crazy crossfades with it…

Have fun!

Cheers,

Torsten

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

@init

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

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

stepsize = slider3;
ccsrc = slider1;

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


@block
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;
                arraypos+=1;
            ) : ( // 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 

 );
7 Likes

Wow, nice work, Torsten. I will definitely give this a shot. I didn’t even know about ReaJS.

Yeah, the whole ReaPlugs bundle is a bit of a hidden champion. I love the ReaDelay - add as many delay taps as you like! It’s lacking ping-pong-delay and modulation, though…

ReaJS is great for small MIDI transformations that go beyond Cantabile’s built-in features - anything that transforms notes or controllers is super-easy to build in JS, e.g. modifying note velocity based on a controller value is just a few lines in ReaJS.

The problem with controller smoothing is that it ReaJS works in audio buffer chunks, but controller smoothing needs to work across buffers and capture and send controller changes at the right time. That’s what made this script a bit convoluted and complicated.

Next on the list is a MIDI jump prevention script - Cantabile’s jump prevention only works with plugin parameters, but sometimes I need jump prevention on simple MIDI volume or modulations routes. That should be far easier in ReaJS :wink:

Cheers,

Torsten

2 Likes

Torsten…What a guy!

3 Likes

I will 2nd that Adrian !!

2 Likes

Torsten - you’re the man!

I just tried this, and it works like a charm.

I sometimes have “dovetails” between sections, where it would be really nice for a chord in one instrument to fade gracefully as another starts playing, but where I don’t have a hand free to work a slider or a foot free to work my volume pedal. (Feet are often busy with footswitches for next song state, Leslie speed, etc, and the sustain pedal.) I was resigned to having these overlaps cruder than I’d like, but your script solves the problem.

Many thanks.

1 Like

Yes, it’s welcome to ‘bump that thread’! A mere 6.5 years after its first appearance this has piqued my interest following my first forays into using Parameter Sets.
preset model
This is really a gem of a feature. We’re all aware of the thumps, bumps and cutoffs which can occur when issuing patch changes to plugins.
The beauty of using Parameter Sets as opposed to using the plugin’s programs, or even Entire Plugin Snapshots which can reset vital parameters, is that Parameter Sets are like a myriad of little fingers reaching in and putting all the knobs and sliders into the desired place, without actually changing the synth’s patch, as long as the parameters have been made available to automation. Each Cantabile State becomes the repository for all these parameters, which are transmitted outside of the MIDI world and don’t clog any pathways. (At least that’s what I’m seeing.)
This opens up a creative use of morphing which would not be so attractive were it based on MIDI.
You would need to make all the assignments, which is going to be a mind numbing task for each synth, whereas using Parameter Sets has all that work done for you, as long the plugin manufacturer has put the work in to making their panel functions automatable.

How this ties into morphing should be pretty obvious by now. We have a (with some obvious exceptions) glitch free way of moving a synth from one state to another, and it seems not unreasonable to consider how those changes could be made over time, all the way up to the lightning fast default we now have. Nearly every function on Cherry’s synths are (so far) immune to choking using this approach.
Omnisphere doesn’t provide automation. Diva does.
It becomes clear which kind of plugins are ‘morph friendly’ and which parameters are likely to cause thunks.
So…
Why not take 10 seconds to move any parameters which have altered between two states?
Synth buttons shift at whatever value the plugin author sets for the swap of their condition. We don’t really care.
Provide curves.
Allow that time to be bindable to a controller.

Anything you could do manually you could now do with a Cantabile state. Filters, envelopes, LFOs, waveforms, all moving seamlessly over a user defined time to new values… with minimal effort to the user.
Suddenly synths are doing things even their authors could not achieve.
Any takers?

2 Likes