forked from: 2009-3-12 鍵盤で演奏 2 (波形を描く)
鍵盤テスト 2
和音をうまく鳴らしながら波形を描いてみてください。
・Adobe - デベロッパーセンター : Flash Player 10で広がるFlashの音の世界
http://www.adobe.com/jp/devnet/flash/articles/flp10_sound.html
この記事を見ながら作りました。
ちょっと調整してみました
黒鍵(?)は調整中なので触るとおかしくなります。
by paq
// forked from hikipuro's 2009-3-12 鍵盤で演奏 2 (波形を描く)
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
*
* この記事を見ながら作りました。
*
*/
//ちょっと調整してみました
//黒鍵(?)は調整中なので触るとおかしくなります。
//by paq
[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;
// 鍵盤でマウスが押されているかどうか。
private var mouseDownFlagKBD:Boolean = false;
// 白鍵にマウスがあるかどうか。
private var mouseFlagKBDw:Boolean = false;
//黒鍵にマウスがあるかどうか。
private var mouseFlagKBDb:Boolean = false;
/**
* コンストラクタ
*/
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);
sprite.addEventListener(Event.ENTER_FRAME, onEnterFrameWhite);
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);
sprite.addEventListener(Event.ENTER_FRAME, onEnterFrameBlack);
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
{
mouseDownFlagKBD = true;
// 押し始めた時のプチノイズを消す
sample[parseInt(event.currentTarget.name)] = 0;
key[parseInt(event.currentTarget.name)] = true;
}
/**
* 鍵盤の上でマウスが離された時
* @param event
*/
private function onMouseUp(event:MouseEvent):void
{
mouseDownFlagKBD = false;
key[parseInt(event.currentTarget.name)] = false;
}
/**
* 白鍵からマウスが離れているかチェックする
* @param event
*/
private function onEnterFrameWhite(event:Event):void
{
if (event.currentTarget.hitTestPoint(stage.mouseX, stage.mouseY) == true && mouseDownFlagKBD == true && mouseFlagKBDb == false) {
if(key[parseInt(event.currentTarget.name)] == false)
{
// 押し始めた時のプチノイズを消す
sample[parseInt(event.currentTarget.name)] = 0;
key[parseInt(event.currentTarget.name)] = true;
}
}
if ((event.currentTarget.hitTestPoint(stage.mouseX, stage.mouseY) == false && key[parseInt(event.currentTarget.name)] == true) || (mouseFlagKBDb == true && key[parseInt(event.currentTarget.name)] == true))
{
key[parseInt(event.currentTarget.name)] = false;
drawRect(event.target.graphics, 0, 0, 40, 120, 0xFFFFFF, 0xDDDDDD);
mouseFlagKBDw = false;
}
}
/**
* 黒鍵からマウスが離れているかチェックする
* @param event
*/
private function onEnterFrameBlack(event:Event):void
{
if ((event.currentTarget.hitTestPoint(stage.mouseX, stage.mouseY) == false && key[parseInt(event.currentTarget.name)] == true) || (mouseFlagKBDw == true && key[parseInt(event.currentTarget.name)] == true))
{
key[parseInt(event.currentTarget.name)] = false;
drawRect(event.target.graphics, 0, 0, 30, 80, 0x000000, 0x888888);
mouseFlagKBDb = false;
}else if (event.currentTarget.hitTestPoint(stage.mouseX, stage.mouseY) == true && mouseDownFlagKBD == true && mouseFlagKBDw == true) {
if(key[parseInt(event.currentTarget.name)] == false)
{
// 押し始めた時のプチノイズを消す
sample[parseInt(event.currentTarget.name)] = 0;
key[parseInt(event.currentTarget.name)] = true;
}
}
}
/**
* 白鍵の上にマウスが乗った時
* @param event
*/
private function onMouseOverWhite(event:MouseEvent):void
{
mouseFlagKBDw = true;
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
{
mouseFlagKBDw = true;
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();
}
}
}