web audio api 实现音频播放

最近被选中做音视频,挺幸运的吧,一直在接触新的项目,每次都能被分到新的项目组,干好多费头发的事情

上周五肝到12点半,总算是把音频编辑上了线

总结了一下,决定写一写,也盘点一下遇到的坑

web audio API是 HTML5新增的API,提供了在web上控制音频的一个有效通用的系统,开发者可以自选音频源,对音频添加特效,添加空间效果,使音频可视化,等等。

注:目前这个API浏览器支持度并不高,pc浏览器支持较好的有firefox、Chrome和safari,Safari上也依然有不少兼容问题,移动端支持android5.0及以上,iOS端是6.1以上版本支持;

web audio 从获取数据到播放的过程大概如下图:

web audio api 实现音频播放_第1张图片

Inputs: audio的输入节点,可以是buffer,也可以是audio对象;

Effects:操控音频的节点(目前就用到这个:GainNode, 后续用到其他的再补充)GainNode可以用来控制音量大小,默认为静音,即0,也可以用来设置音频播放的淡入淡出;

Destination:音频播放节点,负责把声音传输给扬声器或耳机;

完整的web audio流程如下:

1. 获取音频文件,实例化一个音频对象;

2. 将获取到的音频文件转成ArrayBuffer;

3. 用ArrayBuffer ,通过AudioContext 实例化一个AudioBuffer对象;

4. 用createBufferSource() 创建一个bufferSource对象,将AudioBuffer 赋值给bufferSource.buffer;

5. 用connect()把bufferSource和GainNode连接,然后再连接到音频播放节点Destination,开始播放音频;

具体代码分析如下:

1. 实例化一个音频对象;

const AudioContext = window.AudioContext || (window.webkitAudioContext as AudioContext)
this.audioContext = new AudioContext()

2. 将获取到的音频文件转成ArrayBuffer;

上传音频文件可以直接通过input file来获取,将上传的音频文件解析后上传至服务器,获取到audioUrl,上传音频这里就不多做介绍了;

async function getAudioArrayBuffer(audioUrl: string): Promise {
  const res = await fetch(audioUrl)
  return res.arrayBuffer()
}

const arrayBuffer = await getAudioArrayBuffer(audioUrl)

3. 用ArrayBuffer ,通过AudioContext 实例化一个AudioBuffer对象;

该方法涉及到一个兼容问题:iOS6上报错:Not enough arguments,iOS不知道是基于 promise 的decodeAudioData,所以此处需使用回调;

async function getAudioBuffer(arrayBuffer: ArrayBuffer, audioContext: AudioContext): Promise {
  let resolveFn
  const promise = new Promise(resolve => resolveFn = resolve)
  audioContext.decodeAudioData(arrayBuffer, resolveFn)
  return promise as Promise
}

this.audioBuffer = await getAudioBuffer(arrayBuffer, this.audioContext)

4. 创建一个bufferSource对象,将AudioBuffer 赋值给bufferSource.buffer;然后用connect连接;

this.bufferSourceNode = this.audioContext.createBufferSource()
this.volumeGainNode = this.audioContext.createGain()
this.fadeGainNode = this.audioContext.createGain()

this.bufferSourceNode.buffer = this.audioBuffer
this.bufferSourceNode.connect(this.volumeGainNode)
this.volumeGainNode.connect(this.fadeGainNode)
this.fadeGainNode.connect(this.audioContext.destination)

5. 接下来就可以开始播放了,播放前需设置音量和淡入淡出效果(如果是以默认值播放的话就不用,我这里涉及到操作,所以有预设值)

setVolume(value?: number) {
    this.volume = value ?? this.volume
    this.volumeGainNode?.gain?.value = this.volume / 100
}

setFadein() {
    this.fadein = Math.min(this.fadein, this.audioDuration / 2, 5)
    if (this.fadein === 0) return
    const waveArray = new Float32Array(2)
    waveArray[0] = 0.001
    waveArray[1] = 1
    this.fadeGainNode?.gain?.setValueCurveAtTime(waveArray, this.currentTime, this.fadein)
}

setFadeout() {
    this.fadeout = Math.min(this.fadeout, this.audioDuration / 2, 5)
    if (this.fadeout === 0) return
    const waveArray = new Float32Array(2)
    waveArray[0] = 1
    waveArray[1] = 0.001
    this.fadeGainNode?.gain?.setValueCurveAtTime(waveArray, this.currentTime + this.audioDuration - this.fadeout + 0.001, this.fadeout)
}

play() {
   if (!this.audioContext) return
   this.setVolume()
   this.setFadein()
   this.setFadeout()

   // 暂停后的播放
   this.audioContext.resume()
   // 设置播放开始时间startTime,有就设置,没有就不设置,默认为0,如需跳转至某个时间点播放,则需设置startTime值
   this.bufferSourceNode?.start(this.currentTime, this.startTime, this.audioDuration);
}

pause() {
   if (!this.isPlaying) return
   // 暂停音频上下文中的时间进程,暂停音频硬件访问并减少进程中的CPU/电池使用
   this.audioContext?.suspend()
}

resume() {
   if (this.isPlaying) return
   this.audioContext?.resume()
   this.isPlaying = true
}

这样的话一个基础的播放就实现了。

你可能感兴趣的:(JavaScript,音视频)