Task 5 of 5
Everything at once. Take the 256-sample second you built in task 1 — components at 5, 12 and 40 Hz — and throw away three samples in four. What you have left is 64 samples of the same second, which is a signal at 64 Hz. Nyquist has just dropped from 128 Hz to 32 Hz, and the 40 Hz component is now on the wrong side of it.
You can say exactly what happens, in advance, with the fold from task 2 — applied to the new rate:
5 Hz → fold(5, 64) = 5 Hz survives
12 Hz → fold(12, 64) = 12 Hz survives
40 Hz → fold(40, 64) = −24 Hz comes back as 24 Hz, mirrored
Not "roughly", not "some artefacts": the decimated samples are identical, number for number, to a signal synthesised from 5, 12 and −24 Hz at 64 Hz. Prove it — plane 0 keeps every fourth sample, plane 1 synthesises the prediction, and the two planes agree.
This is the wagon wheel. A 24 frames-per-second camera pointed at a wheel turning 30 times a second folds it to 30 − 24 = 6 rev/s forwards; at 20 rev/s it folds to 20 − 24 = −4 rev/s, and the wheel appears to roll backwards. Same arithmetic, and the negative sign is doing real work.
And this is why every resampler filters before it decimates: once the 40 Hz component is sitting on top of the 24 Hz band there is no undoing it — the two are the same numbers, and no amount of cleverness downstream can separate them. Remove it while it is still distinguishable, which is what "Convolution & Filters" is for.
signal by 4 into plane 0, synthesise the folded
prediction into plane 1, and show the two are the same signal.signal[this.thread.x * this.constants.factor]tones, each folded into the new base band at 64 Hzt = this.thread.x / 64, not / 256console.log the three folded frequenciesKeeping one sample in factor divides the rate by the same number:
const newRate = this.constants.sampleRate / this.constants.factor; // 64
Everything in plane 1 — the fold and the time — uses newRate, never 256.
Straight from task 2, with the new rate:
const f = tones[c][1];
const a = f - Math.round(f / newRate) * newRate;
Keep the sign. Math.abs here turns −24 Hz into a different signal.
Compute the decimated sample, then overwrite it when this thread is in plane 1:
let out = signal[this.thread.x * this.constants.factor];
if (this.thread.y === 1) {
// … the loop over this.constants.parts …
out = s;
}
return out;This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.