
2026/02/10 2:44
**Game Boy Advance のオーディオ補間** * Game Boy Advance(GBA)は、音声再生を滑らかにするために 16‑bit 線形補間アルゴリズムを採用しています。 * 主な特徴 * **サンプリング周波数**:32 kHz(固定) * **補間方式**:一次線形で、前回のサンプル値と現在のサンプル値を利用 * **目的**:低周波信号における聴覚的なステップアーティファクトを軽減すること * **実装**:CPU ベース。各オーディオフレームは、2 回の乗算と 1 回の加算で処理されます。 * 実際に使用する上での注意点 * GBA の限られた演算リソースに対して十分効率的です。 * 連続サンプルをバッファリングするため、わずかな遅延(約 1 ms)が生じます。 * エミュレータでは、本物の音再現性を確保するために広く利用されています。
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
記事は、ゲームボーイアドバンス(GBA)エミュレータ向けのリアルタイムオーディオ強調法を提示し、アンチエイリアシングとノイズを低減しつつ高いパフォーマンスを維持します。GBAハードウェアは音声をパルス幅変調(PWM)でミキシングしており、4 つのサンプリング周波数(32 768 Hz–262 144 Hz)のうち最も多く使われるのは 65 536 Hz です。エミュレータの出力レートに対して最近傍補間を行うと、特に PCM 周波数が低い場合(約10 k–14 kHz)に歪みが生じます。
新しいアプローチでは PWM をバイパスし、各オーディオチャネルをその真のソースレートから直接再サンプリングします。計算式は
gba_clock / ((0x10000 - timer.reload) * timer.clock_divider) で、タイマーが変化するたびに更新されます。PSG チャネルについては、著者は二つの戦略を示しています:出力レートへの最近傍補間、または 2 097 152 Hz の高周波サンプリング後にダウンサンプリング;いずれもローパスフィルタが必要です。
6‑ポイント立方ヘルミット スプラインと窓付き sinc の二つの補間アルゴリズムを実装しています。Metroid: Zero Mission でのサンプルテストでは、低 PCM 周波数(約13 kHz)で立方ヘルミットが一般的にクリア(マフリングが少ない)、高周波数(約21 kHz)では sinc がより優れた性能を示し、ヒスが減ります。ダウンサンプリング後の PSG 出力には動的ローパスフィルタ(カットオフ=0.5×PCMサンプルレート)が適用され、低 PCM 周波数時に高周波数支配を抑制します。
この方法は任意の GBA タイトルで機能し、MP2K HQ(NanoBoyAdvance)バージョンはその特定オーディオドライバーを使用するゲームに限定されます。パフォーマンスへの影響は主に PSG ダウンサンプリングから来ており、それでも許容範囲内です。また、8‑ビットサンプルの静的/ヒスは完全には除去できませんが、全体として音質の忠実度は顕著に向上します。
ユーザーにとってはほぼオーバーヘッドなしで明らかにクリアな音声を提供し、開発者には既存のエミュレータに統合可能な実用的で即実装できるソリューションとして有益です。
本文
この投稿では、Game Boy Advance(GBA)エミュレータが実装できる音声強化手法について説明し、高いレベルで音響のエイリアシングとノイズを減らす方法を紹介します。
まずは例として Metroid: Zero Mission の比較画像です。
Metroid: Zero Mission – Brinstar(正確な補間)
Metroid: Zero Mission – Brinstar(強化された補間)
後者の録音はよりクリアです! ただし、少しこもったように聞こえるかもしれませんが、最初の録音のひどいエイリアシングよりははるかに好ましいと考えます。
GBA 音声を改善する他の手法(NanoBoyAdvance の MP2K HQ など)もありますが、補間アプローチは任意の GBA タイトルで機能します(ただし品質はタイトルによって異なります)。MP2K HQ は MP2K オーディオドライバ(M4A/Sappy とも呼ばれる)を使用するゲームにしか対応していません。これは多くのゲームに適用できますが、すべてではありません。
この手法は新しいものではなく、VBA‑M は長らく強化補間をサポートしています。ただし実装の詳細は若干異なる点があります。
背景
前回の記事で GBA 音声ハードウェアの仕組みについて詳しく説明しました。ここでは音声補間改善に最も関係する部分だけをまとめます:
- GBA のオーディオハードウェアは、PWM(パルス幅変調)で混合された音声サンプルを 32 768 Hz ~ 262 144 Hz の4種類のサンプリング周波数のいずれかで出力します。
大半の GBA ゲームは 65 536 Hz を使用していますが、時折 32 768 Hz(例:Castlevania: Circle of the Moon)を使うゲームもあります。 - GBA は各オーディオチャネルの周波数を PWM サンプリング周波数にリサンプリングする際に「最近傍補間」(nearest‑neighbor interpolation)を適用します。すなわち、現在のサンプルだけを出力します。
これにより、特に低いサンプルレート(10 000〜14 000 Hz 程度)の PCM チャネルでエイリアシングが顕著になります。たとえば Metroid の例では 13 379 Hz が使われています。
補間を強化する主なアイデアは単純です:GBA PWM ハードウェアの動作を正確に模倣する代わりに、エミュレータ自身が独自の補間アルゴリズムで PCM チャネルのサンプルレートからエミュレータの音声出力サンプルレートへ直接リサンプリングします。
ソース・サンプルレート
まずはソースサンプルレートを算出する必要があります。
前回の記事で述べたように、PCM チャネルのサンプルレートは GBA タイマーのクロック分周器とリロード値から計算できます。
gba_clock = 1 << 24 # 16 777 216 Hz sample_rate = gba_clock / ((0x10000 - timer.reload) * timer.clock_divider)
サンプルレートは小数になることがありますが、最終的な分周器値
((0x10000 - reload) * divider) は必ず正整数です。
多くのゲームでは PCM 音声を同じサンプルレートで再生しますが、曲ごとに異なるサンプルレートを使用することもあります(例:Castlevania: Circle of the Moon のメインメニュー音楽はより高いサンプルレートです)。
以下の状況が発生したら、必ずチャネルのサンプルレートを再計算または確認します:
- ゲームがチャネルに割り当てるタイマーを変更したとき
- タイマー0または1 が有効/無効になったとき
- タイマー0または1 のリロード値やクロック分周器が変わったとき
- タイマー1 のカスケードモードが有効/無効になったとき
タイマーが停止している PCM チャネルは、FIFO から最後に取得したサンプルを継続的に出力します。完全に正確ではありませんが、この状態を「ミュート」とみなすこともできます。ただし、ゲーム側で頻繁にオーディオタイマーを無効化・再有効化するケースは極めて稀です(例:Driver 2 Advance)。
タイマー1 をカスケードモードで使用した事例は見たことがありませんが、理論上ハードウェアはサポートしています。効果的なサンプルレートは次のように計算できます。
gba_clock = 1 << 24 gba_clock_divider = (0x10000 - timers[0].reload) * timers[0].clock_divider \ * (0x10000 - timers[1].reload) sample_rate = gba_clock / gba_clock_divider
実装では、カスケードモードのタイマー1 を追跡しているチャネルに対しては最近傍補間へフォールバックします(他の全ての処理には影響しません)。このケースを見たことがないため、特に問題があるとは思いません。
リサンプリングとミキシング
各 PCM チャネルのソースサンプルレートがわかったら、それからエミュレータ出力サンプルレート(例:48 000 Hz)へリサンプリングします。PWM サンプリング周波数を経由する必要はありません。正確なエミュレーションを目指さないのであれば、直接リサンプリングすれば十分です。
ここからは GBA 固有の問題ではなく、任意のリサンプリングアルゴリズム(またはライブラリ)を使用できます。PCM チャネルが FIFO からサンプルを取り出すたびに入力として渡します。自前のリサンプリング実装を使うことで、補間方法を正確に制御できます。
PSG チャネルについても同様に別途リサンプリングを行います。最近傍補間を使用するとハードウェアに近い音が得られます(ただし実際のハードウェアは PWM 周波数(通常 65 536 Hz)へ最近傍補間します)。
高品質な PSG リサンプリングには、GB/GBC エミュレータと同様に「2 097 152 Hz」で全 4 チャネルをサンプリングし、その周波数で混合してから出力サンプルレートへインターポレーションする手法があります。これは PSG の任意の周波数が 2 097 152 Hz の偶数除数であることと、PSG 音声生成の仕組み上「サンプル変更間は無限に繰り返される」と仮定できるためです(実際にはそうではありませんが、2 097 152 Hz でサンプリングすれば問題ありません)。インターポレーションには低域通過フィルタを組み合わせてエイリアシングを防ぎます。
最終ミキシングでは、全サンプルを符号付き(signed)に保ち、GBA の音声バイアス機能は完全に無視します。正確なエミュレーションのように最低ビットを切り捨てたり整数化したりする必要はありません。リサンプリングプロセスが浮動小数点出力サンプルを返すため、単純にそのまま浮動小数点で混合できます。もしクリッピングを防ぎたいならサンプルを縮小しても構いません(実際のハードウェアより音量は低くなります)。
私の強化リサンプリング実装では、2 種類の補間アルゴリズムをサポートしています:6‑point キュービック Hermite 補間とウィンドウ付き sinc 補間です。
キュービック Hermite
キュービック Hermite は以前に Sega CD の記事で紹介したものと同じ実装です:
def interpolate_cubic_hermite_6p(samples, n, x): [ym2, ym1, y0, y1, y2, y3] = samples[n-2:n+4] c0 = y0 c1 = 1/12 * (ym2 - y2) + 2/3 * (y1 - ym1) c2 = 5/4 * ym1 - 7/3 * y0 + 5/3 * y1 - 1/2 * y2 \ + 1/12 * y3 - 1/6 * ym2 c3 = 1/12 * (ym2 - y3) + 7/12 * (y2 - ym1) + 4/3 * (y0 - y1) return ((c3 * x + c2) * x + c1) * x + c0
このアルゴリズムはアップサンプリング(出力レートがソースより高い)に対して非常にうまく機能します。ほとんどの GBA PCM チャネルはこれに該当します。ただし、ダウンサンプリングにはソース信号に低域通過フィルタを適用しないとエイリアシングが生じます。
Sinc 補間
Sinc(バンドリミテッド)補間は極めて高品質ですが、実装も複雑です。主なアプローチとして次の 2 つがあります:
- Digital Audio Resampling Home Page(私の実装の参考元)
- Band‑Limited Sound Synthesis
GBA の低サンプルレートでは、必ずしも sinc 補間が単純な補間より音質を向上させるわけではありません。比較例を紹介します。
例示
まずは Brinstar を再び取り上げます。ただし今回はウィンドウ付き sinc バージョンも併記します(最初の例はキュービック Hermite)。このゲームは約 13 379 Hz のサンプルレートです。
Metroid: Zero Mission – Brinstar (正確なリサンプリング)
Metroid: Zero Mission – Brinstar (キュービック Hermite 補間)
Metroid: Zero Mission – Brinstar (ウィンドウ付き sinc 補間)
ウィンドウ付き sinc バージョンはエイリアシングが少なくなる一方で、非常にこもった印象になります。私は個人的にキュービック版の音を好みます。
一般的に sinc 補間はエイリアシングとノイズをより完全に除去しますが、Nyquist 周波数(ここでは約 6 689 Hz)以上の周波数を完全にカットすると音がマフル化します。これは元の 13 379 Hz 信号を正確にリサンプリングした上での結果ですが、必ずしも「より良い」わけではありません。
次に Fire Emblem: The Blazing Blade の例(同じく 13 379 Hz)です:
Fire Emblem – Strike (正確なリサンプリング)
Fire Emblem – Strike (キュービック Hermite 補間)
Fire Emblem – Strike (ウィンドウ付き sinc 補間)
こちらもキュービック版を好みます。sinc バージョンは非常にこもった印象です。
ただし、すべてのケースでそうなるわけではありません。次の Mega Man Zero 4 の例(サンプルレート約 21 024 Hz)をご覧ください:
Mega Man Zero 4 – Esperanto (正確なリサンプリング)
Mega Man Zero 4 – Esperanto (キュービック Hermite 補間)
Mega Man Zero 4 – Esperanto (ウィンドウ付き sinc 補間)
3 バージョンともに 8‑bit サンプルの定量化ノイズ(スタティック/ハイッシング)があるものの、sinc バージョンは最もノイズが少なく、キュービック版と大差ありません。
さらに Castlevania: Circle of the Moon のメインメニュー音楽(PCM サンプルレート 42 048 Hz、PWM 周波数 32 768 Hz)を例に挙げます。正確なエミュレーションでは非常に悪い音になります。
Castlevania: Circle of the Moon – Main Menu (正確なリサンプリング)
Castlevania: Circle of the Moon – Main Menu (キュービック Hermite 補間)
Castlevania: Circle of the Moon – Main Menu (ウィンドウ付き sinc 補間)
Golden Sun: The Lost Age の例
最後に、極めて高いサンプルレート 63 072 Hz を持つ Golden Sun: The Lost Age の曲を示します。高いサンプルレートにも関わらず、ゲームの音質が良くても一部ノイズが目立ちます。
Golden Sun: The Lost Age – Gloomy Caves (正確なリサンプリング)
Golden Sun: The Lost Age – Gloomy Caves (キュービック Hermite 補間)
Golden Sun: The Lost Age – Gloomy Caves (ウィンドウ付き sinc 補間)
sinc 補間はノイズを多少除去しますが、差は大きくありません。キュービック版の方が全体的にバランスが良いです。
PSG の問題
Castlevania: Aria of Sorrow の曲(PCM と PSG 両チャネル使用)を例示します:
Castlevania: Aria of Sorrow – Castle Corridor (正確なリサンプリング)
Castlevania: Aria of Sorrow – Castle Corridor (キュービック Hermite 補間)
Castlevania: Aria of Sorrow – Castle Corridor (ウィンドウ付き sinc 補間)
PCM は約 10 512 Hz と非常に低いサンプルレートです。高品質補間で PCM のエイリアシングは抑えられますが、PSG の高周波成分はそのまま残り、PCM 曲よりも際立って聞こえてしまいます。
解決策としては PSG 出力に低域通過フィルタ(LPF)を掛ける方法があります。サンプルレート 10 512 Hz の Nyquist 周波数である 5 256 Hz をカットオフとした LPF を適用すると、次のようになります:
Castlevania: Aria of Sorrow – Castle Corridor (キュービック Hermite 補間 + PSG LPF)
Castlevania: Aria of Sorrow – Castle Corridor (ウィンドウ付き sinc 補間 + PSG LPF)
LPF を掛けると、PSG の高周波ノイズが抑えられ、全体のバランスが向上します。逆に、PSG がメインで PCM がサブの場合(例:Mega Man Battle Network 2 タイトルスクリーン)は、sinc 補間を使った方が必ずしも良いとは限りません。
実装では、PCM サンプルレートの 0.5 倍をカットオフ周波数に設定し、サンプルレートが変わるたびに 2 次バターワースフィルタを生成します。これは完璧ではありませんが、PSG の高周波成分を十分に減衰させるには十分です。
LPF は PCM を 2 097 152 Hz から 48 000 Hz にリサンプリングした後で適用します。理由は、より急峻なカットオフが可能であり、計算コストも低いためです。
総括
この手法は GBA 音声をリアルタイムでクリーンアップするのに非常に有効です。ただし、特に PSG のダウンサンプリング部分では性能への影響が大きくなる可能性があります。8‑bit サンプル入力から来るノイズ(静音/ハイッシング)は完全には除去できませんが、エイリアシングは大幅に減少し、一部のノイズも軽減されます。
以上で本文を終わります。