Project: MOOD
Automated Rule-based Music Generation Ge Wang Design Document and Methods of
Implementation MOOD is an automated, rule-base, “tactical” music “composer”. This document discussed the design issues
that went into developing MOOD. It is
divided among the following sections. (2) Premise and inner workings (3) Progress
in level of intelligence (4) Objects,
and data Structures. MOOD is an automated music composition engine. The input is comprised of sets of specialized rules, which are
dynamically added to the framework. The
main processing unit of the MOOD engine traverses each of the components
and the rules, evaluates each decision, and ultimately composes the melodies,
harmonies, and rhythms. The capability
to compose polyphonic (multiple, interacting voices) is also supported. MOOD is intended not only to serve as a composition
engine, but also as a machine that can be extended with user-defined rule
sets. Each rule set, uses the
internal MOOD interface and data types to apply its contents (different
compositional rules) to the decision-making process. The output of MOOD is independent of the input and
represented as the notes of the composition, which can be easily converted to
different type of media. In this case,
the MOOD engine outputs to a MIDI / text format, with which the final written
notes are readable as pitches and can be listened to using MIDI. The MOOD composition engine is based on the idea
that music is an highly creative process – and, in many ways, a logical and
organized one. It is also built on the
theory that while it is very difficult to construct a creative piece of
music, given an vast array of musical possibilities - it is often much easier
to determine which of the possibilities are bad, and gradually eliminate as
many of them as possible. This subtractive
method is the foundation of MOOD. MOOD is a constraint-based generator.
Initially, it has no rules for composition, and is completely random
within the framework of the engine.
Independent rules are then added by component, each of which will try to
further narrow down the possibilities of the next segment of composition. Ultimately, when there no more criteria upon
which to eliminate any more possibilities (notes, harmonies, and rhythms), the
engine will make a randomized decision among the choices that remain. The goal, in this case, is to reduce each
decision to a minimum set of choices. The components of MOOD are divided between the composers,
which will apply rules to an increasingly constrained set of choices in a
careful order, and the supporting data structures with which one is able to
represent the rules and structure within the music. In MOOD, a composer is an organized set of rules
that are applied in a predetermined order to the composition. The composition, represented as a composition
stream, is passed from composer to composer, each of which will try to
further narrow down the choices for the next decision. At the end of each round, the terminal
composers (Melody, Bass, and Voice composers) use the choices that still
remain to determine the final outcome of the next segment of music. The various types of composers in MOOD
are as follows. Tone Composer – this composer lays down the fundamental harmonic shape and
progression, through the use of note masks and harmony masks (see
section 4). It contains the rules and
constraints that dictate the overall tonal shape of the composition. Melody Shape Composer – This builds on the results of the Tone Composer
to outline a melodic shape, recording important melodic movements. Notes outlined in this module will very
likely be used as part of the final composition. Bass Shape Composer – This is similar to the Melody Shape Composer, but
composes a bass line. Rules of this
module are able to use the knowledge gained from the Tone Composer and the
Melody Shape Composer (or vice versa).
This allows the composition to examine and follow the harmonic movement
of the piece. Rating Composer – This composer analyzes the tonal progression constructed
so far from the Tone, Melody, and Bass Shape Composers, and assign ratings to
pitches that could be used as melody. A
vector of ratings accompanies each harmonic change. Each element in vector represents the attractiveness of a pitch
to harmonic movement and the melodic shapes.
This is a very important step, as the ratings will be the final
determining factors for the placement of each note. While the rating Composer is not the only composer that can
assign or modify the pitch rating, it will most likely do most of the
evaluation. For example, a rating
composer could enforce a tendency to follow stepwise melodic movement. Motive Composer – This composer will examine the melodic and bass shapes and add
embellishments to the melodic line. The
melody and bass composer will eventually fill in the notes that are empty. This is the place to really liven up the
composition rhythmically. Melody Composer – Melody composer is the final place to add constraints to the
composition stream. At the end, the
composer is responsible for choosing from the remaining choices of notes. Bass Composer
– Like the Melody Composer, the Bass Composer allow different part-writing
rules to be applied to the bass line.
Similarly, the bass composer will make the decision on all notes in the
bass, from the remaining choices. progress in levels of intelligence The section documents the progress of the MOOD
engine and introduces some algorithms used by the engine during the composition
process. The first musical piece
generated is almost completely tonally random.
It follows this algorithm: out.newTrack( 3,
40 ); for (int i = 0; i <
numStuff; i++) { for (int j = 0; j
< 9; j++) { out.addNote ( ourStrVector [randInt(NUM_PITCH_CLASSES)], randInt(2, 4), 1 );
out.advanceBeat(); } } out.endTrack(); Here is the resulting piece: Next,
I created a pitch mask, with which I filtered out notes that would be
dissonant by eliminating notes that are not in the key of ‘C’. NoteMask mask = new
NoteMask(false); mask.setTrue(C_NATURAL); mask.setTrue(D_NATURAL); mask.setTrue(E_NATURAL); mask.setTrue(F_NATURAL); mask.setTrue(G_NATURAL); mask.setTrue(A_NATURAL); mask.setTrue(B_NATURAL); for (int i = 0; i
< 40; i++) { for (int j = 0; j < 8; j++) { out.addNote ( ourStrVector[getInt(mask)], randInt(2, 4), 1 ); out.advanceHalfBeat(); } } Here is the result: By
filtering out the notes that doesn’t ‘belong’ in the key, we end up with a less
random piece of composition. Next,
we restrain the notes to be chosen from a varying set of ‘chords’. Each chord in the series of chords is
randomly generated through a mask, and then the mask is applied to the random
note generation. We introduce a chord
mask to filter out chords that we don’t want. At this point, the quality of each chord (major, minor,
diminished, augmented, major 7th, minor 7th, diminished 7th,
dominant 7th, and half diminished) are not dependent on the root of
the chord chosen, thus this model still leaves room open for notes that are in
different keys. NoteMask mask = new
NoteMask (true); NoteMask chMask =
new NoteMask (false); chMask.setTrue
(C_NATURAL); chMask.setTrue
(D_NATURAL); chMask.setTrue
(E_NATURAL); chMask.setTrue
(G_NATURAL); chMask.setTrue
(A_NATURAL); for (int i = 0; i
< 40; i++) { chordMask(mask, getInt(chMask), randInt(9));
for (int j
= 0; j < 8; j++) { out.addNote( ourStrVector[getInt(mask)],
randInt(2,4), 1 ); out.advanceHalfBeat();
} } Here is the result: Though now you may be able to make
out the harmonic structure of each segment, the order of the chords are in not
particular structure. So now we try the
cycle of fifth for the root of each chord in the progression. Additionally, we
limit each chord to have MAJOR quality. int chordRoot =
C_NATURAL; for (int i = 0; i
< 40; i++) { // cycle of
5th chordMask(mask, chordRoot = (chordRoot
+ 7) % NUM_PITCH_CLASSES_ABS,
MAJOR ); for (int j
= 0; j < 8; j++) { out.addNote( ourStrVector[getInt(mask)], randInt(2,4), 1
); out.advanceHalfBeat(); } } Here is the result: Example of MOOD – OS Engine
Compositions The following are results from an actual rule set loaded
into the MOOD engine and composed using its infrastructure. It uses three rules (one each for
ToneComposer, MelodyComposer, BassComposer) public void
process(MoodCompStream stream) { int loop =
0; MaskLine
mline = stream.toneLine(); QualLine
qline = stream.quality(); int
register = 4; Pitch
prevPitch = null; Note note; Pitch
currPitch = new Pitch(stream.tonic, register); qline.first(); for(mline.first();
!mline.isDone(); mline.next()) { RateMask newMask = RegisterMask ( qline.currentQual().rateMask, register, currPitch );
int
pitchVal = newMask.getRandHighest(); if
(pitchVal != MooMask.INVALID_VALUE) { currPitch
= new Pitch(pitchVal); note
= new Note(currPitch); stream.melody().addNewNode ( note, mline.currentTime() ); } qline.next(); } Here are the results: Representation for Music Objects: PitchClass Pitch Measure Note Chord Interval Key MOOD engine classes
Composition Modules Composer (base class) ToneComposer MelodyShapeComposer BassShapeComposer MotiveComposer RatingComposer MelodyComposer BassComposer
Composition Data
Structures
Masks MooMask PitchClassMask PitchMask RateMask
MoodCompStream TimeLine NoteLine QualLine MaskLine MIDI output MidiEvent MidiFileOutStream MidiHeader MidiTextWriter MidiTimeRep MidiWriter NotePacket PrintWriterPlus |