2003/03/20 改訂版

PlayStation2のムービー(PSS形式)は動画部分はMPEG-2であるが、 オーディオ部分は独自形式である。
オーディオ部分はPCM形式とADPCM形式とDolby Digital(AC-3)形式の3通りがある。
そこで、このページではPSS形式のオーディオ部分のフォーマット について解説しようと思う。

PSSとは

PSSとはPlayStation Streamの略である。
MPEG2 Program Streamに基づいたストリームである。
MPEG2 Video、音声(PCM, ADPCM)、任意のデータを多重化することにより 作製されているストリームの事をさす。

PS2では、このPSS形式がムービーの標準形式となっている。
ゲームにより、一つのムービーごとにファイルが分かれている場合や、 全てのムービーが一つのファイルにアーカイブされている場合もある。

PSSとは別に、メーカー独自のムービー形式を取っていることもある。
PS2ではIPUにより低水準デコード処理ができるので、まれにメーカー独自形式の ムービーが存在する。Final Fantasy Xなどが良い例である。
これに関しては説明が長く難しくなるため、説明は省略する。

以下では、PSS形式についてのみ述べることにする。

PSSファイルの構造

ヘッダに使われている識別子、ブロックサイズなどの数値は ビッグエンディアンで格納されているので注意すること。
以下では識別子はビッグエンディアンで書くので プログラミングの際には注意すること。

識別子の種類

基本的にはデータは
[識別子 4-byte big endian][ブロックサイズ 2-byte big endian][データ]
という形式である。ただし、識別子が 00 00 01 ba の場合は例外。 (下記のType 2.を参照)

識別子が

のどのタイプに属しているか調べる。

Type 4.の場合はブロックサイズ分だけデータをシークさせて次の 識別子を読み込み、上の4つの場合分けを調べる。

Type 3.はファイルの終端を表しているので、この識別子が現れたところで プログラム終了。

Type 2.の場合は、識別子の直後から +0x0a だけデータをシークさせて次の識別子を読み込み、上の4つの場合分けを調べる。
ただし、これは正確ではない。識別子の先頭から +0x0d の位置から1バイトで
reserved: 5-bit value equal to '11111'
pack_stuffing_length: 3-bit MSB
で格納されており、pack_stuffing_length の数だけ 0xff が挿入されている。
よって、場合によっては +0x0a だけシークさせてはまずいかもしれないが、
今のところ pack_stuffing_length = 0 のもの、つまり 1111 1000 = 0xf8 しか見たことがないので
問題はないかもしれない。ちゃんとやるならばこれを考慮した実装にすべき。
たとえば、pack_stuffing_length = 4 なら、1111 1100 = 0xfc となる。


Type 1.の場合が目的とするオーディオデータが続いている。

オーディオデータ部分のヘッダ構造

一番最初にType 1.が見つかった場合と2度目以降のType 1.ではデータの 構造が異なっている。
ここでは一番最初のType 1.のデータ構造を説明する。
一番最初にType 1.が見つかり、オーディオデータが続いている場合 だけ、チャンクなどがあるためヘッダが大きくなっている。

00 00 01 bd [ブロックサイズ 2-byte big endian] [ブロックサイズ分のデータ]

識別子の最初の位置を0バイト目として、初回のみに現れる特殊なヘッダは、

+0x00識別子 00 00 01 bd
+0x04ビッグエンディアンの2バイトのブロックサイズ
+0x06ブロックサイズ分のデータがこの位置から続く
+0x140xa0:PCM, 0xa1:ADPCM
+0x15Stream number(0〜65535) 2バイトビッグエンディアン
+0x17"SShd"
+0x1bSub-header length
+0x1f0x01:PCM, 0x10:ADPCM
+0x23リトルエンディアンの4バイトでサンプリングレート
+0x27リトルエンディアンの4バイトでチャンネル数
+0x2bリトルエンディアンの4バイトでインターリーブサイズ
+0x37"SSbd"
+0x3bリトルエンディアンの4バイトでトータルのオーディオデータサイズ
+0x3fこの位置からオーディオデータが続く。
オーディオデータのサイズは +0x06 から +0x3e までの余分なヘッダ
をブロックサイズから差し引いた分である。

注:初回のみに現れるオーディオデータブロックのヘッダサイズは 0x3f バイトである。
ただし、一番最初のオーディオデータの部分にのみ"SShd"というIDや
サンプリングレートなどが書かれていて、2度目以降のオーディオデータ
にはこのようなチャンクは存在していないので注意すること。
2度目以降のヘッダサイズは 0x17 バイトである。


オーディオ形式の2度目以降のヘッダ、またはデータ形式のヘッダは以下のとおり。

+0x00識別子 00 00 01 bd
+0x04ビッグエンディアンの2バイトのブロックサイズ
+0x06ブロックサイズ分のデータがこの位置から続く。
+0x140xa0:PCM, 0xa1:ADPCM, 0x90:データ(主にDolby Digital(AC-3)に利用される)
+0x15Stream number(0〜65535) 2バイトビッグエンディアン
+0x17この位置からオーディオデータが続く。
オーディオデータのサイズは +0x06 から +0x16 までの余分なヘッダ
をブロックサイズから差し引いた分である。


Stream numberは一つのムービーの中に多言語のサウンド、もしくは複数のデータが入っている場合に利用される。
複数個ストリームがあれば、それぞれに対して初回のみに現れる特別なヘッダが存在する。
以下で示すデコード法はStream numberごとに別々に行う必要がある。

Dolby Digital(AC-3)の場合は、PCM, ADPCMのような初回のみに現れる特殊なチャンクは存在しない。つまり、PCM, ADPCMでいうところの2度目以降のヘッダの形式しか存在しないので注意が必要である。要するにデータ扱いになっている。

オーディオデータのデコード(PCMの場合)

データはステレオの場合、

struct PCM_Data {
    char Left[intlv];
    char Right[intlv];
};
の構造体になっている。ここで、intlvは上記ヘッダのインターリーブサイズである。
つまり intlv*2 バイトが一つのデータのかたまりである。
wavファイルはLRLR…の順番にデータが並んでいるので
*(short*)&Left[0], *(short*)&Right[0], *(short*)&Left[2], *(short*)&Right[2],...
という順番にデータを出力していけばよい。
wavデータなのでshort型で扱いたいが、後々の理由からchar型の方が扱いやすい。

さて、ここで問題が起こるが、ブロックサイズが intlv の倍数で あれば問題はないが、そうでない場合がある。
例えばブロックサイズが 0xffa の場合を考えてみよう。
初回のType 1.の場合、チャンクなどが含まれるのでヘッダサイズが大きく なっており、オーディオデータのサイズは

0xffa - (0x3f - 0x06) = 0xfc1 バイト。

2度目以降では、

0xffa - (0x17 - 0x06) = 0xfe9 バイトになるので注意。
今回は、初回の場合を考える。また、ここではintlv=0x200とする。

0xfc1 = 0x400 * 3 + 0x3c1

なので、構造体で確保できない 0x3c1 バイトのデータが余ってしまう。
この場合、Leftに 0x200 バイト入れ、Rightに 0x1c1 バイト入れることは できる。
このように奇数バイトを格納しないといけないのでchar型を使うとよい。

さて、0x200 - 0x1c1 = 0x3f バイトのRight分のデータはどこにあるかと いうと、次のオーディオデータブロックにある。
次のオーディオデータブロックのデータの最初から 0x3f バイト分が 残りのRightにはいる。
これで、オーディオデータのブロックをまたぐが、0x200 バイト分のRightデータ が用意できたことになる。
以降は、0x400 バイトの構造体が続き、余った部分は今説明したことと 同じことをやっていけばよい。
このように、データブロックをまたいでオーディオデータが繋がっていく わけだが、
トータルのオーディオデータサイズは intlv*2 の倍数にちょうど なっているので、ファイル終端処理の問題は生じない。

オーディオデータのデコード(ADPCMの場合)

1c 00 01 23 45 67 89 ab cd ef 01 23 45 67 89 ab

の様な 0x10 バイトをひとかたまりとする形式で、
0バイト目がスケール、フィルタ係数を決める。
1バイト目は通常 00 固定。00 以外はデータ開始やデータ終端を表す。
残り 0x0e バイトがオーディオデータ。

データはステレオの場合、

struct ADPCM_Data {
    char Left[intlv];
    char Right[intlv];
};
の構造体になっている。
デコードはLRを別々に行い、0x10 バイトをひとかたまりとした デコードをする。

この場合も、PCMの時と同様、0x10 バイトに満たないところで データブロックが終わり、
次のデータブロックから続きが始まるという構造になっている。
また、PCM の時と同様にトータルのオーディオデータサイズは intlv*2 の倍数 になっているので問題は生じない。
ただし、一番最後のデコードに際しては、構造体全てがADPCMデータではなく、 途中でデータ終端になる。オフセット 1 バイト目が 00 以外になっていることで 終端が判定できる。

オーディオデータ(Dolby Digital(AC-3)の場合)

AC-3データはPSS形式ではオーディオとしてではなく、データとして扱われる。
したがって、最初に書いたように、オーディオ用のヘッダなどは存在しないので 注意が必要である。
データとして扱われる以上、AC-3データをどう埋め込むかはゲームに依存する。
そのまま、AC-3データが続くものや、AC-3用のヘッダが付加されているものなどがある。
2byte単位のデータも、リトルエンディアンであったりビッグエンディアンだったりする。
このあたりはゲームに依存するので、AC-3データを抽出する場合はゲームごとに 処理を変える必要がある。

参考文献

ADPCMのデコード法に関しては、

XA ADPCMフォーマット解説
Final Fantasy VIII ムービー吸出し(プログラムのソースがdownloadできます。)

を参考にしてください。0x10 バイト単位でのデコード法は後者のリンクから
ダウンロードできるFF8関連のツールのソース中に書いてあるので参考にしましょう。

Dolby Digital(AC-3)の仕様書は、ATSC Document A/52A を参照してください。
古いAC-3の仕様書は、ATSC Document A/52(20 Dec 95) を参照してください。