diff options
| author | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 17:51:13 -0700 |
|---|---|---|
| committer | Alexander Kassouni <alex@kassouni.net> | 2026-09-05 17:51:13 -0700 |
| commit | 46227e2003de28202cc4e82bca4f7f4745476c4e (patch) | |
| tree | fe450ac33ffa13747613c3cfdf36ea24b3036c46 /docs/gating.html | |
init
Diffstat (limited to 'docs/gating.html')
| -rw-r--r-- | docs/gating.html | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/docs/gating.html b/docs/gating.html new file mode 100644 index 0000000..9e78ac0 --- /dev/null +++ b/docs/gating.html @@ -0,0 +1,180 @@ +<!doctype html> +<html lang="en"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>1. Gating & voice selection — Animalese</title> +<link rel="stylesheet" href="style.css"> +</head> +<body> + +<nav class="crumb"><a href="index.html">Animalese pipeline</a> / 1. Gating & voice selection</nav> + +<h1>1. Gating & voice selection</h1> +<p class="lede">Before a single character is converted, two decisions are made: whether +this message speaks at all, and with whose voice.</p> + +<h2>Voice mode</h2> + +<p>There are three voice modes, pushed to the audio engine by +<code>sAdo_SetVoiceMode</code> and stored in <code>sou_voice_mode</code>:</p> + +<table> +<tr><th>Mode</th><th>Name</th><th>Behaviour</th></tr> +<tr><td><code>0</code></td><td><code>VOICE_MODE_ANIMALESE</code></td><td>full phonology, samples from the voice bank</td></tr> +<tr><td><code>1</code></td><td><code>VOICE_MODE_CLICK</code></td><td>every non-space character plays <code>NA_SE_BEBE</code>, one blip</td></tr> +<tr><td><code>2</code></td><td><code>VOICE_MODE_SILENT</code></td><td>nothing</td></tr> +</table> + +<p>The mode is not simply the player's config setting. It is the product of the setting +and of <em>who is talking</em> — <code>mMsg_sound_voice_mode_get</code>:</p> + +<pre>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</pre> + +<p class="src">Source: <code>src/game/m_msg_sound.c_inc:259</code></p> + +<p>So the config's "animalese" setting only unlocks animalese for messages that pass +<code>mMsg_check_sound_animal</code>. Everything else — item descriptions, bulletin +boards, system prompts — is click mode no matter what.</p> + +<h3>What counts as an animal talking</h3> + +<p><code>mMsg_check_sound_animal</code> (<code>m_msg_sound.c_inc:66</code>) returns true when the +<code>VOICE_CLICK</code> flag is clear <em>and</em> any of:</p> + +<ul> + <li><code>msg_p->show_actor_name</code> — the window has a speaker nameplate;</li> + <li><code>mMsg_check_sound_special</code> — message number in + <code>0x1743–0x1756</code> (event start), <code>0x1799–0x17AC</code> (event end), + or <code>0x3A8A–0x3AF0</code> (Dial-a-Psychic);</li> + <li><code>mMsg_check_sound_shasho</code> — message number in <code>0x35AF–0x35BE</code> + (the conductor).</li> +</ul> + +<p>The special ranges exist so that disembodied event narration still gets a voice. +They are forced to <strong>spec 4</strong> regardless of any actor +(<code>mMsg_sound_spec_change_voice_force</code>, <code>:288</code>).</p> + +<h2>Spec: which of the three voices</h2> + +<p>The speaker maps to a "spec" (2–9), and the spec selects a sequence and bank. +<code>Na_SpecChange</code> (<code>game64.c_inc:3459</code>) 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:</p> + +<table> +<tr><th>Spec</th><th>Sequence</th><th>Bank</th><th>Instruments</th><th>Used by</th></tr> +<tr><td>2, 5, 7, 9</td><td>243</td><td>156</td><td>119</td><td>default villagers, most specials, male player, Timmy/Tommy, Farley</td></tr> +<tr><td>3</td><td>244</td><td>157</td><td>83</td><td>Wendell, Pete, Booker, Jack</td></tr> +<tr><td>4, 6, 8</td><td>245</td><td>158</td><td>119</td><td>Pelly, Phyllis, Mabel, Sable, Blanca, female player, Joan, event narration</td></tr> +</table> + +<p>Spec also selects a base timbre inside <code>Na_VoiceSe</code> +(<code>game64.c_inc:2760</code>), reapplied on every call:</p> + +<table> +<tr><th>Spec</th><th>Volume (<code>_08</code>)</th><th>Pitch (<code>_14</code>)</th><th>Character</th></tr> +<tr><td>2, 3, 4</td><td>0.65</td><td>1.00</td><td>baseline</td></tr> +<tr><td>5, 6</td><td>0.90</td><td>1.00</td><td>player (louder, unmodulated)</td></tr> +<tr><td>7</td><td>0.90</td><td>1.30</td><td>squeaky — Timmy & Tommy</td></tr> +<tr><td>8</td><td>0.90</td><td>0.75</td><td>low — Joan</td></tr> +<tr><td>9</td><td>0.70</td><td>0.65</td><td>very low — Farley</td></tr> +</table> + +<p>Note that <strong>spec is orthogonal to bank</strong>: 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.</p> + +<h3>Villagers</h3> + +<p><code>mNpc_GetNpcSoundSpec</code> (<code>src/game/m_npc.c:4116</code>) indexes by personality:</p> + +<pre>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</pre> + +<p>An out-of-range <code>looks</code> falls back to <code>mNpc_LOOKS_BOY</code> (spec 2).</p> + +<h3>Special NPCs</h3> + +<p>Non-villagers fall through to <code>mNpc_GetNpcSoundSpecNotAnimal</code>, a linear scan of +<code>l_sp_actor_name</code> (<code>m_npc.c:32</code>). The default when not found is spec 2. +Everything in that table is spec 2 except:</p> + +<table> +<tr><th>Spec</th><th>NPCs</th></tr> +<tr><td>3</td><td>Wendell, Pete, Booker, Jack</td></tr> +<tr><td>4</td><td>Pelly, Phyllis, Mabel, Sable, Blanca</td></tr> +<tr><td>8</td><td>Joan</td></tr> +<tr><td>9</td><td>Farley</td></tr> +</table> + +<h3>The player's own voice: specs 5 and 6</h3> + +<p>Specs 5 and 6 never appear in any NPC table. They are set from the submenu +(<code>src/game/m_submenu.c:364</code>) when a menu that can type text opens:</p> + +<pre>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</pre> + +<p>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 <code>Na_VoiceSe</code>:</p> + +<ul> + <li>every punctuation code (<code>0x81</code>, <code>0x84</code>, <code>0x85</code>, + <code>0x86</code>) plays <code>NA_SE_BEBE</code> instead of being silently dropped — + the typing click;</li> + <li>the <code>?</code> pitch bump and <code>!</code> volume bump never apply;</li> + <li>the emotion LFO is skipped entirely;</li> + <li>anti-repetition substitution is disabled.</li> +</ul> + +<p>See <a href="editor.html">the editor path</a> for what actually gets spoken there.</p> + +<h3>Timmy and Tommy</h3> + +<p>Two text control codes toggle a temporary spec override, used for Nook's nephews:</p> + +<table> +<tr><th>Code</th><th>Value</th><th>Effect</th></tr> +<tr><td><code>mFont_CONT_CODE_VOICE_TRUE</code></td><td>92 (0x5C)</td><td>restore <code>msg_p->spec</code></td></tr> +<tr><td><code>mFont_CONT_CODE_VOICE_FALSE</code></td><td>93 (0x5D)</td><td>switch to spec 7 (pitch × 1.3)</td></tr> +</table> + +<p>Both guard on the actor being <code>SP_NPC_MAMEDANUKI0/1</code>, so the codes are inert in +any other message. <span class="src"><code>m_msg_sound.c_inc:317–340</code></span></p> + +<h2>Hard gates inside Na_VoiceSe</h2> + +<p>Four early returns drop the call regardless of everything above +(<code>game64.c_inc:2724–2735</code>):</p> + +<ul> + <li><code>voice > 0x12B</code> — NPC voice id out of range (299 max);</li> + <li>animalese mode and <code>Nap_ReadSubPort(VOICE_GROUP, 0, 1) != sou_now_voice_seq</code> + — the voice sequence has not finished loading yet, so early characters of the + first line after a spec change are silently dropped;</li> + <li><code>sou_scene_mode == 0</code> — audio not in a playing scene;</li> + <li><code>a == 0x85 && b == 0xFF && c == 0xFF</code> — a lone mute character, + handled at the very top before any state is touched.</li> +</ul> + +<div class="pager"> + <span><a href="index.html">← Index</a></span> + <span><a href="encoding.html">2. Text → voice code →</a></span> +</div> + +</body> +</html> |