Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sound pileup issue #14047

Merged
merged 12 commits into from
Jul 13, 2023
31 changes: 27 additions & 4 deletions packages/dev/core/src/Audio/sound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Logger } from "../Misc/logger";
import { _WarnImport } from "../Misc/devTools";
import type { ISoundOptions } from "./Interfaces/ISoundOptions";
import { EngineStore } from "../Engines/engineStore";
import type { IAudioEngine } from "./Interfaces/IAudioEngine";
import type { Observer } from "../Misc/observable";

/**
* Defines a sound that can be played in the application.
Expand Down Expand Up @@ -164,6 +166,8 @@ export class Sound {
private _urlType: "Unknown" | "String" | "Array" | "ArrayBuffer" | "MediaStream" | "AudioBuffer" | "MediaElement" = "Unknown";
private _length?: number;
private _offset?: number;
private _tryToPlayTimeout: Nullable<NodeJS.Timeout>;
private _audioUnlockedObserver?: Nullable<Observer<IAudioEngine>>;

/**
* @internal
Expand Down Expand Up @@ -431,6 +435,8 @@ export class Sound {
this._connectedTransformNode.unregisterAfterWorldMatrixUpdate(this._registerFunc);
this._connectedTransformNode = null;
}

this._clearTimeoutsAndObservers();
}
}

Expand Down Expand Up @@ -744,6 +750,8 @@ export class Sound {
public play(time?: number, offset?: number, length?: number): void {
if (this._isReadyToPlay && this._scene.audioEnabled && Engine.audioEngine?.audioContext) {
try {
this._clearTimeoutsAndObservers();

let startTime = time ? Engine.audioEngine?.audioContext.currentTime + time : Engine.audioEngine?.audioContext.currentTime;
if (!this._soundSource || !this._streamingSource) {
if (this._spatialSound && this._soundPanner) {
Expand Down Expand Up @@ -793,15 +801,15 @@ export class Sound {
// Waiting for the audio engine to be unlocked by user click on unmute
Engine.audioEngine?.lock();
if (this.loop || this.autoplay) {
Engine.audioEngine?.onAudioUnlockedObservable.addOnce(() => {
this._audioUnlockedObserver = Engine.audioEngine?.onAudioUnlockedObservable.addOnce(() => {
tryToPlay();
});
}
});
}
} else {
if (this.loop || this.autoplay) {
Engine.audioEngine?.onAudioUnlockedObservable.addOnce(() => {
this._audioUnlockedObserver = Engine.audioEngine?.onAudioUnlockedObservable.addOnce(() => {
tryToPlay();
});
}
Expand Down Expand Up @@ -848,13 +856,13 @@ export class Sound {

if (Engine.audioEngine?.audioContext.state === "suspended") {
// Wait a bit for FF as context seems late to be ready.
setTimeout(() => {
this._tryToPlayTimeout = setTimeout(() => {
if (Engine.audioEngine?.audioContext!.state === "suspended") {
// Automatic playback failed.
// Waiting for the audio engine to be unlocked by user click on unmute
Engine.audioEngine.lock();
if (this.loop || this.autoplay) {
Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
this._audioUnlockedObserver = Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
tryToPlay();
});
}
Expand Down Expand Up @@ -891,6 +899,7 @@ export class Sound {
*/
public stop(time?: number): void {
if (this.isPlaying) {
this._clearTimeoutsAndObservers();
if (this._streaming) {
if (this._htmlAudioElement) {
this._htmlAudioElement.pause();
Expand All @@ -915,6 +924,8 @@ export class Sound {
this._onended();
};
this._soundSource.stop(stopTime);
} else {
this.isPlaying = false;
}
} else if (this.isPaused) {
this.isPaused = false;
Expand All @@ -928,6 +939,7 @@ export class Sound {
*/
public pause(): void {
if (this.isPlaying) {
this._clearTimeoutsAndObservers();
if (this._streaming) {
if (this._htmlAudioElement) {
this._htmlAudioElement.pause();
Expand Down Expand Up @@ -1250,4 +1262,15 @@ export class Sound {
}
this._offset = value;
}

private _clearTimeoutsAndObservers() {
if (this._tryToPlayTimeout) {
clearTimeout(this._tryToPlayTimeout);
this._tryToPlayTimeout = null;
}
if (this._audioUnlockedObserver) {
Engine.audioEngine?.onAudioUnlockedObservable.remove(this._audioUnlockedObserver);
this._audioUnlockedObserver = null;
}
}
}