|
|
|
|
|
_Note - the following API is under heavy development and will likely change over time._
|
|
|
|
|
|
If you have any suggestions, definitely please let me know!
|
|
|
|
|
|
[[_TOC_]]
|
|
|
|
|
|
There are several different ways that various functions, classes, singleton objects and other facilities are added to LPX Scripter.
|
|
|
|
|
|
# `MIDI` Helper Functions
|
|
|
|
|
|
LogicPro Scripter has a built in object called `MIDI = {}` which contains a number of helper midi functions such as translating pitch numbers to names, etc. Some more functions have been added as follows:
|
|
|
|
|
|
- **MIDI.ticksFromBeat** ( beat, [PPQN] )
|
|
|
|
|
|
Convert the fractional part of a beat position into midi ticks using PPQN. If no PPQN value is provided, it will default to 960ppqn.
|
|
|
|
|
|
|
|
|
- **MIDI.beatFromTicks** ( beat, ticks, [PPQN] )
|
|
|
|
|
|
Convert ticks to fractional beat value. The `beat` argument can be any beat value, but it will be rounded to the nearest integer before applying the `ticks` argument and converting back to a fractional beat return value. If PPQN is not provided, it will default to 960.
|
|
|
|
|
|
- **MIDI.roundBeatPos** ( beat, PPWN )
|
|
|
|
|
|
This function rounds a given fractional beat value to any PPQN resolution desired.
|
|
|
|
|
|
- **MIDI.millisecondsFromBeats** ( beat, tempo )
|
|
|
|
|
|
Convert fractional beat value to time in milliseconds while applying the desired tempo.
|
|
|
|
|
|
- **MIDI.samplesFromBeats** ( beat, tempo, rate )
|
|
|
|
|
|
Convert fractional beat value to time in samples at the given sample rate and tempo
|
|
|
|
|
|
- **MIDI.beatsFromMilliseconds** ( ms, tempo )
|
|
|
|
|
|
Calculate a fractional beat value from a given number of milliseconds at a certain tempo.
|
|
|
|
|
|
- **MIDI.beatsFromSamples** ( samples, tempo, rate )
|
|
|
|
|
|
Calculate a fractional beat value representing a given number of samples at the given tempo and sample rate.
|
|
|
|
|
|
- **MIDI.isPitchRange** ( event, low, high )
|
|
|
|
|
|
Return true/false if the given Event object is within the specified `pitch` range. The `low` and `high` parameters can be specified either as an integer or by name as string, such as `"C#3"`. Applicable to `NoteOn`, `NoteOff` and `PolyPressure` Event objects.
|
|
|
|
|
|
- **MIDI.isValueRange** ( event, low, high )
|
|
|
|
|
|
Return true/false if the given Event object is within the specified `value` range. Applicable to `ControlChange`, `ChannelPressure`, `PolyPressure` and `PitchBend` Event objects.
|
|
|
|
|
|
- **MIDI.isNumberRange** ( event, low, high )
|
|
|
|
|
|
Return true/false if the given Event object is within the specified `number` range. Applicable to `ProgramChange` and `ControlChange` Event objects.
|
|
|
|
|
|
- **MIDI.isVelocityRange** ( event, low, high )
|
|
|
|
|
|
Return true/false if the given Event object is within the specified `velocity` range. Applicable to `NoteOn`, `NoteOff` Event objects.
|
|
|
|
|
|
|
|
|
# `LPX` Scripter Objects
|
|
|
|
|
|
A new object called `LPX` has been added. This will primarily contain global objects and data that are useful for scripting with Scripter. These objects are pre-created by Scripter and always available in a running script.
|
|
|
|
|
|
## LPX.GuiParameters
|
|
|
|
|
|
- **Description**
|
|
|
|
|
|
This global object will contain a performance-enhanced way to access GUI parameters, which normally require a lot of CPU to access using the normal provided `GetParameter` function.
|
|
|
|
|
|
This Object is pre-instantiated and always accessible. in order to use this feature, first make sure to add the following line to the `ParameterChanged` callback:
|
|
|
|
|
|
```javascript
|
|
|
function ParameterChanged(id, val) {
|
|
|
LPX.GuiParameters.cache(id, val);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
After setting up the above, simply use the GuiParameters.get() method to retrieve GUI parameters more efficiently:
|
|
|
|
|
|
```javascript
|
|
|
.
|
|
|
var val = LPX.GuiParameters.get(1);
|
|
|
.
|
|
|
```
|
|
|
|
|
|
- **Methods**
|
|
|
|
|
|
- **LPX.GuiParameters.get** (id)
|
|
|
|
|
|
Retrieves the current GUI value without costly call to `GetParameter`
|
|
|
|
|
|
- **LPX.GuiParameters.cache** (id, val)
|
|
|
|
|
|
This should be called in `ParameterChanged` callback. This will cache each GUI parameter whenever the users changes the GUI setting or the Scripter changes it with `SetParameter`.
|
|
|
|
|
|
- **LPX.GuiParameters.set** (id, val)
|
|
|
|
|
|
This is provided as a convenience wrapper around Apple's `SetParameter` function. If you like you can use the `GuiParameters` methods to always access your GUI parameter, both for getting and setting. But there is no particular advantage here at this time other then more clarity in the script.
|
|
|
|
|
|
|
|
|
## LPX.console (_Buffered Logging_)
|
|
|
|
|
|
- **Description**
|
|
|
|
|
|
The built in `Trace` feature of Scripter is very limited and often drops many traced messages with the following error message in the Scripter window:
|
|
|
|
|
|
```javascript
|
|
|
...console bandwidth exceeded, thinning some traces...
|
|
|
...console bandwidth exceeded, thinning some traces...
|
|
|
...console bandwidth exceeded, thinning some traces...
|
|
|
...console bandwidth exceeded, thinning some traces...
|
|
|
```
|
|
|
|
|
|
LPX provides a special object called `LPX.console` which can be used to buffer all tracing to the scripter window in such a way that no messages will ever be missed.
|
|
|
|
|
|
In order to use this feature it is necessary to add one line to the `Idle()` callback function:
|
|
|
|
|
|
```javascript
|
|
|
function Idle() {
|
|
|
LPX.console.flush();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Once you have done that, then simply use the `LPX.console.log()` method instead of Trace. For example:
|
|
|
|
|
|
```javascript
|
|
|
LPX.console.log("hello world");
|
|
|
```
|
|
|
|
|
|
If you find that you are still getting the bandwidth error message, you can adjust the size of the buffer to a smaller size until the problem goes away by setting the `flushSize` attribute:
|
|
|
|
|
|
```javascript
|
|
|
LPX.console.flushSize = 15;
|
|
|
```
|
|
|
|
|
|
Using a larger value can speed up the logging, which is often lagging way behind actual activity due to the nature of the `Idle()` function. That is a good thing because it makes sure the logging never gets in the way of midi processing. However if you want it to go faster, use a larger `flushSize` until you start seeing the bandwidth error message than back it off from there.
|
|
|
|
|
|
Default `flushSize` value is 20
|
|
|
|
|
|
- **Methods**
|
|
|
|
|
|
- **LPX.console.log** (msg)
|
|
|
|
|
|
- **LPX.console.flush** ();
|
|
|
|
|
|
# Midi Helper Classes
|
|
|
|
|
|
Some helpful classes have been added to handle common tasks. These classes must be instantiated in a Scripter script by using the `new` keyword. For example:
|
|
|
|
|
|
```javascript
|
|
|
var myTimer = new MidiTimer;
|
|
|
```
|
|
|
|
|
|
## MidiMatrix class
|
|
|
|
|
|
- **Description**
|
|
|
|
|
|
This class is useful for setting up keyboard layers and splits. This is accomplished by setting up `ranges` in the MidiMatrix and then having MidiMatrix schedule events accordingly. Events filtered through the MidiMatrix will be limited to the given ranges. A JSON structure be used in the constructor to configure the splits and layers.
|
|
|
|
|
|
- **Example:**
|
|
|
|
|
|
```javascript
|
|
|
var myMatrix = new MidiMatrix([
|
|
|
{ low:"C2", high:"C3", channel:1 },
|
|
|
{ low:"C1", high:"C2", channel:2 }]);
|
|
|
|
|
|
function HandleMIDI(event) {
|
|
|
myMatrix.send(event);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
The above example will filter out any events below C1 or above C3. From C1-C2 events will be channelized to channel 2, C2-C3 will be channelized to channel 1, and notice that C2 will go to both channel 1 and channel 2.
|
|
|
|
|
|
- **JSON configuration**
|
|
|
|
|
|
The JSON configuration can be passed into the constructor to setup the Matrix. This structure can be either a single `{}` json object; or it can be an array of json objects as in the above example. Here is an example with only a single range:
|
|
|
|
|
|
```javascript
|
|
|
var myMatrix = new MidiMatrix({ low:"C2", high:"C3", channel:1 });
|
|
|
```
|
|
|
|
|
|
The `low` and `high` parameters can be specified either as a pitch integer or as a pitch name string.
|
|
|
|
|
|
- **Methods**
|
|
|
|
|
|
- **MidiMatrix.addRange** (...)
|
|
|
|
|
|
This method can be used to add ranges to a Matrix after it has already been constructed. Note it is probably preferable to just create a new MidiMatrix when you need it to be different.
|
|
|
_Note, this may become internal only later._
|
|
|
|
|
|
- **MidiMatrix.send** (event)
|
|
|
|
|
|
Method to send the given event through the Matrix to the LogicPro midi engine through the configured ranges
|
|
|
|
|
|
|
|
|
## MidiTimer class
|
|
|
|
|
|
- **Description**
|
|
|
|
|
|
This class can be used to setup Timers so that functions can be automatically executed when a certain amount of time has gone by.
|
|
|
|
|
|
Each instance of `MidiTimer` can have multiple triggers activated at the same time, so its probably not necessary to have more than one MidiTimer object in any given script.
|
|
|
|
|
|
The Timer works by executing a named function that you provide for each `trigger`
|
|
|
|
|
|
- **Example:**
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
function doStuff(beat) {
|
|
|
let e = new NoteOn;
|
|
|
e.channel = 1;
|
|
|
e.pitch = 50;
|
|
|
e.velocity = 100;
|
|
|
e.sendAtBeat(beat);
|
|
|
}
|
|
|
|
|
|
var myTimer = new MidiTimer;
|
|
|
myTimer.triggerAtBeat(doStuff, 5); // at beat 5, call function
|
|
|
|
|
|
function ProcessMIDI() {
|
|
|
myTimer.process();
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Note that the MidiTimer class has a special method called `process` which must be called within the `ProcessMIDI` callback in order to perform its duties.
|
|
|
|
|
|
Note also that the assigned function for the timer trigger, in this case it is called `doStuff` has an optional `beat` parameter. This value will be provided to that function by the MidiTimer trigger in case it is needed.
|
|
|
|
|
|
Note that it is required to enable Timing info in your script:
|
|
|
|
|
|
```javascript
|
|
|
var NeedsTimingInfo = true;
|
|
|
```
|
|
|
|
|
|
- **Methods**
|
|
|
|
|
|
- **MidiTimer.process** ()
|
|
|
|
|
|
This should be placed inside the `ProcessMIDI` callback so that triggers will be called at the right beat.
|
|
|
|
|
|
- **MidiTimer.triggerAtBeat** ( function, beat )
|
|
|
|
|
|
This method will schedule the named function to be executed during playback when the specified beat is within the process block range of `ProcessMIDI`.
|
|
|
|
|
|
- **MidiTimer.triggeAfterBeats** ( function, beats )
|
|
|
|
|
|
This method will schedule the named function to be executed during playback after the specified number of beats have elapsed after setting the trigger.
|
|
|
|
|
|
- **MidiTimer.triggerAfterMilliseconds** ( function, ms )
|
|
|
|
|
|
This method will schedule the named function to be executed after the specified number of milliseconds have elapsed. This is in effect regardless of whether LogicPro is currently playing back. Note that this timing is not based on on the sequencer's timeline but on the realtime clock of the computer during script processing.
|
|
|
|
|
|
- **Future Dev**
|
|
|
|
|
|
In the future may add a millisecond timer that can fire off as a delta from a starting beat position time, so that it will be a certain number of exact milliseconds of the actual music.
|
|
|
|
|
|
# Event class extensions
|
|
|
|
|
|
The Event classes have been extended with numerous convenience functions. More info will be provided later.
|
|
|
|
|
|
- **Event.ticksFromBeat**
|
|
|
- **Event.valueToName**
|
|
|
- **Event.pitchToName**
|
|
|
- **Note.isOff**
|
|
|
- **Event.isPitchRange**
|
|
|
- **Event.isValueRange**
|
|
|
- **Event.isNumberRange**
|
|
|
- **Event.isVelocityRange**
|
|
|
|
|
|
# Future Development
|
|
|
|
|
|
- Mapper
|
|
|
- Scaler
|
|
|
- Keyswitcher
|
|
|
- Sequencer
|
|
|
- NoteOn Tracking (what is sustaining)
|
|
|
- auto chasing
|
|
|
- articulationID to CC mapper
|
|
|
- Humanizer
|
|
|
- Randomizer
|
|
|
- DataByChannel Tracker?
|
|
|
- General Purpose condition matcher, like transformer
|
|
|
- Envelope Follower
|
|
|
- SustainTracker
|
|
|
- Helper functions for bank change messages
|
|
|
- Helper functions related to sustain pedal
|
|
|
- MidiMonitoring
|
|
|
- Dynamic GUI handling
|
|
|
- Automation tricks
|
|
|
- detect start/stop
|
|
|
- Middle C setting
|
|
|
- TODO what about cycle modes in the above?
|
|
|
- MidiClass.js based data storage, so that external program can be used to load parameter sets into MidiClass.js and chosen from a script without needing to copy and paste into scripter window.
|
|
|
- DEBUG MODES that can automatically log debug information when a flag is enabled.
|
|
|
- Arp, step sequencer, chords, scale conformance, lookahead, PDC, quantization, phrase playback, midibuffer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|