forked from: forked from: まだ研究中 シーッ!

by Limeflash
Draw a Tasty Ramen !
* 
* You can edit and modify every piece of this code.
* Load more pictures of GU (ingredients of ramen)
* from flickr or draw one by yourself.
* Make it look tasty.
*
♥0 | Line 293 | Modified 2010-02-01 06:19:50 | MIT License
play

ActionScript3 source code

/**
 * Copyright Limeflash ( http://wonderfl.net/user/Limeflash )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/fhDD
 */

// forked from hacker_57o638hu's forked from: まだ研究中 シーッ!
// forked from alumican_net's まだ研究中 シーッ!
/*
 * 
 * Draw a Tasty Ramen !
 * 
 * You can edit and modify every piece of this code.
 * Load more pictures of GU (ingredients of ramen)
 * from flickr or draw one by yourself.
 * Make it look tasty.
 *
 */
package
{
	import flash.display.*;
	import org.libspark.thread.*;
	import com.flashdynamix.utils.SWFProfiler;
	
	public class FlashTest extends Sprite
	{
		public function FlashTest():void
		{
			//ステージ初期化
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align     = StageAlign.TOP_LEFT;
			stage.frameRate = 60;
			
			//スレッドの開始
			Thread.initialize(new EnterFrameThreadExecutor());
			new MainThread(this).start();
			
			//プロファイラの表示
			SWFProfiler.init(this);
		}
	}
}

import flash.display.*;
import flash.errors.IOError;
import flash.events.*;
import flash.geom.Matrix;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.net.*;
import flash.system.LoaderContext;
import flash.text.*;
import flash.utils.ByteArray;
import org.libspark.thread.*;
import org.libspark.thread.errors.*;
import org.libspark.thread.threads.display.LoaderThread;
import org.libspark.thread.threads.media.SoundLoaderThread;
import org.libspark.thread.threads.net.URLLoaderThread;
import org.libspark.thread.utils.*;

internal class MainThread extends Thread
{
	//----------------------------------------
	//CONSTANTS
	//----------------------------------------
	
	/**
	 * ピッチ可変再生のためのバッファ長
	 */
	private const SOUND_BUFFER_LENGHT:uint = 2048; 
	
	/**
	 * Soundファイルのビットレート(KHz)
	 */
	private const SOUND_BITRATE:Number = 44.1;
	
	/**
	 * π
	 */
	private const PI:Number  = Math.PI;
	private const PI2:Number = Math.PI * 2;
	
	/**
	 * ステージサイズ
	 */
	private const W:uint = 465;
	private const H:uint = 465;
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	//----------------------------------------
	
	//ベースSprite
	private var _base:Sprite;
	
	//外部Bitmap配列
	private var _bmps:Array;
	
	//外部Sound配列
	private var _sounds:Array;
	
	//合成Sound
	private var _track:Sound;
	
	//Soundの再生ピッチ
	private var _soundPitch:Number;
	
	//Soundの生データ
	private var _soundBytes:ByteArray;
	
	//Soundの再生位置
	private var _soundPosition:Number;
	
	//Soundの総バイト数
	private var _soundBytesTotal:Number;
	
	//動的音生成用のSound
	private var _soundDynamic:Sound;
	
	//動的音生成用のSoundChannel
	private var _soundChannel:SoundChannel;
	
	//ディスク用Sprite
	private var _disc:Sprite;
	
	//どんぶり
	private var _bowl:Sprite;
	
	//ディスクをスクラッチ中ならtrue
	private var _isScratching:Boolean;
	
	//スクラッチ速度
	private var _scratchVelocity:Number;
	
	//スクラッチの角度
	private var _newScratchAngle:Number;
	private var _oldScratchAngle:Number;
	
	//SEの画像データ
	private var _notes:Array;
	
	//SEの音データ
	private var _noteBytes:Array;
	
	//stageの参照
	public var stage:Stage;
	
	//デバッグ用TextField
	public var field:TextField;	
	
	
	
	
	
	//----------------------------------------
	//METHODS
	//----------------------------------------
	
	public function MainThread(base:Sprite):void
	{
		this.stage = base.stage;
		_base      = base;
		
		//デバッグ用TextFieldの生成
		field = _base.addChild(new TextField()) as TextField;
		field.width  = 465;
		field.height = 465;
		field.text = "test";
		field.selectable = false;
		field.mouseEnabled = false;
	}
	
	/**
	 * スレッドの実行関数
	 */
	override protected function run():void 
	{
		_loadImages();
	}
	
	/**
	 * 画像の読み込みを開始する
	 */
	private function _loadImages():void
	{
		var urls:Array = [
			"http://lab.alumican.net/wonderfl/ramen_beats_box/3787648401_0b3d62a314_o.png", /* ラーメン */
			"http://lab.alumican.net/wonderfl/ramen_beats_box/3787648415_1f857d46cf_o.png", /* ナルト1  */
			"http://lab.alumican.net/wonderfl/ramen_beats_box/3788456920_5101174e6c_o.png", /* ナルト2  */
			"http://lab.alumican.net/wonderfl/ramen_beats_box/3788456906_91e357245f_o.png"  /* メンマ   */
		];
		_loadDatas(urls, "img", _onImagesLoadComplete);
	}
	
	/**
	 * 画像の読み込み完了ハンドラ
	 */
	private function _onImagesLoadComplete(container:Array, type:String):void 
	{
		//読み込んだBitmap配列
		_bmps = container;
		
		//----------------------------------------
		//ラーメンの配置
		_disc = _base.addChild( new Sprite() ) as Sprite;
		_disc.x = W / 2;
		_disc.y = H / 2 - 30;
		_disc.scaleX = _disc.scaleY = 0.7;
		
		var bowlBmp:Bitmap = _bmps[0];
		bowlBmp.smoothing = true;
		bowlBmp.x = -bowlBmp.width  / 2;
		bowlBmp.y = -bowlBmp.height / 2;
		
		_bowl = new Sprite();
		_bowl.addChild(bowlBmp);
		
		_disc.addChild(_bowl);
		
		//----------------------------------------
		//具材
		_notes = new Array(_bmps.length - 1);
		var offsetX:Number = (W - 80 * (_notes.length - 1)) / 2;
		for (var i:int = 0; i < _notes.length; ++i) 
		{
			var bmp:Bitmap = _bmps[i + 1];
			
			var scale:Number = 60 / bmp.width;
			var resized:BitmapData = new BitmapData(scale * bmp.width, scale * bmp.height, true, 0x0);
			resized.draw(bmp.bitmapData, new Matrix(scale, 0, 0, scale));
			
			var bmp2:Bitmap = new Bitmap(resized);
			bmp2.x = -bmp2.width  / 2;
			bmp2.y = -bmp2.height / 2;
			
			var spr:Sprite = new Sprite();
			spr.addChild(bmp2);
			spr.x = offsetX + i * 80;
			spr.y = H - 40;
			
			_base.addChild(spr);
			
			_notes[i] = spr;
		}
		
		//----------------------------------------
		//音の読み込みを開始する
		_loadSounds();
	}
	
	/**
	 * 音の読み込みを開始する
	 */
	private function _loadSounds():void
	{
		var urls:Array = [
			//"http://lab.alumican.net/wonderfl/ramen_beats_box/p-stone.mp3", /* ベーストラック */
			"http://lab.alumican.net/wonderfl/ramen_beats_box/bit243-1.mp3", /* ベーストラック */
			"http://lab.alumican.net/wonderfl/ramen_beats_box/hat_close_003.mp3",
			"http://lab.alumican.net/wonderfl/ramen_beats_box/kick_002.mp3",
			"http://lab.alumican.net/wonderfl/ramen_beats_box/snare_002.mp3"
		];
		_loadDatas(urls, "sound", _onSoundsLoadComplete);
	}
	
	/**
	 * 音の読み込み完了ハンドラ
	 */
	private function _onSoundsLoadComplete(container:Array, type:String):void 
	{
		//読み込んだSound配列
		_sounds = container;
		
		//リズムトラック
		_track = _sounds[0];
		
		//SEオブジェクト
		_noteBytes = new Array(_sounds.length - 1);
		for (var i:int = 0; i < _noteBytes.length; ++i) 
		{
			//Soundオブジェクトから生データ(ByteArray)を抽出する
			var se:Sound     = _sounds[i + 1];
			var ba:ByteArray = new ByteArray();
			se.extract(ba, se.length * SOUND_BITRATE * 8);
			_noteBytes[i] = ba;
		}
		
		//再生開始
		play();
	}
	
	
	
	
	
	/**
	 * Music start!!
	 */
	private function play():void
	{
		//再生開始
		_soundPitch = 1;
		_startSound(_track);
		
		//ディスクの回転開始
		_disc.rotation = 0;
		
		//ピッチ計測開始
		_soundPitch   = 1;
		_isScratching = false;
		_bowl.addEventListener(Event.ENTER_FRAME, _updatePitch);
		_bowl.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
		_bowl.buttonMode = true;
		
		_newScratchAngle= Math.atan2(_base.mouseY - _disc.y, _base.mouseX - _disc.x);
		_oldScratchAngle = _newScratchAngle;
		_scratchVelocity = 0;
	}
	
	/**
	 * ピッチ調整
	 */
	private function _updatePitch(e:Event):void 
	{
		_oldScratchAngle = _newScratchAngle;
		_newScratchAngle = Math.atan2(_base.mouseY - _disc.y, _base.mouseX - _disc.x);
		
		var targetPitch:Number;
		if (_isScratching)
		{
			var targetVelocity:Number = _newScratchAngle - _oldScratchAngle;
			if (targetVelocity < 0 ) targetVelocity += PI2;
			if (targetVelocity > PI) targetVelocity -= PI2;
			targetVelocity *= 50;
			
 			_scratchVelocity += (targetVelocity - _scratchVelocity) * 0.5;
			targetPitch       = _scratchVelocity;
		}
		else
		{
			_scratchVelocity += (0 - _scratchVelocity) * 0.1;
			targetPitch       = 1.0 + _scratchVelocity;
		}
		
		_soundPitch += (targetPitch - _soundPitch) * 0.1;
	}
	
	/**
	 * マウスダウンハンドラ
	 */
	private function _mouseDownHandler(e:MouseEvent):void
	{
		_isScratching = true;
		stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUpHandler);
	}
	
	/**
	 * マウスアップハンドラ
	 */
	private function _mouseUpHandler(e:MouseEvent):void
	{
		_isScratching = false;
		stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseUpHandler);
	}
	
	/**
	 * サウンドの再生を開始する
	 */
	private function _startSound(sound:Sound):void
	{
		_soundBytes = new ByteArray();
		
		//Soundオブジェクトから生データ(ByteArray)を抽出する
		while (sound.extract(_soundBytes, SOUND_BUFFER_LENGHT) >= SOUND_BUFFER_LENGHT) { };
		
		//Sound再生ヘッダを先頭へ移動する
		_soundPosition = 0;
		
		//Soundのバイト数を取得する
		_soundBytesTotal = _soundBytes.length;
		
		//3倍にする(読み取り領域がはみ出たときのため)
		_soundBytes.writeBytes(_soundBytes);
		_soundBytes.writeBytes(_soundBytes);
		
		//空のSoundを再生開始する
		_soundDynamic = new Sound();
		_soundDynamic.addEventListener(SampleDataEvent.SAMPLE_DATA, _onSoundSampleDataHandler);
		_soundChannel = _soundDynamic.play();
		
		_synthSound(_noteBytes[2], 0);
	}
	
	/**
	 * サウンドのサンプルデータハンドラ
	 */
	private function _onSoundSampleDataHandler(e:SampleDataEvent):void 
	{
		//ピッチを0.5の倍数に調整する
		var pitch:Number = Math.round(_soundPitch * 2) / 2;
		
		for (var i:uint = 0; i < SOUND_BUFFER_LENGHT; ++i) 
		{
			//ByteArrayの読み取りヘッダを移動する
			_soundPosition += pitch * 8;
			
			//ループ処理
			if (_soundPosition >= _soundBytesTotal - 8) _soundPosition -= _soundBytesTotal;
			if (_soundPosition <  8                   ) _soundPosition += _soundBytesTotal;
			
			//読み込みヘッダの移動(1トラック分オフセット)
			_soundBytes.position  = _soundBytesTotal + _soundPosition;
			
			//ByteArrayの読み込みと書き込み
			e.data.writeFloat( _soundBytes.readFloat() );
			e.data.writeFloat( _soundBytes.readFloat() );
		}
		
		_disc.rotation = 360 * _soundPosition / _soundBytesTotal;
	}
	
	/**
	 * Soundを合成する
	 */
	private function _synthSound(bytes:ByteArray, position:uint):void
	{
		var offset0:int = -_soundBytesTotal;
		var offset1:int = +_soundBytesTotal;
		
		var len:uint  = bytes.length;
		var last:uint = position + len;
		var dst:Number;
		var src:Number;
		
		bytes.position = 0;
		
		for (var i:uint = position; i < last; i += 4) 
		{
			//合成
			_soundBytes.position = i;
			src = _soundBytes.readFloat();
			dst = _soundBytes.readFloat() + bytes.readFloat();
			dst *= 0.9;
			
			//中心の波形に書き込む
			_soundBytes.position = i;
			_soundBytes.writeFloat(dst);
			
			//前の波形に書き込む
			_soundBytes.position = i + offset0;
			_soundBytes.writeFloat(dst);
			
			//後ろの波形に書き込む
			if (i + len < _soundBytesTotal)
			{
				_soundBytes.position = i + offset1;
				_soundBytes.writeFloat(dst);
			}
		}
	}
	
	
	
	
	
	//----------------------------------------
	//VERSATILE METHODS
	//----------------------------------------
	
	/**
	 * URL配列で与えられたデータを読み込む汎用メソッド
	 */
	private function _loadDatas(urls:Array, type:String, onComplete:Function = null, onError:Function = null):void
	{
		//読み込んだデータを格納する配列
		var container:Array = new Array();
		
		//読み込み開始
		var loaders:ParallelExecutor = new ParallelExecutor();
		for (var i:uint = 0; i < urls.length; ++i) 
		{
			var thread:Thread = (type == "text" ) ? new URLLoaderThread( new URLRequest(urls[i]) )   :
			                    (type == "sound") ? new SoundLoaderThread( new URLRequest(urls[i]) ) :
			                                        new LoaderThread( new URLRequest(urls[i]), new LoaderContext(true) ) ;
			loaders.addThread(thread);
		}
		loaders.start();
		loaders.join();
		
		//読み込み完了ハンドラ
		function onLoadComplete():void
		{
			//読み込んだデータを配列に格納する
			for (var i:uint = 0; i < urls.length; ++i) 
			{
				var data:* = (type == "text" ) ? URLLoaderThread( loaders.getThreadAt(i) ).loader.data :
				             (type == "sound") ? SoundLoaderThread( loaders.getThreadAt(i) ).sound     :
				                                 LoaderThread( loaders.getThreadAt(i) ).loader.content ;
				container.push(data);
			}
			//コールバック関数の呼び出し
			if (onComplete != null) onComplete(container, type);
		}
		
		//読み込みエラーハンドラ
		function onLoadError(e:Error, t:Thread):void
		{
			var url:String = (type == "text" ) ? URLLoaderThread(t).request.url   :
			                 (type == "sound") ? SoundLoaderThread(t).request.url :
			                                     LoaderThread(t).request.url      ;
			trace("error : _loadDatas, url = " + url + ", type = " + type);
			//コールバック関数の呼び出し
			(onError != null) ? onError() : next(null);
		}
		
		//ハンドラの登録
		next(onLoadComplete);
		error(IOError, onLoadComplete);
		error(SecurityError, onLoadError);
	}
	
	/**
	 * デバッグ出力
	 */
	public function log(...args):void
	{
		var argn:uint = args.length;
		for (var i:uint = 0; i < argn; ++i)
		{
			if (args[i] == "")
			{
				field.text = "";
				continue;
			}
			field.appendText(String(args[i]) + "\n");
		}
	}
}