Spaces:
Running
Running
import { useEffect, useRef, useImperativeHandle, forwardRef } from 'react' | |
import WaveSurfer from 'wavesurfer.js' | |
// @ts-ignore: No types for timeline.esm.js | |
import TimelinePlugin from 'wavesurfer.js/dist/plugins/timeline.esm.js' | |
import API from '../API' | |
export interface AudioPlayerHandle { | |
getCurrentTime: () => number | |
setTime: (t: number) => void | |
play: () => void | |
pause: () => void | |
} | |
const AudioPlayer = forwardRef< | |
AudioPlayerHandle, | |
{ | |
src: string | |
playing: boolean | |
// seekTo: number | |
onPlay: () => void | |
onPause: () => void | |
onAudioProcess?: (currentTime: number) => void | |
} | |
>(({ src, playing, onPlay, onPause, onAudioProcess }, ref) => { | |
const containerRef = useRef<HTMLDivElement>(null) | |
const wavesurferRef = useRef<WaveSurfer | null>(null) | |
useImperativeHandle(ref, () => ({ | |
getCurrentTime: () => wavesurferRef.current?.getCurrentTime() || 0, | |
setTime: (t: number) => wavesurferRef.current?.setTime(t), | |
play: () => wavesurferRef.current?.play(), | |
pause: () => wavesurferRef.current?.pause(), | |
})) | |
// Sync play/pause and seek | |
useEffect(() => { | |
if (wavesurferRef.current) { | |
// wavesurferRef.current.setTime(seekTo) | |
if (playing) { | |
wavesurferRef.current.play() | |
} else { | |
wavesurferRef.current.pause() | |
} | |
} | |
}, [ | |
playing, | |
// seekTo | |
]) | |
useEffect(() => { | |
if (!containerRef.current) return | |
if (wavesurferRef.current) { | |
wavesurferRef.current.destroy() | |
wavesurferRef.current = null | |
} | |
const proxiedUrl = API.getProxiedUrl(src) | |
const bottomTimeline = TimelinePlugin.create({ | |
height: 16, | |
timeInterval: 0.1, | |
primaryLabelInterval: 1, | |
style: { fontSize: '10px' }, | |
}) | |
wavesurferRef.current = WaveSurfer.create({ | |
container: containerRef.current, | |
waveColor: 'rgb(200, 0, 200)', | |
progressColor: 'rgb(100, 0, 100)', | |
url: proxiedUrl, | |
minPxPerSec: 100, | |
mediaControls: true, | |
plugins: [bottomTimeline], | |
}) | |
wavesurferRef.current.on('play', onPlay) | |
wavesurferRef.current.on('pause', onPause) | |
if (onAudioProcess) { | |
wavesurferRef.current.on('audioprocess', onAudioProcess) | |
} | |
return () => { | |
if (wavesurferRef.current && onAudioProcess) { | |
wavesurferRef.current.un('audioprocess', onAudioProcess) | |
} | |
wavesurferRef.current?.destroy() | |
wavesurferRef.current = null | |
} | |
}, [src]) | |
return ( | |
<div className="w-full"> | |
<div ref={containerRef} /> | |
</div> | |
) | |
}) | |
export default AudioPlayer | |