Create a gist now

Instantly share code, notes, and snippets.

@dimaip /shri-audio.html Secret
Created Aug 9, 2015

What would you like to do?
ШРИ 2015 Задание 3
<!DOCTYPE html>
<html>
<head>
<title>Audio player - ШРИ 2015 задание 3</title>
<style>
#fileDrag {
text-align: center;
padding: 20px 0;
margin: 20px 0;
color: #aaa;
border: 2px dashed #aaa;
cursor: default;
}
#fileDrag.hover {
color: black;
border-color: black;
}
input[type="range"] {
display: block;
}
#equalizer {
transform: rotate(270deg);
display: inline-block;
margin-left: 50px;
margin-top: -40px;
}
.presetSelector {
margin-top: 30px;
}
</style>
</head>
<body>
<div class="audioPlayer">
<label for="fileInput">Выберите аудио файл: </label> <input id="fileInput" type="file" accept="audio/*" />
<div id="fileDrag">...или затащите его сюда</div>
<div>Информация об аудиозаписи: <span id="fileinfo"></span></div>
<button id='playAudio'>Воспроизведение</button>
<button id='stopAudio'>Стоп</button>
<div>
<div class="presetSelector">
<label for="presetSelector">Пресеты:</label>
<select id="presetSelector">
<option value="flat" selected="selected">Flat</option>
<option value="classic">Classic</option>
<option value="rock">Rock</option>
</select>
</div>
<div>
<div id="equalizer"></div>
</div>
</div>
<div>Божественная визуализация спектра:</div>
<canvas id="canvas"></canvas>
</div>
<script>
// Include 3rd-party lib to decode id3 tags
(function(){var e=function(t){this.type=t||e.OPEN_URI;this.size=null;this.file=null};e.OPEN_FILE=1;e.OPEN_URI=2;e.OPEN_LOCAL=3;if(typeof require==="function"){var t=require("fs")}e.prototype.open=function(i,r){this.file=i;var n=this;switch(this.type){case e.OPEN_LOCAL:t.stat(this.file,function(e,i){if(e){return r(e)}n.size=i.size;t.open(n.file,"r",function(e,t){if(e){return r(e)}n.fd=t;r()})});break;case e.OPEN_FILE:this.size=this.file.size;r();break;default:this.ajax({uri:this.file,type:"HEAD"},function(e,t,i){if(e){return r(e)}n.size=parseInt(i.getResponseHeader("Content-Length"));r()});break}};e.prototype.close=function(){if(this.type===e.OPEN_LOCAL){t.close(this.fd)}};e.prototype.read=function(t,i,r){if(typeof i==="function"){r=i;i=0}if(this.type===e.OPEN_LOCAL){this.readLocal(t,i,r)}else if(this.type===e.OPEN_FILE){this.readFile(t,i,r)}else{this.readUri(t,i,r)}};e.prototype.readBlob=function(e,t,i,r){if(typeof t==="function"){r=t;t=0}else if(typeof i==="function"){r=i;i="application/octet-stream"}this.read(e,t,function(e,t){if(e){r(e);return}r(null,new Blob([t],{type:i}))})};e.prototype.readLocal=function(e,i,r){var n=new Buffer(e);t.read(this.fd,n,0,e,i,function(e,t,i){if(e){return r(e)}var n=new ArrayBuffer(i.length),a=new Uint8Array(n);for(var l=0;l<i.length;l++){a[l]=i[l]}r(null,n)})};e.prototype.ajax=function(e,t){var i={type:"GET",uri:null,responseType:"text"};if(typeof e==="string"){e={uri:e}}for(var r in e){i[r]=e[r]}var n=new XMLHttpRequest;n.onreadystatechange=function(){if(n.readyState!==4)return;if(n.status!==200&&n.status!==206){return t("Received non-200/206 response ("+n.status+")")}t(null,n.response,n)};n.responseType=i.responseType;n.open(i.type,i.uri,true);if(i.range){i.range=[].concat(i.range);if(i.range.length===2){n.setRequestHeader("Range","bytes="+i.range[0]+"-"+i.range[1])}else{n.setRequestHeader("Range","bytes="+i.range[0])}}n.send()};e.prototype.readUri=function(e,t,i){this.ajax({uri:this.file,type:"GET",responseType:"arraybuffer",range:[t,t+e-1]},function(e,t){if(e){return i(e)}return i(null,t)})};e.prototype.readFile=function(e,t,i){var r=this.file.slice(t,t+e),n=new FileReader;n.onload=function(e){i(null,e.target.result)};n.onerror=function(e){i("File read failed")};n.readAsArrayBuffer(r)};DataView.prototype.getString=function(e,t,i){t=t||0;e=e||this.byteLength-t;if(e<0){e+=this.byteLength}var r="";if(typeof Buffer!=="undefined"){var n=[];for(var a=t;a<t+e;a++){n.push(this.getUint8(a))}return new Buffer(n).toString()}else{for(var a=t;a<t+e;a++){r+=String.fromCharCode(this.getUint8(a))}if(i){return r}return decodeURIComponent(escape(r))}};DataView.prototype.getStringUtf16=function(e,t,i){t=t||0;e=e||this.byteLength-t;var r=false,n="",a=false;if(typeof Buffer!=="undefined"){n=[];a=true}if(e<0){e+=this.byteLength}if(i){var l=this.getUint16(t);if(l===65534){r=true}t+=2;e-=2}for(var o=t;o<t+e;o+=2){var s=this.getUint16(o,r);if(s>=0&&s<=55295||s>=57344&&s<=65535){if(a){n.push(s)}else{n+=String.fromCharCode(s)}}else if(s>=65536&&s<=1114111){s-=65536;if(a){n.push(((1047552&s)>>10)+55296);n.push((1023&s)+56320)}else{n+=String.fromCharCode(((1047552&s)>>10)+55296)+String.fromCharCode((1023&s)+56320)}}}if(a){return new Buffer(n).toString()}else{return decodeURIComponent(escape(n))}};DataView.prototype.getSynch=function(e){var t=0,i=2130706432;while(i){t>>=1;t|=e&i;i>>=8}return t};DataView.prototype.getUint8Synch=function(e){return this.getSynch(this.getUint8(e))};DataView.prototype.getUint32Synch=function(e){return this.getSynch(this.getUint32(e))};DataView.prototype.getUint24=function(e,t){if(t){return this.getUint8(e)+(this.getUint8(e+1)<<8)+(this.getUint8(e+2)<<16)}return this.getUint8(e+2)+(this.getUint8(e+1)<<8)+(this.getUint8(e)<<16)};var i=function(t,r){var n={type:i.OPEN_URI};if(typeof t==="string"){t={file:t,type:i.OPEN_URI}}else if(typeof window!=="undefined"&&window.File&&t instanceof window.File){t={file:t,type:i.OPEN_FILE}}for(var a in t){n[a]=t[a]}if(!n.file){return r("No file was set")}if(n.type===i.OPEN_FILE){if(typeof window==="undefined"||!window.File||!window.FileReader||typeof ArrayBuffer==="undefined"){return r("Browser does not have support for the File API and/or ArrayBuffers")}}else if(n.type===i.OPEN_LOCAL){if(typeof require!=="function"){return r("Local paths may not be read within a browser")}}else{}var l=["Blues","Classic Rock","Country","Dance","Disco","Funk","Grunge","Hip-Hop","Jazz","Metal","New Age","Oldies","Other","Pop","R&B","Rap","Reggae","Rock","Techno","Industrial","Alternative","Ska","Death Metal","Pranks","Soundtrack","Euro-Techno","Ambient","Trip-Hop","Vocal","Jazz+Funk","Fusion","Trance","Classical","Instrumental","Acid","House","Game","Sound Clip","Gospel","Noise","AlternRock","Bass","Soul","Punk","Space","Meditative","Instrumental Pop","Instrumental Rock","Ethnic","Gothic","Darkwave","Techno-Industrial","Electronic","Pop-Folk","Eurodance","Dream","Southern Rock","Comedy","Cult","Gangsta Rap","Top 40","Christian Rap","Pop / Funk","Jungle","Native American","Cabaret","New Wave","Psychedelic","Rave","Showtunes","Trailer","Lo-Fi","Tribal","Acid Punk","Acid Jazz","Polka","Retro","Musical","Rock & Roll","Hard Rock","Folk","Folk-Rock","National Folk","Swing","Fast Fusion","Bebob","Latin","Revival","Celtic","Bluegrass","Avantgarde","Gothic Rock","Progressive Rock","Psychedelic Rock","Symphonic Rock","Slow Rock","Big Band","Chorus","Easy Listening","Acoustic","Humour","Speech","Chanson","Opera","Chamber Music","Sonata","Symphony","Booty Bass","Primus","Porn Groove","Satire","Slow Jam","Club","Tango","Samba","Folklore","Ballad","Power Ballad","Rhythmic Soul","Freestyle","Duet","Punk Rock","Drum Solo","A Cappella","Euro-House","Dance Hall","Goa","Drum & Bass","Club-House","Hardcore","Terror","Indie","BritPop","Negerpunk","Polsk Punk","Beat","Christian Gangsta Rap","Heavy Metal","Black Metal","Crossover","Contemporary Christian","Christian Rock","Merengue","Salsa","Thrash Metal","Anime","JPop","Synthpop","Rock/Pop"];var o={};o.types={TALB:"album",TBPM:"bpm",TCOM:"composer",TCON:"genre",TCOP:"copyright",TDEN:"encoding-time",TDLY:"playlist-delay",TDOR:"original-release-time",TDRC:"recording-time",TDRL:"release-time",TDTG:"tagging-time",TENC:"encoder",TEXT:"writer",TFLT:"file-type",TIPL:"involved-people",TIT1:"content-group",TIT2:"title",TIT3:"subtitle",TKEY:"initial-key",TLAN:"language",TLEN:"length",TMCL:"credits",TMED:"media-type",TMOO:"mood",TOAL:"original-album",TOFN:"original-filename",TOLY:"original-writer",TOPE:"original-artist",TOWN:"owner",TPE1:"artist",TPE2:"band",TPE3:"conductor",TPE4:"remixer",TPOS:"set-part",TPRO:"produced-notice",TPUB:"publisher",TRCK:"track",TRSN:"radio-name",TRSO:"radio-owner",TSOA:"album-sort",TSOP:"performer-sort",TSOT:"title-sort",TSRC:"isrc",TSSE:"encoder-settings",TSST:"set-subtitle",TAL:"album",TBP:"bpm",TCM:"composer",TCO:"genre",TCR:"copyright",TDY:"playlist-delay",TEN:"encoder",TFT:"file-type",TKE:"initial-key",TLA:"language",TLE:"length",TMT:"media-type",TOA:"original-artist",TOF:"original-filename",TOL:"original-writer",TOT:"original-album",TP1:"artist",TP2:"band",TP3:"conductor",TP4:"remixer",TPA:"set-part",TPB:"publisher",TRC:"isrc",TRK:"track",TSS:"encoder-settings",TT1:"content-group",TT2:"title",TT3:"subtitle",TXT:"writer",WCOM:"url-commercial",WCOP:"url-legal",WOAF:"url-file",WOAR:"url-artist",WOAS:"url-source",WORS:"url-radio",WPAY:"url-payment",WPUB:"url-publisher",WAF:"url-file",WAR:"url-artist",WAS:"url-source",WCM:"url-commercial",WCP:"url-copyright",WPB:"url-publisher",COMM:"comments",APIC:"image",PIC:"image"};o.imageTypes=["other","file-icon","icon","cover-front","cover-back","leaflet","media","artist-lead","artist","conductor","band","composer","writer","location","during-recording","during-performance","screen","fish","illustration","logo-band","logo-publisher"];o.parse=function(e,t,i){i=i||0;t=t||4;var r={tag:null,value:null},n=new DataView(e);if(t<3){return o.parseLegacy(e)}var a={id:n.getString(4),type:n.getString(1),size:n.getUint32Synch(4),flags:[n.getUint8(8),n.getUint8(9)]};if(a.flags[1]!==0){return false}if(!a.id in o.types){return false}r.tag=o.types[a.id];if(a.type==="T"){var s=n.getUint8(10);if(s===0||s===3){r.value=n.getString(-11,11)}else if(s===1){r.value=n.getStringUtf16(-11,11,true)}else if(s===2){r.value=n.getStringUtf16(-11,11)}else{return false}if(a.id==="TCON"&&!!parseInt(r.value)){r.value=l[parseInt(r.value)]}}else if(a.type==="W"){r.value=n.getString(-10,10)}else if(a.id==="COMM"){var s=n.getUint8(10),u=14,f=0;for(var g=u;;g++){if(s===1||s===2){if(n.getUint16(g)===0){u=g+2;break}g++}else{if(n.getUint8(g)===0){u=g+1;break}}}if(s===0||s===3){r.value=n.getString(-1*u,u)}else if(s===1){r.value=n.getStringUtf16(-1*u,u,true)}else if(s===2){r.value=n.getStringUtf16(-1*u,u)}else{return false}}else if(a.id==="APIC"){var s=n.getUint8(10),c={type:null,mime:null,description:null,data:null};var u=11,f=0;for(var g=u;;g++){if(n.getUint8(g)===0){f=g-u;break}}c.mime=n.getString(f,u);c.type=o.imageTypes[n.getUint8(u+f+1)]||"other";u+=f+2;f=0;for(var g=u;;g++){if(n.getUint8(g)===0){f=g-u;break}}c.description=f===0?null:n.getString(f,u);c.data=e.slice(u+1);r.value=c}return r.tag?r:false};o.parseLegacy=function(e){var t={tag:null,value:null},i=new DataView(e),r={id:i.getString(3),type:i.getString(1),size:i.getUint24(3)};if(!r.id in o.types){return false}t.tag=o.types[r.id];if(r.type==="T"){var n=i.getUint8(7);t.value=i.getString(-7,7);if(r.id==="TCO"&&!!parseInt(t.value)){t.value=l[parseInt(t.value)]}}else if(r.type==="W"){t.value=i.getString(-7,7)}else if(r.id==="COM"){var n=i.getUint8(6);t.value=i.getString(-10,10);if(t.value.indexOf("\x00")!==-1){t.value=t.value.substr(t.value.indexOf("\x00")+1)}}else if(r.id==="PIC"){var n=i.getUint8(6),a={type:null,mime:"image/"+i.getString(3,7).toLowerCase(),description:null,data:null};a.type=o.imageTypes[i.getUint8(11)]||"other";var s=11,u=0;for(var f=s;;f++){if(i.getUint8(f)===0){u=f-s;break}}a.description=u===0?null:i.getString(u,s);a.data=e.slice(s+1);t.value=a}return t.tag?t:false};var s={};s.parse=function(e,t){var i={title:null,album:null,artist:null,year:null,v1:{title:null,artist:null,album:null,year:null,comment:null,track:null,version:1},v2:{version:[null,null]}},r={v1:false,v2:false},n=function(e){if(r.v1&&r.v2){i.title=i.v2.title||i.v1.title;i.album=i.v2.album||i.v1.album;i.artist=i.v2.artist||i.v1.artist;i.year=i.v1.year;t(e,i)}};e.read(128,e.size-128,function(e,t){if(e){return n("Could not read file")}var a=new DataView(t);if(t.byteLength!==128||a.getString(3,null,true)!=="TAG"){r.v1=true;return n()}i.v1.title=a.getString(30,3).replace(/(^\s+|\s+$)/,"")||null;i.v1.artist=a.getString(30,33).replace(/(^\s+|\s+$)/,"")||null;i.v1.album=a.getString(30,63).replace(/(^\s+|\s+$)/,"")||null;i.v1.year=a.getString(4,93).replace(/(^\s+|\s+$)/,"")||null;if(a.getUint8(125)===0){i.v1.comment=a.getString(28,97).replace(/(^\s+|\s+$)/,"");i.v1.version=1.1;i.v1.track=a.getUint8(126)}else{i.v1.comment=a.getString(30,97).replace(/(^\s+|\s+$)/,"")}i.v1.genre=l[a.getUint8(127)]||null;r.v1=true;n()});e.read(14,0,function(t,a){if(t){return n("Could not read file")}var l=new DataView(a),s=10,u=0,f;if(a.byteLength!==14||l.getString(3,null,true)!=="ID3"||l.getUint8(3)>4){r.v2=true;return n()}i.v2.version=[l.getUint8(3),l.getUint8(4)];f=l.getUint8(5);if((f&128)!==0){r.v2=true;return n()}if((f&64)!==0){s+=l.getUint32Synch(11)}u+=l.getUint32Synch(6);e.read(u,s,function(e,t){if(e){r.v2=true;return n()}var a=new DataView(t),l=0;while(l<t.byteLength){var s,u,f,g=true;for(var c=0;c<3;c++){f=a.getUint8(l+c);if((f<65||f>90)&&(f<48||f>57)){g=false}}if(!g)break;if(i.v2.version[0]<3){u=t.slice(l,l+6+a.getUint24(l+3))}else{u=t.slice(l,l+10+a.getUint32Synch(l+4))}s=o.parse(u,i.v2.version[0]);if(s){i.v2[s.tag]=s.value}l+=u.byteLength}r.v2=true;n()})})};var u=new e(n.type);u.open(n.file,function(e){if(e){return r("Could not open specified file")}s.parse(u,function(e,t){r(e,t);u.close()})})};i.OPEN_FILE=e.OPEN_FILE;i.OPEN_URI=e.OPEN_URI;i.OPEN_LOCAL=e.OPEN_LOCAL;if(typeof module!=="undefined"&&module.exports){module.exports=i}else{if(typeof define==="function"&&define.amd){define("id3",[],function(){return i})}else{window.id3=i}}})();
/*
* Audio player
*/
function AudioPlayer () {
var audioContext = new ( window.AudioContext || window.webkitAudioContext )();
/*
* Player engine
* Responsible for loading a file and playing it back
*/
var playerEngine = new function () {
var audioBuffer,
bufferSource,
parent = this;
this.loadFile = function (file) {
var reader = new FileReader();
document.getElementById('fileinfo').innerHTML = file.name;
reader.onload = function(e) {
id3(file, function(err, tags) {
document.getElementById('fileinfo').innerHTML = tags.title+ ' - ' + tags.artist;
});
audioContext.decodeAudioData(this.result, function(buf) {
audioBuffer = buf;
parent.play();
});
};
reader.readAsArrayBuffer(file);
}
this.play = function () {
bufferSource = audioContext.createBufferSource();
bufferSource.connect(equalizer.filters[0]);
bufferSource.buffer = audioBuffer;
bufferSource.start(0);
}
this.stop = function () {
bufferSource.stop();
}
};
(function setupAudioPlayer(){
document.getElementById('playAudio').addEventListener('click', function() {
playerEngine.play();
});
document.getElementById('stopAudio').addEventListener('click', function() {
playerEngine.stop();
});
function dragEnter(e) {
e.stopPropagation();
e.preventDefault();
fileDrag.className = 'hover';
}
function dragLeave(e) {
e.stopPropagation();
e.preventDefault();
fileDrag.className = '';
}
function dragOver(e) {
e.stopPropagation();
e.preventDefault();
}
function drop(e) {
e.preventDefault();
e.stopPropagation();
fileDrag.className = '';
var files = e.target.files || e.dataTransfer.files;
playerEngine.loadFile(files[0]);
}
var fileDrag = document.getElementById('fileDrag');
fileDrag.addEventListener("dragover", dragOver, false);
fileDrag.addEventListener("dragenter", dragEnter, false);
fileDrag.addEventListener("dragleave", dragEnter, false);
fileDrag.addEventListener("drop", drop, false);
function fileInputChanged(){
playerEngine.loadFile(this.files[0]);
};
var fileInput = document.getElementById('fileInput');
fileInput.addEventListener("change", fileInputChanged, false);
}());
/*
* Equalizer
* Responsible for setting up filters and binding them to equlizer inputs
*/
var equalizer = new function () {
// Presets are so messed up just to hear the effect :)
var PRESETS = {
flat: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
classic: [-4, -2, 0, 0, 0, 2, 1, 0, -1, -1],
rock: [10, 7, 2, -3, -5, 16, 0, 3, 4, 5]
};
var equalizer = document.getElementById('equalizer'),
parent = this;
this.filters = function createFilters () {
function createFilter (frequency) {
var filter = audioContext.createBiquadFilter();
filter.type = 'peaking';
filter.frequency.value = frequency;
filter.Q.value = 1;
filter.gain.value = 0;
return filter;
};
var frequencies = [60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000],
filters = frequencies.map(createFilter);
filters.reduce(function (prev, curr) {
prev.connect(curr);
return curr;
});
return filters;
}();
var equalizerInputs = function createInputs () {
var inputs = [];
for (var i = 0; i < 10; i++) {
(function(){
var ii = i,
item = document.createElement('input');
item.setAttribute('min', -16);
item.setAttribute('max', 16);
item.setAttribute('step', 0.1);
item.setAttribute('value', 0);
item.setAttribute('type', 'range');
item.addEventListener('change', function (e) {
parent.filters[ii].gain.value = e.target.value;
}, false);
equalizer.appendChild(item);
inputs.push(item);
}());
}
return inputs;
}();
this.presetEqualizer = function (preset) {
[].forEach.call(equalizerInputs, function (item, i) {
item.value = PRESETS[preset][i];
// Tirgger change on the input manually
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", false, true);
item.dispatchEvent(evt);
});
};
};
document.getElementById('presetSelector').addEventListener('change', function() {
equalizer.presetEqualizer(document.getElementById('presetSelector').value);
});
/*
* Visualizer
* Responsible for creating crazy ass animation
*/
var visualizer = new function () {
var canvas = document.getElementById('canvas'),
gfx = canvas.getContext('2d');
this.analyser = audioContext.createAnalyser()
var parent = this;
function updateCanvas() {
requestAnimationFrame(updateCanvas);
gfx.clearRect(0, 0, 800, 600);
gfx.fillStyle = 'gray';
gfx.fillRect(0, 0, 800, 600);
var data = new Uint8Array(128);
parent.analyser.getByteFrequencyData(data);
gfx.fillStyle = 'red';
for(var i=0; i<data.length; i++) {
gfx.fillRect(100+i*4,100+256-data[i]*2,3,100);
}
}
requestAnimationFrame(updateCanvas);
window.setInterval(updateCanvas, 50);
};
// Connect nodes
equalizer.filters[equalizer.filters.length - 1].connect(visualizer.analyser);
visualizer.analyser.connect(audioContext.destination);
}
var player = new AudioPlayer();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment