Skip to content

Config Reference

Every table and key in the config — the base Data\SKSE\Plugins\AudioUtil\AudioUtil.toml and any AudioUtil\config\*.toml overlays, which are merged into one effective config. Defaults shown are the DLL's internal defaults, applied when a key is absent from every file. All keys/names are normalized (case- and space-insensitive).

Paths written 'Sound\...' are Data-relative. Single-quoted TOML literal strings are used throughout so backslashes need no escaping.

[general]

Base-only section

[general] (along with [ppa], the [lipsync] scalar tuning, [captions], and the [gag] enable/default_category toggles) is a global section, read only from the base AudioUtil.toml. If a config\*.toml overlay sets any of these, it is ignored with a warning in AudioUtil.log. The additive exceptions inside those tables — [lipsync] block_categories and [gag] keywords/itemsdo merge from every file. See the merge rules.

[general]
log_level = "info"            # trace | debug | info | warn | error
sound_flags = 0x1A            # BuildSoundDataFromFile flags
sound_priority = 128
default_female_slot = "F0"
default_male_slot = "M0"
default_creature_slot = "C0"    # unrouted creatures; "" = silent (never borrow a human slot)
pc_female_slot = ""           # reserved player slot; empty = no reservation
pc_male_slot = ""
voice_3d = true               # 3D-position voices at the speaker; false = flat/2D
voice_no_interrupt = false    # skip a new line while its channel is still playing
voice_attenuation = true      # distance-based volume falloff for follow-positioned sounds
attenuation_near = 200.0      # full volume within this distance (~3 m)
attenuation_far = 1800.0      # volume reaches the floor at/beyond this distance (~26 m)
attenuation_floor = 0.05      # minimum volume factor for far sounds (0.0 = silent)
Key Type Default Meaning
log_level string "info" Verbosity of AudioUtil.log.
sound_flags int 0x1A BuildSoundDataFromFile flags. Sweep with DebugPlayFile if audio is silent or not 3D.
sound_priority int 128 Sound priority passed to the engine.
sfx_slot slot id "SFX0" The [[slot]] whose categories PlaySFX resolves before the flat [sfx] table (see SFX). "" = table only.
default_female_slot slot id "F1" Slot for unrouted female actors.
default_male_slot slot id "M1" Slot for unrouted male actors. Creatures never reach it — see below.
default_creature_slot slot id "" Slot for an unrouted creature (an actor whose race carries no ActorTypeNPC keyword). Creatures never fall through to default_male_slot or its blind scan: everything those can pick is an 'F'/'M' human voice pack, so an unmapped frostbite spider would otherwise speak human lines. "" = the creature resolves to nothing and stays silent, and GetSlotForActor returns "" so a script can test before playing. Explicit routing (a [race_map] hint, an [npc_overrides] pin, PlayVoiceFromSlot) is unaffected.
pc_female_slot slot id "" Reserved for the player; no NPC ever resolves to it.
pc_male_slot slot id "" Same, for a male PC.
voice_3d bool true true = 3D-position each voice at the speaker (distance attenuation). false = play flat/2D at full volume so every speaker is equally audible. Lipsync is unaffected either way.
voice_no_interrupt bool false true = when a PlayVoice names a channel still playing a line, skip the new line instead of cutting the old one off (per channel — different speakers still overlap). SFX and PlayFile/PlayFolder are unaffected.
voice_attenuation bool false Distance-based volume falloff for follow-positioned sounds (voice + SFX). Loose-file playback has no sound-descriptor rolloff curve, so voice_3d positions a sound but the engine barely lowers its volume with distance — a scene across the room otherwise plays at full volume. When on, each follow-positioned instance is scaled by the player→speaker distance, re-sampled every 250 ms while the line plays so movement tracks.
attenuation_near float 200.0 Full volume within this distance (game units, ~70/m — so ~3 m).
attenuation_far float 1800.0 Volume reaches attenuation_floor at/beyond this distance (~26 m). Between near and far the curve is an inverse-distance rolloff (−6 dB per doubling, the point-source law) renormalized to land on the floor at far.
attenuation_floor float 0.05 Minimum volume factor for far sounds (0.0 = silent beyond attenuation_far).
fuz_cache_max_mb int 256 Size cap for Sound\AudioUtilFuzCache\ (each played .fuz decodes its audio there once, as PCM wav — see PlayFile). Checked once per launch and on ReloadConfig; oldest files deleted first. ~88 KB per second of audio, so the default holds on the order of a thousand voice lines. 0 = unlimited. Deleting the folder by hand is always safe — it rebuilds on demand.

[ppa]

[ppa]
enable = false                # neutral default ships disabled
event_rate_ms = 2000
Key Type Default Meaning
enable bool true (internal) Gate the Accurate Penetration bridge. The shipped neutral TOML sets false; the bridge also needs the PPA plugin present.
event_rate_ms int 2000 Min interval per receiver for AudioUtilPPA_Update mod events. Floored at 1000 ms (values below are clamped up). Context-bit changes still fire immediately.

[lipsync]

Scalars base-only; block_categories is additive

The scalar tuning below (enable, gain, attack_ms, release_ms, min_level, block_in_dialogue) is base-only — an overlay that sets it is ignored with a warning. block_categories is the exception: it merges from every file (base + overlays, union), so an add-on can mark its own category pools mouth-still without owning the base config.

[lipsync]
enable = true
gain = 1.0                    # 0.0-2.0 mouth-open strength
attack_ms = 30                # how fast the mouth opens toward a louder level
release_ms = 90               # how fast it closes on quiet / clip end
min_level = 0.04              # envelope levels below this keep the mouth closed
block_in_dialogue = true      # never lipsync an actor while they talk to the player
block_categories = ["Slurp"]  # never drive the mouth (additive — merges across overlays)
Key Type Default Meaning
enable bool true Amplitude-envelope lipsync master switch.
gain float 1.0 Mouth-open strength, 0.02.0 (1.0 = envelope as-is).
attack_ms int 30 Opening speed toward a louder level.
release_ms int 90 Closing speed on quiet / clip end.
min_level float 0.04 Envelope levels below this keep the mouth closed.
block_in_dialogue bool true Suppress lipsync for an actor while they're in a dialogue with the player — the game's own dialogue/voice system drives that mouth from the real voice file, so AudioUtil stays off it to avoid fighting. Checked when the line starts and re-checked on a 500 ms throttle, so a conversation started mid-line hands the mouth back.
block_categories string list (none) Additive (merges from the base + every overlay). Requested categories that never drive lipsync — the line plays mouth-still. Matched on the requested name (normalized), before aliasing, across every slot. For pools that aren't vocalization (oral SFX / slurping) or where another system owns the mouth (a climax/ahegao face). Same effect as passing blockLipSync=true to PlayVoice, but declared once in config.

Runtime overrides: SetLipSyncEnabled / SetLipSyncGain. ReloadConfig restores these TOML values.

Lipsync is suppressed automatically for a gagged actor — when the speaker wears a marker configured in [gag], the device owns the mouth and the DLL won't drive it (checked when the line starts and re-checked on a 500 ms throttle, so a gag equipped mid-line hands the mouth over). No mouth-open threshold to tune.

[captions]

On-screen captions for played lines. When a wav has a same-named .toml sidecar next to it (moan_03.wav + moan_03.toml) holding per-language text, the text for the configured language is shown as a regular game subtitle attributed to the speaking actor while the line plays (and an AudioUtil_Caption mod event is sent — see the API page). No sidecar = no caption, so leaving this enabled is safe; voice packs opt in per file by shipping sidecars. Sidecars are loose files only (not read out of BSAs).

# Sound\SLOVE\F1\Moan\moan_03.toml — sidecar for moan_03.wav
en = "Does this count as extra follower duty?"
ru = "Это считается дополнительной службой?"
[captions]
enable = true
language = "auto"             # sidecar key to read; "auto" = follow the game's sLanguage ini
hud = true                    # false = only send the mod event (consumer draws its own UI)
Key Type Default Meaning
enable bool true Captions master switch (base-only, like all scalars here).
language string "auto" Which sidecar key to read. "auto" maps the game's sLanguage ini setting to a two-letter code (ENGLISHen, RUSSIANru, GERMANde, …; an unmapped language matches by its lowercased full name). Any other value is used verbatim (lowercased), so packs may invent their own keys. A missing localization falls back to the sidecar's en text.
hud bool true false = never inject the HUD subtitle, only send the AudioUtil_Caption mod event — for a consumer mod that renders captions with its own widget (pair with GetHandleCaption).

Runtime override: SetCaptionsEnabled. ReloadConfig restores the TOML value (and re-resolves the language + re-reads edited sidecars).

[gag]

Gag / deepthroat / mouth-owning devices. When a speaking actor wears any configured gag marker — a keywords keyword or a specific items item form — AudioUtil (1) routes their voice through the slot's gag_slot so a muffled clip plays instead of the clear line, and (2) suppresses lipsync (see above). Detection is native — the DLL reads the actor's worn items, no Papyrus wiring.

Reach for keywords first: one keyword matches a whole device family. Use items for a one-off gag that carries no usable keyword — you point straight at the worn item form (usually an ARMO).

Dormant unless keywords or items is set, so the SFW-neutral default is unaffected.

[gag]
enable = true
default_category = "GagMoan"     # muffled catch-all for categories the gag slot lacks
keywords = [                     # 'Plugin.esp|FormID' (hex), like [npc_overrides] keys
  'Devious Devices - Assets.esm|7EB8',
  'ZaZAnimationPack.esm|8A4D',
  'Toys.esm|8C2',
]
items = [                        # specific worn item forms, same 'Plugin.esp|FormID' form
  'MyGagMod.esp|000800',
]
Key Type Default Meaning
enable bool true Master switch. Feature is also inert while both keywords and items are empty.
default_category category "" Played from the gag slot when the requested category has no audio there — a muffled catch-all so a gagged actor never leaks a clear line (and never falls silent). Empty = no catch-all.
keywords list of 'Plugin.esp\|FormID' (none) Worn keywords that mark an actor as gagged. Hex form id, 0x optional; same format as [npc_overrides] keys. A keyword whose plugin isn't in the load order is skipped (one debug line), so listing optional mods is harmless.
items list of 'Plugin.esp\|FormID' (none) Specific worn item forms (usually ARMO) that mark an actor as gagged — for a gag with no keyword to match. Same form-id format and same skip-if-missing behavior as keywords.

See Resolution → gag redirect for how the redirect interacts with the category chain.

[[slot]]

An array of tables — one per voice pack. See Overview → three ways and Resolution.

[[slot]]
id = "M4"
sex = "male"                  # "male" | "female" | "all"
path = 'Sound\fx\MyMod\M4'    # scanned for <Category>\*.wav subfolders
fallback = "M1"               # optional: backfill empty categories from this slot
gag_slot = "M4gag"            # optional: muffled parallel slot used when gagged
# variation = "B"             # optional schema label (see below); default "A"
Key Type Meaning
id string Slot id ("F1", "M4", "C2", …).
sex string "male", "female", or "all". "all" is sex-neutral: it matches either sex on explicit routes ([race_map]/[voicetype_map]/[npc_overrides]) but is skipped by the blind default-by-sex fallback. For the category layer it shares the male aliases/[category_fallbacks.male] (where creature/neutral fallbacks are authored) and skips [male_only_remap]. Use it for creature slots (a creature's reported sex is unreliable) and the sfx slot. Plain "male"/"female" otherwise; creatures otherwise read as male.
path path Folder scanned for <Category>\*.wav subfolders. Optional if the slot is defined purely by explicit categories. Loose files only.
fallback slot id Optional. Per-category backfill slot when a category resolves to nothing here. Chains capped at 4 hops.
gag_slot slot id Optional. A parallel slot (another [[slot]], same category names, muffled audio) used instead of this one when the speaking actor is gagged. See [gag].
variation string Optional per-slot schema label, surfaced to Papyrus via GetSlotVariation. "B" = the alternate layout, anything else (incl. unset) = "A". AudioUtil does not interpret it — resolution, fallback and scanning are unchanged regardless of value. It exists purely for a consumer mod whose voice packs ship in more than one folder/category layout, so the consumer can read it per resolved slot and gate its own category routing. Leave unset if all packs share one layout.
[slot.categories] table Optional explicit categories (below). Win over same-named scanned folders.

[slot.categories] — explicit categories

A category value may be an array (a file list) or a string (one folder):

[[slot]]
id = "C1"
sex = "male"
[slot.categories]
# array: explicit file list — needs no loose files, may point into a BSA
BattleCry = [
  'Sound\FX\NPC\Giant\AttackVocal\NPC_Giant_AttackVocal_01.wav',
  'Sound\FX\NPC\Giant\AttackVocal\NPC_Giant_AttackVocal_02.wav',
]
# string: one folder scanned for this category ('Sound\...' = full Data path,
# otherwise relative to the slot's path). Loose files only.
WarCry = 'Sound\fx\SomeOtherMod\shouts'
Rally = 'Sound\fx\SomeOtherMod\shouts'
  • Array (file list) — bypasses the filesystem scan, so it can reference BSA-packed audio (loose files still win over archives at play time). List a file more than once to weight it — a clip listed twice is drawn about twice as often, and the shuffle bag's no-back-to-back guard still keeps it from playing twice in a row.
  • String (folder) — scanned like a slot folder; lets several categories share one pool of files without copies. Loose files only.

Slot resolution tables

Full behavior in Voice & Category Resolution.

[voicetype_remap]             # rename a voicetype (one hop) before slot lookup
enable = true
MaleGuard = "MaleNord"        # values are voicetype names, not slot ids

[voicetype_map]               # voicetype -> slot id, or a list of candidates
MaleEvenToned = "M1"
MaleBandit = ["M3", "M4"]

[race_map]                    # race-editor-id substring -> slot id(s); longest hint wins
Nord = "M4"
Troll = "C1"

[npc_overrides]               # 'Plugin.esp|FormID' -> slot; ESL: last 3 hex digits
'MyFollower.esp|000D62' = "F2"
Table Key Value
[voicetype_remap] enable (bool) + voicetype name Target voicetype name (must be a [voicetype_map] key).
[voicetype_map] voicetype name Slot id or list of slot ids (spread deterministically per actor).
[race_map] race-id substring Slot id or list. Longest matching hint wins.
[npc_overrides] 'Plugin.esp\|FormID' Slot id. Checked before voicetype/race.

Category layer tables

Full behavior in Category resolution.

[category_aliases.female]     # script name -> on-disk folder
BattleCry = "War Shout"
[category_aliases.male]
# ...

[male_only_remap]             # male slots only: female-engine category -> male category
ComfortLines = "Calm Lines"

[category_fallbacks.female]   # substitute when a category has no folder (chained)
Whisper = "Murmur"
[category_fallbacks.male]
# ...
Table Applies to Meaning
[category_aliases.female] / .male that sex's slots Rename a requested category to the actual folder name.
[male_only_remap] male slots only Substitute a male category for a female-engine category name.
[category_fallbacks.female] / .male that sex's slots Substitute when a category resolves to no folder. Chained since 0.9.18 (max 8 hops): a substitute that also has no folder falls through to its own entry, so the table can be a ladder. The full ladder is walked in one slot before the slot's fallback slot.

[tags]

Defines the tag vocabulary for tag-scored pools: named axes of mutually-exclusive tokens, each axis with a match weight (≤ 64 tokens total). The plugin ships none — no [tags] anywhere means the whole tag layer is dormant (category folders scan flat; PlayVoiceTaggedPlayVoice). Additive across the base + overlays like slots/sfx, so several consumer mods can ship their own axes and coexist: same-named axes union their tokens (no file can remove another's), weight is last-writer-wins in sorted filename order.

[tags]
mood      = { tokens = ["calm", "angry", "afraid"],  weight = 8 }
intensity = { tokens = ["soft", "intense"],          weight = 4 }
target    = { tokens = ["human", "beast", "undead"], weight = 1 }

See Tag-Scored Pools for how packs tag files and how a call's facts pick a pool.

[groups]

Startup volumes, 0.01.0. Startup state only — a consumer mod overrides at runtime via SetGroupVolume (AudioUtil ships no MCM of its own). Groups are created on first use; these are just the conventional ones.

[groups]
pc_high = 1.0
pc_low = 1.0
partner_high = 1.0
partner_low = 1.0
sfx = 1.0
oneshot = 1.0

SFX — the sfx slot + [sfx] table

PlaySFX(name, ...) plays a named shuffle-bag pool, and a voice category with no folder falls through to an sfx pool of the same name (last resort). A name resolves in two places, in order:

  1. A category of the sfx slot — id SFX0 by default, set sfx_slot in [general] to rename ("" disables it). Since it's a normal [[slot]], each sfx pool can use any category form — a scanned folder, an explicit file list (BSA-capable), or a folder ref. Preferred, and the only way to give an sfx pool BSA-packed audio.
  2. The [sfx] table — flat name = folder, loose files only. The value is a full Sound\... Data-relative path, same as a slot path.

Names are matched directly (no category_aliases/fallbacks). The slot's sex is irrelevant to sfx lookup.

[[slot]]
id = "SFX0"                          # PlaySFX resolves categories here first
sex = "all"                          # sex-neutral (irrelevant to sfx lookup)
[slot.categories]
Clap = 'Sound\fx\MyMod\SFX\Clap'          # scanned folder
Slap = ['Sound\fx\MyMod\SFX\slap01.wav']  # explicit list — may point into a BSA

[sfx]                                # legacy flat table, still supported
Thud = 'Sound\fx\MyMod\thuds'        # full Data path (loose files only)
Clap = 'Sound\fx\MyMod\SFX\Clap'     # full Data path