Author: Max Pellizzaro
Date: March 15th 2008
version: 3.0.2

Sound in 3D World

Objective of the tutorial

With this tutorial you will learn how to use the new class Sound3D that will allow you to transform audio volume and pan relative to the Camera3D.
The closer your object will be to the camera, the lauder will be; the farther will be the quieter will be.

How to

Set up

The Document class must be changed to Example024.as The name of the class in the .as file and the name of the constructor now is: Example024. You will need some extra classes for this tutorial: two classes to link two images placed in the library of the .fla GUI, and one class to link a Sound element that is placed in the library as well. We have already seen how to do so, if you don't remember have a look here.
All the required files can be found here:

example024.rar

The AS Code

package 
{
	import flash.utils.*;
	import flash.display.*;
	import flash.events.*;
	import flash.ui.*;
	import flash.media.Sound;
	// Sandy 
	import sandy.core.Scene3D;
	import sandy.core.data.*;
	import sandy.core.scenegraph.*;
	import sandy.materials.*;
	import sandy.materials.attributes.*;
	import sandy.primitive.*;


	public class Example024 extends Sprite
	{
		
		private var sphereS:Sphere;
		private var img:MyBall=new MyBall();
        private var bitmap:Bitmap=new Bitmap(img);
		private var img2:MyCourt=new MyCourt();
        private var bitmap2:Bitmap=new Bitmap(img2);
		private var sound:Sound= new BallBounce();

		private var plane:Plane3D;
		private var tg1:TransformGroup;
		private var tg2:TransformGroup;
		private var bounce:Sound3D;
		
		private var scene:Scene3D;
		private var camera:Camera3D;

		private var moveDirection:Number = -1;
		private var speed:Number = 3;
		
		public function Example024()
		{
			// Sandy code
			camera = new Camera3D( 600, 300 );
			camera.z = -400;
			camera.y = 100;
			camera.lookAt (0,0,0);
			var root:Group = createScene();
			scene = new Scene3D( "scene", this, camera, root );
		
			stage.addEventListener (KeyboardEvent.KEY_DOWN, keyPressedHandler);
			stage.addEventListener (MouseEvent.MOUSE_MOVE, mouseMovedHandler);
			addEventListener ( Event.ENTER_FRAME, enterFrameHandler );
		}
		
		private function createScene ():Group
		{
			// Create the root Group
			var g:Group = new Group();
			tg1 = new TransformGroup('myGroup');
			tg2 = new TransformGroup('myGroup');

			// Create a cube so we have something to show
			sphereS = new Sphere( "theSphere", 20,15,15);
			
			var material:BitmapMaterial = new BitmapMaterial( bitmap.bitmapData );
            var app:Appearance = new Appearance( material );			
			
			sphereS.appearance = app;
			sphereS.useSingleContainer = false;

			sphereS.x = 100;
			sphereS.y = 0;
			sphereS.z = 0;

			// bottom plane
			plane = new Plane3D( "thePlane", 300, 500, 15, 15, Plane3D.ZX_ALIGNED );
			plane.y = -100;
			plane.rotateY = 0;
			
			var material2:BitmapMaterial = new BitmapMaterial( bitmap2.bitmapData );
            var app2:Appearance = new Appearance( material2 );
			
			plane.useSingleContainer = false;
			plane.enableBackFaceCulling = false;
			plane.appearance = app2;
			
			bounce = new Sound3D("bounce", sound, 1,3,1500);
			bounce.type = Sound3D.SPEECH;
			bounce.loops = 0;
			
			tg1.addChild(sphereS);
			tg1.addChild(bounce);
			tg2.addChild(plane);
			tg2.addChild(tg1);
            
			g.addChild (tg2);
			
			return g;
		}
		
		private function mouseMovedHandler (event:MouseEvent):void
		{
			tg2.rotateY=(event.stageX-600/2)/5;
		}
		
		private function keyPressedHandler (event:KeyboardEvent):void
		{
			switch (event.keyCode)
			{
				case Keyboard.RIGHT :
					tg1.moveLateraly(+5);
					break;
				case Keyboard.LEFT :
					tg1.moveLateraly(-5);
					break;
				case Keyboard.UP :
					tg1.moveHorizontally(+5);
					break;
				case Keyboard.DOWN :
					tg1.moveHorizontally(-5);
					break;
					
			}
		}
		
		private function enterFrameHandler ( event : Event ):void
		{
			scene.render ();
			sphereS.roll -= 4;
			sphereS.y += speed * moveDirection;
			if (sphereS.y < -85){
			 bounce.play();
			 moveDirection = 1;
			}
			else if (sphereS.y > 100)
			 moveDirection = -1;
		}
	}
}

Examining the code

In this tutorial we are going to have a bouncing ball on the ground. So the first things we need to have are the two bitmap objects (one for the ground and one for the basketball), and one Sound object for the bouncing sound.

private var img:MyBall=new MyBall();
private var bitmap:Bitmap=new Bitmap(img);
private var img2:MyCourt=new MyCourt();
private var bitmap2:Bitmap=new Bitmap(img2);
private var sound:Sound= new BallBounce();

Now that we have our elements, let’s start to create our scene. The scene is compose by these three elements: the ball (a Sphere), the ground (a Plane) and the sound, an instance of the new class called: Sound3D. Let’s have a deeper look on how we create the sound element:

bounce = new Sound3D("bounce", sound, 1,3,1500);
bounce.type = Sound3D.SPEECH;
bounce.loops = 0;

The first line instantiate the sound element. The constructor can take 5 parameters:

Now we need to add the Sound3D to the bouncing ball. To do so we will create a TransformGroup that will join together the ball (sphere) and the sound just created.

tg1 = new TransformGroup('myGroup');
...
...
tg1.addChild(sphereS);
tg1.addChild(bounce);

We have linked the ball object and the sound object because we want to move them together in our scene, so when we will move the ball, the sound will go together with the ball.

case Keyboard.RIGHT :
     tg1.moveLateraly(+5);
     break;
case Keyboard.LEFT :
     tg1.moveLateraly(-5);
     break;
case Keyboard.UP :
    tg1.moveHorizontally(+5);
    break;
case Keyboard.DOWN :
    tg1.moveHorizontally(-5);
    break;

Now we need to make the sound “alive”, and this is pretty easy, as soon as the ball hits the gound we will call the play() method of the Sound3D object, that is bounce.play()

private function enterFrameHandler ( event : Event ):void
		{
			scene.render ();
			sphereS.roll -= 4;
			sphereS.y += speed * moveDirection;
			if (sphereS.y < -85){
			 bounce.play();
			 moveDirection = 1;
			}
			else if (sphereS.y > 100)
			 moveDirection = -1;
		}

And now let’s see the result !

The output

Move the ball with the arrow keys, push the ball far away, and the sound will gradually disappear.