@wvdsh/sdk-js 1.3.20 → 1.3.21

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.
package/dist/index.d.ts CHANGED
@@ -43,17 +43,24 @@ declare abstract class WavedashManager {
43
43
  * e.g. a PIXI/GDevelop intro video), force-muting it before playback begins
44
44
  * regardless of how it was created — the one path the DOM-based sources and
45
45
  * the `muted` setter all miss.
46
+ *
47
+ * Speech synthesis (`window.speechSynthesis`): bypasses both Web Audio and HTML
48
+ * media entirely, so it gets its own shim — `speak()` forces the utterance's
49
+ * native volume to 0 while muted.
46
50
  */
47
51
  declare class AudioManager extends WavedashManager {
48
52
  private _isMuted;
49
53
  private contexts;
50
54
  private elements;
51
55
  private intendedMuted;
56
+ private intendedUtteranceVolume;
52
57
  private originalAudioContext;
53
58
  private originalWebKitAudioContext;
54
59
  private originalAudio;
55
60
  private originalMutedDescriptor;
56
61
  private originalPlay;
62
+ private originalSpeak;
63
+ private originalUtteranceVolumeDescriptor;
57
64
  private mutationObserver;
58
65
  constructor(sdk: WavedashSDK);
59
66
  isMuted(): boolean;
@@ -78,6 +85,18 @@ declare class AudioManager extends WavedashManager {
78
85
  */
79
86
  private trackElement;
80
87
  private installShims;
88
+ /**
89
+ * Shim `window.speechSynthesis` so speech respects the SDK mute state.
90
+ *
91
+ * Never swallows speak(): utterances have a lifecycle the game may sequence
92
+ * off (onstart/onend, synth.speaking/pending checks), so every call is
93
+ * delegated and silenced via volume instead. Volume is sampled at speak()
94
+ * time, so forcing the native value to 0 right before delegating silences
95
+ * anything spoken while muted; in-flight speech at the mute edge is
96
+ * deliberately left to finish (can't be softened mid-utterance, and
97
+ * cancel() would discard the pending queue).
98
+ */
99
+ private shimSpeechSynthesis;
81
100
  private shimAudioContextClass;
82
101
  destroy(): void;
83
102
  }
package/dist/index.js CHANGED
@@ -124,12 +124,16 @@ var AudioManager = class extends WavedashManager {
124
124
  // HTML media elements we know about + their game-intended muted state
125
125
  this.elements = new WeakRefSet();
126
126
  this.intendedMuted = /* @__PURE__ */ new WeakMap();
127
+ // Speech synthesis utterances + their game-intended volume
128
+ this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
127
129
  // Originals (restored on destroy)
128
130
  this.originalAudioContext = null;
129
131
  this.originalWebKitAudioContext = null;
130
132
  this.originalAudio = null;
131
133
  this.originalMutedDescriptor = null;
132
134
  this.originalPlay = null;
135
+ this.originalSpeak = null;
136
+ this.originalUtteranceVolumeDescriptor = null;
133
137
  this.mutationObserver = null;
134
138
  this.handleMute = (data) => {
135
139
  if (this._isMuted === data.isMuted) return;
@@ -275,6 +279,66 @@ var AudioManager = class extends WavedashManager {
275
279
  return originalPlay.call(this);
276
280
  };
277
281
  })(this);
282
+ this.shimSpeechSynthesis();
283
+ }
284
+ /**
285
+ * Shim `window.speechSynthesis` so speech respects the SDK mute state.
286
+ *
287
+ * Never swallows speak(): utterances have a lifecycle the game may sequence
288
+ * off (onstart/onend, synth.speaking/pending checks), so every call is
289
+ * delegated and silenced via volume instead. Volume is sampled at speak()
290
+ * time, so forcing the native value to 0 right before delegating silences
291
+ * anything spoken while muted; in-flight speech at the mute edge is
292
+ * deliberately left to finish (can't be softened mid-utterance, and
293
+ * cancel() would discard the pending queue).
294
+ */
295
+ shimSpeechSynthesis() {
296
+ if (!window.speechSynthesis || typeof SpeechSynthesisUtterance === "undefined") {
297
+ return;
298
+ }
299
+ this.originalUtteranceVolumeDescriptor = Object.getOwnPropertyDescriptor(
300
+ SpeechSynthesisUtterance.prototype,
301
+ "volume"
302
+ ) ?? null;
303
+ const volDesc = this.originalUtteranceVolumeDescriptor;
304
+ if (volDesc?.get && volDesc?.set) {
305
+ ((manager) => {
306
+ Object.defineProperty(SpeechSynthesisUtterance.prototype, "volume", {
307
+ configurable: true,
308
+ get() {
309
+ const intended = manager.intendedUtteranceVolume.get(this);
310
+ return intended !== void 0 ? intended : volDesc.get.call(this);
311
+ },
312
+ set(value) {
313
+ manager.intendedUtteranceVolume.set(this, value);
314
+ volDesc.set.call(this, value);
315
+ }
316
+ });
317
+ })(this);
318
+ }
319
+ const speechSynthesis = window.speechSynthesis;
320
+ const originalSpeak = speechSynthesis.speak;
321
+ this.originalSpeak = originalSpeak;
322
+ ((manager) => {
323
+ speechSynthesis.speak = function(utterance) {
324
+ if (manager._isMuted) {
325
+ if (!manager.intendedUtteranceVolume.has(utterance)) {
326
+ const current = volDesc?.get ? volDesc.get.call(utterance) : utterance.volume;
327
+ manager.intendedUtteranceVolume.set(utterance, current);
328
+ }
329
+ if (volDesc?.set) volDesc.set.call(utterance, 0);
330
+ else utterance.volume = 0;
331
+ } else {
332
+ const intended = manager.intendedUtteranceVolume.get(utterance);
333
+ if (intended !== void 0) {
334
+ if (volDesc?.set) volDesc.set.call(utterance, intended);
335
+ else utterance.volume = intended;
336
+ manager.intendedUtteranceVolume.delete(utterance);
337
+ }
338
+ }
339
+ return originalSpeak.call(speechSynthesis, utterance);
340
+ };
341
+ })(this);
278
342
  }
279
343
  shimAudioContextClass(Original) {
280
344
  return /* @__PURE__ */ ((manager) => class extends Original {
@@ -320,6 +384,16 @@ var AudioManager = class extends WavedashManager {
320
384
  if (this.originalAudio) {
321
385
  window.Audio = this.originalAudio;
322
386
  }
387
+ if (this.originalSpeak && window.speechSynthesis) {
388
+ window.speechSynthesis.speak = this.originalSpeak;
389
+ }
390
+ }
391
+ if (this.originalUtteranceVolumeDescriptor && typeof SpeechSynthesisUtterance !== "undefined") {
392
+ Object.defineProperty(
393
+ SpeechSynthesisUtterance.prototype,
394
+ "volume",
395
+ this.originalUtteranceVolumeDescriptor
396
+ );
323
397
  }
324
398
  if (this.originalPlay) {
325
399
  HTMLMediaElement.prototype.play = this.originalPlay;
@@ -334,6 +408,7 @@ var AudioManager = class extends WavedashManager {
334
408
  this.contexts.clear();
335
409
  this.elements.clear();
336
410
  this.intendedMuted = /* @__PURE__ */ new WeakMap();
411
+ this.intendedUtteranceVolume = /* @__PURE__ */ new WeakMap();
337
412
  super.destroy();
338
413
  }
339
414
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wvdsh/sdk-js",
3
- "version": "1.3.20",
3
+ "version": "1.3.21",
4
4
  "type": "module",
5
5
  "description": "Wavedash JavaScript SDK",
6
6
  "main": "./dist/client.js",