@adatechnology/game-audio 0.7.0 → 0.9.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/README.md +69 -1
- package/assets/sfx/CREDITOS.md +2 -2
- package/assets/sfx/manifest.json +48 -0
- package/dist/bank-Dsj1VAht.d.ts +391 -0
- package/dist/chunk-JV5XXCQE.js +180 -0
- package/dist/chunk-JV5XXCQE.js.map +1 -0
- package/dist/{chunk-BXUGSOPY.js → chunk-UUWKSG4J.js} +240 -167
- package/dist/chunk-UUWKSG4J.js.map +1 -0
- package/dist/fitted.d.ts +89 -0
- package/dist/fitted.js +139 -0
- package/dist/fitted.js.map +1 -0
- package/dist/index.d.ts +138 -3
- package/dist/index.global.js +642 -200
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +258 -20
- package/dist/index.js.map +1 -1
- package/dist/{locale-CJfDpWnI.d.ts → locale-YHyJwN2P.d.ts} +108 -440
- package/dist/testing.d.ts +5 -4
- package/dist/testing.js +2 -1
- package/package.json +6 -2
- package/dist/chunk-BXUGSOPY.js.map +0 -1
package/dist/index.global.js
CHANGED
|
@@ -4,6 +4,7 @@ var GameAudio = (() => {
|
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
8
|
var __export = (target, all) => {
|
|
8
9
|
for (var name in all)
|
|
9
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -17,6 +18,7 @@ var GameAudio = (() => {
|
|
|
17
18
|
return to;
|
|
18
19
|
};
|
|
19
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
20
22
|
|
|
21
23
|
// src/index.ts
|
|
22
24
|
var index_exports = {};
|
|
@@ -64,13 +66,19 @@ var GameAudio = (() => {
|
|
|
64
66
|
LIBRARY_BANK: () => LIBRARY_BANK,
|
|
65
67
|
LIBRARY_LOOPS: () => LIBRARY_LOOPS,
|
|
66
68
|
LIBRARY_SOUNDS: () => LIBRARY_SOUNDS,
|
|
69
|
+
LOCAL_PACK_BANK_ID: () => LOCAL_PACK_BANK_ID,
|
|
70
|
+
LOCAL_PACK_LICENSES: () => LOCAL_PACK_LICENSES,
|
|
71
|
+
LOCAL_PACK_PREFERENCES: () => LOCAL_PACK_PREFERENCES,
|
|
67
72
|
LOOP_SMOOTHING: () => LOOP_SMOOTHING,
|
|
68
73
|
LOOP_START_ABOVE: () => LOOP_START_ABOVE,
|
|
74
|
+
LocalSoundPackError: () => LocalSoundPackError,
|
|
69
75
|
MIX_PROFILES: () => MIX_PROFILES,
|
|
70
76
|
OPEN_CUTOFF: () => OPEN_CUTOFF,
|
|
71
77
|
PAN_LIMIT: () => PAN_LIMIT,
|
|
72
78
|
PAN_MIN_DISTANCE: () => PAN_MIN_DISTANCE,
|
|
73
79
|
PICKUP_CLASSES: () => PICKUP_CLASSES,
|
|
80
|
+
RADIO_BANK: () => RADIO_BANK,
|
|
81
|
+
RADIO_TRACKS: () => RADIO_TRACKS,
|
|
74
82
|
SAMPLE_ENVIRONMENTS: () => SAMPLE_ENVIRONMENTS,
|
|
75
83
|
SDK_LICENSES: () => SDK_LICENSES,
|
|
76
84
|
SFX_BLOWS: () => SFX_BLOWS,
|
|
@@ -80,9 +88,12 @@ var GameAudio = (() => {
|
|
|
80
88
|
SFX_LAYERS: () => SFX_LAYERS,
|
|
81
89
|
SFX_MATERIALS: () => SFX_MATERIALS,
|
|
82
90
|
SFX_TIERS: () => SFX_TIERS,
|
|
91
|
+
TRIM_FADE: () => TRIM_FADE,
|
|
83
92
|
USER_SAMPLES_BANK: () => USER_SAMPLES_BANK,
|
|
84
93
|
USER_SOUND_PREFIX: () => USER_SOUND_PREFIX,
|
|
85
94
|
VOICE_GROUPS: () => VOICE_GROUPS,
|
|
95
|
+
aliasSample: () => aliasSample,
|
|
96
|
+
applyLocalSoundPack: () => applyLocalSoundPack,
|
|
86
97
|
audioBenchTextOf: () => audioBenchTextOf,
|
|
87
98
|
balanceOf: () => balanceOf,
|
|
88
99
|
bufferOf: () => bufferOf,
|
|
@@ -104,6 +115,7 @@ var GameAudio = (() => {
|
|
|
104
115
|
crossGains: () => crossGains,
|
|
105
116
|
cutoffDivisor: () => cutoffDivisor,
|
|
106
117
|
dbOf: () => dbOf,
|
|
118
|
+
defaultIdleScheduler: () => defaultIdleScheduler,
|
|
107
119
|
drawBetween: () => drawBetween,
|
|
108
120
|
encodeWav: () => encodeWav,
|
|
109
121
|
endDropDb: () => endDropDb,
|
|
@@ -117,6 +129,7 @@ var GameAudio = (() => {
|
|
|
117
129
|
impactSoundId: () => impactSoundId,
|
|
118
130
|
inlineLoader: () => inlineLoader,
|
|
119
131
|
isCalibrationUrl: () => isCalibrationUrl,
|
|
132
|
+
isShippableLicense: () => isShippableLicense,
|
|
120
133
|
isSilent: () => isSilent,
|
|
121
134
|
kitSampleBank: () => kitSampleBank,
|
|
122
135
|
kitSampleDefaults: () => kitSampleDefaults,
|
|
@@ -124,17 +137,22 @@ var GameAudio = (() => {
|
|
|
124
137
|
listenerDistance: () => listenerDistance,
|
|
125
138
|
loadKitSamples: () => loadKitSamples,
|
|
126
139
|
loadUserSamples: () => loadUserSamples,
|
|
140
|
+
localPackSamples: () => localPackSamples,
|
|
127
141
|
loopCrossfade: () => loopCrossfade,
|
|
142
|
+
loopRate: () => loopRate,
|
|
128
143
|
measureCriteria: () => measureCriteria,
|
|
129
144
|
noiseClassRecipe: () => noiseClassRecipe,
|
|
130
145
|
onsetIndex: () => onsetIndex,
|
|
131
146
|
panFor: () => panFor,
|
|
147
|
+
parseLocalSoundPack: () => parseLocalSoundPack,
|
|
132
148
|
parseSfxName: () => parseSfxName,
|
|
133
149
|
peakIndex: () => peakIndex,
|
|
134
150
|
peakOf: () => peakOf,
|
|
135
151
|
pickTake: () => pickTake,
|
|
136
152
|
planSample: () => planSample,
|
|
137
153
|
planSound: () => planSound,
|
|
154
|
+
pointSamples: () => pointSamples,
|
|
155
|
+
radioTrack: () => radioTrack,
|
|
138
156
|
realize: () => realize,
|
|
139
157
|
realizeSample: () => realizeSample,
|
|
140
158
|
recordOutput: () => recordOutput,
|
|
@@ -269,6 +287,20 @@ var GameAudio = (() => {
|
|
|
269
287
|
sources: [
|
|
270
288
|
"sdk: the crackles of every fire share four voices"
|
|
271
289
|
]
|
|
290
|
+
},
|
|
291
|
+
debris_knock: {
|
|
292
|
+
maxConcurrent: 3,
|
|
293
|
+
cooldown: 0.04,
|
|
294
|
+
sources: [
|
|
295
|
+
"sdk: forty parts coming loose in one blast are a few knocks, not forty (room log: thunk x34 in under a second when a molotov broke)"
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
sizzle: {
|
|
299
|
+
maxConcurrent: 1,
|
|
300
|
+
cooldown: 0.5,
|
|
301
|
+
sources: [
|
|
302
|
+
"sdk: every melting part of a burning bin is one sizzle at a time (room log: hiss x34 in 1.6 s on a plastic bin)"
|
|
303
|
+
]
|
|
272
304
|
}
|
|
273
305
|
};
|
|
274
306
|
|
|
@@ -420,16 +452,16 @@ var GameAudio = (() => {
|
|
|
420
452
|
}
|
|
421
453
|
return pending;
|
|
422
454
|
}
|
|
423
|
-
function drawLayer(
|
|
455
|
+
function drawLayer(layer2, scope) {
|
|
424
456
|
const pending = { at: 0, gain: 1, filters: [], modulators: [] };
|
|
425
|
-
for (const key of Object.keys(
|
|
426
|
-
if (key === "at" &&
|
|
427
|
-
else if (key === "duration" &&
|
|
428
|
-
else if (key === "gain" &&
|
|
429
|
-
else if (key === "source" &&
|
|
430
|
-
else if (key === "source" &&
|
|
431
|
-
else if (key === "filters" &&
|
|
432
|
-
else if (key === "modulators" &&
|
|
457
|
+
for (const key of Object.keys(layer2)) {
|
|
458
|
+
if (key === "at" && layer2.at !== void 0) pending.at = draw(layer2.at, scope);
|
|
459
|
+
else if (key === "duration" && layer2.duration !== void 0) pending.duration = draw(layer2.duration, scope);
|
|
460
|
+
else if (key === "gain" && layer2.gain !== void 0) pending.gain = draw(layer2.gain, scope);
|
|
461
|
+
else if (key === "source" && layer2.source.kind === "osc") pending.automation = drawAutomation(layer2.source.frequency, scope);
|
|
462
|
+
else if (key === "source" && layer2.source.kind === "formant_voice") pending.voice = drawVoice(layer2.source, scope);
|
|
463
|
+
else if (key === "filters" && layer2.filters) pending.filters = layer2.filters.bank.map((spec) => drawFilter(spec, scope));
|
|
464
|
+
else if (key === "modulators" && layer2.modulators) pending.modulators = layer2.modulators.map((modulator) => drawModulator(modulator, scope));
|
|
433
465
|
}
|
|
434
466
|
return pending;
|
|
435
467
|
}
|
|
@@ -440,8 +472,8 @@ var GameAudio = (() => {
|
|
|
440
472
|
if (source.offset === "fit") return scope.random() * Math.max(0, noiseSeconds - duration);
|
|
441
473
|
return scope.random() * (source.offsetScale ?? 1);
|
|
442
474
|
}
|
|
443
|
-
function finishSource(
|
|
444
|
-
const source =
|
|
475
|
+
function finishSource(layer2, pending, duration, offset) {
|
|
476
|
+
const source = layer2.source;
|
|
445
477
|
if (source.kind === "noise") return { kind: "noise", offset, loop: source.loop === true };
|
|
446
478
|
if (source.kind === "osc") return { kind: "osc", wave: source.wave, frequency: finishAutomation(pending.automation ?? {}, duration) };
|
|
447
479
|
const voice2 = pending.voice;
|
|
@@ -463,23 +495,23 @@ var GameAudio = (() => {
|
|
|
463
495
|
function finishEnvelope(envelope2, duration, level) {
|
|
464
496
|
return envelope2.map(([time, value, curve, fixed2]) => ({ time: timeOf(time, duration), value: fixed2 === "fixed" ? value : value * level, curve }));
|
|
465
497
|
}
|
|
466
|
-
function planLayer(
|
|
467
|
-
const pending = drawLayer(
|
|
498
|
+
function planLayer(layer2, scope, defaults, levelInEnvelope, soundLevel) {
|
|
499
|
+
const pending = drawLayer(layer2, scope);
|
|
468
500
|
const duration = pending.duration ?? defaults.duration;
|
|
469
501
|
if (duration === void 0) throw new Error("game-audio: a layer needs a duration (its own or the recipe\u2019s)");
|
|
470
|
-
const offset = drawOffset(
|
|
502
|
+
const offset = drawOffset(layer2.source, duration, scope, defaults.noiseSeconds);
|
|
471
503
|
const level = pending.gain * soundLevel;
|
|
472
|
-
const sourceFrequency =
|
|
504
|
+
const sourceFrequency = layer2.source.kind === "noise" ? 0 : startFrequency(pending.automation ?? pending.voice?.frequency ?? {});
|
|
473
505
|
const planned = {
|
|
474
506
|
at: pending.at,
|
|
475
507
|
duration,
|
|
476
|
-
tail:
|
|
508
|
+
tail: layer2.tail ?? defaults.tail ?? DEFAULT_TAIL,
|
|
477
509
|
level,
|
|
478
|
-
...
|
|
479
|
-
source: finishSource(
|
|
480
|
-
...
|
|
510
|
+
...layer2.wet === void 0 ? {} : { wet: layer2.wet },
|
|
511
|
+
source: finishSource(layer2, pending, duration, offset),
|
|
512
|
+
...layer2.filters ? {
|
|
481
513
|
filters: {
|
|
482
|
-
mode:
|
|
514
|
+
mode: layer2.filters.mode,
|
|
483
515
|
bank: pending.filters.map(({ spec, frequency, q, gain, level: filterLevel }) => ({
|
|
484
516
|
type: spec.type,
|
|
485
517
|
frequency: finishAutomation(frequency, duration),
|
|
@@ -490,10 +522,10 @@ var GameAudio = (() => {
|
|
|
490
522
|
}
|
|
491
523
|
} : {},
|
|
492
524
|
modulators: pending.modulators.map((modulator) => finishModulator(modulator, sourceFrequency)),
|
|
493
|
-
envelope: finishEnvelope(
|
|
525
|
+
envelope: finishEnvelope(layer2.envelope, duration, levelInEnvelope ? level : 1)
|
|
494
526
|
};
|
|
495
|
-
if (!
|
|
496
|
-
return Array.from({ length:
|
|
527
|
+
if (!layer2.repeat) return [planned];
|
|
528
|
+
return Array.from({ length: layer2.repeat.count }, (_, index) => ({ ...planned, at: planned.at + index * (layer2.repeat?.spacing ?? 0) }));
|
|
497
529
|
}
|
|
498
530
|
function scaleAutomation(automation, divisor) {
|
|
499
531
|
const scale = (value) => value === void 0 ? void 0 : value / divisor;
|
|
@@ -501,12 +533,12 @@ var GameAudio = (() => {
|
|
|
501
533
|
for (const key of ["value", "from", "to"]) if (automation[key] !== void 0) scaled[key] = scale(automation[key]);
|
|
502
534
|
return scaled;
|
|
503
535
|
}
|
|
504
|
-
function muffle(
|
|
505
|
-
if (divisor === 1 || !
|
|
506
|
-
const bank =
|
|
507
|
-
return { ...
|
|
536
|
+
function muffle(layer2, divisor) {
|
|
537
|
+
if (divisor === 1 || !layer2.filters) return layer2;
|
|
538
|
+
const bank = layer2.filters.bank.map((filter) => filter.type === "lowpass" ? { ...filter, frequency: scaleAutomation(filter.frequency, divisor) } : filter);
|
|
539
|
+
return { ...layer2, filters: { ...layer2.filters, bank } };
|
|
508
540
|
}
|
|
509
|
-
var layerEnd = (
|
|
541
|
+
var layerEnd = (layer2) => layer2.at + layer2.duration + layer2.tail;
|
|
510
542
|
function planSound(recipe2, input = {}) {
|
|
511
543
|
const listener = input.listener ?? DEFAULT_LISTENER;
|
|
512
544
|
const distance = input.distance ?? (input.position ? listenerDistance(listener, input.position, input.distanceMode) : 0);
|
|
@@ -534,7 +566,7 @@ var GameAudio = (() => {
|
|
|
534
566
|
noiseSeconds: input.noiseSeconds ?? DEFAULT_NOISE_SECONDS
|
|
535
567
|
};
|
|
536
568
|
const divisor = cutoffDivisor(recipe2.falloff, distance) / (1 - OCCLUSION_CUTOFF * occlusion);
|
|
537
|
-
const layers = recipe2.layers.flatMap((
|
|
569
|
+
const layers = recipe2.layers.flatMap((layer2) => planLayer(layer2, scope, defaults, order !== "layered", level)).map((layer2) => muffle(layer2, divisor));
|
|
538
570
|
return { ...base, silent: false, length: Math.max(0, ...layers.map(layerEnd)), layers };
|
|
539
571
|
}
|
|
540
572
|
|
|
@@ -600,7 +632,16 @@ var GameAudio = (() => {
|
|
|
600
632
|
const index = pickTake(refs.length, input.memory?.get(key), input.random);
|
|
601
633
|
input.memory?.set(key, index);
|
|
602
634
|
const ref = refs[index];
|
|
603
|
-
layers.push({
|
|
635
|
+
layers.push({
|
|
636
|
+
role,
|
|
637
|
+
url: ref.url,
|
|
638
|
+
take: index,
|
|
639
|
+
gain: gain * (ref.gain ?? 1) * level,
|
|
640
|
+
rate,
|
|
641
|
+
at,
|
|
642
|
+
...ref.offset === void 0 ? {} : { offset: ref.offset },
|
|
643
|
+
...ref.duration === void 0 ? {} : { duration: ref.duration }
|
|
644
|
+
});
|
|
604
645
|
};
|
|
605
646
|
take("close", close, gains.close, 0);
|
|
606
647
|
take("far", far, gains.far, 0);
|
|
@@ -747,12 +788,48 @@ var GameAudio = (() => {
|
|
|
747
788
|
}
|
|
748
789
|
return merged;
|
|
749
790
|
}
|
|
791
|
+
var trimmed = (refs, alias) => refs.filter((ref) => alias.only === void 0 || ref.url.includes(alias.only)).map((ref) => ({
|
|
792
|
+
...ref,
|
|
793
|
+
...alias.offset === void 0 ? {} : { offset: alias.offset },
|
|
794
|
+
...alias.duration === void 0 ? {} : { duration: alias.duration }
|
|
795
|
+
}));
|
|
796
|
+
function aliasSample(alias, file) {
|
|
797
|
+
const close = trimmed(file.close, alias);
|
|
798
|
+
if (close.length === 0) return void 0;
|
|
799
|
+
const { tiers: _tiers, fallback: _fallback, choke: _choke, ...rest } = file;
|
|
800
|
+
return {
|
|
801
|
+
...rest,
|
|
802
|
+
id: alias.id,
|
|
803
|
+
close,
|
|
804
|
+
...file.far ? { far: trimmed(file.far, alias) } : {},
|
|
805
|
+
...alias.loop === void 0 ? {} : { loop: alias.loop },
|
|
806
|
+
...alias.baseFrequency === void 0 ? {} : { baseFrequency: alias.baseFrequency },
|
|
807
|
+
...alias.gain === void 0 ? {} : { gain: alias.gain },
|
|
808
|
+
...alias.pitch === void 0 ? {} : { pitch: alias.pitch },
|
|
809
|
+
...alias.falloff ? { falloff: alias.falloff } : {},
|
|
810
|
+
fallback: alias.id,
|
|
811
|
+
sources: [...file.sources, `alias: ${alias.id} \u2192 ${alias.file}`]
|
|
812
|
+
};
|
|
813
|
+
}
|
|
750
814
|
function createSoundRegistry(banks = []) {
|
|
751
815
|
const sounds = /* @__PURE__ */ new Map();
|
|
752
816
|
const loops = /* @__PURE__ */ new Map();
|
|
753
817
|
const sets = /* @__PURE__ */ new Map();
|
|
754
818
|
const samples = /* @__PURE__ */ new Map();
|
|
755
819
|
const owners = /* @__PURE__ */ new Map();
|
|
820
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
821
|
+
const derived = /* @__PURE__ */ new Map();
|
|
822
|
+
const resolveAlias = (id) => {
|
|
823
|
+
const alias = aliases.get(id);
|
|
824
|
+
if (!alias) return void 0;
|
|
825
|
+
const cached = derived.get(id);
|
|
826
|
+
if (cached) return cached;
|
|
827
|
+
const file = samples.get(alias.file);
|
|
828
|
+
if (!file) return void 0;
|
|
829
|
+
const sample = aliasSample(alias, file);
|
|
830
|
+
if (sample) derived.set(id, sample);
|
|
831
|
+
return sample;
|
|
832
|
+
};
|
|
756
833
|
const registry = {
|
|
757
834
|
register(bank) {
|
|
758
835
|
for (const sound of bank.sounds ?? []) {
|
|
@@ -772,6 +849,11 @@ var GameAudio = (() => {
|
|
|
772
849
|
samples.set(sample.id, sample);
|
|
773
850
|
owners.set(sample.id, bank.id);
|
|
774
851
|
}
|
|
852
|
+
for (const alias of bank.sampleAliases ?? []) {
|
|
853
|
+
aliases.set(alias.id, alias);
|
|
854
|
+
if (!owners.has(alias.id)) owners.set(alias.id, bank.id);
|
|
855
|
+
}
|
|
856
|
+
derived.clear();
|
|
775
857
|
},
|
|
776
858
|
patch(id, patch) {
|
|
777
859
|
const sound = sounds.get(id);
|
|
@@ -780,22 +862,31 @@ var GameAudio = (() => {
|
|
|
780
862
|
if (loop) loops.set(id, mergeRecords(loop, patch));
|
|
781
863
|
const sample = samples.get(id);
|
|
782
864
|
if (sample) samples.set(id, mergeRecords(sample, patch));
|
|
865
|
+
derived.clear();
|
|
783
866
|
},
|
|
784
867
|
remove(id) {
|
|
785
868
|
sounds.delete(id);
|
|
786
869
|
loops.delete(id);
|
|
787
870
|
sets.delete(id);
|
|
788
871
|
samples.delete(id);
|
|
872
|
+
aliases.delete(id);
|
|
789
873
|
owners.delete(id);
|
|
874
|
+
derived.clear();
|
|
875
|
+
},
|
|
876
|
+
removeSample(id) {
|
|
877
|
+
samples.delete(id);
|
|
878
|
+
aliases.delete(id);
|
|
879
|
+
if (!sounds.has(id) && !loops.has(id) && !sets.has(id)) owners.delete(id);
|
|
880
|
+
derived.clear();
|
|
790
881
|
},
|
|
791
882
|
sound: (id) => sounds.get(id),
|
|
792
883
|
loop: (id) => loops.get(id),
|
|
793
884
|
set: (id) => sets.get(id),
|
|
794
|
-
sample: (id) => samples.get(id),
|
|
885
|
+
sample: (id) => samples.get(id) ?? resolveAlias(id),
|
|
795
886
|
soundIds: () => [...sounds.keys()],
|
|
796
887
|
setIds: () => [...sets.keys()],
|
|
797
888
|
loopIds: () => [...loops.keys()],
|
|
798
|
-
sampleIds: () => [...samples.keys()],
|
|
889
|
+
sampleIds: () => [...samples.keys(), ...[...aliases.keys()].filter((id) => !samples.has(id) && resolveAlias(id) !== void 0)],
|
|
799
890
|
bankOf: (id) => owners.get(id)
|
|
800
891
|
};
|
|
801
892
|
for (const bank of banks) registry.register(bank);
|
|
@@ -1223,6 +1314,8 @@ var GameAudio = (() => {
|
|
|
1223
1314
|
var DEFAULT_LOOP_CROSSFADE = 0.25;
|
|
1224
1315
|
var LOOKAHEAD = 1.5;
|
|
1225
1316
|
var VOLUME_SMOOTHING = 0.05;
|
|
1317
|
+
var RATE_SMOOTHING = 0.04;
|
|
1318
|
+
var RATE_RANGE = [0.25, 4];
|
|
1226
1319
|
function loopCrossfade(sound) {
|
|
1227
1320
|
if (!sound.loop) return void 0;
|
|
1228
1321
|
return sound.loop === true ? DEFAULT_LOOP_CROSSFADE : Math.max(0, sound.loop.crossfade ?? DEFAULT_LOOP_CROSSFADE);
|
|
@@ -1234,6 +1327,7 @@ var GameAudio = (() => {
|
|
|
1234
1327
|
param.setValueAtTime(value(0), at);
|
|
1235
1328
|
for (let step = 1; step <= CROSSFADE_STEPS; step++) param.linearRampToValueAtTime(value(step / CROSSFADE_STEPS), at + seconds * step / CROSSFADE_STEPS);
|
|
1236
1329
|
};
|
|
1330
|
+
var loopRate = (sound, frequency) => sound.baseFrequency && sound.baseFrequency > 0 && frequency > 0 ? Math.max(RATE_RANGE[0], Math.min(RATE_RANGE[1], frequency / sound.baseFrequency)) : 1;
|
|
1237
1331
|
function createSampleLoopHandle(sound, host, volume = 0) {
|
|
1238
1332
|
const crossfade = loopCrossfade(sound) ?? DEFAULT_LOOP_CROSSFADE;
|
|
1239
1333
|
let level = volume;
|
|
@@ -1243,13 +1337,17 @@ var GameAudio = (() => {
|
|
|
1243
1337
|
let segments = [];
|
|
1244
1338
|
let last;
|
|
1245
1339
|
let timer;
|
|
1340
|
+
let panner;
|
|
1341
|
+
let rate = 1;
|
|
1342
|
+
let position;
|
|
1246
1343
|
const entry = {
|
|
1247
1344
|
get created() {
|
|
1248
1345
|
return master !== void 0;
|
|
1249
1346
|
},
|
|
1250
1347
|
rewire() {
|
|
1251
|
-
master
|
|
1252
|
-
|
|
1348
|
+
const out = panner ?? master;
|
|
1349
|
+
out?.disconnect();
|
|
1350
|
+
out?.connect(host.output(sound.bus));
|
|
1253
1351
|
},
|
|
1254
1352
|
stop() {
|
|
1255
1353
|
stopped = true;
|
|
@@ -1266,23 +1364,29 @@ var GameAudio = (() => {
|
|
|
1266
1364
|
segments = [];
|
|
1267
1365
|
master?.disconnect();
|
|
1268
1366
|
master = void 0;
|
|
1367
|
+
panner?.disconnect();
|
|
1368
|
+
panner = void 0;
|
|
1269
1369
|
fallback?.stop();
|
|
1270
1370
|
fallback = void 0;
|
|
1271
1371
|
host.unregister(entry);
|
|
1272
1372
|
}
|
|
1273
1373
|
};
|
|
1274
|
-
const
|
|
1374
|
+
const nextTake = () => {
|
|
1275
1375
|
const takes2 = sound.close;
|
|
1276
1376
|
const take = pickTake(takes2.length, last, host.random);
|
|
1277
1377
|
last = take;
|
|
1278
1378
|
const ref = takes2[take];
|
|
1279
|
-
|
|
1379
|
+
const buffer = ref ? host.buffer(ref.url) : void 0;
|
|
1380
|
+
return ref && buffer ? { buffer, ref } : void 0;
|
|
1280
1381
|
};
|
|
1281
1382
|
const schedule = (context, output, at, first) => {
|
|
1282
|
-
const
|
|
1283
|
-
if (!
|
|
1383
|
+
const next = nextTake();
|
|
1384
|
+
if (!next) return void 0;
|
|
1385
|
+
const { buffer, ref } = next;
|
|
1386
|
+
const offset = Math.max(0, Math.min(ref.offset ?? 0, buffer.duration));
|
|
1284
1387
|
const source = context.createBufferSource();
|
|
1285
1388
|
source.buffer = buffer;
|
|
1389
|
+
source.playbackRate.value = rate;
|
|
1286
1390
|
const gain = context.createGain();
|
|
1287
1391
|
source.connect(gain);
|
|
1288
1392
|
gain.connect(output);
|
|
@@ -1290,15 +1394,15 @@ var GameAudio = (() => {
|
|
|
1290
1394
|
if (buffer.duration < 2 * crossfade || sound.close.length === 1) {
|
|
1291
1395
|
source.loop = true;
|
|
1292
1396
|
if (!first) ramp2(gain.gain, "in", at, Math.max(1e-3, fade));
|
|
1293
|
-
source.start(at);
|
|
1397
|
+
source.start(at, offset);
|
|
1294
1398
|
segments.push({ source, gain, end: Number.POSITIVE_INFINITY });
|
|
1295
1399
|
return void 0;
|
|
1296
1400
|
}
|
|
1297
1401
|
if (first) gain.gain.setValueAtTime(1, at);
|
|
1298
1402
|
else ramp2(gain.gain, "in", at, fade);
|
|
1299
|
-
const end3 = at + buffer.duration;
|
|
1403
|
+
const end3 = at + (buffer.duration - offset) / rate;
|
|
1300
1404
|
ramp2(gain.gain, "out", end3 - fade, fade);
|
|
1301
|
-
source.start(at);
|
|
1405
|
+
source.start(at, offset);
|
|
1302
1406
|
source.stop(end3);
|
|
1303
1407
|
segments.push({ source, gain, end: end3 });
|
|
1304
1408
|
source.onended = () => {
|
|
@@ -1319,14 +1423,26 @@ var GameAudio = (() => {
|
|
|
1319
1423
|
pump(context, output, next, false);
|
|
1320
1424
|
}, wait);
|
|
1321
1425
|
};
|
|
1322
|
-
const
|
|
1426
|
+
const spatial = () => {
|
|
1427
|
+
if (!position || !host.listener) return 1;
|
|
1428
|
+
return falloffGain(sound.falloff ?? DEFAULT_SAMPLE_FALLOFF, 1, listenerDistance(host.listener(), position, host.distance));
|
|
1429
|
+
};
|
|
1430
|
+
const fileLevel = () => level * (sound.gain ?? 1) * spatial();
|
|
1431
|
+
const place = (context) => {
|
|
1432
|
+
if (panner && position && host.listener) panner.pan.setTargetAtTime(panFor(host.listener(), position), context.currentTime, VOLUME_SMOOTHING);
|
|
1433
|
+
};
|
|
1323
1434
|
const startFiles = () => {
|
|
1324
1435
|
if (stopped || master || !host.isReady(sound)) return master !== void 0;
|
|
1325
1436
|
const context = host.ready();
|
|
1326
1437
|
if (!context) return false;
|
|
1327
1438
|
master = context.createGain();
|
|
1328
1439
|
master.gain.setValueAtTime(fileLevel(), context.currentTime);
|
|
1329
|
-
|
|
1440
|
+
if (sound.pan !== false && position && typeof context.createStereoPanner === "function") {
|
|
1441
|
+
panner = context.createStereoPanner();
|
|
1442
|
+
if (host.listener) panner.pan.value = panFor(host.listener(), position);
|
|
1443
|
+
master.connect(panner);
|
|
1444
|
+
panner.connect(host.output(sound.bus));
|
|
1445
|
+
} else master.connect(host.output(sound.bus));
|
|
1330
1446
|
host.register(entry);
|
|
1331
1447
|
pump(context, master, context.currentTime, true);
|
|
1332
1448
|
if (fallback) {
|
|
@@ -1352,9 +1468,15 @@ var GameAudio = (() => {
|
|
|
1352
1468
|
set(next) {
|
|
1353
1469
|
if (stopped) return;
|
|
1354
1470
|
if (next.volume !== void 0) level = host.muted(sound.bus) ? 0 : Math.max(0, next.volume);
|
|
1471
|
+
if (next.position) position = next.position;
|
|
1472
|
+
if (next.frequency !== void 0 && sound.baseFrequency) rate = loopRate(sound, next.frequency);
|
|
1355
1473
|
if (level > 0) ensure();
|
|
1356
1474
|
const context = host.ready();
|
|
1357
|
-
if (master && context)
|
|
1475
|
+
if (master && context) {
|
|
1476
|
+
master.gain.setTargetAtTime(fileLevel(), context.currentTime, VOLUME_SMOOTHING);
|
|
1477
|
+
place(context);
|
|
1478
|
+
if (next.frequency !== void 0 && sound.baseFrequency) for (const segment of segments) segment.source.playbackRate.setTargetAtTime(rate, context.currentTime, RATE_SMOOTHING);
|
|
1479
|
+
}
|
|
1358
1480
|
fallback?.set(next.volume === void 0 ? next : { ...next, volume: level });
|
|
1359
1481
|
},
|
|
1360
1482
|
stop: entry.stop
|
|
@@ -1516,9 +1638,9 @@ var GameAudio = (() => {
|
|
|
1516
1638
|
}
|
|
1517
1639
|
|
|
1518
1640
|
// src/webaudio/realize-game.ts
|
|
1519
|
-
var end = (
|
|
1520
|
-
var firstFilter = (
|
|
1521
|
-
var offsetOf = (
|
|
1641
|
+
var end = (layer2, start) => start + layer2.duration + layer2.tail;
|
|
1642
|
+
var firstFilter = (layer2) => layer2.filters?.bank[0];
|
|
1643
|
+
var offsetOf = (layer2) => layer2.source.kind === "noise" ? layer2.source.offset : 0;
|
|
1522
1644
|
function setFilter(node, filter, time) {
|
|
1523
1645
|
if (!filter) return;
|
|
1524
1646
|
node.type = filter.type;
|
|
@@ -1527,28 +1649,28 @@ var GameAudio = (() => {
|
|
|
1527
1649
|
}
|
|
1528
1650
|
function realizeBurst(plan, target) {
|
|
1529
1651
|
const { context } = target;
|
|
1530
|
-
const
|
|
1531
|
-
const time = target.time +
|
|
1652
|
+
const layer2 = plan.layers[0];
|
|
1653
|
+
const time = target.time + layer2.at;
|
|
1532
1654
|
const gain = context.createGain();
|
|
1533
1655
|
gain.connect(target.output);
|
|
1534
|
-
const source = noiseSource(target,
|
|
1656
|
+
const source = noiseSource(target, layer2.source.kind === "noise" && layer2.source.loop);
|
|
1535
1657
|
const filter = context.createBiquadFilter();
|
|
1536
1658
|
source.connect(filter);
|
|
1537
1659
|
filter.connect(gain);
|
|
1538
|
-
setFilter(filter, firstFilter(
|
|
1539
|
-
applyEnvelope(gain.gain,
|
|
1540
|
-
source.start(time, offsetOf(
|
|
1541
|
-
source.stop(end(
|
|
1660
|
+
setFilter(filter, firstFilter(layer2), time);
|
|
1661
|
+
applyEnvelope(gain.gain, layer2.envelope, time);
|
|
1662
|
+
source.start(time, offsetOf(layer2));
|
|
1663
|
+
source.stop(end(layer2, time));
|
|
1542
1664
|
return voiceOf([source]);
|
|
1543
1665
|
}
|
|
1544
1666
|
function realizeScream(plan, target) {
|
|
1545
1667
|
const { context } = target;
|
|
1546
|
-
const
|
|
1547
|
-
const time = target.time +
|
|
1548
|
-
const source = noiseSource(target,
|
|
1668
|
+
const layer2 = plan.layers[0];
|
|
1669
|
+
const time = target.time + layer2.at;
|
|
1670
|
+
const source = noiseSource(target, layer2.source.kind === "noise" && layer2.source.loop);
|
|
1549
1671
|
const gain = context.createGain();
|
|
1550
1672
|
gain.connect(target.output);
|
|
1551
|
-
for (const band of
|
|
1673
|
+
for (const band of layer2.filters?.bank ?? []) {
|
|
1552
1674
|
const filter = context.createBiquadFilter();
|
|
1553
1675
|
filter.type = band.type;
|
|
1554
1676
|
if (band.q !== void 0) filter.Q.value = band.q;
|
|
@@ -1561,7 +1683,7 @@ var GameAudio = (() => {
|
|
|
1561
1683
|
level.connect(gain);
|
|
1562
1684
|
}
|
|
1563
1685
|
const sources = [source];
|
|
1564
|
-
const lfos =
|
|
1686
|
+
const lfos = layer2.modulators.map((modulator) => {
|
|
1565
1687
|
const lfo = context.createOscillator();
|
|
1566
1688
|
const depth = context.createGain();
|
|
1567
1689
|
if (modulator.wave) lfo.type = modulator.wave;
|
|
@@ -1571,75 +1693,75 @@ var GameAudio = (() => {
|
|
|
1571
1693
|
depth.connect(gain.gain);
|
|
1572
1694
|
return lfo;
|
|
1573
1695
|
});
|
|
1574
|
-
applyEnvelope(gain.gain,
|
|
1575
|
-
source.start(time, offsetOf(
|
|
1576
|
-
source.stop(end(
|
|
1696
|
+
applyEnvelope(gain.gain, layer2.envelope, time);
|
|
1697
|
+
source.start(time, offsetOf(layer2));
|
|
1698
|
+
source.stop(end(layer2, time));
|
|
1577
1699
|
for (const lfo of lfos) {
|
|
1578
1700
|
lfo.start(time);
|
|
1579
|
-
lfo.stop(end(
|
|
1701
|
+
lfo.stop(end(layer2, time));
|
|
1580
1702
|
sources.push(lfo);
|
|
1581
1703
|
}
|
|
1582
1704
|
return voiceOf(sources);
|
|
1583
1705
|
}
|
|
1584
1706
|
function realizeGroan(plan, target) {
|
|
1585
1707
|
const { context } = target;
|
|
1586
|
-
const
|
|
1587
|
-
const time = target.time +
|
|
1708
|
+
const layer2 = plan.layers[0];
|
|
1709
|
+
const time = target.time + layer2.at;
|
|
1588
1710
|
const oscillator = context.createOscillator();
|
|
1589
1711
|
const gain = context.createGain();
|
|
1590
1712
|
const filter = context.createBiquadFilter();
|
|
1591
|
-
const band = firstFilter(
|
|
1592
|
-
if (
|
|
1713
|
+
const band = firstFilter(layer2);
|
|
1714
|
+
if (layer2.source.kind === "osc") oscillator.type = layer2.source.wave;
|
|
1593
1715
|
if (band) {
|
|
1594
1716
|
filter.type = band.type;
|
|
1595
1717
|
if (band.q !== void 0) filter.Q.value = band.q;
|
|
1596
1718
|
}
|
|
1597
|
-
if (
|
|
1598
|
-
assign(oscillator.frequency,
|
|
1599
|
-
rampTo(oscillator.frequency,
|
|
1719
|
+
if (layer2.source.kind === "osc") {
|
|
1720
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1721
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1600
1722
|
}
|
|
1601
1723
|
if (band) assign(filter.frequency, band.frequency, time);
|
|
1602
|
-
applyEnvelope(gain.gain,
|
|
1724
|
+
applyEnvelope(gain.gain, layer2.envelope, time);
|
|
1603
1725
|
oscillator.connect(filter);
|
|
1604
1726
|
filter.connect(gain);
|
|
1605
1727
|
gain.connect(target.output);
|
|
1606
1728
|
oscillator.start(time);
|
|
1607
|
-
oscillator.stop(end(
|
|
1729
|
+
oscillator.stop(end(layer2, time));
|
|
1608
1730
|
return voiceOf([oscillator]);
|
|
1609
1731
|
}
|
|
1610
1732
|
function realizeBoom(plan, target) {
|
|
1611
1733
|
const { context } = target;
|
|
1612
|
-
const
|
|
1613
|
-
const time = target.time +
|
|
1734
|
+
const layer2 = plan.layers[0];
|
|
1735
|
+
const time = target.time + layer2.at;
|
|
1614
1736
|
const oscillator = context.createOscillator();
|
|
1615
1737
|
const gain = context.createGain();
|
|
1616
|
-
if (
|
|
1617
|
-
oscillator.type =
|
|
1618
|
-
assign(oscillator.frequency,
|
|
1619
|
-
rampTo(oscillator.frequency,
|
|
1738
|
+
if (layer2.source.kind === "osc") {
|
|
1739
|
+
oscillator.type = layer2.source.wave;
|
|
1740
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1741
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1620
1742
|
}
|
|
1621
|
-
applyEnvelope(gain.gain,
|
|
1743
|
+
applyEnvelope(gain.gain, layer2.envelope, time);
|
|
1622
1744
|
oscillator.connect(gain);
|
|
1623
1745
|
gain.connect(target.output);
|
|
1624
1746
|
oscillator.start(time);
|
|
1625
|
-
oscillator.stop(end(
|
|
1747
|
+
oscillator.stop(end(layer2, time));
|
|
1626
1748
|
return voiceOf([oscillator]);
|
|
1627
1749
|
}
|
|
1628
1750
|
function realizeStatic(plan, target) {
|
|
1629
1751
|
const { context } = target;
|
|
1630
|
-
const
|
|
1631
|
-
const time = target.time +
|
|
1752
|
+
const layer2 = plan.layers[0];
|
|
1753
|
+
const time = target.time + layer2.at;
|
|
1632
1754
|
const gain = context.createGain();
|
|
1633
1755
|
const source = context.createBufferSource();
|
|
1634
1756
|
const filter = context.createBiquadFilter();
|
|
1635
1757
|
source.buffer = target.noise();
|
|
1636
|
-
setFilter(filter, firstFilter(
|
|
1637
|
-
applyEnvelope(gain.gain,
|
|
1758
|
+
setFilter(filter, firstFilter(layer2), time);
|
|
1759
|
+
applyEnvelope(gain.gain, layer2.envelope, time);
|
|
1638
1760
|
source.connect(filter);
|
|
1639
1761
|
filter.connect(gain);
|
|
1640
1762
|
gain.connect(target.output);
|
|
1641
|
-
source.start(time, offsetOf(
|
|
1642
|
-
source.stop(end(
|
|
1763
|
+
source.start(time, offsetOf(layer2));
|
|
1764
|
+
source.stop(end(layer2, time));
|
|
1643
1765
|
return voiceOf([source]);
|
|
1644
1766
|
}
|
|
1645
1767
|
function realizeChord(plan, target) {
|
|
@@ -1651,14 +1773,14 @@ var GameAudio = (() => {
|
|
|
1651
1773
|
const source = layers[index].source;
|
|
1652
1774
|
if (source.kind === "osc") oscillators[index].type = source.wave;
|
|
1653
1775
|
}
|
|
1654
|
-
layers.forEach((
|
|
1655
|
-
if (
|
|
1776
|
+
layers.forEach((layer2, index) => {
|
|
1777
|
+
if (layer2.source.kind === "osc") assign(oscillators[index].frequency, layer2.source.frequency, target.time + layer2.at);
|
|
1656
1778
|
});
|
|
1657
1779
|
applyEnvelope(gain.gain, layers[0].envelope, target.time + layers[0].at);
|
|
1658
1780
|
for (const oscillator of oscillators) oscillator.connect(gain);
|
|
1659
1781
|
gain.connect(target.output);
|
|
1660
|
-
layers.forEach((
|
|
1661
|
-
layers.forEach((
|
|
1782
|
+
layers.forEach((layer2, index) => oscillators[index].start(target.time + layer2.at));
|
|
1783
|
+
layers.forEach((layer2, index) => oscillators[index].stop(end(layer2, target.time + layer2.at)));
|
|
1662
1784
|
return voiceOf(oscillators);
|
|
1663
1785
|
}
|
|
1664
1786
|
function realizeCalls(plan, target) {
|
|
@@ -1670,34 +1792,34 @@ var GameAudio = (() => {
|
|
|
1670
1792
|
if (band) filter.type = band.type;
|
|
1671
1793
|
filter.connect(gain);
|
|
1672
1794
|
gain.connect(target.output);
|
|
1673
|
-
const oscillators = plan.layers.map((
|
|
1674
|
-
const time = target.time +
|
|
1795
|
+
const oscillators = plan.layers.map((layer2) => {
|
|
1796
|
+
const time = target.time + layer2.at;
|
|
1675
1797
|
const oscillator = context.createOscillator();
|
|
1676
|
-
if (
|
|
1677
|
-
oscillator.type =
|
|
1678
|
-
assign(oscillator.frequency,
|
|
1679
|
-
rampTo(oscillator.frequency,
|
|
1798
|
+
if (layer2.source.kind === "osc") {
|
|
1799
|
+
oscillator.type = layer2.source.wave;
|
|
1800
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1801
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1680
1802
|
}
|
|
1681
1803
|
if (band) {
|
|
1682
1804
|
assign(filter.frequency, band.frequency, time);
|
|
1683
1805
|
if (band.q !== void 0) filter.Q.value = band.q;
|
|
1684
1806
|
}
|
|
1685
1807
|
const envelope2 = context.createGain();
|
|
1686
|
-
applyEnvelope(envelope2.gain,
|
|
1808
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1687
1809
|
oscillator.connect(envelope2);
|
|
1688
1810
|
envelope2.connect(filter);
|
|
1689
1811
|
oscillator.start(time);
|
|
1690
|
-
oscillator.stop(end(
|
|
1812
|
+
oscillator.stop(end(layer2, time));
|
|
1691
1813
|
return oscillator;
|
|
1692
1814
|
});
|
|
1693
1815
|
return voiceOf(oscillators);
|
|
1694
1816
|
}
|
|
1695
1817
|
|
|
1696
1818
|
// src/webaudio/realize-layered.ts
|
|
1697
|
-
var end2 = (
|
|
1698
|
-
function outputStage2(plan,
|
|
1819
|
+
var end2 = (layer2, start) => start + layer2.duration + layer2.tail;
|
|
1820
|
+
function outputStage2(plan, layer2, target) {
|
|
1699
1821
|
const { context } = target;
|
|
1700
|
-
const level = gainNode(context,
|
|
1822
|
+
const level = gainNode(context, layer2.level);
|
|
1701
1823
|
let node = level;
|
|
1702
1824
|
if (plan.pan !== void 0 && typeof context.createStereoPanner === "function") {
|
|
1703
1825
|
const panner = context.createStereoPanner();
|
|
@@ -1706,54 +1828,54 @@ var GameAudio = (() => {
|
|
|
1706
1828
|
node = panner;
|
|
1707
1829
|
}
|
|
1708
1830
|
node.connect(target.output);
|
|
1709
|
-
if (
|
|
1710
|
-
const send = gainNode(context,
|
|
1831
|
+
if (layer2.wet && target.wet) {
|
|
1832
|
+
const send = gainNode(context, layer2.wet);
|
|
1711
1833
|
node.connect(send);
|
|
1712
1834
|
send.connect(target.wet);
|
|
1713
1835
|
}
|
|
1714
1836
|
return level;
|
|
1715
1837
|
}
|
|
1716
|
-
function realizeNoiseLayer(plan,
|
|
1838
|
+
function realizeNoiseLayer(plan, layer2, target) {
|
|
1717
1839
|
const { context } = target;
|
|
1718
|
-
const time = target.time +
|
|
1719
|
-
const source = noiseSource(target,
|
|
1720
|
-
const filters = (
|
|
1840
|
+
const time = target.time + layer2.at;
|
|
1841
|
+
const source = noiseSource(target, layer2.source.kind === "noise" && layer2.source.loop);
|
|
1842
|
+
const filters = (layer2.filters?.bank ?? []).map((filter) => {
|
|
1721
1843
|
const node = filterNode(context, filter, time);
|
|
1722
1844
|
rampTo(node.frequency, filter.frequency, time);
|
|
1723
1845
|
return node;
|
|
1724
1846
|
});
|
|
1725
1847
|
const envelope2 = context.createGain();
|
|
1726
|
-
applyEnvelope(envelope2.gain,
|
|
1848
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1727
1849
|
let tail = source;
|
|
1728
1850
|
for (const filter of filters) tail = tail.connect(filter);
|
|
1729
1851
|
tail.connect(envelope2);
|
|
1730
|
-
envelope2.connect(outputStage2(plan,
|
|
1731
|
-
source.start(time,
|
|
1732
|
-
source.stop(end2(
|
|
1852
|
+
envelope2.connect(outputStage2(plan, layer2, target));
|
|
1853
|
+
source.start(time, layer2.source.kind === "noise" ? layer2.source.offset : 0);
|
|
1854
|
+
source.stop(end2(layer2, time));
|
|
1733
1855
|
return [source];
|
|
1734
1856
|
}
|
|
1735
|
-
function realizeToneLayer(plan,
|
|
1857
|
+
function realizeToneLayer(plan, layer2, target) {
|
|
1736
1858
|
const { context } = target;
|
|
1737
|
-
const time = target.time +
|
|
1859
|
+
const time = target.time + layer2.at;
|
|
1738
1860
|
const oscillator = context.createOscillator();
|
|
1739
|
-
if (
|
|
1740
|
-
oscillator.type =
|
|
1741
|
-
assign(oscillator.frequency,
|
|
1742
|
-
rampTo(oscillator.frequency,
|
|
1861
|
+
if (layer2.source.kind === "osc") {
|
|
1862
|
+
oscillator.type = layer2.source.wave;
|
|
1863
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1864
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1743
1865
|
}
|
|
1744
1866
|
const envelope2 = context.createGain();
|
|
1745
|
-
applyEnvelope(envelope2.gain,
|
|
1867
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1746
1868
|
oscillator.connect(envelope2);
|
|
1747
|
-
envelope2.connect(outputStage2(plan,
|
|
1869
|
+
envelope2.connect(outputStage2(plan, layer2, target));
|
|
1748
1870
|
oscillator.start(time);
|
|
1749
|
-
oscillator.stop(end2(
|
|
1871
|
+
oscillator.stop(end2(layer2, time));
|
|
1750
1872
|
return [oscillator];
|
|
1751
1873
|
}
|
|
1752
|
-
function realizeVoiceLayer(plan,
|
|
1753
|
-
const source =
|
|
1874
|
+
function realizeVoiceLayer(plan, layer2, target) {
|
|
1875
|
+
const source = layer2.source;
|
|
1754
1876
|
if (source.kind !== "formant_voice") return [];
|
|
1755
1877
|
const { context } = target;
|
|
1756
|
-
const time = target.time +
|
|
1878
|
+
const time = target.time + layer2.at;
|
|
1757
1879
|
const oscillator = context.createOscillator();
|
|
1758
1880
|
oscillator.type = source.wave;
|
|
1759
1881
|
assign(oscillator.frequency, source.frequency, time);
|
|
@@ -1774,10 +1896,10 @@ var GameAudio = (() => {
|
|
|
1774
1896
|
for (const formant of formants) formant.connect(mix);
|
|
1775
1897
|
breathLevel.connect(mix);
|
|
1776
1898
|
const envelope2 = context.createGain();
|
|
1777
|
-
applyEnvelope(envelope2.gain,
|
|
1899
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1778
1900
|
mix.connect(envelope2);
|
|
1779
|
-
envelope2.connect(outputStage2(plan,
|
|
1780
|
-
const stop = end2(
|
|
1901
|
+
envelope2.connect(outputStage2(plan, layer2, target));
|
|
1902
|
+
const stop = end2(layer2, time);
|
|
1781
1903
|
oscillator.start(time);
|
|
1782
1904
|
vibrato.start(time);
|
|
1783
1905
|
breath.start(time, source.breath.offset);
|
|
@@ -1787,24 +1909,24 @@ var GameAudio = (() => {
|
|
|
1787
1909
|
return [oscillator, vibrato, breath];
|
|
1788
1910
|
}
|
|
1789
1911
|
function realizeLayered(plan, target) {
|
|
1790
|
-
const sources = plan.layers.flatMap((
|
|
1791
|
-
if (
|
|
1792
|
-
if (
|
|
1793
|
-
return realizeVoiceLayer(plan,
|
|
1912
|
+
const sources = plan.layers.flatMap((layer2) => {
|
|
1913
|
+
if (layer2.source.kind === "noise") return realizeNoiseLayer(plan, layer2, target);
|
|
1914
|
+
if (layer2.source.kind === "osc") return realizeToneLayer(plan, layer2, target);
|
|
1915
|
+
return realizeVoiceLayer(plan, layer2, target);
|
|
1794
1916
|
});
|
|
1795
1917
|
return voiceOf(sources);
|
|
1796
1918
|
}
|
|
1797
1919
|
function realizeSiren(plan, target) {
|
|
1798
1920
|
const { context } = target;
|
|
1799
|
-
const
|
|
1800
|
-
const time = target.time +
|
|
1921
|
+
const layer2 = plan.layers[0];
|
|
1922
|
+
const time = target.time + layer2.at;
|
|
1801
1923
|
const oscillator = context.createOscillator();
|
|
1802
|
-
if (
|
|
1803
|
-
oscillator.type =
|
|
1804
|
-
assign(oscillator.frequency,
|
|
1805
|
-
rampTo(oscillator.frequency,
|
|
1924
|
+
if (layer2.source.kind === "osc") {
|
|
1925
|
+
oscillator.type = layer2.source.wave;
|
|
1926
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1927
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1806
1928
|
}
|
|
1807
|
-
const depths =
|
|
1929
|
+
const depths = layer2.modulators.map((modulator) => {
|
|
1808
1930
|
const lfo = context.createOscillator();
|
|
1809
1931
|
if (modulator.wave) lfo.type = modulator.wave;
|
|
1810
1932
|
lfo.frequency.value = modulator.rate;
|
|
@@ -1815,19 +1937,19 @@ var GameAudio = (() => {
|
|
|
1815
1937
|
});
|
|
1816
1938
|
const lfos = depths.map(({ lfo }) => lfo);
|
|
1817
1939
|
const envelope2 = context.createGain();
|
|
1818
|
-
applyEnvelope(envelope2.gain,
|
|
1940
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1819
1941
|
for (const { depth, target: modulated } of depths) if (modulated === "gain") depth.connect(envelope2.gain);
|
|
1820
|
-
const filters = (
|
|
1942
|
+
const filters = (layer2.filters?.bank ?? []).map((filter) => filterNode(context, filter, time));
|
|
1821
1943
|
let tail = oscillator;
|
|
1822
1944
|
for (const filter of filters) tail = tail.connect(filter);
|
|
1823
1945
|
tail.connect(envelope2);
|
|
1824
1946
|
envelope2.connect(target.output);
|
|
1825
|
-
if (
|
|
1826
|
-
const send = gainNode(context,
|
|
1947
|
+
if (layer2.wet && target.wet) {
|
|
1948
|
+
const send = gainNode(context, layer2.wet);
|
|
1827
1949
|
envelope2.connect(send);
|
|
1828
1950
|
send.connect(target.wet);
|
|
1829
1951
|
}
|
|
1830
|
-
const stop = end2(
|
|
1952
|
+
const stop = end2(layer2, time);
|
|
1831
1953
|
oscillator.start(time);
|
|
1832
1954
|
for (const lfo of lfos) lfo.start(time);
|
|
1833
1955
|
oscillator.stop(stop);
|
|
@@ -1836,38 +1958,38 @@ var GameAudio = (() => {
|
|
|
1836
1958
|
}
|
|
1837
1959
|
|
|
1838
1960
|
// src/webaudio/realize-weapon.ts
|
|
1839
|
-
function noiseLayer(
|
|
1961
|
+
function noiseLayer(layer2, target, time) {
|
|
1840
1962
|
const { context } = target;
|
|
1841
|
-
const source = noiseSource(target,
|
|
1842
|
-
const filters = (
|
|
1963
|
+
const source = noiseSource(target, layer2.source.kind === "noise" && layer2.source.loop);
|
|
1964
|
+
const filters = (layer2.filters?.bank ?? []).map((filter) => filterNode(context, filter, time));
|
|
1843
1965
|
const envelope2 = context.createGain();
|
|
1844
|
-
applyEnvelope(envelope2.gain,
|
|
1966
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1845
1967
|
let tail = source;
|
|
1846
1968
|
for (const filter of filters) tail = tail.connect(filter);
|
|
1847
1969
|
tail.connect(envelope2).connect(target.output);
|
|
1848
|
-
source.start(time,
|
|
1849
|
-
source.stop(time +
|
|
1970
|
+
source.start(time, layer2.source.kind === "noise" ? layer2.source.offset : 0);
|
|
1971
|
+
source.stop(time + layer2.duration + layer2.tail);
|
|
1850
1972
|
return source;
|
|
1851
1973
|
}
|
|
1852
|
-
function toneLayer(
|
|
1974
|
+
function toneLayer(layer2, target, time) {
|
|
1853
1975
|
const { context } = target;
|
|
1854
1976
|
const oscillator = context.createOscillator();
|
|
1855
|
-
if (
|
|
1856
|
-
oscillator.type =
|
|
1857
|
-
assign(oscillator.frequency,
|
|
1858
|
-
rampTo(oscillator.frequency,
|
|
1977
|
+
if (layer2.source.kind === "osc") {
|
|
1978
|
+
oscillator.type = layer2.source.wave;
|
|
1979
|
+
assign(oscillator.frequency, layer2.source.frequency, time);
|
|
1980
|
+
rampTo(oscillator.frequency, layer2.source.frequency, time);
|
|
1859
1981
|
}
|
|
1860
1982
|
const envelope2 = context.createGain();
|
|
1861
|
-
applyEnvelope(envelope2.gain,
|
|
1983
|
+
applyEnvelope(envelope2.gain, layer2.envelope, time);
|
|
1862
1984
|
oscillator.connect(envelope2).connect(target.output);
|
|
1863
1985
|
oscillator.start(time);
|
|
1864
|
-
oscillator.stop(time +
|
|
1986
|
+
oscillator.stop(time + layer2.duration + layer2.tail);
|
|
1865
1987
|
return oscillator;
|
|
1866
1988
|
}
|
|
1867
1989
|
function realizeWeapon(plan, target) {
|
|
1868
|
-
const sources = plan.layers.map((
|
|
1869
|
-
const time = target.time +
|
|
1870
|
-
return
|
|
1990
|
+
const sources = plan.layers.map((layer2) => {
|
|
1991
|
+
const time = target.time + layer2.at;
|
|
1992
|
+
return layer2.source.kind === "noise" ? noiseLayer(layer2, target, time) : toneLayer(layer2, target, time);
|
|
1871
1993
|
});
|
|
1872
1994
|
return voiceOf(sources);
|
|
1873
1995
|
}
|
|
@@ -1919,19 +2041,19 @@ var GameAudio = (() => {
|
|
|
1919
2041
|
rate: modulator.rate * rate,
|
|
1920
2042
|
...modulator.target === "frequency" ? { depth: modulator.depth * rate } : {}
|
|
1921
2043
|
});
|
|
1922
|
-
var tuneLayer = (
|
|
1923
|
-
...
|
|
1924
|
-
at:
|
|
1925
|
-
duration:
|
|
1926
|
-
tail:
|
|
1927
|
-
source: tuneSource(
|
|
1928
|
-
...
|
|
1929
|
-
modulators:
|
|
1930
|
-
envelope:
|
|
2044
|
+
var tuneLayer = (layer2, rate) => ({
|
|
2045
|
+
...layer2,
|
|
2046
|
+
at: layer2.at / rate,
|
|
2047
|
+
duration: layer2.duration / rate,
|
|
2048
|
+
tail: layer2.tail / rate,
|
|
2049
|
+
source: tuneSource(layer2.source, rate),
|
|
2050
|
+
...layer2.filters ? { filters: { ...layer2.filters, bank: layer2.filters.bank.map((filter) => tuneFilter(filter, rate)) } } : {},
|
|
2051
|
+
modulators: layer2.modulators.map((modulator) => tuneModulator(modulator, rate)),
|
|
2052
|
+
envelope: layer2.envelope.map((point) => ({ ...point, time: point.time / rate }))
|
|
1931
2053
|
});
|
|
1932
2054
|
function retunePlan(plan, rate) {
|
|
1933
2055
|
if (rate === 1 || !(rate > 0)) return plan;
|
|
1934
|
-
return { ...plan, length: plan.length / rate, layers: plan.layers.map((
|
|
2056
|
+
return { ...plan, length: plan.length / rate, layers: plan.layers.map((layer2) => tuneLayer(layer2, rate)) };
|
|
1935
2057
|
}
|
|
1936
2058
|
function createSetPicker() {
|
|
1937
2059
|
const last = /* @__PURE__ */ new Map();
|
|
@@ -2034,6 +2156,7 @@ var GameAudio = (() => {
|
|
|
2034
2156
|
}
|
|
2035
2157
|
|
|
2036
2158
|
// src/webaudio/realize-sample.ts
|
|
2159
|
+
var TRIM_FADE = 0.04;
|
|
2037
2160
|
function realizeSample(plan, target) {
|
|
2038
2161
|
const context = target.context;
|
|
2039
2162
|
let head = target.output;
|
|
@@ -2055,16 +2178,26 @@ var GameAudio = (() => {
|
|
|
2055
2178
|
voice2.connect(head);
|
|
2056
2179
|
head = voice2;
|
|
2057
2180
|
const sources = [];
|
|
2058
|
-
for (const
|
|
2059
|
-
const buffer = target.buffer(
|
|
2181
|
+
for (const layer2 of plan.layers) {
|
|
2182
|
+
const buffer = target.buffer(layer2.url);
|
|
2060
2183
|
if (!buffer) continue;
|
|
2061
2184
|
const source = context.createBufferSource();
|
|
2062
2185
|
source.buffer = buffer;
|
|
2063
|
-
source.playbackRate.value =
|
|
2064
|
-
const gain = gainNode(context,
|
|
2186
|
+
source.playbackRate.value = layer2.rate;
|
|
2187
|
+
const gain = gainNode(context, layer2.gain);
|
|
2065
2188
|
source.connect(gain);
|
|
2066
2189
|
gain.connect(head);
|
|
2067
|
-
|
|
2190
|
+
const at = target.time + layer2.at;
|
|
2191
|
+
const offset = Math.max(0, Math.min(layer2.offset ?? 0, buffer.duration));
|
|
2192
|
+
if (layer2.duration === void 0) source.start(at, offset);
|
|
2193
|
+
else {
|
|
2194
|
+
const duration = Math.max(0, Math.min(layer2.duration, buffer.duration - offset));
|
|
2195
|
+
const end3 = at + duration / layer2.rate;
|
|
2196
|
+
const fade = Math.min(TRIM_FADE, (end3 - at) / 4);
|
|
2197
|
+
gain.gain.setValueAtTime(layer2.gain, end3 - fade);
|
|
2198
|
+
gain.gain.linearRampToValueAtTime(0, end3);
|
|
2199
|
+
source.start(at, offset, duration);
|
|
2200
|
+
}
|
|
2068
2201
|
sources.push(source);
|
|
2069
2202
|
}
|
|
2070
2203
|
const realized = voiceOf(sources);
|
|
@@ -2084,7 +2217,11 @@ var GameAudio = (() => {
|
|
|
2084
2217
|
}
|
|
2085
2218
|
function sampleLength(plan, seconds) {
|
|
2086
2219
|
let length = 0;
|
|
2087
|
-
for (const
|
|
2220
|
+
for (const layer2 of plan.layers) {
|
|
2221
|
+
const file = seconds(layer2.url) ?? 0;
|
|
2222
|
+
const played = layer2.duration === void 0 ? file - (layer2.offset ?? 0) : Math.min(layer2.duration, file - (layer2.offset ?? 0));
|
|
2223
|
+
length = Math.max(length, layer2.at + Math.max(0, played) / layer2.rate);
|
|
2224
|
+
}
|
|
2088
2225
|
return length;
|
|
2089
2226
|
}
|
|
2090
2227
|
|
|
@@ -2100,8 +2237,8 @@ var GameAudio = (() => {
|
|
|
2100
2237
|
}
|
|
2101
2238
|
return curve;
|
|
2102
2239
|
}
|
|
2103
|
-
var usesNoise = (recipe2) => recipe2.layers.some((
|
|
2104
|
-
var planUsesNoise = (plan) => plan.layers.some((
|
|
2240
|
+
var usesNoise = (recipe2) => recipe2.layers.some((layer2) => layer2.source.kind !== "osc");
|
|
2241
|
+
var planUsesNoise = (plan) => plan.layers.some((layer2) => layer2.source.kind !== "osc");
|
|
2105
2242
|
function createAudioEngine(options = {}) {
|
|
2106
2243
|
const seed = options.seed ?? 1;
|
|
2107
2244
|
const registry = createSoundRegistry(options.banks);
|
|
@@ -2293,6 +2430,7 @@ var GameAudio = (() => {
|
|
|
2293
2430
|
const set = registry.set(id);
|
|
2294
2431
|
return set ? chooser.pick(set, random) : { id, rate: 1 };
|
|
2295
2432
|
};
|
|
2433
|
+
const wanted = /* @__PURE__ */ new Set();
|
|
2296
2434
|
const loadSounds = (sounds) => {
|
|
2297
2435
|
const audio = ready();
|
|
2298
2436
|
if (!audio) return Promise.resolve({ loaded: [], failed: sounds.flatMap(sampleUrls).map((url2) => ({ url: url2, error: "no audio context" })) });
|
|
@@ -2340,7 +2478,7 @@ var GameAudio = (() => {
|
|
|
2340
2478
|
level: plan.level,
|
|
2341
2479
|
distance: plan.distance,
|
|
2342
2480
|
...plan.pan === void 0 ? {} : { pan: plan.pan },
|
|
2343
|
-
layers: plan.layers.map((
|
|
2481
|
+
layers: plan.layers.map((layer2) => ({ role: layer2.role, url: layer2.url, take: layer2.take, rate: layer2.rate })),
|
|
2344
2482
|
...plan.tier ? { tier: plan.tier } : {}
|
|
2345
2483
|
});
|
|
2346
2484
|
voices.setLength(voiceId, sampleLength(plan, (url2) => sampleStore.seconds(url2)) + (playOptions.delay ?? 0));
|
|
@@ -2417,13 +2555,20 @@ var GameAudio = (() => {
|
|
|
2417
2555
|
};
|
|
2418
2556
|
return {
|
|
2419
2557
|
registry,
|
|
2420
|
-
register: (bank) =>
|
|
2558
|
+
register: (bank) => {
|
|
2559
|
+
registry.register(bank);
|
|
2560
|
+
if (wanted.size === 0) return;
|
|
2561
|
+
const now = [...wanted].map((id) => registry.sample(id)).filter((sample) => sample !== void 0);
|
|
2562
|
+
for (const sample of now) wanted.delete(sample.id);
|
|
2563
|
+
if (now.length > 0) void loadSounds(now).catch(() => void 0);
|
|
2564
|
+
},
|
|
2421
2565
|
plan: (id, playOptions = {}) => planWith(id, playOptions, createRng(playOptions.seed ?? seed)),
|
|
2422
2566
|
planSample(id, playOptions = {}) {
|
|
2423
2567
|
const sample = registry.sample(id);
|
|
2424
2568
|
return sample ? planSample(sample, sampleInput(playOptions, createRng(playOptions.seed ?? seed), false)) : null;
|
|
2425
2569
|
},
|
|
2426
2570
|
loadSamples(ids) {
|
|
2571
|
+
for (const id of ids ?? []) if (!registry.sample(id)) wanted.add(id);
|
|
2427
2572
|
const chosen = (ids ?? registry.sampleIds()).map((id) => registry.sample(id)).filter((sample) => sample !== void 0);
|
|
2428
2573
|
return loadSounds(chosen);
|
|
2429
2574
|
},
|
|
@@ -2473,7 +2618,9 @@ var GameAudio = (() => {
|
|
|
2473
2618
|
random: engineRandom,
|
|
2474
2619
|
register: loopHost.register,
|
|
2475
2620
|
unregister: loopHost.unregister,
|
|
2476
|
-
muted: (bus) => busSettings.get(bus)?.muted === true
|
|
2621
|
+
muted: (bus) => busSettings.get(bus)?.muted === true,
|
|
2622
|
+
listener: () => listener,
|
|
2623
|
+
...options.distance ? { distance: options.distance } : {}
|
|
2477
2624
|
},
|
|
2478
2625
|
loopOptions.volume
|
|
2479
2626
|
);
|
|
@@ -2787,12 +2934,12 @@ var GameAudio = (() => {
|
|
|
2787
2934
|
brightness.push(onset < 0 ? 0 : centroidHz(result.channels, result.sampleRate, onset));
|
|
2788
2935
|
}
|
|
2789
2936
|
const far = planSample(sound, { random: createRng(1), distance: CRITERIA_TARGETS.distances.at(-1) });
|
|
2790
|
-
const switched = (far?.farMix ?? 0) === 1 && (far?.layers.some((
|
|
2937
|
+
const switched = (far?.farMix ?? 0) === 1 && (far?.layers.some((layer2) => layer2.role === "far") ?? false);
|
|
2791
2938
|
const pass = decreasing(levels) && decreasing(brightness) && switched;
|
|
2792
2939
|
return {
|
|
2793
2940
|
id: "C3",
|
|
2794
2941
|
pass,
|
|
2795
|
-
value: `${levels.map((level) => db(level)).join(" \u203A ")}; ${brightness.map((
|
|
2942
|
+
value: `${levels.map((level) => db(level)).join(" \u203A ")}; ${brightness.map((hz2) => `${Math.round(hz2)} Hz`).join(" \u203A ")}${switched ? "; longe = far" : "; sem camada far"}`,
|
|
2796
2943
|
target: "cai e escurece com a dist\xE2ncia; troca para a vers\xE3o de longe depois de ~50 m",
|
|
2797
2944
|
data: { distances: CRITERIA_TARGETS.distances, levels, brightness, switched }
|
|
2798
2945
|
};
|
|
@@ -2827,11 +2974,11 @@ var GameAudio = (() => {
|
|
|
2827
2974
|
for (let index = 0; index < CRITERIA_TARGETS.plays; index++) if (engine.play(options.sound, { distance: 1, delay: 0.1 + index * 0.3 })) played++;
|
|
2828
2975
|
return played;
|
|
2829
2976
|
});
|
|
2830
|
-
const close = result.events.map((event) => event.layers?.find((
|
|
2831
|
-
const takes2 = close.map((
|
|
2977
|
+
const close = result.events.map((event) => event.layers?.find((layer2) => layer2.role === "close"));
|
|
2978
|
+
const takes2 = close.map((layer2) => layer2?.url ?? "");
|
|
2832
2979
|
const repeats = takes2.filter((take, index) => index > 0 && take === takes2[index - 1]).length;
|
|
2833
2980
|
const distinct = new Set(takes2).size;
|
|
2834
|
-
const rates = close.map((
|
|
2981
|
+
const rates = close.map((layer2) => layer2?.rate ?? 1);
|
|
2835
2982
|
const spread = (Math.max(...rates) - Math.min(...rates)) / 2;
|
|
2836
2983
|
const pitch = sound.pitch ?? 0.04;
|
|
2837
2984
|
const pass = distinct >= CRITERIA_TARGETS.minTakes && repeats === 0 && pitch >= CRITERIA_TARGETS.pitch[0] && pitch <= CRITERIA_TARGETS.pitch[1] && spread > 0.01;
|
|
@@ -2869,7 +3016,7 @@ var GameAudio = (() => {
|
|
|
2869
3016
|
const result = await render(options, 1.2, (engine) => engine.play(options.sound, { distance: 1, seed: 1, intensity, delay: EVENT_AT }) ? 1 : 0);
|
|
2870
3017
|
const onset = onsetIndex(result.channels, 0, 1e-5);
|
|
2871
3018
|
const event = result.events[0];
|
|
2872
|
-
return { tier: event?.tier ?? "", rms: onset < 0 ? -Infinity : rmsDb(result.channels, onset, onset + result.sampleRate * 0.3), files: event?.layers?.find((
|
|
3019
|
+
return { tier: event?.tier ?? "", rms: onset < 0 ? -Infinity : rmsDb(result.channels, onset, onset + result.sampleRate * 0.3), files: event?.layers?.find((layer2) => layer2.role === "close")?.url ?? "" };
|
|
2873
3020
|
};
|
|
2874
3021
|
const light = await at(0.1);
|
|
2875
3022
|
const heavy = await at(0.9);
|
|
@@ -3212,9 +3359,9 @@ var GameAudio = (() => {
|
|
|
3212
3359
|
function parseSfxName(name) {
|
|
3213
3360
|
const match = /^(.*?)(?:_(close|far|tail_in|tail_out))?(?:_(\d+))?$/.exec(name);
|
|
3214
3361
|
const id = match?.[1] || name;
|
|
3215
|
-
const
|
|
3362
|
+
const layer2 = match?.[2] ?? "close";
|
|
3216
3363
|
const take = match?.[3] ? Number(match[3]) : 1;
|
|
3217
|
-
return { id, layer, take };
|
|
3364
|
+
return { id, layer: layer2, take };
|
|
3218
3365
|
}
|
|
3219
3366
|
function splitTier(id) {
|
|
3220
3367
|
const match = /^(.*)_(light|medium|heavy)$/.exec(id);
|
|
@@ -3290,7 +3437,16 @@ var GameAudio = (() => {
|
|
|
3290
3437
|
// a drawer stays on its recipe: the recorded runs (1 to 1.9 s) outlast a drawer's pull
|
|
3291
3438
|
// an item taken in the hand (game-destruction-three grab_* keys)
|
|
3292
3439
|
grab_hard: "item_pickup_generic",
|
|
3293
|
-
grab_soft: "item_pickup_leather"
|
|
3440
|
+
grab_soft: "item_pickup_leather",
|
|
3441
|
+
// the hand foley of the test room (game-audio FOLEY_BANK) whose id is not a file id: a wooden leaf shut and slammed, an item picked up by material.
|
|
3442
|
+
// The metal door stays on its recipe (two of its takes run 10 s), and so do the fridge, the microwave and paper (no file for them).
|
|
3443
|
+
door_wood_close: "door_wood_shut",
|
|
3444
|
+
door_wood_slam: "door_slam_wood",
|
|
3445
|
+
item_pickup_metal: "item_pickup_generic",
|
|
3446
|
+
item_pickup_plastic: "item_pickup_generic",
|
|
3447
|
+
item_pickup_glass: "item_pickup_generic",
|
|
3448
|
+
item_pickup_wood: "item_pickup_generic",
|
|
3449
|
+
item_pickup_fabric: "item_pickup_leather"
|
|
3294
3450
|
};
|
|
3295
3451
|
function kitSampleDefaults(sound) {
|
|
3296
3452
|
const bus = VOICE_IDS.test(sound.id) ? "voices" : KIT_CATEGORY_BUS[sound.category];
|
|
@@ -3340,6 +3496,20 @@ var GameAudio = (() => {
|
|
|
3340
3496
|
if (options.preload && engine.loadSamples) void engine.loadSamples(options.preload).catch(() => void 0);
|
|
3341
3497
|
return bank;
|
|
3342
3498
|
}
|
|
3499
|
+
function pointSamples(engine, files, options = {}) {
|
|
3500
|
+
const baseUrl = options.baseUrl ?? "";
|
|
3501
|
+
const samples = Object.entries(files).map(([id, urls]) => {
|
|
3502
|
+
const close = (typeof urls === "string" ? [urls] : [...urls]).map((url2) => ({ url: `${baseUrl}${url2}` }));
|
|
3503
|
+
if (close.length === 0) throw new Error(`pointSamples: ${id} has no file`);
|
|
3504
|
+
const kit = engine.registry.sample(id);
|
|
3505
|
+
if (!kit) return { id, bus: options.bus ?? "world", close, sources: ["game: pointSamples"] };
|
|
3506
|
+
const { far: _far, tiers: _tiers, tail: _tail, ...numbers } = kit;
|
|
3507
|
+
return { ...numbers, close };
|
|
3508
|
+
});
|
|
3509
|
+
const bank = { id: options.id ?? "game_samples", samples };
|
|
3510
|
+
engine.register(bank);
|
|
3511
|
+
return bank;
|
|
3512
|
+
}
|
|
3343
3513
|
|
|
3344
3514
|
// src/user-samples.ts
|
|
3345
3515
|
var USER_SAMPLES_BANK = "user_samples";
|
|
@@ -3394,6 +3564,145 @@ var GameAudio = (() => {
|
|
|
3394
3564
|
return result;
|
|
3395
3565
|
}
|
|
3396
3566
|
|
|
3567
|
+
// src/local-pack.ts
|
|
3568
|
+
var LOCAL_PACK_LICENSES = ["CC0", "CC-BY", "unknown", "proprietary"];
|
|
3569
|
+
var LOCAL_PACK_PREFERENCES = ["pack", "sdk"];
|
|
3570
|
+
var LocalSoundPackError = class extends Error {
|
|
3571
|
+
constructor(problems) {
|
|
3572
|
+
super(`invalid local sound pack: ${problems.join("; ")}`);
|
|
3573
|
+
__publicField(this, "problems");
|
|
3574
|
+
this.name = "LocalSoundPackError";
|
|
3575
|
+
this.problems = problems;
|
|
3576
|
+
}
|
|
3577
|
+
};
|
|
3578
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3579
|
+
var isLicense = (value) => LOCAL_PACK_LICENSES.includes(value);
|
|
3580
|
+
var isBus = (value) => BUS_IDS.includes(value);
|
|
3581
|
+
function entryProblems(id, entry) {
|
|
3582
|
+
if (!isRecord(entry)) return [`${id}: not an object`];
|
|
3583
|
+
const problems = [];
|
|
3584
|
+
const files = entry.files;
|
|
3585
|
+
if (!Array.isArray(files) || files.length === 0 || !files.every((file) => typeof file === "string" && file.length > 0)) problems.push(`${id}: files must be a non-empty list of paths`);
|
|
3586
|
+
if (!isLicense(entry.license)) problems.push(`${id}: license must be one of ${LOCAL_PACK_LICENSES.join(", ")}`);
|
|
3587
|
+
if (entry.prefer !== void 0 && !LOCAL_PACK_PREFERENCES.includes(entry.prefer)) problems.push(`${id}: prefer must be pack or sdk`);
|
|
3588
|
+
if (entry.bus !== void 0 && !isBus(entry.bus)) problems.push(`${id}: unknown bus ${String(entry.bus)}`);
|
|
3589
|
+
if (entry.gain !== void 0 && (typeof entry.gain !== "number" || entry.gain < 0)) problems.push(`${id}: gain must be a number \u2265 0`);
|
|
3590
|
+
if (entry.score !== void 0 && (typeof entry.score !== "number" || entry.score < 0 || entry.score > 10)) problems.push(`${id}: score must be 0\u201310`);
|
|
3591
|
+
return problems;
|
|
3592
|
+
}
|
|
3593
|
+
function parseLocalSoundPack(value) {
|
|
3594
|
+
if (!isRecord(value)) throw new LocalSoundPackError(["the pack is not an object"]);
|
|
3595
|
+
const problems = [];
|
|
3596
|
+
if (value.version !== 1) problems.push("version must be 1");
|
|
3597
|
+
if (value.baseUrl !== void 0 && typeof value.baseUrl !== "string") problems.push("baseUrl must be a string");
|
|
3598
|
+
if (!isRecord(value.sounds)) problems.push("sounds must be an object of id \u2192 entry");
|
|
3599
|
+
else for (const [id, entry] of Object.entries(value.sounds)) problems.push(...entryProblems(id, entry));
|
|
3600
|
+
if (problems.length > 0) throw new LocalSoundPackError(problems);
|
|
3601
|
+
return value;
|
|
3602
|
+
}
|
|
3603
|
+
var isShippableLicense = (license) => SDK_LICENSES.includes(license);
|
|
3604
|
+
var CATALOG = new Map([...SFX_CATALOG, ...SFX_IMPACTS].map((entry) => [entry.id, entry]));
|
|
3605
|
+
var busOf = (id, entry) => {
|
|
3606
|
+
if (entry.bus) return entry.bus;
|
|
3607
|
+
const category = CATALOG.get(id)?.category ?? "";
|
|
3608
|
+
return kitSampleDefaults({ id, category })?.bus ?? "world";
|
|
3609
|
+
};
|
|
3610
|
+
function localPackSamples(pack, options = {}) {
|
|
3611
|
+
const baseUrl = options.baseUrl ?? pack.baseUrl ?? "";
|
|
3612
|
+
return Object.entries(pack.sounds).map(([id, entry]) => {
|
|
3613
|
+
const current = CATALOG.get(id)?.current;
|
|
3614
|
+
const isLoop = /_loop(_|$)/.test(id);
|
|
3615
|
+
return {
|
|
3616
|
+
id,
|
|
3617
|
+
bus: busOf(id, entry),
|
|
3618
|
+
close: entry.files.map((file) => ({ url: `${baseUrl}${file}` })),
|
|
3619
|
+
pitch: isLoop ? 0 : 0.04,
|
|
3620
|
+
...isLoop ? { loop: true } : {},
|
|
3621
|
+
...entry.gain === void 0 ? {} : { gain: entry.gain },
|
|
3622
|
+
...current ? { fallback: current } : {},
|
|
3623
|
+
license: entry.license,
|
|
3624
|
+
sources: entry.files.map((file) => `local: ${file} ${entry.license}`)
|
|
3625
|
+
};
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
// src/local-pack-loader.ts
|
|
3630
|
+
var LOCAL_PACK_BANK_ID = "local_sound_pack";
|
|
3631
|
+
var STAGING_PREFIX = "local_pack_staging:";
|
|
3632
|
+
var defaultIdleScheduler = (task) => {
|
|
3633
|
+
const idle = globalThis;
|
|
3634
|
+
if (idle.requestIdleCallback && idle.cancelIdleCallback) {
|
|
3635
|
+
const handle2 = idle.requestIdleCallback(task);
|
|
3636
|
+
const cancel = idle.cancelIdleCallback;
|
|
3637
|
+
return () => cancel(handle2);
|
|
3638
|
+
}
|
|
3639
|
+
const handle = setTimeout(task, 0);
|
|
3640
|
+
return () => clearTimeout(handle);
|
|
3641
|
+
};
|
|
3642
|
+
function applyLocalSoundPack(engine, pack, options = {}) {
|
|
3643
|
+
const schedule = options.schedule ?? defaultIdleScheduler;
|
|
3644
|
+
const samples = localPackSamples(pack, options);
|
|
3645
|
+
const statuses = new Map(samples.map((sample) => [sample.id, "pending"]));
|
|
3646
|
+
const priors = /* @__PURE__ */ new Map();
|
|
3647
|
+
const report = { file: [], sdk: [], fallback: [] };
|
|
3648
|
+
const queue = [...samples];
|
|
3649
|
+
let cancel;
|
|
3650
|
+
let disposed = false;
|
|
3651
|
+
let finish = () => void 0;
|
|
3652
|
+
const done = new Promise((resolve) => {
|
|
3653
|
+
finish = resolve;
|
|
3654
|
+
});
|
|
3655
|
+
const swapIn = (sample) => {
|
|
3656
|
+
const prior = engine.registry.sample(sample.id);
|
|
3657
|
+
const bank = engine.registry.bankOf(sample.id);
|
|
3658
|
+
priors.set(sample.id, { ...prior ? { sample: prior } : {}, ...bank ? { bank } : {} });
|
|
3659
|
+
engine.register({ id: LOCAL_PACK_BANK_ID, samples: [{ ...sample, ...prior?.fallback ? { fallback: prior.fallback } : {} }] });
|
|
3660
|
+
statuses.set(sample.id, "file");
|
|
3661
|
+
report.file.push(sample.id);
|
|
3662
|
+
};
|
|
3663
|
+
const loadOne = async (sample) => {
|
|
3664
|
+
if (pack.sounds[sample.id]?.prefer === "sdk" && engine.registry.sample(sample.id)) {
|
|
3665
|
+
statuses.set(sample.id, "sdk");
|
|
3666
|
+
report.sdk.push(sample.id);
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3669
|
+
const staging = `${STAGING_PREFIX}${sample.id}`;
|
|
3670
|
+
engine.register({ id: LOCAL_PACK_BANK_ID, samples: [{ ...sample, id: staging }] });
|
|
3671
|
+
const loaded = await engine.loadSamples([staging]);
|
|
3672
|
+
engine.registry.removeSample(staging);
|
|
3673
|
+
if (disposed) return;
|
|
3674
|
+
if (loaded.failed.length === 0) return swapIn(sample);
|
|
3675
|
+
statuses.set(sample.id, "fallback");
|
|
3676
|
+
report.fallback.push({ id: sample.id, error: loaded.failed.map((entry) => `${entry.url}: ${entry.error}`).join("; ") });
|
|
3677
|
+
};
|
|
3678
|
+
const next = () => {
|
|
3679
|
+
const sample = queue.shift();
|
|
3680
|
+
if (!sample || disposed) {
|
|
3681
|
+
if (!disposed) finish(report);
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
void loadOne(sample).finally(() => {
|
|
3685
|
+
if (!disposed) cancel = schedule(next);
|
|
3686
|
+
});
|
|
3687
|
+
};
|
|
3688
|
+
cancel = schedule(next);
|
|
3689
|
+
return {
|
|
3690
|
+
done,
|
|
3691
|
+
status: (id) => statuses.get(id),
|
|
3692
|
+
unlicensed: () => samples.filter((sample) => statuses.get(sample.id) === "file" && !isShippableLicense(sample.license)).map((sample) => sample.id),
|
|
3693
|
+
dispose() {
|
|
3694
|
+
disposed = true;
|
|
3695
|
+
cancel?.();
|
|
3696
|
+
for (const [id, prior] of priors) {
|
|
3697
|
+
if (prior.sample) engine.register({ id: prior.bank ?? LOCAL_PACK_BANK_ID, samples: [prior.sample] });
|
|
3698
|
+
else engine.registry.removeSample(id);
|
|
3699
|
+
}
|
|
3700
|
+
priors.clear();
|
|
3701
|
+
finish(report);
|
|
3702
|
+
}
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3397
3706
|
// src/data/game-sfx.json
|
|
3398
3707
|
var game_sfx_default = {
|
|
3399
3708
|
sources: ["jogo:L62089-62107"],
|
|
@@ -3693,10 +4002,140 @@ var GameAudio = (() => {
|
|
|
3693
4002
|
duration: 0.5,
|
|
3694
4003
|
layers: [{ source: { kind: "osc", wave: "sine", frequency: { set: 880 } }, repeat: { count: 3, spacing: 0.7 }, envelope: [[0, 0.06, "set"], [0.5, 1e-4, "exp", "fixed"]] }],
|
|
3695
4004
|
falloff: { model: "none" }
|
|
4005
|
+
},
|
|
4006
|
+
{
|
|
4007
|
+
// the parking sensor: a short high beep, repeated by the cabin faster as the obstacle comes close
|
|
4008
|
+
id: "parking_beep",
|
|
4009
|
+
bus: "world",
|
|
4010
|
+
duration: 0.09,
|
|
4011
|
+
layers: [{ source: { kind: "osc", wave: "sine", frequency: { set: 2300 } }, envelope: [[0, 0.07, "set"], [0.08, 1e-4, "exp", "fixed"]] }],
|
|
4012
|
+
falloff: { model: "none" }
|
|
4013
|
+
},
|
|
4014
|
+
{
|
|
4015
|
+
// an emergency siren's wail: a tone rising and falling slowly (one sweep about every 3 s), played again by the cabin as it ends
|
|
4016
|
+
id: "siren_wail",
|
|
4017
|
+
bus: "world",
|
|
4018
|
+
order: "siren",
|
|
4019
|
+
duration: 3,
|
|
4020
|
+
tail: 0.05,
|
|
4021
|
+
layers: [
|
|
4022
|
+
{
|
|
4023
|
+
source: { kind: "osc", wave: "sawtooth", frequency: { set: 1050 } },
|
|
4024
|
+
modulators: [{ target: "frequency", wave: "sine", rate: 0.33, depth: 420 }],
|
|
4025
|
+
filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 2400 } }] },
|
|
4026
|
+
envelope: [[0, 1e-4, "set", "fixed"], [0.08, 0.07, "linear"], [2.95, 0.07, "set"], [3, 1e-4, "exp", "fixed"]]
|
|
4027
|
+
}
|
|
4028
|
+
],
|
|
4029
|
+
falloff: { model: "none" }
|
|
4030
|
+
},
|
|
4031
|
+
{
|
|
4032
|
+
// the yelp: the same tone swept fast, for a junction
|
|
4033
|
+
id: "siren_yelp",
|
|
4034
|
+
bus: "world",
|
|
4035
|
+
order: "siren",
|
|
4036
|
+
duration: 2,
|
|
4037
|
+
tail: 0.05,
|
|
4038
|
+
layers: [
|
|
4039
|
+
{
|
|
4040
|
+
source: { kind: "osc", wave: "sawtooth", frequency: { set: 1100 } },
|
|
4041
|
+
modulators: [{ target: "frequency", wave: "triangle", rate: 4, depth: 380 }],
|
|
4042
|
+
filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 2600 } }] },
|
|
4043
|
+
envelope: [[0, 1e-4, "set", "fixed"], [0.05, 0.07, "linear"], [1.95, 0.07, "set"], [2, 1e-4, "exp", "fixed"]]
|
|
4044
|
+
}
|
|
4045
|
+
],
|
|
4046
|
+
falloff: { model: "none" }
|
|
4047
|
+
},
|
|
4048
|
+
{
|
|
4049
|
+
// the wires under the column touching: a crackle of bright noise
|
|
4050
|
+
id: "hotwire_spark",
|
|
4051
|
+
bus: "world",
|
|
4052
|
+
duration: 0.12,
|
|
4053
|
+
layers: [
|
|
4054
|
+
{
|
|
4055
|
+
source: { kind: "noise", offset: "random" },
|
|
4056
|
+
filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 3200 } }] },
|
|
4057
|
+
modulators: [{ target: "gain", wave: "square", rate: 60, depth: 0.1, base: 0.12 }],
|
|
4058
|
+
envelope: [[0, 0.2, "set"], [0.12, 1e-4, "exp", "fixed"]]
|
|
4059
|
+
}
|
|
4060
|
+
],
|
|
4061
|
+
falloff: { model: "none" }
|
|
4062
|
+
},
|
|
4063
|
+
{
|
|
4064
|
+
// the glovebox latch: a small plastic click and the lid dropping
|
|
4065
|
+
id: "glovebox_latch",
|
|
4066
|
+
bus: "world",
|
|
4067
|
+
duration: 0.14,
|
|
4068
|
+
layers: [
|
|
4069
|
+
{ source: { kind: "noise", offset: "random" }, filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 1800 }, q: 2 }] }, envelope: [[0, 0.16, "set"], [0.03, 1e-4, "exp", "fixed"]] },
|
|
4070
|
+
{ source: { kind: "osc", wave: "triangle", frequency: { set: 240 } }, envelope: [[0, 1e-4, "set", "fixed"], [0.06, 0.08, "linear"], [0.14, 1e-4, "exp", "fixed"]] }
|
|
4071
|
+
],
|
|
4072
|
+
falloff: { model: "none" }
|
|
3696
4073
|
}
|
|
3697
4074
|
];
|
|
3698
4075
|
var CABIN_BANK = { id: "vehicle_cabin", sounds: CABIN_SOUNDS, loops: [] };
|
|
3699
4076
|
|
|
4077
|
+
// src/library/radio.ts
|
|
4078
|
+
var hz = (midi) => 440 * 2 ** ((midi - 69) / 12);
|
|
4079
|
+
var layer = (note, beat, wave, level) => {
|
|
4080
|
+
const [at, midi, beats] = note;
|
|
4081
|
+
const length = beats * beat;
|
|
4082
|
+
return {
|
|
4083
|
+
at: at * beat,
|
|
4084
|
+
duration: length,
|
|
4085
|
+
source: { kind: "osc", wave, frequency: { set: hz(midi) } },
|
|
4086
|
+
envelope: [[0, 1e-4, "set"], [0.01, level, "linear"], [Math.max(0.02, length * 0.9), level * 0.35, "linear"], [length, 1e-4, "exp", "fixed"]]
|
|
4087
|
+
};
|
|
4088
|
+
};
|
|
4089
|
+
function radioTrack(spec) {
|
|
4090
|
+
const beat = 60 / spec.bpm;
|
|
4091
|
+
const recipes = Array.from({ length: spec.bars }, (_, bar) => {
|
|
4092
|
+
const inBar = (notes) => notes.filter((n) => n[0] >= bar * 4 && n[0] < bar * 4 + 4).map((n) => [n[0] - bar * 4, n[1], n[2]]);
|
|
4093
|
+
const layers = [
|
|
4094
|
+
...inBar(spec.bass).map((n) => layer(n, beat, "triangle", 0.16)),
|
|
4095
|
+
...inBar(spec.chords).map((n) => layer(n, beat, "sine", 0.05)),
|
|
4096
|
+
...inBar(spec.melody).map((n) => layer(n, beat, spec.wave, 0.07))
|
|
4097
|
+
];
|
|
4098
|
+
return { id: `${spec.id}_${bar}`, bus: "world", duration: 4 * beat, layers, falloff: { model: "none" } };
|
|
4099
|
+
});
|
|
4100
|
+
return { song: { id: spec.id, barSeconds: 4 * beat, bars: recipes.map((r) => r.id) }, recipes };
|
|
4101
|
+
}
|
|
4102
|
+
var chord = (bar, root, minor = false) => [[bar * 4, root, 4], [bar * 4, root + (minor ? 3 : 4), 4], [bar * 4, root + 7, 4]];
|
|
4103
|
+
var bassLine = (roots, pattern) => roots.flatMap((root, bar) => pattern.map((b) => [bar * 4 + b, root - 12, 0.9]));
|
|
4104
|
+
var TRACKS = [
|
|
4105
|
+
// a bright pop loop: I–V–vi–IV in C
|
|
4106
|
+
radioTrack({
|
|
4107
|
+
id: "radio_track_pop",
|
|
4108
|
+
bpm: 112,
|
|
4109
|
+
bars: 4,
|
|
4110
|
+
wave: "square",
|
|
4111
|
+
bass: bassLine([48, 55, 57, 53], [0, 1.5, 2, 3]),
|
|
4112
|
+
chords: [...chord(0, 60), ...chord(1, 55), ...chord(2, 57, true), ...chord(3, 53)],
|
|
4113
|
+
melody: [[0, 76, 1], [1, 74, 0.5], [1.5, 72, 1.5], [4, 74, 1], [5, 76, 1], [6, 79, 2], [8, 76, 1], [9, 74, 1], [10, 72, 2], [12, 72, 1], [13, 74, 1], [14, 72, 2]]
|
|
4114
|
+
}),
|
|
4115
|
+
// a syncopated loop in A minor, the bass on the off-beats
|
|
4116
|
+
radioTrack({
|
|
4117
|
+
id: "radio_track_groove",
|
|
4118
|
+
bpm: 100,
|
|
4119
|
+
bars: 4,
|
|
4120
|
+
wave: "triangle",
|
|
4121
|
+
bass: bassLine([57, 62, 64, 57], [0, 0.75, 1.5, 2.5, 3.25]),
|
|
4122
|
+
chords: [...chord(0, 57, true), ...chord(1, 62, true), ...chord(2, 64), ...chord(3, 57, true)],
|
|
4123
|
+
melody: [[0.5, 72, 0.5], [1.25, 76, 0.75], [2.5, 74, 1], [4.5, 77, 0.5], [5.25, 74, 0.75], [6.5, 72, 1.5], [8.5, 71, 0.5], [9.25, 74, 0.75], [10.5, 76, 1.5], [12.5, 72, 0.5], [13.25, 69, 2]]
|
|
4124
|
+
}),
|
|
4125
|
+
// a slow blues-like loop in F with a swung melody
|
|
4126
|
+
radioTrack({
|
|
4127
|
+
id: "radio_track_slow",
|
|
4128
|
+
bpm: 76,
|
|
4129
|
+
bars: 4,
|
|
4130
|
+
wave: "sine",
|
|
4131
|
+
bass: bassLine([53, 58, 53, 60], [0, 2]),
|
|
4132
|
+
chords: [...chord(0, 65), ...chord(1, 58), ...chord(2, 65), ...chord(3, 60)],
|
|
4133
|
+
melody: [[0, 72, 1.5], [1.67, 75, 0.33], [2, 77, 2], [4, 74, 1], [5, 72, 1], [6, 70, 2], [8, 72, 1.5], [9.67, 69, 0.33], [10, 65, 2], [12, 67, 1], [13, 70, 1], [14, 72, 2]]
|
|
4134
|
+
})
|
|
4135
|
+
];
|
|
4136
|
+
var RADIO_TRACKS = TRACKS.map((t) => t.song);
|
|
4137
|
+
var RADIO_BANK = { id: "vehicle_radio", sounds: TRACKS.flatMap((t) => t.recipes), loops: [] };
|
|
4138
|
+
|
|
3700
4139
|
// src/recipes/foley.ts
|
|
3701
4140
|
var FOLEY_FALLOFF = {
|
|
3702
4141
|
model: "inverse_square",
|
|
@@ -4338,11 +4777,11 @@ var GameAudio = (() => {
|
|
|
4338
4777
|
...s.splash ? [sceneNoise({ type: "bandpass", f: s.splash.f * take.f, f2: s.splash.f2 * take.f, q: 1.4, d: s.splash.d * take.t * weight ** 0.4, vol: s.splash.vol * gait.vol * take.v * weight ** 0.5 })] : []
|
|
4339
4778
|
];
|
|
4340
4779
|
}
|
|
4341
|
-
var quieter = (level, layers) => layers.map((
|
|
4342
|
-
const gain =
|
|
4343
|
-
if (typeof gain === "number") return { ...
|
|
4344
|
-
if (Array.isArray(gain)) return { ...
|
|
4345
|
-
return
|
|
4780
|
+
var quieter = (level, layers) => layers.map((layer2) => {
|
|
4781
|
+
const gain = layer2.gain ?? 1;
|
|
4782
|
+
if (typeof gain === "number") return { ...layer2, gain: gain * level };
|
|
4783
|
+
if (Array.isArray(gain)) return { ...layer2, gain: [gain[0] * level, gain[1] * level] };
|
|
4784
|
+
return layer2;
|
|
4346
4785
|
});
|
|
4347
4786
|
var recipe = (id, layers, falloff, bus = "world") => ({ id, bus, pan: true, layers, falloff, sources: ["sdk"] });
|
|
4348
4787
|
var takeId = (id, index) => `${id}_${String(index + 1).padStart(2, "0")}`;
|
|
@@ -4358,7 +4797,7 @@ var GameAudio = (() => {
|
|
|
4358
4797
|
(take) => quieter(LANDING_LEVEL, [
|
|
4359
4798
|
// both feet, a hair apart, then the knees taking the weight (a low body thump) and the clothes settling
|
|
4360
4799
|
...footLayers(surface, take, GAITS.run, LANDINGS[landing2].weight),
|
|
4361
|
-
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((
|
|
4800
|
+
...footLayers(surface, { ...take, v: take.v * 0.6 }, GAITS.run, LANDINGS[landing2].weight * 0.6).map((layer2) => ({ ...layer2, at: (typeof layer2.at === "number" ? layer2.at : 0) + 0.018 * take.t })),
|
|
4362
4801
|
sceneTone({ f: 60 * take.f, f2: 38, d: 0.14 * LANDINGS[landing2].weight * take.t, vol: 0.22 * LANDINGS[landing2].weight ** 0.7 * take.v, delay: 0.02 }),
|
|
4363
4802
|
sceneNoise({ type: "bandpass", f: [1800, 2600], q: 0.8, d: 0.18, vol: LANDINGS[landing2].cloth * take.v, delay: 0.04 })
|
|
4364
4803
|
]),
|
|
@@ -4404,7 +4843,7 @@ var GameAudio = (() => {
|
|
|
4404
4843
|
fall_damage: (take) => [
|
|
4405
4844
|
...BODY.body_fall(take),
|
|
4406
4845
|
sceneNoise({ type: "highpass", f: 2800, d: 0.02, vol: 0.35 * take.v, delay: 0.01 }),
|
|
4407
|
-
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((
|
|
4846
|
+
...voice({ f0: 170, f1: 110, d: 0.32, fA: 650, fB: 1150, noise: 0.25, vol: 0.32 })(take).map((layer2) => ({ ...layer2, at: 0.08 }))
|
|
4408
4847
|
],
|
|
4409
4848
|
player_hurt: voice({ f0: 150, f1: 105, d: 0.26, fA: 620, fB: 1100, noise: 0.3, vol: 0.35 }),
|
|
4410
4849
|
player_breath_run: (take) => [
|
|
@@ -4506,6 +4945,9 @@ var GameAudio = (() => {
|
|
|
4506
4945
|
},
|
|
4507
4946
|
register(bank) {
|
|
4508
4947
|
engine.register(bank);
|
|
4948
|
+
},
|
|
4949
|
+
preload(sounds) {
|
|
4950
|
+
void engine.loadSamples(sounds).catch(() => void 0);
|
|
4509
4951
|
}
|
|
4510
4952
|
};
|
|
4511
4953
|
}
|
|
@@ -4766,11 +5208,11 @@ var GameAudio = (() => {
|
|
|
4766
5208
|
const planned = plan();
|
|
4767
5209
|
if (!planned) return `${text.sheet.title}: ${engine.registry.sound(state.sound) ? text.sheet.silent : text.sheet.none}`;
|
|
4768
5210
|
const head = `${text.sheet.title}: ${planned.id} (${planned.bus}) ${text.sheet.level} ${planned.level.toFixed(4)} ${text.sheet.distance} ${planned.distance.toFixed(1)} m${planned.pan === void 0 ? "" : ` ${text.sheet.pan} ${planned.pan.toFixed(3)}`} ${planned.length.toFixed(3)} ${text.sheet.length}`;
|
|
4769
|
-
const layers2 = planned.layers.map((
|
|
4770
|
-
const source =
|
|
4771
|
-
const filters = (
|
|
4772
|
-
const envelope2 =
|
|
4773
|
-
return ` ${text.sheet.layer} ${index + 1}: ${source} at ${
|
|
5211
|
+
const layers2 = planned.layers.map((layer2, index) => {
|
|
5212
|
+
const source = layer2.source.kind === "noise" ? `noise@${layer2.source.offset.toFixed(4)}` : layer2.source.kind === "osc" ? `${layer2.source.wave}@${layer2.source.frequency.value ?? layer2.source.frequency.from}` : layer2.source.kind;
|
|
5213
|
+
const filters = (layer2.filters?.bank ?? []).map((filter) => `${filter.type}@${(filter.frequency.value ?? filter.frequency.from ?? 0).toFixed(1)}`).join(" ");
|
|
5214
|
+
const envelope2 = layer2.envelope.map((point) => `${point.time.toFixed(3)}:${point.value.toPrecision(3)}`).join(" ");
|
|
5215
|
+
return ` ${text.sheet.layer} ${index + 1}: ${source} at ${layer2.at.toFixed(3)} for ${layer2.duration.toFixed(3)} ${filters} [${envelope2}]`;
|
|
4774
5216
|
});
|
|
4775
5217
|
return [head, ...layers2].join("\n");
|
|
4776
5218
|
};
|