Melody
and Accompaniment
This tutorial will look at a file
which uses two different synthesis instruments. One for a
stochastic melody and the other for an chordal accompaniment.
If yo have not already gone through
the tutorial Audio 101, now would be a good
time, as this tutorial will build upon it.
This is what the result of this
tutorial file can sound like:
Click here to
view the source file..
Let's have a closer look:
import jm.JMC; import jm.music.data.*; import jm.util.*; import jm.audio.*; public class MelodyAndAccomp implements JMC{ private Score s = new Score("AU demo"); private Part p1 = new Part("Melody", 0); private Part p2 = new Part("Chords", 1); private int sampleRate = 8000; private Instrument[] insts = {new SineInst(sampleRate), new TriangleInst(sampleRate)}; private int bars = 4; //measures in American private static void main(String[] args) { new MelodyAndAccomp(); }
|
Here we see import statements and variables very similar
to what we saw in AudioScale (from
the Audio 101 tutorial). The main obvious difference is that we
have defined two different instruments this time: the TriangleInst we
used in AudioScale,
and the SineInst. This is
because we are going to use a different sound for each of the two parts
(SineInst for the melody and TriangleInst for the chords). When we
declare these, we store them in an Instrument array called insts.
This will be used later.
Note also that the
Part constructors are different - they do not specify a channel.
This is because we are not using MIDI in this program (if we were
using both MIDI as well as audio we could leave them in there. Infact,
even if we aren't using MIDI at all, there's not difference in the
output if you specify a channel - it's irrelevant!). The
instrument numbers for these parts are relative to the insts array
- the SineInst being instrument 0, and the TriangleInst being
instrument 1. We tell the audio render to use this array to define
the instruments later (when we actually 'render' ut, of course - see
the last section of this tutorial).
|
public MelodyAndAccomp() { makeMelody(); makeChords(); completeScore(); renderAudio(); }
|
The constructor is
extremely simple - it simply runs the following methods in the
appropriate order.
|
private void makeMelody() { Phrase phr = new Phrase(); int temp, newPitch; //variable to store random number int[] mode = {0,2,4,5,7,9,11,12}; //major scale degrees int prevPitch = 60; //start on middle C for(short i=0;i<bars*8-3;){ temp = (int)(Math.random()*14 - 7); newPitch = prevPitch + temp; for (short j=0; j< mode.length; j++) { if ( newPitch%12 == mode[j]) { if(i == bars*8-4) { Note n = new Note(newPitch, M, (int)(Math.random()*50+60)); phr.addNote(n); } else { Note n = new Note(newPitch, Q, (int)(Math.random()*50+60)); phr.addNote(n); } prevPitch = newPitch; i++; } } } p1.addPhrase(phr); } private void makeChords() { CPhrase cp = new CPhrase(0.0); for(short i=0;i<bars*2;i++) { int x = (int)(Math.random()*3); System.out.println("chord var x is " + x); if (x==0) { int[] pitchArray = {c3,e3,g3}; cp.addChord(pitchArray, SQ); cp.addChord(pitchArray, Q); cp.addChord(pitchArray, C); cp.addChord(pitchArray, SQ); } else if (x==1) { int[] pitchArray = {d3,f3,a3}; cp.addChord(pitchArray, M); } else { int[] pitchArray = {b2,d3,g3}; cp.addChord(pitchArray, C); cp.addChord(pitchArray, C); } } p2.addCPhrase(cp); } private void completeScore() { s.addPart(p1); s.addPart(p2); }
|
The above methods
should be relatively easy to understand (assuming you have already
covered the workings of the jMusic data representations such as Score
and CPhrase, tutorials for which are found elsewhere - try the Dot tutorials), and are fairly well commented.
After running these methods we will have a completed score....
|
private void renderAudio() { Write.au(s, "Melody_and_Accomp.au", insts); } }
|
...which
we can then send to Write.au(), specifying that it should use the
Instruments in inststo
render the appropriate sounds. This successfully gives us an .au
file of our piece! Simple!
|