11K — Raw PCM Audio (.11K / .5K / .8K)¶
FA audio files are raw, headerless, signed 8-bit mono PCM. The sample rate is encoded in the file extension:
| Extension | Sample rate | Notes |
|---|---|---|
.5K |
5512 Hz | Half of .11K; the extension is a rounded label. Not 5000 — see below |
.8K |
8000 Hz | Confirmed via TOOLKIT LIBPTR cache |
.11K |
11025 Hz | Most common |
.22K |
22050 Hz | Supported by TOOLKIT; not observed in FA LIBs (may be ATF/USNF only) |
The rate is not stored in the file — it comes from the extension — so no round-trip
can contradict it, and lib/src/audio.cpp returned 5000 Hz for .5K while this table
said 5512. Every .5K the tool emitted played 10% long, and the unit test asserted the
wrong value, so the suite stayed green (#491).
The briefing videos are an independent clock that settles it. Audio is shared per
3-character group (VDO.md), the paired .FBC holds one u32 per frame, and the
.VDO header gives the fps — none of which the audio codec has any say in:
| bytes | at 5000 Hz | at 5512 Hz | video (1,685 frames ÷ 15 fps) | |
|---|---|---|---|---|
IQC.5K |
619,237 | 123.85 s (+10.2%) | 112.34 s (+0.0%) | 112.33 s |
tests/test_audio.cpp now checks this against the whole install (106 tracks): a narration
may end before its clip does, but it may never outrun it.
Tools¶
fx¶
fx audio info <file.11K|.5K|.8K> # sample rate, sample count, duration
fx audio unpack <file.11K|.5K|.8K> [-o out.wav] # raw PCM → WAV
fx audio pack <in.wav> -o <out.11K|.5K|.8K> # WAV → raw PCM
[-r 11025] # override sample rate (default from ext)
The output extension determines the stored sample rate when packing.
Input WAV must be mono, 8-bit; fx rejects stereo or 16-bit input.
Other Tools¶
Audacity can also import the raw PCM file directly without the fx step
(File → Import → Raw Data: signed 8-bit, mono, sample rate from extension).
- Audacity — free, cross-platform; raw import, noise reduction, pitch/tempo tools
- Adobe Audition
$— paid; professional mastering and spectral repair
File Layout¶
Single-byte samples; no multi-byte integers.
- Signed 8-bit (
int8_t), range −128..127 - Mono (1 channel)
- No header, no footer — the file is raw samples from byte 0
Filename Prefix Conventions¶
The filename prefix (before any letters) is an engine convention, not a format difference:
| Prefix | Meaning | Example |
|---|---|---|
& |
Looping ambient / cockpit sound | &AFTB2.11K |
^ |
Voice / radio callout (one-shot) | ^ENGAGE.11K, ^MLTRY-A.11K |
# |
Radio speech phrase — a second voice family beside ^; .5K only in the retail corpus (×85 in FA_2, many stems shared with ^) |
#AIRBORN.5K, #ALMSTHM.5K |
fx lib unpack maps only & to _ on extraction (via ealib_safe_name, which
remaps & * ? " < > | / \ : for cross-platform legality); ^ is a legal filename
character and extracts with the caret intact (e.g. ^ENGAGE.11K), as does #. See
LIB.md. The original names are preserved in memory for
patching.
WAV Conversion¶
WAV stores 8-bit audio as unsigned (0..255), not signed. Apply ^ 0x80 on
both directions:
// FA raw → WAV sample
wav_byte = (uint8_t)((int8_t)fa_byte + 128); // equivalently: fa_byte ^ 0x80
// WAV sample → FA raw
fa_byte = (int8_t)(wav_byte - 128);
WAV header (mono, 8-bit, sample rate R):
RIFF chunk: "RIFF" + (file_size - 8) u32LE + "WAVE"
fmt chunk: "fmt " + 16 u32LE + 1 u16LE (PCM) + 1 u16LE (channels)
+ R u32LE (sample rate) + R u32LE (byte rate)
+ 1 u16LE (block align) + 8 u16LE (bits per sample)
data chunk: "data" + sample_count u32LE + [samples]
To create a .5K variant of a sound: pack at 11025 Hz after doubling playback
speed 2×, then rename the extension to .5K. The engine plays it at half rate,
yielding the correct pitch with reduced quality.
Engine Notes¶
AIL Runtime Integration (the game executable)¶
The game executable calls the Miles Audio Interface Library (AIL) API directly. The following API calls and globals were confirmed via Ghidra analysis of the The game executable main binary:
Initialization sequence:
_AIL_startup_0();
_AIL_set_preference_8(4, 0x78); // MIDI preference
_AIL_midiOutOpen_12(&_musicDriverHandle, 0, 0xFFFFFFFF); // open MIDI driver
_AIL_lock_0();
_AIL_set_XMIDI_master_volume_8(...);
_AIL_unlock_0();
_AIL_register_timer_4(&_PollMod__YGXK_Z) → _timerHandle; // register poll callback
_AIL_set_timer_frequency_8(_timerHandle, 0x1e); // 30 Hz
_AIL_start_timer_4(_timerHandle);
_AIL_set_preference_8(0xf, 0);
_AIL_set_preference_8(0xe, 0x4000);
_AIL_set_preference_8(0, 0x10);
_AIL_set_preference_8(2, 0x28f);
_AIL_waveOutOpen_16(&_soundDriverHandle, 0, 0, &fmt); // 22050 Hz, stereo, 16-bit
// Allocate mix channels (loop):
_AIL_allocate_sample_handle_4(_soundDriverHandle) → _mixChanHandle[i]
Runtime globals:
| Global | Type | Role |
|---|---|---|
_musicDriverHandle__3PAU_MDI_DRIVER__A |
MDI_DRIVER* | MIDI driver handle |
_soundDriverHandle__3PAU_DIG_DRIVER__A |
DIG_DRIVER* | Digital audio driver handle (22050 Hz stereo 16-bit) |
_timerHandle__3JA |
HTIMER | 30 Hz poll timer for _PollMod |
_mixChanHandle__3PAPAU_SAMPLE__A |
HSAMPLE[] | Per-channel digital audio handles |
_ailActive__3DA |
bool | AIL system active flag |
Channel teardown: _AIL_end_sample_4(channel) — called per channel on
shutdown.
The WAIL32.DLL and msapi.dll are the AIL wrapper DLLs loaded at runtime. Their import surface has not been traced (no Ghidra output available for the secondary project). IP.EXE audio path is also uncharted.
Round-Trip Notes¶
The format is raw samples with no derived fields, so WAV → PCM → WAV round
trips are byte-identical; tests/test_audio.cpp asserts the PCM round-trip.
The ^ 0x80 signedness flip is exactly self-inverse.
An earlier draft of this page described .MUS files as named pointers to
.11K audio; the confirmed decode shows .MUS playlists drive .XMI
MIDI tracks instead — see MUS.md for the corrected music-slot table
and modding workflow.
Related¶
Formats: LIB — container archives (and the &/^ extraction
mapping); MUS — the music playlist DLLs (MIDI, not PCM);
CB8 — FMV clips pair with a .11K of the same stem;
VDO — briefing videos share one .11K narration per 3-character
group prefix (AAC.11K for AACA…AACE), not per full stem.