@bardway/trpg-media 0.0.0-stage → 0.1.0

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/audio.js ADDED
@@ -0,0 +1,625 @@
1
+ // src/AudioByteCache.ts
2
+ var AudioByteCache = class {
3
+ constructor(fetchAudio = fetch) {
4
+ this.fetchAudio = fetchAudio;
5
+ }
6
+ fetchAudio;
7
+ records = /* @__PURE__ */ new Map();
8
+ async load(url) {
9
+ let record = this.records.get(url);
10
+ if (!record) {
11
+ const controller = new AbortController();
12
+ const promise = this.fetchAudio(url, { signal: controller.signal }).then(async (response) => {
13
+ if (!response.ok) throw new Error(`Audio request failed: ${response.status}`);
14
+ const bytes = await response.arrayBuffer();
15
+ if (controller.signal.aborted) throw new DOMException("Audio load cancelled", "AbortError");
16
+ return bytes;
17
+ });
18
+ record = { controller, promise };
19
+ this.records.set(url, record);
20
+ const owner = record;
21
+ void promise.catch(() => {
22
+ if (this.records.get(url) === owner) this.records.delete(url);
23
+ });
24
+ }
25
+ return (await record.promise).slice(0);
26
+ }
27
+ clear() {
28
+ for (const record of this.records.values()) record.controller.abort();
29
+ this.records.clear();
30
+ }
31
+ };
32
+
33
+ // src/audio/SessionAudioEngine.ts
34
+ var SessionAudioEngine = class {
35
+ constructor(createContext = () => new AudioContext(), fetchAudio = fetch, busIds = [
36
+ "ui",
37
+ "action",
38
+ "impact",
39
+ "status",
40
+ "dice",
41
+ "field",
42
+ "cinematic"
43
+ ], limiterBuses = ["dice"], report = () => void 0) {
44
+ this.createContext = createContext;
45
+ this.busIds = busIds;
46
+ this.limiterBuses = limiterBuses;
47
+ this.report = report;
48
+ if (busIds.length === 0 || new Set(busIds).size !== busIds.length || busIds.some((bus) => !bus.trim()))
49
+ throw new Error("Audio bus IDs must be nonempty and unique");
50
+ this.bytes = new AudioByteCache(fetchAudio.bind(globalThis));
51
+ }
52
+ createContext;
53
+ busIds;
54
+ limiterBuses;
55
+ report;
56
+ bytes;
57
+ context = null;
58
+ master = null;
59
+ buses = /* @__PURE__ */ new Map();
60
+ busGains = /* @__PURE__ */ new Map();
61
+ buffers = /* @__PURE__ */ new Map();
62
+ activeVoices = /* @__PURE__ */ new Map();
63
+ playbackCleanups = /* @__PURE__ */ new Map();
64
+ lastCueTimes = /* @__PURE__ */ new Map();
65
+ busMute = /* @__PURE__ */ new Set();
66
+ soloBus = null;
67
+ masterGain = 1;
68
+ fieldSource = null;
69
+ fieldUrl = null;
70
+ fieldRevision = 0;
71
+ unlockGeneration = 0;
72
+ destroying = false;
73
+ _state = "blocked";
74
+ get state() {
75
+ return this._state;
76
+ }
77
+ async unlock(preloadUrls = []) {
78
+ if (this.destroying) return false;
79
+ if (this._state === "ready") return true;
80
+ const generation = ++this.unlockGeneration;
81
+ this._state = "initializing";
82
+ try {
83
+ this.ensureGraph();
84
+ await this.context.resume();
85
+ if (generation !== this.unlockGeneration) return false;
86
+ this._state = "ready";
87
+ void this.preload(preloadUrls);
88
+ return true;
89
+ } catch (error) {
90
+ if (generation !== this.unlockGeneration) return false;
91
+ this._state = error instanceof DOMException && (error.name === "NotAllowedError" || error.name === "AbortError") ? "blocked" : "failed";
92
+ return false;
93
+ }
94
+ }
95
+ async preload(urls) {
96
+ await Promise.allSettled(urls.map((url) => this.load(url)));
97
+ }
98
+ async play(url, options) {
99
+ if (this._state !== "ready" || options.signal?.aborted) return null;
100
+ const context = this.context;
101
+ const generation = this.unlockGeneration;
102
+ let cleanup;
103
+ try {
104
+ const buffer = await this.load(url);
105
+ if (this._state !== "ready" || !this.context || this.context !== context || generation !== this.unlockGeneration || options.signal?.aborted)
106
+ return null;
107
+ const source = this.context.createBufferSource();
108
+ const voiceGain = this.context.createGain();
109
+ const panner = "createStereoPanner" in this.context && typeof this.context.createStereoPanner === "function" ? this.context.createStereoPanner() : null;
110
+ const now = this.context.currentTime;
111
+ const volumeCeiling = Math.max(1, Math.min(4, options.volumeCeiling ?? 1));
112
+ const volume = Math.max(0, Math.min(volumeCeiling, options.volume ?? 1));
113
+ const fadeInSeconds = Math.max(0, options.fadeInMs ?? 0) / 1e3;
114
+ voiceGain.gain.setValueAtTime(fadeInSeconds > 0 ? 0 : volume, now);
115
+ if (fadeInSeconds > 0) voiceGain.gain.linearRampToValueAtTime(volume, now + fadeInSeconds);
116
+ source.buffer = buffer;
117
+ source.loop = options.loop ?? false;
118
+ const playbackRate = source.playbackRate;
119
+ if (playbackRate) playbackRate.value = Math.max(0.01, options.playbackRate ?? 1);
120
+ source.connect(voiceGain);
121
+ if (panner) {
122
+ panner.pan.value = Math.max(-1, Math.min(1, options.pan ?? 0));
123
+ voiceGain.connect(panner);
124
+ panner.connect(this.buses.get(options.bus));
125
+ } else voiceGain.connect(this.buses.get(options.bus));
126
+ let finished = false;
127
+ const finish = () => {
128
+ if (finished) return;
129
+ finished = true;
130
+ options.signal?.removeEventListener("abort", abort);
131
+ this.playbackCleanups.delete(source);
132
+ source.disconnect();
133
+ voiceGain.disconnect();
134
+ panner?.disconnect();
135
+ try {
136
+ options.onEnded?.();
137
+ } catch (error) {
138
+ this.reportSafely(error);
139
+ }
140
+ };
141
+ const abort = () => {
142
+ try {
143
+ source.stop();
144
+ } finally {
145
+ finish();
146
+ }
147
+ };
148
+ cleanup = finish;
149
+ source.onended = finish;
150
+ this.playbackCleanups.set(source, finish);
151
+ options.signal?.addEventListener("abort", abort, { once: true });
152
+ if (options.signal?.aborted) {
153
+ finish();
154
+ return null;
155
+ }
156
+ source.start(0, Math.max(0, options.startOffsetMs ?? 0) / 1e3);
157
+ return source;
158
+ } catch {
159
+ cleanup?.();
160
+ if (options.signal?.aborted || this.context !== context || generation !== this.unlockGeneration)
161
+ return null;
162
+ this._state = "failed";
163
+ return null;
164
+ }
165
+ }
166
+ async playCue(cue, optionsOrSeed = {}, legacyOnEnded) {
167
+ const options = typeof optionsOrSeed === "string" ? { deterministicSeed: optionsOrSeed, onEnded: legacyOnEnded } : optionsOrSeed;
168
+ const deterministicSeed = options.deterministicSeed ?? cue.id;
169
+ const onEnded = options.onEnded;
170
+ if (this._state !== "ready" || !this.context || options.signal?.aborted) return null;
171
+ const nowMs = this.context.currentTime * 1e3;
172
+ const cooldownId = options.cooldownId ?? cue.id;
173
+ const voiceGroupId = options.voiceGroupId ?? cue.id;
174
+ const lastTime = this.lastCueTimes.get(cooldownId);
175
+ if (lastTime !== void 0 && nowMs - lastTime < Math.max(0, options.cooldownMs ?? cue.cooldownMs))
176
+ return null;
177
+ const voices = this.activeVoices.get(voiceGroupId) ?? [];
178
+ if (voices.length >= Math.max(1, options.maxVoices ?? cue.polyphony)) {
179
+ if (cue.stealMode === "ignoreNewest") return null;
180
+ voices.shift()?.stop();
181
+ }
182
+ const random = deterministicUnit(deterministicSeed);
183
+ const rate = options.playbackRate ?? interpolate(cue.pitchMin ?? 1, cue.pitchMax ?? cue.pitchMin ?? 1, random);
184
+ let source = null;
185
+ source = await this.play(cue.asset, {
186
+ signal: options.signal,
187
+ bus: cue.bus,
188
+ loop: cue.loop,
189
+ volume: options.volume ?? cue.gain,
190
+ volumeCeiling: options.volumeCeiling,
191
+ playbackRate: rate,
192
+ pan: options.pan ?? interpolate(cue.panMin ?? 0, cue.panMax ?? cue.panMin ?? 0, random),
193
+ startOffsetMs: cue.startOffsetMs,
194
+ fadeInMs: cue.fadeInMs,
195
+ fadeOutMs: cue.fadeOutMs,
196
+ onEnded: () => {
197
+ this.releaseVoice(voiceGroupId, source);
198
+ onEnded?.();
199
+ }
200
+ });
201
+ if (!source || options.signal?.aborted) return null;
202
+ this.lastCueTimes.set(cooldownId, nowMs);
203
+ this.activeVoices.set(voiceGroupId, [...voices, source]);
204
+ return source;
205
+ }
206
+ stopCueGroup(groupId) {
207
+ for (const source of this.activeVoices.get(groupId) ?? []) source.stop();
208
+ this.activeVoices.delete(groupId);
209
+ }
210
+ async playField(url, volume = 1) {
211
+ await this.replaceField(url, volume);
212
+ }
213
+ async replaceField(url, volume = 1) {
214
+ const revision = ++this.fieldRevision;
215
+ this.stopFieldSource();
216
+ if (!url) return;
217
+ const source = await this.play(url, { bus: "field", loop: true, volume });
218
+ if (revision !== this.fieldRevision) {
219
+ source?.stop();
220
+ return;
221
+ }
222
+ this.fieldUrl = url;
223
+ this.fieldSource = source;
224
+ }
225
+ suspendField() {
226
+ this.buses.get("field")?.gain.setValueAtTime(0, this.context?.currentTime ?? 0);
227
+ }
228
+ resumeField() {
229
+ if (this.fieldUrl)
230
+ this.buses.get("field")?.gain.setValueAtTime(1, this.context?.currentTime ?? 0);
231
+ }
232
+ stopField() {
233
+ this.fieldRevision += 1;
234
+ this.stopFieldSource();
235
+ }
236
+ stopFieldSource() {
237
+ const source = this.fieldSource;
238
+ this.fieldSource = null;
239
+ this.fieldUrl = null;
240
+ source?.stop();
241
+ }
242
+ setMasterGain(value) {
243
+ this.masterGain = Math.max(0, Math.min(1, value));
244
+ if (this.master) this.master.gain.value = this.masterGain;
245
+ }
246
+ setBusGain(bus, value, rampMs = 0) {
247
+ const node = this.buses.get(bus);
248
+ if (!node) return;
249
+ this.busGains.set(bus, Math.max(0, Math.min(1, value)));
250
+ this.applyBusAudibility(rampMs);
251
+ }
252
+ setBusMuted(bus, muted) {
253
+ if (muted) this.busMute.add(bus);
254
+ else this.busMute.delete(bus);
255
+ this.applyBusAudibility();
256
+ }
257
+ solo(bus) {
258
+ this.soloBus = bus;
259
+ this.applyBusAudibility();
260
+ }
261
+ async destroy() {
262
+ this.destroying = true;
263
+ this.unlockGeneration += 1;
264
+ this._state = "blocked";
265
+ try {
266
+ this.stopField();
267
+ } catch (error) {
268
+ this.reportSafely(error);
269
+ }
270
+ const cleanups = [...this.playbackCleanups];
271
+ this.playbackCleanups.clear();
272
+ this.buffers.clear();
273
+ this.bytes.clear();
274
+ this.activeVoices.clear();
275
+ this.lastCueTimes.clear();
276
+ this.buses.clear();
277
+ this.busGains.clear();
278
+ const context = this.context;
279
+ this.context = null;
280
+ this.master = null;
281
+ try {
282
+ for (const [source, finish] of cleanups) {
283
+ try {
284
+ source.stop();
285
+ } catch (error) {
286
+ this.reportSafely(error);
287
+ }
288
+ try {
289
+ finish();
290
+ } catch (error) {
291
+ this.reportSafely(error);
292
+ }
293
+ }
294
+ await context?.close();
295
+ } finally {
296
+ this.destroying = false;
297
+ }
298
+ }
299
+ reportSafely(error) {
300
+ try {
301
+ this.report(error);
302
+ } catch {
303
+ }
304
+ }
305
+ ensureGraph() {
306
+ if (this.context) return;
307
+ this.context = this.createContext();
308
+ this.master = this.context.createGain();
309
+ this.master.gain.value = this.masterGain;
310
+ this.master.connect(this.context.destination);
311
+ for (const bus of this.busIds) {
312
+ const gain = this.context.createGain();
313
+ if (this.limiterBuses.includes(bus) && typeof this.context.createDynamicsCompressor === "function") {
314
+ const limiter = this.context.createDynamicsCompressor();
315
+ limiter.threshold.value = -1;
316
+ limiter.knee.value = 0;
317
+ limiter.ratio.value = 20;
318
+ limiter.attack.value = 1e-3;
319
+ limiter.release.value = 0.08;
320
+ gain.connect(limiter);
321
+ limiter.connect(this.master);
322
+ } else gain.connect(this.master);
323
+ this.buses.set(bus, gain);
324
+ this.busGains.set(bus, 1);
325
+ }
326
+ }
327
+ load(url) {
328
+ const existing = this.buffers.get(url);
329
+ if (existing) return existing;
330
+ const context = this.context;
331
+ const generation = this.unlockGeneration;
332
+ if (!context) return Promise.reject(new Error("Audio context is not initialized"));
333
+ const loading = this.bytes.load(url).then((data) => {
334
+ if (context !== this.context || generation !== this.unlockGeneration)
335
+ throw new DOMException("Audio context replaced", "AbortError");
336
+ return context.decodeAudioData(data);
337
+ }).catch((error) => {
338
+ if (this.buffers.get(url) === loading) this.buffers.delete(url);
339
+ throw error;
340
+ });
341
+ this.buffers.set(url, loading);
342
+ return loading;
343
+ }
344
+ releaseVoice(cueId, source) {
345
+ if (!source) return;
346
+ const voices = this.activeVoices.get(cueId);
347
+ if (!voices) return;
348
+ const next = voices.filter((voice) => voice !== source);
349
+ if (next.length === 0) this.activeVoices.delete(cueId);
350
+ else this.activeVoices.set(cueId, next);
351
+ }
352
+ applyBusAudibility(rampMs = 0) {
353
+ for (const [bus, node] of this.buses) {
354
+ const audible = !this.busMute.has(bus) && (!this.soloBus || this.soloBus === bus);
355
+ const value = audible ? this.busGains.get(bus) ?? 1 : 0;
356
+ const now = this.context?.currentTime ?? 0;
357
+ if (rampMs > 0) {
358
+ if (typeof node.gain.cancelAndHoldAtTime === "function") node.gain.cancelAndHoldAtTime(now);
359
+ else {
360
+ node.gain.cancelScheduledValues(now);
361
+ node.gain.setValueAtTime(node.gain.value, now);
362
+ }
363
+ node.gain.linearRampToValueAtTime(value, now + rampMs / 1e3);
364
+ } else {
365
+ node.gain.cancelScheduledValues(now);
366
+ node.gain.setValueAtTime(value, now);
367
+ }
368
+ }
369
+ }
370
+ };
371
+ var interpolate = (min, max, amount) => min + (max - min) * amount;
372
+ var deterministicUnit = (value) => {
373
+ let hash = 2166136261;
374
+ for (let index = 0; index < value.length; index += 1) {
375
+ hash ^= value.charCodeAt(index);
376
+ hash = Math.imul(hash, 16777619);
377
+ }
378
+ return (hash >>> 0) / 4294967295;
379
+ };
380
+
381
+ // src/audio/HtmlAudioSessionBackend.ts
382
+ var classifyFailure = (error) => error instanceof DOMException && (error.name === "NotAllowedError" || error.name === "AbortError") ? "blocked" : "failed";
383
+ var HtmlAudioSessionBackend = class {
384
+ constructor(report = () => void 0) {
385
+ this.report = report;
386
+ }
387
+ report;
388
+ elements = /* @__PURE__ */ new Map();
389
+ activeCueGroups = /* @__PURE__ */ new Map();
390
+ playbackOwners = /* @__PURE__ */ new WeakMap();
391
+ playbackCleanups = /* @__PURE__ */ new Map();
392
+ fieldElement = null;
393
+ masterGain = 1;
394
+ busGains = /* @__PURE__ */ new Map();
395
+ unlockGeneration = 0;
396
+ destroying = false;
397
+ _state = "blocked";
398
+ get state() {
399
+ return this._state;
400
+ }
401
+ async unlock(preloadUrls = []) {
402
+ if (this.destroying) return false;
403
+ if (this._state === "ready") return true;
404
+ const generation = ++this.unlockGeneration;
405
+ this._state = "initializing";
406
+ try {
407
+ const firstUrl = preloadUrls.at(0);
408
+ if (firstUrl) {
409
+ const firstAudio = this.getElement(firstUrl);
410
+ firstAudio.pause();
411
+ firstAudio.currentTime = 0;
412
+ await firstAudio.play();
413
+ if (generation !== this.unlockGeneration) return false;
414
+ firstAudio.pause();
415
+ firstAudio.currentTime = 0;
416
+ }
417
+ for (const url of preloadUrls.slice(1)) this.getElement(url).load();
418
+ this._state = "ready";
419
+ return true;
420
+ } catch (error) {
421
+ if (generation !== this.unlockGeneration) return false;
422
+ this._state = classifyFailure(error);
423
+ return false;
424
+ }
425
+ }
426
+ async preload(urls) {
427
+ urls.forEach((url) => this.getElement(url).load());
428
+ }
429
+ async play(url, options) {
430
+ if (this._state !== "ready" || options.signal?.aborted) return null;
431
+ const generation = this.unlockGeneration;
432
+ const audio = this.getElement(url);
433
+ this.playbackCleanups.get(audio)?.();
434
+ const owner = Symbol(url);
435
+ this.playbackOwners.set(audio, owner);
436
+ audio.pause();
437
+ audio.currentTime = 0;
438
+ audio.loop = options.loop ?? false;
439
+ audio.playbackRate = Math.max(0.01, options.playbackRate ?? 1);
440
+ audio.volume = Math.max(0, Math.min(1, options.volume ?? 1)) * this.masterGain * (this.busGains.get(options.bus) ?? 1);
441
+ let finished = false;
442
+ const finish = () => {
443
+ if (finished) return;
444
+ finished = true;
445
+ options.signal?.removeEventListener("abort", abort);
446
+ if (this.playbackOwners.get(audio) === owner) this.playbackCleanups.delete(audio);
447
+ try {
448
+ options.onEnded?.();
449
+ } catch (error) {
450
+ this.reportSafely(error);
451
+ }
452
+ };
453
+ const abort = () => {
454
+ if (this.playbackOwners.get(audio) === owner) {
455
+ audio.pause();
456
+ audio.currentTime = 0;
457
+ }
458
+ finish();
459
+ };
460
+ audio.onended = finish;
461
+ this.playbackCleanups.set(audio, finish);
462
+ options.signal?.addEventListener("abort", abort, { once: true });
463
+ try {
464
+ await audio.play();
465
+ if (this.playbackOwners.get(audio) !== owner || generation !== this.unlockGeneration || this._state !== "ready") {
466
+ finish();
467
+ return null;
468
+ }
469
+ if (options.signal?.aborted) {
470
+ abort();
471
+ return null;
472
+ }
473
+ return audio;
474
+ } catch (error) {
475
+ if (this.playbackOwners.get(audio) !== owner || generation !== this.unlockGeneration || this._state !== "ready" || options.signal?.aborted) {
476
+ finish();
477
+ return null;
478
+ }
479
+ if (error instanceof DOMException && error.name === "AbortError") return audio;
480
+ this._state = classifyFailure(error);
481
+ finish();
482
+ return null;
483
+ }
484
+ }
485
+ async playCue(cue, options = {}) {
486
+ const groupId = options.voiceGroupId ?? cue.id;
487
+ let audio = null;
488
+ audio = await this.play(cue.asset, {
489
+ signal: options.signal,
490
+ bus: cue.bus,
491
+ loop: cue.loop,
492
+ volume: options.volume ?? cue.gain,
493
+ playbackRate: options.playbackRate,
494
+ pan: options.pan,
495
+ startOffsetMs: cue.startOffsetMs,
496
+ fadeInMs: cue.fadeInMs,
497
+ fadeOutMs: cue.fadeOutMs,
498
+ onEnded: () => {
499
+ if (audio) this.releaseCueGroup(groupId, audio);
500
+ options.onEnded?.();
501
+ }
502
+ });
503
+ if (audio && !options.signal?.aborted) {
504
+ const active = this.activeCueGroups.get(groupId) ?? /* @__PURE__ */ new Set();
505
+ active.add(audio);
506
+ this.activeCueGroups.set(groupId, active);
507
+ }
508
+ return audio;
509
+ }
510
+ stopCueGroup(groupId) {
511
+ for (const audio of this.activeCueGroups.get(groupId) ?? []) {
512
+ audio.pause();
513
+ audio.currentTime = 0;
514
+ this.playbackCleanups.get(audio)?.();
515
+ }
516
+ this.activeCueGroups.delete(groupId);
517
+ }
518
+ async playField(url, volume = 1) {
519
+ await this.replaceField(url, volume);
520
+ }
521
+ async replaceField(url, volume = 1) {
522
+ this.stopField();
523
+ if (!url) return;
524
+ const audio = this.getElement(url);
525
+ audio.loop = true;
526
+ this.fieldElement = audio;
527
+ await this.play(url, { bus: "field", loop: true, volume });
528
+ }
529
+ suspendField() {
530
+ this.fieldElement?.pause();
531
+ }
532
+ resumeField() {
533
+ if (this.fieldElement && this._state === "ready") void this.fieldElement.play();
534
+ }
535
+ stopField() {
536
+ const audio = this.fieldElement;
537
+ if (!audio) return;
538
+ audio.pause();
539
+ audio.currentTime = 0;
540
+ this.fieldElement = null;
541
+ }
542
+ setMasterGain(value) {
543
+ this.masterGain = Math.max(0, Math.min(1, value));
544
+ }
545
+ setBusGain(bus, value) {
546
+ this.busGains.set(bus, Math.max(0, Math.min(1, value)));
547
+ }
548
+ setBusMuted(bus, muted) {
549
+ void bus;
550
+ void muted;
551
+ }
552
+ solo(bus) {
553
+ void bus;
554
+ }
555
+ async destroy() {
556
+ this.destroying = true;
557
+ this.unlockGeneration += 1;
558
+ this._state = "blocked";
559
+ const elements = [...this.elements.values()];
560
+ const cleanups = [...this.playbackCleanups.values()];
561
+ this.playbackCleanups.clear();
562
+ this.elements.clear();
563
+ this.activeCueGroups.clear();
564
+ this.fieldElement = null;
565
+ try {
566
+ for (const audio of elements) {
567
+ try {
568
+ audio.pause();
569
+ } catch (error) {
570
+ this.reportSafely(error);
571
+ }
572
+ }
573
+ for (const cleanup of cleanups) {
574
+ try {
575
+ cleanup();
576
+ } catch (error) {
577
+ this.reportSafely(error);
578
+ }
579
+ }
580
+ } finally {
581
+ this.destroying = false;
582
+ }
583
+ }
584
+ reportSafely(error) {
585
+ try {
586
+ this.report(error);
587
+ } catch {
588
+ }
589
+ }
590
+ getElement(url) {
591
+ const existing = this.elements.get(url);
592
+ if (existing) return existing;
593
+ const audio = new Audio(url);
594
+ audio.preload = "auto";
595
+ this.elements.set(url, audio);
596
+ return audio;
597
+ }
598
+ releaseCueGroup(groupId, audio) {
599
+ const active = this.activeCueGroups.get(groupId);
600
+ if (!active) return;
601
+ active.delete(audio);
602
+ if (active.size === 0) this.activeCueGroups.delete(groupId);
603
+ }
604
+ };
605
+
606
+ // src/audio/SessionAudioPlaybackPolicy.ts
607
+ var productionAudioPlaybackPolicy = {
608
+ mode: "production",
609
+ allowSemanticSideEffects: true,
610
+ allowScrubAudio: false
611
+ };
612
+ var previewAudioPlaybackPolicy = {
613
+ mode: "preview",
614
+ allowSemanticSideEffects: true,
615
+ allowScrubAudio: false,
616
+ deterministicSeed: "visual-lab"
617
+ };
618
+ export {
619
+ AudioByteCache,
620
+ HtmlAudioSessionBackend,
621
+ SessionAudioEngine,
622
+ previewAudioPlaybackPolicy,
623
+ productionAudioPlaybackPolicy
624
+ };
625
+ //# sourceMappingURL=audio.js.map