audio MIDI Music Algorithms Interfaces Programming Acoustics Context
> Audio > Synthesis > Layering Sounds    
 
   

Layering Sounds

This tutorial will demonstrate how waveforms of different types can be added together at the instrument level.

Hear the result of this tutorial's music below.

BreathyFluteTest.au [731K]


View / Download source - Instrument source code

View / Download source - Composition source code

Let's have a closer look.

import jm.audio.io.*;
import jm.audio.synth.*;
import jm.music.data.Note;
import jm.audio.AudioObject;

/**
* A basic layered synthesis instrument
* @author Andrew R. Brown after Andrew Sorensen
*/

public final class BreathyFluteInst extends jm.audio.Instrument{

//----------------------------------------------
// Attributes
//----------------------------------------------

/** The points to use in the construction of Envelopes */

private EnvPoint[] pointArray = new EnvPoint[10];


/** The points to use in the construction of Envelopes */

private EnvPoint[] pointArray2 = new EnvPoint[10];


/** The number of channels */

private int channels;


/** the sample rate passed to the instrument */

private int sampleRate;


The import statements are giving us access to all the other classes that we need to construct this instrument.  

The attributes or "fields", that are declared here are used later on for various purposes.
 You can see what the purpose is quite easily, because they are well commented.



//----------------------------------------------
// Constructor
//----------------------------------------------
/**
* Basic default constructor to set an initial
* sampling rate.
* @param sampleRate
*/

public BreathyFluteInst(int sampleRate){
this(sampleRate, 1);
}


/**
* A constructor to set an initial
* sampling rate and number of channels.
* @param sampleRate
*/

public BreathyFluteInst(int sampleRate, int channels){
this.sampleRate = sampleRate;
this.channels = channels;

// tone env

EnvPoint[] tempArray = {
new EnvPoint((float)0.0, (float)0.0),
new EnvPoint((float)0.2, (float)1.0),
new EnvPoint((float)0.5, (float)0.7),
new EnvPoint((float)0.8, (float)0.5),
new EnvPoint((float)1.0, (float)0.0)
};
pointArray = tempArray;

// Noise env

EnvPoint[] tempArray2 = {
new EnvPoint((float)0.0, (float)0.0),
new EnvPoint((float)0.05, (float)0.7),
new EnvPoint((float)0.2, (float)0.1),
new EnvPoint((float)1.0, (float)0.0)
};
pointArray2 = tempArray2;
}

This is an example of "overloaded" constructors.  It means that you can create a BreathyFluteInst object in two different ways:

        new BreathyFluteInst( **desired sample rate**)

or

        new BreathyFluteInst(**desired sample rate**, **desired channel**)



In the first method, there is no way of specifying the number of channels, so a default value of 1 is used.
 Andrew has done a little trick that enables code to be reused rather than copied.
 You'll note that the first constructor method is very short, in fact it only has one line.
 What it does is call the second constructor method, passing the information that was given to it as well as extra default information (channel = 1).

Remember that from outside, the command new BreathyFluteInst(,) is effectively a call to the constructor method of class.  
So if you are inside the class, it makes sense that the command this(,) calls the same constructor. (the only difference is that this(,)
doesn't return a new instance of the class)



//----------------------------------------------
// Methods
//----------------------------------------------

/**
* Initialisation method used to build the objects that
* this instrument will use
*/

public void createChain(){

// fundamental tone

Oscillator wt = new Oscillator(this, Oscillator.SINE_WAVE,
this.sampleRate, channels);

Volume vol = new Volume(wt, (float)1.0);


// overtone

Oscillator wt2 = new Oscillator(this, Oscillator.SINE_WAVE,
this.sampleRate, channels);
wt2.setFrqRatio((float)2.001);
Volume vol2 = new Volume(wt2, (float)0.5);


// overtone

Oscillator wt3 = new Oscillator(this, Oscillator.SINE_WAVE,
this.sampleRate, channels);
wt3.setFrqRatio((float)4.0);
Volume vol3 = new Volume(wt3, (float)0.1);


// breath

Noise noise = new Noise(this, Noise.WHITE_NOISE, this.sampleRate );
Volume vol4 = new Volume(noise, (float)0.2);


// all together now

AudioObject[] parts = {vol, vol2, vol3, vol4};
Add add = new Add(parts);
Envelope env = new Envelope(add, pointArray);
StereoPan span = new StereoPan(env);
SampleOut sout = new SampleOut(span);
}
}

You should know by now that createChain is the fun method.  
This part will explain how to have multilayered fun!

This time, a slightly different approach is being used.
 There are four different sounds, and rather than linking them sequentially they are run in parallel.

Each of them (the sounds) take "this" as their first argument.
 As you know, the first argument into an AudioObject has to be another AudioObject that feeds into it.
 You also know that feeding the AudioObject "this", signifies it to be at the start of the audio chain.  

Looking at the above code, you might be able to see that all of the sounds are at the start of the audio chain.
 This means that they will be played in parallel.  

To mix them together into the same audio line, the special AudioObject Add is used.
 Add takes an array of AudioObjects.
 As you can see it takes all of the AudioObjects that were running in parallel above.  

Some stereo panning is done, and then signal is sent out to sample output.

import jm.JMC;
import jm.music.data.*;
import jm.audio.*;
import jm.util.*;

/**
* @author Andrew R. Brown
*/

public final class BreathyFluteTest implements JMC{
public static void main(String[] args){
Score score = new Score("JMDemo - Audio test");
Part part = new Part("wave", 0);
Phrase phr = new Phrase(0.0);
int sampleRate = 44100;
Instrument inst = new BreathyFluteInst(sampleRate);

Note rootNote = new Note(60, 0.25,
(int)(Math.random() * 70 + 40));
for (int i = 1; i < 24; i++) {
Note note = new Note(i + 60, 0.25,
(int)(Math.random() * 70 + 40));
if (note.isScale(MINOR_SCALE)) {
phr.addNote(rootNote);
phr.addNote(note);
}
}
Note lastNote = new Note(84, 2.0,
(int)(Math.random() * 70 + 40));
phr.addNote(lastNote);

part.addPhrase(phr);
score.addPart(part);
Write.au(score, "BreathyFluteTest.au", inst);
}
}


This class uses the instrument that we dissected above to play a simple stochastic melody.  For more info on algorithmic composition click here.
 

jMusic Australia Council Queensland University of Technology Sitemap Contact Home Home http://www.qut.com http://explodingart.com/jmusic http://www.ozco.gov.au

Digital Instrument making Home