【Web Audio API】OfflineAudioContextを用いたブラウザでの音声再生

# 概要
AudioContextインターフェイスの一種である[OfflineAudioContext](https://developer.mozilla.org/ja/docs/Web/API/OfflineAudioContext)を用いて、API経由で取得した音声データ(バイナリ)をブラウザで再生するところまでのサンプルコードとなります。

# メリット
OfflineAudioContextを使用するメリットとしては、以下の点が挙げられます。
### 音声処理の向上
スピーカではなく、AudioBufferに出力することでバックグラウンドでの処理がとても速いとのこと

>Offline/background audio processing
It is possible to process/render an audio graph very quickly in the background — rendering it to an AudioBuffer rather than to the device's speakers — with the following.
OfflineAudioContext

https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API
# OfflineAudioContextを使用する場面
OfflineAudioContextを使用する場面は下記のことが考えれらます。
### バックグラウンドで音声のレンダリング処理をしたいとき
音声をリアルタイムに再生する必要はない時、事前にレンダリングしておいて必要な時に再生する場面

### 音声の品質を確保したいとき
リアルタイムに処理しないため、システム負荷などの環境に左右されないため

# サンプルコード(React)
APIリクエストにはFetchの代わりに[Axios](https://www.npmjs.com/package/axios)を使用しています。
```typescript:AudioPlayer.tsx
import React from 'react';
import playAudioData from './playAudioData';

const AudioPlayer = () => {
  return (
    <>
      <button onClick={playAudioData}>音声を再生</button>
    </>
  )
};

export default AudioPlayer;

```
```typescript:playAudioData.ts
import axios from "axios";

const playAudioData = async () => {
  try {
    const audioContext = new AudioContext();
    const offlineCtx = new OfflineAudioContext(2, 44100 * 40, 44100);

    //APIから音声データ(バイナリ)を取得
    const response: AxiosResponse = await axios.get("URL", {data}); //リクエスト先に応じて書き換えてください。
    const audioData: ArrayBuffer = audioData.body;

    //音声データを処理してAudioBufferに出力
    const decodedBuffer = await audioContext.decodeAudioData(audioData);
    const source = new AudioBufferSourceNode(offlineCtx, {
      buffer: decodedBuffer,
    });
    source.connect(offlineCtx.destination);
    source.start();

    // AudioBufferに保存された音声データを再生する
    offlineCtx.startRendering()
    .then((renderedBuffer) => {
      const song = new AudioBufferSourceNode(audioContext, {
        buffer: renderedBuffer,
      });
      song.connect(audioContext.destination);
      song.start();
    })

  } catch (error) {
    console.log(error)
  }

};

export default playAudioData;
```

音声を再生を押すと、APIが取得した音声データを再生します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4017964/ccd551fb-f519-4f4d-912d-eca8fbc3c004.png)

比較としてOfflineAudioContextを使用しないコードも載せておきます。
```typescript:playAudioData.ts
const playAudioData = async () => {
  try {
    const audioContext = new AudioContext();
    
    const response = await axios.get("URL", {data});
    const audioData = response.body;
    
    const audioBuffer = await audioContext.decodeAudioData(audioData);
    const source = audioContext.createBufferSource();
    source.buffer = audioBuffer;
    source.connect(audioContext.destination);
    source.start(0);
  } catch (error) {
    console.log(error)
  }

};

export default playAudioData;
```

# まとめ
OfflineAudioContextを用いて再生するサンプルコードを紹介いたしました。今回のサンプルコードは音声データ処理と音声再生処理を一つのメソッドにして実行してしまっていますが、音声を事前にレンダリングしておきたい場合にはメソッドを分けて実装することで、メリットの恩恵を受けれるかと思います。

# 参考
https://developer.mozilla.org/ja/docs/Web/API/OfflineAudioContext

https://github.com/mdn/webaudio-examples/blob/main/offline-audio-context-promise/script.js