Пример #1
0
define( function( require, exports, module ) {

	var Events = require( 'D3.Events' );

	function FourierTransform( bufferSize, sampleRate ) {

		this.bufferSize = bufferSize;
		this.sampleRate = sampleRate;
		this.bandwidth  = 2 / bufferSize * sampleRate / 2;

		this.spectrum   = new Float32Array( bufferSize/2 );
		this.real       = new Float32Array( bufferSize );
		this.imag       = new Float32Array( bufferSize );

		this.peakBand   = 0;
		this.peak       = 0;


	}

	FourierTransform.prototype.getBandFrequency = function( index ){
		return this.bandwidth * index + this.bandwidth / 2;
	};

	FourierTransform.prototype.calculateSpectrum = function( ) {
		var spectrum  = this.spectrum,
		real      = this.real,
		imag      = this.imag,
		bSi       = 2 / this.bufferSize,
		sqrt      = Math.sqrt,
		rval, ival, mag;

		for (var i = 0, N = this.bufferSize/2; i < N; i++) {
			rval = real[i];
			ival = imag[i];
			mag = bSi * sqrt( rval * rval + ival * ival );

			if ( mag > this.peak ) {
				this.peakBand = i;
				this.peak = mag;
			}

			spectrum[i] = mag;
		}
	};

	function FFT( bufferSize, sampleRate ) {

		FourierTransform.call( this, bufferSize, sampleRate );

		this.reverseTable = new Uint32Array(bufferSize);

		var limit = 1;
		var bit = bufferSize >> 1;

		var i;

		while (limit < bufferSize) {
			for (i = 0; i < limit; i++) {
				this.reverseTable[i + limit] = this.reverseTable[i] + bit;
			}

			limit = limit << 1;
			bit = bit >> 1;
		}

		this.sinTable = new Float32Array(bufferSize);
		this.cosTable = new Float32Array(bufferSize);

		for (i = 0; i < bufferSize; i++) {
			this.sinTable[i] = Math.sin(-Math.PI/i);
			this.cosTable[i] = Math.cos(-Math.PI/i);
		}

	}

	FFT.prototype = Object.create( FourierTransform.prototype );

	FFT.prototype.forward = function(buffer) {

		var bufferSize	= this.bufferSize,
		cosTable        = this.cosTable,
		sinTable        = this.sinTable,
		reverseTable    = this.reverseTable,
		real            = this.real,
		imag            = this.imag,
		spectrum        = this.spectrum;

		var k = Math.floor( Math.log( bufferSize ) / Math.LN2 );

		if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
		if (bufferSize !== buffer.length)  { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }

		var halfSize = 1,
		phaseShiftStepReal,	phaseShiftStepImag,
		currentPhaseShiftReal, currentPhaseShiftImag,
		off, tr, ti, tmpReal, i;

		for (i = 0; i < bufferSize; i++) {
			real[i] = buffer[reverseTable[i]];
			imag[i] = 0;
		}

		while (halfSize < bufferSize) {
			
			phaseShiftStepReal = cosTable[halfSize];
			phaseShiftStepImag = sinTable[halfSize];

			currentPhaseShiftReal = 1;
			currentPhaseShiftImag = 0;

			for (var fftStep = 0; fftStep < halfSize; fftStep++) {
				i = fftStep;

				while (i < bufferSize) {
					off = i + halfSize;
					tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
					ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);

					real[off] = real[i] - tr;
					imag[off] = imag[i] - ti;
					real[i] += tr;
					imag[i] += ti;

					i += halfSize << 1;
				}

				tmpReal = currentPhaseShiftReal;
				currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
				currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
			}

			halfSize = halfSize << 1;
		}

		return this.calculateSpectrum( );
	};

	function Dancer( src ){

		this.audio = new Audio( src );
		this.audio.loop = true;
		this.context = window.AudioContext ? new window.AudioContext( ) : new window.webkitAudioContext( );

		this.proc = this.context.createScriptProcessor ?
					this.context.createScriptProcessor( Dancer.SAMPLE_SIZE / 2, 1, 1 ) :
					this.context.createJavaScriptNode( Dancer.SAMPLE_SIZE / 2, 1, 1 );
		this.gain = this.context.createGainNode();

		this.fft = new FFT( Dancer.SAMPLE_SIZE / 2, Dancer.SAMPLE_RATE );
		this.signal = new Float32Array( Dancer.SAMPLE_SIZE / 2 );

		this.frequency = [ 0, 10 ];
		this.threshold = 0.2;
		this.decay     = 0.02;

		this.currentThreshold = this.threshold;

		var __this__ = this;

		this.proc.onaudioprocess = function ( e ) {
			__this__.update.call( __this__, e );
		};

		if ( this.audio.readyState < 3 ) {
			this.audio.addEventListener( 'canplay', function ( ) {
				__this__.connectContext( );
			} );
		} else {
			this.connectContext( );
		}

		this.audio.addEventListener( 'progress', function ( e ) {
			if ( e.currentTarget.duration ) {
				__this__.progress = e.currentTarget.seekable.end( 0 ) / e.currentTarget.duration;
			}
		} );

	}

	Events.mixTo( Dancer );

	Dancer.prototype.spectrum = function( ){
		return this.fft.spectrum;
	};
	Dancer.prototype.waveform = function( ){
		return this.signal;
	};
	Dancer.prototype.time = function( ){
		return this.audio.currentTime;
	};

	
	Dancer.prototype.play = function( ){
		this.audio.play( );
	};

	Dancer.prototype.pause = function( ){
		this.audio.pause( );
	};

	Dancer.prototype.update = function ( e ) {

		var
		buffers = [],
		channels = e.inputBuffer.numberOfChannels,
		resolution = Dancer.SAMPLE_SIZE / channels,
		sum = function ( prev, curr ) {
		return prev[ i ] + curr[ i ];
		}, i;

		for ( i = channels; i--; ) {
			buffers.push( e.inputBuffer.getChannelData( i ) );
		}

		for ( i = 0; i < resolution; i++ ) {
			this.signal[ i ] = channels > 1 ?
			buffers.reduce( sum ) / channels :
			buffers[ 0 ][ i ];
		}

		this.fft.forward( this.signal );


		var magnitude = this.maxAmplitude( this.frequency );

		if ( magnitude >= this.currentThreshold && magnitude >= this.threshold ) {
			this.currentThreshold = magnitude;
			this.trigger( Dancer.ON_KICK, magnitude );
		} else {
			this.trigger( Dancer.OFF_KICK, magnitude );
			this.currentThreshold -= this.decay;
		}

		this.trigger( Dancer.DANCING, magnitude );

	};

	Dancer.prototype.connectContext = function ( ) {

		this.source = this.context.createMediaElementSource( this.audio );

		this.source.connect( this.proc );
		this.source.connect( this.gain );

		this.gain.connect( this.context.destination );
		this.proc.connect( this.context.destination );

	};

	Dancer.prototype.maxAmplitude = function( frequency ){

		var max = 0,
			fft = this.spectrum( );

		if ( !frequency.length ) {
			return frequency < fft.length ?
			fft[ ~~frequency ] :
			null;
		}

		for ( var i = frequency[ 0 ], l = frequency[ 1 ]; i <= l; i++ ) {
			if ( fft[ i ] > max ) { max = fft[ i ]; }
		}
		return max;
	};

	Dancer.SAMPLE_SIZE = 2048;
	Dancer.SAMPLE_RATE = 44100;

	Dancer.ON_KICK = 'onKick';
	Dancer.OFF_KICK = 'offKick';
	Dancer.DANCING = 'dancing';

	module.exports = Dancer;

} );
Пример #2
0
define( function( require, exports, module ){
	
	var Events = require( 'D3.Events' );
	var listenEvents = require( 'D3.listenEvents' );

	// --------------- Key --------------- //
	function Key( status, kCode, kName, prevent ){

		this.status = status;
		this.keyCode = kCode;
		this.keyName = kName;
		this.prevent = prevent || false;

	}
	
	Key.prototype.isPressed = function( keyStatus ){ 
		return ( ( keyStatus & this.status ) === this.status ) 
	};
	
	Key.prototype.press = function( keyStatus ){

		keyStatus |= this.status;
		return keyStatus;
	
	};
	
	Key.prototype.lift = function( keyStatus ){ 


		keyStatus &= ~ this.status;
		return keyStatus;

	};

	// --------------- KeyManager --------------- //
	function KeyManager( element ){

		this._keyStatus = 0;

		this.__keyNum = 0;
		this.__keys = {};
		var de = this.domEvents = listenEvents( element );

		var _this = this;
		
		de.on( listenEvents.MOUSE_DOWN, function( event ){

			var event = event.elementEvent;
			var key = _this.__keys[ event.button ];
			key && key.prevent && event.preventDefault( );
			_this.down( event.button );

		} );
		
		de.on( listenEvents.MOUSE_UP, function( event ){

			var event = event.elementEvent;
			var key = _this.__keys[ event.button ];
			key && key.prevent && event.preventDefault( );
			_this.up( event.button );

		} );
		
		de.on( listenEvents.KEY_DOWN, function( event ){

			var event = event.elementEvent;
			var key = _this.__keys[ event.keyCode ];
			key && key.prevent && event.preventDefault( );
			_this.down( event.keyCode );

		} );
		
		de.on( listenEvents.KEY_UP, function( event ){

			var event = event.elementEvent;
			var key = _this.__keys[ event.keyCode ];
			key && key.prevent && event.preventDefault( );
			_this.up( event.keyCode );

		} );
		
		de.on( listenEvents.MOUSE_OUT, function( event ){

			var event = event.elementEvent;
			for( var i = 0; i<3; i++ ){
				var key = _this.__keys[ i ];
				key && ( _this._keyStatus = key.lift( this._keyStatus ) );
			}

		} );

	}

	Events.mixTo( KeyManager );


	KeyManager.KEY_DOWN = 'keyDown';
	KeyManager.KEY_UP 	= 'keyUp';

	KeyManager.LEFT		= 'mouseLeft';
	KeyManager.MIDDLE	= 'mouseMiddle';
	KeyManager.RIGHT	= 'mouseRight';


	KeyManager.prototype.bindKey = function( kCode, kName, prevent ){

		var kId = this.__keyNum;

		if( typeof( kCode ) !== 'number' ){
		
			console.error( 'KeyManager.bindKey( ' + kId + ', ' + kCode + ', ' + ( kName ? kName : '' ) + ' ) failed: the type of the first variable must be SFInt32' );
			return this;
		
		}

		if( this[ kCode ] ){

			console.warn( 'KeyManager.bindKey(' + kId + ', ' + kCode + ', ' + ( kName ? kName : '') + ' ): the KeyCode \"' + KeyCode + '\" has been used!' );    		
		
		}

		this.__keys[ kName ] = this.__keys[ kCode ] = new Key( 1 << kId, kCode, kName ? kName : '', prevent );

		this.__keyNum ++;

		return kCode;

	};
	// TODO: add unbind
	KeyManager.prototype.unbindKey = function( kCode ){};

	KeyManager.prototype.down = function( kCode ){

		var key = this.__keys[ kCode ];

		if( key ) {

			this._keyStatus = key.press( this._keyStatus );
			this.trigger( KeyManager.KEY_DOWN, key.keyName, this.domEvents.getEvent( ) );
		
		}

	};

	KeyManager.prototype.up = function( kCode ){

		var key = this.__keys[ kCode ];

		if( key ) {
			
			this._keyStatus = key.lift( this._keyStatus );
			this.trigger( KeyManager.KEY_UP, key.keyName, this.domEvents.getEvent( ) );
		
		}

	};

	KeyManager.prototype.getStatus = function( ){
	
		return this._keyStatus;
	
	};

	KeyManager.prototype.isContainedOne = function( ){

		for( var i = 0; i < arguments.length; i++ ){

			var key = this.__keys[ arguments[ i ] ];

			if( key && key.isPressed( this._keyStatus ) ){
				return true;
			}
		
		}
		return false;

	};

	KeyManager.prototype.isContainedAll = function( ){

		for( var i=0; i < arguments.length; i++ ){

			var key = this._keys[ arguments[ i ] ];

			if( !key || !key.isPressed( this._keyStatus ) ){
				return false;
			}
		}
		return true;

	};

	KeyManager.prototype.isContainedWith = function( ){

		var nowStatus = 0;

		for( var i=0; i < arguments.length; i++ ){
			nowStatus |= this._keys[ arguments[ i ] ].status;
		}

		if( this.isContainedAll.apply( this, arguments ) && nowStatus === this.getStatus( ) ){

		}else{
			return false;
		}
		return true;

	};

	module.exports = KeyManager;

} );