スーファミサウンドの裏で何が行われているか
qSPCの開発も山場を越えた感があるのでたまには進捗報告以外の話を……
多分、普段は宇宙語が書いてあるブログだと思って見てない人が多いと思うので前提知識を手短に書いておくと:
- スーパーファミコンにはメインのCPUと別にサウンド用のCPUが乗っている
- オーディオデバイスにはサウンドCPUからしか触れない
- サウンドCPUはメイン側とは独立したメモリを抱えている
- このメモリは64KBしかない
- メインCPUとサウンドCPUのやり取りには、あまり速くない通信路を使う必要がある
という制約があるので、いかにデータをコンパクトにして、最低限の物だけをサウンドCPUに送るかという所で開発者の腕が試されることになる。で、プロの技はどんなもんだろうかと、メモリダンプを見ながら定番のグラディウスIIIをやってみると……
ボスを倒して、ステージが変わるタイミングで一瞬画面が止まるので、ここで曲を入れ替えていると思ったんだけど、ここでは転送している様子がないな。転送している様子がないという事は、グラディウスIIIのBGMは全曲分、他のデータとの兼ね合いを考えれば数KBに詰め込まれているということになる。
マジで?いくらコナミでもそんなことできるの?
そこで、ゲーム中にミュージックドライバがどういう挙動をしているのか調べてみることにした。具体的には、Snes9xを改造して、S-SMP(サウンド専用CPU)のメモリアクセスと(メインCPUからの)データ転送をログに記録して可視化する。上手くいけばシーケンスデータにアクセスしている様子が浮かび上がるんじゃないか?という目論見だがさて……
・・・
というわけで。CPU/DSPのエミュレーション部分にロガーを仕込んだが、さすがにメモリアクセスを全部記録するとログが恐ろしく巨大になるので、画面の更新時間を基準にして3フレーム(1/20秒)単位で、128バイトごとのリード・ライトをサマリとして出力するようにした。これなら数分ゲームをプレイしてもログは1MBか2MBといったところで現実的。
で、このログをJavascriptで作った可視化ツールに食わすとこうなる。ああでも、スクリーンショットは手作業で貼り付けた。
こんな感じで、青はRAM内のリード、赤はライトを表していて、本体(メインCPU)側からデータをロードすると大量のライト(赤い線)が現れる。シーケンス(楽譜)データを読んでいる部分はまとまったリード(青)という形でなんとなく見える。ちなみに薄い青はDSPによるサンプリングデータの読み出しなので、これが出ている箇所はほぼ確実にサンプリングデータが入っていることになる。
※ちなみに使っているROMはちゃんと自力で吸い出しているので悪しからず
さて、グラディウスIIIの様子を見てみると?
「グラディウスIII」はゲーム中に次の曲を先読みしている
最初に結論を言ってしまうと、全曲を詰め込んでいるわけではなかった。もっと巧妙なことをしていた。
グラディウスIIIのミュージックドライバには、通常の処理をしながらデータを受け取る機能が付いていて、次に流す曲のシーケンスを裏でロードしているのである。
確かにグラディウスというゲームは展開が決まっているので、次の曲を先回りして準備しておくという処理もできそうである。
いやでも、SFC版はボーナスステージってあったよね?
というのが気になったので頑張ってボーナスステージに入るところまで進めてみた。すると……
なんと、ボーナスステージの入り口が近づいてくると、シーケンスを先読みしていることが分かる。プレイヤーが実際にボーナスステージに入るかどうかは分からないので、入らなければこのデータは捨てられるということになる。この時代に投機実行をやってたのか。昔のコナミ凄い。いや、メタルギアシリーズのファンじゃないので、今のコナミに恨みがあるかというと別に無いんだけど、そろそろ外注でいいからグラ6作ってくんないかな。
↓
修正
気になったのでボーナスステージに入らなかった場合と比較したところ、先読みしているのはボスBGMで「ボーナスステージに入るとボスBGMを破棄してボーナスステージ曲をロード」が正しいようでした。まあこれでも投機的と言えるか。もっとも、読み込みにかかる時間を勘案して決めたら結果的にこうなっただけで、最初から意識して実装したわけではないと思う。
◀ ボーナスステージに入った場合
◀ ボーナスステージに入らなかった場合
「ドラゴンボールZ 超武闘伝2」は「気」を溜める裏でサンプリングデータも溜めている
ドラゴンボールZ 超武闘伝シリーズ。
これ、みんなやったよね。今はプリパラやアイカツスターズばっかり見てる人間も昔は毎週ドラゴンボール見てたんですよ。
それは置いといてこのゲーム「デモ必殺技」というシステムがある。お馴染みの「かめはめ波」といった大技を出すと、ゲームの進行が中断しデモ画面が入って、派手に技を撃ってくれるというものである。
(どんなものかは動画を見た方が早い)
ちびっ子達の心を掴むのに一役買ったデモ必殺技だが、このボイスのデータは12KBぐらいある。64KBのうちの12KBはなかなか馬鹿にできないサイズなので、常に置いておくわけにはいかない。そこで、プレイヤーがデモ必殺技のコマンドを入れた瞬間に、通常技用のボイス(これは汎用的に使えるよう、具体的な技名じゃなくて掛け声になっている)を潰して、デモ必殺技用のボイスをロードしている。デモが終わると通常技用のボイスをロードし直してゲームに戻る。
具体的には以下のような感じ(動画とキャラが違ってごめんなさい)
ちなみに1P側と2P側は均等に容量を割り当てられていて、デモ必殺技の時も各プレイヤーの側だけを潰すようになっている。デモ必殺技の途中で撃たれた側がコマンドを入力すると撃ち返せるというシステムがあるので、相手の領域まで潰してしまうと、撃ち返しのボイスが出せないからだと思われる。
ボイス以外の部分はもちろん共用の効果音やBGM用の楽器が入っている、筈。
このゲーム、世間的にはトーセが作ったことになっているようだけど、本当はどうなんでしょうね。ナムコとくっつく前のバンダイが作ってるわけがないのは確かだけど。
テイルズオブファンタジアのボーカル付きオープニング
あれもどうなってるのか見ようと思って秋葉原行ってカセット買ってきたんですけどね……
なんかRetrodeでは吸い出せなくて。
フォーラムにも「困った!」という書き込みがあったけど特に解決したわけでもなく渋い感じで終わってしまった。
カットしていないログの巨大画像はこちら
qSPC 2.0 (ほぼ)完成
qSPC進捗
クイックロード(勝手に決めた用語)実装
64KBのサウンドドライバを丸々入れ替えると2秒ぐらい待たされるので、シーケンスが入っている先頭付近(もうちょっと削れるけど今のところ8KB)だけを入れ替える機能。当然、サンプリングデータは全曲で共通でないとぶっ壊れるので、条件を満たしている場合のみROM内に「クイックロード可」のフラグを立てておく機能をコンパイラに実装している。
ところで、一番苦労した箇所が……ロード時にSpinner(風車みたいなアイコン)を表示する処理。無くてもいいだろ!って言われそうだけど、スーファミ時代に無かった(と思う)UIが表示されるのは面白いかなと思ったのでなんとか実装した。
なぜ苦労したか? どうもPPU(グラフィック)とSPC(サウンド)へのアクセスは競合するらしく、画面更新をVBLANK割り込みで処理したままSPCにアクセスすると、データが不規則にぶっ壊れるのである。残念ながら回路図までは読めないんだけど、エミュレータのソースを見る限り、何か悪いことが起きそうなことだけは確かだった(特にDMA/HDMAが怪しい)。ということで、SPCへの転送中は割り込みを止めて、レギュラーの処理で画面を更新している。
進捗
MMLコンパイラ(qSPC2)
土日でまあまあがっつり、進めた。
シーケンスデータをドライバへの埋め込む部分を主に実装し、次は音色データ周り。
ここは大きく変えようと思っている部分で、なぜかというと旧版は「insts」という固定のフォルダにbrr(SFC用の音声サンプリングデータ)を入れておくとそれが使われる、という仕様だったけど、これだと楽器セットを変えようと思うとフォルダの中身を丸ごと入れ替えないといけない。
よく考えると楽器セットは曲に付随するものなので、曲データの中から自由に指定できた方がいいなということで
#using "jazz" @0 cdefgab
などとすると「jazz」フォルダの中にあるbrr一式(+設定ファイル)が使われるという仕様にした。ADSR等をMML中に無理に書く必要もないなということで、設定ファイルの中に移動。
とりあえず音が出るところまで進めたら、曲を複数持って入れ替える機能(アルバム機能と呼んでいる)の設計をするという流れで。