File: jzik.js - Tab length: 1 2 4 8 - Lines: on off - No wrap: on off

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]'; }