<!-- NEW4 — TEST 4 · THE QUILT · part 265 -->
s can be used to analyse audio data.
 *
 * ```js
 * // create an AudioListener and add it to the camera
 * const listener = new THREE.AudioListener();
 * camera.add( listener );
 *
 * // create an Audio source
 * const sound = new THREE.Audio( listener );
 *
 * // load a sound and set it as the Audio object's buffer
 * const audioLoader = new THREE.AudioLoader();
 * audioLoader.load( 'sounds/ambient.ogg', function( buffer ) {
 * 	sound.setBuffer( buffer );
 * 	sound.setLoop(true);
 * 	sound.setVolume(0.5);
 * 	sound.play();
 * });
 *
 * // create an AudioAnalyser, passing in the sound and desired fftSize
 * const analyser = new THREE.AudioAnalyser( sound, 32 );
 *
 * // get the average frequency of the sound
 * const data = analyser.getAverageFrequency();
 * ```
 */
class AudioAnalyser {

	/**
	 * Constructs a new audio analyzer.
	 *
	 * @param {Audio} audio - The audio to analyze.
	 * @param {number} [fftSize=2048] - The window size in samples that is used when performing a Fast Fourier Transform (FFT) to get frequency domain data.
	 */
	constructor( audio, fftSize = 2048 ) {

		/**
		 * The global audio listener.
		 *
		 * @type {AnalyserNode}
		 */
		this.analyser = audio.context.createAnalyser();
		this.analyser.fftSize = fftSize;

		/**
		 * Holds the analyzed data.
		 *
		 * @type {Uint8Array}
		 */
		this.data = new Uint8Array( this.analyser.frequencyBinCount );

		audio.getOutput().connect( this.analyser );

	}

	/**
	 * Returns an array with frequency data of the audio.
	 *
	 * Each item in the array represents the decibel value for a specific frequency.
	 * The frequencies are spread linearly from 0 to 1/2 of the sample rate.
	 * For example, for 48000 sample rate, the last item of the array will represent
	 * the decibel value for 24000 Hz.
	 *
	 * @return {Uint8Array} The frequency data.
	 */
	getFrequencyData() {

		this.analyser.getByteFrequencyData( this.data );

		return this.data;

	}

	/**
	 * Returns the average of the frequencies returned by {@link AudioAnalyser#getFrequencyData}.
	 *
	 * @return {number} The average frequency.
	 */
	getAverageFrequency() {

		let value = 0;
		const data = this.getFrequencyData();

		for ( let i = 0; i < data.length; i ++ ) {

			value += data[ i ];

		}

		return value / data.length;

	}

}

/**
 * Buffered scene graph property that allows weighted accumulation; used internally.
 */
class PropertyMixer {

	/**
	 * Constructs a new property mixer.
	 *
	 * @param {PropertyBinding} binding - The property binding.
	 * @param {string} typeName - The keyframe track type name.
	 * @param {number} valueSize - The keyframe track value size.
	 */
	constructor( binding, typeName, valueSize ) {

		/**
		 * The property binding.
		 *
		 * @type {PropertyBinding}
		 */
		this.binding = binding;

		/**
		 * The keyframe track value size.
		 *
		 * @type {number}
		 */
		this.valueSize = valueSize;

		let mixFunction,
			mixFunctionAdditive,
			setIdentity;

		// buffer layout: [ incoming | accu0 | accu1 | orig | addAccu | (optional work) ]
		//
		// interpolators can use .buffer as their .result
		// the data then goes to 'incoming'
		//
		// 'accu0' and 'accu1' are used frame-interleaved for
		// the cumulative result and are compared to detect
		// changes
		//
		// 'orig' stores the original state of the property
		//
		// 'add' is used for additive cumulative results
		//
		// 'work' is optional and is only present for quaternion types. It is used
		// to store intermediate quaternion multiplication results

		switch ( typeName ) {

			case 'quaternion':
				mixFunction = this._slerp;
				mixFunctionAdditive = this._slerpAdditive;
				setIdentity = this._setAdditiveIdentityQuaternion;

				this.buffer = new Float64Array( valueSize * 6 );
				this._workIndex = 5;
				break;

			case 'string':
			case 'bool':
				mixFunction = this._select;

				// Use the regular mix function and for additive on these types,
				// additive is not relevant for non-numeric types
				mixFunctionAdditive = this._select;

				setIdentity = this._setAdditiveIdentityOther;

				this.buffer = new Array( valueSize * 5 );
				break;

			default:
				mixFunction = this._lerp;
				mixFunctionAdditive = this._lerpAdditive;
				setIdentity = this._setAdditiveIdentityNumeric;

				this.buffer = new Float64Array( valueSize * 5 );

		}

		this._mixBufferRegion = mixFunction;
		this._mixBufferRegionAdditive = mixFunctionAdditive;
		this._setIdentity = setIdentity;
		this._origIndex = 3;
		this._addIndex = 4;

		/**
		 * Accumulated weight of the property binding.
		 *
		 * @type {number}
		 * @default 0
		 */
		this.cumulativeWeight = 0;

		/**
		 * Accumulated additive weight of the property binding.
		 *
		 * @type {number}
		 * @default 0
		 */
		this.cumulativeWeightAdditive = 0;

		/**
		 * Number of active keyframe tracks currently using this property binding.
		 *
		 * @type {number}
		 * @default 0
		 */
		this.useCount = 0;

		/**
		 * Number