サウンドデータの解析
ZANACのサウンドデータについての解析です。


サウンドドライバとサウンドデータ
この文書では、サウンドドライバとは『サウンドデータを元に、ゲーム進行に合わせて音楽や効果音を演奏するプログラム』、
サウンドデータは『サウンドドライバに渡す音楽や効果音のデータ(楽譜のようなもの)』という意味で使っています。

サウンドドライバは、各ゲームごと、もしくはゲームメーカーやプログラマごとに異なります。 また、サウンドドライバ用のサウンドデータも、そのサウンドドライバが理解できる形で作成する必要があるので、サウンドドライバごとに異なることになります。

そのため、「あるゲームの音楽を解析して演奏したい!」ということであれば、まずサウンドドライバを解析してサウンドデータの形式を特定し、それに沿ってサウンドデータを解析し、 楽譜、もしくはMIDI等、別形式のサウンドデータに変換する必要があります。

ただし、プログラムを調べるのが面倒ということであれば、特にサウンドデータに重点を絞って解析することもできると思います。

音楽データは、
・音程データ・・・発音する音の高さ、すなわち音程の情報
・音長データ・・・発音する音の長さの情報
・制御データ・・・その他、ある部分の繰り返し(ループ)等の制御構造、音色の変更、左右のスピーカーへの割り振り(ステレオ)設定などの情報

の集合と言えますので、暗号解読のような形でサウンドデータの意味を理解することも無理ではありません。
「NES版ZANACの曲をディスク版に」を書いた当初も、この形でファミコン版ZANACのサウンドデータ解析を行っていました。


ZANACサウンドデータ解析(1) サウンドデータの場所
以下に、暗号解読風にサウンドデータを解析した方法を記します。他のゲームでも参考になるかもしれません。

まずは、そのゲームのROMイメージ等の中で、サウンドデータがどこにあるかを知る必要があります。
キャラクタグラフィックデータの形式がわかっていれば、視覚化してみて、明らかに違う部分を除くこともできます。


サウンドデータの場所を突き止める際、キーポイントとなるのは「曲データ開始アドレステーブル」です。

多くのゲームでは、サウンドドライバは、ゲームのメインプログラムとは切り離されて別個に動いている(割り込み処理など)ことが多いようです。 メインプログラムでは他にもすることがたくさんあり(自機や敵キャラクタ表示、マップの読み出しと表示)、音楽のみにかかりきりになることはできないからでしょうか?

サウンドドライバで割り込みを使う理由は、タイマ割り込み等を使用して正確な発音時間決めるため、という理由のほうが大きいようです。 また、メイン処理と音楽データ解析&演奏処理を分割でき、プログラムのメンテナンス性も向上します。 ひょっとしたら、サウンドドライバだけ別のゲーム用に使いまわしたりもできるかもしれません。

このようにメイン処理とサウンドドライバとが分割されていれば、ゲーム中で「ああ、ゲームオーバーだから、悲しい曲を演奏したいなあ」という必要が発生した場合、 メイン処理が、いちいち目的の曲データの1バイト目から読み出し、楽譜の通りに発音し、休符があったら発音を止める・・・といった面倒な処理は行う必要はありません。
演奏したい曲をサウンドデータに伝えるだけで良いことになります。

ZANACでは、05199H近辺のルーチンがこの役割をしています。
「Aレジスタに入れられた曲データ番号を元に、RAMのサウンド用ワークエリアを初期化する」ルーチンです。
このとき、曲データ番号と実際の曲データの位置を関連付けているのが、先ほど出てきた「曲データ開始アドレステーブル」です。


ROMイメージから曲データテーブルを探す方法ですが、これは直感しかないようです。
2バイトごとに、だいたい昇順にデータが並んでいる部分を探してゆきます。といっても、ROMイメージの中には他にもマップデータテーブル、敵キャラクタテーブル等もあるので、一筋縄ではいかないかもしれませんが・・・。

ちなみに、それが曲データテーブルなのか?を調べるには、バイナリエディタ等でテーブルの値を書き換えてみます。
要は全てのテーブルの内容を同じ値にしてしまえばいいのです。それでゲームを実行してみて演奏される音楽が同一の曲になれば、そこが曲データテーブルです。
また、この方法で「テーブルの何番目がどの曲の開始アドレスを表しているのか?」も調べることができます。

ZANACでは、05236H〜0526BHまでが曲データテーブルです。
全部で54バイトなので、2バイトごとに区切ると27曲分の曲データ開始アドレスが判ります。
「MSXのZANACってそんなに音楽の種類あったっけ?」と思うかもしれないですが、バックに流れる曲以外にも、爆発音、武器発射音等の各種効果音のデータも曲データとして扱われているためです。


ZANACサウンドデータ解析(2) サウンドデータの構成
判明した曲データの開始アドレス、およびそれがどの曲を示しているか?の情報から、各曲ごとにデータを見比べてみます。

サウンドドライバの解析ができていない状態では、一見、どこをどう見ればいいか途方にくれるかもしれませんが、そのような場合は 「短い曲」「鳴っているチャンネル数が少ない曲」のデータに注目します。
(チャンネル数について・・・MSXでは、最大同時3チャンネル(ただし、3チャンネル目はノイズも発音可能らしい)発音可能です。
例えば、ゲーム中のメインテーマは3チャンネルをフルに使って演奏されていますが、効果音などは1チャンネルのみで演奏されています。)

すると、各曲データの頭1バイトについては、「発音するチャンネル数」を示していることに気づきます。

次に、曲データの2バイト目以降から見ていくと、1バイト目のチャンネル数分、9バイトの情報が並んでいます。9バイトの情報の最後の2バイトについては、何かのアドレスを示しているようで、 特に1チャンネルのみの曲データの場合、9バイトデータの直後のアドレスが記載されていることがわかります。
この9バイトのデータを、「チャンネル毎のヘッダ情報」と呼ぶことにします。

ヘッダ情報には、各チャンネルのデータがどこから始まっているのか?の情報とともに、初期状態でのそのチャンネルのボリューム等の情報が記載されています。
しかし、これはサウンドドライバの解析が進むまでは、一時置いておいてもいいでしょう。

【ZANACの曲データの構成について】
・1バイト目には発音するチャンネル数の情報(1バイト)
・各チャンネル毎のヘッダ情報(9バイト×チャンネル数)
・以降、ヘッダ末尾に示されたアドレスから、チャンネル毎の曲データが表記される


ZANACサウンドデータ解析(3) サウンドデータの内容
ここまでわかれば、後は各チャンネルの曲データについて、これが実際の音符にどのように対応しているのかを調べるだけです。

解析対象は、せっかくなので1UP音に着目しましょう。この曲はCOMPILE社ゲームの多くに使用されているもので、ZANAC以外で耳にする機会も多いです。

05660H〜0568AHが1UP音のデータです。
まずは、先頭の19バイト(1+9+9)は飛ばし、各チャンネルのデータを見比べます。
音楽的素養のある人であれば、1UP音の楽譜を頭に思い浮かべるか、もしくはどんな楽器でもいいので実際に演奏してみましょう。

全くの余談ですが、昔のゲームではプレイヤーの音楽的センスが問われるものがいくつかありました。
メガドライブの「ソーサリアン」という非常に面白いゲームがありました。 ゲーム中盤、鳴っている曲の音程が判る『絶対音感』に近い素養を要求されます。ある特定の曲を、ゲームフィールドに設置されたサンゴを叩いて再現しなければならないのですが、 中学生時代、当然絶対音感の無い私は、小学生以来使っていなかった鍵盤ハーモニカ(ピアニカ)を取り出してゲームに臨みました。

以下、楽譜の代わりにMML表記による1UPの曲を記載します。
"TSS Clipborad Player"(以下TSSCP)準拠のMMLですので、TSSCPをお持ちの方は以下の二行をコピーすることで、実際の音楽が演奏されます。

t108%0@1v15 l8 o6e  l16e g+f+e g+<c+l4> b ;
t108%0@1v15 l8 o6c+ l16c+c+d d e  e l4 g+ ;

ZANACの曲データに戻ると、

1番目のチャンネルでは「38 E5 38 E3・・・82」
2番目のチャンネルでは「35 E5 35 E3・・・82」

というデータになっています。これと実際の楽譜(もしくはMML)とを比較してみましょう。

各チャンネル先頭に「38」「35」があり、その後、同じく「E5」「E3」が続いています。チャンネルデータの最後には「82」があります。

先ほどのMML(楽譜)を見ると、1行目すなわち1チャンネル目は「o6e」(オクターブ6のミ)、2行目すなわち2チャンネル目は「o6c+」(オクターブ6のド#)の発音で始まっています。
また、eとc+は、半音で3だけずれています。つまり、「38」「35」は、それぞれ「o6e」「o6c+」を示す「音程データ」であると推測できます。

次に、「E5」「E3」については、1チャンネル目と2チャンネル目で同じ位置に記載されています。MMLを見ると、両チャンネルとも、出だしの2音は同じ長さで発音されています。(l8=8分音符、l16=16分音符)
よって、これらは音の長さを示す「音長データ」であることがわかります。

最後に、曲データの末尾にある「82」については、ただデータの終わりを示しているだけではなく、重要な意味を持っています。
曲データには、「演奏が終わったらループして演奏を繰り返す曲」「一回のみ演奏する曲」の二種類があります。メインの曲などは前者、効果音などは後者です。
今回の1UP音は後者の一回のみの曲ですので、演奏が終わったらもう繰り返してはいけない、ということをサウンドドライバに教えてあげなければいけません。
この「82」は、曲データの終わりを示す「制御データ」となります。


ZANACサウンドデータ解析(4) 音程データ、音長データ、制御データについて
音程データ、音長データ、制御データのそれぞれについてまとめます。
各チャンネルデータは、16進数表記で以下の3つのいずれかに分類されます。

000H〜07FH  音程データ
0E0H〜0F2H? 音長データ
080H〜0DFH  制御データ

まず音程データについては、00のときは休符を示し、値が1増える毎に半音階上がります。実際の音程との対応は、演奏してみて決めるのがよいでしょう。
この文書では「035H」を「o6c+」(オクターブ6のド#)としています。


次に音長データは、19段階で発音の長さを表します。この音長データと発音長さの対応には、「音長テーブル」が使用されます。
ZANACでは0526CH〜0527EH近辺に、1バイト×19個のテーブルがあります。最低の発音単位は、テーブル上では"1"と定義されています。 おそらく、1=1/60[sec]位ではないかと思われます。
以下に、テーブルの値を、さらに音符で表現した表を示します。

表:音長テーブルと実際の発音長さの関係
音長データ発音長さ(16進表記)発音長さ(10進表記)MMLのlコマンド音符
E00x0116464分音符
E10x0223232分音符
E20x03332.付点32分音符
E30x0441616分音符
E40x06616.付点16分音符
E50x08888分音符
E60x0c128.付点8分音符
E70x101644分音符
E80x18244.付点4分音符
E90x203222分音符
EA0x30482.付点2分音符
EB0x40641全音符(1分音符)
EC0x60961.全音符の1.5倍
ED0x801281^1全音符2つ分
EE0xc01921^1^1全音符3つ分
EF0x000????ゼロ!?
F00x12184^324分音符と32分音符
F10x1C284.^16付点4分音符と16分音符
F20x24362^162分音符と16分音符

テーブル中E0〜EEは、その上の値の2倍、もしくは1.5倍になっています。
EFは、ちょっと特殊で発音長さ0を持っています。0=発音しない、ということのようにも見えますが、「止めるまで発音し続ける」という 特殊な音長データのようです。
また、F0〜F2については、他の音長の2倍、1.5倍で表現できない、特殊な長さの音長データです。


最後に、制御データについてですが、これらはデータの後、1〜2バイトの追加データを取ることが多いです。
以下に制御データの一覧を示します。ただし、まだ不明な部分も多いです。

表:制御データ一覧
データ意味
80 ll hhhhllアドレスにジャンプ
81 ll hhループカウンタを-1し、アドレスhhllにジャンプ
82演奏終了
83 ll hh不明
84 nn不明
85 nn不明
86 nn音量相対変化
87 nn他の曲演奏
88 nnループカウンタをnnにセットする。
89 nn不明
8A ll hh移調読み。(hhll+現在のループカウンタ値)で示されるアドレスの値だけ音程を変化させる。
8B ll hh不明
8C ll hh不明
DF nn音長の即値指定?

制御データで特に注目すべきなのが、8Aの「移調読み」というものです。
これは、ある曲データを繰り返して演奏する際、発音する音程を変化させて演奏する機能です。
カラオケなどで「キーを変えて低くして歌う」などの機能があると思いますが、それと同様のものです。
この機能により、サウンドデータをかなり小さくまとめることができます。

いま、以下のような曲データを考えます。

t72%0@1v14 l32o3
a<<a>a<e<e>a>aa> a<<a>a<e<e>a>aa> {・・・(1)}
a<<a>a<e<e>a>aa> a<<a>a<e<e>a>aa
c<<c>cg<gc>cc> c<<c>cg<gc>cc> {・・・(2)}
c<<c>cg<gc>cc> c<<c>cg<gc>cc>
f<<f>f<c<c>f>ff> f<<f>f<c<c>f>ff> {・・・(3)}
f<<f>f<c<c>f>ff> f<<f>f<c<c>f>ff>
e<<e>eb<be>ee> e<<e>eb<be>ee> {・・・(4)}
e<<e>eb<be>ee> e<<e>eb<be>ee;

全体をぱっと見ると、(1)〜(4)の4種類のMMLデータが、2行ずつ並んでいるかのように見えると思います。 (>の有無など、細かい部分は無視してください)

このデータを繰り返し命令などを使って、より小さくまとめようとする場合、普通であれば 「(1)を2回繰り返す」「(2)を2回繰り返す」・・・とまとめていくのですが、それでもデータ量としてはせいぜい、半減するくらいです。

t72%0@1v14 l32o3
[2 a<<a>a<e<e>a>aa> a<<a>a<e<e>a>aa>]< {・・・(1')}
[2 c<<c>cg<gc>cc> c<<c>cg<gc>cc>] {・・・(2')}
[2 f<<f>f<c<c>f>ff> f<<f>f<c<c>f>ff>] {・・・(3')}
[2 e<<e>eb<be>ee> e<<e>eb<be>ee>] {・・・(4')}

しかし、ZANACのサウンドデータでは、この曲の演奏は「(1)を8回繰り返す」という命令と、先出の移調読みの命令の組み合わせで表記されています。

(2)〜(4)のMMLデータは、それぞれ(1)を基準に、半音で3、8、7ずらしたデータとなっています。
移調読み命令では、現在のループカウンタを元に、hhllで示される移調読みデータテーブルから、半音でいくつ音程変更をするのかのデータを読み取り、 演奏する音程を変化させます。

初めてこの仕組みを見つけたとき、「ZANACのサウンドドライバはここまで考えられている、なんて凄いんだ!」と感銘を受けたのですが、TSSCPのマニュアルを見ると、「マクロ命令」というもので同様の機能があるようです。結構、サウンドドライバとしては普通の機能なのでしょうか・・・?
いつか、ZANAC開発当時にリリースされていた、他のゲームのサウンドドライバも解析して比べてみたいものです。


サウンドデータについてはここまで解析できれば、一応、他の楽器などで演奏できるところまではこぎつけられます。
しかし、ソフトウェアエンベロープ、メインの曲と効果音の同時演奏などについては、やはりサウンドドライバの解析を行わないと難しいところがあります。
サウンドドライバについてはまだ解析中のため、今後に譲ることとします。

"TSS Clipborad Player"でMSX版ZANACの曲を聞くも見てみてください。

(2006/03/27)


ZANAC解析のトップへ戻る

トップへ戻る