What's new

How can I get smooth midi CC automation in Reaper ?

I'm not fluent in Reaper's JSFX coding, but I think CCEnv should be written as follows (corrected line in red):

desc:CCEnv
// ReaControlMIDI replacement for continuous controllers
// Author: Panu Aaltio

slider1:0<0,127,1>#1 Mod Wheel
slider2:0<0,127,1>#11 Expression

@init

sentCC[11] = 0;
sentCC[1] = 0;

previousCC[11] = -1;

_global.ccBuffer_11 = -1;
_global.ccBuffer_01 = -1;

COOLDOWN_TIMER = 64;
cooldown = 0;

channel = 0;

@block

_global.ccBuffer_11 > -1 ? (
midisend(0, 11*16 + channel, 11 + _global.ccBuffer_11 * 256);

slider2 = _global.ccBuffer_11;
sentCC[11] = _global.ccBuffer_11;

cooldown = COOLDOWN_TIMER;

slider_automate(slider2);
previousCC[11] = _global.ccBuffer_11;
_global.ccBuffer_11 = -1;

);

_global.ccBuffer_01 > -1 ? (
midisend(0, 11*16 + channel, 1 + _global.ccBuffer_01 * 256);

slider1 = _global.ccBuffer_01;
sentCC[1] = _global.ccBuffer_01;

cooldown = COOLDOWN_TIMER;

slider_automate(slider1);
_global.ccBuffer_01 = -1;
);

cooldown > 0 ? (
cooldown = cooldown - 1;

// If still ignoring envelope data, make sure old envelope doesn't come through
previousCC[11] > -1 && previousCC[11] != slider2 ? (
slider2 = previousCC[11];
slider_automate(slider2);
);
previousCC[1] > -1 && previousCC[1] != slider1 ? (
slider1 = previousCC[1];
slider_automate(slider1);
);
);

sentCC[11] != slider2 ? (
sentCC[11] = slider2;
cooldown == 0 ? (
midisend(0, 11*16 + channel, 11 + sentCC[11] * 256);
);
);

sentCC[1] != slider1 ? (
sentCC[1] = slider1;
cooldown == 0 ? (
midisend(0, 11*16 + channel, 1 + sentCC[1] * 256);
);
);


Previously, that line contained 11 + _global.ccBuffer_01 etc. -- I believe that determines the CC number for output. Looks like a copy/paste error on the author's part.
 
I'm not fluent in Reaper's JSFX coding, but I think CCEnv should be written as follows (corrected line in red):


_global.ccBuffer_01 > -1 ? (
midisend(0, 11*16 + channel, 1 + _global.ccBuffer_01 * 256);

Previously, that line contained 11 + _global.ccBuffer_01 etc. -- I believe that determines the CC number for output. Looks like a copy/paste error on the author's part.

*Sounds of the Hallelujah Chorus fanfare* :) Yes, that was it! I scanned that code for hours looking for just such a typo and I couldn't see it for trying.

Thank you & @EvilDragon *so* much for all your help guys. Both CC1 & CC11 faders now writing directly to envelopes and not to the Piano roll. I can now insert these plugs into my template knowing they'll just work. I think this is the nearest we can get to a native implementation of this in Reaper so it will be useful for a lot of people (like me) who can't code one themselves.

If it's OK with you I'll update that thread over at the Reaper forum later today and put a comment on the original upload at stash.reaper.com so people can do the quick fix.

Looking forward to a productive day getting these in the monster template now... :) Cheers!!
 
Looking forward to a productive day getting these in the monster template now... :) Cheers!!
Been playing around with this and noticed the CCEnv_Input script cuts off all CC64 input as well. Any way around losing the sustain pedal?
 
Been playing around with this and noticed the CCEnv_Input script cuts off all CC64 input as well. Any way around losing the sustain pedal?
Bear in mind that I'm not running the script to check functionality right now -- just quickly looking at the code -- but the scripts are set up to catch CC1 and CC11 specifically, so anything else gets ignored. Modifying the code to catch CC64 as well should be a simple copy/paste affair, so here we go:

The Input FX plugin (CCenv_input)

desc:CCEnv Input
// Companion plugin for CCEnv, this one should be placed in the Input FX on the same channel.

@init
_global.ccBuffer_64 = -1;
_global.ccBuffer_11 = -1;
_global.ccBuffer_01 = -1;

@block

while (
midirecv(mpos, msg1, msg23) ? (

status = msg1;
statusHi = (msg1 / 16) | 0;
statusLo = msg1 - (statusHi * 16);

statusHi == 11 ? ( // CC
ccValue = (msg23/256)&$xff;
ccNum = msg23&$xff;
ccBuffer[ccNum] = ccValue;

ccNum == 64 ? _global.ccBuffer_64 = ccValue;
ccNum == 11 ? _global.ccBuffer_11 = ccValue;
ccNum == 1 ? _global.ccBuffer_01 = ccValue;
) : (
midisend(mpos, msg1, msg23);
);
);
);

The regular FX plugin (inserted in as the first FX on a track) (CCenv)

desc:CCEnv
// ReaControlMIDI replacement for continuous controllers
// Author: Panu Aaltio

slider1:0<0,127,1>#1 Mod Wheel
slider2:0<0,127,1>#11 Expression
slider3:0<0,127,1>#64 Sustain Pedal

@init

sentCC[64] = 0;
sentCC[11] = 0;
sentCC[1] = 0;

previousCC[64] = -1;
previousCC[11] = -1;
previousCC[1] = -1;

_global.ccBuffer_64 = -1;
_global.ccBuffer_11 = -1;
_global.ccBuffer_01 = -1;

COOLDOWN_TIMER = 64;
cooldown = 0;

channel = 0;

@block

_global.ccBuffer_64 > -1 ? (
midisend(0, 11*16 + channel, 64 + _global.ccBuffer_64 * 256);

slider3 = _global.ccBuffer_64;
sentCC[64] = _global.ccBuffer_64;

cooldown = COOLDOWN_TIMER;

slider_automate(slider3);
previousCC[64] = _global.ccBuffer_64;
_global.ccBuffer_64 = -1;
);

_global.ccBuffer_11 > -1 ? (
midisend(0, 11*16 + channel, 11 + _global.ccBuffer_11 * 256);

slider2 = _global.ccBuffer_11;
sentCC[11] = _global.ccBuffer_11;

cooldown = COOLDOWN_TIMER;

slider_automate(slider2);
previousCC[11] = _global.ccBuffer_11;
_global.ccBuffer_11 = -1;
);

_global.ccBuffer_01 > -1 ? (
midisend(0, 11*16 + channel, 1 + _global.ccBuffer_01 * 256);


slider1 = _global.ccBuffer_01;
sentCC[1] = _global.ccBuffer_01;

cooldown = COOLDOWN_TIMER;

slider_automate(slider1);
previousCC[1] = _global.ccBuffer_01;
_global.ccBuffer_01 = -1;
);

cooldown > 0 ? (
cooldown = cooldown - 1;

// If still ignoring envelope data, make sure old envelope doesn't come through
previousCC[64] > -1 && previousCC[64] != slider3 ? (
slider3 = previousCC[64];
slider_automate(slider3);
);
previousCC[11] > -1 && previousCC[11] != slider2 ? (
slider2 = previousCC[11];
slider_automate(slider2);
);
previousCC[1] > -1 && previousCC[1] != slider1 ? (
slider1 = previousCC[1];
slider_automate(slider1);
);
);

sentCC[64] != slider3 ? (
sentCC[64] = slider3;
cooldown == 0 ? (
midisend(0, 11*16 + channel, 64 + sentCC[11] * 256);
);
);

sentCC[11] != slider2 ? (
sentCC[11] = slider2;
cooldown == 0 ? (
midisend(0, 11*16 + channel, 11 + sentCC[11] * 256);
);
);

sentCC[1] != slider1 ? (
sentCC[1] = slider1;
cooldown == 0 ? (
midisend(0, 11*16 + channel, 1 + sentCC[1] * 256);
);
);

Again, I haven't actually run the modified code, just done the copy/pasting, so let me know if it doesn't work and I'll dig into it and properly bugfix it. In principle, it should be possible to create a more generalized code structure that would let the execution iterate through an arbitrary list of CC numbers without needing all this copy/paste nonsense to add new ones, but it would require substantial rewriting that I'm not inclined to attempt.

EDIT:
I may have misunderstood -- looks like the input script totally kills all CC data except what it passes to the envelope writer. My fix should put CC64 as an envelope, too, but if you'd rather just pass CC64 through as normal MIDI data, that shouldn't be too hard to set up, so let me know if that's the case.
 
Last edited:
For some reason the default ControlMIDI plugin loads my CPU and I have clicks and other artifacts.. Everything is ok when I draw in MIDI editor (sometimes I just hate Reaper). I'll try this one. Thanks.
So we have to use these two plugins together or what? I just do not quite understand.
 
I may have misunderstood -- looks like the input script totally kills all CC data except what it passes to the envelope writer. My fix should put CC64 as an envelope, too, but if you'd rather just pass CC64 through as normal MIDI data, that shouldn't be too hard to set up, so let me know if that's the case.
The CC64 data records, but doesn't work on playback. Interesting. Might be better to make CC64 a pass through. But I'm an idiot when it comes to coding.
 
P.s. Checked it and It works perfectly without any spikes. Nice to know! I don't record a CC data frequently, so it doesn't matter, but the plugin overall is very helpful and works better than the original one. Thank you very much!
And answering to my personal question: yes, these plugins should be used together. CCenv_input first, then CCenv.
 
For some reason the default ControlMIDI plugin loads my CPU and I have clicks and other artifacts
I have problems with ControlMIDI as well. For some reason it doesn't seem to like my modwheel. CCEnv doesn't have that problem.
 
For some reason the default ControlMIDI plugin loads my CPU and I have clicks and other artifacts.

That is weird. I mean... RCM is a plugin like any other, but all it does is just MIDI processing, this is absolutely not CPU intensive and I'm not sure how it would cause artifacts of any sorts...
 
@EvilDragon yes, you're absolutely right. That's weird as hell. But Reaper and MIDI are not friends. Try somehow to compose a very big project with a LOT of MIDI tracks. A project can sound perfectly in the main Reaper window, but if you'll open MIDI editor, everything will start to crackle. And the more tracks you can see in the editor, the worse the CPU load will be. It just how it works, you know? Reaper is very buggy sometimes.
 
That's actually not a bug, it's a matter of one setting. Preferences->Audio->Buffering->[x] Anticipative FX Processing->[ ] Allow on tracks with open MIDI editors (will increase MIDI preview latency)

If you have that last checkbox disabled, that means all the tracks that are shown in the MIDI editor as visible and/or editable (AND their associated sends, if any!) get pushed onto the main audio thread for processing, slamming that core. This is in order to provide instantaneous MIDI preview as you input the notes in. If you enable this checkbox, there will be latency when inputting new notes, but you won't have any crackles. It's a tradeoff.
 
So I'm now actually running the scripts instead of just looking at them, and I've noticed that CCenv_input is sporadically passing bits of MIDI data through even though it shouldn't, but I see how to fix it. (Input processing should be happening at the sample rate instead of every block.) I'll post a revision after I see if anything else needs fixing.
 
Major overhaul of the entire thing.

CCEnv Input now has a GUI that lets you select what CCs you want to write as envelopes. Unselected CCs will be passed through as regular MIDI data. All instances of CCEnv Input will sync their CC selection state to each other. Put instances of CCEnv Input on all tracks that need them, then just leave one of the instances open to control all of them. CCEnv Input's selection state saves with the Reaper project.

CCEnv is now split into two parts to accommodate all 128 CCs (because JSFX plugins can only have 64 sliders). The sliders on the plugin screen may go off the screen on smaller monitors. I could fix this by splitting it into three or four instead of two if that's an issue for people, but I figure seeing the sliders isn't that big a deal since it's the automation lanes that are actually important.

A possible change in the future would be to allow you to decouple the instance syncing so you can choose to set/save different input configurations for different tracks.

I've not bug tested all of this too thoroughly, but it should be functional.
 

Attachments

  • pmcrockett - CCenv.zip
    4.3 KB · Views: 53
Major overhaul of the entire thing.

CCEnv Input now has a GUI that lets you select what CCs you want to write as envelopes. Unselected CCs will be passed through as regular MIDI data. All instances of CCEnv Input will sync their CC selection state to each other. Put instances of CCEnv Input on all tracks that need them, then just leave one of the instances open to control all of them. CCEnv Input's selection state saves with the Reaper project.

CCEnv is now split into two parts to accommodate all 128 CCs (because JSFX plugins can only have 64 sliders). The sliders on the plugin screen may go off the screen on smaller monitors. I could fix this by splitting it into three or four instead of two if that's an issue for people, but I figure seeing the sliders isn't that big a deal since it's the automation lanes that are actually important.

A possible change in the future would be to allow you to decouple the instance syncing so you can choose to set/save different input configurations for different tracks.

I've not bug tested all of this too thoroughly, but it should be functional.
WOW. Looking forward to checking this out. Thanks, man!
 
Top Bottom