Back to Big Rig Rush
SOURCE / PINNED RELEASE

Made of little things.

Big Rig Rush

Release
ec9b6549f752…
Author-recorded commit
74b729258b1a…
License
LICENSE
Author’s source reference
nostr://npub1sj2vketepqw9v6fmge5fdzuj7atj9sv6qv2jss05n2z6rl64lcfqumdsnt/wss%3A%2F%2Fgit.napplet.soy%2F/n-0a99f0cae40

Archive hash verified: 900e22810340ccb1…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tests/audio.test.mjs
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import ts from 'typescript';
const require = createRequire(import.meta.url);
const harnessRequire = createRequire(require.resolve('@napplet/conformance-cli/package.json'));
const { chromium } = harnessRequire('playwright');
const compile = source => ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.ESNext } }).outputText.replace(/^export /gm, '').replace(/^import .*$/gm, '');
const crash = compile(readFileSync('src/game/crash-synth.ts', 'utf8'));
const audio = compile(process.env.AUDIO_SOURCE ? readFileSync(process.env.AUDIO_SOURCE, 'utf8') : readFileSync('src/game/audio.ts', 'utf8'));

for (const missingHold of [false, true]) {
const mode = missingHold ? 'without cancelAndHoldAtTime' : 'with native automation';

test('dense procedural mix stays below full scale at every volume ' + mode, { timeout: 30000 }, async () => {
  const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
  try {
    const page = await browser.newPage();
    await page.evaluate(missing => {
      if (missing) Object.defineProperty(AudioParam.prototype, 'cancelAndHoldAtTime', { value: undefined, configurable: true });
    }, missingHold);
    await page.addScriptTag({ content: `{ ${crash}\n{ ${audio}\nwindow.AudioTestSfx = Sfx; } }` });
    for (const volume of [1, 0.4, 0]) {
      const result = await page.evaluate(async volume => {
        // Repeatable noise, impulses, crash synthesis and debris scheduling.
        let seed = 12345;
        Math.random = () => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed / 4294967296; };
        const ctx = new OfflineAudioContext(2, 48000 * 8, 48000);
        const filters = [];
        const createFilter = ctx.createBiquadFilter.bind(ctx);
        ctx.createBiquadFilter = () => {
          const node = createFilter();
          filters.push(node);
          return node;
        };
        let sources = 0, live = 0, maxLive = 0;
        for (const method of ['createOscillator', 'createBufferSource']) {
          const create = ctx[method].bind(ctx);
          ctx[method] = () => {
            const source = create();
            sources++;
            maxLive = Math.max(maxLive, ++live);
            source.addEventListener('ended', () => live--);
            return source;
          };
        }
        const s = new window.AudioTestSfx();
        s.setVolume(volume);
        s.ctx = ctx;
        s.build(ctx);
        s.setVoice('jet');
        s.setFlight(1, 1);
        s.driveUpdate(1, true, false, true, 1);
        s.gunUpdate(true, 65);
        s.fireLevel(1);
        // Schedule event storms on real audio-clock boundaries, like gameplay.
        let requests = 0;
        const failures = [];
        for (let k = 1; k <= 12; k++) {
          ctx.suspend(k * 0.25).then(() => {
            try {
              for (let i = 0; i < 12; i++) {
                const pan = (i % 3 - 1) * 0.8;
                s.impact(1.7, i % 2 ? 'car' : 'pileup', pan);
                s.extremeHit(2.3, pan);
                s.blast(i % 3 ? 'medium' : 'collapse-stone', pan, i * 80);
                s.explosion(1.5, pan);
                s.structureHit(140, pan);
                s.partyBeat(pan, 1);
                s.raveBeat(pan, 1);
                s.tools(pan);
                s.honk(true, pan);
                requests += 9;
              }
              if (k === 1) { s.bigBlast(0, 2); s.airRaid(); }
              s.fireCrackle(1);
              s.siren('police', -0.4, 1);
              s.missileLock(true);
            } catch (error) { failures.push(String(error)); }
            finally { void ctx.resume(); }
          });
        }
        const buffer = await ctx.startRendering();
        let peak = 0, sum = 0, clipped = 0;
        for (let ch = 0; ch < 2; ch++) for (const sample of buffer.getChannelData(ch)) {
          peak = Math.max(peak, Math.abs(sample));
          sum += sample * sample;
          if (Math.abs(sample) >= 1) clipped++;
        }
        return { volume, peak, rms: Math.sqrt(sum / (buffer.length * 2)), clipped, requests, sources, maxLive, failures, fixedFilters: filters.every(node => node.channelCountMode === 'explicit'), stereoFilters: filters.filter(node => node.channelCount === 2).length };
      }, volume);
      console.log(result);
      assert.deepEqual(result.failures, []);
      assert.equal(result.fixedFilters, true);
      assert.equal(result.stereoFilters, 2, 'master and reverb filtering must preserve stereo');
      assert.ok(result.maxLive < 900, 'simultaneous sources must stay bounded under event storms');
      assert.equal(result.clipped, 0);
      assert.ok(result.peak <= 0.91, `peak ${result.peak} exceeds output headroom`);
      if (volume > 0) assert.ok(result.rms > 0.005, 'mix must remain audible');
      else assert.equal(result.peak, 0);
    }
  } finally { await browser.close(); }
});

test('scheduled layers survive cleanup and gun crackle starts during held fire ' + mode, { timeout: 15000 }, async () => {
  const browser = await chromium.launch({ headless: true, args: ['--no-sandbox'] });
  try {
    const page = await browser.newPage();
    await page.evaluate(missing => {
      if (missing) Object.defineProperty(AudioParam.prototype, 'cancelAndHoldAtTime', { value: undefined, configurable: true });
    }, missingHold);
    await page.addScriptTag({ content: `{ ${crash}\n{ ${audio}\nwindow.AudioTestSfx = Sfx; } }` });
    const result = await page.evaluate(async () => {
      const ctx = new OfflineAudioContext(2, 48000, 48000);
      const s = new window.AudioTestSfx();
      s.ctx = ctx;
      s.build(ctx);
      const dest = s.out(-0.6, s.fx);
      let disconnects = 0;
      const disconnect = dest.disconnect.bind(dest);
      dest.disconnect = (...args) => { disconnects++; return disconnect(...args); };
      s.tone(dest, 'sine', 600, 600, 0, 0.05, 0.1);
      s.tone(dest, 'sine', 600, 600, 0.3, 0.3, 0.1);
      s.gunUpdate(true, 65);
      // Measure the crackle alone, so another part of the gun cannot mask a failure.
      const capture = ctx.createGain();
      s.gunVoice.crackle.connect(capture).connect(ctx.destination);
      s.master.gain.cancelScheduledValues(0);
      s.master.gain.value = 0;
      let earlyDisconnects = -1;
      const failures = [];
      for (let i = 1; i < 46; i++) ctx.suspend(i / 60).then(() => {
        try {
          s.gunUpdate(i < 36 || i >= 40, 65);
          if (i === 12) earlyDisconnects = disconnects;
        } catch (error) { failures.push(String(error)); }
        finally { void ctx.resume(); }
      });
      const buffer = await ctx.startRendering();
      let energy = 0;
      for (const sample of buffer.getChannelData(0).slice(24000, 28000)) energy += sample * sample;
      return { earlyDisconnects, disconnects, energy, failures };
    });
    assert.deepEqual(result.failures, []);
    assert.equal(result.earlyDisconnects, 0, 'panner must stay connected for its future layer');
    assert.equal(result.disconnects, 1, 'panner must disconnect once all layers finish');
    assert.ok(result.energy > 0.001, 'held gun fire must not postpone shell crackle indefinitely');
  } finally { await browser.close(); }
});

}