Language Constructs in Detail

In this chapter, you will learn more about types, declarations and other topics.

Comments

You can have both line comments and multiline comments. Single line comments start with a double slash and end at the line end:

// This is a line comment

Block comments start with /* and end with */ and they can be nested.

/*
    This is a
    multiline comment and
    /* you can nest multiline comments */
    as well
*/

Constants

GPScript supports integer, floating point, boolean and string constants (more on strings below). For convenience, MIDI note names can be used in place of integers. E.g, C3 is the same as 60, D#4 (or Eb4) is the same as 75. SysEx Message constants can be defined as well (more on SysEx messages below).

Declarations

You can declare global variables and local variables. The general syntax for a declaration is:

var identifier : type [optional initialization]

If you want to declare multiple variables of the same type, just separate them with a comma:

var i, j, k : Integer [optional initialization]

If you are declaring multiple variables one after the other, you do not need to repeat the var keyword (although you can)

var i : Integer [optional initialization]
    s : String [optional initialization]
    n : NoteMessage [optional initialization]

Global variable are defined outside of any callbacks or user-defined functions and are visible only from the point at which you declare them. Certain types such as Plugins, Widgets and function generators (ADSR etc.,) can only be declared at global scope.

The type system

All variables must be declared with a type. All the available types are listed in the list of types, in the reference part of this manual.

Primitive types

Primitive types include Integer, Double and Boolean. With a couple of exceptions, operators for primitive types are part of the language syntax itself. For example, arithmetic operators (+, - , *, /, %) and comparators (<, <=, ==, !=, >, >=) can be used on both Integer and Double.

Opaque types

Opaque types are types whose behavior is controlled completely by system functions and you have no direct access to them. Variables that refer to “physical” widgets and plugin blocks instances of opaque types. Some opaque data types do not have any “physical” existence. These include function generators, the NoteTracker and the ChordRecognizer types. Over time, as new functionality is added to Gig Performer, more opaque data types are likely to be added.

Hybrid types

There are a couple of types that are treated specially, with some built-in operations even though they are otherwise opaque.

Strings

Language designers struggle with the best way to handle strings. Some leave all string processing up to their libraries (but still have language support to define string constants), others make strings be a special type with language support. GPScript adopts a hybrid approach. There are numerous library functions for manipulating strings but the + operator is overloaded so that strings can be concatenated together. More importantly, if you start an expression with a String type, then the RHS 1 of the + operator can be of type Integer or Double and the value will be automatically converted to a String type. This makes it easy to write such things as

Print("The value is " + i) // Display the value of i in the script log window

and is mainly intended for debugging purposes although it can be convenient to use this mechanism to set the caption of a label or widget, e.g.,

SetWidgetCaption(w, "Value: " + v)

String constants

Like many languages, a string constant is simply a sequence of characters bracketed with the double-quote character ". If you need to include a double-quote inside your string, you can use the backslash character to escape the quote.

"This is a string containing an embedded \" double-quote"

HereDoc string

You can bracket text with <<< and >>> so as to allow a string to be spread out over multiple lines.

Var multilineText: String =
<<<
First line of text
Second line of text
>>>

System Exclusive (SysEx) messages

You can define a constant that represents a SysEx message. The format is as follows:

# F0 12 34 56 .. F7

Spaces are optional and intended for readability when defining longer messages that may be difficult to read. You must include # and F0 at the beginning and you must have F7 at the end – you can use lowercase though – and the hex bytes in between must be between 00 and EF. Variables of type SysexMessage can be used to store a SysEx message:

Var mySysex : SysexMessage = # F0 12 34 56 F7  // works
Var mySysex : SysexMessage = # F1 12 34 56 F7  // will not compile as the message does not begin with F0

There are system functions available for sending SysEx messages to MIDI In blocks and MIDI Out blocks as well as for getting and changing values in a SysEx message. That last is useful for parameterized real-time SysEx messages.

Parameters

Note

Parameters are available in Scriptlets only.

A Scriptlet plugin can define variables representing three different kinds of parameters:

  • Continuous:

    Continuous parameters can have floating point values between 0.0 and 1.0.

    Var Foo : Continuous Parameter = 0  // Declare a continuous parameter called Foo
    Var a,b,c : Parameter = 0.5         // Declare three parameters initialized to 0.5
    
  • Subrange:

    A subrange parameter is a sequence of integer values. Note that MIDI Note Names can be used in place of integer values.

    Var Subrange Channel : Parameter 1..16    // Declare a subrange parameter called Channel
    Var ScaleRoot : Parameter C3 .. B3 = D3;  // Define a range of scale roots and initialize to D
    
  • Discrete:

    A discrete parameter can hold any one of a predefined number of strings.

    Var Leslie : Discrete Parameter "Slow", "Off", "Fast"
    

Note

The types Continuous, Subrange and Discrete are optional in parameter declarations as they can be inferred from the clause on the right hand side of the parameter keyword. However, should you need to use an array of parameters, you will need to refer to these types explicitly. It is not possible to have an array containing different parameter types.

The AsNoteNames operator can be used to create a discrete parameter that is intended to allow a sequence of note names to be conveniently defined. This is particularly useful if you need parameters to span a significant number of MIDI notes. For example:

Var notes : Parameter AsNoteNames C3 .. E3

is equivalent to writing:

Var notes : Parameter "C3", "C#3", "D3", "D#3", "E3"

Changing a parameter value from your script is as simple as assigning a new value to the parameter variable. Examples:

Channel = 2
Leslie = "Off"

As with most other variables, you can (and should) follow a declaration with an initialization. The compiler will produce a warning message if you do not include an initialization.

Var Leslie : Parameter "Slow", "Off", "Fast" = "Off"

Assignments to parameter variables are checked at runtime so if, for example, you tried to assign 17 to that Channel parameter above, it will quietly fail. In any such failure situation, the parameter will be set to the minimum possible value.

When you declare a parameter variable for a Scriptlet plugin, the name of that variable is used automatically as the parameter name that you see in the plugin editor or in the widget mapping parameter list. This can cause conflicts if the parameter name you want to use is the same as some other variable or is the same as an existing system function. For example, you cannot declare a parameter variable called “Transpose” since there is already a GPScript system function with that name. However, since it might typically be desirable to use such a word for display purposes, you can optionally include an explicit parameter name in the declaration:

Var MyTranspose("transpose") : Parameter -12 .. 12

The declaration above will declare a new variable called MyTranspose which you will use to reference the parameter in your script. However, the word transpose will be used for the parameter name for that plugin as far as widgets are concerned.

Parameter numbers

Under the covers, parameters are integer numbers. Parameter numbers start at zero and increment by one for each new parameter that you declare. You can access the parameter number directly by appending a hashsign (#) to the parameter identifier:

Var a,b,c : Parameter = 0.5 // Declare three parameters initialized to 0.5
Print(b)                    // Prints the value 0.5, the value of that parameter
Print(b#)                   // Prints 1, the second parameter number

Arrays

It is possible to have arrays of types (although currently you cannot have an array of arrays). Arrays currently have a maximum length of 256. Array indexing is zero-based.

var mySmallArray : Integer[32]

This creates an array that can contain 32 values. Attempts to reference the array outside the defined range will be trapped by the runtime system and the script terminated. While this adds a little cost, we feel that array range errors are sufficiently common that it’s worth the cost. In the future, we may add an option to disable range checking.

You can pass arrays as dynamic parameters to functions and the system function Size will return the current size of the array parameter.

Function IntSum(someArray : Integer Array) returns Integer
    var s : Integer  // Track the sum
    index : Integer

    For i = 0; i < Size(someArray); i = i + 1 do
        s = s + someArray[i]
    End

    result = s  // Return the result
End

Dynamic arrays

Rather than defining a constant size, you can add the keyword Array after any primitive type to create a dynamic array. For example, the following declaration is allowed:

Var
    Fader1, Fader2, Fader3, Fader4 : Widget
    Faders : Widget Array

Initialization
    Faders = [Fader1, Fader2, Fader3, Fader4] // Array initialization
End

or more conveniently:

Var
    Fader1, Fader2, Fader3, Fader4 : Widget
    Faders : Widget Array = [Fader1, Fader2, Fader3, Fader4]

Then you can use array indices to reference individual widgets. The Size function works as expected and will return the current size of the array which of course is 4 in the above example.

Currently allowed array types include all the types (Integer, String, Boolean, Double, MidiMessage, Widget) and the three parameter types, Subrange, Discrete and Continuous.

The empty array [] can be used to clear the contents of a dynamic array.

Var
    x : Integer Array = [1, 2, 3, 4]

Initialization
    x = []
End

Footnotes

1

RHS is short for right hand side.