166 lines
4.6 KiB
TypeScript
166 lines
4.6 KiB
TypeScript
import React, {
|
|
forwardRef,
|
|
useCallback,
|
|
useEffect,
|
|
useImperativeHandle,
|
|
useRef
|
|
} from 'react';
|
|
import { AudioPlayerProps } from './config/type';
|
|
|
|
const RawAudioPlayer: React.FC<AudioPlayerProps> = forwardRef((props, ref) => {
|
|
const { autoplay = false } = props;
|
|
const audioRef = React.useRef<HTMLAudioElement>(null);
|
|
|
|
// =================== audio context ======================
|
|
const audioContext = useRef<any>(null);
|
|
const analyser = useRef<any>(null);
|
|
const dataArray = useRef<any>(null);
|
|
// ========================================================
|
|
|
|
const initAudioContext = useCallback(() => {
|
|
audioContext.current = new (window.AudioContext ||
|
|
window.webkitAudioContext)();
|
|
|
|
analyser.current = audioContext.current.createAnalyser();
|
|
analyser.current.fftSize = 512;
|
|
dataArray.current = new Uint8Array(analyser.current.frequencyBinCount);
|
|
}, []);
|
|
|
|
const generateVisualData = useCallback(() => {
|
|
const source = audioContext.current.createMediaElementSource(
|
|
audioRef.current
|
|
);
|
|
source.connect(analyser.current);
|
|
analyser.current.connect(audioContext.current.destination);
|
|
}, []);
|
|
|
|
const initEnvents = () => {
|
|
if (!audioRef.current) {
|
|
return;
|
|
}
|
|
|
|
audioRef.current.addEventListener('complete', () => {});
|
|
|
|
audioRef.current.addEventListener('play', () => {
|
|
props.onAnalyse?.(dataArray.current, analyser);
|
|
props.onPlay?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('pause', () => {
|
|
props.onAnalyse?.(dataArray.current, analyser);
|
|
props.onPause?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('timeupdate', () => {
|
|
props.onTimeUpdate?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('ended', () => {
|
|
props.onEnded?.();
|
|
});
|
|
// add all other events
|
|
|
|
audioRef.current.addEventListener('canplay', () => {
|
|
props.onCanPlay?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('loadeddata', () => {
|
|
initEnvents();
|
|
if (!audioContext.current) {
|
|
initAudioContext();
|
|
generateVisualData();
|
|
}
|
|
props.onLoadedData?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('seeked', () => {
|
|
props.onSeeked?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('seeking', () => {
|
|
props.onSeeking?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('volumechange', () => {
|
|
props.onVolumeChange?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('audioprocess', () => {
|
|
const current = audioRef.current?.currentTime || 0;
|
|
props.onAudioProcess?.(current);
|
|
});
|
|
|
|
audioRef.current.addEventListener('playing', () => {
|
|
props.onPlaying?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('loadedmetadata', () => {
|
|
const duration = audioRef.current?.duration || 0;
|
|
props.onLoadedMetadata?.(duration);
|
|
props.onReady?.(duration);
|
|
});
|
|
|
|
audioRef.current.addEventListener('ended', () => {
|
|
props.onEnded?.();
|
|
});
|
|
|
|
audioRef.current.addEventListener('loadeddata', () => {
|
|
props.onLoadedData?.();
|
|
});
|
|
};
|
|
|
|
useImperativeHandle(ref, () => ({
|
|
play: () => {
|
|
audioRef.current?.play();
|
|
},
|
|
pause: () => {
|
|
audioRef.current?.pause();
|
|
}
|
|
}));
|
|
|
|
useEffect(() => {
|
|
if (audioRef.current) {
|
|
console.log('audioRef.current', audioRef.current, props.url);
|
|
initEnvents();
|
|
}
|
|
return () => {
|
|
if (audioContext.current) {
|
|
audioContext.current.close();
|
|
}
|
|
// remove all events
|
|
|
|
audioRef.current?.removeEventListener('play', () => {});
|
|
audioRef.current?.removeEventListener('pause', () => {});
|
|
audioRef.current?.removeEventListener('timeupdate', () => {});
|
|
audioRef.current?.removeEventListener('ended', () => {});
|
|
audioRef.current?.removeEventListener('canplay', () => {});
|
|
audioRef.current?.removeEventListener('loadeddata', () => {});
|
|
audioRef.current?.removeEventListener('seeked', () => {});
|
|
audioRef.current?.removeEventListener('seeking', () => {});
|
|
audioRef.current?.removeEventListener('volumechange', () => {});
|
|
audioRef.current?.removeEventListener('audioprocess', () => {});
|
|
audioRef.current?.removeEventListener('playing', () => {});
|
|
audioRef.current?.removeEventListener('loadedmetadata', () => {});
|
|
audioRef.current?.removeEventListener('ended', () => {});
|
|
audioRef.current?.removeEventListener('loadeddata', () => {});
|
|
};
|
|
}, [audioRef.current]);
|
|
|
|
return (
|
|
<audio
|
|
controls
|
|
autoPlay={autoplay}
|
|
src={props.url}
|
|
ref={audioRef}
|
|
style={{
|
|
position: 'absolute',
|
|
left: '-9999px',
|
|
opacity: 0
|
|
}}
|
|
preload="metadata"
|
|
></audio>
|
|
);
|
|
});
|
|
|
|
export default RawAudioPlayer;
|