{"record":{"id":"170733e40e9a8762","repo":"Hmbown/CodeWhale","slug":"invalid-pet-voice","errorCode":null,"errorMessage":"Invalid pet voice.","messagePattern":"Invalid pet voice\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":1630,"sourceCode":"}\nexports.PetScore = PetScore;\n/** Sample-addressed noise: no global RNG, identical samples when chunked/seeking. */\nfunction noise(seed, sample) {\n    let x = (seed + Math.imul(sample, 0x6D2B79F5)) >>> 0;\n    x = Math.imul(x ^ x >>> 15, x | 1);\n    x ^= x + Math.imul(x ^ x >>> 7, x | 61);\n    return ((x ^ x >>> 14) >>> 0) / 2147483648 - 1;\n}\nfunction renderPetPCM(voices, startSample, length, sampleRate = 48_000) {\n    if (!Number.isInteger(sampleRate) || sampleRate < 8000 || sampleRate > 96000\n        || !Number.isSafeInteger(startSample) || startSample < 0 || !Number.isInteger(length) || length < 0 || length > sampleRate * 120)\n        throw new Error('Invalid pet PCM range.');\n    const left = new Float32Array(length), right = new Float32Array(length);\n    for (const v of voices) {\n        if (![v.start, v.duration, v.frequency, v.gain, v.pan].every(Number.isFinite)\n            || v.start < 0 || v.duration <= 0 || v.duration > 10 || v.frequency <= 0 || v.frequency > sampleRate / 2\n            || v.gain < 0 || v.gain > 1 || Math.abs(v.pan) > 1 || !['tone', 'noise'].includes(v.kind))\n            throw new Error('Invalid pet voice.');\n        const first = Math.max(startSample, Math.ceil(v.start * sampleRate));\n        const last = Math.min(startSample + length, Math.ceil((v.start + v.duration) * sampleRate));\n        const pan = (v.pan + 1) * Math.PI / 4, seed = (0xC0FFEE ^ (0, model_js_1.stableHash)(v.id)) >>> 0;\n        for (let absolute = first; absolute < last; absolute++) {\n            const age = absolute / sampleRate - v.start;\n            const envelope = Math.min(1, age / .015, (v.duration - age) / .045);\n            const sample = v.kind === 'noise' ? noise(seed, absolute) * Math.exp(-age * 14)\n                : Math.sin(2 * Math.PI * v.frequency * age) * .88 + Math.sin(4 * Math.PI * v.frequency * age) * .12;\n            const value = sample * Math.max(0, envelope) * v.gain, at = absolute - startSample;\n            left[at] += value * Math.cos(pan);\n            right[at] += value * Math.sin(pan);\n        }\n    }\n    // Limiting is a presentation operation and cannot perturb voice scheduling.\n    for (let i = 0; i < length; i++) {\n        left[i] = (0, model_js_1.clamp)(left[i], -1, 1);\n        right[i] = (0, model_js_1.clamp)(right[i], -1, 1);\n    }","sourceCodeStart":1612,"sourceCodeEnd":1648,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L1612-L1648","documentation":"renderPetPCM validates every voice descriptor before mixing it into the PCM buffers. It throws 'Invalid pet voice.' when any voice has a non-finite start/duration/frequency/gain/pan, a negative start, a duration outside (0, 10] seconds, a frequency outside (0, sampleRate/2] (Nyquist), gain outside [0, 1], |pan| > 1, or a kind other than 'tone' or 'noise'. Each voice is guarded individually so one bad cue cannot corrupt the whole render.","triggerScenarios":"Passing a voice object where v.frequency is 0, NaN, or above sampleRate/2; v.duration is 0 or > 10; v.gain is negative or > 1; v.pan outside [-1, 1]; v.kind misspelled (e.g. 'sine' instead of 'tone'); or any numeric field missing/undefined so Number.isFinite fails.","commonSituations":"A scheduling bug emitting a voice with duration 0; a frequency derived from a sim channel that exceeds the new sampleRate after a rate change; a renamed voice kind after a refactor; NaN leaking in from division by zero in cue generation.","solutions":["Validate each voice against the same predicate before calling: finite numbers, 0 < start, 0 < duration <= 10, 0 < frequency <= sampleRate/2, 0 <= gain <= 1, |pan| <= 1, kind in ['tone','noise']","Fix the cue generator so it never emits frequency >= sampleRate/2 (clamp to Nyquist)","Correct the voice kind spelling to 'tone' or 'noise'","Filter out non-finite numbers at the source (skip or repair NaN durations/frequencies)"],"exampleFix":"// before\naddVoice({ kind: 'sine', frequency: 26000, duration: 0, gain: 1.5, pan: 2 });\n// after\naddVoice({ kind: 'tone', frequency: Math.min(freq, sampleRate / 2 - 1), duration: Math.min(Math.max(dur, 0.01), 10), gain: clamp(gain, 0, 1), pan: clamp(pan, -1, 1) });","handlingStrategy":"validation","validationCode":"function isValidVoice(v, sampleRate) {\n  return !!v && ['tone','noise'].includes(v.kind)\n    && [v.start, v.duration, v.frequency, v.gain, v.pan].every(Number.isFinite)\n    && v.start >= 0 && v.duration > 0 && v.duration <= 10\n    && v.frequency > 0 && v.frequency <= sampleRate / 2\n    && v.gain >= 0 && v.gain <= 1 && Math.abs(v.pan) <= 1;\n}\nvoices = voices.filter(v => isValidVoice(v, sampleRate));","typeGuard":"const isVoice = (v) => typeof v === 'object' && (v.kind === 'tone' || v.kind === 'noise') && Number.isFinite(v.start) && Number.isFinite(v.duration) && Number.isFinite(v.frequency) && Number.isFinite(v.gain) && Number.isFinite(v.pan);","tryCatchPattern":"try {\n  return renderPetPCM(voices, start, len, sr);\n} catch (err) {\n  if (err.message === 'Invalid pet voice.') return renderPetPCM(voices.filter(isValidVoiceCue), start, len, sr);\n  throw err;\n}","preventionTips":["Clamp frequency to Nyquist (sampleRate/2) when generating cues","Clamp duration into (0, 10] and gain into [0, 1] at cue creation","Only emit kinds 'tone' and 'noise'; never ad-hoc kind strings","Skip NaN-producing cue math instead of forwarding NaN"],"tags":["audio","validation","argument-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}