Things a faithful port has to reproduce, things a clean reimplementation should drop, and where earlier notes were wrong.
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.
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.
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.
n is a vowel. Sou_BoinShiinCheck groups it with
a e i o u. This changes when silent e and magic e fire, so
done, gone and none get the silent-e drop.h is silent. Sou_TanboinHenkan('h') returns
0x00, and code 0 has no instrument (guarded by
if (a != 0 && a <= 0x77)). Standalone h makes no sound;
it only speaks through ch sh th ha he hi ho oh.mMsg_sound_voice_entry
refuses voice_no == 0x81 in the "first character" branch, so word-initial
pairing always starts on a letter.pan stays at
0x3F and reverbVolume at 0 for the life of the program. A
villager at the far edge of the screen is exactly as loud and as centred as one in
your face — the only positional SE in the game that ignores position.sou_voice_se[0]
and [1] are always written together, and Sou_VoiceMake pushes
both every frame — so a new syllable retroactively changes the volume and pitch
of the one still ringing.mMsg_check_sound_animal; and the submenu forces
sAdo_SetVoiceMode(0) regardless of config, so letter-writing always
speaks even for players who set the game to silent.Na_VoiceSe returns early until
Nap_ReadSubPort(VOICE_GROUP, 0, 1) == sou_now_voice_seq. The first
syllables of the first line with a new speaker can be silently lost.The following supersede statements in ../extraction.md as originally written.
| Was | Actually |
|---|---|
"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.