Flash - SPC Player

I released Flash SPC Player which created by FlashDevelop with Adobe Alchemy.
SPC decoeder code that has used the snes_spc-0.9.0.

- snes_spc-0.9.0
http://www.fly.net/~ant/libs/audio.html


SPC-700 emulation is completely running probably.
This sound does not look like Real SNES because maybe DSP code was simplified version.
Real version DSP code is too heavy for execution on the Flash Player.

Notice: snes_spc-0.9.0's licence is LGPL. be careful it's usage.
SPC Player

Flash Player is not found or disabled JavaScript.

Get Adobe Flash player

SPC Player
Require Flash Player 10 or later for Playing.

Source Code (AS3)

package 
{
	import cmodule.libspc.CLibInit;
	import flash.display.SimpleButton;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.system.Security;
	import flash.system.SecurityDomain;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	/**
	 * SPC Player Sample
	 * @author Hikipuro
	 */
	public class Main extends Sprite 
	{
		/**
		 * SPC File's URL
		 */
		private const spcurl:String = "smw-10a.spc";
		
		/**
		 * URL Loader for SPC file
		 */
		private var urlLoader:URLLoader;
		
		/**
		 * SPC library
		 */
		private var libspc:Object;
		
		/**
		 * ByteArray for wave sound
		 */
		private var out:ByteArray;
		
		/**
		 * sound playing flag
		 */
		private var playing:Boolean;
		
		/**
		 * SPC file's binary
		 */
		private var spcdata:ByteArray;
		
		/**
		 * sound object
		 */
		private var sound:Sound;
		
		/**
		 * "Play" button
		 */
		private var playButton:SimpleButton;
		
		/**
		 * "Stop" button
		 */
		private var stopButton:SimpleButton;
		
		/**
		 * "Loading" text
		 */
		private var loadingTextFiled:TextField;
		
		/**
		 * constructor
		 */
		public function Main():void 
		{
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		/**
		 * initialize method
		 * @param	e
		 */
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			
			// init member
			playing = false;
			
			// load snes_spc-0.9.0
			var loader:CLibInit = new CLibInit;
			libspc = loader.init();
			
			// load SPC in URL
			urlLoader = new URLLoader();
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.addEventListener(Event.COMPLETE, onLoadComplete);
			urlLoader.load(new URLRequest(spcurl));
			
			// create "Play" button
			playButton = new SimpleButton();
			playButton.upState = makeButton(0xDDDDDD, 100, 20, 10, "Play");
			playButton.overState = makeButton(0xEEEEEE, 100, 20, 10, "Play");
			playButton.downState = makeButton(0xCCCCCC, 100, 20, 10, "Play");
			playButton.hitTestState = playButton.upState;
			playButton.addEventListener(MouseEvent.MOUSE_DOWN, onPlayStopButtonDown);
			playButton.x = (320 - playButton.width) / 2;
			playButton.y = (240 - playButton.height) / 2;
			addChild(playButton);
			playButton.visible = false;
			
			// create "Stop" button
			stopButton = new SimpleButton();
			stopButton.upState = makeButton(0xDDDDDD, 100, 20, 10, "Stop");
			stopButton.overState = makeButton(0xEEEEEE, 100, 20, 10, "Stop");
			stopButton.downState = makeButton(0xCCCCCC, 100, 20, 10, "Stop");
			stopButton.hitTestState = stopButton.upState;
			stopButton.addEventListener(MouseEvent.MOUSE_DOWN, onPlayStopButtonDown);
			stopButton.x = (320 - stopButton.width) / 2;
			stopButton.y = (240 - stopButton.height) / 2;
			addChild(stopButton);
			stopButton.visible = false;
			
			// create "Loading" text
			loadingTextFiled = new TextField();
			loadingTextFiled.autoSize = TextFieldAutoSize.CENTER;
			loadingTextFiled.text = "Loading ...";
			loadingTextFiled.x = (320 - loadingTextFiled.width) / 2;
			loadingTextFiled.y = (240 - loadingTextFiled.height) / 2;
			addChild(loadingTextFiled);
		}
		
		/**
		 * load complete event for URL Loader
		 * @param	e
		 */
		private function onLoadComplete(e:Event):void 
		{
			// SPC file's content into ByteArray
			spcdata = urlLoader.data as ByteArray;
			spcdata.endian = Endian.LITTLE_ENDIAN;
			spcdata.position = 0;
			
			// prepare output buffer
			out = new ByteArray();
			out.endian = Endian.LITTLE_ENDIAN;
			
			// show play button
			loadingTextFiled.visible = false;
			playButton.visible = true;
		}
		
		/**
		 * audio data require event
		 * @param	event
		 */
		private function onSampleData(e:SampleDataEvent):void 
		{
			var data:ByteArray = e.data;
			
			// execute SPC emulator for getting wave audio
			out.position = 0;
			libspc.play(out, 8192);
			out.position = 0;
			
			// executing while for wave length
			for (var i:uint = 0; i < 8192; i++) {
				var s1:Number = 0;
				var s2:Number = 0;
				
				// play() method returned for 16 bit wave sound data
				// but it is not playable for Flash Player.
				// converting float type wave sound data.
				s1 = out.readShort();
				s1 /= 32768;
				s2 = out.readShort();
				s2 /= 32768;
				
				// control sound volume
				//s *= 0.5;
				
				// write wave sound buffer
				data.writeFloat(s1); // left channel
				data.writeFloat(s2); // right channel
			}
		}
		
		/**
		 * "Play" / "Stop" button pressed event
		 * @param	e
		 */
		private function onPlayStopButtonDown(e:MouseEvent):void 
		{
			if ( playing == false ) {
				playing = true;
				playButton.visible = false;
				stopButton.visible = true;
			
				// Flash's Sound object uses sampling rate for 44100 Hz.
				// to a little slowly play sound (SNES's sampling rate 32000 Hz)
				// 256 is normal tempo.
				var tempo:int = 256 / (44100 / 32000);
				libspc.load(spcdata, spcdata.length, tempo);
				
				// create new sound object and play sound
				sound = new Sound();
				sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
				sound.play(0, 1);
			} else {
				playing = false;
				playButton.visible = true;
				stopButton.visible = false;
				
				sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
				sound = null;
			}
		}
		
		/**
		 * create button method
		 * @param	color	surface color
		 * @param	width	
		 * @param	height	
		 * @param	round	corner roundness
		 * @param	text	button's caption
		 * @return	button spirte object
		 */
		private function makeButton(color:uint, width:int, height:int, round:int, text:String):Sprite
		{
			var t:TextField = new TextField();
			var s:Sprite = new Sprite();
			s.graphics.lineStyle(2);
			s.graphics.beginFill(color);
			s.graphics.drawRoundRect(0, 0, width, height, round);
			s.graphics.endFill();
			
			t.text = text;
			t.selectable = false;
			t.width = width;
			t.autoSize = TextFieldAutoSize.CENTER;
			s.addChild(t);
			return s;
		}
	}
	
}