play(fadeDuration, volume, whenPlayingPromise, whenPlayingResolve, whenEndedPromise, whenEndedResolve) {
    const state = this._state;
    if (state === audio_object.STATE_ERROR) {
      this._playingPromise = this._playingAndFadedInPromise = this._errorPromise;
    } else if (state !== audio_object.STATE_PLAYING) {
      if (state !== audio_object.STATE_PAUSED) {
        // create promise for when this starts playing
        if (whenPlayingPromise != null && whenPlayingResolve != null) {
          this._when_playing_promise = whenPlayingPromise;
          this._when_playing_resolve = whenPlayingResolve;
        } else {
          this._on_playing();
          this._when_playing_promise = new Promise((resolve, reject) => {
            this._when_playing_resolve = resolve;
          });
        }

        // create promise for when this has ended
        if (whenEndedPromise != null && whenEndedResolve != null) {
          this._when_ended_promise = whenEndedPromise;
          this._when_ended_resolve = whenEndedResolve;
        } else {
          this._on_ended();
          this._when_ended_promise = new Promise((resolve, reject) => {
            this._when_ended_resolve = resolve;
          });
        }

        // ensure audio source is set
        if (this._element.src !== this.src) {
          this._element.src = this.src;
        }
      }

      // set as playing
      this._state = audio_object.STATE_PLAYING;

      // play audio
      const playingPromise = this._element.play();
      if (playingPromise != null) {
        this._playingPromise = playingPromise.catch(this._on_error.bind(this));
      }

      // start at 0 volume and fade in
      this.set_volume(0);
      this._playingAndFadedInPromise = this.fade_to(fadeDuration, volume);
    }

    return this._playingAndFadedInPromise;
  }
Plays audio and returns a promise.
@param [fadeDuration=0]
@param [volume=1]
@returns {Promise}