16.6 Web Audio API(网页音频接口) 使用录音

现在你已经建立了一个振荡器,让我们现在看看它是如何来处理录音的。有些声音是很难使用振荡器来重现的,所以在许多情况下必须使用录制的声音来重现真实的声音。格式可以是`.MP3 `,` .ogg `,` .wav `等,更多信息请查看完整列表。我喜欢用`.MP3 `因为它是轻量级,有广泛支持且具有很好的音质。
你不能像图片一样简单地通过一个网址得到声音。我们需要通过发送一个XMLHttpRequest请求来得到文件,解码数据,并放入缓冲区。

class Buffer {

  constructor(context, urls) {  
    this.context = context;
    this.urls = urls;
    this.buffer = [];
  }

  loadSound(url, index) {
    let request = new XMLHttpRequest();
    request.open('get', url, true);
    request.responseType = 'arraybuffer';
    let thisBuffer = this;
    request.onload = function() {
      thisBuffer.context.decodeAudioData(request.response, function(buffer) {
        thisBuffer.buffer[index] = buffer;
        updateProgress(thisBuffer.urls.length);
        if(index == thisBuffer.urls.length-1) {
          thisBuffer.loaded();
        }       
      });
    };
    request.send();
  };

  loadAll() {
    this.urls.forEach((url, index) => {
      this.loadSound(url, index);
    })
  }

  loaded() {
    // what happens when all the files are loaded
  }

  getSoundByIndex(index) {
    return this.buffer[index];
  }

}

让我们看看构造函数: 我们收到我们在声音类中所做的Context;收到的Url列表将被加载;一个空数组用于缓冲。

我们有两个方法:loadsound和loadAll。 loadAll循环通过URL列表来调用loadSound方法。它重要的是传递索引,我们才能在不管哪个请求首先加载下把缓冲的声音放入正确的数组元素。这也让我们看到最后一个请求意味着缓冲区加载的完成。

然后你可以调用loaded()方法来做一些像隐藏加载指示器之类事情。最后在播放时通过getSoundByIndex(index) 方法从播放缓冲区中获取相应的值。

decodeAudioData方法是新的语法,现在还不能在Safari中工作。

context.decodeAudioData(audioData).then(function(decodedData) {
  // use the decoded data here
});

然后我们必须为声音创建类。现在我们有完整的类来处理录音:

class Sound() {

  constructor(context, buffer) {
    this.context = context;
    this.buffer = buffer;
  }

  init() {
    this.gainNode = this.context.createGain();
    this.source = this.context.createBufferSource();
    this.source.buffer = this.buffer;
    this.source.connect(this.gainNode);
    this.gainNode.connect(this.context.destination);
  }

  play() {
    this.setup();
    this.source.start(this.context.currentTime);
  }  

  stop() {
    this.gainNode.gain.exponentialRampToValueAtTime(0.001, this.context.currentTime + 0.5);
    this.source.stop(this.context.currentTime + 0.5);
  }

}

构造函数接收了Context和Buffer缓冲。我们调用createBufferSource()方法来代替我们以前做的createOscillator()方法。我们使用getSoundByIndex()方法来获取缓冲的音符(从缓冲数组中来的元素)。现在我们创建缓冲区(而不是振荡器),设置缓冲区,然后将其连接到目标(或增益和其他滤波器)。

let buffer = new Buffer(context, sounds);
buffer.loadAll();

sound = new Sound(context, buffer.getSoundByIndex(id));
sound.play();

现在我们要创建缓冲区实例并调用loadAll方法来加载所有的声音到缓冲区。我们同时也要用getSoundById方法来获取我们真正所需要的,把它传送给声音类并调用play()方法。ID可以作为按钮上的数据属性存储,你点击则播放声音。

这里有一个在线实例,使用了上述缓冲区、录制的音符等技术(由于音频资源较多,未完整实现,仅供参考):