feat(t2-live): archi audio Voie A + Bugs 4/5/6 + indicateur de prise de parole (Sprint 6e)
- 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.
This commit is contained in:
parent
9bf95f5c05
commit
72795e924e
16 changed files with 848 additions and 257 deletions
61
public/pcm-record-processor.js
Normal file
61
public/pcm-record-processor.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue