Hostingheaderbarlogoj
Join InMotion Hosting for $3.49/mo & get a year on Tuts+ FREE (worth $180). Start today.
Advertisement

Manipulating Sounds in AS3 With the Standing Wave 3 Library

by
Gift

Want a free year on Tuts+ (worth $180)? Start an InMotion Hosting plan for $3.49/mo.

In this tutorial we will be exploring the AS3 sound manipulation library Standing Wave 3. We will create a sinewave piano, use a single MP3 to play many different pitches, make a chord and scale out of a single MP3, mix sounds together, and write the mixed sound to a ByteArray as .wav data.


Step 1: Explaining Standing Wave 3

The Standing Wave 3 library is a subset of the library that was used to create the very popular online music scoring application at noteflight.com. The library takes advantage of the dynamic sound generation that came along with Flash Player 10, and uses the newly created Alchemy extension to the Flash Player, which allows you to compile C++ code to run on the open source ActionScript Virtual Machine (AVM2).


Step 2: Create New Flash Project

Go to File > New and choose "ActionScript 3.0" with the following properties.

  • W:600
  • H:600
  • Background Color:#CCCCCC

Save this file as "StandingWave.fla"


Step 3: Create "Main.as"

Go to File > New and choose "Actionscript 3.0 Class".

Save this file as "Main.as" and set it as the Document Class.



Step 4: Adding the Library

You will need to grab a copy of the Standing Wave Library at github.

Once you have extracted the .zip file, you will need to copy the com folder (which is located in the standingwave > sw3 > src directory) into your project directory.

Next you have to add awave.swc, which is located in the standingwave > alchemy directory, to your project. Go to File > Actionscript Settings, then click on the "Library Path" tab to add the swc. If you're not sure how to do this see the tutorial on how to use an external library in your Flash projects.


Step 5: Coding the Constructor

Inside "Main.as" add the following code.

 
package{ 
	//Class extends MovieClip 
	import flash.display.MovieClip; 
	//Needed for our Buttons and Complete Events 
	import flash.events.MouseEvent; 
	import flash.events.Event;	 
    //Needed for standing Wave Library 
	import flash.media.Sound; 
	import com.noteflight.standingwave3.elements.*; 
	import com.noteflight.standingwave3.filters.*; 
	import com.noteflight.standingwave3.formats.*; 
	import com.noteflight.standingwave3.generators.*; 
	import com.noteflight.standingwave3.modulation.*; 
	import com.noteflight.standingwave3.output.*; 
	import com.noteflight.standingwave3.performance.*; 
	import com.noteflight.standingwave3.sources.*; 
	import com.noteflight.standingwave3.utils.*; 
	public class Main extends MovieClip{ 
			 
		public function Main(){ 
			 
	 
		} 
		 
		 
	}//Close the class 
}//Close the package

Here we import the classes we will need throughout the tutorial and set up our Main() constructor function.


Step 6: Note Frequencies

To be able to effectively take advantage of the Standing Wave library, we need a reference to note frequencies. You can find a nice chart on the www.phy.mtu.edu website.

Don't even worry about trying to memorize these, or having to constantly refer to this chart; we will create a nice "NoteUtils" class along the way that will reference all the notes shown in this chart.


Step 7: Audio Sources

Standing wave makes use of Audio Sources. These can be produced in a number of ways: by extracting sound from an sound file, using an existing array of values to construct a sound, or generating sound from mathematical functions at runtime.

A good video showing some basic sound manipulation using flashes dynamic sound generation is this one on Lee Brimelow's GotoAndLearn.com website.

Some of the Audio Sources available are:

  • SineSource - allows you to generate sine waves
  • LoopSource - allows you to loop another source.
  • SamplerSource - provides a convenient way to perform complex sample wavetable playback functionality. It has flexible start and loop points, and accepts pitch modulations.

All Audio Sources implement the IAudioSource Interface.


Step 8: Interface for Playing a Sinewave

Add two button components and a label to the stage. You can get to the component panel by going to Window > Components (alternatively just press CTRL+F7).

Give the first button the instance name "sinewave_play_btn" and change the label property to "Play".


Give the other button the instance name "sinewave_stop_btn" and change the label property to "Stop".

Now change the label's text property to "Play/Stop Sinewave".


Position the components as below.

  • play_sinewave_btn: X:20.00 Y:53.00
  • stop_sinewave_btn: X:136.00 Y:53.00
  • Play/Stop sinwave label X:80.00 Y:7.00

Step 9: Playing a Sinewave

Just under the line public class Main extends MovieClip add the following.

 
public class Main extends MovieClip{ 
var sinewavePlayer:AudioPlayer = new AudioPlayer(); 
var sinewave:IAudioSource;

The AudioPlayer class is what we use to play the sounds in Standing Wave, and the sinewave variable is an IAudioSource that we will feed into the player.

Add the following to the Main() constructor.

 
public function Main(){ 
	addButtonListeners(); 
}

This function will be used to add listeners to our buttons, which will allow us to play and stop the sinewave when they are clicked.

Add the following underneath the Main() function.

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
}

Here we add some Listeners that will call the "playSinewave" and "stopSinewave" functions when a user clicks on the buttons.

Add the following underneath the addButtonListeners function.

 
private function playSinewave(e:Event):void{ 
	e.target.enabled = false; 
	sinewave = new SineSource(new AudioDescriptor(),5,440,0.2); 
	sinewavePlayer.addEventListener(Event.SOUND_COMPLETE,onPlaybackComplete); 
	player.play(sinewave); 
}

Here we disable the button, create a new SineSource (see below), add an Event.SOUND_COMPLETE listener that will be called when the sound finishes playing, and tell the player to play the sinewave.

The SineSource takes as parameters an AudioDescriptor, which we will explain shortly, the amount of seconds you want the sinewave to play, the frequency of the sinewave, and the amplitude of the sinewave. You will want to keep the amplitude at 0.5 or below, putting it any higher can cause the audio to clip.

Add the following below the playSinewave() function you added above.

 
private function onPlaybackComplete(e:Event):void{ 
   sinewave_play_btn.enabled = true; 
}

This function is called when the sound finishes playing and it simply enables the sinewave_play_btn.

Add the following below the doComplete() function you added above.

 
private function stopSinewave(e:Event):void{ 
	player.stop(sinewave); 
	sinewave = null; 
	 
}

Here we stop the sinewave, set it to null, and enable the sinewave_play_btn.

The AudioDescriptor describes the characteristics of an audio stream in terms of number of channels and sample rate. Every IAudioSource instance is associated with an AudioDescriptor that indicates what kind of audio data is available from it. Although we have not passed any parameters to it, you can pass two: the Rate, and the number of channels. Examine the AS Docs to learn more about it.

You can now test the movie.


Step 9: Explanation of the "NoteUtils" Class

We are going to create a "NoteUtils" class, that will help us along. Having to constantly reference the chart I linked to above is no fun, and wastes time. This class will make use of public static const variables, so we will not even need to instantiate the class to use them.

We will create the Notes shown in the aforementioned chart from C0 to D#8. Since we cannot use the "#"(sharp) sign in our variable names, we will use an "S" in its place - C#3 will be CS3, F#4 will be FS4, and so on.

We could take our time and type all these variables in, but I have a better solution. By following a certain formula and using a for loop, we can create all these notes in one shot and just copy and paste them into the class.

At the bottom of the chart I mentioned above you will see a link to the equations used to create the chart. By studying the equation we can come up with a formula to use in our for loop. We will create the class in the next step.


Step 10: Creating the "NoteUtils" Class

Go to File > New and choose "Actionscript File". Save this as "NoteUtils.as".

Add the following to the "NoteUtils.as".

 
package{ 
    import flash.display.Sprite; 
	public class NoteUtils extends Sprite{ 
	    public function NoteUtils(){ 
			 
		} 
	} 
}

Here we just set up our class to extend Sprite, and code the constructor function.

We are going to use the timeline in our "StandingWave.fla" to generate code that defines the notes for this class, then copy this new code into our AS file, and once we are finished we will delete the code from the timeline.

So.. select the first frame in the timeline.


Then go to Window > Actions or just press F9. Next, enter the following code.

 
var NoteArray:Array = ["C","CS","D","DS","E","F","FS","G","GS","A","AS","B"]; 
var sub:int = -1; 
for (var i:int = 0; i < 100; ++i){ 
var Notes = (16.35 * Math.pow(1.05946309,i)).toFixed(2); 
if(i%12 == 0){ 
   sub++ 
} 
trace ("public static const "+NoteArray[i%12]+sub+":Number ="+Notes);

We declare a NoteArray to hold all 12 notes that are available (remember the "S" stands for Sharp). We then create a sub variable, and set it to -1. The sub will be appended to the notes. We then code our for loop, using the formula from the link above.

Because we are starting from "C0" we use 16.35 as the value of f0 from the formula. We also used AS3's toFixed(2) function to force each Note to a precision of 2 decimal places.

Next we check whether i%12 == 0 - that is, whether it can be divided evenly by 12, the number of semitones in an octave - and if can we increment sub by 1. (Hopefully you can see why we started with -1 as the initial value of sub now).

Finally, we use trace() to trace out each variable. The trace() may look a little cryptic so let me break it down.

First we just trace "public static const" since we want our variables static and unchangeable. Next we add onto this NoteArray[i%12]+sub - the "i%12" forces the index to always be one of the indices of the NoteArray; for example if i were 16 then it would resolve to NoteArray[4]. Finally we tack on ":Number =" and the actual value of Notes.

Go ahead and test the movie; you will see all the notes nicely formatted in the output window as so:

 
	public static const C0:Number =16.35 
	public static const CS0:Number =17.32 
	public static const D0:Number =18.35 
	public static const DS0:Number =19.44 
	.....

You may notice that they are not dead on according to the chart, but I do not think being off by a small amount will matter much. You can always copy the values manually from the chart if you wish.

Copy the lines from the output window and paste them into the "NoteUtils.as" you created in the step above just below the line public class NoteUtils extends Sprite.

 
public class NoteUtils extends Sprite{ 
	public static const C0:Number =16.35 
	public static const CS0:Number =17.32 
	public static const D0:Number =18.35 
	public static const DS0:Number =19.44 
	.....

Now make sure you delete the code off the main timeline in the "StandingWave.fla".


Step 11: Explanation of the Sinewave Piano

We will be creating a sinewave piano whose notes will span from "C4" to "C5". We will use our newly created "NoteUtils" class to make a reference to the notes on the piano.

Here is a preview:


Step 12: Creating the Piano Keys

Select the rectangle tool and make sure the following properties are set.

  • Stroke: Black
  • Fill: White

Now drag a rectangle out on the stage, then use the selection tool to select both the fill and stroke.

Next set the following properties on the rectangle.

  • W: 26.00
  • H: 106.00

Now making sure both the fill and stroke are still selected, right-click on the rectangle and choose "Convert to Symbol". Give it the name "WhiteKey" and make sure "Export for ActionScript" is checked of and the class name is "WhiteKey".


Next select the rectangle tool and make sure the fill is set to black and there is no stroke color selected.


Now drag a rectangle out on the stage, and use the selection tool to select it, then give it the following properties.

  • W: 18.00
  • H: 65.00

Right click on the rectangle and choose "Convert to Symbol", give it the name "BlackKey" and make sure "Export for Actionscript" is checked and that the class name is "BlackKey".


Step 13: Creating the Piano Key Classes

Go to File > New and choose "Actionscript File", save this as "WhiteKey.as" and then enter the following code.

 
package  { 
    import flash.display.Sprite; 
	public class WhiteKey extends Sprite{ 
		 
		private var frequency:Number=0; 
 
		public function WhiteKey() { 
			 
		} 
		 
		public function setFrequency(freq:Number):void{ 
			this.frequency = freq; 
		} 
		 
		public function getFrequency():Number{ 
			return this.frequency; 
		} 
		 
	} 
	 
}

Here we open our package and import the Sprite class.The frequency variable is used as the frequency of the Piano Key. If the piano key is "A4" then this would equal 440. We then have have getters and setters for this variable.

Now go to File > New and choose "Actionscript File", and save this as "BlackKey.as" and enter the following code.

 
package  { 
	import flash.display.Sprite; 
	public class BlackKey extends Sprite{ 
		 
		private var frequency:Number; 
 
		public function BlackKey() { 
			 
		} 
		 
		public function setFrequency(freq:Number):void{ 
			this.frequency = freq; 
		} 
		 
		public function getFrequency():Number{ 
			return this.frequency; 
		} 
         
	} 
	 
}

This works the exact same way as the "WhiteKey" class.


Step 14: Coding the Sinewave Piano

Add the following variables, below the ones you already have.

 
var keyboardPlayer:AudioPlayer = new AudioPlayer(); 
var pianoSinewave:IAudioSource; 
var whiteKeyFrequencies:Array = [NoteUtils.C4,NoteUtils.D4,NoteUtils.E4,NoteUtils.F4,NoteUtils.G4,NoteUtils.A4,NoteUtils.B4,NoteUtils.C5]; 
var blackKeyFrequencies:Array = [NoteUtils.CS4,NoteUtils.DS4,NoteUtils.FS4,NoteUtils.GS4,NoteUtils.AS4]; 
var blackKeyPositions:Array = [39,69,127,157,187];

The keyboardPlayer will be the player for the keyboard. The pianoSinewave will be used to play the sinewave notes of the piano. The whiteKeyFrequencies and blackKeyFrequencies variables hold the corresponding frequecies of the piano key's by using our NoteUtils class. Finally, the blackKeyPositions are used to position the black keys of the piano.

Add the following inside the Main() constructor.

 
public function Main(){ 
	addButtonListeners(); 
	setupPianoKeyBoard(); 
 
}

Next add the following code just below the stopSinewave() function.

 
 
private function setupPianoKeyBoard():void{ 
	var xPos:Number = 20.00; 
	var yPos:Number = 120.00; 
	for(var i=0;i&lt8;i++){ 
		var whiteKey:WhiteKey = new WhiteKey(); 
		whiteKey.setFrequency(whiteKeyFrequencies[i]); 
		whiteKey.x = xPos; 
		whiteKey.y = yPos; 
		addChild(whiteKey); 
		whiteKey.addEventListener(MouseEvent.MOUSE_DOWN,playPianoNote); 
		whiteKey.addEventListener(MouseEvent.MOUSE_UP,stopPianoNote); 
		xPos +=29; 
	} 
	for(var j=0;j&ltblackKeyPositions.length;j++){ 
		var blackKey:BlackKey = new BlackKey(); 
		blackKey.setFrequency(blackKeyFrequencies[j]); 
		blackKey.x = blackKeyPositions[j]; 
		blackKey.y = yPos; 
		addChild(blackKey); 
		blackKey.addEventListener(MouseEvent.MOUSE_DOWN,playPianoNote); 
		blackKey.addEventListener(MouseEvent.MOUSE_UP,stopPianoNote); 
	} 
}

We first set up the xPos and yPos variables. These are used to position the piano keys on the stage. Next we run a for loop for the 8 White Keys, inside this loop we create a whiteKey, set it's frequency, position, and add it to the stage. Next we add two Event Listeners and increment the xPos variable so the key are staggered.

We then run a for loop for the Black Keys, inside this for loop we do much the same as the for loop for the WhiteKeys.


Step 15: Piano Key Listeners

Add the following below the setupPianoKeyBoard() function.

 
 
private function playPianoNote(e:Event):void{ 
	pianoSinewave = new SineSource(new AudioDescriptor(),10,e.target.getFrequency()); 
	player.play(pianoSinewave); 
} 
	 
private function stopPianoNote(e:Event):void{ 
	player.stop(); 
}

In the playPianoNote() function we create a new pianoSinewave, with a length of 10 seconds, and call the getFrequency() function on the key that was pressed.

The stopPianoNote() function simply tells the player to stop playing.


Step 16: Using SoundSource

Having examined how to use the SineSource we will now focus our attention on using SoundSource. SoundSource uses embedded MP3s or MP3s that you can load in at runtime.

In the project files there is a file called "piano.mp3". Go to File > Import > Import To Library and choose this file.

Go to the Library, right click on the MP3 and choose "Properties". On the "Options" tab make sure the compression is set to "MP3", that "convert stereo to mono" is unchecked, that "Bit Rate" is set to "128kbps", and that "Quality" is set to "fast".


Next select the "ActionScript" tab, make sure "Export for Actionscript" is checked and that the "Class" is set to "Piano".


Go to the components panel and drag two labels, a slider, a checkbox, and a button onto the stage. Give them the following properties:

  • First Label: X:42.00, Y:244.0, W:186.0, H:22.0, Text = "Using IAudio Source to play a sound"
  • Second Label: X:50, Y:280, Text ="Gain"
  • Slider: X:113.0, Y:285.00, Instance Name = "gain_slider"
  • CheckBox: X:88, Y:306.00, Instance Name = "echo_checkbox"
  • Button: X:93.00, Y:343.00, Instance Name = "piano_snd_btn"

Step 17: Coding PlayPianoSound()

The PlayPianoSound will be used to play the piano sound, with the gain set by the slider and optionally applying an echo by selecting the "Echo ON/OFF" checkbox.

We need to a new variable, so add the following to the bottom of your variable declaration.

 
var pianoSoundPlayer:AudioPlayer = new AudioPlayer(); 
var pianoSound:Sound = new Piano();

Here the pianoSoundPlayer will be used to play the Piano Sound, and the pianoSound is a reference to the instance of piano.mp3 we create with a class of "Piano".

Add the following beneath the stopPianoNote() function.

 
private function playPianoSound(e:Event):void{ 
	gain_slider.enabled = false; 
	echo_checkbox.enabled = false; 
	e.target.enabled = false; 
	var pianoSource:IAudioSource = new SoundSource(pianoSound,new AudioDescriptor()); 
	 
	var pianoGain:IAudioSource = new GainFilter(pianoSource,gain_slider.value); 
	var pianoEcho:IAudioSource = new EchoFilter(pianoGain,100,1,0.5); 
	pianoSoundPlayer.addEventListener(Event.SOUND_COMPLETE,pianoSoundComplete); 
	if(echo_checkbox.selected){ 
		pianoSoundPlayer.play(pianoEcho); 
	}else{ 
		pianoSoundPlayer.play(pianoGain); 
	} 
}

We first disable the controls so the user cannot press them again until the sound is finished playing. Next create an new pianoSource as a SoundSource using the pianoSound we created above.

Standing Wave has quite a few Filters available; to use them you create them as an IAudioSource and pass in another IAudioSource to which it will be applied. This allows you to "stack" the filters on top of one another.

We first create a "Gain Filter", and pass the pianoSource to it, and use the gain_slider.value for the amount of gain. We then "stack" the pianoGain onto a new "Echo Filter", and add an Event.SOUND_COMPLETE to be called when the sound finishes playing.

The "Echo Filter" takes, as its parameters: an IAudioSource, the time of the echo, the wet, and decay settings.

Be sure to check out the other filters Standing Wave comes with. They are located in the com > noteflight > standingwave3 > filters directory.

We check if the echo_checkbox is selected and if it is we play the pianoEcho source, otherwise we just play the pianoGain source.

Add the following below the playPianoSound() we just created above.

 
private function pianoSoundComplete(e:Event):void{ 
    piano_snd_btn.enabled = true; 
    gain_slider.enabled = true; 
    echo_checkbox.enabled = true; 
}

Here we simply enable the controls.

Finally add the following inside the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
}

Try it out:

Step 18: Using the ResamplingFilter

Standing Wav has a ResamplingFilter that allows you to resample an audio source to a new frequency. In this step we will use the resample filter to resample an mp3 of a piano that is A4 (440hz).

In the download files there is a "piano2.mp3"; go to File > Import > Import to Library and import this MP3. Once you have it imported, go to the library and right click on the MP3. Go to the "ActionScript" tab and make sure "Export for ActionScript" is checked, then set the "Class" to "Piano2".

Now at the bottom of your variable declarations, add the following

 
var piano2Sound:Sound = new Piano2();

All we are doing here is instantiating an instance of the "Piano2" sound.

Next go to the Components panel, and drag a Button and ComboBox to the stage. (I am not including the Label here to save space.) Give the Button and ComboBox the following properties:

  • Button: X:150.00, Y:438.00, Label="Play", Instance Name ="resample_btn"
  • ComboBox: X:30, Y:438, Instance Name="resample_combo"

Now we'll populate the ComboBox. We'll populate it with the frequencies from C4 to B6, and use the MP3 we imported in the step above with the ResamplingFilter to play each of these frequencies.

Enter the following code beneath the pianoSoundComplete() function you added in the steps above.

 
function populateComboBox():void{ 
var NoteArray:Array = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"]; 
var FrequencyArray:Array = [NoteUtils.C4,NoteUtils.CS4,NoteUtils.D4,NoteUtils.DS4,NoteUtils.E4,NoteUtils.F4,NoteUtils.FS4,NoteUtils.G4,NoteUtils.GS4,NoteUtils.A4,NoteUtils.AS4,NoteUtils.B4, 
					NoteUtils.C5,NoteUtils.CS5,NoteUtils.D5,NoteUtils.DS5,NoteUtils.E5,NoteUtils.F5,NoteUtils.FS5,NoteUtils.G4,NoteUtils.GS5,NoteUtils.A5,NoteUtils.AS5,NoteUtils.B5, 
					NoteUtils.C6,NoteUtils.CS6,NoteUtils.D6,NoteUtils.DS6,NoteUtils.E6,NoteUtils.F6,NoteUtils.FS6,NoteUtils.G4,NoteUtils.GS6,NoteUtils.A6,NoteUtils.AS6,NoteUtils.B6]; 
var sub = 3; 
for(var i:int = 0;i<36;i++){ 
	if(i % 12 == 0){ 
		sub++; 
	} 
	resample_combo.addItem( 
		{ 
			label:NoteArray[i%12]+sub.toString(),  
			frequency:FrequencyArray[i] 
		} 
	); 
	} 
}

Here we use a similiar method we used to create the "NoteUtils" class, toward the beginning of this tutorial. The NoteArray holds the notes that will be shown in the labels of the ComboBox. and the FrequencyArray holds the frequencies of the notes.

In the for loop we check whether i%12 == 0; if so, we increment the sub variable. We then use addItem() to add the label and the frequency to each label in the ComboBox. The frequency part of addItem() acts like a dynamic variable for the ComboBox. When we choose, say, "E4" from the ComboBox then the frequency would be equal to NoteUtils.E4 from the FrequencyArray.


Step 19: Playing the Resampled MP3

Add the following beneath the populateComboBox() function you added in the step above.

 
function playResample(e:Event):void{ 
	var resampledSoundPlayer:AudioPlayer = new AudioPlayer(); 
	var resampleFactor:* = (resample_combo.selectedItem.frequency/440).toFixed(3); 
	var baseSound:IAudioSource = new SoundSource(piano2Sound,new AudioDescriptor()); 
	var resampledSound:IAudioSource = new ResamplingFilter(baseSound,resampleFactor); 
	resampledSoundPlayer.play(resampledSound); 
	trace(resampleFactor); 
}

We first set up an resampledAudioPlayer that will be used to play the sound. Next we set a resampleFactor variable that will be used as the "resampleFactor" within the ResamplingFilter. We type it as "*" which means any data type, because when we declare it it needs to use the toFixed() method which returns a String, but inside the ResamplingFilter it needs to be of type Number.

Next we set our baseSound to the piano2Sound we created in the step above and set the resampledSound as a new ResamplingFilter, passing in the baseSound and the resampleFactor.

The way we calculate the resample factor is to take the new frequency and divide it by the base frequency as you can see in the resampleFactor declaration above.

You can see a chart that shows the differences between the notes on the phy.mtu.edu website.

The last thing we need to do is add the EventListener to the button, so add the following code within the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
}

You can now test the movie and resample the MP3 to a wide variety of frequencies.


Step 20: Playing a Chord

One thing that has always been hard to accomplish in Flash, is to achieve perfect timing when playing multiple audio sources. If you wanted to play one sound after another, relying on SOUND_COMPLETE did not provide a consistent way to achieve this. Standing Wave has solved this problem with its Performance class. You can play multiple sounds at the exact same time and play sounds one after another with consistent timing.

Go to Window > Components and drag a button onto the stage. Then give it the following properties.

  • X: 97.00
  • Y: 532.00
  • Label:"Play"
  • Instance Name = "resampleChord_btn"

Now add the following code below the playResample() function you added in the steps above.

 
function playChord(e:Event):void{ 
	var baseSound:IAudioSource = new SoundSource(piano2Sound,new AudioDescriptor()); 
	var C5Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.C5/440); 
	var E5Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.E5/440); 
	var G5Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.G5/440); 
   
	var chordPerformance:ListPerformance = new ListPerformance(); 
	chordPerformance.addSourceAt(0, C5Note); 
	chordPerformance.addSourceAt(0, E5Note); 
	chordPerformance.addSourceAt(0, G5Note); 
   
	var chordSource:IAudioSource = new AudioPerformer(chordPerformance,new AudioDescriptor()); 
	var chordPlayer:AudioPlayer = new AudioPlayer(); 
	chordPlayer.play(chordSource); 
   
}

Here we set up our baseSound like in the previous example. Then we set up the C5Note, E5Note, and G5Note variables using the ResamplingFilter. The notes of a C Major Chord are "C,E,G". We use our NoteUtils class to make it dead simple.

Next we set up a ListPerformance variable named chordPerformance and use the addSourceAt() method to add the notes. The first parameter is the "position" you want the note added at. Since we want the sounds all playing at the same time we add all of them at position 0. Like arrays, the ListPerformance starts at 0.

Lastly we create an IAudioSource as an AudioPerformer (passing in the chordPerformance), create an AudioPlay, and finally play the chordSource.

All that is left to do is add the EventListener for the button. Add the following to the addButtonListners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
 }

Now go ahead and test the movie.


Step 21: Playing a Scale

Having looked at how to play a chord with the resample filter and Performance, we will now play a scale. The code is nearly identical; all we are doing differently is adding more notes and (when we use the addSourceAt() method) offsetting the sounds by 1 each time.

Go to Window > Components and drag a Button to the stage, then give it the following properties:

  • X:400
  • Y:48
  • Label:"Play"
  • Instance Name = "resampleScale_btn"

Add the following beneath the playChord() function you added in the step above:

 
function playScale(e:Event):void{ 
	var baseSound:IAudioSource = new SoundSource(piano2Sound,new AudioDescriptor()); 
	var C4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.C4/440); 
	var D4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.D4/440); 
	var E4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.E4/440); 
	var F4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.F4/440); 
	var G4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.G4/440); 
	var A4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.A4/440); 
	var B4Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.B4/440); 
	var C5Note:IAudioSource = new ResamplingFilter(baseSound,NoteUtils.C5/440); 
 
	var scalePerformance:ListPerformance = new ListPerformance(); 
	scalePerformance.addSourceAt(0, C4Note); 
	scalePerformance.addSourceAt(1, D4Note); 
	scalePerformance.addSourceAt(2, E4Note); 
	scalePerformance.addSourceAt(3, F4Note); 
	scalePerformance.addSourceAt(4, G4Note); 
	scalePerformance.addSourceAt(5, A4Note); 
	scalePerformance.addSourceAt(6, B4Note); 
	scalePerformance.addSourceAt(7, C5Note); 
 
	var scaleSource:IAudioSource = new AudioPerformer(scalePerformance,new AudioDescriptor()); 
	scalePlayer.addEventListener(Event.SOUND_COMPLETE,scaleComplete); 
	var scalePlayer:AudioPlayer = new AudioPlayer(); 
	scalePlayer.play(scaleSource); 
}

The notes of a C major scale are "C,D,E,F,G,A,B,C"; once again our NoteUtils class makes this a breeze.

We need to add the event listener for the button, so add the following to the addButtonListners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
	resampleScale_btn.addEventListener(MouseEvent.CLICK,playScale); 
}

We also added an SOUND_COMPLETE event to the player so the user cannot press the button until the player is finished playing

Add the following code below the playScale function you added above.

 
private function scaleComplete(e:Event):void{ 
	resampleScale_btn.enabled = true; 
 }

Here we simply enable the button. Try it out:


Step 21: Loop Source

Another type of Source is the Loop Source. This allows you to repeatedly loop a section of audio.

Drag two buttons to the stage and give them the following properties.

  • Button1: X:340, Y:107, Label:"Play", Instance Name = "loopPlay_btn"
  • Button2: X:460, Y:107, Label:"Stop", Instance Name = "loopStop_btn"

Add the following to the your variable declarations along with the other Audio Players:

 
var loopPlayer:AudioPlayer = new AudioPlayer();

Here we set up a new Audio Player. Nothing new here.

 
private function playLoopSource(e:Event):void{ 
	var length:Number = pianoSound.length * 44.1; 
	var mySample:Sample = new Sample( new AudioDescriptor(44100, 2), length); 
	mySample.extractSound(pianoSound, 0, length); 
	var myLoop:LoopSource = new LoopSource(new AudioDescriptor(44100, 2), mySample); 
	myLoop.firstFrame = 1; 
	myLoop.startFrame = 1; 
	myLoop.endFrame = myLoop.frameCount; 
	loopPlayer.play(myLoop); 
}

The first thing we do here is get the frame length of the audio. Since the sound was encoded at 44.1 khz we simply take the sounds length and multiply it by 44.1.

We then set up a new Sample - we have not discussed this yet, but will do so in the next step. The sample takes as parameters an AudioDescriptor and the number of frames for the sample; here we use length since we want our sample to hold the same amount of frames as the source sound.

We next use the sample's extractSound() method which extracts a Audio Source's sound into the sample. As parameters it takes: the source sound, the position within that sound to start extracting from, and the number of frames to extract. Since we want the whole sound we used 0 and length respectively for the latter two.

After that we set up a LoopSource and pass in mySample; we set the firstFrame to 1, the startFrame to 1, and the endFrame to the myLoop.frameCount which returns the amount of frames. So essentially what we are doing is looping through the whole sample.

Add the following beneath the playLoopSource function you added above.

 
private function stopLoopSource(e:Event):void{ 
    loopPlayer.stop(); 
}

Here we simply stop the loopPlayer, otherwise we would have a never ending sound.

Lastly we need to add the event listeners for our buttons. Add the following within the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
	resampleScale_btn.addEventListener(MouseEvent.CLICK,playScale); 
	loopPlay_btn.addEventListener(MouseEvent.CLICK,playLoopSource); 
	loopStop_btn.addEventListener(MouseEvent.CLICK,stopLoopSource); 
}

Go ahead and test the movie.


Step 22: Samples

We introduced Samples in the previous step. In Standing Wave, Samples are the heart of the library. If you are up on your math and DSP processing then samples are your playground.

The class offers many methods to slice, dice, and re-arrange the Samples. We will not be getting too deep into this but be sure to check the AS Docs and example source code to learn more about what you can do with samples.

In this step we will load an MP3 from the server and use extractSound() to extract the sound into the sample.

Drag a button onto the stage and give it the following properties:

  • X:402
  • Y:182
  • Label:"Load"
  • Instance Name = "loadPlay_btn"

Since we are loading a sound we need to import URLRequest, so enter the following at the bottom of your import statements:

 
import flash.net.URLRequest;

Next, add the following to the bottom of your variable declarations.

 
var pianoSound3:Sound;

Here we set up an Sound object to be used for the sound we will be loading in.

Next add the following code below the stopLoopSource() function you added in the step above.

 
private function loadSample(e:Event):void{ 
    pianoSound3 = new Sound(new URLRequest("piano.mp3")); 
    pianoSound3.addEventListener(Event.COMPLETE,soundLoaded); 
}

Here we load a sound in and add an Event.COMPLETE for when the sound has finished loading.

Add the following code below the function you just created:

 
private function soundLoaded(e:Event):void{ 
    loadPlay_btn.removeEventListener(MouseEvent.CLICK,loadSample); 
    loadPlay_btn.label = "Play"; 
    loadPlay_btn.addEventListener(MouseEvent.CLICK,playSample); 
}

This function is called when the sound has finished loading. We remove the event listener for the loadSample() function, change the button's label, and add an event listener to the button.

Add the following code below the function you just added:

 
private function playSample(e:Event):void{ 
	e.target.enabled = false; 
	var samplePlayer:AudioPlayer = new AudioPlayer(); 
	samplePlayer.addEventListener(Event.SOUND_COMPLETE,sampleComplete); 
	var length:Number = pianoSound3.length * 44.1; 
	var mySample = new Sample( new AudioDescriptor(44100, 2), length); 
	mySample.extractSound(pianoSound3, 0, length); 
	samplePlayer.play(mySample); 
}

Here we disable the button, create a new AudioPlayer, add an event listener to the AudioPlayer, and use very much the same method as the step above; the only key difference is that we are doing this on a sound we have loaded from the server. Lastly we play the sound.

Add the following below the function you created above:

 
private function sampleComplete(e:Event):void{ 
	loadPlay_btn.enabled = true; 
}

Here we simply enable the button.

Lastly, we need to add the Event Listener for the button, so add the following within the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
	resampleScale_btn.addEventListener(MouseEvent.CLICK,playScale); 
	loopPlay_btn.addEventListener(MouseEvent.CLICK,playLoopSource); 
	loopStop_btn.addEventListener(MouseEvent.CLICK,stopLoopSource); 
	loadPlay_btn.addEventListener(MouseEvent.CLICK,loadSample); 
}

You can now test the movie.


Step 22: Resample a Sample

One of the methods of the Sample Class allows you to resample a Sample. This method changes both the speed and pitch of the sample.

Add a Button to the stage and give it the following properties:

  • X: 402
  • Y: 274
  • Label: "Play"
  • Instance Name = "resampleSample_btn"

Next, add the following code beneath the sampleComplete() function you added in the step above.

 
private function resampleSample(e:Event):void{ 
	e.target.enabled = false; 
	var resamplePlayer:AudioPlayer = new AudioPlayer; 
	resamplePlayer.addEventListener(Event.SOUND_COMPLETE,resampleComplete); 
	var length:Number = pianoSound.length * 44.1; 
	var mySample = new Sample( new AudioDescriptor(44100, 2), length); 
	mySample.extractSound(pianoSound, 0, length); 
	var resampledSample:Sample = new Sample(new AudioDescriptor(44100,2),mySample.frameCount); 
	resampledSample.resampleIn(mySample,2.0); 
	resamplePlayer.play(resampledSample); 
}

First, we disable the button, create an AudioPlayer, add an Event Listener to the AudioPlayer and once again use the same method as the previous steps to get the sound into the Sample - you should be used to this by now.

The next thing we do is create an "empty" sample with the instance name resampledSample and set its length equal to mySample by using mySample.frameCount. We then call the resampleIn() method and pass the sample we want to resample and the speed. We used 2.0 as the speed in this example. Lastly we play the sample.

Add the following code below the function you created above:

 
private function resampleComplete(e:Event):void{ 
	  resampleSample_btn.enabled = true; 
}

Here we simply enable the button.

Last thing to do is add the event listener for the button. Add the following code within the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
	resampleScale_btn.addEventListener(MouseEvent.CLICK,playScale); 
	loopPlay_btn.addEventListener(MouseEvent.CLICK,playLoopSource); 
	loopStop_btn.addEventListener(MouseEvent.CLICK,stopLoopSource); 
	loadPlay_btn.addEventListener(MouseEvent.CLICK,loadSample); 
	resampleSample_btn.addEventListener(MouseEvent.CLICK,resampleSample); 
}

You can now test the movie.


Step 23: Mixing Samples

Another cool little method Sample offers is to mix one or more Samples together. We will be doing just that in this step.

Add a button to the stage and give it the following properties.

  • X: 402
  • Y: 340
  • Label: "Play"
  • Instance Name: "mixSamples_btn"

In the project files you will find a file named "drumbeat.mp3". Import this to the Library, then right-click it and choose "Properties"; on the "ActionScript" tab make sure "Export for ActionScript" is checked and that the "Class" is set to "Drumbeat".


At the bottom of your variable declarations add the following:

 
var drumBeat:Sound = new Drumbeat();

Here we are simply creating an instance of the DrumBeat.

Add the following code beneath the resampleComplete() function you added in the step above.

 
private function mixSamples(e:Event):void{ 
    e.target.enabled = false; 
    var mixSamplePlayer:AudioPlayer = new  AudioPlayer(); 
    mixSamplePlayer.addEventListener(Event.SOUND_COMPLETE,mixSamplesComplete); 
    var length:Number = pianoSound.length * 44.1; 
    var mixSample1 = new Sample( new AudioDescriptor(44100, 2), length); 
    mixSample1.extractSound(pianoSound, 0, length); 
    var length2:Number = drumBeat.length * 44.1; 
    var mixSample2 = new Sample(new AudioDescriptor(44100,2),length2) 
    mixSample2.extractSound(drumBeat,0,length); 
    mixSample1.mixIn(mixSample2,1.0); 
    mixSamplePlayer.play(mixSample1); 
}

Here we disable the button, create a new AudioPlayer, add an event listener to the AudioPlayer and use the same methods as before to create a sample and extract the sounds into the sample.

We then mix mixSample2 into mixSample1 by calling the first Sample's mixIn() method. The first parameter is the Sample you want to mix in and the second parameter is the gain; here we put it at full gain. Lastly we play mixSample1 which now has the mixSample2 mixed in with it.

Add the following code below the function you added above.

 
private function mixSamplesComplete(e:Event):void{ 
	mixSamples_btn.enabled = true; 
 }

Here we simply enable the button.

You can now test the movie.


Step 24: Bonus - Encoding a Sample

In this bonus step we will use part of some code from SiON ROKUON
on wonderfl.net
to encode the Samples data as a .wav file, and then use FileReference to download it.

A useful method of the Sample class is writeWavBytes() which writes the Samples data to a byte array as wav data. You can then encode that data.

Enter the following code beneath the mixSamplesComplete() method you added in the step above:

 
private function encodeSample(e:Event):void{ 
    var length:Number = pianoSound.length * 44.1; 
    var mixSample1 = new Sample( new AudioDescriptor(44100, 2), length); 
    mixSample1.extractSound(pianoSound, 0, length); 
    var _data:ByteArray = new ByteArray(); 
    mixSample1.writeWavBytes(_data); 
    var _wav:ByteArray = new ByteArray(); 
	_wav.endian = Endian.LITTLE_ENDIAN; 
	_wav.writeUTFBytes("RIFF"); 
	var len : int = _data.length; 
	_wav.writeInt(len + 36); 
	_wav.writeUTFBytes("WAVE"); 
	_wav.writeUTFBytes("fmt "); 
	_wav.writeInt(16); 
	_wav.writeShort(1); 
	_wav.writeShort(2); 
	_wav.writeInt(44100); 
	_wav.writeInt(176400); 
	_wav.writeShort(4); 
	_wav.writeShort(16); 
	_wav.writeUTFBytes("data"); 
	_wav.writeInt(len); 
	_data.position = 0; 
	_wav.writeBytes(_data);		   
	var fr : FileReference = new FileReference(); 
	fr.save( _wav, "mixed.wav"); 
   
}

We once again extract a sound into the sample, then we setup a variable _data as a ByteArray, go through the encoding process,and pass the final _wav ByteArray to the FileReference save method.

We will need to import the FileReference class, ByteArray, and Endian so enter the following to the bottom of your import statement.

 
import flash.utils.ByteArray; 
import flash.net.FileReference; 
import flash.utils.Endian;

Lastly we need to add a button and setup an EventListener for the button. Drag a button to the stage and give it the following properties:

  • X: 402
  • Y: 414
  • Label:"Encode"
  • Instance Name = "encode_btn"

Now add the following to the addButtonListeners() function:

 
private function addButtonListeners():void{ 
	sinewave_play_btn.addEventListener(MouseEvent.CLICK,playSinewave); 
	sinewave_stop_btn.addEventListener(MouseEvent.CLICK,stopSinewave); 
	piano_snd_btn.addEventListener(MouseEvent.CLICK,playPianoSound); 
	resample_btn.addEventListener(MouseEvent.CLICK,playResample); 
	resampleChord_btn.addEventListener(MouseEvent.CLICK,playChord); 
	resampleScale_btn.addEventListener(MouseEvent.CLICK,playScale); 
	loopPlay_btn.addEventListener(MouseEvent.CLICK,playLoopSource); 
	loopStop_btn.addEventListener(MouseEvent.CLICK,stopLoopSource); 
	loadPlay_btn.addEventListener(MouseEvent.CLICK,loadSample); 
	resampleSample_btn.addEventListener(MouseEvent.CLICK,resampleSample); 
	mixSamples_btn.addEventListener(MouseEvent.CLICK,mixSamples); 
	encode_btn.addEventListener(MouseEvent.CLICK,encodeSample); 
}

You can now test the movie.


Conclusion

This tutorial gave an introduction to the Standing Wave Library. We barely even touched the surface of what is possible, however, so if you're interested in learning more about its capabilites be sure to dig into the AS Docs and source code. Finally make sure you check out NoteFlight.com; Standing Wave 3 is based on a subset of the Noteflight Score Editor's audio engine.

Advertisement