![]()
© 2002 Bruno Lartillot
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.
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)))
=> 
A subspec can be a subnote, a repeated subnote, a declaration or a reference as described below:
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.
An expression of the form n
*subnote which means that the specified subnote is repeated n times.For example:2*((:c)(:d)(:e)(:c))
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
xis the section represented by...:
(note (:c) ((:d) x=((:c)(:d)(:e)) ...))
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
adoes not figure inside one of the scopes enclosing the reference ofain the first case, andais 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,
xhas 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)).
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.
(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)
(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).
(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).
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)
(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:
0 or :treble goes to channel 1, 1 or :soprano to channel
2... and 15 or :french-violin to channel 16. Note that :percussion
goes to channel 10, which generally conforms to the expectation of midi synthesizers.score
note type for more.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)
(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).
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.
(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*)
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 :
note
type, described above;In this category, one find fermatas, little notes, notes with articulation marks, like staccato or tenuto, or ornamentation like trills or mordents and tremolos.
(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)))
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)))

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

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

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:

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

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

(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.
(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.
(measure :time value ...)
The :time property determines
the time signature. It can be :
:common-time
or :cut-time;(3
8) for a time signature of 3/8;nil, in which case, the score
is unmeasured.: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))))

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

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

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

(glissando ...)
(portamento ...)
Just an example to explain:
(realize (score :free-expansion-factor 2 (glissando (:c)(:c5))(portamento
(:c)(:c5))))

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

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

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

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

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

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

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.
(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 :
:common-time
or :cut-time;(3
8) for a time signature of 3/8;nil, in which case, the score
is unmeasured.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:
:brace
or :bracket, in which case
the global system is grouped by a brace or a bracket;() ( the empty
list), the system determines itself whether to put a brace, a bracket or nothing.
If the system has only one staff, no glyph is drawn. It it contains two staves,
a brace is drawn. Else it is a bracket;() if no staff name is
wanted;
(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
(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))))

(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.
(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.
(init-programs)
Reinits for all the midi chanels the midi program
to 0 (acoustic grand piano).
(test-programs)
Sends one midi note to each midi chanel in order to actually activate program changes.
(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.
(compo-help)
This function describes the full grammar of the compo language.
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.