The Function module enables you to write your own script to control what the module does. The scripting "language" is familiar calculator script, so you can start using it right away without needing to invest time learning a programming language.
Here is an example of a script that averages 3 inputs and sends the result to the output:
Out sends a value to an output:
Out(Num, Value) - sends Value to output number Num.
In1, In2, and In3 return the values from inputs 1 to 3.
So, inputs 1, 2, and 3 are being added, then divided by 3 to get the average, and then sent to Output 1.
Creating a Function module
To create the above Function module, right click on the Workspace to get the pop up module menu, and select Function . Click on the module's upper right Property button to open the Property dialog. In the General tab, you can give the module a name, such as "Average3," as well as a custom tool tip which will show 1-line module doc in the status bar when you mouse over the saved function in the popup module creation menu. If you put a comment in the larger text box it will show as a multline balloon type tool tip instead of the single tool tip line whenever you mouse over the module in the workspace.
You can also change the icon color. Icon colors in Function modules have no effect on the module, they exist only to provide visual grouping - you can set them to whatever color you like, or ignore them.
The default Function module has one input and one output; we want 3 inputs, so click on the Input tab and click More twice for a total of 3 inputs. As with other modules with a flexible number of inputs and outputs, you can enter custom labels and tool tips. The module already has 1 Output, so there is no need to change that.
Select the Function tab to enter the function script. When first created Function modules simply pass the input to the output of the module, so you will see the default function:
This text area is a simplified text editor, similar to Notepad. Highlight the existing function and hit backspace to delete it, then type in the averaging function (or you can simply copy it from this page and paste it in):
Click on the Test button to make sure there are no syntax errors - if nothing happens, the function is good; if you get a error message, correct the error ant Test again. Finally, click on OK to update the Function module and exit the Property dialog. Your new Function module will now average its 3 inputs and send the result to the output.
Function, Fn Reset, Fn Load
There are three Function script tabs. They are identical except for when the function runs.
A script in the Function tab runs much like other standard modules. You can select whether it should run always, or just upon a strobe or change at input 1, with the option menu at the bottom center of the Property dialog.
A script in the Fn Reset module runs only on the System Reset Flag - which occurs whenever the internal clocks are reset, same as when the Clock modules reset, etc.; and a script in Fn Load runs only on the Load Flag - which occurs when a Patch, Macro or Function file is loaded. You can also manually trigger Load and Reset with the toolbar buttons.
The purpose of the three different scripts is to provide setup functions when needed. the Fn Load script always runs first, then the Fn Reset, followed by the Function script. So you can initialize arrays or variables before they are used for the first time, and be assured they will be run prior to the Function scrip running.
Function Execution Options
The Function module executes its script on every clock tick by default, however this can be changedt to run only when a strobe is received in input 1, or only when input 1 is changed by selecting the option button on the bottom section of the module properties pop up:
Run Always - default action, executes on every clock tick.
Run on In1 Strobe - when selected, executes only when input 1 receives a strobe or clock-On.
Run on In1 Change - when selected, executes only when input 1 changes.
Additionally, the Reset and Load pages can be disconnected from the system Reset and Load flags, and forced to run ahead of the Function page when a strobe is received in input 2 (Reset page) or Input 3 (Load page). This is useful for conditional execution, for example when you want to reset the default values of a function that normally runs in the Function page:
FnReset when In2 Strobe - check to enable execution of Reset page script when input 2 receives a strobe.
FnLoad when In3 Strobe - check to enable execution of Load page script when input 3 receives a strobe.
These options are grayed out if the Function module does not have an input 2 or 3. Add the inputs and activate the options with the Inputs tab "More" button.
The execution order is: Load Page, Reset Page, Function Page. The Reset and Load pages only run on an input 2 or 3 strobe, but if either or both runs, the Function page will always follow, with input 2 or 3 acting like an input 1 strobe. In no event will pages run more than once per clock tick. So if inputs 1, 2, and 3 receive simultaneous strobes, each page will execute once on that clock tick: the Load page, then the Reset page, then the Function page.
The Fn Prototypes tab provides a quick reference to all built in functions. It serves both as a quick reference for functions, and as an aid to help prevent syntax errors
To see how the prototypes work, use it to find the reference for the Out function, used above. Start by placing the cursor in the Function tab edit window (or the Fn Reset or Fn Load edit windows). Then click on the Fn Prototypes tab, and click on the "+" icon of "Variables and Constants." The section opens and you will see the line:
Out(N, X) ~ Send X to module Out #N
Move the cursor to the line and click on it. You are returned to the editor window and the function text "Out(N, X)" is inserted at the cursor. Replace "N" and "X" with values or other functions that return values, and you have a working function.
Loading and saving functions
Function modules may be saved and loaded just as Macro modules. To save a Function module, click on the Save button in its properties.
To load a Function module right click on the module Workspace background, and select it from the popup menu. When you load the function module it will show up at the Workspace position where you right clicked.
The scripting language is a slightly expanded version of standard calculator script, that you use with hand held calculators. In general, you write functions as you would with a calculator, using parentheses to control grouping and precedence: as with calculators, calculation proceeds from inner parentheses to outer.
The comment character is a semicolon";" - everything after a semicolon on a line is ignored. Comments do not take space in the function compiler memory and they take no processing time, so you can and should use comments liberally. Here is an example of a commented line:
Out(1, In1) ; the default function sends input 1 to output 1
Functions are separated by spaces or lines. Generally it is a good idea to place each function in a script on a separate line, with a comment. You may have up to 128 separate functions per script, but you should avoid having more than a few lines of script per function module in order to maintain readability. The computation overhead in breaking a large function into several function modules is negligible, and it makes your script much easier to understand when you need to make changes to it later.
All functions and sub functions return a value when they are evaluated (run). Some functions are not run for the value they produce, but for the "side effect," and usually these functions will simply return 0 after doing whatever they do. Some functions return a different value if an error is encountered. The particular error behavior of a function will be listed in the function documentation.
Items separated by a comma are evaluated left to right, and the expression result is the last item evaluated. Within a comma field, items will be given the following operator precedence from highest to lowest:
Anything inside parenthesis is performed first () Factorial, percentage !, % Exponentiation ^ Negation (unary) - Multiplication, division *, / Integer division \ Other operators Modulo (remainder) MOD Addition, subtraction +, - Relational operators <, >, >=, <=, =, <> AND operator OR, XOR (exclusive or) EQV (equivalence) IMP (implication)
When consecutive operators have the same priority, they are evaluated from left to right. This means that an expression such as "a-b-c" is evaluated as "(a-b)-c". Generally, it is a good idea to make liberal use of parentheses to be sure your expression is being evaluated the way you want.
The compiler supports notations for binary, octal, and hexadecimal numbers. These numbers must be preceded by the character # followed by b, o, or h for binary, octal, or hexadecimal. The prefixes are part of the number following, so no space is allowed. With #h legal numerals are 0-9 and A-F; with #o, only 0-7 are allowed; and with #b only 0 and 1 are allowed.
Note that the numeric base format for function script is slightly different than for module inputs. The decimal value 127 can be entered into a module input as hexadecimal "&H7F" but the same value in calculator script would be "#H7F."
The default number type for the function compiler is double precision floating point, the same as MusicWonk values. Unless otherwise noted, you may assume functions, variables, etc. will be in that format and they will automatically be converted to and from other numeric types as needed.
Double precision floating point numbers have an enormous range: -1.79769313486232E308 to -4.94065645841247E-324 for negative values; 4.94065645841247E-324 to 1.79769313486232E308 for positive values.
Values for True and False
Logical values in calculator script are the same as generally used by MusicWonk modules. False is always 0 and True is anything else. Relational operators (>, <, <=, >=, =, <>) return a 1 for True.
Each Function module created has a set of private variables for use by any of the 3 function scripts (Function, Fn Reset, Fn Load) within it but not visible by other Function modules. These are the letter variables A to H, and J-Z, with I reserved as the Loop index. Note that letter variables H and J-Z are shared with the private array elements 100-127. For this reason, you should avoid either using private array elements 100-127, or avoid using the letter variables H and J-Z in any one particular Function module.
Since letter variables are private to the Function module, you can use the same letter names in other Function modules, and they will refer to only to the variables within that module.
To fetch the contents of a letter variable, simply use it:
Out(1, A) ; sends value stored in A to output 1
To store a value into a letter variable attach .(), as A.(3) to store 3 into A.
To access Function modules outputs, use Out(num, value), where num is the 1 based (starts with 1, not 0) output number and value is the value to send to it. To access Function module inputs, use In(num) with num as the 1-based input number. Properly speaking these are arrays, not variables. But they are so oftern used with letter variables, so we group them here.
Inputs 1-20 can also be accessed by using their variable-like aliases, In1 to In20.
Prototypes for Variables and Constants
A ~ Return value of variable A-H and J-Z ( H, J-Z are shared with local array elements 100-127)
A.(N) ~ Assign value N to variable A-H and J-Z ( H, J-Z are shared with local array elements 100-127)
I ~ Loop index when looping (0 based)
In(N) ~ Return value at module In #N (1 based, 1st input is 1 not 0)
In1 ~ In20 ~ Alias for In(1) - In(20)
Out(N, X) ~ Send X to module Out #N (1 based)
GetNumIns() ~ Returns number of inputs
GetNumOuts() ~ Returns number of outputs
Pi ~ 3.14159265358979
Phi ~ 1.61803398875
e ~ 2.718281828459
Ran ~ Random value between 0 and 1
The text functions give the ability to input text strings, to store them in local text variables, and to combine, or concatenate, the text strings. Other text manipulation functions, such as extracting a string from within a string, are available through the Text modules, by passing the text between the Text and Function modules.
The functions that return a text string (i.e. TxA and TxIn) may be used anywhere within a function where quoted text is called for. For example you could use an input to display a status message with:
One caution, when using the functions that return a string (TxA, TxIn, and directory names, but not the assignment functions) only use them within a function that calls for a string argument. Using string-returning functions outside of a function that requires a string will result in unpredictible behaviour and possibly even a crash.
TxA ~ Return the text of string variable A-D
TxA.("text") ~ Assign string "text" to text variable A-D
TxIn(N) ~ Retrn text string at module In #N (1 based)
TxOut(N, "text") ~ Send string "text" to Out #N (1 based)
AppDir ~ Return text string of Application directory
PatchDir ~ Return text string of Patch directory
MacroDir ~ Return text string of Macro directory
FunctionDir ~ Return text string of Function directory
ArrayDir ~ Return text string of Array directory
DataDir ~ Return text string of Data directory
NamesDir ~ Return text string of Names directory
+ ~ concatenate when placed between strings
The File/Link functions act both as regular file read/write functions that give great flexibility to the data format, and also they can be used to make direct communication links with multiple instances of MusicWonk, or with MusicWonk and other programs that support link files, such as the Analog Box2 soft synth. Links may be made with multiple applications on the same computer, or with different computers connected in a Local Area Network.
File/Links look like random read/write files, and in fact you can open any file as a link file, or you can ignore the link capability and just use the File/Link functions to access data files of your own choosing. To open a network connection, simply open the link file as a networked file.
To make a file link between two (or more) applications, open the same file with each application you want to link, then treat the data locations (records) as input and output channels. When sending data, simply write to the location, which effectively makes it an output channel relative to the application doing the writing. When reading data generated by another application, read from the location the external application is writing to. For example, one program might treat file location 1 as the outpt channel, writing data to that location; and it would treat file location 2 as the input channel, reading data from that location. Then a second program would treat the two locations the reverse: location 1 would be the input channel, and location 2 would be the output channel.
OpenFile(type="Double", fname="", handle=0) ~ Opens a file for linked read or write. Returns 0 if file is successfully opened, 1 on error. If the file does not exist, one will be created. Any opened files are closed automatically when MusicWonk exits, when a new patch is loaded, or when a handle is reused. All parameters are optional.
"Type" is a string literal or string variable that defines the data type of the file. Valid type names are:
- Byte - 1 byte per record, 8-bit unsigned integers 0-255, or text characters. Byte is the default type.
- Word - 2 bytes per record, 16-bit signed integers -32,768 to 32,767.
- Long - 4 bytes per record, 32-bit signed long integers -2,147,483,648 to 2,147,483,647.
- Single - 4 bytes per record, 32-bit signed single floating point- 3.402823E38 to -1.401298E-45 for negative values; 1.401298E-45 to 3.402823E38 for positive values.
- Double - 8 bytes per record, 64 bit signed double floating point -1.79769313486232E308 to -4.94065645841247E-324 for negative values; 4.94065645841247E-324 to 1.79769313486232E308 for positive values.
- (string) - set to type string by giving the length of the longest string you plan to use.
fname is the name of the link file to be opened. File names may be literal (in quotes) or a string variable. You can give a full name+path ("c:\directory\name.ext"), or a network name ("\\computer\alias\directory\name.ext"). You can also just give a name+ext and the link file will be placed in the same directory as the current patch. Finally, you can simply omit the name, and the file will be created with the name, "Link00.@@@" in the MusicWonk array directory (the number after "Link" is actually the handle you give it, which will default to 0 if you do not give a handle)..
handle is a value from 0 to 99 that defines the opened link file to be used with ReadFromFile() and WriteToFile() below. Defaults to 0. Use handle if you are going to use more than 1 link file at a time, up to 100 simultaneously opened files. It does not matter what number you assign the handle, as long as you use the same number for subsequent reads and writes. If you open a file with a handle that is currently in use, the open file is first closed before the handle is reused.
FileExists("filename") ~ returns 1 if filename exists, 0 if not found Works for any file, not just a link file.
GetLinkFileName(handle=0) ~ Returns handle's filename string as set by OpenFile()
CleanupLinkFiles(flag=1) ~ Delete link files on exit/load (0=none, 1=default only, 2=all)
WriteToFile(value, position=1, handle=0) ~ Write to file opened with OpenFile(). Positions are 1 based.
TxWriteToFile("string", position=1, handle=0) ~ Write text to file opened with OpenFile()
ReadFromFile(position=1, handle=0) ~ Read from file opened with OpenFile()
TxReadFromFile(position=1, handle=0) ~ Read text from file opened with OpenFile()
Functions modules have both private and global arrays.
A Private array, like a private variable, is available only to the scripts within a Function module, and not available to other Function modules. The default size of the local array is 128 elements, 0-99 available as array elements, and 100-127 reserved for the letter variables, but you can change that to whatever size you like with the DimLocal function. Just be sure not to use array elements 100 to 127, or to not use the letter variables H-Z. In addition to being private, the local array is persistent - whatever values you place into it, remain until changed; and the array is stored with the module when you save it. For this reason, while MusicWonk allows you to set the private array to any size you like, it is a good idea to keep the size small, no more than 1000 elements or so, and use Global arrays for really large arrays.
Global arrays are available to any Function module, and in fact are the same arrays used by the Array sequencer modules, so they are accessable to these modules as well. These arrays are assigned a handle, which may be from 100 to 999, for a total of 900 possible arrays. You should never give arbitary array numbers; instead always use an Ary# handle as returned by the NewArray() function, or by the array modules that return array numbers.
The first 100 arrays, numbered 0-99 are not used by the array manager and should not be used by modules or functions. They are available for compatibility with earlier versions of the software, but may be removed in a future version.
Global arrays are not saved with the module or function, but they may be saved and loaded with the SaveNameArray and LoadNameArray functions. You can give just a file name to place the array into a special Array directory; or you can give a full path + name to place the array file wherever you choose. Do not give the file extension, as MusicWonk arrays always have the extension "awa," which will be supplied by the function.
Note that while MusicWonk does not place constraints on the size of arrays, they will be constrained by the amount of memory available to the computer, and the amount of CPU time required to process array functions. As always, there are tradeoffs to be made, and and computer resources should be managed carefully.
Caution: some array functions such as creating new arrays and resizing them should not be run every clock tick; instead they should only be placed in the Load or Restart edit box, or in the Function box only if Run on Strobe or Run on Change is checked.
Prototypes for Private Arrays
Fetch(Index) ~ Return value at Index (0-127) of private array
Store(Index, Val) ~ Assign val to private array at Index (0-127)
DimLocal(Size) ~ Resize local array to new Size elements
GetDimLocal() ~ Return local array size (max element number)
Prototypes for Global Arrays
Get(Ary, Index) ~ Return global Ary value at Index (0 based)
Put(Ary, Index, Val) ~ Assign Val to global Ary at Index (0 based)
NewArray(Size=128) ~ Creates new global array of Size elements, returns array number
ResizeArray(Ary, Size) ~ Resize Ary to Size elements, preserves existing values
GetDimArray(Ary) ~ Return global Ary size (max element number)
CopyArray(fromAry, toAry) ~ Copy & resize toAray with fromAry data
RotateArray(Ary, Offset) ~ Shift elements so 0th starts at offset & wraps
SortArray(Ary, dir=0) ~ Dir=0 for descending (default); 1 for ascending
HSortArray(Ary, HTable) ~ Harmonic Sort, HTable 1-12 set with Ary module Dialog pop up
SmoothArray(Ary, Amt) ~ Smooth (low pass filter) array values, Amt range 1-100
ScatterArray(Ary, depth) ~ Scatter (shuffle) array values from 0 to depth
NormalizeArray(Ary, Range, Offset=0, Int=0) ~ Int=1 to force to integers
GetAverageArray(Ary) ~ Return the average of all elements in the array
GetHighestArray(Ary) ~ Return the index of the highest value in an array
GetLowestArray(Ary) ~ Return the index of the lowest value in an array
FindInArray(Ary, Value) ~ Return the index of the given value in an array, -1 if not found
FillArray(Ary,val=0,bump=0, dens) ~ Fill array with Val, inc by +/- bump, density >1 for holes
Ary1dCA(Ary, Rule) ~ 1dCA step on given array, using Rule (0-255), returns number live cells
AryLife(Ary, Rows, Cols) ~ Game of Life step on given array, returns number of live cells
OpenArray(Ary, start, length) ~ Insert empty elements into an array and resize it
CloseArray(Ary, start, length) ~ Delete elements in an array and resize it
MergeArray(Ary1, start, Ary2, start2=0, length=0) ~ Append Ary2 to end of Ary1, resizing Ary1
CopyToLocal(Ary) ~ Copy given Ary to local array, to be saved with module
CopyFromLocal(Ary) ~ Copy local array to given global array
LoadNameArray("name", Ary) ~ Load previously saved array into Ary & resize it as needed
SaveNameArray("name", Ary) ~ Save array to name-only in Array dir or to full path & name
Prototypes for Math
#h ~ Base prefix for hexadecimal: #h7F = 127
#b ~ Base prefix for binary: #b1110 = 14
Min(A, B) ~ returns smallest; also: Min(A, B, C...etc.)
Max(A,B) ~ returns largest; also: Max(A, B, C...etc.)
+ - * / ~ Arithmetic Operators: A + B
\ ~ Integer divide, truncates: A \ B
Mod ~ Modulus (remainder): A Mod B
Abs(N) ~ Absolute
Ceil(N) ~ Round Up
Int(N) ~ Truncate fractional part
Fract(N) ~ Return fractional part
Sgn(N) ~ Sign: returns ~1 if neg, 0 if 0, 1 if pos
A full set of double precision floating point math functions are available.
Prototypes for Powers
Sqr(N) ~ Square root
% ~ Percent: 35% returns 0.35
Fact(N) ~ Factorial (also N!): Fact(5) = 5! = 120
^ ~ Rase to power: 2 ^ 10 = 1024
Exp(N) ~ e ^ N: Exp(3) = 20.08
Exp2(N) ~ 2 ^ N: Exp2(3) = 8; exp2(1/12) = 12th root of 2
Exp10(N) ~ 10 ^ N: Exp10(3) = 1000
Log(N) ~ Natural log: Log(16) = 2.77
Log2(N) ~ Log base 2: Log2(8) = 3
Log10(N) ~ Log base 10: Llog10(100) = 2
Relational operators work on the parameters as a whole and except for IIF, they return True (1) or False (0)
IIF is a special function that is defined as: IIF(condition, ifTrue, ifFalse). If the value of condition is True, then the value of the ifTrue parameter is returned, else the value of the ifFalse parameter is returned. Both ifTrue and ifFalse parameters are evaluated, so this function can not be used as a switch.
Prototypes for Comparison
= ~ Returns True if equal: A = B
<> ~ True if not equal: A <> B
< ~ True if less than: A < B
<= ~ True if less than or equal: A <= B
> ~ True if greater than: A > B
>= ~ True if greater than or equal: A >= B
IIF(Condition, ifTrue, ifFalse) ~ all parms evalueated
These are bitwise operators that return results on a bit by bit basis. Numeric values are automatically converted to 32 bit integers for the operations, then converted back to double precision floating point to return the results.
Prototypes for Logical
Not(N) ~ Bitwise NOT: Not(12) = ~13
And ~ bitwise AND: (#b101 And #h1E) = 4
Or ~ Bitwise OR: (13 Or 6) = 15
Xor ~ Bitwise Exclusive OR: (9 Xor 3) = 10
Eqv ~ Bitwise Equivalence: (6 Eqv 9) = ~16
Imp ~ Bitwise Implication: (1 IMP 5) = ~1
A full set of double precision trig functions are available, including Arc (Inverse) and Hyperbolic functions. The default mode for trig functions is Radian; but you can change that by entering Radian or Degree or Grad (Gradient) for the mode you want. These functions return a number indicating the new mode: 1 for Radian, 2 for Degree, and 3 for Grad. If you want to query the current mode, use the special variable TrigMode (no parenthesis), which returns the current mode as above but does not change it.
Prototypes for Trig
Radian ~ Set mode to Radian (default)
Degree ~ Set mode to Degree
Grad ~ Set mode to Grad
TrigMode ~ returns mode 1, 2, or 3
Sin(N) ~ Sine
Cos(N) ~ Cosine
Tan(N) ~ Tangent
Asin(N) ~ Arc sine
Acos(N) ~ Arc cosine
Atan(N) ~ Arc tangent
Sec(N) ~ Secant
Cot(N) ~ Cotangent
Prototypes for Hyperbolic Trig
Sineh(N) ~ Hyperbolic sine
Cosh(N) ~ Hyperbolic cosine
Tanh(N) ~ Hyperbolic tangent
Coth(N) ~ Hyperbolic cotangent
Sech(N) ~ Hyperbolic secant
Csch(N) ~ Hyperbolic cosecant
Asinh(N) ~ Hyperbolic arc sine
Acosh(N) ~ Hyperbolic arc cosine
Atanh(N) ~ Hyperbolic arc tangent
Acoth(N) ~ Hyperbolic arc cotangent
Asech(N) ~ Hyperbolic arc secant
Acsch(N) ~ Hyperbolic arc cosecant
These functions do not fit in any neatly defined catagory, so they are grouped toghther here.
You can loop functions, however the entire function as defined in the script page is looped; you cannot loop a single statment within a function. Since MusicWonk is modular, this is not a problem: to loop a particular statment, simply break it out into a separate Function module.
The Loop(number) function causes the script page it is on to loop number of times. It does not matter where in the script you place the function as the entire page of script is looped; however it is good practice to place it as the first statment, for readibility. When a script is looping, you can access the 0-based iteration number with the loop variable I.
MasterClock(clocknumber) reads the System AutoClock for the given clocknumber (2-192). These are countdown clocks, when they are 0 the clock fires. So you can read them as logical values, with False (0) to indicate the trigger point, or you can read the value to indicate the duration within the clock cycle.
StatusMessage(messagetext) prints the text string messagetext on the MusicWonk status message at the bottom of the screen. Since messagetext is a string, it must be in quotes, such as:
StatusMessage("Notification of process completed")
Prototypes for Special
Loop(N) ~ Cause function to immediately repeat N times
I ~ Loop index when looping (0 based)
MasterClock(N) ~ Reads timer countdown for Auto Sync N (2=192)
RGB( Red, Green, Blue) ~ Returns the 24 bit integer from the RGB components.
pRed(Color) ~ Returns the red part of a 24 bit RGB color integer.
pGreen(Color) ~ Returns the green part of a 24 bit RGB color integer.
pBlue(Color) ~ Returns the blue part of a 24 bit RGB color integer.
BoxWithin(left, top, width, height, tleft, ttop, twidth, theight) ~ true if box is within target box
StatusMessage(str$) ~ display given string on the status bar
LoadPatch(fName$) ~ load MusicWonk patch, returns 1 on success
Shell(fName$, Mode=1) ~ Run external program, returns handle or 0 on error
(Mode: 0=Hide, 1=Normal, 2=MinFocus, 3=MaxFocus, 4=NoFocus, 6=MinNoFocus)
MusicWonk is Copyright © 2003-2010 by John Dunn and Algorithmic Arts. All Rights Reserved