Journey: Interlocking theme variations

In this tutorial we create a composition by making a polyphonic texture from themes derived from the same melodic material. The piece is in two small classes and provides a good example of programming as well as musical structuring processes.

Here is an example of the output from the Journey class.

Below is the source code for both the Journey.java file and the NPart.java file (both are required to run the program). Click to view it or right(option)-click to download it.

Click here to view Journey.java source

Click here to view NPart.java source

Lets have a closer look at the Journey class first:

import jm.JMC;
import jm.music.data.*;
import jm.util.View;
import
jm.util.Play;   /** * This class creates a collage of similar parts, each * of which may overlap and be rhythmicaly juxtaposed. * The phrase repetition is in a minimalist style. * The creation of parts is handled by the NPart class. * @author Andrew Brown */   public class Journey implements JMC { private Score score = new Score(); public static void main(String[] args) { new Journey(); }

The first few lines import the required jMusic packages and classes.

After a comment to describe the piece, the class is declared. First, a Score object is created at the class level. This means that this object will be 'visible' to all methods in this class. Because it is private it can't be seen outside this class.

The first method is main() in which all we do is create a new instance of the Journey class. In effect the main() method simply calls the constructor of this class.

The new keyword is important in these lines of code. It is used to create an instance of the Score class, then used to create an instance of the Journey class.

 
public Journey() {
// create new parts
for(int i=0; i<8;i++) {
new NPart(newPart());
}

// display the score data on screen
View.print(score);

// play the score Play.midi(score);
}

Above is the constructor method for the Journey class. Remember that the constructor method has the same name as the class ( Journey in this case) and does not have a return type specified in the first line like all other methods do.

This method provides an overview of our compositional process. First eight new parts are created in a loop. Next, the View class is used to print out the music as text so we can see what was generated visually.  Thirdly,the Play class is called upon to playback the composition.

The loop at the top has one line only. This line uses the new keyword again to create instances of the NPart class (we'll see more of it below). The NPart class takes a Part as an argument. This is created by the newPart() method because we want to set some values of the part before giving it over to the NPart class.

	private Part newPart() {
Part p = new Part();
p.setChannel(score.size());
score.addPart(p);
return p;
}
}

This newPart() method saves us some typing because we want to do things to each part we create. By making it a separate method we write it once and call it many times.

"What does it do?" I hear you ask. First, it creates a standard jMusic Part object. Next, it sets the channel depending on how many parts already exist. Using the size() method of the score it looks to see how many parts there are and uses that to set the channel. That way each new part is on a new channel. (Remember that if you save jMusic scores as MIDI files, or play them with QuickTime, you are limited to 16 channels (0-15)).

The part is added to the score and then passed back to the calling method, and in this case on to the NPart class for further stuffing-around-with.

The final curly bracket closes the class.


Well, that's the Journey class dealt with. Now let's look at the NPart class in detail:

import jm.JMC;
import jm.music.data.*;
import jm.music.tools.Mod;
import jm.util.*;
 
public class NPart implements JMC {
private Part part;
private int[] pitches = {C4, E4, G4, A4, D4, F4, E4};
private double[] rhythms = {Q, Q, Q, Q, Q, Q, C};
private double partLength = 40.0;
private double fadeInLength = 12.0;
private double fadeOutLength = 12.0;
private double nPartStartTime;
private int panPosition = (int)(Math.random() * 127);

The import statements provide access to the classes we need for this program.

A whole bunch of variables are declared for use in this class. This is done because many aspects of the class are going to vary from instance to instance. This is how we provide musical variety to the piece. So let's examine each class variable.

The part provides a local holder for the Part passed to this class by Journey.

pitches and rhythms hold the data to make up the notes. Changing these will change the stylistic character of the piece - you may want to give that a shot.

partLength is the duration of the music in this part in beats (crotchets).

fadeInLength and fadeOutLength specify how long it takes to ramp up and down the loudness of notes at the beginning and ending of the part. Compositionally this has the effect of crossfading the parts which makes the piece more interesting.

nPartStartTime contains the time (in beats) when the music of this part should begin.

panPosition holds the information about where in the stereo spectrum notes from this part should emerge.

	public NPart(Part p) {
this.part = p;
p.setInstrument((int)(Math.random()*60));
nPartStartTime = (int)(Math.random() * 200) * SQ;
// make a phrase for this part p.addPhrase(makePhrase()); }  

There are two methods in this class, the first (above) is the constructor. Note that it has the same name as the class. The constructor is run (is 'called') when a new instance of the class is made (is 'constructed').

First, the part passed from the Journey class is assigned to the local variable part.

Next, an instrument (sound) for this part is calculated at random between between 0 an 59.

Then a start time for this class is calculated at random somewhere within the first 200 semiquaver steps of the piece.

Each part in this piece has just one Phrase and the last line calls the makePhrase() method to make it and adds it to the part, all in one fowl swoop.

 
private Phrase makePhrase() {
Phrase phr = new Phrase( nPartStartTime);
phr.addNoteList(pitches, rhythms);
phr.setPan(Math.random());
Mod.shuffle(phr);
Mod.cycle(phr, partLength);
Mod.fadeIn(phr, fadeInLength);
Mod.fadeOut(phr, fadeOutLength);
return phr;
}

}
 

This method generates a phrase based on notes attributes (pitch and rhythm) specified earlier as class variables.

First, it makes an empty phrase and sets the start time.

Second, it uses the Phrase.addNoteList() method to generate notes based on the pitches and rhythm values provided as arrays earlier.

Thirdly, it sets the pan position for each note in the phrase. The pan position is calculated at random. In jMusic pan positions between 0 and 1 and in the stereo field - 0 is extreme left, and 1 is extreme right, so 0.5 is in the middle. By default the Math.random() method generates double values between 0 and 1. Neat hey!

At this stage the notes in the phrase are set. But we want each phrase to be somewhat different so we modify the phase to add variety. To do this we make use of the Mod class in jm.music.tools directory. We imported this class at the top of the code - have a look to check.

The first modification is shuffle()which randomly mixes up the note order of the phrase.

The second modification is cycle() which loops the phrase until it fills the required number of beats. This makes the phrase the length we specified with the variable partLength.

The third and fourth modifications fade in and out the loudness of the notes over a specified length.

Finally we pass back the generated and modified phrase to the calling method (above).


Important lessons from this tutorial:

The breaking up of this program into many small methods saves repetitive typing and makes the code easier to read and changes easier to implement. Similarly, the use of class variables means that musical changes (such as the length of the parts) can be changed in one location only and then wherever that variable is used the changes will cascade through the file. The division of the program into more than one class file keeps each file managable in size, and provides a physical division where there is a logial (composition) one. In this case working on the parts is kept separate from working on the score structure of those parts.

You should try changing the variables to see how they effect the music, and then try some more significant changes, perhaps adding different modifications to the phrase, or being more 'careful' about the choice of instrument or start time by replacing the simple (and dumb) random choices made so far.

 


J Music Tutorial Index