どんなおと?
...
@author ...
/**
* Copyright knd ( http://wonderfl.net/user/knd )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/k0Ms
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import flash.utils.ByteArray;
import flash.ui.Keyboard;
/**
* ...
* @author ...
*/
[SWF(backgroundColor="0xffffff")]
public class TestWave extends Sprite
{
private var wave:SampledWave;
private var data:Vector.<Number>;
private var text: TextField = new TextField();
private var freq: Number = 440.0;
private var halftoneUp: Number = Math.pow(2, 1 / 12);
private var halftoneDown:Number = Math.pow(2, -1 / 12);
private var mySound:Sound;
private var wvBMData: BitmapData;
private var wvImg: Bitmap;
public function TestWave() {
var dummyV:Vector.<Number> = new Vector.<Number>();
dummyV.push(0);
wave = new SampledWave(dummyV);
wvBMData = new BitmapData(stage.stageWidth, stage.stageHeight>>2, true, 0x0);
wvImg = new Bitmap(wvBMData);
wvImg.y = wvImg.height * 3;
addChild(wvImg);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
mySound = new Sound();
mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleData);
mySound.play();
text.autoSize = TextFieldAutoSize.LEFT;
text.textColor = 0xff0000;
addChild(text);
text.text = freq.toFixed(1) + " [Hz] (↑キー:半音上げる/↓キー:半音下げる)";
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
gInitialize();
}
private function gInitialize():void {
var _w:int = stage.stageWidth;
var _h:int = stage.stageHeight;
graphics.clear();
graphics.lineStyle(0, 0x999999);
_h /= 4;
graphics.moveTo(0, _h);
graphics.lineTo(_w, _h);
_h *= 2;
graphics.moveTo(0, _h);
graphics.lineTo(_w, _h);
_h *= 1.5;
graphics.moveTo(0, _h);
graphics.lineTo(_w, _h);
}
private var isDrawing:Boolean = false;
private function mouseDown(e:MouseEvent):void {
//removeChild(wvImg);
wvImg.alpha = 0.5;
var _w:int = stage.stageWidth;
var _h:int = stage.stageHeight;
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_OUT, mouseUp);
stage.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
gInitialize();
graphics.lineStyle(2, 0);
graphics.moveTo(mouseX, mouseY);
isDrawing = true;
}
private function mouseMove(e:MouseEvent):void {
if (isDrawing) {
var $x:Number = mouseX;
var $y:Number = mouseY;
graphics.lineTo($x, $y);
}
}
private function mouseUp(e:MouseEvent):void {
if (isDrawing) {
//if(mySound.isBuffering)mySound.close();
stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMove);
stage.removeEventListener(MouseEvent.MOUSE_UP, mouseUp);
stage.removeEventListener(MouseEvent.MOUSE_OUT, mouseUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown);
correctData();
isDrawing = false;
//addChild(wvImg);
wvImg.alpha = 1.0;
}
}
private function correctData():void {
var _w:int = stage.stageWidth;
var _h:int = stage.stageHeight;
var bmd:BitmapData = new BitmapData(_w, _h);
bmd.draw(this);
data = new Vector.<Number>();
//SpriteをBMPデータに写して、色が0x0だったら波形データに加える
for (var i:int = 0; i < _w; i++ ) {
for ( var j:int = 0; j < _h; j++) {
if ( bmd.getPixel(i, j) == 0) {
data.push(j);
break;
}
}
}
//波形データを補正する。
//最大値と最小値の中間地点を0に
//spriteの高さを1として補正
//var sum :Number = 0;
var max : Number = 0;
var min: Number = _h;
for each(var n:Number in data) {
//sum += n;
max = Math.max(max, n);
min = Math.min(min, n);
}
var avr :Number = (max + min) >> 1;//sum / data.length;
var amp :Number = _h //Math.max(max - avr, avr - min);
for (var k:int = 0; k < data.length; k++) {
var $n :Number = data.shift();
$n = ($n - avr) / amp;
data.push($n);
}
wave = new SampledWave(data, freq);
}
private function sampleData(event:SampleDataEvent):void {
var _ba:ByteArray = new ByteArray();
wvBMData.lock();
wvBMData.fillRect(wvBMData.rect, 0x0);
var _hw : int = wvBMData.height / 2;
for (var i:int = 0; i < 2048; i++)
{
var f:Number = wave.next();
_ba.writeFloat(f);
_ba.writeFloat(f);
if (i < wvBMData.width ) {
wvBMData.setPixel32(i, _hw * (1 + f), 0xffff0000);
}
}
wvBMData.unlock();
event.data.writeBytes(_ba);
}
private function keyDown(e:KeyboardEvent):void {
var key: uint = e.keyCode ;
if (key == Keyboard.DOWN) {
freq *= halftoneDown;
wave = new SampledWave(data, freq);
}
if (key == Keyboard.UP) {
freq *= halftoneUp;
wave = new SampledWave(data, freq);
}
text.text = freq.toFixed(1) + " [Hz] (↑キー:半音上げる/↓キー:半音下げる)";
}
}
}
class SampledWave {
private static const RATE:Number = 44100.0;
private var samplingData: Vector.<Number>;
private var soundData: Vector.<Number>;
private var dataLength: int;
private var freq: Number;
private var wavelength: Number; //サンプリング数換算での波長
public function SampledWave(sampling: Vector.<Number>, f: Number = 440) {
samplingData = sampling.concat();
soundData = new Vector.<Number>();
var i:int;
//freq = f; //サンプリング点を元に周波数fの波を生成する
var k: Number = RATE / f; //1周期あたりのサンプリング回数
var n: int; //kをn倍すると最も小数点以下の値が小さいようなnを求める
var nk: Number = 0;
var df: Number; // nkと最も近い整数との差
var min: Number = 0.5; //dfの最小値
var thld:Number = k * 0.001; //threshold
for (i = 1; nk <= RATE; i++){
nk += k;
df = diffInt(nk);
if(df < min){
n = i;
min =df;
if(df < thld) break; //必要以上にVectorが大きくならない様に閾値が欲しい
}
}
dataLength = Math.round(n * k); //出力データはn周期分となる
wavelength = Number(dataLength) / n; //データ長換算での1周期は、ある有理数になる
freq = wavelength / RATE; //入力f に対する実効周波数
var smpLen: int = sampling.length;
var lastIndex: int = smpLen - 1;
var position: Number; //soundDataのインデックスが何周期分に相当するか
var ratio: Number; //サンプリング点間のどの位置にあるか
for (i =0; i < dataLength; i++){
position = smpLen * ofDecimal(i/wavelength);
ratio = ofDecimal(position);
//単純に線形補間した値をsoundDataに追加
soundData.push(
position < lastIndex ? //sampling の終端で循環させる必要がある
(1 - ratio) * sampling[Math.floor(position)] + ratio * sampling[Math.ceil(position)]:
(1 - ratio) * sampling[lastIndex] + ratio * sampling[0]
);
}
}
private var index: int = 0;
public function next():Number { //indexを循環させながらデータを返す
if (index >= dataLength) index = 0;
return soundData[index++];
}
static private function diffInt(x: Number):Number{ //最も近い整数との差を返す
return Math.abs(x - Math.round(x));
}
static private function ofDecimal(x:Number):Number{ //小数点以下を返す
return x - Math.floor(x);
}
}