@adatechnology/game-audio 0.5.0 → 0.6.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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SampleSound, a as SampleLoader, B as BusId, b as SampleCrossover, F as Falloff, c as SoundBank, L as LoopRecipe, d as SoundRecipe, P as ParamRef, R as Range, e as Layer, f as SoundSet, g as SoundPlan, h as SamplePlan, A as AudioLoopHandle, i as LoopEntry, M as MixProfile, j as AudioEngine } from './locale-BugSe8dA.js';
2
- export { k as ActionCheck, l as AudioBenchText, m as AudioChannel, n as AudioEngineOptions, o as AudioScenarioLike, p as AudioStats, q as AudioTestModule, r as AudioTestModuleOptions, s as Automation, t as BENCH_BANK, u as BUILD_ORDERS, v as BUS_IDS, w as BUS_PRIORITY, x as BuildOrder, y as BusSetting, C as CRITERIA, z as CRITERIA_TARGETS, D as CriterionId, E as CriterionResult, G as Curve, H as DEFAULT_CROSSOVER, I as DEFAULT_LISTENER, J as DEFAULT_MAX_VOICES, K as DEFAULT_MIX, N as DEFAULT_SAMPLE_CUTOFF_K, O as DEFAULT_SAMPLE_FALLOFF, Q as DEFAULT_SAMPLE_PITCH, T as DEFAULT_TAIL_GAIN, U as DeepPartial, V as DistanceMode, W as Envelope, X as EnvelopePoint, Y as FilterBank, Z as FilterSpec, _ as FormantVoiceSource, $ as LAYER_FLOOR, a0 as LayerSource, a1 as Listener, a2 as LoopOrder, a3 as LoopSet, a4 as MIX_PROFILES, a5 as MeasureOptions, a6 as Modulator, a7 as NoiseSource, a8 as OPEN_CUTOFF, a9 as OscSource, aa as PAN_LIMIT, ab as PAN_MIN_DISTANCE, ac as PlanInput, ad as PlannedAutomation, ae as PlannedEnvelopePoint, af as PlannedFilter, ag as PlannedLayer, ah as PlannedModulator, ai as PlannedSampleLayer, aj as PlannedSource, ak as PlayOptions, al as SAMPLE_ENVIRONMENTS, am as SFX_BLOWS, an as SFX_CATALOG, ao as SFX_CATEGORIES, ap as SFX_IMPACTS, aq as SFX_MATERIALS, ar as SampleEnvironment, as as SampleLayerRole, at as SampleLoadReport, au as SampleMemory, av as SampleOptions, aw as SamplePlanInput, ax as SampleRef, ay as SampleStatus, az as SampleStore, aA as SampleTail, aB as SampleTier, aC as SfxBlow, aD as SfxCategory, aE as SfxEntry, aF as SfxMaterial, aG as SfxPriority, aH as SfxState, aI as SoundRegistry, aJ as StartQueueOptions, aK as Time, aL as VOICE_GROUPS, aM as VoiceDecision, aN as VoiceGroup, aO as VoiceHandle, aP as VoiceManager, aQ as VoiceManagerOptions, aR as VoiceRefusal, aS as VoiceRequest, aT as VoiceStartEvent, aU as VoiceStats, aV as audioBenchTextOf, aW as busGain, aX as busPriority, aY as createAudioEngine, aZ as createAudioTestModule, a_ as createSampleStore, a$ as createSoundRegistry, b0 as createVoiceManager, b1 as crossGains, b2 as farMixAt, b3 as fetchLoader, b4 as impactSoundId, b5 as inlineLoader, b6 as limiterCurve, b7 as listenerDistance, b8 as measureCriteria, b9 as panFor, ba as pickTake, bb as planSample, bc as planSound, bd as recordOutput, be as resolveMix, bf as sampleCutoff, bg as sampleUrls, bh as sfxState, bi as tierFor } from './locale-BugSe8dA.js';
1
+ import { S as SampleSound, a as SampleLoader, B as BusId, b as SfxManifest, c as SoundBank, d as SampleDefaults, F as Falloff, L as LoopRecipe, e as SoundRecipe, P as ParamRef, R as Range, f as Layer, g as SoundSet, h as SoundPlan, i as SamplePlan, A as AudioLoopHandle, j as LoopEntry, M as MixProfile, k as AudioEngine } from './locale-CJfDpWnI.js';
2
+ export { l as ActionCheck, m as AudioBenchText, n as AudioChannel, o as AudioEngineOptions, p as AudioScenarioLike, q as AudioStats, r as AudioTestModule, s as AudioTestModuleOptions, t as Automation, u as BENCH_BANK, v as BUILD_ORDERS, w as BUS_IDS, x as BUS_PRIORITY, y as BuildOrder, z as BusSetting, C as CRITERIA, D as CRITERIA_TARGETS, E as CriterionId, G as CriterionResult, H as Curve, I as DEFAULT_CROSSOVER, J as DEFAULT_LISTENER, K as DEFAULT_MAX_VOICES, N as DEFAULT_MIX, O as DEFAULT_SAMPLE_CUTOFF_K, Q as DEFAULT_SAMPLE_FALLOFF, T as DEFAULT_SAMPLE_PITCH, U as DEFAULT_TAIL_GAIN, V as DeepPartial, W as DistanceMode, X as Envelope, Y as EnvelopePoint, Z as FilterBank, _ as FilterSpec, $ as FormantVoiceSource, a0 as KIT_SAMPLES_BANK, a1 as LAYER_FLOOR, a2 as LayerSource, a3 as Listener, a4 as LoadUserSamplesOptions, a5 as LoopOrder, a6 as LoopSet, a7 as MIX_PROFILES, a8 as MeasureOptions, a9 as Modulator, aa as NoiseSource, ab as OPEN_CUTOFF, ac as OscSource, ad as PAN_LIMIT, ae as PAN_MIN_DISTANCE, af as PlanInput, ag as PlannedAutomation, ah as PlannedEnvelopePoint, ai as PlannedFilter, aj as PlannedLayer, ak as PlannedModulator, al as PlannedSampleLayer, am as PlannedSource, an as PlayOptions, ao as SAMPLE_ENVIRONMENTS, ap as SDK_LICENSES, aq as SFX_BLOWS, ar as SFX_CATALOG, as as SFX_CATEGORIES, at as SFX_IMPACTS, au as SFX_LAYERS, av as SFX_MATERIALS, aw as SFX_TIERS, ax as SampleBankOptions, ay as SampleCrossover, az as SampleEnvironment, aA as SampleLayerRole, aB as SampleLoadReport, aC as SampleMemory, aD as SampleOptions, aE as SamplePlanInput, aF as SampleRef, aG as SampleStatus, aH as SampleStore, aI as SampleTail, aJ as SampleTier, aK as SdkLicense, aL as SfxBlow, aM as SfxCategory, aN as SfxEntry, aO as SfxFileEntry, aP as SfxLayer, aQ as SfxManifestLike, aR as SfxManifestSound, aS as SfxMaterial, aT as SfxPriority, aU as SfxState, aV as SoundRegistry, aW as SoundSource, aX as StartQueueOptions, aY as Time, aZ as USER_SAMPLES_BANK, a_ as USER_SOUND_PREFIX, a$ as UserSampleBankOptions, b0 as UserSampleBankResult, b1 as UserSfxFile, b2 as UserSfxManifest, b3 as UserSfxSound, b4 as UserSoundPick, b5 as UserSoundPicks, b6 as UserSoundRole, b7 as VOICE_GROUPS, b8 as VoiceDecision, b9 as VoiceGroup, ba as VoiceHandle, bb as VoiceManager, bc as VoiceManagerOptions, bd as VoiceRefusal, be as VoiceRequest, bf as VoiceStartEvent, bg as VoiceStats, bh as audioBenchTextOf, bi as busGain, bj as busPriority, bk as createAudioEngine, bl as createAudioTestModule, bm as createSampleStore, bn as createSoundRegistry, bo as createVoiceManager, bp as crossGains, bq as farMixAt, br as fetchLoader, bs as impactSoundId, bt as inlineLoader, bu as limiterCurve, bv as listenerDistance, bw as loadUserSamples, bx as measureCriteria, by as panFor, bz as parseSfxName, bA as pickTake, bB as planSample, bC as planSound, bD as recordOutput, bE as resolveMix, bF as sampleBankFromManifest, bG as sampleCutoff, bH as sampleUrls, bI as sfxState, bJ as soundSourceOf, bK as splitTier, bL as tierFor, bM as userSampleBank } from './locale-CJfDpWnI.js';
3
3
  import { AudioSink } from '@adatechnology/game-contracts';
4
4
 
5
5
  /** Deterministic mulberry32 generator; floats in [0, 1). The same generator the golden tool seeds `Math.random` with, so a team routine and its recipe draw the same numbers. */
@@ -52,82 +52,6 @@ declare function countHits(channels: Channels, sampleRate: number, threshold: nu
52
52
  /** Peak of the last `seconds` of a file against its own peak, dB (≤ −40: it fades out, it does not end on a cut). */
53
53
  declare function endDropDb(channels: Channels, sampleRate: number, seconds?: number): number;
54
54
 
55
- /**
56
- * The manifest the converter (`scripts/sfx-convert.mjs`) writes next to the converted files: every sound id with its takes per layer, the file
57
- * facts the criteria check (rate, channels, peak, length) and where each file came from. `sampleBankFromManifest` turns it into a `SoundBank`
58
- * of `SampleSound`s, with the bus and the numbers of each sound given by the caller.
59
- */
60
-
61
- /** Licences the SDK accepts (the files are redistributed on npm): public domain or attribution only. */
62
- declare const SDK_LICENSES: readonly ["CC0", "CC-BY"];
63
- type SdkLicense = (typeof SDK_LICENSES)[number];
64
- type SfxFileEntry = {
65
- /** Path from the manifest's folder: `armas/pistol_9mm_close_01.ogg`. */
66
- readonly file: string;
67
- readonly sampleRate: number;
68
- readonly channels: number;
69
- /** dBFS. */
70
- readonly peak: number;
71
- readonly seconds: number;
72
- readonly source: string;
73
- readonly author: string;
74
- readonly license: SdkLicense;
75
- };
76
- /** The layers a file name can end in: `<id>_<layer>_<nn>` (`pistol_9mm_close_01`); a name without one is a `close` take. */
77
- declare const SFX_LAYERS: readonly ["close", "far", "tail_in", "tail_out"];
78
- type SfxLayer = (typeof SFX_LAYERS)[number];
79
- type SfxManifestSound = {
80
- readonly id: string;
81
- readonly category: string;
82
- readonly layers: Partial<Readonly<Record<SfxLayer, readonly SfxFileEntry[]>>>;
83
- };
84
- type SfxManifest = {
85
- readonly version: 1;
86
- readonly format: {
87
- readonly codec: 'vorbis';
88
- readonly sampleRate: number;
89
- readonly channels: number;
90
- readonly peakDb: number;
91
- };
92
- readonly sounds: readonly SfxManifestSound[];
93
- };
94
- /** The numbers of one sound that the files do not say (bus, curve, pitch...). */
95
- type SampleDefaults = {
96
- readonly bus: BusId;
97
- readonly gain?: number;
98
- readonly pitch?: number;
99
- readonly crossover?: SampleCrossover;
100
- readonly tailGain?: number;
101
- readonly falloff?: Falloff;
102
- readonly cutoffK?: number;
103
- readonly group?: string;
104
- readonly priority?: number;
105
- readonly fallback?: string;
106
- };
107
- type SampleBankOptions = {
108
- readonly id: string;
109
- /** Prefix of every url (where the manifest's folder is served: `/sfx/`). Default ''. */
110
- readonly baseUrl?: string;
111
- /** Numbers per sound id, or one function; a sound with neither is left out (it has no bus). */
112
- readonly sounds?: Readonly<Record<string, SampleDefaults>>;
113
- readonly defaults?: (sound: SfxManifestSound) => SampleDefaults | undefined;
114
- };
115
- /** The strength suffixes of a sound id (`kick_hit_heavy`): takes of the tiers of `kick_hit`, from these intensities. */
116
- declare const SFX_TIERS: Readonly<Record<string, number>>;
117
- /** Splits a file name (no extension) into its sound id, layer and take number. */
118
- declare function parseSfxName(name: string): {
119
- readonly id: string;
120
- readonly layer: SfxLayer;
121
- readonly take: number;
122
- };
123
- /** The base id and tier of a sound id: `kick_hit_heavy` → `kick_hit`, `heavy`. */
124
- declare function splitTier(id: string): {
125
- readonly base: string;
126
- readonly tier?: string;
127
- };
128
- /** A bank of sample sounds from a manifest. */
129
- declare function sampleBankFromManifest(manifest: SfxManifest, options: SampleBankOptions): SoundBank;
130
-
131
55
  /** The bus of a manifest category (the folder the file sits in). */
132
56
  declare const KIT_CATEGORY_BUS: Readonly<Record<string, BusId>>;
133
57
  /**
@@ -136,7 +60,10 @@ declare const KIT_CATEGORY_BUS: Readonly<Record<string, BusId>>;
136
60
  */
137
61
  declare const KIT_SAMPLE_ALIASES: Readonly<Record<string, string>>;
138
62
  /** The numbers of a sound the files do not say: the bus of its folder, a little pitch spread. */
139
- declare function kitSampleDefaults(sound: SfxManifestSound): SampleDefaults | undefined;
63
+ declare function kitSampleDefaults(sound: {
64
+ readonly id: string;
65
+ readonly category: string;
66
+ }): SampleDefaults | undefined;
140
67
  type KitSampleBankOptions = {
141
68
  /** Where the `assets/sfx/` folder is served (`./sfx/`). */
142
69
  readonly baseUrl?: string;
@@ -326,6 +253,9 @@ declare const LIBRARY_SOUNDS: readonly SoundRecipe[];
326
253
  declare const LIBRARY_LOOPS: readonly LoopRecipe[];
327
254
  declare const LIBRARY_BANK: SoundBank;
328
255
 
256
+ declare const CABIN_SOUNDS: readonly SoundRecipe[];
257
+ declare const CABIN_BANK: SoundBank;
258
+
329
259
  /** Small sounds of the hand: `vol / (1 + d² × 0.02)`, lowpass cutoffs `/ (1 + d × 0.04)`, silent below 0.004 (a latch at 10 m is a third, at 50 m gone). */
330
260
  declare const FOLEY_FALLOFF: Falloff;
331
261
  /** The doors and lids of furniture and vehicles, by what the leaf is (the room picks one from the item, else from the leaf's material). */
@@ -479,4 +409,4 @@ type EngineSink = AudioSink & {
479
409
  /** Lends an engine as the host's `audio`: sounds by key, loops, the listener, and banks registered into the same engine. */
480
410
  declare function createAudioSink(engine: AudioEngine): EngineSink;
481
411
 
482
- export { AudioEngine, AudioLoopHandle, BODY_FALLOFF, BODY_FOLEY_BANK, BODY_FOLEY_SET_IDS, BODY_FOLEY_TAKES, BusId, CALIBRATION_ID, CALIBRATION_LOOP_ID, CALIBRATION_LOOP_SOUND, CALIBRATION_SOUND, CROSSFADE_STEPS, DEFAULT_LOOP_CROSSFADE, DISTANCE_CURVES, type DistanceCurveName, type EngineSink, FOLEY_BANK, FOLEY_FALLOFF, FOOTSTEP_FALLOFF, Falloff, GAME_SFX_BANK, GAME_SFX_CLASSES, GAME_SFX_FALLBACK, ITEM_FOLEY, KIT_CATEGORY_BUS, KIT_SAMPLE_ALIASES, type KitSampleBankOptions, LEAF_FOLEY, LEAF_FOLEY_SETS, LIBRARY_BANK, LIBRARY_LOOPS, LIBRARY_SOUNDS, LOOP_SMOOTHING, LOOP_START_ABOVE, Layer, type LeafFoley, type LoadKitSamplesOptions, type LoopAction, LoopRecipe, type LoopState, type MixChain, MixProfile, type NoiseClass, PICKUP_CLASSES, ParamRef, type PickupClass, Range, type RealizeTarget, type RealizedSampleVoice, type RealizedVoice, SDK_LICENSES, SFX_LAYERS, SFX_TIERS, type SampleBankOptions, SampleCrossover, type SampleDefaults, SampleLoader, type SampleLoopHost, SamplePlan, SampleSound, type SampleTarget, type SceneLength, type SceneNoise, type SceneRecipeOptions, type SceneTone, type SceneVoice, type SdkLicense, type SetPicker, type SfxFileEntry, type SfxLayer, type SfxManifest, type SfxManifestSound, SoundBank, SoundPlan, SoundRecipe, SoundSet, balanceOf, bufferOf, buildChain, calibrationLoader, centroidHz, countHits, createAudioSink, createLoopState, createRng, createSetPicker, cutoffDivisor, dbOf, drawBetween, encodeWav, endDropDb, equalPowerIn, falloffGain, fillImpulse, fillNoise, globalContext, isCalibrationUrl, isSilent, kitSampleBank, kitSampleDefaults, loadKitSamples, loopCrossfade, noiseClassRecipe, onsetIndex, parseSfxName, peakIndex, peakOf, realize, realizeSample, retunePlan, ringSeconds, rmsDb, sampleBankFromManifest, sampleLength, sceneMany, sceneNoise, sceneRecipe, sceneTone, sceneVoice, splitTier, streamFor, streamSeed, unlockContext, withAliases };
412
+ export { AudioEngine, AudioLoopHandle, BODY_FALLOFF, BODY_FOLEY_BANK, BODY_FOLEY_SET_IDS, BODY_FOLEY_TAKES, BusId, CABIN_BANK, CABIN_SOUNDS, CALIBRATION_ID, CALIBRATION_LOOP_ID, CALIBRATION_LOOP_SOUND, CALIBRATION_SOUND, CROSSFADE_STEPS, DEFAULT_LOOP_CROSSFADE, DISTANCE_CURVES, type DistanceCurveName, type EngineSink, FOLEY_BANK, FOLEY_FALLOFF, FOOTSTEP_FALLOFF, Falloff, GAME_SFX_BANK, GAME_SFX_CLASSES, GAME_SFX_FALLBACK, ITEM_FOLEY, KIT_CATEGORY_BUS, KIT_SAMPLE_ALIASES, type KitSampleBankOptions, LEAF_FOLEY, LEAF_FOLEY_SETS, LIBRARY_BANK, LIBRARY_LOOPS, LIBRARY_SOUNDS, LOOP_SMOOTHING, LOOP_START_ABOVE, Layer, type LeafFoley, type LoadKitSamplesOptions, type LoopAction, LoopRecipe, type LoopState, type MixChain, MixProfile, type NoiseClass, PICKUP_CLASSES, ParamRef, type PickupClass, Range, type RealizeTarget, type RealizedSampleVoice, type RealizedVoice, SampleDefaults, SampleLoader, type SampleLoopHost, SamplePlan, SampleSound, type SampleTarget, type SceneLength, type SceneNoise, type SceneRecipeOptions, type SceneTone, type SceneVoice, type SetPicker, SfxManifest, SoundBank, SoundPlan, SoundRecipe, SoundSet, balanceOf, bufferOf, buildChain, calibrationLoader, centroidHz, countHits, createAudioSink, createLoopState, createRng, createSetPicker, cutoffDivisor, dbOf, drawBetween, encodeWav, endDropDb, equalPowerIn, falloffGain, fillImpulse, fillNoise, globalContext, isCalibrationUrl, isSilent, kitSampleBank, kitSampleDefaults, loadKitSamples, loopCrossfade, noiseClassRecipe, onsetIndex, peakIndex, peakOf, realize, realizeSample, retunePlan, ringSeconds, rmsDb, sampleLength, sceneMany, sceneNoise, sceneRecipe, sceneTone, sceneVoice, streamFor, streamSeed, unlockContext, withAliases };
@@ -29,6 +29,8 @@ var GameAudio = (() => {
29
29
  BUILD_ORDERS: () => BUILD_ORDERS,
30
30
  BUS_IDS: () => BUS_IDS,
31
31
  BUS_PRIORITY: () => BUS_PRIORITY,
32
+ CABIN_BANK: () => CABIN_BANK,
33
+ CABIN_SOUNDS: () => CABIN_SOUNDS,
32
34
  CALIBRATION_ID: () => CALIBRATION_ID,
33
35
  CALIBRATION_LOOP_ID: () => CALIBRATION_LOOP_ID,
34
36
  CALIBRATION_LOOP_SOUND: () => CALIBRATION_LOOP_SOUND,
@@ -54,6 +56,7 @@ var GameAudio = (() => {
54
56
  GAME_SFX_FALLBACK: () => GAME_SFX_FALLBACK,
55
57
  ITEM_FOLEY: () => ITEM_FOLEY,
56
58
  KIT_CATEGORY_BUS: () => KIT_CATEGORY_BUS,
59
+ KIT_SAMPLES_BANK: () => KIT_SAMPLES_BANK,
57
60
  KIT_SAMPLE_ALIASES: () => KIT_SAMPLE_ALIASES,
58
61
  LAYER_FLOOR: () => LAYER_FLOOR,
59
62
  LEAF_FOLEY: () => LEAF_FOLEY,
@@ -77,6 +80,8 @@ var GameAudio = (() => {
77
80
  SFX_LAYERS: () => SFX_LAYERS,
78
81
  SFX_MATERIALS: () => SFX_MATERIALS,
79
82
  SFX_TIERS: () => SFX_TIERS,
83
+ USER_SAMPLES_BANK: () => USER_SAMPLES_BANK,
84
+ USER_SOUND_PREFIX: () => USER_SOUND_PREFIX,
80
85
  VOICE_GROUPS: () => VOICE_GROUPS,
81
86
  audioBenchTextOf: () => audioBenchTextOf,
82
87
  balanceOf: () => balanceOf,
@@ -118,6 +123,7 @@ var GameAudio = (() => {
118
123
  limiterCurve: () => limiterCurve2,
119
124
  listenerDistance: () => listenerDistance,
120
125
  loadKitSamples: () => loadKitSamples,
126
+ loadUserSamples: () => loadUserSamples,
121
127
  loopCrossfade: () => loopCrossfade,
122
128
  measureCriteria: () => measureCriteria,
123
129
  noiseClassRecipe: () => noiseClassRecipe,
@@ -146,11 +152,13 @@ var GameAudio = (() => {
146
152
  sceneTone: () => sceneTone,
147
153
  sceneVoice: () => sceneVoice,
148
154
  sfxState: () => sfxState,
155
+ soundSourceOf: () => soundSourceOf,
149
156
  splitTier: () => splitTier,
150
157
  streamFor: () => streamFor,
151
158
  streamSeed: () => streamSeed,
152
159
  tierFor: () => tierFor,
153
160
  unlockContext: () => unlockContext,
161
+ userSampleBank: () => userSampleBank,
154
162
  withAliases: () => withAliases
155
163
  });
156
164
 
@@ -2213,10 +2221,12 @@ var GameAudio = (() => {
2213
2221
  noise: () => noiseBuffer(store, audio, mix.noise.seconds),
2214
2222
  time
2215
2223
  });
2224
+ const recipeBank = registry.bankOf(plan.id);
2216
2225
  announce({
2217
2226
  id: plan.id,
2218
2227
  kind: "recipe",
2219
2228
  ...fallbackOf ? { fallbackOf } : {},
2229
+ ...recipeBank ? { bank: recipeBank } : {},
2220
2230
  requestedAt,
2221
2231
  startAt: time,
2222
2232
  outputLatency: latencyOf(audio),
@@ -2318,9 +2328,11 @@ var GameAudio = (() => {
2318
2328
  const time = requestedAt + (playOptions.delay ?? 0);
2319
2329
  const chokeList = sample.choke ? choked(sample.id) : void 0;
2320
2330
  const realized = realizeSample(plan, { context: audio, output: sampleOutput(plan.bus), buffer: (url2) => sampleStore.buffer(url2), time });
2331
+ const sampleBank = registry.bankOf(plan.id);
2321
2332
  announce({
2322
2333
  id: plan.id,
2323
2334
  kind: "sample",
2335
+ ...sampleBank ? { bank: sampleBank } : {},
2324
2336
  requestedAt,
2325
2337
  startAt: time,
2326
2338
  outputLatency: latencyOf(audio),
@@ -3009,7 +3021,11 @@ var GameAudio = (() => {
3009
3021
  ["casing_drop_terra_pistol", 1],
3010
3022
  ["casing_drop_terra_rifle", 1],
3011
3023
  ["casing_drop_terra_shell", 1],
3012
- ["rocket_fire", 2]
3024
+ ["rocket_fire", 2],
3025
+ ["smg_45_auto", 2, "sfx_pistol"],
3026
+ ["weapon_draw", 2],
3027
+ ["suppressor_attach", 3],
3028
+ ["suppressor_detach", 3]
3013
3029
  ]),
3014
3030
  ...entries("explosoes-fogo", [
3015
3031
  ["explosion_grenade", 1, "boom"],
@@ -3023,7 +3039,11 @@ var GameAudio = (() => {
3023
3039
  ["fire_loop_large", 1],
3024
3040
  ["fire_extinguish", 2, "hiss"],
3025
3041
  ["gas_leak_hiss", 2],
3026
- ["structure_collapse", 2, "crumble"]
3042
+ ["structure_collapse", 2, "crumble"],
3043
+ ["grenade_bounce", 1],
3044
+ ["c4_plant", 2],
3045
+ ["c4_beep", 2],
3046
+ ["c4_disarm", 2]
3027
3047
  ]),
3028
3048
  ...entries("personagem", [
3029
3049
  ["footstep_concrete_walk", 1, "sfx_step"],
@@ -3039,7 +3059,19 @@ var GameAudio = (() => {
3039
3059
  ["blunt_hit", 1],
3040
3060
  ["bite", 1, "sfx_flesh"],
3041
3061
  ["body_fall", 1],
3042
- ["player_hurt", 2]
3062
+ ["player_hurt", 2],
3063
+ ["footstep_tile_walk", 1, "sfx_step"],
3064
+ ["footstep_water_walk", 1, "sfx_step"],
3065
+ ["footstep_grate_walk", 2, "sfx_step"],
3066
+ ["footstep_duct_walk", 3, "sfx_step"],
3067
+ ["ladder_climb", 2],
3068
+ ["swim", 2],
3069
+ ["zombie_groan", 1],
3070
+ ["zombie_alert", 1],
3071
+ ["zombie_attack", 1],
3072
+ ["zombie_pain", 1],
3073
+ ["zombie_claw_hit", 1, "sfx_flesh"],
3074
+ ["zombie_claw_miss", 2]
3043
3075
  ]),
3044
3076
  ...entries("veiculos", [
3045
3077
  ["engine_loop_car", 1],
@@ -3067,7 +3099,8 @@ var GameAudio = (() => {
3067
3099
  ["wind_loop", 2],
3068
3100
  ["thunder_near", 2],
3069
3101
  ["thunder_far", 2],
3070
- ["water_splash", 2]
3102
+ ["water_splash", 2],
3103
+ ["helicopter_loop", 3]
3071
3104
  ])
3072
3105
  ];
3073
3106
  var SFX_MATERIALS = [
@@ -3246,6 +3279,59 @@ var GameAudio = (() => {
3246
3279
  return bank;
3247
3280
  }
3248
3281
 
3282
+ // src/user-samples.ts
3283
+ var USER_SAMPLES_BANK = "user_samples";
3284
+ var KIT_SAMPLES_BANK = "kit_samples";
3285
+ var USER_SOUND_PREFIX = "user:";
3286
+ function soundSourceOf(event) {
3287
+ if (event.kind === "recipe") return "synth";
3288
+ return event.bank === USER_SAMPLES_BANK ? "user" : "sdk";
3289
+ }
3290
+ function userSampleBank(manifest, options = {}) {
3291
+ const playing = [];
3292
+ const behindKit = [];
3293
+ const sounds = [];
3294
+ for (const picked of manifest.sounds) {
3295
+ const sound = { ...picked, id: splitTier(picked.id).base };
3296
+ sounds.push({ ...sound, id: `${USER_SOUND_PREFIX}${sound.id}` });
3297
+ if (sound.role === "fallback" && options.kitHas?.(sound.id)) {
3298
+ behindKit.push(sound.id);
3299
+ continue;
3300
+ }
3301
+ sounds.push(sound);
3302
+ playing.push(sound.id);
3303
+ }
3304
+ const bank = sampleBankFromManifest(
3305
+ { sounds },
3306
+ {
3307
+ id: USER_SAMPLES_BANK,
3308
+ baseUrl: options.baseUrl ?? "",
3309
+ defaults: (sound) => kitSampleDefaults({ id: sound.id.replace(USER_SOUND_PREFIX, ""), category: sound.category })
3310
+ }
3311
+ );
3312
+ return { bank, playing, behindKit };
3313
+ }
3314
+ var fetchManifestJson = async (url2) => {
3315
+ const page = globalThis.location?.href;
3316
+ if (new URL(url2, page ?? "http://localhost/").protocol === "file:") throw new Error(`${url2}: file:// is not fetched`);
3317
+ const response = await fetch(url2);
3318
+ if (!response.ok) throw new Error(`${url2}: ${response.status}`);
3319
+ return await response.json();
3320
+ };
3321
+ async function loadUserSamples(engine, options = {}) {
3322
+ const baseUrl = options.baseUrl ?? "./sfx-local/";
3323
+ let manifest;
3324
+ try {
3325
+ manifest = await (options.fetchManifest ?? fetchManifestJson)(`${baseUrl}manifest.json`);
3326
+ } catch {
3327
+ return null;
3328
+ }
3329
+ const kitHas = options.kitHas ?? ((id) => Boolean(engine.registry?.sample(id)));
3330
+ const result = userSampleBank(manifest, { baseUrl, kitHas });
3331
+ engine.register(result.bank);
3332
+ return result;
3333
+ }
3334
+
3249
3335
  // src/data/game-sfx.json
3250
3336
  var game_sfx_default = {
3251
3337
  sources: ["jogo:L62089-62107"],
@@ -3473,6 +3559,82 @@ var GameAudio = (() => {
3473
3559
  var LIBRARY_LOOPS = [engine_saw_default, rotor_blades_default, crowd_rumble_default];
3474
3560
  var LIBRARY_BANK = { id: "library", sounds: LIBRARY_SOUNDS, loops: LIBRARY_LOOPS };
3475
3561
 
3562
+ // src/library/cabin.ts
3563
+ var CABIN_SOUNDS = [
3564
+ {
3565
+ id: "blinker_tick",
3566
+ bus: "world",
3567
+ duration: 0.03,
3568
+ layers: [
3569
+ // the relay: a click of bright noise and a short low knock
3570
+ { source: { kind: "noise", offset: "random" }, filters: { mode: "serial", bank: [{ type: "highpass", frequency: { set: 2600 } }] }, envelope: [[0, 0.22, "set"], [0.025, 1e-4, "exp", "fixed"]] },
3571
+ { source: { kind: "osc", wave: "triangle", frequency: { set: 180 } }, envelope: [[0, 0.12, "set"], [0.03, 1e-4, "exp", "fixed"]] }
3572
+ ],
3573
+ falloff: { model: "none" }
3574
+ },
3575
+ {
3576
+ id: "starter_crank",
3577
+ bus: "world",
3578
+ duration: 0.9,
3579
+ layers: [
3580
+ // the starter motor turning the engine over: low noise chopped at the compression strokes
3581
+ {
3582
+ source: { kind: "noise", offset: "random" },
3583
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 420 } }] },
3584
+ modulators: [{ target: "gain", wave: "square", rate: 11, depth: 0.08, base: 0.1 }],
3585
+ envelope: [[0, 1e-4, "set", "fixed"], [0.05, 0.25, "linear"], [0.8, 0.22, "set"], [0.9, 1e-4, "exp", "fixed"]]
3586
+ },
3587
+ { source: { kind: "osc", wave: "sawtooth", frequency: { set: 95, to: 120, curve: "linear", over: 0.9 } }, filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 300 } }] }, envelope: [[0, 0.04, "set"], [0.9, 1e-4, "exp", "fixed"]] }
3588
+ ],
3589
+ falloff: { model: "none" }
3590
+ },
3591
+ {
3592
+ id: "car_alarm",
3593
+ bus: "world",
3594
+ order: "siren",
3595
+ duration: 3,
3596
+ tail: 0.1,
3597
+ layers: [
3598
+ // the whoop of a car alarm: a square swept up and down three times a second
3599
+ {
3600
+ source: { kind: "osc", wave: "square", frequency: { set: 1100 } },
3601
+ modulators: [{ target: "frequency", wave: "triangle", rate: 3, depth: 420 }],
3602
+ filters: { mode: "serial", bank: [{ type: "lowpass", frequency: { set: 2600 } }] },
3603
+ envelope: [[0, 1e-4, "set", "fixed"], [0.05, 0.05, "linear"], [2.9, 0.05, "set"], [3, 1e-4, "exp", "fixed"]]
3604
+ }
3605
+ ],
3606
+ falloff: { model: "none" }
3607
+ },
3608
+ {
3609
+ id: "alarm_chirp",
3610
+ bus: "world",
3611
+ duration: 0.1,
3612
+ layers: [{ source: { kind: "osc", wave: "square", frequency: { set: 2300 } }, repeat: { count: 2, spacing: 0.16 }, envelope: [[0, 0.05, "set"], [0.09, 1e-4, "exp", "fixed"]] }],
3613
+ falloff: { model: "none" }
3614
+ },
3615
+ {
3616
+ id: "wiper_swish",
3617
+ bus: "world",
3618
+ duration: 0.45,
3619
+ layers: [
3620
+ {
3621
+ source: { kind: "noise", offset: "random" },
3622
+ filters: { mode: "serial", bank: [{ type: "bandpass", frequency: { set: 700, to: 1100, curve: "linear", over: 0.45 }, q: 1.2 }] },
3623
+ envelope: [[0, 1e-4, "set", "fixed"], [0.2, 0.06, "linear"], [0.45, 1e-4, "exp", "fixed"]]
3624
+ }
3625
+ ],
3626
+ falloff: { model: "none" }
3627
+ },
3628
+ {
3629
+ id: "seatbelt_chime",
3630
+ bus: "world",
3631
+ duration: 0.5,
3632
+ 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"]] }],
3633
+ falloff: { model: "none" }
3634
+ }
3635
+ ];
3636
+ var CABIN_BANK = { id: "vehicle_cabin", sounds: CABIN_SOUNDS, loops: [] };
3637
+
3476
3638
  // src/recipes/foley.ts
3477
3639
  var FOLEY_FALLOFF = {
3478
3640
  model: "inverse_square",