スマートフォンを楽器にしてみた

笛吹きエンジニアの@mottox2です。

皆さん、クソアプリつくっていますか？

僕はこれまでつくっていませんでした。ということで今回は初めてのクソアプリということでスマートフォンを楽器にしてみました。

https://tone.mottox2.com

この記事は[クソアプリ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/kuso-app)の21日目の記事です。

## つくったもの
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">スマホの傾きと連動して音が鳴るクソアプリを作りました。 <a href="https://t.co/Zq3kb1THhy">https://t.co/Zq3kb1THhy</a> <a href="https://t.co/ejYJAuU6Fe">pic.twitter.com/ejYJAuU6Fe</a></p>&mdash; もっと@締切駆動 (@mottox2) <a href="https://twitter.com/mottox2/status/1076012148285878272?ref_src=twsrc%5Etfw">December 21, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## なんなのか？

みなさん、こういう楽器はご存知でしょうか？[^2]
これはテルミン（テレミン）といって世界初の電子楽器らしいです。[^1]手を調整することで音程と音量が操作できるとっても難しい楽器です。

[^1]: Wikipediaより: https://ja.wikipedia.org/wiki/%E3%83%86%E3%83%AB%E3%83%9F%E3%83%B3
[^2]: 「いらすとや」さんからです。どんな絵でもあってびっくりです。

![image.png (102.2 kB)](https://img.esa.io/uploads/production/attachments/6967/2018/12/21/4651/547d26c1-696d-48b2-9a62-b1c12d359c5f.png)

クソアプリカレンダーの皆さんは簡単に使えるようにしている人が多かったので、これをWebで再現してみました。

## クソポイント
Twitterの動画を見てくれた方はわかるかもしれませんが、難しくて曲にならないというのがクソポイントです。
2日でつくったのでクソコードだったり、各所に雑な箇所も残っていたりするのもクソポイントです。

**要するにクソアプリ**なわけです。

## 技術的なお話
このアプリの仕組みをざっくりいうと、**スマホのジャイロセンサーで周波数を決定し、Web Audio APIでそのジャイロセンサーに基づいた周波数の音を出す**という内容です。

### ジャイロセンサー
![Google Developers](https://developers.google.com/web/fundamentals/native-hardware/device-orientation/images/beta.png?hl=ja)
[ICSさんの記事](https://ics.media/entry/4095)を参考に今回は[Device Orientation Event](https://w3c.github.io/deviceorientation/)を利用しました。
これで端末の向きが変わったときにイベントが飛んでくるので、向きと周波数が連動するような処理を書きました。

Webエンジニア7年目なのですが、初めてセンサー類の処理を扱いました (;´д｀)…

センサーのデバッグについていうとChrome Developersにもセンサーの値をいじる方法があります。
ですが、本アプリの性質上、常に値が変わる想定のアプリケーションではあまり有効なデバッグ方法ではありませんでした。

<img width="509" alt="スクリーンショット 2018-12-21 2.43.37.png (94.2 kB)" src="https://img.esa.io/uploads/production/attachments/6967/2018/12/21/4651/10e6265a-4521-446f-9388-10f572dac44e.png">

そこで、AndroidのUSBデバッグを介したChrome DevToolsのデバッグを行いました。
これは普通のスマホWebをデバッグする際にもよく使われる手法ですが、センサー類のデバッグも同時にできるので非常に有用な方法でした。
iPhoneのデバッグもSafariで同様に出来ます。SafariはForm周りで辛い動作があるのでよくお世話になっています。スマホWebで消耗している人で知らない人は絶対に知っておいたほうがいいです。

<img width="1424" alt="スクリーンショット 2018-12-21 2.45.45.png (925.5 kB)" src="https://img.esa.io/uploads/production/attachments/6967/2018/12/21/4651/9e99ea87-17b0-4795-a30a-1fe2eaabd886.png">

#### つまりポイント
センサーに対応していない端末の検知につまずきました。[Google Developers](https://developers.google.com/web/fundamentals/native-hardware/device-orientation/?hl=ja)的には`window.DeviceOrientationEvent`を利用すればよいという記述がありますが、少なくともPC Chromeではtrueになってしまいます。
妥協案としてUserAgentを用いてスマホかPCかを判定することにしました。いい解決案を知っている人がいれば教えてください。

### Web Audio APIで音を出す
<img width="650" alt="スクリーンショット 2018-12-21 1.53.57.png (127.6 kB)" src="https://img.esa.io/uploads/production/attachments/6967/2018/12/21/4651/46dc64f1-ea51-4db9-b4e4-dd9e630cd496.png">

Web Audio APIといっても直接使うのは大変なので[Tone.js](https://tonejs.github.io/)というライブラリを使って音を出すようにしました。
例えば以下のようなコードで「ド」（英語やドイツ語では`C`がド）の音を鳴らせます。簡単ですね。


```js:サンプルコード
const synth = new Tone.Synth().toMaster()
synth.triggerAttack('C4')
```

この出した音に`setNode`というメソッドが生えているので、ジャイロセンサーをもとに計算した周波数を引数として渡すと出したい音程の音がなります。

```js:出したい周波数の音を出すコード
const synth = new Tone.Synth().toMaster()
const monophonic = synth.triggerAttack('C4')
// frequencyはジャイロセンサーをもとに計算した周波数
monophonic.setNote(frequency)
```

これでスマートフォンの傾きが変化すると、音程が変わるようになりました。

ただ音階は線形ではないので、音程が均等になるようにしたかったのですが、線形に変化するようになっています。クソアプリだからしょうがない。

#### つまりポイント
Web Audio APIにはAutoplay Policyといって問答無用で音を鳴らそうとするとブラウザに再生を阻害されます。
Chrome71からなので今月からそのポリシーが適用されたらしいです。
Tone.jsのStable版では対応しておらず音がなりませんでした、1時間調べた結果、開発中のdev版を利用することで回避出来ました。

### [WIP] 周波数から音階を表示する
吹奏楽部の人がこういう機械をもっていませんでしたか？この機械はチューナーといい音程がどれぐらい正しいかを確認する機械です。
ピアノを見ているとドレミファソラシド（とその半音）の音しか認識出来ません。[^3]しかし、ドとド＃、ミとファの中間の音など中途半端な音が存在しており、歌や楽器を演奏しているときは正しい音が出ているか確認する必要があります。
正しい音を確認するためにチューナーで音が正しいかを確認します。

[^3]: ピアノでもずっと使っていると音がずれていきます。このズレを直す調律師という職業も存在します。かっこいいですよね！

![チューナー](https://3.bp.blogspot.com/-cDxEGk5Uw04/W64Dyfi4kXI/AAAAAAABPJs/ruZIaYFPyfsh9ISAjXK3nSBuCijV8J_SQCLcBGAs/s400/music_tuner.png)

今回のアプリケーションでは簡単に中途半端な音を出すことが出来ます。
その音が正しいのか、正しくないのかを簡単に表示できるインターフェースを用意する必要がありました。

[TODO] 音階と周波数の関係を書く

### create-react-app + TypeScript
自分はReactをよく使うので、プロトタイプを作るときはcreate-react-appというfacebookが公開しているボイラープレートを展開してくれるCLIをもとにReactアプリケーションを立ち上げます。
また、create-react-appはここ最近TypeScriptのテンプレートにも対応したのでより便利になっています。
次のコマンドで立ち上げられるのでReact + TypeScriptに興味がある方はぜひお使いください！

```bash
npx create-react-app webtone --typescript
```

### Netlifyでデプロイ
Netlifyは2018年において無料でGitHub上のサイトを公開する最も便利な手段です。
GitHubやBitBucketのリポジトリと連動して、静的サイトをデプロイできるプラットフォームです。

独自ドメインもNetlify上で登録しており、それと紐付けることでデプロイが終わりました。
3分もかからずデプロイが出来ました。サーバーサイドが必要ないものは迷わずNetlifyに上げるようにしています。

他のCDNと比較して配信が遅いと言われがちですが、速度が重要なわけではないのでNetlifyで十分だと思っています。自分が速度と立ち上げ速度を求めるならFirebase Hostingを選ぶ気がします。

## 感想
2日前までは別のものをつくっていたのですが、開発者向けのもので一般の人が使うものではありませんでした。
いろんな人のカレンダーを見ていくうちに、誰でも使えるものがいいなと思い今回のものを作ることにしました。
開発期間が2日なので荒い場所もあるのですが多めに見てくれると嬉しいです。