@yali-engine/audio 0.0.0-stage → 0.4.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.d.ts +132 -0
- package/dist/audio.js +465 -0
- package/dist/audio.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +174 -0
- package/dist/plugin.js +530 -0
- package/dist/plugin.js.map +1 -0
- package/dist/space.d.ts +52 -0
- package/dist/space.js +133 -0
- package/dist/space.js.map +1 -0
- package/package.json +24 -3
- package/README.md +0 -3
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { type DistanceModel, type ListenerPose } from './space.ts';
|
|
2
|
+
/** A clip id (`AudioClips.add` / `load`); 0 is none. */
|
|
3
|
+
export type ClipId = number;
|
|
4
|
+
/** What one voice plays and how (the `audio.Source` fields, decoded). */
|
|
5
|
+
export interface VoiceParams {
|
|
6
|
+
clip: ClipId;
|
|
7
|
+
volume: number;
|
|
8
|
+
pitch: number;
|
|
9
|
+
loop: boolean;
|
|
10
|
+
/** Placed in 3D (HRTF); else heard as it is, at `volume`. */
|
|
11
|
+
spatial: boolean;
|
|
12
|
+
distanceModel: DistanceModel;
|
|
13
|
+
refDistance: number;
|
|
14
|
+
maxDistance: number;
|
|
15
|
+
rolloff: number;
|
|
16
|
+
/** Cone angles in degrees (360: none) and the gain outside the outer one. */
|
|
17
|
+
coneInner: number;
|
|
18
|
+
coneOuter: number;
|
|
19
|
+
coneOuterGain: number;
|
|
20
|
+
/** How occluded it is (ADR-0127): 0 clear to 1 behind a wall, muffled and quieter (with `AudioMixer.occlusion`). */
|
|
21
|
+
occlusion?: number;
|
|
22
|
+
}
|
|
23
|
+
/** `audio.Source.distanceModel`'s number for each model. */
|
|
24
|
+
export declare const DISTANCE_MODEL: Readonly<Record<DistanceModel, number>>;
|
|
25
|
+
/** The model a `distanceModel` number names (inverse for anything else). */
|
|
26
|
+
export declare function modelOf(n: number): DistanceModel;
|
|
27
|
+
/**
|
|
28
|
+
* Decoded clips, by id. Decoding needs an AudioContext (any: an OfflineAudioContext decodes too).
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const step = await audio.clips.load('sounds/step.ogg');
|
|
32
|
+
*/
|
|
33
|
+
export declare class AudioClips {
|
|
34
|
+
#private;
|
|
35
|
+
readonly context: BaseAudioContext;
|
|
36
|
+
constructor(context: BaseAudioContext);
|
|
37
|
+
/** Stores a decoded buffer; returns its id. */
|
|
38
|
+
add(buffer: AudioBuffer): ClipId;
|
|
39
|
+
/**
|
|
40
|
+
* A clip streamed from `url` instead of decoded whole (ADR-0116): music and long ambiences, which
|
|
41
|
+
* decoded would hold minutes of samples. Each voice plays it through its own `<audio>` element,
|
|
42
|
+
* so it starts before it has all downloaded. Nothing is fetched until a voice plays it.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* const music = audio.clips.stream('music/theme.ogg');
|
|
46
|
+
* world.insert(e, audio.Source, audioSource(music, { loop: true, spatial: false }));
|
|
47
|
+
*/
|
|
48
|
+
stream(url: string): ClipId;
|
|
49
|
+
/** The URL clip `id` streams from (`stream`), or undefined for a decoded one. */
|
|
50
|
+
streamUrl(id: ClipId): string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* The clip for asset path `path` decoded from `bytes` (the asset loader, ADR-0116): the same id
|
|
53
|
+
* every time for one path, so a hot reload swaps the sound under it (voices pick it up when they
|
|
54
|
+
* next start).
|
|
55
|
+
*/
|
|
56
|
+
decodeFor(path: string, bytes: ArrayBuffer): Promise<ClipId>;
|
|
57
|
+
/** Decodes encoded bytes (wav, ogg, mp3, flac, as the browser decodes them); returns the id. */
|
|
58
|
+
decode(bytes: ArrayBuffer): Promise<ClipId>;
|
|
59
|
+
/** Fetches and decodes `url` once (the same promise for the same URL). */
|
|
60
|
+
load(url: string): Promise<ClipId>;
|
|
61
|
+
/** Clip `id`'s buffer, or undefined (none, or a streamed clip). */
|
|
62
|
+
get(id: ClipId): AudioBuffer | undefined;
|
|
63
|
+
get count(): number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The voices: one Web Audio chain per playing source (buffer source → gain → HRTF panner →
|
|
67
|
+
* master), keyed by whatever the caller uses (the plugin: the entity). The listener stays at the
|
|
68
|
+
* origin; sources are placed relative to it each frame (f64 subtraction first, ADR-0096).
|
|
69
|
+
*/
|
|
70
|
+
export declare class AudioMixer {
|
|
71
|
+
#private;
|
|
72
|
+
readonly context: BaseAudioContext;
|
|
73
|
+
readonly clips: AudioClips;
|
|
74
|
+
readonly master: GainNode;
|
|
75
|
+
/** What every spatial voice also sends to: the reverb zones' input (ADR-0116). */
|
|
76
|
+
readonly send: GainNode;
|
|
77
|
+
/** Voices started since the mixer was made. */
|
|
78
|
+
started: number;
|
|
79
|
+
constructor(context: BaseAudioContext, clips?: AudioClips, element?: (url: string) => HTMLMediaElement);
|
|
80
|
+
/** Starts the frame's zones: those not `zone`d before `endZones` are let go. */
|
|
81
|
+
beginZones(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Zone `key` this frame: a reverb of `decay` seconds heard at `wet × weight` (the listener's share
|
|
84
|
+
* of it, `zoneWeight`). Its impulse response is made once per decay (again when it changes by 5%).
|
|
85
|
+
*/
|
|
86
|
+
zone(key: number, decay: number, wet: number, weight: number): void;
|
|
87
|
+
/** Ends the frame's zones: lets go of the zones no entity asked for. */
|
|
88
|
+
endZones(): void;
|
|
89
|
+
/** The zones and how loud each is now (wet × the listener's weight), for tools and tests. */
|
|
90
|
+
get zones(): {
|
|
91
|
+
decay: number;
|
|
92
|
+
level: number;
|
|
93
|
+
}[];
|
|
94
|
+
/** Master volume (linear). */
|
|
95
|
+
get volume(): number;
|
|
96
|
+
set volume(v: number);
|
|
97
|
+
/** Starts a frame: voices not `update`d before `endFrame` stop. */
|
|
98
|
+
beginFrame(listener: ListenerPose): void;
|
|
99
|
+
/**
|
|
100
|
+
* Voice `key` this frame: playing `p` at `position` (f64, world) facing `forward`. Starts it if
|
|
101
|
+
* needed (or restarts it on a new clip); returns false when it has finished (a clip that does
|
|
102
|
+
* not loop, played to its end).
|
|
103
|
+
*/
|
|
104
|
+
update(key: number, p: VoiceParams, position: ArrayLike<number>, forward: readonly number[]): boolean;
|
|
105
|
+
/** Ends the frame: stops the voices no source asked for. */
|
|
106
|
+
endFrame(): void;
|
|
107
|
+
/** Stops voice `key` now (a source set to stop). */
|
|
108
|
+
stop(key: number): void;
|
|
109
|
+
/** Playing voices (an ended one stays until its source lets it go). */
|
|
110
|
+
get voices(): number;
|
|
111
|
+
/**
|
|
112
|
+
* Early reflection `i` (made on first use): every spatial voice's send heard again after `delay`
|
|
113
|
+
* seconds at `gain`, panned to `pan` (−1 left … 1 right), as the wall it bounced off places it. The
|
|
114
|
+
* plugin sets one per direction it casts from the listener; `gain` 0 silences one. Gliding, so a
|
|
115
|
+
* moving listener does not click.
|
|
116
|
+
*/
|
|
117
|
+
reflection(i: number, delay: number, gain: number, pan: number): void;
|
|
118
|
+
/** The early reflections' gains now (tools and tests). */
|
|
119
|
+
get reflections(): readonly number[];
|
|
120
|
+
/** Stops every voice. */
|
|
121
|
+
dispose(): void;
|
|
122
|
+
/**
|
|
123
|
+
* Occlusion (ADR-0127): spatial voices started from now on get a low-pass between their gain and
|
|
124
|
+
* panner, and `VoiceParams.occlusion` muffles them towards `occlusionHz` and lowers them to
|
|
125
|
+
* `occlusionGain` of their volume. Off: the chain is as before.
|
|
126
|
+
*/
|
|
127
|
+
occlusion: boolean;
|
|
128
|
+
/** The low-pass cutoff of a fully occluded voice (Hz). */
|
|
129
|
+
occlusionHz: number;
|
|
130
|
+
/** The share of its volume a fully occluded voice keeps. */
|
|
131
|
+
occlusionGain: number;
|
|
132
|
+
}
|
package/dist/audio.js
ADDED
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
import { YaliError } from '@yali-engine/core';
|
|
2
|
+
import { impulseResponse, relative } from "./space.js";
|
|
3
|
+
const MODELS = ['linear', 'inverse', 'exponential'];
|
|
4
|
+
/** `audio.Source.distanceModel`'s number for each model. */
|
|
5
|
+
export const DISTANCE_MODEL = { linear: 0, inverse: 1, exponential: 2 };
|
|
6
|
+
/** The model a `distanceModel` number names (inverse for anything else). */
|
|
7
|
+
export function modelOf(n) {
|
|
8
|
+
return MODELS[n] ?? 'inverse';
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Decoded clips, by id. Decoding needs an AudioContext (any: an OfflineAudioContext decodes too).
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const step = await audio.clips.load('sounds/step.ogg');
|
|
15
|
+
*/
|
|
16
|
+
export class AudioClips {
|
|
17
|
+
context;
|
|
18
|
+
/** Per id: its decoded buffer, or the URL it streams from (ADR-0116). */
|
|
19
|
+
#buffers = [];
|
|
20
|
+
#streams = [];
|
|
21
|
+
#byUrl = new Map();
|
|
22
|
+
#byPath = new Map();
|
|
23
|
+
constructor(context) {
|
|
24
|
+
this.context = context;
|
|
25
|
+
}
|
|
26
|
+
/** Stores a decoded buffer; returns its id. */
|
|
27
|
+
add(buffer) {
|
|
28
|
+
this.#streams.push(null);
|
|
29
|
+
return this.#buffers.push(buffer);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A clip streamed from `url` instead of decoded whole (ADR-0116): music and long ambiences, which
|
|
33
|
+
* decoded would hold minutes of samples. Each voice plays it through its own `<audio>` element,
|
|
34
|
+
* so it starts before it has all downloaded. Nothing is fetched until a voice plays it.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const music = audio.clips.stream('music/theme.ogg');
|
|
38
|
+
* world.insert(e, audio.Source, audioSource(music, { loop: true, spatial: false }));
|
|
39
|
+
*/
|
|
40
|
+
stream(url) {
|
|
41
|
+
this.#buffers.push(null);
|
|
42
|
+
return this.#streams.push(url);
|
|
43
|
+
}
|
|
44
|
+
/** The URL clip `id` streams from (`stream`), or undefined for a decoded one. */
|
|
45
|
+
streamUrl(id) {
|
|
46
|
+
return this.#streams[id - 1] ?? undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The clip for asset path `path` decoded from `bytes` (the asset loader, ADR-0116): the same id
|
|
50
|
+
* every time for one path, so a hot reload swaps the sound under it (voices pick it up when they
|
|
51
|
+
* next start).
|
|
52
|
+
*/
|
|
53
|
+
async decodeFor(path, bytes) {
|
|
54
|
+
const id = this.#byPath.get(path);
|
|
55
|
+
if (id === undefined) {
|
|
56
|
+
const fresh = await this.decode(bytes);
|
|
57
|
+
this.#byPath.set(path, fresh);
|
|
58
|
+
return fresh;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
this.#buffers[id - 1] = await this.context.decodeAudioData(bytes.slice(0));
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
throw new YaliError('AUDIO_DECODE', `${path} could not be decoded: ${e instanceof Error ? e.message : String(e)}`, 'Use a format this browser decodes (wav, ogg, mp3, flac).');
|
|
65
|
+
}
|
|
66
|
+
return id;
|
|
67
|
+
}
|
|
68
|
+
/** Decodes encoded bytes (wav, ogg, mp3, flac, as the browser decodes them); returns the id. */
|
|
69
|
+
async decode(bytes) {
|
|
70
|
+
try {
|
|
71
|
+
return this.add(await this.context.decodeAudioData(bytes.slice(0)));
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
throw new YaliError('AUDIO_DECODE', `The audio data could not be decoded: ${e instanceof Error ? e.message : String(e)}`, 'Use a format this browser decodes (wav, ogg, mp3, flac).');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Fetches and decodes `url` once (the same promise for the same URL). */
|
|
78
|
+
load(url) {
|
|
79
|
+
let p = this.#byUrl.get(url);
|
|
80
|
+
if (!p) {
|
|
81
|
+
p = fetch(url).then(async (r) => {
|
|
82
|
+
if (!r.ok)
|
|
83
|
+
throw new YaliError('AUDIO_FETCH', `${url}: HTTP ${r.status}.`, 'Check the URL and that the server serves it.');
|
|
84
|
+
return this.decode(await r.arrayBuffer());
|
|
85
|
+
});
|
|
86
|
+
p.catch(() => this.#byUrl.delete(url));
|
|
87
|
+
this.#byUrl.set(url, p);
|
|
88
|
+
}
|
|
89
|
+
return p;
|
|
90
|
+
}
|
|
91
|
+
/** Clip `id`'s buffer, or undefined (none, or a streamed clip). */
|
|
92
|
+
get(id) {
|
|
93
|
+
return this.#buffers[id - 1] ?? undefined;
|
|
94
|
+
}
|
|
95
|
+
get count() {
|
|
96
|
+
return this.#buffers.length;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The voices: one Web Audio chain per playing source (buffer source → gain → HRTF panner →
|
|
101
|
+
* master), keyed by whatever the caller uses (the plugin: the entity). The listener stays at the
|
|
102
|
+
* origin; sources are placed relative to it each frame (f64 subtraction first, ADR-0096).
|
|
103
|
+
*/
|
|
104
|
+
export class AudioMixer {
|
|
105
|
+
context;
|
|
106
|
+
clips;
|
|
107
|
+
master;
|
|
108
|
+
/** What every spatial voice also sends to: the reverb zones' input (ADR-0116). */
|
|
109
|
+
send;
|
|
110
|
+
#voices = new Map();
|
|
111
|
+
#seen = new Set();
|
|
112
|
+
#rel = [0, 0, 0];
|
|
113
|
+
/** Makes a streamed clip's element (tests replace it; the default is `new Audio(url)`). */
|
|
114
|
+
#element;
|
|
115
|
+
#zones = new Map();
|
|
116
|
+
#zonesSeen = new Set();
|
|
117
|
+
/** Voices started since the mixer was made. */
|
|
118
|
+
started = 0;
|
|
119
|
+
constructor(context, clips = new AudioClips(context), element) {
|
|
120
|
+
this.context = context;
|
|
121
|
+
this.clips = clips;
|
|
122
|
+
this.master = context.createGain();
|
|
123
|
+
this.master.connect(context.destination);
|
|
124
|
+
this.send = context.createGain();
|
|
125
|
+
this.#element =
|
|
126
|
+
element ??
|
|
127
|
+
((url) => {
|
|
128
|
+
const el = new Audio(url);
|
|
129
|
+
el.crossOrigin = 'anonymous';
|
|
130
|
+
el.preload = 'auto';
|
|
131
|
+
return el;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// ── Reverb zones (ADR-0116) ─────────────────────────────────────────────────────────────────
|
|
135
|
+
/** Starts the frame's zones: those not `zone`d before `endZones` are let go. */
|
|
136
|
+
beginZones() {
|
|
137
|
+
this.#zonesSeen.clear();
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Zone `key` this frame: a reverb of `decay` seconds heard at `wet × weight` (the listener's share
|
|
141
|
+
* of it, `zoneWeight`). Its impulse response is made once per decay (again when it changes by 5%).
|
|
142
|
+
*/
|
|
143
|
+
zone(key, decay, wet, weight) {
|
|
144
|
+
this.#zonesSeen.add(key);
|
|
145
|
+
let z = this.#zones.get(key);
|
|
146
|
+
const d = Math.min(6, Math.max(0.05, decay));
|
|
147
|
+
if (!z || Math.abs(z.decay - d) > z.decay * 0.05) {
|
|
148
|
+
if (z)
|
|
149
|
+
this.#dropZone(z);
|
|
150
|
+
z = this.#makeZone(d);
|
|
151
|
+
this.#zones.set(key, z);
|
|
152
|
+
}
|
|
153
|
+
const g = Math.max(0, wet) * Math.min(1, Math.max(0, weight));
|
|
154
|
+
// Glide, so walking through a zone's edge does not click.
|
|
155
|
+
if (g !== z.level)
|
|
156
|
+
z.gain.gain.setTargetAtTime(g, this.context.currentTime, 0.05);
|
|
157
|
+
z.level = g;
|
|
158
|
+
}
|
|
159
|
+
/** Ends the frame's zones: lets go of the zones no entity asked for. */
|
|
160
|
+
endZones() {
|
|
161
|
+
for (const [key, z] of this.#zones) {
|
|
162
|
+
if (!this.#zonesSeen.has(key)) {
|
|
163
|
+
this.#dropZone(z);
|
|
164
|
+
this.#zones.delete(key);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/** The zones and how loud each is now (wet × the listener's weight), for tools and tests. */
|
|
169
|
+
get zones() {
|
|
170
|
+
return [...this.#zones.values()].map((z) => ({ decay: z.decay, level: z.level }));
|
|
171
|
+
}
|
|
172
|
+
#makeZone(decay) {
|
|
173
|
+
const c = this.context;
|
|
174
|
+
const rate = c.sampleRate;
|
|
175
|
+
const buffer = c.createBuffer(2, Math.max(1, Math.round(decay * rate)), rate);
|
|
176
|
+
impulseResponse(buffer.getChannelData(0), buffer.getChannelData(1), rate, decay, Math.round(decay * 1000));
|
|
177
|
+
const convolver = c.createConvolver();
|
|
178
|
+
convolver.normalize = true;
|
|
179
|
+
convolver.buffer = buffer;
|
|
180
|
+
const gain = c.createGain();
|
|
181
|
+
gain.gain.value = 0;
|
|
182
|
+
this.send.connect(convolver);
|
|
183
|
+
convolver.connect(gain);
|
|
184
|
+
gain.connect(this.master);
|
|
185
|
+
return { decay, convolver, gain, level: 0 };
|
|
186
|
+
}
|
|
187
|
+
#dropZone(z) {
|
|
188
|
+
this.send.disconnect(z.convolver);
|
|
189
|
+
z.convolver.disconnect();
|
|
190
|
+
z.gain.disconnect();
|
|
191
|
+
}
|
|
192
|
+
/** Master volume (linear). */
|
|
193
|
+
get volume() {
|
|
194
|
+
return this.master.gain.value;
|
|
195
|
+
}
|
|
196
|
+
set volume(v) {
|
|
197
|
+
this.master.gain.value = Math.max(0, v);
|
|
198
|
+
}
|
|
199
|
+
/** Starts a frame: voices not `update`d before `endFrame` stop. */
|
|
200
|
+
beginFrame(listener) {
|
|
201
|
+
this.#seen.clear();
|
|
202
|
+
const l = this.context.listener;
|
|
203
|
+
const [fx, fy, fz] = listener.forward;
|
|
204
|
+
const [ux, uy, uz] = listener.up;
|
|
205
|
+
if (l.forwardX) {
|
|
206
|
+
l.positionX.value = 0;
|
|
207
|
+
l.positionY.value = 0;
|
|
208
|
+
l.positionZ.value = 0;
|
|
209
|
+
l.forwardX.value = fx;
|
|
210
|
+
l.forwardY.value = fy;
|
|
211
|
+
l.forwardZ.value = fz;
|
|
212
|
+
l.upX.value = ux;
|
|
213
|
+
l.upY.value = uy;
|
|
214
|
+
l.upZ.value = uz;
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
l.setOrientation(fx, fy, fz, ux, uy, uz);
|
|
218
|
+
}
|
|
219
|
+
this.#listener = listener.position;
|
|
220
|
+
}
|
|
221
|
+
#listener = [0, 0, 0];
|
|
222
|
+
/**
|
|
223
|
+
* Voice `key` this frame: playing `p` at `position` (f64, world) facing `forward`. Starts it if
|
|
224
|
+
* needed (or restarts it on a new clip); returns false when it has finished (a clip that does
|
|
225
|
+
* not loop, played to its end).
|
|
226
|
+
*/
|
|
227
|
+
update(key, p, position, forward) {
|
|
228
|
+
this.#seen.add(key);
|
|
229
|
+
let v = this.#voices.get(key);
|
|
230
|
+
if (v && (v.clip !== p.clip || v.loop !== p.loop || !!v.panner !== p.spatial)) {
|
|
231
|
+
this.#stop(v);
|
|
232
|
+
v = undefined;
|
|
233
|
+
}
|
|
234
|
+
if (!v) {
|
|
235
|
+
const buffer = this.clips.get(p.clip);
|
|
236
|
+
const url = buffer ? undefined : this.clips.streamUrl(p.clip);
|
|
237
|
+
if (!buffer && !url)
|
|
238
|
+
return true;
|
|
239
|
+
const started = this.#start(key, buffer ?? url, p);
|
|
240
|
+
if (!started)
|
|
241
|
+
return true;
|
|
242
|
+
v = started;
|
|
243
|
+
}
|
|
244
|
+
if (v.ended)
|
|
245
|
+
return false;
|
|
246
|
+
// Occluded (ADR-0127): muffled (the low-pass falls towards `occlusionHz`) and quieter, gliding so a wall does not click.
|
|
247
|
+
const occ = v.filter ? Math.min(1, Math.max(0, p.occlusion ?? 0)) : 0;
|
|
248
|
+
v.gain.gain.value = Math.max(0, p.volume) * (1 - (1 - this.occlusionGain) * occ);
|
|
249
|
+
if (v.filter)
|
|
250
|
+
v.filter.frequency.setTargetAtTime(OPEN_HZ * (this.occlusionHz / OPEN_HZ) ** occ, this.context.currentTime, 0.03);
|
|
251
|
+
const rate = p.pitch > 0 ? p.pitch : 1;
|
|
252
|
+
if (v.element)
|
|
253
|
+
v.element.playbackRate = rate;
|
|
254
|
+
else
|
|
255
|
+
v.node.playbackRate.value = rate;
|
|
256
|
+
const pn = v.panner;
|
|
257
|
+
if (pn) {
|
|
258
|
+
const r = relative(position, this.#listener, this.#rel);
|
|
259
|
+
if (pn.positionX) {
|
|
260
|
+
pn.positionX.value = r[0];
|
|
261
|
+
pn.positionY.value = r[1];
|
|
262
|
+
pn.positionZ.value = r[2];
|
|
263
|
+
pn.orientationX.value = forward[0];
|
|
264
|
+
pn.orientationY.value = forward[1];
|
|
265
|
+
pn.orientationZ.value = forward[2];
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
pn.setPosition(r[0], r[1], r[2]);
|
|
269
|
+
}
|
|
270
|
+
pn.distanceModel = p.distanceModel;
|
|
271
|
+
pn.refDistance = Math.max(1e-3, p.refDistance);
|
|
272
|
+
pn.maxDistance = Math.max(pn.refDistance, p.maxDistance);
|
|
273
|
+
pn.rolloffFactor = Math.max(0, p.rolloff);
|
|
274
|
+
pn.coneInnerAngle = p.coneInner;
|
|
275
|
+
pn.coneOuterAngle = p.coneOuter;
|
|
276
|
+
pn.coneOuterGain = p.coneOuterGain;
|
|
277
|
+
}
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
/** Ends the frame: stops the voices no source asked for. */
|
|
281
|
+
endFrame() {
|
|
282
|
+
for (const [key, v] of this.#voices) {
|
|
283
|
+
if (!this.#seen.has(key)) {
|
|
284
|
+
this.#stop(v);
|
|
285
|
+
this.#voices.delete(key);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/** Stops voice `key` now (a source set to stop). */
|
|
290
|
+
stop(key) {
|
|
291
|
+
const v = this.#voices.get(key);
|
|
292
|
+
if (v) {
|
|
293
|
+
this.#stop(v);
|
|
294
|
+
this.#voices.delete(key);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/** Playing voices (an ended one stays until its source lets it go). */
|
|
298
|
+
get voices() {
|
|
299
|
+
let n = 0;
|
|
300
|
+
for (const v of this.#voices.values())
|
|
301
|
+
if (!v.ended)
|
|
302
|
+
n++;
|
|
303
|
+
return n;
|
|
304
|
+
}
|
|
305
|
+
// ── Early reflections (ADR-0130) ────────────────────────────────────────────────────────────
|
|
306
|
+
#taps = [];
|
|
307
|
+
/**
|
|
308
|
+
* Early reflection `i` (made on first use): every spatial voice's send heard again after `delay`
|
|
309
|
+
* seconds at `gain`, panned to `pan` (−1 left … 1 right), as the wall it bounced off places it. The
|
|
310
|
+
* plugin sets one per direction it casts from the listener; `gain` 0 silences one. Gliding, so a
|
|
311
|
+
* moving listener does not click.
|
|
312
|
+
*/
|
|
313
|
+
reflection(i, delay, gain, pan) {
|
|
314
|
+
const c = this.context;
|
|
315
|
+
while (this.#taps.length <= i) {
|
|
316
|
+
const tap = { delay: c.createDelay(MAX_REFLECTION_DELAY), gain: c.createGain(), pan: c.createStereoPanner(), level: 0 };
|
|
317
|
+
tap.gain.gain.value = 0;
|
|
318
|
+
this.send.connect(tap.delay);
|
|
319
|
+
tap.delay.connect(tap.gain);
|
|
320
|
+
tap.gain.connect(tap.pan);
|
|
321
|
+
tap.pan.connect(this.master);
|
|
322
|
+
this.#taps.push(tap);
|
|
323
|
+
}
|
|
324
|
+
const t = this.#taps[i];
|
|
325
|
+
const now = c.currentTime;
|
|
326
|
+
const g = Math.max(0, gain);
|
|
327
|
+
t.delay.delayTime.setTargetAtTime(Math.min(MAX_REFLECTION_DELAY, Math.max(0, delay)), now, 0.05);
|
|
328
|
+
if (g !== t.level)
|
|
329
|
+
t.gain.gain.setTargetAtTime(g, now, 0.05);
|
|
330
|
+
t.pan.pan.setTargetAtTime(Math.min(1, Math.max(-1, pan)), now, 0.05);
|
|
331
|
+
t.level = g;
|
|
332
|
+
}
|
|
333
|
+
/** The early reflections' gains now (tools and tests). */
|
|
334
|
+
get reflections() {
|
|
335
|
+
return this.#taps.map((t) => t.level);
|
|
336
|
+
}
|
|
337
|
+
/** Stops every voice. */
|
|
338
|
+
dispose() {
|
|
339
|
+
for (const v of this.#voices.values())
|
|
340
|
+
this.#stop(v);
|
|
341
|
+
this.#voices.clear();
|
|
342
|
+
for (const z of this.#zones.values())
|
|
343
|
+
this.#dropZone(z);
|
|
344
|
+
this.#zones.clear();
|
|
345
|
+
for (const t of this.#taps) {
|
|
346
|
+
t.delay.disconnect();
|
|
347
|
+
t.gain.disconnect();
|
|
348
|
+
t.pan.disconnect();
|
|
349
|
+
}
|
|
350
|
+
this.#taps.length = 0;
|
|
351
|
+
this.send.disconnect();
|
|
352
|
+
this.master.disconnect();
|
|
353
|
+
}
|
|
354
|
+
#streamWarned = false;
|
|
355
|
+
/** A voice over a decoded buffer, or a streamed clip's URL (null: this context cannot stream). */
|
|
356
|
+
#start(key, source, p) {
|
|
357
|
+
const c = this.context;
|
|
358
|
+
let node;
|
|
359
|
+
let element = null;
|
|
360
|
+
if (typeof source === 'string') {
|
|
361
|
+
const ctx = c;
|
|
362
|
+
if (typeof ctx.createMediaElementSource !== 'function') {
|
|
363
|
+
if (!this.#streamWarned) {
|
|
364
|
+
this.#streamWarned = true;
|
|
365
|
+
console.warn('[yali] audio: this AudioContext cannot stream (no createMediaElementSource, e.g. an OfflineAudioContext); streamed clips stay silent. Decode them instead (clips.load).');
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
element = this.#element(source);
|
|
370
|
+
element.loop = p.loop;
|
|
371
|
+
element.preservesPitch = false;
|
|
372
|
+
node = ctx.createMediaElementSource(element);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
const b = c.createBufferSource();
|
|
376
|
+
b.buffer = source;
|
|
377
|
+
b.loop = p.loop;
|
|
378
|
+
node = b;
|
|
379
|
+
}
|
|
380
|
+
const gain = c.createGain();
|
|
381
|
+
node.connect(gain);
|
|
382
|
+
let panner = null;
|
|
383
|
+
let filter = null;
|
|
384
|
+
if (p.spatial) {
|
|
385
|
+
panner = c.createPanner();
|
|
386
|
+
panner.panningModel = 'HRTF';
|
|
387
|
+
if (this.occlusion) {
|
|
388
|
+
filter = c.createBiquadFilter();
|
|
389
|
+
filter.type = 'lowpass';
|
|
390
|
+
filter.frequency.value = OPEN_HZ;
|
|
391
|
+
gain.connect(filter);
|
|
392
|
+
filter.connect(panner);
|
|
393
|
+
}
|
|
394
|
+
else
|
|
395
|
+
gain.connect(panner);
|
|
396
|
+
panner.connect(this.master);
|
|
397
|
+
// Spatial sounds reach the reverb zones too (ADR-0116); music and UI stay dry.
|
|
398
|
+
panner.connect(this.send);
|
|
399
|
+
}
|
|
400
|
+
else
|
|
401
|
+
gain.connect(this.master);
|
|
402
|
+
const v = { key, node, element, gain, panner, filter, clip: p.clip, loop: p.loop, ended: false };
|
|
403
|
+
if (element) {
|
|
404
|
+
element.onended = () => {
|
|
405
|
+
v.ended = true;
|
|
406
|
+
};
|
|
407
|
+
// Autoplay rules may refuse it until a gesture; the plugin resumes the context on one, and the element plays then.
|
|
408
|
+
const el = element;
|
|
409
|
+
void el.play().catch(() => {
|
|
410
|
+
const retry = () => void el.play().catch(() => { });
|
|
411
|
+
if (typeof addEventListener === 'function')
|
|
412
|
+
addEventListener('pointerdown', retry, { once: true, capture: true });
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
const b = node;
|
|
417
|
+
b.onended = () => {
|
|
418
|
+
v.ended = true;
|
|
419
|
+
};
|
|
420
|
+
b.start();
|
|
421
|
+
}
|
|
422
|
+
this.started++;
|
|
423
|
+
this.#voices.set(key, v);
|
|
424
|
+
return v;
|
|
425
|
+
}
|
|
426
|
+
#stop(v) {
|
|
427
|
+
if (v.element) {
|
|
428
|
+
v.element.onended = null;
|
|
429
|
+
v.element.pause();
|
|
430
|
+
v.element.removeAttribute('src');
|
|
431
|
+
v.element.load();
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
const b = v.node;
|
|
435
|
+
b.onended = null;
|
|
436
|
+
if (!v.ended) {
|
|
437
|
+
try {
|
|
438
|
+
b.stop();
|
|
439
|
+
}
|
|
440
|
+
catch {
|
|
441
|
+
// never started or already stopped
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
v.node.disconnect();
|
|
446
|
+
v.gain.disconnect();
|
|
447
|
+
v.filter?.disconnect();
|
|
448
|
+
v.panner?.disconnect();
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Occlusion (ADR-0127): spatial voices started from now on get a low-pass between their gain and
|
|
452
|
+
* panner, and `VoiceParams.occlusion` muffles them towards `occlusionHz` and lowers them to
|
|
453
|
+
* `occlusionGain` of their volume. Off: the chain is as before.
|
|
454
|
+
*/
|
|
455
|
+
occlusion = false;
|
|
456
|
+
/** The low-pass cutoff of a fully occluded voice (Hz). */
|
|
457
|
+
occlusionHz = 600;
|
|
458
|
+
/** The share of its volume a fully occluded voice keeps. */
|
|
459
|
+
occlusionGain = 0.35;
|
|
460
|
+
}
|
|
461
|
+
/** The low-pass cutoff of a clear voice (Hz): above hearing, so it changes nothing audible. */
|
|
462
|
+
const OPEN_HZ = 20000;
|
|
463
|
+
/** The longest early reflection, s (a wall 50 m away and back). */
|
|
464
|
+
const MAX_REFLECTION_DELAY = 0.3;
|
|
465
|
+
//# sourceMappingURL=audio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { AudioClips, AudioMixer, type ClipId, DISTANCE_MODEL, modelOf, type VoiceParams } from './audio.ts';
|
|
2
|
+
export { audioClipLoader, type AudioComponents, type AudioContextResource, audioPlugin, type AudioPluginOptions, audioSource, registerAudioComponents, REVERB_ZONE_FIELDS, reverbZone, type ReverbZoneOptions, SOURCE_FIELDS, type SourceOptions, } from './plugin.ts';
|
|
3
|
+
export { cameraPose, type DistanceModel, distanceGain, dopplerFactor, impulseResponse, type ListenerPose, poseOf, relative, rotate, rotateTo, zoneWeight } from './space.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// @yali-engine/audio: spatial sound on entities through Web Audio (ADR-0096).
|
|
2
|
+
export { AudioClips, AudioMixer, DISTANCE_MODEL, modelOf } from "./audio.js";
|
|
3
|
+
export { audioClipLoader, audioPlugin, audioSource, registerAudioComponents, REVERB_ZONE_FIELDS, reverbZone, SOURCE_FIELDS, } from "./plugin.js";
|
|
4
|
+
export { cameraPose, distanceGain, dopplerFactor, impulseResponse, poseOf, relative, rotate, rotateTo, zoneWeight } from "./space.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAe,cAAc,EAAE,OAAO,EAAoB,MAAM,YAAY,CAAC;AAC5G,OAAO,EACL,eAAe,EAGf,WAAW,EAEX,WAAW,EACX,uBAAuB,EACvB,kBAAkB,EAClB,UAAU,EAEV,aAAa,GAEd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAsB,YAAY,EAAE,aAAa,EAAE,eAAe,EAAqB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|