Warning: mysql_connect(): Can't connect to MySQL server on 'mysql.sourceforge.net' (111) in /home/groups/c/co/compo/htdocs/inc/variables.php on line 7

Warning: mysql_select_db(): supplied argument is not a valid MySQL-Link resource in /home/groups/c/co/compo/htdocs/inc/variables.php on line 8

Warning: mysql_query(): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) in /home/groups/c/co/compo/htdocs/inc/lib.stats.php on line 17

Warning: mysql_query(): A link to the server could not be established in /home/groups/c/co/compo/htdocs/inc/lib.stats.php on line 17

Warning: mysql_close(): no MySQL-Link resource supplied in /home/groups/c/co/compo/htdocs/inc/lib.stats.php on line 18

Compo 3 - Reference

© 2002 Bruno Lartillot

1  Working inside compo

2  note

2.1  Subspecifier

2.1.1  Subnote

2.2.1  Repeated subnote

2.3.1  Declaration

2.4.1  Reference

2.2  Generalities about numeric properties

2.2.1  HEIght

2.2.2  DURation

2.2.3  POSition

2.2.4  Form

2.2.5  VOIce and voice class

2.2.6  DYNamics

2.2.7  Computed Values

3  Global variable assignment

4  Note types

4.1 Real notes

4.1.1  Fermata

4.1.2 Grace note

4.1.3  Unslashed note

4.1.4  Articulation notes

4.1.5  Ornamentation notes

4.1.6  Tremolo

4.2  Fields

4.2.1  Clef change

4.2.2  Key change

4.2.3  Measure

4.2.4  Bar

4.2.5  Beam

4.2.6  Tie

4.2.7  Slur

4.2.8  Arrow and Arpeggio

4.2.9  Glissando and Portamento

4.2.10  Octave

4.2.11  Repeat

4.2.12  Crescendo and Diminuendo

4.2.13  Rehearsal letter or number

4.2.14  Page or line mark

4.2.15  Controlers

4.2.16  Time variation

4.2.17  Legato

4.2.18  Non-hierarchical events

4.3  "Artificial" notes

4.3.1  Lyrics

4.3.2  Mute

5  Score printing

5.1  Score

5.2  Cleaves

6  Midi performance

6.1  midi

6.2  Program change

6.3  Init programs

6.4  test programs

7  Coercers

8  Help

9  Localization

1  Working inside compo

When launching compo, one gets a console window with a prompt (a character like ? or >) followed by a cursor echoing any text character typed on the keyboard. All the syntaxes described below can be entered at this level. The expression is evaluated by compo as soon as the enter key is pressed, with the expected side effects produced (mostly the creation of a midi or a postscript file). This is a simple way of trying compo or testing simple compo expressions. But this working mode has two limitations: it does not allow the entering of large expressions and it allows not to backup the typing for future sessions.

The normal way of working is to create one text file for each work using any text editor and to load such a file in compo's environment by typing:

(load filename)

The filename format depends on your system. It can be something like "/usr/dir/file" (Unix), "Principal:folder:file" (Mac OS) or "C:\dir\file" (Windows). Also, depending on your Lisp environment, the load function can figure as an entry in a menu with a dialog box allowing to select interactively the file to be loaded.

Compo is case unsensitive. Consequently, each syntax or examples described below can be typed alternatively in lower or upper case, so:

(note :hei :d) <=> (NOTE :HEI :D) <=> (nOtE :HeI :d)

Inside a text file, line separators or spaces can be inserted anywhere in the text, provided that they do not figure inside a symbol, a keyword (a symbol starting by :) or a number. This allows to structure and indent the text in a readable way. For example:

(note :sync
  (:soprano
    (:c)(:d)(:e))
  (:bass
    (:c3)(:g2)(:c3)))

It is possible to put some comments inside a big work. A comment is any text starting with a semi-colon, until the end of the current line. For example:

; Last night, I have dreamed that fate was knocking at my door...
(note
  (:8th
    (:rest)(:g)(:g)(:g) (fermata :ef :h)) ; ...one first time...
  (:8th
    (:rest)(:f)(:f)(:f) (fermata :d :h))) ; ...then one second time, more insistently!

Note that the comments are visible only in the source file, for a better comprehension of the source, but have absolutely no effect in midi playing nor score printing.

2  note

The term note is taken in compo in a more general meaning than in the current language. Music can generally be described in terms of elementary notes, or atoms, and hierarchical structures combining such atoms. In compo, both atoms and structures are called notes since, as described below, they share a same set of properties. Hence the general and recursive definition is :

A note is either an atom (elementary note) or any structure of notes (container note).

The general form of the note constructor is :

(type [name] property* subspec*)

The brackets indicates that the name is optional, and the star indicates that a property or a subspec can appear any number of times, including zero.

The type is a name taken from a predefined list, determining the particular behaviour of the note being defined. This is the way to define lyrics, key, time or clef change, beams... Each available type is described further in the present reference. For now, just note that the more generic type is note.

A name is any string (like "intro"). Depending on the type of the note, the name can serve different purpose which are described later, along with the corresponding types. As a default, names defined at elementary note level, are used to write annotations above the staff:

(realize (score ("This is an annotation" :c)(:d)(:e)(:f)(:g)))

And a name defined at the root level defines the title of the work:

(realize (score "This is the title" (:c)(:d)(:e)(:f)(:g)))

Setting the name of an elementary note to a number or a list of numbers or strings is the way to indicates voicing or figuring of chords:

(realize (score (:name 1 :c)(:name 2 :d)(:name 3 :e)))

=>

(realize (score :chord (:name ("b7" 5 "#") :c)(:e)(:g)(:bf)))

=>

2.1  Subspecifier

A subspec can be a subnote, a repeated subnote, a declaration or a reference as described below:

2.1.1  Subnote

A subnote has the same form as the note constructor except that the type is not required:

([type] [name] property* subspec*)

If the type is not provide, it defaults to note.

2.2.1  Repeated subnote

An expression of the form n * subnote which means that the specified subnote is repeated n times.For example: 2*((:c)(:d)(:e)(:c))

2.3.1  Declaration

An expression of the form name = subnote which simply binds the specified name to the defined subnote. A valid name should not be a keyword (starting with :), should contain at least one letter and should not contain any occurence of the = character.

The binding of the name to the subnote remains valid until the end of the scope where the declaration appears, scopes being materialized in compo by pairs of parenthesis. In the following example, the validity of the name x is the section represented by ...:

(note (:c) ((:d) x=((:c)(:d)(:e)) ...))

2.4.1  Reference

A name, figuring alone. Each name encountered as a reference, is replaced by the subnote currently associated to that name, so:

(note x=((:c)(:d)(:e)) x)) <=> (note x=((:c)(:d)(:e)) ((:c)(:d)(:e))))

According to the previous rule, the two following examples are valid expressions:

(note a=((:c)(:d)(:e)) a)

(note a=((:c)(:d)(:e)) (:f a))

but these two are not:

(note (a=((:c)(:d)(:e))) a)

(note (a=(:c a)))

Since the definition of a does not figure inside one of the scopes enclosing the reference of a in the first case, and a is not completely defined when its reference appears in the second.

The references contained inside a declaration are not resolved when processing the declaration, but when this declaration is referenced itself later. This allows to write ahead references, as follows:

(note x=(:f y) y=((:c)(:d)(:e)) x) <=> (note (:f ((:c)(:d)(:e)))

A declared name should be referenced at least once to have any effect in the realization. So, x has absolutely no effect in this expression:

(note x=((:c)(:d)(:e)) (:f))

A name can be locally redefined. In the expression:

(note a=((:c)(:d)(:e)) (a=((:f)(:g)(:a)) a) a)

the first reference of a refers to ((:f)(:g)(:a)) while the second refers to ((:c)(:d)(:e)).

2.2  Generalities about numeric properties

Notes hold five properties which are said to be numeric properties : respectively the height, the duration, the position, the voice and the dynamics. In elementary notes, the numeric properties serve to compute final values (midi or score parameters). In a container note, the numeric properties serve as modifiers for the numeric properties in its subnotes.

This modification process of the notes by their container is recursive. The root note modifies its subnotes, which then modify their own subnotes and so on. Note that the note objects are not actually modified. The scope of this modification is only the computing of the resulting sequence (this process is called the realization in compo's terminology).

How the numeric properties are modified is explained below for each numeric property. As a convention, a term super-propertyname refers to the value of the property propertyname in the direct container of a given note. Consider this example:

(note :d (:e))

If we consider the elementary note (:e) in the example above, super-HEI represents the height value of its direct container, that is :d.

Wherever a keyword is allowed as a value to a property, it can figure alone (i.e. not preceded by its property keyword). For example :REST can figure alone inside a note definition, and is an abreviation to :HEI :REST.

2.2.1  HEIght

(note :HEI value)

The height property of a note is specified by the keyword :HEI followed by a value representing the pitch in cents, C4 being equal to 0.

In addition to numbers, keywords are allowed as height values. The format is:

For instance : 400, :g, :e5 or :fff2 are valid height values.

nil or :rest as a height value defines a rest note.

The english syntax is the default one. Changing the syntax is done by typing at the prompt level the lisp expression :

(setf *default-syntax* :latin)

And the effect of the following expression is to return back to the english syntax:

(setf *default-syntax* :english)

If a keyword is provided, it is converted in cents according to a non-equal temperament as a default. If you want to switch in equal temperament, just enter the Lisp expression :

(setf *default-temperament* *equal-temperament*)

And enter this expression to return to the default non-equal temperament:

(setf *default-temperament* *compo-temperament*)

The reason why compo uses a specific temperament rather than working in equal temperament, is that it allows to maintain the difference between d sharp and e flat for example, which is often usefull, particularily when dealing with the classical tonal music. At this level, this use of musical temperaments is just for internal representation of note heights, not for audio restitution. There is another way to specify temperaments at the audio restitution level which is described along with the midi type.

HEI is modified to the value of the expression (+ HEI super-HEI):

(note :d (:e)) <=> (note (:f))

HEI defaults to 0:

(note :HEI :c4) <=> (note)

2.2.2  DURation

(note :DUR value)

The duration property of a note is specified by the keyword :DUR followed by a proportional representation of the duration, where 1 corresponds to the quarter, 1/2 to the 8th, 4 to the whole note...

In addition to numbers, the keywords :DW (double whole) :W (whole), :H (half), :Q (quarter), :8th, :16th, :32th, :64th (english syntax), :DR (double ronde), :R (ronde), :B (blanche), :N (noire), :C (croche), :DC (double-croche), :TC (triple croche) and :QC (quadruple croche) (latin syntax), possibly with one or two leading dots are allowed as durations.

DUR is modified to the value of the expression (* DUR super-DUR):

(note :h (:q.)) <=> (note (:h.))

DUR defaults to 1:

(note :DUR :q) <=> (note)

The final conversion of a duration value to a midi duration is obtained by multiplying the value by 1000 and rounding the result to an integer. Thus, any number corresponds to a valid midi duration (expressed in milliseconds).

2.2.3  POSition

(note :POS value)

The position property of a note is specified by the keyword :POS followed by a proportional representation of the position, homogeneous to that used for durations. The positions are offsets in fact. Their real effect on note positionning depends on the form property of the super note. See the form property for more.

No keyword is allowed as a position value.

POS is modified to the value of the expression : (* POS super-DUR):

(note :h (:POS 3)) <=> (note (:POS 6))

POS defaults to 0:

(note :POS 0) <=> (note)

The final conversion of a position value to a midi duration is obtained by multiplying the value by 1000 and rounding the result to an integer. Thus, any number corresponds to a valid midi offset (expressed in milliseconds).

2.2.4  Form

The form property of a note is specified by a keyword which can be one of :juxt, :chord or :sync.

When set to :juxt, the horizontal position of each subnote is computed so the subnote is shifted, according to the value of POS, relatively to the end of the subnote appearing before it, in the subnote list:

(realize (score :juxt (:pos 1 :c4)(:pos 0 :e4)))

When set to :chord, the horizontal position of each elementary subnote is set to the position of the container, regardless of any POSition value at the subnotes level:

(realize (score :chord :pos 1 (:c4)(:e4)))

When set to :sync, the horizontal position of each direct subnote is computed so the subnote is shifted, according to the value of POS, relatively to the position of the container. The difference between :sync and :chord is that only the direct subnotes are synchronized in the case of :sync, though all the subnotes at any level are synchronized in the case of :chord. Concretely, :sync is good for synchronizing several voices, while :chord is good for defining chords or agregations inside a voice. :sync should always be used with subnotes having separate voice values (see voice):

(realize (score :sync
           (:treble (:c)(:chord (:b3)(:d))(:chord (:c)(:e)))
           (:bass (:c3)(:g2)(:c3))))

The form property defaults to :juxt :

(note :juxt) <=> (note)

2.2.5  VOIce and voice class

(note :VOI value)

(note :VOICE-CLASS value)

A note can be related either to a voice or to a voice class. Generally, one just have to consider the voice class of a note, which is specified by the keyword :VOICE-CLASS followed by any positive or negative integer

In addition to integers, a keyword in :french-violin, :treble, :soprano, :mezzo-soprano, :alto, :tenor, :baritone, :bass, :sub-bass, :double-bass and :percussion is allowed as a voice class. Those keywords correspond respectively to integer values from -1 to 9, :treble corresponding to the neutral value 0.

In fact, voice classes represent sets of 256 voices each. Subnotes of a super note which is given a voice class, can be given a voice, which is an integer value from 0 to 255, by specifying the couple :VOI value. This two level hierarchy is usefull to have multiple voices sharing a common behaviour:

The following example should clarify the practical purpose of voice classification:

(realize
  (score :sync :tied-voices (:treble :bass)
    (:treble :sync
      (:voi 0 (:c)(:d)(:e))
      (:voi 1 (:c)(:b3)(:c)))
    (:bass :sync
      (:voi 0 :g3 :h.)
      (:voi 1 (:c3)(:g2)(:c3)))))


VOI is modified to the value of the expression : (+ VOI super-VOI):

(note :VOI 1 (:VOI 3)) <=> (note (:VOI 4))

VOI defaults to 0:

(note :treble) <=> (note)

2.2.6  DYNamics

(note :DYN value)

The dynamics property of a note is specified by the keyword :DYN followed by any number representing the note dynamics. In addition to integers, the keywords :dynmp, :dynp, :dynpp, :dynppp, :dynpppp, :dynmf, :dynf, :dynff, :dynfff and :dynffff are allowed as dynamics. The corresponding integers are multiple of 12, from -60 to 48, :dynmf being equal to 0. The keywords :dynfz, :dynsf, :dynsfz, :dynsff, :dynrfz and :dynfp are also valid as dynamics values. They correspond respectively to the integer values 2, 4, 6, 8 and 10.

:DYN is modified to the value of the expression : (+ DYN super-DYN):

(note :dynf (:dynff)) <=> (note (:dynfff))

:DYN defaults to 0:

(note :dynmf) <=> (note)

The final conversion of a dynamics value to a midi velocity is obtained by rounding the value to an integer, then adding 60 to the value, choosing the min between the result and 127and choosing the max between the result and 0. Thus, any number corresponds to a valid midi velocity (from 0 to 127).

2.2.7  Computed Values

The value given to any numeric property is not necessarily a literal number or a keyword like 20 or :8th, it can be a computation in the form:

(operator operand1 operand2...operandn)

Where operator can be any arithmetic operator like +, -, * or /, and each operand can itself be (recursively) a value. The final value given to the property is the result of this computation. For example, the two values (+ 20 (* 10 4)) and 60 are equivalent. A concrete example of this facility is the representation of a note which duration is the sum of two elementary durations, like:

Rather than writing something unclear like (note :dur 9/2), it is smarter to write (note :dur (+ :w :8th)).

For a given property, the keywords allowed as operands to a computed value are the same that those allowed as a literal value.

3  Global variable assignment

(setf name note)

This standard Common Lisp facility of global variable assignment is used in compo to allow global declarations. Names globally defined that way can be used as references.

(setf *a* (note (:c)(:d)(:e)))

(note *a*)

4  Note types

The goal is to provide almost the same richness as does cmn, the excellent score writter by Bill Schottstaedt which is used in background.

Note types are divided into three categories :

4.1 Real notes

In this category, one find fermatas, little notes, notes with articulation marks, like staccato or tenuto, or ornamentation like trills or mordents and tremolos.

4.1.1  Fermata

(fermata :suspens value)

The fermata type accept an extra property named :suspens which is a duration, defaulting to 0. This duration is added to the normal duration of the note, and so delays as much the following note, in the context of a midi realization. It has no effect on score drawing:

(realize (score (:c)(:b3)(:c)(fermata :suspens :h :d)(:d)(:e)(:c)(fermata :suspens :h :d)))

ear it

4.1.2 Grace note

The grace-note type allows to specify one or several little notes preceding a normal note. Grace note should be synchronized with the real note they are tied to, using the :chord form:

(realize (score (:c)(:chord (grace-note :16th (:b3)(:c)(:d))(:c))(:e)(:c)))

4.1.3  Unslashed note

The unslashed type behaves like a grace note, except that is sound like an appogiatura, i. e. it starts at the normal time of the note to which it is tied, delaying it. It has the same representation as a grace note, without the slash:

(realize (score (:c)(:chord (unslashed :16th (:b3)(:c)(:d))(:c))(:e)(:c)))

4.1.4  Articulation notes

Articulation notes are summarized below:

(realize (score (staccato)(accent)(little-swell)(wedge)
                (tenuto)(marcato)(down-bow)(up-bow)
                (detache)(martele)(thumb)(natural-harmonic)
                (stopped-note)(open-note)(pedal)(pedal-off)
                (bartok-pizzicato)(snap-pizzicato)(left-hand-pizzicato)))

An articulation note type can be specified on an elementary note, in which case, it applies only on this note:

(realize (score (:c)(staccato :d)(:e))

Or it can be specified at a container level, in which case, it is inherited by each elementary note of the group:

(realize (score (:c)(staccato (:d)(:e)(:f))(:g)))

4.1.5  Ornamentation notes

Ornamentation notes are summarized below:

(realize (score (mordent)(inverted-mordent)(double-mordent)(turn)
                (short-trill)(trill)))

Ornamentation notes accept an extra property named :SIGN which can be one of :flat, :natural, :sharp, :small-flat, :small-natural and :small-sharp. For example:

(realize (score (turn :sign :small-flat)))

Trill notes accept two extra property named :SIGN-POSITION which can be one of :right (the default) , :up or :in-parentheses and :WAVY-LINE which can be nil (the default) ot t. For example:

(realize (score :free-expansion-factor 2 (trill :sign :small-flat :sign-position :in-parentheses :wavy-line t :w)(:d)(:e)(:f)(:g)))

Ornaments represent elementary notes on the score, though they can be container notes, in which case, their subnotes are only taken into account in midi realizations. Thus, one can specify each note of a trill for example, for a precise midi realization, while keeping the score readable:

(realize (score (trill (:32th (:d)(:c)(:d)(:c)(:d)(:c)(:b3 :h)))(:c)))

Sounds:

In the case where no subnotes are specified for an ornament, a default pattern is provided for each type. Here are the correspondences:

(realize (midi (mordent))) sounds like:

(realize (midi (inverted-mordent))) sounds like:

(realize (midi (double-mordent))) sounds like:

(realize (midi (turn))) sounds like:

(realize (midi (trill))) or (realize (midi (short-trill))) sound like:

4.1.6  Tremolo

(tremolo :density value :attenuation value subnote1 subnote2)

A tremolo should have exactly two subnotes, each being either an elementary note, or a chord. The density property determines the number of repetitions of the couple of two subnotes. The higher the density, the faster each repetition, so the global duration of a tremolo, is always constant. The density defaults to 4. The attenuation property takes a dynamics keyword which allows to attenuate the midi velocity while playing high-density tremolos. The name property of a tremolo is used as a textual indication of the tremolo on the score. It defaults to "trem.":

(realize (score (tremolo "trem. 8" :density 8 :attenuation :dynp
                  (:chord (:c)(:e))
                  (:d))))

 

4.2  Fields

A field apply only in the scope where it is declared, i.e. the pair of parenthesis that encloses it. This scope can cover several staves. If a field b shadows another field a of the same type, by being declared inside the scope of a, a is restored at the end of b scope. The following example shows the correspondence between the scope of a key change in the compo definition, and the result in the produced score. Note that the initial G Major key is automatically restored after the modulation in F Major, without the need of indicate this restore explicitly:

(realize (score :key :g
            (:sync (:treble (:c)(:d)(:e))(:bass (:c)(:a3)(:g3)))
            (key-change :key :f :sync (:treble (:f)(:g)(:a))(:bass (:f3)(:bf3)(:a3)))
            (:sync (:treble (:b)(:c5))(:bass (:g3)(:c3)))))

4.2.1  Clef change

(clef-change :clef value ...)

The :clef property can be one of :french-violin, :treble, :tenor-treble, :soprano, :mezzo-soprano, :alto, :tenor, :baritone, :baritone-F, :bass, :sub-bass, :double-bass or :percussion, which give the following result, in the same order:

:clef defaults to :treble.

4.2.2  Key change

(key-change :key value ...)

The :key property can be one of :C, :CS, :DF, :D, :EF, :E, :F, :FS, :GF, :G, :AF, :A, :BF, :B and :CF.

:key defaults to :C.

See the introduction on fields above for an example of a key change.

4.2.3  Measure

(measure :time value ...)

The :time property determines the time signature. It can be :

:time defaults to :common-time.

(realize (score
           (measure :time 3 (:c5 :h.)(:b :h.))
           (measure :time :cut-time :h (:c5)(:g)(:a)(:g))
           (measure :time (3 8) :8th (:c5)(:b)(:c5)(:a)(:b)(:c5))))

(realize (score (measure :time nil (:c)(:d)(:e)(:f)(:g)(:a)(:b)(:c5))))

4.2.4  Bar

Normally, compo determines automatically where to place bars on the score, according to the measure. However, it is possible to place explicit bars. This is usefull particularly on unmeasured scores, where bars can be used as guide bars. Placing bars manually on a measured piece of music do not affect compo's automatic beat counting.

(realize (score :time nil (:c)(:d)(:e)(:f)(:g)(bar)(:a)(:b)(:c5)))

4.2.5  Beam

(beam ...)

A beam makes, voice per voice, each subnote with a duration less or equal to an eighth, to be beamed. Since cmn automatically beams notes according to beat boundaries, it is often not necssary to specify beams. However, beam can be used to change the default behaviour. For example, the default beam drawing for this compo definition:

(realize (score :8th (:c)(:d)(:e)(:f)))

Is:

If for some reason, one would prefer to have the beam between d and e, the solution is:

(realize (score :8th (:c)(beam (:d)(:e))(:f)))

4.2.6  Tie

(tie ...)

Ties are automatically handled by compo. Normally, one do not have to specify them. However, there can be cases at the limit, where a tie have to be specified manually.

4.2.7  Slur

(slur ...)

A slur draws, voice per voice, a curved line from the first of its subnotes to the last subnote.

(realize (score (slur :sync (:treble (:c)(:d)(:e))(:bass (:c)(:g3)(:c)))))

Note the automatic distribution of slurs between each voice defined inside the scope of a slur field.

4.2.8  Arrow and Arpeggio

(arrow :direction value ...)

(arpeggio :direction value ...)

(no-arpeggio ...)

An arrow is a graphism that indicates whether a chord should be played from low to acute or the contrary. Since arrows apply only on chords, the form property default to :chord on arrows. The direction can be :up or :down. arpeggio and no-arpeggio are specialized arrow. The :direction property has no sense in the case of a no-arpeggio since the goal is to play all the notes at the same time.

(realize (score (arrow :direction :up (:c)(:e)(:g))
                (arpeggio :direction :down (:c)(:e)(:g))
                (no-arpeggio (:c)(:e)(:g))))

4.2.9  Glissando and Portamento

(glissando ...)

(portamento ...)

Just an example to explain:

(realize (score :free-expansion-factor 2 (glissando (:c)(:c5))(portamento (:c)(:c5))))

4.2.10  Octave

(octave :8ves value ...)

The :8ves property determines the number of octaves the staff is transposed to. It can be 1, 2, -1, -2 or 0 and defaults to 1.

(realize (score
           (octave :8ves 1 :c5 (:c)(:d)(:e)(:f))
           (octave :8ves 2 :c5 (:g)(:a)(:b)(:c5))
           (octave :8ves -1 :c3 (:c5)(:b)(:a)(:g))
           (octave :8ves -2 :c3 (:f)(:e)(:d)(:c))))

If there are notes rather far upwards or downwards from the staff, cmn manages automatically octave transpositions, which is in many case very usefull.

(realize (score :c7 (:c)(:d)(:e)(:f)(:g)))

But if in some case one wants to enforce cmn not to make any octave transposition, this is the case where one have to specify an octave event with :8ves being set to 0.

(realize (score (octave :8ves 0 :c7 (:c)(:d)(:e)(:f)(:g))))

4.2.11  Repeat

(repeat ...)

A repeat field defines a section to be repeated twice. On the score, it is enclosed between a repeat begin and a repeat end sign, and it is played twice in the case of a midi performance. If the beginning of the repeat section is the beginning of the score, no repeat begin sign is drawned.

(realize (score (repeat (:c)(:d)(:e)(:c))(repeat (:e)(:f)(:g :h))))

4.2.12  Crescendo and Diminuendo

(crescendo :amp value ...)

(diminuendo :amp value ...)

The :amp property represent the amplitude and takes a dynamics value.

Midi performance behaviour:

According to the current dynamics value at the beginning of the crescendo or diminuendo, and to the specified amplitude, the dynamics value of every elementary note included in the field are recomputed in order to reflect a lineary ascending or descending progression.

Score drawing behaviour:

An open or a close swell, of the same duration than the field, is drawn under each staff enclosed in the scope of the field.

(realize (score (crescendo (:c)(:d)(:e)(:f)(:g))))

4.2.13  Rehearsal letter or number

(rehearsal-letter :frame value)

(rehearsal-number :frame value :reset value)

Rehearsal letters or numbers are elementary events (without subnotes), can be placed anywhere along the score and are drawn above the score. The system automatically increments the number or the letter for the next rehearsal mark. The :frame property specifies whether the sign should be framed or not and if the frame is a box or a circle. The values are :none, :box or :circle. The :reset property of rehearsal-number specifies to which number the count is reset.

(realize (score (rehearsal-number :frame :box :reset 10)(:c)(:d)(:e)(:f)(:g)(:a)(:b)(:c5)(rehearsal-number)(:d5)))

4.2.14  Page or line mark

(page-mark)

(line-mark)

line-mark enforces a line feed, while page-mark enforces a page feed. Both are elementary events that can be placed anywhere along the score.

(realize (score (:c)(:d)(:e)(:f)(line-mark)(:g)(:a)(:b)(:c5)))

4.2.15  Controlers

(controler :id value :channel value :neutral value :min value :max value ...)

(level-controler ...)

(tone-controler ...)

(pitch-controler ...)

(pan-controler ...)

(fx-controler ...)

(fx2-controler ...)

Controlers allow to define fields inside which any dynamics variation controls the level of a midi controler. :id and :channel are the midi controler id and channel respectively. Be carefull to the fact that midi channels have values between 0 and 15 in compo, not between 1 and 16. If :chan is set to nil (the defaults), the midi control messages goes to the same channel than the midi notes. This is the common expected behaviour. One case where midi control messages should be sent to another channel, is when one intends to control a mixer to which the synthesizer is connected. :min and :max should be set to the minimum and the maximum values for this midi controler. :neutral is the value to which the midi controler should be reset when the controler field ends. It should generally corresponds to the initial value of the midi controler.

Six specialized controlers are provided. They correspond to six common ways of controling sound: level-controler controls the amplitude level, tone-controler controls tone parameters like filters frequency, resonance, LFO amplitude or rate..., pitch-controler controls the pitch-bend wheel, pan-controler the panoramics, fx-controler and fx2-controler can be affected to auxiliary commands controlling effects like reverberation or equalization.

Except the pitch control which corresponds to the pitch-bend standard midi message, each specialized controler has to be adapted according to one's synthesizer chart. For example, if your sequencer affects the id 7 to level control, you should have the following settings in your cminit.lisp file:

(setf *level-controler-id* 7)
(setf *level-controler-chan* nil)
(setf *level-controler-neutral* 77)
(setf *level-controler-min* 0)
(setf *level-controler-max* 127)

The same applies for each specialized controler.

4.2.16  Time variation

(time-var :name value :dur value ...)

A time variation field allows to use the :dur numeric property to control an acceleration or slowing down of the tempo, unless affecting the durations representation in the score. In fact, the duration specified at the time-var field level modifies, in the sense defined above (see duration), the subnotes duration in the case of a midi performance, but not in the case of a score drawing. Any value given to the :name property of a field is written above each staff. In addition to simple string values, A list of a duration keyword and a number allows to specify a tempo value, and a list of two duration keywords specifies a tempo change.

(realize (score (time-var :name (:q 120) (:c)(:d)(:e)(:f))
                (time-var :name (:h :q) :8th (:h (:g)(:a)(:b)(:c5)))))

ear it

4.2.17  Legato

(legato ...)

A legato field is intended to be used along with monophonic midi synthesizers. With such devices, if a note starts while the previous one is not finished, the second one continues the first one in the same amplitude enveloppe. According to this behaviour, any beam, slur, glissando or portamento field defined inside the scope of a legato field is midi-realized in a particular way: the duration of each note inside the beam, slur, glissando or portamento is augmented in order to partially recover the next one. Of course, this augmentation does not apply in the case of a score-realization.

(realize (midi (legato (:c)(beam (:d)(:e))))) => ear it

4.2.18  Non-hierarchical events

(beam-begin :sym value ...)(beam-end :sym value ...)

(slur-begin :sym value ...)(slur-end :sym value ...)

The way of describing musical structures in compo is normally hierarchical. But in some cases, structures have to be declared in a non-hierarchical way. Let's consider the following example:

The problem is that neither the slur group nor the beam group is hierarchically included inside the other group. So they cannot be described hierarchically. The solution is to used events to mark the begin and the end of each group, with a symbol making possible this matching between one begin event and its end. Here is the compo definition of this example:

(realize
  (score :8th
    (slur-begin :sym a)(:c :h)(beam-begin :sym b)(:d)(:e)
    (slur-end :sym a)(:f)(:g)(beam-end :sym b)(:a)))

4.3  "Artificial" notes

4.3.1  Lyrics

A note defined with the lyrics type denotes a section where elementary notes define lyrics to be written under the staves rather than real notes to be drawned on the staves. The text to be written is the string given as a name to each elementary note. This text will figure under the staff corresponding to the same voice than the voice of the elementary note defining it, and at the position of this elementary note, provided that there is at list one real note at this voice starting at the same position. For example, in :

(note :sync
  (lyrics :sync
     (:soprano ("Oooh")("Aaah"))
     (:tenor ("Iiih")))
  (:soprano (:d)))

"Oooh" is written under the D at the soprano voice, but "Aaah" is ignored since it figures at a position where no real note is present at the soprano voice, while "Iiih" is ignored since there is no real note at the tenor voice.

It is possible to superpose two stanzas (but not more) under a same staff, using the :line property, which holds the line number. :line defaults to 1:

(realize (score :free-expansion-factor 2 :sync
           (lyrics ("Au")("clair")("de")("la")("lu-" :h)("-ne" :h))
           (lyrics :line 2 ("Pre-")("-te")("moi")("ta")("plu-" :h)("-me" :h))
           ((:c)(:c)(:c)(:d)(:e :h)(:d :h))))

Accordingly to compo's style, it is possible to have the same lyrics written under several staves while writting it just once :

(realize (score :free-expansion-factor 2 :sync
           stanza=(("Au")("clair")("de")("la")("lu-" :h)("-ne" :h))
           (lyrics :soprano stanza)
           (:soprano (:c)(:c)(:c)(:d)(:e :h)(:d :h))
           (lyrics :bass stanza)
           (:bass (:c)(:g3)(:e3)(:f3)(:g3 :h)(:g2 :h))))

4.3.2  Mute

All the notes defined inside the scope of a mute field are not played while being midi-realized, however, any midi control event due to dynamics variation, inside a controller field (see controler) is effectively generated. In fact, the mute field is usefull to define particular voices that only serve to modulate midi controlers without playing notes.

For example, a simple way to define a left to right panoramics progression could be (the pan-controler id and channel correspond to my synthesizer):

(realize (midi (pan-controler :id 28 :channel 15 (crescendo :dyn -64 :amp 128 (:c)(:d)(:e)))))

The problem is that any dynamics variation (the crescendo in this case) always applies to midi note velocities. So, the left to right panoramics progression is parasitized by a dynamics crescendo that is not expected. The solution is to define the pan-controler field with its crescendo inside a mute voice synchronized with our main voice:

(realize (midi :sync
          ((:c)(:d)(:e))
          (mute (pan-controler :voi 1 :id 28 :channel 15 (crescendo :dyn -64 :amp 128 (:h.))))))

=> ear it

Generally, it is worse separate controlers sections in such muted voices, than defining them along with notes, since they do not interferate on note velocities.

5  Score printing

5.1  Score

(score :key value
       :time value
       :anacrusis value
       :voice-order value
       :tied-voices value
       :system-disp value
       :output-type value
       :output-file value
       :size value
       :transposers value
       :automatic-line-breaks value
       :automatic-beat-subdivision-numbers value
       :automatic-measure-numbers value
       :curvy-flags value
       :always-show-staff-names value
       :all-output-in-one-file value
       :page-height value
       :page-width value
       :left-margin value
       :right-margin value
       :header-margin value
       :footer-margin value
       :line-separation value
       :staff-separation value
       :system-separation value
       :free-expansion-factor value ...)

A score expression can only figure at root level. That is, the expression (note (score)) is illegal.

As for key-change The :key property can be one of :C, :CS, :DF, :D, :EF, :E, :F, :FS, :GF, :G, :G, :AF, :A,:BF, :B and :CF. It defaults to :C.

As for measure, the :time property determines the time signature. It can be :

The time defaults to the :common-time.

The :anacrusis property determines the position at which starts the score. It allows to have a score started with an incomplete measure. For example : (score :anacrusis 3 (:c)(:d)(:e)) will print a score starting with an only one time first measure. :anacrusis defaults to 0.

The :voice-order property allows to change the default order of voice classes presentation from top to bottom (see VOIce):

(realize (score :voice-order (:bass :treble) :sync
  (:treble (:c)(:d)(:e))
  (:bass (:c)(:g3)(:c))))

The :tied-voices property takes the list of the voice classes for which each voice are printed in the same staff. In the following example, according to the value given to :tied-voices only treble voices are grouped on the same staff, while bass voices are not:

(realize
  (score :sync :tied-voices (:treble)
    (:treble :sync
      (:voi 0 (:c)(:d)(:e))
      (:voi 1 (:c)(:b3)(:c)))
    (:bass :sync
      (:voi 0 :g3 :h.)
      (:voi 1 (:c3)(:g2)(:c3)))))


As a default, no voice class is tied.

The system-disp defines the global disposition of the system. It can be an item or a list:

(realize (score :voice-order (:baritone :treble :bass)
                :system-disp ((:bracket "Me" "You" "Him") (:brace ()()))
                :w :sync
           (:baritone :sync (:bf3)(:voi 1 :g3)(:voi 2 :c3))
           (:treble :e)
           (:bass :c3)))

The :output-type keyword argument determines the type of formatting. It can be :postscript, :quickdraw (for Macintosh, needs MCL), or :x (for Unix, needs xcmnw and motif). In :postscript, the score is formatted as a postscript file. In :quickdraw or :x, the score is previewed. It defaults to the current value of the variable cmn::*cmn-output-type*.

The :output-file keyword argument allows to change the emplacement and name of the produced postscript file. It defaults to the file pointed by the value of the variable *default-output-file*. It accepts any Lisp pathname.

The :size keyword argument fixes the proportional size of the score. It defaults to the current value of the variable *default-size*.

The :transposers keyword argument allows to specify voice classes that behave as transposers. This is usefull for instruments like the trumpet for example, which transposes in D, that is for which a D is written C on the score. The value should be a list of transposers, each being a couple of the form (voice-class . height):

(realize (score :transposers ((:treble . :D)) (:c)(:d)(:e)))

The following keywords arguments corresponds to options provided by cmn. The description comes from the cmn reference documentation (the value between parenthesis correspond to the default value):

:automatic-line-breaks (t) should line breaks be added by cmn

:automatic-beat-subdivision-numbers (t) should irregular subdivisions be numbered by cmn

:automatic-measure-numbers (nil) can be t = 1, a number, :by-line, or :by-page

:curvy-flags (t) curved or straight flags

:always-show-staff-names (t) should staves continue to be named after 1st

:all-output-in-one-file (nil) put page breaks in output file (rather than separate files)

:page-height (11.0 inches) (29.7 cm) -- cm if cmn::*cmn-units* = :cm

:page-width (8.5 inches) (21.0 cm)

:left-margin (0.5 inches) (1.0 cm)

:right-margin (0.5 inches) (1.0 cm)

:header-margin (1.0 inches) (2.0 cm)

:footer-margin (1.0 inches) (2.0 cm)

:line-separation (2.0 inches) white space between lines of music

:staff-separation (1.5 inches) white space between staves

:system-separation (1.5 inches) white space between systems

:free-expansion-factor (1.25 inches) white space added during justification

5.2  Cleaves

(cleaves ((voice-class1 . value1)(voice-class2 . value2) ...))

Change the cleave associated to the specified voice class list.

Each voice-class can be any voice keyword as defined in VOIce. Each value can be any clef as defined in Clef change.

Each voice class that is not present in the list is associated, as a default, to the clef of the same name.

Such a global cleave assignment replaces any previous cleave assignment on the same voice classes and stays valid until the next cleave assignment.

Example:

(cleaves ((:treble . :soprano)))
(realize (score ((:c)(:d)(:e))))

6  Midi performance

6.1  midi

(midi :midi-file value :tempo value ...)

A midi expression can only figure at root level. That is, the expression (note (midi)) is illegal.

The :midi-file keyword argument allows to change the emplacement and name of the produced midi file. It defaults to the file pointed by the value of the variable *default-midi-file*. It accepts any Lisp pathname.

The :tempo keyword argument defines the number of quarters per minute. It defaults to 60.

6.2  Program change

(program-change voice-class [program])

Change the midi program (musical instrument) associated to the specified voice class .

The voice class is any integer or keyword allowed as a voice property.

The program is any of the following keywords (which corresponds to the General Sound normalized list) :

:acoustic-grand-piano :bright-acoustic-piano :electric-grand-piano :honky-tonk :electric-piano-1 :electric-piano-2 :harpsichord :clavicord
:celesta :glockenspiel :music-box :vibraphone :marimba :xylophone :tubular-bells :dulcimmer
:drawbar-organ :percussive-organ :rock-organ :church-organ :reel-organ :accordian :harmonica :tango-accordian
:nylon-acoustic-guitar :steel-acoustic-guitar :jazz-electric-guitar :clean-electric-guitar :muted-electric-guitar :overdriven-guitar :distortion-guitar :guitar-harmonics
:acoustic-bass :finger-electric-bass :pick-electric-bass :fretless-bass :slap-bass-1 :slap-bass-2 :synth-bass-1 :synth-bass-2
:violin :viola :cello :contrabass :tremolo-strings :pizzicato-strings :orchestral-strings :timpani
:string-ensemble-1 :string-ensemble-2 :synth-strings-1 :synth-strings-2 :choir-aahs :voice-oohs :synth-voice :orchestra-hit
:trumpet :trombone :tuba :muted-trompet :french-horn :brass-section :synth-brass-1 :synth-brass-2
:soprano-sax :alto-sax :tenor-sax :baritone-sax :oboe :english-horn :bassoon :clarinet
:piccolo :flute :recorder :pan-flute :blown-bottle :shakuhachi :whistle :ocarina
:square-lead :sawtooth-lead :calliope-lead :chiff-lead :charang-lead :voice-lead :fifths-lead :bass+lead
:new-age-pad :warm-pad :polysynth-pad :choir-pad :bowed-pad :metallic-pad :halo-pad :sweep-pad
:rain :soundtrack :crystal :atmosphere :brightness :goblins :echoes :sci-fi
:sitar :banjo :shamisen :koto :kalimba :bagpipe :fiddle :shanai
:tingle-bell :agogo :steel-drums :woodblock :taiko-drum :melodic-tom :synth-drum :reverse-cymbal
:guitar-fret-noise :breath-noise :seashore :bird-tweet :telephone-ring :helicopter :applause :gunshot

If program is not provided, the user is prompted to choose interactively a value from this list.

6.3  Init programs

(init-programs)

Reinits for all the midi chanels the midi program to 0 (acoustic grand piano).

6.4  test programs

(test-programs)

Sends one midi note to each midi chanel in order to actually activate program changes.

7  Coercers

(note :COERCER value)

Compo's coercion facility is a very powerfull tool, not absolutely necessary to understand, when beginning with compo. Using the coercer facility of compo requires knowledge of Common Lisp's type specifiers. Please refer to any Common Lisp documentation (like Common Lisp, the Language - Steele) if you are not already familiar with type specifiers.

The coercer property of a note is specified by the keyword :COERCER followed by any Common Lisp type specifier. Only subnotes that finally conform to this type specifier are kept in the result. "Finally" means that this coercion is applied on the resulting subnotes set, after any other computing like numeric property transfomation, positioning...

Consider this example:

(realize (score (:c)(:d)(fermata :e)))

Now, with a coercer that select only the subnotes of type fermata:

(realize (score :coercer fermata (:c)(:d)(fermata :e)))

A rather more usefull coercer is the note-type type specifier. Its form is:

(note-type type [:hei typespec] [:dur typespec] [:pos typespec] [:voi typespec] [:dyn typespec])

Specifying the first parameter type in a note-type coercer supposes to have knowledge of the class model of compo, and is out of the scope of this reference. For the moment, let's assume that setting it to t is enough to have it neutralized, and let's consider the other parameters. As indicated by the braces, each type specifier on a numeric property is optional. If no type specifier is specified for a given numeric property, no filter is applied according to the value of this property. That is:

:coercer (note-type t :hei t) <=> :coercer (note-type t) <=> :coercer t

note-type allows to specify filters on numeric property values, those filters acting on the resulting set of subnotes. For example, (note-type t :pos (integer 2 4)) is a filter on positions such that only subnotes whose positions are integer values between 2 and 4 included, are kept in the result. For example:

(realize (score (:c)(:d)(:e)(:f :8th)(:g :8th)(:a)))

Coerced that way:

(realize (score :coercer (note-type t :pos (integer 2 4))
            (:c)(:d)(:e)(:f :8th)(:g :8th)(:a)))

Any property keyword is allowed as a type specifier parameter and is replaced by its numeric value:

:coercer (note-type t :dur (rational :q :w)) <=> :coercer (note-type t :dur (rational 1 4))

The kind of coercers described above are filters. More generally, coercers can modify subnotes rather than filtering them. Compo provides several such coercers: temperaments, mode and shuffle.

The available temperaments are eq-temp (equal temperament), py-temp (pythagorician temperament), zar-temp (zarlinian temperament) and bali-temp (balinian temperament). They are intended to be used only along with the midi type, for midi playing. Note that a midi note has eq-temp, not t, as a default coercer. This makes sure that a midi realization will play notes according to an equal temperament as a default, which is nowadays the common expected result for western people ears. Be carefull that if one specifies any coercer to a midi structure, this coercer overrides the default eq-temp coercer. So, in order to keep the default temperament behaviour, any coercer being not itself a temperament to be specified at a midi structure level, should often be combined with eq-temp this way:

(realize (midi :coercer (and eq-temp other-coercer) ...))

The mode coercer moves subnotes height towards the closest degree of a mode given as parameters. Any tune in a major mode for example:

(realize (score (:c5)(:d5)(:e5)(:c5)(:d5)(:b)(:c5)(:g)))

can easily be minorized that way:

(realize (score :coercer (mode :c :d :ef :f :g :af :b)
            (:c5)(:d5)(:e5)(:c5)(:d5)(:b)(:c5)(:g)))

The shuffle coercer shifts according to a given delta value, the position of subnotes which do not fall on a beat. The beat and the delta are given as parameters. Thus, any tune written in a binary rhythm:

(realize (score (:8th (:d5)(:c5)(:d5))(:c5)(:d5 :8th)))

can easily be ternarized:

(realize (score :coercer (shuffle 1 1/6)
           (:8th (:d5)(:c5)(:d5))(:c5)(:d5 :8th)))

In fact, the practical way of using shuffle is to apply it on midi rendering rather than on score printing. The example above could be concretely managed like this:

The musical structure is defined first, with some jazzy indication that it has to be performed in a ternary way:

(setf *example* (note (:8th ("Swing" :d5)(:c5)(:d5))(:c5)(:d5 :8th)))

The score printing form simply refers to this "abstract" structure:

(realize (score *example*))

While the midi rendering form ternarizes it:

(realize (midi :coercer (shuffle 1 1/6) *example*)) => ear it

Of course, coercer are not necessarily applied to root notes. It is even their main interest to be usable locally.

Compo provides an Application Programming Interface that allows, among other extensions, to provide new coercers. The description of this API is out of the scope of this user reference. Refer to Compo's API reference (currently being written) for more.

8  Help

(compo-help)

This function describes the full grammar of the compo language.

9  Localization

The file compo/src/localization.lisp contains the list of all the textual messages used by the system. Feel free to change those texts (only the texts, not the variable names) in order to have those texts localized to your natural language. Then just restart compo. At this time, your modification will be taken into account by the system.