# Sound bank audio — `sound.pak` / `.slb` / XMA1 Reverse-engineered 2026-07-19 (static). `dat/sound.pak` (+ `sound.p00..p04`, ~1.08 GB) is the game's entire audio bank: **9519** IPFB entries, each an XACT `.slb` sound bank wrapping **XMA1** audio. ## Names Entry keys are `name_hash()` of filenames stored verbatim in the IDXD string pools of `tables.pak` → `eng\sounds.tbl` (entry 39) and `jpn\sounds.tbl` (entry 45). All 9519 recovered. Families: - `\Voice\VOICE__.slb` — per-line character voice (SPK = ADAN, TCAF, RHIN, ADPL, BIRD, ACRO, ZZZZ…). Only `eng`/`jpn` voice exists. - `\etc\VOICE_{A,B,C,D}_.slb`, `\Briefing\BR_.slb`. - **`\Movie\VOICE_.slb`** — the continuous voice track for cutscene `.wmv` (e.g. `eng\Movie\VOICE_RT07A.slb`). Played from the video start. - `BGM_###.slb` (32) — music. `Static.slb`, `JNGL_###` — SFX/ambience. Only English and Japanese have voice; subtitles cover more languages, voice does not. See [`crate::slb::movie_voice_name`]. ## Codec XMA1: RIFF `fmt ` tag **0x0165**, a 32-byte `XMAWAVEFORMAT`, **48000 Hz**. The content is **mono** — some clips carry it in the **left channel only** (right is silence), others are **dual-mono** (L = R). Downmix to mono by taking the left channel (it always holds the full signal). `XMAWAVEFORMAT` (one stream, 32-byte `fmt `): SampleRate @ fmt+16 (u32 LE), Channels @ fmt+29 (u8), ChannelMask @ fmt+30 (u16). Movie voices report 2 channels / mask 0x2 despite the mono content. ## `.slb` layouts → decodable RIFF Two on-disc layouts (`sound.pak` movie voices split ≈ 63 RIFF / 15 headerless): - **RIFF present** — a `RIFF/WAVE` sits inside the bank; its 32-byte `fmt ` is the real header. The XMA packets are **everything after that RIFF's `data` chunk header** (`slb[data_pos+8..]`); the declared `data` size is unreliable (sometimes half the real length), so take to end and clamp to the media length downstream. Some banks have trailing bank data past the real audio — always beyond the video length, so clamping to the video duration drops it. - **Headerless** — no `RIFF` anywhere; a fixed **1392-byte** header precedes raw XMA1 packets (48 kHz, 2 channels). Data = `slb[1392..]`. [`crate::slb::to_xma_riff`] rebuilds a standalone XMA1 `RIFF/WAVE` for either layout, ready for FFmpeg's `xma1` decoder. ## Decode (FFmpeg) `ffmpeg -i rebuilt.riff -af "pan=mono|c0=c0" -t out.wav` — the RIFF is fed to the `xma1` decoder, downmixed to the left channel, clamped to length. Verified: `VOICE_S00A` → 93.9 s == `S00A.wmv` 93.87 s; `VOICE_ADV` → 137.3 s == 137.7 s; `VOICE_RT07A` → 49 s ≈ 50 s. ## Reading one entry cheaply `sound.pak` is ~1 GB. Use [`crate::PakArchive::parse_toc`] on the small `.pak` index, binary-search the name-hash, then read just `[offset, offset+size)` from the `.pNN` segments (the viewer's `read_segment_range` seeks into the right segment) instead of loading the whole archive.