GM/GS フォールバックバンク
このページは内蔵シンセサイザーの General MIDI/GS 側を扱います。データ不要の GM フォールバックバンクと、その GS バリエーション音色・ドラムキットバリエーションを扱います。バウンスが GM プログラムチェンジに追従する仕組み、SoundFont プレイヤーが実装する GS のアーキテクチャ層と挿入エフェクト、SoundFont からバンクへ落ちる条件も扱います。プログラムごとの音色一覧は GM 音色マップ にあります。
GS は Roland が定めた General MIDI の拡張規格です。ここにある内容はすべて独立した再実装であり、libsonare はいかなるハードウェアメーカーとも提携・承認関係になく、その音色は特定のモジュールの音を再現するものではありません。
ここに置いてあるのは GS の具体的な側面(アドレス、各種の表、スロット単位の詳細)です。ブラウザで動くオーディオエンジンがなぜ GS に対応しているのかは音源方式にまとめてあります。仕様と食い違う箇所が拡張・意図的な相違・縮退のどれにあたるのかは後述します。
GM フォールバックバンク
GM フォールバックは、最後の手段として単純なサイン波を鳴らすだけのバンクではありません。SoundFont が未読み込み、または一部のプログラムを持たないとき、NativeSynth は要求された GM プログラムに近い内蔵の合成音源を選びます。ピアノは較正済みのエンジン、そのほかのアコースティック楽器は物理モデル、エレピとシンセブラスは FM、シンセのリード・パッド・エフェクトは減算合成のオシレーターで鳴ります。ドラムは GM/GS ドラムキットのバリエーションへ送り、利用可能な場合は GS EFX を内蔵インサートチェーンへルーティングします。128 プログラムそれぞれのエンジンは GM 音色マップにあります。目的は、データ不要のプレビューと欠けたプログラムのカバーであり、完成済みのサンプル楽器並みのリアリティではありません。どのエンジンが較正済みかは内蔵シンセサイザーに書いてあります。
あらかじめ知っておきたい点が 1 つあります。bourdon や trumpet-rank のような名前付きのパイプオルガン音色は名前付きプリセットカタログにのみ存在し、GM プログラムルーティングには現れません(プログラム19は教会オルガンのフルーパイプで、プログラム20-23はフリーリードのリードオルガン、アコーディオン、ハーモニカ、バンドネオン(タンゴアコーディオン)です)。
初学者向けに言い換えると、正確な音色や本番向けのサンプル楽器が必要なら SoundFont を使い、軽量で常に鳴るプレビューや欠けたプログラムの保険には NativeSynth フォールバックを使う、という使い分けです。
GS バリエーション音色
GM のプログラム番号が選ぶのはキャピタル音色です。GS と GM2(General MIDI の後継改訂版。MIDI の基礎を参照)はどちらも、プログラムチェンジの前に Bank Select を送ることで、そのキャピタルのバリエーション(ワイドピアノ、デチューンオルガン、12 弦ギターなど)を選ぶ手段を用意しています。フォールバックバンクは、17 のキャピタルプログラムのもとに 30 のバリエーションを音色として持ちます。このページのプログラム番号は 0 始まりです(0 から数えるか、1 から数えるか)。
各バリエーションは1 つの音色に対して 2 つのアドレスを持ちます。GS のバンクセレクト MSB 番号と、GM2 の番号(GM2 のメロディ用 MSB 121(0x79)と一緒に送るバンクセレクト LSB)です。そのため、GS 向けに作られたファイルと GM2 向けに作られたファイルは同じ音色で鳴ります。片方が相手の規格の番号を使ったせいでキャピタルに落ちる、ということが起きません。
| キャピタルプログラム | バリエーション | GS MSB | GM2 LSB(MSB 121) |
|---|---|---|---|
| 0 Acoustic Grand Piano | ワイド | 8 | 1 |
| 0 Acoustic Grand Piano | ダーク | 16 | 2 |
| 1 Bright Acoustic Piano | ワイド | 8 | 1 |
| 2 Electric Grand Piano | ワイド | 8 | 1 |
| 3 Honky-tonk Piano | ワイド | 8 | 1 |
| 4 Electric Piano 1 | デチューン | 8 | 1 |
| 4 Electric Piano 1 | ベロシティ切り替え | 16 | 2 |
| 4 Electric Piano 1 | 60年代 | 24 | 3 |
| 5 Electric Piano 2 | デチューン | 8 | 1 |
| 5 Electric Piano 2 | ベロシティ切り替え | 16 | 2 |
| 6 Harpsichord | カップルド(8′+4′ オクターブ) | 8 | 1 |
| 6 Harpsichord | ワイド(2 クワイアのステレオ) | 16 | 2 |
| 6 Harpsichord | キーオフのジャックノイズ | 24 | 3 |
| 11 Vibraphone | ワイド | 8 | 1 |
| 12 Marimba | ワイド | 8 | 1 |
| 14 Tubular Bells | チャーチベル | 8 | 1 |
| 14 Tubular Bells | カリヨン | 9 | 2 |
| 16 Drawbar Organ | デチューン | 8 | 1 |
| 16 Drawbar Organ | 60年代 | 16 | 2 |
| 16 Drawbar Organ | Organ 4 | 32 | 3 |
| 17 Percussive Organ | デチューン | 8 | 1 |
| 17 Percussive Organ | Organ 5 | 32 | 2 |
| 19 Church Organ | フルートレジストレーション | 8 | 1 |
| 19 Church Organ | フルオルガン | 16 | 2 |
| 21 Accordion | イタリアンチューニング | 8 | — |
| 24 Acoustic Guitar (nylon) | ウクレレ | 8 | 1 |
| 24 Acoustic Guitar (nylon) | キーオフノイズ | 16 | 2 |
| 25 Acoustic Guitar (steel) | 12 弦 | 8 | 1 |
| 25 Acoustic Guitar (steel) | マンドリン | 16 | 2 |
| 40 Violin | スローアタック | 8 | 1 |
イタリアンアコーディオンを GS 専用としているのは意図的です。GM2 はプログラム 21 のバリエーション 1 をフレンチアコーディオンに割り当てており、それはキャピタルがすでに鳴らしているドライなチューニングです。GM2 のアドレスをここで採用すると、2 つの規格が互いに矛盾することになります。
どのバリエーションも指さないバンク番号でも音は鳴る
この表にないバリエーションを指す Bank Select の値は、キャピタル音色へ解決されます。これは、持っていないバリエーションを要求されたときに実機のモジュールがする動作と同じです。バンクがまだ持っていない音色を要求したせいでファイルが音を失うことはありませんし、あとからバリエーションを追加しても、変わるのはすでにそれを要求していたファイルだけです。
各バリエーションは、別録りの音源ではなくキャピタル自身の物理モデルから音色を作ります。そのため、キャピタルの音色を作り直すとバリエーションもそれに追随し、取り残されることがありません。3 つのピアノのキャピタルがグランドのワイド音色を共有せず、それぞれ自前のワイドバリエーションを持つのもこのためです。共有すると、どれかがキャピタルより鈍く、小さく、あるいは音程が整いすぎた「バリエーション」になってしまいます。
下の試聴はキャピタルを 1 つ(プログラム 16 のドローバーオルガン)に絞り、バンクが鳴らし分けている 3 つのバリエーションを並べています。3 つだけにしているのは、バリエーションの実体が無いバンクセレクト MSB はキャピタルへ解決され、ビット単位で同一のバッファを返すからです。このプログラムでは MSB 24 がその一例で、音が変わらない選択肢は聴き比べになりません。
drum-kit プリセットと GM ドラムマップ
drum-kit は percussion エンジンを選び、入ってくる MIDI ノートを General MIDI ドラムマップへ割り当てます。ノート番号をピッチとして扱うのではなく、ノート 36 はキック、ノート 38 はアコースティックスネア、というように対応づけます。ドラムパターンのノートを drum-kit をバインドしたデスティネーションへ送ると、各ノートが対応するパーツを鳴らします。
各規格のドラムキットの選び方
このプレイヤーでは 4 つの慣習が出会い、それぞれ別の方法でキットに届きます。
| 慣習 | ドラムパートになる条件 | キットを選ぶもの |
|---|---|---|
| General MIDI | MIDI チャンネル 10(インデックス 9)に固定 | なし(標準キットが 1 つだけ) |
| GS | 既定はチャンネル 10。SysEx(「リズムパートとして使う」)で任意のパートをリズムパートに切り替えられる | そのリズムパートへのプログラムチェンジ。バンクセレクトは使わない |
| GM2 | バンクセレクト MSB 120(0x78) | プログラムチェンジ |
| SoundFont 2 ファイル | バンク 128 に置かれたプリセット | バンク 128 の中のプリセットのプログラム番号 |
SF2 の行は、ファイルの中でキットが置かれる場所を示すもので、ワイヤー上のメッセージではありません。プレイヤーはドラムパートと GM2 の MSB 120 を、読み込んだ SoundFont のバンク 128 へ対応づけます。マニフェストがドラムパートをバンク 128 として報告するのはこのためです。
GS / GM ドラムキットバリエーション
drum-kit は GS 系のドラムキット選択(GS は General MIDI の拡張セット。キットはバンク 128 のリズムパートのプログラム番号で指定します)も認識し、ノートオンの時点で Standard キットをセットごとに作り変えます。Room ならシェルの鳴りを増やし、Power なら音を下げて長くする、といった具合です。
各行には 2 つの番号が並びますが、これらは入れ替えて使えません。プログラムはファイルが送る番号で、リズムパートのプログラムチェンジ番号、つまり規格が定めるアドレスです。インデックスはこのバンク内部のスロット番号です。インデックスは追加のみで、あとから追加されたセットは空いている次の番号を取ります。そのため、セットを追加しても既存のセットの番号が付け替わることはなく、すでに正しく鳴っていたものが別の音になることもありません。
トーンマップの列は、そのセットを最初に定義した世代を信号線上の番号(1 が最も古く、4 が最新)で示したもので、それを選ぶ手段がバンクセレクトです。このメッセージの 2 つのバイトは別々の役割を持ち、役割が重なることはありません。バンクセレクト MSB がバリエーション音色を選び、バンクセレクト LSB がトーンマップを選びます。LSB にバリエーション番号を書いても、選ばれるのは音色ではなく世代です(GM2 は自前の 2 つの MSB のもとで LSB をバリエーション番号として使い回します。その例外は後述の GS アーキテクチャ層にあります)。
要点だけ言えば、LSB 0 はモジュール自身のマップを鳴らし、1〜4 は 4 つの世代のどれかに固定します(古い順)。
「モジュール自身のマップ」がどの機種を指すか
エンジンが対象とするのは最新世代のモジュールで、そのマップがマップ 4 です。古い世代のファイルは LSB で自分の世代を選べば(マップ 3 のファイルなら 3 を送ります)、何も失わずに鳴ります。
トーンマップの違いが音に出るのは、選んだマップにそのキットが無いときだけです。下の表の 26 セットのうち 16 セットは後発のマップで追加されたものなので、古いマップを指定するとそれらは Standard へ落ちます。その世代の実機がする動作そのままです。メロディック側は逆で、バンクが鳴らし分けているバリエーションはすべて、どのマップからも選べるマップ 1 の音色です。だからこそ LSB だけを変えても上のバリエーション表では何も動かず、下のキット表では大きく動きます。
どのセットも、共有された 1 つのパーカッションモデルを作り直したものであり、モデルの複製ではありません。キック、スネア、タム、ハット、シンバルのパラメータがノートオン時に作り変えられます。つまり、土台のモデルを良くすれば 26 セットすべてが同時に良くなり、逆にセットどうしの差はモデルがパラメータを持つ範囲にとどまります。
| プログラム | インデックス | GS 名 | トーンマップ | Standard との音色差 |
|---|---|---|---|---|
| 0 | 0 | Standard | マップ 1 | — |
| 8 | 1 | Room | マップ 1 | シェルの鳴りが増え、残響の尾が長くなる |
| 16 | 2 | Power | マップ 1 | シェルが大きく/低く/長くなる |
| 24 | 3 | Electronic | マップ 1 | サイン波化し、乾いた膜音になる |
| 25 | 4 | Analog Machine(GM2: Analog) | マップ 1 | 減衰サイン波のキック、単一音のスネアとタム |
| 32 | 5 | Jazz | マップ 1 | タイトで高め、柔らかい |
| 40 | 6 | Brush | マップ 1 | スネアが持続するスウィッシュ音になる |
| 48 | 7 | Orchestra | マップ 1 | 膜/シンバルの尾が長くなる |
| 56 | 8 | SFX | マップ 1 | ワンショットのセット(Standard の音色で鳴る) |
| 127 | 9 | Legacy Map | マップ 1 | 短く薄く明るい、GS 以前のドラムマップ |
| 1 | 10 | Standard 2 | マップ 2 | より乾いてタイトな響き。スナッピーが強め |
| 26 | 11 | Dance | マップ 2 | サイン波キック、クラップ寄りのスネア、詰めたハット |
| 49 | 12 | Ethnic | マップ 2 | ハンドドラム。リーム寄りを叩き、シェルは薄い |
| 50 | 13 | Kick & Snare | マップ 2 | 動くのはキックとスネアだけで、残りは Standard |
| 57 | 14 | Rhythm FX | マップ 2 | ワンショットのセット(Standard の音色で鳴る) |
| 2 | 15 | Standard 3 | マップ 3 | 中心を外して叩き、より開いた響き |
| 9 | 16 | Hip Hop | マップ 3 | 低く短く、押しつぶした質感 |
| 10 | 17 | Jungle | マップ 3 | すべてを早めに切り、明るい方向へ寄せる |
| 11 | 18 | Techno | マップ 3 | 完全に合成的な膜と、硬く明るい高域 |
| 27 | 19 | Rhythm Box | マップ 3 | フィルタードノイズのチック。スナッピーのないスネア |
| 28 | 20 | Compact Machine | マップ 3 | 薄く金属的で、アナログ系セットの中で最も胴鳴りが少ない |
| 29 | 21 | Digital Machine | マップ 3 | アナログではなくサンプル。歯切れよく乾いて短い |
| 30 | 22 | Hybrid Machine | マップ 3 | 長く減衰するサイン波キックの上にクリック |
| 52 | 23 | Asia | マップ 3 | ゴングや太鼓。大きく低く、長く鳴る |
| 53 | 24 | Cymbal & Claps | マップ 3 | ワンショットのセット(Standard の音色で鳴る) |
| 58 | 25 | Rhythm FX 2 | マップ 3 | ワンショットのセット(Standard の音色で鳴る) |
ワンショットのセットはアドレスのみで未モデル化
4 つのリズムセットがあります。SFX(プログラム 56)、Rhythm FX(57)、Cymbal & Claps(53)、Rhythm FX 2(58)です。これらは実機の GS では、音色を作り変えたキットではなく、個別のワンショット録音を集めたバンクです。膜モデルが作り変える対象がそもそも存在しないため、プレイヤーはアドレスと名前を認識しつつ、フォールバックマップは 4 つとも Standard キットの音色へ通します。これはデータ不要のフォールバックだけの制約です。これらのアドレスに実際のサンプルを持つ SoundFont を読み込めば、SF2 プレイヤー経由で通常どおり再生されます。GM のサウンドエフェクトプログラム(120-127)は別で、それぞれがノイズ系の固有の音色を持ちます(GM 音色マップを参照)。
下の試聴は、同じグルーヴを 1 小節、いくつかのセットで鳴らします。キットの選び方は GS ファイルと同じで、リズムパートのプログラムチェンジです。表の全セットではなく抜粋になっているのは、全セットをレンダリングして Standard とビット単位で同一になったものを外して作ったからです。上に挙げた 4 つのワンショットプログラムは、まさに設計どおりそうなります。この線引きのためにホスト側でレンダリングする必要はなく、下の問い合わせ関数がその答えを返します。
どのセットが鳴らし分けられているかを問い合わせる
プログラムチェンジやバンクセレクトで実際に音が変わるスロットはどれか、という問いにはエンジン自身が答えます。ピッカーの注記は上の表から書き写すのではなく、3 つの問い合わせ関数から計算してください。WASM/Node では synthGsDrumKitName、synthGsDrumKitIsVoicedApart、synthGsVariationIsVoicedApart で、C ABI と Python にも同じ 3 つがそれぞれの命名規則で用意されています。
synthGsDrumKitName(program)は、リズムパートのプログラムチェンジが選ぶ GS リズムセットの名前('Standard'、'Room'、'Jazz'など)を返します。モジュール自身のトーンマップがそのプログラムにセットを定義していなければnullです。エンジンが返す名前には機種の型番が含まれるものがあります。このページの表ではそうしたセットを機種の種類で呼んでおり、自分のページではこの下の例のように自前のラベルを付けてください。プログラム番号はどちらでも同じです。synthGsDrumKitIsVoicedApart(program)は、セット内のドラムノートが 1 つでも Standard と異なればtrue、セットが Standard とまったく同じ音で鳴るならfalse、そのプログラムにセットがなければnullを返します。プログラム 0 は Standard そのものなので、上の 4 つのワンショットセットと同じくfalseになります。synthGsVariationIsVoicedApart(bank, program)はメロディック側です。バンクセレクトのバリエーションが固有のパッチを持てばtrue、キャピタル音色へ解決されるならfalse、どちらかの引数が0..127の外ならnullです。GS のバンクセレクト MSB と GM2 の LSB は同じバリエーションを指すので、どちらを渡しても受け付けます。なお、この API の他の場所ではドラムバンクを指す128も、ここでは範囲外です。鳴らし分けのないバリエーションをキャピタルへ解決するのは GS の仕様どおりなので、falseは欠落ではなく正しい挙動です。ただし、本当に固有の音を持つバンクと区別できるのはこの問い合わせだけで、これがなければ両方をレンダリングして比較するしかありません。
答えは一覧から引くのではなく導出されます。キット側の述語はセットを各ドラムノートの解決済みパッチに適用してバイト単位で比較し、バリエーション側の述語は解決結果がキャピタル音色そのもののパッチかどうかを見ます。そのため、スロットに音色が付けば答えはそれに合わせて変わり、追随させるべき一覧はありません。存在はするが現在のパッチに対しては何も変えないケースも、そのまま報告されます。基準になるのはモジュール自身のトーンマップで、これは最新世代であり、このビルドが鳴らし分けるセットをすべて含みます。古いトーンマップを選ぶファイルから選べるセットはそれより少なくなります。
どちらの述語も3 つの状態を返すため、真偽値として評価するとその区別が失われます。null は「そこにスロットが存在しない」という意味で、102 のプログラムがこの状態です。if (!voicedApart) と書くと、false を返す少数のセットに加えて、この 102 個の空のプログラムまでプレースホルダーとして数えてしまいます。必ず false と明示的に比較してください。
import { synthGsDrumKitIsVoicedApart, synthGsDrumKitName } from '@libraz/libsonare';
const kits = Array.from({ length: 128 }, (_, program) => program)
.filter((program) => synthGsDrumKitName(program) !== null)
.map((program) => ({
program,
label: `Rhythm set ${program}`, // 自前のラベル。返された名前は表示しない
placeholder: synthGsDrumKitIsVoicedApart(program) === false,
}));import libsonare as sonare
kits = [
{
"program": program,
"label": f"Rhythm set {program}",
"placeholder": sonare.synth_gs_drum_kit_is_voiced_apart(program) is False,
}
for program in range(128)
if sonare.synth_gs_drum_kit_name(program) is not None
]パッチを固定せず GM プログラムに追従させる
バインディングは通常、1 つのデスティネーションに 1 つのパッチを固定します。MIDI にどんなプログラムチェンジが入っていても、そのデスティネーションを通る音はすべて同じ音色で鳴ります。一般的な MIDI ファイルではこれは望ましくありません。チャンネルごとに自分の楽器を選んでほしいからです。
GM プログラム追従を有効にすると、シンセはメロディックなボイスを、追跡しているバンクとプログラムチェンジから解決し、MIDI チャンネル 10 は GM ドラムキットマップへルーティングします。マップが対応しない部分にはバインドしたパッチがフォールバックとして残るので、音が消えることはありません。モードを切っていれば、上記の固定パッチの挙動は変わりません。
// JS バインディングのオプションは SynthPatch オブジェクトに指定する(文字列には指定できない)。
const audio = project.bounceWithSynthInstrument(
{ preset: 'acoustic-piano', useGmPrograms: true },
{ totalFrames: 48000, numChannels: 2 },
);audio = project.bounce_with_synth_instrument(
"acoustic-piano", # マップ外プログラムのフォールバック
auto_select_gm=True,
sample_rate=48000,
)このフラグは C ABI の SonareSynthInstrumentBinding では use_gm_programs、Python のバウンス呼び出しでは auto_select_gm、WASM/Node の JavaScript では SynthPatch 記述子の useGmPrograms です。いずれも既定値は false で、固定パッチをフォールバックにする挙動を保ちます。useGmPrograms はバインディングのオプションで、NativeSynth パッチのフィールドではありません。Python にはパッチ単位のフィールド SynthPatch.use_gm_programs もあります。これはそのデスティネーションについて auto_select_gm より優先され、リアルタイムの set_synth_instrument で使える唯一のスイッチです。2 つの CLI では値なしの --synth フラグがこれにあたります(sonare project bounce --in project.json --synth -o out.wav)。プリセット名を渡した場合はそのパッチに固定されます。
totalFrames を 0 のままにすると、アレンジとパッチのリリーステイルから長さを自動導出します。GM プログラムに追従している場合、テイルはアレンジが実際に鳴らしたプログラムだけを対象にします(長さを省略する)。未知のプリセット名は例外を投げます。bounceWith* が共有するチャンネル・サンプルレート・レイテンシなどは プロジェクトバウンス を参照してください。
GS アーキテクチャ層
SoundFont プレイヤーは GM の上に、GS 向けに作られたアレンジが期待する GS 拡張を実装します(独立した再実装であり、いかなるハードウェアメーカーとも提携・承認関係にありません)。以下はすべてアドレスで指定します。GS の書き込みは System Exclusive のフレームで、3 バイトのアドレスがマップ・その中のブロック・さらにその中のパラメータを指します。
- バイトが届く範囲
- 未到達
- 不在を宣言、行に理由あり
4 つのレベルは、プレイヤーがアドレスごとに保証している内容です。AUDIBLE はバイトを変えればレンダリング結果が変わること、STATE は値が保持され読み戻せるがどのエンジンも読まないこと、ACCEPT はデコードして破棄すること、IGNORE は行が意図的に受け付けないと宣言し、その理由を書いていることを意味します。プレイヤーが持つ 16 パートは、受け取る MIDI ポートが 1 つであることから来ています。50 ** ** と 51 ** ** は別グループのパート用ブロックで、同じ理由から受け付けません。対象機にはそもそもこのアドレスが無く、40 と 41 がどちらのグループを指すかはポートが決めます。WebAssembly の制約ではありません。本当に WebAssembly から来ている制約は音源方式が切り分けています。
- バリエーションバンクフォールバック: SoundFont がカバーしない GS バリエーションバンクは、キャピタル(バンク 0)の音色へフォールバックします。欠けたバリエーションでも無音にならず、正しいファミリーを鳴らします。
- チャンネル 10 のバンク 128 ドラムキット: ドラムプログラムはバンク 128 にあり、慣習でチャンネル 10(インデックス 9)がドラムパートです。
- NRPN パート編集: パートごとのフィルター(TVF)のカットオフとレゾナンス、パートごとの音量(TVA)のエンベロープ、ビブラートを NRPN でパートごとに編集でき、さらに個別のドラム音用のドラムごとの NRPN も使えます。
- GS/GM SysEx: GS Reset、GM System On、「リズムパートに使用」の SysEx を認識します。ホストからのものと、アレンジ内に埋め込まれた SysEx イベントの両方に対応します。プレイヤーのリセットはコントローラーとあわせてシステムエフェクトとマスター EQ もクリアするため、リセット前に設定したリバーブレベルや EQ の持ち上げは鳴り止みます。GS Reset と GM System On は、バンクセレクト LSB で選んだ音色マップもクリアするため、すべてのパートがモジュール自身のマップに戻ります。
- センドリターン方式のシステムエフェクト: 16 パート共通の 1 つのセンドリターンバスに、リバーブ・コーラス・ディレイの 3 ユニットが載っています。各パートの送信量は 2 つの経路の合算です。チャンネル CC センドでは、CC91 がリバーブ送信、CC93 がコーラス送信、CC94 がディレイ送信を担います。リバーブとコーラスには、SF2 ゾーンジェネレーター
reverbEffectsSend/chorusEffectsSendの値がさらに上乗せされます(GS のディレイ送信は CC 専用で、対応する SF2 ジェネレーターはありません)。パワーオン時の送信量はリバーブ 40、コーラス 0 です。このため、リセット SysEx を送らない SMF でも空間の響きが残ります。これとは別に、パートごとのドライブインサート(ゲイン補正付きサチュレーション)もこのバスに並んで存在し、後述する GS の挿入エフェクト(EFX)とは別のものです。 - システムディレイのモード: リバーブの
CHARACTERを 6 にすると Delay、7 にすると Panning Delay になり、それぞれの時間とフィードバック設定で動作します。Panning Delay は左右の出力を交互に鳴らします。独立した GS ディレイユニットは負のフィードバックも保持するため、繰り返すエコーの極性が交互に反転します。 - MIDI 2.0/GM2: MIDI 2.0 のバンク付きプログラムチェンジをデコードします。バンクセレクト LSB(CC#32) の解釈は MSB によって 2 通りに分かれます。
- GM2 のアドレッシング: MSB が GM2 のメロディックバンク(
0x79)またはパーカッションバンク(0x78)のとき、LSB はそのままバリエーション番号(またはパーカッションセット番号)で、GM2 の定義どおりです。 - GS のトーンマップ選択: それ以外の MSB では、LSB は MSB のバリエーション番号がどの世代の音色セットから選ばれるかを指定します。詳しくは GS / GM ドラムキットバリエーションを参照してください。これ以外の値は
0(モジュール自身のマップ)として読まれます。
- GM2 のアドレッシング: MSB が GM2 のメロディックバンク(
LSB は 2 つの意味を持つ
これは Project.midiBankProgram(...) に bankLsb として渡すバイトで、最も間違えやすい値です。GM2 の MSB のもとではバリエーションを選び、GS の MSB のもとではトーンマップを選びます(バリエーション番号は MSB 側にあります)。GS のバリエーション MSB と並べて bankLsb: 1 と書いてもバリエーション 1 にはならず、そのパートが最も古いトーンマップ 1 の音色セットに固定されます。
どこまでが 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 段階のいずれかを持ち、行のないアドレスは、黙って何もしないのではなく不具合として扱われます。したがって縮退は、書かれた理由であって省略ではありません。
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 にあります。
エンジンは単一のモジュールを想定しているため、00 00 7F SYSTEM MODE SET は範囲こそ受け取るものの、実際に効くのは 00 だけです。入力ポートが 1 系統、パートのグループも 1 つです。
GS 挿入エフェクト(EFX)
独自の DSP による再現であり、ハードウェアのデータを同梱するものではありません
libsonare の挿入エフェクトは、公開されている情報をもとに再構成した libsonare 独自のアルゴリズムの組み合わせで、GS の EFX の SysEx とタイプ番号体系に対応づけた独自の DSP による再現です。これにより GS 準拠で作られた MIDI が作曲者の意図したエフェクトを選べます。ただしアルゴリズムは独立しているため、同じアドレス指定とエフェクト構成には従いますが、特定のハードウェアモジュールの音そのものを再現するものではありません。1:1 のエミュレーションではなく、互換性のある再構成として捉えてください。サンプル・ROM データ・ファームウェアの同梱は一切なく、いかなるハードウェアメーカーとの提携や承認を意味するものでもありません。この互換性の背景にある標準や文献については、アルゴリズム根拠を参照してください。
前述のリバーブ・コーラス・ディレイのセンドリターンバスとは別に、GS は挿入エフェクト(EFX)を定義しています。センドリターンバスと異なり、ギターのエフェクターのようにパートの信号経路へ直接挿入されるエフェクトです。仕様上のブロックは 40 03 xx にあり、実機はモジュール全体でこのユニットを 1 つだけ持ちます。libsonare は 16 個持ちます。ユニット 0 は 40 03 xx のまま(意味も既定値もレイアウトも仕様どおり)で、残る 15 個は 40 3u xx にあり、ユニット番号はアドレスのニブルそのものです。40 30 xx はふたたびユニット 0 で、40 03 xx がすでに書き込んでいる記憶領域への 2 つめの入口です。
ルーティングもアドレスを増やしません。40 4x 22 PART EFX ASSIGN は 00 BYPASS と 01 EFX を仕様どおりの意味のまま保ち、自分の値の範囲を広げます。02〜10 がユニット 1〜15 を選びます。00〜10 の外側の値は、他の範囲外書き込みと同じく無視されます。衝突が起きないのは、40 30〜40 3F がどちらの機種のパラメータアドレスマップにも行を持たないからです。そのため仕様に準拠したファイルがこの拡張を使ってしまうことはなく、フィーチャーフラグが無くても安全です。実機は未知のアドレスを無視するので、この拡張を使ったファイルは実機でも挿入エフェクト 1 つで再生されます。
- ルーティングされたパートの経路
- センドとリターン、ウェットのみ
- バイパス: 40 4x 22 = 00
「実機の挙動」という言い方には別々の 2 つが混ざっていて、これを一緒にしてはいけません。モジュール全体でユニットが 1 つというのは、作られた機械のリソース上の制限であって GS の性質ではないので、外してあります。同じユニットへルーティングされたパートが合算されるほうは制限ですらありません。2 本のギターを 1 台のエフェクターへ通せば互いに干渉するのと同じで、エフェクトとはそういうものです。こちらは外していません。ユニットは、割り当てられた全パートの合算に対して 1 回だけ走ります。つまり変わったのはユニット数で、合算は変わっていません。「合算を元に戻す」がユニット数を制限する意味になることは決してありません。
ユニットの出力は 1 本の信号なので、その下流にあるものはパートではなくユニットに属します。システムエフェクトへのセンドはユニット自身のもの(40 3u 17〜19)で、ルーティングされたパートのパートレベルの CC91/CC93/CC94 センドは、残響の尾が二重に送られないよう抑止されます。マスター EQ のルーティングは、そのユニットへ入るパートたちの指定が一致するときにだけそれに従います。40 4x 20 のバイパスが効くのは、そのユニットに載る全パートがバイパスを要求したときだけで、そうでなければユニットは EQ を通ります。EQ を通すのが、どのパートもパワーオン時に持っている状態です。
パートリグと EFX の直列ルーティング
各パートには GS EFX ユニットの前に自身のリグ段があります。実際の経路は 音源 → パートリグ → EFX ユニット → システムエフェクトとマスター EQ です。パートを EFX ユニットへルーティングしても、音源がそのパートへ割り当てる既定のバンクリグはユニットの前に残ります。アンプなどの段を自動的に外すことはありません。ホストがリグを指定するとバンクリグを置き換えますが、EFX へのルーティングだけでは置き換わりません。EFX を割り当てないパートも自身のリグを通り、パート単位のシステムエフェクトセンドを維持します。 パートリグのモードは PART_RIG_MODES = ['bank', 'none', 'chain'] です。bank は現在のプログラムに対する音源自身のリグを使い、プログラムチェンジに追従する唯一のモードです。none は音源の直接出力(DI)を使い、chain はバンクリグを信号順の 1〜8 個のインサート段で置き換えます。各段はインサートファクトリーの processor 名と params オブジェクトで指定し、レイテンシは 48 kHz 基準で最大 256 samples です。無効なプロセッサやパラメータは拒否されます。 part は GS スロットの 0〜15、またはデスティネーション全体の既定値を表す PART_RIG_ALL_PARTS(0xFF)です。特定パートのエントリがデスティネーション既定値を上書きします。プロジェクトでは Project.setPartRig(...)、getPartRig(...)、clearPartRig(...) を使い、Node はリクエストオブジェクト、WASM は位置引数形式を公開します。Python の名前は set_part_rig、get_part_rig、clear_part_rig です。リアルタイムエンジンではバインド済み音源に対して setPartRig(...)/set_part_rig(...) を直接呼び出します。デスティネーションには音源が必要で、パートリグを実装しない組み込み音源やコールバック音源、またはマスタリング機能のないビルドの chain は NotSupported になります。 プロジェクトのエントリは、トラックがそのデスティネーションへまだルーティングされていなくてもデスティネーションとパートごとに保存され、バウンスでは SoundFont と物理モデルのデスティネーションへ適用されます。アンプを含むチェーンは最後のアンプまで入力をモノラルにまとめ、その後でパートのパンを戻します。SMF の書き出しにはパートリグを含めません。
GS の音色調整と EQ のロー/ハイシェルフは 1 次フィルターです。slopeDbOct は 6 dB/octave で、周波数はシェルフのゲイン変化が dB 値で半分になる位置を表します。1 次シェルフでは q は効きません。ホストからパラメトリック EQ のシェルフバンドを設定するときも、slopeDbOct: 6 で同じ次数を選べます。
パラメータのバイトが音に届くまで
公開されている資料に記載された EFX パラメータは、すべて操作子か段の切り替えに使われます。translated は実測の変換表、または記載された範囲から決まる刻みを使う対応です。designed は資料から変換則が決まらない箇所に、音の特性に合う法則を選んだ対応です。enables は段をオン・オフしたり、複数の段から選んだりします。これらは再実装の応答を示します。設計で選んだ法則が、実機でも使われていたと保証するものではありません。
GS 音源モジュールのデモ では、各パラメータの操作先と変換の根拠を確認できます。1 バイトが複数の操作子に届く場合や、セレクターで選んだ段に届く場合もあります。
どのバインディングにも「EFX を設定する」といった専用の型付き API はありません。実際の GS ハードウェアと同じように、EFX のタイプとパラメータは生の SysEx を送ることでのみプログラムします。ライブでは RealtimeEngine.pushMidiSysex() でそのバイト列を送り、オフラインではアレンジの MIDI に埋め込まれた SysEx がバウンス中にインラインで適用されます。
EFX タイプ → 挿入エフェクト
各 EFX タイプ番号が 1 つの挿入エフェクトを選びます。タイプ 0 は Thru(エフェクトなし)です。
| EFX タイプ | GS EFX 名 | libsonare の挿入エフェクト |
|---|---|---|
| 0x0100 | Stereo EQ | パラメトリック EQ |
| 0x0101 | Spectrum | グラフィック EQ |
| 0x0102 | Enhancer | プレゼンスエンハンサー |
| 0x0103 | Humanizer | 母音フィルター |
| 0x0110 | Overdrive | saturation.overdrive → saturation.ampSim |
| 0x0111 | Distortion | saturation.distortion → saturation.ampSim |
| 0x0120 | Phaser | フェイザー |
| 0x0121 | Auto Wah | エンベロープ追従型レゾナントバンドパス |
| 0x0122 | Rotary | 2ローターのロータリースピーカーモデル |
| 0x0123 | Stereo Flanger | フランジャー |
| 0x0124 | Step Flanger | フランジャー |
| 0x0125 | Tremolo | 振幅変調として駆動するリングモジュレーター |
| 0x0126 | Auto Pan | オートパン |
| 0x0130 | Compressor | コンプレッサー |
| 0x0131 | Limiter | リミッター |
| 0x0140 | Hexa Chorus | 6声アンサンブル |
| 0x0141 | Tremolo Chorus | コーラス |
| 0x0142 | Stereo Chorus | コーラス |
| 0x0143 | Space chorus | コーラス(変調なし) |
| 0x0144 | 3D Chorus | コーラス(広がりを付加) |
| 0x0150 | Stereo Delay | ステレオディレイ |
| 0x0151 | Modulation Delay | ステレオディレイ |
| 0x0152–0x0154 | 3-tap/4-tap/Time-Control Delay | ステレオディレイ |
| 0x0155 | Reverb | プレートリバーブ |
| 0x0156 | Gate Reverb | プレートリバーブ(ゲートテールは未実装) |
| 0x0157 | 3D Delay | ステレオディレイ |
| 0x0160 | 2-voice Pitch Shifter | ピッチシフター |
| 0x0161 | Feedback Pitch Shifter | ピッチシフター(フィードバックループは未モデル化) |
| 0x0170/0x0171 | 3D Auto/Manual | バイノーラル定位 |
| 0x0172/0x0173 | Lo-Fi 1/2 | ビットクラッシャー |
Humanizer(0x0103)は、ドライブと母音の操作子を持つ母音フィルターを使います。3D Auto と 3D Manual(0x0170、0x0171)はバイノーラル定位を使います。各パラメータは実測または設計で選んだ変換則を通して操作子に届き、任意の段があるエフェクトでは専用のスイッチでその段を切り替えます。
modern 実現方式の Overdrive と Distortion は、アンプシミュレーターの前に saturation.overdrive または saturation.distortion のペダルを置きます。Amp Type がアンプを選び、Amp Sw がすべてのアンプ段のキャビネットを切り替えます。SoundFont プレイヤーの GM プログラム 29(Overdriven Guitar)と 30(Distortion Guitar)も、ペダルからアンプへ進む同じ順序で鳴ります。
複合 EFX タイプ(多段チェーン)
複合 EFX タイプは、ハードウェアのブロック構成に合わせて、上記と同じ DSP インサートを直列につないだチェーンとして実現されます。たとえばギター用マルチエフェクトも、個々のアンプシミュレーター/コーラス/ディレイのインサートをつないで動作します。下の表は代表例で、実際のマップは 0x0200〜0x020C の 2 段マトリクス(Overdrive/Distortion/Enhancer を Chorus・Flanger・Delay に通す組み合わせ)と、0x0400〜0x0500 のギター/ベース/エレクトリックピアノ/キーボード用マルチプリセットまでを網羅します。
| EFX タイプ | GS EFX 名 | チェーン(信号順) |
|---|---|---|
| 0x0200 | OD → Chorus | オーバードライブペダル → アンプシミュレーター → コーラス |
| 0x0202 | OD → Delay | オーバードライブペダル → アンプシミュレーター → ステレオディレイ |
| 0x0400 | Guitar Multi 1 | コンプレッサー → アンプシミュレーター → コーラス → ディレイ |
| 0x0405 | Bass Multi | コンプレッサー → アンプシミュレーター(ベースキャビネット) → EQ → コーラス |
| 0x0406 | Electric piano Multi | エンハンサー → フェイザー → コーラス → オートパン |
| 0x0500 | Keyboard Multi | リングモジュレーター → EQ → ピッチシフター → フェイザー → ディレイ |
モダンとクラシックの EFX
SoundFont 音源をバインドまたはバウンスするとき、gsEfxRealization で EFX の音作りを選べます。NativeSynth のフォールバックにも同じ指定が適用されます。
modern(既定値)は、現在のインサートプロセッサを組み合わせます。上の対応表はこちらの経路を示しています。classicは、内部で 32 kHz の専用モデルを動かし、プロジェクトのサンプルレートへ変換します。EFX のタイプとパラメータのバイトは共通です。音源のボイスと、システムのリバーブ・コーラス・ディレイは、この選択とは別に動きます。サンプルレート変換の往復と出力 FIFO のプリフィルにより内部遅延が加わりますが、インサートはこれを 0 と報告します。並列パスやドライパスの位置合わせをするホストは、classic ではレポートされるレイテンシに頼らないでください。
GS 音源モジュールのデモ では、再生前に経路を選べます。選択中のパートを EFX に割り当て、エフェクトを選んで聴き比べてください。Thru には比較するインサーションエフェクトがありません。
レベルとバランスのバイト
EFX のバランスのバイトは 2 段のランプ則に従います。ドライとエフェクトのうち大きいほうの側は常にフルレベルに保たれるため、既定の中央値ではどちらも等倍で鳴り、中央から外れると片側だけが下がります。出力とタップのレベルのバイトは、0 以外の値がそれぞれ固有のレベルに対応し、-24 dB で頭打ちになりません(0 は無音)。ディレイとモジュレーションのバイトは、エフェクト自身が許す上限まで届きます。フィードバックは 98% まで、コーラスとアンサンブルのプリディレイは 100 ms までです。
ライブとオフラインでの実現方式
- オフライン(バウンス): アレンジに埋め込まれた EFX の SysEx はレンダー中にインラインで適用されます。バウンスの途中で EFX が変わっても、次のブロックから反映されます。
- ライブ:
pushMidiSysex()はオーディオスレッドの外で新しいエフェクトチェーンを構築し、ウェイトフリーに引き渡すため、ライブエンジンは再生を止めずに EFX の変化を反映できます。
下のデモでは、同じ持続和音を GS 互換プレイヤーで鳴らしながら挿入エフェクトを切り替えて、それぞれがトーンをどう変えるかをドライ音と聴き比べられます。
MIDI ヘルパーで GS バンクをオーサリングする
Project.midiBankProgram(ppq, group, channel, bankMsb, bankLsb, program) は、バンクセレクトとプログラムチェンジを setMidiEvents が受け付ける MIDI イベントへ展開します。GS バリエーションやドラムキットを選ぶ正しい方法です。Project.gmInstrumentName(program)、Project.gmDrumName(note)、Project.gm2InstrumentName(bankLsb, program)、Project.midiCcName(controller) のような静的ヘルパーがスロットに名前を付けるので、オーサリングコードが読みやすくなります。逆方向も対称です。Project.gmProgramForName(name)、Project.gmDrumNoteForName(name)、Project.midiCcIndexForName(name) は正規名から番号を返し(未知の名前は -1)、Project.gmFamilyName(family) と Project.gmFamilyFirstProgram(family) は 16 の GM 楽器ファミリーを列挙します。Project.gm2DrumSetName(bankLsb) と Project.gm2DrumName(bankLsb, note) は GM2 のドラムセットバリエーションに名前を付けます。
GM 音色マップ
128 プログラムすべての楽器名・エンジン・備考をまとめた表は、GM 音色マップにあります。