Dots and Dashes 5

In this tutorial we will orchestrate the score by allocation each phase to a different part and instrument. Finally we get to hear the music! This class allows us to play back the music using QuickTime (Mac OS and Windows only). Also we will make the creation of parts even more efficient by collecting common stages of the process into a separate method.

Here is the source file for this class.

Dot05.java

Have a look through the code and work out what you can, then read the explanation below. There are quite a few new aspects to this program so take your time and read through the explanation a few times. Most of all, try changing the code to ensure you can make predictable alterations that indicate you really know what everything is doing.

Let's have a closer look.
import jm.music.data.*;
import jm.JMC;
import jm.util.*;
import qt.QTPlayer;
import jm.music.tools.*;
 
public class Dot05 implements JMC {
    private static Score aScore = new Score("Composition 101");
    private static int partCounter = 0;
    
    public static void main(String[] args) {
        new Dot05();
    }
    
    public Dot05() {
        double[] n = {C4, QUARTER_NOTE};
        notesToPart(n, 0.0, 23, CLARINET);
        
        double[] phrase2data = {REST, EIGHTH_NOTE, E4, EIGHTH_NOTE,
                REST, QUARTER_NOTE};
        notesToPart(phrase2data, 4.0, 9, FLUTE);
        
        double[] phrase3data = {REST, QUARTER_NOTE, F4, EIGHTH_NOTE,
                REST, EIGHTH_NOTE};
        notesToPart(phrase3data, 8.0, 7, NYLON_GUITAR);
        
        View.show(aScore);
        QTPlayer.display(aScore);
    }
    
    private static void notesToPart(double[] notes, 
                double startTime, int repeats, int instrument) {
        // create a new phrase from the notes and loop it
        Phrase aPhrase = new Phrase(startTime);
        aPhrase.addNoteList(notes);
        Mod.repeat(aPhrase, repeats);
        // create a new part and add the phrase to it
        Part aPart = new Part("Part "+ partCounter, 
                instrument, partCounter);
        aPart.addPhrase(aPhrase);
        // keep track of how many parts have been created
        partCounter++;
        // add the part to the score
        aScore.addPart(aPart);
    }
}

In this version of the program there are two musical differences, 1) each musical line has a different timbre and 2) each line begins at a different point in time.

Between the class definition and the main() method is the declaration of two class variables.

private Score aScore = new Score("Composition 101");

private int partCounter = 0;

The two variables are called aScore and partCounter. aScore is an instance of the Score class. This is another of the jMusic data classes. Scores can contain a number of jMusic Parts. In this case the Score constructor is invoked by the new statement and is passed a score title "Composition 101" as an argument.

The partCounter class variable is of type int (i.e., whole numbers). It is given an initial value of zero. This will be used later to count the number of parts and set channels and a unique title for each part.

Both of these class variables are declared to be private and static. private indicates that they cannot be accessed from outside this class, and static indicates that there will not be copies or instances of them.

Architecturally, the main obvious change in this program is the division of the class into two methods, main() and notesToPart(). Each of the phrases is constructed from arrays of data, even the first phrase with only one note is done this way. This might seem overly complex but by having all phrases constructed in the same way we can leverage the similarity by having each of the phrases share the notesToPart() method.

Having constructed an array of pitch and rhythm pairs the notesToPart method is called, as with:

notesToPart(n, 0.0, 23, CLARINET);

This will run the method and passes it four arguments. To see what they are you can look at the method declaration line, but in English - The first, n, is the array of data. 0.0 is the start time for the phrase, 23 is the number of times the phrase should be repeated, and CLARINET is a JMC constant for a General MIDI sound.

After creating the three musical lines the score is displayed with the show utility;

View.show(aScore);

Notice that show is passed the aScore Score which was declared as a class variable.

Next, we open a utility that will playback the music. The QTplayer, QuickTime player, will display a GUI for us to control.

QTPlayer.display(aScore);

The QTPlayer class calls it's display() method to make visible the player. Remember that this requires Apple's QuickTime for Java to be installed which is NOT a standard part of Java or jMusic so is installed additionally to them.

Now lets look at the next method:

private static void notesToPart(double[] notes, double startTime, int repeats, int instrument) {

The notesToPart() method creates a Phrase from the notes passed to it, then creates a Part and adds the Phrase to that Part. Lastly it adds the Part to the Score called aScore. This process is required for each musical line, so this method is called three times. It is obviously more convenient to only write this out once, rather than three (or more) times. However, making the method generalisable is a little more trouble than making it dedicated to one musical part only, but the more often it is used the more valuable the trade off between complexity and reusability.

The method is private, static, and void (i.e., it does not return any values to the calling method). The rest of the declaration (between brackets) describes the arguments that it will take, and it what order. The first is an array of double values called notes, then a double value called startTime, then an int called repeats, and lastly an in called instrument. This allows for each musical line to differ in each of these ways. They can have different notes, different startTime, a different number of repetitions, and be played by a different instrument.

The first task of the method is to create the phrase - this is similar to previous Dot classes. The line with two slashes "//" is a comment only and has no effect on the program.

// create a new phrase from the notes and loop it
Phrase aPhrase = new Phrase(startTime);
aPhrase.addNoteList(notes);
Mod.repeat(aPhrase, repeats);

A Part is created in which to put the phrase. The Part constructor used in this case takes three arguments, a title, an instrument numbers, and a channel number. In jMusic the timbre of a notes is usually dictated by the Part. The metaphor is that all notes in the flute part have a similar timbre because they are played on a flute instrument. The channel number is mainly required for MIDI and QuickTime playback as only one instrument can be allocated to each channel.

// create a new part and add the phrase to it
Part aPart = new Part("Part "+ partCounter, instrument, partCounter);
aPart.addPhrase(aPhrase);

The partCounter variable starts at zero and then changes each time the notesToPart() method is called as a result of this line. The ++ means add 1 to the current value.

partCounter++;

The Part is lastly added to the Score:

aScore.addPart(aPart);

Phew! The End. Conceptually this class introduces one main concept - adding a method to collapse code that is required several times. Musically, we have separated each phrase into a different part which makes orchestration possible, and we've show how to play back music in jMusic with QuickTime. It's not really as complex as saying it in English implies . . . let's hope :)


jMusic Tutorial Index