See the Lisp section in Input for instructions on how to load the fomus.lisp and fomus.asd files installed with FOMUS.
fomus.lisp provides a macro fms:with-score
that creates a score object and destroys it when execution goes out of scope.
All FOMUS objects and note events created inside the macro apply to the score that is created by it.
By default, with-score
also processes the score object and creates all specified output files before destroying it
(to disable this, set :run
to ‘nil’ in the list of keyword/argument pairs at the top).
In this example, the :filename
keyword/value pair sets FOMUS's filename
setting to the output filename
(the *filename*
variable itself is set to the output filename
before the code is executed). The :cautaccs
keyword/value pair shows how to specify a score-level setting (cautionary accidentals
are disabled).
(fms:with-score (:filename *filename* :sets '(:cautaccs nil) ; score-level settings keyword/value pairs :run t) ; unnecessary, this is true by default (loop for o from 0 below 20 by 1/2 do (fms:note :time o :dur 1/2 :pitch (+ 65 (random 15))))) ; note entry function |
|
Figure 7.1: with-score
Macro
Most of the Lisp examples follow the syntax above. However, it's possible to handle score objects in different ways.
If you don't use the with-score
macro, then all calls operate on a single global score instance.
The run
function must then be called at the end to process the score.
The following examples show how to notate a score with or without the with-score
macro:
;; if there is an existing score object, it is destroyed (fms:free) ;; this destroys an existing score object and creates a new one (fms:new) ;; (both `free' and `new' effectively accomplish the exact same thing--the only ;; difference being that after `new' there is an empty score object sitting in memory) ;; `filename' is set using the `setting' function (fms:setting :filename *filename*) ; a score object is automatically created here if one doesn't exist (loop for o from 0 below 20 by 1/2 do (fms:note :time o :dur 1/2 :pitch (+ 65 (random 15)))) ;; the `run' function processes the score (fms:run) (fms:free) ; destroy the score and free up memory |
|
Figure 7.2: Global Score Object
(fms:with-score () (fms:setting :filename *filename*) (loop for o from 0 below 20 by 1/2 do (fms:note :time o :dur 1/2 :pitch (+ 65 (random 15))))) |
|
Figure 7.3: with-score
Macro (Slightly Different)
A more complex with-score
example follows. The outermost with-score
assigns the name ‘main’ to the score and
also sets the output filename. The inner two scores are assigned the names ‘chunk1’ and ‘chunk2’ and are actually
used as containers or temporary storage. The macro with-scores
makes it more convenient to define multiple score objects by wrapping
multiple definitions in another list. Since the two “chunks” are only used as storage, the :run
argument for these is ‘nil’.
Inside with-score
with-scores
, the current score may always be specified using with-score
followed by the name of the score
(instead of a definition). The same blast-notes
function, then, is called each of the two chunks in this example.
The merge
functions at the end merge the two chunks into the main score (at different times) before they go out of scope
and disappear.
(defun blast-notes (list base upto) (loop with time = 0 do (loop for pitch in list do (fms:note :part 'pt1 :time time :pitch (+ base pitch) :dur 1/2) (incf time 1/2) when (>= time upto) do (return-from blast-notes)) (incf base))) (fms:with-score (:name 'main ; the main score :filename *filename*) (fms:with-scores ((:name 'chunk1 :parts '((:id pt1 :inst guitar)) ; a score chunk :run nil) (:name 'chunk2 :parts '((:id pt1 :inst guitar)) ; another score chunk :run nil)) (fms:with-score chunk1 ; call same function on different chunks (blast-notes '(0 2 1 4) 55 (+ 7 1/2))) (fms:with-score chunk2 (blast-notes '(0 3 4 2) 56 (+ 7 1/2))) (fms:merge :to 'main :from 'chunk1 :time 1) ; insert the chunks at time offsets 1 and 9 (fms:merge :to 'main :from 'chunk2 :time 9))) |
|
Figure 7.4: Multiple with-score
Macros
Integer, rational and floating point types are preserved when they are sent to FOMUS. It's generally better to use integers and rationals whenever possible.
(fms:setting :filename *filename*) (fms:note :time 0 :dur 1/2 :pitch 60) (fms:note :time 0.5 :dur 0.5 :pitch 62.0) (fms:note :time 1 :dur 1/3 :pitch 64) (fms:note :time 1.333 :dur 1/3 :pitch 66.0) (fms:note :time 5/3 :dur 0.333 :pitch 68) (fms:run) |
|
Figure 7.5: Numbers
Symbols and keywords can be used whenever fomus requires a string value. The symbol characters are always converted to lower case.
(fms:with-score (:filename *filename* :parts '((:id "prt1" :inst "guitar") ; using strings (:id prt2 :inst TUBA))) ; using symbols (loop for o from 0 below 3 by 1/2 for p from 0 do (fms:note :part 'prt1 :time o :dur 1/2 :pitch (+ p 65)) (fms:note :part "prt2" :time o :dur 1/2 :pitch (- 48 p)))) |
|
Figure 7.6: Symbols/Strings
Any FOMUS setting documented as requiring a “yes/no” value is a boolean value and may be entered using ‘t’ or ‘nil’.
(fms:with-score (:filename *filename* :sets '((:quartertones t) (:double-accs nil)) ; boolean values :parts '((:id "prt1" :inst "guitar"))) (loop for o from 0 below 3 by 1/2 for p from 0 by 1/2 do (fms:note :part 'prt1 :time o :dur 1/2 :pitch (+ 65 p)))) |
|
Figure 7.7: Boolean Values
Lists or nested lists in FOMUS are simply entered with Lisp lists. Settings that are described as “mappings” from strings to values are also entered with lists.
(fms:with-score (:filename *filename* :sets '((:timesigs ((2 8) (4 8) (8 8))) ; nested list of possible time signatures (:keysig-defs ; mapping from keysig symbols to lists (asmin (fs cs gs ds as es bs) afmaj (bf ef af df) afmin (bf ef af df gf cf ff) amaj (fs cs gs) amin () bfmaj (bf ef) bfmin (bf ef af df gf) bmaj (fs cs gs ds as) bmin (fs cs) csmaj (fs cs gs ds as es bs) csmin (fs cs gs ds) cfmaj (bf ef af df gf cf ff) cmaj () cmin (bf ef af) dsmin (fs cs gs ds as es) dfmaj (bf ef af df gf) dmaj (fs cs) dmin (bf) efmaj (bf ef af) efmin (bf ef af df gf cf) emaj (fs cs gs ds) emin (fs) fsmaj (fs cs gs ds as es) fsmin (fs cs gs) fmaj (bf) fmin (bf ef af df) gsmin (fs cs gs ds as) gfmaj (bf ef af df gf cf) gmaj (fs))) (:keysig "dmaj")) :parts '((:id "prt1" :inst "guitar"))) (loop for o from 0 below 3 by 1/2 for p from 0 do (fms:note :part 'prt1 :time o :dur 1/2 :pitch (+ 65 p) :voice '(1 2)))) ; list of voices |
|
Figure 7.8: Lists