| <script lang="ts"> | |
| import { onMount } from "svelte"; | |
| import type { I18nFormatter } from "@gradio/utils"; | |
| import WaveSurfer from "wavesurfer.js"; | |
| import RecordPlugin from "wavesurfer.js/dist/plugins/record.js"; | |
| import type { WaveformOptions } from "../shared/types"; | |
| import DeviceSelect from "../shared/DeviceSelect.svelte"; | |
| export let recording = false; | |
| export let paused_recording = false; | |
| export let stop: () => void; | |
| export let record: () => void; | |
| export let i18n: I18nFormatter; | |
| export let waveform_settings: Record<string, any>; | |
| export let waveform_options: WaveformOptions = { | |
| show_recording_waveform: true | |
| }; | |
| let micWaveform: WaveSurfer; | |
| let waveformRecord: RecordPlugin; | |
| let microphoneContainer: HTMLDivElement; | |
| let micDevices: MediaDeviceInfo[] = []; | |
| onMount(() => { | |
| create_mic_waveform(); | |
| }); | |
| const create_mic_waveform = (): void => { | |
| if (micWaveform !== undefined) micWaveform.destroy(); | |
| if (!microphoneContainer) return; | |
| micWaveform = WaveSurfer.create({ | |
| ...waveform_settings, | |
| height: 100, | |
| container: microphoneContainer | |
| }); | |
| waveformRecord = micWaveform.registerPlugin(RecordPlugin.create()); | |
| }; | |
| </script> | |
| <div class="mic-wrap"> | |
| {#if waveform_options.show_recording_waveform} | |
| <div | |
| bind:this={microphoneContainer} | |
| style:display={recording ? "block" : "none"} | |
| /> | |
| {/if} | |
| <div class="controls"> | |
| {#if recording} | |
| <button | |
| class={paused_recording ? "stop-button-paused" : "stop-button"} | |
| on:click={() => { | |
| waveformRecord?.stopMic(); | |
| stop(); | |
| }} | |
| > | |
| <span class="record-icon"> | |
| <span class="pinger" /> | |
| <span class="dot" /> | |
| </span> | |
| {paused_recording ? i18n("audio.pause") : i18n("audio.stop")} | |
| </button> | |
| {:else} | |
| <button | |
| class="record-button" | |
| on:click={() => { | |
| waveformRecord?.startMic(); | |
| record(); | |
| }} | |
| > | |
| <span class="record-icon"> | |
| <span class="dot" /> | |
| </span> | |
| {i18n("audio.record")} | |
| </button> | |
| {/if} | |
| <DeviceSelect bind:micDevices {i18n} /> | |
| </div> | |
| </div> | |
| <style> | |
| .controls { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| flex-wrap: wrap; | |
| } | |
| .mic-wrap { | |
| display: block; | |
| align-items: center; | |
| margin: var(--spacing-xl); | |
| } | |
| .stop-button-paused { | |
| display: none; | |
| height: var(--size-8); | |
| width: var(--size-20); | |
| background-color: var(--block-background-fill); | |
| border-radius: var(--radius-3xl); | |
| align-items: center; | |
| border: 1px solid var(--neutral-400); | |
| margin-right: 5px; | |
| } | |
| .stop-button-paused::before { | |
| content: ""; | |
| height: var(--size-4); | |
| width: var(--size-4); | |
| border-radius: var(--radius-full); | |
| background: var(--primary-600); | |
| margin: 0 var(--spacing-xl); | |
| } | |
| .stop-button::before { | |
| content: ""; | |
| height: var(--size-4); | |
| width: var(--size-4); | |
| border-radius: var(--radius-full); | |
| background: var(--primary-600); | |
| margin: 0 var(--spacing-xl); | |
| animation: scaling 1800ms infinite; | |
| } | |
| .stop-button { | |
| height: var(--size-8); | |
| width: var(--size-20); | |
| background-color: var(--block-background-fill); | |
| border-radius: var(--radius-3xl); | |
| align-items: center; | |
| border: 1px solid var(--primary-600); | |
| margin-right: 5px; | |
| display: flex; | |
| } | |
| .record-button::before { | |
| content: ""; | |
| height: var(--size-4); | |
| width: var(--size-4); | |
| border-radius: var(--radius-full); | |
| background: var(--primary-600); | |
| margin: 0 var(--spacing-xl); | |
| } | |
| .record-button { | |
| height: var(--size-8); | |
| width: var(--size-24); | |
| background-color: var(--block-background-fill); | |
| border-radius: var(--radius-3xl); | |
| display: flex; | |
| align-items: center; | |
| border: 1px solid var(--neutral-400); | |
| } | |
| @keyframes scaling { | |
| 0% { | |
| background-color: var(--primary-600); | |
| scale: 1; | |
| } | |
| 50% { | |
| background-color: var(--primary-600); | |
| scale: 1.2; | |
| } | |
| 100% { | |
| background-color: var(--primary-600); | |
| scale: 1; | |
| } | |
| } | |
| </style> | |