Skip to content

sound: pan/pan_mod sample the shared source/modulator wave twice per channel instead of once #800

Description

@Akshay-2007-1

Summary

pan(amount) in src/bundles/sound/src/functions.ts builds its left/right channel waves as two independent wrappers around the same underlying (squashed) source wave: gainWave(wave, leftGain) and gainWave(wave, rightGain). When play() later samples both channels, wave(t) gets called once via the left wrapper and again via the right wrapper, for every timestamp - the same underlying wave is evaluated twice per sample instead of once. pan_mod is worse: it does this for both the source wave and the modulator wave, so four evaluations happen where two would do.

For purely mathematical waves (sine_wave etc.) this is just wasted CPU. But if the wave traces back to a student-supplied closure (via closureToWave), it means the CSE machine actually re-runs the student's function body twice (or four times, for pan_mod) per sample instead of once - genuinely duplicated work and an extra, unnecessary "step" through their code.

Why this isn't a quick fix

The natural fix is to share one sample between both channel closures, but play() currently samples the entire left channel first, then the entire right channel (not interleaved sample-by-sample) - see sampleWave/play in the same file. So a naive "cache the last computed value" approach wouldn't help, since by the time the right channel is sampled the cache has long since moved past the relevant t.

Suggested direction

Either:

  • Cache every sampled value across the whole duration (trades memory for correctness), or
  • Restructure play()'s sampling loop (and sampleWave) to interleave left/right sampling per-timestamp, so a single-slot memoization actually works.

Context

Flagged by CodeRabbit's review on #796 (the sound Conductor migration), correctly labeled a "heavy lift" rather than a quick patch - deferred to this follow-up rather than rushed into that PR. See the review thread: #796 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions