three
Version:
JavaScript 3D library
214 lines (157 loc) • 4.77 kB
JavaScript
import { Vector3 } from '../math/Vector3.js';
import { Quaternion } from '../math/Quaternion.js';
import { Clock } from '../core/Clock.js';
import { Object3D } from '../core/Object3D.js';
import { AudioContext } from './AudioContext.js';
const _position = /*@__PURE__*/ new Vector3();
const _quaternion = /*@__PURE__*/ new Quaternion();
const _scale = /*@__PURE__*/ new Vector3();
const _orientation = /*@__PURE__*/ new Vector3();
/**
* The class represents a virtual listener of the all positional and non-positional audio effects
* in the scene. A three.js application usually creates a single listener. It is a mandatory
* constructor parameter for audios entities like {@link Audio} and {@link PositionalAudio}.
*
* In most cases, the listener object is a child of the camera. So the 3D transformation of the
* camera represents the 3D transformation of the listener.
*
* @augments Object3D
*/
class AudioListener extends Object3D {
/**
* Constructs a new audio listener.
*/
constructor() {
super();
this.type = 'AudioListener';
/**
* The native audio context.
*
* @type {AudioContext}
* @readonly
*/
this.context = AudioContext.getContext();
/**
* The gain node used for volume control.
*
* @type {GainNode}
* @readonly
*/
this.gain = this.context.createGain();
this.gain.connect( this.context.destination );
/**
* An optional filter.
*
* Defined via {@link AudioListener#setFilter}.
*
* @type {?AudioNode}
* @default null
* @readonly
*/
this.filter = null;
/**
* Time delta values required for `linearRampToValueAtTime()` usage.
*
* @type {number}
* @default 0
* @readonly
*/
this.timeDelta = 0;
// private
this._clock = new Clock();
}
/**
* Returns the listener's input node.
*
* This method is used by other audio nodes to connect to this listener.
*
* @return {GainNode} The input node.
*/
getInput() {
return this.gain;
}
/**
* Removes the current filter from this listener.
*
* @return {AudioListener} A reference to this listener.
*/
removeFilter() {
if ( this.filter !== null ) {
this.gain.disconnect( this.filter );
this.filter.disconnect( this.context.destination );
this.gain.connect( this.context.destination );
this.filter = null;
}
return this;
}
/**
* Returns the current set filter.
*
* @return {?AudioNode} The filter.
*/
getFilter() {
return this.filter;
}
/**
* Sets the given filter to this listener.
*
* @param {AudioNode} value - The filter to set.
* @return {AudioListener} A reference to this listener.
*/
setFilter( value ) {
if ( this.filter !== null ) {
this.gain.disconnect( this.filter );
this.filter.disconnect( this.context.destination );
} else {
this.gain.disconnect( this.context.destination );
}
this.filter = value;
this.gain.connect( this.filter );
this.filter.connect( this.context.destination );
return this;
}
/**
* Returns the applications master volume.
*
* @return {number} The master volume.
*/
getMasterVolume() {
return this.gain.gain.value;
}
/**
* Sets the applications master volume. This volume setting affects
* all audio nodes in the scene.
*
* @param {number} value - The master volume to set.
* @return {AudioListener} A reference to this listener.
*/
setMasterVolume( value ) {
this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
return this;
}
updateMatrixWorld( force ) {
super.updateMatrixWorld( force );
const listener = this.context.listener;
const up = this.up;
this.timeDelta = this._clock.getDelta();
this.matrixWorld.decompose( _position, _quaternion, _scale );
_orientation.set( 0, 0, - 1 ).applyQuaternion( _quaternion );
if ( listener.positionX ) {
// code path for Chrome (see #14393)
const endTime = this.context.currentTime + this.timeDelta;
listener.positionX.linearRampToValueAtTime( _position.x, endTime );
listener.positionY.linearRampToValueAtTime( _position.y, endTime );
listener.positionZ.linearRampToValueAtTime( _position.z, endTime );
listener.forwardX.linearRampToValueAtTime( _orientation.x, endTime );
listener.forwardY.linearRampToValueAtTime( _orientation.y, endTime );
listener.forwardZ.linearRampToValueAtTime( _orientation.z, endTime );
listener.upX.linearRampToValueAtTime( up.x, endTime );
listener.upY.linearRampToValueAtTime( up.y, endTime );
listener.upZ.linearRampToValueAtTime( up.z, endTime );
} else {
listener.setPosition( _position.x, _position.y, _position.z );
listener.setOrientation( _orientation.x, _orientation.y, _orientation.z, up.x, up.y, up.z );
}
}
}
export { AudioListener };