Skip to content

SoundRenderPathDocs.kt

src/jvmMain/kotlin/afield/audio/synth/SoundRenderPathDocs.kt · 482 lines

package afield.audio.synth

import afield.audio.engine.WavFloatArrayWriter
import java.io.File
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.sqrt

FIELD_TEST id: synth.sound.render.path runner: jvm-main entrypoint: afield.audio.synth.SoundRenderPathDocs tags: [smoke, synth, pattern, tidal, audio, wav, dirt, capture, cpu] timeoutSec: 180 expect: exitCode: 0 stdoutContains: - "sound render path figures rendered"

The audio figures on sound.md, each declaring which voice made it.

SoundRenderPathDocs renders one pattern through patVoice and through the Tidal Dirt-Samples, and SoundProvenanceGuardTest checks every committed .wav against its declared voice.

object SoundRenderPathDocs

The figures behind public/docs/sound.md, and the reason that page can be trusted.

Field has two offline voices for the same pattern and they sound nothing alike. patRenderStereo plays it through patVoice — a sine for bd, filtered noise for sd and hh — and patRenderStereoDirt plays it through the genuine Tidal Dirt-Samples library.

This used to continue "and every audio figure Field has published so far is the first one, and nothing on any page said so". Both halves went stale within days: DocsAudioProvenance measured the corpus and most of it is DoughDspSoundDocs, and public/docs/sound.md now says so at length. Two is also the count of pattern voices and never was the count of ways to make a sound — SoundPathMap has that, and there are six.

So provenance is not a comment here, it is a field. Each Figure declares its Voice, and two mechanisms hold the declaration to it:

  • renderWav refuses to write a Voice.DIRT_SAMPLES figure out of the fallback. On a machine with no library the docs build fails with an explanation instead of publishing the stand-in under a caption that says "real samples".
  • SoundProvenanceGuardTest decodes the committed .wav, correlates it against a fresh stand-in render of the same pattern, and fails if the answer disagrees with the declaration. That check needs no sample library, which is exactly why it is the useful one: a build machine with no Dirt-Samples can still catch a "real drums" figure that has silently become bursts of white noise.

Add a figure by appending one Figure to FIGURES and putting its Voice.pageLabel beside the fence on the page. The guard enforces both directions, so half of that will fail the build.

object SoundRenderPathDocs {

enum class Voice

Which voice a figure is made of — declared by the figure, checked against the audio.

    enum class Voice(
        /**
         * The exact sentence the page must carry next to this figure.
         *
         * Verbatim, so the claim in the prose and the claim in the code cannot drift apart
         * silently. Prose around it is free; this string has to be present.
         */
        val pageLabel: String
    ) {

Line comments, not KDoc: an enum entry that takes a constructor argument is not a form KDocExtractor.parseDeclaration can read, so a KDoc block here slides onto whatever parses next. A bare entry is fine; STAND_IN("…") is not. See KDocAttachmentGuard.

patVoice: no assets, works on a clean checkout, sounds like bursts of white noise.

        STAND_IN("**Voice: the stand-in synth (`patVoice`).** No sample library was used."),

patRenderStereoDirt against a real Dirt-Samples install, every token matched.

        DIRT_SAMPLES("**Voice: real Dirt-Samples one-shots, every token matched.**"),

Real samples where the library had them, patVoice per-event where it did not.

        MIXED("**Voice: real Dirt-Samples where the library had the token, the stand-in synth per event where it did not.**"),

        ;

val usesSamples: Boolean get() = this != STAND_IN

True when this figure claims any of its sound came out of the sample library.

class Figure

One published audio figure: a pattern, a tempo, and the voice it claims to be made of.

pattern is a factory rather than a value because the guard renders each figure several times and a PatPattern is a query function — sharing one across renders would be fine today and a subtle bug the first time one of them caches.

    class Figure(
        val name: String,
        val title: String,
        val voice: Voice,
        val cycles: Int,
        val cps: Double,
        val pattern: () -> PatPattern<String>,
        /** The synth voice used, either as the figure itself or as the per-event fallback. */
        val synth: PatVoiceSpec = patVoice(),
        /**
         * The tokens this figure expects the library *not* to have, and therefore to voice through
         * the synth fallback.
         *
         * Declared rather than discovered, because the provenance guard has to work on a machine
         * with no sample library: it splits the pattern into the part that should have come from
         * samples and the part that should not, and measures the committed `.wav` against each
         * half separately. Empty for a [Voice.STAND_IN] or a fully-matched [Voice.DIRT_SAMPLES]
         * figure; non-empty is what makes a figure [Voice.MIXED].
         */
        val expectedUnmatched: Set<String> = emptySet(),
    ) {

init

        init {
            require((voice == Voice.MIXED) == expectedUnmatched.isNotEmpty()) {
                "figure '$name': ${Voice.MIXED} means exactly 'some tokens fall back', so expectedUnmatched " +
                    "must be non-empty for it and empty for every other voice (got $expectedUnmatched for $voice)"
            }
        }
    }

    private const val CPS = 2.0

fun theBeat(): PatPattern<String> = pat("bd ~ sd ~, hh*8") with gain(0.9)

The beat every A/B figure on the page uses, so the two voices are compared on one pattern.

val FIGURES: List<Figure>

    val FIGURES: List<Figure> = listOf(

        // 1 ── the default voice, named for what it is.
        Figure(
            name = "beat-stand-in",
            title = "pat(\"bd ~ sd ~, hh*8\") through patVoice",
            voice = Voice.STAND_IN,
            cycles = 4,
            cps = CPS,
            pattern = { theBeat() },
        ),

        // 2 ── the identical pattern, the other voice. The whole point of the page in one A/B.
        Figure(
            name = "beat-dirt",
            title = "the identical pattern through the real Dirt samples",
            voice = Voice.DIRT_SAMPLES,
            cycles = 4,
            cps = CPS,
            pattern = { theBeat() },
        ),

        // 3 ── the per-event fallback, made audible. `zzzznotadrum` is in no Dirt install, so the
        // library is present and used for `bd`/`hh` while that one token gets the synth voice.
        Figure(
            name = "per-event-fallback",
            title = "one token the library has never heard of",
            voice = Voice.MIXED,
            cycles = 4,
            cps = CPS,
            pattern = { pat("bd zzzznotadrum bd hh") with gain(0.9) },
            synth = patVoice(wave = PatWave.SQUARE, baseHz = 330.0, decay = 0.05, sustain = 0.3),
            expectedUnmatched = setOf("zzzznotadrum"),
        ),
    )

    private val byName: Map<String, Figure> = FIGURES.associateBy { it.name }

fun figure(name: String): Figure

Looks a figure up by name, with a message that lists the alternatives.

    fun figure(name: String): Figure =
        byName[name] ?: error("unknown sound-render-path figure '$name'; known: ${byName.keys.sorted()}")

class Rendered

A rendered figure: the audio, and what it turned out to be made of.

    class Rendered(
        val figure: Figure,
        val audio: PatSoundDocs.Stereo,
        val result: DirtRender.DirtRenderResult?,
    ) {

val usedSamples: Boolean get() = result?.usedSamples == true

True when real samples were mixed in — always false for a Voice.STAND_IN figure.

fun describe(): String = result?.describe() ?: "synth voice (patVoice; this figure asks for it)"

One line naming the voice, for a box to print or a build log to carry.

fun render(name: String): Rendered

Renders name through the voice it declares.

A Voice.DIRT_SAMPLES figure is an error when no library can be found: the caller asked for real samples and cannot have them, and the one thing this file exists to prevent is silently substituting the other voice under a caption that says otherwise. The library is DirtRender.requirePinnedLibrary: ./gradlew fetchDocsDirtSamples provides it.

    fun render(name: String): Rendered {
        val figure = figure(name)
        return when (figure.voice) {
            Voice.STAND_IN -> Rendered(
                figure,
                patRenderStereo(figure.pattern(), figure.cycles, figure.cps, figure.synth),
                null,
            )

            Voice.DIRT_SAMPLES, Voice.MIXED -> {
                // The pinned subset, not whichever install dirtSampleRoot finds first: the
                // provenance audit compares bytes, so every machine must render from the same ones.
                // Throws with the fetch command when it is missing; rendering through the stand-in
                // would publish a figure whose caption is a lie.
                val library = DirtRender.requirePinnedLibrary()
                val result = patRenderStereoDirt(
                    figure.pattern(), figure.cycles, figure.cps, library = library, voice = figure.synth,
                )
                check(result.usedSamples) { "figure '$name': a library was found and still nothing came from it" }
                check(result.matched.isNotEmpty()) {
                    "figure '$name': the library matched no token at all (${result.unmatched}), so this " +
                        "render is entirely the stand-in wearing a sample-library caption"
                }
                Rendered(figure, result.audio, result)
            }
        }
    }

fun standInReference(name: String, tokens: Set<String>? = null): PatSoundDocs.Stereo

The stand-in render of a figure's pattern, whatever voice the figure declares.

The reference the provenance guard correlates a committed .wav against. Available on every machine, including one with no sample library, which is what makes the guard portable.

tokens restricts the render to the events whose value is in the set — the two halves of a Voice.MIXED figure, rendered separately. Null means the whole pattern.

    fun standInReference(name: String, tokens: Set<String>? = null): PatSoundDocs.Stereo {
        val figure = figure(name)
        val source = figure.pattern()
        val pattern = if (tokens == null) source else {
            PatPattern { arc -> source.events(arc).filter { it.value.trim() in tokens } }
        }
        return patRenderStereo(pattern, figure.cycles, figure.cps, figure.synth)
    }

fun sampleBackedReference(name: String): PatSoundDocs.Stereo

The stand-in render of just the events the library is expected to play from samples.

    fun sampleBackedReference(name: String): PatSoundDocs.Stereo {
        val figure = figure(name)
        val unmatched = figure.expectedUnmatched
        return standInReference(name, tokensOf(figure).filterNot { it in unmatched }.toSet())
    }

fun fallbackReference(name: String): PatSoundDocs.Stereo

The stand-in render of just the events expected to fall back to the synth voice.

    fun fallbackReference(name: String): PatSoundDocs.Stereo =
        standInReference(name, figure(name).expectedUnmatched)

fun tokensOf(figure: Figure): Set<String>

Every distinct atom the figure's pattern schedules over its own length.

    fun tokensOf(figure: Figure): Set<String> =
        figure.pattern()
            .events(PatArc(PatQ.ZERO, PatQ.of(figure.cycles)))
            .map { it.value.trim() }
            .toSet()

fun renderWav(name: String, outPath: String)

Offline-renders figure name to a stereo 16-bit PCM WAV at outPath.

The renderWav(name, path) contract the docs runner's audio="…" fence calls over the backdoor (public/docs_hooks.py, ensure_audio).

    fun renderWav(name: String, outPath: String) {
        val rendered = render(name)
        val audio = rendered.audio
        require(audio.frames > 0) { "figure '$name' rendered no samples" }
        require(audio.left.any { it != 0f } || audio.right.any { it != 0f }) { "figure '$name' rendered silence" }
        WavFloatArrayWriter.writeStereo16(outPath, audio.left, audio.right, PatSoundDocs.SR, normalizePeak = 0.9f)
    }

provenance measurement

fun waveformSimilarity(a: PatSoundDocs.Stereo, b: PatSoundDocs.Stereo): Double

How much two renders of the same pattern are the same waveform: normalised zero-lag cross-correlation of the two channels, worst channel wins.

Scale-invariant, so the writer's peak normalisation and 16-bit quantisation do not move it, and it reads waveform identity rather than rhythm — two renders of one pattern put their onsets in exactly the same places whichever voice played them, so anything that keys off timing cannot tell them apart. This can. Returns 0 against a silent reference.

    fun waveformSimilarity(a: PatSoundDocs.Stereo, b: PatSoundDocs.Stereo): Double {
        val n = minOf(a.frames, b.frames)
        if (n == 0) return 0.0
        val left = PatSoundDocs.correlation(a.left.copyOf(n), b.left.copyOf(n))
        val right = PatSoundDocs.correlation(a.right.copyOf(n), b.right.copyOf(n))
        return max(abs(left), abs(right))
    }

class ProvenanceMeasurement

What a rendered figure is actually made of, measured against three stand-in references.

All three are computable with no sample library anywhere, which is the entire trick: a build machine that cannot render the real samples can still tell whether a committed figure was rendered from them.

    class ProvenanceMeasurement(
        val figure: Figure,
        /** Against the stand-in render of the whole pattern. ~1 means "this IS the stand-in". */
        val toFullStandIn: Double,
        /** Against the stand-in render of only the events that should have come from samples. */
        val toSampleBacked: Double,
        /** Against the stand-in render of only the events that should have fallen back. */
        val toFallback: Double,
    ) {

override fun toString(): String

        override fun toString(): String =
            "full=${"%.4f".format(toFullStandIn)} sampleBacked=${"%.4f".format(toSampleBacked)} " +
                "fallback=${"%.4f".format(toFallback)}"

fun disagreements(): List<String>

The ways this audio contradicts the voice its figure declares — empty when it does not.

A list rather than a throw so both the FIELD_TEST and SoundProvenanceGuardTest can report every problem in one run instead of one per rebuild.

        fun disagreements(): List<String> = buildList {
            when (figure.voice) {
                Voice.STAND_IN ->
                    if (toFullStandIn < STAND_IN_SIMILARITY_FLOOR) {
                        add(
                            "declares ${figure.voice} but the audio is not the stand-in render " +
                                "(similarity $toFullStandIn < $STAND_IN_SIMILARITY_FLOOR)"
                        )
                    }

                Voice.DIRT_SAMPLES, Voice.MIXED -> {
                    // The load-bearing one: the events that claim to be samples must not be the
                    // synth. This holds whatever fraction of the figure fell back, which is why it
                    // is measured against the sample-backed half rather than the whole file.
                    if (toSampleBacked > SAMPLE_BACKED_SIMILARITY_CEILING) {
                        add(
                            "declares ${figure.voice} but its sample-backed events sound exactly like " +
                                "the stand-in synth (similarity $toSampleBacked > " +
                                "$SAMPLE_BACKED_SIMILARITY_CEILING) — this figure has silently become " +
                                "bursts of white noise"
                        )
                    }
                    if (toFullStandIn >= STAND_IN_SIMILARITY_FLOOR) {
                        add(
                            "declares ${figure.voice} but is the whole-render fallback " +
                                "(similarity to the plain stand-in render $toFullStandIn)"
                        )
                    }
                    if (figure.voice == Voice.MIXED && toFallback < FALLBACK_SIMILARITY_FLOOR) {
                        add(
                            "declares ${figure.voice}, so ${figure.expectedUnmatched} should be audible " +
                                "through the synth voice, and it is not (similarity $toFallback < " +
                                "$FALLBACK_SIMILARITY_FLOOR)"
                        )
                    }
                }
            }
        }
    }

fun measureProvenance(name: String, audio: PatSoundDocs.Stereo)

Measures audio — a fresh render or a decoded committed .wav — against figure name.

    fun measureProvenance(name: String, audio: PatSoundDocs.Stereo) = ProvenanceMeasurement(
        figure = figure(name),
        toFullStandIn = waveformSimilarity(audio, standInReference(name)),
        toSampleBacked = waveformSimilarity(audio, sampleBackedReference(name)),
        toFallback = waveformSimilarity(audio, fallbackReference(name)),
    )

FIELD_TEST entry point

fun main(args: Array<String>)

Renders every figure to a temp dir and asserts the audio is real, and really the declared voice. Writes NO committed assets; pass an output dir as args[0] to regenerate those.

DIRT_SAMPLES figures need the library, so on a machine without it this reports them as skipped rather than failing — a build box has no 400MB third-party download and should not pretend otherwise. The parts that run everywhere still run.

body — 69 lines, from line 373
    @JvmStatic
    fun main(args: Array<String>) {
        val outDir = File(
            args.getOrNull(0) ?: System.getenv("SOUND_RENDER_PATH_DIR")
                ?: File(System.getProperty("java.io.tmpdir"), "sound-render-path").path
        )
        outDir.mkdirs()

        val root = DirtRender.pinnedSampleRoot()
        println("Dirt sample root: ${root ?: "(none — DIRT_SAMPLES figures will be skipped; ${DirtRender.PINNED_REMEDY})"}")

        var rendered = 0
        var skipped = 0
        FIGURES.forEach { figure ->
            if (figure.voice == Voice.DIRT_SAMPLES && root == null) {
                println("figure ${figure.name}: SKIPPED (declares ${figure.voice}, no library on this machine)")
                skipped++
                return@forEach
            }
            val result = render(figure.name)
            val audio = result.audio
            check(audio.frames > 0) { "${figure.name}: rendered no samples" }
            check(audio.left.size == audio.right.size) { "${figure.name}: channel length mismatch" }
            check(audio.left.all { it.isFinite() } && audio.right.all { it.isFinite() }) {
                "${figure.name}: non-finite samples"
            }
            val expectedFrames = (figure.cycles / figure.cps * PatSoundDocs.SR).toInt()
            // A sample one-shot may ring past the last cycle in principle; the renderer truncates
            // to the scheduled length, so the frame count is exact either way.
            check(abs(audio.frames - expectedFrames) <= PatSoundDocs.SR / 10) {
                "${figure.name}: expected ~$expectedFrames frames, got ${audio.frames}"
            }
            val peak = PatSoundDocs.peak(audio.mono)
            val rms = PatSoundDocs.rms(audio.mono)
            check(peak > 0.02) { "${figure.name}: effectively silent (peak=$peak)" }
            check(peak <= 4.0) { "${figure.name}: runaway level (peak=$peak)" }
            check(rms > 0.002) { "${figure.name}: RMS too low to be music (rms=$rms)" }
            check(rms < peak) { "${figure.name}: RMS >= peak, which is impossible" }

            // The claim the page makes, measured rather than asserted by comment.
            val measured = measureProvenance(figure.name, audio)
            println(
                "figure ${figure.name}: ${"%.2f".format(audio.frames.toDouble() / PatSoundDocs.SR)}s, " +
                    "peak=${"%.3f".format(peak)}, rms=${"%.4f".format(rms)}, " +
                    "usedSamples=${result.usedSamples}, declares ${figure.voice}, $measured"
            )
            println("  ${result.describe()}")
            if (figure.voice == Voice.MIXED) {
                val dirt = requireNotNull(result.result) { "${figure.name}: a MIXED figure must be a Dirt render" }
                check(dirt.unmatched == figure.expectedUnmatched) {
                    "${figure.name}: declares expectedUnmatched=${figure.expectedUnmatched}, the library " +
                        "actually failed on ${dirt.unmatched}"
                }
            }
            val disagreements = measured.disagreements()
            check(disagreements.isEmpty()) { "${figure.name}: ${disagreements.joinToString("; ")}" }

            val file = File(outDir, "sound-${figure.name}.wav")
            WavFloatArrayWriter.writeStereo16(file, audio.left, audio.right, PatSoundDocs.SR, normalizePeak = 0.9f)
            val bytes = file.readBytes()
            check(String(bytes, 0, 4, Charsets.US_ASCII) == "RIFF") { "${figure.name}: missing RIFF" }
            check(String(bytes, 8, 4, Charsets.US_ASCII) == "WAVE") { "${figure.name}: missing WAVE" }
            check(bytes.size == 44 + audio.frames * 4) { "${figure.name}: WAV data size mismatch" }
            println("  wrote ${file.absolutePath} (${bytes.size} bytes)")
            rendered++
        }

        println("sound render path figures rendered to ${outDir.absolutePath} ($rendered rendered, $skipped skipped)")
    }

the ratchets

Every one of these is a floor near a measured number, not a guess. The measurements are in the KDoc so a later change can see how much room it just used up. Re-measure with run_field_tests.py --id synth.sound.render.path, which prints all three per figure.

const val STAND_IN_SIMILARITY_FLOOR = 0.99

A figure that claims to be the stand-in must essentially be the stand-in.

Not 1.0, because the committed asset is peak-normalised 16-bit PCM — a quantised, rescaled copy. Measured 1.0000 (to four places) for beat-stand-in both as a fresh render and decoded back off disk; the floor sits well below that and far above anything a different voice reaches.

const val SAMPLE_BACKED_SIMILARITY_CEILING = 0.35

The events a figure says came from samples must not sound like the synth.

Never anywhere near zero, and the reason is worth knowing before anyone tightens this: a bass-drum sample is largely a decaying low sine, and the stand-in's bd is a 62 Hz sine, so the two genuinely correlate. Measured 0.0565 for beat-dirt, whose sixteenth-note hats dilute the kick, and 0.1588 for per-event-fallback, which is a quarter kicks. So this ceiling is set from the worse case with roughly a factor of two of headroom, and it is still ~6x below the ~1.0 that a figure reverting to the stand-in produces — which is the only failure it needs to catch.

const val FALLBACK_SIMILARITY_FLOOR = 0.5

A Voice.MIXED figure's fallback events must actually be audible in it.

The per-event fallback measured rather than asserted: the synth-voiced events are mixed in verbatim, so the correlation is roughly the square root of their share of the energy. Measured 0.8074 for per-event-fallback; 0.5 leaves room to rebalance the mix without leaving room for the fallback to vanish.