* Tue Nov 30 06:00:00 JST 2004 Naoyuki Sawa

- ADPCMの仕組み#1




P/ECEの音声データ形式には、通常の「16bit PCM形式」「8bit PCM形式」に加えて、「ADPCM形式」があります。 ADPCM形式には様々な種類がありますが、もっとも一般的なのは16bit符号付きPCM値を4bitに圧縮する方法です。 P/ECEのADPCM形式も、16bit符号付きPCM値を4bitに圧縮しています。 同じ時間の音声データならば、16bit PCM形式と較べて四分の一、8bit PCM形式と較べても半分のサイズになり、音質の劣化も少ないのが特徴です。 僕も、自作のP/ECEゲームで音を鳴らすときには、もっぱらADPCM形式を利用しています。 しかし、三年近くも使い続けていて、未だにADPCMの仕組みを理解していませんでした。。。 今回、Web上の資料とP/ECEカーネルソースを参考に、ADPCMの仕組みを勉強しましたので、その内容を記しておくことにします。 Web上の資料で、ADPCMの仕組みをわかりやすく解説してくれている資料は、意外と少ないようです。 今回参考にさせていただいたのは、「BlendWorks」さんのサイトの『YM2608 ADPCM』の記事。 PC-9801等に利用されているFM音源「YM2608」のADPCM機能を基に、ADPCMの解説と、サンプルプログラムまであります。 とても具体的で丁寧な解説なので、そちらを見れば全部終わってしまうかも知れませんけれど・・・(^^; 僕の場合、理解するのにやや時間を要しましたので、今回は、もっと噛み砕いた解説を目指してみます。
ADPCMは「Adaptive Differential Pulse Code Modulation」の略で、和訳すると「適応的差分PCM」となります。 “過去の値の変化から次の値を予測し、予測値との差をデータとして格納する”PCM方式、といったところです。 なぜ“次の値を予測”する必要があるかというと、格納する値の幅を小さくするためです。たとえば { 101, 198, 303, 401 } という数列があって、次の値が 505 だったとします。単純に前の値との差分を取るだけだと、 505 - 401 = 104 をデータとして格納することになります。 単純に差分を取るだけの方法に対して、予測値との差分を取る方法を使うと、次のようになります。 まず、さきほどの数列を見て、次の値までの変化量はだいたい100ぐらいと予測できますよね?そこで、 401 + 100 = 501 を次の値の予測値とします。で、現実の値は505なので、予測値と現実の値との差分 505 - 501 = 4 をデータとして格納します。 単純に前の値との差分を取った場合の値104に較べて、予測値との差分ならば格納する値はたったの4になりました。 音声データは予測に近い変化をすることが多いので、予測値との差分を取ることによって、値の幅を小さくすることができるわけです。 実際にはここまで単純ではありませんけれど、方針の大枠は以上のようなかんじです。
さて『YM2608 ADPCM』の記事では、先にPCM→ADPCM変換(エンコード)の解説を行い、後でADPCM→PCM変換(デコード)の解説を行っています。 が、ここでは先にデコードの解説を行うことにします。 エンコードよりも、デコードの方が処理が簡単だからです。 まず、YM2608方式のADPCMデコード処理のフローチャートを書いてみました。 処理の手順は『YM2608 ADPCM』の記事と同じですが、用語は僕の独断で日本語に置き換えてみました。 PDFVisio
それでは、一手順づつ追ってみます。 初期状態では“前のデータ”がありませんので、適切な初期値を定める必要があります。 YM2608方式は初期状態で、(仮想的な)前回のPCM出力値を0と仮定します。 PCM値は符号付き16bitで、値の範囲は-32768〜32767なので、0は中央ですね。 また、初期状態での予測変化量を127と仮定します。 なぜ127なのかは、たぶんいろいろ試行錯誤した結果に最善と判断されたのでしょうね。 必然的な値ではないので、初期値が127である理由を深く考える必要はないみたいです。 最初のADPCM値を読み込みました。 最初に述べたように、ADPCM値は1サンプル当り4bitで構成されています。 4bitの内訳は、次のとおりです。
bit3bit2bit1bit0
増減方向変化率
ADPCM値の中に増減方向が含まれている点に注目してください。 YM2608方式のADPCMでは、予測変化量は常に正数で、+か−かは1サンプル毎のADPCM値のbit3にて指定するのです。 変化率は、予測変化量に対する実際の変化量の比率を示します。
変化率実際の変化量
0予測変化量× 1÷8
1予測変化量× 3÷8
2予測変化量× 5÷8
3予測変化量× 7÷8
4予測変化量× 9÷8
5予測変化量×11÷8
6予測変化量×13÷8
7予測変化量×15÷8
予測変化量を中心に、八分の一〜二倍弱までカバーしていることがわかります。 実際の変化量がこの範囲に収まっていれば、おおむね元通りの音声が復元できるというわけです。 増減方向も含めて考えると、次のようになります。
ADPCM値増減方向変化率実際の変化量
00:増加0+予測変化量× 1÷8
10:増加1+予測変化量× 3÷8
20:増加2+予測変化量× 5÷8
30:増加3+予測変化量× 7÷8
40:増加4+予測変化量× 9÷8
50:増加5+予測変化量×11÷8
60:増加6+予測変化量×13÷8
70:増加7+予測変化量×15÷8
81:減少0−予測変化量× 1÷8
91:減少1−予測変化量× 3÷8
101:減少2−予測変化量× 5÷8
111:減少3−予測変化量× 7÷8
121:減少4−予測変化量× 9÷8
131:減少5−予測変化量×11÷8
141:減少6−予測変化量×13÷8
151:減少7−予測変化量×15÷8
初期状態では(仮想的な)前回のPCM値が0、予測変化量が127ですので、最初にカバーできる範囲は次のようになります。 PDFVisio 最初のADPCM値を5、としましょう。一回目のPCM出力値は174、ということになります。 PDFVisio 次回のために、予測変化量を更新します。 今回の予測変化量は127でしたが、実際の変化量はそれよりも大きなもの(174)でした。 そこで、次回の変化量も127よりも大きいだろう見込んで、予測変化量をより大きな値に修正します。 予測変化量を修正する計算式は、今回の変化率によって決まります。 今回の変化率と、予測変化量を修正する計算式との対応は、次のとおりです。
変化率予測変化量を修正する計算式
0次回の予測変化量←今回の予測変化量× 57÷64
1
2
3
4次回の予測変化量←今回の予測変化量× 77÷64
5次回の予測変化量←今回の予測変化量×102÷64
6次回の予測変化量←今回の予測変化量×128÷64
7次回の予測変化量←今回の予測変化量×153÷64
57とか77とかよくわからない係数が並んでいますけれど、これらの係数も試行錯誤によって決まったものらしいので、理由を考える必要はないみたいです。 係数の意味はともかくとして、実際の変化量が予測変化量よりも大きければ次回の予測変化量をより大きく、 実際の変化量が予測変化量よりも小さければ次回の予測変化量をより小さく修正していることがわかります。 なお、予測変化量が127よりも小さくならないよう、また24576よりも大きくならないよう、クリッピングを行っています。 予測変化量が小さくなりすぎると、変化量が大きくなったときに追従が遅れてしまいます。 極端な話、もしも予測変化量が0になってしまうと、その後、どうやっても予測変化量は1以上にならなくなってしまいます。 また、予測変化量が小さくなりすぎると、変化量が小さくなったときに追従が遅れたり、オーバーフローの危険があります。 このような問題を避けるため、予測変化量の上限、下限を決めているのだと思います。 さて、今回の変化率は5、予測変化量は127だったので、次回の予測変化量は 127×102÷64 = 202 となります。 PDFVisio
二回目のADPCM値でカバーできる範囲を示します。 PDFVisio 二回目のADPCM値を10、としましょう。二回目のPCM出力値は48、ということになります。 また、三回目の予測変化量は 202×57÷64 = 179 となります。 PDFVisio 以上のような手順の繰り返しでADPCM→PCM変換(デコード)を行うのが、YM2608方式のADPCMです。
今回は、YM2608方式のADPCMのADPCM→PCM変換(デコード)の手順を追ってみました。 次回は、YM2608方式のADPCMのPCM→ADPCM変換(エンコード)の手順を追ってみます。 さらに次回以降では、P/ECE方式のADPCMの ADPCM→PCM変換(デコード)およびPCM→ADPCM変換(エンコード)の手順を調べ、YM2608方式と比較してみようと思います。 (続きます...)

nsawa@piece-me.org