Merge pull request #566 from foxCaves/master

Added dancer.js
This commit is contained in:
Ryan Kirkman
2012-11-24 04:06:47 -08:00
11 changed files with 4694 additions and 0 deletions
+558
View File
@@ -0,0 +1,558 @@
/*
* Dancer.js (c) 2012 Jordan Santell
* MIT License
* http://github.com/jsantell/dancer.js
*
* v0.0.1
*/
(function() {
var Dancer = function ( source ) {
this.audioAdapter = window.webkitAudioContext ?
new Dancer.adapters.webkit( this ) :
new Dancer.adapters.moz( this );
this.events = {};
this.sections = [];
this.bind( 'update', update );
this.audioAdapter.load( source );
};
Dancer.adapters = {};
Dancer.prototype = {
/* Controls */
play : function () {
this.audioAdapter.play();
return this;
},
stop : function () {
this.audioAdapter.stop();
return this;
},
/* Actions */
createBeat : function ( freq, threshold, decay, onBeat, offBeat ) {
return new Dancer.Beat( this, freq, threshold, decay, onBeat, offBeat );
},
bind : function ( name, callback ) {
if ( !this.events[ name ] ) {
this.events[ name ] = [];
}
this.events[ name ].push( callback );
return this;
},
unbind : function ( name ) {
if ( this.events[ name ] ) {
delete this.events[ name ];
}
return this;
},
trigger : function ( name ) {
var _this = this;
if ( this.events[ name ] ) {
this.events[ name ].forEach(function( callback ) {
callback.call( _this );
});
}
return this;
},
/* Getters */
getTime : function () {
return this.audioAdapter.getTime();
},
// Returns the magnitude of a frequency or average over a range of frequencies
getFrequency : function ( freq, endFreq ) {
var subFreq, sum = 0;
if ( endFreq !== undefined ) {
for ( var i = freq; i <= endFreq; i++ ) {
sum += this.getSpectrum()[ i ];
}
return sum / ( endFreq - freq + 1 );
} else {
return this.getSpectrum()[ freq ];
}
},
getSpectrum : function () {
return this.audioAdapter.getSpectrum();
},
isLoaded : function () {
return this.audioAdapter.isLoaded;
},
isPlaying : function () {
return this.audioAdapter.isPlaying;
},
/* Sections */
after : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > time;
},
callback : callback
});
return this;
},
before : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() < time;
},
callback : callback
});
return this;
},
between : function ( startTime, endTime, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > startTime && _this.getTime() < endTime;
},
callback : callback
});
return this;
},
onceAt : function ( time, callback ) {
var
_this = this,
thisSection = null;
this.sections.push({
condition : function () {
return _this.getTime() > time && !this.called;
},
callback : function () {
callback.call( this );
thisSection.called = true;
},
called : false
});
// Baking the section in the closure due to callback's this being the dancer instance
thisSection = this.sections[ this.sections.length - 1 ];
return this;
}
};
Dancer.addPlugin = function ( name, fn ) {
if ( Dancer.prototype[ name ] === undefined ) {
Dancer.prototype[ name ] = fn;
}
};
function update () {
for ( var i in this.sections ) {
if ( this.sections[ i ].condition() )
this.sections[ i ].callback.call( this );
}
}
window.Dancer = Dancer;
})();
(function() {
var Beat = function ( dancer, options ) {
options = options || {};
this.dancer = dancer;
this.frequency = options.frequency || [ 0, 10 ];
this.threshold = options.threshold || 0.3;
this.decay = options.decay || 0.02;
this.onBeat = options.onBeat;
this.offBeat = options.offBeat;
this.isOn = false;
this.currentThreshold = this.threshold;
var _this = this;
this.dancer.bind( 'update', function () {
_this.onUpdate();
});
};
Beat.prototype = {
on : function () {
this.isOn = true;
return this;
},
off : function () {
this.isOn = false;
return this;
},
onUpdate : function () {
if ( !this.isOn ) { return; }
var magnitude = this.maxAmplitude( this.frequency );
if ( magnitude >= this.currentThreshold &&
magnitude >= this.threshold ) {
this.currentThreshold = magnitude;
this.onBeat && this.onBeat.call( this.dancer, magnitude );
} else {
this.offBeat && this.offBeat.call( this.dancer, magnitude );
this.currentThreshold -= this.decay;
}
},
maxAmplitude : function ( frequency ) {
var
max = 0,
fft = this.dancer.getSpectrum();
// Sloppy array check
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;
}
};
window.Dancer.Beat = Beat;
})();
(function() {
var
SAMPLE_SIZE = 2048,
SAMPLE_RATE = 44100;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.context = window.audioContext ?
new window.AudioContext() :
new window.webkitAudioContext();
this.isLoaded = false;
this.isPlaying= false;
};
adapter.prototype = {
load : function ( path, callback ) {
var
req = new XMLHttpRequest(),
_this = this;
this.source = this.context.createBufferSource();
req.open( 'GET', path, true );
req.responseType = 'arraybuffer';
req.onload = function () {
if ( _this.context.decodeAudioData ) {
_this.context.decodeAudioData( req.response, function( buffer ) {
_this.source.buffer = buffer;
}, function( e ) {
console.log( e );
});
} else {
_this.source.buffer = _this.context.createBuffer( req.response, false );
}
_this.source.connect( _this.context.destination );
_this.source.connect( _this.proc );
_this.proc.connect( _this.context.destination );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
};
req.send();
this.proc = this.context.createJavaScriptNode( SAMPLE_SIZE / 2, 1, 1 );
this.proc.onaudioprocess = function ( e ) {
_this.update.call( _this, e );
};
this.source.connect( this.context.destination );
this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE );
this.signal = new Float32Array( SAMPLE_SIZE / 2 );
},
play : function () {
var _this = this;
this.isLoaded ?
play() :
this.dancer.bind( 'loaded', play );
function play () {
_this.source.noteOn( 0.0 );
_this.isPlaying = true;
}
},
stop : function () {
this.source.noteOff(0);
this.isPlaying = false;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.context.currentTime;
},
update : function ( e ) {
if ( !this.isPlaying ) { return; }
var
bufferL = e.inputBuffer.getChannelData(0),
bufferR = e.inputBuffer.getChannelData(1);
for ( var i = 0, j = SAMPLE_SIZE / 2; i < j; i++ ) {
this.signal[ i ] = ( bufferL[ i ] + bufferR[ i ] ) / 2;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.webkit = adapter;
})();
(function() {
var adapter = function ( dancer ) {
this.dancer = dancer;
this.audio = new Audio();
this.isLoaded = this.isPlaying = false;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.audio.src = path;
this.audio.addEventListener( 'loadedmetadata', function( e ) {
_this.fbLength = _this.audio.mozFrameBufferLength;
_this.channels = _this.audio.mozChannels;
_this.rate = _this.audio.mozSampleRate;
_this.fft = new FFT( _this.fbLength / _this.channels, _this.rate );
_this.signal = new Float32Array( _this.fbLength / _this.channels );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, false);
this.audio.addEventListener( 'MozAudioAvailable', function( e ) {
_this.update( e );
}, false);
},
play : function () {
this.audio.play();
this.isPlaying = true;
},
stop : function () {
this.audio.pause();
this.isPlaying = false;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.currentTime;
},
update : function ( e ) {
if ( !this.isLoaded ) return;
for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) {
this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.moz = adapter;
})();
/*
* DSP.js - a comprehensive digital signal processing library for javascript
*
* Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
* Copyright 2010 Corban Brook. All rights reserved.
*
*/
// Fourier Transform Module used by DFT, FFT, RFFT
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;
/**
* Calculates the *middle* frequency of an FFT band.
*
* @param {Number} index The index of the FFT band.
*
* @returns The middle frequency in Hz.
*/
this.getBandFrequency = function(index) {
return this.bandwidth * index + this.bandwidth / 2;
};
this.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 = 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;
}
};
}
/**
* FFT is a class for calculating the Discrete Fourier Transform of a signal
* with the Fast Fourier Transform algorithm.
*
* @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
* @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
*
* @constructor
*/
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);
}
}
/**
* Performs a forward transform on the sample buffer.
* Converts a time domain signal to frequency domain spectra.
*
* @param {Array} buffer The sample buffer. Buffer Length must be power of 2
*
* @returns The frequency spectrum array
*/
FFT.prototype.forward = function(buffer) {
// Locally scope variables for speed up
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 = Math.cos(-Math.PI/halfSize);
//phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
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();
};
File diff suppressed because one or more lines are too long
+977
View File
@@ -0,0 +1,977 @@
/*
* Dancer.js (c) 2012 Jordan Santell
* MIT License
* http://github.com/jsantell/dancer.js
*
* v0.2.0
*/
(function() {
var Dancer = function ( source, codecs ) {
this.audioAdapter = Dancer._getAdapter( this );
this.events = {};
this.sections = [];
this.bind( 'update', update );
this.source = Dancer._makeSupportedPath( source, codecs );
this.audioAdapter.load( this.source );
};
Dancer.adapters = {};
Dancer.prototype = {
/* Controls */
play : function () {
this.audioAdapter.play();
return this;
},
stop : function () {
this.audioAdapter.stop();
return this;
},
/* Actions */
createBeat : function ( freq, threshold, decay, onBeat, offBeat ) {
return new Dancer.Beat( this, freq, threshold, decay, onBeat, offBeat );
},
bind : function ( name, callback ) {
if ( !this.events[ name ] ) {
this.events[ name ] = [];
}
this.events[ name ].push( callback );
return this;
},
unbind : function ( name ) {
if ( this.events[ name ] ) {
delete this.events[ name ];
}
return this;
},
trigger : function ( name ) {
var _this = this;
if ( this.events[ name ] ) {
this.events[ name ].forEach(function( callback ) {
callback.call( _this );
});
}
return this;
},
/* Getters */
getTime : function () {
return this.audioAdapter.getTime();
},
// Returns the magnitude of a frequency or average over a range of frequencies
getFrequency : function ( freq, endFreq ) {
var sum = 0;
if ( endFreq !== undefined ) {
for ( var i = freq; i <= endFreq; i++ ) {
sum += this.getSpectrum()[ i ];
}
return sum / ( endFreq - freq + 1 );
} else {
return this.getSpectrum()[ freq ];
}
},
getSpectrum : function () {
return this.audioAdapter.getSpectrum();
},
isLoaded : function () {
return this.audioAdapter.isLoaded;
},
isPlaying : function () {
return this.audioAdapter.isPlaying;
},
/* Sections */
after : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > time;
},
callback : callback
});
return this;
},
before : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() < time;
},
callback : callback
});
return this;
},
between : function ( startTime, endTime, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > startTime && _this.getTime() < endTime;
},
callback : callback
});
return this;
},
onceAt : function ( time, callback ) {
var
_this = this,
thisSection = null;
this.sections.push({
condition : function () {
return _this.getTime() > time && !this.called;
},
callback : function () {
callback.call( this );
thisSection.called = true;
},
called : false
});
// Baking the section in the closure due to callback's this being the dancer instance
thisSection = this.sections[ this.sections.length - 1 ];
return this;
}
};
function update () {
for ( var i in this.sections ) {
if ( this.sections[ i ].condition() )
this.sections[ i ].callback.call( this );
}
}
window.Dancer = Dancer;
})();
(function ( Dancer ) {
var CODECS = {
'mp3' : 'audio/mpeg;',
'ogg' : 'audio/ogg; codecs="vorbis"',
'wav' : 'audio/wav; codecs="1"',
'aac' : 'audio/mp4; codecs="mp4a.40.2"'
},
audioEl = document.createElement( 'audio' );
Dancer.options = {};
Dancer.setOptions = function ( o ) {
for ( var option in o ) {
if ( o.hasOwnProperty( option ) ) {
Dancer.options[ option ] = o[ option ];
}
}
};
Dancer.isSupported = function () {
if ( !window.Float32Array || !window.Uint32Array ) {
return null;
} else if ( window.AudioContext || window.webkitAudioContext ) {
return 'webaudio';
} else if ( window.Audio && ( new window.Audio() ).mozSetup ) {
return 'audiodata';
} else if ( FlashDetect.versionAtLeast( 9 ) ) {
return 'flash';
} else {
return '';
}
};
Dancer.canPlay = function ( type ) {
var canPlay = audioEl.canPlayType;
return !!(
Dancer.isSupported() === 'flash' ?
type.toLowerCase() === 'mp3' :
audioEl.canPlayType &&
audioEl.canPlayType( CODECS[ type.toLowerCase() ] ).replace( /no/, ''));
};
Dancer.addPlugin = function ( name, fn ) {
if ( Dancer.prototype[ name ] === undefined ) {
Dancer.prototype[ name ] = fn;
}
};
Dancer._makeSupportedPath = function ( source, codecs ) {
if ( !codecs ) { return source; }
for ( var i = 0; i < codecs.length; i++ ) {
if ( Dancer.canPlay( codecs[ i ] ) ) {
return source + '.' + codecs[ i ];
}
}
return source;
};
Dancer._getAdapter = function ( instance ) {
switch ( Dancer.isSupported() ) {
case 'webaudio':
return new Dancer.adapters.webkit( instance );
case 'audiodata':
return new Dancer.adapters.moz( instance );
case 'flash':
return new Dancer.adapters.flash( instance );
default:
return null;
}
};
})( window.Dancer );
(function() {
var Beat = function ( dancer, options ) {
options = options || {};
this.dancer = dancer;
this.frequency = options.frequency || [ 0, 10 ];
this.threshold = options.threshold || 0.3;
this.decay = options.decay || 0.02;
this.onBeat = options.onBeat;
this.offBeat = options.offBeat;
this.isOn = false;
this.currentThreshold = this.threshold;
var _this = this;
this.dancer.bind( 'update', function () {
_this.onUpdate();
});
};
Beat.prototype = {
on : function () {
this.isOn = true;
return this;
},
off : function () {
this.isOn = false;
return this;
},
onUpdate : function () {
if ( !this.isOn ) { return; }
var magnitude = this.maxAmplitude( this.frequency );
if ( magnitude >= this.currentThreshold &&
magnitude >= this.threshold ) {
this.currentThreshold = magnitude;
this.onBeat && this.onBeat.call( this.dancer, magnitude );
} else {
this.offBeat && this.offBeat.call( this.dancer, magnitude );
this.currentThreshold -= this.decay;
}
},
maxAmplitude : function ( frequency ) {
var
max = 0,
fft = this.dancer.getSpectrum();
// Sloppy array check
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;
}
};
window.Dancer.Beat = Beat;
})();
(function() {
var
SAMPLE_SIZE = 2048,
SAMPLE_RATE = 44100;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.context = window.AudioContext ?
new window.AudioContext() :
new window.webkitAudioContext();
this.isLoaded = false;
this.isPlaying = false;
this.isDisconnected = false;
};
function connectContext () {
this.source = this.context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect( this.context.destination );
this.source.connect( this.proc );
this.source.connect( this.context.destination );
}
adapter.prototype = {
load : function ( path ) {
var
req = new XMLHttpRequest(),
_this = this;
req.open( 'GET', path, true );
req.responseType = 'arraybuffer';
req.onload = function () {
if ( _this.context.decodeAudioData ) {
_this.context.decodeAudioData( req.response, function( buffer ) {
_this.buffer = buffer;
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, function( e ) {
console.log( e );
});
} else {
_this.buffer = _this.context.createBuffer( req.response, false );
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
};
req.send();
this.proc = this.context.createJavaScriptNode( SAMPLE_SIZE / 2, 1, 1 );
this.proc.onaudioprocess = function ( e ) {
_this.update.call( _this, e );
};
this.proc.connect( this.context.destination );
this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE );
this.signal = new Float32Array( SAMPLE_SIZE / 2 );
},
play : function () {
var _this = this;
this.isLoaded ? play() : this.dancer.bind( 'loaded', play );
function play () {
if ( _this.isDisconnected ) {
connectContext.call( _this );
}
_this.source.noteOn( 0.0 );
_this.startTime = _this.context.currentTime;
_this.isPlaying = true;
}
},
stop : function () {
if ( this.isPlaying ) {
this.source.noteOff( 0.0 );
this.isDisconnected = true;
this.endTime = this.getTime();
}
this.isPlaying = false;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.isPlaying ?
this.context.currentTime - ( this.startTime || 0 ) :
this.endTime || 0;
},
update : function ( e ) {
if ( !this.isPlaying ) { return; }
var
buffers = [],
channels = e.inputBuffer.numberOfChannels,
resolution = SAMPLE_SIZE / channels;
for ( i = channels; i--; ) {
buffers.push( e.inputBuffer.getChannelData( i ) );
}
for ( i = 0; i < resolution; i++ ) {
this.signal[ i ] = channels > 1 ?
buffers.reduce( bufferReduce ) / channels :
buffers[ 0 ][ i ];
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
function bufferReduce ( prev, curr ) {
return prev[ i ] + curr[ i ];
}
Dancer.adapters.webkit = adapter;
})();
(function() {
var adapter = function ( dancer ) {
this.dancer = dancer;
this.audio = new Audio();
this.isLoaded = this.isPlaying = false;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.audio.src = path;
this.audio.addEventListener( 'loadedmetadata', function( e ) {
_this.fbLength = _this.audio.mozFrameBufferLength;
_this.channels = _this.audio.mozChannels;
_this.rate = _this.audio.mozSampleRate;
_this.fft = new FFT( _this.fbLength / _this.channels, _this.rate );
_this.signal = new Float32Array( _this.fbLength / _this.channels );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, false);
this.audio.addEventListener( 'MozAudioAvailable', function( e ) {
_this.update( e );
}, false);
},
play : function () {
this.audio.play();
this.isPlaying = true;
},
stop : function () {
this.audio.pause();
this.isPlaying = false;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.currentTime;
},
update : function ( e ) {
if ( !this.isLoaded ) return;
for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) {
this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.moz = adapter;
})();
(function() {
var
DEBUG_MODE = false,
SAMPLE_SIZE = 1024,
SAMPLE_RATE = 44100,
smLoaded = false,
smLoading = false,
CONVERSION_COEFFICIENT = 0.93;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.isLoaded = this.isPlaying = false;
this.wave_L = [];
this.wave_R = [];
this.spectrum = [];
window.SM2_DEFER = true;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.path = path || this.path;
!window.soundManager && !smLoading && loadSM.call( this );
if ( window.soundManager ) {
this.audio = soundManager.createSound({
id : 'dancer' + Math.random() + '',
url : this.path,
stream : true,
autoPlay : false,
autoLoad : true,
whileplaying : function () {
_this.update();
},
onload : function () {
_this.fft = new FFT( SAMPLE_SIZE, SAMPLE_RATE );
_this.signal = new Float32Array( SAMPLE_SIZE );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
});
}
},
play : function () {
if ( !this.isPlaying && this.isLoaded ) {
this.audio.play();
this.isPlaying = true;
}
},
stop : function () {
this.audio.stop();
this.isPlaying = false;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.position / 1000;
},
update : function () {
if ( !this.isLoaded ) return;
this.wave_L = this.audio.waveformData.left;
this.wave_R = this.audio.waveformData.right;
for ( var i = 0, j = this.wave_L.length; i < j; i++ ) {
this.signal[ 2 * i ] = (( parseFloat(this.wave_L[ i ]) + parseFloat(this.wave_R[ i ])) * CONVERSION_COEFFICIENT );
this.signal[ i * 2 + 1 ] = this.signal[ i * 2 ];
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
function loadSM () {
var adapter = this;
smLoading = true;
loadScript( Dancer.options.flashJS, function () {
soundManager = new SoundManager();
soundManager.flashVersion = 9;
soundManager.flash9Options.useWaveformData = true;
soundManager.useWaveformData = true;
soundManager.useHighPerformance = true;
soundManager.useFastPolling = true;
soundManager.multiShot = false;
soundManager.debugMode = false;
soundManager.debugFlash = false;
soundManager.url = Dancer.options.flashSWF;
soundManager.onready(function () {
smLoaded = true;
adapter.load();
});
soundManager.ontimeout(function(){
console.error( 'Error loading SoundManager2.swf' );
});
soundManager.beginDelayedInit();
});
}
function loadScript ( url, callback ) {
var
script = document.createElement( 'script' ),
appender = document.getElementsByTagName( 'script' )[0];
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
appender.parentNode.insertBefore( script, appender );
}
Dancer.adapters.flash = adapter;
})();
/*
* DSP.js - a comprehensive digital signal processing library for javascript
*
* Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
* Copyright 2010 Corban Brook. All rights reserved.
*
*/
// Fourier Transform Module used by DFT, FFT, RFFT
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;
/**
* Calculates the *middle* frequency of an FFT band.
*
* @param {Number} index The index of the FFT band.
*
* @returns The middle frequency in Hz.
*/
this.getBandFrequency = function(index) {
return this.bandwidth * index + this.bandwidth / 2;
};
this.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 = 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;
}
};
}
/**
* FFT is a class for calculating the Discrete Fourier Transform of a signal
* with the Fast Fourier Transform algorithm.
*
* @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
* @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
*
* @constructor
*/
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);
}
}
/**
* Performs a forward transform on the sample buffer.
* Converts a time domain signal to frequency domain spectra.
*
* @param {Array} buffer The sample buffer. Buffer Length must be power of 2
*
* @returns The frequency spectrum array
*/
FFT.prototype.forward = function(buffer) {
// Locally scope variables for speed up
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 = Math.cos(-Math.PI/halfSize);
//phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
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();
};
/*
Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
Code licensed under the BSD License: http://www.featureblend.com/license.txt
Version: 1.0.4
*/
var FlashDetect = new function(){
var self = this;
self.installed = false;
self.raw = "";
self.major = -1;
self.minor = -1;
self.revision = -1;
self.revisionStr = "";
var activeXDetectRules = [
{
"name":"ShockwaveFlash.ShockwaveFlash.7",
"version":function(obj){
return getActiveXVersion(obj);
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash.6",
"version":function(obj){
var version = "6,0,21";
try{
obj.AllowScriptAccess = "always";
version = getActiveXVersion(obj);
}catch(err){}
return version;
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash",
"version":function(obj){
return getActiveXVersion(obj);
}
}
];
/**
* Extract the ActiveX version of the plugin.
*
* @param {Object} The flash ActiveX object.
* @type String
*/
var getActiveXVersion = function(activeXObj){
var version = -1;
try{
version = activeXObj.GetVariable("$version");
}catch(err){}
return version;
};
/**
* Try and retrieve an ActiveX object having a specified name.
*
* @param {String} name The ActiveX object name lookup.
* @return One of ActiveX object or a simple object having an attribute of activeXError with a value of true.
* @type Object
*/
var getActiveXObject = function(name){
var obj = -1;
try{
obj = new ActiveXObject(name);
}catch(err){
obj = {activeXError:true};
}
return obj;
};
/**
* Parse an ActiveX $version string into an object.
*
* @param {String} str The ActiveX Object GetVariable($version) return value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseActiveXVersion = function(str){
var versionArray = str.split(",");//replace with regex
return {
"raw":str,
"major":parseInt(versionArray[0].split(" ")[1], 10),
"minor":parseInt(versionArray[1], 10),
"revision":parseInt(versionArray[2], 10),
"revisionStr":versionArray[2]
};
};
/**
* Parse a standard enabledPlugin.description into an object.
*
* @param {String} str The enabledPlugin.description value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseStandardVersion = function(str){
var descParts = str.split(/ +/);
var majorMinor = descParts[2].split(/\./);
var revisionStr = descParts[3];
return {
"raw":str,
"major":parseInt(majorMinor[0], 10),
"minor":parseInt(majorMinor[1], 10),
"revisionStr":revisionStr,
"revision":parseRevisionStrToInt(revisionStr)
};
};
/**
* Parse the plugin revision string into an integer.
*
* @param {String} The revision in string format.
* @type Number
*/
var parseRevisionStrToInt = function(str){
return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision;
};
/**
* Is the major version greater than or equal to a specified version.
*
* @param {Number} version The minimum required major version.
* @type Boolean
*/
self.majorAtLeast = function(version){
return self.major >= version;
};
/**
* Is the minor version greater than or equal to a specified version.
*
* @param {Number} version The minimum required minor version.
* @type Boolean
*/
self.minorAtLeast = function(version){
return self.minor >= version;
};
/**
* Is the revision version greater than or equal to a specified version.
*
* @param {Number} version The minimum required revision version.
* @type Boolean
*/
self.revisionAtLeast = function(version){
return self.revision >= version;
};
/**
* Is the version greater than or equal to a specified major, minor and revision.
*
* @param {Number} major The minimum required major version.
* @param {Number} (Optional) minor The minimum required minor version.
* @param {Number} (Optional) revision The minimum required revision version.
* @type Boolean
*/
self.versionAtLeast = function(major){
var properties = [self.major, self.minor, self.revision];
var len = Math.min(properties.length, arguments.length);
for(i=0; i<len; i++){
if(properties[i]>=arguments[i]){
if(i+1<len && properties[i]==arguments[i]){
continue;
}else{
return true;
}
}else{
return false;
}
}
};
/**
* Constructor, sets raw, major, minor, revisionStr, revision and installed public properties.
*/
self.FlashDetect = function(){
if(navigator.plugins && navigator.plugins.length>0){
var type = 'application/x-shockwave-flash';
var mimeTypes = navigator.mimeTypes;
if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
var version = mimeTypes[type].enabledPlugin.description;
var versionObj = parseStandardVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revisionStr = versionObj.revisionStr;
self.revision = versionObj.revision;
self.installed = true;
}
}else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
var version = -1;
for(var i=0; i<activeXDetectRules.length && version==-1; i++){
var obj = getActiveXObject(activeXDetectRules[i].name);
if(!obj.activeXError){
self.installed = true;
version = activeXDetectRules[i].version(obj);
if(version!=-1){
var versionObj = parseActiveXVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revision = versionObj.revision;
self.revisionStr = versionObj.revisionStr;
}
}
}
}
}();
};
FlashDetect.JS_RELEASE = "1.0.4";
File diff suppressed because one or more lines are too long
+995
View File
@@ -0,0 +1,995 @@
/*
* Dancer.js (c) 2012 Jordan Santell
* MIT License
* http://github.com/jsantell/dancer.js
*
* v0.2.1
*/
(function() {
var Dancer = function ( source, codecs ) {
this.audioAdapter = Dancer._getAdapter( this );
this.events = {};
this.sections = [];
this.bind( 'update', update );
this.source = Dancer._makeSupportedPath( source, codecs );
this.audioAdapter.load( this.source );
};
Dancer.adapters = {};
Dancer.prototype = {
/* Controls */
play : function () {
this.audioAdapter.play();
return this;
},
stop : function () {
this.audioAdapter.stop();
return this;
},
/* Actions */
createBeat : function ( freq, threshold, decay, onBeat, offBeat ) {
return new Dancer.Beat( this, freq, threshold, decay, onBeat, offBeat );
},
bind : function ( name, callback ) {
if ( !this.events[ name ] ) {
this.events[ name ] = [];
}
this.events[ name ].push( callback );
return this;
},
unbind : function ( name ) {
if ( this.events[ name ] ) {
delete this.events[ name ];
}
return this;
},
trigger : function ( name ) {
var _this = this;
if ( this.events[ name ] ) {
this.events[ name ].forEach(function( callback ) {
callback.call( _this );
});
}
return this;
},
/* Getters */
getTime : function () {
return this.audioAdapter.getTime();
},
// Returns the magnitude of a frequency or average over a range of frequencies
getFrequency : function ( freq, endFreq ) {
var sum = 0;
if ( endFreq !== undefined ) {
for ( var i = freq; i <= endFreq; i++ ) {
sum += this.getSpectrum()[ i ];
}
return sum / ( endFreq - freq + 1 );
} else {
return this.getSpectrum()[ freq ];
}
},
getWaveform : function () {
return this.audioAdapter.getWaveform();
},
getSpectrum : function () {
return this.audioAdapter.getSpectrum();
},
isLoaded : function () {
return this.audioAdapter.isLoaded;
},
isPlaying : function () {
return this.audioAdapter.isPlaying;
},
/* Sections */
after : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > time;
},
callback : callback
});
return this;
},
before : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() < time;
},
callback : callback
});
return this;
},
between : function ( startTime, endTime, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > startTime && _this.getTime() < endTime;
},
callback : callback
});
return this;
},
onceAt : function ( time, callback ) {
var
_this = this,
thisSection = null;
this.sections.push({
condition : function () {
return _this.getTime() > time && !this.called;
},
callback : function () {
callback.call( this );
thisSection.called = true;
},
called : false
});
// Baking the section in the closure due to callback's this being the dancer instance
thisSection = this.sections[ this.sections.length - 1 ];
return this;
}
};
function update () {
for ( var i in this.sections ) {
if ( this.sections[ i ].condition() )
this.sections[ i ].callback.call( this );
}
}
window.Dancer = Dancer;
})();
(function ( Dancer ) {
var CODECS = {
'mp3' : 'audio/mpeg;',
'ogg' : 'audio/ogg; codecs="vorbis"',
'wav' : 'audio/wav; codecs="1"',
'aac' : 'audio/mp4; codecs="mp4a.40.2"'
},
audioEl = document.createElement( 'audio' );
Dancer.options = {};
Dancer.setOptions = function ( o ) {
for ( var option in o ) {
if ( o.hasOwnProperty( option ) ) {
Dancer.options[ option ] = o[ option ];
}
}
};
Dancer.isSupported = function () {
if ( !window.Float32Array || !window.Uint32Array ) {
return null;
} else if ( window.AudioContext || window.webkitAudioContext ) {
return 'webaudio';
} else if ( window.Audio && ( new window.Audio() ).mozSetup ) {
return 'audiodata';
} else if ( FlashDetect.versionAtLeast( 9 ) ) {
return 'flash';
} else {
return '';
}
};
Dancer.canPlay = function ( type ) {
var canPlay = audioEl.canPlayType;
return !!(
Dancer.isSupported() === 'flash' ?
type.toLowerCase() === 'mp3' :
audioEl.canPlayType &&
audioEl.canPlayType( CODECS[ type.toLowerCase() ] ).replace( /no/, ''));
};
Dancer.addPlugin = function ( name, fn ) {
if ( Dancer.prototype[ name ] === undefined ) {
Dancer.prototype[ name ] = fn;
}
};
Dancer._makeSupportedPath = function ( source, codecs ) {
if ( !codecs ) { return source; }
for ( var i = 0; i < codecs.length; i++ ) {
if ( Dancer.canPlay( codecs[ i ] ) ) {
return source + '.' + codecs[ i ];
}
}
return source;
};
Dancer._getAdapter = function ( instance ) {
switch ( Dancer.isSupported() ) {
case 'webaudio':
return new Dancer.adapters.webkit( instance );
case 'audiodata':
return new Dancer.adapters.moz( instance );
case 'flash':
return new Dancer.adapters.flash( instance );
default:
return null;
}
};
})( window.Dancer );
(function() {
var Beat = function ( dancer, options ) {
options = options || {};
this.dancer = dancer;
this.frequency = options.frequency || [ 0, 10 ];
this.threshold = options.threshold || 0.3;
this.decay = options.decay || 0.02;
this.onBeat = options.onBeat;
this.offBeat = options.offBeat;
this.isOn = false;
this.currentThreshold = this.threshold;
var _this = this;
this.dancer.bind( 'update', function () {
_this.onUpdate();
});
};
Beat.prototype = {
on : function () {
this.isOn = true;
return this;
},
off : function () {
this.isOn = false;
return this;
},
onUpdate : function () {
if ( !this.isOn ) { return; }
var magnitude = this.maxAmplitude( this.frequency );
if ( magnitude >= this.currentThreshold &&
magnitude >= this.threshold ) {
this.currentThreshold = magnitude;
this.onBeat && this.onBeat.call( this.dancer, magnitude );
} else {
this.offBeat && this.offBeat.call( this.dancer, magnitude );
this.currentThreshold -= this.decay;
}
},
maxAmplitude : function ( frequency ) {
var
max = 0,
fft = this.dancer.getSpectrum();
// Sloppy array check
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;
}
};
window.Dancer.Beat = Beat;
})();
(function() {
var
SAMPLE_SIZE = 2048,
SAMPLE_RATE = 44100;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.context = window.AudioContext ?
new window.AudioContext() :
new window.webkitAudioContext();
this.isLoaded = false;
this.isPlaying = false;
this.isDisconnected = false;
};
function connectContext () {
this.source = this.context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect( this.context.destination );
this.source.connect( this.proc );
this.source.connect( this.context.destination );
}
adapter.prototype = {
load : function ( path ) {
var
req = new XMLHttpRequest(),
_this = this;
req.open( 'GET', path, true );
req.responseType = 'arraybuffer';
req.onload = function () {
if ( _this.context.decodeAudioData ) {
_this.context.decodeAudioData( req.response, function( buffer ) {
_this.buffer = buffer;
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, function( e ) {
console.log( e );
});
} else {
_this.buffer = _this.context.createBuffer( req.response, false );
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
};
req.send();
this.proc = this.context.createJavaScriptNode( SAMPLE_SIZE / 2, 1, 1 );
this.proc.onaudioprocess = function ( e ) {
_this.update.call( _this, e );
};
this.proc.connect( this.context.destination );
this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE );
this.signal = new Float32Array( SAMPLE_SIZE / 2 );
},
play : function () {
var _this = this;
this.isLoaded ? play() : this.dancer.bind( 'loaded', play );
function play () {
if ( _this.isDisconnected ) {
connectContext.call( _this );
}
_this.source.noteOn( 0.0 );
_this.startTime = _this.context.currentTime;
_this.isPlaying = true;
}
},
stop : function () {
if ( this.isPlaying ) {
this.source.noteOff( 0.0 );
this.isDisconnected = true;
this.endTime = this.getTime();
}
this.isPlaying = false;
},
getWaveform : function () {
return this.signal;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.isPlaying ?
this.context.currentTime - ( this.startTime || 0 ) :
this.endTime || 0;
},
update : function ( e ) {
if ( !this.isPlaying ) { return; }
var
buffers = [],
channels = e.inputBuffer.numberOfChannels,
resolution = SAMPLE_SIZE / channels,
i;
for ( i = channels; i--; ) {
buffers.push( e.inputBuffer.getChannelData( i ) );
}
for ( i = 0; i < resolution; i++ ) {
this.signal[ i ] = channels > 1 ?
buffers.reduce(function ( prev, curr ) {
return prev[ i ] + curr[ i ];
}) / channels :
buffers[ 0 ][ i ];
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.webkit = adapter;
})();
(function() {
var adapter = function ( dancer ) {
this.dancer = dancer;
this.audio = new Audio();
this.isLoaded = this.isPlaying = false;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.audio.src = path;
this.audio.addEventListener( 'loadedmetadata', function( e ) {
_this.fbLength = _this.audio.mozFrameBufferLength;
_this.channels = _this.audio.mozChannels;
_this.rate = _this.audio.mozSampleRate;
_this.fft = new FFT( _this.fbLength / _this.channels, _this.rate );
_this.signal = new Float32Array( _this.fbLength / _this.channels );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, false);
this.audio.addEventListener( 'MozAudioAvailable', function( e ) {
_this.update( e );
}, false);
},
play : function () {
this.audio.play();
this.isPlaying = true;
},
stop : function () {
this.audio.pause();
this.isPlaying = false;
},
getWaveform : function () {
return this.signal;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.currentTime;
},
update : function ( e ) {
if ( !this.isLoaded ) return;
for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) {
this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.moz = adapter;
})();
(function() {
var
SAMPLE_SIZE = 1024,
SAMPLE_RATE = 44100,
smLoaded = false,
smLoading = false,
CONVERSION_COEFFICIENT = 0.93;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.isLoaded = this.isPlaying = false;
this.wave_L = [];
this.wave_R = [];
this.spectrum = [];
window.SM2_DEFER = true;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.path = path || this.path;
!window.soundManager && !smLoading && loadSM.call( this );
if ( window.soundManager ) {
this.audio = soundManager.createSound({
id : 'dancer' + Math.random() + '',
url : this.path,
stream : true,
autoPlay : false,
autoLoad : true,
whileplaying : function () {
_this.update();
},
onload : function () {
_this.fft = new FFT( SAMPLE_SIZE, SAMPLE_RATE );
_this.signal = new Float32Array( SAMPLE_SIZE );
_this.waveform = new Float32Array( SAMPLE_SIZE );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
});
}
},
play : function () {
if ( !this.isPlaying && this.isLoaded ) {
this.audio.play();
this.isPlaying = true;
}
},
stop : function () {
this.audio.stop();
this.isPlaying = false;
},
getWaveform : function () {
return this.waveform;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.position / 1000;
},
update : function () {
if ( !this.isLoaded ) return;
this.wave_L = this.audio.waveformData.left;
this.wave_R = this.audio.waveformData.right;
var avg;
for ( var i = 0, j = this.wave_L.length; i < j; i++ ) {
avg = parseFloat(this.wave_L[ i ]) + parseFloat(this.wave_R[ i ]);
this.waveform[ 2 * i ] = avg / 2;
this.waveform[ i * 2 + 1 ] = avg / 2;
this.signal[ 2 * i ] = avg * CONVERSION_COEFFICIENT;
this.signal[ i * 2 + 1 ] = avg * CONVERSION_COEFFICIENT;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
function loadSM () {
var adapter = this;
smLoading = true;
loadScript( Dancer.options.flashJS, function () {
soundManager = new SoundManager();
soundManager.flashVersion = 9;
soundManager.flash9Options.useWaveformData = true;
soundManager.useWaveformData = true;
soundManager.useHighPerformance = true;
soundManager.useFastPolling = true;
soundManager.multiShot = false;
soundManager.debugMode = false;
soundManager.debugFlash = false;
soundManager.url = Dancer.options.flashSWF;
soundManager.onready(function () {
smLoaded = true;
adapter.load();
});
soundManager.ontimeout(function(){
console.error( 'Error loading SoundManager2.swf' );
});
soundManager.beginDelayedInit();
});
}
function loadScript ( url, callback ) {
var
script = document.createElement( 'script' ),
appender = document.getElementsByTagName( 'script' )[0];
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
appender.parentNode.insertBefore( script, appender );
}
Dancer.adapters.flash = adapter;
})();
/*
* DSP.js - a comprehensive digital signal processing library for javascript
*
* Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
* Copyright 2010 Corban Brook. All rights reserved.
*
*/
// Fourier Transform Module used by DFT, FFT, RFFT
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;
/**
* Calculates the *middle* frequency of an FFT band.
*
* @param {Number} index The index of the FFT band.
*
* @returns The middle frequency in Hz.
*/
this.getBandFrequency = function(index) {
return this.bandwidth * index + this.bandwidth / 2;
};
this.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 = 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;
}
};
}
/**
* FFT is a class for calculating the Discrete Fourier Transform of a signal
* with the Fast Fourier Transform algorithm.
*
* @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
* @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
*
* @constructor
*/
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);
}
}
/**
* Performs a forward transform on the sample buffer.
* Converts a time domain signal to frequency domain spectra.
*
* @param {Array} buffer The sample buffer. Buffer Length must be power of 2
*
* @returns The frequency spectrum array
*/
FFT.prototype.forward = function(buffer) {
// Locally scope variables for speed up
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 = Math.cos(-Math.PI/halfSize);
//phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
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();
};
/*
Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
Code licensed under the BSD License: http://www.featureblend.com/license.txt
Version: 1.0.4
*/
var FlashDetect = new function(){
var self = this;
self.installed = false;
self.raw = "";
self.major = -1;
self.minor = -1;
self.revision = -1;
self.revisionStr = "";
var activeXDetectRules = [
{
"name":"ShockwaveFlash.ShockwaveFlash.7",
"version":function(obj){
return getActiveXVersion(obj);
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash.6",
"version":function(obj){
var version = "6,0,21";
try{
obj.AllowScriptAccess = "always";
version = getActiveXVersion(obj);
}catch(err){}
return version;
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash",
"version":function(obj){
return getActiveXVersion(obj);
}
}
];
/**
* Extract the ActiveX version of the plugin.
*
* @param {Object} The flash ActiveX object.
* @type String
*/
var getActiveXVersion = function(activeXObj){
var version = -1;
try{
version = activeXObj.GetVariable("$version");
}catch(err){}
return version;
};
/**
* Try and retrieve an ActiveX object having a specified name.
*
* @param {String} name The ActiveX object name lookup.
* @return One of ActiveX object or a simple object having an attribute of activeXError with a value of true.
* @type Object
*/
var getActiveXObject = function(name){
var obj = -1;
try{
obj = new ActiveXObject(name);
}catch(err){
obj = {activeXError:true};
}
return obj;
};
/**
* Parse an ActiveX $version string into an object.
*
* @param {String} str The ActiveX Object GetVariable($version) return value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseActiveXVersion = function(str){
var versionArray = str.split(",");//replace with regex
return {
"raw":str,
"major":parseInt(versionArray[0].split(" ")[1], 10),
"minor":parseInt(versionArray[1], 10),
"revision":parseInt(versionArray[2], 10),
"revisionStr":versionArray[2]
};
};
/**
* Parse a standard enabledPlugin.description into an object.
*
* @param {String} str The enabledPlugin.description value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseStandardVersion = function(str){
var descParts = str.split(/ +/);
var majorMinor = descParts[2].split(/\./);
var revisionStr = descParts[3];
return {
"raw":str,
"major":parseInt(majorMinor[0], 10),
"minor":parseInt(majorMinor[1], 10),
"revisionStr":revisionStr,
"revision":parseRevisionStrToInt(revisionStr)
};
};
/**
* Parse the plugin revision string into an integer.
*
* @param {String} The revision in string format.
* @type Number
*/
var parseRevisionStrToInt = function(str){
return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision;
};
/**
* Is the major version greater than or equal to a specified version.
*
* @param {Number} version The minimum required major version.
* @type Boolean
*/
self.majorAtLeast = function(version){
return self.major >= version;
};
/**
* Is the minor version greater than or equal to a specified version.
*
* @param {Number} version The minimum required minor version.
* @type Boolean
*/
self.minorAtLeast = function(version){
return self.minor >= version;
};
/**
* Is the revision version greater than or equal to a specified version.
*
* @param {Number} version The minimum required revision version.
* @type Boolean
*/
self.revisionAtLeast = function(version){
return self.revision >= version;
};
/**
* Is the version greater than or equal to a specified major, minor and revision.
*
* @param {Number} major The minimum required major version.
* @param {Number} (Optional) minor The minimum required minor version.
* @param {Number} (Optional) revision The minimum required revision version.
* @type Boolean
*/
self.versionAtLeast = function(major){
var properties = [self.major, self.minor, self.revision];
var len = Math.min(properties.length, arguments.length);
for(i=0; i<len; i++){
if(properties[i]>=arguments[i]){
if(i+1<len && properties[i]==arguments[i]){
continue;
}else{
return true;
}
}else{
return false;
}
}
};
/**
* Constructor, sets raw, major, minor, revisionStr, revision and installed public properties.
*/
self.FlashDetect = function(){
if(navigator.plugins && navigator.plugins.length>0){
var type = 'application/x-shockwave-flash';
var mimeTypes = navigator.mimeTypes;
if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
var version = mimeTypes[type].enabledPlugin.description;
var versionObj = parseStandardVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revisionStr = versionObj.revisionStr;
self.revision = versionObj.revision;
self.installed = true;
}
}else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
var version = -1;
for(var i=0; i<activeXDetectRules.length && version==-1; i++){
var obj = getActiveXObject(activeXDetectRules[i].name);
if(!obj.activeXError){
self.installed = true;
version = activeXDetectRules[i].version(obj);
if(version!=-1){
var versionObj = parseActiveXVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revision = versionObj.revision;
self.revisionStr = versionObj.revisionStr;
}
}
}
}
}();
};
FlashDetect.JS_RELEASE = "1.0.4";
File diff suppressed because one or more lines are too long
+995
View File
@@ -0,0 +1,995 @@
/*
* Dancer.js (c) 2012 Jordan Santell
* MIT License
* http://github.com/jsantell/dancer.js
*
* v0.2.1
*/
(function() {
var Dancer = function ( source, codecs ) {
this.audioAdapter = Dancer._getAdapter( this );
this.events = {};
this.sections = [];
this.bind( 'update', update );
this.source = Dancer._makeSupportedPath( source, codecs );
this.audioAdapter.load( this.source );
};
Dancer.adapters = {};
Dancer.prototype = {
/* Controls */
play : function () {
this.audioAdapter.play();
return this;
},
stop : function () {
this.audioAdapter.stop();
return this;
},
/* Actions */
createBeat : function ( freq, threshold, decay, onBeat, offBeat ) {
return new Dancer.Beat( this, freq, threshold, decay, onBeat, offBeat );
},
bind : function ( name, callback ) {
if ( !this.events[ name ] ) {
this.events[ name ] = [];
}
this.events[ name ].push( callback );
return this;
},
unbind : function ( name ) {
if ( this.events[ name ] ) {
delete this.events[ name ];
}
return this;
},
trigger : function ( name ) {
var _this = this;
if ( this.events[ name ] ) {
this.events[ name ].forEach(function( callback ) {
callback.call( _this );
});
}
return this;
},
/* Getters */
getTime : function () {
return this.audioAdapter.getTime();
},
// Returns the magnitude of a frequency or average over a range of frequencies
getFrequency : function ( freq, endFreq ) {
var sum = 0;
if ( endFreq !== undefined ) {
for ( var i = freq; i <= endFreq; i++ ) {
sum += this.getSpectrum()[ i ];
}
return sum / ( endFreq - freq + 1 );
} else {
return this.getSpectrum()[ freq ];
}
},
getWaveform : function () {
return this.audioAdapter.getWaveform();
},
getSpectrum : function () {
return this.audioAdapter.getSpectrum();
},
isLoaded : function () {
return this.audioAdapter.isLoaded;
},
isPlaying : function () {
return this.audioAdapter.isPlaying;
},
/* Sections */
after : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > time;
},
callback : callback
});
return this;
},
before : function ( time, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() < time;
},
callback : callback
});
return this;
},
between : function ( startTime, endTime, callback ) {
var _this = this;
this.sections.push({
condition : function () {
return _this.getTime() > startTime && _this.getTime() < endTime;
},
callback : callback
});
return this;
},
onceAt : function ( time, callback ) {
var
_this = this,
thisSection = null;
this.sections.push({
condition : function () {
return _this.getTime() > time && !this.called;
},
callback : function () {
callback.call( this );
thisSection.called = true;
},
called : false
});
// Baking the section in the closure due to callback's this being the dancer instance
thisSection = this.sections[ this.sections.length - 1 ];
return this;
}
};
function update () {
for ( var i in this.sections ) {
if ( this.sections[ i ].condition() )
this.sections[ i ].callback.call( this );
}
}
window.Dancer = Dancer;
})();
(function ( Dancer ) {
var CODECS = {
'mp3' : 'audio/mpeg;',
'ogg' : 'audio/ogg; codecs="vorbis"',
'wav' : 'audio/wav; codecs="1"',
'aac' : 'audio/mp4; codecs="mp4a.40.2"'
},
audioEl = document.createElement( 'audio' );
Dancer.options = {};
Dancer.setOptions = function ( o ) {
for ( var option in o ) {
if ( o.hasOwnProperty( option ) ) {
Dancer.options[ option ] = o[ option ];
}
}
};
Dancer.isSupported = function () {
if ( !window.Float32Array || !window.Uint32Array ) {
return null;
} else if ( window.AudioContext || window.webkitAudioContext ) {
return 'webaudio';
} else if ( window.Audio && ( new window.Audio() ).mozSetup ) {
return 'audiodata';
} else if ( FlashDetect.versionAtLeast( 9 ) ) {
return 'flash';
} else {
return '';
}
};
Dancer.canPlay = function ( type ) {
var canPlay = audioEl.canPlayType;
return !!(
Dancer.isSupported() === 'flash' ?
type.toLowerCase() === 'mp3' :
audioEl.canPlayType &&
audioEl.canPlayType( CODECS[ type.toLowerCase() ] ).replace( /no/, ''));
};
Dancer.addPlugin = function ( name, fn ) {
if ( Dancer.prototype[ name ] === undefined ) {
Dancer.prototype[ name ] = fn;
}
};
Dancer._makeSupportedPath = function ( source, codecs ) {
if ( !codecs ) { return source; }
for ( var i = 0; i < codecs.length; i++ ) {
if ( Dancer.canPlay( codecs[ i ] ) ) {
return source + '.' + codecs[ i ];
}
}
return source;
};
Dancer._getAdapter = function ( instance ) {
switch ( Dancer.isSupported() ) {
case 'webaudio':
return new Dancer.adapters.webkit( instance );
case 'audiodata':
return new Dancer.adapters.moz( instance );
case 'flash':
return new Dancer.adapters.flash( instance );
default:
return null;
}
};
})( window.Dancer );
(function() {
var Beat = function ( dancer, options ) {
options = options || {};
this.dancer = dancer;
this.frequency = options.frequency || [ 0, 10 ];
this.threshold = options.threshold || 0.3;
this.decay = options.decay || 0.02;
this.onBeat = options.onBeat;
this.offBeat = options.offBeat;
this.isOn = false;
this.currentThreshold = this.threshold;
var _this = this;
this.dancer.bind( 'update', function () {
_this.onUpdate();
});
};
Beat.prototype = {
on : function () {
this.isOn = true;
return this;
},
off : function () {
this.isOn = false;
return this;
},
onUpdate : function () {
if ( !this.isOn ) { return; }
var magnitude = this.maxAmplitude( this.frequency );
if ( magnitude >= this.currentThreshold &&
magnitude >= this.threshold ) {
this.currentThreshold = magnitude;
this.onBeat && this.onBeat.call( this.dancer, magnitude );
} else {
this.offBeat && this.offBeat.call( this.dancer, magnitude );
this.currentThreshold -= this.decay;
}
},
maxAmplitude : function ( frequency ) {
var
max = 0,
fft = this.dancer.getSpectrum();
// Sloppy array check
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;
}
};
window.Dancer.Beat = Beat;
})();
(function() {
var
SAMPLE_SIZE = 2048,
SAMPLE_RATE = 44100;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.context = window.AudioContext ?
new window.AudioContext() :
new window.webkitAudioContext();
this.isLoaded = false;
this.isPlaying = false;
this.isDisconnected = false;
};
function connectContext () {
this.source = this.context.createBufferSource();
this.source.buffer = this.buffer;
this.source.connect( this.context.destination );
this.source.connect( this.proc );
this.source.connect( this.context.destination );
}
adapter.prototype = {
load : function ( path ) {
var
req = new XMLHttpRequest(),
_this = this;
req.open( 'GET', path, true );
req.responseType = 'arraybuffer';
req.onload = function () {
if ( _this.context.decodeAudioData ) {
_this.context.decodeAudioData( req.response, function( buffer ) {
_this.buffer = buffer;
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, function( e ) {
console.log( e );
});
} else {
_this.buffer = _this.context.createBuffer( req.response, false );
connectContext.call( _this );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
};
req.send();
this.proc = this.context.createJavaScriptNode( SAMPLE_SIZE / 2, 1, 1 );
this.proc.onaudioprocess = function ( e ) {
_this.update.call( _this, e );
};
this.proc.connect( this.context.destination );
this.fft = new FFT( SAMPLE_SIZE / 2, SAMPLE_RATE );
this.signal = new Float32Array( SAMPLE_SIZE / 2 );
},
play : function () {
var _this = this;
this.isLoaded ? play() : this.dancer.bind( 'loaded', play );
function play () {
if ( _this.isDisconnected ) {
connectContext.call( _this );
}
_this.source.noteOn( 0.0 );
_this.startTime = _this.context.currentTime;
_this.isPlaying = true;
}
},
stop : function () {
if ( this.isPlaying ) {
this.source.noteOff( 0.0 );
this.isDisconnected = true;
this.endTime = this.getTime();
}
this.isPlaying = false;
},
getWaveform : function () {
return this.signal;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.isPlaying ?
this.context.currentTime - ( this.startTime || 0 ) :
this.endTime || 0;
},
update : function ( e ) {
if ( !this.isPlaying ) { return; }
var
buffers = [],
channels = e.inputBuffer.numberOfChannels,
resolution = SAMPLE_SIZE / channels,
i;
for ( i = channels; i--; ) {
buffers.push( e.inputBuffer.getChannelData( i ) );
}
for ( i = 0; i < resolution; i++ ) {
this.signal[ i ] = channels > 1 ?
buffers.reduce(function ( prev, curr ) {
return prev[ i ] + curr[ i ];
}) / channels :
buffers[ 0 ][ i ];
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.webkit = adapter;
})();
(function() {
var adapter = function ( dancer ) {
this.dancer = dancer;
this.audio = new Audio();
this.isLoaded = this.isPlaying = false;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.audio.src = path;
this.audio.addEventListener( 'loadedmetadata', function( e ) {
_this.fbLength = _this.audio.mozFrameBufferLength;
_this.channels = _this.audio.mozChannels;
_this.rate = _this.audio.mozSampleRate;
_this.fft = new FFT( _this.fbLength / _this.channels, _this.rate );
_this.signal = new Float32Array( _this.fbLength / _this.channels );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}, false);
this.audio.addEventListener( 'MozAudioAvailable', function( e ) {
_this.update( e );
}, false);
},
play : function () {
this.audio.play();
this.isPlaying = true;
},
stop : function () {
this.audio.pause();
this.isPlaying = false;
},
getWaveform : function () {
return this.signal;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.currentTime;
},
update : function ( e ) {
if ( !this.isLoaded ) return;
for ( var i = 0, j = this.fbLength / 2; i < j; i++ ) {
this.signal[ i ] = ( e.frameBuffer[ 2 * i ] + e.frameBuffer[ 2 * i + 1 ] ) / 2;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
Dancer.adapters.moz = adapter;
})();
(function() {
var
SAMPLE_SIZE = 1024,
SAMPLE_RATE = 44100,
smLoaded = false,
smLoading = false,
CONVERSION_COEFFICIENT = 0.93;
var adapter = function ( dancer ) {
this.dancer = dancer;
this.isLoaded = this.isPlaying = false;
this.wave_L = [];
this.wave_R = [];
this.spectrum = [];
window.SM2_DEFER = true;
};
adapter.prototype = {
load : function ( path ) {
var _this = this;
this.path = path || this.path;
!window.soundManager && !smLoading && loadSM.call( this );
if ( window.soundManager ) {
this.audio = soundManager.createSound({
id : 'dancer' + Math.random() + '',
url : this.path,
stream : true,
autoPlay : false,
autoLoad : true,
whileplaying : function () {
_this.update();
},
onload : function () {
_this.fft = new FFT( SAMPLE_SIZE, SAMPLE_RATE );
_this.signal = new Float32Array( SAMPLE_SIZE );
_this.waveform = new Float32Array( SAMPLE_SIZE );
_this.isLoaded = true;
_this.dancer.trigger( 'loaded' );
}
});
}
},
play : function () {
if ( !this.isPlaying && this.isLoaded ) {
this.audio.play();
this.isPlaying = true;
}
},
stop : function () {
this.audio.stop();
this.isPlaying = false;
},
getWaveform : function () {
return this.waveform;
},
getSpectrum : function () {
return this.fft.spectrum;
},
getTime : function () {
return this.audio.position / 1000;
},
update : function () {
if ( !this.isLoaded ) return;
this.wave_L = this.audio.waveformData.left;
this.wave_R = this.audio.waveformData.right;
var avg;
for ( var i = 0, j = this.wave_L.length; i < j; i++ ) {
avg = parseFloat(this.wave_L[ i ]) + parseFloat(this.wave_R[ i ]);
this.waveform[ 2 * i ] = avg / 2;
this.waveform[ i * 2 + 1 ] = avg / 2;
this.signal[ 2 * i ] = avg * CONVERSION_COEFFICIENT;
this.signal[ i * 2 + 1 ] = avg * CONVERSION_COEFFICIENT;
}
this.fft.forward( this.signal );
this.dancer.trigger( 'update' );
}
};
function loadSM () {
var adapter = this;
smLoading = true;
loadScript( Dancer.options.flashJS, function () {
soundManager = new SoundManager();
soundManager.flashVersion = 9;
soundManager.flash9Options.useWaveformData = true;
soundManager.useWaveformData = true;
soundManager.useHighPerformance = true;
soundManager.useFastPolling = true;
soundManager.multiShot = false;
soundManager.debugMode = false;
soundManager.debugFlash = false;
soundManager.url = Dancer.options.flashSWF;
soundManager.onready(function () {
smLoaded = true;
adapter.load();
});
soundManager.ontimeout(function(){
console.error( 'Error loading SoundManager2.swf' );
});
soundManager.beginDelayedInit();
});
}
function loadScript ( url, callback ) {
var
script = document.createElement( 'script' ),
appender = document.getElementsByTagName( 'script' )[0];
script.type = 'text/javascript';
script.src = url;
script.onload = callback;
appender.parentNode.insertBefore( script, appender );
}
Dancer.adapters.flash = adapter;
})();
/*
* DSP.js - a comprehensive digital signal processing library for javascript
*
* Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
* Copyright 2010 Corban Brook. All rights reserved.
*
*/
// Fourier Transform Module used by DFT, FFT, RFFT
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;
/**
* Calculates the *middle* frequency of an FFT band.
*
* @param {Number} index The index of the FFT band.
*
* @returns The middle frequency in Hz.
*/
this.getBandFrequency = function(index) {
return this.bandwidth * index + this.bandwidth / 2;
};
this.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 = 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;
}
};
}
/**
* FFT is a class for calculating the Discrete Fourier Transform of a signal
* with the Fast Fourier Transform algorithm.
*
* @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
* @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
*
* @constructor
*/
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);
}
}
/**
* Performs a forward transform on the sample buffer.
* Converts a time domain signal to frequency domain spectra.
*
* @param {Array} buffer The sample buffer. Buffer Length must be power of 2
*
* @returns The frequency spectrum array
*/
FFT.prototype.forward = function(buffer) {
// Locally scope variables for speed up
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 = Math.cos(-Math.PI/halfSize);
//phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
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();
};
/*
Copyright (c) Copyright (c) 2007, Carl S. Yestrau All rights reserved.
Code licensed under the BSD License: http://www.featureblend.com/license.txt
Version: 1.0.4
*/
var FlashDetect = new function(){
var self = this;
self.installed = false;
self.raw = "";
self.major = -1;
self.minor = -1;
self.revision = -1;
self.revisionStr = "";
var activeXDetectRules = [
{
"name":"ShockwaveFlash.ShockwaveFlash.7",
"version":function(obj){
return getActiveXVersion(obj);
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash.6",
"version":function(obj){
var version = "6,0,21";
try{
obj.AllowScriptAccess = "always";
version = getActiveXVersion(obj);
}catch(err){}
return version;
}
},
{
"name":"ShockwaveFlash.ShockwaveFlash",
"version":function(obj){
return getActiveXVersion(obj);
}
}
];
/**
* Extract the ActiveX version of the plugin.
*
* @param {Object} The flash ActiveX object.
* @type String
*/
var getActiveXVersion = function(activeXObj){
var version = -1;
try{
version = activeXObj.GetVariable("$version");
}catch(err){}
return version;
};
/**
* Try and retrieve an ActiveX object having a specified name.
*
* @param {String} name The ActiveX object name lookup.
* @return One of ActiveX object or a simple object having an attribute of activeXError with a value of true.
* @type Object
*/
var getActiveXObject = function(name){
var obj = -1;
try{
obj = new ActiveXObject(name);
}catch(err){
obj = {activeXError:true};
}
return obj;
};
/**
* Parse an ActiveX $version string into an object.
*
* @param {String} str The ActiveX Object GetVariable($version) return value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseActiveXVersion = function(str){
var versionArray = str.split(",");//replace with regex
return {
"raw":str,
"major":parseInt(versionArray[0].split(" ")[1], 10),
"minor":parseInt(versionArray[1], 10),
"revision":parseInt(versionArray[2], 10),
"revisionStr":versionArray[2]
};
};
/**
* Parse a standard enabledPlugin.description into an object.
*
* @param {String} str The enabledPlugin.description value.
* @return An object having raw, major, minor, revision and revisionStr attributes.
* @type Object
*/
var parseStandardVersion = function(str){
var descParts = str.split(/ +/);
var majorMinor = descParts[2].split(/\./);
var revisionStr = descParts[3];
return {
"raw":str,
"major":parseInt(majorMinor[0], 10),
"minor":parseInt(majorMinor[1], 10),
"revisionStr":revisionStr,
"revision":parseRevisionStrToInt(revisionStr)
};
};
/**
* Parse the plugin revision string into an integer.
*
* @param {String} The revision in string format.
* @type Number
*/
var parseRevisionStrToInt = function(str){
return parseInt(str.replace(/[a-zA-Z]/g, ""), 10) || self.revision;
};
/**
* Is the major version greater than or equal to a specified version.
*
* @param {Number} version The minimum required major version.
* @type Boolean
*/
self.majorAtLeast = function(version){
return self.major >= version;
};
/**
* Is the minor version greater than or equal to a specified version.
*
* @param {Number} version The minimum required minor version.
* @type Boolean
*/
self.minorAtLeast = function(version){
return self.minor >= version;
};
/**
* Is the revision version greater than or equal to a specified version.
*
* @param {Number} version The minimum required revision version.
* @type Boolean
*/
self.revisionAtLeast = function(version){
return self.revision >= version;
};
/**
* Is the version greater than or equal to a specified major, minor and revision.
*
* @param {Number} major The minimum required major version.
* @param {Number} (Optional) minor The minimum required minor version.
* @param {Number} (Optional) revision The minimum required revision version.
* @type Boolean
*/
self.versionAtLeast = function(major){
var properties = [self.major, self.minor, self.revision];
var len = Math.min(properties.length, arguments.length);
for(i=0; i<len; i++){
if(properties[i]>=arguments[i]){
if(i+1<len && properties[i]==arguments[i]){
continue;
}else{
return true;
}
}else{
return false;
}
}
};
/**
* Constructor, sets raw, major, minor, revisionStr, revision and installed public properties.
*/
self.FlashDetect = function(){
if(navigator.plugins && navigator.plugins.length>0){
var type = 'application/x-shockwave-flash';
var mimeTypes = navigator.mimeTypes;
if(mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description){
var version = mimeTypes[type].enabledPlugin.description;
var versionObj = parseStandardVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revisionStr = versionObj.revisionStr;
self.revision = versionObj.revision;
self.installed = true;
}
}else if(navigator.appVersion.indexOf("Mac")==-1 && window.execScript){
var version = -1;
for(var i=0; i<activeXDetectRules.length && version==-1; i++){
var obj = getActiveXObject(activeXDetectRules[i].name);
if(!obj.activeXError){
self.installed = true;
version = activeXDetectRules[i].version(obj);
if(version!=-1){
var versionObj = parseActiveXVersion(version);
self.raw = versionObj.raw;
self.major = versionObj.major;
self.minor = versionObj.minor;
self.revision = versionObj.revision;
self.revisionStr = versionObj.revisionStr;
}
}
}
}
}();
};
FlashDetect.JS_RELEASE = "1.0.4";
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+22
View File
@@ -0,0 +1,22 @@
{
"name": "dancer.js",
"filename": "dancer.min.js",
"version": "0.3.1",
"description": "high-level audio API, designed to make sweet visualizations",
"homepage": "http://jsantell.github.com/dancer.js",
"keywords": [
"audio"
],
"maintainers": [
{
"name": "Jordan Santell",
"email": "jsantell@gmail.com"
}
],
"repositories": [
{
"type": "git",
"url": "https://github.com/jsantell/dancer.js"
}
]
}