# Payoff: Keep One Sample in Four

*Task 5 of 5 · [Sampling & Aliasing](https://gpu.rocks/learn/sampling-and-aliasing-ad14836c.md) · GPU.js Learn*

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:

```js
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.

## Goal

**Goal:** decimate `signal` by 4 into plane 0, synthesise the folded
prediction into plane 1, and show the two are the same signal.

## Requirements

- Plane 0 keeps every fourth sample: `signal[this.thread.x * this.constants.factor]`
- Plane 1 sums the components of `tones`, each folded into the new base band at `64 Hz`
- Use the new rate for time as well: `t = this.thread.x / 64`, not `/ 256`
- `console.log` the three folded frequencies

## Hint 1 — the new sample rate

Keeping one sample in `factor` divides the rate by the same number:

```js
const newRate = this.constants.sampleRate / this.constants.factor;   // 64
```

Everything in plane 1 — the fold and the time — uses `newRate`, never 256.

## Hint 2 — the fold, again

Straight from task 2, with the new rate:

```js
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.

## Hint 3 — branching between the planes

Compute the decimated sample, then overwrite it when this thread is in plane 1:

```js
let out = signal[this.thread.x * this.constants.factor];
if (this.thread.y === 1) {
  // … the loop over this.constants.parts …
  out = s;
}
return out;
```

## Same idea elsewhere

Decimation with a pre-filter is the bottom half of every mipmap, every audio
sample-rate converter and every image pyramid: CUDA's NPP and Metal Performance Shaders ship it,
and a GPU that generates mipmaps without filtering produces exactly the shimmering this task
predicts. It is also why "Measuring Speed Honestly" matters here — the filter is the expensive
part, and skipping it is the tempting, wrong optimisation.

## Starter code

```js
// Keep one sample in four. Predict exactly what that does to each component.
const gpu = new GPU({ mode });

const decimate = gpu.createKernel(function (signal, tones) {
  // TODO: plane 0 — keep every this.constants.factor-th sample.
  // TODO: plane 1 — synthesise the same components at the NEW rate,
  //       each frequency folded into the new base band.
  return 0;
}, {
  output: [64, 2],
  constants: { sampleRate: 256, factor: 4, parts: 3 },
});

const planes = decimate(signal, tones);
console.log('kept:', planes[0].length, 'samples at', 256 / 4, 'Hz');
console.log('first four kept:     ', planes[0][0], planes[0][1], planes[0][2], planes[0][3]);
console.log('first four predicted:', planes[1][0], planes[1][1], planes[1][2], planes[1][3]);

// TODO: log where each component of tones lands at the new rate — fold each
//       frequency into the 64 Hz base band, exactly as in task 2.
```

---

Interactive version: https://gpu.rocks/learn/sampling-and-aliasing-ad14836c/5

[Previous task](https://gpu.rocks/learn/sampling-and-aliasing-ad14836c/4.md)
