1. Gating & voice selection

Before a single character is converted, two decisions are made: whether this message speaks at all, and with whose voice.

Voice mode

There are three voice modes, pushed to the audio engine by sAdo_SetVoiceMode and stored in sou_voice_mode:

ModeNameBehaviour
0VOICE_MODE_ANIMALESEfull phonology, samples from the voice bank
1VOICE_MODE_CLICKevery non-space character plays NA_SE_BEBE, one blip
2VOICE_MODE_SILENTnothing

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.

What counts as an animal talking

mMsg_check_sound_animal (m_msg_sound.c_inc:66) returns true when the VOICE_CLICK flag is clear and any of:

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).

Spec: which of the three voices

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:

SpecSequenceBankInstrumentsUsed by
2, 5, 7, 9243156119default villagers, most specials, male player, Timmy/Tommy, Farley
324415783Wendell, Pete, Booker, Jack
4, 6, 8245158119Pelly, 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:

SpecVolume (_08)Pitch (_14)Character
2, 3, 40.651.00baseline
5, 60.901.00player (louder, unmodulated)
70.901.30squeaky — Timmy & Tommy
80.900.75low — Joan
90.700.65very 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.

Villagers

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).

Special NPCs

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:

SpecNPCs
3Wendell, Pete, Booker, Jack
4Pelly, Phyllis, Mabel, Sable, Blanca
8Joan
9Farley

The player's own voice: specs 5 and 6

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:

See the editor path for what actually gets spoken there.

Timmy and Tommy

Two text control codes toggle a temporary spec override, used for Nook's nephews:

CodeValueEffect
mFont_CONT_CODE_VOICE_TRUE92 (0x5C)restore msg_p->spec
mFont_CONT_CODE_VOICE_FALSE93 (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

Hard gates inside Na_VoiceSe

Four early returns drop the call regardless of everything above (game64.c_inc:2724–2735):

← Index 2. Text → voice code →