LPX Scripter: Random MIDI Event Suppression

About

I’ve been playing around with LPX’s Scripter, starting with random numbers a bit since Scripter utilizes Apple’s JavaScriptCore framework. I find music automation to be a fascinating topic, because it combines my enjoyment of random numbers, from years of board games and role playing games, and music theory and composition, even though my skill in the latter is not nearly as strong as in the former.

One of the first scripts I created has been a lot of fun to play with. It randomly suppresses MIDI events at any point in the MIDI FX chain based on a simple percentage chance of the event actually happening. Here is the script as a whole, suitable for copy and pasting into Scripter.

/* Apple API */
var PluginParameters = [];

/* Mute Threshold Slider */
var resolution = 100;
var minValue = 0;
var maxValue = resolution;
var numberOfSteps = resolution;
var defaultValue = resolution / 2;
PluginParameters.push({ name: "Play %", type: "lin", unit: "", minValue: minValue, maxValue: maxValue, numberOfSteps: numberOfSteps, defaultValue: defaultValue });

/* Apple API */
function HandleMIDI(event) {
        if (event instanceof NoteOn) {
            if (triggerEvent("Play %")) {
                event.send();
            }
        }
    else {
        event.send();
    }
}

function triggerEvent(name) {
	var p = GetParameter(name);
	return (Math.round(Math.random() * resolution) < = p) ? true : false;
}

N.B.: I updated this code since the original post to better manage the resolution of the control, and change the Math.ceil to Math.round because even the thought of there being bias was bugging me.

Use Cases

This script is an easy way to create variations in your music, particularly when combining it with other MIDI FX. It's best used when in combination with other MIDI effects.

  1. I have found it useful for exploring different synth settings and sounds while letting the computer run around the keyboard playing in a reasonable approximation of human playing. Tinkering with synths becomes a lot easier when someone else is playing for me.
  2. Applying it to a drum track can create variations that run the gamut from jazz to Aphex Twin-like beats.
  3. Applying it to a chord progression can create any number of musical variations. There are a number of Apple loops that are also MIDI data, and this can be used to find variants of those loops. 

It's that last use case where I have had the most fun. I particularly enjoy ambient techno music, harkening back to the Instinct Ambient label through today (Apple Music's curated "Pure Ambient" playlist is now my go-to resource). Combining this script with the other MIDI effects and applying to a good chord progression has led to some interesting music. It's random, so it's not all perfect, but every once in a while I get a good hook worth playing with more.

How It Works

The script is fairly straightforward as far as Scripter goes, and it's easy to add it into LPX and start using it. But here is how it works for readers unfamiliar with Scripter in general. Hopefully someone in the LPX community will learn something new and apply Scripter in new and interesting ways.

First, the user-changeable control is created. I have detailed information about how controls are managed on my documentation page for Scripter Controls.

/* Apple API */
var PluginParameters = [];

/* Mute Threshold Slider */
var resolution = 100;
var minValue = 0;
var maxValue = resolution;
var numberOfSteps = resolution;
var defaultValue = resolution / 2;
PluginParameters.push({ name: "Play %", type: "lin", unit: "", minValue: minValue, maxValue: maxValue, numberOfSteps: numberOfSteps, defaultValue: defaultValue });

By default, this is set to a simple percentage. But I have found that when I get into the lower percentages to create more spacious or elegant melodies, 0%–20% isn’t enough, and more nuance is desired. To resolve this, I change the resolution variable to a higher number like 256. I’ve found anything more than that is kind of pointless, and too high a number results in unexpected behavior in the slider.

As a style matter, Apple’s examples place this code at the end of the scripts, but I prefer to have them at the beginning, if only because it’s the first thing I see when I open Scripter, and it makes tweaking the UI really easy since the Editor is tricky to use.

Next comes the actual triggering of a MIDI event. Essentially, if the NoteOn event is detected, then it calls a custom function that determines whether to suppress the event or not. Every other kind of event is passed through harmlessly.

/* Apple API */
function HandleMIDI(event) {
	if (event instanceof NoteOn) {
		if (triggerEvent("Play %")) {
			event.send();
		}
	}
	else {
		event.send();
	}
}

Because we only care about whether a note should play or not, the function looks for the NoteOn MIDI event. This does not look for “parent” Note events because the NoteOff event needs to be handled later, which I’ve learned is kind of a hassle to manage manually with the minimal troubleshooting the editor provides.

This is an important distinction, because if the Note event is handled but not NoteOff, then a real cacophony of sound quickly builds, and processes peak out with too many MIDI events. This outcome is obscured with quickly-releasing notes and percussion, but is quickly obvious with instruments that never release, like the organs, and when recording MIDI output to another track.

The triggerEvent function which does the actual work of determine whether a note is played is simpler than it looks. All it does is take the user setting from a slider with a given name, randomly gets a number between 1–100, then returns true if the user value is less than or equal to the random number.

function triggerEvent(name) {
	var p = GetParameter(name);
	return (Math.ceil(Math.round() * resolution) <= p) ? true : false;
}

I don’t recall where I found the specific code that creates the random number, but it works fine for what’s being done here. Without getting into too many details, here is how it works. Math.random effectively returns a number between 0 and 1. That number is multiplied by 100 to make it a percentage. Math.round makes it a whole number. The resulting integer is compared to the user’s setting, and if it’s less or equal to the random integer, it returns true, else it returns false.

How to use in a channel’s MIDI FX

This script is used in the MIDI FX section of a channel (highlighted in red below). Note, this can only be used with MIDI FX, and other track types like audio and some drum machines will not allow for it.

By itself, the script simply determines whether a note should be played or not. The real action is when chaining with other MIDI FX, which I’ll get into in the next section.Example channel showing how the random suppression script can be chained with other MIDI effects in the MIDI FX chain.

The behavior in Logic we are leveraging here is that MIDI events are passed through the same way as other signal processors in a channel: from the top down. Events are triggered by the input, and then intercepted by the topmost MIDI FX in the chain. Results of that MIDI FX is passed onto the next MIDI FX.

In the above example, there are three effects in the MIDI chain:

  1. “Scripter” is the random note suppression script. (If only there was a way to show a custom script name in the UI.)
    1. The script determines whether a note should be played (the NoteOn event executed). Those events are passed to the next effect.
  2. “Arp” is the built-in “Arpeggiator” effect.
    1. Arpeggiator can create new events based on the passed event. All of those events are passed to the next effect.
  3. The second “Scripter” is another independent instance of the random note suppression script.
    1. This instance determines which events sent to it from Arpeggiator get played.

Of course, further effects can be added, with this random suppression script in between them, to create some fun and surprising variations, which I dive into in the next section.

Step-by-Step Usage

I highly recommend setting up a MIDI channel to record output data to another channel like I have in this example. I have instructions on my site on how to do this in the Logic Pro X section.

Per that setup, here are two tracks, one with a simple chord progression.

The chord progression in the “Source” track will be the source material for everything to come next. MIDI effects will be applied to it, and the results will be recorded in the “Output” track. When everything is setup correctly, I am able to record the looped MIDI region in the source track to the output track as a single region.

Adding the script on its own, and setting it to 50% (the default) results in the following output.

This is the default output of the script, which is largely uninteresting on its own. Typically, I go down to 33% for the chords so I can prevent whole chords being played, and 20% if I want something akin to tonal accents. Once I get settled on the probability, I add in more MIDI effects like the Arpeggiator to make things more interesting.

Adding the Arpeggiator, set to the default patch, and adding another instance of the random suppression script yields the following output.

Now I have something that is more interesting and playful, if repetitive. As before, the random threshold is still the default, and I always makes some adjustment to get the desired effect. But, even with the default, it’s not half bad.

Playing with the settings in Arpeggiator yields more interesting results by increasing the octave range, note order, and other settings. Changing the patch from the default to Chord Groove 1 changes the output in subtle but important ways.

This gets into something more interesting and somewhat realistic. I only changed the Arpeggiator patch, and left the suppression threshold alone.

Next, I’ll add the Note Repeater MIDI effect, set to the “Five Up” patch, which transposes pitches upwards and lowers the velocity on each repeat. Then, I’ll add yet another instance of the random suppression yields the following results.

It’s a bit much, and now it sounds like something from a 60s science fiction movie, which can be cool in its own way. Changing the last random threshold yields more manageable results.

Okay, so it’s still a 60s sci fi movie, just not as noisy. But the point here is between the various MIDI effects, there now exists massive potential for productive experiments with easy ways to shape the sound as desired.

Generally, this is the channel patch I wind up on, with everything in the chain, adjusting and disabling effects as needed. The output becomes really fun with the ES-series synths, but I have also been able to get scarily good human-like output with the judicious use of random suppression and the arpeggiator.

Further Development

Given Scripter’s MIDI API and use of JavascriptCore, there are obviously a lot of directions for development. Reviewing Apple’s sample scripts yields a bunch of ideas, like handling suppression and shaping for specific pitch ranges like on a drum kit patch, making crazy blast beats on the kick drum or build a new kind of sequencer.

So, that’s it. If you have any questions, please feel free to reach out via the email address casually buried in my About page, and you can also find me on LinkedIn. In the meantime, have fun with the script, and never, ever stop making music.