80 lines
2.6 KiB
Elixir
80 lines
2.6 KiB
Elixir
defmodule WhisperWeb.AudioChannel do
|
|
@moduledoc """
|
|
Phoenix Channel que gestiona la recepción de audio en tiempo real desde el cliente.
|
|
|
|
El audio se envía en chunks binarios y se acumula en un buffer temporal (`AudioBuffer`).
|
|
Al recibir el evento `"stop_audio"`, todos los chunks se combinan y se guardan como un único archivo WAV final.
|
|
"""
|
|
|
|
use Phoenix.Channel
|
|
require Logger
|
|
alias Phoenix.PubSub
|
|
|
|
def join("audio:lobby", _payload, socket) do
|
|
ref = socket_id(socket)
|
|
Logger.info("Cliente conectado al canal audio:lobby")
|
|
{:ok, socket}
|
|
end
|
|
|
|
@doc """
|
|
Maneja la entrada de un chunk de audio desde el cliente.
|
|
|
|
El mensaje binario contiene un encabezado JSON (con la tasa de muestreo) seguido del audio binario.
|
|
Se almacena el chunk en `AudioBuffer` y también se guarda como archivo WAV parcial.
|
|
"""
|
|
def handle_in("audio_chunk", {:binary, raw_binary}, socket) do
|
|
<<header_len::16, rest::binary>> = raw_binary
|
|
<<header::binary-size(header_len), audio::binary>> = rest
|
|
|
|
%{"sample_rate" => rate} = Jason.decode!(header)
|
|
ref = socket_id(socket)
|
|
|
|
Logger.info("Chunk recibido: #{byte_size(audio)} bytes, sample_rate: #{rate}")
|
|
AudioBuffer.append(ref, {rate, audio})
|
|
|
|
# {:ok, path} = AudioSaver.save_chunk_as_wav(ref, audio, rate, "part")
|
|
# AudioFilesList.add_file(path)
|
|
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
|
|
@doc """
|
|
Recupera todos los chunks acumulados en el buffer, los concatena y guarda un archivo WAV final (sufijo `"final"`).
|
|
"""
|
|
def handle_in("stop_audio", _payload, socket) do
|
|
|
|
Logger.info("🛑 Grabación detenida por cliente")
|
|
|
|
ref = socket_id(socket)
|
|
chunks = AudioBuffer.get_and_clear(ref)
|
|
|
|
if chunks != [] do
|
|
[{rate, _} | _] = chunks
|
|
full_audio = Enum.map(chunks, fn {_, bin} -> bin end) |> IO.iodata_to_binary()
|
|
{:ok, path} = AudioSaver.save_chunk_as_wav(ref, full_audio, rate, "final")
|
|
|
|
Task.start(fn ->
|
|
transcription = Whisper.SendToModel.large(path)
|
|
Logger.info("✅ Transcripción completa:\n#{transcription}")
|
|
message = %{"chunks" => [%{"text" => transcription}]}
|
|
Phoenix.PubSub.broadcast(Whisper.PubSub, "transcription", {:transcription, Jason.encode!(message)})
|
|
File.rm!(path)
|
|
end)
|
|
end
|
|
|
|
{:noreply, socket}
|
|
end
|
|
|
|
defp socket_id(socket), do: socket.transport_pid |> :erlang.pid_to_list() |> List.to_string()
|
|
|
|
def save_raw(ref, bin) do
|
|
File.mkdir_p!("recordings/")
|
|
filename = "#{ref}_#{Whisper.Counter.next(ref)}.raw"
|
|
path = Path.join("recordings", filename)
|
|
File.write!(path, bin)
|
|
{:ok, path}
|
|
end
|
|
end
|