Jump to content

MIDI Velocity Curve Generator


keni
 Share

Recommended Posts

Hello!

 

I created a tool that lets you create custom MIDI velocity curves that you can load in Scripter. The tool is browser-based, and is available here:

https://sumire-io.gitlab.io/midi-velocity-curve-generator/

 

You can move the points to generate a velocity curve that suits your playing style (double click to add or remove points). The Scripter JavaScript code is generated in real-time in the editor below. The first line of the code (comment) is the curve data in JSON format. You can paste this JSON string into the input field to load an existing velocity curve in the curve editor.

 

I created this tool because my Yamaha keyboard has a very weird velocity curve (maximum velocity is about 110). This has caused problems with sampled piano libraries, because I cannot reach any velocities over 110 when I'm playing. I'm aware of the Velocity Processor MIDI Plugin in Logic, but it can only do very basic velocity manipulation in my opinion.

Link to comment
Share on other sites

keni, VERY cool! I'd like to hear more about how you setup the webpage on gitlab. Clever way to provide a UI, I like it! It makes me think I'd like to use a similar approach for some other Scripter things i have that badly need a better UI.

OSX 12.x (Monterey) on OpenCore - Logic Pro 10.7.4, VePro7, Mainstage3 - 5,1 MacPro 3.46ghz x 12 96gb ram

Link to comment
Share on other sites

keni, VERY cool! I'd like to hear more about how you setup the webpage on gitlab. Clever way to provide a UI, I like it! It makes me think I'd like to use a similar approach for some other Scripter things i have that badly need a better UI.

 

Thank you very much. The webpage is a git repository on Gitlab. The source code repository is available here: https://gitlab.com/sumire-io/midi-velocity-curve-generator.

 

You can use Gitlab Pages to host static HTML pages on Gitlab. Here is a step-by-step tutorial: https://about.gitlab.com/2016/04/07/gitlab-pages-setup/.

If your repository consists only of a static HTML site, like in my case, it's very simple. You just need to create one file in the root folder, as instructed in the tutorial (Option A section). Github has a similar Github Pages feature as well, if you prefer Github. https://help.github.com/en/articles/what-is-github-pages

Link to comment
Share on other sites

  • 1 year later...

Brilliant- this helped me out a lot with my old controller!

 

Still wish there was a simple velocity mapper that could be placed in the MIDI fx slot for on the fly adjustment ;-)

MacBookPro11,3 - i7 - 2,6 GHz - 16GB

macOS 10.14.6 (Mojave)

RME FF UCX

LPX 10.5.1

Link to comment
Share on other sites

Still wish there was a simple velocity mapper that could be placed in the MIDI fx slot for on the fly adjustment ;-)

Add a Modulator and set to Volume

Modulator.thumb.png.cecd32d21d92070627ba52a51c0bdd08.png

MacBook Pro 10.8.5 2.2 GHz Intel Core 2 Duo 6GB Ram - Logic Pro X (10.2) - MacMini 10.13.6 2GHz Intel Core i7 16GB Ram - GarageBand 10.4.5 Logic Pro X (10.4.8) - iPad Mini iOS 12 - iOS GarageBand 2.0.1 - Qosimo X70-A 10.13.6 Intel® Core™ i7-4700MQ Processor 32GB DDR3L 1600MHz memory, 2-500GB 7200rpm hard drives - Logic Pro X (10.4.8) - MacMini M1 11.6.1 Apple M1 16GB Ram 1TB SSD Logic Pro X (10.7.1) Rosetta 2 not installed

Link to comment
Share on other sites

I'm not very versed in MIDI, so good chance that I don't get it... but the LFO controlling the volume is something different from just applying a fixed factor to a velocity value based on a table/function/factor.

MacBookPro11,3 - i7 - 2,6 GHz - 16GB

macOS 10.14.6 (Mojave)

RME FF UCX

LPX 10.5.1

Link to comment
Share on other sites

I'm not very versed in MIDI, so good chance that I don't get it... but the LFO controlling the volume is something different from just applying a fixed factor to a velocity value based on a table/function/factor.

The Modulator doesn't just do a curve, but the patterns can be anything.

Here's just a few but it's up to. you to create ANY type of pattern as well as following a RATE.

6.thumb.png.da4ce894d2b891105fe50a477f80d7a2.png5.thumb.png.f56745873fa121cce745c044f25ffbb4.png4.thumb.png.a152d8a530c075937fb8993f0896e7f8.png3.thumb.png.055b9492d1ecbec94b0b7dca4f99a9b4.png2.thumb.png.3e2c90418a127b34341c8615d486922a.png1.thumb.png.136852e95f0d9a8069f519cefcf25185.png

 

The other thing I'll mention as well is, put this on a External Instrument track using IAC and you'll be able to capture the pattern and then you can further modify this as well.

MacBook Pro 10.8.5 2.2 GHz Intel Core 2 Duo 6GB Ram - Logic Pro X (10.2) - MacMini 10.13.6 2GHz Intel Core i7 16GB Ram - GarageBand 10.4.5 Logic Pro X (10.4.8) - iPad Mini iOS 12 - iOS GarageBand 2.0.1 - Qosimo X70-A 10.13.6 Intel® Core™ i7-4700MQ Processor 32GB DDR3L 1600MHz memory, 2-500GB 7200rpm hard drives - Logic Pro X (10.4.8) - MacMini M1 11.6.1 Apple M1 16GB Ram 1TB SSD Logic Pro X (10.7.1) Rosetta 2 not installed

Link to comment
Share on other sites

The environment is the nest place to do this with a transformer map, so that the velocity adjusted midi will be recorded to track regions.

 

Otherwise the above website generates a script you can tweak slightly to use in scripter for velocity. If those don’t work I have a velocity curve script I shared in the midifx sub forum a few years ago that should work for you.

OSX 12.x (Monterey) on OpenCore - Logic Pro 10.7.4, VePro7, Mainstage3 - 5,1 MacPro 3.46ghz x 12 96gb ram

Link to comment
Share on other sites

Using the above works fine.

Still I would love to find a MIDI fx plugin with similar functionality to the Velocity Editor in Pianoteq.

What's nice about that: curve can have any shape, visual feedback while playing/creating curves, easier to use than environment transformer.

In the meantime I can get by with the script fed by the tool above.

Ultimately I'll have to get a better masterkeyboard at some point ;-)

MacBookPro11,3 - i7 - 2,6 GHz - 16GB

macOS 10.14.6 (Mojave)

RME FF UCX

LPX 10.5.1

Link to comment
Share on other sites

I have looked many times and unfortunately there isn’t a dedicated velocity curve midifx plugin for Mac out there.

I was questioning my google-skills because of this ;-)

Seems to me this is such a frequent use-case... curious that such a plugin does not exist

Maybe a business opportunity for some smart people here on the forum ;-)

MacBookPro11,3 - i7 - 2,6 GHz - 16GB

macOS 10.14.6 (Mojave)

RME FF UCX

LPX 10.5.1

Link to comment
Share on other sites

It would not be a hard one to do in JUCE maybe I will eventually.

 

here is a script I made a few years ago that might help you since its a little more interactive, you can slide a slider to dial in an exponential curve that works, you just can't really see what the curve looks like... Well I think there is a way to have it display some kind of textual curve on the Scripter window if you hit a button or something, I can't remember now, but it works, last time I checked which was a few years ago.

 

NOTE - looks like I didn't include velocity in this version, but I can add that later, I am running out the door right now, or you can, it would not be a hard add.

 

//
// EventGamma
// Version 0.4
//
// This script will provide a non-linear accleration to CC, Pitchbend and 
// aftertouch midi events.  The factor value establishes the amount of curve
// above or below the linear line.  A factor of zero will be the linear line
// 
// TODO, generate a lookup table to avoid using math during PLAY

var NeedsTimingInfo = true;
var PluginParameters = [];

function HandleMIDI(event) {

   var gamma = GuiParameter(0);
   
   // if the gamma is 1 then just forward the event.
   if (gamma==1) {
       event.send();
       return;
   }
   
   // if CC events are being included, then range is 0-127
   if (event instanceof ControlChange && GuiParameter(3) == 1) {
       event.value = scale(event.value, 127, gamma);
   }
   
   // if Pitch Bend events, the range is -8291 to 8192
   else if (event instanceof PitchBend && GuiParameter(4) == 1) {
       
       // if event.value is not zero, then accelerate it.  use offset to 
       // force positive range 
   	    if (event.value!=0) {
   	        const OFFSET = 8192;
           var temp = event.value+OFFSET;
           var maxPitch = OFFSET+OFFSET;
           event.value = scale(event.value, maxPitch, gamma)-OFFSET;
       }
   }
   
   // if aftertouch, then range is 0-127
   else if (event instanceof ChannelPressure && GuiParameter(5) == 1) {
       event.value = scale(event.value, 127, gamma);
   }
   else if (event instanceof PolyPressure && GuiParameter(6) == 1) {
       event.value = scale(event.value, 127, gamma);
   }
   
   // send event
   event.send();
}

//============================================
// couple of globals related to the graphing
//============================================

var gGraphFlush = false;
var gGraph = [];
const MAXFLUSH = 10;

//======================================================
// Scales the value based on gamma and range
//
function scale(value, range, gamma) {

   // off means off, no matter what.  Should we do this?
   if(value == 0) {
       return 0;
   }
   
   // calculate scaled value
   var out = Math.round(range * Math.pow((value/range),gamma)); 
   // for incoming value over 1, always scale to at least 1
   if(out == 0) {
       out = 1;
   }

   return out;
}

//===================================================================
// function that will send to console a graph if the selected curve
//
function plotCurve(gamma) {
   
   // if flush in progress then ignore this
   if (gGraphFlush) {
        return;
   }
   
   var graph = new Array(64);
   
   for (var x=0;x<64;x++) {
       graph[x] = new Array(32);
       for (var y=0;y<32;y++) {        
           graph[x][y] = 0;
       }
   }
       
   // calculate Y values, cut X size in half and cut Y value in half again to shrink height of graph
   for (var x=0;x<64;x++) {
       var y = scale(x*2, 127, gamma)/4;
       graph[x][Math.round(y)] = 1;
   }
   
   // build output strings, 
    for(var y=31;y>=0;y--) {
       var str = "|";
       for (var x=0;x<64;x++) {

           if (graph[x][y] == 1) {
               str = str + "x";
           }
           else if (x/2==y) {
               str = str + ".";
           }
           else {
               str = str + " ";
           }
       }
       gGraph.push(str);
   }

   // add x axis
   var xaxis = "+";
   for(var x=0;x<64;x++) {
       xaxis = xaxis + "-";
   }
   gGraph.push(xaxis);
   
   gGraphFlush = true;
}

//=============================================
//  handle when they hit the show graph button
//
function ParameterChanged(idx, val) {

   PluginParameters[idx].data = val;
   
   if (idx == 1) {
       plotCurve(GuiParameter(0));
   }
}


//=========================================================
// use Idle() function to flush graph to console
//
function Idle() {

   if(gGraphFlush) {
       
       // Trace("printing graph");
       
       var i = 0;
       while( gGraph.length > 0 && i < MAXFLUSH) {
           Trace(gGraph.shift());
           i++
       }
       if (gGraph.length <= 0) {
           gGraphFlush = false;
       }
   }
}

PluginParameters.push({
   name: "Gamma",
   type: "log",
   defaultValue: 1,
   minValue: .1,
   maxValue: 7.9,
   numberOfSteps: 100,
   hidden: false,
   disableAutomation: false
});

PluginParameters.push({
   name: "Show Graph",
   type: "momentary",
   hidden: false,
   disableAutomation: true
});

PluginParameters.push({
   name: "-------- Filter -------",
   type: "text",
   hidden: false,
   disableAutomation: true
});

PluginParameters.push({
   name: "Control Change",
   type: "checkbox",
   defaultValue: 1,
   hidden: false,
   disableAutomation: true
});

PluginParameters.push({
   name: "Pitch Bend",
   type: "checkbox",
   defaultValue: 0,
   hidden: false,
   disableAutomation: true
});

PluginParameters.push({
   name: "Channel Pressure",
   type: "checkbox",
   defaultValue: 0,
   hidden: false,
   disableAutomation: true
});

PluginParameters.push({
   name: "Poly Pressure",
   type: "checkbox",
   defaultValue: 0,
   hidden: false,
   disableAutomation: true
});

function GuiParameter(id) {
   // if script was recently initialized, reload GUI value
   if(PluginParameters[id].data == undefined) {
       PluginParameters[id].data = GetParameter(id);
   }
   if(id < PluginParameters.length) {
       return PluginParameters[id].data;
   }
}

function Reset() {
}

Reset();
Edited by Dewdman42

OSX 12.x (Monterey) on OpenCore - Logic Pro 10.7.4, VePro7, Mainstage3 - 5,1 MacPro 3.46ghz x 12 96gb ram

Link to comment
Share on other sites

And here is a much much much simpler script I did a long time ago, along similar lines, there is no GUI, but you could add a slider easily enough to make it interactive to dial it in

 

viewtopic.php?f=45&t=132971&p=678514&hilit=gamma#p678514

OSX 12.x (Monterey) on OpenCore - Logic Pro 10.7.4, VePro7, Mainstage3 - 5,1 MacPro 3.46ghz x 12 96gb ram

Link to comment
Share on other sites

Here, I made a simple one for velocity, no coding required. Just open Scripter, copy and paste this into the script editor and hit the Run Script button. You'll see a slider you can slide to more or less aggressive curves...this mimics video gamma curves I guess.

 

viewtopic.php?f=45&t=132971&p=818934#p818934

OSX 12.x (Monterey) on OpenCore - Logic Pro 10.7.4, VePro7, Mainstage3 - 5,1 MacPro 3.46ghz x 12 96gb ram

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...