-
-
Save dimaip/e8acf0b1b87c3083bbe9 to your computer and use it in GitHub Desktop.
ШРИ 2015 Задание 3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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