ブラウザで動く波形編集アプリをReactで作った
リンク
以下の URL から登録不要で起動できます。
目指したもの
誰でも直感的に操作がわかる波形編集アプリ。
機能
- 複数ファイルのインポート、結合
- フェード
- クリップごとの音量調整
- クリップの分割・長さの調整
- コンプレッサー
- EQ
- リバーブ
- VU メーター
- wav ファイルでのエクスポート
技術スタック
- Typescript
- React
- styled-components
- zustand
- Tone.js
- Netlify
React とか styled-components について言うことは特に無いので、zustand と Tone.js のみ所感を書きます。
zustand - 状態管理ライブラリ
zustand は redux のような状態管理ライブラリです。ただ redux と比べて以下のような特徴があります。
- flux アーキテクチャのような縛りがなく、ボイラープレートコードが少ない。
- ストアはただのグローバルオブジェクトみたいなイメージで、なんでも放り込める(それが良いかは別として)。
- selector は redux 同様で柔軟に書ける。
- Provider で囲む必要がない。
シンプルに、サクッとグローバルステートを管理するぶんにはかなり便利に感じます。それでいて selector や購読を使いこなせばパフォーマンスチューニングもしっかりできる。
ただ、そうとうに柔軟なことができてしまうので、大人数で大規模な開発をする場合は向かないかもしれません。
Tone.js
Tone.js には全面的にお世話になっています。
Web アプリでオーディオを扱う以上、WebAudioAPI を使うことになるわけですが、Tone.js はさらに高レベルの API を提供してくれます。
たとえば、生の WebAudioAPI では AudioContext
の持つクロックを使用して音源の再生タイミングをスケジューリングするんですが、これはコンテクスト作成時からひたすら加算されていく時間に過ぎません。音楽的な「拍」の概念はありません。
Tone.js の Transport
はその無機的なクロックをいい感じにラッピングして、音楽的なタイミングでの再生をサポートしてくれます。リピートや、音源を途中から再生したときの処理もよしなにしてくれます。
ただ一点引っかかったのは、3 バンドイコライザの実装である EQ3
の音質がかなりアレだということです。github の issue としても挙がっていましたが、全バンドを0にしていてもカットオフ周波数あたりが相当に削れているようです。メンテナによると仕様らしいので、僕の使い方がどこか間違っていたのか・・・? Fono では Biquad Filter を使って自前の実装をしています。
TODO
波形のレンダリング
オーディオ波形は canvas に描画しています。wavesurferの実装を参考にしたんですが、パフォーマンス不足によりズームやクリップ長の変更時にガクガクします。別に javascript の限界というわけではなく、他の波形編集アプリを触ってみるとスムースに動いているので、ここは改善したいです。
エフェクト
エフェクトの種類もそうですが、範囲指定で適用できるようにしたいですね。
タイムラインの表示
拡大・縮小してもつねに 1 秒単位で刻まれているのをなんとかしたい。
Discussion
wavのエクスポートはTone.jsの機能ですか?
Web Auido APIでミックスしたオーディオファイルをエクスポートする方法をググると、録音する方法ばっかり出てくるので、「一々音を最初から最後まで再生して録音することなく、エクスポートできてる!」と思って感動しました。
AudioBuffer
からwavファイルを生成するのは以下のようなスクリプトで行っています(このブログを参考にしました)。ただマスターに接続されているエフェクトや音源をすべて書き出したい場合は、
OfflineContext
にバックグラウンドでレンダリングして、そのバッファーをソースにする必要があります。しかもプレイバック用のノードをそのまま使えるわけではないので、常に同期しておくか最後にクローンしなければいけないので結構面倒です。(という理解ですが、間違っていたらすみません・・・)OfflineContext
自体はWebAudioAPIに存在していますが、これもTone.jsを使うといろいろな便利機能が使えます。