Next: , Previous: Lisp Examples, Up: Lisp Examples


7.1 Some Basic Lisp Examples

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

lsp001.png

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

lsp025.png

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)))))

lsp014.png

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)))

lsp038.png

Figure 7.4: Multiple with-score Macros

7.1.1 Data Types

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)

lsp029.png

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))))

lsp028.png

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))))

lsp030.png

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

lsp031.png

Figure 7.8: Lists