In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: Ramen! Beats!! Box!!!  forked from: colin challenge for amateurs

It's Show Time ! 
* Make a music & Let's Scratch ramens !
* 
* ・具をトッピングしたり、どんぶりをスクラッチしたり、レバーをいじったりして遊びながら、
*  おいしいラーメン (音楽) を作ってください。
* 
* ・「Shiftキー + 既に置いてある具材をドラッグ」 で置き直せます。
* 
* ・トッピングした音は自動でビートに吸着するので、
*  適当に置いてもそれなりに聴けると思います。
* 
* 一部に propan_mode さん(http://propanmode.net/)が配布されている効果音と、
* 音声読み上げソフト SofTalk (http://cncc.hp.infoseek.co.jp/)を使わせていただいています、多謝。
* 
* コードの汚さはおいしさに反比例しています。
* さあレッツラ ラーメン!
* 
* @author alumican.net<Yukiya Okuda>
* @link http://alumican.net/
* 
*****************************************
* 
* 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.
*
/**
 * Copyright taizou1988 ( http://wonderfl.net/user/taizou1988 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/a9Cv
 */

// forked from alumican_net's Ramen! Beats!! Box!!!    forked from: colin challenge for amateurs
/* It's Show Time ! 
 * Make a music & Let's Scratch ramens !
 * 
 * ・具をトッピングしたり、どんぶりをスクラッチしたり、レバーをいじったりして遊びながら、
 *  おいしいラーメン (音楽) を作ってください。
 * 
 * ・「Shiftキー + 既に置いてある具材をドラッグ」 で置き直せます。
 * 
 * ・トッピングした音は自動でビートに吸着するので、
 *  適当に置いてもそれなりに聴けると思います。
 * 
 * 一部に propan_mode さん(http://propanmode.net/)が配布されている効果音と、
 * 音声読み上げソフト SofTalk (http://cncc.hp.infoseek.co.jp/)を使わせていただいています、多謝。
 * 
 * コードの汚さはおいしさに反比例しています。
 * さあレッツラ ラーメン!
 * 
 * @author alumican.net<Yukiya Okuda>
 * @link http://alumican.net/
 * 
 *****************************************
 * 
 * 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
		{
			//キャプチャ制御
			Wonderfl.disable_capture();
		//	Wonderfl.capture_delay(30);
			
			//ステージ初期化
			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.*;
import flash.events.*;
import flash.geom.*;
import flash.media.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.ui.*;
import flash.utils.*;
import org.libspark.betweenas3.*;
import org.libspark.betweenas3.easing.*;
import org.libspark.betweenas3.tweens.*;
import org.libspark.thread.*;
import org.libspark.thread.errors.*;
import org.libspark.thread.threads.display.*;
import org.libspark.thread.threads.media.*;
import org.libspark.thread.threads.net.*;
import org.libspark.thread.utils.*;





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                           MainThread
 * メインスレッド
 */
internal class MainThread extends Thread
{
	//----------------------------------------
	//CONSTANTS
	//----------------------------------------
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	//----------------------------------------
	
	/**
	 * stageの参照
	 */
	public var stage:Stage;
	
	/**
	 * ベースSprite
	 */
	private var _base:Sprite;
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * コンポーザークラス
	 */
	private var _composer:Composer;
	
	
	
	
	
	//----------------------------------------
	//METHODS
	//----------------------------------------
	
	public function MainThread(base:Sprite):void
	{
		this.stage = base.stage;
		_base      = base;
		
		//デバッガの初期化
		Debugger.initialize(_base);
	}
	
	/**
	 * スレッドの実行関数
	 */
	override protected function run():void 
	{
		Debugger.log("", "now loading...");
		
		//外部リソースを読み込む
		_resource = new Resource();
		_resource.start();
		_resource.join();
		
		next(_loadResourceComplete);
	}
	
	/**
	 * 外部リソースの読み込み完了ハンドラ
	 */
	private function _loadResourceComplete():void
	{
		Debugger.log("", "initializing...");
		
		_composer = new Composer(_base, _resource);
		_composer.start();
		_composer.join();
		
		next(null);
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                             Composer
 * コンポーザー
 */
internal class Composer extends Thread
{
	//----------------------------------------
	//CONSTANTS
	//----------------------------------------
	
	/**
	 * ステージサイズ
	 */
	private const W:uint = 465;
	private const H:uint = 465;
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	//----------------------------------------
	
	/**
	 * stageの参照
	 */
	public var stage:Stage;
	
	/**
	 * ベースSprite
	 */
	public function get base():Sprite { return _base; }
	private var _base:Sprite;
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * ターンテーブル
	 */
	private var _tables:Array;
	
	/**
	 * ターンテーブルの数
	 */
	 private var _tableCount:uint;
	
	/**
	 * ピッチコントローラ
	 */
	 private var _pitchController:PitchController;
	 
	 /**
	  * 音符アタッチャー
	  */
	 private var _noteAttacher:NoteAttacher;
	 
	 /**
	  * カーソル
	  */
	 private var _cursor:Cursor;
	
	
	
	
	
	//----------------------------------------
	//METHODS
	//----------------------------------------
	
	public function Composer(base:Sprite, resource:Resource):void
	{
		this.stage = base.stage;
		_base      = base;
		_resource  = resource;
	}
	
	/**
	 * スレッドの実行関数
	 */
	override protected function run():void 
	{
		Debugger.log("", "run");
		
		_setupTables();
		_setupPitchController();
		_setupNoteAttacher();
		_setupCursor();
		
		//再生開始
		play();
	}
	
	/**
	 * ターンテーブルを配置する
	 */
	private function _setupTables():void
	{
		_tableCount = 2;
		_tables     = new Array(_tableCount);
		var table:Turntable;
		for (var i:uint = 0; i < _tableCount; ++i)
		{
			table = new Turntable(_resource, this, i);
			table.x = W / 4 * (i * 2 + 1);
			table.y = H / 2 - 30;
			_base.addChild(table);
			
			//配列に格納する
			_tables[i] = table;
		}
	}
	
	/**
	 * ピッチコントローラの配置
	 */
	private function _setupPitchController():void
	{
		_pitchController = new PitchController(_resource, this);
		_pitchController.x = 30;
		_pitchController.y = 40;
		_base.addChild(_pitchController);
	}
	
	/**
	 * アタッチャーの配置
	 */
	private function _setupNoteAttacher():void
	{
		_noteAttacher = new NoteAttacher(_resource, this);
		_base.addChild(_noteAttacher);
		
		_noteAttacher.x = (W - _noteAttacher.width) / 2;
		_noteAttacher.y = H - 70;
	}
	
	/**
	 *マウストレーラーの配置
	 */
	private function _setupCursor():void
	{
		_cursor = new Cursor(_resource, this);
		_base.addChild(_cursor);
	}
	
	/**
	 * 再生開始
	 */
	public function play():void
	{
		Debugger.log("");
		
		var table:Turntable;
		for (var i:uint = 0; i < _tableCount; ++i)
		{
			table = _tables[i];
			table.play();
		}
	}
	
	/**
	 * ピッチコントローラからの再生ピッチの変更通知
	 */
	public function notifyPitchControllerUpdate(pitch:Number):void
	{
		var table:Turntable;
		for (var i:uint = 0; i < _tableCount; ++i)
		{
			table = _tables[i];
			table.defaultPitch = pitch;
		}
	}
	
	/**
	 * 音を合成する(xy座標)
	 */
	public function synthSoundOnXY(bytes:Array, x:Number, y:Number, object:DisplayObject, mode:String):Array
	{
		var gp:Point = _noteAttacher.localToGlobal( new Point(x, y) );
		var table:Turntable, p:Point;
		var result:Array = new Array();
		for (var i:uint = 0; i < _tableCount; ++i)
		{
			table = _tables[i];
			p = table.getPolarFromXY( table.globalToLocal( gp.clone() ) );
			
			if (p.x <= 1)
			{
				(mode == "add") ? table.addFilling(object, p.x, p.y) : table.removeFilling(object);
				result.push(
					{
						discID   : i,
						position : table.synthSoundUsingAngle(bytes[i], p.y, mode),
						distance : p.x,
						angle    : p.y
					}
				);
			}
		}
		return result;
	}
	
	/**
	 * 音を合成する(バイト番号)
	 */
	public function synthSoundOnPosition(bytes:Array, discID:uint, position:uint, mode:String):void
	{
		Turntable(_tables[discID]).synthSoundUsingPosition(bytes[discID], position, mode);
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                            Turntable
 * ターンテーブル
 */
internal class Turntable extends Sprite
{
	//----------------------------------------
	//CONSTANTS
	
	/**
	 * ピッチ可変再生のためのバッファ長
	 */
	private const SOUND_BUFFER_LENGHT:uint = 2048; 
	
	/**
	 * サンプリングレート(KHz)
	 */
	private const SOUND_SAMPLING_RATE:Number = 44.1;
	
	/**
	 * π
	 */
	private const PI:Number  = Math.PI;
	private const PI2:Number = Math.PI * 2;
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * コンポーザー
	 */
	private var _composer:Composer;
	
	/**
	 * ディスクID
	 */
	private var _discID:uint;
	
	
	
	
	
	/**
	 * メイントラック
	 */
	private var _track:Sound;
	
	/**
	 * 再生ピッチ
	 */
	public function get pitch():Number { return _pitch; }
	public function set pitch(value:Number):void { _pitch = value; }
	private var _pitch:Number;
	
	/**
	 * デフォルトの再生ピッチ
	 */
	public function get defaultPitch():Number { return _defaultPitch; }
	public function set defaultPitch(value:Number):void { _defaultPitch = value; }
	private var _defaultPitch:Number;
	
	/**
	 * メイントラックのByteデータ
	 */
	private var _trackBytes:ByteArray;
	
	/**
	 * メイントラックのByteデータ(合成前)
	 */
	private var _trackBytesOriginal:ByteArray;
	
	//メイントラックの再生位置
	private var _trackPosition:Number;
	
	//メイントラックの総バイト数
	private var _trackBytesTotal:Number;
	
	//動的生成Sound
	private var _dynamicSound:Sound;
	
	//動的生成SoundChannel
	private var _dynamicSoundChannel:SoundChannel;
	
	/**
	 * 再生中の場合はtrue
	 */
	public function get isPlaying():Boolean { return _dynamicSoundChannel != null; }
	
	
	
	
	
	/**
	 * disc用Sprite
	 */
	private var _disc:Sprite;
	
	/**
	 * どんぶり用Sprite
	 */
	private var _bowl:Bitmap;
	
	/**
	 * 具材用Sprite
	 */
	private var _filling:Sprite;
	
	/**
	 * ボタン領域
	 */
	private var _area:Sprite;
	
	/**
	 * スクラッチ中ならばtrue
	 */
	public function get isScratching():Boolean { return _isScratching; }
	private var _isScratching:Boolean;
	
	/**
	 * スクラッチ速度
	 */
	public function get scratchVelocity():Number { return _scratchVelocity; }
	private var _scratchVelocity:Number;
	
	//スクラッチの角度
	private var _newScratchAngle:Number;
	private var _oldScratchAngle:Number;
	
	
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function Turntable(resource:Resource, composer:Composer, discID:uint):void
	{
		_resource = resource;
		_composer = composer;
		_discID   = discID;
		
		addEventListener(Event.ADDED_TO_STAGE, _initialize);
		addEventListener(Event.REMOVED_FROM_STAGE, _finalize);
	}
	
	/**
	 * 初期化関数
	 */
	private function _initialize(e:Event):void 
	{

		_initializeImages();
		_initializeSounds();
	}
	
	/**
	 * 画像の初期化
	 */
	private function _initializeImages():void
	{
		//----------------------------------------
		//ディスク
		_disc = new Sprite();
		addChild(_disc);
		
		//----------------------------------------
		//どんぶり画像
		_bowl = _resource.bowlBmps(0);
		_bowl.x = -_bowl.width  / 2;
		_bowl.y = -_bowl.height / 2;
		_bowl.smoothing  = true;
		_disc.addChild(_bowl);
		
		//----------------------------------------
		//ボタン領域
		_area = new Sprite();
		var g:Graphics = _area.graphics;
		g.beginFill(0x0, 0);
		g.drawCircle(0, 0, _bowl.width / 2);
		g.endFill();
		_area.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
		_area.buttonMode = true;
		_disc.addChild(_area);
		
		//----------------------------------------
		//具材
		_filling = new Sprite();
		_disc.addChild(_filling);
		
		_filling.mouseEnabled  = false;
		_filling.mouseChildren = false;
		stage.addEventListener(KeyboardEvent.KEY_DOWN, _keyDownHandler);
		stage.addEventListener(KeyboardEvent.KEY_UP  , _keyUpHandler);
	}
	
	/**
	 * 音の初期化
	 */
	private function _initializeSounds():void 
	{
		//----------------------------------------
		//ベーストラック
		_track = _resource.trackSounds(_discID);
		_trackBytes = new ByteArray();
		
		//Soundオブジェクトから生データ(ByteArray)を抽出する
		_track.extract(_trackBytes, _track.length * SOUND_SAMPLING_RATE * 8, 0);
		
		//Sound再生ヘッダを先頭へ移動する
		_trackPosition = 0;
		
		//Soundのバイト数を取得する
		_trackBytesTotal = _trackBytes.length;
		
		//3倍にする(読み取り領域がはみ出たときのため)
		var single:ByteArray = new ByteArray();
		single.writeBytes(_trackBytes, 0, _trackBytes.length);
		_trackBytes.writeBytes(single);
		_trackBytes.writeBytes(single);
		
		//復元用に保存する
		_trackBytesOriginal = new ByteArray();
		_trackBytes.position = 0;
		_trackBytes.readBytes(_trackBytesOriginal, 0, _trackBytes.length);
		
		//動的音生成のSoundを生成する
		_dynamicSound = new Sound();
		_dynamicSound.addEventListener(SampleDataEvent.SAMPLE_DATA, _onSoundSampleDataHandler);
		
		//デフォルトの再生ピッチ
		_defaultPitch = 1.0;
		
		//再生ピッチ
		_pitch = 1.0;
		
		//スクラッチ状況の初期化
		_isScratching    = false;
		_newScratchAngle = Math.atan2(mouseY, mouseX);
		_oldScratchAngle = _newScratchAngle;
		_scratchVelocity = 0;
	}
	
	/**
	 * 終了関数
	 */
	private function _finalize(e:Event):void
	{
		_finalizeImages();
		_finalizeSounds();
	}
	
	/**
	 * 画像の終了処理
	 */
	private function _finalizeImages():void
	{
	}
	
	/**
	 * 音の終了処理
	 */
	private function _finalizeSounds():void
	{
	}
	
	/**
	 * 再生を開始する
	 */
	public function play():void
	{
		if (isPlaying) return;
		
		//ピッチ計測開始
		addEventListener(Event.ENTER_FRAME, _updatePitch);
		
		//再生開始
		_dynamicSoundChannel = _dynamicSound.play();
	}
	
	/**
	 * 再生を停止する
	 */
	public function stop():void
	{
		if (!isPlaying) return;
		
		//ピッチ計測停止
		removeEventListener(Event.ENTER_FRAME, _updatePitch);
		
		//再生停止
		_dynamicSoundChannel.stop();
		_dynamicSoundChannel = null;
	}
	
	/**
	 * 再生ピッチ調整
	 */
	private function _updatePitch(e:Event):void 
	{
		_oldScratchAngle = _newScratchAngle;
		_newScratchAngle = Math.atan2(mouseY, mouseX);
		
		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       = _defaultPitch + _scratchVelocity;
		}
		
		_pitch += (targetPitch - _pitch) * 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 _onSoundSampleDataHandler(e:SampleDataEvent):void 
	{
		//ピッチを0.5の倍数に調整する
		var normPitch:Number = Math.round(_pitch * 2) / 2;
		
		for (var i:uint = 0; i < SOUND_BUFFER_LENGHT; ++i) 
		{
			//ByteArrayの読み取りヘッダを移動する
			_trackPosition += normPitch * 8;
			
			//ループ処理
			if      (_trackPosition > _trackBytesTotal - 8) _trackPosition -= _trackBytesTotal;
			else if (_trackPosition <  7                  ) _trackPosition += _trackBytesTotal;
			
			//読み込みヘッダの移動(1トラック分オフセット)
			_trackBytes.position = _trackBytesTotal + _trackPosition;
			
			//ByteArrayの読み込みと書き込み
			e.data.writeFloat( _trackBytes.readFloat() / 2 );
			e.data.writeFloat( _trackBytes.readFloat() / 2 );
		}
		
		//ディスクの回転
		_disc.rotation = 360 * _trackPosition / _trackBytesTotal;
	}
	
	
	
	
	
	/**
	 * トラックをSound合成前の状態に復元する
	 */
	public function resetTrack():void
	{
		//トラックの復元
		_trackBytesOriginal.readBytes(_trackBytes, 0, _trackBytesOriginal.length);
		_trackBytesOriginal.position = 0;
	}
	
	
	
	
	
	/**
	 * Soundを合成する
	 */
	public function synthSoundUsingPosition(bytes:ByteArray, position:uint, mode:String):uint
	{
		var len:uint  = bytes.length;
		var last:uint = position + _trackBytesTotal + len;
		var dst:Number;
		
		//ブレンドモード
		var op:Number = (mode == "add") ?  1 :
						(mode == "sub") ? -1 : 0;
		
		bytes.position = 0;
		
		for (var i:uint = position + _trackBytesTotal; i < last; i += 4) 
		{
			//合成(あとでちゃんと調べる)
			_trackBytes.position = i;
			dst = _trackBytes.readFloat() + op * bytes.readFloat() * 2;
			
			//中心の波形に書き込む
			_trackBytes.position = i;
			_trackBytes.writeFloat(dst);
			
			//前の波形に書き込む
			_trackBytes.position = i - _trackBytesTotal;
			_trackBytes.writeFloat(dst);
			
			//後ろの波形に書き込む
			if (i + len < _trackBytesTotal * 2)
			{
				_trackBytes.position = i + _trackBytesTotal;
				_trackBytes.writeFloat(dst);
			}
		}
		
		bytes.position = 0;
		
		return position;
	}
	
	/**
	 * 任意のdisc角度にSoundを合成する
	 */
	public function synthSoundUsingAngle(bytes:ByteArray, angle:Number, mode:String):uint
	{
		//なんか音が遅れるんですけど
		var adjust:Number = (_discID == 0) ? -0.10 : -0.05;
		
		//真上で鳴らす
		var ratio:Number = 0.75 - angle / PI2 + adjust;
		ratio %= 1;
		if (ratio < 0) ratio += 1;
		
		//var position:Number = _trackBytesTotal * ratio;
		//position = Math.round(position / 8) * 8;
		
		//ビートに乗せる
		var position:Number = _trackBytesTotal * ratio;
		var unit:int = 2 * SOUND_SAMPLING_RATE * 1000;
		position = Math.round(position / unit) * unit;
		
		synthSoundUsingPosition(bytes, position, mode);
		
		return position;
	}
	
	/**
	 * disc中心を基準点とするxy座標から、discの回転を考慮した極座標を取得する
	 * x : 角度[0, 2pi]
	 * y : 中心からの距離[0, +inf](1で円周上)
	 */
	public function getPolarFromXY(p:Point):Point
	{
		var x:Number = p.x;
		var y:Number = p.y;
		
		var dist:Number  = Math.sqrt(x * x + y * y) / (_area.width / 2);
		var angle:Number = Math.atan2(y, x) - PI2 * _trackPosition / _trackBytesTotal;
		
		if (angle < 0  ) angle += PI2;
		if (angle > PI2) angle -= PI2;
		
		return new Point(dist, angle);
	}
	
	/**
	 * 具材のDisplayObjectを追加する
	 */
	public function addFilling(object:DisplayObject, dist:Number = 0, angle:Number = 0):DisplayObject
	{
		dist *= _area.width / 2;
		object.x = dist * Math.cos(angle);
		object.y = dist * Math.sin(angle);
		object.rotation -= _disc.rotation;
		
		return _filling.addChild(object);
	}
	
	/**
	 * 具材のDisplayObjectを削除する
	 */
	public function removeFilling(object:DisplayObject):DisplayObject
	{
		var angle:Number = _disc.rotation + Math.atan2(object.y, object.x) * 180 / PI;
		object.rotation += angle;
		
		return (_filling.contains(object)) ? _filling.removeChild(object) : null;
	}
	
	/**
	 * 全ての具材のDisplayObjectを削除する
	 */
	public function removeAllFilling():Array
	{
		var removed:Array = new Array();
		for (var i:uint = _filling.numChildren - 1; i >= 0; --i)
		{
			removed.push( _filling.removeChildAt(i) );
		}
		return removed;
	}
	
	/**
	 * キープレスハンドラ
	 */
	private function _keyDownHandler(e:KeyboardEvent):void 
	{
		if (e.keyCode == 16)
		{
			_filling.mouseEnabled  = true;
			_filling.mouseChildren = true;
		}
	}
	
	/**
	 * キーアップハンドラ
	 */
	private function _keyUpHandler(e:KeyboardEvent):void 
	{
		if (e.keyCode == 16)
		{
			_filling.mouseEnabled  = false;
			_filling.mouseChildren = false;
		}
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                         NoteAttacher
 * 音の合成UI
 */
internal class NoteAttacher extends Sprite
{
	//----------------------------------------
	//CONSTANTS
	
	/**
	 * サンプリングレート(KHz)
	 */
	private const SOUND_SAMPLING_RATE:Number = 44.1;
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * コンポーザー
	 */
	private var _composer:Composer;
	
	/**
	 * 合成音のドラッグ元DisplayObject配列
	 */
	private var _noteObjects:Array;
	
	/**
	 * 合成音数
	 */
	private var _noteCount:uint;
	
	/**
	 * ドラッグ中のDisplayObject
	 */
	private var _draggingObject:MovieClip;
	
	/**
	 * 合成音のバイト列
	 */
	private var _noteBytes:Array;
	
	/**
	 * 具材名再生用SoundChannel
	 */
	private var _fillingSoundChannel:SoundChannel;
	
	
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function NoteAttacher(resource:Resource, composer:Composer):void
	{
		_resource = resource;
		_composer = composer;
		
		addEventListener(Event.ADDED_TO_STAGE, _initialize);
		addEventListener(Event.REMOVED_FROM_STAGE, _finalize);
	}
	
	/**
	 * 初期化関数
	 */
	private function _initialize(e:Event):void 
	{
		//コピー元を生成する
		_noteCount   = _resource.noteCount;
		_noteObjects = new Array(_noteCount);
		_noteBytes   = new Array(_noteCount);
		
		var p:Number = 0;
		for (var i:uint = 0; i < _noteCount; ++i)
		{
			//----------------------------------------
			//ドラッグ用のDisplayObject
			
			var bmp:Bitmap = _resource.fillingBmps(i);
			bmp.x = -bmp.width  / 2;
			bmp.y = -bmp.height / 2;
			bmp.smoothing = true;
			
			p += (i == 0) ? bmp.width / 2 : bmp.width / 2 + 10;
			
			var object:MovieClip = new MovieClip();
			object.addChild(bmp);
			object.initX = object.x = p;
			object.initY = object.y = 0;
			object.id = i;
			object.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
			object.buttonMode = true;
			addChild(object);
			
			p += bmp.width / 2;
			
			_noteObjects[i] = object;
			
			//----------------------------------------
			//Soundオブジェクトから生データ(ByteArray)を抽出する
			_noteBytes[i] = new Array(2);
			for (var j:uint = 0; j < 2; ++j)
			{
				var note:Sound   = _resource.noteSounds(j, i);
				var ba:ByteArray = new ByteArray();
				note.extract(ba, note.length * SOUND_SAMPLING_RATE * 8);
				_noteBytes[i][j] = ba;
			}
		}
	}
	
	/**
	 * 終了関数
	 */
	private function _finalize(e:Event):void
	{
	}
	
	/**
	 * コピー元DisplayObjectのマウスダウンハンドラ
	 */
	private function _mouseDownHandler(e:MouseEvent):void
	{
		var o:MovieClip = _draggingObject = MovieClip(e.currentTarget);
		o.startDrag(false, null);
		o.x = mouseX;
		o.y = mouseY;
		
		//具材名を再生する
		if (_fillingSoundChannel != null) _fillingSoundChannel.stop();
		_fillingSoundChannel = _resource.fillingSounds(o.id).play();
		
		stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUpHandler);
	}
	
	/**
	 * コピー元DisplayObjectのマウスアップハンドラ
	 */
	private function _mouseUpHandler(e:MouseEvent):void
	{
		var o:MovieClip = _draggingObject;
		var id:uint = o.id;
		
		//ドラッグ停止
		o.stopDrag();
		
		//具材の生成
		var bmp:Bitmap = _resource.fillingBmps(id);
		bmp.x = -bmp.width  / 2;
		bmp.y = -bmp.height / 2;
		var filling:MovieClip = new MovieClip();
		filling.id     = id;
		filling.addEventListener(MouseEvent.MOUSE_DOWN, _fillingMouseDownHandler);
		filling.addChild(bmp);
		
		//合成
		var result:Array = _composer.synthSoundOnXY(_noteBytes[id], o.x, o.y, filling, "add");
		if (result.length > 0)
		{
			filling.discID   = result[0].discID;
			filling.position = result[0].position;
			//_resource.noteSounds(id).play();
			
			//元の座標に戻す
			o.x = o.initX;
			o.y = o.initY;
			o.scaleX = o.scaleY = 0;
			BetweenAS3.tween(o, { scaleX:1, scaleY:1 }, null, 0.5, Bounce.easeOut).play();
		}
		else
		{
			//元の座標に戻す
			BetweenAS3.tween(o, { x:o.initX, y:o.initY, scaleX:1, scaleY:1 }, null, 0.5, Quart.easeOut).play();
		}
		
		stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseUpHandler);
		
		_draggingObject = null;
	}
	
	/**
	 * 配置済み具材のマウスダウンハンドラ
	 */
	private function _fillingMouseDownHandler(e:MouseEvent):void 
	{
		var o:MovieClip = _draggingObject = MovieClip(e.currentTarget);
		var id:uint = o.id;
		
		//音を取る
		_composer.synthSoundOnPosition(_noteBytes[id], o.discID, o.position, "sub");
		
		//ドラッグ開始
		addChild(o);
		o.startDrag(false, null);
		o.x = mouseX;
		o.y = mouseY;
		
		//具材名を再生する
		if (_fillingSoundChannel != null) _fillingSoundChannel.stop();
		_fillingSoundChannel = _resource.fillingSounds(o.id).play();
		
		stage.addEventListener(MouseEvent.MOUSE_UP, _fillingMouseUpHandler);
	}
	
	/**
	 * 配置済み具材のマウスアップハンドラ
	 */
	private function _fillingMouseUpHandler(e:MouseEvent):void 
	{
		var o:MovieClip = _draggingObject;
		var id:uint = o.id;
		
		//ドラッグ終了
		o.stopDrag();
		
		//音の合成
		var result:Array = _composer.synthSoundOnXY(_noteBytes[id], o.x, o.y, o, "add");
		if (result.length > 0)
		{
			//再着地成功
			o.discID   = result[0].discID;
			o.position = result[0].position;
			o.distance = result[0].distance;
			o.angle    = result[0].angle;
		}
		else
		{
			//再着地失敗
			BetweenAS3.serial(
				BetweenAS3.tween(o, { scaleX:0, scaleY:0 }, null, 0.5, Quart.easeOut),
				BetweenAS3.func(function():void { removeChild(o); } )
			).play();
			
			o.removeEventListener(MouseEvent.MOUSE_DOWN, _fillingMouseDownHandler);
		}
		stage.removeEventListener(MouseEvent.MOUSE_UP, _fillingMouseUpHandler);
		
		_draggingObject = null;
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                      PitchController
 * ピッチコントローラ
 */
internal class PitchController extends Sprite
{
	//----------------------------------------
	//CONSTANTS
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * コンポーザー
	 */
	private var _composer:Composer;
	
	/**
	 * スライダーオブジェクト
	 */
	private var _slider:Sprite;
	
	/**
	 * ベースオブジェクト
	 */
	 private var _base:Sprite;
	 
	/**
	 * スライダーが示す再生ピッチ
	 */
	 private var _pitch:Number;
	
	/**
	 * スライダーのデフォルト座標
	 */
	 private var _defaultSliderPosition:Number;
	
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function PitchController(resource:Resource, composer:Composer):void
	{
		_resource = resource;
		_composer = composer;
		
		addEventListener(Event.ADDED_TO_STAGE, _initialize);
		addEventListener(Event.REMOVED_FROM_STAGE, _finalize);
	}
	
	/**
	 * 初期化関数
	 */
	private function _initialize(e:Event):void 
	{
		//----------------------------------------
		//ベースの生成
		_base = new Sprite();
		var g:Graphics = _base.graphics;
		g.beginFill(0xeeeeee);
		g.drawRoundRect(0, 0, 400, 3, 3);
		g.endFill();
		addChild(_base);
		
		_defaultSliderPosition = _base.x + _base.width / 2;
		
		//----------------------------------------
		//スライダーの生成
		_slider = new Sprite();
		var bmp:Bitmap = _resource.fillingBmps(5);
		bmp.x = -bmp.width  / 2;
		bmp.y = -bmp.height / 2;
		bmp.smoothing = true;
		_slider.addChild(bmp);
		_slider.x =  _defaultSliderPosition;
		_slider.addEventListener(MouseEvent.MOUSE_DOWN, _sliderMouseDownHandler);
		_slider.buttonMode = true;
		_slider.doubleClickEnabled = true;
		addChild(_slider);
		
		_calcPitch();
		
		addEventListener(Event.ENTER_FRAME, _updateRotation);
	}
	
	/**
	 * 終了関数
	 */
	private function _finalize(e:Event):void
	{
		removeEventListener(Event.ENTER_FRAME, _updateRotation);
	}
	
	/**
	 * スライダーの座標から計算されたピッチを通知する
	 */
	private function _calcPitch():void
	{
		//再生ピッチ計算(真ん中が1)
		_pitch = (_slider.x / (_base.x + _base.width)) * 10 - 4;
		if (_pitch < 0.5) _pitch -= 1.0;
	 }
	
	/**
	 * スライダーのマウスダウンハンドラ
	 */
	 private function _sliderMouseDownHandler(e:MouseEvent):void
	 {
		//ドラッグ開始
		_slider.startDrag(false, new Rectangle(_base.x, _base.height / 2, _base.width, 0));
		
		stage.addEventListener(MouseEvent.MOUSE_MOVE, _sliderMouseMoveHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, _sliderMouseUpHandler);
		
		removeEventListener(Event.ENTER_FRAME, _updateAutoBackHandler);
	 }
	 
	/**
	 * スライダーのマウスアップハンドラ
	 */
	 private function _sliderMouseUpHandler(e:MouseEvent):void
	 {
		//ドラッグ停止
		_slider.stopDrag();
		
		stage.removeEventListener(MouseEvent.MOUSE_MOVE, _sliderMouseMoveHandler);
		stage.removeEventListener(MouseEvent.MOUSE_UP, _sliderMouseUpHandler);
		
		addEventListener(Event.ENTER_FRAME, _updateAutoBackHandler);
	}
	 
	/**
	 * スライダーのドラッグハンドラ
	 */
	 private function _sliderMouseMoveHandler(e:MouseEvent):void
	 {
		_calcPitch();
		_composer.notifyPitchControllerUpdate(_pitch);
	 }
	 
	 /**
	  * マウスを話したときに自動で戻る
	  */
	 private function _updateAutoBackHandler(e:Event):void
	 {
		var d:Number = _defaultSliderPosition - _slider.x;
		var a:Number = (d > 0) ? d : -d;
		
		if (a < 1.0)
		{
			_slider.x = _defaultSliderPosition;
			removeEventListener(Event.ENTER_FRAME, _updateAutoBackHandler);
		}
		else
		{
			_slider.x += d * 0.1;
		}
		
		_calcPitch();
		_composer.notifyPitchControllerUpdate(_pitch);
	 }
	 
	 /**
	  * ぐるぐる回しとく
	  */
	 private function _updateRotation(e:Event):void
	 {
		_slider.rotation += _pitch * 5;
	 }
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                               Cursor
 * カーソル
 */
internal class Cursor extends Sprite
{
	//----------------------------------------
	//CONSTANTS
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * リソース管理クラス
	 */
	private var _resource:Resource;
	
	/**
	 * コンポーザー
	 */
	private var _composer:Composer;
	
	/**
	 * マウストレーラー
	 */
	private var _cursor:Sprite;
	
	/**
	 * 通常時カーソル
	 */
	private var _upBmp:Bitmap;
	
	/**
	 * マウスダウン時カーソル
	 */
	 private var _downBmp:Bitmap;
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function Cursor(resource:Resource, composer:Composer):void
	{
		_resource = resource;
		_composer = composer;
		
		addEventListener(Event.ADDED_TO_STAGE, _initialize);
		addEventListener(Event.REMOVED_FROM_STAGE, _finalize);
	}
	
	/**
	 * 初期化関数
	 */
	private function _initialize(e:Event):void 
	{
		//カーソル画像の取得
		_upBmp = _resource.cursorBmps(0);
		_upBmp.smoothing = true;
		
		_downBmp = _resource.cursorBmps(1);
		_downBmp.smoothing = true;
		_downBmp.visible = false;
		
		//マウストレーラーの生成
		_cursor = new Sprite();
		_cursor.mouseEnabled  = false;
		_cursor.mouseChildren = false;
		_cursor.addChild(_upBmp);
		_cursor.addChild(_downBmp);
		addChild(_cursor);
		
		Mouse.hide();
		var context:ContextMenu = new ContextMenu();
		context.addEventListener(ContextMenuEvent.MENU_SELECT, _contectMenuSelectHandler);
		_composer.base.contextMenu = context;
		
		stage.addEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP  , _mouseUpHandler);
		stage.addEventListener(MouseEvent.MOUSE_MOVE, _mouseMoveHandler);
	}
	
	/**
	 * 終了関数
	 */
	private function _finalize(e:Event):void
	{
		_cursor.removeChild(_upBmp);
		_cursor.removeChild(_downBmp);
		
		stage.removeEventListener(MouseEvent.MOUSE_DOWN, _mouseDownHandler);
		stage.removeEventListener(MouseEvent.MOUSE_UP  , _mouseUpHandler);
		stage.removeEventListener(MouseEvent.MOUSE_MOVE, _mouseMoveHandler);
		
		_upBmp   = null;
		_downBmp = null;
		_cursor  = null;
	}
	
	/**
	 * マウスダウンハンドラ
	 */
	private function _mouseDownHandler(e:MouseEvent):void
	{
		_upBmp.visible   = false;
		_downBmp.visible = true;
	}
	
	/**
	 * マウスアップハンドラ
	 */
	private function _mouseUpHandler(e:MouseEvent):void
	{
		_upBmp.visible   = true;
		_downBmp.visible = false;
	}
	
	/**
	 * マウス移動ハンドラ
	 */
	private function _mouseMoveHandler(e:MouseEvent):void
	{
		_cursor.x = mouseX;
		_cursor.y = mouseY;
	}
	
	/**
	 * 右クリックメニュー表示時にマウスカーソルを隠す
	 * @param	e
	 */
	private function _contectMenuSelectHandler(e:ContextMenuEvent):void 
	{
		Mouse.hide();
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                             Resource
 * リソース管理クラス
 */
internal class Resource extends Thread
{
	//----------------------------------------
	//CONSTANTS
	
	private const BASE_URL:String = "http://lab.alumican.net/wonderfl/ramen_beats_box/";
//	private const BASE_URL:String = "assets/";
	
	/**
	 * 画像URL配列
	 */
	private const IMAGE_URLS:Array = [
		BASE_URL + "imgs/ramen.png", /* ラーメン */
		
		BASE_URL + "imgs/cursor_up.png",   /* 箸(開) */
		BASE_URL + "imgs/cursor_down.png", /* 箸(閉) */
		
		BASE_URL + "imgs/kakuni.png",   /* 角煮 */
		BASE_URL + "imgs/chashu.png",   /* チャーシュー */
		BASE_URL + "imgs/menma.png",    /* メンマ   */
		BASE_URL + "imgs/nitamago.png", /* 煮卵     */
		BASE_URL + "imgs/mame.png",     /* 豆 */
		BASE_URL + "imgs/naruto.png"    /* ナルト   */
	];
	
	/**
	 * 音URL配列
	 */
	private const SOUND_URLS:Array = [
		BASE_URL + "sounds/bit235_etc02.mp3",  /* ギタートラック */
		BASE_URL + "sounds/bit243_bass01.mp3", /* ベーストラック */
	//	BASE_URL + "sounds/bit243_drum02.mp3", /* ドラムトラック */
		
		//disc1
		BASE_URL + "sounds/note_0.mp3",
		BASE_URL + "sounds/note_1.mp3",
		BASE_URL + "sounds/note_2.mp3",
		BASE_URL + "sounds/note_3.mp3",
		BASE_URL + "sounds/note_4.mp3",
		BASE_URL + "sounds/note_5.mp3",
		
		//disc2
		BASE_URL + "sounds/kick_004.mp3",
		BASE_URL + "sounds/kick_009.mp3",
		BASE_URL + "sounds/snare_010.mp3",
		BASE_URL + "sounds/snare_019.mp3",
		BASE_URL + "sounds/voice_hey_omachi.mp3",
		BASE_URL + "sounds/voice_wow_oisii.mp3",
		
		//filling name
		BASE_URL + "sounds/voice_kakuni.mp3",
		BASE_URL + "sounds/voice_chashu.mp3",
		BASE_URL + "sounds/voice_menma.mp3",
		BASE_URL + "sounds/voice_nitamago.mp3",
		BASE_URL + "sounds/voice_mame.mp3",
		BASE_URL + "sounds/voice_naruto.mp3"
	];
	
	/**
	 * ラーメンドンブリの数
	 */
	public const bowlCount:uint = 1;
	
	/**
	 * カーソル用Bitmapの数
	 */
	public const cursorCount:uint = 2;
	
	/**
	 * ラーメン具材の数
	 */
	public const fillingCount:uint = IMAGE_URLS.length - bowlCount - 2;
	
	/**
	 * ベーストラックの数
	 */
	public const trackCount:uint = 2;
	
	/**
	 * サウンドエフェクトの数
	 */
	public const noteCount:uint = fillingCount;
	
	/**
	 * サウンドエフェクトのサブセット数
	 */
	public const noteSubsetCount:uint = 2;
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * 読み込んだ画像データ配列
	 */
	public function bmps(index:uint):Bitmap { return new Bitmap(Bitmap(_bmps[index]).bitmapData.clone(), "auto", true); }
	private var _bmps:Array;
	
	/**
	 * 読み込んだ音データ配列
	 */
	public function sounds(index:uint):Sound { return _sounds[index]; }
	private var _sounds:Array;
	
	/**
	 * ラーメンドンブリのBitmap
	 */
	public function bowlBmps(index:uint):Bitmap { return bmps(index); }
	
	/**
	 * カーソル用のBitmap
	 */
	public function cursorBmps(index:uint):Bitmap { return bmps(index + bowlCount); }
	
	/**
	 * ラーメン具材のBitmap
	 */
	public function fillingBmps(index:uint):Bitmap { return bmps(index + bowlCount + cursorCount); }
	
	/**
	 * ベーストラックのSound
	 */
	public function trackSounds(index:uint):Sound { return sounds(index); }
	
	/**
	 * 合成用のSound
	 */
	public function noteSounds(subset:uint, index:uint):Sound { return sounds(index + trackCount + subset * noteCount); }
	
	/**
	 * 具材名のSound
	 */
	public function fillingSounds(index:uint):Sound { return sounds(index + trackCount + noteSubsetCount * noteCount); }
	
	
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function Resource():void
	{
	}
	
	/**
	 * スレッドの実行関数
	 */
	override protected function run():void 
	{
		//素材の読み込み開始
		_loadImages();
	}
	
	/**
	 * 画像の読み込みを開始する
	 */
	private function _loadImages():void
	{
		Debugger.log("", "loading images...");
		_loadDatas(IMAGE_URLS, "img", _onImagesLoadComplete);
	}
	
	/**
	 * 音の読み込みを開始する
	 */
	private function _loadSounds():void
	{
		Debugger.log("", "loading sounds...");
		_loadDatas(SOUND_URLS, "sound", _onSoundsLoadComplete);
	}
	
	/**
	 * 画像の読み込み完了ハンドラ
	 */
	private function _onImagesLoadComplete(container:Array, type:String):void 
	{
		//読み込んだ画像配列
		_bmps = container;
		
		//音の読み込みを開始する
		_loadSounds();
	}
	
	/**
	 * 音の読み込み完了ハンドラ
	 */
	private function _onSoundsLoadComplete(container:Array, type:String):void 
	{
		//読み込んだ音配列
		_sounds = container;
	}
	
	/**
	 * 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);
	}
	
	/**
	 * ディープコピーを生成する
	 */
	static public function clone(src:*):*
	{
		var ba:ByteArray = new ByteArray();
		ba.writeObject(src);
		ba.position = 0;
		return ba.readObject();
	}
}





//------------------------------------------------------------------------------------------------------------------------------------------------------
/**                                                                                                                                             Debugger
 * デバッグ用クラス
 */
internal class Debugger
{
	//----------------------------------------
	//CONSTANTS
	
	
	
	
	
	//----------------------------------------
	//VARIABLES
	
	/**
	 * ログ出力用TextField
	 */
	static private var _field:TextField;
	
	
	
	
	
	//----------------------------------------
	//METHODS
	
	/**
	 * コンストラクタ
	 */
	public function Debugger():void
	{
	}
	
	/**
	 * 初期化関数
	 */
	static public function initialize(base:Sprite):void
	{
		_field = base.addChild(new TextField()) as TextField;
		_field.width        = 465;
		_field.height       = 465;
		_field.selectable   = false;
		_field.mouseEnabled = false;
	}
	
	/**
	 * ログ出力
	 */
	static 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");
		}
	}
}