Skip to content

音频录制

音频工厂函数封装

  • 1、调用音频工厂函数时会自动实例化一个音频实例对象,并且把开始、结束、音频上传等函数,进行二次封装提供给执行者
  • 2、在执行开始录音函数时,内部会自动标录音状态,并且开始进行录音,自动监听音频流存储在队列中,同时内部自动开一个一个定时器,用来计算录音的时长
  • 3、在执行录音结束函数时,更新录音状态,并且自动的清除定时器
js
import axios from 'axios'
import { Ref, ref } from 'vue'

export interface InstanceRecord {
  startRecord: () => void
  stopRecord: () => void
  uploadAudio: () => void
  audioStream: Ref<Blob[]>
  playAudioStream: () => void
  audioUrl: Ref<string>
  checkRecordedAudio: Ref<boolean>
  durationStr: Ref<string>
  duration: Ref<number>
}

export default async function useRecord(maxDuration = 60): Promise<InstanceRecord> {
  const audioStream = ref<Blob[]>([])
  const audioUrl = ref<string>('')
  const checkRecordedAudio = ref<boolean>(false)
  const durationStr = ref<string>('')
  const duration = ref<number>(0)
  const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
  const mediaRecorder = new MediaRecorder(stream)
  // 开始录制音频
  const startRecord = () => {
    let timer: any = 0
    audioStream.value = []
    mediaRecorder.start()
    durationStr.value = ''
    duration.value = 0
    const startTime = new Date().getTime()
    checkRecordedAudio.value = true
    const secondsToMinutes = (seconds: number) => {
      const minutes = Math.floor(seconds / 60)
      const remainingSeconds = seconds % 60
      return `${minutes}:${remainingSeconds < 10 ? '0' : ''}${remainingSeconds}`
    }
    const computedDuration = () => {
      const endTime = new Date().getTime()
      duration.value = (endTime - startTime) / 1000
      durationStr.value = secondsToMinutes(parseInt(String(duration.value)))
      if (duration.value >= maxDuration) {
        mediaRecorder.stop()
      }
    }
    computedDuration()
    timer = setInterval(computedDuration, 1000)

    mediaRecorder.addEventListener('dataavailable', (event: BlobEvent) => {
      audioStream.value.push(event.data)
    })
    // 音频录制结束
    mediaRecorder.addEventListener('stop', () => {
      checkRecordedAudio.value = false
      clearInterval(timer)
      computedDuration()
    })
  }
  const stopRecord = () => {
    mediaRecorder.stop()
  }

  // 音频上传
  const uploadAudio = () => {
    const blob = new Blob(audioStream.value, { type: 'audio/ogg; codecs=opus' })
    const formData = new FormData()
    formData.append('file', blob)
    axios({
      method: 'post',
      url: 'http://localhost:3000/upload/record',
      data: formData,
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    }).then((res) => {
      audioUrl.value = res.data.url
    })
  }

  // 播放音频
  const playAudioStream = () => {
    const blob = new Blob(audioStream.value, { type: 'audio/ogg; codecs=opus' })
    const audio = new Audio(URL.createObjectURL(blob))
    audio.play()
  }

  return {
    startRecord,
    stopRecord,
    uploadAudio,
    audioStream,
    playAudioStream,
    audioUrl,
    checkRecordedAudio,
    durationStr,
    duration,
  }
}