Voice Settings and Overrides

Create voice packs that replace framework voice playback during PPA-tracked sex scenes. Voice presets can match actor sex, race, base actor, scene context, penetrated site, depth, speed, and girth, then play shuffled audio with optional lip sync.

Overview

Voice configs are independent TOML files. Each file can define global voice timing settings, one or more [[VoicePreset]] entries, or both.

At runtime, PPA evaluates the presets for each tracked actor in a sex scene. When a preset matches and one of its triggers fires, PPA plays one sound from that preset and disables the framework voice for that actor.

Mental Model

Use predicates to decide who and when a preset applies. Use triggers to decide how often matching presets can speak. Use priority to decide which matching preset wins when several triggers fire at once.

Config Files

Place voice config files directly in:

Data/SKSE/Plugins/ppa-voice-configs/

Every direct child with a .toml extension is loaded. Files are sorted by filename, then resolved in that order. A file can force other files to resolve first with a top-level Inherits array.

Inherits = ["00_shared_voice_base.toml"]
File FieldTypeDescription
Inheritsstring[]Optional top-level list of other voice config filenames. Parent paths are resolved relative to ppa-voice-configs.
[Settings]tableOptional global voice tuning. These values are not per-preset.
[[VoicePreset]]arrayOne or more voice presets to append to the global preset list.
Global Settings Load Order

[Settings] is one global snapshot. Each resolved file replaces it with that file's settings or defaults, so keep global voice tuning in a late-loading file such as zzz_voice_globals.toml and avoid later voice files that omit [Settings].

Audio and Lip Files

Sounds entries are paths relative to the game's Data folder. Wildcards support one * per path and expand alphabetically at load time.

Sounds = [
    "Sound\\PPA\\Voices\\Female\\moan_*.wav",
    "Sound\\PPA\\Voices\\Female\\single_line.wav",
]

For lip sync, place a matching .lip file next to the audio using the same relative stem. For Sound\PPA\Voices\Female\moan_01.wav, PPA looks for Sound\PPA\Voices\Female\moan_01.lip.

Missing Files

Wildcard directories that do not exist expand to nothing. Non-wildcard sound paths are kept as written and can still fail later if the audio file cannot be opened by Skyrim.

Quick Start

This is the smallest useful voice preset: it matches female actors and plays one random sound every two seconds while no other PPA voice line is active.

[[VoicePreset]]
Name = "Female interval voice"
Priority = 0
Sounds = ["Sound\\PPA\\Voices\\Female\\moan_*.wav"]

[VoicePreset.Predicates]
Sex = [1]

[[VoicePreset.Triggers]]
Type = "Interval"
Interval = 2.0
RandomChance = 70
MinDelay = 1.0

Global Settings

Global settings control volume and thrust detection for every voice preset.

SettingDefaultDescription
MasterVolume1.0Volume applied to every PPA voice sound handle.
VelocitySmoothFactor0.3Smoothing factor for depth velocity used by thrust detection.
ThrustStartVelocity5.0Smoothed forward velocity required before an OnThrust trigger can fire.
ThrustMinWithdrawalRatio0.65Required withdrawal depth as a ratio of the previous stroke peak before a new thrust can be detected.
ThrustMinPeakVelocity8.0Minimum peak velocity required during the current forward stroke.
ThrustMinDepth0.5Minimum penetration depth required before a thrust event can be accepted.
[Settings]
MasterVolume = 0.85
VelocitySmoothFactor = 0.35
ThrustStartVelocity = 5.0
ThrustMinWithdrawalRatio = 0.65
ThrustMinPeakVelocity = 8.0
ThrustMinDepth = 0.5

Voice Presets

A preset contains the audio pool, matching rules, trigger rules, and priority for one type of voice line.

Preset FieldTypeDescription
NamestringOptional label used for human organization.
PriorityintHigher priority wins when multiple matching presets fire in the same frame. Default is 0.
Soundsstring[]Data-relative sound paths. Empty presets never play.
[VoicePreset.Predicates]tableAll configured predicate groups must pass.
[[VoicePreset.Triggers]]arrayOne or more trigger rules. If omitted, the preset gets one default interval trigger at two seconds.

Sounds inside a preset use a shuffle bag. PPA plays every sound once before reshuffling, and avoids immediately repeating the last sound when there is more than one choice.

Predicates

Predicates are optional. If a preset has no predicates, it can match any tracked actor. When predicates are present, every predicate must pass before triggers are considered.

PredicateTypeDescription
Sexint[]Matches actor sex. Skyrim values are 0 male and 1 female.
Racesstring[]Matches the actor's race FormID.
BaseIDsstring[]Matches the actor base FormID. Use this for a specific NPC voice override.
PenetrationDepthMin / MaxfloatRequires max penetration depth to fall within the range. The predicate is only created when max is greater than min.
SpeedMin / MaxfloatRequires tracked penetration speed. If SpeedMax is omitted or not greater than min, the check becomes minimum-only.
PenisGirthMin / MaxfloatRequires max penetrator girth to fall within the range. Set both min and max.
PenetratedSitesstring[]Matches active penetrated sites. Values are Vagina, Anus, Mouth, HandL, and HandR.
PenetratingSitesstring[]Parsed by the loader, but the current voice runtime does not populate penetrating-site data. Prefer PenetratedSites.
Contextsstring[]Requires scene context flags such as Vaginal, Anal, Oral, Aggressive, FemDom, Loving, Dirty, Boobjob, Handjob, Footjob, or Masturbation.

String predicates for sites and contexts can be negated with !. Non-negated site values in the same array are ORed together, then negated values are applied as extra must-not-match checks.

[VoicePreset.Predicates]
Sex = [1]
Races = ["Skyrim.esm|013745"]
PenetratedSites = ["Mouth", "!Anus"]
Contexts = ["Oral", "!Aggressive"]
PenetrationDepthMin = 0.4
PenetrationDepthMax = 2.2
PenisGirthMin = 0.8
PenisGirthMax = 1.6
FormID Syntax

Use PluginName|LocalID, for example Skyrim.esm|013745. In voice configs, a FormID without a plugin name defaults to Skyrim.esm.

Hand Sites

The parser accepts Hands, but active hand interactions are expanded to HandL and HandR for evaluation. Use both explicit hand values when you want either hand.

Triggers

A preset can contain multiple triggers. If any trigger fires while the preset matches, the preset becomes a candidate to play.

Trigger FieldDefaultDescription
TypeIntervalInterval, OnThrust, or Random. Unknown values fall back to Interval.
Interval2.0Seconds between checks for Interval triggers.
RandomChance100.0For Interval and OnThrust, percent chance when the event happens. For Random, approximate percent chance per second.
MinDelay0.0Minimum seconds after this trigger successfully played before it can fire again.
[[VoicePreset.Triggers]]
Type = "OnThrust"
RandomChance = 55
MinDelay = 1.25

[[VoicePreset.Triggers]]
Type = "Interval"
Interval = 4.0
RandomChance = 30

OnThrust uses depth changes from active sites and the global thrust settings. The config loader does not currently parse a per-trigger site field, so an OnThrust trigger watches any detected thrust for the matching actor.

Runtime Selection

1

PPA builds an evaluation context for each tracked actor in a sex scene.

2

Presets are sorted highest priority first after all files load. Equal priorities keep file load order.

3

When no PPA voice line is active, every matching preset's triggers are evaluated.

4

If multiple presets fire in the same frame, the highest priority candidate plays.

5

While a PPA voice line is active, trigger timers keep advancing but no new line starts until the current line ends.

Use higher priority for specific cases such as oral, aggressive, named NPC, or creature-race lines. Use lower priority for broad fallback moans.

Troubleshooting

SymptomLikely CauseFix
No voice system activityNo loaded preset contains sounds, or ppa-voice-configs does not exist.Create the folder and add at least one .toml file with a non-empty Sounds array.
Preset never matchesPredicate is too strict, wrong FormID syntax, or context tags are not present for that animation.Start with only Sex and PenetratedSites, then add constraints one at a time.
Wildcard finds nothingThe wildcard directory is missing under Data or the prefix/suffix does not match filenames.Test with one explicit sound path before switching to wildcards.
Globals seem resetA later-loaded file omitted [Settings], replacing globals with defaults.Move global tuning to a last-loading file and avoid later voice TOML files.
No lip syncThe matching .lip file is missing or has a different stem.Place the .lip beside the audio path using the exact same filename stem.

Example: Base Voice Pack

00_female_base.toml

Broad female fallback lines with one interval trigger.

[[VoicePreset]]
Name = "Female fallback"
Priority = 0
Sounds = ["Sound\\PPA\\Voices\\Female\\fallback_*.wav"]

[VoicePreset.Predicates]
Sex = [1]

[[VoicePreset.Triggers]]
Type = "Interval"
Interval = 2.6
RandomChance = 65
MinDelay = 1.0

Example: Oral Thrust Lines

This higher-priority preset only applies during oral scenes where the actor's mouth is the penetrated site.

Inherits = ["00_female_base.toml"]

[[VoicePreset]]
Name = "Female oral thrusts"
Priority = 50
Sounds = ["Sound\\PPA\\Voices\\Female\\oral_thrust_*.wav"]

[VoicePreset.Predicates]
Sex = [1]
PenetratedSites = ["Mouth"]
Contexts = ["Oral"]
PenetrationDepthMin = 0.45
PenetrationDepthMax = 3.0

[[VoicePreset.Triggers]]
Type = "OnThrust"
RandomChance = 45
MinDelay = 0.9

Example: Actor Override

Use BaseIDs for a specific NPC or mod-added actor. This preset wins over the broad fallback by priority.

[[VoicePreset]]
Name = "Specific NPC voice"
Priority = 100
Sounds = ["Sound\\PPA\\Voices\\UniqueNPC\\line_*.wav"]

[VoicePreset.Predicates]
BaseIDs = ["MyFollowerPlugin.esp|000D62"]
PenetratedSites = ["Vagina", "Anus"]

[[VoicePreset.Triggers]]
Type = "Random"
RandomChance = 18
MinDelay = 3.0

Example: Global Tuning

Put global tuning in a file that loads after the preset files.

zzz_voice_globals.toml

This file contains only global settings. Because it loads last, later files do not reset the voice globals back to defaults.

[Settings]
MasterVolume = 0.8
VelocitySmoothFactor = 0.32
ThrustStartVelocity = 5.5
ThrustMinWithdrawalRatio = 0.65
ThrustMinPeakVelocity = 8.5
ThrustMinDepth = 0.5