Source: client/SimpleFreesound.js

import * as loaders from 'waves-loaders';
import FreesoundQuery from '../common/FreesoundQuery';

const loader = new loaders.AudioBufferLoader();

/**
 * @memberof module:client
 *
 * @class <b><h5>client.SimpleFreesound</h5></b>
 *
 * Client side class allowing to query detailed info on sounds and download them
 * from <a href="http://freesound.org" target="_blank">freesound</a>.
 *
 * - members
 *     - [soundsInfo]{@link module:client.SimpleFreesound#soundsInfo}
 *     - [currentSoundsInfo]{@link module:client.SimpleFreesound#currentSoundsInfo}
 *     - [buffers]{@link module:client.SimpleFreesound#buffers}
 * - methods
 *     - [query]{@link module:client.SimpleFreesound#query}
 *     - [queryFromIds]{@link module:client.SimpleFreesound#queryFromIds}
 *     - [download]{@link module:client.SimpleFreesound#download}
 *     - [queryAndDownload]{@link module:client.SimpleFreesound#queryAndDownload}
 *     - [clear]{@link module:client.SimpleFreesound#clear}
 *
 * Powered by
 * <a href="http://freesound.org/docs/api/" target="_blank">freesound api</a>.
 *
 * @param {String} apiKey - Your api key, as generated from your freesound
 * developer account when creating a new application.
 * @param {Boolean} [storeSoundsInfo=false] - Store all sounds detailed informations,
 * including preview urls, to optimize the number of queries to the API (can be memory consuming).
 *
 * @example
 * <script type="text/javascript" src="simple-freesound.min.js"></script>
 *
 * <script type="text/javascript">
 *   var apiKey = "your_freesound_api_key_goes_here";
 *   var sf = new simpleFreesound.SimpleFreesound(apiKey);
 *
 *   sf.query({
 *     search: [ 'drum', 'bass' ],
 *     duration: [ 0.01, 1 ]
 *   });
 * </script>
 */
class SimpleFreesound extends FreesoundQuery {
  constructor(apiKey, storeSoundsInfo = false) {
    super(apiKey, storeSoundsInfo);
    this._buffers =[];
  }

  /**
   * An object containing every detailed information obtained since
   * instantiation or last call to
   * [<code>clear()</code>]{@link module:client.SimpleFreesound#clear}.
   *
   * @property {Object} soundsInfo
   */
  get soundsInfo() {
    return this._mapToObject(this._soundsInfo);
  }

  set soundsInfo(si) {
    this._soundsInfo = this._objectToMap(si);
  }

  /**
   * An object containing the detailed information obtained from the last call to
   * [<code>query()</code>]{@link module:client.SimpleFreesound#query},
   * [<code>queryFromIds()</code>]{@link module:client.SimpleFreesound#queryFromIds},
   * [<code>download()</code>]{@link module:client.SimpleFreesound#download} or
   * [<code>queryAndDownload()</code>]{@link module:client.SimpleFreesound#queryAndDownload}.
   *
   * @property {Object} currentSoundsInfo
   */
  get currentSoundsInfo() {
    return this._mapToObject(this._currentSoundsInfo);
  }

  set currentSoundsInfo(csi) {
    this._currentSoundsInfo = this._objectToMap(csi);
  }

  /**
   * Array of the buffers stored internally on download success, after call to
   * [<code>download()</code>]{@link module:client.SimpleFreesound#download} or
   * [<code>queryAndDownload()</code>]{@link module:client.SimpleFreesound#queryAndDownload}.
   *
   * @property {Array.AudioBuffer} buffers
   */
  get buffers() {
    return this._buffers;
  }

  /**
   * Get a list of sound ids with detailed information, that correspond to a set of query parameters.
   *
   * @param {Object} queryParams - The parameters used to build the query.
   * @param {Array.String} [queryParams.search] - The search terms that will be used to build the query.
   * @param {Array.String} [queryParams.username] - A list of usernames to search files from.
   * @param {Array} [queryParams.duration] - An array of size 2 : [ minDuration, maxDuration ] (in seconds).
   * If maxDuration is not a number, it will be interpreted as "*" (no maximum duration).
   *
   * @returns {Promise} A Promise object that resolves with the list of new sound ids if the query goes well.
   *
   * @throws {Error} An error if a problem occurs during the query.
   */
  query(queryParams) {
    return super.query(queryParams);
  }

  /**
   * Get detailed information of sounds from their ids.
   *
   * @param {Array.Number} ids - The ids of the sounds we want to get the detailed info of.
   *
   * @returns {Promise} A promise that will resolve with an array of the sound ids
   * the detailed info of which needed to be queried.
   *
   * @throws {Error} An error if a problem occurs during the query.
   */
  queryFromIds(ids) {
    return super.queryFromIds(ids);
  }

  /**
   * Download hq mp3 previews from their sound ids.
   *
   * @param {Array.Number} [ids=null] - The ids of the sounds to download.
   * If <code>null</code>, the ids from
   * [<code>currentSoundsInfo</code>]{@link module:client.SimpleFreesound#currentSoundsInfo}
   * will be used.
   *
   * @returns {Promise} A promise that will resolve if the downloads go well.
   *
   * @throws {Error} An error if a problem occurs during the downloads.
   */
  download(ids = null) {
    if (ids === null)
      ids = Array.from(this._currentSoundsInfo.keys());

    return this._downloadFilesFromUrls(ids);
  }

  /**
   * Download sounds hq mp3 previews from queried sound information.
   *
   * @param {Object} queryParams - The parameters used to build the query.
   * @param {Array.String} [queryParams.search] - The search terms that will be used to build the query.
   * @param {Array.String} [queryParams.username] - A list of usernames to search files from.
   * @param {Array} [queryParams.duration] - An array of size 2 : [ minDuration, maxDuration ] (in seconds).
   * If maxDuration is not a number, it will be interpreted as "*" (no maximum duration).
   *
   * @param {String} [destination='.'] - The folder in which to save the downloaded files.
   *
   * @returns {Promise} A Promise object that resolves with an array of the
   * downloaded <code>AndioBufferif</code>s the downloading go well.
   *
   * @throws {Error} An error if a problem occurs during the downloads.
   */
  queryAndDownload(queryParams) {
    return new Promise((resolve, reject) => {
      super.query(queryParams)
        .then(updatedIds => {
          const ids = Array.from(this._currentSoundsInfo.keys());
          this._downloadFilesFromUrls(ids);
        })
        .then(updatedIds => resolve(updatedIds))
    });
  }

  /***
   * Cancel all unresolved yet promises (queries and downloads).
   */
  abort() {
    // TODO (no native way to cancel unresolved yet promises)
    // maybe using Promise.race() with a cancellable promise and
    // the result of Promise.all in a same Array / iterable ... ?
  }

  /** @private */
  _downloadFilesFromUrls(ids) {
    return new Promise((resolve, reject) => {
      const urls = [];

      for (let i = 0; i < ids.length; i++) {
        let url = this._currentSoundsInfo.get(ids[i])['previews']['preview-hq-mp3'];
        url = url.split(':');//.splice(1);
        url[0] = 'https';
        url = url.join(':');

        urls.push(url);
      }

      loader.load(urls)
        .then(buffers => {
          this._buffers = buffers;

          for (let i = 0; i < ids.length; i++) {
            const soundInfo = this._currentSoundsInfo.get(ids[i]);
            soundInfo['buffer'] = buffers[i];
            this._currentSoundsInfo.set(ids[i], soundInfo);
          }

          resolve(this._buffers);
        });
    });
  }

  /**
   * Clear the internal sound information lists.
   */
  clear() {
    this._soundsInfo = new Map();
    this._currentSoundsInfo = new Map();
  }
};

export default SimpleFreesound;