Source: audio/index.js

const core = require('engine/core');
const loader = require('engine/loader');
const { Resource } = loader;
const { Howl, Howler } = require('engine/audio/howler.core');
const EventEmitter = require('engine/EventEmitter');
const config = require('game/config');

/**
 * Audio manager.
 * @private
 */
const audio = new EventEmitter();

Object.assign(audio, {
  Howl: Howl,
  Howler: Howler,

  /**
   * Map of loaded audio files.
   * @memberof module:engine/audio
   * @type {Object<String, Howl>}
   */
  sounds: {},
  /**
   * Whether audio is muted.
   * @memberof module:engine/audio
   * @type {boolean}
   */
  muted: false,
  /**
   * Mute.
   * @memberof module:engine/audio
   * @method mute
   */
  mute: function() { Howler.mute(true); audio.muted = true; audio.emit('mute', true); },
  /**
   * Unmute.
   * @memberof module:engine/audio
   * @method unmute
   */
  unmute: function() { Howler.mute(false); audio.muted = false; audio.emit('mute', false); },

  /**
   * Get/set global audio volume.
   * @memberof module:engine/audio
   * @method volume
   * @param {number} v Volume to set.
   */
  volume: function(v) { Howler.volume(v); },
});

let mutedBeforePause = false;
core.on('pause', function() {
  if (audio.muted) {
    mutedBeforePause = true;
  }
  audio.mute();
});
core.on('resume', function() {
  if (!mutedBeforePause) {
    audio.unmute();
  }
});

// Utils
const AudioUse = (config.audio && Array.isArray(config.audio.use)) ? config.audio.use : ['webm', 'mp3'];
/**
 * Get file extension from a path
 * @private
 * @param {String} path  Full path.
 * @return {String} Extension
 */
function getFileExt(path) {
  return (/[.]/.exec(path)) ? /[^.]+$/.exec(path) : undefined;
}
/**
 * Split "|" separated extensions.
 * @private
 * @param {String} ext Full extension string.
 * @return {Array} List of extensions.
 */
function splitExts(ext) {
  return (/[|]/.exec(ext)) ? ext.split('|') : ext;
}

/**
 * Overrided `load` function.
 * @private
 * @param  {Function} cb Callback when loading completed.
 */
function load(cb) {
  if (this.isLoading) {return;}

  if (this.isComplete) {
    if (cb) {setTimeout(() => cb(this), 1);}

    return;
  }
  else if (cb) {
    this.onComplete.once(cb);
  }

  this.data = new Howl({ src: this.url });
  this.loadType = Resource.LOAD_TYPE.AUDIO;
  this.type = Resource.TYPE.AUDIO;

  this._setFlag(Resource.STATUS_FLAGS.LOADING, true);
  this.onStart.dispatch(this);

  this.data.on('loaderror', this._boundOnError, false);
  this.data.on('load', this._boundComplete, false);

  // Save to sound hash
  audio.sounds[this.name] = this.data;
}
/**
 * Overrided `complete` function.
 * @private
 */
function complete() {
  if (this.data) {
    this.data.off('loaderror', this._boundOnError, false);
    this.data.off('load', this._boundComplete, false);
  }

  if (this.isComplete) {
    throw new Error('Complete called again for an already completed resource.');
  }

  this._setFlag(Resource.STATUS_FLAGS.COMPLETE, true);
  this._setFlag(Resource.STATUS_FLAGS.LOADING, false);

  this.onComplete.dispatch(this);
}

// Add middleware to support Howler.js
loader.pre((res, next) => {
  let i, ext = getFileExt(res.url);
  let urlWithoutExt = res.url.slice(0, ext.index);

  // Check whether this resource is a supported audio file
  for (i = 0; i < AudioUse.length; i++) {
    if (ext[0].indexOf(AudioUse[i]) >= 0) {
      res.url = splitExts(ext[0]);

      // Has a list of extensions
      if (Array.isArray(res.url)) {
        for (i = 0; i < res.url.length; i++) {
          res.url[i] = `${urlWithoutExt}${res.url[i]}`;
        }
      }
      // Has a single extension
      else {
        res.url = [`${urlWithoutExt}${res.url}`];
      }

      // Use specific load and complete functions
      res.load = load;
      res.complete = complete;
      res._boundComplete = res.complete.bind(res);

      next();
      return;
    }
  }

  next();
});

/**
 * Audio module is a simple wrapper of Howler.js.
 * For more details, see the [Howler.js official site](http://goldfirestudios.com/blog/104/howler.js-Modern-Web-Audio-Javascript-Library).
 *
 * @example <caption>Load audio files</caption>
 * import loader from 'engine/loader';
 *
 * // Add audio file with extensions, and give it an `id` for later use.
 * loader.add('bgm', 'bgm.ogg');
 * // You can
 * loader.add('bgm2', 'bgm2.webm|mp3');
 *
 * // Note that ONLY files with extensions in `config.audio.use` will be
 * // properly loaded.
 *
 * @example <caption>Play loaded sound file</caption>
 * import audio from 'engine/audio';
 *
 * // Sound objects are just `Howl` instances
 * audio.sounds['bgm'].loop(true).play();
 *
 * @emits mute
 * @emits unmute
 *
 * @exports engine/audio
 *
 * @requires engine/EventEmitter
 * @requires engine/core
 * @requires engine/loader
 * @requires engine/audio/howler.core
 * @requires game/config
 */
module.exports = audio;