[Topic]
SAL

Contents

  1. Introduction
  2. SAL Mode
  3. Language
  4. Symbolic Expressions
  5. Reserved words
  6. Notes and Lacunæ

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:

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]
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
[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 sexpr sets 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 values
for var in sexpr increments 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 sexpr iteration continues while sexpr is true
until sexpr iteration stops when sexpr is true
conditional statements
when sexpr action executed if sexpr is true
until sexpr action executed if sexpr is false
action statements
any SAL statement statements are executed, they do not return values
finally statements
finally statement executes 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]
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 statementsmeaning
for var over 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 statementsmeaning
wait sexpr causes 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:

operatormeaning
= 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:

operatormeaning
+ 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

begin below by chdir define else end finally for from
function if in load loop output over print process repeat
return rts run set sprout system then to unless until
variable wait when while with

Notes and Lacunæ