playcanvas
Version:
Open-source WebGL/WebGPU 3D engine for the web
162 lines (161 loc) • 4.82 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { Debug } from "../../core/debug.js";
import { EventHandler } from "../../core/event-handler.js";
import { math } from "../../core/math/math.js";
import { Listener } from "./listener.js";
const CONTEXT_STATE_RUNNING = "running";
const USER_INPUT_EVENTS = [
"click",
"touchstart",
"mousedown"
];
class SoundManager extends EventHandler {
/**
* Create a new SoundManager instance.
*/
constructor() {
super();
/**
* @type {AudioContext|null}
* @private
*/
__publicField(this, "_context", null);
/**
* @type {() => void}
* @private
*/
__publicField(this, "_unlockHandlerFunc");
/** @private */
__publicField(this, "_userSuspended", false);
/**
* The listener associated with this manager.
*
* @type {Listener}
*/
__publicField(this, "listener");
/** @private */
__publicField(this, "_volume", 1);
this._unlockHandlerFunc = this._unlockHandler.bind(this);
this.listener = new Listener(this);
}
/**
* Sets the global volume for the manager. All {@link SoundInstance}s will scale their volume
* with this volume. Valid between [0, 1].
*
* @type {number}
*/
set volume(volume) {
volume = math.clamp(volume, 0, 1);
this._volume = volume;
this.fire("volumechange", volume);
}
/**
* Gets the global volume for the manager.
*
* @type {number}
*/
get volume() {
return this._volume;
}
get suspended() {
return this._userSuspended;
}
/**
* Get the Web Audio API context. Returns null if the environment does not support the Web
* Audio API.
*
* @type {AudioContext|null}
* @ignore
*/
get context() {
if (!this._context) {
const AudioContextCtor = typeof AudioContext !== "undefined" && AudioContext || typeof webkitAudioContext !== "undefined" && webkitAudioContext;
if (!AudioContextCtor) {
Debug.warnOnce("No support for Web Audio API found");
return null;
}
this._context = new AudioContextCtor();
if (this._context.state !== CONTEXT_STATE_RUNNING) {
this._registerUnlockListeners();
}
}
return this._context;
}
suspend() {
if (!this._userSuspended) {
this._userSuspended = true;
if (this._context && this._context.state === CONTEXT_STATE_RUNNING) {
this._suspend();
}
}
}
resume() {
if (this._userSuspended) {
this._userSuspended = false;
if (this._context && this._context.state !== CONTEXT_STATE_RUNNING) {
this._resume();
}
}
}
destroy() {
this.fire("destroy");
const context = this._context;
if (context) {
this._removeUnlockListeners();
context.close();
this._context = null;
}
}
// resume the sound context
_resume() {
const context = this._context;
if (!context) return;
context.resume().then(() => {
const source = context.createBufferSource();
source.buffer = context.createBuffer(1, 1, context.sampleRate);
source.connect(context.destination);
source.start(0);
source.onended = (event) => {
source.disconnect(0);
this.fire("resume");
};
}, (e) => {
Debug.error(`Attempted to resume the AudioContext on SoundManager.resume(), but it was rejected ${e}`);
}).catch((e) => {
Debug.error(`Attempted to resume the AudioContext on SoundManager.resume(), but threw an exception ${e}`);
});
}
// resume the sound context and fire suspend event if it succeeds
_suspend() {
const context = this._context;
if (!context) return;
context.suspend().then(() => {
this.fire("suspend");
}, (e) => {
Debug.error(`Attempted to suspend the AudioContext on SoundManager.suspend(), but it was rejected ${e}`);
}).catch((e) => {
Debug.error(`Attempted to suspend the AudioContext on SoundManager.suspend(), but threw an exception ${e}`);
});
}
_unlockHandler() {
this._removeUnlockListeners();
if (!this._userSuspended && this._context && this._context.state !== CONTEXT_STATE_RUNNING) {
this._resume();
}
}
_registerUnlockListeners() {
USER_INPUT_EVENTS.forEach((eventName) => {
window.addEventListener(eventName, this._unlockHandlerFunc, false);
});
}
_removeUnlockListeners() {
USER_INPUT_EVENTS.forEach((eventName) => {
window.removeEventListener(eventName, this._unlockHandlerFunc, false);
});
}
}
export {
SoundManager
};