@sadhaka/loom-engine 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audio/audio-asset-cache.d.ts +12 -0
- package/dist/audio/audio-asset-cache.d.ts.map +1 -0
- package/dist/audio/audio-asset-cache.js +53 -0
- package/dist/audio/audio-asset-cache.js.map +1 -0
- package/dist/audio/audio-asset-loader.d.ts +16 -0
- package/dist/audio/audio-asset-loader.d.ts.map +1 -0
- package/dist/audio/audio-asset-loader.js +100 -0
- package/dist/audio/audio-asset-loader.js.map +1 -0
- package/dist/audio/audio-listener-resource.d.ts +19 -0
- package/dist/audio/audio-listener-resource.d.ts.map +1 -0
- package/dist/audio/audio-listener-resource.js +57 -0
- package/dist/audio/audio-listener-resource.js.map +1 -0
- package/dist/audio/cue-catalog.d.ts +39 -0
- package/dist/audio/cue-catalog.d.ts.map +1 -0
- package/dist/audio/cue-catalog.js +186 -0
- package/dist/audio/cue-catalog.js.map +1 -0
- package/dist/audio/music-director.d.ts +18 -0
- package/dist/audio/music-director.d.ts.map +1 -0
- package/dist/audio/music-director.js +177 -0
- package/dist/audio/music-director.js.map +1 -0
- package/dist/audio/spatial-audio-bus.d.ts +66 -0
- package/dist/audio/spatial-audio-bus.d.ts.map +1 -0
- package/dist/audio/spatial-audio-bus.js +341 -0
- package/dist/audio/spatial-audio-bus.js.map +1 -0
- package/dist/audio/spatial-audio-system.d.ts +17 -0
- package/dist/audio/spatial-audio-system.d.ts.map +1 -0
- package/dist/audio/spatial-audio-system.js +98 -0
- package/dist/audio/spatial-audio-system.js.map +1 -0
- package/dist/audio/zone-audio-system.d.ts +78 -0
- package/dist/audio/zone-audio-system.d.ts.map +1 -0
- package/dist/audio/zone-audio-system.js +206 -0
- package/dist/audio/zone-audio-system.js.map +1 -0
- package/dist/director/ai/ai-plugin-registry.d.ts +20 -0
- package/dist/director/ai/ai-plugin-registry.d.ts.map +1 -0
- package/dist/director/ai/ai-plugin-registry.js +232 -0
- package/dist/director/ai/ai-plugin-registry.js.map +1 -0
- package/dist/director/ai/mock-ai-plugin.d.ts +24 -0
- package/dist/director/ai/mock-ai-plugin.d.ts.map +1 -0
- package/dist/director/ai/mock-ai-plugin.js +83 -0
- package/dist/director/ai/mock-ai-plugin.js.map +1 -0
- package/dist/director/ai/plugin-context.d.ts +27 -0
- package/dist/director/ai/plugin-context.d.ts.map +1 -0
- package/dist/director/ai/plugin-context.js +152 -0
- package/dist/director/ai/plugin-context.js.map +1 -0
- package/dist/director/ai/plugin.d.ts +57 -0
- package/dist/director/ai/plugin.d.ts.map +1 -0
- package/dist/director/ai/plugin.js +32 -0
- package/dist/director/ai/plugin.js.map +1 -0
- package/dist/director/index.d.ts +27 -0
- package/dist/director/index.d.ts.map +1 -0
- package/dist/director/index.js +26 -0
- package/dist/director/index.js.map +1 -0
- package/dist/director/zone/mock-zone-bridge.d.ts +22 -0
- package/dist/director/zone/mock-zone-bridge.d.ts.map +1 -0
- package/dist/director/zone/mock-zone-bridge.js +107 -0
- package/dist/director/zone/mock-zone-bridge.js.map +1 -0
- package/dist/director/zone/sse-zone-bridge.d.ts +40 -0
- package/dist/director/zone/sse-zone-bridge.d.ts.map +1 -0
- package/dist/director/zone/sse-zone-bridge.js +164 -0
- package/dist/director/zone/sse-zone-bridge.js.map +1 -0
- package/dist/director/zone/zone-event-bridge.d.ts +21 -0
- package/dist/director/zone/zone-event-bridge.d.ts.map +1 -0
- package/dist/director/zone/zone-event-bridge.js +24 -0
- package/dist/director/zone/zone-event-bridge.js.map +1 -0
- package/dist/director/zone/zone-event-envelope.d.ts +90 -0
- package/dist/director/zone/zone-event-envelope.d.ts.map +1 -0
- package/dist/director/zone/zone-event-envelope.js +104 -0
- package/dist/director/zone/zone-event-envelope.js.map +1 -0
- package/dist/director/zone/zone-event-log.d.ts +17 -0
- package/dist/director/zone/zone-event-log.d.ts.map +1 -0
- package/dist/director/zone/zone-event-log.js +46 -0
- package/dist/director/zone/zone-event-log.js.map +1 -0
- package/dist/director/zone/zone-event-system.d.ts +14 -0
- package/dist/director/zone/zone-event-system.d.ts.map +1 -0
- package/dist/director/zone/zone-event-system.js +179 -0
- package/dist/director/zone/zone-event-system.js.map +1 -0
- package/dist/director/zone/zone-state-resource.d.ts +15 -0
- package/dist/director/zone/zone-state-resource.d.ts.map +1 -0
- package/dist/director/zone/zone-state-resource.js +60 -0
- package/dist/director/zone/zone-state-resource.js.map +1 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +33 -0
- package/dist/server/index.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class AudioAssetCache {
|
|
2
|
+
private buffers;
|
|
3
|
+
get(name: string): AudioBuffer | null;
|
|
4
|
+
has(name: string): boolean;
|
|
5
|
+
set(name: string, buffer: AudioBuffer): void;
|
|
6
|
+
drop(name: string): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
list(): ReadonlyArray<string>;
|
|
9
|
+
}
|
|
10
|
+
export declare const RESOURCE_AUDIO_ASSET_CACHE = "audio_asset_cache";
|
|
11
|
+
export declare function createAudioAssetCache(): AudioAssetCache;
|
|
12
|
+
//# sourceMappingURL=audio-asset-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-asset-cache.d.ts","sourceRoot":"","sources":["../../src/audio/audio-asset-cache.ts"],"names":[],"mappings":"AAYA,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAuC;IAGtD,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAMrC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAO1B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAO5C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKxB,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,aAAa,CAAC,MAAM,CAAC;CAG9B;AAKD,eAAO,MAAM,0BAA0B,sBAAsB,CAAC;AAI9D,wBAAgB,qBAAqB,IAAI,eAAe,CAEvD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// AudioAssetCache - in-memory store of decoded AudioBuffers keyed by name.
|
|
2
|
+
//
|
|
3
|
+
// Per LOOM-AUDIO-SPEC §4.1. The cache is a thin Map<string, AudioBuffer>
|
|
4
|
+
// wrapper. AudioAssetLoader writes into it; CueCatalog and MusicDirector
|
|
5
|
+
// read from it. Lifetime is the AudioContext's lifetime; clear() is the
|
|
6
|
+
// hard reset for tests / scene transitions.
|
|
7
|
+
//
|
|
8
|
+
// Naming: callers choose name on load(). Defaults to URL basename without
|
|
9
|
+
// extension when omitted (the loader handles that derivation; the cache
|
|
10
|
+
// just stores whatever name it's handed). Re-loading the same name
|
|
11
|
+
// overwrites silently.
|
|
12
|
+
export class AudioAssetCache {
|
|
13
|
+
buffers = new Map();
|
|
14
|
+
// Get a previously-loaded buffer. Returns null if not in the cache.
|
|
15
|
+
get(name) {
|
|
16
|
+
return this.buffers.get(name) ?? null;
|
|
17
|
+
}
|
|
18
|
+
// True once a buffer has been placed under name (via the loader's
|
|
19
|
+
// store path; tests can also call set directly through factory init).
|
|
20
|
+
has(name) {
|
|
21
|
+
return this.buffers.has(name);
|
|
22
|
+
}
|
|
23
|
+
// Internal entry point used by AudioAssetLoader after decode resolves.
|
|
24
|
+
// Exposed publicly so consumers can pre-seed buffers from another
|
|
25
|
+
// source (synthesized OfflineAudioContext output, for instance).
|
|
26
|
+
set(name, buffer) {
|
|
27
|
+
this.buffers.set(name, buffer);
|
|
28
|
+
}
|
|
29
|
+
// Remove a single asset. Future get() returns null. The buffer object
|
|
30
|
+
// is GC'd if no live source still references it (any AudioBufferSource
|
|
31
|
+
// already started keeps a reference until it ends).
|
|
32
|
+
drop(name) {
|
|
33
|
+
this.buffers.delete(name);
|
|
34
|
+
}
|
|
35
|
+
// Clear the whole cache. Useful for tests + scene-boundary teardown.
|
|
36
|
+
clear() {
|
|
37
|
+
this.buffers.clear();
|
|
38
|
+
}
|
|
39
|
+
// Active entry names (for debug + tests).
|
|
40
|
+
list() {
|
|
41
|
+
return Array.from(this.buffers.keys());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Resource key for the world's resource registry. Engine consumers
|
|
45
|
+
// register an AudioAssetCache instance under this key alongside
|
|
46
|
+
// RESOURCE_AUDIO_BUS so cues + music can resolve named assets.
|
|
47
|
+
export const RESOURCE_AUDIO_ASSET_CACHE = 'audio_asset_cache';
|
|
48
|
+
// Factory mirrors createTimeResource / createVeilBudgetResource so
|
|
49
|
+
// engine.create wiring is uniform.
|
|
50
|
+
export function createAudioAssetCache() {
|
|
51
|
+
return new AudioAssetCache();
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=audio-asset-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-asset-cache.js","sourceRoot":"","sources":["../../src/audio/audio-asset-cache.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,wEAAwE;AACxE,4CAA4C;AAC5C,EAAE;AACF,0EAA0E;AAC1E,wEAAwE;AACxE,mEAAmE;AACnE,uBAAuB;AAEvB,MAAM,OAAO,eAAe;IAClB,OAAO,GAA6B,IAAI,GAAG,EAAE,CAAC;IAEtD,oEAAoE;IACpE,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACxC,CAAC;IAED,kEAAkE;IAClE,sEAAsE;IACtE,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,uEAAuE;IACvE,kEAAkE;IAClE,iEAAiE;IACjE,GAAG,CAAC,IAAY,EAAE,MAAmB;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,sEAAsE;IACtE,uEAAuE;IACvE,oDAAoD;IACpD,IAAI,CAAC,IAAY;QACf,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,qEAAqE;IACrE,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,0CAA0C;IAC1C,IAAI;QACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;CACF;AAED,mEAAmE;AACnE,gEAAgE;AAChE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAE9D,mEAAmE;AACnE,mCAAmC;AACnC,MAAM,UAAU,qBAAqB;IACnC,OAAO,IAAI,eAAe,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AudioBus } from './audio-bus.js';
|
|
2
|
+
import type { AudioAssetCache } from './audio-asset-cache.js';
|
|
3
|
+
export interface AudioAssetManifest {
|
|
4
|
+
[name: string]: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class AudioAssetLoader {
|
|
7
|
+
private audioBus;
|
|
8
|
+
private cache;
|
|
9
|
+
private inflight;
|
|
10
|
+
private constructor();
|
|
11
|
+
static create(audioBus: AudioBus, cache: AudioAssetCache): AudioAssetLoader;
|
|
12
|
+
load(url: string, name?: string): Promise<AudioBuffer>;
|
|
13
|
+
preload(manifest: AudioAssetManifest): Promise<void>;
|
|
14
|
+
inflightCount(): number;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=audio-asset-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-asset-loader.d.ts","sourceRoot":"","sources":["../../src/audio/audio-asset-loader.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,WAAW,kBAAkB;IAEjC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAyBD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAa;IAE7B,OAAO;IAKP,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,gBAAgB;IAQrE,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BtD,OAAO,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1D,aAAa,IAAI,MAAM;CAGxB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// AudioAssetLoader - URL fetch + decode pipeline for audio assets.
|
|
2
|
+
//
|
|
3
|
+
// Per LOOM-AUDIO-SPEC §4.1. Two responsibilities:
|
|
4
|
+
// 1. fetch a URL via the Fetch API, decode the response into an
|
|
5
|
+
// AudioBuffer through audioBus.ctx.decodeAudioData, and store it
|
|
6
|
+
// in the AudioAssetCache under a caller-chosen name (or the URL
|
|
7
|
+
// basename without extension if name omitted).
|
|
8
|
+
// 2. preload a manifest of {name -> URL} entries, resolving once all
|
|
9
|
+
// load. Reject on the first load failure (consumer can wrap in
|
|
10
|
+
// Promise.allSettled if they want partial success).
|
|
11
|
+
//
|
|
12
|
+
// Failure semantics: if fetch or decode rejects, the cache is NOT
|
|
13
|
+
// touched - prior values under the same name survive a failed reload.
|
|
14
|
+
// Inflight counter increments on fetch start and decrements after the
|
|
15
|
+
// decode resolves OR rejects (regardless of cache write outcome) so a
|
|
16
|
+
// "still loading" UI can drive off it without sticking on errors.
|
|
17
|
+
// Derive a fallback asset name from a URL. Strips the query string and
|
|
18
|
+
// fragment, keeps the last path segment, then drops a trailing
|
|
19
|
+
// extension (.mp3, .ogg, .wav, .aac, .opus, etc.). Returns the URL as-is
|
|
20
|
+
// if no segments or no recognizable extension boundary - the cache will
|
|
21
|
+
// still accept any string as a key.
|
|
22
|
+
function basenameFromUrl(url) {
|
|
23
|
+
// Strip query + hash.
|
|
24
|
+
var clean = url.split('?')[0].split('#')[0];
|
|
25
|
+
// Take the last path segment.
|
|
26
|
+
var segments = clean.split('/');
|
|
27
|
+
var last = segments[segments.length - 1] ?? clean;
|
|
28
|
+
if (last.length === 0) {
|
|
29
|
+
// Trailing slash; fall back to the whole stripped URL.
|
|
30
|
+
return clean;
|
|
31
|
+
}
|
|
32
|
+
// Drop trailing extension (last dot, if any).
|
|
33
|
+
var dot = last.lastIndexOf('.');
|
|
34
|
+
if (dot > 0) {
|
|
35
|
+
return last.substring(0, dot);
|
|
36
|
+
}
|
|
37
|
+
return last;
|
|
38
|
+
}
|
|
39
|
+
export class AudioAssetLoader {
|
|
40
|
+
audioBus;
|
|
41
|
+
cache;
|
|
42
|
+
inflight = 0;
|
|
43
|
+
constructor(audioBus, cache) {
|
|
44
|
+
this.audioBus = audioBus;
|
|
45
|
+
this.cache = cache;
|
|
46
|
+
}
|
|
47
|
+
static create(audioBus, cache) {
|
|
48
|
+
return new AudioAssetLoader(audioBus, cache);
|
|
49
|
+
}
|
|
50
|
+
// Fetch + decode one URL. On success, stores the decoded AudioBuffer
|
|
51
|
+
// in the cache under `name` (or basename of URL when name is omitted)
|
|
52
|
+
// and returns the buffer. On failure (fetch reject, non-OK status, or
|
|
53
|
+
// decode reject), the promise rejects and the cache is left untouched.
|
|
54
|
+
async load(url, name) {
|
|
55
|
+
var assetName = name !== undefined ? name : basenameFromUrl(url);
|
|
56
|
+
this.inflight++;
|
|
57
|
+
try {
|
|
58
|
+
var response = await fetch(url);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
throw new Error('AudioAssetLoader.load: fetch failed for "' + url + '": HTTP ' +
|
|
61
|
+
String(response.status));
|
|
62
|
+
}
|
|
63
|
+
var arrayBuf = await response.arrayBuffer();
|
|
64
|
+
// decodeAudioData returns a Promise in modern browsers (deprecated
|
|
65
|
+
// callback signature still works but Promise is canonical).
|
|
66
|
+
var buffer = await this.audioBus.ctx.decodeAudioData(arrayBuf);
|
|
67
|
+
this.cache.set(assetName, buffer);
|
|
68
|
+
return buffer;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
// Always decrement, even on failure - the inflight counter tracks
|
|
72
|
+
// pending operations, not successful outcomes.
|
|
73
|
+
this.inflight--;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Bulk preload. Resolves when ALL loads complete; rejects with the
|
|
77
|
+
// first underlying error if any single load rejects. Manifest entries
|
|
78
|
+
// run concurrently (Promise.all). Successfully-loaded entries land in
|
|
79
|
+
// the cache regardless of whether siblings later failed - this matches
|
|
80
|
+
// the "partial success allowed downstream" semantics of fetch+decode.
|
|
81
|
+
async preload(manifest) {
|
|
82
|
+
var entries = Object.keys(manifest);
|
|
83
|
+
if (entries.length === 0)
|
|
84
|
+
return;
|
|
85
|
+
var loads = [];
|
|
86
|
+
for (var i = 0; i < entries.length; i++) {
|
|
87
|
+
var n = entries[i];
|
|
88
|
+
var u = manifest[n];
|
|
89
|
+
loads.push(this.load(u, n));
|
|
90
|
+
}
|
|
91
|
+
await Promise.all(loads);
|
|
92
|
+
}
|
|
93
|
+
// Number of load() calls in flight. Useful for "X assets still
|
|
94
|
+
// loading" UI; not a throttle. Increments on fetch start, decrements
|
|
95
|
+
// when the inner pipeline resolves or rejects.
|
|
96
|
+
inflightCount() {
|
|
97
|
+
return this.inflight;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=audio-asset-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-asset-loader.js","sourceRoot":"","sources":["../../src/audio/audio-asset-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AudioListenerPose } from './spatial-audio-bus.js';
|
|
2
|
+
export type { AudioListenerPose } from './spatial-audio-bus.js';
|
|
3
|
+
export interface AudioListenerResource {
|
|
4
|
+
pose: AudioListenerPose;
|
|
5
|
+
lastUpdateFrame: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const DEFAULT_LISTENER_FORWARD: {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
z: number;
|
|
11
|
+
};
|
|
12
|
+
export declare const DEFAULT_LISTENER_UP: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
z: number;
|
|
16
|
+
};
|
|
17
|
+
export declare function createAudioListenerResource(): AudioListenerResource;
|
|
18
|
+
export declare const RESOURCE_AUDIO_LISTENER = "audio_listener";
|
|
19
|
+
//# sourceMappingURL=audio-listener-resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-listener-resource.d.ts","sourceRoot":"","sources":["../../src/audio/audio-listener-resource.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,YAAY,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAMhE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,iBAAiB,CAAC;IAGxB,eAAe,EAAE,MAAM,CAAC;CACzB;AAID,eAAO,MAAM,wBAAwB,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAEvE,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAElE,CAAC;AAWF,wBAAgB,2BAA2B,IAAI,qBAAqB,CAWnE;AAKD,eAAO,MAAM,uBAAuB,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// AudioListenerResource - the world's snapshot of where the player is
|
|
2
|
+
// hearing from.
|
|
3
|
+
//
|
|
4
|
+
// SpatialAudioBus.setListener writes the AudioContext.listener
|
|
5
|
+
// directly, but engine consumers also want a queryable resource so a
|
|
6
|
+
// secondary system (minimap, debug HUD, deterministic recording) can
|
|
7
|
+
// read the same pose without round-tripping through the AudioContext
|
|
8
|
+
// (which is synchronous in Web Audio but opaque in Node tests).
|
|
9
|
+
//
|
|
10
|
+
// SpatialAudioSystem (PHASE_RENDER) updates this each tick from the
|
|
11
|
+
// local character's transform and pushes the same pose into
|
|
12
|
+
// SpatialAudioBus.setListener. Renderer code that needs to gate work
|
|
13
|
+
// on "did the listener move this frame?" can compare lastUpdateFrame
|
|
14
|
+
// against the TimeResource frame counter.
|
|
15
|
+
//
|
|
16
|
+
// Default orientation per LOOM-AUDIO-SPEC.md §8.4:
|
|
17
|
+
// forward = (0, 0, -1) - camera looks down -Z (top-down 2D)
|
|
18
|
+
// up = (0, 1, 0)
|
|
19
|
+
// These are fixed in v1; only position changes per frame.
|
|
20
|
+
//
|
|
21
|
+
// Re-exports the AudioListenerPose type from spatial-audio-bus.ts for
|
|
22
|
+
// import convenience so consumers wiring just the resource don't need
|
|
23
|
+
// to pull in the bus module too.
|
|
24
|
+
// Default forward / up vectors per spec §8.4. Exported for tests and
|
|
25
|
+
// for consumers that want to compare against the canonical defaults.
|
|
26
|
+
export const DEFAULT_LISTENER_FORWARD = {
|
|
27
|
+
x: 0, y: 0, z: -1,
|
|
28
|
+
};
|
|
29
|
+
export const DEFAULT_LISTENER_UP = {
|
|
30
|
+
x: 0, y: 1, z: 0,
|
|
31
|
+
};
|
|
32
|
+
// Factory: returns a fresh AudioListenerResource at the world origin
|
|
33
|
+
// with the canonical default orientation. The resource is the same
|
|
34
|
+
// shape used by SpatialAudioBus.setListener, so engine consumers can
|
|
35
|
+
// pipe it straight through:
|
|
36
|
+
// var listener = createAudioListenerResource();
|
|
37
|
+
// spatialBus.setListener(listener.pose);
|
|
38
|
+
//
|
|
39
|
+
// lastUpdateFrame starts at 0 (never updated yet); SpatialAudioSystem
|
|
40
|
+
// stamps it on the first tick.
|
|
41
|
+
export function createAudioListenerResource() {
|
|
42
|
+
return {
|
|
43
|
+
pose: {
|
|
44
|
+
x: 0,
|
|
45
|
+
y: 0,
|
|
46
|
+
z: 0,
|
|
47
|
+
forward: { x: DEFAULT_LISTENER_FORWARD.x, y: DEFAULT_LISTENER_FORWARD.y, z: DEFAULT_LISTENER_FORWARD.z },
|
|
48
|
+
up: { x: DEFAULT_LISTENER_UP.x, y: DEFAULT_LISTENER_UP.y, z: DEFAULT_LISTENER_UP.z },
|
|
49
|
+
},
|
|
50
|
+
lastUpdateFrame: 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// Resource key for the world's resource registry. Engine consumers
|
|
54
|
+
// register an AudioListenerResource under this key; SpatialAudioSystem
|
|
55
|
+
// reads + writes it.
|
|
56
|
+
export const RESOURCE_AUDIO_LISTENER = 'audio_listener';
|
|
57
|
+
//# sourceMappingURL=audio-listener-resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-listener-resource.js","sourceRoot":"","sources":["../../src/audio/audio-listener-resource.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,gBAAgB;AAChB,EAAE;AACF,+DAA+D;AAC/D,qEAAqE;AACrE,qEAAqE;AACrE,qEAAqE;AACrE,gEAAgE;AAChE,EAAE;AACF,oEAAoE;AACpE,4DAA4D;AAC5D,qEAAqE;AACrE,qEAAqE;AACrE,0CAA0C;AAC1C,EAAE;AACF,mDAAmD;AACnD,+DAA+D;AAC/D,wBAAwB;AACxB,0DAA0D;AAC1D,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,iCAAiC;AAiBjC,qEAAqE;AACrE,qEAAqE;AACrE,MAAM,CAAC,MAAM,wBAAwB,GAAwC;IAC3E,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAwC;IACtE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;CACjB,CAAC;AAEF,qEAAqE;AACrE,mEAAmE;AACnE,qEAAqE;AACrE,4BAA4B;AAC5B,kDAAkD;AAClD,2CAA2C;AAC3C,EAAE;AACF,sEAAsE;AACtE,+BAA+B;AAC/B,MAAM,UAAU,2BAA2B;IACzC,OAAO;QACL,IAAI,EAAE;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,CAAC,EAAE,CAAC;YACJ,OAAO,EAAE,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC,EAAE;YACxG,EAAE,EAAE,EAAE,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,mBAAmB,CAAC,CAAC,EAAE;SACrF;QACD,eAAe,EAAE,CAAC;KACnB,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,uEAAuE;AACvE,qBAAqB;AACrB,MAAM,CAAC,MAAM,uBAAuB,GAAG,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AudioBus } from './audio-bus.js';
|
|
2
|
+
import type { AudioAssetCache } from './audio-asset-cache.js';
|
|
3
|
+
import type { PositionalPlayOptions, SpatialAudioBus, SpatialSourceHandle } from './spatial-audio-bus.js';
|
|
4
|
+
export type { PositionalPlayOptions, SpatialAudioBus, SpatialSourceHandle, };
|
|
5
|
+
export interface CueDefinition {
|
|
6
|
+
asset: string;
|
|
7
|
+
bus?: 'sfx' | 'music' | 'voice' | 'ui' | string;
|
|
8
|
+
spatial?: boolean;
|
|
9
|
+
defaults?: Partial<PositionalPlayOptions> & {
|
|
10
|
+
gain?: number;
|
|
11
|
+
rate?: number;
|
|
12
|
+
};
|
|
13
|
+
cooldownMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export type CuePlayOptions = Partial<PositionalPlayOptions> & {
|
|
16
|
+
gain?: number;
|
|
17
|
+
rate?: number;
|
|
18
|
+
x?: number;
|
|
19
|
+
y?: number;
|
|
20
|
+
};
|
|
21
|
+
export declare class CueCatalog {
|
|
22
|
+
private audioBus;
|
|
23
|
+
private spatialBus;
|
|
24
|
+
private cache;
|
|
25
|
+
private cues;
|
|
26
|
+
private cooldowns;
|
|
27
|
+
private liveHandles;
|
|
28
|
+
private constructor();
|
|
29
|
+
static create(audioBus: AudioBus, spatialBus: SpatialAudioBus, cache: AudioAssetCache): CueCatalog;
|
|
30
|
+
register(name: string, def: CueDefinition): void;
|
|
31
|
+
unregister(name: string): void;
|
|
32
|
+
has(name: string): boolean;
|
|
33
|
+
list(): ReadonlyArray<string>;
|
|
34
|
+
play(name: string, options?: CuePlayOptions): SpatialSourceHandle | null;
|
|
35
|
+
stopAll(name: string): void;
|
|
36
|
+
private markPlayed;
|
|
37
|
+
}
|
|
38
|
+
export declare const RESOURCE_CUE_CATALOG = "cue_catalog";
|
|
39
|
+
//# sourceMappingURL=cue-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cue-catalog.d.ts","sourceRoot":"","sources":["../../src/audio/cue-catalog.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EACV,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACpB,MAAM,wBAAwB,CAAC;AAMhC,YAAY,EACV,qBAAqB,EACrB,eAAe,EACf,mBAAmB,GACpB,CAAC;AAEF,MAAM,WAAW,aAAa;IAE5B,KAAK,EAAE,MAAM,CAAC;IAEd,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;IAIhD,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,QAAQ,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAE7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,cAAc,GACxB,OAAO,CAAC,qBAAqB,CAAC,GAC5B;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAiB7D,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,IAAI,CAAyC;IACrD,OAAO,CAAC,SAAS,CAAyC;IAG1D,OAAO,CAAC,WAAW,CAAoD;IAEvE,OAAO;IAMP,MAAM,CAAC,MAAM,CACX,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,eAAe,EAC3B,KAAK,EAAE,eAAe,GACrB,UAAU;IAIb,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,GAAG,IAAI;IAOhD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAS9B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,IAAI,IAAI,aAAa,CAAC,MAAM,CAAC;IAQ7B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,mBAAmB,GAAG,IAAI;IA2FxE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAc3B,OAAO,CAAC,UAAU;CAGnB;AAKD,eAAO,MAAM,oBAAoB,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// CueCatalog - named sound events with predefined wiring.
|
|
2
|
+
//
|
|
3
|
+
// Per LOOM-AUDIO-SPEC §4.2. Gameplay code calls cues.play('boss_spawn',
|
|
4
|
+
// {x, y}) and the catalog handles AudioBus / SpatialAudioBus routing,
|
|
5
|
+
// asset lookup, gain/rate defaults, and per-cue cooldowns. Spatial cues
|
|
6
|
+
// route through SpatialAudioBus.playPositional and require x + y in
|
|
7
|
+
// the play options; non-spatial cues route through AudioBus.playOneShot
|
|
8
|
+
// on the named bus (default 'sfx').
|
|
9
|
+
//
|
|
10
|
+
// Cooldown enforcement: each cue tracks its last-play timestamp; play()
|
|
11
|
+
// invocations within cooldownMs of the prior play return null without
|
|
12
|
+
// touching the audio graph. Useful for "boss_hit" so rapid-fire hits
|
|
13
|
+
// don't stack into clipping.
|
|
14
|
+
//
|
|
15
|
+
// stopAll(name): for spatial cues that loop (boss combat ambience), the
|
|
16
|
+
// catalog tracks live SpatialSourceHandles per cue name and exposes
|
|
17
|
+
// stopAll to kill them on a single command (e.g. "boss died, stop the
|
|
18
|
+
// loop"). Non-spatial one-shots are not tracked - they end on their own.
|
|
19
|
+
// Time source. Real implementation uses performance.now(); tests can
|
|
20
|
+
// inject a deterministic clock by overriding this through the public
|
|
21
|
+
// constructor seam later. v1 uses the global clock for simplicity.
|
|
22
|
+
function nowMs() {
|
|
23
|
+
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
|
|
24
|
+
return performance.now();
|
|
25
|
+
}
|
|
26
|
+
return Date.now();
|
|
27
|
+
}
|
|
28
|
+
export class CueCatalog {
|
|
29
|
+
audioBus;
|
|
30
|
+
spatialBus;
|
|
31
|
+
cache;
|
|
32
|
+
cues = new Map();
|
|
33
|
+
cooldowns = new Map();
|
|
34
|
+
// Live spatial source handles per cue name. Cleared on stopAll(name).
|
|
35
|
+
// Non-spatial one-shots are NOT tracked - they end naturally.
|
|
36
|
+
liveHandles = new Map();
|
|
37
|
+
constructor(audioBus, spatialBus, cache) {
|
|
38
|
+
this.audioBus = audioBus;
|
|
39
|
+
this.spatialBus = spatialBus;
|
|
40
|
+
this.cache = cache;
|
|
41
|
+
}
|
|
42
|
+
static create(audioBus, spatialBus, cache) {
|
|
43
|
+
return new CueCatalog(audioBus, spatialBus, cache);
|
|
44
|
+
}
|
|
45
|
+
register(name, def) {
|
|
46
|
+
this.cues.set(name, def);
|
|
47
|
+
// Re-registering resets cooldown state so a fresh definition starts
|
|
48
|
+
// from a clean slate (avoids stale lastPlay timestamps on overwrite).
|
|
49
|
+
this.cooldowns.set(name, { lastPlayMs: -Infinity });
|
|
50
|
+
}
|
|
51
|
+
unregister(name) {
|
|
52
|
+
this.cues.delete(name);
|
|
53
|
+
this.cooldowns.delete(name);
|
|
54
|
+
// Stop any live handles for this cue so they don't outlive the
|
|
55
|
+
// definition.
|
|
56
|
+
this.stopAll(name);
|
|
57
|
+
this.liveHandles.delete(name);
|
|
58
|
+
}
|
|
59
|
+
has(name) {
|
|
60
|
+
return this.cues.has(name);
|
|
61
|
+
}
|
|
62
|
+
list() {
|
|
63
|
+
return Array.from(this.cues.keys());
|
|
64
|
+
}
|
|
65
|
+
// Play a registered cue. Spatial cues require x + y in options.
|
|
66
|
+
// Returns a handle for spatial cues; null for non-spatial cues, for
|
|
67
|
+
// unknown cues, for cues whose asset is not in the cache, or when
|
|
68
|
+
// cooldown is active.
|
|
69
|
+
play(name, options) {
|
|
70
|
+
var def = this.cues.get(name);
|
|
71
|
+
if (!def)
|
|
72
|
+
return null;
|
|
73
|
+
// Cooldown gate. Suppress play and leave lastPlay untouched if too
|
|
74
|
+
// soon after the prior successful play.
|
|
75
|
+
if (def.cooldownMs !== undefined && def.cooldownMs > 0) {
|
|
76
|
+
var entry = this.cooldowns.get(name) ?? { lastPlayMs: -Infinity };
|
|
77
|
+
var t = nowMs();
|
|
78
|
+
if (t - entry.lastPlayMs < def.cooldownMs) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Asset must be in cache.
|
|
83
|
+
var buffer = this.cache.get(def.asset);
|
|
84
|
+
if (!buffer)
|
|
85
|
+
return null;
|
|
86
|
+
// Merge defaults under the explicit options. The intent is "options
|
|
87
|
+
// override defaults"; we copy each key path explicitly for the
|
|
88
|
+
// strict-mode-friendly subset the spec uses.
|
|
89
|
+
var merged = {};
|
|
90
|
+
if (def.defaults) {
|
|
91
|
+
for (var k in def.defaults) {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
|
+
merged[k] = def.defaults[k];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (options) {
|
|
97
|
+
for (var k2 in options) {
|
|
98
|
+
var v = options[k2];
|
|
99
|
+
if (v !== undefined) {
|
|
100
|
+
merged[k2] = v;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
var spatial = def.spatial === true;
|
|
105
|
+
if (spatial) {
|
|
106
|
+
if (typeof merged.x !== 'number' || typeof merged.y !== 'number') {
|
|
107
|
+
// Spatial cue requires position. Drop silently rather than throwing -
|
|
108
|
+
// matches "Returns null for ... failure" contract.
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// Build a complete PositionalPlayOptions. We carry all merged
|
|
112
|
+
// fields into the spatial bus call.
|
|
113
|
+
var posOpts = {
|
|
114
|
+
x: merged.x,
|
|
115
|
+
y: merged.y,
|
|
116
|
+
};
|
|
117
|
+
if (typeof merged.z === 'number')
|
|
118
|
+
posOpts.z = merged.z;
|
|
119
|
+
if (merged.distanceModel !== undefined)
|
|
120
|
+
posOpts.distanceModel = merged.distanceModel;
|
|
121
|
+
if (typeof merged.refDistance === 'number')
|
|
122
|
+
posOpts.refDistance = merged.refDistance;
|
|
123
|
+
if (typeof merged.maxDistance === 'number')
|
|
124
|
+
posOpts.maxDistance = merged.maxDistance;
|
|
125
|
+
if (typeof merged.rolloffFactor === 'number')
|
|
126
|
+
posOpts.rolloffFactor = merged.rolloffFactor;
|
|
127
|
+
if (typeof merged.gain === 'number')
|
|
128
|
+
posOpts.gain = merged.gain;
|
|
129
|
+
if (typeof merged.rate === 'number')
|
|
130
|
+
posOpts.rate = merged.rate;
|
|
131
|
+
if (typeof merged.loop === 'boolean')
|
|
132
|
+
posOpts.loop = merged.loop;
|
|
133
|
+
var handle = this.spatialBus.playPositional(buffer, posOpts);
|
|
134
|
+
if (!handle)
|
|
135
|
+
return null;
|
|
136
|
+
// Track for stopAll. We DO NOT auto-cleanup non-loop one-shots
|
|
137
|
+
// here - the SpatialAudioBus is the source of truth on lifetime.
|
|
138
|
+
// stopAll is the primary cleanup path; if a cue is a one-shot the
|
|
139
|
+
// tracked handle just becomes a no-op stop later.
|
|
140
|
+
var setForName = this.liveHandles.get(name);
|
|
141
|
+
if (!setForName) {
|
|
142
|
+
setForName = new Set();
|
|
143
|
+
this.liveHandles.set(name, setForName);
|
|
144
|
+
}
|
|
145
|
+
setForName.add(handle);
|
|
146
|
+
this.markPlayed(name);
|
|
147
|
+
return handle;
|
|
148
|
+
}
|
|
149
|
+
// Non-spatial: route through AudioBus.playOneShot on the named bus.
|
|
150
|
+
var busName = def.bus ?? 'sfx';
|
|
151
|
+
var oneShotOpts = {};
|
|
152
|
+
if (typeof merged.rate === 'number')
|
|
153
|
+
oneShotOpts.rate = merged.rate;
|
|
154
|
+
if (typeof merged.gain === 'number')
|
|
155
|
+
oneShotOpts.gain = merged.gain;
|
|
156
|
+
this.audioBus.playOneShot(busName, buffer, oneShotOpts);
|
|
157
|
+
this.markPlayed(name);
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
// Stop all live spatial sources for this cue. Non-spatial one-shots
|
|
161
|
+
// are not tracked; this is a no-op for cues that have never produced
|
|
162
|
+
// a spatial handle.
|
|
163
|
+
stopAll(name) {
|
|
164
|
+
var setForName = this.liveHandles.get(name);
|
|
165
|
+
if (!setForName)
|
|
166
|
+
return;
|
|
167
|
+
setForName.forEach(function (h) {
|
|
168
|
+
try {
|
|
169
|
+
h.stop();
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
// Best-effort cleanup; never let a misbehaving handle take down
|
|
173
|
+
// sibling cues.
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
setForName.clear();
|
|
177
|
+
}
|
|
178
|
+
markPlayed(name) {
|
|
179
|
+
this.cooldowns.set(name, { lastPlayMs: nowMs() });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// Resource key for the world's resource registry. Engine consumers
|
|
183
|
+
// register a CueCatalog instance under this key alongside the audio
|
|
184
|
+
// bus + spatial bus + cache.
|
|
185
|
+
export const RESOURCE_CUE_CATALOG = 'cue_catalog';
|
|
186
|
+
//# sourceMappingURL=cue-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cue-catalog.js","sourceRoot":"","sources":["../../src/audio/cue-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { AudioBus } from './audio-bus.js';
|
|
2
|
+
import type { AudioAssetCache } from './audio-asset-cache.js';
|
|
3
|
+
export declare class MusicDirector {
|
|
4
|
+
private audioBus;
|
|
5
|
+
private cache;
|
|
6
|
+
private current;
|
|
7
|
+
private constructor();
|
|
8
|
+
static create(audioBus: AudioBus, cache: AudioAssetCache): MusicDirector;
|
|
9
|
+
playMusic(name: string, fadeInMs?: number): void;
|
|
10
|
+
stopMusic(fadeOutMs?: number): Promise<void>;
|
|
11
|
+
crossfadeMusic(name: string, fadeMs?: number): void;
|
|
12
|
+
currentMusic(): string | null;
|
|
13
|
+
private startTrack;
|
|
14
|
+
private fadeOutAndStop;
|
|
15
|
+
private hardStop;
|
|
16
|
+
}
|
|
17
|
+
export declare const RESOURCE_MUSIC_DIRECTOR = "music_director";
|
|
18
|
+
//# sourceMappingURL=music-director.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"music-director.d.ts","sourceRoot":"","sources":["../../src/audio/music-director.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAY9D,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,OAAO,CAA4B;IAE3C,OAAO;IAKP,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,aAAa;IAOxE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAgBhD,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa5C,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAwBnD,YAAY,IAAI,MAAM,GAAG,IAAI;IAM7B,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,cAAc;IAoCtB,OAAO,CAAC,QAAQ;CAmBjB;AAKD,eAAO,MAAM,uBAAuB,mBAAmB,CAAC"}
|