Signal Flow
This tutorial explains the standard way of
indicating signal flow and how to create an jMusic instrument from a
signal flow diagram.
Hear the result of this tutorial's music
below.
Gongs2.mp3 [60K]
View / Download source
- Instrument source code
View / Download source -
Composition source code
Lets have a closer look.
import jm.audio.io.*;
import jm.audio.synth.*;
import jm.music.data.Note;
import jm.audio.AudioObject;
public final class VaryDecaySineInst extends jm.audio.Instrument{
private EnvPoint[] pointArray = new EnvPoint[10];
private int channels;
private int sampleRate;
public VaryDecaySineInst(int sampleRate){
this(sampleRate, 2);
}
EnvPoint[] tempArray = {
new EnvPoint((float)0.0, (float)0.0),
new EnvPoint((float)1.0, (float)0.001),
new EnvPoint((float)0.5, (float)0.1),
new EnvPoint((float)0.05, (float)(Math.random() * 0.4 + 0.3)),
new EnvPoint((float)0.0, (float)(Math.random() * 0.3 + 0.7))
};
pointArray = tempArray;
}
public void createChain(){
WaveTable wt = new WaveTable(this, this.sampleRate,
Oscillator.getSineWave(this.sampleRate), channels);
Envelope env = new Envelope(wt, pointArray);
Volume vol = new Volume(env,(float)1.0);
StereoPan span = new StereoPan(vol);
SampleOut sout = new SampleOut( span, "jmusic.tmp");
}
}
|
Music V
One of the earliest computer
music langauges was Music V (five) written by Max Matthews at
Bell Labs in the USA. This language established the convention
of using flow diagrams to depict signal flow, and this tradition
is continued in jMusic in the audio chain structure.
The SimpleSineInst class (previous
tutorial) would be depicted by the Music V signal flow diagram
shown to the right. The top object is a break-point envelope generator
that take two attributes, the time and amplitude of the points.
The output of this modifies the aplitude of a sine wave oscillator,
which also takes a frequency (pitch) attribute.
While this diagram is the traditional representation a more accurate
representaion of the jMusic chain of events is depicted below.
|
|
In jMusic this process would be articulated
in an Instrument as:
public void createChain(){
WaveTable wt = new WaveTable(this, this.sampleRate,
Oscillator.getSineWave(this.sampleRate), channels);
Envelope env = new Envelope(wt, pointArray);
SampleOut sout = new SampleOut(env);
} |
The jMusic chain order operates from the sample origin onward, that
is, the samples are first created by the oscillator which is why it
is first in the chain,
THEN the sample amplitide is adjusted by the envelope object.
You will see the Music V style diagrams in many books (and in these
tutorials) so it is useful to understand how to translate them into
a jMusic instrument.
Audio Object chains
The SineInst class is represented below as
a flow diagram and code.
public void createChain(){
WaveTable wt = new WaveTable(this, this.sampleRate,
Oscillator.getSineWave(this.sampleRate), channels);
Envelope env = new Envelope(wt, pointArray);
Volume vol = new Volume(env,(float)1.0);
StereoPan span = new StereoPan(vol);
SampleOut sout = new SampleOut( span, "jmusic.tmp"); } |
The difference is that this class adds more functionality to the instrument,
implied but not specified by the diagram.
The Volume object allows the Instrument to respond to a Note's dynamic
values and the StereoPan object enables the Instrument
(given that it has two channels) to respond to a Note's pan value.
The MusicV diagram can been seen as a somewhat abstract representation
of the audio process.
it indicates the major synthesis components an omits details such as
wheather the signal is mono or stereo.
Variable Sine Instrument
For this tutorial's composition (Gong2) we've created a variation on
the SineInst class that allows for some randomness in the envelope settings.
This means that each instance of the instrument will be slightly different
- thus providing some timbral variety in the rendered file.
The createChain() method of VaryDecaySineInst is identicale to the
SineInst class.
The interest is in the specifying of the Envelope points in the constructor,
as shown below.
public VaryDecaySineInst(int sampleRate, int channels){
this.sampleRate = sampleRate;
this.channels = channels;
EnvPoint[] tempArray = {
new EnvPoint((float)0.0, (float)0.0),
new EnvPoint((float)1.0, (float)0.001),
new EnvPoint((float)0.5, (float)0.1),
new EnvPoint((float)0.05, (float)(Math.random() * 0.4 + 0.3)),
new EnvPoint((float)0.0, (float)(Math.random() * 0.3 + 0.7))
};
pointArray = tempArray;
}
|
The decay and release portions of the envelope
are set to random positions within certain constraints.
new EnvPoint((float)0.05, (float)(Math.random() * 0.4 + 0.3)),
new EnvPoint((float)0.0, (float)(Math.random() * 0.3 + 0.7)) |
The varying decay rates of the sine waves will mean that the timbral
belnd between harmonis will change throughout the notes duration,
creating a more dynamic timbre and (hopefully) a more insteresting sound.
as you can see the instrument definition in jMusic is quite flexible
and,
with some imagination, can result in novel sounds.
Gong2 Composition
This class elaborates on the previous Gong composition by plaing each
note in a different part, and therefore with a different instrument.
T his is required to exploit the variations in envelope discussed above.
import jm.music.data.*;
import jm.music.tools.*;
import jm.JMC;
import jm.audio.*;
import jm.util.*;
public class Gongs2 implements JMC {
public static void main(String[] args) {
new Gongs2();
}
public Gongs2() {
Score score = new Score();
Part part0 = new Part("zero", 0);
part0.addPhrase(new Phrase(new Note(CS4, 8.0)));
Part part1 = new Part("one", 1);
part1.addPhrase(new Phrase(new Note(BF4, 8.0)));
Part part2 = new Part("two", 2);
part2.addPhrase(new Phrase(new Note(D5, 8.0)));
Part part3 = new Part("three", 3);
part3.addPhrase(new Phrase(new Note(E5, 8.0)));
Part part4 = new Part("four", 4);
part4.addPhrase(new Phrase(new Note(A5, 8.0)));
score.addPart(part0);
score.addPart(part1);
score.addPart(part2);
score.addPart(part3);
score.addPart(part4);
View.show(score);
int sampleRate = 22000;
Instrument variableSine1 = new VaryDecaySineInst(sampleRate);
Instrument variableSine2 = new VaryDecaySineInst(sampleRate);
Instrument variableSine3 = new VaryDecaySineInst(sampleRate);
Instrument variableSine4 = new VaryDecaySineInst(sampleRate);
Instrument variableSine5 = new VaryDecaySineInst(sampleRate);
Instrument[] ensemble = {variableSine1, variableSine2, variableSine3,
variableSine4, variableSine5};
Write.au(score, "Gongs2.au", ensemble);
}
}
|
Note that in decalring new parts, a different
instrument number is assigned to each:
Part part3 = new Part("three", 3); |
To accomodate several instruments an instrument
array (called ensemble) is constructed and then passed to the rendering
method.
Instrument[] ensemble = {variableSine1, variableSine2, variableSine3,
variableSine4, variableSine5};
Write.au(score, "Gongs2.au", ensemble); |
|