Guido Word Music

This is an implementation of one of the oldest known algorithmic music processes. It is rule based, not recombinatorial. Each vowel is allocated a pitch. This implementation is not strict (as the original was designed for Latin texts) but has been adapter to Roman languages and modern tonal sensibilities. It is quite intriguing how close this process is the Arvo Pärt's compositional processes! (Read about it in Hiller's book on Pärt.)

The interface:
Guido Interface

This demo is a realisation of Mozart's design in jMusic by Andrew Troedson.

Click here to view source part 1.

Click here to view source part 2.

To hear the result play the Quicktime movie below.

Andrew Troedson's comments:

An Overview

Based on Guido d'Arezzo's lookup chart for generating pitches from syllables (ca. 1000 A.D.), this melody generation tool creates a monophonic phrase by extrapolating from a given text. A set of rules (see Table 2.1 below) is used to select note pitches depending on the vowels found in the text. Although d'Arezzo's original intention was simply to provide an approximate guide from which a composer could make selections to fit his/her taste, this tool automates the process, and itself chooses from the given note options. As well as this, the wordMusic tool sets the selected notes to a rhythm dependant on the number of consonants between each vowel (see Table 2.2).

Table 2.1: The note pitch value options assigned by wordMusic to each vowel

g2

a2

b2

c3

d3

e3

f3

g3

a3

b3

c4

d4

e4

f4

g4

a4

A

E

I

O

U

A

E

I

O

U

A

E

I

O

U

A

Table 2.2: The rhythm values assigned by wordMusic dependant of the number of consonants between each vowel.

<2

2-3

4

>4

Quaver

Crotchet

Minim

Dotted Minim

Technicalities

This interpretation of d'Arezzo's lookup chart is set up in a single class, wordMusic.java. However, a GUI interface which utilizes this class and allows new text to be easily entered, has also been created and is called wordMusicMaker.java. The default name of the generated MIDI file is "wordMusic.mid", although this can be easily changed when using the GUI interface.

Observations

This melody generation tool, while based on a set of very simple rules, is quite effective in creating musically interesting phrases. Because the music it composes is generated entirely based on this set of rules, it can be said to be taking an algorithmic approach to the generation of music.

Initially, the simplicity of the rules used can be deceiving, however when looked at more closely, they do have some theoretical grounding. Most melodies that are written for specific texts do set notes to every syllable, and the rhythm of the text does in general have some relationship to the length of the words (and the distance between adjacent vowels). Although this is not an exact relationship (and problems occur with words that do not contain vowels, for example "sky"), the results produced by its implementation are very acceptable. It should also be noted that the original rhythm algorithm involved a greater variety of note lengths (ranging from semiquavers to semibrieves), however this was simplified as the resulting phrases tended to be somewhat disjointed.

Of course, the algorithm used could be extended further to take voice leading into account, or could even be developed to produce polyphonic phrases, however, in its current form, the wordMusic melody generation tool does provide a good example of the possibilities of algorithmically generating music - even with only the simplest of rules.


The code:

Let's have a closer look.

/*
* A class which creates a GUI interface for
* the wordMusic class - allowing for easy
* entry of different texts
* @author Andrew Troedson
*/

import java.awt.*;
import java.awt.event.*;

public class wordMusicMaker extends Frame implements ActionListener, WindowListener{

TextField fileName;
TextArea wordInput;
wordMusic musicMaker = new wordMusic();

public void actionPerformed(ActionEvent ae){
if(ae.getActionCommand() == "ActionCompose") {
musicMaker.compose(wordInput.getText(), fileName.getText());
}
}

// Deal with the window closebox
public void windowClosing(WindowEvent we) {
System.exit(0);
}
//other WindowListener interface methods
//They do nothing but are required to be present
public void windowActivated(WindowEvent we) {};
public void windowClosed(WindowEvent we) {};
public void windowDeactivated(WindowEvent we) {};
public void windowIconified(WindowEvent we) {};
public void windowDeiconified(WindowEvent we) {};
public void windowOpened(WindowEvent we) {};

public static void main(String args[]){
wordMusicMaker wmm = new wordMusicMaker("Word Music");
}

public wordMusicMaker(String title){
super(title);

//register the closebox event
this.addWindowListener(this);

//assign the frame an icon (only seen in Windows OS)
Image i = Toolkit.getDefaultToolkit().getImage("wmicon.gif");
this.setIconImage(i);
setLayout(new BorderLayout());
Color lightGrey = new Color(240,240,240);
this.setBackground(lightGrey);

Label mainTitle = new Label("WORD MUSIC by Andrew Troedson", 1);
add(mainTitle, "North");

wordInput = new TextArea("Type text here...", 6, 40, 3);
wordInput.setBackground(Color.white);
add(wordInput, "Center");

Panel windowBottom = new Panel();
Label outputFile = new Label("Output File:",1);
fileName = new TextField("wordMusic.mid",16);
Button compose = new Button("Compose");
compose.setBackground(Color.lightGray);
compose.setForeground(Color.darkGray);
compose.addActionListener(this);
compose.setActionCommand("ActionCompose");

windowBottom.add(outputFile);
windowBottom.add(fileName);
windowBottom.add(compose);
add(windowBottom, "South");

Panel eastBorder = new Panel();
add(eastBorder, "East");
Panel westBorder = new Panel();
add(westBorder, "West");

pack();
show();
}
}
The code is adequately commented such that you should be able to follow it thorugh. It mainly implements a GUI which calls the wordMaker class whose code is below.

/* A class which generates music from words
* Based on Guido d'Arezzo's lookup chart for generating
* pitches from syllabes (ca. 1000 A.D.)
* @author Andrew Troedson
*
* g2 a2 b2 c3 d3 e3 f3 g3 a3 b3 c4 d4 e4 f4 g4 a4
* A E I O U A E I O U A E I O U A
*/

import java.text.*; //contains the StringCharacterIterator
import jm.music.data.*; //the jMusic classes
import jm.JMC;
import jm.util.*;

public final class wordMusic implements JMC {

//the passage from which the melody will be generated
String passage;
//the name of the file produced
String fileName;

public wordMusic() {
}

public void setPassage(String passage) {
this.passage = passage;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

//compose method takes two args: the passage String and the fileName String
public void compose(String p, String f) {

passage = p;
fileName = f;
Score score = new Score("wordMusic", 120);
Part piano = new Part("Piano", PIANO, 0);
Phrase phrase = new Phrase(0.0);
Note nextNote = new Note();

//set a default passage if none is given
if (passage.length() == 0) {
setPassage("twinkle twinkle little star,"+
"how I wonder what you are,"+
" up above the world so high,"+
" like a diamond in the sky.");
}

//set a default filename if none is given
if (fileName.length() == 0) {
setFileName("wordMusic.mid");
}

//change all the letters in the passage variable to
//Upper Case to remove case sensitivity

passage = passage.toUpperCase();
//assign a StringCharacterIterator to the passage
StringCharacterIterator iter = new StringCharacterIterator(passage);

//the variable used to count the number of non-vowels
//(ie consonants and symbols) between each vowel

//this number is sent to setNoteLength() every time
//a new note is made

int nonVowelCounter = 0;

//this variable is set each new note is created, and
//sets the Pitch of the new note
(this is chosen at
//random from the 3 or 4 options allowed for that vowel
//in d'Arezzo's lookup chart)

int notePitch;

//the variable used to make the random pitch selection
int randNum;

//the main iteration loop which goes through passage
//and makes the notes
these notes are then added to
//the Phrase phrase

for(int i=0; i<passage.length(); i++) {

iter.setIndex(i);
char nextChar = iter.current();
System.out.println(i + " " + nextChar);

switch (nextChar) {
case 'A':

randNum = ((int)(java.lang.Math.random()*4));

if (randNum == 0){
notePitch = G2;}
else if (randNum == 1){
notePitch = E3;}
else if (randNum == 2){
notePitch = C4;}
else {
notePitch = a4;}

nextNote = new Note(notePitch,setNoteLength(
nonVowelCounter));
phrase.addNote(nextNote);
nonVowelCounter = 0;
break;

case 'E':

randNum = ((int)(java.lang.Math.random()*3));

if (randNum == 0){
notePitch = A2;}
else if (randNum == 1){
notePitch = F3;}
else {
notePitch = D4;}

nextNote = new Note(notePitch,setNoteLength(
nonVowelCounter));
phrase.addNote(nextNote);
nonVowelCounter = 0;
break;

case 'I':

randNum = ((int)(java.lang.Math.random()*3));

if (randNum == 0){
notePitch = B2;}
else if (randNum == 1){
notePitch = G3;}
else {
notePitch = E4;}

nextNote = new Note(notePitch,setNoteLength(
nonVowelCounter));
phrase.addNote(nextNote);
nonVowelCounter = 0;
break;

case 'O':

randNum = ((int)(java.lang.Math.random()*3));

if (randNum == 0){
notePitch = C3;}
else if (randNum == 1){
notePitch = A3;}
else {
notePitch = F4;}

nextNote = new Note(notePitch,setNoteLength(
nonVowelCounter));
phrase.addNote(nextNote);
nonVowelCounter = 0;
break;

case 'U':

randNum = ((int)(java.lang.Math.random()*3));

if (randNum == 0){
notePitch = D3;}
else if (randNum == 1){
notePitch = B3;}
else {
notePitch = G4;}

nextNote = new Note(notePitch,setNoteLength(
nonVowelCounter));
phrase.addNote(nextNote);
nonVowelCounter = 0;
break;

//for all other letters (ie all the consonants)
//and syllables, no notes are created

default:
//add 1 to the consonant counter
nonVowelCounter++;
break;
}
}

piano.addPhrase(phrase);
View.print(piano);

//add part (instrument) to the score
score.addPart(piano);

//OK now we test SMF write and read

Write.midi(score, fileName);
}

//an internal method which decides on the length of each note
//depending on the number of consonants (as well as spaces
//and other symbols) between vowels
static double setNoteLength(int counter){

double noteLength;
switch (counter) {
case (0):
noteLength = Q;
break;
case (1):
noteLength = Q;
break;
case (2):
noteLength = C;
break;
case (3):
noteLength = C;
break;
case (4):
noteLength = M;
break;
default: //(ie more than 4 consonants)
noteLength = MD;
break;
}
return noteLength;
}
}
This is the class which does all the work. The main thing to notice is the switch statement which checks to see if the nexts letter is a vowel. A switch statement is also used to select a duration based on the number of letters between vowels.



jMusic Tutorial Index