78 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			78 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { Socket } from "phoenix";
 | 
						|
 | 
						|
export const VadHook = {
 | 
						|
  async mounted() {
 | 
						|
    const statusDiv = document.getElementById("vad-status");
 | 
						|
 | 
						|
    const ortScript = document.createElement("script");
 | 
						|
    ortScript.src = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.14.0/dist/ort.js";
 | 
						|
 | 
						|
    const vadScript = document.createElement("script");
 | 
						|
    vadScript.src = "https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.22/dist/bundle.min.js";
 | 
						|
 | 
						|
    ortScript.onload = () => {
 | 
						|
      vadScript.onload = async () => {
 | 
						|
        this.socket = new Socket("ws://localhost:4003/socket");
 | 
						|
        this.socket.connect();
 | 
						|
        this.channel = this.socket.channel("audio:lobby");
 | 
						|
        await this.channel.join().receive("ok", () => {
 | 
						|
          console.log("✅ Canal audio:lobby unido.");
 | 
						|
        });
 | 
						|
 | 
						|
        const myvad = await vad.MicVAD.new({
 | 
						|
          onSpeechStart: () => {
 | 
						|
            statusDiv.textContent = "🎤 Voz detectada...";
 | 
						|
          },
 | 
						|
          onSpeechEnd: async (float32Audio) => {
 | 
						|
            statusDiv.textContent = "✅ Voz finalizada. Enviando audio...";
 | 
						|
 | 
						|
            // Enviar el audio correctamente formateado
 | 
						|
            await sendAudioChunk(float32Audio, this.channel);
 | 
						|
 | 
						|
            // Indicar stop si querés (como payload vacío JSON)
 | 
						|
            this.channel.push("stop_audio", {});
 | 
						|
          }
 | 
						|
        });
 | 
						|
 | 
						|
        myvad.start();
 | 
						|
        statusDiv.textContent = "🚀 VAD iniciado.";
 | 
						|
      };
 | 
						|
      document.body.appendChild(vadScript);
 | 
						|
    };
 | 
						|
    document.body.appendChild(ortScript);
 | 
						|
    
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// Función de helper para enviar el chunk
 | 
						|
function float32ToInt16(float32Array) {
 | 
						|
  const int16Array = new Int16Array(float32Array.length);
 | 
						|
  for (let i = 0; i < float32Array.length; i++) {
 | 
						|
    let s = Math.max(-1, Math.min(1, float32Array[i]));
 | 
						|
    int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
 | 
						|
  }
 | 
						|
  return int16Array;
 | 
						|
}
 | 
						|
 | 
						|
async function sendAudioChunk(float32Audio, channel) {
 | 
						|
  const pcm16 = float32ToInt16(float32Audio);
 | 
						|
  const header = JSON.stringify({ sample_rate: 16000 });
 | 
						|
  const headerBytes = new TextEncoder().encode(header);
 | 
						|
  const audioBytes = new Uint8Array(pcm16.buffer); // same as merged in el otro ejemplo
 | 
						|
  const totalLength = 2 + headerBytes.length + audioBytes.length;
 | 
						|
  const buffer = new ArrayBuffer(totalLength);
 | 
						|
  const view = new DataView(buffer);
 | 
						|
 | 
						|
  // Encabezado: longitud en big endian
 | 
						|
  view.setUint16(0, headerBytes.length, false); // <== big endian
 | 
						|
 | 
						|
  // Copiar header y audio al buffer
 | 
						|
  new Uint8Array(buffer, 2, headerBytes.length).set(headerBytes);
 | 
						|
  new Uint8Array(buffer, 2 + headerBytes.length).set(audioBytes);
 | 
						|
 | 
						|
  // Enviar el buffer binario
 | 
						|
  channel.push("audio_chunk", buffer);
 | 
						|
  console.log("📤 Chunk binario enviado");
 | 
						|
}
 | 
						|
 |