@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,59 @@
1
+ import { isAudioFormatValid } from './audio-engine.js';
2
+ import { getFileExtension, loadAudioArrayBuffer } from './audio-fetch.js';
3
+
4
+ function wrapAudioBuffer(audioBuffer) {
5
+ return {
6
+ _audioBuffer: audioBuffer,
7
+ get duration() {
8
+ return audioBuffer.duration;
9
+ },
10
+ get sampleRate() {
11
+ return audioBuffer.sampleRate;
12
+ },
13
+ get channelCount() {
14
+ return audioBuffer.numberOfChannels;
15
+ },
16
+ get length() {
17
+ return audioBuffer.length;
18
+ }
19
+ };
20
+ }
21
+ async function decodeUrl(engine, url) {
22
+ const data = await loadAudioArrayBuffer(url);
23
+ return await engine._ctx.decodeAudioData(data);
24
+ }
25
+ async function createSoundBufferAsync(engine, source, options = {}) {
26
+ if (typeof source === "object" && source !== null && "_audioBuffer" in source) {
27
+ return source;
28
+ }
29
+ if (typeof AudioBuffer !== "undefined" && source instanceof AudioBuffer) {
30
+ return wrapAudioBuffer(source);
31
+ }
32
+ if (source instanceof ArrayBuffer) {
33
+ return wrapAudioBuffer(await engine._ctx.decodeAudioData(source));
34
+ }
35
+ if (typeof source === "string") {
36
+ return wrapAudioBuffer(await decodeUrl(engine, source));
37
+ }
38
+ if (Array.isArray(source)) {
39
+ const skipCodecCheck = options.skipCodecCheck ?? false;
40
+ for (const url of source) {
41
+ const format = getFileExtension(url);
42
+ if (!skipCodecCheck && (!format || !isAudioFormatValid(engine, format))) {
43
+ continue;
44
+ }
45
+ try {
46
+ return wrapAudioBuffer(await decodeUrl(engine, url));
47
+ } catch {
48
+ if (format) {
49
+ engine._invalidFormats.add(format);
50
+ }
51
+ }
52
+ }
53
+ throw new Error("No decodable audio source found in URL list.");
54
+ }
55
+ return wrapAudioBuffer(source);
56
+ }
57
+
58
+ export { createSoundBufferAsync };
59
+ //# sourceMappingURL=sound-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-buffer.js","sources":["../../../src/audio/sound-buffer.ts"],"sourcesContent":["/**\n * Decoded audio buffer.\n *\n * Faithful port of AudioV2 `_WebAudioStaticSoundBuffer` — collapsed to pure\n * state + a `createSoundBufferAsync` factory. Source may be an `AudioBuffer`, a\n * raw `ArrayBuffer`, a single URL, or an array of URLs (first decodable wins).\n */\n\nimport { type AudioEngine, isAudioFormatValid } from \"./audio-engine.js\";\nimport { getFileExtension, loadAudioArrayBuffer } from \"./audio-fetch.js\";\n\n/** A decoded audio buffer ready to be played by one or more sounds. */\nexport interface SoundBuffer {\n /** The underlying Web Audio buffer. @internal */\n _audioBuffer: AudioBuffer;\n /** Duration in seconds. */\n readonly duration: number;\n /** Sample rate in Hz. */\n readonly sampleRate: number;\n /** Number of channels. */\n readonly channelCount: number;\n /** Length in samples. */\n readonly length: number;\n}\n\n/** Source accepted when creating a sound or sound buffer. */\nexport type SoundSource = ArrayBuffer | AudioBuffer | SoundBuffer | string | string[];\n\n/** Options for {@link createSoundBufferAsync}. */\nexport interface SoundBufferOptions {\n /** Skip the browser codec check when selecting among multiple URLs. */\n skipCodecCheck?: boolean;\n}\n\nfunction wrapAudioBuffer(audioBuffer: AudioBuffer): SoundBuffer {\n return {\n _audioBuffer: audioBuffer,\n get duration() {\n return audioBuffer.duration;\n },\n get sampleRate() {\n return audioBuffer.sampleRate;\n },\n get channelCount() {\n return audioBuffer.numberOfChannels;\n },\n get length() {\n return audioBuffer.length;\n },\n };\n}\n\nasync function decodeUrl(engine: AudioEngine, url: string): Promise<AudioBuffer> {\n const data = await loadAudioArrayBuffer(url);\n return await engine._ctx.decodeAudioData(data);\n}\n\n/**\n * Loads and decodes audio into a reusable {@link SoundBuffer}.\n * @param engine - The audio engine.\n * @param source - An `AudioBuffer`, `ArrayBuffer`, URL, or URL list.\n * @param options - Decode options.\n * @returns A promise that resolves with the decoded buffer.\n */\nexport async function createSoundBufferAsync(engine: AudioEngine, source: SoundSource, options: SoundBufferOptions = {}): Promise<SoundBuffer> {\n // Already a decoded SoundBuffer?\n if (typeof source === \"object\" && source !== null && \"_audioBuffer\" in source) {\n return source;\n }\n\n if (typeof AudioBuffer !== \"undefined\" && source instanceof AudioBuffer) {\n return wrapAudioBuffer(source);\n }\n\n if (source instanceof ArrayBuffer) {\n return wrapAudioBuffer(await engine._ctx.decodeAudioData(source));\n }\n\n if (typeof source === \"string\") {\n return wrapAudioBuffer(await decodeUrl(engine, source));\n }\n\n if (Array.isArray(source)) {\n const skipCodecCheck = options.skipCodecCheck ?? false;\n for (const url of source) {\n const format = getFileExtension(url);\n if (!skipCodecCheck && (!format || !isAudioFormatValid(engine, format))) {\n continue;\n }\n try {\n return wrapAudioBuffer(await decodeUrl(engine, url));\n } catch {\n if (format) {\n engine._invalidFormats.add(format);\n }\n }\n }\n throw new Error(\"No decodable audio source found in URL list.\");\n }\n\n // Remaining possibility: an `AudioBuffer` in an environment without the global.\n return wrapAudioBuffer(source as AudioBuffer);\n}\n"],"names":[],"mappings":";;;AAkCA,SAAS,gBAAgB,WAAA,EAAuC;AAC5D,EAAA,OAAO;AAAA,IACH,YAAA,EAAc,WAAA;AAAA,IACd,IAAI,QAAA,GAAW;AACX,MAAA,OAAO,WAAA,CAAY,QAAA;AAAA,IACvB,CAAA;AAAA,IACA,IAAI,UAAA,GAAa;AACb,MAAA,OAAO,WAAA,CAAY,UAAA;AAAA,IACvB,CAAA;AAAA,IACA,IAAI,YAAA,GAAe;AACf,MAAA,OAAO,WAAA,CAAY,gBAAA;AAAA,IACvB,CAAA;AAAA,IACA,IAAI,MAAA,GAAS;AACT,MAAA,OAAO,WAAA,CAAY,MAAA;AAAA,IACvB;AAAA,GACJ;AACJ;AAEA,eAAe,SAAA,CAAU,QAAqB,GAAA,EAAmC;AAC7E,EAAA,MAAM,IAAA,GAAO,MAAM,oBAAA,CAAqB,GAAG,CAAA;AAC3C,EAAA,OAAO,MAAM,MAAA,CAAO,IAAA,CAAK,eAAA,CAAgB,IAAI,CAAA;AACjD;AASA,eAAsB,sBAAA,CAAuB,MAAA,EAAqB,MAAA,EAAqB,OAAA,GAA8B,EAAC,EAAyB;AAE3I,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,IAAA,IAAQ,kBAAkB,MAAA,EAAQ;AAC3E,IAAA,OAAO,MAAA;AAAA,EACX;AAEA,EAAA,IAAI,OAAO,WAAA,KAAgB,WAAA,IAAe,MAAA,YAAkB,WAAA,EAAa;AACrE,IAAA,OAAO,gBAAgB,MAAM,CAAA;AAAA,EACjC;AAEA,EAAA,IAAI,kBAAkB,WAAA,EAAa;AAC/B,IAAA,OAAO,gBAAgB,MAAM,MAAA,CAAO,IAAA,CAAK,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,EACpE;AAEA,EAAA,IAAI,OAAO,WAAW,QAAA,EAAU;AAC5B,IAAA,OAAO,eAAA,CAAgB,MAAM,SAAA,CAAU,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,EAC1D;AAEA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AACvB,IAAA,MAAM,cAAA,GAAiB,QAAQ,cAAA,IAAkB,KAAA;AACjD,IAAA,KAAA,MAAW,OAAO,MAAA,EAAQ;AACtB,MAAA,MAAM,MAAA,GAAS,iBAAiB,GAAG,CAAA;AACnC,MAAA,IAAI,CAAC,mBAAmB,CAAC,MAAA,IAAU,CAAC,kBAAA,CAAmB,MAAA,EAAQ,MAAM,CAAA,CAAA,EAAI;AACrE,QAAA;AAAA,MACJ;AACA,MAAA,IAAI;AACA,QAAA,OAAO,eAAA,CAAgB,MAAM,SAAA,CAAU,MAAA,EAAQ,GAAG,CAAC,CAAA;AAAA,MACvD,CAAA,CAAA,MAAQ;AACJ,QAAA,IAAI,MAAA,EAAQ;AACR,UAAA,MAAA,CAAO,eAAA,CAAgB,IAAI,MAAM,CAAA;AAAA,QACrC;AAAA,MACJ;AAAA,IACJ;AACA,IAAA,MAAM,IAAI,MAAM,8CAA8C,CAAA;AAAA,EAClE;AAGA,EAAA,OAAO,gBAAgB,MAAqB,CAAA;AAChD;;;;"}
@@ -0,0 +1,57 @@
1
+ import { getBusInputNode } from './audio-bus.js';
2
+ import { createSoundSubGraph, connectSoundSubGraph, disposeSoundSubGraph, setSoundSubGraphVolume } from './sound-sub-graph.js';
3
+
4
+ async function createSoundSourceAsync(engine, node, options = {}) {
5
+ const graph = createSoundSubGraph(engine._ctx, engine, options.volume ?? 1);
6
+ let outBus = null;
7
+ if (options.outBus) {
8
+ outBus = options.outBus;
9
+ } else if (options.outBusAutoDefault !== false) {
10
+ outBus = engine._mainBus;
11
+ }
12
+ if (outBus) {
13
+ connectSoundSubGraph(graph, getBusInputNode(outBus));
14
+ }
15
+ node.connect(graph._in);
16
+ const source = {
17
+ name: options.name ?? "",
18
+ _engine: engine,
19
+ _node: node,
20
+ _graph: graph,
21
+ _outBus: outBus,
22
+ _instances: /* @__PURE__ */ new Set([{ _volumeNode: node }]),
23
+ _dispose: () => disposeSoundSource(source)
24
+ };
25
+ engine._sounds.add(source);
26
+ return source;
27
+ }
28
+ async function createMicrophoneSoundSourceAsync(engine, options = {}) {
29
+ let mediaStream;
30
+ try {
31
+ mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
32
+ } catch (e) {
33
+ throw new Error("Unable to access microphone: " + String(e), { cause: e });
34
+ }
35
+ const node = new MediaStreamAudioSourceNode(engine._ctx, { mediaStream });
36
+ return createSoundSourceAsync(engine, node, { outBusAutoDefault: false, ...options });
37
+ }
38
+ function setSoundSourceVolume(source, value, options) {
39
+ setSoundSubGraphVolume(source._graph, value, options);
40
+ }
41
+ function disposeSoundSource(source) {
42
+ const node = source._node;
43
+ if (node) {
44
+ if (typeof MediaStreamAudioSourceNode !== "undefined" && node instanceof MediaStreamAudioSourceNode) {
45
+ for (const track of node.mediaStream.getTracks()) {
46
+ track.stop();
47
+ }
48
+ }
49
+ node.disconnect();
50
+ source._node = null;
51
+ }
52
+ disposeSoundSubGraph(source._graph);
53
+ source._engine._sounds.delete(source);
54
+ }
55
+
56
+ export { createMicrophoneSoundSourceAsync, createSoundSourceAsync, disposeSoundSource, setSoundSourceVolume };
57
+ //# sourceMappingURL=sound-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-source.js","sources":["../../../src/audio/sound-source.ts"],"sourcesContent":["/**\n * Live / external audio input sources — opt-in feature module.\n *\n * Faithful port of AudioV2 `AbstractSoundSource` / `_WebAudioSoundSource`\n * (`createMicrophoneSoundSourceAsync` + `createSoundSourceAsync`), re-architected\n * to Lite idioms (pure state + standalone functions). A source wraps an\n * arbitrary Web Audio `AudioNode` (typically a `MediaStreamAudioSourceNode` from\n * the microphone) and routes it through a {@link SoundSubGraph}, so the spatial,\n * stereo, and analyzer features apply to it exactly as they do to sounds.\n *\n * Signal flow:\n * `sourceNode` -\\> `graph._in` -\\> (spatial/stereo) -\\> `graph._volume` -\\> `outBus`\n *\n * The wrapped node is tracked as a single pseudo-instance so the shared\n * {@link rebuildSoundSubGraphHead} logic reconnects it when spatial/stereo are\n * enabled after creation.\n */\n\nimport { type AudioEngine } from \"./audio-engine.js\";\nimport { type PrimaryAudioBus, getBusInputNode } from \"./audio-bus.js\";\nimport { type RampOptions } from \"./audio-param.js\";\nimport { type SoundSubGraph, connectSoundSubGraph, createSoundSubGraph, disposeSoundSubGraph, setSoundSubGraphVolume } from \"./sound-sub-graph.js\";\n\n/** Options for {@link createSoundSourceAsync} and {@link createMicrophoneSoundSourceAsync}. */\nexport interface SoundSourceOptions {\n /** Optional name. */\n name?: string;\n /** Output bus. Defaults to the engine's main bus unless {@link outBusAutoDefault} is `false`. */\n outBus?: PrimaryAudioBus;\n /**\n * Whether the output bus defaults to the engine's main bus when {@link outBus}\n * is not set. Defaults to `true` for {@link createSoundSourceAsync} and `false`\n * for {@link createMicrophoneSoundSourceAsync} (a mic is usually analyzed, not\n * played back, to avoid feedback).\n */\n outBusAutoDefault?: boolean;\n /** Initial volume. Defaults to `1`. */\n volume?: number;\n}\n\n/**\n * A sound fed by a live or external Web Audio input node (e.g. a microphone).\n * Accepted anywhere an {@link AudioGraphHost} is — enable spatial, stereo, or\n * analyzer features on it directly.\n */\nexport interface AudioInputSource {\n /** The source name. */\n readonly name: string;\n /** @internal */ _engine: AudioEngine;\n /** The wrapped input node; `null` after disposal. @internal */ _node: AudioNode | null;\n /** @internal */ _graph: SoundSubGraph;\n /** @internal */ _outBus: PrimaryAudioBus | null;\n /** Single pseudo-instance wrapping the input node, for head reconnection. @internal */ _instances: Set<{ _volumeNode: AudioNode }>;\n /** @internal */ _dispose(): void;\n}\n\n/**\n * Wraps an arbitrary Web Audio node as a sound source, routed to the given\n * output bus (or the engine's default main bus).\n * @param engine - The audio engine.\n * @param node - The input node to wrap (its output is routed through the graph).\n * @param options - Source options.\n * @returns A promise that resolves with the ready source.\n */\nexport async function createSoundSourceAsync(engine: AudioEngine, node: AudioNode, options: SoundSourceOptions = {}): Promise<AudioInputSource> {\n const graph = createSoundSubGraph(engine._ctx, engine, options.volume ?? 1);\n\n let outBus: PrimaryAudioBus | null = null;\n if (options.outBus) {\n outBus = options.outBus;\n } else if (options.outBusAutoDefault !== false) {\n outBus = engine._mainBus;\n }\n if (outBus) {\n connectSoundSubGraph(graph, getBusInputNode(outBus));\n }\n\n node.connect(graph._in);\n\n const source: AudioInputSource = {\n name: options.name ?? \"\",\n _engine: engine,\n _node: node,\n _graph: graph,\n _outBus: outBus,\n _instances: new Set([{ _volumeNode: node }]),\n _dispose: () => disposeSoundSource(source),\n };\n\n engine._sounds.add(source);\n return source;\n}\n\n/**\n * Requests microphone access and wraps the resulting stream as a sound source.\n * The source does not auto-connect to the main bus by default (to avoid\n * feedback); attach an analyzer or set an `outBus` to use it.\n * @param engine - The audio engine.\n * @param options - Source options.\n * @returns A promise that resolves with the ready microphone source.\n * @throws If microphone access is denied or unavailable.\n */\nexport async function createMicrophoneSoundSourceAsync(engine: AudioEngine, options: SoundSourceOptions = {}): Promise<AudioInputSource> {\n let mediaStream: MediaStream;\n try {\n mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n } catch (e) {\n throw new Error(\"Unable to access microphone: \" + String(e), { cause: e });\n }\n\n const node = new MediaStreamAudioSourceNode(engine._ctx as AudioContext, { mediaStream });\n return createSoundSourceAsync(engine, node, { outBusAutoDefault: false, ...options });\n}\n\n/**\n * Sets the source volume, optionally ramping.\n * @param source - The sound source.\n * @param value - Linear gain (`1` = unity).\n * @param options - Optional ramp options.\n */\nexport function setSoundSourceVolume(source: AudioInputSource, value: number, options?: RampOptions): void {\n setSoundSubGraphVolume(source._graph, value, options);\n}\n\n/**\n * Disposes a sound source, stopping any microphone tracks and releasing its\n * graph.\n * @param source - The sound source.\n */\nexport function disposeSoundSource(source: AudioInputSource): void {\n const node = source._node;\n if (node) {\n if (typeof MediaStreamAudioSourceNode !== \"undefined\" && node instanceof MediaStreamAudioSourceNode) {\n for (const track of node.mediaStream.getTracks()) {\n track.stop();\n }\n }\n node.disconnect();\n source._node = null;\n }\n disposeSoundSubGraph(source._graph);\n source._engine._sounds.delete(source);\n}\n"],"names":[],"mappings":";;;AAgEA,eAAsB,sBAAA,CAAuB,MAAA,EAAqB,IAAA,EAAiB,OAAA,GAA8B,EAAC,EAA8B;AAC5I,EAAA,MAAM,QAAQ,mBAAA,CAAoB,MAAA,CAAO,MAAM,MAAA,EAAQ,OAAA,CAAQ,UAAU,CAAC,CAAA;AAE1E,EAAA,IAAI,MAAA,GAAiC,IAAA;AACrC,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAChB,IAAA,MAAA,GAAS,OAAA,CAAQ,MAAA;AAAA,EACrB,CAAA,MAAA,IAAW,OAAA,CAAQ,iBAAA,KAAsB,KAAA,EAAO;AAC5C,IAAA,MAAA,GAAS,MAAA,CAAO,QAAA;AAAA,EACpB;AACA,EAAA,IAAI,MAAA,EAAQ;AACR,IAAA,oBAAA,CAAqB,KAAA,EAAO,eAAA,CAAgB,MAAM,CAAC,CAAA;AAAA,EACvD;AAEA,EAAA,IAAA,CAAK,OAAA,CAAQ,MAAM,GAAG,CAAA;AAEtB,EAAA,MAAM,MAAA,GAA2B;AAAA,IAC7B,IAAA,EAAM,QAAQ,IAAA,IAAQ,EAAA;AAAA,IACtB,OAAA,EAAS,MAAA;AAAA,IACT,KAAA,EAAO,IAAA;AAAA,IACP,MAAA,EAAQ,KAAA;AAAA,IACR,OAAA,EAAS,MAAA;AAAA,IACT,UAAA,sBAAgB,GAAA,CAAI,CAAC,EAAE,WAAA,EAAa,IAAA,EAAM,CAAC,CAAA;AAAA,IAC3C,QAAA,EAAU,MAAM,kBAAA,CAAmB,MAAM;AAAA,GAC7C;AAEA,EAAA,MAAA,CAAO,OAAA,CAAQ,IAAI,MAAM,CAAA;AACzB,EAAA,OAAO,MAAA;AACX;AAWA,eAAsB,gCAAA,CAAiC,MAAA,EAAqB,OAAA,GAA8B,EAAC,EAA8B;AACrI,EAAA,IAAI,WAAA;AACJ,EAAA,IAAI;AACA,IAAA,WAAA,GAAc,MAAM,SAAA,CAAU,YAAA,CAAa,aAAa,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,EAC3E,SAAS,CAAA,EAAG;AACR,IAAA,MAAM,IAAI,MAAM,+BAAA,GAAkC,MAAA,CAAO,CAAC,CAAA,EAAG,EAAE,KAAA,EAAO,CAAA,EAAG,CAAA;AAAA,EAC7E;AAEA,EAAA,MAAM,OAAO,IAAI,0BAAA,CAA2B,OAAO,IAAA,EAAsB,EAAE,aAAa,CAAA;AACxF,EAAA,OAAO,sBAAA,CAAuB,QAAQ,IAAA,EAAM,EAAE,mBAAmB,KAAA,EAAO,GAAG,SAAS,CAAA;AACxF;AAQO,SAAS,oBAAA,CAAqB,MAAA,EAA0B,KAAA,EAAe,OAAA,EAA6B;AACvG,EAAA,sBAAA,CAAuB,MAAA,CAAO,MAAA,EAAQ,KAAA,EAAO,OAAO,CAAA;AACxD;AAOO,SAAS,mBAAmB,MAAA,EAAgC;AAC/D,EAAA,MAAM,OAAO,MAAA,CAAO,KAAA;AACpB,EAAA,IAAI,IAAA,EAAM;AACN,IAAA,IAAI,OAAO,0BAAA,KAA+B,WAAA,IAAe,IAAA,YAAgB,0BAAA,EAA4B;AACjG,MAAA,KAAA,MAAW,KAAA,IAAS,IAAA,CAAK,WAAA,CAAY,SAAA,EAAU,EAAG;AAC9C,QAAA,KAAA,CAAM,IAAA,EAAK;AAAA,MACf;AAAA,IACJ;AACA,IAAA,IAAA,CAAK,UAAA,EAAW;AAChB,IAAA,MAAA,CAAO,KAAA,GAAQ,IAAA;AAAA,EACnB;AACA,EAAA,oBAAA,CAAqB,OAAO,MAAM,CAAA;AAClC,EAAA,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,CAAO,MAAM,CAAA;AACxC;;;;"}
@@ -0,0 +1,72 @@
1
+ import { createRampParam, setRampTarget } from './audio-param.js';
2
+
3
+ function createSoundSubGraph(ctx, clock, volume = 1) {
4
+ const gain = new GainNode(ctx);
5
+ gain.gain.value = volume;
6
+ return {
7
+ _ctx: ctx,
8
+ _volume: gain,
9
+ _volumeRamp: createRampParam(gain.gain, clock),
10
+ _spatial: null,
11
+ _stereo: null,
12
+ _analyzer: null,
13
+ _root: null,
14
+ _in: gain,
15
+ _out: gain
16
+ };
17
+ }
18
+ function rebuildSoundSubGraphHead(graph, instances) {
19
+ const spatial = graph._spatial;
20
+ const stereo = graph._stereo;
21
+ let head;
22
+ if (spatial && stereo) {
23
+ const root = graph._root ??= new GainNode(graph._ctx);
24
+ root.disconnect();
25
+ root.connect(spatial._inputNode);
26
+ root.connect(stereo._inputNode);
27
+ head = root;
28
+ } else {
29
+ if (graph._root) {
30
+ graph._root.disconnect();
31
+ graph._root = null;
32
+ }
33
+ head = spatial?._inputNode ?? stereo?._inputNode ?? graph._volume;
34
+ }
35
+ const oldHead = graph._in;
36
+ if (oldHead === head) {
37
+ return;
38
+ }
39
+ graph._in = head;
40
+ if (instances) {
41
+ for (const instance of instances) {
42
+ try {
43
+ instance._volumeNode.disconnect(oldHead);
44
+ } catch {
45
+ }
46
+ instance._volumeNode.connect(head);
47
+ }
48
+ }
49
+ }
50
+ function setSoundSubGraphVolume(graph, value, options) {
51
+ setRampTarget(graph._volumeRamp, value, options);
52
+ }
53
+ function connectSoundSubGraph(graph, downstream) {
54
+ graph._out.connect(downstream);
55
+ }
56
+ function disconnectSoundSubGraph(graph, downstream) {
57
+ graph._out.disconnect(downstream);
58
+ }
59
+ function disposeSoundSubGraph(graph) {
60
+ graph._spatial?._dispose();
61
+ graph._spatial = null;
62
+ graph._stereo?._dispose();
63
+ graph._stereo = null;
64
+ graph._analyzer?._dispose();
65
+ graph._analyzer = null;
66
+ graph._root?.disconnect();
67
+ graph._root = null;
68
+ graph._volume.disconnect();
69
+ }
70
+
71
+ export { connectSoundSubGraph, createSoundSubGraph, disconnectSoundSubGraph, disposeSoundSubGraph, rebuildSoundSubGraphHead, setSoundSubGraphVolume };
72
+ //# sourceMappingURL=sound-sub-graph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sound-sub-graph.js","sources":["../../../src/audio/sound-sub-graph.ts"],"sourcesContent":["/**\n * Per-sound sub-graph.\n *\n * Faithful port of AudioV2 `_WebAudioBaseSubGraph` + `_WebAudioBusAndSoundSubGraph`\n * + `_VolumeWebAudioSubNode`. The volume `GainNode` is always present; the\n * spatial, stereo, and analyzer sub-nodes are optional and spliced in on demand\n * by their (tree-shaken) feature modules.\n *\n * Signal flow (mirrors AudioV2 `_onSubNodesChanged`):\n * - none: `instances` -\\> `_volume` (`_in` === `_out`)\n * - spatial OR stereo: `instances` -\\> `subNode._inputNode` -\\> `_volume`\n * - spatial AND stereo: `instances` -\\> `_root` (split) -\\> [spatial, stereo] -\\> `_volume`\n * - analyzer (any of the above): `_volume` also fans out to the analyzer tap\n */\n\nimport { type RampClock, type RampOptions, type RampParam, createRampParam, setRampTarget } from \"./audio-param.js\";\n\n/**\n * Minimal structural view of a pre-volume sub-node (spatial or stereo), kept\n * local so this module does NOT import the feature modules (Pillar 4:\n * tree-shaking) and so the `.d.ts` rollup has no import cycle. The full node\n * (assigned by the feature functions) is structurally compatible. @internal\n */\nexport interface PreVolumeSlot {\n /** Head input node of the sub-node. @internal */ _inputNode: AudioNode;\n /** @internal */ _dispose(): void;\n}\n\n/** Minimal structural view of the analyzer tap sub-node. @internal */\nexport interface AnalyzerSlot {\n /** @internal */ _dispose(): void;\n}\n\n/** Sound sub-graph state. @internal */\nexport interface SoundSubGraph {\n /** @internal */ _ctx: BaseAudioContext;\n /** @internal */ _volume: GainNode;\n /** @internal */ _volumeRamp: RampParam;\n /** Optional spatial (3D panner) sub-node. @internal */ _spatial: PreVolumeSlot | null;\n /** Optional stereo-pan sub-node. @internal */ _stereo: PreVolumeSlot | null;\n /** Optional analyzer tap off {@link _volume}. @internal */ _analyzer: AnalyzerSlot | null;\n /** Split gain feeding both spatial and stereo when both are present. @internal */ _root: GainNode | null;\n /** Head node — where playing instances connect. @internal */ _in: AudioNode;\n /** Tail node — connects to the output bus. @internal */ _out: AudioNode;\n}\n\n/** @internal */\nexport function createSoundSubGraph(ctx: BaseAudioContext, clock: RampClock, volume = 1): SoundSubGraph {\n const gain = new GainNode(ctx);\n gain.gain.value = volume;\n return {\n _ctx: ctx,\n _volume: gain,\n _volumeRamp: createRampParam(gain.gain, clock),\n _spatial: null,\n _stereo: null,\n _analyzer: null,\n _root: null,\n _in: gain,\n _out: gain,\n };\n}\n\n/**\n * Recomputes the head node (`_in`) after a pre-volume sub-node (spatial/stereo)\n * is added or removed, mirroring AudioV2 `_onSubNodesChanged`. When both spatial\n * and stereo are present they run in parallel, fed by a split `_root` gain. Any\n * live instances are reconnected from the old head to the new one. @internal\n */\nexport function rebuildSoundSubGraphHead(graph: SoundSubGraph, instances?: Iterable<{ _volumeNode: AudioNode }>): void {\n const spatial = graph._spatial;\n const stereo = graph._stereo;\n\n let head: AudioNode;\n if (spatial && stereo) {\n const root = (graph._root ??= new GainNode(graph._ctx));\n root.disconnect();\n root.connect(spatial._inputNode);\n root.connect(stereo._inputNode);\n head = root;\n } else {\n if (graph._root) {\n graph._root.disconnect();\n graph._root = null;\n }\n head = spatial?._inputNode ?? stereo?._inputNode ?? graph._volume;\n }\n\n const oldHead = graph._in;\n if (oldHead === head) {\n return;\n }\n graph._in = head;\n\n if (instances) {\n for (const instance of instances) {\n try {\n instance._volumeNode.disconnect(oldHead);\n } catch {\n // Instance may not yet be connected; ignore.\n }\n instance._volumeNode.connect(head);\n }\n }\n}\n\n/** @internal */\nexport function setSoundSubGraphVolume(graph: SoundSubGraph, value: number, options?: RampOptions): void {\n setRampTarget(graph._volumeRamp, value, options);\n}\n\n/** Connects the sub-graph output to a downstream input node. @internal */\nexport function connectSoundSubGraph(graph: SoundSubGraph, downstream: AudioNode): void {\n graph._out.connect(downstream);\n}\n\n/** Disconnects the sub-graph output from a downstream input node. @internal */\nexport function disconnectSoundSubGraph(graph: SoundSubGraph, downstream: AudioNode): void {\n graph._out.disconnect(downstream);\n}\n\n/** @internal */\nexport function disposeSoundSubGraph(graph: SoundSubGraph): void {\n graph._spatial?._dispose();\n graph._spatial = null;\n graph._stereo?._dispose();\n graph._stereo = null;\n graph._analyzer?._dispose();\n graph._analyzer = null;\n graph._root?.disconnect();\n graph._root = null;\n graph._volume.disconnect();\n}\n"],"names":[],"mappings":"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"}