# 概要
**オーディオ技術のないエンジニアにも, ブラウザでオーディオを扱える**というコンセプトで実装し始めた [Web Audio API ライブラリ](https://github.com/Korilakkuma/XSound). 現状, Web Audio API はある程度オーディオ処理を抽象化した JavaScript API ではありますが, (例えば, `BiquadFilterNode` のようにデジタルフィルタの詳細を知らなくても 8 種類のデジタルフィルタが利用可能となっています. ところが, 本格的なオーディオ処理をするためには, 結局のところ, オーディオ技術が必要になります (例えば, フェイザーというオーディオ処理を実装するには, 基礎的なオーディオ技術をもとに, `BiquadFilterNode` を組み合わせる必要があります) . XSound は, Web Audio API をさらに抽象化し,
- 直感的な記述で実装できる
- オーディオ技術がなくてもある程度の機能が実装できる
以上 2 つを実現したライブラリとなっています. また, 将来に渡って, 破壊的な変更を最小限にできるように, **ライブラリ・フレームワークに依存しない**実装となっています.
# 機能リスト
- サウンドの生成
- ワンショットオーディオ (ピアノの音など) の再生
- 楽曲データの再生
- WebRTC からの音声取得と加工
- MML (Music Macro Language) による自動演奏機能
- サウンドエフェクト処理
- 波形描画
- WAV ファイルの動的生成による録音機能
- WebSocket によるバイナリメッセージング機能
- Audio Sprite (オーディオの部分切り取り)
[XSound.app](https://xsound.app/) はライブラリの機能をほとんど網羅した Web アプリケーションです.
# プロダクトの内容と設計
- オーディオ信号処理の理解や Web Audio API のやや奇怪な API を抽象化し, 簡単に Web オーディオプログラミングを可能にする
- ネイティブアプリケーションの移植ではない, Web Music アプリケーションの創造をサポートしたい
- これまで, 多くの Web Audio API を利用した Web アプリケーションがネイティブアプリの移植に過ぎない
- **Web** をオーディオプラットフォームにする理由として弱い (インストール不要, クロスプラットフォーム対応などは技術的優位性とは言えない. 特に, 音楽用途の場合, 0.025 msec ~ 0.05 msec 程度の精度が求められる. それよりレイテンシーが長くなると, 音楽として成立しなくなる)
- **Web** をオーディオプラットフォームにする最大のモチベーションは, ネイティブアプリにとらわれないアイデアだと考えている (例えば, どんなに WebAssembly でネイティブに近いパフォーマンスを実現しても, Web であるが故の制限 (例えば, `SharedArrayBuffer` の複雑な制限) や, I/O 関連の最適化は難しい. 例えば, Web MIDI API などは Working Draft のまま長い間仕様すら更新されていない). そのアイデアの試行錯誤を形にするきっかけやヒントを与えることが目的
- したがって, 特定の目的があるプロダクト, 例えば, Web DAW などを作成する場合には向かない. それ専用のライブラリを使うことを推奨している (howler.js, Tone.js, Tuna.js など. たまに質問を受けることがあるが, そのように回答している)
- しかしながら, 理由としてはあとづけで, もともとは [XSound.app](https://xsound.app/) の制作において, UI とオーディオ信号処理の分離, すなわち, ビューとロジックの分離を進めているうちに, いつしか (2013 年半ばぐらいから ?) ライブラリとして再利用可能なプロダクトになっていた (ちなみに, git でバージョン管理もできていない頃の[最初期のバージョンは公開している](https://xsound.app/v201210/index.html). ソースを見るとあきらかに, UI とオーディオロジックの分離ができていない)
- 音源の抽象クラスである, `SoundModule` に必要なオーディオ処理を集約して, サブクラス (個別の音源, 例えば, オシレーターやノイズ, 楽曲データ, WebRTC からの音声など) において, 必要な処理を override, また, 個別に必要なオーディオ処理を実装 (例えば, ノイズサプレッサーなどは `StreamModule` (WebRTC からの入力音をソースとするモジュール) でしか使わないので, `SoundModule` では実装していない). これが基本設計であり, 結果として, テンプレートメソッドパターンになっている. 端的には,
- 抽象クラス `SoundModule` のオーディオ信号処理をもたせる
- サウンドソースに応じたサブクラスで必要に応じてメソッド定義
- MML (Music Macro Language) は例外的で, MML のパースと, 委譲された (DI された), `SoundModule` のサブクラスをパース結果の音楽演奏情報にしたがって, 自動演奏を実行する (また, `SoundModule` を継承しない).
## どういう考えでコードを書いたか、なにに注意したか
- 音源は異なっても (`X` 関数で取得する `SoundModule` のサブクラス), 可能な限り同一の API (メソッド) を提供する
- `setup` (アプリケーション起動時に必要な初期化処理)
- `ready` (再生前に必要な処理. スケジューリングなど)
- `start` (再生)
- `stop` (一時停止)
- `param` (パラメータの取得・設定)
- `module` (モジュールを取得して, アクティブ状態にしたり, パラメータを設定したりする)
- Web Audio API における `AudioNode` の複雑な `connect` (モジュラールーティング) を抽象化する重要なメソッド
- これらは, サブクラスごとにシグネチャが異なるので, 抽象メソッドにして実装を強制できていないのは課題
- 実装もれを人力チェックするのは少々しんどい
- `SoundModule` に共通するオーディオ処理を集約して継承する
- 一般的には, 継承より委譲のほうが結合度が低くなり, モジュールの独立性としては高くなるが, アプリケーションコードが簡易になるように継承 (テンプレートメソッドパターン) を採用している (version 1.0 から, 現在のバージョンまで変わらない)
- 委譲にすると, アプリケーションコード側で必要なモジュールを委譲 (DI) する必要があり, アプリケーション側で必要な実装が多くなる. また, 少なからず, オーディオ信号処理の理解が必要になる可能性が高くなる.
- 開発開始当時 (2012 年), 一般的に使われていた jQuery のような記述で使えるようにしたいという漠然とした理由もある
- ただし, 代償として, ライブラリ側では `SoundModule` が肥大化してしまっているので, サブクラスでボイラーテンプレートのようなコードや, ボイラーテンプレートのような単体テストがたくさんあるのは問題点としてある
## 工夫した点、苦労した点など
### アーキテクチャレベル
- 継承関係を検討する
- 例えば, `AudioModule` と `MediaModule` は同じような機能をもっているので, `MediaModule` を `AudioModule` のサブクラスにすることも考えたが, 継承の継承はさらに結合度が高くなる (モジュールの独立性が低くなる), また, 実装が増えても, アプリケーションコードには影響がない.
- 委譲で問題ないモジュールは継承させない (例 `MML` クラス)
### Recorder
- 録音データでメモリを不要に使わない (`number[]` ではなく, `Float32Array[]` に格納する)
- ミキシング処理
- 各録音ラインの, 左右チャンネルで `Float32Array[]` のトラックを, `Float32Array` にフラット化
- 各録音ラインの, チャンネルごとの合成・平均化 (オーディオ信号処理におけるインターリーブ処理)
- 量子化ビット 16 bit の場合の WAVE ファイル生成 (リトルエンディアンでデータを格納する必要がある)
- ビット演算で, 下位 8 bit を先に配列に格納
- シフト演算して, 上位 8 bit を次の要素に格納
### MML (DSL)
- 大量のパラメータをアプリケーションから与えるのではなく, 言語を与えて目的を達成する
- 正規表現の問題解決のアプローチと同じ
- 自身が初めて実装した言語処理型 (正規言語, 有限オートマントで受理可能な文字列)
- 初期の実装では, トークナイズや抽象構文木の生成はせず, 正規表現で音楽情報に変換していた
- 抽象構文木の設計
- 左部分木は常に数値
- 右部分木に進むほど, 音楽的な時間も進む (木のレベルが大きくなる)
- 一般的に, 探索に不利な木構造 (クイックソートが最悪計算量になるような木構造) であるが, 目的は (実行前の) 音楽情報への変換なので問題ではない
- むしろ, 右部分木が深くなるほど, 音楽的な時間も進んでいるという情報を表現しているので適している
- ABC 記譜法へのトランスパイル
- abc.js によって SVG による楽譜が出力できる
- ただし, このトランスパイルは正規表現による変換なので, こちらも抽象構文木を生成して変換できるようにしたい (ABC 記譜法のほうが, より多くの音楽表現ができるので, 言語仕様の拡張が十分にありうる)
- ハイライト処理. 実装自体は難しくないが, HTML としてあつかう必要があるので, パフォーマンスを考慮して, ハイライト処理を OFF にできるようにしている (マシンスペックが低い場合などを考慮).
- 同様の考えは, ビジュアライザーにもあり, Canvas や SVG への描画のアニメーションは, 少なからずパフォーマンスを落とすので, スペックの低いマシンでの利用を想定して, OFF にできるようにしている
### ScriptProcessorNode から AudioWorklet への移行
- ライブラリなので (CDN としても使えるようにしているので), 1 ファイルにまとめるためにハッキーな実装を試行錯誤 (バンドラーで `/^.+Processor$/` で終わるクラスを識別し圧縮しないようにする).
- `AudioWorkletProcessor` のサブクラスを定義して, `toString` で文字列にして, Data URL で `addModule` する.
- そのまま, バンドルすると, メインスレッドのコードとしてまとめられてしまうので, 上記のようなバンドラーの設定で解決 (できればしたくはなかったが).
### オーディオ信号処理の理解
TypeScript など Web フロントエンド関連の技術と Web Audio API を理解しているだけではここまでの実装はできないと思う. 少なからず, オーディオ信号処理を大学院での専攻としていたことが活きている
### 2012 年 10 月から現在まで
モチベーションはその時々で変わってきたが, 12 年近く, 機能追加やバグフィックス, リファクタリングを続けて, また, Web フロントエンド技術のエコシステム (npm によるパッケージ管理, TypeScript による型システム・型検査, ドキュメントの自動生成や, ESLint による潜在的なバグの autofix など) をあわせてバージョンアップを続けてきたこと.
#### 技術スタックの刷新
[Release note](https://github.com/Korilakkuma/XSound/releases/tag/v3.0.0)
- TypeScript 100%
- Enable to use ATA (Automatic Type Acquisition)
- Improve code quality
- Type check
- Use Jest instead of Jasmine & Karma
# 実績・評価
[v1.x.x のリポジトリ](https://github.com/Korilakkuma/XSound.js) のスター数と合算すると 200 を超えており, これはニッチな Web Audio API ライブラリというドメインにおいて, メジャーな部類に入ると言えます. 実際,
[9 libraries to kickstart your Web Audio stuff](https://dev.to/areknawo/9-libraries-to-kickstart-your-web-audio-stuff-460p) で, 9 つの有用なライブラリの 1 つ取り上げられており,
> XSound is a batteries-included library for everything audio. From basic management and loading through streaming, effects, ending with visualizations and recording, this libraries provides almost everything! It also has nice, semi-chainable API with solid documentation.
- オーディオ処理のほとんどすべてを実装している
- 堅実な [API ドキュメント](https://xsound.jp/docs/)がある
上記 2 点が評価されています.
また, 国内を代表するエンジニアの方からも[評価を受けています](https://twitter.com/voluntas/status/1059810428199493637).