PCM Driver for Gameboy


img ゲームボーイの音源を使って PCM 音声を再生します。

一般的 (?) には 第 3 チャンネルの波形メモリ音源を使って再生するところですが
実機での検証も兼ねて、4 チャンネルそれぞれで再生を行い、各チャンネルの
出力の違いを調べてみることにしました。

4 チャンネル同時発声 GB リズムマシーンは果たして実用になるのでしょうか。





■ WAV -> PCM 変換ツールを作る

まずは wav ファイルを変換するプログラムを作ります。

gbpcm_20150912.zip  .NET Frameworkが必要です。Windows Update で入手できます。



「Ref」ボタンでファイルを選択するか、WAVファイルをドラッグ&ドロップすると、選択されたファイルが表示されます。
その状態で出力オプションをチェックし、「Output」ボタンを押すと、GB 用 PCMファイルが出力されます。

Windows で再生できる RIFF WAV のみ対応。
かなりいい加減なので賢い補完とか一切やりません。
ちゃんとした音声波形編集ソフトでノイズリダクションなり補完処理なりを加えた上で、この変換ツールに通した方が良いでしょう。
あと、22050Hz -> 4096Hz などのダウンサンプルは(一応)可能ですが、2048Hz -> 4096Hz などのアップサンプルは無理です。ごめんなさい。

PC-98 時代の人なら こうのたけし氏作の PCMCONV (Vector)をご存じかもしれません。
そちらでも Packed-4bit-PCM への変換が可能です。16byte には揃えてくれませんが。

SoX が使えるといいのですが、あちらは 4bit に変換できないようなんですよね。
ディザ付き変換やエフェクトが使えて優れものなのですが・・・。

設定項目は大体わかると思いますが、1data/1byte に設定した場合、サブ項目として「set bit3=1」というのがあります。
これは PCM ドライバがレジスタに 4bit ずつデータを書き込む際、データと同時にエンベロープフラグを立てて書き込むと
プチノイズが多少軽減できるというおまじないのようなテクニックがあり、これをあらかじめデータ側でフラグ立て処理するというものです。

$00 を終了サインにする設定はデータとしての $00 が $01 に置換されます。音質にやや影響します。
1data/1byte かつ bit3=1 設定 ではデータとしての $00 は $08 になる(音質には影響なし)のでこの置換は起こりません。

16byte揃えのチェックは波形メモリ音源での PCM が 16byte(32データ)単位なので、波形メモリPCMを使用する場合はチェックするとよいでしょう。

別ページの GBMC で使用する場合、起動直後の状態(4096Hz,2data/1byte,16byte揃え)のまま変換してください。

「3bit PCM」の項目はこのページの下の「マスターボリュームによる 3bit PCM 再生」を参照のこと。
3bit に変換した場合は L,R で同じデータが出力されます。本来は左右で分けて 2ch PCM やステレオ再生みたいなことも可能なのですが、
あまり使い道が無い(PCM 再生中は他の音が出せない)ので省略しました。
なお、3bit PCM の場合、終端サインは $FF を使うので「$00 を終端にする」項目は無意味です。

以上の設定を弄って出来たデータフォーマットは全て再生プログラムの仕様次第なので、結局は扱いやすいように加工するのがベストです:-)


■ GB リズムボックスを作る

リズムボックスを作るに辺り、フォーマットは以下の通りにしました。
・4096Hz
・2data/1byte
・データを 16byte 揃えにする
・16byte目のデータの末尾が $00 なら終了と判定する。
・$00 を終了サインとするので、データとしての $00 を $01 に書き換える

次に再生ルーチンを作ります。
といっても、タイマー割り込みで 1/4096 秒毎に再生処理を呼び出すだけです。
各再生処理は以下の通り、チャンネルによって音の出し方が異なります。

NR12,NR22,NR4276543210
VVVVETTT
bit7-4:初期ボリューム
bit3:エンベロープ 増加/減少
bit2-0:エンベロープステップ時間

FF30-FF3F76543210
HHHHLLLL
16バイトの上下4bitずつ(計32データ)を波形データとして連続再生する
16バイト1ループの再生時間は音程周波数 NR33,NR34に依存する


で、出来たのがこれ。 pcm.zip (12.6KB) : GameBoy Rhythm Box
推奨エミュレータは KiGB ということにしておきます。TGB だと酷いことに…。
パッドの上下左右、4ボタンでそれぞれ音が鳴ります。
内訳は以下(書いておかないと判別できないくらい音質悪いので)。
シンバルのような高周波成分のある音は低サンプリングレートには厳しすぎ。
困ったことに実機でも GBC と GBASP でまともに聴けるチャンネルが違ったりします。
共通して鳴るのは やはり波形メモリ音源くらいでしょうか。
エミュレータも種類によって得手不得手が全然違います。
こんな無茶をする市販ソフトが無いために対応していないだけだとは思いますが。


・追記 2009.12.18

で、サンプリングレートを上げたらどうなるか、やってみたのがこちら。pcm_16K.zip リズムボックス

上のが 4096Hz なのに対し、こちらは 16384Hz。LSDJ よりもレートが高いです。
ch.Dの出音が相変わらずひどいのはさておき、矩形波チャンネルが意外と頑張っています。シンバルとか合格点。
音の終わりが切れるのは元の WAV ファイルが原因です。

16384 Hz だとファイルサイズが巨大になるのと、速度的に組み込んでの常用は難しそうな気がしますが
なかなか可能性を感じさせる結果に。
ch.C 限定のサンプル再生だと再生レート÷ 32 の割り込みで良いのでかなり楽なんですけどねぇ。


■ マスターボリュームによる3bitPCM再生

上記の再生品質があまりに酷いので、別な方法での PCM 再生を試してみることにします。
これまでは各チャンネルの音量レジスタや波形データ 4bit を弄ることで PCM 再生を実現していましたが
GB には波形出力を調節できるもう一つのレジスタがあります。それがマスターボリュームです。

NR5076543210

0LLL0RRR
bit6-4:左出力ボリューム
bit2-0:右出力ボリューム

マスターボリュームと言うだけあって出力の最終段です。
間接的に弄ることになる各チャンネルの音量レジスタよりはプチノイズに強いかも(?)

3bit なので 0-7 の 8段階しか使えませんが、このデータを左右同じボリュームとして書き込みます。
出力のソースは ch.D のノイズチャンネルで、以下のように初期化しておきます。

[rNR41] $FF 長さレジスタ。特に意味無し。
[rNR42] $08 エンベロープの向きを上昇にセット。音量は 0
[rNR43] $00 ノイズ周波数
[rNR44] $BF 長さレジスタを使わず、垂れ流しでキーオン

ソースと GBROM イメージpcm2.zip
A ボタンを押すと音声再生を行います。8192Hz での音声再生です。
結構クリアに聞こえるのが分かると思います。
ただ一つの問題は、マスターボリュームを使うので、BGM との同期再生が無理ということ…。

上記ソースでは、8192Hz/3bit/2data1byte で作ったデータを再生します。
1data/1byte にすると、3bit の PCMデータを 0nnn0nnn のように、あらかじめレジスタに書き込みやすいように揃えて出力します。
「set bit3=1」にはチェックを入れないでください。

終端には $FF を自動的に付け加えます。
ソースでは、$FF が現れるまでデータを再生する仕組みです。

なにげに PCM の定位可変な感じなのですが、そこまでは試していません。

■ LSDJ のドラムキットを分割する

LSDJ には 4bitPCM を再生するための機能があり、そのためのドラムキットが多数公開されています。
仕様によると、1つのドラムキットにつき 15個までの PCMファイルが定義できるようです。
これを個別の PCM ファイルに分解してしまう機能を付けました。



[Ref]で LSDJのドラムキットファイル(*.kit)を選択するか、ドラッグ&ドロップすると選択されたファイルが表示されます。
その状態で [Kit]ボタンを押すと、ドラムキットに含まれている PCM ファイルを出力します。

same as source にチェックが入っている場合は、単に分割するだけです。
サンプルレート変換などは一切行われません。
2data/1byte、終端記号無し、16バイト揃え無し、のデータになります。

4096Hz/8192Hz のいずれかにチェックが入っていると、ダウンサンプリングして出力します(GBMC用)。
2data/1byte、終端記号無し、16byte揃えの強制フォーマットです。
元のサンプリングレートが 11,468Hz なので、4096Hz/8192Hzにすると悲しいことになりますが、使えるものは使いたいということで。

ファイルネームは元のファイル名+キット内の内部サンプル名(3文字)が基本。
ループ指定があれば、そこに「_Loop」がつきます。
同じサンプル名があると困るので、そのときはサンプル番号を追加して区別します。

チェックが色々甘いので、ヘッダのおかしなファイルはうまく変換できないかもしれません。
LSDJで使えている限りは大丈夫だと思いますが。
ヘッダ情報は以下を参考にしました。

http://wiki.littlesounddj.com/RomStructure


▲ TOP