Beautifl - Flash Gallery

Thumbnail : cat voice keyboard
cat voice keyboard
narutohyper 2010-03-29 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

package
{
/*
 cat voice keyboard

 作っては見たけれど、動的にサンプリング音源をFFT変換するには、
 かなり無理があり、実用的ではないかも。
 なので、あらかじめ必要な音を全部生成しキーボードに割り付けてみましたが
 それはそれで、最初にかなり時間がかかるので、やはり全音サンプリングデータを
 取り込んだ方が、早い気がしますね。
 
 なお、PhaseVocoder&FFTは、海外サイト
 http://iq12.com/blog/2009/08/25/real-time-pitch-shifting/ 
 より、拝借
 情報を頂いた、alumican_netさんに多謝
*/

	
	import flash.display.Sprite;
	import flash.display.Shape;
	import flash.events.Event;
	import flash.media.Sound;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.net.*;
	import flash.text.*;
	import net.hires.debug.Stats;
 
	[SWF(width = 465, height = 465, frameRate = 60, backgroundColor=0x727B80)]

	public class catKeyboard extends Sprite {
		//private var loadData:String = "http://marubayashi.net/archive/sample/cat/guitar.mp3";
		private var loadData:String = "http://marubayashi.net/archive/sample/cat/cat.mp3";
		private var source:Sound = new Sound();
		private var white:Sprite;
		private var black:Sprite;
		private var comment:TextField;
		private var progressVar:Sprite
		
		public function catKeyboard() {
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.HIGH;

			
			var back:Sprite=new Sprite()
			back.graphics.beginFill(0x727B80,1)
			back.graphics.drawRect(0,0,465,465)
			addChild(back)
			addChild(new Stats())

			//簡易loadingBar
			progressVar = new Sprite()
			addChild(progressVar)
			progressVar.y=465/2+80

			//Bar
			for (var i:uint=0;i<24;i++) {
				catWalk(i,0.2)
			}

			comment=new TextField()
			comment.autoSize=TextFieldAutoSize.CENTER
			comment.selectable=false;
			comment.mouseEnabled=false;
			var format:TextFormat=new TextFormat();
			format.color=0x000000
			format.size=12;
			format.font='_ゴシック';
			format.align='center';
			comment.defaultTextFormat=format
			comment.x=465/2
			comment.y=465/2
			addChild(comment)
			comment.text="音源生成中\nマシンスペックにもよりますが、かなり時間がかかります。";

			source.addEventListener(Event.COMPLETE, loadComplete);
			source.load(new URLRequest(loadData));
		}


		private function loadComplete(event:Event):void {

			//鍵盤の作成
			var keyArray:Array=[];
			white=new Sprite()
			black=new Sprite()
			white.scaleX=white.scaleY=0.65;
			black.scaleX=black.scaleY=0.65;
			white.x=4;
			black.x=4;
			white.y=150;
			black.y=150;

			var counter:uint=0;
			var oldCounter:uint=99;
			addEventListener(Event.ENTER_FRAME, onRenderTick);

			function setKey():void {
				oldCounter=counter
				keyArray[counter]=new Keyboard(counter,source)

				if (keyArray[counter].color) {
					black.addChild(keyArray[counter])
				} else {
					white.addChild(keyArray[counter])
				}
				keyArray[counter].addEventListener(Keyboard.MAKE_SOUND_END, nextKey);
				keyArray[counter].makeSound()
			}

			function nextKey():void {
				keyArray[counter].removeEventListener(Keyboard.MAKE_SOUND_END, nextKey);
				counter++;
				if (counter>=24) {
					addChild(white)
					addChild(black)
					removeEventListener(Event.ENTER_FRAME, onRenderTick);
					removeChild(comment)
				}
			}

			function onRenderTick(e:Event):void {
				if (counter!=oldCounter) {
					catWalk(counter,1,0xFFFFFF)
					setKey()
				}
			}

		}


		private function catWalk(id:uint,alpha:Number=1,color:uint=0x333333):void {
			var mark:Shape = new Shape()
			mark.scaleX=0.5
			mark.scaleY=0.5
			mark.graphics.beginFill(color,alpha)
			mark.graphics.moveTo(-10,-4)
			mark.graphics.curveTo(-10,-15,-4,-10)
			mark.graphics.lineTo(-1,-6)
			mark.graphics.lineTo(5,-4)
			mark.graphics.curveTo(9,0,5,4)
			mark.graphics.lineTo(-1,6)
			mark.graphics.lineTo(-4,10)
			mark.graphics.curveTo(-10,15,-10,4)
			mark.graphics.endFill()
			mark.graphics.beginFill(color,alpha)
			mark.graphics.drawEllipse(3-5,-10-4,10,8)
			mark.graphics.drawEllipse(12-5,-4.5-4,10,8)
			mark.graphics.drawEllipse(12-5, 4.5-4,10,8)
			mark.graphics.drawEllipse(3-5, 10-4,10,8)
			mark.graphics.endFill()
			progressVar.addChild(mark)
			mark.x=id*19+10
			mark.y=id%2*10
		}


	}
}



import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.media.Sound;


class Keyboard extends Sprite {
	public static const MAKE_SOUND_END:String = 'make_sound_end';

	private var id:uint=0
	private var source:Sound;
	private var sound:SoundPitchShift;
	public var color:uint=0

	public function Keyboard(_id:uint,_source:Sound) {
		source=_source
		id=_id
		var i:uint
		var n:Array=new Array(	0, 35, 50, 85,100,150,185,200,235,250,285,300,350,385,400,435,450,500,535,550,585,600,635,650)
		var m:Array=new Array(	0,	1,	0,	1,	0,	0,	1,	0,	1,	0,	1,	0,	0,	1,	0,	1,	0,	0,	1,	0,	1,	0,	1,	0)
		var result:Sprite = new Sprite();

		graphics.lineStyle(1,0x000000,1,true)
		if (m[id]) {
			graphics.beginFill(0x000000,1)
			graphics.drawRect(0,0,30,100)
			x=n[id]
		} else {
			graphics.beginFill(0xFFFFFF,1)
			graphics.drawRect(0,0,50,200)
			x=n[id]
		}
		color=m[id]
		addEventListener(MouseEvent.MOUSE_DOWN,onDown)

	}

	private function onDown(e:MouseEvent):void {
		trace('onDown')
		sound.play();
	}


	public function makeSound():void {
		//周波数(1=基準ラ、0.5=位置オクターブ下がる、2=1オクターブ上がる)
		var k:Array=new Array(-12,-11,-10,-9, -8, -7, -6, -5, -4, -3, -2,  -1,	0,	1,	2,	3,	4,	5,	6,	7,	8,	9, 10, 11)
		var key:Number=k[id]
		sound = new SoundPitchShift(source);

		sound.stereo=false//true
		sound.fftFrameSize = 2048/2
		sound.osamp=3

		if (key) {
			sound.pitch=1*Math.pow(2,key/12)
		} else if (key < 0) {
			sound.pitch=1/Math.pow(2,Math.abs(key)/12)
		} else if (!key) {
			sound.pitch=1.01
		}
		trace(sound.pitch)


		sound.makeSound();
		this.dispatchEvent(new Event(MAKE_SOUND_END));
	}

}






import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.getTimer;

class SoundPitchShift extends Sprite{
	public var mp3: Sound;
	private var _sound: Sound;
	
	private var _target: ByteArray;
	private var _soundBuffer: ByteArray;
	
	private var _leftChannel:Vector.<Number> = new Vector.<Number>(8192/2);
	private var _rightChannel:Vector.<Number> = new Vector.<Number>(8192/2);
	
	private var _leftPitchShifter:PitchShifter;
	private var _rightPitchShifter:PitchShifter;
	
	public var pitch: Number = 1.0;

	public function SoundPitchShift(source:Sound)
	{
		_target = new ByteArray();
		mp3 = source;
	}

	public function makeSound():void {
		//変調したsoundを作成・保存
		_soundBuffer=onSampleDataAll()
	}
	

	public function play(start:Number=0,loop:uint=0):void {
		//_constructShifters();
		trace('音Start')
		var _target:ByteArray=_soundBuffer
		var _position:Number=0;
		_sound = new Sound();
		_sound.addEventListener( SampleDataEvent.SAMPLE_DATA, onSampleDataRequest);
		_sound.play(start,loop);

		function onSampleDataRequest(e:SampleDataEvent):void {
			var data: ByteArray = e.data;

			_target.position=_position

			var read:int=8192/2
			if (0>_target.length/8-(_target.position/8+read)) {
				read=_target.length/8-(_target.position/8)
			}
			_position+=read*8

			for( var i:int = 0; i < read; ++i )
			{
				data.writeFloat(_target.readFloat()*0.5);
				data.writeFloat(_target.readFloat()*0.5);
			}

		}


	}


	private var _fftFrameSize:int = 1024;
	public function get fftFrameSize():int	{ return _fftFrameSize }
	public function set fftFrameSize(v:int):void
	{
		if (v != _fftFrameSize)
		{
			_fftFrameSize = v;
			_constructShifters();
		}
	}
	
	private var _osamp:int = 4;
	public function get osamp():int	{ return _osamp }
	public function set osamp(v:int):void
	{
		if (v != _osamp)
		{
			_osamp = v;
			_constructShifters();
		}
	}
	
	private var _sampleRate:int = 44100;
	public function get sampleRate():int	{ return _sampleRate }
	public function set sampleRate(v:int):void
	{
		if (v != _sampleRate)
		{
			_sampleRate = v;
			_constructShifters();
		}
	}
	
	private var _stereo:Boolean = false;
	public function get stereo():Boolean	{ return _stereo }
	public function set stereo(v:Boolean):void
	{
		if (_stereo = v)
			_rightPitchShifter = new PitchShifter( _fftFrameSize, _osamp, _sampleRate );
	}
	
	public var settings:String;

	private function _constructShifters():void
	{
		settings = (_stereo?"stereo-":"mono-") + _fftFrameSize + "-" + _osamp + "-" + _sampleRate;
		_cpuFIFO = new Vector.<Number>;
		
		_leftPitchShifter = new PitchShifter( _fftFrameSize, _osamp, _sampleRate );
		if ( _stereo )
			_rightPitchShifter = new PitchShifter( _fftFrameSize, _osamp, _sampleRate );
	}

	
	public var doPitch:Boolean;
	public var doStereo:Boolean;
	
	private var _maxCPUPerSetting:Object = { }
	private var _cpuFIFO:Vector.<Number>;
	private var _cpu:Number = 0.0;

	
	public function get cpu():Number { return _cpu }
	


	public function onSampleDataAll():ByteArray {

		var s:int = getTimer();
		
		//-- SHORTCUT
		var data: ByteArray = new ByteArray();
		
		//-- REUSE INSTEAD OF RECREATION
		_target.position = 0;
		var read: int = mp3.extract( _target, mp3.length / 1000 * 44100,0);
		_target.position = 0;
		trace(read,mp3.length)
		var currentMax:Number = _maxCPUPerSetting[settings];
		if (isNaN(currentMax)) currentMax = 0.0;
		
		doPitch = currentMax < 1.5;
		doStereo = currentMax < 1.0;

		for( var i:int = 0; i < read; ++i )
		{
			if (doStereo)
			{
				_leftChannel[i] = _target.readFloat()*0.5;
				_rightChannel[i] = _target.readFloat()*0.5;
			}
			else
			{
				//-- AVG LEFT AND RIGHT CHANNELS
				_leftChannel[i] = .5 * ( _target.readFloat() + _target.readFloat() ) * 0.5;
			}
		}
		
		if ( doPitch ) {
			_leftPitchShifter.pitchShift( pitch, read, _leftChannel );
			
			if (_stereo && doStereo) {
				_rightPitchShifter.pitchShift( pitch, read, _rightChannel );
			}
		}
		
		for( i = 0 ; i < read ; ++i )
		{
			data.writeFloat( _leftChannel[i] );
			data.writeFloat( _stereo && doStereo ? _rightChannel[i] : _leftChannel[i] );
		}

		_cpu = 0.0;
		_cpuFIFO.unshift( (getTimer() - s) * _sampleRate * 1.220703125E-7 );
		var l:int = _cpuFIFO.length;
		if(l>6) _cpuFIFO.splice( --l, 1 );
		for ( i = 0 ; i < l ; ++i )
			_cpu += _cpuFIFO[i];
		_cpu /= Number(l);
		
		if ( currentMax < _cpu )
			_maxCPUPerSetting[settings] = _cpu;

		return data;

	}
	
}


/****************************************************************************
*
* NAME: PitchShifter.as
* VERSION: 1.0
* HOME URL: http://iq12.com/
* KNOWN BUGS: none
*
* SYNOPSIS: Routine for doing pitch shifting while maintaining
* duration using the Short Time Fourier Transform.
*
* DESCRIPTION: The routine takes a pitchShift factor value which is between 0.5
* (one octave down) and 2. (one octave up). A value of exactly 1 does not change
* the pitch. numSampsToProcess tells the routine how many samples in indata[0...
* numSampsToProcess-1] should be pitch shifted and moved to outdata[0 ...
* numSampsToProcess-1]. The two buffers can be identical (ie. it can process the
* data in-place). fftFrameSize defines the FFT frame size used for the
* processing. Typical values are 1024, 2048 and 4096. It may be any value <=
* MAX_FRAME_LENGTH but it MUST be a power of 2. osamp is the STFT
* oversampling factor which also determines the overlap between adjacent STFT
* frames. It should at least be 4 for moderate scaling ratios. A value of 32 is
* recommended for best quality. sampleRate takes the sample rate for the signal 
* in unit Hz, ie. 44100 for 44.1 kHz audio. The data passed to the routine in 
* indata[] should be in the range [-1.0, 1.0), which is also the output range 
* for the data, make sure you scale the data accordingly (for 16bit signed integers
* you would have to divide (and multiply) by 32768). 
*
* COPYRIGHT 1999-2006 Stephan M. Bernsee <smb [AT] dspdimension [DOT] com>
*
* 						The Wide Open License (WOL)
*
* Permission to use, copy, modify, distribute and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice and this license appear in all source copies. 
* THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF
* ANY KIND. See http://www.dspguru.com/wol.htm for more information.
*
*****************************************************************************/
 
/****************************************************************************
*
* This code was converted to AS3/FP10 by Arnaud Gatouillat <fu [AT] iq12 [DOT] com>
* from C# code by Michael Knight ( madmik3 at gmail dot com. )
* http://sites.google.com/site/mikescoderama/
* 
*****************************************************************************/

/****************************************************************************
*
* The functions `realft' and `four1' are based on those in Press, W.H., et al.,
* Numerical Recipes in C: the Art of Scientific Computing (Cambridge Univ. Press,
* 1989;  2nd ed., 1992).
* 
*****************************************************************************/

class PitchShifter
{
	private var gInFIFO		:Vector.<Number>;
	private var gOutFIFO	:Vector.<Number>;
	private var gFFTworksp	:Vector.<Number>;
	private var gLastPhase	:Vector.<Number>;
	private var gSumPhase	:Vector.<Number>;
	private var gOutputAccum:Vector.<Number>;
	private var gAnaFreq	:Vector.<Number>;
	private var gAnaMagn	:Vector.<Number>;
	private var gSynFreq	:Vector.<Number>;
	private var gSynMagn	:Vector.<Number>;

	private var freqPerBin:Number, expct:Number;
	private var gRover:int, inFifoLatency:int, stepSize:int, fftFrameSize2:int;
	
	private var fftFrameSize:int, osamp:int, sampleRate:Number;
	
	/* pre-computed values for speed */
	private var windowValues		:Vector.<Number>;
	private var windowValuesFactored:Vector.<Number>;
	private var invPI:Number, invFftFrameSizePI2:Number, osampPI2:Number, invOsampPI2FreqBin:Number;
	
	private var PI:Number		= Math.PI
	private var TWOPI:Number	= 2 * Math.PI

	public function PitchShifter(fftFrameSize:int, osamp:int, sampleRate:Number)
	{
		this.fftFrameSize	= fftFrameSize;
		this.osamp			= osamp;
		this.sampleRate		= sampleRate;
		
		gInFIFO			= new Vector.<Number>(fftFrameSize);
		gOutFIFO		= new Vector.<Number>(fftFrameSize, true);
		gFFTworksp		= new Vector.<Number>(2 * fftFrameSize + 2, true);
		gLastPhase		= new Vector.<Number>(fftFrameSize / 2 + 1, true);
		gSumPhase		= new Vector.<Number>(fftFrameSize / 2 + 1, true);
		gOutputAccum	= new Vector.<Number>(2 * fftFrameSize, true);
		gAnaFreq		= new Vector.<Number>(fftFrameSize, true);
		gAnaMagn		= new Vector.<Number>(fftFrameSize, true);
		gSynFreq		= new Vector.<Number>(fftFrameSize, true);
		gSynMagn		= new Vector.<Number>(fftFrameSize, true);
		
		/* set up some handy variables */
		fftFrameSize2= fftFrameSize / 2;
		stepSize = fftFrameSize / osamp;
		freqPerBin = sampleRate / Number(fftFrameSize);
		expct = 2.0 * PI * Number(stepSize) / Number(fftFrameSize);
		inFifoLatency = fftFrameSize - stepSize;
		
		invPI = 1 / PI;
		invFftFrameSizePI2 = PI * 2 / fftFrameSize;
		osampPI2 = osamp / ( 2 * PI );
		invOsampPI2FreqBin = 1 / ( freqPerBin * osampPI2);
		
		windowValues			= new Vector.<Number>(fftFrameSize);
		windowValuesFactored	= new Vector.<Number>(fftFrameSize);

		var invFftFrameSize2:Number = 2.0 / (fftFrameSize2 * osamp);
		for (var k:int = 0, t:Number = 0.0; k < fftFrameSize; ++k, t += invFftFrameSizePI2)
		{
			var window: Number = -.5 * Math.cos(t) + .5;
			windowValues[k] = window;
			windowValuesFactored[k] = window * invFftFrameSize2;
		}
	}

	public function pitchShift(pitchShift:Number, numSampsToProcess:int, indata:Vector.<Number>):void
	{
		var magn:Number, phase:Number, tmp:Number, window:Number, real:Number, imag:Number, t:Number;
		var i:int, k:int, qpd:int, index:int, n:int;

		var outdata:Vector.<Number> = indata;
		if (gRover == 0) gRover = inFifoLatency;
		
		/* main processing loop */
		for (i = 0; i < numSampsToProcess; ++i)
		{
			/* As long as we have not yet collected enough data just read in */
			gInFIFO[gRover] = indata[i];
			outdata[i] = gOutFIFO[gRover - inFifoLatency];
			++gRover;
			
			/* now we have enough data for processing */
			if (gRover >= fftFrameSize)
			{
				gRover = inFifoLatency;

				/* do windowing and re,im interleave */
				for (k = 0, n = 1; k < fftFrameSize; ++k, ++n)
				{
					gFFTworksp[n] = gInFIFO[k] * windowValues[k];
					gFFTworksp[++n] = 0.0;
				}
				/* ***************** ANALYSIS ******************* */
				/* do transform */
				realft(gFFTworksp, fftFrameSize, -1);
				/* this is the analysis step */
				for (k = 0; k <= fftFrameSize2; ++k)
				{
					/* de-interlace FFT buffer */
					real = gFFTworksp[n = 1 + (k << 1)];
					imag = gFFTworksp[n + 1];

					/* compute magnitude and phase */
					magn = 2.0 * Math.sqrt(real * real + imag * imag);
					phase = Math.atan2(imag, real);

					/* compute phase difference */
					tmp = phase - gLastPhase[k];
					gLastPhase[k] = phase;

					/* subtract expected phase difference */
					tmp -= k * expct;

					/* map delta phase into +/- Pi interval */
					qpd = int(tmp * invPI);
					if (qpd >= 0)	qpd += qpd & 1;
					else			qpd -= qpd & 1;
					tmp -= PI * Number(qpd);

					/* get deviation from bin frequency from the +/- Pi interval */
					tmp *= osampPI2;

					/* compute the k-th partials' true frequency */
					tmp = (k + tmp) * freqPerBin;

					/* store magnitude and true frequency in analysis arrays */
					gAnaMagn[k] = magn;
					gAnaFreq[k] = tmp;

				}
				/* ***************** PROCESSING ******************* */
				/* this does the actual pitch shifting */
				for (var zero:int = 0; zero < fftFrameSize; ++zero)
				{
					gSynMagn[zero] = 0.0;
					gSynFreq[zero] = 0.0;
				}

				for (k = 0, n = pitchShift > 1.0 ? int(fftFrameSize2 / pitchShift) : fftFrameSize2; k <= n; ++k)
				{
					index = int(k * pitchShift);
					gSynMagn[index] += gAnaMagn[k];
					gSynFreq[index] = gAnaFreq[k] * pitchShift;
				}
				/* ***************** SYNTHESIS ******************* */
				/* this is the synthesis step */
				for (k = 0; k <= fftFrameSize2; ++k)
				{
					/* get magnitude and true frequency from synthesis arrays */
					magn = gSynMagn[k];

					/* subtract bin mid frequency */
					/* get bin deviation from freq deviation */
					/* take osamp into account */
					/* add the overlap phase advance back in */
					/* accumulate delta phase to get bin phase */
					phase = (gSumPhase[k] += (gSynFreq[k] - Number(k) * freqPerBin) * invOsampPI2FreqBin + Number(k) * expct);

					/* get real and imag part and re-interleave */
					gFFTworksp[n = 1 + (k << 1)] = magn * Math.cos(phase);
					gFFTworksp[n + 1] = magn * Math.sin(phase);
				}
				
				/* zero negative frequencies */
				for (k = fftFrameSize + 3, n = 1 + (fftFrameSize << 1); k < n; ++k)
				{
					gFFTworksp[k] = 0.0;
				}
				/* do inverse transform */
				realft(gFFTworksp, fftFrameSize, 1);

				/* do windowing and add to output accumulator */
				for (k = 0, n = 1; k < fftFrameSize; ++k, ++n, ++n)
				{
					gOutputAccum[k] += windowValuesFactored[k] * gFFTworksp[n];
				}
				for (k = 0; k < stepSize; ++k)
				{
					gOutFIFO[k] = gOutputAccum[k];
				}

				//memmove(gOutputAccum, gOutputAccum + stepSize, fftFrameSize * sizeof(Number));
				/* shift accumulator */
				/* move input FIFO */
				for (k = 0, n = stepSize; k < inFifoLatency; ++k, ++n)
				{
					gOutputAccum[k] = gOutputAccum[n];
					gInFIFO[k] = gInFIFO[n];
				}
				for ( ;  k < fftFrameSize; ++k, ++n)
				{
					gOutputAccum[k] = gOutputAccum[n];
				}
			}
		}
	}

	private function realft( data:Vector.<Number>, n:int, isign:int ):void
	{
		var i:int, i1:int, i2:int, i3:int, i4:int, n2p3:int;
		var c1:Number = 0.5, c2:Number, h1r:Number, h1i:Number, h2r:Number, h2i:Number;
		var wr:Number, wi:Number, wpr:Number, wpi:Number, wtemp:Number, theta:Number;

		theta = PI/n;
		if (isign == 1)
		{
			c2 = -0.5;
			four1(data, n, 1);
		} 
		else
		{
			c2 = 0.5;
			theta = -theta;
		}
		wtemp = Math.sin(0.5 * theta);
		wpr = -2.0 * wtemp * wtemp;
		wpi = Math.sin(theta);
		wr = 1.0 + wpr;
		wi = wpi;
		n2p3 = 2 * n + 3;
		for (i = 2; i <= n / 2; ++i)
		{
			i4 = 1 + (i3 = n2p3 - (i2 = 1 + ( i1 = i + i - 1)));
			h1r =  c1 * (data[i1] + data[i3]);
			h1i =  c1 * (data[i2] - data[i4]);
			h2r = -c2 * (data[i2] + data[i4]);
			h2i =  c2 * (data[i1] - data[i3]);
			data[i1] =	h1r + wr * h2r - wi * h2i;
			data[i2] =	h1i + wr * h2i + wi * h2r;
			data[i3] =	h1r - wr * h2r + wi * h2i;
			data[i4] = -h1i + wr * h2i + wi * h2r;
			wr = (wtemp = wr) * wpr - wi * wpi + wr;
			wi = wi * wpr + wtemp * wpi + wi;
		}
		if (isign == 1)
		{
			data[1] = (h1r = data[1]) + data[2];
			data[2] = h1r - data[2];
		}
		else
		{
			data[1] = c1 * ((h1r = data[1]) + data[2]);
			data[2] = c1 * (h1r - data[2]);
			four1(data, n, -1);
			data=data;
		}
	}

	private function four1(data:Vector.<Number>, nn:int, isign:int):void
	{
		var n:int, mmax:int, m:int, j:int, istep:int, i:int;
		var wtemp:Number, wr:Number, wpr:Number, wpi:Number, wi:Number, theta:Number;
		var tempr:Number, tempi:Number;
		var j1:int, i1:int;
		n = nn << 1;
		j = 1;
		for (i = 1; i < n; i += 2)
		{
			if (j > i)
			{
				j1 = j + 1;
				i1 = i + 1;
				tempr = data[j];	data[j] = data[i];		data[i] = tempr;
				tempr = data[j1];	data[j1] = data[i1];	data[i1] = tempr;
			}
			m = n >> 1;
			while (m >= 2 && j > m)
			{
				j -= m;
				m >>= 1;
			}
			j += m;
		}
		mmax = 2;
		while (n > mmax)
		{
			istep = 2 * mmax;
			theta = TWOPI / (isign * mmax);
			wtemp = Math.sin(0.5 * theta);
			wpr = -2.0 * wtemp * wtemp;
			wpi = Math.sin(theta);
			wr = 1.0;
			wi = 0.0;
			for (m = 1; m < mmax; m += 2)
			{
				for (i = m; i <= n; i += istep)
				{
					i1 = i +1;
					j1 = 1+ (j = i + mmax);
					tempr = wr*data[j]	 - wi*data[j1];
					tempi = wr*data[j1]  + wi*data[j];
					data[j] 	= data[i] 	- tempr;
					data[j1]	= data[i1]	- tempi;
					data[i]  += tempr;
					data[i1] += tempi;
				}
				wr = (wtemp = wr) * wpr - wi * wpi + wr;
				wi = wi * wpr + wtemp * wpi + wi;
			}
			mmax = istep;
		}
	}

}