The Gig Performer scripting language, which we have rather unimaginatively named GP Script, is a special purpose language designed to add programmable and responsive functionality to Gig Performer. For example, you can write scripts with code that can respond to incoming MIDI note events and then transpose them, make chords out of them constrained to a scale, or use them to change widget values or to directly control parameters of a plugin (keyboard tracking, anyone?). Script code can be triggered in response to your moving a knob, slider or button. There are also time generators that can be used as LFOs (with varying shapes) or as ADSRs.
Events and Callbacks¶
Generally, GP Script is event driven. It doesn’t do anything until something “happens” such as your playing a key, turning a knob, switching to another rackspace or to a different variation. A certain amount of time passing can also count as something “happening”. All of these examples are known as events and when you write code that can respond to an event, that code is called a callback. GP Script can currently handle the following events:
MIDI events (notes, CC values, Aftertouch, etc.)
Plugin parameter changes
LFOs (also known as Time Generators)
OSC Messages (coming soon)
A simple example¶
var pi : double Initialization pi = 3.14159 Print(pi) End
So, what’s going on here?
Well, first we declared a variable called
pi and whose value must be a floating-point number (
In GP Script all variables must be declared, and they must have a type.
The type of a variable defines the kinds of values that the variable can represent and also determines what kinds of operations can be applied to it.
So called strongly typed languages make it easier to prevent certain kinds of common bugs.
Initialization defines the beginning of a callback that will be executed just once when a rackspace is initially loaded.
In this example we simply assign a value to the variable and “print” its value.
In the context of Gig Performer, printing means to show the item in a special window called the Script Logger.
End completes the definition of this callback.
A more interesting example¶
// Declare a global variable var A800 : MidiInBlock // A800 is a named block in Gig Performer // Callback when a note event (on or off) is received On NoteEvent (m : NoteMessage) From A800 SendNow(A800, m) // Send the note to the A800 MidiIn block SendNow(A800, Transpose(m, 5)) // Also send a transposed note End
Like before, the first thing we’re doing is declaring a variable.
But unlike the example above that just used a floating-point number, the variable A800 is declared with the type
That type represents a Gig Performer MIDI In plugin block.
Consequently, the name of the variable, in this case,
A800, is significant.
For this script to work there must be a MIDI In block in your rackspace and its scripting name, more properly known as a handle, must be
So in this example, we are binding a variable name to an entity in Gig Performer.
Next, we define a callback.
A callback is a block of code that will run automatically when the event associated with that callback occurs.
The callback above responds to MIDI note events (both NoteOn and NoteOff events) that arrive at the MIDI In block named
There are other callbacks that respond to other MIDI events such as Control Change events or Aftertouch.
The actual message itself is accessed through the incoming parameter argument
m which has the type
Once you define a callback for a MIDI event you have full responsibility for that event.
GP Script will not automatically send the MIDI event back to the MIDI In block which means that the MIDI In block will not forward the event to whatever synth plugins are connected to it.
So if you want the incoming note to be played, you have to explicitly send it out, which we do with the statement
Immediately afterwards, we send the note again, after transposing it by 5 semitones.
Voilà, instant chords.
GP Script is a programming language designed specifically for use with Gig Performer. Rather than integrating an existing language such as Lua, Python or ChaiScript and living with some compromises, we felt it worthwhile to have an essentially seamless environment that would immediately make sense. The clean syntax reflects that integration. GP Script is influenced more by Algol/Pascal. While we will assume for now that the reader has at least some basic understanding of programming there will be plenty of samples to help users to get up to speed quickly.
As mentioned above, GP Script is a strongly typed language; every variable must be declared along with a type.
However, along with the usual types such as integers, strings, doubles and so forth, GPScript has built-in types that represent plugins, widgets, various kinds of LFOs and some special purpose types such as
So as easily as you can write
var i : Integer
to represent an integer value and perform operations such as addition and subtraction, you can write
var A800 : MidiInBlock
Which represents a named
MidiInBlock, presumably associated with an A800 MIDI keyboard controller.
Now you can do operations such as manipulate incoming MIDI messages before sending them on to whatever audio plugin is connected to it.
GP Script includes the usual looping and conditional statements and of course you can define your own functions. There is also an extensive collection of efficient built-in functions you can leverage.
A list of built-in functions is provided in the included reference.
Scripts belong to rackspaces, meaning that you can have a completely separate script running in each of your rackspaces.
The top of the GP Script editor window has two buttons that provide you with templates to help you get started as well as system function code completion.
<Identifier> ':=' <Expression>
Assign an expression to a variable. For example:
a := 42 b := a + 1 // You can also write an assignment using just the '=' character str = "I don't like C syntax"
For <Assignment> ';' <BoolExpression> ';' <Assignment> Do <Statements> End
The for loop lets you iterate a statement block until the Boolean expression evaluates to
It is often used to process an array of values.
For i = 0; i < 10; i = i + 1 Do //statements here End
Syntax: While <BoolExpression> Do <Statements> End
while statement lets you loop though a block of statements until the Boolean expression evaluates to
The important difference between a while loop and a For loop is that the former does not have an explicit clause to update the expression to be tested.
An example for the while loop:
Done = false While !Done Do // Statements, one of which ultimately sets Done to true End
Be very careful with both these looping constructs.
If the condition does not ultimately evaluate to
false, the loop will never end and Gig Performer will hang.
If <BoolExpression> Then <Statements> [ [ Elsif <BoolExpression> Then <Statements>] Else <Statements> ] End
This is the usual conditional test.
The requirement for
End prevents bugs due to the dangling else problem.
Elsif clause (as many as you need) is optional as is the
give an example
Select [<BoolExpression> Do <Statements>] End
Select statement is a better option than the
If statement when you need to test for many conditions.
While similar in concept to the C
switch statement or the Pascal
case statement, the
Select statement doesn’t require each case to be a constant value.
Select a > b Do Print("A is greater than B") TimeNow() - previousTime > 1000 Do Print("At least one second has passed") previousTime = TimeNow() true Do Print("This executes if none of the other expressions were true") End
GP Script includes a large and growing collection of system functions for a variety of purposes such as math functions (rounding or scaling numbers), creation and modification of MIDI messages, management of function generators. A list of functions that are built into GP Script is provided in the included reference.
You can also write your own functions in GP Script.
Function <name> ([<identifier> : <type> [, <identifier> : <type>]]) [Autotype] [Returns <type>] <Local variable declarations> <Statements> End
Functions must have a unique name. Zero or more parameters can be defined and parameters are always passed in by value. Note that types such as Widgets, Plugins and dynamic arrays are objects and so what’s really being passed by value is the reference (a pointer) to the object.
Functions can optionally return a value in which case the
Returns clause must be present.
If the Returns clause is present, then a variable called
result exists in the function scope and whatever is assigned to that variable will be the return value.
There is no mechanism to return from the middle of a function.
Function SumNumbers(a,b,c : integer) Returns Integer result = a + b + c End
Function PlayMajorChord(keyboard : MidiInBlock, root : NoteMessage) SendNow(keyboard, root) // Original note SendNow(keyboard, Transpose(root, 4)) // The third SendNow(keyboard, Transpose(root, 7)) // The fifth End
GP Script has the usual comparison operators (
!=) as well as boolean operators (
Precedence rules are such that you very rarely require parentheses.
GP Script also has a ternary operator following the style of Algol and, more recently, Haskell:
Result = If widgetValue > 0.5 Then 1 Else 0 End
Note that the
Else is not optional in the ternary operator.