@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
208 lines • 6.59 kB
JavaScript
import { Observable } from "../../Misc/observable.js";
import { AbstractNamedAudioNode } from "./abstractAudioNode.js";
import { _GetVolumeAudioProperty, _GetVolumeAudioSubNode } from "./subNodes/volumeAudioSubNode.js";
import { _AudioAnalyzer } from "./subProperties/audioAnalyzer.js";
/**
* Abstract class representing a sound in the audio engine.
*/
export class AbstractSound extends AbstractNamedAudioNode {
constructor(name, engine) {
super(name, engine, 3 /* AudioNodeType.HAS_INPUTS_AND_OUTPUTS */); // Inputs are for instances.
this._analyzer = null;
this._newestInstance = null;
this._outBus = null;
this._privateInstances = new Set();
this._state = 1 /* SoundState.Stopped */;
this._instances = this._privateInstances;
/**
* Observable for when the sound stops playing.
*/
this.onEndedObservable = new Observable();
this._onInstanceEnded = (instance) => {
if (this._newestInstance === instance) {
this._newestInstance = null;
}
this._privateInstances.delete(instance);
if (this._instances.size === 0) {
this._state = 1 /* SoundState.Stopped */;
this.onEndedObservable.notifyObservers(this);
}
};
this._onOutBusDisposed = () => {
this._outBus = null;
};
}
/**
* The analyzer features of the sound.
*/
get analyzer() {
return this._analyzer ?? (this._analyzer = new _AudioAnalyzer(this._subGraph));
}
/**
* Whether the sound should start playing automatically. Defaults to `false`.
*/
get autoplay() {
return this._options.autoplay;
}
/**
* The current playback time of the sound, in seconds.
*/
get currentTime() {
const instance = this._getNewestInstance();
return instance ? instance.currentTime : 0;
}
set currentTime(value) {
this.startOffset = value;
const instance = this._getNewestInstance();
if (instance) {
instance.currentTime = value;
}
}
/**
* Whether the sound should loop. Defaults to `false`.
*/
get loop() {
return this._options.loop;
}
set loop(value) {
this._options.loop = value;
}
/**
* The maximum number of instances that can play at the same time. Defaults to `Infinity`.
*/
get maxInstances() {
return this._options.maxInstances;
}
set maxInstances(value) {
this._options.maxInstances = value;
}
/**
* The output bus for the sound. Defaults to `null`.
* - If not set or `null`, the sound is automatically connected to the audio engine's default main bus.
* @see {@link AudioEngineV2.defaultMainBus}
*/
get outBus() {
return this._outBus;
}
set outBus(outBus) {
if (this._outBus === outBus) {
return;
}
if (this._outBus) {
this._outBus.onDisposeObservable.removeCallback(this._onOutBusDisposed);
if (!this._disconnect(this._outBus)) {
throw new Error("Disconnect failed");
}
}
this._outBus = outBus;
if (this._outBus) {
this._outBus.onDisposeObservable.add(this._onOutBusDisposed);
if (!this._connect(this._outBus)) {
throw new Error("Connect failed");
}
}
}
/**
* The time within the sound buffer to start playing at, in seconds. Defaults to `0`.
*/
get startOffset() {
return this._options.startOffset;
}
set startOffset(value) {
this._options.startOffset = value;
}
/**
* The state of the sound.
*/
get state() {
return this._state;
}
/**
* The output volume of the sound.
*/
get volume() {
return _GetVolumeAudioProperty(this._subGraph, "volume");
}
set volume(value) {
// The volume subnode is created on initialization and should always exist.
const node = _GetVolumeAudioSubNode(this._subGraph);
if (!node) {
throw new Error("No volume subnode");
}
node.volume = value;
}
/**
* Releases associated resources.
*/
dispose() {
super.dispose();
this.stop();
this._analyzer?.dispose();
this._analyzer = null;
this._newestInstance = null;
this._outBus = null;
this._privateInstances.clear();
this.onEndedObservable.clear();
}
/**
* Pauses the sound.
*/
pause() {
const it = this._instances.values();
for (let next = it.next(); !next.done; next = it.next()) {
next.value.pause();
}
this._state = 5 /* SoundState.Paused */;
}
/**
* Resumes the sound.
*/
resume() {
if (this._state !== 5 /* SoundState.Paused */) {
return;
}
const it = this._instances.values();
for (let next = it.next(); !next.done; next = it.next()) {
next.value.resume();
}
this._state = 3 /* SoundState.Started */;
}
_beforePlay(instance) {
if (this.state === 5 /* SoundState.Paused */ && this._instances.size > 0) {
this.resume();
return;
}
instance.onEndedObservable.addOnce(this._onInstanceEnded);
this._privateInstances.add(instance);
this._newestInstance = instance;
}
_afterPlay(instance) {
this._state = instance.state;
}
_getNewestInstance() {
if (this._instances.size === 0) {
return null;
}
if (!this._newestInstance) {
const it = this._instances.values();
for (let next = it.next(); !next.done; next = it.next()) {
this._newestInstance = next.value;
}
}
return this._newestInstance;
}
_setState(state) {
this._state = state;
}
_stopExcessInstances() {
if (this.maxInstances < Infinity) {
const numberOfInstancesToStop = Array.from(this._instances).filter((instance) => instance.state === 3 /* SoundState.Started */).length - this.maxInstances;
const it = this._instances.values();
for (let i = 0; i < numberOfInstancesToStop; i++) {
const instance = it.next().value;
instance.stop();
}
}
}
}
//# sourceMappingURL=abstractSound.js.map