# Web Audio API

Web Audio API 用于操作声音。这个 API 可以让网页发出声音。

## 基本用法

浏览器原生提供`AudioContext`对象，该对象用于生成一个声音的上下文，与扬声器相连。

```javascript
const audioContext = new AudioContext();
```

然后，获取音源文件，将其在内存中解码，就可以播放声音了。

```javascript
const context = new AudioContext();

fetch('sound.mp4')
  .then(response => response.arrayBuffer())
  .then(arrayBuffer => context.decodeAudioData(arrayBuffer))
  .then(audioBuffer => {
    // 播放声音
    const source = context.createBufferSource();
    source.buffer = audioBuffer;
    source.connect(context.destination);
    source.start();
  });
```

## context.createBuffer()

`context.createBuffer()`方法生成一个内存的操作视图，用于存放数据。

```javascript
const buffer = audioContext.createBuffer(channels, signalLength, sampleRate);
```

`createBuffer`方法接受三个参数。

- channels：整数，表示声道。创建单声道的声音，该值为 1。
- signalLength：整数，表示声音数组的长度。
- sampleRate：浮点数，表示取样率，即一秒取样多少次。

`signalLength`和`sampleRate`这两个参数决定了声音的长度。比如，如果取样率是`1/3000`（每秒取样3000次），声音数组长度是6000，那么播放的声音是2秒长度。

接着，使用`buffer.getChannelData`方法取出一个声道。

```javascript
const data = buffer.getChannelData(0)
```

上面代码中，`buffer.getChannelData`的参数`0`表示取出第一个声道。

下一步，将声音数组放入这个声道。

```javascript
const data = buffer.getChannelData(0)

// singal 是一个声音数组
// singalLengal 是该数组的长度
for (let i = 0; i < signalLength; i += 1) {
  data[i] = signal[i]
}
```

最后，使用`context.createBufferSource`方法生成一个声音节点。

```javascript
// 生成一个声音节点
const node = audioContext.createBufferSource();
// 将声音数组的内存对象，放入这个节点
node.buffer = buffer;
// 将声音上下文与节点连接
node.connect(audioContext.destination);
// 开始播放声音
node.start(audioContext.currentTime);
```

默认情况下，播放一次后就将停止播放。如果需要循环播放，可以将节点对象的`looping`属性设为`true`。

```javascript
node.looping = true;
```

## 过滤器

Web Audio API 原生提供了一些过滤器（filter），用来处理声音。

首先，使用`context.createBiquadFilter`方法建立过滤器实例。

```javascript
const filter = audioContext.createBiquadFilter();
```

然后，通过`filter.type`属性指定过滤器的类型。

```javascript
filter.type = 'lowpass';
```

目前，过滤器有以下这些类型。

- lowpass
- highpass
- bandpass
- lowshelf
- highshelf
- peaking
- notch
- allpass

然后指定过滤器的频率（frequency）属性。

```javascript
filter.frequency.value = frequency
```

最后，过滤器实例连接节点实例，就可以生效了。

```javascript
sourceNode.connect(filter);
```