Skip to content

refactor(audio): schedule every tone through one envelope helper - #136

Merged
wolasss merged 1 commit into
masterfrom
refactor-audio-schedule-every-tone-throu-20260821-070502
Aug 21, 2026
Merged

refactor(audio): schedule every tone through one envelope helper#136
wolasss merged 1 commit into
masterfrom
refactor-audio-schedule-every-tone-throu-20260821-070502

Conversation

@wolasss

@wolasss wolasss commented Aug 21, 2026

Copy link
Copy Markdown
Owner

The metronome click and the two-tone session-end chime were each hand-rolling
the same oscillator-plus-gain envelope: create both nodes, pin the gain at the
0.0001 floor, ramp exponentially up to a peak, ramp back down, wire them to the
destination, start, stop, track. Three copies of eight lines, in playClickAt
and twice inside playSessionEndChime's playTone.

What changed

  • Added a private AudioEngine.scheduleTone({ type, frequency, startTime, attack, peak, decayEnd, stopAt, nodes }) in src/lib/audio/engine.ts and expressed all
    three call sites through it.
  • Its times are absolute points on the context clock, not durations from
    startTime. That is deliberate: each caller keeps evaluating its own
    arithmetic (toneStart + duration * 0.88 and friends) exactly as before, so
    the floats that reach the hardware are bit-for-bit the ones it computed. A
    duration-based helper would re-associate those expressions and quietly move
    the ramps.
  • nodes defaults to the transport's scheduledNodes; the chime passes
    this.chimeNodes, so stopScheduledSounds(true) still spares it.

No behaviour change and no public API change. Every scheduled value is
identical: the click's 1320/880 Hz, 0.12/0.08 peaks, 0.01 attack, 0.12 decay and
0.14 stop; the chime's 783.99/523.25 Hz bodies with shimmers an octave up at a
0.42 peak factor and the 0.88/0.9 duration factors.

Verifying that

The existing tests asserted the click's frequency and start/stop but never the
chime's ramps or the click's decay, so before touching engine.ts I added two
characterization tests to src/lib/audio/engine.test.ts that record every fake
oscillator and gain call for one click and one chime and compare them with exact
equality — toEqual over mock.calls, no closeTo, and every expected number
written as the same expression the engine evaluates so the bits match by
construction. The chime test also asserts all four oscillators survive
stopScheduledSounds(true).

Those tests passed before the refactor and pass unchanged after it; nothing in
the test file was relaxed to accommodate the new code.

  • npx vitest run src/lib/audio/engine.test.ts — 44 passed, both before and after
  • npm run check (lint → typecheck:e2evitest runtsc -b && vite build) —
    all four green, 939 tests across 67 files

How to test

This is an internal refactor with no observable change — the click and the chime
sound exactly as they did. The proof is the test suite:

  1. npm run check — all four stages should pass (939 tests).
  2. To see the safety net specifically, run
    npx vitest run src/lib/audio/engine.test.ts and look for
    "a click schedules one exact envelope" and
    "the end chime schedules four exact envelopes". Nudging any scheduled
    number in scheduleTone or a call site — say the shimmer's 0.42 to 0.43
    should fail them immediately.
  3. Optionally by ear: npm run dev, start a session with the metronome on to
    hear the click, and let a short session run out to hear the two-tone chime.

Live preview (this branch): https://agents.marten-gecko.ts.net:10744/

@wolasss
wolasss merged commit 5a84b67 into master Aug 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant