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: Ripple Beat - 音に反応する波紋

Ripple Beatの波紋エフェクトのサンプル
http://www.yuichiroharai.com/ripplebeat/

@author Yuichiroh Arai
/**
 * Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/rHGx
 */






package 
{
    import com.adobe.serialization.json.*;    
    import flash.display.*;
    import flash.events.*;
    import flash.filters.DisplacementMapFilter;
    import flash.filters.DisplacementMapFilterMode;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.media.*;
    import flash.net.*;
    import flash.system.ApplicationDomain;
    import flash.system.SecurityDomain;
    import flash.system.LoaderContext;
    import flash.system.System;
    import flash.text.*;
    import flash.text.Font;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;
    import net.hires.debug.Stats;

    public class RippleBeatSample extends Sprite 
    {
        private const FPS:uint = 60; 
        private const STAGE_SIZE:uint = 465; 
        private var TRACK_ID_LIST:Vector.<String> = Vector.<String>(["2739989", "2436514", "3017670", "2077856", "844498", "2815309"]);
        private var _trackIdIndex:int=0;
        
        public function RippleBeatSample():void {
            stage.frameRate = FPS;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            _initBack();
            
            _loadFont(complete);
            
            function complete():void {
                _initMessage();
                _initInfo();
                _initImage();
                _initSelect();
                _initControl();
                stage.addEventListener(Event.RESIZE, _onResize);
                stage.addEventListener(Event.FULLSCREEN, _onResize);
                stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:Event):void{_changeImageRipple()});
                // トラックをロード
                _loadTrack(TRACK_ID_LIST[_trackIdIndex]); 
            }
        }
        
        /**
         * トラックIDを指定してトラックをロード
         * 
         * @param    id        BeatportのトラックID
         */
        private function _loadTrack(id:String):void {
            _textFieldInfo.visible = false;
            _hideImage();
            _hideSelect();
            _hideControl();
            _changeMessage("NOW LODING");
            _showMessage();
            _loadBeatport(id, step1, error);

            function step1():void {
                _makeRipple(step2);
            }
            function step2():void {
                _hideMessage();
                _changeInfo();
                _textFieldInfo.visible = true;
                _changeImage();
                _changeSelect();
                _changeControl();
                _showImage();
                _loadAndPlaySound(step3);
            }
            function step3():void {
                _showSelect();
                _showControl();
            }
            function error():void {
                _hideMessage();
                _changeMessage("LOADING ERROR...");
                _textFieldMessage.visible = true;
                _showSelect();
            }
        }

        /**
         * 前or次のトラックをロード
         * 
         * @param    prev    前のトラックをロードするかどうか。デフォルト(false)は次のトラックをロード。
         */
        private function _loadTrackOrder(prev:Boolean=false):void {
            if (prev) {
                if (--_trackIdIndex < 0) _trackIdIndex = TRACK_ID_LIST.length-1;
                _loadTrack(TRACK_ID_LIST[_trackIdIndex]);
            } else {
                if (++_trackIdIndex > TRACK_ID_LIST.length-1) _trackIdIndex = 0;
                _loadTrack(TRACK_ID_LIST[_trackIdIndex]);
            }
        }
        /**
         * 任意のトラックをロード
         * 
         * @param    index    トラックリストのインデックス
         */
        private function _loadTrackSelect(index:uint):void {
            _trackIdIndex = (index > TRACK_ID_LIST.length-1) ? TRACK_ID_LIST.length : index;
            _loadTrack(TRACK_ID_LIST[_trackIdIndex]);
        }
        
        /**
         * ステージリサイズ時の処理
         */
        private function _onResize(e:Event):void {
            _moveMessage();
            _moveInfo();
            _moveImage();
            _moveSelect();
            _moveControl();
            _resizeBack();
        }
        
        
// ----------------------------------------------------------------------------------------------------
// ジャケット画像、ボーダーの表示
//
        private const IMAGE_SIZE_MAX:uint = 212;
        private var _bmpImage:Bitmap;
        private var _bmpBorder:Bitmap;
        private var _bmpRipple:Bitmap;
        private var _rippleImage:Boolean=false;
        
        /**
         * 表示画像の初期化
         */
        private function _initImage():void {
            _bmpRipple = new Bitmap();
            addChild(_bmpRipple);
            _bmpImage = new Bitmap();
            addChild(_bmpImage);
            _bmpBorder = new Bitmap();
            addChild(_bmpBorder);
        }
        /**
         * 表示画像の変更
         */
        private function _changeImage():void {
            _bmpImage.bitmapData = _track.image;
            _bmpBorder.bitmapData = _track.border;
            _moveImage();
        }
        /**
         * 表示画像の配置を更新
         */
        private function _moveImage():void {
            _bmpImage.x = _bmpRipple.x = int((stage.stageWidth - _bmpImage.width)/2);
            _bmpImage.y = _bmpRipple.y = int((stage.stageHeight - _bmpImage.height)/2);

            _bmpBorder.x = _bmpImage.x - 1;
            _bmpBorder.y = _bmpImage.y - 1;
        }
        /**
         * 表示画像の表示
         */
        private function _showImage():void {
            if (_rippleImage) {
                _bmpRipple.visible = true;
            } else {
                _bmpImage.visible = true;
            }            
            _bmpBorder.visible = true;
        }
        /**
         * 表示画像の非表示
         */
        private function _hideImage():void {
            _bmpImage.visible = false;
            _bmpRipple.visible = false;
            _bmpBorder.visible = false;
        }
        /**
         * ジャケット画像と波紋画像の表示を切り替え
         */
        private function _changeImageRipple():void {
            if (_rippleImage) {
                _rippleImage = false;
                _bmpImage.visible = true;
                _bmpRipple.visible = false;
                _bmpRipple.bitmapData = null;
            } else {
                _rippleImage = true;
                _bmpImage.visible = false;
                _bmpRipple.visible = true;
                _bmpImage.filters = null;
            }
        }


// ----------------------------------------------------------------------------------------------------
// DisplacementMapFilterで使用する波紋エフェクトのBitmapDataを作成
//
        // 波紋の波長
        private const RIPPLE_WAVELENGTH:uint = 4;
        private var _rippleList:Vector.<BitmapData>;
        
        /**
         * 波紋エフェクトのBitmapDataのリストを作成
         * 
         * @param    callback    コールバック関数
         */
        private function _makeRipple(callback:Function):void {
            var num:uint, width:uint, height:uint, widthHarf:uint, heightHarf:uint, lengthList:Vector.<Number>;

            if (_rippleList != null) {
                for (num=0;num<_rippleList.length;num++) {
                    if (_rippleList[num] != null) _rippleList[num].dispose();
                }
            }
            
            _rippleList = new Vector.<BitmapData>(RIPPLE_WAVELENGTH, true);
            
            width = _track.image.width;
            height = _track.image.height;
            widthHarf = uint(width/2);
            heightHarf = uint(height/2);
            
            makeData();

            // 処理に時間がかかるため、1フレームごとに実行。
            num = 0;
            addEventListener(Event.ENTER_FRAME, enterframe);
            function enterframe(e:Event):void {
                if (num < RIPPLE_WAVELENGTH) {
                    // 位相をずらしながら、BitmapDataを生成
                    _rippleList[num] = makeBitmapData(num);
                } else {
                    removeEventListener(Event.ENTER_FRAME, enterframe);
                    if (callback is Function) callback.apply();
                }
                ++num;
            }

            // 事前に各ピクセルの中心からの距離を計算
            function makeData():void {
                var area:uint, d:Number, diagonal:Number, 
                i:uint, j:uint, ii:uint, ij:uint, x:int, y:int, xx:uint, yy:uint, ll:Number;

                area = width * height;
                d = Math.sqrt(widthHarf*widthHarf + heightHarf*heightHarf);
                diagonal = (d == uint(d)) ? d : uint(d+1);
                ++diagonal;
                
                lengthList = new Vector.<Number>(area, true);
                for (j=0;j<height;j++) {
                    ij = j * height;
                    y = j - heightHarf;
                    yy = y*y;
                    for (i=0;i<width;i++) {
                        ii = ij + i;
                        x = i - widthHarf;
                        xx = x*x;
                        ll = xx + yy;
                        lengthList[ii] = Math.sqrt(ll);
                    }
                }
            }
            // 位相を指定してBitmapDataを作成
            function makeBitmapData(phase:uint=0):BitmapData {
                var i:uint, j:uint, ij:uint, ii:uint, x:int, y:int, len:Number, unit:Number, amp:Number, sx:Number, sy:Number, c:uint, bitmapData:BitmapData;
                
                unit = 2*Math.PI/RIPPLE_WAVELENGTH;
                bitmapData = new BitmapData(width, height, false, 0x008080);
                bitmapData.lock();
                for (j=0;j<height;j++) {
                    ij = j * height;
                    y = j - heightHarf;
                    for (i=0;i<width;i++) {
                        ii = ij + i;
                        x = i - widthHarf;
                        len = lengthList[ii];
                        amp = Math.round(Math.sin(((len-phase) % RIPPLE_WAVELENGTH)*unit) * 1000000000000000) / 1000000000000000;
                        sx = x*0x80/len*amp;
                        sy = y*0x80/len*amp;
                        c = (0x80 - int(sx + 0.5))*0x100 + 0x80 - int(sy + 0.5);
                        bitmapData.setPixel(i, j, c);
                    }
                }
                bitmapData.unlock();
                return bitmapData;
            }
        }


// ----------------------------------------------------------------------------------------------------
// サウンドのロード、再生、停止など
//
        private var _sound:Vector.<Sound>;
        private var _soundChannel:SoundChannel;
        private var _soundPosition:Number=0;
        
        /**
         * サウンドをロードしてから再生
         * 
         * @param    callback    サウンドのオープン(≒再生)時のコールバック
         */
        private function _loadAndPlaySound(callback:Function):void {
            if (_soundChannel != null) {
                _soundChannel.stop();
            }
            if (_sound != null && _sound[0] != null) {
                _sound[0].removeEventListener(Event.COMPLETE, _onCompleteSound);
                try { _sound[0].close(); } catch (e:Error) {}
                delete _sound[0];
                _sound = null;
                removeEventListener(Event.ENTER_FRAME, _onPlayingSound);
            }

            _resetSpectrum();
            _soundPosition = 0;
            _sound = Vector.<Sound>([new Sound()]);
            _sound[0].addEventListener(Event.OPEN, open);
            _sound[0].load(new URLRequest(_track.urlSound), new SoundLoaderContext(1000, true));

            function open(e:Event):void {
                _soundChannel = _sound[0].play(_soundPosition);
                _soundChannel.addEventListener(Event.SOUND_COMPLETE, _onCompleteSound);
                addEventListener(Event.ENTER_FRAME, _onPlayingSound);
                _sound[0].removeEventListener(Event.OPEN, open);
                if (callback is Function) callback.apply();
            }
        }
        /**
         * サウンドを再生
         */
        private function _playSound():void {
            if (_sound == null || _sound[0] == null) return;
            _soundChannel = _sound[0].play(_soundPosition);
            _soundChannel.addEventListener(Event.SOUND_COMPLETE, _onCompleteSound);
            addEventListener(Event.ENTER_FRAME, _onPlayingSound);
        }
        /**
         * サウンドをポーズ
         */
        private function _pauseSound():void {
            if (_soundChannel == null || _sound == null || _sound[0] == null) return;
            _soundPosition = _soundChannel.position;
            _soundChannel.stop();
            _soundChannel.removeEventListener(Event.SOUND_COMPLETE, _onCompleteSound);
            _soundChannel = null;
            removeEventListener(Event.ENTER_FRAME, _onPlayingSound);
            _bmpImage.filters = null;
        }
        /**
         * サウンドの再生完了時の処理。次のトラックを自動再生。
         */
        private function _onCompleteSound(e:Event):void {
            if (_soundChannel == null || _sound == null || _sound[0] == null) return;
            _switchControl();
            _soundPosition = 0;
            _soundChannel.removeEventListener(Event.SOUND_COMPLETE, _onCompleteSound);
            _soundChannel = null;
            removeEventListener(Event.ENTER_FRAME, _onPlayingSound);
            _bmpImage.filters = null;
            
            _loadTrackOrder();
        }


// ----------------------------------------------------------------------------------------------------
// スペクトラム
//        
        private const POINT_0:Point = new Point(0, 0);
        private const SPECTRUM_MAX:Number = 1.4142136;
        private var _spectrumCurrent:Number;
        private var _spectrumTarget:Number;
        private var _spectrumMax:Number;
        private var _phaseCurrent:Number;
        
        /**
         * スペクトラムのリセット
         */
        private function _resetSpectrum():void {
            _spectrumCurrent = _spectrumTarget = _spectrumMax = _phaseCurrent = 0;
        }

        /**
         * サウンドに合わせて波紋エフェクトを適用
         */
        private function _onPlayingSound(e:Event):void {
            var spectrum:Number, phase:Number, scale:Number, dmf:DisplacementMapFilter;
            
            // 周波数帯の低い方の1/4を使用。0-1の範囲に変換。
            spectrum = _analyzeSound(2)[0]/SPECTRUM_MAX;
            // トラック中の最大値を取得し、その中での現在値を0-1の範囲に変換。
            if (_spectrumMax < spectrum) _spectrumMax = spectrum;
            if (_spectrumMax > 0) spectrum = spectrum/_spectrumMax;
            
            // スペクトラムの値が0%
            if (spectrum == 0) {
                _spectrumTarget = spectrum;
                phase = 0;
            // スペクトラムの値が80%より上
            } else if (spectrum > 0.8) {
                _spectrumTarget = spectrum;
                phase = (0.5+spectrum)*30/FPS;
            // スペクトラムの値が80%以下、30%より上
            } else if (spectrum > 0.3) {
                _spectrumTarget = spectrum/3;
                phase = (0.2+spectrum)*30/FPS;
            // スペクトラムの値が30%以下、0%より上
            } else {
                _spectrumTarget = 0.1;
                phase = 0.5*30/FPS;
            }
            // 前の値から次の値へ75%ずつ変位
            _spectrumCurrent += (_spectrumTarget - _spectrumCurrent)*0.75;

            // 位相の現在値を計算
            if ((_phaseCurrent += phase) >= 4) _phaseCurrent -= 4;
        
            scale = _spectrumCurrent*12;
            if (_rippleImage) {
                _bmpRipple.bitmapData = _rippleList[uint(_phaseCurrent)];
            } else {
                dmf = new DisplacementMapFilter(_rippleList[uint(_phaseCurrent)], POINT_0, 2, 4, scale, scale, DisplacementMapFilterMode.CLAMP, 0, 0);
                _bmpImage.filters = [dmf];
            }
        }


// ----------------------------------------------------------------------------------------------------
// サウンド解析
//    
        private const STRETCH_FACTOR_LIST:Vector.<Number> = Vector.<Number>([257, 129, 65, 33, 17, 9, 5, 3, 2, 1]);
        private const SPECTRUM_LENGTH_LIST:Vector.<Number> = Vector.<Number>([1, 2, 4, 8, 16, 32, 64, 128, 256, 256]);
        
        // 全周波数帯を2^divに分割し、左右のチャンネルの大きい方のスペクトラムデータを取得します。
        private function _analyzeSound(div:int=0):Vector.<Number> {
            var i:uint, len:uint, size:uint, position:uint, l:Number, r:Number, 
            stretchFactor:uint, bytes:ByteArray, vector:Vector.<Number>;
            
            if (div > 9) div = 9;
            stretchFactor = STRETCH_FACTOR_LIST[div];
            len = SPECTRUM_LENGTH_LIST[div];
            size = 1024/len;

            bytes = new ByteArray();
            SoundMixer.computeSpectrum(bytes, true, stretchFactor);
            
            vector = new Vector.<Number>(len, true);
            for (i=0; i<len; i++) {
                position = i*size;
                bytes.position = position;
                l = bytes.readFloat();
                bytes.position = position + 1024;
                r = bytes.readFloat();
                vector[i] = (l > r) ? l : r;
            }
            return vector;
        }





// ----------------------------------------------------------------------------------------------------
// Beatport API
//
        private const BP_RGB:uint = 0xa0d626; // Beatportカラー
        //private const BP_TRACK_JSON = "http://api.beatport.com/catalog/tracks?format=json&v=1.0";
        private const BP_TRACK_URL_XML:String = "http://api.beatport.com/catalog/tracks?format=xml&v=1.0";
        private const BP_TRACK_IMAGE_URL:String = "http://geo-media.beatport.com/image_size/212x212/"; // 212×212のジャケット画像取用
        private const BP_TRACK_BORDER_ARGB:uint = 0xff808080;

        private var _urlLoader:URLLoader;
        private var _loader:Loader;
        private var _track:Track;

        /**
         * Beatport APIを通してトラック情報とジャケット画像をロードします。
         * 
         * @param        id                    BeatportのトラックID
         * @param        callbackComplete    ロード成功時のコールバック関数
         * @param        callBackError        エラー時のコールバック関数
         */
        private function _loadBeatport(id:String, callbackComplete:Function, callbackError:Function):void {
            if (_track != null) _track.destroy();
            _track = new Track();
            
            _urlLoader = new URLLoader();
            _urlLoader.addEventListener(Event.COMPLETE, onSuccessInfo);
            _urlLoader.addEventListener(IOErrorEvent.IO_ERROR, onError);
            _urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
            //_urlLoader.load(new URLRequest(BP_TRACK_JSON + "&id=" + id));
            _urlLoader.load(new URLRequest(BP_TRACK_URL_XML + "&id=" + id));

            // トラック情報のロード成功時
            function onSuccessInfo(e:Event):void {
                // var resultList:Array;
                var resultList:XMLList;
                
                _urlLoader.removeEventListener(Event.COMPLETE, onSuccessInfo);
                _urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
                _urlLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);
                _urlLoader = null;
                
                
                /*resultList = JSON.decode(e.target.data).results;
                if (resultList.length > 0) {
                    _decodeBeatportJson(resultList[0]);
                } else {
                    if (callbackError is Function) callbackError.apply();
                    return;
                }*/
                resultList = new XMLList(e.target.data).result;

                if (resultList.length() > 0) {
                    _parseBeatportXml(resultList[0]);
                } else {
                    if (callbackError is Function) callbackError.apply();
                    return;
                }
                
                // 続いてジャケット画像のロード
                _loader = new Loader();
                _loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSuccessImage);
                _loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onError);
                _loader.load(new URLRequest(_track.urlImage), new LoaderContext(true));
            }
            
            // ジャケット画像のロード成功時
            function onSuccessImage(e:Event):void {
                var bmpdTemp:BitmapData;
                
                // BitmapDataを抜き出し
                _track.image = Bitmap(e.target.content).bitmapData;
                
                // 画像が小さい場合は2倍に拡大
                if (_track.small) {
                    bmpdTemp = new BitmapData(_track.image.width*2, _track.image.height*2, false, 0);
                    bmpdTemp.draw(_track.image, new Matrix(2, 0, 0, 2, 0, 0), null, null, null, true);
                    _track.image.dispose();
                    _track.image = bmpdTemp;
                }
                
                // ボーダーの作成
                _track.border = makeBorderBitmapData(_track.image.width+2, _track.image.height+2, BP_TRACK_BORDER_ARGB)
                
                _loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onSuccessImage);
                _loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, onError);
                _loader = null;
                if (callbackComplete is Function) callbackComplete.apply();
            }
            
            // ロード失敗時のリスナー
            function onError(e:ErrorEvent):void {
                if (callbackError is Function) callbackError.apply();
            }
        }
        
        /**
         * Beatport APIで取得したJSONデータをデコードします。
         * 
         * @param    json    トラック情報の入ったJSONオブジェクト
         */
        /*private function _decodeBeatportJson(jsonObject:Object):void {
            var mixName:String, artist:String, artistList:Array, performer:Object;

            _track.urlSound = jsonObject.sampleUrl;

            // 画像のURL(大きい画像)
            if (jsonObject.images.large != undefined) {
                _track.urlImage = BP_TRACK_IMAGE_URL + jsonObject.images.large.url.match(/([^\/]*)?$/)[0]; // 212×212
            // 画像のURL(小さい画像)
            } else {
                _track.small = true;
                _track.urlImage = jsonObject.images.medium.url; // 60×60
            }

            mixName = (jsonObject.mixName == "" || jsonObject.mixName == "Original Mix") ? "" : " (" + jsonObject.mixName + ")";
            artistList = [];
            for each (performer in jsonObject.artists) {
                if (performer.type == "Artist") {
                    artistList.push(performer.name);
                }
            }
            artist = artistList.join(", ");
            _track.info = String(jsonObject.name).toUpperCase() + String(mixName).toUpperCase() + " / " + String(artist).toUpperCase() + " / " + String(jsonObject.label.name).toUpperCase();
        }*/
        
        /**
         * Beatport APIで取得したXMLデータを解析します。
         * 
         * @param    xml    トラック情報の入ったXMLオブジェクト
         */
        private function _parseBeatportXml(xml:XML):void {
            var mixName:String, artist:String, artistList:Array, performer:Object;

            _track.urlSound = xml.document.track.@url;
            // 画像のURL(大きい画像)
            if (xml.document.track.image.(@ref=='release'&&@width=='500') != undefined) {
                _track.urlImage = BP_TRACK_IMAGE_URL + xml.document.track.image.(@ref=='release'&&@width=='500').@url.match(/([^\/]*)?$/)[0]; // 212×212
            // 画像のURL(小さい画像)
            } else if (xml.document.track.image.(@ref=='release'&&@width=='60') != undefined) {
                _track.small = true;
                _track.urlImage = xml.document.track.image.(@ref=='release'&&@width=='60').@url; // 60×60 リリース画像
            } else if (xml.document.track.image.(@ref=='label'&&@width=='60') != undefined) {
                _track.small = true;
                _track.urlImage = xml.document.track.image.(@ref=='label'&&@width=='60').@url; // 60×60 レーベル画像
            } else {
                _track.small = true;
                _track.urlImage = xml.document.track.image.(@ref=='defalut'&&@width=='60').@url; // 60×60 デフォルト画像
            }

            mixName = (xml.document.track.mixName == "" || xml.document.track.mixName == "Original Mix") ? "" : " (" + xml.document.track.mixName + ")";
            artistList = [];
            for each (performer in xml.document.track.performer) {
                if (performer.@ref == "Artist") {
                    artistList.push(performer.name);
                }
            }
            artist = artistList.join(", ");
            _track.info = String(xml.document.track.name).toUpperCase() + String(mixName).toUpperCase() + " / " + String(artist).toUpperCase() + " / " + String(xml.document.track.label.name).toUpperCase();
        }


// ----------------------------------------------------------------------------------------------------
// トラック情報(タイトル / アーティスト / レーベル)の表示
//
        private var _textFieldInfo:TextField;        

        /**
         * トラック情報を初期化
         */
        private function _initInfo():void {
            _textFieldInfo = new TextField();
            _textFieldInfo.defaultTextFormat = new TextFormat(FONT_NAME_KROEGER_0655, 8, 0xffffff);
            _textFieldInfo.embedFonts = true;
            _textFieldInfo.autoSize = TextFieldAutoSize.LEFT;
            _textFieldInfo.multiline = false;
            _textFieldInfo.selectable = false;
            addChild(_textFieldInfo);
        }
        /**
         * トラック情報のテキストを変更
         */
        private function _changeInfo():void {
            _textFieldInfo.text = _track.info;
            _moveInfo();
        }
        /**
         * トラック情報の配置を更新
         */
        private function _moveInfo():void {
            _textFieldInfo.x = int((stage.stageWidth - _textFieldInfo.width)/2);
            _textFieldInfo.y = int((stage.stageHeight + IMAGE_SIZE_MAX)/2 + 50);
        }


// ----------------------------------------------------------------------------------------------------
// トラックを再生/停止するコントローラ 
//        
        private const CONTROL_SIZE:uint = 50;
        private var _bmpControlPlay:Bitmap;
        private var _bmpControlPause:Bitmap;
        private var _buttonControl:SimpleButton;
        private var _buttonPlay:Boolean;
        
        /**
         * コントローラを初期化
         */
        private function _initControl():void {
            var s:Shape, g:Graphics, bmp:Bitmap, bmpd:BitmapData;
            
            s = new Shape();
            g = s.graphics;
            
            // 再生
            g.clear();
            g.lineStyle(1, 0, 0.25);
            g.beginFill(0, 0.75);
            g.drawCircle(25, 25, 20);
            g.endFill();
            g.lineStyle(0, 0, 0);
            g.beginFill(BP_RGB, 0.9);
            g.moveTo(19, 15);
            g.lineTo(34, 25);
            g.lineTo(19, 35);
            g.lineTo(19, 15);
            g.endFill();
            bmpd = new BitmapData(CONTROL_SIZE, CONTROL_SIZE, true, 0);
            bmpd.draw(s);
            _bmpControlPlay = new Bitmap(bmpd);
            _bmpControlPlay.visible = false;
            addChild(_bmpControlPlay);
            
            // 停止
            g.clear();
            g.lineStyle(1, 0, 0.25);
            g.beginFill(0, 0.75);
            g.drawCircle(25, 25, 20);
            g.endFill();
            g.lineStyle(0, 0, 0);
            g.beginFill(BP_RGB, 0.9);
            g.drawRect(18, 17, 4, 16);
            g.drawRect(28, 17, 4, 16);
            g.endFill();
            bmpd = new BitmapData(CONTROL_SIZE, CONTROL_SIZE, true, 0);
            bmpd.draw(s);
            _bmpControlPause = new Bitmap(bmpd);
            _bmpControlPause.visible = false;
            addChild(_bmpControlPause);
            
            // 透明ボタン
            bmp = new Bitmap();
            _buttonControl = new SimpleButton(bmp, bmp, bmp, bmp);
            _buttonControl.addEventListener(MouseEvent.ROLL_OVER, onRollOver);
            _buttonControl.addEventListener(MouseEvent.ROLL_OUT, onRollOut);
            _buttonControl.addEventListener(MouseEvent.CLICK, onClick);
            
            addChild(_buttonControl);
            
            _moveControl();
            
            function onClick(e:MouseEvent):void {
                if (_buttonPlay) {
                    _pauseSound();
                } else {
                    _playSound();    
                }
                _switchControl();
            }
            
            function onRollOver(e:MouseEvent):void {
                if (_buttonPlay) {
                    _bmpControlPause.visible = true;
                } else {
                    _bmpControlPlay.visible = true;
                }
            }
            function onRollOut(e:MouseEvent):void {
                if (_buttonPlay) {
                    _bmpControlPause.visible = false;
                } else {
                    _bmpControlPlay.visible = false;
                }
            }
        }
        /**
         * コントローラの透明ボタンのサイズをトラック画像に合わせて更新
         */
        private function _changeControl():void {
            var bmp:Bitmap;
            bmp = Bitmap(_buttonControl.hitTestState);
            
            if (bmp.bitmapData != null) bmp.bitmapData.dispose();
            bmp.bitmapData = new BitmapData(_track.image.width, _track.image.height, true, 0);
            _moveControl();
            
            _buttonPlay = true;
        }
        /**
         * コントローラの再生/停止を切り替え
         */
        private function _switchControl():void {
            if (_bmpControlPlay.visible || _bmpControlPause.visible) {
                if (_buttonPlay) {
                    _bmpControlPlay.visible = true;
                    _bmpControlPause.visible = false;
                    _buttonPlay = false;
                } else {
                    _bmpControlPlay.visible = false;
                    _bmpControlPause.visible = true;
                    _buttonPlay = true;
                }
            } else {
                _buttonPlay = !_buttonPlay;
            }
        }
        /**
         * コントローラの配置を更新
         */
        private function _moveControl():void {
            _bmpControlPlay.x = int((stage.stageWidth - CONTROL_SIZE)/2);
            _bmpControlPlay.y = int((stage.stageHeight - CONTROL_SIZE)/2);
            _bmpControlPause.x = int((stage.stageWidth - CONTROL_SIZE)/2);
            _bmpControlPause.y = int((stage.stageHeight - CONTROL_SIZE)/2);
            _buttonControl.x = int((stage.stageWidth - _buttonControl.width)/2);
            _buttonControl.y = int((stage.stageHeight - _buttonControl.height)/2);
        }
        /**
         * コントローラを表示
         */
        private function _showControl():void {
            _buttonControl.visible = true;
            _buttonControl.mouseEnabled = true;
        }
        /**
         * コントローラを非表示
         */
        private function _hideControl():void {
            _buttonControl.visible = false;
            _buttonControl.mouseEnabled = false;
        }

        
// ----------------------------------------------------------------------------------------------------
// トラックの選択と現在位置表示のナビゲーション
//
        private const SELECT_SIZE:uint = 12;
        private var _spSelect:Sprite;
        private var _bmpdSelect:BitmapData;
        private var _buttonSelectList:Vector.<SimpleButton>;
        
        /**
         * 選択ナビゲーションを初期化
         */
        private function _initSelect():void {
            var i:uint, len:uint, s:Shape, g:Graphics, bmp:Bitmap, button:SimpleButton;
            
            _spSelect = new Sprite();
            addChild(_spSelect);
            _buttonSelectList = new Vector.<SimpleButton>();
            s = new Shape();
            g = s.graphics;
            
            // ○
            g.beginFill(0x404040, 1);
            g.drawCircle(6, 6, 6);
            g.endFill();
            _bmpdSelect = new BitmapData(SELECT_SIZE, SELECT_SIZE, true, 0);
            _bmpdSelect.draw(s);
            
            len = TRACK_ID_LIST.length;
            for (i=0;i<len;i++) {
                bmp = new Bitmap(_bmpdSelect);
                button = new SimpleButton(bmp, bmp, bmp, bmp);
                button.x = i*20;
                _spSelect.addChild(button);
                _buttonSelectList[i] = button;
                button.addEventListener(MouseEvent.CLICK, _onClickSelect);
                button.addEventListener(MouseEvent.ROLL_OVER, _onRollOverSelect);
                button.addEventListener(MouseEvent.ROLL_OUT, _onRollOutSelect);
            }
            _changeSelect();
            _moveSelect();
        }
            private function _onClickSelect(e:MouseEvent):void {
                _loadTrackSelect(_buttonSelectList.indexOf(e.target));
            }
            private function _onRollOverSelect(e:MouseEvent):void {
                _buttonSelectList[_buttonSelectList.indexOf(e.target)].transform.colorTransform = new ColorTransform(0, 0, 0, 1, 0x80, 0x80, 0x80, 0);
            }
            private function _onRollOutSelect(e:MouseEvent):void {
                _buttonSelectList[_buttonSelectList.indexOf(e.target)].transform.colorTransform = new ColorTransform();
            }
        /**
         * 選択ナビゲーションの要素を追加
         */
        private function _addSelect():void {
            var i:uint, bmp:Bitmap, bmpd:BitmapData, button:SimpleButton;
            
            i = _buttonSelectList.length;
            bmp = new Bitmap(_bmpdSelect);
            button = new SimpleButton(bmp, bmp, bmp, bmp);
            button.x = i*20;
            _spSelect.addChild(button);
            _buttonSelectList.push(button);
            button.addEventListener(MouseEvent.CLICK, _onClickSelect);
            button.addEventListener(MouseEvent.ROLL_OVER, _onRollOverSelect);
            button.addEventListener(MouseEvent.ROLL_OUT, _onRollOutSelect);
            _moveSelect();
        }
        
        /**
         * 選択ナビゲーションの現在位置を変更
         */
        private function _changeSelect():void {
            var i:uint, len:uint;
            
            len = _buttonSelectList.length;
            for (i=0;i<len;i++) {
                if (i == _trackIdIndex) {
                    _buttonSelectList[i].mouseEnabled = false;
                    _buttonSelectList[i].transform.colorTransform = new ColorTransform(0, 0, 0, 1, ((BP_RGB >> 16) & 255)*0.8, ((BP_RGB >> 8) & 255)*0.8, (BP_RGB & 255)*0.8, 0);
                } else {
                    _buttonSelectList[i].mouseEnabled = true;
                    _buttonSelectList[i].transform.colorTransform = new ColorTransform()
                }
            }
        }
        /**
         * 選択ナビゲーションの配置を更新
         */
        private function _moveSelect():void {
            _spSelect.x = int((stage.stageWidth - _spSelect.width)/2);
            _spSelect.y = int((stage.stageHeight + IMAGE_SIZE_MAX)/2 + 15);
        }
        /**
         * 選択ナビゲーションを表示
         */
        private function _showSelect():void {
            _spSelect.visible = true;
            _spSelect.mouseChildren = true;
        }
        /**
         * 選択ナビゲーションを表示
         */
        private function _hideSelect():void {
            _spSelect.visible = false;
            _spSelect.mouseChildren = false;
        }


// ----------------------------------------------------------------------------------------------------
// 画面の中心にメッセージを表示
//
        private var _textFieldMessage:TextField;
        private var _messageTime:uint;
        /**
         * メッセージ表示を初期化
         */
        private function _initMessage():void {
            _textFieldMessage = new TextField;
            _textFieldMessage.defaultTextFormat = new TextFormat(FONT_NAME_KROEGER_0655, 8, 0xffffff);
            _textFieldMessage.embedFonts = true;
            _textFieldMessage.autoSize = TextFieldAutoSize.LEFT;
            _textFieldMessage.multiline = false;
            _textFieldMessage.selectable = false;
            addChild(_textFieldMessage);
        }
        /**
         * メッセージ表示のテキストを変更
         * 
         * @param    text    表示メッセージ
         */
        private function _changeMessage(text:String):void {
            _textFieldMessage.text = text;
            _moveMessage();
        }
        /**
         * メッセージ表示の配置を更新
         */
        private function _moveMessage():void {
            _textFieldMessage.x = int((stage.stageWidth - _textFieldMessage.width)/2);
            _textFieldMessage.y = int((stage.stageHeight - _textFieldMessage.height)/2);
        }
        /**
         * メッセージ表示の点滅を開始
         */
        private function _showMessage():void {
            addEventListener(Event.ENTER_FRAME, _blinkMessage);
            _textFieldMessage.visible = true;
        }
        /**
         * メッセージ表示の点滅を終了
         */
        private function _hideMessage():void {
            removeEventListener(Event.ENTER_FRAME, _blinkMessage);
            _textFieldMessage.visible = false;
        }
        /**
         * メッセージ表示を一定間隔で点滅
         * 
         * @param    e    イベント
         */
        private function _blinkMessage(e:Event):void {
            var timer:uint;
            
            if ((timer = getTimer()) - _messageTime > 25) {
                _messageTime = timer;
                _textFieldMessage.visible = !_textFieldMessage.visible;
            }
        }
        

// ----------------------------------------------------------------------------------------------------
// 背景
//
        private var _bmpBack:Bitmap;
        /**
         * 背景用のBitmapを作成
         */
        private function _initBack():void {
            _bmpBack = new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false, 0));
            addChildAt(_bmpBack, 0);
        }
        /**
         * 背景用のBitmapをリサイズ
         */
        private function _resizeBack():void {
            _bmpBack.bitmapData.dispose();
            _bmpBack.bitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, false, 0);
        }
        
        
// ----------------------------------------------------------------------------------------------------
// フォント内蔵のSWFをロード、登録
//            
        private const FONT_CLASS_KROEGER_0655:String = "Kroeger0655";
        private const FONT_NAME_KROEGER_0655:String = "kroeger 06_55";
        private const FONT_SWF_URL:String = "http://global.yuichiroharai.com/swf/FontKroeger.swf";
        
        /**
         * フォント内蔵のSWFをロード
         * 
         * @param    callback    コールバック関数
         */
        private function _loadFont(callBack:Function):void {
            var loader:Loader;
            
            loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete);
            loader.load(new URLRequest(FONT_SWF_URL), new LoaderContext(true, ApplicationDomain.currentDomain, SecurityDomain.currentDomain));
            
            function complete(e:Event):void {
                try {
                    Font.registerFont(loader.contentLoaderInfo.applicationDomain.getDefinition(FONT_CLASS_KROEGER_0655) as Class);
                } catch (e:Error) { return; }
                
                loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, complete);
                loader = null;
                if (callBack is Function) callBack.apply();
            }
        }
        
        
// ----------------------------------------------------------------------------------------------------
// スタッツ
//
        /**
         * スタッツを表示
         */
        private function _initStats():void {
            var stats:Stats;
            stats = new Stats();
            addChild(stats);
        }


// ----------------------------------------------------------------------------------------------------
// ユーティリティメソッド
//
        /**
         * ボーダーBitmapDataを作成
         * 
         * @param    width        横幅
         * @param    height        高さ
         * @param    argb        ARGBカラー
         * @return                ボーダーBitmapData
         */
        public function makeBorderBitmapData(width:uint, height:uint, argb:uint):BitmapData {
            var bmpd:BitmapData, i:uint;
            
            bmpd = new BitmapData(width, height, true, 0);
            bmpd.lock();
            for (i=0;i<height;i++) {
                bmpd.setPixel32(0, i, argb);
                bmpd.setPixel32(width-1, i, argb);
            }
            for (i=1;i<width-1;i++) {
                bmpd.setPixel32(i, 0, argb);
                bmpd.setPixel32(i, height-1, argb);
            }
            bmpd.unlock();
            return bmpd;
        }


    }
}



// ----------------------------------------------------------------------------------------------------
// トラックデータを格納するクラス
//
    import flash.display.BitmapData;
    import flash.media.Sound;
    class Track {    
        public var info:String;                    // トラック名 (ミックス名) / アーティスト / レーベル
        public var urlSound:String;                // サウンドのURL
        public var urlImage:String;                // ジャケット画像のURL
        public var image:BitmapData;            // ジャケット画像
        public var border:BitmapData;            // ボーダー画像
        public var small:Boolean=false;            // 小さい画像のトラックかどうか
        
        public function destroy():void {
            info = null
            urlSound = null;
            urlImage = null;
            if (image != null) image.dispose();
            image = null;
            if (border != null) border.dispose();
            border = null;
            small = false;
        }
    }