|
|
@@ -1,48 +1,239 @@
|
|
|
import { Object3D } from '../core/Object3D.js';
|
|
|
|
|
|
+/**
|
|
|
+ * Represents a non-positional ( global ) audio object.
|
|
|
+ *
|
|
|
+ * This and related audio module make use of the [Web Audio API]{@link https://www.w3.org/TR/webaudio-1.1/}.
|
|
|
+ *
|
|
|
+ * ```js
|
|
|
+ * // create an AudioListener and add it to the camera
|
|
|
+ * const listener = new THREE.AudioListener();
|
|
|
+ * camera.add( listener );
|
|
|
+ *
|
|
|
+ * // create a global 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();
|
|
|
+ * });
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * @augments Object3D
|
|
|
+ */
|
|
|
class Audio extends Object3D {
|
|
|
|
|
|
+ /**
|
|
|
+ * Constructs a new audio.
|
|
|
+ *
|
|
|
+ * @param {AudioListener} listener - The global audio listener.
|
|
|
+ */
|
|
|
constructor( listener ) {
|
|
|
|
|
|
super();
|
|
|
|
|
|
this.type = 'Audio';
|
|
|
|
|
|
+ /**
|
|
|
+ * The global audio listener.
|
|
|
+ *
|
|
|
+ * @type {AudioListener}
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.listener = listener;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The audio context.
|
|
|
+ *
|
|
|
+ * @type {AudioContext}
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.context = listener.context;
|
|
|
|
|
|
+ /**
|
|
|
+ * The gain node used for volume control.
|
|
|
+ *
|
|
|
+ * @type {GainNode}
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.gain = this.context.createGain();
|
|
|
this.gain.connect( listener.getInput() );
|
|
|
|
|
|
+ /**
|
|
|
+ * Whether to start playback automatically or not.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
this.autoplay = false;
|
|
|
|
|
|
+ /**
|
|
|
+ * A reference to an audio buffer.
|
|
|
+ *
|
|
|
+ * Defined via {@link Audio#setBuffer}.
|
|
|
+ *
|
|
|
+ * @type {?AudioBuffer}
|
|
|
+ * @default null
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.buffer = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Modify pitch, measured in cents. +/- 100 is a semitone.
|
|
|
+ * +/- 1200 is an octave.
|
|
|
+ *
|
|
|
+ * Defined via {@link Audio#setDetune}.
|
|
|
+ *
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.detune = 0;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Whether the audio should loop or not.
|
|
|
+ *
|
|
|
+ * Defined via {@link Audio#setLoop}.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @default false
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.loop = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Defines where in the audio buffer the replay should
|
|
|
+ * start, in seconds.
|
|
|
+ *
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
+ */
|
|
|
this.loopStart = 0;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Defines where in the audio buffer the replay should
|
|
|
+ * stop, in seconds.
|
|
|
+ *
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
+ */
|
|
|
this.loopEnd = 0;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * An offset to the time within the audio buffer the playback
|
|
|
+ * should begin, in seconds.
|
|
|
+ *
|
|
|
+ * @type {number}
|
|
|
+ * @default 0
|
|
|
+ */
|
|
|
this.offset = 0;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Overrides the default duration of the audio.
|
|
|
+ *
|
|
|
+ * @type {undefined|number}
|
|
|
+ * @default undefined
|
|
|
+ */
|
|
|
this.duration = undefined;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * The playback speed.
|
|
|
+ *
|
|
|
+ * Defined via {@link Audio#setPlaybackRate}.
|
|
|
+ *
|
|
|
+ * @type {number}
|
|
|
+ * @readonly
|
|
|
+ * @default 1
|
|
|
+ */
|
|
|
this.playbackRate = 1;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Indicates whether the audio is playing or not.
|
|
|
+ *
|
|
|
+ * This flag will be automatically set when using {@link Audio#play},
|
|
|
+ * {@link Audio#pause}, {@link Audio#stop}.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @readonly
|
|
|
+ * @default false
|
|
|
+ */
|
|
|
this.isPlaying = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Indicates whether the audio playback can be controlled
|
|
|
+ * with method like {@link Audio#play} or {@link Audio#pause}.
|
|
|
+ *
|
|
|
+ * This flag will be automatically set when audio sources are
|
|
|
+ * defined.
|
|
|
+ *
|
|
|
+ * @type {boolean}
|
|
|
+ * @readonly
|
|
|
+ * @default true
|
|
|
+ */
|
|
|
this.hasPlaybackControl = true;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Holds a reference to the current audio source.
|
|
|
+ *
|
|
|
+ * The property is automatically by one of the `set*()` methods.
|
|
|
+ *
|
|
|
+ * @type {?AudioNode}
|
|
|
+ * @readonly
|
|
|
+ * @default null
|
|
|
+ */
|
|
|
this.source = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Defines the source type.
|
|
|
+ *
|
|
|
+ * The property is automatically by one of the `set*()` methods.
|
|
|
+ *
|
|
|
+ * @type {('empty'|'audioNode'|'mediaNode'|'mediaStreamNode'|'buffer')}
|
|
|
+ * @readonly
|
|
|
+ * @default 'empty'
|
|
|
+ */
|
|
|
this.sourceType = 'empty';
|
|
|
|
|
|
this._startedAt = 0;
|
|
|
this._progress = 0;
|
|
|
this._connected = false;
|
|
|
|
|
|
+ /**
|
|
|
+ * Can be used to apply a variety of low-order filters to create
|
|
|
+ * more complex sound effects e.g. via `BiquadFilterNode`.
|
|
|
+ *
|
|
|
+ * The property is automatically set by {@link Audio#setFilters}.
|
|
|
+ *
|
|
|
+ * @type {Array<AudioNode>}
|
|
|
+ * @readonly
|
|
|
+ */
|
|
|
this.filters = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the output audio node.
|
|
|
+ *
|
|
|
+ * @return {GainNode} The output node.
|
|
|
+ */
|
|
|
getOutput() {
|
|
|
|
|
|
return this.gain;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the given audio node as the source of this instance.
|
|
|
+ *
|
|
|
+ * {@link Audio#sourceType} is set to `audioNode` and {@link Audio#hasPlaybackControl} to `false`.
|
|
|
+ *
|
|
|
+ * @param {AudioNode} audioNode - The audio node like an instance of `OscillatorNode`.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setNodeSource( audioNode ) {
|
|
|
|
|
|
this.hasPlaybackControl = false;
|
|
|
@@ -54,6 +245,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the given media element 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;
|
|
|
@@ -65,6 +264,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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;
|
|
|
@@ -76,6 +283,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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;
|
|
|
@@ -87,6 +302,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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} A reference to this instance.
|
|
|
+ */
|
|
|
play( delay = 0 ) {
|
|
|
|
|
|
if ( this.isPlaying === true ) {
|
|
|
@@ -124,6 +347,13 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Pauses the playback of the audio.
|
|
|
+ *
|
|
|
+ * Can only be used with compatible audio sources that allow playback control.
|
|
|
+ *
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
pause() {
|
|
|
|
|
|
if ( this.hasPlaybackControl === false ) {
|
|
|
@@ -158,6 +388,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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} A reference to this instance.
|
|
|
+ */
|
|
|
stop( delay = 0 ) {
|
|
|
|
|
|
if ( this.hasPlaybackControl === false ) {
|
|
|
@@ -182,6 +420,12 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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 ) {
|
|
|
@@ -208,6 +452,12 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Disconnects to the audio source. This is used internally on
|
|
|
+ * initialisation and when setting / removing filters.
|
|
|
+ *
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
disconnect() {
|
|
|
|
|
|
if ( this._connected === false ) {
|
|
|
@@ -240,12 +490,23 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the current set filters.
|
|
|
+ *
|
|
|
+ * @return {Array<AudioNode>} The list of filters.
|
|
|
+ */
|
|
|
getFilters() {
|
|
|
|
|
|
return this.filters;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets an array of filters and connects them with the audio source.
|
|
|
+ *
|
|
|
+ * @param {Array<AudioNode>} [value] - A list of filters.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setFilters( value ) {
|
|
|
|
|
|
if ( ! value ) value = [];
|
|
|
@@ -266,6 +527,12 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Defines the detuning of oscillation in cents.
|
|
|
+ *
|
|
|
+ * @param {number} value - The detuning of oscillation in cents.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setDetune( value ) {
|
|
|
|
|
|
this.detune = value;
|
|
|
@@ -280,24 +547,48 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the detuning of oscillation in cents.
|
|
|
+ *
|
|
|
+ * @return {number} The detuning of oscillation in cents.
|
|
|
+ */
|
|
|
getDetune() {
|
|
|
|
|
|
return this.detune;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the first filter in the list of filters.
|
|
|
+ *
|
|
|
+ * @return {AudioNode|undefined} The first filter in the list of filters.
|
|
|
+ */
|
|
|
getFilter() {
|
|
|
|
|
|
return this.getFilters()[ 0 ];
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Applies a single filter node to the audio.
|
|
|
+ *
|
|
|
+ * @param {AudioNode} [filter] - The filter to set.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setFilter( filter ) {
|
|
|
|
|
|
return this.setFilters( filter ? [ filter ] : [] );
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the playback rate.
|
|
|
+ *
|
|
|
+ * Can only be used with compatible audio sources that allow playback control.
|
|
|
+ *
|
|
|
+ * @param {number} [value] - The playback rate to set.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setPlaybackRate( value ) {
|
|
|
|
|
|
if ( this.hasPlaybackControl === false ) {
|
|
|
@@ -319,12 +610,20 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the current playback rate.
|
|
|
+
|
|
|
+ * @return {number} The playback rate.
|
|
|
+ */
|
|
|
getPlaybackRate() {
|
|
|
|
|
|
return this.playbackRate;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Automatically called when playback finished.
|
|
|
+ */
|
|
|
onEnded() {
|
|
|
|
|
|
this.isPlaying = false;
|
|
|
@@ -332,6 +631,13 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the loop flag.
|
|
|
+ *
|
|
|
+ * Can only be used with compatible audio sources that allow playback control.
|
|
|
+ *
|
|
|
+ * @return {boolean} Whether the audio should loop or not.
|
|
|
+ */
|
|
|
getLoop() {
|
|
|
|
|
|
if ( this.hasPlaybackControl === false ) {
|
|
|
@@ -345,6 +651,14 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the loop flag.
|
|
|
+ *
|
|
|
+ * Can only be used with compatible audio sources that allow playback control.
|
|
|
+ *
|
|
|
+ * @param {boolean} value - Whether the audio should loop or not.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setLoop( value ) {
|
|
|
|
|
|
if ( this.hasPlaybackControl === false ) {
|
|
|
@@ -366,6 +680,13 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the loop start value which defines where in the audio buffer the replay should
|
|
|
+ * start, in seconds.
|
|
|
+ *
|
|
|
+ * @param {number} value - The loop start value.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setLoopStart( value ) {
|
|
|
|
|
|
this.loopStart = value;
|
|
|
@@ -374,6 +695,13 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the loop end value which defines where in the audio buffer the replay should
|
|
|
+ * stop, in seconds.
|
|
|
+ *
|
|
|
+ * @param {number} value - The loop end value.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setLoopEnd( value ) {
|
|
|
|
|
|
this.loopEnd = value;
|
|
|
@@ -382,12 +710,23 @@ class Audio extends Object3D {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the volume.
|
|
|
+ *
|
|
|
+ * @return {number} The volume.
|
|
|
+ */
|
|
|
getVolume() {
|
|
|
|
|
|
return this.gain.gain.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Sets the volume.
|
|
|
+ *
|
|
|
+ * @param {number} value - The volume to set.
|
|
|
+ * @return {Audio} A reference to this instance.
|
|
|
+ */
|
|
|
setVolume( value ) {
|
|
|
|
|
|
this.gain.gain.setTargetAtTime( value, this.context.currentTime, 0.01 );
|