@babylonjs/lite 1.4.0 → 1.5.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.
Files changed (122) hide show
  1. package/dist/index.js +381 -375
  2. package/dist/index.js.map +1 -1
  3. package/index.d.ts +757 -0
  4. package/lib/audio/analyzer.js +65 -0
  5. package/lib/audio/analyzer.js.map +1 -0
  6. package/lib/audio/audio-bus.js +38 -0
  7. package/lib/audio/audio-bus.js.map +1 -0
  8. package/lib/audio/audio-engine.js +188 -0
  9. package/lib/audio/audio-engine.js.map +1 -0
  10. package/lib/audio/audio-fetch.js +18 -0
  11. package/lib/audio/audio-fetch.js.map +1 -0
  12. package/lib/audio/audio-param.js +96 -0
  13. package/lib/audio/audio-param.js.map +1 -0
  14. package/lib/audio/audio-signal.js +46 -0
  15. package/lib/audio/audio-signal.js.map +1 -0
  16. package/lib/audio/bus.js +33 -0
  17. package/lib/audio/bus.js.map +1 -0
  18. package/lib/audio/host-types.js +2 -0
  19. package/lib/audio/host-types.js.map +1 -0
  20. package/lib/audio/index.js +12 -0
  21. package/lib/audio/index.js.map +1 -0
  22. package/lib/audio/sound-buffer.js +59 -0
  23. package/lib/audio/sound-buffer.js.map +1 -0
  24. package/lib/audio/sound-source.js +57 -0
  25. package/lib/audio/sound-source.js.map +1 -0
  26. package/lib/audio/sound-sub-graph.js +72 -0
  27. package/lib/audio/sound-sub-graph.js.map +1 -0
  28. package/lib/audio/spatial.js +466 -0
  29. package/lib/audio/spatial.js.map +1 -0
  30. package/lib/audio/static-sound.js +313 -0
  31. package/lib/audio/static-sound.js.map +1 -0
  32. package/lib/audio/stereo.js +40 -0
  33. package/lib/audio/stereo.js.map +1 -0
  34. package/lib/audio/streaming-sound.js +377 -0
  35. package/lib/audio/streaming-sound.js.map +1 -0
  36. package/lib/audio/unmute-ui.js +72 -0
  37. package/lib/audio/unmute-ui.js.map +1 -0
  38. package/lib/audio/visualizer.js +101 -0
  39. package/lib/audio/visualizer.js.map +1 -0
  40. package/lib/engine/engine.js +1 -1
  41. package/lib/index.js +11 -0
  42. package/lib/index.js.map +1 -1
  43. package/lib/light/types.js.map +1 -1
  44. package/lib/loader-gltf/animation-pointer-basecolor.js +25 -0
  45. package/lib/loader-gltf/animation-pointer-basecolor.js.map +1 -0
  46. package/lib/loader-gltf/animation-pointer-ext.js +244 -0
  47. package/lib/loader-gltf/animation-pointer-ext.js.map +1 -0
  48. package/lib/loader-gltf/animation-pointer-lights.js +46 -0
  49. package/lib/loader-gltf/animation-pointer-lights.js.map +1 -0
  50. package/lib/loader-gltf/animation-pointer.js +4 -1
  51. package/lib/loader-gltf/animation-pointer.js.map +1 -1
  52. package/lib/loader-gltf/gltf-animation.js +5 -3
  53. package/lib/loader-gltf/gltf-animation.js.map +1 -1
  54. package/lib/loader-gltf/gltf-color-normalize.js +10 -1
  55. package/lib/loader-gltf/gltf-color-normalize.js.map +1 -1
  56. package/lib/loader-gltf/gltf-feature-animation-pointer.js +67 -47
  57. package/lib/loader-gltf/gltf-feature-animation-pointer.js.map +1 -1
  58. package/lib/loader-gltf/gltf-feature-lights-punctual.js +51 -9
  59. package/lib/loader-gltf/gltf-feature-lights-punctual.js.map +1 -1
  60. package/lib/loader-gltf/gltf-feature-primitive.js +20 -0
  61. package/lib/loader-gltf/gltf-feature-primitive.js.map +1 -0
  62. package/lib/loader-gltf/gltf-feature-registry.js +25 -0
  63. package/lib/loader-gltf/gltf-feature-registry.js.map +1 -1
  64. package/lib/loader-gltf/gltf-feature-skeleton.js +18 -3
  65. package/lib/loader-gltf/gltf-feature-skeleton.js.map +1 -1
  66. package/lib/loader-gltf/gltf-interleave.js +3 -2
  67. package/lib/loader-gltf/gltf-interleave.js.map +1 -1
  68. package/lib/loader-gltf/gltf-light-pointer-state.js +18 -0
  69. package/lib/loader-gltf/gltf-light-pointer-state.js.map +1 -0
  70. package/lib/loader-gltf/gltf-parser.js +7 -1
  71. package/lib/loader-gltf/gltf-parser.js.map +1 -1
  72. package/lib/loader-gltf/gltf-pbr-builder-ext.js +1 -1
  73. package/lib/loader-gltf/gltf-pbr-builder-ext.js.map +1 -1
  74. package/lib/loader-gltf/gltf-pbr-builder.js +1 -1
  75. package/lib/loader-gltf/gltf-pbr-builder.js.map +1 -1
  76. package/lib/loader-gltf/gltf-sampler-denorm.js +20 -0
  77. package/lib/loader-gltf/gltf-sampler-denorm.js.map +1 -0
  78. package/lib/loader-gltf/gltf-sampler-desc.js +11 -2
  79. package/lib/loader-gltf/gltf-sampler-desc.js.map +1 -1
  80. package/lib/loader-gltf/gltf-uv-denorm.js +28 -0
  81. package/lib/loader-gltf/gltf-uv-denorm.js.map +1 -0
  82. package/lib/loader-gltf/load-gltf.js +15 -6
  83. package/lib/loader-gltf/load-gltf.js.map +1 -1
  84. package/lib/material/material-rebuild.js +4 -0
  85. package/lib/material/material-rebuild.js.map +1 -1
  86. package/lib/material/mesh-features.js +8 -1
  87. package/lib/material/mesh-features.js.map +1 -1
  88. package/lib/material/pbr/fragments/reflectance-fragment.js +1 -1
  89. package/lib/material/pbr/fragments/reflectance-fragment.js.map +1 -1
  90. package/lib/material/pbr/fragments/refraction-rtt-fragment.js +1 -1
  91. package/lib/material/pbr/fragments/refraction-rtt-fragment.js.map +1 -1
  92. package/lib/material/pbr/pbr-pipeline.js +7 -3
  93. package/lib/material/pbr/pbr-pipeline.js.map +1 -1
  94. package/lib/material/pbr/pbr-primitive-resolver.js +34 -0
  95. package/lib/material/pbr/pbr-primitive-resolver.js.map +1 -0
  96. package/lib/material/pbr/pbr-renderable.js +1 -1
  97. package/lib/material/pbr/pbr-renderable.js.map +1 -1
  98. package/lib/material/shader/shader-material.js +9 -5
  99. package/lib/material/shader/shader-material.js.map +1 -1
  100. package/lib/material/shader/shader-thin-instance.js +1 -1
  101. package/lib/material/shader/shader-thin-instance.js.map +1 -1
  102. package/lib/material/standard/standard-renderable.js +1 -1
  103. package/lib/material/standard/standard-renderable.js.map +1 -1
  104. package/lib/mesh/mesh-dispose.js +1 -0
  105. package/lib/mesh/mesh-dispose.js.map +1 -1
  106. package/lib/mesh/thin-instance-cull-binding.js +15 -6
  107. package/lib/mesh/thin-instance-cull-binding.js.map +1 -1
  108. package/lib/scene/scene-core.js +1 -0
  109. package/lib/scene/scene-core.js.map +1 -1
  110. package/lib/scene/scene-material-swap.js +2 -0
  111. package/lib/scene/scene-material-swap.js.map +1 -1
  112. package/lib/shadow/csm-shadow-task-hooks.js +67 -9
  113. package/lib/shadow/csm-shadow-task-hooks.js.map +1 -1
  114. package/lib/sprite/sprite-2d.js +4 -0
  115. package/lib/sprite/sprite-2d.js.map +1 -1
  116. package/lib/sprite/sprite-pipeline.js +25 -22
  117. package/lib/sprite/sprite-pipeline.js.map +1 -1
  118. package/lib/text/_gpu/text-pipeline.js +1 -1
  119. package/lib/text/_gpu/text-pipeline.js.map +1 -1
  120. package/lib/text/text-renderer.js +3 -1
  121. package/lib/text/text-renderer.js.map +1 -1
  122. package/package.json +3 -3
@@ -0,0 +1,65 @@
1
+ const Defaults = {
2
+ fftSize: 2048,
3
+ minDecibels: -100,
4
+ maxDecibels: -30,
5
+ smoothing: 0.8
6
+ };
7
+ function applyAnalyzerOptions(node, options) {
8
+ if (options.fftSize !== void 0) {
9
+ node.fftSize = options.fftSize;
10
+ }
11
+ if (options.minDecibels !== void 0) {
12
+ node.minDecibels = options.minDecibels;
13
+ }
14
+ if (options.maxDecibels !== void 0) {
15
+ node.maxDecibels = options.maxDecibels;
16
+ }
17
+ if (options.smoothing !== void 0) {
18
+ node.smoothingTimeConstant = options.smoothing;
19
+ }
20
+ }
21
+ function createAnalyzerSubNode(engine, options) {
22
+ const node = new AnalyserNode(engine._ctx);
23
+ node.fftSize = Defaults.fftSize;
24
+ node.minDecibels = Defaults.minDecibels;
25
+ node.maxDecibels = Defaults.maxDecibels;
26
+ node.smoothingTimeConstant = Defaults.smoothing;
27
+ applyAnalyzerOptions(node, options);
28
+ return {
29
+ _engine: engine,
30
+ _node: node,
31
+ _dispose: () => {
32
+ node.disconnect();
33
+ }
34
+ };
35
+ }
36
+ function ensureAnalyzerSubNode(host, options = {}) {
37
+ const graph = host._graph;
38
+ if (graph._analyzer) {
39
+ const existing = graph._analyzer;
40
+ applyAnalyzerOptions(existing._node, options);
41
+ return existing;
42
+ }
43
+ const node = createAnalyzerSubNode(host._engine, options);
44
+ graph._volume.connect(node._node);
45
+ graph._analyzer = node;
46
+ return node;
47
+ }
48
+ function enableAnalyzer(host, options = {}) {
49
+ ensureAnalyzerSubNode(host, options);
50
+ }
51
+ function getByteFrequencyData(host, out) {
52
+ ensureAnalyzerSubNode(host)._node.getByteFrequencyData(out);
53
+ }
54
+ function getFloatFrequencyData(host, out) {
55
+ ensureAnalyzerSubNode(host)._node.getFloatFrequencyData(out);
56
+ }
57
+ function getByteTimeDomainData(host, out) {
58
+ ensureAnalyzerSubNode(host)._node.getByteTimeDomainData(out);
59
+ }
60
+ function getFloatTimeDomainData(host, out) {
61
+ ensureAnalyzerSubNode(host)._node.getFloatTimeDomainData(out);
62
+ }
63
+
64
+ export { enableAnalyzer, getByteFrequencyData, getByteTimeDomainData, getFloatFrequencyData, getFloatTimeDomainData };
65
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sources":["../../../src/audio/analyzer.ts"],"sourcesContent":["/**\n * Frequency / amplitude analysis — opt-in feature module.\n *\n * Faithful port of AudioV2 `_AudioAnalyzer` / `_WebAudioAnalyzer`, re-architected\n * to Lite idioms (pure state + standalone functions). Building the sub-node is\n * lazy, so importing nothing from here costs nothing.\n *\n * The analyzer wraps a Web Audio `AnalyserNode`. Mirroring AudioV2\n * `_WebAudioBaseSubGraph._onSubNodesChanged`, it is a passive *tap* off the\n * volume node (`_volume.connect(analyser)`); it is never part of the audible\n * through-chain, so it does not affect the sub-graph head.\n */\n\nimport { type AudioEngine } from \"./audio-engine.js\";\nimport { type AudioGraphHost, type AudioGraphHostState } from \"./host-types.js\";\n\n/** Default analyzer settings (match AudioV2 / Web Audio defaults). */\nconst Defaults = {\n fftSize: 2048,\n minDecibels: -100,\n maxDecibels: -30,\n smoothing: 0.8,\n} as const;\n\n/** Options for {@link enableAnalyzer}. */\nexport interface AudioAnalyzerOptions {\n /** FFT window size (a power of two, `32`–`32768`). Defaults to `2048`. */\n fftSize?: number;\n /** Minimum power value for the dB range, in dBFS. Defaults to `-100`. */\n minDecibels?: number;\n /** Maximum power value for the dB range, in dBFS. Defaults to `-30`. */\n maxDecibels?: number;\n /** Time-averaging constant in `[0, 1]`. Defaults to `0.8`. */\n smoothing?: number;\n}\n\n/** Analyzer tap sub-node state. Pure state — driven by the analyzer functions. @internal */\nexport interface AnalyzerSubNode {\n /** @internal */ _engine: AudioEngine;\n /** @internal */ _node: AnalyserNode;\n /** @internal */ _dispose: () => void;\n}\n\nfunction applyAnalyzerOptions(node: AnalyserNode, options: AudioAnalyzerOptions): void {\n if (options.fftSize !== undefined) {\n node.fftSize = options.fftSize;\n }\n if (options.minDecibels !== undefined) {\n node.minDecibels = options.minDecibels;\n }\n if (options.maxDecibels !== undefined) {\n node.maxDecibels = options.maxDecibels;\n }\n if (options.smoothing !== undefined) {\n node.smoothingTimeConstant = options.smoothing;\n }\n}\n\nfunction createAnalyzerSubNode(engine: AudioEngine, options: AudioAnalyzerOptions): AnalyzerSubNode {\n const node = new AnalyserNode(engine._ctx);\n node.fftSize = Defaults.fftSize;\n node.minDecibels = Defaults.minDecibels;\n node.maxDecibels = Defaults.maxDecibels;\n node.smoothingTimeConstant = Defaults.smoothing;\n applyAnalyzerOptions(node, options);\n return {\n _engine: engine,\n _node: node,\n _dispose: () => {\n node.disconnect();\n },\n };\n}\n\n/**\n * Lazily build the analyzer tap and attach it to the host's volume output.\n * Idempotent; re-applies any supplied options to an existing analyzer.\n */\nfunction ensureAnalyzerSubNode(host: AudioGraphHostState, options: AudioAnalyzerOptions = {}): AnalyzerSubNode {\n const graph = host._graph;\n if (graph._analyzer) {\n // The graph stores a structural slot; this feature module owns the full node.\n const existing = graph._analyzer as AnalyzerSubNode;\n applyAnalyzerOptions(existing._node, options);\n return existing;\n }\n\n const node = createAnalyzerSubNode(host._engine, options);\n graph._volume.connect(node._node);\n graph._analyzer = node;\n return node;\n}\n\n// ─── Public API ──────────────────────────────────────────────────────\n\n/**\n * Enables (or reconfigures) frequency/amplitude analysis on a sound or bus,\n * building the analyzer tap on first use. Pulls the analyzer module only when\n * called.\n * @param host - A `StaticSound`, `StreamingSound`, or `AudioBus`.\n * @param options - Analyzer options (fftSize, dB range, smoothing).\n */\nexport function enableAnalyzer(host: AudioGraphHost, options: AudioAnalyzerOptions = {}): void {\n ensureAnalyzerSubNode(host, options);\n}\n\n/**\n * Writes the current frequency-domain data as unsigned bytes into `out`,\n * building the analyzer tap on first use. `out` should be sized to the\n * analyzer's `frequencyBinCount` (`fftSize / 2`).\n * @param host - A `StaticSound`, `StreamingSound`, or `AudioBus`.\n * @param out - Destination buffer; values are written in place.\n */\nexport function getByteFrequencyData(host: AudioGraphHost, out: Uint8Array): void {\n ensureAnalyzerSubNode(host)._node.getByteFrequencyData(out as Uint8Array<ArrayBuffer>);\n}\n\n/**\n * Writes the current frequency-domain data as floats (in dBFS) into `out`,\n * building the analyzer tap on first use. `out` should be sized to the\n * analyzer's `frequencyBinCount` (`fftSize / 2`).\n * @param host - A `StaticSound`, `StreamingSound`, or `AudioBus`.\n * @param out - Destination buffer; values are written in place.\n */\nexport function getFloatFrequencyData(host: AudioGraphHost, out: Float32Array): void {\n ensureAnalyzerSubNode(host)._node.getFloatFrequencyData(out as Float32Array<ArrayBuffer>);\n}\n\n/**\n * Writes the current time-domain (waveform) data as unsigned bytes into `out`,\n * building the analyzer tap on first use. Each sample is centered on `128`.\n * `out` should be sized to the analyzer's `fftSize`.\n * @param host - A `StaticSound`, `StreamingSound`, or `AudioBus`.\n * @param out - Destination buffer; values are written in place.\n */\nexport function getByteTimeDomainData(host: AudioGraphHost, out: Uint8Array): void {\n ensureAnalyzerSubNode(host)._node.getByteTimeDomainData(out as Uint8Array<ArrayBuffer>);\n}\n\n/**\n * Writes the current time-domain (waveform) data as floats in `[-1, 1]` into\n * `out`, building the analyzer tap on first use. `out` should be sized to the\n * analyzer's `fftSize`.\n * @param host - A `StaticSound`, `StreamingSound`, or `AudioBus`.\n * @param out - Destination buffer; values are written in place.\n */\nexport function getFloatTimeDomainData(host: AudioGraphHost, out: Float32Array): void {\n ensureAnalyzerSubNode(host)._node.getFloatTimeDomainData(out as Float32Array<ArrayBuffer>);\n}\n"],"names":[],"mappings":"AAiBA,MAAM,QAAA,GAAW;AAAA,EACb,OAAA,EAAS,IAAA;AAAA,EACT,WAAA,EAAa,IAAA;AAAA,EACb,WAAA,EAAa,GAAA;AAAA,EACb,SAAA,EAAW;AACf,CAAA;AAqBA,SAAS,oBAAA,CAAqB,MAAoB,OAAA,EAAqC;AACnF,EAAA,IAAI,OAAA,CAAQ,YAAY,MAAA,EAAW;AAC/B,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,OAAA;AAAA,EAC3B;AACA,EAAA,IAAI,OAAA,CAAQ,gBAAgB,MAAA,EAAW;AACnC,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAAA,EAC/B;AACA,EAAA,IAAI,OAAA,CAAQ,gBAAgB,MAAA,EAAW;AACnC,IAAA,IAAA,CAAK,cAAc,OAAA,CAAQ,WAAA;AAAA,EAC/B;AACA,EAAA,IAAI,OAAA,CAAQ,cAAc,MAAA,EAAW;AACjC,IAAA,IAAA,CAAK,wBAAwB,OAAA,CAAQ,SAAA;AAAA,EACzC;AACJ;AAEA,SAAS,qBAAA,CAAsB,QAAqB,OAAA,EAAgD;AAChG,EAAA,MAAM,IAAA,GAAO,IAAI,YAAA,CAAa,MAAA,CAAO,IAAI,CAAA;AACzC,EAAA,IAAA,CAAK,UAAU,QAAA,CAAS,OAAA;AACxB,EAAA,IAAA,CAAK,cAAc,QAAA,CAAS,WAAA;AAC5B,EAAA,IAAA,CAAK,cAAc,QAAA,CAAS,WAAA;AAC5B,EAAA,IAAA,CAAK,wBAAwB,QAAA,CAAS,SAAA;AACtC,EAAA,oBAAA,CAAqB,MAAM,OAAO,CAAA;AAClC,EAAA,OAAO;AAAA,IACH,OAAA,EAAS,MAAA;AAAA,IACT,KAAA,EAAO,IAAA;AAAA,IACP,UAAU,MAAM;AACZ,MAAA,IAAA,CAAK,UAAA,EAAW;AAAA,IACpB;AAAA,GACJ;AACJ;AAMA,SAAS,qBAAA,CAAsB,IAAA,EAA2B,OAAA,GAAgC,EAAC,EAAoB;AAC3G,EAAA,MAAM,QAAQ,IAAA,CAAK,MAAA;AACnB,EAAA,IAAI,MAAM,SAAA,EAAW;AAEjB,IAAA,MAAM,WAAW,KAAA,CAAM,SAAA;AACvB,IAAA,oBAAA,CAAqB,QAAA,CAAS,OAAO,OAAO,CAAA;AAC5C,IAAA,OAAO,QAAA;AAAA,EACX;AAEA,EAAA,MAAM,IAAA,GAAO,qBAAA,CAAsB,IAAA,CAAK,OAAA,EAAS,OAAO,CAAA;AACxD,EAAA,KAAA,CAAM,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA;AAChC,EAAA,KAAA,CAAM,SAAA,GAAY,IAAA;AAClB,EAAA,OAAO,IAAA;AACX;AAWO,SAAS,cAAA,CAAe,IAAA,EAAsB,OAAA,GAAgC,EAAC,EAAS;AAC3F,EAAA,qBAAA,CAAsB,MAAM,OAAO,CAAA;AACvC;AASO,SAAS,oBAAA,CAAqB,MAAsB,GAAA,EAAuB;AAC9E,EAAA,qBAAA,CAAsB,IAAI,CAAA,CAAE,KAAA,CAAM,oBAAA,CAAqB,GAA8B,CAAA;AACzF;AASO,SAAS,qBAAA,CAAsB,MAAsB,GAAA,EAAyB;AACjF,EAAA,qBAAA,CAAsB,IAAI,CAAA,CAAE,KAAA,CAAM,qBAAA,CAAsB,GAAgC,CAAA;AAC5F;AASO,SAAS,qBAAA,CAAsB,MAAsB,GAAA,EAAuB;AAC/E,EAAA,qBAAA,CAAsB,IAAI,CAAA,CAAE,KAAA,CAAM,qBAAA,CAAsB,GAA8B,CAAA;AAC1F;AASO,SAAS,sBAAA,CAAuB,MAAsB,GAAA,EAAyB;AAClF,EAAA,qBAAA,CAAsB,IAAI,CAAA,CAAE,KAAA,CAAM,sBAAA,CAAuB,GAAgC,CAAA;AAC7F;;;;"}
@@ -0,0 +1,38 @@
1
+ import { setMainBusVolume } from './bus.js';
2
+ import { createSoundSubGraph, connectSoundSubGraph, disposeSoundSubGraph, setSoundSubGraphVolume } from './sound-sub-graph.js';
3
+
4
+ function _isAudioBus(bus) {
5
+ return "_graph" in bus;
6
+ }
7
+ function getBusInputNode(bus) {
8
+ return _isAudioBus(bus) ? bus._graph._in : bus._in;
9
+ }
10
+ async function createAudioBusAsync(engine, name, options = {}) {
11
+ await engine._isReady;
12
+ const graph = createSoundSubGraph(engine._ctx, engine, options.volume ?? 1);
13
+ const outBus = options.outBus ?? engine._mainBus;
14
+ connectSoundSubGraph(graph, getBusInputNode(outBus));
15
+ const bus = {
16
+ name,
17
+ _engine: engine,
18
+ _graph: graph,
19
+ _outBus: outBus,
20
+ _dispose: () => disposeAudioBus(bus)
21
+ };
22
+ engine._buses.add(bus);
23
+ return bus;
24
+ }
25
+ function setBusVolume(bus, value, options) {
26
+ if (_isAudioBus(bus)) {
27
+ setSoundSubGraphVolume(bus._graph, value, options);
28
+ } else {
29
+ setMainBusVolume(bus, value, options);
30
+ }
31
+ }
32
+ function disposeAudioBus(bus) {
33
+ disposeSoundSubGraph(bus._graph);
34
+ bus._engine._buses.delete(bus);
35
+ }
36
+
37
+ export { createAudioBusAsync, disposeAudioBus, getBusInputNode, setBusVolume };
38
+ //# sourceMappingURL=audio-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-bus.js","sources":["../../../src/audio/audio-bus.ts"],"sourcesContent":["/**\n * Generic audio bus (mixer node).\n *\n * Faithful port of AudioV2 `AudioBus` / `_WebAudioBus`, collapsed to pure state\n * + standalone functions. A bus owns a `SoundSubGraph` (Phase 2: a single\n * volume `GainNode`) and routes its output into another bus (`outBus`), forming\n * a mixer tree that ultimately reaches the engine main bus, main out, and the\n * context destination.\n *\n * Signal flow: `upstream` becomes `bus._graph._in` then `bus._graph._out` then\n * the `outBus` input node, and so on up to `mainOut` then `ctx.destination`.\n */\n\nimport { type AudioEngine } from \"./audio-engine.js\";\nimport { type RampOptions } from \"./audio-param.js\";\nimport { type MainBus, setMainBusVolume } from \"./bus.js\";\nimport { type SoundSubGraph, connectSoundSubGraph, createSoundSubGraph, disposeSoundSubGraph, setSoundSubGraphVolume } from \"./sound-sub-graph.js\";\n\n/** A bus that sounds or other buses can output to (the engine main bus or a generic {@link AudioBus}). */\nexport type PrimaryAudioBus = AudioBus | MainBus;\n\n/** Options for {@link createAudioBusAsync}. */\nexport interface AudioBusOptions {\n /** Initial volume. Defaults to `1`. */\n volume?: number;\n /** Output bus. Defaults to the engine's default main bus. */\n outBus?: PrimaryAudioBus;\n}\n\n/** A generic mixer bus. Pure state — drive it with the bus functions. */\nexport interface AudioBus {\n /** Bus name. */\n readonly name: string;\n\n /** @internal */ _engine: AudioEngine;\n /** @internal */ _graph: SoundSubGraph;\n /** @internal */ _outBus: PrimaryAudioBus;\n /** @internal */ _dispose(): void;\n}\n\n/** Narrows a {@link PrimaryAudioBus} to a generic {@link AudioBus}. */\nfunction _isAudioBus(bus: PrimaryAudioBus): bus is AudioBus {\n return \"_graph\" in bus;\n}\n\n/** Resolves the Web Audio input node where upstream sounds/buses connect. @internal */\nexport function getBusInputNode(bus: PrimaryAudioBus): AudioNode {\n return _isAudioBus(bus) ? bus._graph._in : bus._in;\n}\n\n/**\n * Creates a generic mixer bus routed to another bus (defaulting to the engine's\n * default main bus). Multiple sounds/buses can be routed through a bus to mix\n * and control their combined volume.\n * @param engine - The audio engine.\n * @param name - A name for the bus.\n * @param options - Bus options.\n * @returns A promise that resolves with the ready bus.\n */\nexport async function createAudioBusAsync(engine: AudioEngine, name: string, options: AudioBusOptions = {}): Promise<AudioBus> {\n await engine._isReady;\n\n const graph = createSoundSubGraph(engine._ctx, engine, options.volume ?? 1);\n const outBus = options.outBus ?? engine._mainBus;\n connectSoundSubGraph(graph, getBusInputNode(outBus));\n\n const bus: AudioBus = {\n name,\n _engine: engine,\n _graph: graph,\n _outBus: outBus,\n _dispose: () => disposeAudioBus(bus),\n };\n\n engine._buses.add(bus);\n return bus;\n}\n\n/** Sets a bus's output volume, optionally ramping. Works on a generic bus or the main bus. */\nexport function setBusVolume(bus: PrimaryAudioBus, value: number, options?: RampOptions): void {\n if (_isAudioBus(bus)) {\n setSoundSubGraphVolume(bus._graph, value, options);\n } else {\n setMainBusVolume(bus, value, options);\n }\n}\n\n/** Disposes a generic bus, releasing its sub-graph. */\nexport function disposeAudioBus(bus: AudioBus): void {\n disposeSoundSubGraph(bus._graph);\n bus._engine._buses.delete(bus);\n}\n"],"names":[],"mappings":";;;AAyCA,SAAS,YAAY,GAAA,EAAuC;AACxD,EAAA,OAAO,QAAA,IAAY,GAAA;AACvB;AAGO,SAAS,gBAAgB,GAAA,EAAiC;AAC7D,EAAA,OAAO,YAAY,GAAG,CAAA,GAAI,GAAA,CAAI,MAAA,CAAO,MAAM,GAAA,CAAI,GAAA;AACnD;AAWA,eAAsB,mBAAA,CAAoB,MAAA,EAAqB,IAAA,EAAc,OAAA,GAA2B,EAAC,EAAsB;AAC3H,EAAA,MAAM,MAAA,CAAO,QAAA;AAEb,EAAA,MAAM,QAAQ,mBAAA,CAAoB,MAAA,CAAO,MAAM,MAAA,EAAQ,OAAA,CAAQ,UAAU,CAAC,CAAA;AAC1E,EAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,IAAU,MAAA,CAAO,QAAA;AACxC,EAAA,oBAAA,CAAqB,KAAA,EAAO,eAAA,CAAgB,MAAM,CAAC,CAAA;AAEnD,EAAA,MAAM,GAAA,GAAgB;AAAA,IAClB,IAAA;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,MAAA,EAAQ,KAAA;AAAA,IACR,OAAA,EAAS,MAAA;AAAA,IACT,QAAA,EAAU,MAAM,eAAA,CAAgB,GAAG;AAAA,GACvC;AAEA,EAAA,MAAA,CAAO,MAAA,CAAO,IAAI,GAAG,CAAA;AACrB,EAAA,OAAO,GAAA;AACX;AAGO,SAAS,YAAA,CAAa,GAAA,EAAsB,KAAA,EAAe,OAAA,EAA6B;AAC3F,EAAA,IAAI,WAAA,CAAY,GAAG,CAAA,EAAG;AAClB,IAAA,sBAAA,CAAuB,GAAA,CAAI,MAAA,EAAQ,KAAA,EAAO,OAAO,CAAA;AAAA,EACrD,CAAA,MAAO;AACH,IAAA,gBAAA,CAAiB,GAAA,EAAK,OAAO,OAAO,CAAA;AAAA,EACxC;AACJ;AAGO,SAAS,gBAAgB,GAAA,EAAqB;AACjD,EAAA,oBAAA,CAAqB,IAAI,MAAM,CAAA;AAC/B,EAAA,GAAA,CAAI,OAAA,CAAQ,MAAA,CAAO,MAAA,CAAO,GAAG,CAAA;AACjC;;;;"}
@@ -0,0 +1,188 @@
1
+ import { createAudioSignal } from './audio-signal.js';
2
+ import { createMainOut, setMainOutVolume, createMainBus, disposeMainBus, disposeMainOut } from './bus.js';
3
+
4
+ const FormatMimeTypes = {
5
+ aac: "audio/aac",
6
+ ac3: "audio/ac3",
7
+ flac: "audio/flac",
8
+ m4a: "audio/mp4",
9
+ mp3: 'audio/mpeg; codecs="mp3"',
10
+ mp4: "audio/mp4",
11
+ ogg: 'audio/ogg; codecs="vorbis"',
12
+ wav: "audio/wav",
13
+ webm: 'audio/webm; codecs="vorbis"'
14
+ };
15
+ function isOfflineContext(ctx) {
16
+ return typeof OfflineAudioContext !== "undefined" && ctx instanceof OfflineAudioContext;
17
+ }
18
+ async function createAudioEngineAsync(options = {}) {
19
+ const ctx = options.audioContext ?? new AudioContext();
20
+ const isOffline = isOfflineContext(ctx);
21
+ const onStateChanged = createAudioSignal();
22
+ const onUserGesture = createAudioSignal();
23
+ let resolveReady;
24
+ const isReady = new Promise((resolve) => {
25
+ resolveReady = resolve;
26
+ });
27
+ const engine = {
28
+ get state() {
29
+ return isOffline ? "running" : ctx.state;
30
+ },
31
+ get currentTime() {
32
+ return ctx.currentTime ?? 0;
33
+ },
34
+ get _currentTime() {
35
+ return ctx.currentTime ?? 0;
36
+ },
37
+ get onStateChanged() {
38
+ return onStateChanged;
39
+ },
40
+ get onUserGesture() {
41
+ return onUserGesture;
42
+ },
43
+ _ctx: ctx,
44
+ _isOffline: isOffline,
45
+ _rampDuration: typeof options.parameterRampDuration === "number" ? Math.max(0, options.parameterRampDuration) : 0.01,
46
+ _volume: options.volume ?? 1,
47
+ // Assigned just below once the engine (the ramp clock) exists.
48
+ _mainOut: void 0,
49
+ _mainBus: void 0,
50
+ _validFormats: /* @__PURE__ */ new Set(),
51
+ _invalidFormats: /* @__PURE__ */ new Set(),
52
+ _sounds: /* @__PURE__ */ new Set(),
53
+ _buses: /* @__PURE__ */ new Set(),
54
+ _listener: null,
55
+ _spatialUpdaters: /* @__PURE__ */ new Set(),
56
+ _spatialAutoStop: null,
57
+ _disposers: [],
58
+ _onStateChanged: onStateChanged,
59
+ _onUserGesture: onUserGesture,
60
+ _isReady: isReady
61
+ };
62
+ engine._mainOut = createMainOut(ctx, engine);
63
+ setMainOutVolume(engine._mainOut, engine._volume);
64
+ engine._mainBus = createMainBus("default", ctx, engine, engine._mainOut);
65
+ _wireStateAndResume(engine, options);
66
+ _wireUserGesture(engine, options);
67
+ resolveReady();
68
+ await isReady;
69
+ return engine;
70
+ }
71
+ function _wireStateAndResume(engine, options) {
72
+ const ctx = engine._ctx;
73
+ if (engine._isOffline || typeof ctx.addEventListener !== "function") {
74
+ return;
75
+ }
76
+ const resumeOnPause = options.resumeOnPause !== false;
77
+ const retryInterval = options.resumeOnPauseRetryInterval ?? 1e3;
78
+ let started = false;
79
+ let resumeTimer = null;
80
+ const clearResumeTimer = () => {
81
+ if (resumeTimer !== null) {
82
+ clearInterval(resumeTimer);
83
+ resumeTimer = null;
84
+ }
85
+ };
86
+ const onStateChange = () => {
87
+ const state = engine.state;
88
+ if (state === "running") {
89
+ clearResumeTimer();
90
+ started = true;
91
+ } else if ((state === "suspended" || state === "interrupted") && started && resumeOnPause) {
92
+ clearResumeTimer();
93
+ resumeTimer = setInterval(() => {
94
+ void ctx.resume();
95
+ }, retryInterval);
96
+ }
97
+ engine._onStateChanged._notify(state);
98
+ };
99
+ ctx.addEventListener("statechange", onStateChange);
100
+ engine._disposers.push(() => {
101
+ clearResumeTimer();
102
+ ctx.removeEventListener("statechange", onStateChange);
103
+ });
104
+ }
105
+ function _wireUserGesture(engine, options) {
106
+ const resumeOnInteraction = options.resumeOnInteraction !== false;
107
+ if (engine._isOffline || typeof document === "undefined" || typeof document.addEventListener !== "function") {
108
+ return;
109
+ }
110
+ const onGesture = () => {
111
+ if (resumeOnInteraction) {
112
+ void engine._ctx.resume();
113
+ }
114
+ engine._onUserGesture._notify();
115
+ };
116
+ document.addEventListener("click", onGesture);
117
+ engine._disposers.push(() => document.removeEventListener("click", onGesture));
118
+ }
119
+ async function unlockAudioEngineAsync(engine) {
120
+ if (engine._isOffline) {
121
+ return;
122
+ }
123
+ const ctx = engine._ctx;
124
+ if (ctx.state !== "running") {
125
+ await ctx.resume();
126
+ }
127
+ }
128
+ function setMasterVolume(engine, value, options) {
129
+ engine._volume = value;
130
+ setMainOutVolume(engine._mainOut, value, options);
131
+ }
132
+ function getMasterVolume(engine) {
133
+ return engine._volume;
134
+ }
135
+ function isAudioFormatValid(engine, format) {
136
+ if (engine._validFormats.has(format)) {
137
+ return true;
138
+ }
139
+ if (engine._invalidFormats.has(format)) {
140
+ return false;
141
+ }
142
+ const mimeType = FormatMimeTypes[format];
143
+ if (mimeType === void 0) {
144
+ return false;
145
+ }
146
+ if (typeof Audio === "undefined") {
147
+ return true;
148
+ }
149
+ if (new Audio().canPlayType(mimeType) === "") {
150
+ engine._invalidFormats.add(format);
151
+ return false;
152
+ }
153
+ engine._validFormats.add(format);
154
+ return true;
155
+ }
156
+ function disposeAudioEngine(engine) {
157
+ engine._spatialAutoStop?.();
158
+ engine._spatialAutoStop = null;
159
+ engine._spatialUpdaters.clear();
160
+ engine._listener?._dispose();
161
+ for (const sound of Array.from(engine._sounds)) {
162
+ sound._dispose();
163
+ }
164
+ engine._sounds.clear();
165
+ for (const bus of Array.from(engine._buses)) {
166
+ bus._dispose();
167
+ }
168
+ engine._buses.clear();
169
+ for (const dispose of engine._disposers) {
170
+ try {
171
+ dispose();
172
+ } catch (e) {
173
+ console.warn(`Audio disposer failed: ${e.message}`);
174
+ }
175
+ }
176
+ engine._disposers.length = 0;
177
+ disposeMainBus(engine._mainBus);
178
+ disposeMainOut(engine._mainOut);
179
+ const ctx = engine._ctx;
180
+ if (!engine._isOffline && ctx.state !== "closed") {
181
+ void ctx.close();
182
+ }
183
+ engine._onStateChanged._clear();
184
+ engine._onUserGesture._clear();
185
+ }
186
+
187
+ export { createAudioEngineAsync, disposeAudioEngine, getMasterVolume, isAudioFormatValid, setMasterVolume, unlockAudioEngineAsync };
188
+ //# sourceMappingURL=audio-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-engine.js","sources":["../../../src/audio/audio-engine.ts"],"sourcesContent":["/**\n * Audio engine — lifecycle, master output, default main bus, autoplay unlock.\n *\n * Faithful port of AudioV2 `_WebAudioEngine` + `CreateAudioEngineAsync`,\n * re-shaped to pure state + standalone functions. Re-architected per Babylon\n * Lite pillars:\n * - No module-level side effects (no `Instances[]`, no module-scope Observable).\n * - Caller owns the engine handle (no `LastCreatedAudioEngine`).\n * - `Observable` becomes `AudioSignal`.\n * - Global hooks (click listener, resume timer) are registered into\n * `engine._disposers` and torn down on dispose.\n * - `OfflineAudioContext` is fully supported for deterministic, headless\n * PCM rendering (the primary audio test gate).\n */\n\nimport { type AudioSignal, type AudioSignalImpl, createAudioSignal } from \"./audio-signal.js\";\nimport { type RampOptions } from \"./audio-param.js\";\nimport { type MainBus, type MainOut, createMainBus, createMainOut, disposeMainBus, disposeMainOut, setMainOutVolume } from \"./bus.js\";\nimport type { Vec3 } from \"../math/types.js\";\n\n/**\n * Minimal structural view of the spatial listener, kept local so this module\n * does NOT import the spatial feature module (Pillar 4: tree-shaking) and so the\n * `.d.ts` rollup has no `AudioEngine` \\<-\\> `SpatialListener` import cycle. The\n * full listener (assigned by the spatial feature functions) is structurally\n * compatible. @internal\n */\nexport interface SpatialListenerSlot {\n /** Listener world position, read by source distance attenuation. @internal */ _position: Vec3;\n /** @internal */ _dispose(): void;\n}\n\n/** Audio context state, mirroring the Web Audio `AudioContextState` plus `\"interrupted\"`. */\nexport type AudioEngineState = \"running\" | \"suspended\" | \"closed\" | \"interrupted\";\n\n/** Options for {@link createAudioEngineAsync}. */\nexport interface AudioEngineOptions {\n /**\n * The audio context to use. Pass an `OfflineAudioContext` for deterministic,\n * faster-than-realtime rendering (used by the audio test suite). When omitted\n * a real-time `AudioContext` is created.\n */\n audioContext?: BaseAudioContext;\n /** Master output volume. Defaults to `1`. */\n volume?: number;\n /** Default parameter ramp smoothing, in seconds. Defaults to `0.01`. */\n parameterRampDuration?: number;\n /** Auto-resume the context on user interaction (click). Defaults to `true`. */\n resumeOnInteraction?: boolean;\n /** Auto-resume the context if the browser pauses playback. Defaults to `true`. */\n resumeOnPause?: boolean;\n /** Retry interval (ms) for `resumeOnPause`. Defaults to `1000`. */\n resumeOnPauseRetryInterval?: number;\n}\n\n/**\n * A Babylon Lite audio engine. Pure state — operate on it with the audio\n * functions (`createSoundAsync`, `setMasterVolume`, `disposeAudioEngine`, …).\n */\nexport interface AudioEngine {\n /** Current context state. Always `\"running\"` for an offline context. */\n readonly state: AudioEngineState;\n /** The audio context's current time, in seconds. */\n readonly currentTime: number;\n /** Fires whenever {@link state} changes. */\n readonly onStateChanged: AudioSignal<AudioEngineState>;\n /** Fires on every qualifying user gesture (click), not just the first. */\n readonly onUserGesture: AudioSignal<void>;\n\n /** @internal */ readonly _ctx: BaseAudioContext;\n /** @internal */ readonly _isOffline: boolean;\n /** Ramp-clock current time (satisfies `RampClock`). @internal */ readonly _currentTime: number;\n /** Ramp-clock default duration (satisfies `RampClock`). @internal */ _rampDuration: number;\n /** @internal */ _volume: number;\n /** @internal */ _mainOut: MainOut;\n /** @internal */ _mainBus: MainBus;\n /** @internal */ readonly _validFormats: Set<string>;\n /** @internal */ readonly _invalidFormats: Set<string>;\n /** @internal */ readonly _sounds: Set<{ _dispose(): void }>;\n /** @internal */ readonly _buses: Set<{ _dispose(): void }>;\n /** Lazily-built spatial listener (only when spatial audio is used). @internal */ _listener: SpatialListenerSlot | null;\n /** Per-frame spatial update closures, registered while attached. @internal */ readonly _spatialUpdaters: Set<() => void>;\n /** Stops the spatial auto-update loop, if running. @internal */ _spatialAutoStop: (() => void) | null;\n /** @internal */ readonly _disposers: Array<() => void>;\n /** @internal */ readonly _onStateChanged: AudioSignalImpl<AudioEngineState>;\n /** @internal */ readonly _onUserGesture: AudioSignalImpl<void>;\n /** @internal */ readonly _isReady: Promise<void>;\n}\n\nconst FormatMimeTypes: { [key: string]: string } = {\n aac: \"audio/aac\",\n ac3: \"audio/ac3\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n mp3: 'audio/mpeg; codecs=\"mp3\"',\n mp4: \"audio/mp4\",\n ogg: 'audio/ogg; codecs=\"vorbis\"',\n wav: \"audio/wav\",\n webm: 'audio/webm; codecs=\"vorbis\"',\n};\n\nfunction isOfflineContext(ctx: BaseAudioContext): boolean {\n return typeof OfflineAudioContext !== \"undefined\" && ctx instanceof OfflineAudioContext;\n}\n\n/**\n * Creates and initializes an audio engine.\n * @param options - Engine options.\n * @returns A promise that resolves with the ready-to-use engine.\n */\nexport async function createAudioEngineAsync(options: AudioEngineOptions = {}): Promise<AudioEngine> {\n const ctx = options.audioContext ?? new AudioContext();\n const isOffline = isOfflineContext(ctx);\n\n const onStateChanged = createAudioSignal<AudioEngineState>();\n const onUserGesture = createAudioSignal<void>();\n\n let resolveReady!: () => void;\n const isReady = new Promise<void>((resolve) => {\n resolveReady = resolve;\n });\n\n const engine: AudioEngine = {\n get state(): AudioEngineState {\n return isOffline ? \"running\" : ((ctx as AudioContext).state as AudioEngineState);\n },\n get currentTime(): number {\n return ctx.currentTime ?? 0;\n },\n get _currentTime(): number {\n return ctx.currentTime ?? 0;\n },\n get onStateChanged(): AudioSignal<AudioEngineState> {\n return onStateChanged;\n },\n get onUserGesture(): AudioSignal<void> {\n return onUserGesture;\n },\n _ctx: ctx,\n _isOffline: isOffline,\n _rampDuration: typeof options.parameterRampDuration === \"number\" ? Math.max(0, options.parameterRampDuration) : 0.01,\n _volume: options.volume ?? 1,\n // Assigned just below once the engine (the ramp clock) exists.\n _mainOut: undefined as unknown as MainOut,\n _mainBus: undefined as unknown as MainBus,\n _validFormats: new Set<string>(),\n _invalidFormats: new Set<string>(),\n _sounds: new Set<{ _dispose(): void }>(),\n _buses: new Set<{ _dispose(): void }>(),\n _listener: null,\n _spatialUpdaters: new Set<() => void>(),\n _spatialAutoStop: null,\n _disposers: [],\n _onStateChanged: onStateChanged,\n _onUserGesture: onUserGesture,\n _isReady: isReady,\n };\n\n // Build the output graph: mainBus._volume -> mainOut._gain -> destination.\n engine._mainOut = createMainOut(ctx, engine);\n setMainOutVolume(engine._mainOut, engine._volume);\n engine._mainBus = createMainBus(\"default\", ctx, engine, engine._mainOut);\n\n _wireStateAndResume(engine, options);\n _wireUserGesture(engine, options);\n\n resolveReady();\n await isReady;\n return engine;\n}\n\nfunction _wireStateAndResume(engine: AudioEngine, options: AudioEngineOptions): void {\n const ctx = engine._ctx;\n if (engine._isOffline || typeof (ctx as AudioContext).addEventListener !== \"function\") {\n return;\n }\n\n const resumeOnPause = options.resumeOnPause !== false;\n const retryInterval = options.resumeOnPauseRetryInterval ?? 1000;\n\n let started = false;\n let resumeTimer: ReturnType<typeof setInterval> | null = null;\n\n const clearResumeTimer = (): void => {\n if (resumeTimer !== null) {\n clearInterval(resumeTimer);\n resumeTimer = null;\n }\n };\n\n const onStateChange = (): void => {\n const state = engine.state;\n if (state === \"running\") {\n clearResumeTimer();\n started = true;\n } else if ((state === \"suspended\" || state === \"interrupted\") && started && resumeOnPause) {\n clearResumeTimer();\n resumeTimer = setInterval(() => {\n void (ctx as AudioContext).resume();\n }, retryInterval);\n }\n engine._onStateChanged._notify(state);\n };\n\n (ctx as AudioContext).addEventListener(\"statechange\", onStateChange);\n engine._disposers.push(() => {\n clearResumeTimer();\n (ctx as AudioContext).removeEventListener(\"statechange\", onStateChange);\n });\n}\n\nfunction _wireUserGesture(engine: AudioEngine, options: AudioEngineOptions): void {\n const resumeOnInteraction = options.resumeOnInteraction !== false;\n if (engine._isOffline || typeof document === \"undefined\" || typeof document.addEventListener !== \"function\") {\n return;\n }\n\n const onGesture = (): void => {\n if (resumeOnInteraction) {\n void (engine._ctx as AudioContext).resume();\n }\n engine._onUserGesture._notify();\n };\n\n document.addEventListener(\"click\", onGesture);\n engine._disposers.push(() => document.removeEventListener(\"click\", onGesture));\n}\n\n/**\n * Unlocks (resumes) the audio engine. Browsers require a user gesture before a\n * real-time context can produce sound; call this from a click/tap handler.\n * @param engine - The audio engine.\n */\nexport async function unlockAudioEngineAsync(engine: AudioEngine): Promise<void> {\n if (engine._isOffline) {\n return;\n }\n const ctx = engine._ctx as AudioContext;\n if (ctx.state !== \"running\") {\n await ctx.resume();\n }\n}\n\n/**\n * Sets the master output volume, optionally ramping to the new value.\n * @param engine - The audio engine.\n * @param value - Target volume (1 = unchanged).\n * @param options - Optional ramp shape/duration.\n */\nexport function setMasterVolume(engine: AudioEngine, value: number, options?: RampOptions): void {\n engine._volume = value;\n setMainOutVolume(engine._mainOut, value, options);\n}\n\n/** The master output volume. */\nexport function getMasterVolume(engine: AudioEngine): number {\n return engine._volume;\n}\n\n/**\n * Whether the given audio file format/extension can be decoded by the browser.\n * In non-browser (offline render / test) environments where no `Audio` element\n * exists, the format is assumed valid.\n * @internal\n */\nexport function isAudioFormatValid(engine: AudioEngine, format: string): boolean {\n if (engine._validFormats.has(format)) {\n return true;\n }\n if (engine._invalidFormats.has(format)) {\n return false;\n }\n\n const mimeType = FormatMimeTypes[format];\n if (mimeType === undefined) {\n return false;\n }\n\n if (typeof Audio === \"undefined\") {\n return true;\n }\n\n if (new Audio().canPlayType(mimeType) === \"\") {\n engine._invalidFormats.add(format);\n return false;\n }\n\n engine._validFormats.add(format);\n return true;\n}\n\n/**\n * Disposes the audio engine: stops all sounds, tears down global hooks, and\n * closes the (non-offline) audio context.\n * @param engine - The audio engine to dispose.\n */\nexport function disposeAudioEngine(engine: AudioEngine): void {\n engine._spatialAutoStop?.();\n engine._spatialAutoStop = null;\n engine._spatialUpdaters.clear();\n engine._listener?._dispose();\n\n for (const sound of Array.from(engine._sounds)) {\n sound._dispose();\n }\n engine._sounds.clear();\n\n for (const bus of Array.from(engine._buses)) {\n bus._dispose();\n }\n engine._buses.clear();\n\n for (const dispose of engine._disposers) {\n try {\n dispose();\n } catch (e) {\n console.warn(`Audio disposer failed: ${(e as Error).message}`);\n }\n }\n engine._disposers.length = 0;\n\n disposeMainBus(engine._mainBus);\n disposeMainOut(engine._mainOut);\n\n const ctx = engine._ctx;\n if (!engine._isOffline && (ctx as AudioContext).state !== \"closed\") {\n void (ctx as AudioContext).close();\n }\n\n engine._onStateChanged._clear();\n engine._onUserGesture._clear();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ let fileExtensionRegex = null;
2
+ function getFileExtension(url) {
3
+ fileExtensionRegex ??= new RegExp("\\.(\\w{3,4})($|\\?)");
4
+ return url.match(fileExtensionRegex)?.[1];
5
+ }
6
+ function cleanAudioUrl(url) {
7
+ return url.replace(/#/gm, "%23");
8
+ }
9
+ async function loadAudioArrayBuffer(url) {
10
+ const response = await fetch(cleanAudioUrl(url));
11
+ if (!response.ok) {
12
+ throw new Error(`HTTP ${response.status} loading '${url}': ${response.statusText}`);
13
+ }
14
+ return await response.arrayBuffer();
15
+ }
16
+
17
+ export { cleanAudioUrl, getFileExtension, loadAudioArrayBuffer };
18
+ //# sourceMappingURL=audio-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-fetch.js","sources":["../../../src/audio/audio-fetch.ts"],"sourcesContent":["/**\n * Audio network + decode helpers.\n *\n * Babylon Lite fetches audio with the same plain `fetch()` path every other Lite\n * loader uses (texture, glTF, env, splat). The AudioV2 `WebRequest` custom\n * header / URL-modifier layer is intentionally dropped — Lite has no such layer.\n */\n\nlet fileExtensionRegex: RegExp | null = null;\n\n/**\n * Extracts the 3–4 char file extension (before any query string), exactly as\n * written in the URL — not lowercased, matching AudioV2 `_FileExtensionRegex`.\n * @internal\n */\nexport function getFileExtension(url: string): string | undefined {\n fileExtensionRegex ??= new RegExp(\"\\\\.(\\\\w{3,4})($|\\\\?)\");\n return url.match(fileExtensionRegex)?.[1];\n}\n\n/** Escapes `#` so URLs with fragments load correctly. @internal */\nexport function cleanAudioUrl(url: string): string {\n return url.replace(/#/gm, \"%23\");\n}\n\n/**\n * Loads an `ArrayBuffer` from a URL via plain `fetch()`.\n * @internal\n */\nexport async function loadAudioArrayBuffer(url: string): Promise<ArrayBuffer> {\n const response = await fetch(cleanAudioUrl(url));\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} loading '${url}': ${response.statusText}`);\n }\n return await response.arrayBuffer();\n}\n"],"names":[],"mappings":"AAQA,IAAI,kBAAA,GAAoC,IAAA;AAOjC,SAAS,iBAAiB,GAAA,EAAiC;AAC9D,EAAA,kBAAA,KAAuB,IAAI,OAAO,sBAAsB,CAAA;AACxD,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,kBAAkB,CAAA,GAAI,CAAC,CAAA;AAC5C;AAGO,SAAS,cAAc,GAAA,EAAqB;AAC/C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,KAAA,EAAO,KAAK,CAAA;AACnC;AAMA,eAAsB,qBAAqB,GAAA,EAAmC;AAC1E,EAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,aAAA,CAAc,GAAG,CAAC,CAAA;AAC/C,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,aAAa,GAAG,CAAA,GAAA,EAAM,QAAA,CAAS,UAAU,CAAA,CAAE,CAAA;AAAA,EACtF;AACA,EAAA,OAAO,MAAM,SAAS,WAAA,EAAY;AACtC;;;;"}
@@ -0,0 +1,96 @@
1
+ const CurveLength = 100;
2
+ const MinRampDuration = 1e-6;
3
+ let TmpCurveValues = null;
4
+ let ExpCurve = null;
5
+ let LogCurve = null;
6
+ const TmpLineValues = new Float32Array([0, 0]);
7
+ function getExpCurve() {
8
+ if (!ExpCurve) {
9
+ ExpCurve = new Float32Array(CurveLength);
10
+ const increment = 1 / (CurveLength - 1);
11
+ let x = increment;
12
+ for (let i = 1; i < CurveLength; i++) {
13
+ ExpCurve[i] = Math.exp(-11.512925464970227 * (1 - x));
14
+ x += increment;
15
+ }
16
+ }
17
+ return ExpCurve;
18
+ }
19
+ function getLogCurve() {
20
+ if (!LogCurve) {
21
+ LogCurve = new Float32Array(CurveLength);
22
+ const increment = 1 / CurveLength;
23
+ let x = increment;
24
+ for (let i = 0; i < CurveLength; i++) {
25
+ LogCurve[i] = 1 + Math.log10(x) / Math.log10(CurveLength);
26
+ x += increment;
27
+ }
28
+ }
29
+ return LogCurve;
30
+ }
31
+ function getRampCurveValues(shape, from, to) {
32
+ if (shape === "linear") {
33
+ TmpLineValues[0] = from;
34
+ TmpLineValues[1] = to;
35
+ return TmpLineValues;
36
+ }
37
+ if (!TmpCurveValues) {
38
+ TmpCurveValues = new Float32Array(CurveLength);
39
+ }
40
+ const normalizedCurve = shape === "exponential" ? getExpCurve() : getLogCurve();
41
+ const direction = Math.sign(to - from);
42
+ const range = Math.abs(to - from);
43
+ if (direction === 1) {
44
+ for (let i = 0; i < normalizedCurve.length; i++) {
45
+ TmpCurveValues[i] = from + range * normalizedCurve[i];
46
+ }
47
+ } else {
48
+ let j = CurveLength - 1;
49
+ for (let i = 0; i < normalizedCurve.length; i++, j--) {
50
+ TmpCurveValues[i] = from - range * (1 - normalizedCurve[j]);
51
+ }
52
+ }
53
+ return TmpCurveValues;
54
+ }
55
+ function createRampParam(param, clock) {
56
+ return { _param: param, _clock: clock, _targetValue: param.value, _rampEndTime: 0 };
57
+ }
58
+ function isRamping(rp) {
59
+ return rp._clock._currentTime < rp._rampEndTime;
60
+ }
61
+ let warnedRampFailure = false;
62
+ function setRampTarget(rp, value, options) {
63
+ if (!Number.isFinite(value)) {
64
+ console.warn(`Attempted to set audio parameter to non-finite value: ${value}`);
65
+ return;
66
+ }
67
+ rp._param.cancelScheduledValues(0);
68
+ const shape = typeof options?.shape === "string" ? options.shape : "linear";
69
+ const startTime = rp._clock._currentTime;
70
+ if (shape === "none") {
71
+ rp._param.value = rp._targetValue = value;
72
+ rp._rampEndTime = startTime;
73
+ return;
74
+ }
75
+ rp._targetValue = value;
76
+ const rampDuration = rp._clock._rampDuration;
77
+ let duration = typeof options?.duration === "number" ? Math.max(options.duration, rampDuration) : rampDuration;
78
+ duration = Math.max(rampDuration, duration);
79
+ if (duration < MinRampDuration) {
80
+ rp._param.setValueAtTime(value, startTime);
81
+ return;
82
+ }
83
+ try {
84
+ const from = Number.isFinite(rp._param.value) ? rp._param.value : 0;
85
+ rp._param.setValueCurveAtTime(getRampCurveValues(shape, from, value), startTime, duration);
86
+ rp._rampEndTime = startTime + duration;
87
+ } catch (e) {
88
+ if (!warnedRampFailure) {
89
+ console.warn(`Audio parameter ramping failed: ${e.message}`);
90
+ warnedRampFailure = true;
91
+ }
92
+ }
93
+ }
94
+
95
+ export { createRampParam, getRampCurveValues, isRamping, setRampTarget };
96
+ //# sourceMappingURL=audio-param.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-param.js","sources":["../../../src/audio/audio-param.ts"],"sourcesContent":["/**\n * Audio parameter ramping — faithful port of Babylon.js AudioV2\n * `audioUtils._GetAudioParamCurveValues` + `_WebAudioParameterComponent`,\n * re-shaped to pure functions + a small pure-state wrapper.\n *\n * The exponential/logarithmic normalized curves are cached via lazy-init\n * (never at module scope) so this module has zero import-time side effects.\n * The curve math is copied verbatim from AudioV2 to preserve identical output.\n */\n\n/** Ramp shape used when changing an audio parameter (volume, pan, position…). */\nexport type AudioRampShape = \"none\" | \"linear\" | \"exponential\" | \"logarithmic\";\n\n/** Options for ramping an audio parameter's value. */\nexport interface RampOptions {\n /** Ramp time, in seconds. Clamped to at least the engine's `parameterRampDuration`. */\n duration?: number;\n /** Ramp shape. Defaults to `\"linear\"`. */\n shape?: AudioRampShape;\n}\n\n/**\n * Supplies the audio clock and default ramp duration for a parameter ramp.\n * The `AudioEngine` state object satisfies this.\n * @internal\n */\nexport interface RampClock {\n /** The audio context's `currentTime`, in seconds. @internal */\n readonly _currentTime: number;\n /** Default ramp smoothing, in seconds. @internal */\n readonly _rampDuration: number;\n}\n\nconst CurveLength = 100;\n\n/**\n * Minimum ramp duration in seconds. Below this the value is set immediately —\n * there is no perceptual difference, so a curve is not scheduled.\n */\nconst MinRampDuration = 0.000001;\n\nlet TmpCurveValues: Float32Array | null = null;\nlet ExpCurve: Float32Array | null = null;\nlet LogCurve: Float32Array | null = null;\nconst TmpLineValues = new Float32Array([0, 0]); // typed-array literal — bundler-safe, no side effect.\n\nfunction getExpCurve(): Float32Array {\n if (!ExpCurve) {\n ExpCurve = new Float32Array(CurveLength);\n const increment = 1 / (CurveLength - 1);\n let x = increment;\n for (let i = 1; i < CurveLength; i++) {\n ExpCurve[i] = Math.exp(-11.512925464970227 * (1 - x));\n x += increment;\n }\n }\n return ExpCurve;\n}\n\nfunction getLogCurve(): Float32Array {\n if (!LogCurve) {\n LogCurve = new Float32Array(CurveLength);\n const increment = 1 / CurveLength;\n let x = increment;\n for (let i = 0; i < CurveLength; i++) {\n LogCurve[i] = 1 + Math.log10(x) / Math.log10(CurveLength);\n x += increment;\n }\n }\n return LogCurve;\n}\n\n/**\n * Returns the value-curve array for a `setValueCurveAtTime` call ramping from\n * `from` to `to` with the given shape. Linear returns a shared 2-element array;\n * exp/log return a shared 100-element array. Both are reused buffers — copy if\n * you need to retain the values.\n * @internal\n */\nexport function getRampCurveValues(shape: Exclude<AudioRampShape, \"none\">, from: number, to: number): Float32Array {\n if (shape === \"linear\") {\n TmpLineValues[0] = from;\n TmpLineValues[1] = to;\n return TmpLineValues;\n }\n\n if (!TmpCurveValues) {\n TmpCurveValues = new Float32Array(CurveLength);\n }\n\n const normalizedCurve = shape === \"exponential\" ? getExpCurve() : getLogCurve();\n\n const direction = Math.sign(to - from);\n const range = Math.abs(to - from);\n\n if (direction === 1) {\n for (let i = 0; i < normalizedCurve.length; i++) {\n TmpCurveValues[i] = from + range * normalizedCurve[i]!;\n }\n } else {\n let j = CurveLength - 1;\n for (let i = 0; i < normalizedCurve.length; i++, j--) {\n TmpCurveValues[i] = from - range * (1 - normalizedCurve[j]!);\n }\n }\n\n return TmpCurveValues;\n}\n\n/**\n * Pure-state wrapper around a Web Audio `AudioParam` that performs shaped\n * ramping. Mirrors AudioV2 `_WebAudioParameterComponent`.\n * @internal\n */\nexport interface RampParam {\n /** @internal */ _param: AudioParam;\n /** @internal */ _clock: RampClock;\n /** @internal */ _targetValue: number;\n /** @internal */ _rampEndTime: number;\n}\n\n/** @internal */\nexport function createRampParam(param: AudioParam, clock: RampClock): RampParam {\n return { _param: param, _clock: clock, _targetValue: param.value, _rampEndTime: 0 };\n}\n\n/** Whether the parameter is currently mid-ramp. @internal */\nexport function isRamping(rp: RampParam): boolean {\n return rp._clock._currentTime < rp._rampEndTime;\n}\n\nlet warnedRampFailure = false;\n\n/**\n * Sets the target value of the parameter, ramping with the given shape/duration.\n * Faithful port of `_WebAudioParameterComponent.setTargetValue`.\n * @internal\n */\nexport function setRampTarget(rp: RampParam, value: number, options?: RampOptions): void {\n if (!Number.isFinite(value)) {\n console.warn(`Attempted to set audio parameter to non-finite value: ${value}`);\n return;\n }\n\n rp._param.cancelScheduledValues(0);\n\n const shape: AudioRampShape = typeof options?.shape === \"string\" ? options.shape : \"linear\";\n const startTime = rp._clock._currentTime;\n\n if (shape === \"none\") {\n rp._param.value = rp._targetValue = value;\n rp._rampEndTime = startTime;\n return;\n }\n\n rp._targetValue = value;\n\n const rampDuration = rp._clock._rampDuration;\n let duration = typeof options?.duration === \"number\" ? Math.max(options.duration, rampDuration) : rampDuration;\n duration = Math.max(rampDuration, duration);\n\n if (duration < MinRampDuration) {\n rp._param.setValueAtTime(value, startTime);\n return;\n }\n\n try {\n const from = Number.isFinite(rp._param.value) ? rp._param.value : 0;\n rp._param.setValueCurveAtTime(getRampCurveValues(shape, from, value), startTime, duration);\n rp._rampEndTime = startTime + duration;\n } catch (e) {\n if (!warnedRampFailure) {\n console.warn(`Audio parameter ramping failed: ${(e as Error).message}`);\n warnedRampFailure = true;\n }\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ function createAudioSignal() {
2
+ let entries = null;
3
+ const remove = (callback) => {
4
+ if (!entries) {
5
+ return;
6
+ }
7
+ const i = entries.findIndex((e) => e.cb === callback);
8
+ if (i !== -1) {
9
+ entries.splice(i, 1);
10
+ }
11
+ };
12
+ return {
13
+ add(callback) {
14
+ (entries ??= []).push({ cb: callback, once: false });
15
+ return () => remove(callback);
16
+ },
17
+ addOnce(callback) {
18
+ (entries ??= []).push({ cb: callback, once: true });
19
+ return () => remove(callback);
20
+ },
21
+ _removeCallback(callback) {
22
+ remove(callback);
23
+ },
24
+ _notify(value) {
25
+ if (!entries || entries.length === 0) {
26
+ return;
27
+ }
28
+ const snapshot = entries.slice();
29
+ for (const entry of snapshot) {
30
+ if (entry.once) {
31
+ remove(entry.cb);
32
+ }
33
+ entry.cb(value);
34
+ }
35
+ },
36
+ _clear() {
37
+ entries = null;
38
+ },
39
+ _hasObservers() {
40
+ return !!entries && entries.length > 0;
41
+ }
42
+ };
43
+ }
44
+
45
+ export { createAudioSignal };
46
+ //# sourceMappingURL=audio-signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio-signal.js","sources":["../../../src/audio/audio-signal.ts"],"sourcesContent":["/**\n * Lightweight event signal — Babylon Lite's tree-shakable replacement for the\n * core `Observable`. Pure state + standalone-ish callback set. No module-level\n * side effects: the callback set is created lazily on first `add`.\n *\n * Behavioural parity with the subset of `Observable` the audio engine uses:\n * `add`, `addOnce`, `removeCallback`, `notifyObservers`, `clear`, `hasObservers`.\n */\n\n/** Public read surface of a signal — consumers can only subscribe. */\nexport interface AudioSignal<T> {\n /** Subscribe. Returns an unsubscribe function. */\n add(callback: (value: T) => void): () => void;\n /** Subscribe for a single notification. Returns an unsubscribe function. */\n addOnce(callback: (value: T) => void): () => void;\n}\n\n/** Full read/write signal — held internally by the engine/sounds. @internal */\nexport interface AudioSignalImpl<T> extends AudioSignal<T> {\n /** @internal */ _removeCallback(callback: (value: T) => void): void;\n /** @internal */ _notify(value: T): void;\n /** @internal */ _clear(): void;\n /** @internal */ _hasObservers(): boolean;\n}\n\ninterface Entry<T> {\n cb: (value: T) => void;\n once: boolean;\n}\n\n/** Create a new signal. No allocation happens until the first subscriber. @internal */\nexport function createAudioSignal<T>(): AudioSignalImpl<T> {\n let entries: Entry<T>[] | null = null;\n\n const remove = (callback: (value: T) => void): void => {\n if (!entries) {\n return;\n }\n const i = entries.findIndex((e) => e.cb === callback);\n if (i !== -1) {\n entries.splice(i, 1);\n }\n };\n\n return {\n add(callback) {\n (entries ??= []).push({ cb: callback, once: false });\n return () => remove(callback);\n },\n addOnce(callback) {\n (entries ??= []).push({ cb: callback, once: true });\n return () => remove(callback);\n },\n _removeCallback(callback) {\n remove(callback);\n },\n _notify(value) {\n if (!entries || entries.length === 0) {\n return;\n }\n // Snapshot so callbacks may add/remove during iteration.\n const snapshot = entries.slice();\n for (const entry of snapshot) {\n if (entry.once) {\n remove(entry.cb);\n }\n entry.cb(value);\n }\n },\n _clear() {\n entries = null;\n },\n _hasObservers() {\n return !!entries && entries.length > 0;\n },\n };\n}\n"],"names":[],"mappings":"AA+BO,SAAS,iBAAA,GAA2C;AACvD,EAAA,IAAI,OAAA,GAA6B,IAAA;AAEjC,EAAA,MAAM,MAAA,GAAS,CAAC,QAAA,KAAuC;AACnD,IAAA,IAAI,CAAC,OAAA,EAAS;AACV,MAAA;AAAA,IACJ;AACA,IAAA,MAAM,IAAI,OAAA,CAAQ,SAAA,CAAU,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,QAAQ,CAAA;AACpD,IAAA,IAAI,MAAM,EAAA,EAAI;AACV,MAAA,OAAA,CAAQ,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,IACvB;AAAA,EACJ,CAAA;AAEA,EAAA,OAAO;AAAA,IACH,IAAI,QAAA,EAAU;AACV,MAAA,CAAC,OAAA,KAAY,EAAC,EAAG,IAAA,CAAK,EAAE,EAAA,EAAI,QAAA,EAAU,IAAA,EAAM,KAAA,EAAO,CAAA;AACnD,MAAA,OAAO,MAAM,OAAO,QAAQ,CAAA;AAAA,IAChC,CAAA;AAAA,IACA,QAAQ,QAAA,EAAU;AACd,MAAA,CAAC,OAAA,KAAY,EAAC,EAAG,IAAA,CAAK,EAAE,EAAA,EAAI,QAAA,EAAU,IAAA,EAAM,IAAA,EAAM,CAAA;AAClD,MAAA,OAAO,MAAM,OAAO,QAAQ,CAAA;AAAA,IAChC,CAAA;AAAA,IACA,gBAAgB,QAAA,EAAU;AACtB,MAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,IACnB,CAAA;AAAA,IACA,QAAQ,KAAA,EAAO;AACX,MAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG;AAClC,QAAA;AAAA,MACJ;AAEA,MAAA,MAAM,QAAA,GAAW,QAAQ,KAAA,EAAM;AAC/B,MAAA,KAAA,MAAW,SAAS,QAAA,EAAU;AAC1B,QAAA,IAAI,MAAM,IAAA,EAAM;AACZ,UAAA,MAAA,CAAO,MAAM,EAAE,CAAA;AAAA,QACnB;AACA,QAAA,KAAA,CAAM,GAAG,KAAK,CAAA;AAAA,MAClB;AAAA,IACJ,CAAA;AAAA,IACA,MAAA,GAAS;AACL,MAAA,OAAA,GAAU,IAAA;AAAA,IACd,CAAA;AAAA,IACA,aAAA,GAAgB;AACZ,MAAA,OAAO,CAAC,CAAC,OAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,CAAA;AAAA,IACzC;AAAA,GACJ;AACJ;;;;"}
@@ -0,0 +1,33 @@
1
+ import { createRampParam, setRampTarget } from './audio-param.js';
2
+
3
+ function createMainOut(ctx, clock) {
4
+ const gain = new GainNode(ctx);
5
+ gain.connect(ctx.destination);
6
+ return { _gain: gain, _volumeRamp: createRampParam(gain.gain, clock) };
7
+ }
8
+ function setMainOutVolume(mainOut, value, options) {
9
+ setRampTarget(mainOut._volumeRamp, value, options);
10
+ }
11
+ function disposeMainOut(mainOut) {
12
+ mainOut._gain.disconnect();
13
+ }
14
+ function createMainBus(name, ctx, clock, mainOut) {
15
+ const volume = new GainNode(ctx);
16
+ volume.connect(mainOut._gain);
17
+ return {
18
+ name,
19
+ _volume: volume,
20
+ _volumeRamp: createRampParam(volume.gain, clock),
21
+ _in: volume,
22
+ _out: volume
23
+ };
24
+ }
25
+ function setMainBusVolume(bus, value, options) {
26
+ setRampTarget(bus._volumeRamp, value, options);
27
+ }
28
+ function disposeMainBus(bus) {
29
+ bus._volume.disconnect();
30
+ }
31
+
32
+ export { createMainBus, createMainOut, disposeMainBus, disposeMainOut, setMainBusVolume, setMainOutVolume };
33
+ //# sourceMappingURL=bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bus.js","sources":["../../../src/audio/bus.ts"],"sourcesContent":["/**\n * Main output + main bus.\n *\n * Faithful port of AudioV2 `_WebAudioMainOut` and `_WebAudioMainBus` collapsed\n * to pure state + functions. Graph (Phase 1):\n *\n * `mainBus._volume (GainNode)` -\\> `mainOut._gain (GainNode)` -\\> `ctx.destination`\n *\n * Sounds connect their sub-graph output into `mainBus._in`.\n */\n\nimport { type RampClock, type RampOptions, type RampParam, createRampParam, setRampTarget } from \"./audio-param.js\";\n\n/** Engine master output node. @internal */\nexport interface MainOut {\n /** @internal */ _gain: GainNode;\n /** @internal */ _volumeRamp: RampParam;\n}\n\n/** A main audio bus. Sounds route here by default. Pure state — drive it with the bus functions. */\nexport interface MainBus {\n /** Bus name. */\n readonly name: string;\n\n /** @internal */ _volume: GainNode;\n /** @internal */ _volumeRamp: RampParam;\n /** Input node where upstream sounds/buses connect. @internal */ _in: AudioNode;\n /** Output node feeding the main out. @internal */ _out: AudioNode;\n}\n\n/** @internal */\nexport function createMainOut(ctx: BaseAudioContext, clock: RampClock): MainOut {\n const gain = new GainNode(ctx);\n gain.connect(ctx.destination);\n return { _gain: gain, _volumeRamp: createRampParam(gain.gain, clock) };\n}\n\n/** @internal */\nexport function setMainOutVolume(mainOut: MainOut, value: number, options?: RampOptions): void {\n setRampTarget(mainOut._volumeRamp, value, options);\n}\n\n/** @internal */\nexport function disposeMainOut(mainOut: MainOut): void {\n mainOut._gain.disconnect();\n}\n\n/** @internal */\nexport function createMainBus(name: string, ctx: BaseAudioContext, clock: RampClock, mainOut: MainOut): MainBus {\n const volume = new GainNode(ctx);\n volume.connect(mainOut._gain);\n return {\n name,\n _volume: volume,\n _volumeRamp: createRampParam(volume.gain, clock),\n _in: volume,\n _out: volume,\n };\n}\n\n/** @internal */\nexport function setMainBusVolume(bus: MainBus, value: number, options?: RampOptions): void {\n setRampTarget(bus._volumeRamp, value, options);\n}\n\n/** @internal */\nexport function disposeMainBus(bus: MainBus): void {\n bus._volume.disconnect();\n}\n"],"names":[],"mappings":";;AA+BO,SAAS,aAAA,CAAc,KAAuB,KAAA,EAA2B;AAC5E,EAAA,MAAM,IAAA,GAAO,IAAI,QAAA,CAAS,GAAG,CAAA;AAC7B,EAAA,IAAA,CAAK,OAAA,CAAQ,IAAI,WAAW,CAAA;AAC5B,EAAA,OAAO,EAAE,OAAO,IAAA,EAAM,WAAA,EAAa,gBAAgB,IAAA,CAAK,IAAA,EAAM,KAAK,CAAA,EAAE;AACzE;AAGO,SAAS,gBAAA,CAAiB,OAAA,EAAkB,KAAA,EAAe,OAAA,EAA6B;AAC3F,EAAA,aAAA,CAAc,OAAA,CAAQ,WAAA,EAAa,KAAA,EAAO,OAAO,CAAA;AACrD;AAGO,SAAS,eAAe,OAAA,EAAwB;AACnD,EAAA,OAAA,CAAQ,MAAM,UAAA,EAAW;AAC7B;AAGO,SAAS,aAAA,CAAc,IAAA,EAAc,GAAA,EAAuB,KAAA,EAAkB,OAAA,EAA2B;AAC5G,EAAA,MAAM,MAAA,GAAS,IAAI,QAAA,CAAS,GAAG,CAAA;AAC/B,EAAA,MAAA,CAAO,OAAA,CAAQ,QAAQ,KAAK,CAAA;AAC5B,EAAA,OAAO;AAAA,IACH,IAAA;AAAA,IACA,OAAA,EAAS,MAAA;AAAA,IACT,WAAA,EAAa,eAAA,CAAgB,MAAA,CAAO,IAAA,EAAM,KAAK,CAAA;AAAA,IAC/C,GAAA,EAAK,MAAA;AAAA,IACL,IAAA,EAAM;AAAA,GACV;AACJ;AAGO,SAAS,gBAAA,CAAiB,GAAA,EAAc,KAAA,EAAe,OAAA,EAA6B;AACvF,EAAA,aAAA,CAAc,GAAA,CAAI,WAAA,EAAa,KAAA,EAAO,OAAO,CAAA;AACjD;AAGO,SAAS,eAAe,GAAA,EAAoB;AAC/C,EAAA,GAAA,CAAI,QAAQ,UAAA,EAAW;AAC3B;;;;"}
@@ -0,0 +1,2 @@
1
+
2
+ //# sourceMappingURL=host-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-types.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ export { createAudioEngineAsync, disposeAudioEngine, getMasterVolume, setMasterVolume, unlockAudioEngineAsync } from './audio-engine.js';
2
+ export { SoundState, createSoundAsync, disposeSound, pauseSound, playSound, resumeSound, setSoundVolume, stopSound } from './static-sound.js';
3
+ export { createStreamingSoundAsync, disposeStreamingSound, pauseStreamingSound, playStreamingSound, preloadStreamingInstanceAsync, preloadStreamingInstancesAsync, resumeStreamingSound, setStreamingSoundVolume, stopStreamingSound } from './streaming-sound.js';
4
+ export { createAudioBusAsync, disposeAudioBus, setBusVolume } from './audio-bus.js';
5
+ export { attachSpatialTarget, detachSpatialTarget, enableSpatial, setSpatialAutoUpdate, setSpatialListener, setSpatialListenerPosition, setSpatialOrientation, setSpatialPosition, updateSpatialAudio } from './spatial.js';
6
+ export { enableStereo, setStereoPan } from './stereo.js';
7
+ export { enableAnalyzer, getByteFrequencyData, getByteTimeDomainData, getFloatFrequencyData, getFloatTimeDomainData } from './analyzer.js';
8
+ export { createMicrophoneSoundSourceAsync, createSoundSourceAsync, disposeSoundSource, setSoundSourceVolume } from './sound-source.js';
9
+ export { createUnmuteUI, disposeUnmuteUI, setUnmuteUIEnabled } from './unmute-ui.js';
10
+ export { createAudioVisualizer, disposeAudioVisualizer, renderAudioVisualizerFrame, startAudioVisualizer, stopAudioVisualizer } from './visualizer.js';
11
+ export { createSoundBufferAsync } from './sound-buffer.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}