Topic
SAL
SAL is an infix syntax for working with Common Music. It provides an easy-to-learn, command-based language that does not require any familiarity with Lisp to work with. The name SAL stands for Simple Algorithmic Language (or Secretly Another Lisp, as you wish) and is a hommage to Sal Martirano, a brilliant and influential composer who was a professor at the UIUC School of Music for many years.
The SAL system consists of two software components: a lexer/parser
that is loaded into Common Music and an Emacs mode
(sal-mode) that supports editing and executing SAL
programs.
A SAL program consists of commands,
statements and symbolic expressions. A command
is a "top-level" statement that is executed to accomplish a
task such as triggering output, defining functions and musical
processes, loading files, and so on. A statement is a construct in the
SAL language, such as set, loop,
define, etc. A symbolic expression , or sexpr
is something that evaluates to a value.
SAL mode is an Emacs editing mode that provides an editing menu, syntax highlighting and evaluation services for SAL programs.
| Start CM | |
| Show REPL | |
| Execute (<Enter>) | |
| Debug | > |
| Use System | > |
| Show Directory | |
| Set Directory... | |
| Load File... | |
| Compile File... | |
| Import File... | |
| Play File... | |
| Help | > |
Sal mode colorizes program text:
;; Syntax highlighting in SAL mode. ; The 'define' command define process simp(num, rate, dur, lowkey, hikey, amp, chan) run repeat num output make(<midi>, time: now(), duration: dur, keynum: between(lowkey,hikey) , amplitude: amp, channel: chan) wait rate end ; The 'open' command open "test.mid", tempo: 90 ; The 'sprout' command sprout simp(16, .25, .5, 60, 70, .3, 0)
To evaluate a SAL command, place the cursor at the end of the statement and press Enter on the keypad. Output associated with the command will appear in the Lisp REPL window.
begin [var-decl] {statement}+ end Sequences one or more statements as a single statement. Local
variables can be declared using an optional with statement followed by a sequence of
one or more statements and terminated with the required
end tag.
begin with kn = random(128), pc = kn % 12 print "a random keynum: ", kn print "its pitch class: ", pc end
chdir dirChange the working directory to dir. The value should be a directory string or variable containing a directory string.
chdir "~/"
define variable name [= sexpr] {, name [= sexpr]}*Defines a global variable name with optional value sexpr. If sexpr is not provided the value of the variable defaults to false. Use commas to define more than one variable at a time.
define variable myrow = {0 1 11 2 10 3 9 4 8 5 7 6} , mytempo = pick(60, 90, 120, 144)
define function name ([parameter] {, parameter}*) {statement}A function definition consists of the name of the
function, a series of zero or more parameters, or input
variables, enclosed within () and followed by a single
statement which will be executed when the function is called. Values
passed to the function will be available in the corresponding
parameter. Use a begin
block to declare local variables or to include more than one statement
in the body of the function. Use the return statement to return one or more
values from a function. A function without a return statement returns #f as
its value.
define function rowtransp (row, pc) loop with l = {} for i in row set l &= (i + pc) % 12 finally return l end begin with rr = shuffle({0 1 2 3 4 5 6 7 8 9 10 11} ) , pc = random(12), tr = rowtransp( rr, pc ) print "row: ", rr, " int:", pc, " transposed: ", tr end
define process name ([parameter] {, parameter}*) {statement} Musical process definitions are very similar to functions except that they do not have
return statements and they must contain a run block as their final statement. The run block is syntactically identical to the loop statement except that it supports several additional statements specific to musical process definitions: output and wait.
define process foo(n, r) run repeat n for k = between(40, 70) output make(<midi>, time: now() keynum: k] if even?(k) then output make(<midi>, time: now() ,keynum: k + 12) else begin output make(<midi>, time: now(), keynum: k - 12) if odds(.4) then output make(<midi>, time: now(), keynum: k - random(12)) end wait random(r) end open "test.mid" sprout foo(20, .4)
exec sexpr {, sexpr}*Executes one or more comma-delimited sexprs. Sexprs should produce side effect(s) since nothing is printed or returned by the command itself.
exec cd()
if sexpr then [true-statement] [else false-statement]
Executes an optional true-statement if sexpr is true. Otherwise (sexpr is false) an optional false-statement is executed.
begin with d1 = random(6), d2 = random(6) if (d1 = 0 & d2 = 0) then print "Snake eyes!" else print "Loser :(" end
load fileLoads a Lisp or SAL source file into Lisp. File should be a pathname string or variable containing a pathname string.
load "mycomp.sal"
loop [var-decl] {stepping}* {stopping}* {action}+ [finally]
endDefines an iteration. Optional local variables can be declared using the with statement followed by zero or more stepping statements,
zero or more stopping statements, one or more action statements, an optional finally statement and terminated with the required end tag.
stepping statements repeatsexprsets the iteration limit to sexpr forvar=sexpr [thensexpr2]sets var to the value of sexpr on each iteration. If thenis specified sexpr is the initial value and sexpr2 are the subsequent valuesforvarinsexprincrements var over elements the list sexpr forvar [fromsexpr] [to|belowsexpr] [bysexpr]increments var from an on optional starting value to an ending value by an optional amount stopping statements whilesexpriteration continues while sexpr is true untilsexpriteration stops when sexpr is true conditional statements whensexpraction executed if sexpr is true untilsexpraction executed if sexpr is false action statements any SAL statement statements are executed, they do not return values finally statements finallystatementexecutes statement as soon as iteration has completed
loop with keys = {}, even = {}, sum = 0, lo = 128, hi = -1 repeat 10 for k = random(128) set keys &= k, sum += k, lo <= k, hi >= k when even?(k) set even &= k finally begin with avr = sum / 10.0 print "keys: ", keys, " even: ", even print "average: ", avr, " low: ", lo, " high: ", hi end end
open stream {, param: sexpr}* Opens a stream (connection) to a file, port, seq or plot. Stream should be a stream object or a pathname string. You can specify named parameters for the stream as appropriate to its type when you open it.
open "test.mid", tempo: pick(60, 90, 144), play: #t
output event Sends a musical event or list of the same to the open output stream. See define process for more information.
plot {plot | param: sexpr} [, ...] Generates a plot (graphical display) of data according to the specified arguments and the open plotting stream. Currently only Gnuplot streams are supported. See gnuplot for more information.
open "test.plt", title: "My envelopes" plot {0 1 100 0}, {0 0 50 1 75 1 100 0}
print sexpr {, sexpr}*Prints one or more comma-delimited sexprs. The printout appears in Common Music's REPL window.
print "You are a ", odds(.3, "winner!", "loser!")
return sexprReturns a value from a function. If a function definition does not
contain a return statement it will return boolean false as its
value. See define function for more information.
rts status Starts and stops CM's realtime scheduler. Status determines the status of the scheduler:
status meaning 1 start rts running using seconds as the scheduling time format 1000 start rts running using milliseconds as the scheduling time format -1 pause rts 0 stop rts :? print current status
rts 1 rts :?
run [var-decl] {stepping}* {stopping}* {statement}+ [finally]
end Almost identical in format to loop but the iteration is defined over time, by a musical process. The run block supports the following additional statements:
stepping statements meaning forvaroverpattern [by length]increments var over items from pattern. If byis specified then var is set to a list of length elements on each iteration. If the length is boolean true then var is set to the next period's worth of elements.action statements meaning waitsexprcauses the musical process to wait by sexpr seconds until the process runs again.
See define process for more information.
set var op sexpr {, var op sexpr}*Assigns a value to one or more comma delimited variables. Each variable var is set to the value of sexpr according to the assignment operator op:
operator meaning =sets var to the value of sexpr +=increments var by the value of sexpr *=scales var by the value of sexpr &=collects value of sexpr at the end of the list in var ^=appends the list sexpr to the end of the list in var @=collects value of sexpr at the front of the list in var <=minimizes var with the value sexpr >=maximizes var with the value sexpr
loop with a, b = 0, c = 1, d = {}, e = {}, f = -1, g = 0 for i below 5 set a = i, b += 1, c *= 2, d &= i, e @= i, f <= i, g >= i finally print list(a,b,c,d,e,f,g) end
sprout object [, offset]Triggers algorithmic output from object to the open output stream. If a file is open each sprout results in a new instance of that file being computed. The object can be a single object (process, seq, etc) or a list of the same. If offset is provided its value is a start time offset or list of the same.
open "test.mid" sprout list(make(<midi>, time: 0, keynum: random(128)), make(<midi>, time: 1, keynum: random(128)) )
system sys {, param: sexprLoads an ASDF defined software system into Lisp. Sys should be the string or self-evaluating symbol name of the system to load. See software installation for more information.
system "fomus"
wait sexprSets the next run time of a musical process to sexpr, typically a value in seconds.
See define process for more information.
with var [= sexpr] {, var [= sexpr]}*Declares one or more comma-delimited variables with optional initial values, which default to false if unspecified. Declarations can appear at the start of block statements like begin, loop and run
Integers, floats and ratios. Example:
print 1, 3.3, 1/3
Arithmetic and logical expressions are infix. The following operators are defined:
| operator | meaning |
|---|---|
+ | addition |
- | subtraction |
* | multiplication |
/ | division |
% | modulus (remainder after division) |
^ | exponentiation |
= | equal |
!= | not equal |
> | greater than |
< | less than |
>= | greater than or equal |
<= | less than or equal |
~= | general equality |
& | logical and |
| | logical or |
! | logical not |
Operators must be delimited from their operands using spaces and/or parentheses. In other words, 2 / 4 is division and 2/4 is a ratio; 2 + 4 is addition and 2+4 is nothing. Standard operator precedence rules apply if no parentheses are provided.
print 10 ^ 2 + 1 print (1 + 3) * 4 print 2 != 1 + random(2) print 13 % 12 print 2 >= 1 & 3 = 2 + 1 print {c e g} ~= {c e g}
#t is boolean true and #f is boolean false.
#? ( test , true [, false ] )
print #? (random(2) = 0, 2 * pi, -1 )
A string of uninterpreted characters enclosed within "".
print "A4 is ", 440
Alphanumeric names for variables, functions, etc.
print pi, random(100)
Alphanumeric names preceded by a colon
print :foo ~= :foo
Sequences of constant data enclosed within {}. Lists may contain numbers, booleans, symbols, strings, and other lists.
print {c 60 e 64}
A variable reference vvv.sss where vvv is the name of the variable and sss is the name of a slot. Slot value references can appear in expressions and values can be assigned to slots by the set statement.
set foo = make(<midi>), foo.keynum = random(128) print foo.keynum / 2
A variable identifier followed by one or more comma-delimited indices enclosed within [].
set x = list(100, 200, 300) print x[1], " ", x[random(3)]
A function name followed by zero or more comma-delimited arguments enclosed within ().
Commas separate the individual arguments. Some functions support named parameters, in which the name of the argument with a colon precedes its value.
print list() print pick(1, 2 * 3, 4) print make(<midi>, keynum: random(128), duration: .5)
+ - * / if. To get at the Lisp functions you can use
the Sal replacements: plus minus times divide. The
conditional value #? can be used in place of
Lisp's (if ...) special form.