Sound
Music, ambience and interface sounds as loose audio files in a mod, wired up by .ulaudio cue files.
Every sound the game plays comes from mod files: loose .mp3, .wav or .flac files in the mod's Audio/ folder, and .ulaudio files that name them. There is no import step. Drop a file in, point a cue at it, and the game plays it on its next start.
Replacing the main menu song, giving every bar its own playlist, or silencing the city: each is one cue in a .ulaudio file.
Cue files: .ulaudio
json// Audio/MyMod.ulaudio
{
"Cues": {
"Music.MainMenu": { "Files": ["Music/theme.mp3"] },
"Music.Venue.bar": { "Files": ["Music/Bar/*.mp3"], "Mode": "Shuffle", "Volume": 0.8 },
"Ambience.Venue.cafe": { "Files": ["Ambience/cafe_morning.mp3", "Ambience/cafe_busy.mp3"] },
"UI.Click": { "Files": ["UI/click.wav"], "Volume": 0.5 }
}
}
A cue is a sound the game asks for by name. Its fields:
| Field | Default | Meaning |
|---|---|---|
Files |
— | Paths relative to the .ulaudio file. A * in the file name takes every match in that folder. Several files: one is picked at random each time, or all of them in turn with "Mode": "Shuffle" |
Bus |
from the cue's name | Music, Ambience, Interface or Effects. The player's volume slider for that bus applies. Without it, the start of the cue's name decides (Music.…, Ambience.…, UI.…) |
Volume |
1 |
This cue's own level, multiplied by the player's volume sliders |
Mode |
Shuffle for a music cue with several files |
Shuffle: play every file once, in a random order, then reshuffle, forever |
Loop |
on for music and ambience | Loop the one file that was picked |
FadeIn, FadeOut |
2 s for music and ambience, 0 otherwise |
Crossfade length when the cue starts or gives way to another |
Use |
— | Another cue's name: this cue plays that one instead. Venues that Use the same station carry on with its song from one door to the next |
Inserts |
— | For a shuffled cue: { "Files": ["Jingles/*.mp3"], "Every": 2 } plays one of these files (an ad, a station jingle) after every 2 songs |
Cues with the same name in a later mod replace the earlier one whole (see load order). A cue with an empty Files list is silence on purpose: "Music.City": { "Files": [] } turns the map's music off. A cue whose files are all missing (a Music/Bar/*.mp3 folder still empty) counts as no cue at all, so the game falls back as if it were not there.
Stations
A station is an ordinary shuffled cue with inserts, and venues point at it with Use:
json"Radio.FM": { "Files": ["Music/*.mp3", "Music/City/*.mp3"], "Mode": "Shuffle",
"Inserts": { "Files": ["Jingles/*.mp3"], "Every": 2 } },
"Music.City": { "Use": "Radio.FM" },
"Music.Venue.cafe": { "Use": "Radio.FM" }
ul_Base ships two: Radio.FM (songs with ads, on the map and in shops, cafés, restaurants and arcades) and Radio.Night (dance music without ads, in bars, pubs and clubs). The cue's Volume is the station's; a cue that Uses it plays at that volume.
The cues the game asks for
| Cue | When |
|---|---|
Music.MainMenu |
The start screen. It keeps playing through character creation |
Music.City, Ambience.City |
On the map |
Music.Venue.<tag> |
Inside a venue: its amenity tag (bar, nightclub), else its group (food, leisure, homes), else Music.Venue.Default. If none of them exists, the city's music carries on |
Ambience.Venue.<tag> |
The same lookup for the room's background sound. None: no ambience |
UI.Click |
Any enabled button or checkbox pressed, on every screen |
Sex.Moan.<voice>.<level> |
A moan during a sex scene. Each person keeps one voice; the level follows their arousal. Voices, levels and timing are set in Anims/SceneSounds.json |
Sex.Thrust, or the cue thrust_by_tag names |
At each beat of a sex position: the moment the two hips stop closing in. Found in any clip, nothing to mark |
Music and ambience are separate channels, so a bar can have both a playlist and a crowd behind it. Asking for the cue that is already playing changes nothing, so walking from one bar to the next keeps the song going when both name the same cue.
Files
- Formats:
.mp3,.wav(PCM) and.flac. Ogg Vorbis is not read; convert it (ffmpeg -i in.ogg out.wav). - Size: music and ambience are decoded when they start and dropped when they stop. Interface sounds stay in memory, so keep them short.
- Levels: keep songs in one playlist at about the same loudness (around −15 LUFS). The game does not even them out.
- Loops: an ambience loops sample-exact, so cut the file where its end meets its start.
- Licences: only ship what you may redistribute. Put the credits beside the files (
Audio/UI/CREDITS.txtinul_Baseis an example).
The player's sliders (overall, music, ambience, interface) and the sound on/off switch are in Settings → Sound.
Voices
A person can play a sound file from their mouth, with the lips moving to it:
lualocal a = Game:GetPlayer()
a:speak("Voice/greeting_01.wav") -- a file under any mod's Audio/ folder; later mods win
-- a:isSpeaking(), a:stopSpeaking()
The engine reads the clip once and moves the Genesis 8 visemes to it (jaw on loudness, the vowel shapes from the spectrum, lips shut on the short stops, hiss shapes on "s" and "sh"), so any clip works: a recorded line, a scream, a laugh, a generated voice. The sound comes from the person's head, spatialised, on the Voice bus (its own volume setting). Scene lines without a clip still move the mouth, silently, to their text for about the time the line takes to read.
The news on television
Every furniture piece whose .ulmat has a Screen zone is a television. When a bulletin is on
air and a screen stands in a visible room, the engine films two anchors in a studio nobody can
visit and shows the same picture on every screen (one small capture, not one per screen), the
voice coming from the nearest television. The bulletin itself comes from a mod:
luaNews:broadcast({
day = 12, channel = "Underlives News",
anchors = { A = anchorAId, B = anchorBId }, -- person ids (People:create once, keep the ids)
items = {
{ who = "A", text = "Good evening. This is Underlives News.", clip = "Voice/open_03.wav" },
{ who = "B", text = "A body was found last night at a bar downtown.", severity = 5 },
},
})
clip is optional: without one the anchor mouths the line silently. wardrobe names the
uniform the anchors wear on air, from Clothes/Outfits/uniforms.txt (work, the office suits, when omitted). The shipped ul_News mod
composes a bulletin at the first hour of each day from the Witness rows nobody has reported on,
the election, and what other mods push with News_push{ who = "A", text = "...", severity = 3 };
every sentence it can say is in ul_News/Lua/ul_news_data.lua, and a mod adds stories for its own
Witness act types by extending NewsData.BEATS from a Lua/ file that loads after it.