diff --git a/index.html b/index.html index 09a9f2b1..d5b73260 100755 --- a/index.html +++ b/index.html @@ -32,7 +32,7 @@
- +by @captbaritone - GitHub
- + diff --git a/media.js b/media.js index 881506fe..56d1dbeb 100644 --- a/media.js +++ b/media.js @@ -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)); }, diff --git a/visualizer.js b/visualizer.js new file mode 100644 index 00000000..6698bb20 --- /dev/null +++ b/visualizer.js @@ -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 + } +} diff --git a/winamp.css b/winamp.css index cdce392c..921ed9c8 100755 --- a/winamp.css +++ b/winamp.css @@ -197,6 +197,14 @@ a:focus { outline: none; } width: 9px; } +#visualizer { + position: absolute; + width: 75px; + height: 16px; + top: 43px; + left: 24px; +} + .text { position: absolute; diff --git a/winamp.js b/winamp.js index cd63d4da..0c0f81f1 100755 --- a/winamp.js +++ b/winamp.js @@ -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 }