/** * pcm-record-processor.js — AudioWorklet processor d'ENREGISTREMENT T2 Live * (Sprint 6e, Voie A — tap temps réel). * * Branché en dérivation sur le `mixGain` de capture (point de convergence * micro + voix IA dans le contexte PARTAGÉ). Il LIT le mix au rate NATIF du * contexte (typiquement 48 kHz), convertit Float32 → Int16 little-endian, et * envoie des chunks (~4096 samples) au thread principal via `port.postMessage`. * * Aucun rééchantillonnage : on enregistre au rate natif (le WAV est écrit à ce * même rate côté useAudioRecording). L'alignement temporel micro/IA est natif — * les deux voix partagent l'horloge unique du contexte (plus de réassemblage * offline à base d'offsets). * * Le node est tiré par le graphe via mixGain → recordNode → gain(0) → * destination (sink muet) ; ce processor n'écrit rien sur ses sorties (silence), * il ne fait que prélever l'entrée. Le gain(0) garantit zéro résidu audible. * * Vanille JS (pas TS) : les AudioWorklet processors s'exécutent dans un scope * global isolé qui ne peut pas importer depuis le bundle TS. */ const RECORD_CHUNK_SIZE = 4096 class PcmRecordProcessor extends AudioWorkletProcessor { constructor() { super() this.buffer = new Float32Array(0) } process(inputs) { const input = inputs[0] if (!input || !input[0]) return true const channelData = input[0] // mono (mix micro + IA) const merged = new Float32Array(this.buffer.length + channelData.length) merged.set(this.buffer) merged.set(channelData, this.buffer.length) this.buffer = merged while (this.buffer.length >= RECORD_CHUNK_SIZE) { const chunk = this.buffer.slice(0, RECORD_CHUNK_SIZE) this.buffer = this.buffer.slice(RECORD_CHUNK_SIZE) // Float32 [-1, 1] → Int16 PCM little-endian const pcm = new ArrayBuffer(chunk.length * 2) const view = new DataView(pcm) for (let i = 0; i < chunk.length; i++) { const s = Math.max(-1, Math.min(1, chunk[i])) view.setInt16(i * 2, s < 0 ? s * 0x8000 : s * 0x7fff, true) } this.port.postMessage(pcm, [pcm]) } return true } } registerProcessor('pcm-record-processor', PcmRecordProcessor)