Fold soundThing's changes back down, and expose the two new switches

The three changes that went up came back as part of soundThing, along
with two more that they made possible. The engine here is now b73e5c0
character for character, except that em-dashes and arrows in comments are
written as ASCII because this tree is ASCII only - a local rule, not an
improvement, and not sent up.

So synth.h's "what was changed" list is gone. There is nothing to list:
what has to be kept current is only that if either copy changes, the
other one has to be told.

---- What came back ----

A VOICE CAN END ITSELF. Naming the level's source said what shapes a
voice; nothing said what ends one, so the only thing that could ever
finish one was a key coming up. A game is nearly all one-shots and not
one of them wants its length decided by how long a note was held. Exposed
as parameter 0x51: 0 gated, 1 triggered.

AND A ONE-SHOT IS THE SAME ONE-SHOT TWICE. A triggered voice re-arms its
oscillators, and an LFO can be told to start over with each voice -
parameter 3 of either LFO. Both halves are needed and the check proves
it: with the LFO left free, two triggered hits still differ.

Their note warned that whatever applies a patch to a channel has to set
these or they hold synthInit's defaults. Checked: Voyager never calls
synthSyncVoices, so their 0001 is a no-op here as they predicted, and
nothing reaches into an LFO's phase, so the struct split is safe.

---- What it is for ----

Lander's crash is a triggered voice now, so boomOff is gone. Nothing has
to remember to end a bang.

SoundPatch learnt voice_levelSource, voice_gate and lfo<N>_mode, which
the new soundThing writes - without that it would have refused every
patch saved from it, since an unknown field stops the tool on purpose. A
patch from before those fields still converts, and says in its own
comments that it predates the level routing.

Three checks, each seen to fail on its own break: a gated voice still
sounding with nothing holding it, a triggered one down to nothing with no
gate ever dropped, and two hits identical sample for sample.

One test bug worth keeping: the first version of the repeatability check
struck the second note while the first was still ringing, so what it
found and compared as "the second hit" was a point in the middle of the
first one's tail. It now looks for sound after SILENCE rather than sound
after an offset.
This commit is contained in:
Anachronaut
2026-09-04 16:02:40 -04:00
parent a8b6b09a59
commit b2ff8d64e5
22 changed files with 532 additions and 107 deletions
+21
View File
@@ -82,6 +82,17 @@
// LFOs. Nought is the one that could not be said before - see synth.h.
#define SP_LEVEL_SOURCE 0x50
// ---- Whether a note waits to be let go of ----
//
// 0 is gated and 1 is triggered. Gated is what a keyboard wants: the sound lasts as long as
// the finger does, and dropping the gate starts the release. Triggered is what a GAME wants -
// a bang, a pickup, a door - where the note is struck and then plays its own length, and
// nothing has to remember to end it. Sustain and release have no meaning in a triggered
// voice, because both are answers to a question about a key that is not being asked.
//
// A program driving one-shots does not need port 0x45 at all once this is set.
#define SP_VOICE_GATE 0x51
// The LFOs belong to the whole device rather than to a channel, so these ignore whichever
// channel is selected.
#define SP_LFO0 0x60 // 0x60-0x6F and 0x70-0x7F
@@ -89,6 +100,16 @@
#define SP_LFO_ACTIVE 0x00
#define SP_LFO_WAVE 0x01
#define SP_LFO_RATE 0x02
// ---- Whether it starts over when a voice does ----
//
// 0 free and 1 retriggered. Free is one cycle running under everything, which is what vibrato
// across a held chord wants. Retriggered starts at the beginning of its shape every time a
// voice begins, which is the only way a one-shot sounds the SAME twice: a free LFO is wherever
// the wall clock left it, so the same drum caught at a different moment is a different drum.
//
// The mode belongs to the LFO and the cycle belongs to the voice, so retriggering costs
// nothing to a channel that is not using it.
#define SP_LFO_MODE 0x03
// ---- Samples come from the machine's clock ----
//