SoundPatch: design a sound where it can be heard, then convert it

The bang was guessed at directly in bytes and came out as a low gurgle,
which is what guessing at bytes gets you: a cutoff of 40 looks small and
is 57 Hz, so the filter sweep ended almost shut. Voyager's sound device
is soundThing's voice engine with the editor taken off, so a patch
designed in soundThing - where there is a screen, a keyboard and a pair
of ears - makes the same sound here. What differs is only how it arrives.

SoundPatch converts one into the other. Every parameter the device takes
is a documented function of a natural value and all of them invert: times
are squared into four seconds, cutoff and rate are exponential, depths
and detune are centred on 128. It writes a table of a count and that many
parameter and value pairs, and playPatch hands it to the device - so
every sound after this costs a table and a call rather than forty lines
of its own.

Two things the conversion has to say out loud. soundThing has no field
for the level routing, because there it is always the amplitude envelope,
so the table says so explicitly - a channel keeps its patch between notes
and a leftover from a previous one would otherwise be carried in. And a
field the tool does not recognise STOPS it: a patch format that has moved
on would otherwise produce a table that quietly means something else.

THE BUILD DOES NOT DEPEND ON IT. soundThing lives in its own repository
and is not needed to build anything here; the tables are checked in and
the tool is for when a sound is being changed.

Lander's crash now plays Kick808 as a stand-in until a bang is designed
for it, and the difference is the point: the hand-guessed patch wandered
between 1600 and 8200 for eight tenths of a second, and this decays
3492, 2743, 2037, 1515, 1040, 614, 87, nothing.

The docs check caught the tool count in two manuals, which is what it is
for.
This commit is contained in:
Anachronaut
2026-09-04 13:00:18 -04:00
parent 9ae59bfccb
commit a8b6b09a59
18 changed files with 385 additions and 101 deletions
+31 -3
View File
@@ -24,8 +24,10 @@ wrote Asm.sbx: program 7533, data 4099, labels 555
| [`Source/Assembler`](Source/Assembler) | The assembler that runs on a host |
| [`Source/DiskTool`](Source/DiskTool) | SplitDisk, which reads and writes SplitBit's filesystem |
| [`Source/Linter`](Source/Linter) | SplitLint, which points out needlessly long assembly forms |
| [`Source/Patch`](Source/Patch) | SoundPatch, which turns a soundThing patch into a table the sound device takes |
| [`Programs/Examples`](Programs/Examples) | Programs to read: hello, a calculator, Fibonacci, a prime sieve, Life, the colours |
| [`Programs/Libraries`](Programs/Libraries) | Code included by name rather than linked, since there is no linker |
| [`Programs/Sounds`](Programs/Sounds) | Patch tables, converted from soundThing and checked in so the build never needs it |
| [`Programs/Loader`](Programs/Loader) | The standalone loader CosmOS grew out of |
| [`Programs/CosmOS`](Programs/CosmOS) | The operating system, its applications, and the native assembler |
| [`Programs/testPrograms`](Programs/testPrograms) | What the test suite drives |
@@ -114,7 +116,7 @@ Manual.
## Getting Started:
Clone it and build the four tools. You need gcc and make, or similar:
Clone it and build the five tools. You need gcc and make, or similar:
```
git clone https://github.com/RealBusinessAccount/SplitBit-Emulator.git
@@ -304,6 +306,32 @@ this is what turns such a count into a list of routine names.
Without `-o` the output takes the source file's name, in the directory you called the assembler from, with the extension the format asks for: `.bin` for a boot image and `.sbx` for a loadable program. Included files are looked for beside the file that includes them, and then along the directories given with `-I`.
## Making A Patch: SoundPatch
```
./SoundPatch <patch.json> <label> [output.asm]
```
The sound device is soundThing's voice engine with the editor taken off, so a patch designed
in soundThing makes the same sound here. What differs is how it arrives: soundThing writes
seconds and hertz, and the device takes bytes through a selector. SoundPatch converts one into
the other and writes a table a program can hand to the device.
A byte is not a number of seconds, and that is the whole reason this exists. Times are squared
into four seconds, because the difference between five and fifty milliseconds is the character
of a percussive sound and the difference between three and four seconds is nothing anybody can
hear. Cutoff and LFO rate are exponential, because hearing is logarithmic. Depths and detune
are centred on 128, so half of nothing is no change. The first sound written for a game here
was guessed at directly in bytes: a cutoff of 40 looks small and is 57 Hz, and the bang came
out as a low gurgle.
A field the tool does not recognise stops it rather than being skipped, because a patch format
that has moved on would otherwise produce a table that quietly means something else.
**The build does not depend on it.** soundThing lives in its own repository and is not
required to build anything here: the tables it produces are checked into `Programs/Sounds`,
and the tool is for when a sound is being changed.
## Checking Assembly: SplitLint
```
@@ -611,7 +639,7 @@ $(BUILD)/%.bin: %.asm
make test
```
Builds the four tools, checks they build clean under strict ISO C, and runs nine scripts.
Builds the five tools, checks they build clean under strict ISO C, and runs nine scripts.
`Tests/run.sh` assembles and runs every program in `Programs/` and compares the results
against recorded output; six more ask the questions a recorded file cannot answer. Between
them they check the two assemblers against each other byte for byte, the two SBFS
@@ -630,7 +658,7 @@ correct.
make sanitize
```
Rebuilds all four tools with the address and undefined behaviour sanitizers and runs the
Rebuilds all five tools with the address and undefined behaviour sanitizers and runs the
whole suite under them. It catches reads and writes past the end of an array, use after
free, leaks, and undefined arithmetic, takes about twice as long, and puts the ordinary
binaries back when it finishes.