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: Instrument

webカムとマイク入力を利用したサンプラー&3トラックシーケンサーです。

録画/録音したデータをビートの効いたトラックに仕上げてくれます。

口笛、手拍子、ボイスパーカーション等々、打楽器系(?)の音声がオススメ。

サムネイルのコンテクストメニューからエフェクトを選べます。


ビジュアライザーはあんまり注視すると目が痛くなるかも。
/**
 * Copyright kappa-lab ( http://wonderfl.net/user/kappa-lab )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/aO8a
 */

package 
{
    import caurina.transitions.Tweener;
    import caurina.transitions.properties.ColorShortcuts;
    
    import com.bit101.components.*;
    
    import flash.display.*;
    import flash.events.*;
    import flash.geom.ColorTransform;
    import flash.media.*;
    import flash.ui.ContextMenu;
    import flash.ui.ContextMenuItem;
    import flash.utils.ByteArray;

    /**
     * ...
     * @author gamiken@kappa-lab
     */
    [SWF(backgroundColor = "0x0",width="456", height="456")]
    public class Main4 extends Sprite
    {
        public static const NUM_RECORDER:int=3;
        private var _mic:Microphone;
        private var _cam:Camera;
        private var _vid:Video;
        private var _g:Graphics;
        private var _recorders:Vector.<RecUnit>;
        private var _numRecorded:int;
        private var _mainCol:Sprite;
        private var _resetBtn:PushButton;
        public function Main4() 
        {
            ColorShortcuts.init();
            if (stage) init() else addEventListener(Event.ADDED_TO_STAGE,init);
        }
        
        private function init(e:Event=null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            contextMenu = new ContextMenu();
            contextMenu.hideBuiltInItems();

            stage.quality = StageQuality.LOW;
            stage.scaleMode = "noScale";
            stage.align = "topLeft";

            graphics.beginFill(0);
            graphics.drawRect(0,0,stage.stageWidth,stage.stageHeight);
            _mainCol = addChild(new Sprite()) as Sprite;
            _resetBtn = new PushButton(_mainCol, 10, 425,"RESET",reset);
            _resetBtn.visible = false;
            
            logger = new Text(this, 10,425,"preparing...");
            logger.height = 20;
            logger.mouseChildren = false;
            _mic = Microphone.getMicrophone();
            _mic.rate = 44;
            _mic.setSilenceLevel(0);
            _cam = Camera.getCamera();
            _cam.setMode(160,120,_cam.fps);
            _vid = new Video(_cam.width, _cam.height);
            _vid.attachCamera(_cam);
            initRecUnits();
            _cam.addEventListener(ActivityEvent.ACTIVITY, onActivity);
        }
        
        private function onActivity(e:ActivityEvent):void 
        {
            log(e);
            _cam.removeEventListener(ActivityEvent.ACTIVITY, onActivity);
            Tweener.addCaller(this, { time:.3, count:1, onComplete:startRecordUnits } ); 
        }
        
        private function initRecUnits():void
        {
            _numRecorded = 0;
            _recorders = new Vector.<RecUnit>();
            var scale:Number = 1
            for (var i:int = 0; i < NUM_RECORDER; i++) 
            {
                var ru:RecUnit = _mainCol.addChild( new RecUnit(_mic, _vid)) as RecUnit;
                _recorders.push(ru);
                ru.mouseEnabled = false;
                ru.scaleX = ru.scaleY = scale;
                ru.y = 10 + (_vid.height * scale + 10) * (i);
                ru.x = 10;
                ru.addEventListener(MouseEvent.MOUSE_UP,onRecUnitMouseUp);
            }
        }
        
        private function onRecUnitMouseUp(e:MouseEvent):void
        {
            var ru:RecUnit = e.target as RecUnit;
            var x:Number = ru.x;
             var y:Number = ru.y;
            var scale:Number = ru.scaleX
            var scopeScaleX:Number = ru.scope.scaleX
            ru.scaleX = ru.scaleY = .5
            ru.x = master.vj.x;
            ru.y = stage.stageHeight-100;    
            ru.scope.scaleX = 0.003;
            ru.addEventListener("recordComplete", onRecorded);
            stage.mouseChildren = false;
            function onRecorded(e:Event):void 
            {
                stage.mouseChildren = true;
                ru.scaleX = ru.scaleY = scale;
                ru.scope.scaleX = scopeScaleX;
                ru.removeEventListener("recordComplete", onRecorded);
                ru.x = x;
                ru.y = y;
            }
        }
            
        
        private function startRecordUnits():void
        {
            var ru:RecUnit = _recorders[_numRecorded];
            ru.mouseEnabled =  false;
            ru.addEventListener("recordComplete", onRecorded);
            ru.record();
            logger.text += "/"+NUM_RECORDER.toString()
        }
        
        private function onRecorded(e:Event):void 
        {
            e.target.removeEventListener("recordComplete", onRecorded);
            if (++_numRecorded < NUM_RECORDER) {
                startRecordUnits();
            }else {
                completeAllRecoreders();
            }
        }
        
        private function reset(e:MouseEvent=null):void
        {
            log("RESET");
            stage.removeEventListener(Event.RESIZE,onResize);

            master.stop();
            master.vj.stop();

            stage.mouseChildren = false;
            _resetBtn.visible=false;
            Tweener.addTween(_mainCol,{time:.3,alpha:0,transition:"linear",onComplete:startReRecord});
            function startReRecord():void
            {
                logger.alpha = 1;
                logger.x = 10;
                logger.transform.colorTransform = new ColorTransform();

                master.vj.visible = false;
                _mainCol.alpha= 1;
                stage.mouseChildren = true;
                
                var scale:Number = 1
                for (var i:int = 0; i < NUM_RECORDER; i++) 
                {
                    var ru:RecUnit = _recorders[i];
                    ru.scaleX = ru.scaleY = scale;
                    ru.y = 10 + (_vid.height * scale + 10) * (i);
                    ru.x = 10;
                    ru.scope.scaleX = 0.002;
                    ru.mouseEnabled = false;
                    ru.reset();
                }
                _numRecorded = 0;
                startRecordUnits();
            }
        }
        private function completeAllRecoreders():void
        {
            if(!master){
                master = new Master(_recorders,new VJ(_recorders))
                _mainCol.addChild(master.vj);
                master.vj.x = int((stage.stageWidth - master.vj.width) * .5);
                master.vj.y = 30;
                _resetBtn.x = master.vj.x;
            }
            
            var offsetY:Number = stage.stageHeight-160;
            var offsetX:Number = master.vj.x;

            onResize()
            
            master.vj.visible = true;
            master.play();
            master.vj.play();
            
            _resetBtn.visible = true;
            logger.x = _resetBtn.x+_resetBtn.width+10;
            logger.transform.colorTransform = new ColorTransform(-.5,-.5,-.5,1,180,180,180);
            log("PLAY");
            stage.addEventListener(Event.RESIZE,onResize);
        }
        
        private function onResize(e:Event=null):void
        {
            var offsetY:Number = stage.stageHeight-160;
            var offsetX:Number = master.vj.x;
            
            for each(var ru:RecUnit in _recorders) 
            {
                ru.transform.colorTransform = new ColorTransform(.5,.5,.5)
                ru.scaleX = ru.scaleY = .4;
                ru.scope.scaleX = 0.0007;
                ru.mouseEnabled = true;
                ru.y = offsetY
                ru.x = offsetX
                offsetX = ru.x + ru.width + 10;
            }
            _resetBtn.x = master.vj.x;    
            logger.x = _resetBtn.x + _resetBtn.width + 10;
            _resetBtn.y = logger.y = stage.stageHeight-30

        }
    }
}

import caurina.transitions.Equations;
import caurina.transitions.Tweener;
import caurina.transitions.properties.ColorShortcuts;

import com.bit101.components.Text;

import flash.display.*;
import flash.events.*;
import flash.filters.ConvolutionFilter;
import flash.geom.ColorTransform;
import flash.geom.Point;
import flash.media.*;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.ContextMenu;
import flash.ui.ContextMenuItem;
import flash.utils.ByteArray;

import flashx.textLayout.formats.TextAlign;



internal var logger:Text
internal function log(...arg):void
{
    trace(arg)
    var str:String=""
    for each(var p:* in arg) str += p.toString() + ", ";
    logger.text = str.slice(0, -2);
}


class RecUnit extends Sprite 
{
    static public const REC_DURATION:Number = 2;
    static public const ACTIVE_LEVEL:Number = 0.05;
//    static public const SILENCE_LEVEL:Number = 0.2;

    private static var _totalRec:int = 0;
    
    private var _mic:Microphone;
    private var _vid:Video;

    private var _samples:Samples;
    private var _samplesTmp:Samples;
    
    private var _numDetects:int;
    
    public var imgCnt:Sprite;
    public var scope:Shape;
    
    private var _id:int;
    private var _indexImage:int;
    private var _totalImage:int;
    private var _lastImage:DisplayObject;
    private var _g:Graphics;
    private var _loopImage:Boolean;
    private var _loopSound:Boolean;
    
    private var _endImage:int;
    private var _startImage:int;
    
    private var _color1:uint;
    private var _color2:uint;
    private var _isRollover:Boolean;
    private var _countDown:CountDown;
    
    public function RecUnit(mic:Microphone, vid:Video)
    {
        mouseChildren = false;
        _mic = mic;
        _vid = vid;
        _id = _totalRec;
        _totalRec++;
        _indexImage = 0;
        
        initContextMenu();
        
        graphics.lineStyle(0, 0x333333);
        graphics.drawRect(0, 0, vid.width, vid.height);
        graphics.lineTo(vid.width, vid.height)
        imgCnt = addChild(new Sprite()) as Sprite;;
        buttonMode = true;
        _isRollover = false;
        switch(_totalRec){
            case 1: _color1= 0x20D0D0; _color2= 0xCCFF00; break;
            case 2: _color1= 0x1E9B9C; _color2= 0xAADD00; break;
            case 3: _color1= 0x116161; _color2= 0x88AA00; break;
        }
        
        scope = addChild(new Shape()) as Shape;
        scope.x = _vid.width;
        scope.y = _vid.height * .5;
        _g = scope.graphics;
        _g.lineStyle(1, _color1);
        _g.lineTo(REC_DURATION*44100,0)
        scope.scaleX = .002;

        _countDown = addChild(new CountDown()) as CountDown;
        _countDown.x = 80;
        _countDown.y = 60;
        _countDown.addEventListener(Event.COMPLETE,_record);
        
        reset();
        
        addEventListener(MouseEvent.ROLL_OVER,onRollover);
        addEventListener(MouseEvent.ROLL_OUT,onRollout);
        addEventListener(MouseEvent.MOUSE_UP,onMouseup);
    }
    
    private function initContextMenu():void
    {
        var menu:ContextMenu = new ContextMenu()
        menu.hideBuiltInItems();
        var nomal:ContextMenuItem = new ContextMenuItem("* Nomal")
        var compress:ContextMenuItem = new ContextMenuItem("- Compress");
        var strech:ContextMenuItem = new ContextMenuItem("- Strech");
        var reverse:ContextMenuItem = new ContextMenuItem("- Reverce");
        menu.customItems.push(nomal);
        menu.customItems.push(compress);
        menu.customItems.push(strech);
        menu.customItems.push(reverse);
        
        nomal.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,onMenuItemSelect);
        compress.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,onMenuItemSelect);
        strech.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,onMenuItemSelect);
        reverse.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT,onMenuItemSelect);
        contextMenu = menu;
        function onMenuItemSelect(e:ContextMenuEvent):void
        {
            nomal.caption = "- Nomal";
            compress.caption = "- Compress";
            strech.caption = "- Strech";
            reverse.caption = "- Reverse";
            switch(e.target){
                case nomal: _samples.activeIndex    = 0; nomal.caption    = "* Nomal";    break;
                case compress: _samples.activeIndex = 1; compress.caption = "* Compress"; break;
                case strech: _samples.activeIndex   = 2; strech.caption   = "* Strech";   break;
                case reverse: _samples.activeIndex  = 3; reverse.caption  = "* Reverse";  break;
            }
        }
    }
    

    private function onRollover(e:MouseEvent):void
    {
        Tweener.addTween(this,{time:.001,
            _color_redMultiplier:1,
            _color_greenMultiplier:1,
            _color_blueMultiplier:1,
            transition:"linear"}
        );
        _isRollover=true;
    }
    private function onRollout(e:MouseEvent):void
    {
        Tweener.addTween(this,{time:.001,
            _color_redMultiplier:.5,
            _color_greenMultiplier:.5,
            _color_blueMultiplier:.5,
            transition:"linear"}
        );
        _isRollover=false;
    }
    private function onMouseup(e:MouseEvent):void
    {
        reset();
        _countDown.alpha=0
        record(false);
    }
    
    public function reset():void
    {
        var len:Number = imgCnt.numChildren;
        for (var i :int = 0; i < len; i++) imgCnt.removeChildAt(0);

        transform.colorTransform = new ColorTransform();

        _g.clear();
        _g.lineStyle(1, _color1);
        _g.lineTo(REC_DURATION*44100,0)
        _g.lineStyle(1, _color2,.6);
        _g.moveTo(0, 0);
        _countDown.sandby();
    }    
    
    public function record(enableCountDown:Boolean=true):void
    {
        dispatchEvent(new Event("recordStart"));
        log("REC START: " + _id);
        
        if(!enableCountDown) _record() else _countDown.play();
        
    }
    
    private function _record(e:Event=null):void
    {
        _samplesTmp = new Samples();
        _mic.addEventListener(SampleDataEvent.SAMPLE_DATA, recSound);
        _vid.addEventListener(Event.EXIT_FRAME, recImage);
    }
    
    private function recordComplete():void
    {
        analizeSound();
        if(_samples) _samples.dispose();
        _samples = _samplesTmp;
        _totalImage = _samples.images.length;
        log("REC FINISH:" + _id);
        
        dispatchEvent(new Event("recordComplete"));
    }
    
    public function playSound(loop:Boolean=false):void
    {
        var s:Sound = new Sound();
        s.addEventListener(SampleDataEvent.SAMPLE_DATA, writeData);
        var i:int = 0;
        var sampleLegth:int = _samples.original.length;
        var len:int = 0;
        function writeData(se:SampleDataEvent):void {
            len += 8192;
            if (len >= sampleLegth) {
                len = sampleLegth;
                if (!_loopSound) {
                    s.removeEventListener(SampleDataEvent.SAMPLE_DATA, writeData)
                }else {
                        
                }
            }
            for (i; i < len; i++){
                var smp:Number = _samples.original[i];
                se.data.writeFloat(smp);
                se.data.writeFloat(smp);
            }
        }        
        s.play();
    }
    
    public function hideAll():void
    {
        for each(var img:DisplayObject in _samples.images) img.visible = false;
    }
    
    public function playImage(loop:Boolean=false):void
    {
        hideAll();
        _lastImage = _samples.images[_indexImage];
        _loopImage = loop
        addEventListener(Event.ENTER_FRAME,onPlayImage)
    }
    
    private function onPlayImage(e:Event):void 
    {
        _lastImage.visible = false;
        _samples.images[_indexImage].visible = true;
        _lastImage = _samples.images[_indexImage];
        _indexImage = (++_indexImage) % _totalImage;
        if (_indexImage >= _totalImage-1 && !_loopImage) stopImage();
    }
    
    public function playImageByRage(start:int, end:int):void
    {
        _startImage = _indexImage = start;
        _endImage = end;
        hideAll();
        _lastImage = _samples.images[_indexImage];
        transform.colorTransform = new ColorTransform();
        var rand:Function = Math.random
        if(!_isRollover){
            Tweener.addTween(this,{time:.001,
                _color_redMultiplier  :.5 + 1 * rand(),
                _color_greenMultiplier:.5 + 1 * rand(),
                _color_blueMultiplier :.5 + 1 * rand(),
                transition:"linear"}
            );
            Tweener.addTween(this,{time:.05,delay:.002,
                _color_redMultiplier:.5,
                _color_greenMultiplier:.5,
                _color_blueMultiplier:.5,
                transition:"linear"}
            );
        }
        addEventListener(Event.ENTER_FRAME,onPlayImageByRage);
    }
    
    private function onPlayImageByRage(e:Event):void 
    {
        _lastImage.visible = false;
        _samples.images[_indexImage].visible = true;
        _lastImage = _samples.images[_indexImage];
        _indexImage++;
        if (_indexImage >= _endImage )stopImage();
    }

    public function playDetects(index:int):void
    {
        master.addVoice(_samples.activeSample[index]);
        playImageByRage(_samples.imageQue[index][0], _samples.imageQue[index][1]);
    }
    
    public function stopImage():void
    {
        removeEventListener(Event.ENTER_FRAME, onPlayImage);
        removeEventListener(Event.ENTER_FRAME, onPlayImageByRage);
    }
    
    private function recImage(e:Event):void 
    {
        var v:Video = e.target as Video
        var bmd:BitmapData = new BitmapData(v.width, v.height, true, 0);
        bmd.draw(v)
        var bm:Bitmap = new Bitmap(bmd);
        _samplesTmp.images.push(bm);
        imgCnt.addChild(bm);
    }
    
    private function recSound(e:SampleDataEvent):void 
    {
        var len:int = e.data.length/4;
        var i:int;
        var offset:int = _samplesTmp.original.length;
        for (i = 0; i < len ; i++) {
            
            var smp:Number = e.data.readFloat();
            if (smp > .8) smp = .8;
            if (smp < -.8) smp = -.8;
            _samplesTmp.original.push(smp);
            
            _g.lineTo((offset + i), smp * 30);
        }
        
        if(offset+i > REC_DURATION*44100){
            _mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, recSound);
            _vid.removeEventListener(Event.EXIT_FRAME, recImage);
            recordComplete();
        }
    }
    
    private function analizeSound():void
    {
        _g.lineStyle(1,0x333333);
        
        var len:int = _samplesTmp.original.length;
        var _thresh:Number = ACTIVE_LEVEL;
        var pre:Number = 0;
        var p1:int = 0;
        var p2:int = 0;
        var dtc:Vector.<Number>;
        var cmp:Vector.<Number>;
        var str:Vector.<Number>;
        var img:Vector.<int>;
        var c:int = 0;
        var i:int = 0;
        var minBlank:int=1000;
        var minBuffer:int=1500;
        var maxBuffer:int=20000;
        for (i; i < len; i++) 
        {
            var smp:Number = _samplesTmp.original[i];
            pre = smp;
            
            
            if (p1 == 0 && (smp > _thresh || smp < -_thresh)) {
                p1 = i;
                dtc = new Vector.<Number>(); 
                cmp = new Vector.<Number>(); 
                str = new Vector.<Number>(); 
                img = new Vector.<int>();
                img.push(int(i / len * _samplesTmp.images.length));
            }else if(p1 > 0 && p2==0 && (smp < _thresh || smp > -_thresh)){
                p2 = i;
            }else if(p2 > 0 && (smp > _thresh || smp < -_thresh)){
                p2 = 0;
            }else if (p2 > 0 && i - p2 > minBlank) {
                if(i-p1 > minBuffer)    close();
                p1 = p2 = 0;
            }else if (p1>0 && i - p1 > maxBuffer) {
                p2 = i;
                close();
                p1 = p2 = 0;
            }
            
            if (dtc){
                dtc.push(smp);
                if(i%2==0) cmp.push(smp);
                str.push(pre+(smp-pre)*.5);
                str.push(smp);
            } 
        }
        
        function close():void
        {
            _g.beginFill(0x999999, .5);
            _g.drawRect(p1, -30, p2-p1, 60);
            _g.endFill()
            img.push(int(i / len * _samplesTmp.images.length));
            _samplesTmp.imageQue.push(img)
            _samplesTmp.detects.push(dtc);
            _samplesTmp.compless.push(cmp);
            _samplesTmp.strech.push(str);
            _samplesTmp.reverse.push(dtc.slice(0,dtc.length-1).reverse());
            c++;
        }
        
        if (p1 > 0) {
            p2 = len - 1;
            close();
        }
        if (c == 0) {
            p1 = 0;
            p2 = i = 3000;
            dtc = _samplesTmp.original.slice(0,i); 
            cmp = _samplesTmp.original.slice(0,i); 
            str = _samplesTmp.original.slice(0,i); 
            img = new Vector.<int>();
            img.push(int(p1));
            close()
        }
        
        _numDetects = _samplesTmp.detects.length;
        
        trace("active samples:",_numDetects,c)
    }
    
    public function get id():int { return _id; }
    
    public function get numDetects():int { return _numDetects; }
}

class CountDown extends Sprite
{
    private var _tf:TextField;
    private var _count:int;
    private var _eq:*;
    function CountDown()
    {
        _tf=new TextField();
        _tf.embedFonts=true;
        var fmt:TextFormat = logger.textField.defaultTextFormat
        fmt.size = 8;
        fmt.align = TextAlign.CENTER;
        _tf.width = 200;
        _tf.height= 16
        _tf.mouseEnabled = false;
        _tf.defaultTextFormat = fmt;
        _tf.textColor = 0xFFFFFF;
        _tf.text= "3"
        _tf.cacheAsBitmap=true    
        addChild(_tf);
        _tf.x=_tf.width * -.5;
        _tf.y=_tf.height * -.5;
        _eq = Equations.easeOutCirc;
    }
    public function sandby():void
    {
        scaleX=scaleY=1;
        alpha=1;
        _tf.textColor = 0x444444;
        _tf.text= "STAND BY";
    }
    public function play():void
    {
        _tf.textColor = 0xFFFFFF;
        _count = 3;
        _tf.text= _count.toString();
        scaleX=scaleY=2;
        Tweener.addTween(this,{time:.4,alpha:0,scaleX:1,scaleY:1,transition:_eq,onComplete:loop});
    }
    private function loop():void
    {
        alpha=1;
        scaleX=scaleY=2;
        if(--_count>0){
            _tf.text= _count.toString();
            Tweener.addTween(this,{time:.4,alpha:0,scaleX:1,scaleY:1,transition:_eq,onComplete:loop});
        }else{
            _tf.textColor = 0xFF0000;
            _tf.text="REC"
            Tweener.addTween(this,{time:.5,alpha:0,scaleX:1,scaleY:1,transition:_eq,onComplete:onComplete});
        }
    }
    private function onComplete():void
    {
        dispatchEvent(new Event(Event.COMPLETE));
    }
}

class Samples
{
    public var original:Vector.<Number>;
    public var detects:Vector.<Vector.<Number>>;
    public var imageQue:Vector.<Vector.<int>>;
    public var compless:Vector.<Vector.<Number>>;
    public var strech:Vector.<Vector.<Number>>;
    public var reverse:Vector.<Vector.<Number>>;
    public var images:Vector.<Bitmap>;
    private var _activeSample:Vector.<Vector.<Number>>;
    private var _activeIndex:int;

    public function get activeIndex():int{return _activeIndex;}

    public function set activeIndex(value:int):void
    {
        _activeIndex = value;
        switch(value){
            case 0: _activeSample = detects; break;
            case 1: _activeSample = compless; break;
            case 2: _activeSample = strech; break;
            case 3: _activeSample = reverse; break;
            default: _activeSample = detects; break;
        }
    }

    function Samples():void
    {
        original = new Vector.<Number>();
        detects = new Vector.<Vector.<Number>>();
        imageQue = new Vector.<Vector.<int>>();
        compless = new Vector.<Vector.<Number>>();
        strech = new Vector.<Vector.<Number>>();
        reverse = new Vector.<Vector.<Number>>();
        images = new Vector.<Bitmap>();
        activeIndex = 0;
    }
    
    public function get activeSample():Vector.<Vector.<Number>>
    {
        return _activeSample;
    }
    
    public function dispose():void
    {
        for each(var i:Bitmap in images) i.bitmapData.dispose();
    }
}

internal var master:Master;
class Master
{
    public var latency:int = 8000;
    public var vj:VJ;

    private var _sound:Sound;
    private var _soundChannel:SoundChannel;
    private var _stack:Vector.<Vector.<Number>>;
    private var _units:Vector.<RecUnit>;
    private var _voice:Vector.<Vector.<Number>>;
    private var _count:int;
    private var _loop:int;
    private var _metro:Vector.<Number>;
    private var _metro2:Vector.<Number>;
    private var _isPlay:Boolean;
    private var _tracks:Array; 
    private var _time:Number = 30;
    private var _bufSize:int = int(_time * .001 * 44100);
    private var _buf:Vector.<Number> = new Vector.<Number>(_bufSize);
    private var _dry:Number = 1;
    private var _wet:Number = 0;
    private var _fb:Number = 0;
    private var _position:Number = 0;
    
    public function Master(units:Vector.<RecUnit>,vj:VJ)
    {
        this.vj = vj;
        _count = 0;
        _units = units;
        _isPlay = false;
        _tracks = [];
            
        _metro = new Vector.<Number>();
        _metro2 = new Vector.<Number>();
        for (var i:int = 0; i < 1000; i++) {
            _metro.push(Math.sin(i * .5)*.01);
            _metro2.push(Math.sin(i * .2)*.01);
        }
        
        for each(var ru:RecUnit in units){
            ru.addEventListener("recordComplete",onRecordComplete);
        }
        _stack = new Vector.<Vector.<Number>>();
        _sound = new Sound();
    }
    private function onRecordComplete(e:Event):void
    {
        if(!_isPlay) return;
        makeTrack(RecUnit(e.target).id);
    }
    
    public function play():void
    {
        _isPlay = true
        makeTrack(0);
        makeTrack(1);
        makeTrack(2);
        _sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
        _soundChannel=_sound.play();
    }
    
    public function stop():void
    {
        _soundChannel.stop();
        _sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
        _isPlay = false;
    }
    
    public function makeTrack(index:int):void
    {
        log("UPDATE TRACK id:" + index);
        var _t:Array = [];
        for (var i:int = 0; i < 16; i++) {
            var d:int = (Math.random() > .4)?Math.random() * _units[index].numDetects:-1;
            _t.push(d);
        }
        trace(_t);
        _tracks[index] = _t;
    }
    
    public function addVoice(v:Vector.<Number>):Number
    {
        return _stack.push(v.slice(0,v.length-1).reverse());
    }
    
    private function onSampleData(e:SampleDataEvent):void 
    {
        nextQue();
        
        var len:int = _stack.length;
        var i2:int = 0;
        var data:ByteArray = e.data;
        for (var i:int = 0; i < latency; i++) 
        {
            var smp:Number = 0;
            
            for (i2 = 0; i2 < len; i2++) 
            {
                var v:Vector.<Number> = _stack[i2];
                if(v.length>0)smp += v.pop();
            }
            smp *= .33;
            
            var sample:Number = (_buf[_position])?_buf[_position]:0;
            _buf[_position] = smp * _wet + sample * _fb;
            _position++;
            _position %= _bufSize;
            var b:Number = smp * _dry + sample;            
            
            data.writeFloat(b);
            data.writeFloat(b);
            
        }
        
        var v2:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>()
        for (i2 = 0; i2 < len; i2++) {
            v = _stack[i2];
            if (v.length > 0)v2.push(v)
        }
        
        _stack = v2;
        var percent:Number = _loop/(16*11)
        vj.updateIndicator(percent);
        _count++;
        _loop++;
        if (_loop > 63){
            _dry = 0.7;
            _wet = 0.2;
            _fb  = 0.53;
        }
        if (_loop > 127){
            _dry = 0.6
            _wet = 0.3;
            _fb  = 0.63;
        }
            
        if (_loop >= 16 * 11) {
            _dry = 1.0
            _wet = 0;
            _fb  = 0;
            _loop = 0;
            makeTrack(0);
            makeTrack(1);
            makeTrack(2);
            log("UPDATE ALL TRACK");
        }
    }
    
    private function nextQue():void
    {
        if (_count % 8 == 0) {
            addVoice(_metro);
        }
        if (_count % 32 == 0) {
            addVoice(_metro2);
        }
        if (_loop < 32 && _count % 8 != 0) return;
        if (_loop < 64 && _count % 4 != 0) return;
        if (_loop < 128 && _count % 2 != 0) return;
        var i:int = 0;
        var len:int= _tracks.length;
        vj.numDrawTargets = 0;
        for (i;i<len;i++){
            var d:int = _tracks[i][_count % 16];
            if(d != -1){
                _units[i].playDetects(d);
                vj.numDrawTargets++;
            }
        }
    }
}
class VJ extends Sprite
{
    private var _indicator:Sprite;
    private var _indicatorProgress:Shape;
    public var numDrawTargets:int;
    
    private var _units:Vector.<RecUnit>;
    private var _bm:Bitmap;
    private var _bmd:BitmapData;
    private var _tmp:BitmapData;
    private var _pt:Point;
    private var drawTargets:Vector.<IBitmapDrawable>
    private var _mesh:Shape;
    function VJ(units:Vector.<RecUnit>)
    {
        drawTargets = new Vector.<IBitmapDrawable>();
        var len:int = units.length;
        for (var i:int=0; i< len;i++){
            drawTargets[i] = units[i].imgCnt;
        }
        numDrawTargets=1;
        mask = addChild(new Shape());
        _pt = new Point();
        _units = units;
        trace(_units[0].imgCnt.width, _units[0].imgCnt.height)
        _bmd = new BitmapData(_units[0].imgCnt.width, _units[0].imgCnt.height, true, 0);
        _tmp = new BitmapData(_units[0].imgCnt.width, _units[0].imgCnt.height, true, 0);
        _bm = addChild(new Bitmap(_bmd)) as Bitmap;
        Shape(mask).graphics.beginFill(0)
        Shape(mask).graphics.drawRect(0,0,_bm.width,_bm.height);
        Shape(mask).graphics.endFill();
        
        _mesh = new Shape();
        addChild(_mesh);
        drawMesh();

        _indicator = addChild(new Sprite()) as Sprite;
        _indicator.graphics.beginFill(0x0);
        _indicator.graphics.drawRect(0,0,100,1);
        _indicator.graphics.endFill();
        _indicatorProgress = _indicator.addChild(new Shape()) as Shape;
        
        addEventListener(Event.ENTER_FRAME,update);
        addEventListener(Event.ADDED_TO_STAGE,onAddedStage);
    }
    private function drawMesh():void
    {
        var g:Graphics = _mesh.graphics;
        g.clear();
        g.lineStyle(1, 0x0);
        var len:int = _bm.width;
        var l:Number = _bm.height;
        for (var i:int = 1; i < len; i+=2) {
            g.moveTo(i, 0);
            g.lineTo(i,l);
        }
        l = len;
        len = _bm.height
        for (i = 1; i < len; i+=2) {
            g.moveTo(0, i);
            g.lineTo(l, i);
        }
        _mesh.cacheAsBitmap = true;        
    }
    
    private function onAddedStage(e:Event):void
    {
        stage.addEventListener(Event.RESIZE,onResize);
        onResize();
    }
    
    private function onResize(e:Event=null):void
    {
        var w:Number = stage.stageWidth-20;
        var h:Number = stage.stageHeight-300;
        var scale:int = int(h/_units[0].imgCnt.height);
        if(scale<2)scale=2;
        _bm.scaleX = _bm.scaleY = scale;
        mask.height = _bm.height;
        mask.width  = _bm.width;
        mask.height += 1;
        _indicator.width = mask.width;
        _indicator.y = mask.height-1;
        drawMesh();
    }
    public function updateIndicator(percent:Number):void
    {
        var g:Graphics = _indicatorProgress.graphics;
        g.clear();
        g.beginFill(0x990000)
        g.drawRect(0,0,percent*100,1);
        g.endFill();
    }
    
    public function play():void
    {
        addEventListener(Event.ENTER_FRAME,update)
    }
    
    public function stop():void
    {
        removeEventListener(Event.ENTER_FRAME,update)
    }
    
    private function update(e:Event=null):void
    {
        switch(numDrawTargets) {
            case 0: return;
            case 1:_bmd.draw(drawTargets[0]); break;
            case 2:draw2img(); break;
            case 3:draw3img(); break;
        }
    }
    
    private function draw3img():void
    {
        var rand:Array = randomArray(3)
        var r:Number=Math.random()
        if (r > .3) {
            _bmd.draw(drawTargets[rand[0]]);
        }else if(r>.2){
            _bmd.draw(drawTargets[rand[0]], null, new ColorTransform(1, 0, 0));
            _bmd.draw(drawTargets[rand[1]], null, new ColorTransform(0, 1, 0), BlendMode.ADD);
            _bmd.draw(drawTargets[rand[2]], null, new ColorTransform(0, 0, 1), BlendMode.ADD);
        }else{
            edgeEffect(drawTargets[rand[0]]);
            _bmd.draw(_tmp, null, new ColorTransform(1, 0,0));
            edgeEffect(drawTargets[rand[1]]);
            _bmd.draw(_tmp, null, new ColorTransform(0, 0, 1), BlendMode.ADD);
            edgeEffect(drawTargets[rand[2]]);
            _bmd.draw(_tmp, null, new ColorTransform(0, 1, 0), BlendMode.ADD);
        }
    }
    private function randomArray(length:int=2):Array
    {
        var ary:Array = [];
        var ary2:Array = [];
        for(var i:int =0;i< length;i++){ary.push(i);}
        for(i = 0;i< length;i++){ary2.push(ary.splice(ary.length*Math.random(),1));}
        return ary2;
    }
    private function draw2img():void
    {
        var rand:Array = randomArray(2)
        if (Math.random() > .3) {
            _bmd.draw(drawTargets[rand[0]]);
            return;
        }
        _bmd.draw(drawTargets[rand[0]], null, new ColorTransform(1, 0, 1));
        _bmd.draw(drawTargets[rand[1]], null, new ColorTransform(0, 1, 0), BlendMode.ADD);
    }
    
    private function edgeEffect(target:IBitmapDrawable):void
    {
        _tmp.draw(target)
        _tmp.applyFilter(_tmp, _tmp.rect, _pt,
            new ConvolutionFilter(
                3,
                3,
                [
                    0, 5,  1,
                    0, 15, 1,
                    0, 0, -15
                ],
                9, 3, false
            )
        );
    }
}