Hmbown/CodeWhale · error · Error
Invalid pet PCM range.
Error message
Invalid pet PCM range.
What it means
renderPetPCM() validates its range arguments before allocating buffers: sampleRate must be an integer in [8000, 96000], startSample a safe non-negative integer, and length a non-negative integer no larger than sampleRate*120 (two minutes of audio). This guard prevents allocating absurd buffers or synthesizing outside supported sample rates.
Solutions
- Log the three arguments at the call site and check them: integer sampleRate 8000–96000, integer startSample >= 0, integer 0 <= length <= sampleRate*120.
- Clamp or validate duration before converting: length = Math.min(Math.floor(seconds * sampleRate), sampleRate * 120).
- Use Math.round/Math.floor when deriving startSample and length from float seconds to avoid fractional values.
- Pick a supported sample rate constant (e.g. 48_000) instead of reading an unrestricted device rate.
- Wrap the call in try/catch and clamp to the nearest valid range on failure.
Example fix
// before
const { left, right } = renderPetPCM(voices, t * sr, durationMs * sr, deviceRate);
// after
const sr = 48_000;
const start = Math.max(0, Math.round(t * sr));
const len = Math.min(Math.max(0, Math.round(durationMs / 1000 * sr)), sr * 120);
const { left, right } = renderPetPCM(voices, start, len, sr); Defensive patterns
Strategy: validation
Validate before calling
function isPcmRange(startSample: number, length: number, sampleRate: number): boolean {
return Number.isInteger(sampleRate) && sampleRate >= 8000 && sampleRate <= 96000
&& Number.isSafeInteger(startSample) && startSample >= 0
&& Number.isInteger(length) && length >= 0 && length <= sampleRate * 120;
}
if (!isPcmRange(start, len, sr)) throw new Error('clamping needed'); Type guard
function isRenderablePcm(args: unknown): args is [number, number, number] {
return Array.isArray(args) && args.length === 3
&& Number.isInteger(args[2]) && args[2] >= 8000 && args[2] <= 96000
&& Number.isSafeInteger(args[0]) && args[0] >= 0
&& Number.isInteger(args[1]) && args[1] >= 0 && args[1] <= args[2] * 120;
} Try / catch
try {
const pcm = renderPetPCM(voices, start, length, sampleRate);
} catch (err) {
if (err instanceof Error && err.message === 'Invalid pet PCM range.') {
const sr = Math.min(96000, Math.max(8000, Math.round(sampleRate)));
const pcm = renderPetPCM(voices, Math.max(0, Math.round(start)), Math.min(length, sr * 120), sr);
} else throw err;
} Prevention
- Derive sample counts with Math.round(seconds * sampleRate), never raw float math.
- Enforce a max clip duration of 120 s at the product level.
- Use fixed supported sample-rate constants (44100/48000); reject device rates outside 8000–96000.
- Unit-test the boundary values: 8000, 96000, length = sampleRate*120, startSample = 0.
When it happens
Trigger: Calling renderPetPCM(voices, startSample, length) with: a non-integer sampleRate (e.g. 44100.5) or one outside 8000–96000 (e.g. 192000), a negative or fractional startSample, a negative length, a non-integer length, or length exceeding sampleRate*120 (e.g. length=48_000*300 for a 5-minute render).
Common situations: Computing length from milliseconds with a unit error (ms instead of samples); using a device sample rate of 192 kHz; float drift from arithmetic like startSample = seconds * sampleRate producing 44099.999; exporting longer-than-2-minute clips.
Related errors
- CRON range start must be <= end
- CRON value is out of range
- Invalid pet PCM range.
- Invalid pet voice.
- Invalid pet voice.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/107c41ae83ba0245.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/pet-audio.ts:74
}
if (frame.needs === 'call' && window % 10 === 0) add(`call:${window}`, CHANNELS[11].freq * 1.5, .38, .03);
}
}
return out;
}
}
/** Sample-addressed noise: no global RNG, identical samples when chunked/seeking. */
function noise(seed: number, sample: number): number {
let x = (seed + Math.imul(sample, 0x6D2B79F5)) >>> 0;
x = Math.imul(x ^ x >>> 15, x | 1); x ^= x + Math.imul(x ^ x >>> 7, x | 61);
return ((x ^ x >>> 14) >>> 0) / 2147483648 - 1;
}
export function renderPetPCM(voices: readonly PetVoice[], startSample: number, length: number, sampleRate = 48_000): { left: Float32Array; right: Float32Array } {
if (!Number.isInteger(sampleRate) || sampleRate < 8000 || sampleRate > 96000
|| !Number.isSafeInteger(startSample) || startSample < 0 || !Number.isInteger(length) || length < 0 || length > sampleRate * 120)
throw new Error('Invalid pet PCM range.');
const left = new Float32Array(length), right = new Float32Array(length);
for (const v of voices) {
if (![v.start, v.duration, v.frequency, v.gain, v.pan].every(Number.isFinite)
|| v.start < 0 || v.duration <= 0 || v.duration > 10 || v.frequency <= 0 || v.frequency > sampleRate / 2
|| v.gain < 0 || v.gain > 1 || Math.abs(v.pan) > 1 || !['tone', 'noise'].includes(v.kind)) throw new Error('Invalid pet voice.');
const first = Math.max(startSample, Math.ceil(v.start * sampleRate));
const last = Math.min(startSample + length, Math.ceil((v.start + v.duration) * sampleRate));
const pan = (v.pan + 1) * Math.PI / 4, seed = (0xC0FFEE ^ stableHash(v.id)) >>> 0;
for (let absolute = first; absolute < last; absolute++) {
const age = absolute / sampleRate - v.start;
const envelope = Math.min(1, age / .015, (v.duration - age) / .045);
const sample = v.kind === 'noise' ? noise(seed, absolute) * Math.exp(-age * 14)
: Math.sin(2 * Math.PI * v.frequency * age) * .88 + Math.sin(4 * Math.PI * v.frequency * age) * .12;
const value = sample * Math.max(0, envelope) * v.gain, at = absolute - startSample;
left[at] += value * Math.cos(pan); right[at] += value * Math.sin(pan);
}
}
// Limiting is a presentation operation and cannot perturb voice scheduling.View on GitHub (pinned to 433685b202)