203 lines
4.6 KiB
TypeScript
203 lines
4.6 KiB
TypeScript
let audio: HTMLAudioElement | null = null
|
|
let audioContext: AudioContext
|
|
let mediaSource: MediaElementAudioSourceNode
|
|
let analyser: AnalyserNode
|
|
|
|
export const createAudio = () => {
|
|
if (audio) return
|
|
audio = new window.Audio()
|
|
audio.controls = false
|
|
audio.autoplay = true
|
|
audio.preload = 'auto'
|
|
}
|
|
|
|
export const getAnalyser = (): AnalyserNode | null => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
if (audioContext == null) {
|
|
audioContext = new window.AudioContext()
|
|
mediaSource = audioContext.createMediaElementSource(audio)
|
|
analyser = audioContext.createAnalyser()
|
|
analyser.fftSize = 256
|
|
mediaSource.connect(analyser)
|
|
analyser.connect(audioContext.destination)
|
|
}
|
|
return analyser
|
|
}
|
|
|
|
export const hasInitedAnalyser = (): boolean => audioContext != null
|
|
|
|
export const setResource = (src: string) => {
|
|
if (audio) audio.src = src
|
|
}
|
|
|
|
export const setPlay = () => {
|
|
void audio?.play()
|
|
}
|
|
|
|
export const setPause = () => {
|
|
audio?.pause()
|
|
}
|
|
|
|
export const setStop = () => {
|
|
if (audio) {
|
|
audio.src = ''
|
|
audio.removeAttribute('src')
|
|
}
|
|
}
|
|
|
|
export const isEmpty = (): boolean => !audio?.src
|
|
|
|
export const setLoopPlay = (isLoop: boolean) => {
|
|
if (audio) audio.loop = isLoop
|
|
}
|
|
|
|
export const getPlaybackRate = (): number => {
|
|
return audio?.defaultPlaybackRate ?? 1
|
|
}
|
|
|
|
export const setPlaybackRate = (rate: number) => {
|
|
if (!audio) return
|
|
audio.defaultPlaybackRate = rate
|
|
audio.playbackRate = rate
|
|
}
|
|
|
|
export const getMute = (): boolean => {
|
|
return audio?.muted ?? false
|
|
}
|
|
|
|
export const setMute = (isMute: boolean) => {
|
|
if (audio) audio.muted = isMute
|
|
}
|
|
|
|
export const getCurrentTime = () => {
|
|
return audio?.currentTime ?? 0
|
|
}
|
|
|
|
export const setCurrentTime = (time: number) => {
|
|
if (audio) audio.currentTime = time
|
|
}
|
|
|
|
export const setMediaDeviceId = (mediaDeviceId: string) => {
|
|
// @ts-expect-error
|
|
return audio ? audio.setSinkId(mediaDeviceId) : Promise.resolve()
|
|
}
|
|
|
|
export const setVolume = (volume: number) => {
|
|
if (audio) audio.volume = volume
|
|
}
|
|
|
|
export const getDuration = () => {
|
|
return audio?.duration ?? 0
|
|
}
|
|
|
|
// export const getPlaybackRate = () => {
|
|
// return audio?.playbackRate ?? 1
|
|
// }
|
|
|
|
type Noop = () => void
|
|
|
|
export const onPlaying = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('playing', callback)
|
|
return () => {
|
|
audio?.removeEventListener('playing', callback)
|
|
}
|
|
}
|
|
|
|
export const onPause = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio?.addEventListener('pause', callback)
|
|
return () => {
|
|
audio?.removeEventListener('pause', callback)
|
|
}
|
|
}
|
|
|
|
export const onEnded = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('ended', callback)
|
|
return () => {
|
|
audio?.removeEventListener('ended', callback)
|
|
}
|
|
}
|
|
|
|
export const onError = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('error', callback)
|
|
return () => {
|
|
audio?.removeEventListener('error', callback)
|
|
}
|
|
}
|
|
|
|
export const onLoadeddata = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('loadeddata', callback)
|
|
return () => {
|
|
audio?.removeEventListener('loadeddata', callback)
|
|
}
|
|
}
|
|
|
|
export const onLoadstart = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('loadstart', callback)
|
|
return () => {
|
|
audio?.removeEventListener('loadstart', callback)
|
|
}
|
|
}
|
|
|
|
export const onCanplay = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('canplay', callback)
|
|
return () => {
|
|
audio?.removeEventListener('canplay', callback)
|
|
}
|
|
}
|
|
|
|
export const onEmptied = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('emptied', callback)
|
|
return () => {
|
|
audio?.removeEventListener('emptied', callback)
|
|
}
|
|
}
|
|
|
|
export const onTimeupdate = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('timeupdate', callback)
|
|
return () => {
|
|
audio?.removeEventListener('timeupdate', callback)
|
|
}
|
|
}
|
|
|
|
// 缓冲中
|
|
export const onWaiting = (callback: Noop) => {
|
|
if (!audio) throw new Error('audio not defined')
|
|
|
|
audio.addEventListener('waiting', callback)
|
|
return () => {
|
|
audio?.removeEventListener('waiting', callback)
|
|
}
|
|
}
|
|
|
|
// 可见性改变
|
|
export const onVisibilityChange = (callback: Noop) => {
|
|
document.addEventListener('visibilitychange', callback)
|
|
return () => {
|
|
document.removeEventListener('visibilitychange', callback)
|
|
}
|
|
}
|
|
|
|
|
|
export const getErrorCode = () => {
|
|
return audio?.error?.code
|
|
}
|