- [Topic]
- SAL
Contents
Introduction
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
SAL mode is an Emacs editing mode that provides an editing menu, syntax highlighting and evaluation services for SAL programs.
The SAL menu
Start CM | |
Show REPL | |
Execute (<Enter>) | |
Debug | > |
Use System | > |
Show Directory | |
Set Directory... | |
Load File... | |
Compile File... | |
Import File... | |
Play File... | |
Help | > |
- Start CM
- Start/Kill a Common Music session.
- Show REPL
- Makes REPL window visible on screen.
- Execute
- Execute SAL command before point.
- Debug
- Submenu for debugging.
- Trace
- Toggle tracing (debugging).
- Use System
- Submenu for loading software systems.
- Show Directory
- Shows current working directory.
- Set Directory...
- Pop-up dialog sets working directory.
- Load File...
- Pop-up dialog loads SAL or Lisp file.
- Compile File...
- Not yet implemented.
- Import File...
- Not yet implemented.
- Play File...
- Pop-up dialog plays MIDI or audio files.
- Help
- Submenu of Help commands
Syntax Highlighting
Sal mode colorizes program text:
- Comments are red
- Strings are tan
- Commands are blue
- Reserved words are purple
- Class names are green
- Named parameters are pink
- Special notations are aqua
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)
Evaluation Services
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.
Language
- [Statement]
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
- [Statement]
chdir
dir
Change the working directory to dir. The value should be a directory string or variable containing a directory string.
chdir "~/"
- [Statement]
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)
- [Statement]
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
- [Statement]
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)
- [Statement]
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()
- [Statement]
if
sexprthen
[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
- [Statement]
load
file
Loads a Lisp or SAL source file into Lisp. File should be a pathname string or variable containing a pathname string.
load "mycomp.sal"
- [Statement]
loop
[var-decl] {stepping}* {stopping}* {action}+ [finally]end
Defines 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 repeat
sexprsets the iteration limit to sexpr for
var=
sexpr [then
sexpr2]sets var to the value of sexpr on each iteration. If then
is specified sexpr is the initial value and sexpr2 are the subsequent valuesfor
varin
sexprincrements var over elements the list sexpr for
var [from
sexpr] [to|below
sexpr] [by
sexpr]increments var from an on optional starting value to an ending value by an optional amount stopping statements while
sexpriteration continues while sexpr is true until
sexpriteration stops when sexpr is true conditional statements when
sexpraction executed if sexpr is true until
sexpraction executed if sexpr is false action statements any SAL statement statements are executed, they do not return values finally statements finally
statementexecutes 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
- [Statement]
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
- [Process Statement]
output
event
Sends a musical event or list of the same to the open output stream. See define process
for more information.
- [Statement]
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}
- [Statement]
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!")
- [Function Statement]
return
sexpr
Returns 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.
- [Statement]
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 :?
- [Process Statement]
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 for
varover
pattern [by length]increments var over items from pattern. If by
is 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 wait
sexprcauses the musical process to wait by sexpr seconds until the process runs again.
See define process
for more information.
- [Statement]
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
- [Statement]
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)) )
- [Statement]
system
sys {,
param: sexpr
Loads 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"
- [Process Statement]
wait
sexpr
Sets the next run time of a musical process to sexpr, typically a value in seconds.
See define process
for more information.
- [Statement]
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
Symbolic expressions
- Numbers
Integers, floats and ratios. Example:
print 1, 3.3, 1/3
- Expressions
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}
- Boolean Values
#t
is boolean true and#f
is boolean false.- Conditional Values
#? (
test,
true [,
false ])
print #? (random(2) = 0, 2 * pi, -1 )
- Strings
A string of uninterpreted characters enclosed within
""
.print "A4 is ", 440
- Symbols
Alphanumeric names for variables, functions, etc.
print pi, random(100)
- Self-evaluating Symbols
Alphanumeric names preceded by a colon
print :foo ~= :foo
- Lists
Sequences of constant data enclosed within
{}
. Lists may contain numbers, booleans, symbols, strings, and other lists.print {c 60 e 64}
- Slot value references
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
- Array notation
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)]
- Function call notation
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)
Reserved Symbols
Notes and Lacunæ
- Several reserved words shadow Lisp symbols with the same name:
+ - * / 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. - Lisp keywords are legal sexprs but they are not function parameter names.