binbin/desktop/visualizer.js

123 lines
5.7 KiB
JavaScript

var Visualizer = function() {
this.file = null, //the current file
this.fileName = null, //the current file name
this.audioContext = null,
this.source = null, //the audio source
this.info = document.getElementById('info').innerHTML, //this used to upgrade the UI information
this.infoUpdateId = null, //to sotore the setTimeout ID and clear the interval
this.animationId = null,
this.status = 0, //flag for sound is playing 1 or stopped 0
this.forceStop = false,
this.allCapsReachBottom = false
};
Visualizer.prototype = {
ini: function() {
this._prepareAPI();
},
_prepareAPI: function() {
//fix browser vender for AudioContext and requestAnimationFrame
window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext;
window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame;
try {
this.audioContext = new AudioContext();
} catch (e) {
alert('!Your browser does not support AudioContext', false);
console.log(e);
}
},
_visualize: function(url, canvas, audio) {
var audioContext = this.audioContext,
audioBufferSouceNode = audioContext.createMediaElementSource(audio),
analyser = audioContext.createAnalyser(),
that = this;
this.analyser = analyser;
this.canvas = canvas;
//connect the source to the analyser
audioBufferSouceNode.connect(analyser);
//connect the analyser to the destination(the speaker), or we won't hear the sound
analyser.connect(audioContext.destination);
//then assign the buffer to the buffer source node
//play the source
if (!audioBufferSouceNode.start) {
audioBufferSouceNode.start = audioBufferSouceNode.noteOn //in old browsers use noteOn method
audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //in old browsers use noteOn method
};
//stop the previous sound if any
if (this.animationId !== null) {
cancelAnimationFrame(this.animationId);
}
if (this.source !== null) {
this.source.stop(0);
}
//audioBufferSouceNode.start(0);
this.status = 1;
this.source = audioBufferSouceNode;
this._drawSpectrum(analyser, canvas);
},
_restart: function() {
this._drawSpectrum(this.analyser, this.canvas);
},
_stop: function() {
cancelAnimationFrame(this.animationId);
},
_drawSpectrum: function(analyser, canvas) {
var that = this,
monoL = canvas,
//monoR = document.getElementById('mono-R'),
cwidth = monoL.width,
cheight = monoL.height - 3,
meterWidth = 1, //width of the meters in the spectrum
gap = 0.01, //gap between meters
capHeight = 2,
capStyle = '#fff',
meterNum = 20 * (2 + 2), //count of the meters
capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame
ctx = monoL.getContext('2d'),
gradient = ctx.createLinearGradient(0, 0, 0, 300);
gradient.addColorStop(1, themeColors.highlight);
gradient.addColorStop(0, '#fff');
var drawMeter = function() {
var array = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(array);
if (that.status === 0) {
//fix when some sounds end the value still not back to zero
for (var i = array.length - 1; i >= 0; i--) {
array[i] = 0;
};
allCapsReachBottom = true;
for (var i = capYPositionArray.length - 1; i >= 0; i--) {
allCapsReachBottom = allCapsReachBottom && (capYPositionArray[i] === 0);
};
if (allCapsReachBottom) {
cancelAnimationFrame(that.animationId); //since the sound is top and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT!
return;
};
};
var step = Math.round(array.length / meterNum); //sample limited data from the total array
ctx.clearRect(0, 0, cwidth, cheight);
for (var i = 0; i < meterNum; i++) {
var value = array[i * step];
if (capYPositionArray.length < Math.round(meterNum)) {
capYPositionArray.push(value);
};
ctx.fillStyle = capStyle;
//draw the cap, with transition effect
if (value < capYPositionArray[i]) {
ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);
} else {
ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);
capYPositionArray[i] = value;
};
ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look
ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter
}
that.animationId = requestAnimationFrame(drawMeter);
}
this.animationId = requestAnimationFrame(drawMeter);
}
}