Audio Visualizer #2

HTML
<html> <head> <meta name="description" content="HTML5 Audio Spectrum Visualizer"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HTML5 Audio API showcase | Audio visualizer</title> <link type="text/css" rel="stylesheet" href="style/style.css"> </head> <body> <div id="wrapper"> <div id="fileWrapper" class="file_wrapper"> <div id="info"> HTML5 Audio API showcase | An Audio Viusalizer </div> <label for="uploadedFile">Drag & Drop or select a file to play:</label> <input type="file" id="uploadedFile"></input> </div> <div id="visualizer_wrapper"> <canvas id='canvas' width="800" height="350"></canvas> </div> </div> <footer> <small>Made by <a href="https://codepen.io/IlyasR/" target="_blank">IlyasR</a></small> </footer> <script type="text/javascript" src="js/html5_audio_visualizer.js"></script> </body> </html>
CSS
html, body { margin: 0; font-family: arial, "Microsoft YaHei"; background-color: #272822; color: #FEFEFE; } #fileWrapper{ transition:all 0.5s ease; } #fileWrapper:hover{ opacity: 1!important; } #visualizer_wrapper{ text-align: center; } footer{ position: fixed; bottom: 2px; color:#aaa; }
JAVASCRIPT
/** * An audio spectrum visualizer built with HTML5 Audio API * Author:IlyasR * License: MIT * Jan 8 2018 */ window.onload = function() { new Visualizer().ini(); }; 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; //used to upgrade the UI information this.infoUpdateId = null; //to store 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(); this._addEventListner(); }, _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) { this._updateInfo('!Your browser does not support AudioContext', false); console.log(e); } }, _addEventListner: function() { var that = this, audioInput = document.getElementById('uploadedFile'), dropContainer = document.getElementsByTagName("canvas")[0]; //listen the file upload audioInput.onchange = function() { if (that.audioContext===null) {return;}; //the if statement fixes the file selction cancle, because the onchange will trigger even the file selection been canceled if (audioInput.files.length !== 0) { //only process the first file that.file = audioInput.files[0]; that.fileName = that.file.name; if (that.status === 1) { //the sound is still playing but we upload another file, so set the forceStop flag to true that.forceStop = true; }; document.getElementById('fileWrapper').style.opacity = 1; that._updateInfo('Uploading', true); //once the file is ready,start the visualizer that._start(); }; }; //listen the drag & drop dropContainer.addEventListener("dragenter", function() { document.getElementById('fileWrapper').style.opacity = 1; that._updateInfo('Drop it on the page', true); }, false); dropContainer.addEventListener("dragover", function(e) { e.stopPropagation(); e.preventDefault(); //set the drop mode e.dataTransfer.dropEffect = 'copy'; }, false); dropContainer.addEventListener("dragleave", function() { document.getElementById('fileWrapper').style.opacity = 0.2; that._updateInfo(that.info, false); }, false); dropContainer.addEventListener("drop", function(e) { e.stopPropagation(); e.preventDefault(); if (that.audioContext===null) {return;}; document.getElementById('fileWrapper').style.opacity = 1; that._updateInfo('Uploading', true); //get the dropped file that.file = e.dataTransfer.files[0]; if (that.status === 1) { document.getElementById('fileWrapper').style.opacity = 1; that.forceStop = true; }; that.fileName = that.file.name; //once the file is ready,start the visualizer that._start(); }, false); }, _start: function() { //read and decode the file into audio array buffer var that = this, file = this.file, fr = new FileReader(); fr.onload = function(e) { var fileResult = e.target.result; var audioContext = that.audioContext; if (audioContext === null) { return; }; that._updateInfo('Decoding the audio', true); audioContext.decodeAudioData(fileResult, function(buffer) { that._updateInfo('Decode succussfully,start the visualizer', true); that._visualize(audioContext, buffer); }, function(e) { that._updateInfo('!Fail to decode the file', false); console.error(e); }); }; fr.onerror = function(e) { that._updateInfo('!Fail to read the file', false); console.error(e); }; //assign the file to the reader this._updateInfo('Starting read the file', true); fr.readAsArrayBuffer(file); }, _visualize: function(audioContext, buffer) { var audioBufferSouceNode = audioContext.createBufferSource(), analyser = audioContext.createAnalyser(), that = this; //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 audioBufferSouceNode.buffer = buffer; //play the source if (!audioBufferSouceNode.start) { audioBufferSouceNode.start = audioBufferSouceNode.noteOn //in old browsers use noteOn method audioBufferSouceNode.stop = audioBufferSouceNode.noteOff //in old browsers use noteOff 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; audioBufferSouceNode.onended = function() { that._audioEnd(that); }; this._updateInfo('Playing ' + this.fileName, false); this.info = 'Playing ' + this.fileName; document.getElementById('fileWrapper').style.opacity = 0.2; this._drawSpectrum(analyser); }, _drawSpectrum: function(analyser) { var that = this, canvas = document.getElementById('canvas'), cwidth = canvas.width, cheight = canvas.height - 2, meterWidth = 10, //width of the meters in the spectrum gap = 2, //gap between meters capHeight = 2, capStyle = '#fff', meterNum = 800 / (10 + 2), //count of the meters capYPositionArray = []; ////store the vertical position of hte caps for the preivous frame ctx = canvas.getContext('2d'), gradient = ctx.createLinearGradient(0, 0, 0, 300); gradient.addColorStop(1, '#0f0'); gradient.addColorStop(0.5, '#ff0'); gradient.addColorStop(0, '#f00'); 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 stoped 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); }, _audioEnd: function(instance) { if (this.forceStop) { this.forceStop = false; this.status = 1; return; }; this.status = 0; var text = 'HTML5 Audio API showcase | An Audio Viusalizer'; document.getElementById('fileWrapper').style.opacity = 1; document.getElementById('info').innerHTML = text; instance.info = text; document.getElementById('uploadedFile').value = ''; }, _updateInfo: function(text, processing) { var infoBar = document.getElementById('info'), dots = '...', i = 0, that = this; infoBar.innerHTML = text + dots.substring(0, i++); if (this.infoUpdateId !== null) { clearTimeout(this.infoUpdateId); }; if (processing) { //animate dots at the end of the info text var animateDot = function() { if (i > 3) { i = 0 }; infoBar.innerHTML = text + dots.substring(0, i++); that.infoUpdateId = setTimeout(animateDot, 250); } this.infoUpdateId = setTimeout(animateDot, 250); }; } }
Expand for more options Login