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

2009-3-12 鍵盤で演奏 2 (波形を描く)

鍵盤テスト 2
和音をうまく鳴らしながら波形を描いてみてください。

・Adobe - デベロッパーセンター : Flash Player 10で広がるFlashの音の世界
http://www.adobe.com/jp/devnet/flash/articles/flp10_sound.html

この記事を見ながら作りました。
Get Adobe Flash player
by hikipuro 12 Mar 2009
package 
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.GradientType;
    import flash.display.Graphics;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.SampleDataEvent;
    import flash.geom.Matrix;
    import flash.media.Sound;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    
    /**
     * 鍵盤テスト 2
     * 和音をうまく鳴らしながら波形を描いてみてください。
     * 
     * ・Adobe - デベロッパーセンター : Flash Player 10で広がるFlashの音の世界
     * http://www.adobe.com/jp/devnet/flash/articles/flp10_sound.html
     *
     * この記事を見ながら作りました。
     * 
     */
    [SWF(width="320", height="240", backgroundColor="0xFFFFFF", frameRate="30")] 
    public class Main extends Sprite 
    {
        // ループの処理回数
        private const RESOLUTION_HIGH:Number = 8192;
        private const RESOLUTION_MIDDLE:Number = 8192 / 2;
        private const RESOLUTION_LOW:Number = 8192 / 4;
        
        // PI * 2
        private const PI2:Number = Math.PI * 2;
        
        // 音量
        private const VOLUME:Number = 0.2;
        
        // 1 ステップ分の音の高さの変化量
        private const FR:Array = [
                                    Number(0xFF) * 261.6 / 44100, // C
                                    Number(0xFF) * 277.2 / 44100, // C#
                                    Number(0xFF) * 293.7 / 44100, // D
                                    Number(0xFF) * 311.1 / 44100, // D#
                                    Number(0xFF) * 329.6 / 44100, // E
                                    Number(0xFF) * 349.2 / 44100, // F
                                    Number(0xFF) * 370.0 / 44100, // F#
                                    Number(0xFF) * 392.0 / 44100, // G
                                    Number(0xFF) * 415.3 / 44100, // G#
                                    Number(0xFF) * 440.0 / 44100, // A
                                    Number(0xFF) * 466.2 / 44100, // A#
                                    Number(0xFF) * 493.9 / 44100, // B
                                    Number(0xFF) * 523.2 / 44100, // C
                                 ];
        
        // 白鍵と黒鍵の順番
        private const KEY_COLOR:Array = [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0]; 
        
        // 鍵盤ごとの音
        private var sample:Array = new Array();
        
        // キーが押された時 True
        private var key:Array = new Array();
        
        // 波形メモリ
        private var sin_table:Array = new Array();
        
        // 波形を描く画面用スプライト
        private var waveSprite:Sprite = new Sprite();
        
        // 波形を描く画面でマウスが押された時 True
        private var mouseDownFlag:Boolean = false;
        
        // 波形の画面で 1 つ前に指していた X 座標
        private var oldx:int;
        
        /**
         * コンストラクタ
         */
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        /**
         * 初期化メソッド
         * @param    e
         */
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            
            // 波形の準備
            for (var n:int = 0; n < 0x100; n++)
                sin_table[n] = Math.sin(PI2 * (n / 255));
            
            // 白鍵と黒鍵を描く
            var white_index:int = 0;
            
            for (var i:int = 0; i < 13; i++)
            {
                var sprite:Sprite;
                sprite = new Sprite();
                sprite.name = i.toString();
                
                if (KEY_COLOR[i] == 0)
                {
                    sprite.x = white_index * 40;
                    sprite.y = 0;
                    white_index++;
                    drawRect(sprite.graphics, 0, 0, 40, 120, 0xFFFFFF, 0xDDDDDD);
                    sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
                    sprite.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
                    sprite.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverWhite);
                    sprite.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutWhite);
                    addChildAt(sprite, 0);
                } else {
                    sprite.x = white_index * 40 - 15;
                    sprite.y = 0;
                    drawRect(sprite.graphics, 0, 0, 30, 80, 0x000000, 0x888888);
                    sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
                    sprite.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
                    sprite.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverBlack);
                    sprite.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutBlack);
                    addChild(sprite);
                }
                
                sample[i] = 0.0;
            }
            
            // 波形の画面の準備
            waveSprite.x = 50;
            waveSprite.y = 130;
            redrawWaveSprite();
            waveSprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownWaveSprite);
            waveSprite.addEventListener(MouseEvent.MOUSE_UP, onMouseUpWaveSprite);
            waveSprite.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveWaveSprite);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUpWaveSprite);
            addChild(waveSprite);
            
            // 波形をリセットするボタンの準備
            var resetSprite:Sprite = new Sprite();
            resetSprite.x = 10;
            resetSprite.y = 130;
            drawRect(resetSprite.graphics, 0, 0, 30, 20, 0xFF8000, 0xFFDDDD);
            resetSprite.addEventListener(MouseEvent.MOUSE_DOWN, function ():void 
            {
                for (var n:int = 0; n < 0x100; n++)
                    sin_table[n] = Math.sin(PI2 * (n / 255));
                redrawWaveSprite();
            });
            addChild(resetSprite);
            
            // 波形を矩形波にするボタンの準備
            var squareSprite:Sprite = new Sprite();
            squareSprite.x = 10;
            squareSprite.y = 155;
            drawRect(squareSprite.graphics, 0, 0, 30, 20, 0xFF8000, 0xFFDDDD);
            squareSprite.addEventListener(MouseEvent.MOUSE_DOWN, function ():void 
            {
                for (var n:int = 0; n < 0x100; n++) {
                    if (Math.sin(PI2 * (n / 255)) > 0)
                        sin_table[n] = 0.6;
                    else
                        sin_table[n] = -0.6;
                }
                redrawWaveSprite();
            });
            addChild(squareSprite);
            
            // 波形を三角波にするボタンの準備
            var triangleSprite:Sprite = new Sprite();
            triangleSprite.x = 10;
            triangleSprite.y = 180;
            drawRect(triangleSprite.graphics, 0, 0, 30, 20, 0xFF8000, 0xFFDDDD);
            triangleSprite.addEventListener(MouseEvent.MOUSE_DOWN, function ():void 
            {
                var m:int = 0;
                var direction:int = 1;
                for (var n:int = 0; n < 0x100; n++) {
                    sin_table[n] = m / 63;
                    
                    if (direction > 0)
                        m++;
                    else
                        m--;
                    
                    if (m > 63 || m < -63)
                        direction *= -1;
                }
                redrawWaveSprite();
            });
            addChild(triangleSprite);

            // 波形をノイズにするボタンの準備
            var noiseSprite:Sprite = new Sprite();
            noiseSprite.x = 10;
            noiseSprite.y = 205;
            drawRect(noiseSprite.graphics, 0, 0, 30, 20, 0xFF0000, 0xFFDDDD);
            noiseSprite.addEventListener(MouseEvent.MOUSE_DOWN, function ():void 
            {
                for (var n:int = 0; n < 0x100; n++) {
                    sin_table[n] = 1 - Math.random() * 2;
                }
                redrawWaveSprite();
            });
            addChild(noiseSprite);
            
            // サウンドオブジェクトを作成して音をループさせる
            var sound:Sound = new Sound();
            sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
            sound.play(0, 1);
        }
        
        /**
         * 波形の画面を描きなおす
         */
        private function redrawWaveSprite():void 
        {
            var g:Graphics = waveSprite.graphics;
            
            g.clear();
            g.beginFill(0xDDDDDD);
            g.drawRect(0, 0, 256, 100);
            g.endFill();
            g.lineStyle(1, 0xDD0000);
            g.moveTo(0, 50);
            g.lineTo(255, 50);
            g.lineStyle(1, 0x000000);
            
            for (var x:int = 1; x < 0x100; x++)
            {
                var y1:int = sin_table[x - 1] * 50;
                var y2:int = sin_table[x] * 50;
                g.moveTo(x - 1, 50 + y1);
                g.lineTo(x, 50 + y2);
            }
        }
        
        /**
         * 波形の画面でマウスが押された時
         * @param    event
         */
        private function onMouseDownWaveSprite(event:MouseEvent):void 
        {
            mouseDownFlag = true;
            sin_table[waveSprite.mouseX] = Number(waveSprite.mouseY - 50) / 50;
            redrawWaveSprite();
            oldx = waveSprite.mouseX;
        }
        
        /**
         * 波形の画面でマウスが離された時
         * @param    event
         */
        private function onMouseUpWaveSprite(event:MouseEvent):void 
        {
            mouseDownFlag = false;
        }
        
        /**
         * 波形の画面でマウスが動いた時
         * @param    event
         */
        private function onMouseMoveWaveSprite(event:MouseEvent):void 
        {
            if (mouseDownFlag == false)
                return;
                
            var st:int;
            var ed:int;
            
            if (oldx < waveSprite.mouseX) {
                st = oldx;
                ed = waveSprite.mouseX;
            } else {
                st = waveSprite.mouseX;
                ed = oldx;
            }
            
            for (var x:int = st; x <= ed; x++)
            {
                if (x < 0 || x > 255)
                    continue;
                sin_table[x] = Number(waveSprite.mouseY - 50) / 50;
            }
            redrawWaveSprite();
            
            oldx = waveSprite.mouseX;
        }
        
        /**
         * 鍵盤の上でマウスが押された時
         * @param    event
         */
        private function onMouseDown(event:MouseEvent):void 
        {
            // 押し始めた時のプチノイズを消す
            sample[parseInt(event.currentTarget.name)] = 0;

            key[parseInt(event.currentTarget.name)] = true;
        }
        
        /**
         * 鍵盤の上でマウスが離された時
         * @param    event
         */
        private function onMouseUp(event:MouseEvent):void 
        {
            key[parseInt(event.currentTarget.name)] = false;
        }
        
        /**
         * 白鍵の上にマウスが乗った時
         * @param    event
         */
        private function onMouseOverWhite(event:MouseEvent):void 
        {
            event.target.graphics.clear();
            drawRect(event.target.graphics, 0, 0, 40, 120, 0x8888FF, 0xEEEEFF);
        }
        
        /**
         * 白鍵の上からマウスが離れた時
         * @param    event
         */
        private function onMouseOutWhite(event:MouseEvent):void 
        {
            if (key[parseInt(event.currentTarget.name)] == true)
                return;
            event.target.graphics.clear();
            drawRect(event.target.graphics, 0, 0, 40, 120, 0xFFFFFF, 0xDDDDDD);
        }
        
        /**
         * 黒鍵の上にマウスが乗った時
         * @param    event
         */
        private function onMouseOverBlack(event:MouseEvent):void 
        {
            event.target.graphics.clear();
            drawRect(event.target.graphics, 0, 0, 30, 80, 0x000000, 0x8888FF);
        }
        
        /**
         * 黒鍵の上からマウスが離れた時
         * @param    event
         */
        private function onMouseOutBlack(event:MouseEvent):void 
        {
            if (key[parseInt(event.currentTarget.name)] == true)
                return;
            event.target.graphics.clear();
            drawRect(event.target.graphics, 0, 0, 30, 80, 0x000000, 0x888888);
        }
        
        /**
         * オーディオデータ要求イベント
         * @param    event
         */
        private function onSampleData(event:SampleDataEvent):void 
        {
            // 波形の長さ分繰り返す
            for (var i:int = 0; i < RESOLUTION_MIDDLE; i++) {
                var s:Number = 0;
                
                // 鍵盤が押されていたら、その音量を足す
                for (var n:int = 0; n < 13; n++)
                {
                    if (key[n]) {
                        var index:int = int(sample[n]) % 0x100;
                        s += sin_table[index];
                        sample[n] += FR[n];
                    }
                }
                
                // 音量調整
                s *= VOLUME;
                
                // 波形を書き込む
                event.data.writeFloat(s);
                event.data.writeFloat(s);
            }
        }
        
        /**
         * 鍵盤を描く
         * @param    g            描く対象の Graphics オブジェクト
         * @param    x            鍵盤の中心の X 座標
         * @param    y            鍵盤の中心の Y 座標
         * @param    width        鍵盤の幅
         * @param    height        鍵盤の高さ
         * @param    colorLight    明るい場所の色
         * @param    colorDark    暗い場所の色
         */
        private function drawRect(g:Graphics, x:Number, y:Number, width:Number, height:Number, colorLight:uint, colorDark:uint):void
        {
            var colors:Array = [colorLight, colorDark];
            var alphas:Array = [1.0, 1.0];
            var ratios:Array = [0, 255];
            var matrix:Matrix = new Matrix();
            matrix.createGradientBox(width, 
                                     height, 
                                     -Math.PI / 2,
                                     x,
                                     y);
            
            g.lineStyle(1, 0x000000);
            g.beginGradientFill(GradientType.LINEAR, 
                                        colors,
                                        alphas,
                                        ratios,
                                        matrix);
            g.drawRect(x, y, width, height);
            g.endFill();
        }
    }
    
}