<!-- NEW7 — TEST 7 · THE WOVEN CITY · part 262 -->
ement as the source of this instance.
	 *
	 * {@link Audio#sourceType} is set to `mediaNode` and {@link Audio#hasPlaybackControl} to `false`.
	 *
	 * @param {HTMLMediaElement} mediaElement - The media element.
	 * @return {Audio} A reference to this instance.
	 */
	setMediaElementSource( mediaElement ) {

		this.hasPlaybackControl = false;
		this.sourceType = 'mediaNode';
		this.source = this.context.createMediaElementSource( mediaElement );
		this.connect();

		return this;

	}

	/**
	 * Sets the given media stream as the source of this instance.
	 *
	 * {@link Audio#sourceType} is set to `mediaStreamNode` and {@link Audio#hasPlaybackControl} to `false`.
	 *
	 * @param {MediaStream} mediaStream - The media stream.
	 * @return {Audio} A reference to this instance.
	 */
	setMediaStreamSource( mediaStream ) {

		this.hasPlaybackControl = false;
		this.sourceType = 'mediaStreamNode';
		this.source = this.context.createMediaStreamSource( mediaStream );
		this.connect();

		return this;

	}

	/**
	 * Sets the given audio buffer as the source of this instance.
	 *
	 * {@link Audio#sourceType} is set to `buffer` and {@link Audio#hasPlaybackControl} to `true`.
	 *
	 * @param {AudioBuffer} audioBuffer - The audio buffer.
	 * @return {Audio} A reference to this instance.
	 */
	setBuffer( audioBuffer ) {

		this.buffer = audioBuffer;
		this.sourceType = 'buffer';

		if ( this.autoplay ) this.play();

		return this;

	}

	/**
	 * Starts the playback of the audio.
	 *
	 * Can only be used with compatible audio sources that allow playback control.
	 *
	 * @param {number} [delay=0] - The delay, in seconds, at which the audio should start playing.
	 * @return {Audio|undefined} A reference to this instance.
	 */
	play( delay = 0 ) {

		if ( this.isPlaying === true ) {

			warn( 'Audio: Audio is already playing.' );
			return;

		}

		if ( this.hasPlaybackControl === false ) {

			warn( 'Audio: this Audio has no playback control.' );
			return;

		}

		this._startedAt = this.context.currentTime + delay;

		const source = this.context.createBufferSource();
		source.buffer = this.buffer;
		source.loop = this.loop;
		source.loopStart = this.loopStart;
		source.loopEnd = this.loopEnd;
		source.onended = this.onEnded.bind( this );
		source.start( this._startedAt, this._progress + this.offset, this.duration );

		this.isPlaying = true;

		this.source = source;

		this.setDetune( this.detune );
		this.setPlaybackRate( this.playbackRate );

		return this.connect();

	}

	/**
	 * Pauses the playback of the audio.
	 *
	 * Can only be used with compatible audio sources that allow playback control.
	 *
	 * @return {Audio|undefined} A reference to this instance.
	 */
	pause() {

		if ( this.hasPlaybackControl === false ) {

			warn( 'Audio: this Audio has no playback control.' );
			return;

		}

		if ( this.isPlaying === true ) {

			// update current progress

			this._progress += Math.max( this.context.currentTime - this._startedAt, 0 ) * this.playbackRate;

			if ( this.loop === true ) {

				// ensure _progress does not exceed duration with looped audios

				this._progress = this._progress % ( this.duration || this.buffer.duration );

			}

			this.source.stop();
			this.source.onended = null;

			this.isPlaying = false;

		}

		return this;

	}

	/**
	 * Stops the playback of the audio.
	 *
	 * Can only be used with compatible audio sources that allow playback control.
	 *
	 * @param {number} [delay=0] - The delay, in seconds, at which the audio should stop playing.
	 * @return {Audio|undefined} A reference to this instance.
	 */
	stop( delay = 0 ) {

		if ( this.hasPlaybackControl === false ) {

			warn( 'Audio: this Audio has no playback control.' );
			return;

		}

		this._progress = 0;

		if ( this.source !== null ) {

			this.source.stop( this.context.currentTime + delay );
			this.source.onended = null;

		}

		this.isPlaying = false;

		return this;

	}

	/**
	 * Connects to the audio source. This is used internally on
	 * initialisation and when setting / removing filters.
	 *
	 * @return {Audio} A reference to this instance.
	 */
	connect() {

		if ( this.filters.length > 0 ) {

			this.source.connect( this.filters[ 0 ] );

			for ( let i = 1, l = this.filters.length; i < l; i ++ ) {

				this.filters[ i - 1 ].connect( this.filters[ i ] );

			}

			this.filters[ this.filters.length - 1 ].connect( this.getOutput() );

		} else {

			this.source.connect( this.getOutput() );

		}

		this._connected = true;

		return this;

	}

	/**
	 * Disconnects to the audio source. This is used internally on
	 * initialisation and when setting / removing filters.
	 *
	 * @return {Audio|undefined} A reference to this instance.
	 */
	disconnect() {

		if ( this._connected === false ) {

			return;

		}

		if ( this.filters.length > 0 ) {

			this.source.disconnect( this.filters[ 0 ] );

			for ( let i = 1, l = this.filters.length; i < l; i ++ ) {

				this.filters[ i - 1 ].disconnect( t