Mobius provides a powerful feature called scripting that can be used to add new functions or customize the behavior of Mobius to match your looping style. Some of the things that can be done with scripts include:
A script is simply a text file that can be created with any text editor. The contents of the script file are statements in a language called the Mobius Scripting Language. Once the script files have been written, they are registered so that Mobius knows their names and locations. Once a script file has been registered, it may be assigned one or more triggers which can be MIDI messages, computer keyboard presses, or buttons in the Mobius window.
When a trigger for a script is received, the statements in the script are performed. This process is called running the script, executing the script, or calling the script.
The following is an example of a simple script:
wait loop Reverse Halfspeed
When this script is called, it will first wait until the loop reaches its start point, then perform a Reverse and Halfspeed function at exactly the same time.
You may write a script using any text editor, though it is critical that you save the files as "plain text". If you are not in the United States you need to be careful about the character encoding used for the script file. Scripts must be written in an 8-bit character encoding that is compatible with ASCII.
If you are using Notepad on Windows, you must save the file using the ANSI encoding, you must not use Unicode. If you are using Wordpad, in the Save as type: menu you must select either Text Document or Text Document - MS-DOS Format.
If you are using TextEdit on a Mac, you need to save the file in a plain text encoding, either Western (Mac OS Roman) or Western (Windows Latin 1). When you use the Save As menu item, if you see a File Format: menu with items like Rich Text Format or Web Page (.html) then you are editing the file using rich text rather than plain text. Open the TextEdit Preferences window and under the New Document tab select the Plain Text radio button under the Format section. Once you do this create a new document and the Save As window should show a Plain Text Encoding: menu, select Western (Mac OS Roman).
There are many text editors available that present encoding options in different ways or using different words. The presentation may also be different on different versions of the operating system. The main thing to keep in mind is that you must always pick the simplest text format available, usually this is referred to as "plain text" with encodings such as "ansi", "latin 1", "roman", or "MS-DOS". Stay away from anything that says "rich" or "unicode" or that looks like a word processor format.
A script file may have any name, but it is recommended that they have the extension .mos. Scripts may be stored in any directory. On OS X most people keep them in /Library/Application Support/Mobius/scripts. On Windows most people keep them in a directory under the Mobius installation directory, usually c:\Program Files\Mobius 2\scripts.
Once you have created the script file, you need to tell Mobius about it through a process called script registration. There are two ways to register scripts: by file and by directory. When you register a script file, that file is loaded into Mobius when it starts. If you register a script directory, all scripts in that directory are loaded. Registering script directories is convenient if you have a lot of scripts and you don't want to register all of them one at a time. You do need to be careful though to only put things in this directory you really need.
To register a script open the Configuration menu and select Scripts, the Script Registration window will open.
On Windows you will see Add Script and Add Directory buttons that will bring up standard selection dialogs for files or directories. On Mac you will see a similar window but it will only have a single Add button because the standard Mac file selector lets you select both files and directories.
To register a new script or script directory, click one of the add buttons, navigate to the file or directory you want to register and click Open on Windows or Choose on Mac. The name of the file or directory should now appear in the list in the center of the script registration window. If you want to remove an item from the list, click on it so that it becomes highlighted then click the Delete button. Finally click the Ok button at the bottom of the widow to save the changes. The new set of scripts will be loaded into Mobius and are available for binding.
Like build-in functions, scripts are run in response to a trigger such as a MIDI message, computer keyboard key, or a button in the Mobius user interface. The process of associating a trigger with a script is called binding. >See the Controlling Mobius in the Getting Started manual for a full description of how to create bindings. Once you have registered some scripts or script directories you will start seeing their names in the Scripts tab of the binding windows.
Note that scripts will usually appear in the binding windows with a name other than the raw file name. Most scripts include a line starting with !name at the top that specifies a user friendly name you want to see in the binding windows. For example:
!name Global Fade Out
A typical script file may have a name like "fadeoutall.mos" but the name you see in the binding windows might be "Global Fade Out" or something else more meaningful than the file name. You will need to look in each script to see if it contains a !name line and remember to look for that name in the binding windows. If the script doesn't have a !name line you will just see the name of the script file without the ".mos" extension.
When binding a script to a trigger, consider whether you want to use advanced script features such as Sustain Scripts or Long Press Scripts. These features require that the script be bound to a Sustainable Trigger such as a MIDI note message.
TODO: Introduce DBWIN32, Echo, Message, Prompt...
A script is simply a text file containing lines of characters with each line ending with a return character. Each line of text will be one of the following types:
For a given line of text the first significant character determines the type. The first significant character is anything other than white space characters such as spaces and tabs.
If all the characters on a line are white space, the line is empty and is ignored.
If the first significant character is a # the line is a comment and the entire line is ignored.
If the first significant character is a ! the line is a declaration.
If the first significant character is anything other than whitespace, # or ! the line is a statement.
Declarations are used to give Mobius information about how to run the script. They can appear anywhere in the script but it is recommended that you put them all at the top. The order of declarations is not important.
Statements are performed in the order they are written in the script.
This example script shows each of the line types.
# Do the NextLoop function after turning off switch quantize !name Next Loop Now set switchQuant off NextLoop
The first line begins with a # so it is a comment. You may put comments anywhere in the script to explain what the script is doing.
The second line is a declaration, in this example it is specifying the name you want to display in the binding windows.
The third line is empty.
The fourth and fifth lines are statements.
GEEK NOTE:If you have experience with other programming languages, note that Mobius does not support multi-line statements (with or without a line continuation character) and there are no block comments. If you don't know what that means don't worry. All that you need to remember is that a comment, declaration or statement must all be one line.
A statement always begins with a Keyword which is a sequence of letters and numbers with no spaces. Example keywords are Record, set, and if. When we talk about statements, we usually identify them by their keyword, such as "a Record statement" or "an if statement".
After the keyword, a statement may contain arguments or expressions. An argument is a single word or number without spaces, an expression is a complex sequence of characters that may include spaces. Consider this example:
variable nextLoop
In the previous example the keyword is Variable so this is called a Variable statement. After the keyword is the argument nextLoop. The meaning of the argument depends on the keyword, in this case the argument is the name of a script variable. Here is a more complex example:
variable nextLoop loopNumber + 2
In a variable statement, all of the characters after the name argument are an expression. This expression is used to calculate the initial value of the variable. In this example loopNumber + 2 is the expression.
Some statements have no arguments, a few have more than one argument. Most statements will either zero or one expression. When a statement has both an argument and an expression, the argument comes first. In a few rare cases a statement may allow more than one expression, if so the expressions must be separated by a comma.
There are two categories of statements: those that are an intrinsic part of the scripting language, and those that execute Mobius looping functions. Examples of intrinsic statements are variable, set, if and for. Examples of function statements are Record, Overdub and NextLoop.
The intrinsic statements do not change often, but new function statements are added frequently. All the Mobius functions that are visible in the binding windows may be used in scripts, as well as several special functions that are available only in scripts. Whenever we talk about new functions being added to Mobius, you can assume that these functions can also be used in scripts.
GEEK NOTE:If you are familiar with the C language, you can think of intrinsic statements as being similar to the core C language, and the functions being similar to the "stdio.h" standard library.
All of the intrinsic statements will be described in detail because you need to understand all of them to write complex scripts.
The names of the function statements will not be listed here. Instead consult the Mobius Reference manual and find the function you want to use, look for its Internal name, and use that internal name as the first word in the statement.
The script language is case insensitive which means that upper case and lower case letters are considered to be the same. In the examples you will see, function names usually start with a capital letter and variable names start with a lowercase letter. When a function or variable name contains more than one word, a capitalization style called "camel case" is used which means that the initial letter of adjacent words are upper case and the other letters are in lower case. For example:
NextLoop variable saveQuantize quantize
You do not have to follow this style, "NextLoop", "nextloop", "NEXTLOOP", or "NeXtLoOp" all mean the same thing.
Intrinsic statements will begin with a lower case letter in this document, but this is very inconsistent in older script examples. Keywords whose names are found in common programming languages are usually written in all lower case, these include if, else, for, and while. But other Other keywords are often capitalized, such as Variable, Label, and Jump.
TODO: The difference between parameters and variables, User defined and system variables, variable scoping...
A few statements are used to define a range of statements that may be executed more than once or not at all. The range of statements is called a statement block, the statements that surround the block are called block start and block end statements.
The following table lists the block start statements with their corresponding block end statements:
Start | End |
---|---|
if | endif, else, elseif |
elseif | endif, else, elseif |
else | endif |
for | next |
repeat | next |
while | next |
proc | endproc |
How the block statements behave will be described later. The important thing to know now is that statement blocks must have both a start and end statement and you must use the end statement that is appropriate for the start statement.
Here are some examples of statement blocks:
if mode = record Overdub else Reset endif for * set 8thsPerCycle 8 next
To help make it clear where the statement blocks start and end it is recommended that you use indentation for the lines inside the block. Most examples use 2 spaces of indentation but you can use any number. Indentation is not required but it makes the script easier to read and understand.
Block statements may be nested which means that a block may appear inside another block. Here is a simple example of a nested block:
if mode = record if track = 1 Overdub endif endif
When blocks are nested, the block start statement is paired with the nearest block end statement that follows. It is strongly recommended that you use indentation to make it clear which block start and end statements belong together.
The $ is a special character used to indicate that the word that follows it is a parameter or variable reference. In early versions of Mobius the $ was required for references, now it is optional in most places. Since it appears in example scripts it tends to keep reappearing even though it is not required.
The $ is required in a few statements where it is not clear whether a word is a reference, or simply a word. These statements are:
These functions all use the remainder of the line following the keyword as a message to be displayed to the user. The message will be displayed exactly as it is written. For example the statement "message mode" will display the string "mode". If you want to display a message with the name of the current looping mode, you reference the mode variable with a $.
message The current mode is $mode
NOTE: This section introduces concepts that may be difficult for people without programming experience to understand. It is not necessary to understand this section in order to write scripts. But if you write scripts using the wait statement, the !sustain declaration, or the !multiclick declaration, this section will help you understand how to get the most power out of these features.
Scripts are similar to a re-entrant function in a multi-threaded programming language. These concepts should be familiar to most programmers, but they basically mean that there can be several copies of the script running at the same time.
Each of the calls for the down transition, up transition, and sustain notifications are done in an environment similar to a thread. If you use Wait statements, it is possible for the script to be active in more than one thread. By using variables, the threads can communicate with each other.
More here...
In this section we will describe the declarations. These may appear anywhere in the script though there are usually written at the top.
Normally scripts are loaded once when Mobius is first started and remain in memory for as long as Mobius runs. If you make changes to the script file they will not be used by Mobius until you restart it.
The !autoload declaration will cause the script file to be reloaded from the file system every time the script is called. This is very useful when writing new scripts as it allows you to keep Mobius running while you make changes the script.
After the script is working properly you should remove this declaration. Reloading the script causes a small delay before the script is run. This can make it difficult to apply the script to exact locations in the loop.
This option identifies the script as a controller script. When the script is triggered by a MIDI continuous controller, the script is called whenever the value of the controller changes.
Normally, when a script is bound to a MIDI continuous controller, it is assumed that the controller messages are being sent by a momentary switch rather than an expression pedal. Momentary switches typically send a non-zero controller value when the switch is pressed and a zero value when the switch is released. Mobius will only call the script when it receives a non-zero value which means the switch has been pressed. It will not call the script when it receives a zero value after the switch is released, unless you also use the !sustain declaration.
This behavior is usually not desired when the continuous controller values are being generated by an expression pedal. With an expression pedal, you want the script called whenever the position of the pedal changes, even when it goes to zero.
Controller scripts are most often used to change the track controls : Input Level, Output Level, Feedback, Secondary Feedback, and Pan.
They may also be used to change speed and pitch shift for each track, and change preset parameters such as 8ths Per Cycle or any other parameter that has a range of values.
Including the !controller declaration in a script will disable the following directives if they also appear in the script.
Here is an example script that implements a basic volume control. The behavior of this script is exactly the same as binding a controller to the Output Level track control.
!name Output Level !controller set output midiValue
The !controller declaration tells Mobius that this script should be called every time the value of the trigger changes. The word midiValue is a reference to a system variable that will contain the value of the continuous controller that triggered this script. The value will be from 0 to 127.
Here is a more interesting example that sets the output level to the inverse of the controller value. When the controller value is zero (toe up) the output level will be 127, when the controller value is 127 (toe down) the output level will be zero.
!name Backwards Output Level !controller set output 127 - midiValue
If you are familiar with the EDP, controller scripts give you the ability to implement "flip mode" as well as many other combinations of Mobius controls that change in response to a single expression pedal.
Normally scripts do not obey focus lock or track groups. When a script is run it will only run in the selected track.
The !focuslock declaration makes the script sensitive to track focus lock. The script will be run simultaneously in all focused locked tracks. If the Groups Have Focus Lock parameter is true, this declaration will also cause the script to be run in all tracks that have the same group as the selected track.
This option is useful for simple scripts that only operate on one track. It makes the script behave more like the built-in Mobius functions. If however the script uses the for statement to operate on several tracks, you normally do not use the !focuslock option.
This option allows the script to change behavior if you trigger it more than once within a short time. Some people think of this as "double clicking". A script that uses this option is called a multi-click script.
To create a multi-click script, add this declaration:
!multiclick 2000
The numeric argument is the trigger expiration period expressed as a number of milliseconds. The default expiration period is 1000 (1 second) so you only need to include this argument if you want a value other than 1000. In this example the expiration period will be two seconds.
Next add Label statements to the script with these names:
As always this is best described with an example:
!name Multiclick Test # the default value is 1000 milliseconds, override to 2 seconds !multiclick 2000 message Starting Click Test end label click message Click $clickCount end label endClick message Ending Click Test end
When you trigger the script for the first time, the message "Starting Click Test" is displayed. If you trigger the script again within 2 seconds, a message is displayed showing the number of triggers that have been received from the system variable clickCount. If you wait 2 seconds without triggering the script again, the "Ending Click Test" message is displayed and the script is finished.
Each time the script is triggered, the expiration period starts over at 2 seconds.
GEEK Note: As with sustain scripts, the click and endClick labels are called in different threads so the script may be active in more than one thread. Variables can be used for communication between threads.
This declaration is used to specify the name to be displayed for this script in the binding windows. If no name is specified, the file name is displayed. It is very common to use a !name declaration since the script file name is not always meaningful to the user.
The name consists of all characters after the space after the !name declaration up to the end of the line. In the following example, the name displayed for the script would be "Auto Record 4".
!name Auto Record 4
Like all declarations, !name may appear anywhere in the file but it is usually the first line to make it easier to see.
Normally scripts will not obey the Quantize Mode parameter in the preset. When a script is triggered it will be run immediately.
The !quantize option makes the script sensitive to the Quantize Mode parameter, it will be run at the next quantization point. This makes scripts behave more like built-in functions.
This option is most often used when the script does not begin with a Wait statement.
TODO: The difference between !quantize and Wait...
This option will allow the script to be triggered by a range of MIDI note, controller, or program change messages. The script is bound once to the center of the range, but will then be triggered by messages on either side of the center.
This option is usually used when binding to MIDI note messages. If the script is bound to middle C, then notes on either side of middle C will automatically be bound to the script. This is similar to the way the Pitch Step and Speed Step functions behave. The note you select in the binding window is the center note, pressing notes below the center lowers the pitch/speed, pressing notes above the center raises the pitch/speed.
For spread scripts, the script will be run when any note in the range is pressed. The script can then reference internal variables to determine which note within the range was pressed.
While it will be most common to bind spread scripts to MIDI notes so that you can "play" the script chromatically, you can also bind the script to a range of program changes or continuous controller values.
To define a spread script, include this declaration at the top:
!spread
The default range of the spread is 48 values on either side of the center, which for notes means 4 octaves up and 4 octaves down. You can reduce this range by adding an argument to the !spread declaration:
!spread 4
In the previous example, the 4 argument means that the spread will be 4 notes on either side of the center.
If there is a conflict between a spread script binding and another function that is bound to a specific note, the other function will have priority. For example, if a spread script with a range of 12 is bound to note 64, the script will be called whenever notes 52 to 76 are received. If you also have the Record function bound to note 59, that note will call the Record function, but all the notes around it will still call the script. You can think of this like "holes being punched" into the spread range. You usually do not want to have a spread range conflict with another function binding so it is best to move the center notes so they do not overlap. If you want to have a large spread range, you will need to use different MIDI channels for the center notes.
When a spread script is called, the following system variables may be referenced to adjust the behavior of the script:
For example, if you bind the script to note 64 and you press note 63, the value of triggerOffset will be -1. If you press note 64 triggerOffset will be 0, and if you press note 65 triggerOffset will be 1.
The built-in Speed Step function could be implemented with this script:
!name Script Speed Step !spread 12 SpeedStep triggerOffset
Spread scripts are probably most useful with the SpeedStep, PitchStep, LoopTrigger, and TrackSelect functions. But you could also use them to set the output level or 8thsPerCycle parameter.
As a more interesting example, if you always want to speed shift in octave intervals, you could use this script and you would only need to reserve 9 notes:
!name Speed Step Octaves !spread 4 SpeedStep triggerOffset * 12
This option will cause the script to be run on both the down and up transitions of the trigger. A down transition happens when you press a switch, an up transition happens when you release a switch. A script that uses this option is called a sustain script. Sustain scripts are effective only if you are using a momentary trigger such as a MIDI note, MIDI continuous controller, or computer keyboard key.
To use MIDI notes, the trigger device must send a Note On event when a switch is pressed and a Note Off event when the switch is released. To use MIDI continuous controllers, the switch must send a CC value greater than zero when the switch is pressed and zero when the switch is released. Note that you can not use MIDI program changes for sustain scripts since the program change is only sent when the switch is pressed.
Sustain scripts are also run repeatedly while the switch is held down, this allows it to change behavior depending on how long the switch is held. This allows scripts to have long press behavior like other built-in functions.
To be notified when the trigger goes up, you simply add this label to the script:
label endSustain
The statements following this label will be run when the function trigger goes up.
To be notified while the trigger is held, add this label to the script:
label sustain
The statements following this label will be run every 250 milliseconds (1/4 second) while the trigger is held. You can determine how long the trigger has been held by testing the sustainCount variable. This will have a number starting at 1 and incrementing by 1 every time the sustain script is called.
You can specify the duration of the sustain notifications by adding a number after the !sustain keyword. This number is the number of milliseconds between notifications. For example, this declaration will cause the sustain label to called every second rather than every 1/4 second.
!sustain 1000
Here is a more complex example that demonstrates all of the features of sustain scripts.
!name Sustain Test # default is 250 msec, raise it to 1 sec !sustain 1000 # the Message statement can be used to display brief messages message Starting sustain test end label sustain message Sustained $sustainCount end label endSustain message Ending sustain test end
You can think of this as 3 scripts in one. From the top to the first end are the statements that are run when the switch is pressed.
The statements from label sustain to the next end are called as the switch is held.
The statements from label endSustain to the next end are called when the function trigger goes up.
You do not need to declare both labels, but you do need to remember to put an end before each of the sustain labels.
GEEK Note: Scripts are similar to a re-entrant function in a multi-threaded programming language. Each of the calls for the down transition, up transition, and sustain notifications are done in an environment similar to a thread. If you use Wait statements, it is possible for the script to be active in more than one thread. By using variables, the threads can communicate with each other.
Here is a more realistic example. A long-press of the Record function will normally perform a Reset. This script does something similar but with the Mute function.
!name Mute/Reset Mute end label sustain if sustainCount = 1 Reset endif
Under the sustain label, you can test the sustainCount variable to make the script change behavior the longer the trigger is held. Some examples of this might be:
Here is another example of simple sustain script that raises the speed a fifth while held and drops it back when released.
!name SUSSpeedUp5 SpeedUp 7 end label endSustain SpeedDown 7 end
When you change loops with the switchQantize parameter set something other than off, Mobius will enter a special Switch mode while it waits for the next switch quantization point. During this mode, many functions that you trigger are queued and performed after the loop switch.
By default scripts do not wait until after the loop switch, they are run immediately. The !switchQuanitze option will delay the execution of the script until after the loop switch. This makes scripts behave more like built-in functions.
Note however that the functions performed by the script are not treated like alternate endings to the switch function. For example, if you do an Overdub during the switch quantization and the next loop is empty, the current loop is copied into the next loop. But if you run a script whose first function is Overdub, this will not cause the loop to be copied.
The syntax for each statement will be summarized using a notation that programmers should recognize as similar to BNF. For example:
call <scriptName> | <procName> [<arg> ...]
A line in a script is divided into tokens separated by one or more spaces. The syntax notation describes what each token means. Tokens are often just words without spaces like call. If a token needs to contain spaces it must be surrounded in double quotes like "My Script".
In the syntax notation, tokens that are not surrounded by angle brackets must be written exactly as shown. Tokens that are surrounded by angle brackets must be replaced with tokens that are meaningful for your environment. For example the token <scriptName> would be replaced by the name of the script you want to call.
The vertical bar is used when there is a choice of several tokens that may appear at this location. In this example the token following the Call may be either a procedure name or a script name.
Square brackets are used to surround optional tokens. In this example the call statement may have an optional list of arguments. The ... means that the contents within the brackets may be repeated any number of times.
Two special tokens are used in syntax notation:
Note that some of the intrinsic statements are intended for use only in special testing scripts. The statement keywords are presented using the character case that is most often seen in the example scripts. As mentioned in the Case Insensitivity section, case in function names is ignored.
This statement is used only by developers running Mobius under a debugger. It will have no effect when running Mobius outside of a debugger. Return to your homes, there is nothing to see here.
call <scriptName> | <procName> [<arg> ...]
Calls another script or a procedure, with optional arguments.
When a script is called, the statements in the script are executed as if they were written directly in the calling script. Only scripts that have been registered in the Scripts dialog can be called.
Normally scripts are referenced using the name defined by the !name declaration in the script. If the script has no !name you may reference the script using its file name.
# Call a script using its !name # note that quotes must be used if the script name contains spaces call "My Script" # Call a script using its file name call myscript.mos # Call a script using its file name without the .mos extension call myscript
Script arguments may either be symbols, strings, or variable references. A symbol is simply a word without spaces. A string is a sequence of characters surrounded by double quotes. A variable reference is the name of a variable or parameter prefixed with the $ character.
# Call a script passing a symbol argument call "Set Sub Cycles" 8 # Call a script passing a string argument call ShowMessage "This is my message to you!" # Call a script passing a variable value variable message "This is my message to you!" call ShowMessage $message
In the script being called, arguments are referenced with a number preceded by the $ character. Script arguments are numbered starting from 1. For example:
!name Set Sub Cycles message Changing subcycles to $1 set 8thsPerCycle $1
The previous script will set the 8thsPerCycle preset parameter to the value of the first argument.
Note that scripts that are designed to be called from other scripts usually do not have any declarations other than !name and sometimes !autoload. Any other declarations in the called script will be ignored. If you want the called script to be quantized for example, you will need to use the wait statement in the calling script.
A procedure is a set of statements defined within a script that will not be executed until you call them. Procedures are defined with the proc statement. Procedures are used in the Mobius test scripts but are not often used in user scripts. Their primary use is to prevent the duplication of a set of statements that are needed more than once in the script.
# define a procedure # the statements within the Proc/Endproc will NOT be executed # until you call the procedure # Define a procedure proc ChangeDirection if inReverse Message Leaving reverse mode else Message Entering reverse mode endif Reverse endproc # toggle reverse mode with a message call ChangeDirection # wait for the end of the loop wait loop # toggle reverse mode with a message again call ChangeDirection
Arguments may also be passed to called procedures:
# set 8thsPerCycle to the value of the first argument proc SetSubCycles set 8thsPerCycle $1 endproc # call procedure passing number of subcycles desired call SetSubCycles 8
if <condition> block { elsif <condition> block } [ else block ] endif
Executes a block of code if a condition is met.
A condition is a Boolean Expression. A Boolean expression is an expression that is interpreted as one of only two distinguishable values: True or False.
bool_expression = bool_term { or bool_term } or = | | || bool_term = bool_factor { and bool_factor } and = & | && bool_factor = bool_constant | comparison | not bool_factor | ( bool_expression ) bool_constant = False | True not = not | ! comparison = arithmetic_expression relation arithmetic_expression relation = equal | < | <= | > | >= equal = = | == arithmetic_expression = term { (+|-) term} term = factor { (*|/) factor} factor = constant | variable | ( arithmetic_expression ) variable = mobius_variable, e.g. mode constant = numeric integer or float constant(see Jeff Laron's post)
A real-world example employing if, else, endif
# A script duplicating the Record function including long press to erase a loop. # In addition we're sending arbitrary MIDI controller messages to notify a # connected pedal board with display capabilities. !name Record And Signal # Mobius-Script # behaves like built-in Record but sends signal on events: # - start recording # - stop recording # - delete # # default is 250 msec, raise it to 500 msec !sustain 500 if mode = reset || mode = play Record MidiOut control 15 127 70 else Play MidiOut control 15 126 70 endif end label sustain if sustainCount = 1 Reset MidiOut control 15 125 70 endif end
proc <procedureName>
This statement begins a procedure definition. The argument must be a literal string that defines the procedure name. The procedure definition must be terminated by an endproc statement.
See Procedures for more information on defining and calling procedures.
All Mobius functions may be used in scripts. The names of the functions used in scripts may differ slightly from the names you will see in the UI. In particular, script function names never contain spaces. If a function name is displayed in the UI with spaces such as "Next Loop", the script function name is usually the same with the spaces removed such as "NextLoop".
The function reference section of the Mobius Reference manual contains a complete list of all functions arranged in alphabetical order by the display name you will see in the UI. Within the documentation for each function will be a line beginning with Internal name:, the word after this prefix is the name you would use in a script to execute this function.
Note that the function names displayed in the UI are taken from a file that may be modified by the user. This file is called a message catalog. The default message catalog is in the Mobius installation directory and is named Catalog_USEnglish.txt. If you have modified this file, or if you have asked Mobius to read messages from a different file, the names that appear in the reference manual may not be the same as what you will see in the UI.
We will not provide reference documentation for every function in this manual. We will only document functions and function features that are only accessible from scripts. For more information on what the functions do and how they are influenced by parameters, you must read the documentation in the reference manual.
The function named just Divide without a number is accessible only scripts. With this function the divisor is specified as an argument written after the function name. The argument may be a number or an expression. For example:
Divide 6 Divide subcycles / 2
In scripts you can also use the Divide functions that have a fixed divisor in the function name. These two script statements will have the same effect.
# here the divisor is part of the function name Divide4 # here the divisor is an argument Divide 4
See Divide in the Mobius Reference manual for more information.
Backward forces the loop direction into reverse and sets the Reverse minor mode. This function is provided as a convenience for script writers that need to ensure the loop is playing backward without having to test the inReverse variable. The following two code blocks have the same behavior.
# Force reverse playback Backward # Toggle reverse if we're not already in reverse if !inReverse Reverse endif
See Backward in the Mobius Reference manual for more information.
Forward forces the loop into the normal forward direction. It is provided as a convenience for script writers that need to ensure the loop is playing forward without having to test the inReverse variable. The following two code blocks have the same behavior.
# Force normal playback Forward # Toggle reverse if we're currently in reverse if inReverse Reverse endif
See Forward in the Mobius Reference manual for more information.
SpeedCancel forces the loop out of all speed shifting modes: SpeedToggle, SpeedOctave, SpeedStep and SpeedBend. This is provided as a convenience to script writers that need to ensure the loop is playing at normal speed without having to test the inHalfspeed or speedToggle varialbes or the speedOctave, speedStep, and speedBend controls. The following two code blocks have the same behavior:
# Force normal playback SpeedCancel # Toggle speed if we're currently in 1/2 speed if inHalfspeed SpeedToggle endif
See Speed Cancel in the Mobius Reference manual for more information.
Halfspeed changes the loop speed so that it plays twice as long and sounds one octave lower. It is an older function that is provided for backward compatibility. If you are writing scripts it is recommended that you use the more general SpeedStep function. Executing Halfspeed is identical to executing SpeedStep with an argument of -12.
The following threecode blocks have the same behavior.
# Force 1/2 speed playback Halfspeed # Force 1/2 speed playback SpeedStep -12 # Toggle speed if we're not currently in 1/2 speed if !inHalfspeed SpeedToggle endif
See Halfspeed in the Mobius Reference manual for more information.
The function with internal name InstantMultiply without a number may only be used in scripts. The multiplier is specified as an argument to the function. For example:
InstantMultiply 6
You can also use the InstantMultiply functions with numbers in their names in scripts. These two script statements will have the same effect.
# here the multiplier is part of the function name InstantMultiply4 # here the multiplier is an argument InstantMultiply 4
See Instant Multiply in the Mobius Reference manual for more information.
The function named just Loop without a number is accessible only in scripts. With this function the loop you want to trigger is specified as a function argument, which may be a number or an expression. For example:
Loop 3 Loop loopNumber + 1
Scripts can also use the Loop functions with numbers in their names. These two script statements are the same.
Loop4 Loop 4
See Loop in the Mobius Reference manual for more information.
MidiOut can send any MIDI message to the configured MIDI output device. This function is accessible only from scripts. The MIDI message to send is specified with up to four function arguments. The syntax is:
MidiOut <status> <channel> <value> <velocity> status: noteon noteoff control program stop start continue channel: 0-15 value: 0-127 velocity: 0-127
For example, to send a Note On event on channel 3, note 64, velocity 127, you would write:
MidiOut noteon 3 64 127
This function does not change the current loop and has no effect on major or minor modes.
NOTE: Due to an unconfirmed bug (as of Feb 19, 2024), this function only works if MIDI Through Devices is set either to "(none)" or to the device you want to send MIDI to. (see MIDI Device configuration)
Move will instantly move the loop playback position to a new location. It is accessible only from scripts. The new position is specified as a function argument which may either be a number or an expression whose result is a number. This number is used as a frame number within the loop. The first frame in a loop has frame number zero.
# Jump to a fixed location 1 second from the beginning Move 44100 # Jump to the center of the loop Move loopFrames / 2
See Move in the Mobius Reference manual for more information.
MuteOff will cancel the Mute minor mode. If the loop is not in mute mode the function has no effect. This function is not quantized and is accessible only in scripts. It is a convenience for script writers that want to make sure that mute mode is off without having to worry about the current mute state. You can also use the Mute function but since that toggles mute mode you have to test the current mute state. The following script examples are equivalent:
# Force mute off, ignore if it is already off MuteOff # Toggle mute off if it is currently on if inMute Mute endif
See MuteOff in the Mobius Reference manual for more information.
MuteOn will activate the Mute minor mode and immediately silence the loop. If the loop is already muted the function has no effect. This function is not quantized and is accessible only in scripts. It is a convenience for script writers that want to make sure that the loop is muted without having to worry about the current mute state. You can also use the Mute function but since that toggles mute mode you have to test the current mute state. The following script examples are equivalent:
# Force mute on, ignore if it is already on MuteOn # Toggle mute on if it is currently off if !inMute Mute endif
See MuteOn in the Mobius Reference manual for more information.
OverdubOff forces the Overdub minor mode off. This function is not quantized and is accessible only scripts. It is provided as a convenience for the script writer that needs to turn overdub off, but is more convenient than using the Overdub function that toggles overdub mode. The following two script examples are equivalent:
# Force overdub off OverdubOff # Toggle overdub off if it is on if inOverdub Overdub endif
See OverdubOff in the Mobius Reference manual for more information.
OverdubOn activates the Overdub minor mode. If overdub mode is already active the function has no effect. This function is not quantized and is accessible only in scripts. It is a convenience for the script writer that needs to turn overdub on, but is more convenient than using the Overdub function that toggles overdub mode. The following two script examples are equivalent:
# Force overdub on OverdubOn # Toggle overdub on if it is off if !inOverdub Overdub endif
See OverdubOn in the Mobius Reference manual for more information.
PitchBend will change the playback pitch of the loop up or down in small intervals. The number of intervals is specified as an argument to the fucntion. While this can be called as a function it is expected that pitch bend be usually performed by changing the value of the pitchBend control. Neither the function or the control are quantized.
The range of values is from -8192 to 8181 to match the MIDI pitch wheel.
# change the pitch with the PitchBend function PitchBend 2091 PitchBend -4087 # change the pitch with the pitchBend control set pitchBend 2091 set pitchBend -4087
See Pitch Bend in the Mobius Reference manual for more information.
PitchOctave will change the playback pitch of the loop up or down in octave intervals. The number of octaves is specified as an argument to the fucntion. The octave can also be changed with the pitchOctave control. The difference between the control and the function is that the functions may be quantized but the control is never quantized.
# setting pitch with functions PitchOctave 2 PitchOctave -1 # setting pitch with controls set pitchOctave 2 set pitchOctave -1
See Pitch Octave in the Mobius Reference manual for more information.
PitchStep can change the playback pitch of the loop up or down to achieve a pitch change in a number of semitones. The number of semitones is specified as an argument to the function. You may also change the pitch step by setting the pitchStep control. The function may be quantized but the control is never quantized.
# setting pitch with functions PitchStep 5 PitchStep -7 # setting pitch with controls set pitchStep 5 set pitchStep -1
See Pitch Step in the Mobius Reference manual for more information.
SpeedBend will change the playback speed of the loop up or down in small intervals. The number of intervals is specified as an argument to the fucntion. While this can be called as a function it is expected that speed bend be usually performed by changing the value of the speedBend control.
The range of values is from -8192 to 8181 to match the MIDI pitch wheel.
# change the speed with the SpeedBend function SpeedBend 2091 SpeedBend -4087 # change the speed with the speedBend control set speedBend 2091 set speedBend -4087
See Speed Bend in the Mobius Reference manual for more information.
SpeedOctave will change the playback speed of the loop up or down in octave intervals. The number of octaves is specified as an argument to the fucntion. The octave can also be changed with the speedOctave control. The difference between the control and the function is that the functions may be quantized but the control is never quantized.
# setting speed with functions SpeedOctave 2 SpeedOctave -1 # setting speed with controls set speedOctave 2 set speedOctave -1
See Speed Octave in the Mobius Reference manual for more information.
SpeedStep can change the playback speed of the loop up or down to achieve a pitch change in a number of semitones. The number of semitones is specified as an argument to the function. You may also change the speed step by setting the speedStep control. The function may be quantized but the control is never quantized.
# setting speed with functions SpeedStep 5 SpeedStep -7 # setting speed with controls set speedStep 5 set speedStep -1
See Speed Step in the Mobius Reference manual for more information.
SpeedToggle is similar to SpeedStep in that it changes the speed of the loop up or down by some number of semitones specified as an argument to the function. The difference is that if you execute SpeedToggle a second time without an argument, the shift established by the previous call to SpeedToggle is canceled.
In the following example we raise the speed by 5 semitones, wait for the end of the loop, then lower it by 5 semitones.
# setting the speed toggle SpeedToggle 5 Wait loop # cancel the speed toggle SpeedToggle
See Speed Toggle in the Mobius Reference manual for more information.
The Sample family of functions will play one of the loaded samples. The function named just Sample without a number may only be used in scripts. The sample to trigger is specified as a function argument that may be a number or an expression.
# trigger the first sample Sample1 # another way to trigger the first sample with the number # specified as a function argument Sample 1
See Sample in the Mobius Reference manual for more information.
The Slip family of functions will instantly move the loop playback position forward or backward by a fixed amount. The amount of slippage for the Slip script function is specified as an argument. The value is a positive or negative number or an expression that evaluates to a number. This number is the number of 'units' to slip where the unit is defined by the Slip Mode parameter.
# slip forward 2 cycles set slipMode cycle Slip 2 # slip backward four subcycles set slipMode subcycle Slip -4
See Slip in the Mobius Reference manual for more information.
Explain how sustain functions can be used in scripts with the up/down arguments.
TimeStretch is available only in scripts. It will change both the playback speed and the amount of pitch shift such that the observed pitch stays the same but the playback speed increases or decreases. This function must have a script argument that specifies the number of units of stretch. Time stretch may also be accomplished with the timeStretch track control. The function may be quantized, the control is never quantized.
The range of values is from -8192 to 8181 to match the MIDI pitch wheel.
# change the duration with the function TimeStretch 2091 TimeStretch -4087 # change the duration with the control set timeStretch 2091 set timeStretch -4087
See Time Stretch in the Mobius Reference manual for more information.
The Track family of functions will activate one of the numbered tracks. If the track is already active, the function will have no effect. The function named just Track can only be used in scripts where the track to select is specified as a function argument.
# Select a track using a numbered function Track2 # Select a track using an argument Track 2 # Select a track using an expression Track rand(1,8)
See Track in the Mobius Reference manual for more information.
For a complete description of the concept of loop windowing see the Loop Windowing section of the Mobius Techniques manual.
You may use any of the bindable windowing functions in scripts, but there are two functions available only in scripts that can be used to contorol the loop window. WindowMove sets the starting location of the window and WindowResize adjusts either the starting or ending edges of the window. These functions use arguments to specify the location and size of the window.
The syntax of the WindowMove function is:
WindowMove [unit] amount
The unit argument is optional and if not specified defaults to the value of the Window Slide Unit parameter. The possible values in scripts are loop, cycle, subcycle, msec, frame, layer, start, and end.
# move back one loop set windowSlideUnit loop WindowMove -1 # move back one loop independent of the windowSlideUnit parameter WindowMove loop, -1 # move forward 1 second WindowMove msec, 1000 # move backward 2 subcycles WindowMove subcycle, -2 # orient the window at the beginning of the previous layer WindowMove layer, -1 # orient the window at the beginning of the loop history WindowMove start # orient the window at the end of the loop history WindowMove end
Note that in these examples a comma is required if you are using negative amounts. This is one of the rare functions whose argument list is completely parsed as an expression (Shuffle is another), and since operator precedence is like C, "x -1" is the same as "x - 1" which means to subtract 1 from x. So the arguments "subcycle -1" would be parsed as subtracting 1 from the variable named subcycle. To prevent this you need to put a comma between them, "subcycle,-1", or surround -1 in parantthesis, "subcycle (-1)". It is easier to use commas.
The start and end units are unusual becase they are not relative units for sliding, they are absolute locations within the history and do not need an amount.
The syntax of the WindowResize function is:
WindowResize {start | end} [unit] amount
The first argument must be start or end to indiciate which edge you want to move. The unit and amount arguments are the same as those for the WindowMove function. If the unit is not specified, the value of the Window Edge Unit preset parameter is used.
# Move the left edge back one subcycle WindowResize start,subcycle,-1 # Move the right edge forward one second WindowResize end,msec,1000
Describe in general how parameters can be accessed and set from scripts..
When a parameter value is described as being a "comma separated list" it means that the value to assign in a set statement consists of a list of words separated by a comma. There may also be spaces before and after the commas. The following examples are all the same:
set altFeedbackDisable Multiply,Overdub set altFeedbackDisable Multiply, Overdub set altFeedbackDisable Multiply , Overdub
Each track has a few parameters that are set in the Track Setup. These will have their initial values specified in the Track Setup but they may be changed in scripts.
Value value: true, false
This parameter determines whether the track has focus lock. The parameter may be set with the Focus Lock function or by clicking on the Track Number in the track strip.
Script example:
set focus true FocusLock
Value: an integer from 1 to 8
This parameter determines whether the track is a member of a track group. The value is an integer parameter that has the number of the track group in the current track. If the value is zero there is no track group. Positive group numbers are displayed as letters in the user interface. Group 1 is displayed as A group 2 is displayed as B etc. When setting this parameter from a script you may use either numbers staring from 1 or letters starting from A.
Script example:
set group 1 set group A
Value: a preset name
This parameter has the name of the preset that is active in the track. This is intended for use only in scripts, in the user interface you select presets from the preset menu or from the preset window.
When setting this from scripts you may use either the preset name or the preset number from 1. While setting this parameter in scripts is allowed, it is preferable in scripts to use the Preset built-in script function.
Script example:
set preset 1 set preset "My Preset" Preset 1 Preset "My Preset"
Value: an integer from 1 to infinity
This parameter has the number of the preset that is active in the track. This is intended for use only in scripts, in the user interface you select presets from the preset menu or from the preset window.
When setting this from scripts you may use either the preset name or the preset number from 1. While setting this parameter in scripts is allowed, it is preferable in scripts to use the Preset built-in script function.
Script example:
set presetNumber 1 Preset 1
The parameter is functionally the same as Preset but using it makes it clearer in the script that the value will be a number rather than a name.
Examples of setting track controls from scripts.
Number of times the script has been reentered due to multi-clicks.
The MIDI channel number of the trigger event. This is also embedded in triggerNumber, but it is easier to use here.
The MIDI key/controller number of the trigger event.
The type of MIDI trigger: note, control, program.
The same as triggerValue but has a more obvious name for use in !controller scripts.
The return code of the last ThreadEvent. Currently used only by Prompt statements to convey the selected button. 0 means Ok, 1 means cancel.
Number of times the script has been notified of a sustain.
The unique id of the trigger. For FunctionSourceMidi this will be a combination of the MIDI status, channel, and number. For other sources it will be a key code or other simple number.
An optional extra value associated with the ranged triggers. This will have the relative position of the trigger from the center of the range.
Alias: triggerVelocity
An optional extra value associated with the trigger. For MIDI triggers this will be the second byte, the note velocity for notes or the controller value for controllers.
The number of cycles in the loop.
The current frame relative the current cycle.
The number of frames in one cycle.
The current cycle number, relative to the beginning of the loop.
The number of layers in the current loop. This is also in effect the current layer number since we are always "on" the last layer of the loop. This does not include the number of available redo layers.
The current loop count. This is effectively the same as the "moreLoops" parameter but I like this name better. This should really be an alias of moreLoops so we can get and set it using the same name!!
The current record frame. The value of this variable changes automatically as the loop plays. You might think that setting this variable would cause the playback position to move but this is not allowed. Instead you must use the Move function to change the playback position.
The number of frames in the loop.
The number of the current loop within the track. The first loop number is 1 for consistency with the trigger functions Loop1, Loop2, etc.
The number of redo layers in the current loop.
The current subcycle number, relative to the current cycle.
The current frame relative the current subcycle.
The number of frames in one subcycle.
The number of subCycles in a cycle. This is actually the same as the Subcycles preset parameter and can change with the preset, but we expose it as an internal variable so it is consistent with the other loop variables.
Returns the type name of the next event. Child events are ignored so we will skip over JumpPlayEvents. Now that we have this, could eliminate InReturn and InRealign.
Returns the function name associated with the next event. We subclass NextEventVariableType for the getTrackValue logic.
The number of the next loop if we're in loop switch mode. Loops are numbered from 1. Returns zero if we're not loop switching.
The value of the feedback currently being applied. This will usually be either the Feedback or Secondary Feedback control values depending on which is being used. It will always be zero if we're in Replace, Insert or another mode that does not bring forward any content from the previous loop.
True if half-speed is enabled. From version 2.2 onward this is true only if the value of speedToggle is -12.
True if playback is muted. This usually means that we're also in Mute mode, but if Overdub is also on, mode will be Overdub. Note also that this tests the isMute flag which can be on for other reasons than being in Mute mode.
True if overdub is enabled. Note that this doesn't necessarily mean that the mode is overdub, only that overdub is enabled when we fall back into Play mode.
True if we're in Pause or Pause mode. This is available because the "mode" parameter is not always set to Pause. Once case is if Pause and Overdub are on at the same time mode will be Overdub (I think this is the only case).
True if we're realigning. This similar to a mode, but it is indicated by having a Realign event scheduled.
True if we're in "return" mode. This is a special minor mode that happens after a loop switch with SwitchDuration=OnceReturn, SwitchDuration=SustainReturn, or the RestartOnce function. It is indicated by the presence of a pending Return event.
True if reverse is enabled.
True any form of recording is being performed. Note that this does not necessarily mean you are in Record mode, you could be in Overdub, Multiply, Insert, etc.
This variable will contain the name of the current major mode. The possible values are:
Speed shift, expressed as a scale degree. This is provided for backward compatibility with pre-2.2 scripts. This is now the same as the value of the speedStep track control.
Total pitch shift, expressed as a float times 1000000.
Total speed shift, expressed as a float times 1000000.
The current amount of toggled speed shift being applied to the track. If this is -12 it is the same as inHalfspeed being true.
The current index into the speed sequence for the track. The index starts at zero and has a maximum value of one less than the length of the speed sequence.
This is one of the few variables that may be set in scripts to change the sequencnce position.
The current index into the pitch sequence for the track. The index starts at zero and has a maximum value of one less than the length of the pitch sequence.
This is one of the few variables that may be set in scripts to change the sequencnce position.
True if the track will be unmuted when Global Mute mode is over.
The number of the track operating as the output sync master, -1 if there is no master.
True if the track will be unmuted when Global Mute mode is over.
The number of the current track. The first track is 1.
The number of tracks configured.
The number of the track operating as the track sync master, -1 if there is no master.
The actual Loop frame at the last pulse. The difference between this and syncPulseFrame is the amount of drift (after wrapping).
The current bar count. This will be the same as syncOutBar, syncInBar, or syncHostBar depending on the SyncMode of the current track.
The current bar relative beat count. This will be the same as syncOutBeat, syncInBeat, or syncHostBeat depending on the SyncMode of the current track.
The number of sync drift corrections that have been performed since the sync tracker was locked.
The number of external sync pulses counted during recording.
The number of frames the current track is dealigned from the sync tracker for this track.
The current amount of drift, positive or negative.
The number of sync drift checks that have been performed in this loop since it was recorded.
The length of the sync tracker loop (external loop) in frames.
The loopFrame prior to the last Realign.
The number of sync pulses we have received at the moment in the external loop.
The length of a sync pulse in frames. The value is a float. Since tracks may have different sync sources, the pulse width may also be different for each track.
The total number of sync pulses in the external loop.
The current absolute beat count. This will be the same as syncOutRawBeat, syncInRawBeat, or syncHostRawBeat depending on the SyncMode of the current track.
The current sync tempo. For Sync=Out this is the tempo we calculated. For Sync=In this is the tempo we're smoothing from the external source. For Sync=Host this is the tempo reported by the host.
The current bar count maintained by the internal clock. This is calculated from the raw beat count, modified by the effective beatsPerBar.
The current beat count maintained by the internal clock, relative to the bar.
The current raw beat count maintained by the internal clock. This will be zero if the internal clock is not running.
"true" if we are currently sending MIDI clocks, "false" if not.
"true" if we have send a MIDI Start message, "false" if not.
The number of MIDI Start messages we've sent since the last time we were stopped.
The tempo of the internal clock used for out sync. This is the same value returned by "tempo" but only if the current track is in Sync=Out or Sync=OutUserStart.
The current bar count derived from the external MIDI clock.
The current beat count derived from the external MIDI clock, relative to the bar.
The current beat count derived from the external MIDI clock.
True if we are currently receiving MIDI clocks.
True if we have received a MIDI start or continue message.
The tempo of the external MIDI clock being received. This is the same value returned by "tempo" but only if the current track SyncMode is In, MIDIBeat, or MIDIBar.
The current bar count given by the host.
The current beat count given by the host, relative to the bar.
The current beat count given by the host.
True if we are currently receiving MIDI clocks from the host. Synchronizer has always returned false here, I guess because we make MIDI passing through the host look like MIDI In sync rather than host sync.
True if we have received a MIDI start or continue message from the host. Like syncHostReceiving we've always returned false here.
The tempo advertised by the plugin host.
The number of frames in one audio interrupt block.
Base directory where Mobius configuration is saved. Typically c:\Program Files\Mobius 2 on Windows and /Library/Application Support/Mobius 2 on Mac.
Base directory where Mobius has been installed. Typically c:\Program Files\Mobius 2 on Windows and /Applications/Mobius 2 on Mac.
When set disables the pass through of audio received on the first port. This is used in the unit tests that do their own audio injection, and we don't want random noise coming in from the sound card to pollute it.
The number of frames in the last sample we played.