音源方式
このセクションの他のページは、どれも「呼び出し方」から始まります。プリセットを割り当て、プログラムチェンジを送り、SoundFont を読み込む。このページはその一歩手前、他のページが解決済みとして扱っている 2 つの疑問から始めます。
オーディオエンジンがサンプルデータを一切持たないのはなぜか。 そしてブラウザのタブで動くものが、なぜ 1990 年代の据え置き音源モジュール向けの規格に対応しているのか。
答えは同じで、コストの判断です。
音を鳴らす 4 つの方式と、それぞれのコスト
デジタル楽器が音を作る方法は、大きく 4 通りに分かれます。違いは「どんな音が出せるか」よりも、コストをどこで負担するかにあります。聴き手がダウンロードするバイト数か、発音中にプロセッサが回す演算量か、のどちらかです。
| 方式 | データ量 | 計算量 | 表現はどこから入るか | このエンジンでの実体 |
|---|---|---|---|---|
| サンプル再生 | 上限なく増える | 少ない | ベロシティや奏法ごとに録音した素材のクロスフェード | SoundFont プレイヤー(持ち込んだサンプル用) |
| FM | ほぼゼロ | 中程度 | オペレータ比と、時間変化する変調指数 | 複数のエンジンモード。フォールバックバンクのエレピ、ベル、クラビ |
| 物理モデリング | ほぼゼロ | 多い | 弓圧、息圧、唇の張力といった物理量 | 9 種のアコースティックボイスに加え、撥弦ループ、ハープシコード、モーダル、膜鳴モデル |
| サブトラクティブ(バーチャルアナログ) | ほぼゼロ | 中程度 | エンベロープで動くフィルタのカットオフとレゾナンス | SynthPatch のアナログボイス。リード、パッド、シンセ SE |
容量を食うのは最初の 1 行だけです。サンプルライブラリの容量は音色数 × 音色あたりのレイヤー数 × 収録品質という積で決まり、この 3 つはどれも設計者が増やしたくなるものです。積が小さくなることはありません。
ピアノは調整済みです。その他の物理モデルはまだ調整・キャリブレーションを行っていません。これらはデータ不要のプレビューとして使い、残りの作業は今後のパッチリリースで進めます。
残る 3 行が持つのは少数の係数だけで、代わりにプロセッサの時間を使います。擦弦ボイスは擦弦楽器の録音ではなく、弦と、弓の固着と滑り、弦が張られた胴体をモデル化したループを 1 サンプルごとに解いたものです。表現の入り方が違うのもここから来ます。大きく録れた素材に差し替えるのではなく、弓を強く押し付ける、という入り方になります。
ブラウザではサンプルの道を選べない
1990 年代の音源モジュールとその系譜は最初の道を選び、かなり先まで進みました。音色を増やし、ベロシティレイヤーを増やし、収録品質を上げる。楽器が机の上の箱でディスクを内蔵しているなら、ギガバイト級まで無理なく走れる道です。
ブラウザはその道を選べません。 ここで配信されるのは、訪問者が音を聞く前にダウンロードする 1 つのバイナリで、その中にサンプルデータは 1 バイトも入っていません。そこでコストの置き場所を移しました。ダウンロードから外し、プロセッサに載せたわけです。
- データ — ブラウザが読み込む量
- 計算量 — 発音中のボイス 1 つあたりの処理
この 1 つの判断から、サイト上の 2 つのものが導かれます。別々の機能ではなく、帰結として読むと分かりやすくなります。
- 17 種類のエンジンモード。 音色の幅を録音の数で稼げないなら、合成方式の種類で稼ぐしかありません。各モードは素の音を作る別々のやり方で、幅はサンプルを増やすのではなく方式を複数持つことで確保されています。
- SoundFont がないときに鳴るフォールバックバンク。 MIDI ファイルは何も用意しなくても音にならなければならず、それができるのは背後にデータを持たないバンクだけです。
SoundFont プレイヤーはこれと矛盾しません。訪問者が持ち込むサンプル、つまり手元にあって、すでにダウンロード済みで、自分で読み込むと決めたバンクのために存在します。エンジンがしないのは、サンプルを同梱することです。
GS を選んだ理由
2 つ目の疑問のほうが、たいていの読者には奇妙に映ります。論旨は一行です。音色に名前を付ける手段がないとき、レンダリングするエンジンには番号体系が要る。そしてそれはすでに存在する。
エンジンにはプラグインの接続口がある。WebAssembly ビルドにはそれを置く場所がない
libsonare には、外部の楽器やエフェクトを受け入れるホスト側の接続口があります。src/host/plugin_host.h の InstrumentProvider は、素の PluginDescriptor をコア側のインスタンスに変える抽象ファクトリで、プラグイン SDK を一切名指ししないヘッダオンリーのインタフェースです。それが成り立つ理由はファイル冒頭のコメント自身が書いており、実際のブリッジは「ビルドオプションの背後でツリー外に」置かれます。macOS の Audio Unit バックエンド(src/host/backends/plughost/)がコンパイルされるのは src/host/backends/CMakeLists.txt の if(BUILD_AU_HOST) の内側だけです。このオプションはトップレベルの CMakeLists.txt で既定 OFF と宣言されており、リンクするのは AudioToolbox、AudioUnit、AVFoundation、CoreAudio です。
このフレームワークの並びを見れば、設計論を持ち出すまでもなく WebAssembly 側の結論は出ます。「プラグインが使えない」はブラウザビルドの性質であって、エンジンの性質ではありません。
プラグインがなければ、音色を名指すものがない
これは一見した以上に重い話です。どの音かという問いの答えは、普通はプラグインが持っています。セッションがプラグインを名指し、プラグインがプリセットを名指し、セッションを誰かに渡せば両方が一緒に渡る。プラグインを取り去ると 2 つが同時に壊れます。音色を選ぶための語彙がなくなり、音色を渡すための器もなくなります。
GM と GS は、まさにその問題に対する既存の答えです。プログラムナンバー、バンク、ドラムキット、パートごとの設定、エフェクトセンドは、いずれもファイルの中を一緒に運ばれる値として規定されています。これを理解するレンダラなら、その存在を知らない相手から渡されたファイルでも、意図されたものを鳴らせます。
GS は音ではなく制御プロトコルとして採用されている
ここがこのページの前半と後半をつなぐ部分で、エンジン自身が次のように書いています(src/midi/synth/docs/gs.md)。
GS is implemented here as a way to control physical-model and FM instruments, and the resemblance to a sound module stops at the wire.
(GS はここでは物理モデルと FM の楽器を制御する手段として実装されており、音源モジュールとの類似は信号線の上で終わる。)
GS を名乗ることは、特定のハードウェアの音を再現するという主張ではありません。上の表に並ぶ物理モデルと FM のボイスを指し示すための番号体系です。プログラム 41 はバイオリンを選びますが、応えるのは擦弦モデルであって録音ではなく、プロトコルはもともとそれ以上を保証していません。同じ資料はその帰結も明示しています。あるプログラムの音色がハードウェアと違うという報告は、どちら向きの違いであれ、このプロトコルに対する不具合ではありません。
どこまでが GS で、どこからが意図的に GS でないか
「GS 準拠」とだけ書いて終わると、意図的な違いがすべて見落としのように見えてしまいます。エンジンは自分の差分を 3 種類に分けており、このページも同じ 3 分類を使います。3 つは互いに置き換えできません。1 つは構造上安全なもの、1 つは理由のある決定、1 つは対応できないことの明示です。
| 種別 | 意味 | 代表例 |
|---|---|---|
| 拡張 | 仕様が何も禁じていない場所に足した能力。仕様準拠のファイルがこの能力を使うことはないからこそ安全 | モジュール全体で 1 基だったインサーションエフェクトが16 基になり、40 3u xx ブロックで指定できる(src/midi/synth/docs/gs.md、src/midi/synth/gs_address_table.h) |
| 意図的な相違 | 理由を添えて意図的に選んだ別の挙動 | パン値 00 はハードウェアではランダムだが、ここではセンターとして扱う。バウンス結果をビット単位で一致させることと乱数生成は両立しないため(src/midi/synth/gs_address_table.h の 40 1x 1C と 41 m4 rr の行)。実測機ではリズムパートのコーラスセンドが鳴らないが、ここでは鳴らす。どちらの理由も src/midi/synth/docs/gs.md に書かれている |
| 縮退 | こちら側の構成にその対象が存在しないため、受け取ったうえで捨てる | 40 4x 21 OUTPUT ASSIGN は IGNORE。理由は「libsonare renders one output pair, so there is no second one to route a part to」。50 ** ** と 51 ** ** はポートが 1 系統であるため IGNORE。どちらの理由文字列も行自身が持つもので、src/midi/synth/gs_address_table.h にある |
アドレス空間のすべてのアドレスは 4 段階のいずれかを持ち、行のないアドレスは、黙って何もしないのではなく不具合として扱われます。したがって縮退は、書かれた理由であって省略ではありません。
インサーションエフェクトの拡張
ハードウェアが許すインサーションエフェクトはモジュール全体で 1 基だけで、2 つのパートで有効にすれば両方がその 1 基に混ざります。これは機械側のリソース制限であって GS の性質ではなく、16 基持つ機械があっても仕様に反しないため、この制限は外されています(src/midi/synth/docs/gs.md)。
外しても安全なのは、拡張の置き場所によります。
- ユニット 0 は
40 03 xxのままで、意味も既定値も配置も仕様どおりです。仕様ブロック固有の 2 つのコントロールソースとセンド EQ スイッチも含みます。 - ユニット番号はアドレスのニブルそのもの。 各ユニットは
40 3u xxに並び、どれも同じ00–1Fの配置を持つため、40 30 xxは40 03 xxが書く記憶域への 2 つ目の入口になります。40 30–40 3Fの範囲はどちらの機種のパラメータマップにも行を持たないので、仕様準拠のファイルは構造上ここに触れられません(src/midi/synth/gs_address_table.h)。 - ルーティングはアドレスを増やさず、既存の値域を広げる。
40 4x 22PART EFX ASSIGN は00BYPASS と01EFX をそのままの意味で保ち、02–10がユニット 1〜15 を選びます(src/midi/synth/gs_address_table.hの40 4x 22の行)。00–10の外の値は、他の範囲外の値と同じく無視されます。
外されたのは加算ではない
「ハードウェアの挙動」という言い回しは 2 つの別々の事実をひとまとめに指しており、これを一緒くたにすることこそ、この節が防ごうとしている誤りです。
モジュール全体で 1 基はリソース制限であり、適用されません。同じユニットに割り当てられたパートは 1 つのインスタンスへ加算されるのほうはそもそも制限ではなく、これは外されていません(src/midi/synth/docs/gs.md)。2 本のギターを 1 台のオーバードライブに通せば相互変調しますが、それは機械のユニットが足りないからではなく、エフェクトとはそういうものだからです。2 つのパートを 1 基に通すファイルは、その音を狙って書かれています。したがって「加算を戻す」は「ユニット数を制限する」を意味しません。この 2 つは同じ要求の言い換えではありません。
加算から導かれる帰結が 1 つあります。ユニットの出力は 1 本の信号なので、システムエフェクトへのセンドはユニット自身のもの(40 3u 17–19)になり、各パートのものではありません。
パートごとのインサートは別の段
Sf2PartInsert は、注入されたファクトリで組み立てられるホスト側のパートインサートです。これは GS ではなく、アドレスも持ちません。 そして EFX とは直列に動きます。パート自身の段がパートのバス上で先に走り、ユニットの段は加算後に走ります(src/midi/synth/docs/gs.md)。アンプを通したギターにも、ファイルの指定したコーラスはかかります。この 2 つはチェーンであって択一ではなく、両者を同一視すると両方を読み違えます。
「16 パート」が本当に何から来ているか
16 パートという上限を「ブラウザの制約」に分類したくなりますが、そうではありません。
これは MIDI ポートを 1 系統しか受けないことから来ており、エンジン自身の理由文字列がそう書いています。旧機種が別の 16 パートを指定するために使っていた 50 ** ** と 51 ** ** は IGNORE です。理由は「libsonare receives one port, so there is no second group of parts to address」と「libsonare receives one port, so there is no second group's drum setup to address」です。00 01 xx のチャンネル別ポート割り当ても同じ理由を持ちます。3 つの行はいずれも src/midi/synth/gs_address_table.h にあります。
対象機では 64 パートは 2 つ目のアドレス空間ではなく 4 ポート × 16 であり、加えてこの機種はシングルモジュールの Mode-1 機です。00 00 7F SYSTEM MODE SET は範囲こそ受け取るものの、実際に効くのは 00 だけです(src/midi/synth/docs/gs.md)。つまり実装すべきダブルモジュールモードも存在しません。入力ポートが 1 系統、パートのグループも 1 つ。4 ポートを受けるネイティブビルドでも、同じ計算になります。
本当に WebAssembly から来ている制約
こちらが本物です。
| 制約 | 根拠 |
|---|---|
| プラグインを読み込めない | src/host/plugin_host.h の InstrumentProvider という接続口は抽象ファクトリで、ブリッジはツリー外。Audio Unit バックエンドは BUILD_AU_HOST の背後(src/host/backends/CMakeLists.txt)にあり、macOS のフレームワークを要求し、既定は OFF(CMakeLists.txt) |
| サンプルを同梱しない | 上の図に出ているバイナリサイズ。音色数 × レイヤー数ぶんの録音はダウンロードに収まらない |
| スレッドが 1 つ | このサイトは SharedArrayBuffer なしで動いており(COOP/COEP ヘッダなし)、エンジンは AudioWorklet 内の単一ヒープ |
| システムエフェクト、マスター EQ、EFX はビルドフラグ次第 | SONARE_MIDI_WITH_FX が立つのは BUILD_FX の下だけで、これがないと「the SF2 player renders dry (sends become no-ops)」(src/CMakeLists.txt)。同じブロック群は受け取られはするが無音(src/midi/synth/docs/gs.md) |
次に読むページ
このページは入り口で、細部は続く 3 ページにあります。
- 内蔵シンセサイザー — 共通の制御レイヤー、17 種類のエンジンモード、
SynthPatch、名前付きプリセットカタログ。 - GM / GS フォールバックバンク — 個々のアドレス、バンク、ドラムキット、128 プログラム全体のトーンマップ。
- 物理モデル — モデル化されたボイスそのものと、各ボイスが受け取るパラメータ。
- SoundFont プレイヤー — 持ち込んだサンプルと、モデルバンクとの組み合わせ方。
- MIDI 入力 — 以上のすべてを鍵盤から鳴らす方法。