useAudio.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import { reactive, ref } from 'vue'
  2. import { ElMessage } from 'element-plus'
  3. export default function useAudio(url, playEnd = () => {}) {
  4. return new Promise((resolve, reject) => {
  5. const state: any = reactive({
  6. source: null,
  7. analyser: null,
  8. dataArray: null,
  9. animationId: null,
  10. pauseTime: 0,
  11. })
  12. const volume = ref(0)
  13. const audioContext = new window.AudioContext()
  14. let audioDom: any = document.createElement('audio')
  15. audioDom.setAttribute('src', url)
  16. audioDom.setAttribute('crossorigin', 'anonymous')
  17. document.body.appendChild(audioDom)
  18. // 创建分析器节点
  19. state.analyser = audioContext.createAnalyser()
  20. state.analyser.fftSize = 256
  21. state.dataArray = new Uint8Array(state.analyser.frequencyBinCount)
  22. // 创建媒体元素源节点
  23. state.source = audioContext.createMediaElementSource(audioDom)
  24. // 连接节点:源 -> 分析器 -> 目的地(扬声器)
  25. state.source.connect(state.analyser)
  26. state.analyser.connect(audioContext.destination)
  27. const pause = () => {
  28. cancelAnimationFrame(state.animationId)
  29. audioDom.pause()
  30. volume.value = 0
  31. }
  32. const stop = () => {
  33. pause()
  34. document.body.removeChild(audioDom)
  35. audioDom = null
  36. playEnd()
  37. }
  38. audioDom.oncanplay = () => {
  39. resolve({
  40. volume,
  41. play: () => {
  42. audioDom?.play()
  43. updateVolume()
  44. },
  45. pause,
  46. stop,
  47. })
  48. }
  49. audioDom.onended = () => {
  50. stop()
  51. }
  52. const updateVolume = () => {
  53. if (!state.analyser) return
  54. state.analyser.getByteFrequencyData(state.dataArray)
  55. // 计算平均音量
  56. let sum = 0
  57. for (let i = 0; i < state.dataArray.length; i++) {
  58. sum += state.dataArray[i]
  59. }
  60. const average = sum / state.dataArray.length
  61. // 将音量映射到0-100范围
  62. volume.value = Math.min(100, Math.max(0, Math.round(average / 2.55)))
  63. // 继续动画
  64. state.animationId = requestAnimationFrame(updateVolume)
  65. }
  66. })
  67. }