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;   public final class BreathyFluteInst extends jm.audio.Instrument{	 	private EnvPoint[] pointArray = new EnvPoint[10];	 	private EnvPoint[] pointArray2 = new EnvPoint[10];	 	private int channels;	 	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.
          
          
          
            
               
                 	 	public BreathyFluteInst(int sampleRate){ 	    this(sampleRate, 1); 	} 	 	public BreathyFluteInst(int sampleRate, int channels){ 		this.sampleRate = sampleRate; 	       	this.channels = channels;	        		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;	        		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)
          
          
          
            
               
                 	 	public void createChain(){	        		Oscillator wt = new Oscillator(this, Oscillator.SINE_WAVE,  					       this.sampleRate, channels);
  		Volume vol = new Volume(wt, (float)1.0);	        		Oscillator wt2 = new Oscillator(this, Oscillator.SINE_WAVE,  						this.sampleRate, channels); 		wt2.setFrqRatio((float)2.001); 		Volume vol2 = new Volume(wt2, (float)0.5);	        		Oscillator wt3 = new Oscillator(this, Oscillator.SINE_WAVE,  						this.sampleRate, channels); 		wt3.setFrqRatio((float)4.0); 		Volume vol3 = new Volume(wt3, (float)0.1);	        		Noise noise = new Noise(this, Noise.WHITE_NOISE, this.sampleRate ); 		Volume vol4 = new Volume(noise, (float)0.2);	        		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.*;   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.