|
@@ -0,0 +1,130 @@
|
|
|
+import { reactive, ref } from 'vue'
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
+
|
|
|
+export default function useAudio(url, playEnd = () => {}) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ const state: any = reactive({
|
|
|
+ source: null,
|
|
|
+ analyser: null,
|
|
|
+ dataArray: null,
|
|
|
+ animationId: null,
|
|
|
+ pauseTime: 0,
|
|
|
+ })
|
|
|
+ const volume = ref(0)
|
|
|
+ const audioContext = new window.AudioContext()
|
|
|
+ let audioDom: any = document.createElement('audio')
|
|
|
+ audioDom.setAttribute('src', url)
|
|
|
+ audioDom.setAttribute('crossorigin', 'anonymous')
|
|
|
+ // audioDom.
|
|
|
+ document.body.appendChild(audioDom)
|
|
|
+ // 创建分析器节点
|
|
|
+ state.analyser = audioContext.createAnalyser()
|
|
|
+ state.analyser.fftSize = 256
|
|
|
+ state.dataArray = new Uint8Array(state.analyser.frequencyBinCount)
|
|
|
+ // 创建媒体元素源节点
|
|
|
+ state.source = audioContext.createMediaElementSource(audioDom)
|
|
|
+ // 连接节点:源 -> 分析器 -> 目的地(扬声器)
|
|
|
+ state.source.connect(state.analyser)
|
|
|
+ state.analyser.connect(audioContext.destination)
|
|
|
+ const pause = () => {
|
|
|
+ cancelAnimationFrame(state.animationId)
|
|
|
+ audioDom.pause()
|
|
|
+ volume.value = 0
|
|
|
+ }
|
|
|
+ const stop = () => {
|
|
|
+ pause()
|
|
|
+ document.body.removeChild(audioDom)
|
|
|
+ audioDom = null
|
|
|
+ playEnd()
|
|
|
+ }
|
|
|
+ audioDom.oncanplay = () => {
|
|
|
+ resolve({
|
|
|
+ volume,
|
|
|
+ play: () => {
|
|
|
+ audioDom?.play()
|
|
|
+ updateVolume()
|
|
|
+ },
|
|
|
+ pause,
|
|
|
+ stop,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ audioDom.onended = () => {
|
|
|
+ stop()
|
|
|
+ }
|
|
|
+ // const init = async () => {
|
|
|
+ // try {
|
|
|
+ // const audioContext = new window.AudioContext()
|
|
|
+ // console.log(audioContext)
|
|
|
+ // // 获取音频文件
|
|
|
+ // const response = await fetch(url)
|
|
|
+ // const arrayBuffer = await response.arrayBuffer()
|
|
|
+ // const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
|
|
|
+ // // 创建音频源
|
|
|
+ // state.source = audioContext.createBufferSource()
|
|
|
+ // state.source.buffer = audioBuffer
|
|
|
+ // state.source.onended = () => {
|
|
|
+ // console.log(444)
|
|
|
+ // cancelAnimationFrame(state.animationId)
|
|
|
+ // playEnd()
|
|
|
+ // }
|
|
|
+ // // 创建分析器节点
|
|
|
+ // state.analyser = audioContext.createAnalyser()
|
|
|
+ // state.analyser.fftSize = 256
|
|
|
+ // state.dataArray = new Uint8Array(state.analyser.frequencyBinCount)
|
|
|
+ // // 连接节点:源 -> 分析器 -> 目的地(扬声器)
|
|
|
+ // state.source.connect(state.analyser)
|
|
|
+ // state.analyser.connect(audioContext.destination)
|
|
|
+ // // 开始播放
|
|
|
+ // console.log(state.source)
|
|
|
+ // // 开始音量检测循环
|
|
|
+ // updateVolume()
|
|
|
+ // resolve({
|
|
|
+ // volume,
|
|
|
+ // play: () => {
|
|
|
+ // // 如果是从暂停恢复播放
|
|
|
+ // if (state.pauseTime > 0) {
|
|
|
+ // console.log(state.pauseTime)
|
|
|
+ // state.source = audioContext.createBufferSource()
|
|
|
+ // state.source.buffer = audioBuffer
|
|
|
+ // state.source.connect(state.analyser)
|
|
|
+ // console.log(Math.min(0, state.pauseTime - 1))
|
|
|
+ // state.source.start(0, state.pauseTime - 1)
|
|
|
+ // updateVolume()
|
|
|
+ // } else {
|
|
|
+ // state.source.start(0)
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ // stop: () => {
|
|
|
+ // state.source.stop()
|
|
|
+ // state.source.disconnect()
|
|
|
+ // },
|
|
|
+ // pause: () => {
|
|
|
+ // state.pauseTime = audioContext.currentTime
|
|
|
+ // // 停止当前播放
|
|
|
+ // state.source.stop()
|
|
|
+ // state.source.disconnect()
|
|
|
+ // },
|
|
|
+ // })
|
|
|
+ // } catch (err: any) {
|
|
|
+ // ElMessage.error('无法播放音频: ' + err.message)
|
|
|
+ // console.error('音频播放异常:', err)
|
|
|
+ // reject(err)
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // init()
|
|
|
+ const updateVolume = () => {
|
|
|
+ if (!state.analyser) return
|
|
|
+ state.analyser.getByteFrequencyData(state.dataArray)
|
|
|
+ // 计算平均音量
|
|
|
+ let sum = 0
|
|
|
+ for (let i = 0; i < state.dataArray.length; i++) {
|
|
|
+ sum += state.dataArray[i]
|
|
|
+ }
|
|
|
+ const average = sum / state.dataArray.length
|
|
|
+ // 将音量映射到0-100范围
|
|
|
+ volume.value = Math.min(100, Math.max(0, Math.round(average / 2.55)))
|
|
|
+ // 继续动画
|
|
|
+ state.animationId = requestAnimationFrame(updateVolume)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|