鍵盤で演奏

(サンプル一覧を表示する)

このサンプルは、 Flash Player 10 以降でのみ再生できます。
Flash Player 10 では、サウンドの波形の動的な生成ができるようになったので、
プログラムで音声を作り出すことができるようになりました。
Flash の実行画面
鍵盤で演奏

Flashプレーヤーが入っていないか、JavaScriptが無効になっているようです。

Get Adobe Flash player

鍵盤で演奏
オレンジ色のボタンを押すと、波形が切り替わります。
マウスで波形を描くこともできます。

ソースコード

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;
	import flash.utils.ByteArray;
	import flash.utils.getTimer;
	
	/**
	 * 鍵盤テスト
	 * 
	 * ・Adobe - デベロッパーセンター : Flash Player 10 で広がる Flash の音の世界
	 * http://www.adobe.com/jp/devnet/flash/articles/flp10_sound.html
	 *
	 * この記事を見ながら作りました。
	 * 
	 * @author Hikipuro
	 */
	public class Main extends Sprite 
	{
		// 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] = 0.75 - Math.random() * 1.5;
                }
                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, 255, 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 
		{
			event.target.graphics.clear();
			drawRect(event.target.graphics, 0, 0, 30, 80, 0x000000, 0x888888);
		}
		
		/**
		 * オーディオデータ要求イベント
		 * @param	event
		 */
		private function onSampleData(event:SampleDataEvent):void 
		{
			var data:ByteArray = event.data;
			
			// 波形の長さ分繰り返す
			for (var i:uint = 0; i < 2048; i++) {
				var s:Number = 0;
				
				// 鍵盤が押されていたら、その音量を足す
				for (var n:uint = 0; n < 13; n++)
				{
					if (key[n]) {
						var index:int = int(sample[n]) % 0x100;
						s += sin_table[index];
						sample[n] += FR[n];
					}
				}
				
				// 音量調整
				s *= VOLUME;
				
				// 波形を書き込む
				data.writeFloat(s);
				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();
		}
	}
	
}
		

外部リンク