Before a single character is converted, two decisions are made: whether this message speaks at all, and with whose voice.
There are three voice modes, pushed to the audio engine by
sAdo_SetVoiceMode and stored in sou_voice_mode:
| Mode | Name | Behaviour |
|---|---|---|
0 | VOICE_MODE_ANIMALESE | full phonology, samples from the voice bank |
1 | VOICE_MODE_CLICK | every non-space character plays NA_SE_BEBE, one blip |
2 | VOICE_MODE_SILENT | nothing |
The mode is not simply the player's config setting. It is the product of the setting
and of who is talking — mMsg_sound_voice_mode_get:
mode_type = Save_Get(config).voice_mode // 0 animalese, 1 bebese, 2 silent
if (msg has VOICE_SILENT flag) mode_type = SILENT
if (mMsg_check_sound_animal(msg))
mode = { ANIMALESE, CLICK, SILENT }[mode_type] // an animal is speaking
else if (mode_type == SILENT)
mode = SILENT
else
mode = CLICK // narration, signs, menus
Source: src/game/m_msg_sound.c_inc:259
So the config's "animalese" setting only unlocks animalese for messages that pass
mMsg_check_sound_animal. Everything else — item descriptions, bulletin
boards, system prompts — is click mode no matter what.
mMsg_check_sound_animal (m_msg_sound.c_inc:66) returns true when the
VOICE_CLICK flag is clear and any of:
msg_p->show_actor_name — the window has a speaker nameplate;mMsg_check_sound_special — message number in
0x1743–0x1756 (event start), 0x1799–0x17AC (event end),
or 0x3A8A–0x3AF0 (Dial-a-Psychic);mMsg_check_sound_shasho — message number in 0x35AF–0x35BE
(the conductor).The special ranges exist so that disembodied event narration still gets a voice.
They are forced to spec 4 regardless of any actor
(mMsg_sound_spec_change_voice_force, :288).
The speaker maps to a "spec" (2–9), and the spec selects a sequence and bank.
Na_SpecChange (game64.c_inc:3459) is the switch, and it is a no-op
if the spec is unchanged — otherwise it stops the voice sequence, clears the stay
cache, and starts a new one:
| Spec | Sequence | Bank | Instruments | Used by |
|---|---|---|---|---|
| 2, 5, 7, 9 | 243 | 156 | 119 | default villagers, most specials, male player, Timmy/Tommy, Farley |
| 3 | 244 | 157 | 83 | Wendell, Pete, Booker, Jack |
| 4, 6, 8 | 245 | 158 | 119 | Pelly, Phyllis, Mabel, Sable, Blanca, female player, Joan, event narration |
Spec also selects a base timbre inside Na_VoiceSe
(game64.c_inc:2760), reapplied on every call:
| Spec | Volume (_08) | Pitch (_14) | Character |
|---|---|---|---|
| 2, 3, 4 | 0.65 | 1.00 | baseline |
| 5, 6 | 0.90 | 1.00 | player (louder, unmodulated) |
| 7 | 0.90 | 1.30 | squeaky — Timmy & Tommy |
| 8 | 0.90 | 0.75 | low — Joan |
| 9 | 0.70 | 0.65 | very low — Farley |
Note that spec is orthogonal to bank: specs 5, 7 and 9 all use bank 156 and differ only in this pitch/volume pair. There are three sample sets and eight voices.
mNpc_GetNpcSoundSpec (src/game/m_npc.c:4116) indexes by personality:
static int spec_table[mNpc_LOOKS_NUM] = { 4, 4, 2, 2, 3, 4 };
// normal(girl) 4 | peppy 4 | lazy(boy) 2 | jock 2 | cranky 3 | snooty 4
An out-of-range looks falls back to mNpc_LOOKS_BOY (spec 2).
Non-villagers fall through to mNpc_GetNpcSoundSpecNotAnimal, a linear scan of
l_sp_actor_name (m_npc.c:32). The default when not found is spec 2.
Everything in that table is spec 2 except:
| Spec | NPCs |
|---|---|
| 3 | Wendell, Pete, Booker, Jack |
| 4 | Pelly, Phyllis, Mabel, Sable, Blanca |
| 8 | Joan |
| 9 | Farley |
Specs 5 and 6 never appear in any NPC table. They are set from the submenu
(src/game/m_submenu.c:364) when a menu that can type text opens:
if ((menu_type == mSM_OVL_LEDIT && param0 == 0) ||
Common_Get(now_private)->gender == mPr_SEX_MALE) {
sAdo_SpecChange(5); // male
} else {
sAdo_SpecChange(6); // female
}
sAdo_SetVoiceMode(0); // force animalese on regardless of config
Two things follow. First, letter-writing and name entry force animalese even when the
player has set the config to click or silent. Second, specs 5 and 6 are treated as a
distinct class throughout Na_VoiceSe:
0x81, 0x84, 0x85,
0x86) plays NA_SE_BEBE instead of being silently dropped —
the typing click;? pitch bump and ! volume bump never apply;See the editor path for what actually gets spoken there.
Two text control codes toggle a temporary spec override, used for Nook's nephews:
| Code | Value | Effect |
|---|---|---|
mFont_CONT_CODE_VOICE_TRUE | 92 (0x5C) | restore msg_p->spec |
mFont_CONT_CODE_VOICE_FALSE | 93 (0x5D) | switch to spec 7 (pitch × 1.3) |
Both guard on the actor being SP_NPC_MAMEDANUKI0/1, so the codes are inert in
any other message. m_msg_sound.c_inc:317–340
Four early returns drop the call regardless of everything above
(game64.c_inc:2724–2735):
voice > 0x12B — NPC voice id out of range (299 max);Nap_ReadSubPort(VOICE_GROUP, 0, 1) != sou_now_voice_seq
— the voice sequence has not finished loading yet, so early characters of the
first line after a spec change are silently dropped;sou_scene_mode == 0 — audio not in a playing scene;a == 0x85 && b == 0xFF && c == 0xFF — a lone mute character,
handled at the very top before any state is touched.