オンライン同期可能なシーケンサーを作った話 前編
少し前になるが、また音の出るおもちゃを作ったので記録を残す。
長くなりそうなので前編/後編に分ける。
作ったもの
- ブラウザで動くマルチトラックシーケンサ
- 作成中の楽曲をオンライン保存できる
- URLシェアにより、別のユーザと楽曲の同時編集ができる
- 編集内容は複数クライアント間でリアルタイム同期される
使ったもの
- React/Redux
- Firebase
- Cloud Firestore
- Authentication
- Hosting
- TypeScript
- Web Audio API
発表資料
モチベーションと課題
Reactで音の出るおもちゃを作るのが好きなので、Reactでシンセサイザーを作ったり、Reactでパターンシーケンサを作ったりしてきた。
今まで作ってきたものは音を出したり、パターンの変化を楽しんだりとどちらかといえば即興性の高いものだったが、「曲を作る」となると保存しておき続きを作り進められることや、他者と協力して作曲ができることが求められるだろうと考え、その検証として今回はオンライン保存、同期編集が可能なシーケンサを作るに至った。
- ブラウザ上で音を鳴らせる
- タイミング通りにシーケンス(作成した曲データ)を鳴らせる
という要件については今までの成果物で検証できていたので、今回は
- ブラウザ上でシーケンスをグラフィカルに編集できる
- 編集したものをオンライン保存できる
- 保存したものをシェアして複数のクライアントから同時編集できる
といった点にフォーカスを当てて実装を行った。
完成したものについてはデモを触ってみていただきたいが、実装を進める中で発見のあった箇所について書きたいと思う。
ピアノロールの実装
シーケンス編集の手段としては、一般的なDAW(音楽制作ソフトウェア)なら大抵備えているピアノロールを採用した。
ピアノロールはx軸で時間、y軸で音階を表現するUIだ。
探してみると、Reactでピアノロールを実装している例はいくつかあったものの、そのまま利用できそうなものがない、理想とするピアノロールと違うといった理由から、自前で実装することにした。
ちなみに、私が理想とするピアノロールは、かつて存在したOpcode社のEZ Visionというシーケンサのものなので、今回作ったものもそれに似た挙動になっている。
SVG as JSX
ピアノロールの実装はSVGをReact Componentに内包する形で実装した。
SVGのタグをJSXの中で使うことで、以下のようなメリットが得られた。
- 図形のグループをReact Componentに切り出しやすい
- グリッドのbackground patternも別コンポーネントに切り出した
- svg elementを普通に記述するとそれなりに多くのattributesを渡す必要あるが、Spread Attributesを使うとシンプルに書ける
例):
<rect {...rectProps, x } />
また、クライアント座標とは別の座標系を扱うことができるので、音楽のコンテキストにマッチした座標を使えるといったメリットもある。
- 例: 1小節あたりのclient widthが137pxといった半端な値でも、SVG内ではwidthを480とし、端数を出さずに16分割や12分割を行う(=16分音符や3連8分音符を表現する)ことができる
- client座標 -> SVG座標への変換には以下のようなメソッドを用意した
mouse2svgPoint(e: MouseEvent): SVGPoint { const pt = this.svgElement.createSVGPoint() const matrix = this.svgElement.getScreenCTM() if (!matrix) { pt.x = 0 pt.y = 0 return pt } pt.x = e.clientX pt.y = e.clientY return pt.matrixTransform(matrix.inverse()) }
FirebaseやTypeScriptについては後編で書く