- FMOD Ex 4.44.61, built by FromSoftware, with FMOD Designer projects (
.fev) over sample banks (.fsb, FSB5, all Vorbis). - Banks are encrypted with a reversed bit order and a 31-byte XOR key, and each sample's Vorbis setup header is left out, to be found in a table compiled into
fmodex64.dll. - Animation events name sounds. Footsteps and armour rattles swap in the material code of the floor or the armour. Hits go through
HitEffectSeParam. - Music and wind are MSB sound regions. The Cemetery of Ash has none: it's silent, as in the game.
1The files
| Project | Events | What |
|---|---|---|
main | 2,717 | Everything shared: the player (c0000…), hits (z…), menus (f…), objects (o…), effects (s…) |
smain | 21 | Menu music, a few effects |
m40 | 66 | Firelink's map sounds, one ambience loop |
sm40 (loose bank) | 20 | Streamed ambience (wind) and music: m400000000-3 Iudex Gundyr, m400100000/1 Firelink Shrine |
vm40, rm40 | 1,948, 6 | NPC voices, cutscene sounds |
c<id> | ~50-100 | A character's own sounds, loaded with it (fdp_c%04d.fev) |
Event names are a type letter and nine digits (%c%09d) exe. The letter is the TAE's sound type: a environment, c character, f menu, o object,
p cutscene, s SFX, m music, v voice, g ghost.
2Opening the banks
Encryption
Every byte has its bits reversed, then is XORed with a 31-byte key. The key shows through the zero runs of the header and sample table data. It's FMOD's own FSB encryption, with FromSoftware's key.
Stripped Vorbis
FSB5 keeps only the audio packets. The identification header is rebuilt from the rate and channels (FMOD always uses 256- and 2048-sample blocks), the comment header is empty, and the
setup header (codebooks, floors, residues) comes from 161 headers compiled into fmodex64.dll, keyed by a CRC32 each sample stores. Some are a base header with a patch over its tail exe.
Our bank reader rebuilds each sample as a standard Ogg Vorbis file. All 1,516 samples of the banks we use decode cleanly in FFmpeg. Loop points come along: the bonfire loops from 137,216 to 542,719.
3Event projects
A .fev is a RIFF file whose LGCY chunk is FMOD Designer's own binary project. Its layout was read from fmod_event64.dll's
reader for version 0x45, decompiled in a Ghidra project of its own exe. Every DS3 project parses to its last byte.
event name, volume, pitch, randomisation, priority, max playbacks, mode (3D | world-relative | custom rolloff),
3D max distance, speaker levels, …
layers → { parameter, sounds → sound definition, envelopes }
parameters ("(distance)", "Shahei"), user properties (playDistance, stopDistance, NoHDR …)
sound definitions: play mode, volume and pitch with their randomisation, waveforms → { weight, bank, index }
- Rolloff is a curve per event: a layer envelope on
(distance). The hollow's footstep is full at the source, a third at 20.7 m and silent at 24 m. A second envelope is the 3D pan level (far sounds are more centred). - Variations: a definition lists up to 14 waveforms with weights; the hollow's step has four. Volume and pitch are randomised per play (× 0.82-1 on a run step).
Shahei(遮蔽, occlusion) drives a lowpass when a sound is behind something. What sets it is open.
4Which sound a character plays
TAE events 128-132 (PlaySound_CenterBody, _ByDummypoly, _Weapon…) carry a sound type and id:
| In the clip | Example | Event played |
|---|---|---|
| type 8, floor material | run step 2001 | c0000 + the id with its last two digits replaced by the floor's material: c000002005 on grass, c000002021 in ankle-deep water |
| type 9, armour material | 100 beside each step | the same with the armour's: iron → c000000156 "body-armor" |
| type 1, character | swing 4200 | c000004200 "swing-sword-large" |
The material code is the HitMtrlParam row through SeMaterialConvertParam (identity for 1-49). Which digits are replaced and what happens when a combination has no event are guesses until read in the exe.

c000002003 on soil, then c000002001 on cobblestone, each with the armour's c000000056 a frame earlier.5Hits
victim material (NpcParam: hollow 110 "Flesh _ Weakness")
→ SeMaterialConvertParam → 60
→ HitEffectSeParam row 60: 160 ids in (small, large, large) triplets, by attack material and power
→ z000000060 "iron-cut-meatS_bloodB" · z000001060 "iron-cut-meatL_bloodA/B"
Every id in the rows has a z event in main. A sword hitting a wall plays the wall material's: stone "sparkx", wood "iron-cut-wood", water "mizusibuki".
6Music and ambience by place
MSB sound regions (a type, an id, and up to 16 child regions whose union is the region) place the music and the wind. m40 has 19:
| Region (translated) | Type | Event |
|---|---|---|
| ボス戦闘BGM_グンダ "boss battle BGM, Gundyr" and variants | music | m400000000… |
| 拠点BGM "hub BGM", 火防女BGM "Fire Keeper BGM" | music | m400100000, m400100001 |
| 火防女_レベルアップ呪文 "Fire Keeper's level-up chant" | voice | (vm40) |
| 強風 / 弱風 "strong / light wind" ×8 | environment | a300000100 (stereo) |

m400000000, started by common event 20005831. The knight just stands there; Gundyr kills him at 23 s, and the mix fades.7In our engine
The importer writes one sound bank of ours (.sbank) per project. The engine decodes with stb_vorbis as it plays, resamples linearly to
48 kHz stereo, mixes on the 30 Hz tick and queues on SDL3's audio stream.
What's DS3's
Every event, sample, volume, pitch and randomisation; the distance curves and pan level; max playbacks; loops; the TAE sound events with the floor and armour swap; hit sounds through HitEffectSeParam; the sound regions with the event script's initial state; Gundyr's music by his common event.
Our stand-ins
The listener is the camera; waves are picked at random in every play mode; a master limiter instead of FMOD's HDR mixing; music fades over 2 s. Not done: the occlusion lowpass, reverb (the MSB's sound spaces), the hub's music, objects' and the bonfire's sounds.
Checked by logging every event started: running from the spawn plays soil, then cobblestone, with the armour at each footfall. A hollow fight plays its steps, voice and swings, the knight's swing, and the hits on both sides.