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.*; 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(); System.out.println("Creating chord progression . . ."); 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)); 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; } s.addPart(guitarPart); s.addPart(bassPart); s.addPart(ridePart); s.addPart(snarePart); s.addPart(saxPart); View.show(s); 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; CPhrase chord = new CPhrase(); chord.addChord(pitchArray, C, 50); return chord; } public static CPhrase domSeventh(int rootPitch) { int[] pitchArray = new int[4]; pitchArray[0] = rootPitch; pitchArray[1] = rootPitch + 4; pitchArray[2] = rootPitch - 2; pitchArray[3] = rootPitch - 5; CPhrase chord = new CPhrase(); chord.addChord(pitchArray, C, 50); return chord; } } class JazzBass implements JMC{ public static Phrase bassLine(int 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) { 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() { 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() { 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) { 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; if (rhythmNumb == 0) rhythmLength = rhythm1.length; if (rhythmNumb == 1) rhythmLength = rhythm2.length; if (rhythmNumb == 2) rhythmLength = rhythm3.length; if (rhythmNumb == 3) rhythmLength = rhythm4.length; phr.addNote(new Note(REST, 0.66)); for(int i=0;i<rhythmLength;i++) { while (ok == false) { temp = (int)(Math.random() * 10) - 5; for(int j=0;j<mode.length;j++) { if ((pitch + temp)%12 == (mode[j] + rootPitch)%12) { pitch += temp; ok = true; break; } } } 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 :)
|