import React, { useEffect, useRef, useState } from "react"; import { BsPauseFill, BsPlayFill, BsScissors, BsTrash } from "react-icons/bs"; import WaveSurfer from "wavesurfer.js"; // @ts-ignore import RegionsPlugin from 'wavesurfer.js/dist/plugin/wavesurfer.regions.min.js'; interface Props { audio: string; waveColor: string; progressColor: string; variant?: 'exercise' | 'edit'; onCutsChange?: (cuts: AudioCut[]) => void; } interface AudioCut { id: string; start: number; end: number; } const Waveform = ({ audio, waveColor, progressColor, variant = 'exercise', onCutsChange }: Props) => { const containerRef = useRef(null); const waveSurferRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [cuts, setCuts] = useState([]); const [currentRegion, setCurrentRegion] = useState(null); const [duration, setDuration] = useState(0); useEffect(() => { const waveSurfer = WaveSurfer.create({ container: containerRef?.current || "", responsive: true, cursorWidth: 0, height: variant === 'edit' ? 96 : 24, waveColor, progressColor, barGap: 5, barWidth: 8, barRadius: 4, fillParent: true, hideScrollbar: true, normalize: true, autoCenter: true, ignoreSilenceMode: true, barMinHeight: 4, plugins: variant === 'edit' ? [ RegionsPlugin.create({ dragSelection: true, slop: 5 }) ] : [] }); waveSurfer.load(audio); waveSurfer.on("ready", () => { waveSurferRef.current = waveSurfer; setDuration(waveSurfer.getDuration()); }); waveSurfer.on("finish", () => setIsPlaying(false)); if (variant === 'edit') { waveSurfer.on('region-created', (region) => { setCurrentRegion(region); const newCut: AudioCut = { id: region.id, start: region.start, end: region.end }; setCuts(prev => [...prev, newCut]); onCutsChange?.([...cuts, newCut]); }); waveSurfer.on('region-updated', (region) => { setCuts(prev => prev.map(cut => cut.id === region.id ? { ...cut, start: region.start, end: region.end } : cut )); onCutsChange?.(cuts.map(cut => cut.id === region.id ? { ...cut, start: region.start, end: region.end } : cut )); }); } return () => { waveSurfer.destroy(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [audio, progressColor, waveColor, variant]); const handlePlayPause = () => { setIsPlaying(prev => !prev); waveSurferRef.current?.playPause(); }; const handleDeleteRegion = (cutId: string) => { const region = waveSurferRef.current?.regions?.list[cutId]; if (region) { region.remove(); setCuts(prev => prev.filter(cut => cut.id !== cutId)); onCutsChange?.(cuts.filter(cut => cut.id !== cutId)); } }; const formatTime = (time: number) => { const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; return (
{isPlaying ? ( ) : ( )} {variant === 'edit' && duration > 0 && (
Total Duration: {formatTime(duration)}
)}
{variant === 'edit' && cuts.length > 0 && (

Audio Cuts

{cuts.map((cut) => (
{formatTime(cut.start)} - {formatTime(cut.end)}
))}
)}
); }; export default Waveform;