// 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"); } } } forked from: Ramen! Beats!! Box!!! forked from: colin challenge for amateurs