{"action":"closed","issue":{"url":"https://api.github.com/repos/katspaugh/wavesurfer.js/issues/568","labels_url":"https://api.github.com/repos/katspaugh/wavesurfer.js/issues/568/labels{/name}","comments_url":"https://api.github.com/repos/katspaugh/wavesurfer.js/issues/568/comments","events_url":"https://api.github.com/repos/katspaugh/wavesurfer.js/issues/568/events","html_url":"https://github.com/katspaugh/wavesurfer.js/issues/568","id":114379444,"number":568,"title":"New spectrum plugin","user":{"login":"smolleyes","id":200256,"avatar_url":"https://avatars.githubusercontent.com/u/200256?v=3","gravatar_id":"","url":"https://api.github.com/users/smolleyes","html_url":"https://github.com/smolleyes","followers_url":"https://api.github.com/users/smolleyes/followers","following_url":"https://api.github.com/users/smolleyes/following{/other_user}","gists_url":"https://api.github.com/users/smolleyes/gists{/gist_id}","starred_url":"https://api.github.com/users/smolleyes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smolleyes/subscriptions","organizations_url":"https://api.github.com/users/smolleyes/orgs","repos_url":"https://api.github.com/users/smolleyes/repos","events_url":"https://api.github.com/users/smolleyes/events{/privacy}","received_events_url":"https://api.github.com/users/smolleyes/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-10-31T01:12:47Z","updated_at":"2015-10-31T16:56:52Z","closed_at":"2015-10-31T16:56:52Z","body":"Hi !\r\n\r\nhere's the result of my work for a new spectrum plugin based on the spectrogram plugin and this link\r\n\r\nhttps://developer.mozilla.org/en-US/demos/detail/html5-audio-spectrum-visualizer/launch\r\n\r\n\r\n\r\nthe plugin wavesurfer.spectrum.js:\r\n\r\n``` \r\n'use strict';\r\n\r\nWaveSurfer.Spectrum = {\r\n    init: function (params) {\r\n        var wavesurfer = this.wavesurfer = params.wavesurfer;\r\n\r\n        if (!this.wavesurfer) {\r\n            throw Error('No WaveSurfer instance provided');\r\n        }\r\n\r\n        this.params = params;\r\n        this.audioContext = wavesurfer.backend.audiocontext;\r\n        this.source = wavesurfer.backend.source; //the audio source\r\n        this.analyser = wavesurfer.backend.analyser;\r\n        this.infoUpdateId = null; //to sotore the setTimeout ID and clear the interval\r\n        this.animationId = null;\r\n        this.allCapsReachBottom = false;\r\n        this.buffer = this.wavesurfer.backend.buffer;\r\n        var drawer = this.drawer = this.wavesurfer.drawer;\r\n\r\n        this.container = 'string' == typeof params.container ?\r\n            document.querySelector(params.container) : params.container;\r\n\r\n        if (!this.container) {\r\n            throw Error('No container for WaveSurfer spectrogram');\r\n        }\r\n\r\n        this.fftSamples = this.params.fftSamples || wavesurfer.params.fftSamples || 512;\r\n        this.colorStop1 = this.params.colorStop1 || 'black';\r\n        this.colorStop2 = this.params.colorStop2 || 'black';\r\n        this.colorStop3 = this.params.colorStop3 || 'red';\r\n        this.capStyle   = this.params.capStyle   || 'pink';\r\n        this.meterWidth = this.params.meterWidth || 10, //width of the meters in the spectrum\r\n        this.gapSpace   = this.params.gapSpace   || 2, //gap between meters\r\n        this.capHeight  = this.params.capHeight  || 2,\r\n        this.height     = this.fftSamples / 2;\r\n        this.width      = drawer.width;\r\n\r\n        this.createWrapper();\r\n        this.createCanvas();\r\n        this.render();\r\n\r\n        wavesurfer.on('destroy', this.destroy.bind(this));\r\n    },\r\n\r\n    destroy: function () {\r\n        this.unAll();\r\n        if (this.wrapper) {\r\n            this.wrapper.parentNode.removeChild(this.wrapper);\r\n            this.wrapper = null;\r\n        }\r\n    },\r\n\r\n    createWrapper: function () {\r\n        var prevSpectrogram = this.container.querySelector('spectrogram');\r\n        if (prevSpectrogram) {\r\n            this.container.removeChild(prevSpectrogram);\r\n        }\r\n\r\n        var wsParams = this.wavesurfer.params;\r\n\r\n        this.wrapper = this.container.appendChild(\r\n            document.createElement('spectrogram')\r\n        );\r\n        this.drawer.style(this.wrapper, {\r\n            display: 'block',\r\n            position: 'relative',\r\n            userSelect: 'none',\r\n            webkitUserSelect: 'none',\r\n            height: this.height + 'px'\r\n        });\r\n\r\n        if (wsParams.fillParent || wsParams.scrollParent) {\r\n            this.drawer.style(this.wrapper, {\r\n                width: '100%',\r\n                overflowX: 'hidden',\r\n                overflowY: 'hidden'\r\n            });\r\n        }\r\n\r\n        var my = this;\r\n        this.wrapper.addEventListener('click', function (e) {\r\n            e.preventDefault();\r\n            var relX = 'offsetX' in e ? e.offsetX : e.layerX;\r\n            my.fireEvent('click', (relX / my.scrollWidth) || 0);\r\n        });\r\n    },\r\n\r\n    createCanvas: function () {\r\n        var canvas = this.canvas = this.wrapper.appendChild(\r\n          document.createElement('canvas')\r\n        );\r\n\r\n        this.spectrCc = canvas.getContext('2d');\r\n\r\n        this.wavesurfer.drawer.style(canvas, {\r\n            position: 'absolute',\r\n            zIndex: 4\r\n        });\r\n    },\r\n\r\n    render: function () {\r\n        this.updateCanvasStyle();\r\n        this.startSpectrum();\r\n    },\r\n\r\n    updateCanvasStyle: function () {\r\n        var width = Math.round(this.width / this.pixelRatio) + 'px';\r\n        this.canvas.width = this.width;\r\n        this.canvas.height = this.height;\r\n        this.canvas.style.width = width;\r\n    },\r\n\r\n    startSpectrum: function() {\r\n        var audioContext = this.audioContext;\r\n        var audioBufferSouceNode = this.source;\r\n        //connect the source to the analyser\r\n        audioBufferSouceNode.connect(this.analyser);\r\n        //connect the analyser to the destination(the speaker), or we won't hear the sound\r\n        //wavesurfer.backend.analyser.connect(audioContext.destination);\r\n        //then assign the buffer to the buffer source node\r\n        audioBufferSouceNode.buffer = wavesurfer.backend.buffer;\r\n        //play the source\r\n        if (!audioBufferSouceNode.start) {\r\n            audioBufferSouceNode.start = audioBufferSouceNode.noteOn //in old browsers use noteOn method\r\n            audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //in old browsers use noteOn method\r\n        };\r\n        //stop the previous sound if any\r\n        if (this.animationId !== null) {\r\n            cancelAnimationFrame(this.animationId);\r\n        }\r\n        audioBufferSouceNode.start(0);\r\n        this.status = 1;\r\n        this.source = audioBufferSouceNode;\r\n        audioBufferSouceNode.onended = function() {\r\n            this.destroy(this);\r\n        };\r\n        this.drawSpectrum();\r\n    },\r\n\r\n    drawSpectrum: function() {\r\n        var analyser = this.analyser,\r\n        cwidth = this.canvas.width,\r\n        cheight = this.canvas.height - 2,\r\n        meterWidth = this.meterWidth, //width of the meters in the spectrum\r\n        gap = this.gapSpace, //gap between meters\r\n        capHeight = this.capHeight,\r\n        capStyle = this.capStyle,\r\n        meterNum = 800 / (10 + 2), //count of the meters\r\n        capYPositionArray = []; ////store the vertical position of hte caps for the previous frame\r\n\r\n        var ctx = this.spectrCc,\r\n        gradient = ctx.createLinearGradient(0, 0, 0, 300);\r\n        gradient.addColorStop(1, this.colorStop1);\r\n        gradient.addColorStop(0.5, this.colorStop2);\r\n        gradient.addColorStop(0, this.colorStop3);\r\n        var drawMeter = function() {\r\n            var array = new Uint8Array(analyser.frequencyBinCount);\r\n            analyser.getByteFrequencyData(array);\r\n            if (this.status === 0) {\r\n                //fix when some sounds end the value still not back to zero\r\n                for (var i = array.length - 1; i >= 0; i--) {\r\n                    array[i] = 0;\r\n                };\r\n                this.allCapsReachBottom = true;\r\n                for (var i = capYPositionArray.length - 1; i >= 0; i--) {\r\n                   this.allCapsReachBottom = this.allCapsReachBottom && (capYPositionArray[i] === 0);\r\n                };\r\n                if (this.allCapsReachBottom) {\r\n                    cancelAnimationFrame(this.animationId);\r\n                    return;\r\n                };\r\n            };\r\n            var step = Math.round(array.length / meterNum); //sample limited data from the total array\r\n            ctx.clearRect(0, 0, cwidth, cheight);\r\n            for (var i = 0; i < meterNum; i++) {\r\n                var value = array[i * step];\r\n                if (capYPositionArray.length < Math.round(meterNum)) {\r\n                    capYPositionArray.push(value);\r\n                };\r\n                ctx.fillStyle = capStyle;\r\n                //draw the cap, with transition effect\r\n                if (value < capYPositionArray[i]) {\r\n                    ctx.fillRect(i * 12, cheight - (--capYPositionArray[i]), meterWidth, capHeight);\r\n                } else {\r\n                    ctx.fillRect(i * 12, cheight - value, meterWidth, capHeight);\r\n                    capYPositionArray[i] = value;\r\n                };\r\n                ctx.fillStyle = gradient; //set the filllStyle to gradient for a better look\r\n                ctx.fillRect(i * 12 /*meterWidth+gap*/ , cheight - value + capHeight, meterWidth, cheight); //the meter\r\n            }\r\n            this.animationId = requestAnimationFrame(drawMeter);\r\n        }\r\n        this.animationId = requestAnimationFrame(drawMeter);\r\n    }\r\n};\r\n\r\nWaveSurfer.util.extend(WaveSurfer.Spectrum, WaveSurfer.Observer);\r\n```\r\n\r\nexemple:\r\n\r\n``` \r\n// Play on audio load\r\n    wavesurfer.on('ready', function () {\r\n        var spectrum = Object.create(WaveSurfer.Spectrum);\r\n\r\n        spectrum.init({\r\n            wavesurfer: wavesurfer,\r\n            container: \"#wave-spectrum\",\r\n            fftSamples : 512,\r\n            colorStop1 : 'black', // base color\r\n            colorStop2 : 'black', //middle color\r\n            colorStop3 : 'red', // top color\r\n            capStyle   : 'pink', // small caps on top of meters\r\n            meterWidth : 10, //width of the meters in the spectrum\r\n            gapSpace        : 2, //gap between meters\r\n            capHeight  : 2 // cap height\r\n        });\r\n        wavesurfer.play();\r\n    });\r\n```\r\n\r\nworks great for me :)\r\n\r\nthanks"}}