Almer/Blank Labs

Hype/Papervision3D Demo

So after getting my hands on the new Hype framework, I wanted to put together a simple demo together that utilizes a combination of the SoundAnalyzer class and some 3D eye candy (via Papervision3D).

First off, I can say that I really like being able to play around with the sound spectrum using only a couple lines of code. I also used the Rythm class to run an enter frame method, which is very convenient in that I didn't need to manually set up an event and listener.

Lastly, I wanted to also utilize a tweening engine to smooth out the particle movement, but doing so cut the framerate by about 80% (a little better with TweenMax/TweenLite, but still not good enough). I also briefly played around with the Flint particle emitter, but I'm not familiar enough with Papervision/Flint to get it working how I wanted.

UPDATE - I decided to give a tween engine one more shot, and was able to use TweenMax while keeping the framerate at an acceptable level.

UPDATE 11/06/2009 - Updated code sample to reflect demo swf.

UPDATE 3/29/2010 - It's come to my attention that Wordpress tends to be a bit moody when it comes to code formatting in posts, especially in the case of vectors (which use the greater than/less than characters). That said, I've posted a couple pastebin examples:

Link --> Document class
Link --> ApplicationView class

Here's the demo in action-

..and here's the code-
Paper_Hype_Lab.as (this would be the document class in Flash):
package {
	import __AS3__.vec.Vector;

	import com.almerblanklabs.view.ApplicationView;
	import com.lifeztream.debug.FPS;

	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.ProgressEvent;
	import flash.external.ExternalInterface;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.Security;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;

	import flash.utils.setTimeout;

	import hype.framework.behavior.SimpleBehavior;
	import hype.framework.core.TimeType;
	import hype.framework.rhythm.SimpleRhythm;
	import hype.framework.sound.SoundAnalyzer;

	import org.papervision3d.objects.primitives.Sphere;

	[SWF(width='580', height='580', backgroundColor='#000000')]

	/**
	 *
	 * @author Nolan Butcher
	 *
	 * DISCLAIMER : This code sample is by no means an optimized example. It is a PROTOTYPE.
	 * 				Please keep this in mind when looking through this code.
	 *
	 */	

	public class Paper_Hype_Lab extends Sprite
	{
		private static const ARTIST : String = "Trioptic of the Sound Scientists";
		private static const AUDIO_PATH : String = 'http://flexgraphix.com/blog/wp-content/uploads/2009/11/';
		private static const AUDIO_FILES : Array = [
														{file:'evil_violin.mp3', title:'Evil Violin(Rich \'n Stingy)'},
														{file:'am_i_going_to_die.mp3', title:'Am I Going to Die'},
														{file:'going_pro.mp3', title:'Going Pro'},
														{file:'life_is_easily_taken.mp3', title:'Life is Easily Taken'},
														{file:'bubachu.mp3', title:'Bubachu'},
														{file:'midi_in-out.mp3', title:'Midi In/Out'},
														{file:'may_6.mp3', title:'May 6'},
														{file:'slow_descent.mp3', title:'Slow Descent'},
														{file:'nocens_sonitus.mp3', title:'Nocens Sonitus'},
														{file:'remember.mp3', title:'Remember'},
														{file:'soulful_east_coast.mp3', title:'Soulful East Coast'},
														{file:'sunrise_on_the_highway.mp3', title:'Sunrise on the Highway'},
														{file:'the_sad_truth_of_it_all.mp3', title:'The Sad Truth of it All'},
														{file:'blueberry_haze.mp3', title:'Blueberry Haze'}
													];

		private static const VOLUME : Number = 1;

		private var _view : ApplicationView;
		private var _sphere : Sphere;
		private var _music : Sound;
		private var _sndChannel : SoundChannel;
		private var _soundAnalyzer : SoundAnalyzer;
		private var _freq : Vector. = new Vector.;
		private var _rythm : SimpleRhythm;
		private var _fps : FPS;
		private var _audioIndex : int = 1;

		private var _button : Sprite;
		private var _hitArea : Sprite;

		private var _textField : TextField;
		private var _songText : TextField;

		private var _rotationX : Number = 0.3;
		private var _rotationY : Number = 0.3;
		private var _cameraPitch : Number = 90;
		private var _cameraYaw : Number = 270;

		private var _xDist : Number;
		private var _yDist : Number;

		private var _mouseDown : Boolean = false;

		public function Paper_Hype_Lab()
		{
			Security.allowDomain('*');

			stage.frameRate = 60;

			_sphere = new Sphere(null, 300, 24, 14);

			_view = new ApplicationView();
			_view.object3D = _sphere;
			_view.startRendering();

			addChild(_view);

			_initRythm();
			_initTextField();
			_loadSound();
			_initUI();

			// Get the FPS widget @ www.lifeztream.com
			_fps = new FPS();
			addChild(_fps);

		}

		private function _handleMouseEvents(evt:MouseEvent):void
		{
			switch(String(evt.type))
			{
				case MouseEvent.MOUSE_DOWN:
					_mouseDown = true;
				break;
				case MouseEvent.MOUSE_UP:
					_mouseDown = false;
				break;
			}
		}

		private function _loadSound():void
		{
			var context : SoundLoaderContext = new SoundLoaderContext(1000, true);
			var req : URLRequest = new URLRequest();
				req.url = AUDIO_PATH + AUDIO_FILES[_audioIndex].file;

			if (_rythm && _rythm.isRunning) _pauseRythm();
			if (_sndChannel)
			{
				_sndChannel.stop();
				try {
					_music.close();
				} catch(e:Error) {
					trace ('cannot close sound stream');
				}
			}
			_music = new Sound(req, context);
			_music.addEventListener(IOErrorEvent.IO_ERROR, _handleIOErrorEvents, false, 0, true);

			_sndChannel = _music.play(0, 1, new SoundTransform(VOLUME));
			_sndChannel.addEventListener(Event.SOUND_COMPLETE, _onSoundDone, false, 0, true);

			_soundAnalyzer = new SoundAnalyzer();
			_soundAnalyzer.start();

			_updateSongText();
			if (_rythm) _initRythm();

		}

		private function _onSoundDone(evt:Event):void
		{
			_button.mouseEnabled = false;
			setTimeout(_nextSong, 1000);
		}

		private function _nextSong():void
		{
			_button.mouseEnabled = true;
			_button.dispatchEvent(new MouseEvent(MouseEvent.CLICK));
		}

		private function _handleIOErrorEvents(evt:IOErrorEvent):void
		{
			if (ExternalInterface.available)
			{
				ExternalInterface.call("window.alert('" + AUDIO_FILES[_audioIndex].file + " cannot be loaded')");
			}
		}

		private function _initUI():void
		{
			_hitArea = new Sprite();
			_hitArea.graphics.beginFill(0x000,0);
			_hitArea.graphics.drawRect(0,0,580,580);
			_hitArea.graphics.endFill();
			_hitArea.addEventListener(MouseEvent.MOUSE_DOWN, _handleMouseEvents, false, 0, true);
			_hitArea.addEventListener(MouseEvent.MOUSE_UP, _handleMouseEvents, false, 0, true);
			addChild(_hitArea);

			_button = new Sprite();
			_button.graphics.beginFill(0x666666);
			_button.graphics.drawRect(0,0,100,30);
			_button.graphics.endFill();
			_button.x = 476;
			_button.y = 4;
			_button.buttonMode = true;
			_button.mouseChildren = false;
			_button.useHandCursor = true;
			_button.addEventListener(MouseEvent.CLICK, _handleButtonEvents, false, 0, true);

			var buttonFormat : TextFormat = new TextFormat(null, 14, 0xFFFFFF, true);
			var buttonText : TextField = new TextField();

				buttonText.defaultTextFormat = buttonFormat;
				buttonText.text = "NEXT RIFF";
				buttonText.height = 18;
				buttonText.y = (_button.height - buttonText.textHeight) * .5;
				buttonText.x = (_button.width - buttonText.textWidth) * .5;

			addChild(_button);

			_button.addChild(buttonText);
		}

		private function _pauseRythm():void
		{
			_rythm.stop();
		}

		private function _resumeRythm():void
		{
			_rythm.start();
		}

		private function _initRythm():void
		{
			_rythm = new SimpleRhythm(_trackRythm);
			_rythm.start(TimeType.ENTER_FRAME);
		}

		private function _initTextField():void
		{
			var textFormat : TextFormat = new TextFormat();
				textFormat.color = 0x666666;
				textFormat.align = TextFormatAlign.CENTER;
				textFormat.size = 14;

			_songText = new TextField();
			_songText.defaultTextFormat = textFormat;
			_songText.width = 1;
			_songText.text = "-- :: --";
			_songText.autoSize = TextFieldAutoSize.CENTER;
			_songText.y = (stage.stageHeight - _songText.textHeight) - 4;
			_songText.x = (stage.stageWidth - _songText.width) * 0.5;
			addChild(_songText);

			var artistText : TextField = new TextField();
				artistText.defaultTextFormat = textFormat;
				artistText.width = 1;
				artistText.text = "-- Artist :: " + ARTIST + " --";
				artistText.x = (stage.stageWidth - artistText.width) * 0.5;
				artistText.y = (_songText.y - artistText.textHeight) - 4;
				artistText.autoSize = TextFieldAutoSize.CENTER;

			addChild(artistText);

			_textField = new TextField();
			_textField.width = 1;
			_textField.defaultTextFormat = textFormat;
			_textField.text = "-- Click and drag to rotate sphere --";
			_textField.x = (stage.stageWidth - _textField.width) * 0.5;
			_textField.y = artistText.y - _textField.textHeight - 5;
			_textField.autoSize = TextFieldAutoSize.CENTER;

			addChild(_textField);

		}

		private function _updateSongText():void
		{
			_songText.text = "-- Song :: " + AUDIO_FILES[_audioIndex].title + " --";
			_songText.x = (stage.stageWidth - _songText.width) * 0.5;
		}

		private function _handleButtonEvents(evt:MouseEvent):void
		{
			_sndChannel.removeEventListener(Event.SOUND_COMPLETE, _onSoundDone);
			if (_audioIndex < AUDIO_FILES.length-2)
			{
				_audioIndex++;
			} else {
				_audioIndex = 0;
			}

			_loadSound();
		}

		private function _trackRythm(rythm : SimpleRhythm):void
		{
			for (var i:int = 1;i < _sphere.geometry.vertices.length + 1;++i)
			{
				_freq[i-1] = _soundAnalyzer.getFrequencyIndex(i, 400, 100);
			}

			_view.moveParticles(_freq);

			// Rotates camera around sphere depending on mouse position
			if (_mouseDown)
			{
				_xDist = mouseX - stage.stageWidth * 0.5;
				_yDist = mouseY - stage.stageHeight * 0.5;

				_cameraPitch = _yDist * _rotationX + 90;
				_cameraYaw = _xDist * _rotationY + 270;

				_view.camera.orbit(_cameraPitch, _cameraYaw);
			}

		}
	}
}
ApplicationView.as (extends PV3D's BasicView class):
package com.almerblanklabs.view
{
	import __AS3__.vec.Vector;
	
	import com.greensock.OverwriteManager;
	import com.greensock.TweenMax;
	import com.greensock.easing.Elastic;
	import com.greensock.plugins.MotionBlurPlugin;
	import com.greensock.plugins.TweenPlugin;
	
	import flash.events.Event;
	
	import org.papervision3d.core.geom.Particles;
	import org.papervision3d.core.geom.renderables.Particle;
	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.materials.special.ParticleMaterial;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Sphere;
	import org.papervision3d.view.BasicView;
	
	//import com.greensock.plugins.

	public class ApplicationView extends BasicView
	{
		private var _object3D : DisplayObject3D;
		private var _origVertices : Vector.;
		private var _displayObj : DisplayObject3D;
		private var _satellites : Vector.;
		private var _particles : Particles;
		
		public function ApplicationView()
		{
			super(580, 580);	
			//TweenPlugin.activate([MotionBlurPlugin]);		
		}
		
		override protected function onRenderTick(event:Event=null):void
		{
			super.onRenderTick();
		}
		
		public function set object3D(aValue : DisplayObject3D):void
		{
			_object3D = aValue;
			_setObjectVertices();
		}
		
		private function _setObjectVertices():void
		{
			_origVertices = new Vector.;
			for each(var vert : Vertex3D in _object3D.geometry.vertices)
			{
				var vertObj : Object = {x:vert.x, y:vert.y, z:vert.z};
				_origVertices.push(vertObj);
			}
			
			_initParticles();
		}
		
		private function _initParticles():void
		{			
			var material : ParticleMaterial = new ParticleMaterial(0xff0000, 1, ParticleMaterial.SHAPE_CIRCLE, 10);
			
			_satellites = new Vector.();			
			_particles = new Particles();			
			_displayObj = new DisplayObject3D();
			
			scene.addChild(_particles);
			scene.addChild(_displayObj);
			
			_displayObj.addChild(_particles);
			
			for each(var vert : Vertex3D in _object3D.geometry.vertices)
			{					
				var particle : Particle = new Particle(material);
					particle.x = vert.x;
					particle.y = vert.y;
					particle.z = vert.z;
					
				_particles.addParticle(particle);
				
				_satellites.push(particle);
			}
		}
		
		public function moveParticles(aValue:Vector.):void
		{
				var i : int = 0;
				var j : int = 0;
				var t : int = 0;
				
				// This ensures even distribution among all vertices.
				var divisor : Number = Math.floor(_satellites.length / 256);
				
				for each (var sat : Particle in _satellites)
				{
					// The values used in the x,y,z calculations were achieved through trial and error...
					TweenMax.to(_satellites[i], .2, {
														x: _origVertices[i].x * ((400 / aValue[i]) * 1.2),
														y: _origVertices[i].y * ((400 / aValue[i]) * 1.2),
														z: _origVertices[i].z * ((400 / aValue[i]) * 1.2),
														overwrite: OverwriteManager.PREEXISTING
													} );
					
					
					if (i < (_satellites.length - (divisor - 1))) {
						i += divisor;
					}
					
					
				}
				
			
			_displayObj.localRotationZ += .3;
			_displayObj.localRotationY += .1;			
		}
		
		private function _renderScene():void
		{
			renderer.renderScene(scene, camera, viewport);
		}
	}
}
Enjoy ;)			
			

Category: Code & Samples

Tagged: , , , , ,

3 Responses

  1. Ted says:

    Nice example.

    I think ApplicationView may have some code missing though ?

  2. I just noticed this comment, and added the missing code. However, the syntax highlighting isn't cooperating, so it's a bit hard to read (copy and paste into a text editor for best results).

  3. One more comment:

    The 'pre' code formatting seems to strip out some of my code. I've created a pastebin page with the ApplicationView code here : http://pastebin.com/uyk8gfnh

Leave a Reply