aboutsummaryrefslogtreecommitdiff
path: root/docs/quirks.html
blob: c46fdb28d5634e49098ff67e6f986242e62334ee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>8. Quirks, dead code &amp; corrections — Animalese</title>
<link rel="stylesheet" href="style.css">
</head>
<body>

<nav class="crumb"><a href="index.html">Animalese pipeline</a> / 8. Quirks &amp; corrections</nav>

<h1>8. Quirks, dead code &amp; corrections</h1>
<p class="lede">Things a faithful port has to reproduce, things a clean reimplementation
should drop, and where earlier notes were wrong.</p>

<h2>Bugs that shipped</h2>

<h3>Glyph scale → volume is a dead store</h3>
<pre>if (scale &gt; 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;
}</pre>
<p class="src"><code>game64.c_inc:2994</code></p>
<p>The intent was clearly a smooth scale-to-volume ramp. What ships is a step function:
any glyph larger than normal slams volume to <strong>4.0</strong> (a 6× jump over the
0.65 baseline), anything smaller to <strong>0.4</strong>. Normal-size text passes
<code>scale == 32</code> and skips the block entirely, so this is only audible on
deliberately scaled dialogue.</p>
<p>This is in the matching decomp, so retail does it. A faithful port needs it; a clean
library does not.</p>

<h3>The mode-2 dedup check is tautological</h3>
<pre>case 2: {
    if (sou_num_org == sou_num_org &amp;&amp; sou_num_org == sou_last_num2) i = 1;
}</pre>
<p class="src"><code>game64.c_inc:2711</code></p>
<p>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.</p>

<h3>SHIIN2BOIN is dead, and would read out of bounds</h3>
<pre>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</pre>
<p class="src"><code>game64.c_inc:236</code>, written at <code>:2863</code></p>
<p><code>sou_now_boin</code> is written here and initialised in <code>Sou_InitAudio</code>, and
<strong>never read anywhere in the tree</strong>. Same for <code>sou_nobasu_count</code>
("elongation count"), which is only ever set to 0. Both are vestigial mouth-shape /
vowel tracking, presumably live in the N64 <em>Dōbutsu no Mori</em> code this was ported
from. Because the table is 86 entries and <code>a</code> can reach 119, the write reads
past the array — harmless only because the result is discarded.</p>
<p>The table also has an obvious data oddity: entry 45 is the value <code>45</code>, where
every neighbour is 0–5.</p>

<h2>Design choices that surprise</h2>

<ul>
  <li><strong><code>n</code> is a vowel.</strong> <code>Sou_BoinShiinCheck</code> groups it with
      <code>a e i o u</code>. This changes when silent&nbsp;e and magic&nbsp;e fire, so
      <em>done</em>, <em>gone</em> and <em>none</em> get the silent-<em>e</em> drop.</li>

  <li><strong><code>h</code> is silent.</strong> <code>Sou_TanboinHenkan('h')</code> returns
      <code>0x00</code>, and code 0 has no instrument (guarded by
      <code>if (a != 0 &amp;&amp; a &lt;= 0x77)</code>). Standalone <code>h</code> makes no sound;
      it only speaks through <code>ch sh th ha he hi ho oh</code>.</li>

  <li><strong>A space can never open a window.</strong> <code>mMsg_sound_voice_entry</code>
      refuses <code>voice_no == 0x81</code> in the "first character" branch, so word-initial
      pairing always starts on a letter.</li>

  <li><strong>Animalese is never panned or attenuated.</strong> <code>pan</code> stays at
      <code>0x3F</code> and <code>reverbVolume</code> 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.</li>

  <li><strong>The two subtracks share one parameter set.</strong> <code>sou_voice_se[0]</code>
      and <code>[1]</code> are always written together, and <code>Sou_VoiceMake</code> pushes
      both every frame — so a new syllable retroactively changes the volume and pitch
      of the one still ringing.</li>

  <li><strong>Voice mode is not the config setting.</strong> Animalese only ever plays for
      messages that pass <code>mMsg_check_sound_animal</code>; and the submenu forces
      <code>sAdo_SetVoiceMode(0)</code> regardless of config, so letter-writing always
      speaks even for players who set the game to silent.</li>

  <li><strong>Sequence-load races drop characters.</strong> After a spec change,
      <code>Na_VoiceSe</code> returns early until
      <code>Nap_ReadSubPort(VOICE_GROUP, 0, 1) == sou_now_voice_seq</code>. The first
      syllables of the first line with a new speaker can be silently lost.</li>
</ul>

<h2>Corrections to earlier notes</h2>

<p>The following supersede statements in <code>../extraction.md</code> as originally written.</p>

<table>
<tr><th>Was</th><th>Actually</th></tr>

<tr><td>"Bank 157 has only 83 instruments … that voice collapses every digraph sample
above <code>0x53</code> onto one sound."</td>
<td>Nothing above <code>0x53</code> is a digraph. The phonology engine's maximum output is
<code>0x52</code>, plus <code>0x77</code>. Bank 157 covers the phonology range exactly; the
missing block is the <a href="editor.html">editor's letter names</a>, unreachable at spec 3.
The only real loss is <code>you</code>.</td></tr>

<tr><td>"43 of the 119 codes are unreachable through normal text … only ever hit by the
anti-repetition random substitution."</td>
<td>36 of them (<code>0x53</code>–<code>0x76</code>) are reachable — through the mode-1
editor path — and the random substitution can <em>not</em> reach them, since it only
produces <code>0x00</code>–<code>0x3F</code>.</td></tr>

<tr><td>"<code>Sou_SpecialRoutine10</code> replays it on the next <em>audio</em> frame."</td>
<td>It runs inside <code>Na_GameFrame</code>, which <code>graph.c</code> calls once per
<em>game</em> frame. Same ~17 ms at 60 fps, but the mechanism is the game loop, and it
halves under <code>SetGameFrame(2)</code>.</td></tr>

<tr><td>"♪ … calls <code>Na_MelodyVoice(npc_id)</code> — the villager's tune."</td>
<td>It plays a fixed 16-note pattern <code>{13, 14 × 15}</code>, not a tune. The per-NPC
part is <em>which instrument</em>: <code>npc_voice_id</code> selects a chunk of sequence
248 to copy in. It is the speaker's timbre playing a constant figure.</td></tr>

<tr><td>Specs 5 and 6 listed only in the spec→sequence table, unattributed.</td>
<td>They are the <strong>player's own voice</strong> in text-entry menus — 5 male, 6
female — set from <code>m_submenu.c:364</code>, and are special-cased throughout
<code>Na_VoiceSe</code>.</td></tr>

<tr><td>Voice mode not covered.</td>
<td>Three modes gate the whole pipeline, and the gate depends on the message, not just
the config. See <a href="gating.html">gating</a>.</td></tr>

<tr><td>Mode <code>f == 1</code> not covered.</td>
<td>A third calling convention that bypasses phonology entirely. See
<a href="editor.html">the editor path</a>.</td></tr>
</table>

<p>Everything else in <code>extraction.md</code> §1 was re-verified against the current tree
and holds: the voice table, the 53 digraph entries, both vowel tables, the per-spec
timbre values, and the spec→sequence→bank mapping.</p>

<div class="pager">
  <span><a href="editor.html">← 7. The editor path</a></span>
  <span><a href="reference.html">9. Reference tables →</a></span>
</div>

</body>
</html>