8. Quirks, dead code & corrections

Things a faithful port has to reproduce, things a clean reimplementation should drop, and where earlier notes were wrong.

Bugs that shipped

Glyph scale → volume is a dead store

if (scale > 0x20) {
    scale = scale - 0x20;
    f31 = scale;
    f31 = f31 * 0.15f;
    f30 = sou_voice_se[0]._08 + f31;   // computed…
    f30 = 4.f;                          // …and immediately discarded
    sou_voice_se[0]._08 = f30;
} else {
    scale = 0x1f - scale;
    f31 = scale * 0.02f;
    f30 = sou_voice_se[0]._08 - f31;    // computed…
    f30 = 0.4f;                         // …and immediately discarded
    sou_voice_se[0]._08 = f30;
}

game64.c_inc:2994

The intent was clearly a smooth scale-to-volume ramp. What ships is a step function: any glyph larger than normal slams volume to 4.0 (a 6× jump over the 0.65 baseline), anything smaller to 0.4. Normal-size text passes scale == 32 and skips the block entirely, so this is only audible on deliberately scaled dialogue.

This is in the matching decomp, so retail does it. A faithful port needs it; a clean library does not.

The mode-2 dedup check is tautological

case 2: {
    if (sou_num_org == sou_num_org && sou_num_org == sou_last_num2) i = 1;
}

game64.c_inc:2711

The first comparison is always true. The intent was presumably a three-in-a-row check mirroring mode 0; the effect is a two-in-a-row check. Deferred syllables therefore trigger the random substitution more often than intended.

SHIIN2BOIN is dead, and would read out of bounds

const u8 SHIIN2BOIN[] = { 0, 1, 2, 3, 4, 5, 1, 2, … };   // 86 entries
…
sou_now_boin = SHIIN2BOIN[a];    // a can be up to 0x77 = 119

game64.c_inc:236, written at :2863

sou_now_boin is written here and initialised in Sou_InitAudio, and never read anywhere in the tree. Same for sou_nobasu_count ("elongation count"), which is only ever set to 0. Both are vestigial mouth-shape / vowel tracking, presumably live in the N64 Dōbutsu no Mori code this was ported from. Because the table is 86 entries and a can reach 119, the write reads past the array — harmless only because the result is discarded.

The table also has an obvious data oddity: entry 45 is the value 45, where every neighbour is 0–5.

Design choices that surprise

Corrections to earlier notes

The following supersede statements in ../extraction.md as originally written.

WasActually
"Bank 157 has only 83 instruments … that voice collapses every digraph sample above 0x53 onto one sound." Nothing above 0x53 is a digraph. The phonology engine's maximum output is 0x52, plus 0x77. Bank 157 covers the phonology range exactly; the missing block is the editor's letter names, unreachable at spec 3. The only real loss is you.
"43 of the 119 codes are unreachable through normal text … only ever hit by the anti-repetition random substitution." 36 of them (0x53–0x76) are reachable — through the mode-1 editor path — and the random substitution can not reach them, since it only produces 0x00–0x3F.
"Sou_SpecialRoutine10 replays it on the next audio frame." It runs inside Na_GameFrame, which graph.c calls once per game frame. Same ~17 ms at 60 fps, but the mechanism is the game loop, and it halves under SetGameFrame(2).
"♪ … calls Na_MelodyVoice(npc_id) — the villager's tune." It plays a fixed 16-note pattern {13, 14 × 15}, not a tune. The per-NPC part is which instrument: npc_voice_id selects a chunk of sequence 248 to copy in. It is the speaker's timbre playing a constant figure.
Specs 5 and 6 listed only in the spec→sequence table, unattributed. They are the player's own voice in text-entry menus — 5 male, 6 female — set from m_submenu.c:364, and are special-cased throughout Na_VoiceSe.
Voice mode not covered. Three modes gate the whole pipeline, and the gate depends on the message, not just the config. See gating.
Mode f == 1 not covered. A third calling convention that bypasses phonology entirely. See the editor path.

Everything else in extraction.md §1 was re-verified against the current tree and holds: the voice table, the 55 digraph entries, both vowel tables, the per-spec timbre values, and the spec→sequence→bank mapping.

← 7. The editor path 9. Reference tables →