9. 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 from earlier working notes (extraction.md, since retired — its surviving content lives on the disc page).

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 with one instrument to spare, the missing block is the editor's letter names (unreachable at spec 3), and nothing is lost: sequence 244 explicitly maps you to that 83rd instrument — a dedicated substitute, not a clamp.
"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 the retired notes was re-verified against the current tree and holds: the voice table, the 53 digraph entries, both vowel tables, the per-spec timbre values, and the spec→sequence→bank mapping. The table contents, bank layouts and sequence bytecode were additionally verified byte-for-byte against a dumped disc image (see the disc page).

← 8. Walking the disc image 10. Reference tables →