@babylonjs/core 8.17.0 → 8.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/Audio/audioEngine.d.ts +11 -16
  2. package/Audio/audioEngine.js +79 -189
  3. package/Audio/audioEngine.js.map +1 -1
  4. package/AudioV2/webAudio/webAudioEngine.d.ts +15 -1
  5. package/AudioV2/webAudio/webAudioEngine.js +25 -1
  6. package/AudioV2/webAudio/webAudioEngine.js.map +1 -1
  7. package/AudioV2/webAudio/webAudioMainOut.d.ts +4 -2
  8. package/AudioV2/webAudio/webAudioMainOut.js +19 -5
  9. package/AudioV2/webAudio/webAudioMainOut.js.map +1 -1
  10. package/AudioV2/webAudio/webAudioUnmuteUI.d.ts +6 -0
  11. package/AudioV2/webAudio/webAudioUnmuteUI.js +30 -2
  12. package/AudioV2/webAudio/webAudioUnmuteUI.js.map +1 -1
  13. package/Culling/Helper/computeShaderBoundingHelper.js +1 -1
  14. package/Culling/Helper/computeShaderBoundingHelper.js.map +1 -1
  15. package/Engines/abstractEngine.js +2 -2
  16. package/Engines/abstractEngine.js.map +1 -1
  17. package/Layers/effectLayer.d.ts +4 -0
  18. package/Layers/effectLayer.js +7 -5
  19. package/Layers/effectLayer.js.map +1 -1
  20. package/Layers/effectLayerSceneComponent.d.ts +1 -17
  21. package/Layers/effectLayerSceneComponent.js +0 -11
  22. package/Layers/effectLayerSceneComponent.js.map +1 -1
  23. package/Materials/Node/Blocks/Vertex/morphTargetsBlock.js +1 -1
  24. package/Materials/Node/Blocks/Vertex/morphTargetsBlock.js.map +1 -1
  25. package/Materials/PBR/pbrSubSurfaceConfiguration.js +14 -0
  26. package/Materials/PBR/pbrSubSurfaceConfiguration.js.map +1 -1
  27. package/Materials/Textures/ktx2decoderTypes.d.ts +1 -0
  28. package/Materials/Textures/ktx2decoderTypes.js +2 -0
  29. package/Materials/Textures/ktx2decoderTypes.js.map +1 -1
  30. package/Materials/materialHelper.functions.js +1 -2
  31. package/Materials/materialHelper.functions.js.map +1 -1
  32. package/Materials/uniformBuffer.d.ts +1 -1
  33. package/Materials/uniformBuffer.js +2 -2
  34. package/Materials/uniformBuffer.js.map +1 -1
  35. package/Meshes/trailMesh.js +2 -2
  36. package/Meshes/trailMesh.js.map +1 -1
  37. package/Morph/morphTargetManager.js +3 -1
  38. package/Morph/morphTargetManager.js.map +1 -1
  39. package/Particles/Node/Blocks/Emitters/IShapeBlock.d.ts +14 -0
  40. package/Particles/Node/Blocks/Emitters/IShapeBlock.js +2 -0
  41. package/Particles/Node/Blocks/Emitters/IShapeBlock.js.map +1 -0
  42. package/Particles/Node/Blocks/Emitters/boxShapeBlock.d.ts +2 -1
  43. package/Particles/Node/Blocks/Emitters/boxShapeBlock.js.map +1 -1
  44. package/Particles/Node/Blocks/Emitters/customShapeBlock.d.ts +2 -1
  45. package/Particles/Node/Blocks/Emitters/customShapeBlock.js.map +1 -1
  46. package/Particles/Node/Blocks/Emitters/cylinderShapeBlock.d.ts +2 -1
  47. package/Particles/Node/Blocks/Emitters/cylinderShapeBlock.js.map +1 -1
  48. package/Particles/Node/Blocks/Emitters/meshShapeBlock.d.ts +2 -1
  49. package/Particles/Node/Blocks/Emitters/meshShapeBlock.js.map +1 -1
  50. package/Particles/Node/Blocks/Emitters/pointShapeBlock.d.ts +2 -1
  51. package/Particles/Node/Blocks/Emitters/pointShapeBlock.js.map +1 -1
  52. package/Particles/Node/Blocks/Emitters/sphereShapeBlock.d.ts +4 -3
  53. package/Particles/Node/Blocks/Emitters/sphereShapeBlock.js +4 -4
  54. package/Particles/Node/Blocks/Emitters/sphereShapeBlock.js.map +1 -1
  55. package/Particles/Node/Blocks/systemBlock.js +0 -1
  56. package/Particles/Node/Blocks/systemBlock.js.map +1 -1
  57. package/Particles/Node/index.d.ts +1 -0
  58. package/Particles/Node/index.js +1 -0
  59. package/Particles/Node/index.js.map +1 -1
  60. package/Particles/Node/nodeParticleSystemSet.d.ts +34 -1
  61. package/Particles/Node/nodeParticleSystemSet.helper.d.ts +10 -0
  62. package/Particles/Node/nodeParticleSystemSet.helper.js +90 -0
  63. package/Particles/Node/nodeParticleSystemSet.helper.js.map +1 -0
  64. package/Particles/Node/nodeParticleSystemSet.js +64 -1
  65. package/Particles/Node/nodeParticleSystemSet.js.map +1 -1
  66. package/Particles/particleSystem.d.ts +4 -6
  67. package/Particles/particleSystem.js +6 -8
  68. package/Particles/particleSystem.js.map +1 -1
  69. package/Physics/v2/characterController.d.ts +1 -1
  70. package/Physics/v2/characterController.js +1 -1
  71. package/Physics/v2/characterController.js.map +1 -1
  72. package/Physics/v2/physicsBody.d.ts +1 -1
  73. package/Physics/v2/physicsBody.js +2 -2
  74. package/Physics/v2/physicsBody.js.map +1 -1
  75. package/PostProcesses/RenderPipeline/postProcessRenderEffect.js +2 -2
  76. package/PostProcesses/RenderPipeline/postProcessRenderEffect.js.map +1 -1
  77. package/Shaders/ShadersInclude/imageProcessingFunctions.js +1 -0
  78. package/Shaders/ShadersInclude/imageProcessingFunctions.js.map +1 -1
  79. package/Shaders/ShadersInclude/morphTargetsVertex.js +1 -1
  80. package/Shaders/ShadersInclude/morphTargetsVertex.js.map +1 -1
  81. package/Shaders/ShadersInclude/morphTargetsVertexDeclaration.js +1 -1
  82. package/Shaders/ShadersInclude/morphTargetsVertexDeclaration.js.map +1 -1
  83. package/ShadersWGSL/ShadersInclude/imageProcessingFunctions.js +1 -0
  84. package/ShadersWGSL/ShadersInclude/imageProcessingFunctions.js.map +1 -1
  85. package/ShadersWGSL/ShadersInclude/morphTargetsVertex.js +1 -1
  86. package/ShadersWGSL/ShadersInclude/morphTargetsVertex.js.map +1 -1
  87. package/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.js +1 -1
  88. package/ShadersWGSL/ShadersInclude/morphTargetsVertexDeclaration.js.map +1 -1
  89. package/ShadersWGSL/boundingInfo.compute.js +2 -2
  90. package/ShadersWGSL/boundingInfo.compute.js.map +1 -1
  91. package/XR/features/WebXRControllerMovement.js +1 -1
  92. package/XR/features/WebXRControllerMovement.js.map +1 -1
  93. package/package.json +1 -1
  94. package/scene.d.ts +23 -4
  95. package/scene.js +38 -4
  96. package/scene.js.map +1 -1
@@ -2,6 +2,7 @@ import type { Analyser } from "./analyser.js";
2
2
  import type { Nullable } from "../types.js";
3
3
  import { Observable } from "../Misc/observable.js";
4
4
  import type { IAudioEngine } from "./Interfaces/IAudioEngine.js";
5
+ import { _WebAudioEngine } from "../AudioV2/webAudio/webAudioEngine.js";
5
6
  /**
6
7
  * This represents the default audio engine used in babylon.
7
8
  * It is responsible to play, synchronize and analyse sounds throughout the application.
@@ -9,10 +10,9 @@ import type { IAudioEngine } from "./Interfaces/IAudioEngine.js";
9
10
  */
10
11
  export declare class AudioEngine implements IAudioEngine {
11
12
  private _audioContext;
12
- private _audioContextInitialized;
13
- private _muteButton;
14
- private _hostElement;
15
- private _audioDestination;
13
+ private _masterGain;
14
+ private _tryToRun;
15
+ private _useCustomUnlockedButton;
16
16
  /**
17
17
  * Gets whether the current host supports Web Audio and thus could create AudioContexts.
18
18
  */
@@ -20,7 +20,8 @@ export declare class AudioEngine implements IAudioEngine {
20
20
  /**
21
21
  * The master gain node defines the global audio volume of your audio engine.
22
22
  */
23
- masterGain: GainNode;
23
+ get masterGain(): GainNode;
24
+ set masterGain(value: GainNode);
24
25
  /**
25
26
  * Defines if Babylon should emit a warning if WebAudio is not supported.
26
27
  */
@@ -43,7 +44,8 @@ export declare class AudioEngine implements IAudioEngine {
43
44
  * Defines if the audio engine relies on a custom unlocked button.
44
45
  * In this case, the embedded button will not be displayed.
45
46
  */
46
- useCustomUnlockedButton: boolean;
47
+ get useCustomUnlockedButton(): boolean;
48
+ set useCustomUnlockedButton(value: boolean);
47
49
  /**
48
50
  * Event raised when audio has been unlocked on the browser.
49
51
  */
@@ -52,6 +54,8 @@ export declare class AudioEngine implements IAudioEngine {
52
54
  * Event raised when audio has been locked on the browser.
53
55
  */
54
56
  onAudioLockedObservable: Observable<IAudioEngine>;
57
+ /** @internal */
58
+ _v2: _WebAudioEngine;
55
59
  /**
56
60
  * Gets the current AudioContext if available.
57
61
  */
@@ -60,8 +64,6 @@ export declare class AudioEngine implements IAudioEngine {
60
64
  /**
61
65
  * Instantiates a new audio engine.
62
66
  *
63
- * There should be only one per page as some browsers restrict the number
64
- * of audio contexts you can create.
65
67
  * @param hostElement defines the host element where to display the mute icon if necessary
66
68
  * @param audioContext defines the audio context to be used by the audio engine
67
69
  * @param audioDestination defines the audio destination node to be used by audio engine
@@ -80,14 +82,6 @@ export declare class AudioEngine implements IAudioEngine {
80
82
  /** @internal */
81
83
  _resumeAudioContextOnStateChange(): void;
82
84
  private _resumeAudioContextAsync;
83
- private _initializeAudioContext;
84
- private _tryToRun;
85
- private _triggerRunningStateAsync;
86
- private _triggerSuspendedState;
87
- private _displayMuteButton;
88
- private _moveButtonToTopLeft;
89
- private _onResize;
90
- private _hideMuteButton;
91
85
  /**
92
86
  * Destroy and release the resources associated with the audio context.
93
87
  */
@@ -109,4 +103,5 @@ export declare class AudioEngine implements IAudioEngine {
109
103
  * @param analyser The analyser to connect to the engine
110
104
  */
111
105
  connectToAnalyser(analyser: Analyser): void;
106
+ private _triggerRunningStateAsync;
112
107
  }
@@ -1,7 +1,6 @@
1
1
  import { Observable } from "../Misc/observable.js";
2
- import { Logger } from "../Misc/logger.js";
3
2
  import { AbstractEngine } from "../Engines/abstractEngine.js";
4
- import { IsWindowObjectExist } from "../Misc/domManagement.js";
3
+ import { _WebAudioEngine } from "../AudioV2/webAudio/webAudioEngine.js";
5
4
  // Sets the default audio engine to Babylon.js
6
5
  AbstractEngine.AudioEngineFactory = (hostElement, audioContext, audioDestination) => {
7
6
  return new AudioEngine(hostElement, audioContext, audioDestination);
@@ -12,33 +11,52 @@ AbstractEngine.AudioEngineFactory = (hostElement, audioContext, audioDestination
12
11
  * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic
13
12
  */
14
13
  export class AudioEngine {
14
+ /**
15
+ * The master gain node defines the global audio volume of your audio engine.
16
+ */
17
+ get masterGain() {
18
+ return this._masterGain;
19
+ }
20
+ set masterGain(value) {
21
+ this._masterGain = this._v2.mainOut._inNode = value;
22
+ }
23
+ /**
24
+ * Defines if the audio engine relies on a custom unlocked button.
25
+ * In this case, the embedded button will not be displayed.
26
+ */
27
+ get useCustomUnlockedButton() {
28
+ return this._useCustomUnlockedButton;
29
+ }
30
+ set useCustomUnlockedButton(value) {
31
+ this._useCustomUnlockedButton = value;
32
+ this._v2._unmuteUIEnabled = !value;
33
+ }
15
34
  /**
16
35
  * Gets the current AudioContext if available.
17
36
  */
18
37
  get audioContext() {
19
- if (!this._audioContextInitialized) {
20
- this._initializeAudioContext();
38
+ if (this._v2.state === "running") {
39
+ // Do not wait for the promise to unlock.
40
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
41
+ this._triggerRunningStateAsync();
21
42
  }
22
- return this._audioContext;
43
+ return this._v2._audioContext;
23
44
  }
24
45
  /**
25
46
  * Instantiates a new audio engine.
26
47
  *
27
- * There should be only one per page as some browsers restrict the number
28
- * of audio contexts you can create.
29
48
  * @param hostElement defines the host element where to display the mute icon if necessary
30
49
  * @param audioContext defines the audio context to be used by the audio engine
31
50
  * @param audioDestination defines the audio destination node to be used by audio engine
32
51
  */
33
52
  constructor(hostElement = null, audioContext = null, audioDestination = null) {
34
53
  this._audioContext = null;
35
- this._audioContextInitialized = false;
36
- this._muteButton = null;
37
- this._audioDestination = null;
54
+ this._tryToRun = false;
55
+ this._useCustomUnlockedButton = false;
38
56
  /**
39
57
  * Gets whether the current host supports Web Audio and thus could create AudioContexts.
40
58
  */
41
- this.canUseWebAudio = false;
59
+ this.canUseWebAudio = true;
42
60
  /**
43
61
  * Defines if Babylon should emit a warning if WebAudio is not supported.
44
62
  */
@@ -58,11 +76,6 @@ export class AudioEngine {
58
76
  * a user interaction has happened.
59
77
  */
60
78
  this.unlocked = false;
61
- /**
62
- * Defines if the audio engine relies on a custom unlocked button.
63
- * In this case, the embedded button will not be displayed.
64
- */
65
- this.useCustomUnlockedButton = false;
66
79
  /**
67
80
  * Event raised when audio has been unlocked on the browser.
68
81
  */
@@ -71,45 +84,44 @@ export class AudioEngine {
71
84
  * Event raised when audio has been locked on the browser.
72
85
  */
73
86
  this.onAudioLockedObservable = new Observable();
74
- this._tryToRun = false;
75
- this._onResize = () => {
76
- this._moveButtonToTopLeft();
77
- };
78
- if (!IsWindowObjectExist()) {
79
- return;
80
- }
81
- if (typeof window.AudioContext !== "undefined") {
82
- this.canUseWebAudio = true;
83
- }
84
- const audioElem = document.createElement("audio");
85
- this._hostElement = hostElement;
86
- this._audioContext = audioContext;
87
- this._audioDestination = audioDestination;
88
- try {
89
- if (audioElem &&
90
- !!audioElem.canPlayType &&
91
- (audioElem.canPlayType('audio/mpeg; codecs="mp3"').replace(/^no$/, "") || audioElem.canPlayType("audio/mp3").replace(/^no$/, ""))) {
92
- this.isMP3supported = true;
87
+ const v2 = new _WebAudioEngine({
88
+ audioContext: audioContext ? audioContext : undefined,
89
+ defaultUIParentElement: hostElement?.parentElement ? hostElement.parentElement : undefined,
90
+ });
91
+ // Historically the unmute button is disabled until a sound tries to play and can't, which results in a call
92
+ // to `AudioEngine.lock()`, which is where the unmute button is enabled if no custom UI is requested.
93
+ v2._unmuteUIEnabled = false;
94
+ this._masterGain = new GainNode(v2._audioContext);
95
+ v2._audioDestination = audioDestination;
96
+ v2.stateChangedObservable.add((state) => {
97
+ if (state === "running") {
98
+ this.unlocked = true;
99
+ this.onAudioUnlockedObservable.notifyObservers(this);
93
100
  }
94
- }
95
- catch (e) {
96
- // protect error during capability check.
97
- }
98
- try {
99
- if (audioElem && !!audioElem.canPlayType && audioElem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, "")) {
100
- this.isOGGsupported = true;
101
+ else {
102
+ this.unlocked = false;
103
+ this.onAudioLockedObservable.notifyObservers(this);
101
104
  }
102
- }
103
- catch (e) {
104
- // protect error during capability check.
105
- }
105
+ });
106
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then
107
+ v2._initAsync({ resumeOnInteraction: false }).then(() => {
108
+ v2.mainOut._inNode = this._masterGain;
109
+ v2.stateChangedObservable.notifyObservers(v2.state);
110
+ });
111
+ this.isMP3supported = v2.isFormatValid("mp3");
112
+ this.isOGGsupported = v2.isFormatValid("ogg");
113
+ this._v2 = v2;
106
114
  }
107
115
  /**
108
116
  * Flags the audio engine in Locked state.
109
117
  * This happens due to new browser policies preventing audio to autoplay.
110
118
  */
111
119
  lock() {
112
- this._triggerSuspendedState();
120
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
121
+ this._v2._audioContext.suspend();
122
+ if (!this._useCustomUnlockedButton) {
123
+ this._v2._unmuteUIEnabled = true;
124
+ }
113
125
  }
114
126
  /**
115
127
  * Unlocks the audio engine once a user action has been done on the dom.
@@ -117,7 +129,6 @@ export class AudioEngine {
117
129
  */
118
130
  unlock() {
119
131
  if (this._audioContext?.state === "running") {
120
- this._hideMuteButton();
121
132
  if (!this.unlocked) {
122
133
  // Notify users that the audio stack is unlocked/unmuted
123
134
  this.unlocked = true;
@@ -125,23 +136,8 @@ export class AudioEngine {
125
136
  }
126
137
  return;
127
138
  }
128
- // On iOS, if the audio context resume request was sent from an event other than a `click` event, then
129
- // the resume promise will never resolve and the only way to get the audio context unstuck is to
130
- // suspend it and make another resume request.
131
- if (this._tryToRun) {
132
- // eslint-disable-next-line github/no-then
133
- this._audioContext?.suspend().then(() => {
134
- this._tryToRun = false;
135
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
136
- this._triggerRunningStateAsync();
137
- }, () => {
138
- Logger.Error("Failed to suspend audio context.");
139
- });
140
- }
141
- else {
142
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
143
- this._triggerRunningStateAsync();
144
- }
139
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
140
+ this._triggerRunningStateAsync();
145
141
  }
146
142
  /** @internal */
147
143
  _resumeAudioContextOnStateChange() {
@@ -158,146 +154,32 @@ export class AudioEngine {
158
154
  }
159
155
  // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
160
156
  _resumeAudioContextAsync() {
161
- if (this._audioContext?.resume) {
162
- return this._audioContext.resume();
163
- }
164
- return Promise.resolve();
165
- }
166
- _initializeAudioContext() {
167
- try {
168
- if (this.canUseWebAudio) {
169
- if (!this._audioContext) {
170
- this._audioContext = new AudioContext();
171
- }
172
- // create a global volume gain node
173
- this.masterGain = this._audioContext.createGain();
174
- this.masterGain.gain.value = 1;
175
- if (!this._audioDestination) {
176
- this._audioDestination = this._audioContext.destination;
177
- }
178
- this.masterGain.connect(this._audioDestination);
179
- this._audioContextInitialized = true;
180
- if (this._audioContext.state === "running") {
181
- // Do not wait for the promise to unlock.
182
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
183
- this._triggerRunningStateAsync();
184
- }
185
- }
186
- }
187
- catch (e) {
188
- this.canUseWebAudio = false;
189
- Logger.Error("Web Audio: " + e.message);
190
- }
191
- }
192
- async _triggerRunningStateAsync() {
193
- if (this._tryToRun) {
194
- return;
195
- }
196
- this._tryToRun = true;
197
- try {
198
- await this._resumeAudioContextAsync();
199
- this._tryToRun = false;
200
- if (this._muteButton) {
201
- this._hideMuteButton();
202
- }
203
- // Notify users that the audio stack is unlocked/unmuted
204
- this.unlocked = true;
205
- this.onAudioUnlockedObservable.notifyObservers(this);
206
- }
207
- catch (_e) {
208
- this._tryToRun = false;
209
- this.unlocked = false;
210
- }
211
- }
212
- _triggerSuspendedState() {
213
- this.unlocked = false;
214
- this.onAudioLockedObservable.notifyObservers(this);
215
- this._displayMuteButton();
216
- }
217
- _displayMuteButton() {
218
- if (this.useCustomUnlockedButton || this._muteButton) {
219
- return;
220
- }
221
- this._muteButton = document.createElement("BUTTON");
222
- this._muteButton.className = "babylonUnmuteIcon";
223
- this._muteButton.id = "babylonUnmuteIconBtn";
224
- this._muteButton.title = "Unmute";
225
- const imageUrl = !window.SVGSVGElement
226
- ? "https://cdn.babylonjs.com/Assets/audio.png"
227
- : "data:image/svg+xml;charset=UTF-8,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2239%22%20height%3D%2232%22%20viewBox%3D%220%200%2039%2032%22%3E%3Cpath%20fill%3D%22white%22%20d%3D%22M9.625%2018.938l-0.031%200.016h-4.953q-0.016%200-0.031-0.016v-12.453q0-0.016%200.031-0.016h4.953q0.031%200%200.031%200.016v12.453zM12.125%207.688l8.719-8.703v27.453l-8.719-8.719-0.016-0.047v-9.938zM23.359%207.875l1.406-1.406%204.219%204.203%204.203-4.203%201.422%201.406-4.219%204.219%204.219%204.203-1.484%201.359-4.141-4.156-4.219%204.219-1.406-1.422%204.219-4.203z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E";
228
- const css = ".babylonUnmuteIcon { position: absolute; left: 20px; top: 20px; height: 40px; width: 60px; background-color: rgba(51,51,51,0.7); background-image: url(" +
229
- imageUrl +
230
- "); background-size: 80%; background-repeat:no-repeat; background-position: center; background-position-y: 4px; border: none; outline: none; transition: transform 0.125s ease-out; cursor: pointer; z-index: 9999; } .babylonUnmuteIcon:hover { transform: scale(1.05) } .babylonUnmuteIcon:active { background-color: rgba(51,51,51,1) }";
231
- const style = document.createElement("style");
232
- style.appendChild(document.createTextNode(css));
233
- document.getElementsByTagName("head")[0].appendChild(style);
234
- document.body.appendChild(this._muteButton);
235
- this._moveButtonToTopLeft();
236
- this._muteButton.addEventListener("touchend", () => {
237
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
238
- this._triggerRunningStateAsync();
239
- }, true);
240
- this._muteButton.addEventListener("click", () => {
241
- this.unlock();
242
- }, true);
243
- window.addEventListener("resize", this._onResize);
244
- }
245
- _moveButtonToTopLeft() {
246
- if (this._hostElement && this._muteButton) {
247
- this._muteButton.style.top = this._hostElement.offsetTop + 20 + "px";
248
- this._muteButton.style.left = this._hostElement.offsetLeft + 20 + "px";
249
- }
250
- }
251
- _hideMuteButton() {
252
- if (this._muteButton) {
253
- document.body.removeChild(this._muteButton);
254
- this._muteButton = null;
157
+ if (this._v2._isUsingOfflineAudioContext) {
158
+ return Promise.resolve();
255
159
  }
160
+ return this._v2._audioContext.resume();
256
161
  }
257
162
  /**
258
163
  * Destroy and release the resources associated with the audio context.
259
164
  */
260
165
  dispose() {
261
- if (this.canUseWebAudio && this._audioContextInitialized) {
262
- if (this._connectedAnalyser && this._audioContext) {
263
- this._connectedAnalyser.stopDebugCanvas();
264
- this._connectedAnalyser.dispose();
265
- this.masterGain.disconnect();
266
- this.masterGain.connect(this._audioContext.destination);
267
- this._connectedAnalyser = null;
268
- }
269
- this.masterGain.gain.value = 1;
270
- }
271
- this.WarnedWebAudioUnsupported = false;
272
- this._hideMuteButton();
273
- window.removeEventListener("resize", this._onResize);
166
+ this._v2.dispose();
274
167
  this.onAudioUnlockedObservable.clear();
275
168
  this.onAudioLockedObservable.clear();
276
- // close is async.
277
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
278
- this._audioContext?.close();
279
- this._audioContext = null;
280
169
  }
281
170
  /**
282
171
  * Gets the global volume sets on the master gain.
283
172
  * @returns the global volume if set or -1 otherwise
284
173
  */
285
174
  getGlobalVolume() {
286
- if (this.canUseWebAudio && this._audioContextInitialized) {
287
- return this.masterGain.gain.value;
288
- }
289
- else {
290
- return -1;
291
- }
175
+ return this.masterGain.gain.value;
292
176
  }
293
177
  /**
294
178
  * Sets the global volume of your experience (sets on the master gain).
295
179
  * @param newVolume Defines the new global volume of the application
296
180
  */
297
181
  setGlobalVolume(newVolume) {
298
- if (this.canUseWebAudio && this._audioContextInitialized) {
299
- this.masterGain.gain.value = newVolume;
300
- }
182
+ this.masterGain.gain.value = newVolume;
301
183
  }
302
184
  /**
303
185
  * Connect the audio engine to an audio analyser allowing some amazing
@@ -309,11 +191,19 @@ export class AudioEngine {
309
191
  if (this._connectedAnalyser) {
310
192
  this._connectedAnalyser.stopDebugCanvas();
311
193
  }
312
- if (this.canUseWebAudio && this._audioContextInitialized && this._audioContext) {
313
- this._connectedAnalyser = analyser;
314
- this.masterGain.disconnect();
315
- this._connectedAnalyser.connectAudioNodes(this.masterGain, this._audioContext.destination);
194
+ this._connectedAnalyser = analyser;
195
+ this.masterGain.disconnect();
196
+ this._connectedAnalyser.connectAudioNodes(this.masterGain, this._v2._audioContext.destination);
197
+ }
198
+ async _triggerRunningStateAsync() {
199
+ if (this._tryToRun) {
200
+ return;
316
201
  }
202
+ this._tryToRun = true;
203
+ await this._resumeAudioContextAsync();
204
+ this._tryToRun = false;
205
+ this.unlocked = true;
206
+ this.onAudioUnlockedObservable.notifyObservers(this);
317
207
  }
318
208
  }
319
209
  //# sourceMappingURL=audioEngine.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audioEngine.js","sourceRoot":"","sources":["../../../../dev/core/src/Audio/audioEngine.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Analyser } from \"./analyser\";\r\n\r\nimport type { Nullable } from \"../types\";\r\nimport { Observable } from \"../Misc/observable\";\r\nimport { Logger } from \"../Misc/logger\";\r\nimport { AbstractEngine } from \"../Engines/abstractEngine\";\r\nimport type { IAudioEngine } from \"./Interfaces/IAudioEngine\";\r\nimport { IsWindowObjectExist } from \"../Misc/domManagement\";\r\n\r\n// Sets the default audio engine to Babylon.js\r\nAbstractEngine.AudioEngineFactory = (\r\n hostElement: Nullable<HTMLElement>,\r\n audioContext: Nullable<AudioContext>,\r\n audioDestination: Nullable<AudioDestinationNode | MediaStreamAudioDestinationNode>\r\n) => {\r\n return new AudioEngine(hostElement, audioContext, audioDestination);\r\n};\r\n\r\n/**\r\n * This represents the default audio engine used in babylon.\r\n * It is responsible to play, synchronize and analyse sounds throughout the application.\r\n * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic\r\n */\r\nexport class AudioEngine implements IAudioEngine {\r\n private _audioContext: Nullable<AudioContext> = null;\r\n private _audioContextInitialized = false;\r\n private _muteButton: Nullable<HTMLButtonElement> = null;\r\n private _hostElement: Nullable<HTMLElement>;\r\n private _audioDestination: Nullable<AudioDestinationNode | MediaStreamAudioDestinationNode> = null;\r\n\r\n /**\r\n * Gets whether the current host supports Web Audio and thus could create AudioContexts.\r\n */\r\n public canUseWebAudio: boolean = false;\r\n\r\n /**\r\n * The master gain node defines the global audio volume of your audio engine.\r\n */\r\n public masterGain: GainNode;\r\n\r\n /**\r\n * Defines if Babylon should emit a warning if WebAudio is not supported.\r\n */\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n public WarnedWebAudioUnsupported: boolean = false;\r\n\r\n /**\r\n * Gets whether or not mp3 are supported by your browser.\r\n */\r\n public isMP3supported: boolean = false;\r\n\r\n /**\r\n * Gets whether or not ogg are supported by your browser.\r\n */\r\n public isOGGsupported: boolean = false;\r\n\r\n /**\r\n * Gets whether audio has been unlocked on the device.\r\n * Some Browsers have strong restrictions about Audio and won't autoplay unless\r\n * a user interaction has happened.\r\n */\r\n public unlocked: boolean = false;\r\n\r\n /**\r\n * Defines if the audio engine relies on a custom unlocked button.\r\n * In this case, the embedded button will not be displayed.\r\n */\r\n public useCustomUnlockedButton: boolean = false;\r\n\r\n /**\r\n * Event raised when audio has been unlocked on the browser.\r\n */\r\n public onAudioUnlockedObservable = new Observable<IAudioEngine>();\r\n\r\n /**\r\n * Event raised when audio has been locked on the browser.\r\n */\r\n public onAudioLockedObservable = new Observable<IAudioEngine>();\r\n\r\n /**\r\n * Gets the current AudioContext if available.\r\n */\r\n public get audioContext(): Nullable<AudioContext> {\r\n if (!this._audioContextInitialized) {\r\n this._initializeAudioContext();\r\n }\r\n return this._audioContext;\r\n }\r\n\r\n private _connectedAnalyser: Nullable<Analyser>;\r\n\r\n /**\r\n * Instantiates a new audio engine.\r\n *\r\n * There should be only one per page as some browsers restrict the number\r\n * of audio contexts you can create.\r\n * @param hostElement defines the host element where to display the mute icon if necessary\r\n * @param audioContext defines the audio context to be used by the audio engine\r\n * @param audioDestination defines the audio destination node to be used by audio engine\r\n */\r\n constructor(\r\n hostElement: Nullable<HTMLElement> = null,\r\n audioContext: Nullable<AudioContext> = null,\r\n audioDestination: Nullable<AudioDestinationNode | MediaStreamAudioDestinationNode> = null\r\n ) {\r\n if (!IsWindowObjectExist()) {\r\n return;\r\n }\r\n if (typeof window.AudioContext !== \"undefined\") {\r\n this.canUseWebAudio = true;\r\n }\r\n\r\n const audioElem = document.createElement(\"audio\");\r\n this._hostElement = hostElement;\r\n this._audioContext = audioContext;\r\n this._audioDestination = audioDestination;\r\n\r\n try {\r\n if (\r\n audioElem &&\r\n !!audioElem.canPlayType &&\r\n (audioElem.canPlayType('audio/mpeg; codecs=\"mp3\"').replace(/^no$/, \"\") || audioElem.canPlayType(\"audio/mp3\").replace(/^no$/, \"\"))\r\n ) {\r\n this.isMP3supported = true;\r\n }\r\n } catch (e) {\r\n // protect error during capability check.\r\n }\r\n\r\n try {\r\n if (audioElem && !!audioElem.canPlayType && audioElem.canPlayType('audio/ogg; codecs=\"vorbis\"').replace(/^no$/, \"\")) {\r\n this.isOGGsupported = true;\r\n }\r\n } catch (e) {\r\n // protect error during capability check.\r\n }\r\n }\r\n\r\n /**\r\n * Flags the audio engine in Locked state.\r\n * This happens due to new browser policies preventing audio to autoplay.\r\n */\r\n public lock() {\r\n this._triggerSuspendedState();\r\n }\r\n\r\n /**\r\n * Unlocks the audio engine once a user action has been done on the dom.\r\n * This is helpful to resume play once browser policies have been satisfied.\r\n */\r\n public unlock() {\r\n if (this._audioContext?.state === \"running\") {\r\n this._hideMuteButton();\r\n\r\n if (!this.unlocked) {\r\n // Notify users that the audio stack is unlocked/unmuted\r\n this.unlocked = true;\r\n this.onAudioUnlockedObservable.notifyObservers(this);\r\n }\r\n\r\n return;\r\n }\r\n\r\n // On iOS, if the audio context resume request was sent from an event other than a `click` event, then\r\n // the resume promise will never resolve and the only way to get the audio context unstuck is to\r\n // suspend it and make another resume request.\r\n if (this._tryToRun) {\r\n // eslint-disable-next-line github/no-then\r\n this._audioContext?.suspend().then(\r\n () => {\r\n this._tryToRun = false;\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n },\r\n () => {\r\n Logger.Error(\"Failed to suspend audio context.\");\r\n }\r\n );\r\n } else {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n }\r\n }\r\n\r\n /** @internal */\r\n public _resumeAudioContextOnStateChange(): void {\r\n this._audioContext?.addEventListener(\r\n \"statechange\",\r\n () => {\r\n if (this.unlocked && this._audioContext?.state !== \"running\") {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._resumeAudioContextAsync();\r\n }\r\n },\r\n {\r\n once: true,\r\n passive: true,\r\n signal: AbortSignal.timeout(3000),\r\n }\r\n );\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax\r\n private _resumeAudioContextAsync(): Promise<void> {\r\n if (this._audioContext?.resume) {\r\n return this._audioContext.resume();\r\n }\r\n\r\n return Promise.resolve();\r\n }\r\n\r\n private _initializeAudioContext() {\r\n try {\r\n if (this.canUseWebAudio) {\r\n if (!this._audioContext) {\r\n this._audioContext = new AudioContext();\r\n }\r\n // create a global volume gain node\r\n this.masterGain = this._audioContext.createGain();\r\n this.masterGain.gain.value = 1;\r\n if (!this._audioDestination) {\r\n this._audioDestination = this._audioContext.destination;\r\n }\r\n this.masterGain.connect(this._audioDestination);\r\n this._audioContextInitialized = true;\r\n if (this._audioContext.state === \"running\") {\r\n // Do not wait for the promise to unlock.\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n }\r\n }\r\n } catch (e) {\r\n this.canUseWebAudio = false;\r\n Logger.Error(\"Web Audio: \" + e.message);\r\n }\r\n }\r\n\r\n private _tryToRun = false;\r\n private async _triggerRunningStateAsync() {\r\n if (this._tryToRun) {\r\n return;\r\n }\r\n this._tryToRun = true;\r\n\r\n try {\r\n await this._resumeAudioContextAsync();\r\n this._tryToRun = false;\r\n if (this._muteButton) {\r\n this._hideMuteButton();\r\n }\r\n // Notify users that the audio stack is unlocked/unmuted\r\n this.unlocked = true;\r\n this.onAudioUnlockedObservable.notifyObservers(this);\r\n } catch (_e) {\r\n this._tryToRun = false;\r\n this.unlocked = false;\r\n }\r\n }\r\n\r\n private _triggerSuspendedState() {\r\n this.unlocked = false;\r\n this.onAudioLockedObservable.notifyObservers(this);\r\n this._displayMuteButton();\r\n }\r\n\r\n private _displayMuteButton() {\r\n if (this.useCustomUnlockedButton || this._muteButton) {\r\n return;\r\n }\r\n\r\n this._muteButton = <HTMLButtonElement>document.createElement(\"BUTTON\");\r\n this._muteButton.className = \"babylonUnmuteIcon\";\r\n this._muteButton.id = \"babylonUnmuteIconBtn\";\r\n this._muteButton.title = \"Unmute\";\r\n const imageUrl = !window.SVGSVGElement\r\n ? \"https://cdn.babylonjs.com/Assets/audio.png\"\r\n : \"data:image/svg+xml;charset=UTF-8,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2239%22%20height%3D%2232%22%20viewBox%3D%220%200%2039%2032%22%3E%3Cpath%20fill%3D%22white%22%20d%3D%22M9.625%2018.938l-0.031%200.016h-4.953q-0.016%200-0.031-0.016v-12.453q0-0.016%200.031-0.016h4.953q0.031%200%200.031%200.016v12.453zM12.125%207.688l8.719-8.703v27.453l-8.719-8.719-0.016-0.047v-9.938zM23.359%207.875l1.406-1.406%204.219%204.203%204.203-4.203%201.422%201.406-4.219%204.219%204.219%204.203-1.484%201.359-4.141-4.156-4.219%204.219-1.406-1.422%204.219-4.203z%22%3E%3C%2Fpath%3E%3C%2Fsvg%3E\";\r\n\r\n const css =\r\n \".babylonUnmuteIcon { position: absolute; left: 20px; top: 20px; height: 40px; width: 60px; background-color: rgba(51,51,51,0.7); background-image: url(\" +\r\n imageUrl +\r\n \"); background-size: 80%; background-repeat:no-repeat; background-position: center; background-position-y: 4px; border: none; outline: none; transition: transform 0.125s ease-out; cursor: pointer; z-index: 9999; } .babylonUnmuteIcon:hover { transform: scale(1.05) } .babylonUnmuteIcon:active { background-color: rgba(51,51,51,1) }\";\r\n\r\n const style = document.createElement(\"style\");\r\n style.appendChild(document.createTextNode(css));\r\n document.getElementsByTagName(\"head\")[0].appendChild(style);\r\n\r\n document.body.appendChild(this._muteButton);\r\n\r\n this._moveButtonToTopLeft();\r\n\r\n this._muteButton.addEventListener(\r\n \"touchend\",\r\n () => {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n },\r\n true\r\n );\r\n this._muteButton.addEventListener(\r\n \"click\",\r\n () => {\r\n this.unlock();\r\n },\r\n true\r\n );\r\n\r\n window.addEventListener(\"resize\", this._onResize);\r\n }\r\n\r\n private _moveButtonToTopLeft() {\r\n if (this._hostElement && this._muteButton) {\r\n this._muteButton.style.top = this._hostElement.offsetTop + 20 + \"px\";\r\n this._muteButton.style.left = this._hostElement.offsetLeft + 20 + \"px\";\r\n }\r\n }\r\n\r\n private _onResize = () => {\r\n this._moveButtonToTopLeft();\r\n };\r\n\r\n private _hideMuteButton() {\r\n if (this._muteButton) {\r\n document.body.removeChild(this._muteButton);\r\n this._muteButton = null;\r\n }\r\n }\r\n\r\n /**\r\n * Destroy and release the resources associated with the audio context.\r\n */\r\n public dispose(): void {\r\n if (this.canUseWebAudio && this._audioContextInitialized) {\r\n if (this._connectedAnalyser && this._audioContext) {\r\n this._connectedAnalyser.stopDebugCanvas();\r\n this._connectedAnalyser.dispose();\r\n this.masterGain.disconnect();\r\n this.masterGain.connect(this._audioContext.destination);\r\n this._connectedAnalyser = null;\r\n }\r\n this.masterGain.gain.value = 1;\r\n }\r\n this.WarnedWebAudioUnsupported = false;\r\n this._hideMuteButton();\r\n window.removeEventListener(\"resize\", this._onResize);\r\n\r\n this.onAudioUnlockedObservable.clear();\r\n this.onAudioLockedObservable.clear();\r\n\r\n // close is async.\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._audioContext?.close();\r\n this._audioContext = null;\r\n }\r\n\r\n /**\r\n * Gets the global volume sets on the master gain.\r\n * @returns the global volume if set or -1 otherwise\r\n */\r\n public getGlobalVolume(): number {\r\n if (this.canUseWebAudio && this._audioContextInitialized) {\r\n return this.masterGain.gain.value;\r\n } else {\r\n return -1;\r\n }\r\n }\r\n\r\n /**\r\n * Sets the global volume of your experience (sets on the master gain).\r\n * @param newVolume Defines the new global volume of the application\r\n */\r\n public setGlobalVolume(newVolume: number): void {\r\n if (this.canUseWebAudio && this._audioContextInitialized) {\r\n this.masterGain.gain.value = newVolume;\r\n }\r\n }\r\n\r\n /**\r\n * Connect the audio engine to an audio analyser allowing some amazing\r\n * synchronization between the sounds/music and your visualization (VuMeter for instance).\r\n * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic#using-the-analyser\r\n * @param analyser The analyser to connect to the engine\r\n */\r\n public connectToAnalyser(analyser: Analyser): void {\r\n if (this._connectedAnalyser) {\r\n this._connectedAnalyser.stopDebugCanvas();\r\n }\r\n if (this.canUseWebAudio && this._audioContextInitialized && this._audioContext) {\r\n this._connectedAnalyser = analyser;\r\n this.masterGain.disconnect();\r\n this._connectedAnalyser.connectAudioNodes(this.masterGain, this._audioContext.destination);\r\n }\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"audioEngine.js","sourceRoot":"","sources":["../../../../dev/core/src/Audio/audioEngine.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Analyser } from \"./analyser\";\r\n\r\nimport type { Nullable } from \"../types\";\r\nimport { Observable } from \"../Misc/observable\";\r\nimport { AbstractEngine } from \"../Engines/abstractEngine\";\r\nimport type { IAudioEngine } from \"./Interfaces/IAudioEngine\";\r\nimport { _WebAudioEngine } from \"../AudioV2/webAudio/webAudioEngine\";\r\n\r\n// Sets the default audio engine to Babylon.js\r\nAbstractEngine.AudioEngineFactory = (\r\n hostElement: Nullable<HTMLElement>,\r\n audioContext: Nullable<AudioContext>,\r\n audioDestination: Nullable<AudioDestinationNode | MediaStreamAudioDestinationNode>\r\n) => {\r\n return new AudioEngine(hostElement, audioContext, audioDestination);\r\n};\r\n\r\n/**\r\n * This represents the default audio engine used in babylon.\r\n * It is responsible to play, synchronize and analyse sounds throughout the application.\r\n * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic\r\n */\r\nexport class AudioEngine implements IAudioEngine {\r\n private _audioContext: Nullable<AudioContext> = null;\r\n private _masterGain: GainNode;\r\n private _tryToRun = false;\r\n private _useCustomUnlockedButton: boolean = false;\r\n\r\n /**\r\n * Gets whether the current host supports Web Audio and thus could create AudioContexts.\r\n */\r\n public canUseWebAudio: boolean = true;\r\n\r\n /**\r\n * The master gain node defines the global audio volume of your audio engine.\r\n */\r\n public get masterGain(): GainNode {\r\n return this._masterGain;\r\n }\r\n\r\n public set masterGain(value: GainNode) {\r\n this._masterGain = this._v2.mainOut._inNode = value;\r\n }\r\n\r\n /**\r\n * Defines if Babylon should emit a warning if WebAudio is not supported.\r\n */\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n public WarnedWebAudioUnsupported: boolean = false;\r\n\r\n /**\r\n * Gets whether or not mp3 are supported by your browser.\r\n */\r\n public isMP3supported: boolean = false;\r\n\r\n /**\r\n * Gets whether or not ogg are supported by your browser.\r\n */\r\n public isOGGsupported: boolean = false;\r\n\r\n /**\r\n * Gets whether audio has been unlocked on the device.\r\n * Some Browsers have strong restrictions about Audio and won't autoplay unless\r\n * a user interaction has happened.\r\n */\r\n public unlocked: boolean = false;\r\n\r\n /**\r\n * Defines if the audio engine relies on a custom unlocked button.\r\n * In this case, the embedded button will not be displayed.\r\n */\r\n public get useCustomUnlockedButton(): boolean {\r\n return this._useCustomUnlockedButton;\r\n }\r\n\r\n public set useCustomUnlockedButton(value: boolean) {\r\n this._useCustomUnlockedButton = value;\r\n this._v2._unmuteUIEnabled = !value;\r\n }\r\n\r\n /**\r\n * Event raised when audio has been unlocked on the browser.\r\n */\r\n public onAudioUnlockedObservable = new Observable<IAudioEngine>();\r\n\r\n /**\r\n * Event raised when audio has been locked on the browser.\r\n */\r\n public onAudioLockedObservable = new Observable<IAudioEngine>();\r\n\r\n /** @internal */\r\n public _v2: _WebAudioEngine;\r\n\r\n /**\r\n * Gets the current AudioContext if available.\r\n */\r\n public get audioContext(): Nullable<AudioContext> {\r\n if (this._v2.state === \"running\") {\r\n // Do not wait for the promise to unlock.\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n }\r\n return this._v2._audioContext;\r\n }\r\n\r\n private _connectedAnalyser: Nullable<Analyser>;\r\n\r\n /**\r\n * Instantiates a new audio engine.\r\n *\r\n * @param hostElement defines the host element where to display the mute icon if necessary\r\n * @param audioContext defines the audio context to be used by the audio engine\r\n * @param audioDestination defines the audio destination node to be used by audio engine\r\n */\r\n constructor(\r\n hostElement: Nullable<HTMLElement> = null,\r\n audioContext: Nullable<AudioContext> = null,\r\n audioDestination: Nullable<AudioDestinationNode | MediaStreamAudioDestinationNode> = null\r\n ) {\r\n const v2 = new _WebAudioEngine({\r\n audioContext: audioContext ? audioContext : undefined,\r\n defaultUIParentElement: hostElement?.parentElement ? hostElement.parentElement : undefined,\r\n });\r\n\r\n // Historically the unmute button is disabled until a sound tries to play and can't, which results in a call\r\n // to `AudioEngine.lock()`, which is where the unmute button is enabled if no custom UI is requested.\r\n v2._unmuteUIEnabled = false;\r\n\r\n this._masterGain = new GainNode(v2._audioContext);\r\n v2._audioDestination = audioDestination;\r\n\r\n v2.stateChangedObservable.add((state) => {\r\n if (state === \"running\") {\r\n this.unlocked = true;\r\n this.onAudioUnlockedObservable.notifyObservers(this);\r\n } else {\r\n this.unlocked = false;\r\n this.onAudioLockedObservable.notifyObservers(this);\r\n }\r\n });\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises, github/no-then\r\n v2._initAsync({ resumeOnInteraction: false }).then(() => {\r\n v2.mainOut._inNode = this._masterGain;\r\n v2.stateChangedObservable.notifyObservers(v2.state);\r\n });\r\n\r\n this.isMP3supported = v2.isFormatValid(\"mp3\");\r\n this.isOGGsupported = v2.isFormatValid(\"ogg\");\r\n\r\n this._v2 = v2;\r\n }\r\n\r\n /**\r\n * Flags the audio engine in Locked state.\r\n * This happens due to new browser policies preventing audio to autoplay.\r\n */\r\n public lock() {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._v2._audioContext.suspend();\r\n\r\n if (!this._useCustomUnlockedButton) {\r\n this._v2._unmuteUIEnabled = true;\r\n }\r\n }\r\n\r\n /**\r\n * Unlocks the audio engine once a user action has been done on the dom.\r\n * This is helpful to resume play once browser policies have been satisfied.\r\n */\r\n public unlock() {\r\n if (this._audioContext?.state === \"running\") {\r\n if (!this.unlocked) {\r\n // Notify users that the audio stack is unlocked/unmuted\r\n this.unlocked = true;\r\n this.onAudioUnlockedObservable.notifyObservers(this);\r\n }\r\n\r\n return;\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._triggerRunningStateAsync();\r\n }\r\n\r\n /** @internal */\r\n public _resumeAudioContextOnStateChange(): void {\r\n this._audioContext?.addEventListener(\r\n \"statechange\",\r\n () => {\r\n if (this.unlocked && this._audioContext?.state !== \"running\") {\r\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\r\n this._resumeAudioContextAsync();\r\n }\r\n },\r\n {\r\n once: true,\r\n passive: true,\r\n signal: AbortSignal.timeout(3000),\r\n }\r\n );\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax\r\n private _resumeAudioContextAsync(): Promise<void> {\r\n if (this._v2._isUsingOfflineAudioContext) {\r\n return Promise.resolve();\r\n }\r\n\r\n return this._v2._audioContext.resume();\r\n }\r\n\r\n /**\r\n * Destroy and release the resources associated with the audio context.\r\n */\r\n public dispose(): void {\r\n this._v2.dispose();\r\n\r\n this.onAudioUnlockedObservable.clear();\r\n this.onAudioLockedObservable.clear();\r\n }\r\n\r\n /**\r\n * Gets the global volume sets on the master gain.\r\n * @returns the global volume if set or -1 otherwise\r\n */\r\n public getGlobalVolume(): number {\r\n return this.masterGain.gain.value;\r\n }\r\n\r\n /**\r\n * Sets the global volume of your experience (sets on the master gain).\r\n * @param newVolume Defines the new global volume of the application\r\n */\r\n public setGlobalVolume(newVolume: number): void {\r\n this.masterGain.gain.value = newVolume;\r\n }\r\n\r\n /**\r\n * Connect the audio engine to an audio analyser allowing some amazing\r\n * synchronization between the sounds/music and your visualization (VuMeter for instance).\r\n * @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic#using-the-analyser\r\n * @param analyser The analyser to connect to the engine\r\n */\r\n public connectToAnalyser(analyser: Analyser): void {\r\n if (this._connectedAnalyser) {\r\n this._connectedAnalyser.stopDebugCanvas();\r\n }\r\n\r\n this._connectedAnalyser = analyser;\r\n this.masterGain.disconnect();\r\n this._connectedAnalyser.connectAudioNodes(this.masterGain, this._v2._audioContext.destination);\r\n }\r\n\r\n private async _triggerRunningStateAsync() {\r\n if (this._tryToRun) {\r\n return;\r\n }\r\n this._tryToRun = true;\r\n\r\n await this._resumeAudioContextAsync();\r\n\r\n this._tryToRun = false;\r\n this.unlocked = true;\r\n\r\n this.onAudioUnlockedObservable.notifyObservers(this);\r\n }\r\n}\r\n"]}
@@ -50,9 +50,9 @@ export declare function CreateAudioEngineAsync(options?: Partial<IWebAudioEngine
50
50
  /** @internal */
51
51
  export declare class _WebAudioEngine extends AudioEngineV2 {
52
52
  private _audioContextStarted;
53
+ private _destinationNode;
53
54
  private _invalidFormats;
54
55
  private _isUpdating;
55
- private readonly _isUsingOfflineAudioContext;
56
56
  private _listener;
57
57
  private readonly _listenerAutoUpdate;
58
58
  private readonly _listenerMinUpdateTime;
@@ -71,6 +71,8 @@ export declare class _WebAudioEngine extends AudioEngineV2 {
71
71
  /** @internal */
72
72
  readonly _audioContext: AudioContext;
73
73
  /** @internal */
74
+ readonly _isUsingOfflineAudioContext: boolean;
75
+ /** @internal */
74
76
  readonly isReadyPromise: Promise<void>;
75
77
  /** @internal */
76
78
  stateChangedObservable: Observable<string>;
@@ -94,6 +96,18 @@ export declare class _WebAudioEngine extends AudioEngineV2 {
94
96
  get volume(): number;
95
97
  /** @internal */
96
98
  set volume(value: number);
99
+ /**
100
+ * This property should only be used by the legacy audio engine.
101
+ * @internal
102
+ * */
103
+ get _audioDestination(): AudioNode;
104
+ set _audioDestination(value: Nullable<AudioNode>);
105
+ /**
106
+ * This property should only be used by the legacy audio engine.
107
+ * @internal
108
+ */
109
+ get _unmuteUIEnabled(): boolean;
110
+ set _unmuteUIEnabled(value: boolean);
97
111
  /** @internal */
98
112
  createBusAsync(name: string, options?: Partial<IAudioBusOptions>): Promise<AudioBus>;
99
113
  /** @internal */
@@ -31,9 +31,9 @@ export class _WebAudioEngine extends AudioEngineV2 {
31
31
  constructor(options = {}) {
32
32
  super(options);
33
33
  this._audioContextStarted = false;
34
+ this._destinationNode = null;
34
35
  this._invalidFormats = new Set();
35
36
  this._isUpdating = false;
36
- this._isUsingOfflineAudioContext = false;
37
37
  this._listener = null;
38
38
  this._listenerAutoUpdate = true;
39
39
  this._listenerMinUpdateTime = 0;
@@ -49,6 +49,8 @@ export class _WebAudioEngine extends AudioEngineV2 {
49
49
  this._validFormats = new Set();
50
50
  this._volume = 1;
51
51
  /** @internal */
52
+ this._isUsingOfflineAudioContext = false;
53
+ /** @internal */
52
54
  this.isReadyPromise = new Promise((resolve) => {
53
55
  this._resolveIsReadyPromise = resolve;
54
56
  });
@@ -191,6 +193,28 @@ export class _WebAudioEngine extends AudioEngineV2 {
191
193
  this._mainOut.volume = value;
192
194
  }
193
195
  }
196
+ /**
197
+ * This property should only be used by the legacy audio engine.
198
+ * @internal
199
+ * */
200
+ get _audioDestination() {
201
+ return this._destinationNode ? this._destinationNode : (this._destinationNode = this._audioContext.destination);
202
+ }
203
+ set _audioDestination(value) {
204
+ this._destinationNode = value;
205
+ }
206
+ /**
207
+ * This property should only be used by the legacy audio engine.
208
+ * @internal
209
+ */
210
+ get _unmuteUIEnabled() {
211
+ return this._unmuteUI ? this._unmuteUI.enabled : false;
212
+ }
213
+ set _unmuteUIEnabled(value) {
214
+ if (this._unmuteUI) {
215
+ this._unmuteUI.enabled = value;
216
+ }
217
+ }
194
218
  /** @internal */
195
219
  async createBusAsync(name, options = {}) {
196
220
  const module = await import("./webAudioBus.js");