initial commit
This commit is contained in:
		
							
								
								
									
										659
									
								
								public/scripts/howler/src/plugins/howler.spatial.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										659
									
								
								public/scripts/howler/src/plugins/howler.spatial.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,659 @@
 | 
			
		||||
/*!
 | 
			
		||||
 *  Spatial Plugin - Adds support for stereo and 3D audio where Web Audio is supported.
 | 
			
		||||
 *  
 | 
			
		||||
 *  howler.js v2.2.4
 | 
			
		||||
 *  howlerjs.com
 | 
			
		||||
 *
 | 
			
		||||
 *  (c) 2013-2020, James Simpson of GoldFire Studios
 | 
			
		||||
 *  goldfirestudios.com
 | 
			
		||||
 *
 | 
			
		||||
 *  MIT License
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
(function() {
 | 
			
		||||
 | 
			
		||||
  'use strict';
 | 
			
		||||
 | 
			
		||||
  // Setup default properties.
 | 
			
		||||
  HowlerGlobal.prototype._pos = [0, 0, 0];
 | 
			
		||||
  HowlerGlobal.prototype._orientation = [0, 0, -1, 0, 1, 0];
 | 
			
		||||
 | 
			
		||||
  /** Global Methods **/
 | 
			
		||||
  /***************************************************************************/
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Helper method to update the stereo panning position of all current Howls.
 | 
			
		||||
   * Future Howls will not use this value unless explicitly set.
 | 
			
		||||
   * @param  {Number} pan A value of -1.0 is all the way left and 1.0 is all the way right.
 | 
			
		||||
   * @return {Howler/Number}     Self or current stereo panning value.
 | 
			
		||||
   */
 | 
			
		||||
  HowlerGlobal.prototype.stereo = function(pan) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self.ctx || !self.ctx.listener) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Loop through all Howls and update their stereo panning.
 | 
			
		||||
    for (var i=self._howls.length-1; i>=0; i--) {
 | 
			
		||||
      self._howls[i].stereo(pan);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the position of the listener in 3D cartesian space. Sounds using
 | 
			
		||||
   * 3D position will be relative to the listener's position.
 | 
			
		||||
   * @param  {Number} x The x-position of the listener.
 | 
			
		||||
   * @param  {Number} y The y-position of the listener.
 | 
			
		||||
   * @param  {Number} z The z-position of the listener.
 | 
			
		||||
   * @return {Howler/Array}   Self or current listener position.
 | 
			
		||||
   */
 | 
			
		||||
  HowlerGlobal.prototype.pos = function(x, y, z) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self.ctx || !self.ctx.listener) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the defaults for optional 'y' & 'z'.
 | 
			
		||||
    y = (typeof y !== 'number') ? self._pos[1] : y;
 | 
			
		||||
    z = (typeof z !== 'number') ? self._pos[2] : z;
 | 
			
		||||
 | 
			
		||||
    if (typeof x === 'number') {
 | 
			
		||||
      self._pos = [x, y, z];
 | 
			
		||||
 | 
			
		||||
      if (typeof self.ctx.listener.positionX !== 'undefined') {
 | 
			
		||||
        self.ctx.listener.positionX.setTargetAtTime(self._pos[0], Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.positionY.setTargetAtTime(self._pos[1], Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.positionZ.setTargetAtTime(self._pos[2], Howler.ctx.currentTime, 0.1);
 | 
			
		||||
      } else {
 | 
			
		||||
        self.ctx.listener.setPosition(self._pos[0], self._pos[1], self._pos[2]);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return self._pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the direction the listener is pointing in the 3D cartesian space.
 | 
			
		||||
   * A front and up vector must be provided. The front is the direction the
 | 
			
		||||
   * face of the listener is pointing, and up is the direction the top of the
 | 
			
		||||
   * listener is pointing. Thus, these values are expected to be at right angles
 | 
			
		||||
   * from each other.
 | 
			
		||||
   * @param  {Number} x   The x-orientation of the listener.
 | 
			
		||||
   * @param  {Number} y   The y-orientation of the listener.
 | 
			
		||||
   * @param  {Number} z   The z-orientation of the listener.
 | 
			
		||||
   * @param  {Number} xUp The x-orientation of the top of the listener.
 | 
			
		||||
   * @param  {Number} yUp The y-orientation of the top of the listener.
 | 
			
		||||
   * @param  {Number} zUp The z-orientation of the top of the listener.
 | 
			
		||||
   * @return {Howler/Array}     Returns self or the current orientation vectors.
 | 
			
		||||
   */
 | 
			
		||||
  HowlerGlobal.prototype.orientation = function(x, y, z, xUp, yUp, zUp) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self.ctx || !self.ctx.listener) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the defaults for optional 'y' & 'z'.
 | 
			
		||||
    var or = self._orientation;
 | 
			
		||||
    y = (typeof y !== 'number') ? or[1] : y;
 | 
			
		||||
    z = (typeof z !== 'number') ? or[2] : z;
 | 
			
		||||
    xUp = (typeof xUp !== 'number') ? or[3] : xUp;
 | 
			
		||||
    yUp = (typeof yUp !== 'number') ? or[4] : yUp;
 | 
			
		||||
    zUp = (typeof zUp !== 'number') ? or[5] : zUp;
 | 
			
		||||
 | 
			
		||||
    if (typeof x === 'number') {
 | 
			
		||||
      self._orientation = [x, y, z, xUp, yUp, zUp];
 | 
			
		||||
 | 
			
		||||
      if (typeof self.ctx.listener.forwardX !== 'undefined') {
 | 
			
		||||
        self.ctx.listener.forwardX.setTargetAtTime(x, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.forwardY.setTargetAtTime(y, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.forwardZ.setTargetAtTime(z, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.upX.setTargetAtTime(xUp, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.upY.setTargetAtTime(yUp, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
        self.ctx.listener.upZ.setTargetAtTime(zUp, Howler.ctx.currentTime, 0.1);
 | 
			
		||||
      } else {
 | 
			
		||||
        self.ctx.listener.setOrientation(x, y, z, xUp, yUp, zUp);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      return or;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Group Methods **/
 | 
			
		||||
  /***************************************************************************/
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Add new properties to the core init.
 | 
			
		||||
   * @param  {Function} _super Core init method.
 | 
			
		||||
   * @return {Howl}
 | 
			
		||||
   */
 | 
			
		||||
  Howl.prototype.init = (function(_super) {
 | 
			
		||||
    return function(o) {
 | 
			
		||||
      var self = this;
 | 
			
		||||
 | 
			
		||||
      // Setup user-defined default properties.
 | 
			
		||||
      self._orientation = o.orientation || [1, 0, 0];
 | 
			
		||||
      self._stereo = o.stereo || null;
 | 
			
		||||
      self._pos = o.pos || null;
 | 
			
		||||
      self._pannerAttr = {
 | 
			
		||||
        coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : 360,
 | 
			
		||||
        coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : 360,
 | 
			
		||||
        coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : 0,
 | 
			
		||||
        distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : 'inverse',
 | 
			
		||||
        maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : 10000,
 | 
			
		||||
        panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : 'HRTF',
 | 
			
		||||
        refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : 1,
 | 
			
		||||
        rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : 1
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      // Setup event listeners.
 | 
			
		||||
      self._onstereo = o.onstereo ? [{fn: o.onstereo}] : [];
 | 
			
		||||
      self._onpos = o.onpos ? [{fn: o.onpos}] : [];
 | 
			
		||||
      self._onorientation = o.onorientation ? [{fn: o.onorientation}] : [];
 | 
			
		||||
 | 
			
		||||
      // Complete initilization with howler.js core's init function.
 | 
			
		||||
      return _super.call(this, o);
 | 
			
		||||
    };
 | 
			
		||||
  })(Howl.prototype.init);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the stereo panning of the audio source for this sound or all in the group.
 | 
			
		||||
   * @param  {Number} pan  A value of -1.0 is all the way left and 1.0 is all the way right.
 | 
			
		||||
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
 | 
			
		||||
   * @return {Howl/Number}    Returns self or the current stereo panning value.
 | 
			
		||||
   */
 | 
			
		||||
  Howl.prototype.stereo = function(pan, id) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self._webAudio) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the sound hasn't loaded, add it to the load queue to change stereo pan when capable.
 | 
			
		||||
    if (self._state !== 'loaded') {
 | 
			
		||||
      self._queue.push({
 | 
			
		||||
        event: 'stereo',
 | 
			
		||||
        action: function() {
 | 
			
		||||
          self.stereo(pan, id);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check for PannerStereoNode support and fallback to PannerNode if it doesn't exist.
 | 
			
		||||
    var pannerType = (typeof Howler.ctx.createStereoPanner === 'undefined') ? 'spatial' : 'stereo';
 | 
			
		||||
 | 
			
		||||
    // Setup the group's stereo panning if no ID is passed.
 | 
			
		||||
    if (typeof id === 'undefined') {
 | 
			
		||||
      // Return the group's stereo panning if no parameters are passed.
 | 
			
		||||
      if (typeof pan === 'number') {
 | 
			
		||||
        self._stereo = pan;
 | 
			
		||||
        self._pos = [pan, 0, 0];
 | 
			
		||||
      } else {
 | 
			
		||||
        return self._stereo;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Change the streo panning of one or all sounds in group.
 | 
			
		||||
    var ids = self._getSoundIds(id);
 | 
			
		||||
    for (var i=0; i<ids.length; i++) {
 | 
			
		||||
      // Get the sound.
 | 
			
		||||
      var sound = self._soundById(ids[i]);
 | 
			
		||||
 | 
			
		||||
      if (sound) {
 | 
			
		||||
        if (typeof pan === 'number') {
 | 
			
		||||
          sound._stereo = pan;
 | 
			
		||||
          sound._pos = [pan, 0, 0];
 | 
			
		||||
 | 
			
		||||
          if (sound._node) {
 | 
			
		||||
            // If we are falling back, make sure the panningModel is equalpower.
 | 
			
		||||
            sound._pannerAttr.panningModel = 'equalpower';
 | 
			
		||||
 | 
			
		||||
            // Check if there is a panner setup and create a new one if not.
 | 
			
		||||
            if (!sound._panner || !sound._panner.pan) {
 | 
			
		||||
              setupPanner(sound, pannerType);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (pannerType === 'spatial') {
 | 
			
		||||
              if (typeof sound._panner.positionX !== 'undefined') {
 | 
			
		||||
                sound._panner.positionX.setValueAtTime(pan, Howler.ctx.currentTime);
 | 
			
		||||
                sound._panner.positionY.setValueAtTime(0, Howler.ctx.currentTime);
 | 
			
		||||
                sound._panner.positionZ.setValueAtTime(0, Howler.ctx.currentTime);
 | 
			
		||||
              } else {
 | 
			
		||||
                sound._panner.setPosition(pan, 0, 0);
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              sound._panner.pan.setValueAtTime(pan, Howler.ctx.currentTime);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          self._emit('stereo', sound._id);
 | 
			
		||||
        } else {
 | 
			
		||||
          return sound._stereo;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the 3D spatial position of the audio source for this sound or group relative to the global listener.
 | 
			
		||||
   * @param  {Number} x  The x-position of the audio source.
 | 
			
		||||
   * @param  {Number} y  The y-position of the audio source.
 | 
			
		||||
   * @param  {Number} z  The z-position of the audio source.
 | 
			
		||||
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
 | 
			
		||||
   * @return {Howl/Array}    Returns self or the current 3D spatial position: [x, y, z].
 | 
			
		||||
   */
 | 
			
		||||
  Howl.prototype.pos = function(x, y, z, id) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self._webAudio) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the sound hasn't loaded, add it to the load queue to change position when capable.
 | 
			
		||||
    if (self._state !== 'loaded') {
 | 
			
		||||
      self._queue.push({
 | 
			
		||||
        event: 'pos',
 | 
			
		||||
        action: function() {
 | 
			
		||||
          self.pos(x, y, z, id);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the defaults for optional 'y' & 'z'.
 | 
			
		||||
    y = (typeof y !== 'number') ? 0 : y;
 | 
			
		||||
    z = (typeof z !== 'number') ? -0.5 : z;
 | 
			
		||||
 | 
			
		||||
    // Setup the group's spatial position if no ID is passed.
 | 
			
		||||
    if (typeof id === 'undefined') {
 | 
			
		||||
      // Return the group's spatial position if no parameters are passed.
 | 
			
		||||
      if (typeof x === 'number') {
 | 
			
		||||
        self._pos = [x, y, z];
 | 
			
		||||
      } else {
 | 
			
		||||
        return self._pos;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Change the spatial position of one or all sounds in group.
 | 
			
		||||
    var ids = self._getSoundIds(id);
 | 
			
		||||
    for (var i=0; i<ids.length; i++) {
 | 
			
		||||
      // Get the sound.
 | 
			
		||||
      var sound = self._soundById(ids[i]);
 | 
			
		||||
 | 
			
		||||
      if (sound) {
 | 
			
		||||
        if (typeof x === 'number') {
 | 
			
		||||
          sound._pos = [x, y, z];
 | 
			
		||||
 | 
			
		||||
          if (sound._node) {
 | 
			
		||||
            // Check if there is a panner setup and create a new one if not.
 | 
			
		||||
            if (!sound._panner || sound._panner.pan) {
 | 
			
		||||
              setupPanner(sound, 'spatial');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (typeof sound._panner.positionX !== 'undefined') {
 | 
			
		||||
              sound._panner.positionX.setValueAtTime(x, Howler.ctx.currentTime);
 | 
			
		||||
              sound._panner.positionY.setValueAtTime(y, Howler.ctx.currentTime);
 | 
			
		||||
              sound._panner.positionZ.setValueAtTime(z, Howler.ctx.currentTime);
 | 
			
		||||
            } else {
 | 
			
		||||
              sound._panner.setPosition(x, y, z);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          self._emit('pos', sound._id);
 | 
			
		||||
        } else {
 | 
			
		||||
          return sound._pos;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the direction the audio source is pointing in the 3D cartesian coordinate
 | 
			
		||||
   * space. Depending on how direction the sound is, based on the `cone` attributes,
 | 
			
		||||
   * a sound pointing away from the listener can be quiet or silent.
 | 
			
		||||
   * @param  {Number} x  The x-orientation of the source.
 | 
			
		||||
   * @param  {Number} y  The y-orientation of the source.
 | 
			
		||||
   * @param  {Number} z  The z-orientation of the source.
 | 
			
		||||
   * @param  {Number} id (optional) The sound ID. If none is passed, all in group will be updated.
 | 
			
		||||
   * @return {Howl/Array}    Returns self or the current 3D spatial orientation: [x, y, z].
 | 
			
		||||
   */
 | 
			
		||||
  Howl.prototype.orientation = function(x, y, z, id) {
 | 
			
		||||
    var self = this;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self._webAudio) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If the sound hasn't loaded, add it to the load queue to change orientation when capable.
 | 
			
		||||
    if (self._state !== 'loaded') {
 | 
			
		||||
      self._queue.push({
 | 
			
		||||
        event: 'orientation',
 | 
			
		||||
        action: function() {
 | 
			
		||||
          self.orientation(x, y, z, id);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Set the defaults for optional 'y' & 'z'.
 | 
			
		||||
    y = (typeof y !== 'number') ? self._orientation[1] : y;
 | 
			
		||||
    z = (typeof z !== 'number') ? self._orientation[2] : z;
 | 
			
		||||
 | 
			
		||||
    // Setup the group's spatial orientation if no ID is passed.
 | 
			
		||||
    if (typeof id === 'undefined') {
 | 
			
		||||
      // Return the group's spatial orientation if no parameters are passed.
 | 
			
		||||
      if (typeof x === 'number') {
 | 
			
		||||
        self._orientation = [x, y, z];
 | 
			
		||||
      } else {
 | 
			
		||||
        return self._orientation;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Change the spatial orientation of one or all sounds in group.
 | 
			
		||||
    var ids = self._getSoundIds(id);
 | 
			
		||||
    for (var i=0; i<ids.length; i++) {
 | 
			
		||||
      // Get the sound.
 | 
			
		||||
      var sound = self._soundById(ids[i]);
 | 
			
		||||
 | 
			
		||||
      if (sound) {
 | 
			
		||||
        if (typeof x === 'number') {
 | 
			
		||||
          sound._orientation = [x, y, z];
 | 
			
		||||
 | 
			
		||||
          if (sound._node) {
 | 
			
		||||
            // Check if there is a panner setup and create a new one if not.
 | 
			
		||||
            if (!sound._panner) {
 | 
			
		||||
              // Make sure we have a position to setup the node with.
 | 
			
		||||
              if (!sound._pos) {
 | 
			
		||||
                sound._pos = self._pos || [0, 0, -0.5];
 | 
			
		||||
              }
 | 
			
		||||
 | 
			
		||||
              setupPanner(sound, 'spatial');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (typeof sound._panner.orientationX !== 'undefined') {
 | 
			
		||||
              sound._panner.orientationX.setValueAtTime(x, Howler.ctx.currentTime);
 | 
			
		||||
              sound._panner.orientationY.setValueAtTime(y, Howler.ctx.currentTime);
 | 
			
		||||
              sound._panner.orientationZ.setValueAtTime(z, Howler.ctx.currentTime);
 | 
			
		||||
            } else {
 | 
			
		||||
              sound._panner.setOrientation(x, y, z);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          self._emit('orientation', sound._id);
 | 
			
		||||
        } else {
 | 
			
		||||
          return sound._orientation;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get/set the panner node's attributes for a sound or group of sounds.
 | 
			
		||||
   * This method can optionall take 0, 1 or 2 arguments.
 | 
			
		||||
   *   pannerAttr() -> Returns the group's values.
 | 
			
		||||
   *   pannerAttr(id) -> Returns the sound id's values.
 | 
			
		||||
   *   pannerAttr(o) -> Set's the values of all sounds in this Howl group.
 | 
			
		||||
   *   pannerAttr(o, id) -> Set's the values of passed sound id.
 | 
			
		||||
   *
 | 
			
		||||
   *   Attributes:
 | 
			
		||||
   *     coneInnerAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
 | 
			
		||||
   *                      inside of which there will be no volume reduction.
 | 
			
		||||
   *     coneOuterAngle - (360 by default) A parameter for directional audio sources, this is an angle, in degrees,
 | 
			
		||||
   *                      outside of which the volume will be reduced to a constant value of `coneOuterGain`.
 | 
			
		||||
   *     coneOuterGain - (0 by default) A parameter for directional audio sources, this is the gain outside of the
 | 
			
		||||
   *                     `coneOuterAngle`. It is a linear value in the range `[0, 1]`.
 | 
			
		||||
   *     distanceModel - ('inverse' by default) Determines algorithm used to reduce volume as audio moves away from
 | 
			
		||||
   *                     listener. Can be `linear`, `inverse` or `exponential.
 | 
			
		||||
   *     maxDistance - (10000 by default) The maximum distance between source and listener, after which the volume
 | 
			
		||||
   *                   will not be reduced any further.
 | 
			
		||||
   *     refDistance - (1 by default) A reference distance for reducing volume as source moves further from the listener.
 | 
			
		||||
   *                   This is simply a variable of the distance model and has a different effect depending on which model
 | 
			
		||||
   *                   is used and the scale of your coordinates. Generally, volume will be equal to 1 at this distance.
 | 
			
		||||
   *     rolloffFactor - (1 by default) How quickly the volume reduces as source moves from listener. This is simply a
 | 
			
		||||
   *                     variable of the distance model and can be in the range of `[0, 1]` with `linear` and `[0, ∞]`
 | 
			
		||||
   *                     with `inverse` and `exponential`.
 | 
			
		||||
   *     panningModel - ('HRTF' by default) Determines which spatialization algorithm is used to position audio.
 | 
			
		||||
   *                     Can be `HRTF` or `equalpower`.
 | 
			
		||||
   *
 | 
			
		||||
   * @return {Howl/Object} Returns self or current panner attributes.
 | 
			
		||||
   */
 | 
			
		||||
  Howl.prototype.pannerAttr = function() {
 | 
			
		||||
    var self = this;
 | 
			
		||||
    var args = arguments;
 | 
			
		||||
    var o, id, sound;
 | 
			
		||||
 | 
			
		||||
    // Stop right here if not using Web Audio.
 | 
			
		||||
    if (!self._webAudio) {
 | 
			
		||||
      return self;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Determine the values based on arguments.
 | 
			
		||||
    if (args.length === 0) {
 | 
			
		||||
      // Return the group's panner attribute values.
 | 
			
		||||
      return self._pannerAttr;
 | 
			
		||||
    } else if (args.length === 1) {
 | 
			
		||||
      if (typeof args[0] === 'object') {
 | 
			
		||||
        o = args[0];
 | 
			
		||||
 | 
			
		||||
        // Set the grou's panner attribute values.
 | 
			
		||||
        if (typeof id === 'undefined') {
 | 
			
		||||
          if (!o.pannerAttr) {
 | 
			
		||||
            o.pannerAttr = {
 | 
			
		||||
              coneInnerAngle: o.coneInnerAngle,
 | 
			
		||||
              coneOuterAngle: o.coneOuterAngle,
 | 
			
		||||
              coneOuterGain: o.coneOuterGain,
 | 
			
		||||
              distanceModel: o.distanceModel,
 | 
			
		||||
              maxDistance: o.maxDistance,
 | 
			
		||||
              refDistance: o.refDistance,
 | 
			
		||||
              rolloffFactor: o.rolloffFactor,
 | 
			
		||||
              panningModel: o.panningModel
 | 
			
		||||
            };
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          self._pannerAttr = {
 | 
			
		||||
            coneInnerAngle: typeof o.pannerAttr.coneInnerAngle !== 'undefined' ? o.pannerAttr.coneInnerAngle : self._coneInnerAngle,
 | 
			
		||||
            coneOuterAngle: typeof o.pannerAttr.coneOuterAngle !== 'undefined' ? o.pannerAttr.coneOuterAngle : self._coneOuterAngle,
 | 
			
		||||
            coneOuterGain: typeof o.pannerAttr.coneOuterGain !== 'undefined' ? o.pannerAttr.coneOuterGain : self._coneOuterGain,
 | 
			
		||||
            distanceModel: typeof o.pannerAttr.distanceModel !== 'undefined' ? o.pannerAttr.distanceModel : self._distanceModel,
 | 
			
		||||
            maxDistance: typeof o.pannerAttr.maxDistance !== 'undefined' ? o.pannerAttr.maxDistance : self._maxDistance,
 | 
			
		||||
            refDistance: typeof o.pannerAttr.refDistance !== 'undefined' ? o.pannerAttr.refDistance : self._refDistance,
 | 
			
		||||
            rolloffFactor: typeof o.pannerAttr.rolloffFactor !== 'undefined' ? o.pannerAttr.rolloffFactor : self._rolloffFactor,
 | 
			
		||||
            panningModel: typeof o.pannerAttr.panningModel !== 'undefined' ? o.pannerAttr.panningModel : self._panningModel
 | 
			
		||||
          };
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        // Return this sound's panner attribute values.
 | 
			
		||||
        sound = self._soundById(parseInt(args[0], 10));
 | 
			
		||||
        return sound ? sound._pannerAttr : self._pannerAttr;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (args.length === 2) {
 | 
			
		||||
      o = args[0];
 | 
			
		||||
      id = parseInt(args[1], 10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update the values of the specified sounds.
 | 
			
		||||
    var ids = self._getSoundIds(id);
 | 
			
		||||
    for (var i=0; i<ids.length; i++) {
 | 
			
		||||
      sound = self._soundById(ids[i]);
 | 
			
		||||
 | 
			
		||||
      if (sound) {
 | 
			
		||||
        // Merge the new values into the sound.
 | 
			
		||||
        var pa = sound._pannerAttr;
 | 
			
		||||
        pa = {
 | 
			
		||||
          coneInnerAngle: typeof o.coneInnerAngle !== 'undefined' ? o.coneInnerAngle : pa.coneInnerAngle,
 | 
			
		||||
          coneOuterAngle: typeof o.coneOuterAngle !== 'undefined' ? o.coneOuterAngle : pa.coneOuterAngle,
 | 
			
		||||
          coneOuterGain: typeof o.coneOuterGain !== 'undefined' ? o.coneOuterGain : pa.coneOuterGain,
 | 
			
		||||
          distanceModel: typeof o.distanceModel !== 'undefined' ? o.distanceModel : pa.distanceModel,
 | 
			
		||||
          maxDistance: typeof o.maxDistance !== 'undefined' ? o.maxDistance : pa.maxDistance,
 | 
			
		||||
          refDistance: typeof o.refDistance !== 'undefined' ? o.refDistance : pa.refDistance,
 | 
			
		||||
          rolloffFactor: typeof o.rolloffFactor !== 'undefined' ? o.rolloffFactor : pa.rolloffFactor,
 | 
			
		||||
          panningModel: typeof o.panningModel !== 'undefined' ? o.panningModel : pa.panningModel
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Create a new panner node if one doesn't already exist.
 | 
			
		||||
        var panner = sound._panner;
 | 
			
		||||
        if (!panner) {
 | 
			
		||||
          // Make sure we have a position to setup the node with.
 | 
			
		||||
          if (!sound._pos) {
 | 
			
		||||
            sound._pos = self._pos || [0, 0, -0.5];
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          // Create a new panner node.
 | 
			
		||||
          setupPanner(sound, 'spatial');
 | 
			
		||||
          panner = sound._panner
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update the panner values or create a new panner if none exists.
 | 
			
		||||
        panner.coneInnerAngle = pa.coneInnerAngle;
 | 
			
		||||
        panner.coneOuterAngle = pa.coneOuterAngle;
 | 
			
		||||
        panner.coneOuterGain = pa.coneOuterGain;
 | 
			
		||||
        panner.distanceModel = pa.distanceModel;
 | 
			
		||||
        panner.maxDistance = pa.maxDistance;
 | 
			
		||||
        panner.refDistance = pa.refDistance;
 | 
			
		||||
        panner.rolloffFactor = pa.rolloffFactor;
 | 
			
		||||
        panner.panningModel = pa.panningModel;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return self;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /** Single Sound Methods **/
 | 
			
		||||
  /***************************************************************************/
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Add new properties to the core Sound init.
 | 
			
		||||
   * @param  {Function} _super Core Sound init method.
 | 
			
		||||
   * @return {Sound}
 | 
			
		||||
   */
 | 
			
		||||
  Sound.prototype.init = (function(_super) {
 | 
			
		||||
    return function() {
 | 
			
		||||
      var self = this;
 | 
			
		||||
      var parent = self._parent;
 | 
			
		||||
 | 
			
		||||
      // Setup user-defined default properties.
 | 
			
		||||
      self._orientation = parent._orientation;
 | 
			
		||||
      self._stereo = parent._stereo;
 | 
			
		||||
      self._pos = parent._pos;
 | 
			
		||||
      self._pannerAttr = parent._pannerAttr;
 | 
			
		||||
 | 
			
		||||
      // Complete initilization with howler.js core Sound's init function.
 | 
			
		||||
      _super.call(this);
 | 
			
		||||
 | 
			
		||||
      // If a stereo or position was specified, set it up.
 | 
			
		||||
      if (self._stereo) {
 | 
			
		||||
        parent.stereo(self._stereo);
 | 
			
		||||
      } else if (self._pos) {
 | 
			
		||||
        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  })(Sound.prototype.init);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Override the Sound.reset method to clean up properties from the spatial plugin.
 | 
			
		||||
   * @param  {Function} _super Sound reset method.
 | 
			
		||||
   * @return {Sound}
 | 
			
		||||
   */
 | 
			
		||||
  Sound.prototype.reset = (function(_super) {
 | 
			
		||||
    return function() {
 | 
			
		||||
      var self = this;
 | 
			
		||||
      var parent = self._parent;
 | 
			
		||||
 | 
			
		||||
      // Reset all spatial plugin properties on this sound.
 | 
			
		||||
      self._orientation = parent._orientation;
 | 
			
		||||
      self._stereo = parent._stereo;
 | 
			
		||||
      self._pos = parent._pos;
 | 
			
		||||
      self._pannerAttr = parent._pannerAttr;
 | 
			
		||||
 | 
			
		||||
      // If a stereo or position was specified, set it up.
 | 
			
		||||
      if (self._stereo) {
 | 
			
		||||
        parent.stereo(self._stereo);
 | 
			
		||||
      } else if (self._pos) {
 | 
			
		||||
        parent.pos(self._pos[0], self._pos[1], self._pos[2], self._id);
 | 
			
		||||
      } else if (self._panner) {
 | 
			
		||||
        // Disconnect the panner.
 | 
			
		||||
        self._panner.disconnect(0);
 | 
			
		||||
        self._panner = undefined;
 | 
			
		||||
        parent._refreshBuffer(self);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      // Complete resetting of the sound.
 | 
			
		||||
      return _super.call(this);
 | 
			
		||||
    };
 | 
			
		||||
  })(Sound.prototype.reset);
 | 
			
		||||
 | 
			
		||||
  /** Helper Methods **/
 | 
			
		||||
  /***************************************************************************/
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new panner node and save it on the sound.
 | 
			
		||||
   * @param  {Sound} sound Specific sound to setup panning on.
 | 
			
		||||
   * @param {String} type Type of panner to create: 'stereo' or 'spatial'.
 | 
			
		||||
   */
 | 
			
		||||
  var setupPanner = function(sound, type) {
 | 
			
		||||
    type = type || 'spatial';
 | 
			
		||||
 | 
			
		||||
    // Create the new panner node.
 | 
			
		||||
    if (type === 'spatial') {
 | 
			
		||||
      sound._panner = Howler.ctx.createPanner();
 | 
			
		||||
      sound._panner.coneInnerAngle = sound._pannerAttr.coneInnerAngle;
 | 
			
		||||
      sound._panner.coneOuterAngle = sound._pannerAttr.coneOuterAngle;
 | 
			
		||||
      sound._panner.coneOuterGain = sound._pannerAttr.coneOuterGain;
 | 
			
		||||
      sound._panner.distanceModel = sound._pannerAttr.distanceModel;
 | 
			
		||||
      sound._panner.maxDistance = sound._pannerAttr.maxDistance;
 | 
			
		||||
      sound._panner.refDistance = sound._pannerAttr.refDistance;
 | 
			
		||||
      sound._panner.rolloffFactor = sound._pannerAttr.rolloffFactor;
 | 
			
		||||
      sound._panner.panningModel = sound._pannerAttr.panningModel;
 | 
			
		||||
 | 
			
		||||
      if (typeof sound._panner.positionX !== 'undefined') {
 | 
			
		||||
        sound._panner.positionX.setValueAtTime(sound._pos[0], Howler.ctx.currentTime);
 | 
			
		||||
        sound._panner.positionY.setValueAtTime(sound._pos[1], Howler.ctx.currentTime);
 | 
			
		||||
        sound._panner.positionZ.setValueAtTime(sound._pos[2], Howler.ctx.currentTime);
 | 
			
		||||
      } else {
 | 
			
		||||
        sound._panner.setPosition(sound._pos[0], sound._pos[1], sound._pos[2]);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (typeof sound._panner.orientationX !== 'undefined') {
 | 
			
		||||
        sound._panner.orientationX.setValueAtTime(sound._orientation[0], Howler.ctx.currentTime);
 | 
			
		||||
        sound._panner.orientationY.setValueAtTime(sound._orientation[1], Howler.ctx.currentTime);
 | 
			
		||||
        sound._panner.orientationZ.setValueAtTime(sound._orientation[2], Howler.ctx.currentTime);
 | 
			
		||||
      } else {
 | 
			
		||||
        sound._panner.setOrientation(sound._orientation[0], sound._orientation[1], sound._orientation[2]);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      sound._panner = Howler.ctx.createStereoPanner();
 | 
			
		||||
      sound._panner.pan.setValueAtTime(sound._stereo, Howler.ctx.currentTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sound._panner.connect(sound._node);
 | 
			
		||||
 | 
			
		||||
    // Update the connections.
 | 
			
		||||
    if (!sound._paused) {
 | 
			
		||||
      sound._parent.pause(sound._id, true).play(sound._id, true);
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
})();
 | 
			
		||||
		Reference in New Issue
	
	Block a user