2015-08-10 18:40:20 +00:00
|
|
|
define(["Tone/core/Tone"], function (Tone) {
|
|
|
|
|
2015-10-21 16:11:19 +00:00
|
|
|
"use strict";
|
|
|
|
|
2015-08-10 18:40:20 +00:00
|
|
|
/**
|
|
|
|
* @class Wrapper around the native Web Audio's
|
|
|
|
* [AnalyserNode](http://webaudio.github.io/web-audio-api/#idl-def-AnalyserNode).
|
|
|
|
* Extracts FFT or Waveform data from the incoming signal.
|
|
|
|
* @extends {Tone}
|
|
|
|
* @param {Number=} size The size of the FFT. Value must be a power of
|
|
|
|
* two in the range 32 to 32768.
|
2015-09-11 21:05:27 +00:00
|
|
|
* @param {String=} type The return type of the analysis, either "fft", or "waveform".
|
2015-08-10 18:40:20 +00:00
|
|
|
*/
|
|
|
|
Tone.Analyser = function(){
|
|
|
|
|
|
|
|
var options = this.optionsObject(arguments, ["size", "type"], Tone.Analyser.defaults);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The analyser node.
|
|
|
|
* @private
|
|
|
|
* @type {AnalyserNode}
|
|
|
|
*/
|
|
|
|
this._analyser = this.input = this.context.createAnalyser();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The analysis type
|
|
|
|
* @type {String}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._type = options.type;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The return type of the analysis
|
|
|
|
* @type {String}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._returnType = options.returnType;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The buffer that the FFT data is written to
|
|
|
|
* @type {TypedArray}
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
this._buffer = null;
|
|
|
|
|
|
|
|
//set the values initially
|
|
|
|
this.size = options.size;
|
|
|
|
this.type = options.type;
|
|
|
|
this.returnType = options.returnType;
|
|
|
|
this.minDecibels = options.minDecibels;
|
|
|
|
this.maxDecibels = options.maxDecibels;
|
|
|
|
};
|
|
|
|
|
|
|
|
Tone.extend(Tone.Analyser);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The default values.
|
|
|
|
* @type {Object}
|
|
|
|
* @const
|
|
|
|
*/
|
|
|
|
Tone.Analyser.defaults = {
|
2016-03-04 23:30:15 +00:00
|
|
|
"size" : 1024,
|
2015-08-10 18:40:20 +00:00
|
|
|
"returnType" : "byte",
|
|
|
|
"type" : "fft",
|
|
|
|
"smoothing" : 0.8,
|
|
|
|
"maxDecibels" : -30,
|
|
|
|
"minDecibels" : -100
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-04-17 17:41:54 +00:00
|
|
|
* Possible return types of Tone.Analyser.analyse()
|
2015-08-10 18:40:20 +00:00
|
|
|
* @enum {String}
|
|
|
|
*/
|
|
|
|
Tone.Analyser.Type = {
|
|
|
|
Waveform : "waveform",
|
|
|
|
FFT : "fft"
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2016-04-17 17:41:54 +00:00
|
|
|
* Possible return types of Tone.Analyser.analyse().
|
|
|
|
* byte values are between [0,255]. float values are between
|
|
|
|
* [-1, 1] when the type is set to "waveform" and between
|
|
|
|
* [minDecibels,maxDecibels] when the type is "fft".
|
2015-08-10 18:40:20 +00:00
|
|
|
* @enum {String}
|
|
|
|
*/
|
|
|
|
Tone.Analyser.ReturnType = {
|
|
|
|
Byte : "byte",
|
|
|
|
Float : "float"
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Run the analysis given the current settings and return the
|
|
|
|
* result as a TypedArray.
|
|
|
|
* @returns {TypedArray}
|
|
|
|
*/
|
|
|
|
Tone.Analyser.prototype.analyse = function(){
|
|
|
|
if (this._type === Tone.Analyser.Type.FFT){
|
|
|
|
if (this._returnType === Tone.Analyser.ReturnType.Byte){
|
|
|
|
this._analyser.getByteFrequencyData(this._buffer);
|
|
|
|
} else {
|
|
|
|
this._analyser.getFloatFrequencyData(this._buffer);
|
|
|
|
}
|
|
|
|
} else if (this._type === Tone.Analyser.Type.Waveform){
|
|
|
|
if (this._returnType === Tone.Analyser.ReturnType.Byte){
|
|
|
|
this._analyser.getByteTimeDomainData(this._buffer);
|
|
|
|
} else {
|
2016-03-20 23:01:39 +00:00
|
|
|
if (this.isFunction(AnalyserNode.prototype.getFloatTimeDomainData)){
|
|
|
|
this._analyser.getFloatTimeDomainData(this._buffer);
|
|
|
|
} else {
|
|
|
|
var uint8 = new Uint8Array(this._buffer.length);
|
|
|
|
this._analyser.getByteTimeDomainData(uint8);
|
|
|
|
//referenced https://github.com/mohayonao/get-float-time-domain-data
|
|
|
|
// POLYFILL
|
|
|
|
for (var i = 0; i < uint8.length; i++){
|
|
|
|
this._buffer[i] = (uint8[i] - 128) * 0.0078125;
|
|
|
|
}
|
|
|
|
}
|
2015-08-10 18:40:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return this._buffer;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of analysis. This must be a power of two in the range 32 to 32768.
|
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {Number}
|
|
|
|
* @name size
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "size", {
|
|
|
|
get : function(){
|
|
|
|
return this._analyser.frequencyBinCount;
|
|
|
|
},
|
|
|
|
set : function(size){
|
|
|
|
this._analyser.fftSize = size * 2;
|
|
|
|
this.type = this._type;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2016-04-17 17:41:54 +00:00
|
|
|
* The return type of Tone.Analyser.analyse(), either "byte" or "float".
|
2015-08-10 18:40:20 +00:00
|
|
|
* When the type is set to "byte" the range of values returned in the array
|
2016-04-17 17:41:54 +00:00
|
|
|
* are between 0-255. "float" values are between
|
|
|
|
* [-1, 1] when the type is set to "waveform" and between
|
|
|
|
* [minDecibels,maxDecibels] when the type is "fft".
|
2015-08-10 18:40:20 +00:00
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {String}
|
|
|
|
* @name type
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "returnType", {
|
|
|
|
get : function(){
|
|
|
|
return this._returnType;
|
|
|
|
},
|
|
|
|
set : function(type){
|
|
|
|
if (type === Tone.Analyser.ReturnType.Byte){
|
|
|
|
this._buffer = new Uint8Array(this._analyser.frequencyBinCount);
|
|
|
|
} else if (type === Tone.Analyser.ReturnType.Float){
|
|
|
|
this._buffer = new Float32Array(this._analyser.frequencyBinCount);
|
|
|
|
} else {
|
|
|
|
throw new Error("Invalid Return Type: "+type);
|
|
|
|
}
|
|
|
|
this._returnType = type;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
2016-04-17 17:41:54 +00:00
|
|
|
* The analysis function returned by Tone.Analyser.analyse(), either "fft" or "waveform".
|
2015-08-10 18:40:20 +00:00
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {String}
|
|
|
|
* @name type
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "type", {
|
|
|
|
get : function(){
|
|
|
|
return this._type;
|
|
|
|
},
|
|
|
|
set : function(type){
|
|
|
|
if (type !== Tone.Analyser.Type.Waveform && type !== Tone.Analyser.Type.FFT){
|
|
|
|
throw new Error("Invalid Type: "+type);
|
|
|
|
}
|
|
|
|
this._type = type;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 0 represents no time averaging with the last analysis frame.
|
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {NormalRange}
|
|
|
|
* @name smoothing
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "smoothing", {
|
|
|
|
get : function(){
|
|
|
|
return this._analyser.smoothingTimeConstant;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
this._analyser.smoothingTimeConstant = val;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The smallest decibel value which is analysed by the FFT.
|
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {Decibels}
|
|
|
|
* @name minDecibels
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "minDecibels", {
|
|
|
|
get : function(){
|
|
|
|
return this._analyser.minDecibels;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
this._analyser.minDecibels = val;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The largest decibel value which is analysed by the FFT.
|
|
|
|
* @memberOf Tone.Analyser#
|
|
|
|
* @type {Decibels}
|
|
|
|
* @name maxDecibels
|
|
|
|
*/
|
|
|
|
Object.defineProperty(Tone.Analyser.prototype, "maxDecibels", {
|
|
|
|
get : function(){
|
|
|
|
return this._analyser.maxDecibels;
|
|
|
|
},
|
|
|
|
set : function(val){
|
|
|
|
this._analyser.maxDecibels = val;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clean up.
|
|
|
|
* @return {Tone.Analyser} this
|
|
|
|
*/
|
|
|
|
Tone.Analyser.prototype.dispose = function(){
|
|
|
|
Tone.prototype.dispose.call(this);
|
|
|
|
this._analyser.disconnect();
|
|
|
|
this._analyser = null;
|
|
|
|
this._buffer = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
return Tone.Analyser;
|
|
|
|
});
|