Harmonised Lindenmayer-System

back

Musical Sections as Sub-CSPs
A 'Free' Motif
A Parameterised Free Motif
Three Motifs: Run, Chord Repetition and Arpeggio
Combining Musical Sections (Combining Sub-CSPs)
Music Representation
Harmonic Rules
Algorithmic Creation of the Global Form

This example shows how a CSP can be composed from sub-CSPs in order to control the global musical form. The sub-CSPs in this example create simple musical motifs. Each sub-CSP controls musical features which distinguish a particular motif (e.g., its rhythm or its pitch contour). Other musical features are controlled by the global CSP (e.g., the harmonic structure of the music).

The example also demonstrates one way for combining constraint-based algorithmic composition with 'classical' deterministic algorithmic composition techniques. Each motif specification is defined in a modular way. The global form is defined using a specific symbol for each specific motif in the textual music representation. For example, the form can be a sequence of motif specifications as

[a b c b a]

This textual representation is created either `by hand' or algorithmically. As an example, a sequence of parmeterised motifs specifications is created with an Lindenmayer system which is a popular algorithmic composition techniques.

In this example, the global form (i.e. the motif sequence) is always determined in the CSP definition. Although the global form can be created algorithmically, it is not possible to constrain it by compositional rules. Strasheela's motif model, however, allows the user to constrain the identity and the variation of a motif.

Musical Sections as Sub-CSPs

This section presents a few relatively simple motif definitions. The hope is that their simplicity encourages you to modify this example with your own motifs. Each motif here is musically rather simple, but clearly distinguished. It is either a monophonic note sequence or some more complex musical segment. Each motif fixes some musical aspects and constrains others (e.g. the number of notes in motif and the rhythmic structure is fixed in the following examples, and the pitch contour may be constrained). Each motif is always used in a harmonic context, and the pitches of the motif are implicitly constrained to express the underlying harmony.

A 'Free' Motif

This first motif example specifies the duration of the notes, but puts no constraints on the note pitches (only the pitch domain is restricted to two octaves around middle c). Nevertheless, the motif notes are 'automatically' constrained to express the underlying chord as shown before. This CSP uses a database of typical Jazz chords (e.g., the chord chord(index:1 transposition:0) expresses C7). Again, the lower staff is always not sounding but shows an harmonic analysis (chord roots plus description). The source demonstrates how the solver is called with this CSP — search for MakeMyFreeMotif in the code...

{Score.makeScore
 sim(items:[{MakeMyFreeMotif}
            chord(duration:4*4                  % motif note no * motif note duration
                  index:1
                  transposition:0)]
     startTime:0
     timeUnit:beats(16))                        % duration 16 is a quarter note
 Aux.myCreators}

click the score for sound (mp3)

The motif constructure MakeMyFreeMotif defines the motif quasi literally. Everything is fixed, only the note pitches are set to variables of the domain {48, ..., 72} (i.e. midi pitches two octaves around middle c). The function Score.makeScore has already been used in several examples before. Score.makeScore2 is a cousin which does not fully initialise the score, and that way allows for the combination of its result with other parts of the final score.

proc {MakeMyFreeMotif MyMotif}
   MyMotif = {Score.makeScore2
              seq(info:freeMotif
                  items:[%% duration 4 is a sixtienth,
                         %% pitch is measured in MIDI keynumbers
                         note(duration:4 pitch:{FD.int 48#72})
                         note(duration:4 pitch:{FD.int 48#72})
                         note(duration:4 pitch:{FD.int 48#72})
                         note(duration:4 pitch:{FD.int 48#72})])
              Aux.myCreators}
   %%  Arbitrary further constraints on MyMotif can be added here...
end

A Parameterised Free Motif

The following example variates the example MakeMyFreeMotif above by introducing additional user control via arguments to the new motif constructor MakeMyFreeMotif2. For example, the user may want to control the number of notes in the motif and the duration of each note:

{MakeMyFreeMotif2 unit(n:6
                       noteDuration:8)}

All arguments are optional. For example,

{MakeMyFreeMotif2 unit}

creates the same output as

{MakeMyFreeMotif}

Following is the definition of MakeMyFreeMotif2. Support for optional arguments is defined uses Oz' Adjoin: the variable Settings is bound to a record which adjoins the Default settings and the user-specified arguments in Args (features in Args take precedence over Default). MakeMyFreeMotif2 is defined in such a way that it also 'forwards' arbitrary optional arguments to the constructor of the sequential container which MakeMyFreeMotif2 returns. For example, MakeMyFreeMotif2 handles the argument startTime or endTime properly.

proc {MakeMyFreeMotif2 Args MyMotif}
   Default = unit(n:4
                  noteDuration:4)
   Settings = {Adjoin Default Args}
in
   MyMotif = {Score.makeScore2
              %% filtering out settings specific for MakeMyFreeMotif2 and
              %% 'forward' all other arguments to the seq container
              {Adjoin {Record.subtractList Settings
                       [n noteDuration isStartingWithChord]}
               seq(info:freeMotif
                   items: {LUtils.collectN Settings.n
                           fun {$}
                              note(duration:Settings.noteDuration
                                   pitch:{FD.int 48#72})
                           end})}
              Aux.myCreators}
end

Three Motifs: Run, Chord Repetition and Arpeggio

The next three motif definitions are only briefly mentioned here. Their full implementation can be found in the source. The run motif creates a little run and allows for non-harmonic passing tones.

The chord repetition motif repeats — surprise — a chord.

Finally, the arpeggio motif creates an arpeggio over a chord.

The run motif, the chord repetition motif, and the arpeggio motif all support a few parameters. For example, the following code creates an arpeggio of 5 notes (argument n), which is ascending (argument direction).

{Score.makeScore
 sim(items:[{MakeArpeggio unit(n:5                % No. of motif notes
                               direction:'<:')}   % relation to predecessor
            chord(duration:5*8 % (motif n * motif note duration)
                  index:1
                  transposition:0)]
     startTime:0
     timeUnit:beats(16))
 Aux.myCreators}

Combining Musical Sections (Combining Sub-CSPs)

Music Representation

The previous examples created the full music representation explicitly. In the following, a chord sequence is created implicitly in the background. For this purpose, the following examples replace the function Score.makeScore with HarmoniseScore. This function is defined in the example's source and makes use of HS.score.harmoniseScore.

HarmoniseScore expects the same arguments as Score.makeScore: a textually specified music representation and a specifications of the score object constructors to use. Score object constructors can be any motif definition discussed above — or your own motif definition.

The textually specified music representation uses the same representation principles as the examples before (e.g., see Florid Counterpoint). In addition, this representation can now use symbolic motif names. For example, a sequence of motifs can be specified like the following example. We only need to map each symbol in the music representation to its own score object constructor.

seq(items:[arpeggio run arpeggio])

HarmoniseScore is defined in such a way that motifs can be marked to start simultaneously with a new chord in the chord sequence. However, the motif definitions in this example depends on the harmonic structure. Therefore, the first motif always must be marked that it starts with a new chord. The example below indicates that the constructor MakeArpeggio is used for the motif specification with the symbol arpeggio, and so forth. Motif specifications marked with the feature isStartingWithChord will start with a new chord in the chord sequence.

{HarmoniseScore seq(items:[arpeggio(n:4
                                    direction:'<:'
                                    isStartingWithChord:unit)
                           chordRepetition(isStartingWithChord:unit)
                           chordRepetition(isStartingWithChord:unit)
                           arpeggio(n:3
                                    direction:'>:'
                                    isStartingWithChord:unit)
                           arpeggio(n:4
                                    direction:'>:'
                                    isStartingWithChord:unit)])
 unit(arpeggio:MakeArpeggio
      chordRepetition:MakeChordRepetition)}

(The score output was created automatically — some future version with avoid the clashes of chord symbols)

Harmonic Rules

The above example implicitly creates a harmonic progression. The chord database of all examples here (see HarmonyExamples for an explanation of the chord database concept) consists in seventh chords and related chords from Jazz harmony. There are a few harmonic rules:

Most motif notes express their underlying harmony. Exceptions are only the passing notes in the run motif. Such non-harmonic notes are marked with an x above the note head. Following is another motif sequence which uses a few more motifs.

{HarmoniseScore seq(items:[run(isStartingWithChord:unit)
                           chordRepetition(isStartingWithChord:unit)
                           run(isStartingWithChord:unit)
                           chordRepetition(isStartingWithChord:unit)
                           chordRepetition(isStartingWithChord:unit)
                           freeMotif(n:7
                                     noteDuration:4)
                           arpeggio(n:6
                                    direction:'>:'
                                    isStartingWithChord:unit)])
 unit(arpeggio:MakeArpeggio
      chordRepetition:MakeChordRepetition
      run:MakeRun
      freeMotif:MakeMyFreeMotif2)}

Algorithmic Creation of the Global Form

The gobal musical form — a motif sequence — was manually created in the last examples. This section creates motif sequences by algorithmic means. A Lindenmayer system (L-system) is a formal grammar which can output self-similar fractals. L-systems are an established technique in algorithmic composition (cf. Common Music's rewrite pattern). The example's source defines the L-system generator MyLSystemMotifs. The following L-system definition implements an L-system example which is explained here. The example returns the 7th generation of an L-system with the two rules (a -> a b) and (b -> a).

{MyLSystemMotifs unit(n:7                                 ; generation to output
                      axiom: [b]                          ; starting sequence
                      rules: unit(a: fun {$ R} [a b] end  ; transformation specifications
                                  b: fun {$ R} [a] end))}

This definition results in the following symbol sequence.

[a b a a b a b a a b a a b]

In this example, an L-system generates a sequence of motif specifications. These motif specifications then replace the manually created motif sequences used in the examples above. The L-system of the present example does not only generate a sequence of plain motifs, it also outputs parameters for these motifs (for parameterised L-systems see Prusinkiewicz and Lindenmayer (1990)). For example, it specifies which motifs start with a chord. The default settings for MyLSystemMotif generate a sequence of specifications for the motifs defined above. The third generation of this L-system is generated with the following call.

{MyLSystemMotifs unit(n:3)}

This call outputs the following motif sequence.

[arpeggio(direction:'>:'
          isStartingWithChord:unit
          n:4)
 arpeggio(direction:'>:'
          n:2)
 chordRepetition
 arpeggio(direction:'>:'
          isStartingWithChord:unit
          n:3)
 arpeggio(direction:'>:'
          n:2)
 chordRepetition
 run
 arpeggio(direction:'>:'
          isStartingWithChord:unit
          n:2)
 arpeggio(direction:'<:'
          n:2)
 arpeggio(direction:'>:'
          isStartingWithChord:unit
          n:3)
 arpeggio(direction:'>:'
          n:2)
 chordRepetition]

This sequence results in the following musical example.

(The rhythmic structure of this example does not fit into Lilyponds default 4/4 time...)

The fourth generation — {MyLSystemMotifs unit(n:4)} — is already rather long. The resulting motif sequence specification is here, the musical output is shown below. The notation of this example was edited to express the motivic structure more clearly. Note beamings mark the motifs created by the L-system. `Measures' mark the length of a chord created by the top-level CSP.

full source

back


[TODO:]