Add first draft of sine wave visualization

This commit is contained in:
Jordan Eldredge 2014-11-17 00:00:50 -08:00
parent 275b210313
commit b6f52424ce
5 changed files with 115 additions and 4 deletions

View file

@ -32,7 +32,7 @@
<div id='second-first-digit'></div>
<div id='second-second-digit'></div>
</div>
<div id='visualization'></div>
<canvas id='visualizer' width='150' height='32'></canvas>
</div>
<div class='media-info'>
<div id='song-title' class='text'></div>
@ -70,10 +70,10 @@
<a id='about' target='_blank' href='https://github.com/captbaritone/winamp2-js'></a>
</div>
<p style="position: absolute; bottom: 0;">by <a href='https://twitter.com/captbaritone'>@captbaritone</a> - <a href='https://github.com/captbaritone/winamp2-js'>GitHub</a></p>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.4.0/jszip.min.js"></script>
<script src="file-manager.js"></script>
<script src="visualizer.js"></script>
<script src="media.js"></script>
<script src="font.js"></script>
<script src="skin.js"></script>

View file

@ -7,6 +7,7 @@ Media = {
waiting: function(){},
playing: function(){},
timeupdate: function(){},
visualizerupdate: function(){},
ended: function(){}
},
_startTime: 0,
@ -17,6 +18,12 @@ Media = {
init: function() {
this._gainNode = this._context.createGain();
this._analyser = this._context.createAnalyser();
this._analyser.fftSize = 2048;
this._bufferLength = this._analyser.frequencyBinCount;
this._dataArray = new Uint8Array(this._bufferLength);
this._analyser.getByteTimeDomainData(this._dataArray);
return this;
},
@ -73,7 +80,8 @@ Media = {
this._source = this._context.createBufferSource();
if(this._buffer) {
this._source.buffer = this._buffer;
this._source.connect(this._gainNode);
this._source.connect(this._analyser);
this._analyser.connect(this._gainNode);
this._gainNode.connect(this._context.destination);
this._position = typeof position !== 'undefined' ? position : this._position;
@ -94,7 +102,6 @@ Media = {
stop: function() {
this._silence();
this._position = 0;
this._callbacks.ended();
},
_silence: function() {
@ -137,10 +144,18 @@ Media = {
this.play(time);
},
// There is probably a more reasonable way to do this, rather than having
// it always running.
_draw: function() {
if(this._playing) {
this._updatePosition();
this._callbacks.timeupdate();
// _updatePosition might have stoped the playing
if(this._playing) {
this._analyser.getByteTimeDomainData(this._dataArray);
this._callbacks.visualizerupdate(this._bufferLength, this._dataArray);
}
}
window.requestAnimationFrame(this._draw.bind(this));
},

68
visualizer.js Normal file
View file

@ -0,0 +1,68 @@
/* Use Canvas to recreate the simple Winamp visualizer */
Visualizer = {
init: function(canvasNode) {
this.canvas = canvasNode;
this.canvasCtx = this.canvas.getContext("2d");
this.canvasCtx.imageSmoothingEnabled= false;
this.canvasCtx.translate(1, 1); // http://stackoverflow.com/questions/13593527/canvas-make-the-line-thicker
this.width = this.canvas.width;
this.height = this.canvas.height;
this.NONE = 0;
this.OSCILLOSCOPE = 1;
this.BAR = 2;
return this;
},
clear: function() {
// +/- is just there to deal with offset, meh if its right or not ;)
this.canvasCtx.clearRect(-2, -2, this.width + 2, this.height + 2);
},
paintFrame: function(type, bufferLength, dataArray) {
this.clear();
if(type == this.OSCILLOSCOPE) {
return this._paintOscilloscopeFrame(bufferLength, dataArray);
} else if(type == this.BAR) {
return this._paintBarFrame(bufferLength, dataArray);
}
},
_paintOscilloscopeFrame: function(bufferLength, dataArray) {
function avg() {
var avg = 0;
var count = 0;
for (var l = lastIndex; l < index + 1; l++) {
avg += dataArray[l];
count++;
}
var v = avg / count;
return h * v / 128;
}
this.canvasCtx.lineWidth = 2; // 2 because were shrinking the canvas by 2
this.canvasCtx.strokeStyle = 'rgba(255, 255, 255,1)';
this.canvasCtx.beginPath();
var sliceWidth = bufferLength / this.width * 1;
var h = this.height / 2;
this.canvasCtx.moveTo(-2, h);
var index = 0;
var lastIndex = 0;
for (var i = 0, iEnd = this.width * 1; i < iEnd; i += 2) {
index = i * sliceWidth | 0;
this.canvasCtx.lineTo(i, avg());
lastIndex = index + 1;
}
lastIndex = index + 1;
index = i * sliceWidth | 0;
this.canvasCtx.lineTo(this.width, avg());
this.canvasCtx.stroke();
},
_paintBarFrame: function(bufferLength, dataArray) {
// TODO
}
}

View file

@ -197,6 +197,14 @@ a:focus { outline: none; }
width: 9px;
}
#visualizer {
position: absolute;
width: 75px;
height: 16px;
top: 43px;
left: 24px;
}
.text {
position: absolute;

View file

@ -4,6 +4,9 @@ function Winamp () {
this.fileManager = FileManager;
this.media = Media.init();
this.skin = SkinManager;
this.skinManager = SkinManager;
this.visualizer = Visualizer.init(document.getElementById('visualizer'));
this.visualizerStyle = this.visualizer.OSCILLOSCOPE;
this.nodes = {
'option': document.getElementById('option'),
@ -17,6 +20,7 @@ function Winamp () {
'songTitle': document.getElementById('song-title'),
'time': document.getElementById('time'),
'shadeTime': document.getElementById('shade-time'),
'visualizer': document.getElementById('visualizer'),
'previous': document.getElementById('previous'),
'play': document.getElementById('play'),
'pause': document.getElementById('pause'),
@ -103,7 +107,12 @@ function Winamp () {
self.updateTime();
});
this.media.addEventListener('visualizerupdate', function(bufferLength, dataArray) {
self.visualizer.paintFrame(self.visualizerStyle, bufferLength, dataArray);
});
this.media.addEventListener('ended', function() {
self.visualizer.clear();
self.setStatus('stop');
});
@ -130,6 +139,17 @@ function Winamp () {
self.updateTime();
}
this.nodes.visualizer.onclick = function() {
if(self.visualizerStyle == self.visualizer.NONE) {
// self.visualizerStyle = self.visualizer.BAR;
//} else if(self.visualizerStyle == self.visualizer.BAR) {
self.visualizerStyle = self.visualizer.OSCILLOSCOPE;
} else if(self.visualizerStyle == self.visualizer.OSCILLOSCOPE) {
self.visualizerStyle = self.visualizer.NONE;
}
self.visualizer.clear();
}
this.nodes.previous.onclick = function() {
// Implement this when we support playlists
}