|
import jm.audio.io.*; |
Instrument class overview
jMusic instruments, such as the SimpleSineInst above, extend the Instrument class.
public final class SimpleSineInst extends jm.audio.Instrument{
This provides them with significant functionality and means that the instrument classes you create need only pay attention to the timbral aspects of the instruments, leaving the details of communication between audio objects and jMusic scores to the Instrument super class.
Class variables for sample rate and number of channels can be decalred with constant values or assigned with values from constructors.
private int sampleRate;
private int channels;
In the SimpleSineInst they are assigned from the constructor.
public SimpleSineInst(int sampleRate){
this.sampleRate = sampleRate;
this.channels = 1;
}
jMusic instruments can be rendered at any sample rate. Having the rate passed in the constructor provides the chance to determine the quality and rendering speed at compile time. Note that to accomodate the Nyquist rule the sample rate must be at least twice the frequency of the highest note in the score. In practice using a sample rate of 8000 or above should cause few problems.
The createChain() method
The heart of the Instrument classes is the creatChain() method. All jMusic instrument classes must include this method. The createChain() method is where the synthesis process or signal processing process is defined.
public void createChain(){
Oscillator wt = new Oscillator(this, this.sampleRate,
Oscillator.getSineWave(this.sampleRate), 1);
Envelope env = new Envelope(wt, new EnvPoint[] {
new EnvPoint((float)0.0, (float)0.0),
new EnvPoint((float)1.0, (float)0.1),
new EnvPoint((float)0.0, (float)1.0)
});
SampleOut sout = new SampleOut( env, "jmusic.tmp");
}
The createChain() method in the SimpleSineInst class (shown above) has a chain of three audio objects, Wavetable, Envelope, and SampleOut.
An audio object's position in the chain is indicated by the first constructor argument; the preceeding audio object is passed as the firt argument. For example, the SampleOut instance "sout" is passed the "env" object - this indicates that sout comes after env in the chain. The first object in the chain ( "wt" in this example) takes the instrument instance, "this", as an argument, as in; WaveTable wt = new WaveTable(this, . . .
Audio objects
There are many audio objects provided with jMusic and you are encouraged to write your own. Some of the audio objects are:
Oscillator, Envelope, Volume, Panner, SampleIn, SampleOut, Filter, Add, Splitter, and WhiteNoise.
For a complete list see the jMusic documentation, or look in the jm/audio/synth directory of the jMusic source tree.
All audio objects take one or more arguments, the first of which is by convention the preceeding audio object in the chain, the other arguments vary depending upon the funtion of theaudio object class - check the documentation for details.
Constructing new Audio Objects is beyond the scope of this tutorial and is covered elsewhere.
Sample stream
The hierarchial arrangment of objects is necessary because the Instrument is essentailly a class to generate and/or processes samples. The samples are passed from one object to another along the chain creating a sample stream, as indicated in the diagram below.
To visualise this imagine that the SimpleSineInst generates a raw sione wave in the wt object, then each sample amplitude is adjusted according the the envelope in the env object, and lastly each sample is written to disk by the sout object. This process is shown diagramatically below.
One important, but counter intuitive, aspect of the sample progression through the chain is that the last object in the chain is the first object to be executed. In this case the sout (SampleOut) object runs first, it requests samples from the env (Envelope) object, which in turn requests samples from the wt (WaveTable) object which generates them using a call to the Oscilliator class. In practice then, samples are pulled through the chain from the bottom as required to render each note in the jMusic score. In this way only the number of samples required by each note (determined by it's length and the sample rate) are computed.
in theory, one sample at a time could be passed down the chain but this would be quite inefficient. in practice an array of samples is passed down the chain. By default, an array of 4096 samples are processed by each object at a time and passed on through the chain.
Gong Example
The code below uses multiple notes played by sine waves to create a gong-like timbre.
import jm.music.data.*; |
The SimpleSineInst is used by this example. After creating the score to play the gong sound four times and displaying it using the View.show() method, the Instrument is decalred.
Instrument sine = new SimpleSineInst(sampleRate);
This is the only instrument required for this piece, so it is passed as an argument to the Write.au() method to render the score as a file named Gongs.au using the line;
Write.au(score, "Gongs.au", sine);
You might like to experiment with editing the instrument by changing the envelope point values to change the charracteristic of the gong sound.
|
|
|
© 2001 Andrew Brown |
|
|