読者です 読者をやめる 読者になる 読者になる

Reactでシンセサイザーを作った話

こんにちは、nishayaです。
管理部のエンジニアとして、社内向けのシステムを作ったり、
社内で開催されるイベントでコーヒーを淹れたりしています。

社内向けだからこそできる冒険もある、ということで、
現在はReact/Reduxを用いたSPA開発を行っています。

今回のSpeeeKaigi(下記の記事を参照)では、
ReactとReduxを使ってシンセサイザーを作る話をしました。

tech.speee.jp

発表資料

使用したもの

Web MIDI APIを使用しているため、今回のターゲットブラウザはGoogle Chromeのみとしました。
そのため、webkit プレフィクス付きのAudioContextにも対応していません。

モチベーション

業務でSPA開発にReact/Reduxを使用するという決定をしたものの、
道具として使いこなせていない感があったため、
何か楽しみながら作れるものを開発して勘所を押さえようと考えました。

作りたかったもの

  • 音の出るものが作りたい
  • ただ録音された音を鳴らすだけでは面白くないので、音を合成して作り出したい
  • 音はユーザのインプットに応じてインタラクティブに鳴らしたい

どうやって作るか

  • Web Audioのノードを組み合わせてシンセサイザーを作るとしたら、それぞれをReactのComponentにすると扱いやすいのではないか?
  • 入力装置からのインプットをReduxのstate経由で通知することで、ハンドリングする側のコンポーネントに直接イベントを通知しなくてもよくなり、構成の自由度が上がるのではないか?
  • やはり楽器を作るなら、PCのキーボードではなくMIDIコントローラ(鍵盤など)を使いたい。Web MIDI APIが使えそう

解決すべき点

パフォーマンス

Reactについて、更新の伝播がうまくいかないことがあるとか、 パフォーマンスが悪いという話はなんとなく聞いていました。

楽器として機能するものにするためには、鍵盤を弾いてから発音までのレイテンシが短ければ短いほどよいし、 数十msを超えたらそれは音楽として別物になってしまうことを意味し、致命的といえるため 事前に検証しておくことにしました。

検証では、keydownイベントをトリガーにactionを発行し、 別のコンポーネントでstateの変化を検知するまでの時間を測定しました。

avg. 0.8281000000000586 ms / 50 times
avg. 0.9170500000002039 ms / 100 times
avg. 0.6555400000006857 ms / 500 times

結果としては、平均1ms以下となり、 楽器にも使えそうだということがわかりました。

適切なコンポーネント分割

行った分割は大別して2つ

  • Input/Outputを分割する
  • シンセサイザーの構成要素を分割する

詳細は発表資料を参照してください。

できたもの

発表当初から変更した点

発表当初はMIDIコントローラがないと音が鳴らせなかったんですが、
発表時に、MIDIコントローラなんてものは普通持っていないという意見が多かったので、その日のうちに

  • PCのキーボードによる演奏
  • トラックパッドによる音色変化

を追加しました。

また、週末時間があったので

  • 波形の追加
    • SuperSawなど、複数のオシレータにより太い音を出す波形
    • PeriodicWaveを用いたPWMや疑似三角波(いわゆるファミコンの音)
  • ADSR
  • Filter EG
  • ディレイ
  • ディストーション

などを追加しました。

所感

音を出したいという強いモチベーションがあったので、
不慣れなReact/Reduxを楽しみながら習得することができました。

結局一番役に立ったのは公式ドキュメントでした。
今までのフロント開発とはパラダイムがだいぶ異なるので、
とっつきにくさはあるものの、公式をくまなく読めば、
なぜそのように作るのか、どうしてこれを使うと嬉しいのかが理解できるようになっています。

また、先述のとおり、コンポーネントを分割しておいたため、
機能の追加、変更は容易で、他の部分に影響を与えず独立して行うことができ、
React/Reduxは道具として使いこなせれば気持ちよく開発を進められる技術だと感じました。

次は音の質に特化したmonophonicなシンセサイザーか、
コンポーネント指向のステップシーケンサを作ってみようと考えています。