音频API => AudioContext

遇到需要播放音频的需求,我们不一定要使用audio标签,还可以用音频API AudioContext来帮助我们完成。

前一段在做世界杯超级直播间时有送礼物的需求,例如下图(为了显示,这里调成了无限播放):

只有动画不够,我们需要有声音。
没有用audio,一是不想多插入一个DOM节点,二是想跟随系统的状态,即手机调成震动/静音模式了,这个声音也就不要出了。这个时候AudioContext就可以帮上忙了,代码如下。

let AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx = AudioContext ? new AudioContext() : '';

let soundBuffer = {
  getBuffer(link) {
    return new Promise((resolve, reject) => {
      if (audioCtx) {
        let request = new XMLHttpRequest();
        request.open("GET", link, true);
        request.responseType = "arraybuffer";
        request.onload = function() {
          audioCtx.decodeAudioData(request.response, function(buffer) {
            resolve(buffer)
          }, function(e) {
            console.log('reject');
            reject(e);
          });
        };
        request.send();
      } else {
        reject('not support AudioContext');
      }
    })
  },
  createSound(buffer) {
    if (audioCtx.state != 'running') {
      console.log('重启audioCtx');
      audioCtx.resume();
    }
    let analyser = audioCtx.createAnalyser();
    let gainNode = audioCtx.createGain();
    let source = audioCtx.createBufferSource();
    source.buffer = buffer;
    source.connect(analyser);
    analyser.connect(gainNode);
    gainNode.connect(audioCtx.destination);
    return source;
  }
}
export default soundBuffer;

上述的createSound方法其实没必要那么复杂,因为这里其实只是简单的加载了一个线上音频,并没有用js来生成音频,所以增益器、分析器什么之类的可以不使用,简化版如下:

  createSound(buffer) {
    if (audioCtx.state == 'suspended') {
      console.log('重启audioCtx');
      audioCtx.resume();
    }
    let source = audioCtx.createBufferSource();
    source.buffer = buffer;
    source.connect(audioCtx.destination);
    return source;
  }

注意createSound方法第一行的检测,AudioContext是有状态的
在使用中我们发现,ios手机,经常会切home键回来后,音频不播放了,查了一下文档发现

播放时进入running状态
播放完成切home键会进入interrupt状态
再回到原网页,很大概率会进入到suspended状态
这时候,无论怎么调用start 或者干脆new AudioContext()后重新调用,音频都不会再播放

AudioContext也提供了方法监听他的状态API文档 ,可以监听到挂起后就重置,但是不建议这么做,毕竟系统自动挂起是为了节约性能,我们按需重启即可。


使用上,可以在页面load后preload音频

import soundBuffer from './soundBuffer';

// ...
// preload
soundBuffer
  .getBuffer(item.sound)  // item.sound是音频线上地址
  .then(buf => {
    this.soundBuf[item.name] = buf;  // 存储,方便调用
  })
  .catch(e => {
    console.log(e);
  });


// ... 
// 使用 
let sound;
if (this.soundBuf[item.name]) {
  sound = soundBuffer.createSound(this.soundBuf[item.name]);
  sound.start(0); // 播放
} else {
  console.log("download sound failed?");
}

除了很老旧的机型之外,这个方法播放都是没有问题的,放心使用。

相关链接:
AudioContext MDN

一个音频库Tone,还没看实现github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。