defmodule LivebookWeb.Output.AudioInputComponent do use LivebookWeb, :live_component @impl true def mount(socket) do {:ok, socket |> assign( endianness: System.endianness(), value: nil, audio_url: nil, decoding?: false ) |> allow_upload(:file, accept: :any, max_entries: 1, max_file_size: 100_000_000_000, progress: &handle_progress/3, auto_upload: true )} end @impl true def update(assigns, socket) do {value, assigns} = Map.pop!(assigns, :value) socket = assign(socket, assigns) socket = cond do value == socket.assigns.value -> socket value == nil -> assign(socket, value: value, audio_url: nil) true -> assign(socket, value: value, audio_url: audio_url(socket.assigns.input_id)) end {:ok, socket} end defp audio_url(input_id) do # For the client-side audio preview, we serve audio encoded as WAV # from a separate endpoint. To do that, we encode information in a # token and then the controller fetches input value from the LV. # This is especially important for client-specific inputs in forms. token = LivebookWeb.SessionHelpers.generate_input_token(self(), input_id) ~p"/sessions/audio-input/#{token}" end @impl true def render(assigns) do ~H"""
<.button color="gray" data-btn-record> <.remix_icon icon="mic-line" /> Record <.button color="gray" class="hidden" data-btn-stop> Stop recording <.button color="gray" class="hidden" data-btn-cancel> <.remix_icon icon="close-circle-line" /> Cancel <.button color="gray" data-btn-upload> <.remix_icon icon="upload-2-line" /> Upload
Decoding <.spinner />
<.file_entry name="Audio" entry={entry} on_clear={JS.push("clear_file", target: @myself)} />
""" end @impl true def handle_event("decoding", %{}, socket) do {:noreply, assign(socket, decoding?: true)} end def handle_event("validate", %{}, socket) do {:noreply, socket} end def handle_event("clear_file", %{"ref" => ref}, socket) do {:noreply, cancel_upload(socket, :file, ref)} end defp handle_progress(:file, entry, socket) do if entry.done? do file_ref = consume_uploaded_entry(socket, entry, fn %{path: path} -> {:ok, file_ref} = LivebookWeb.SessionHelpers.register_input_file( socket.assigns.session_pid, path, socket.assigns.input_id, socket.assigns.local, socket.assigns.client_id ) {:ok, file_ref} end) %{"num_channels" => num_channels, "sampling_rate" => sampling_rate} = entry.client_meta value = %{ file_ref: file_ref, num_channels: num_channels, sampling_rate: sampling_rate, format: socket.assigns.format } send_update(LivebookWeb.Output.InputComponent, id: socket.assigns.input_component_id, event: :change, value: value ) end {:noreply, assign(socket, decoding?: false)} end end