001: /*
002: jZik javascript sampler, by r043v/dph/m2m -- noferov@gmail.com
003: under creative commons 3.0 by-nc-sa license -- http://creativecommons.org/licenses/by-nc-sa/3.0/
004:
005: this plugin is dedicated to read audio sprites using html5 audio api
006: it take only one source sound file, copy it to create some channels and play inside small part of the long source file
007: support delay play and repeat, volume or play rate option, and callback on play, stop and repeat event
008: */
009:
010: function jzik(sources,samples,channels,onInit)
011: { var zik = new Object({audio:document.createElement('audio'),source:""});
012: if(!zik.audio.canPlayType) return false;
013: if(sources.ogg !== undefined) if(!!(zik.audio.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, ''))) zik.source="ogg";
014: if(zik.source == "") if(sources.mp3 !== undefined) if(!!(zik.audio.canPlayType('audio/mpeg;').replace(/no/, ''))) zik.source="mp3";
015: if(zik.source == "") if(sources.wav !== undefined) if(!!(zik.audio.canPlayType('audio/wav; codecs="1"').replace(/no/, ''))) zik.source="wav";
016: if(zik.source == "") if(sources.aac !== undefined) if(!!(zik.audio.canPlayType('audio/mp4; codecs="mp4a.40.2"').replace(/no/, ''))) zik.source="aac";
017: if(zik.source == "") return false;
018:
019: for(n in samples){ var spl = samples[n]; if(spl.end === undefined){ if(spl.size === undefined) return false; spl.end = spl.start + spl.size; } };
020:
021: zik.channelsNb = channels;
022: zik.samples = samples;
023: zik.channels = [];
024: zik.activePlay = [];
025: zik.checkTimer = false;
026:
027: zik.audio.src = sources[zik.source]; zik.audio.autobuffer=true; zik.audio.preload='auto'; zik.audio.loop=false; document.body.appendChild(zik.audio);
028: zik.callInit = function(){ if(jzikIsFunction(onInit)) onInit.call(zik); };
029: zik.channelsLoaded = 1;
030:
031: zik.cloneSampleLoaded = function()
032: { if(this.jzloaded || this.readyState < 4) return; this.jzloaded = true; //this.removeEventListener('canplaythrough',zik.cloneSampleLoaded,false);
033: if(++zik.channelsLoaded >= zik.channelsNb) zik.callInit();
034: };
035:
036: zik.masterSampleLoaded = function()
037: { if(this.jzloaded || this.readyState < 4) return; this.jzloaded = true; //this.removeEventListener('canplaythrough',zik.masterSampleLoaded,false);
038: zik.channels[0] = new Object({isplay:false,isfree:true,audio:zik.audio});
039: if(zik.channelsNb == 1) zik.callInit();
040: else
041: for(var n=1; n < zik.channelsNb; n++)
042: { zik.channels[n] = new Object({isplay:false,isfree:true,audio:zik.audio.cloneNode(true)});
043: var a = zik.channels[n].audio; a.jzloaded = false;
044: a.addEventListener('canplaythrough',zik.cloneSampleLoaded,false);
045: if(window.opera !== undefined) zik.channels[n].audio.load(); // manual load for call event in opera.
046: }
047: };
048:
049: zik.audio.jzloaded = false;
050: zik.audio.addEventListener('canplaythrough',zik.masterSampleLoaded,false);
051: zik.play = jzikplay; zik.check = function(){ jzikcheck.call(zik); };
052: zik.audio.load(); return zik;
053: };
054:
055: function jzikcheck()
056: { var zik = this;
057: for(n in zik.activePlay)
058: { var chn = zik.channels[zik.activePlay[n]]; var opt = chn.opt;
059: if(chn.isfree || !chn.isplay) continue;
060: if(chn.startTime != 0)
061: { var ctime = new Date(); ctime = ctime.getTime();
062: if(chn.startTime <= ctime) { chn.startTime=0; chn.audio.play(); if(jzikIsFunction(opt.onPlay)) opt.onPlay.call(chn); }
063: } else {
064: if(chn.audio.currentTime >= chn.stopOffset)
065: { chn.audio.pause();
066: if(opt.repeat > 0) opt.repeat--;
067: if(opt.repeat != 0)
068: { chn.audio.currentTime = chn.startOffset; chn.audio.play();
069: if(jzikIsFunction(opt.onRepeat)) opt.onRepeat.call(chn);
070: } else {
071: if(jzikIsFunction(opt.onEnd)) opt.onEnd.call(chn);
072: chn.isplay = false; zik.activePlay.splice(n,1); chn.isfree = true;
073: }
074: }
075: }
076: }
077: if(zik.activePlay.length <= 0) { clearInterval(zik.checkTimer); zik.checkTimer = false; }
078: };
079:
080: function jzikplay(sample, opts)
081: { var zik = this;
082: var opt = { repeat:0,speed:1,volume:1,delay:0,updateTime:20,onPlay:false,onEnd:false,onRepeat:false }; for(i in opts) opt[i] = opts[i];
083: var spl = zik.samples[sample]; if(spl === undefined) return false;
084: var nchn = 0; while(nchn < zik.channelsNb && (false === zik.channels[nchn].isfree)) nchn++; if(nchn >= zik.channelsNb) return false;
085: var chn = zik.channels[nchn];
086: chn.isfree = false;
087: chn.opt = opt;
088: chn.audio.volume = opt.volume;
089: chn.audio.loop = false;
090: chn.audio.playbackRate = opt.speed;
091: chn.startOffset = spl.start; chn.stopOffset = spl.end; if(chn.stopOffset > chn.audio.duration) chn.stopOffset = chn.audio.duration;
092: chn.audio.currentTime = spl.start;
093: if(opt.delay)
094: { chn.audio.pause(); // firefox autoplay fix
095: var ctime = new Date(); ctime = ctime.getTime();
096: chn.startTime = ctime+chn.delay;
097: } else { chn.startTime = 0; chn.audio.play(); if(jzikIsFunction(opt.onPlay)) opt.onPlay.call(chn); }
098: chn.isplay = true; zik.activePlay.push(nchn);
099: if(zik.checkTimer === false) zik.checkTimer = setInterval(zik.check,opt.updateTime);
100: return true;
101: };
102:
103: function jzikIsFunction(f){ var getType = {}; return f && getType.toString.call(f) == '[object Function]'; }