- Voie A WAV : AudioContext unique au rate natif, tap AudioWorklet sur mixGain, uplink rate-aware 16k, alignement par horloge unique (fin offset/resample/concat). Anti-echo candidat. Cycle start=ws.onopen / stop=Terminer / cancel=aucun WAV. - Bug 4 : 'Voir le rapport' route vers le rapport (navigatingAwayRef). - Bug 5 : 'Annuler' (cancelDialogue) - arret sans evaluation, sans WAV, sans production. - Bug 6 : 'Nouvelle simulation' route selon le type via champ tache propage (Report). - Indicateur de prise de parole : state machine USER_SPEAKING/USER_SILENT (RMS + hysteresis). - Cleanup : retrait instrumentation [BISECT] ; ref VAD renomme lastAiChunkTsRef. - Removed : code mort mixTracksToInt16, resample16kTo24k + tests.
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
/**
|
|
* 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)
|