SOURCE / PINNED RELEASE
Made of little things.
Pursuit PIT
- Release
- 565f4981bdc3…
- Author-recorded commit
- 197afdbed44b…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7
Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// IMA ADPCM: 4 bits per sample, so the radio voices fit in the single-file napplet without
// depending on any browser audio codec. Each clip is encoded from a fresh state.
const STEPS = [
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060,
1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767,
];
const INDEX = [-1, -1, -1, -1, 2, 4, 6, 8];
/** Decode `count` samples starting at byte `offset` into floats in -1..1. */
export function decodeAdpcm(bytes: Uint8Array, offset: number, count: number): Float32Array {
const out = new Float32Array(count);
let predictor = 0;
let index = 0;
for (let i = 0; i < count; i++) {
const byte = bytes[offset + (i >> 1)];
const code = i & 1 ? byte >> 4 : byte & 15;
const step = STEPS[index];
let diff = step >> 3;
if (code & 4) diff += step;
if (code & 2) diff += step >> 1;
if (code & 1) diff += step >> 2;
predictor += code & 8 ? -diff : diff;
predictor = Math.max(-32768, Math.min(32767, predictor));
index = Math.max(0, Math.min(88, index + INDEX[code & 7]));
out[i] = predictor / 32768;
}
return out;
}
/** Encode floats in -1..1; returns ceil(n / 2) bytes. */
export function encodeAdpcm(samples: Float32Array): Uint8Array {
const out = new Uint8Array(Math.ceil(samples.length / 2));
let predictor = 0;
let index = 0;
for (let i = 0; i < samples.length; i++) {
const target = Math.max(-32768, Math.min(32767, Math.round(samples[i] * 32767)));
const step = STEPS[index];
let diff = target - predictor;
let code = 0;
if (diff < 0) {
code = 8;
diff = -diff;
}
let delta = step >> 3;
if (diff >= step) { code |= 4; diff -= step; delta += step; }
if (diff >= step >> 1) { code |= 2; diff -= step >> 1; delta += step >> 1; }
if (diff >= step >> 2) { code |= 1; delta += step >> 2; }
predictor += code & 8 ? -delta : delta;
predictor = Math.max(-32768, Math.min(32767, predictor));
index = Math.max(0, Math.min(88, index + INDEX[code & 7]));
out[i >> 1] |= i & 1 ? code << 4 : code;
}
return out;
}
