Jazz Combo (Beginners!)

This program is a set of classes which produce a simple bass, drums, and guitar backing to an awkward solo line. The solo has constrained stochastic pitches while the rhythm section is largely deterministic.

A final MIDI file can sound like this:

Click here to view source.

The single source file contains several classes. Jazz Combo - the main class. JazzDrums, JazzGuitar, JazzBass, and JazzSax.

Lets have a closer look.
import jm.JMC;
import jm.music.data.*;
import jm.music.tools.*
import jm.util.*;

 
/**
* This class uses the jMusic CPhrase (Chord Phrase)
* The class generates a bass line, chords, melody, and two percussion
* parts around the cycle of 5ths chord progression.
* It uses several static utility classes supporting one main class.
* This program is based upon the Chords example in the jMusic tutorial.
* If this class looks daunting check out the Chords example first.
* @author Andrew Brown
*/

public class JazzCombo implements JMC{

public static void main(String[] args){
new JazzCombo();
}

public JazzCombo() {
Score s = new Score("CPhrase class example", 120);
Part guitarPart = new Part("Guitar",JAZZ_GUITAR, 0);
Part bassPart = new Part("Bass", ACOUSTIC_BASS, 1);
Part ridePart = new Part("Drums", 0, 9);
Part snarePart = new Part("Drums 2", 0, 9);
Part saxPart = new Part("Sax", SAXOPHONE, 2);
Phrase bassPhrase = new Phrase();

//Let us know things have started
System.out.println("Creating chord progression . . .");

//choose rootPitch notes around the cycle of fifths
int rootPitch = c4; //set start pitch to C
for (int i=0;i<6;i++) {
for (int j=0;j<4;j++) { // 4 chords to a bar
guitarPart.addCPhrase(JazzGuitar.triad
(rootPitch));
}
bassPart.addPhrase(JazzBass.bassLine(rootPitch));
ridePart.addPhrase(JazzDrums.swingTime());
snarePart.addPhrase(JazzDrums.swingAccents());
saxPart.addPhrase(JazzSax.melody(rootPitch));
// choose the next root pitch
rootPitch -= 7;
for (int k=0;k<4;k++) {
guitarPart.addCPhrase(JazzGuitar.domSeventh
(rootPitch));
}
bassPart.addPhrase(JazzBass.bassLine2(rootPitch));
ridePart.addPhrase(JazzDrums.swingTime());
snarePart.addPhrase(JazzDrums.swingAccents());
saxPart.addPhrase(JazzSax.melody(rootPitch));
rootPitch += 5;
}

//pack the parts into a score
s.addPart(guitarPart);
s.addPart(bassPart);
s.addPart(ridePart);
s.addPart(snarePart);
s.addPart(saxPart);

//display the music
View.show(s);

// write the score to a MIDIfile
Write.midi(s,"JazzCombo.mid");
}
}

 
The class above calls on the classes below. The main() method class calls the JazzCombo constructor which gets Phrases or CPhrases from each of the support classes.  It then then adds these Phrases/CPhrases to a score, displays it and saves it as a MIDI file.

Notice that this structure allows you to write new classes for any of the parts (drums, bass, etc.) without upsetting any of the others. The use of individual classes also eases maintenance.

In the source code all the classes are in the one file. By doing this they share the import statements, but if these are copied then each class can easily be in its own java file. Notice weather or not the classes are in the same java file they appear as independent class files when compiled.

class JazzGuitar implements JMC{

public static CPhrase triad(int rootPitch) {
// build the chord from the rootPitch
int[] pitchArray = new int[3];
pitchArray[0] = rootPitch;
pitchArray[1] = rootPitch + 4;
pitchArray[2] = rootPitch + 7;
//add chord to the part
CPhrase chord = new CPhrase();
chord.addChord(pitchArray, C, 50);
return chord;
}

public static CPhrase domSeventh(int rootPitch) {

// build the chord from the rootPitch

int[] pitchArray = new int[4];
pitchArray[0] = rootPitch;
pitchArray[1] = rootPitch + 4;
pitchArray[2] = rootPitch - 2;
pitchArray[3] = rootPitch - 5;

//add chord to the part

CPhrase chord = new CPhrase();
chord.addChord(pitchArray, C, 50);
return chord;
}
}
 
class JazzBass implements JMC{

public static Phrase bassLine(int rootPitch) {

// build the bass line from the rootPitch

Phrase phr = new Phrase();
phr.addNote(new Note(rootPitch, C));
phr.addNote(new Note(rootPitch - 2, C));
phr.addNote(new Note(rootPitch - 3, C));
phr.addNote(new Note(rootPitch - 5, C));
return phr;
}

public static Phrase bassLine2(int rootPitch) {

// build the bass line from the rootPitch

Phrase phr = new Phrase();
phr.addNote(new Note(rootPitch, C));
phr.addNote(new Note(rootPitch + 4, C));
phr.addNote(new Note(rootPitch + 7, C));
phr.addNote(new Note(rootPitch + 4, C));
return phr;
}
}

 
This bass class is quite simple. It creates a series of notes based on a root pitch.

class JazzDrums implements JMC{

public static Phrase swingTime() {

// build the ride line

Phrase phr = new Phrase();
int ride = 51;
phr.addNote(new Note(ride, C));
phr.addNote(new Note(ride, 0.67));
phr.addNote(new Note(ride, 0.33));
phr.addNote(new Note(ride, C));
phr.addNote(new Note(ride, C));
return phr;
}

public static Phrase swingAccents() {

// build the bass line from the rootPitch

Phrase phr = new Phrase();
int snare = 38;
for (int i=0;i<4;i++) {
phr.addNote(new Note(REST, 0.67));
phr.addNote(new Note(snare, 0.33,
(int)(Math.random()*60)));
}
return phr;
}
}

 
Jazz drums are charracterised by a dominance of ride cymbal, this is all that is used here. Accents on the snare are added with a call to the Math.random() method to alter the dynamics.

class JazzSax implements JMC{

public static Phrase melody(int rootPitch) {

// build the rhythms

double[] rhythm1 = {0.34,0.66,0.34,0.66,0.34,0.66,0.34};
double[] rhythm2 = {0.34,0.66,0.34,0.66,1.34};
double[] rhythm3 = {1.0,0.34,0.66,0.34,1.0};
double[] rhythm4 = {0.34,0.66,1.0,1.34};
int[] mode = {0,4,5,7,9};
Phrase phr = new Phrase();
int pitch = (int)Math.random()*12 + 65;
int temp = 0;
boolean ok = false;
int rhythmNumb = (int)(Math.random() *4);
int rhythmLength = 0;

//choose a rhythm to use for the phrase

if (rhythmNumb == 0) rhythmLength = rhythm1.length;
if (rhythmNumb == 1) rhythmLength = rhythm2.length;
if (rhythmNumb == 2) rhythmLength = rhythm3.length;
if (rhythmNumb == 3) rhythmLength = rhythm4.length;

// each phrase starts with a rest

phr.addNote(new Note(REST, 0.66));

//loop through the rhythm adding notes in a random walk

for(int i=0;i<rhythmLength;i++) {
while (ok == false) {

//get new interval

temp = (int)(Math.random() * 10) - 5;

//check to see if new note is in the mode

for(int j=0;j<mode.length;j++) {
if ((pitch + temp)%12 ==
(mode[j] + rootPitch)%12) {
pitch += temp;
ok = true;
break;
}
}
}

//add the next note to the phrase

if (rhythmNumb == 0) phr.addNote(
new Note(pitch, rhythm1[i]));
if (rhythmNumb == 1) phr.addNote(
new Note(pitch, rhythm2[i]));
if (rhythmNumb == 2) phr.addNote(
new Note(pitch, rhythm3[i]));
if (rhythmNumb == 3) phr.addNote(
new Note(pitch, rhythm4[i]));
ok = false;
}

return phr;
}
}
Soloing is hard for humans, let alone computers! This is a poor attempt even though it takes quite some code - compared to our current tutorial benchmarks. It uses a pentatonic mode and some set rhythms to constrain the melodic choice to a half-decent but also predictable level. I think it might be easier for me to play the solo live :)



jMusic Tutorial Index