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/items — do 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
dialogue_output_model = true  # NPC sounds play through the game's dialogue output model
voice_slider = true           # voice lines follow the game's Voice volume
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)
voice_log = "off"             # pack-author transcript: off | player | all
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. Where a sound sits in the speakers is dialogue_output_model, not the flags.
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 = each voice follows its speaker, so it is placed and rolled off like a real NPC line (see dialogue_output_model). 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.
dialogue_output_model bool true A sound that follows an NPC (a voice with voice_3d on, or any Play* call given an actor) plays through the game's own Dialogue Output Model 3D default object, the output model a real NPC line uses. The engine then places it in the speakers where the speaker stands and rolls it off with distance by that model's curve (vanilla: 70 to 3000 units; sound overhauls change it). A sound built from a bare file has no output model of its own: false restores the playback before 0.9.30, centered and at full level wherever the speaker is, with voice_attenuation as the only falloff. The player's own lines are left centered either way. A/B it live with autest outmodel.
voice_slider bool true Voice lines (the PlayVoice family, PlayFileWithLipSync, PlayFolderWithLipSync) are scaled by the game's Voice volume (Settings, Audio), the way real dialogue is, so they stay level with it, with or without an actor to follow. PlaySFX, PlayFile and PlayFolder are left alone, and so is a PlayVoice that finds no voice folder and falls back to the sfx slot or the [sfx] table. The slider is read when a line starts. false = voice lines ignore the slider, as before 0.9.30. A/B it live with autest voiceslider.
voice_attenuation bool false Distance-based volume falloff for follow-positioned sounds (voice + SFX) that the engine does not roll off itself, which is all of them while dialogue_output_model is false. Loose-file playback has no sound-descriptor rolloff curve, so such a sound across the room otherwise plays at full volume. When on, each of them is scaled by the player-to-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).
voice_log string "off" Pack-author playback transcript. Writes a second log file beside AudioUtil.log — AudioUtil_Voices.log — with one line per voice line: the category a consumer asked for, the facts it carried, the slot + folder resolution actually landed on, and the exact wav that played. It answers the question AudioUtil.log cannot: "my pack has a folder for this beat, so why did I hear a stock moan?" A line that resolved out of the speaker's own slot is marked !, so a category-name or alias mismatch reads as a column of ! instead of as unexplained silence; MISS lines record calls where nothing played at all. "player" logs the player character only (the usual authoring case), "all" every speaker. Toggle live with autest voicelog — no reload; re-running it restarts the file, so it can capture a single scene.
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.0–2.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. A mod that voices an unvoiced topic itself lifts this for its own lines with dialogueGuard = false on PlayVoiceFromSlotEx / PlayFileWithLipSyncEx.
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 (ENGLISH → en, RUSSIAN → ru, GERMAN → de, …; 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, "D" = the tagged layout, anything else (incl. unset) = "A"; first letter only, case-insensitive. 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; PlayVoiceTagged ≡ PlayVoice). 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.0–1.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