import React, { useRef, useEffect, useState } from "react"; import WaveSurfer from "wavesurfer.js"; import RegionsPlugin from "wavesurfer.js/dist/plugins/regions"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faDownload } from "@fortawesome/free-solid-svg-icons"; import Dropdown from "react-dropdown"; import "react-dropdown/style.css"; import CustomRecordPlugin from "./CustomRecordPlugin"; import { formatTime } from "../lib/time"; const AudioInputsDropdown = (props) => { const [ddOptions, setDdOptions] = useState([]); useEffect(() => { const init = async () => { // Request permission to use audio inputs await navigator.mediaDevices .getUserMedia({ audio: true }) .then((stream) => stream.getTracks().forEach((t) => t.stop())); const devices = await navigator.mediaDevices.enumerateDevices(); const audioDevices = devices .filter((d) => d.kind === "audioinput" && d.deviceId != "") .map((d) => ({ value: d.deviceId, label: d.label })); if (audioDevices.length < 1) return console.log("no audio input devices"); setDdOptions(audioDevices); props.setDeviceId(audioDevices[0].value); }; init(); }, []); const handleDropdownChange = (e) => { props.setDeviceId(e.value); }; return ( ); }; export default function Recorder(props) { const waveformRef = useRef(); const [wavesurfer, setWavesurfer] = useState(null); const [record, setRecord] = useState(null); const [isRecording, setIsRecording] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [deviceId, setDeviceId] = useState(null); const [currentTime, setCurrentTime] = useState(0); const [timeInterval, setTimeInterval] = useState(null); const [duration, setDuration] = useState(0); const [waveRegions, setWaveRegions] = useState(null); const [activeTopic, setActiveTopic] = props.useActiveTopic; const topicsRef = useRef(props.topics); useEffect(() => { document.getElementById("play-btn").disabled = true; if (waveformRef.current) { const _wavesurfer = WaveSurfer.create({ container: waveformRef.current, waveColor: "#777", progressColor: "#222", cursorColor: "OrangeRed", hideScrollbar: true, autoCenter: true, barWidth: 2, height: 90, }); const wsWrapper = _wavesurfer.getWrapper(); wsWrapper.style.cursor = "pointer"; wsWrapper.style.backgroundColor = "#e0c3fc42"; wsWrapper.style.borderRadius = "15px"; _wavesurfer.on("play", () => { setIsPlaying(true); }); _wavesurfer.on("pause", () => { setIsPlaying(false); }); _wavesurfer.on("timeupdate", setCurrentTime); setRecord(_wavesurfer.registerPlugin(CustomRecordPlugin.create())); setWaveRegions(_wavesurfer.registerPlugin(RegionsPlugin.create())); setWavesurfer(_wavesurfer); return () => { _wavesurfer.destroy(); setIsRecording(false); setIsPlaying(false); }; } }, []); useEffect(() => { topicsRef.current = props.topics; if (!isRecording) renderMarkers(); }, [props.topics]); const renderMarkers = () => { if (!waveRegions) return; waveRegions.clearRegions(); for (let topic of topicsRef.current) { const content = document.createElement("div"); content.style = ` border-left: solid 1px orange; padding: 0 2px; font-size: 0.7rem; max-width: 100px; width: max-content; cursor: pointer; background-color: white; border-radius: 0 3px 3px 0; `; content.onmouseover = () => (content.style.backgroundColor = "orange"); content.onmouseout = () => (content.style.backgroundColor = "white"); content.textContent = topic.title.length >= 20 ? topic.title.slice(0, 17) + "..." : topic.title; const region = waveRegions.addRegion({ start: topic.timestamp, content, color: "f00", drag: false, }); region.on("click", (e) => { e.stopPropagation(); setActiveTopic(topic); wavesurfer.setTime(region.start); }); } }; useEffect(() => { if (record) { return record.on("stopRecording", () => { const link = document.getElementById("download-recording"); link.href = record.getRecordedUrl(); link.download = "reflector-recording.webm"; link.style.visibility = "visible"; renderMarkers(); }); } }, [record]); useEffect(() => { if (isRecording) { const interval = setInterval(() => { setCurrentTime((prev) => prev + 1); }, 1000); setTimeInterval(interval); return () => clearInterval(interval); } else { clearInterval(timeInterval); setCurrentTime((prev) => { setDuration(prev); return 0; }); } }, [isRecording]); useEffect(() => { if (activeTopic) { wavesurfer.setTime(activeTopic.timestamp); } }, [activeTopic]); const handleRecClick = async () => { if (!record) return console.log("no record"); if (record.isRecording()) { props.onStop(); record.stopRecording(); setIsRecording(false); document.getElementById("play-btn").disabled = false; } else { const stream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId, noiseSuppression: false, echoCancellation: false, }, }); await record.startRecording(stream); props.setStream(stream); setIsRecording(true); } }; const handlePlayClick = () => { wavesurfer?.playPause(); }; const timeLabel = () => { if (isRecording) return formatTime(currentTime); else if (duration) return `${formatTime(currentTime)}/${formatTime(duration)}`; else ""; }; return (
   
{isRecording && (
)} {timeLabel()}
); }