【令和開幕ハッカソン】Web Audio APIを使って音声を逆再生するサービスを作ってみた

# はじめに
こんにちは。令和元年のGW10連休いかがお過ごしでしたしょうか？

私が所属する会社では「10連休だし何かサービスを作ろうぜ！」ということで
任意の仲間同士でGW中に1人1サービスを作るGWハッカソンを開催しました。大学のサークルみたいですね。

---
GWハッカソンのまとめ記事もありますので、こちらも読んでいただけたら嬉しいです 
→ https://praha-inc.hatenablog.com/entry/2019/05/15/202420

---

今回作ったサービスはこちら
<img width="329" alt="スクリーンショット 2019-06-03 21.03.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32950/ef5c7e3e-ee39-66fb-3f61-298a85cdede9.png">

HEISEI REVERSE PLAYER
https://heisei-reverse-player.kukan-tech.com/

平成の名曲を逆再生でイントロドンするWebアプリです。

この記事では「HEISEI REVERSE PLAYER」の開発経緯とその技術について共有します。

# 対象読者
- 個人開発者
- Web Audio APIをこれから触ってみたい方

# 動機
動機は単純な理由でした。

- iTunes Search APIを使って何か作りたい
- 元号が変わるシーズンなので何かそれに沿ったものを作りたい
- 平成を振り返る？
- 平成振り返りイントロドン？

# 要件
- 逆再生で音声が再生できること
- 音声のソースは著作権に違反しないこと
- Webアプリであること（※Webにこだわる理由は後述します）

# 使用したもの
- Nuxt.js(v2.6)
- Web Audio API
- iTunes Search API
- Firebase hosting

# 技術選定理由
個人的な話ですが、私個人の開発モチベーションの源泉が「人に使ってもらうこと」なので
多くの人が使えるであろう「ブラウザで動くもの」 = 「Web」 にこだわりました。

あとは弊社でNuxt.jsを採用しているので爆速開発が期待できました。

# Webで音声を再生する
Webで音声を再生する方法について紹介します。
## audioタグを使う
お馴染みののHTML5！
以下のようなコードで再生できます。

```html:audio.html
<audio src="sample/sample.mp3">
```

通常の音声再生はこれでいいのですが、今回やりたいことは「**逆再生**」です。

## Web Audio APIを使う
Web Audio APIを使えば逆再生なんてお手のものなんです。自由にエフェクトをかけられるようですね。
JavaScriptを使ってブラウザ上で音声の加工ができるんですよ！すごい！

[Web Audio APIとは](https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API)


とは言ったものの、意外とつらいんですこれが。
Chromeでは再生できるけれども、**Safariでは再生できない**なんてことが普通に起きるんです。

IE？なにそれ美味しいのって感じですね。IEではデバッグしていません。

# Web Audio APIを利用した実装で得た知見
Web Audio APIを利用した実装でハマったポイントや得た知見紹介していきます。

## ハマったポイント
### Safariで音声が再生できない
そもそもブラウザで提供しているWeb Audio APIのクラスが違うので、APIを利用する場合以下のように記述する必要があります。

```Node.js
window.AudioContext = window.AudioContext || window.webkitAudioContext
```

`webkitAudioContext` がSafariのAudio APIでした。

### Safari限定でWeb Audio APIのasync await構文が対応していない

Safariで Web Audio APIの async await構文が対応していませんでした。 then, catchを使う必要があります。
※ 後述したコードにコメント書いていますのでご確認ください。


## 得た知見
### Safariで音声の自動再生ができない
Safariのポリシーで音声を再生するにはユーザのアクションをトリガーする必要があるとのことです。
なので音声を再生する場合、ボタンを押下した時にWeb Audio APIを再生する関数を実行する必要があります。
今回はボタンを押下したタイミングで音声を再生するため問題ありませんでした。

どうしても自動再生がやりたい人はこちらを参照してください。
[Web Audio APIの闇](https://qiita.com/zprodev/items/7fcd8335d7e8e613a01f)



# Web Audio APIで音声を逆再生にするスクリプト

```Node.js
window.AudioContext = window.AudioContext || window.webkitAudioContext
const context = new AudioContext()

export const getAudioSourceWithPlay = (path, isReverse) => {
  const source = context.createBufferSource()
  // fetch APIで音声ファイルを取得
  fetch(path)
    .then(response => response.arrayBuffer())
    .then(arrayBuffer => {
      // 音声ファイルをデコード
      // SafariのdecodeAudioDataがpromise構文に対応していない
      // SafariではSuccess callbackが後から呼ばれることがあるため、source.start(0)もここで呼び出す必要がある
      context.decodeAudioData(arrayBuffer, function(buf) {
        // 逆再生
        if (isReverse) {
          Array.prototype.reverse.call(buf.getChannelData(0)) // left
          Array.prototype.reverse.call(buf.getChannelData(1)) // right
        }
        source.buffer = buf
        source.loop = false
        source.connect(context.destination)
        source.start(0)
      })
    })
  return source
}
```

# 開発課題
開発課題についてまとめておきます。

## 音声再生に時間がかかる
これは音声取得・再生のトリガーをボタン押下時にまとめてしまったため3Gなどの低速回線だとUX悪いです。
事前に取得して圧縮するなどの方法が考えられそうです。

## UIがいまいち
Vuetifyを使いましょう。

# まとめ
開発期間は3日ぐらいでした。
今回は、iTunes Search APIとWeb Audio　APIを初めて利用したので、その仕様の調査に費やす時間が長かったかなと。
手を動かした時間は1.5日ぐらいだったと思います。

HEISEI REVERSE PLAYERでした。
https://heisei-reverse-player.kukan-tech.com/


Happy Coding!