aboutsummaryrefslogtreecommitdiff
path: root/docs/gating.html
diff options
context:
space:
mode:
Diffstat (limited to 'docs/gating.html')
-rw-r--r--docs/gating.html180
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 &amp; voice selection — Animalese</title>
+<link rel="stylesheet" href="style.css">
+</head>
+<body>
+
+<nav class="crumb"><a href="index.html">Animalese pipeline</a> / 1. Gating &amp; voice selection</nav>
+
+<h1>1. Gating &amp; 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-&gt;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 &amp; 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&nbsp;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 &amp;&amp; param0 == 0) ||
+ Common_Get(now_private)-&gt;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-&gt;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 &gt; 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 &amp;&amp; b == 0xFF &amp;&amp; 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>