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

どんなおと?

...
@author ...
Get Adobe Flash player
by knd 03 Sep 2009
/**
 * 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);
	}

}