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