Jump To …

quintus_audio.js

/*global Quintus:false, AudioContext:false, window:false */

Quintus.Audio = function(Q) {

  Q.audio = {
    channels: [],
    channelMax:  Q.options.channelMax || 10,
    active: {},
    play: function() {}
  };


  Q.hasWebAudio = (typeof AudioContext !== "undefined") || (typeof webkitAudioContext !== "undefined");

  if(Q.hasWebAudio) { 
    if(typeof AudioContext !== "undefined") {
      Q.audioContext = new AudioContext();
    } else {
      Q.audioContext = new window.webkitAudioContext();
    }
  }

  Q.enableSound = function() {
    var hasTouch =  !!('ontouchstart' in window);

    if(Q.hasWebAudio) {
      Q.audio.enableWebAudioSound();
    } else {
      Q.audio.enableHTML5Sound();
    }
    return Q;
  };

  Q.audio.enableWebAudioSound = function() {
    Q.audio.type = "WebAudio";

    Q.audio.soundID = 0;

    Q.audio.playingSounds = {};

    Q.audio.removeSound = function(soundID) {
      delete Q.audio.playingSounds[soundID];
    };

Play a single sound, optionally debounced to prevent repeated plays in a short time

    Q.audio.play = function(s,options) {
      var now = new Date().getTime();

See if this audio file is currently being debounced, if it is, don't do anything and just return

      if(Q.audio.active[s] && Q.audio.active[s] > now) { return; }

If any options were passed in, check for a debounce, which is the number of milliseconds to debounce this sound

      if(options && options['debounce']) {
        Q.audio.active[s] = now + options['debounce'];
      } else {
        delete Q.audio.active[s];
      }

      var soundID = Q.audio.soundID++;

      var source = Q.audioContext.createBufferSource();
      source.buffer = Q.asset(s);
      source.connect(Q.audioContext.destination);
      if(options && options['loop']) {
        source.loop = true;
      } else {
        setTimeout(function() {
          Q.audio.removeSound(soundID);
        },source.buffer.duration * 1000);
      }
      source.assetName = s;
      if(source.start) { source.start(0); } else { source.noteOn(0); }

      Q.audio.playingSounds[soundID] = source;


    };

    Q.audio.stop = function(s) {
      for(var key in Q.audio.playingSounds) {
        var snd = Q.audio.playingSounds[key];
        if(!s || s === snd.assetName) {
          if(snd.stop) { snd.stop(0);  } else {  snd.noteOff(0); }
        }
      }
    };

  };

  Q.audio.enableHTML5Sound = function() {
    Q.audio.type = "HTML5";

    for (var i=0;i<Q.audio.channelMax;i++) {	
      Q.audio.channels[i] = {};
      Q.audio.channels[i]['channel'] = new Audio(); 
      Q.audio.channels[i]['finished'] = -1;	
    }

Play a single sound, optionally debounced to prevent repeated plays in a short time

    Q.audio.play = function(s,options) {
      var now = new Date().getTime();

See if this audio file is currently being debounced, if it is, don't do anything and just return

      if(Q.audio.active[s] && Q.audio.active[s] > now) { return; }

If any options were passed in, check for a debounce, which is the number of milliseconds to debounce this sound

      if(options && options['debounce']) {
        Q.audio.active[s] = now + options['debounce'];
      } else {
        delete Q.audio.active[s];
      }

Find a free audio channel and play the sound

      for (var i=0;i<Q.audio.channels.length;i++) {

Check the channel is either finished or not looping

        if (!Q.audio.channels[i]['loop'] && Q.audio.channels[i]['finished'] < now) {	

          Q.audio.channels[i]['channel'].src = Q.asset(s).src;

If we're looping - just set loop to true to prevent this channcel from being used.

          if(options && options['loop']) { 
            Q.audio.channels[i]['loop'] = true;
            Q.audio.channels[i]['channel'].loop = true;
          } else {
            Q.audio.channels[i]['finished'] = now + Q.asset(s).duration*1000;
          }
          Q.audio.channels[i]['channel'].load();
          Q.audio.channels[i]['channel'].play();
          break;
        }
      }
    };

Stop a single sound asset or stop all sounds currently playing

    Q.audio.stop = function(s) {
      var src = s ? Q.asset(s).src : null;
      var tm = new Date().getTime();
      for (var i=0;i<Q.audio.channels.length;i++) {
        if ((!src || Q.audio.channels[i]['channel'].src === src) && 
            (Q.audio.channels[i]['loop'] || Q.audio.channels[i]['finished'] >= tm)) {
          Q.audio.channels[i]['channel'].pause();
          Q.audio.channels[i]['loop'] = false;
        }
      }
    };

  };

};