Topページに戻る

スマイルデコーダのサウンドオプションにIMA ADPCMを使ってみた

デコーダ開発ボードにスピーカとコマンドステーション(DCS50K)を接続して評価中(の写真)


【クリックすると画像が大きくなります】

IMA ADPCMとは

IMA ADPCMは、インタラクティブ・マルチメディア協会(IMA:Interactive Multimedia Association )で
仕様化したADPCMフォーマットで、DVI ADPCMと同じ仕様になります。
Sound OptionではPCM音声データを使用していますが、非圧縮データとなっており、サンプリング周波数×
時間分のデータが必要になります。
今回は良く使われている?といわれているIMA ADPCMを使ってみます。
IMA-ADPCMは16bitリニアPCM音声データを4bitに圧縮されますので、PCM音声データと比較すると音声デー
タを1/2にすることが出来ます。

※今回は8bitのOCR2を使うため、デコードされた16bitデータを8bitに間引いて使っています。

■参考にしたリンク
WAVEファイルを読み込む
音声処理
WAVE形式のファイルフォーマット

■IMA ADPCMで参考にしたリンク
chanさんのソースファイルとマイクロソフトのRIFFフォーマット拡張仕様はすごく参考にさせてもらいました。
chanさんのWebページから:ADPCMの実験
RIFFフォーマット拡張仕様(chanさんのWebページから)
RIFFフォーマット拡張仕様(マイクロソフトサイト)
5.5.4 IMA-ADPCM Audio Dataの基本構造わかりやすい
電波系電波時計〜DVI ADPCM(IMA ADPCM)DE-CODE解説
AVR336:ADPCM Decoder
AN643:Adaptive Differential Pulse Code Modulation using PICmicro[tm]Microcontrollers
P/ECE研究記録〜ADPCMの仕組み

■wiki
IMA ADPCM
DVI ADPCM
ADPCM

■Arduino スマイルデコーダスケッチ
DSDCCSpeakA_E233.ZIPやあさんにまとめてもらったバージョン
β4のダウンロード私がいじり倒しているバージョン

■ツール
ADPCM用のヘッダファイルの作成やあさん作成ツール
Audacity

IMA ADPCMへの変換

IMA ADPCMデータへの変換は、Audacityを使いました。

ファイル→オーディオの書き出しの時に、以下の図のような設定をしてIMA ADPCMデータを作成しました。


【クリックすると画像が大きくなります】

バイナリファイルからテキストファイルへ

IMA-ADPCMのバイナリファイルは、以下のPerlスクリプトでテキストベースの.H形式に変換しました。

Perlスクリプトは、ココにあったのですが
Webページが無くなってしまいました。
「うー!にゃー!」をArduinoで再現してみた その2
これは、ArduinoベースのIMA-ADPCMを使っているひとつのサンプルだったのですが・・・

やあさんが、waveデータからヘッダファイルを作成するDSWave2C_20151105.ZIPを作成して頂きましたので
Perlが苦手の人は良いかもしれません。

#-------------------------------------------------------------------------------
# sdbin2h.pl
#-------------------------------------------------------------------------------

$op0 = $ARGV[0];
$op1 = $ARGV[1];
$op2 = $ARGV[2];

$inf=$op0;
$outf=$op1;

print "IN=$inf\n";
print "OUT=$outf\n";

$size = -s $inf;         # ファイルのサイズを得る
open(IN, $inf);          # ファイルを開く
open(OUT, ">$outf");          # ファイルを開く
binmode(IN);                   # バイナリモードにする


print OUT "
// $inf

const signed char $op2 _data[] PROGMEM ={
";

for(;;)
{
	read(IN, $buf, 20);         # データを読み込む
	last if(eof IN);

	 @c = unpack("C*",$buf);
	 	 	print OUT "    ";
	foreach $data (@c)
	 {
$data = sprintf"0x%02x",$data;		#16進数で出力
	 	 	print OUT  "$data";
	 	 	print OUT  ",";
	 }
	 	 	print OUT  "\n";

}

print OUT "
};
";

close(IN);                     # クローズする
close(OUT);                    # クローズする
#-------------------------------------------------------------------------------
Windowsで実行する場合は、ActivePerlが必要です。

コマンドラインで↓のような感じで実行します。(下記例はDOS窓で実行しました。)
(c:\Perl\foo に、sdbin2h.pl , wave ファイルをあらかじめ格納してあります。)

C:\Perl\foo>

C:\Perl\foo>perl sdbin2h.pl E233_Alert1_imaADPCM.wav E233_Alert1_imaADPCM.h E233_Alert1
IN=E233_Alert1_imaADPCM.wav
OUT=E233_Alert1_imaADPCM.h

C:\Perl\foo>perl sdbin2h.pl E233_Alert3_imaADPCM.wav E233_Alert3_imaADPCM.h E233_Alert3
IN=E233_Alert3_imaADPCM.wav
OUT=E233_Alert3_imaADPCM.h

C:\Perl\foo>perl sdbin2h.pl E233_break1_imaADPCM.wav E233_break1_imaADPCM.h E233_break1
IN=E233_break1_imaADPCM.wav
OUT=E233_break1_imaADPCM.h

C:\Perl\foo>

こんな感じにテキストファイル化されます。
Perl でテキストを出力された直後は、1kh _data[] のように、半角スペースが開いているので
半角スペースを削除します。

// 1khz.wav
const signed char 1kh_data[] PROGMEM ={
    0x52,0x49,0x46,0x46,0x34,0x20,0x00,0x00,0x57,0x41,0x56,0x45,0x66,0x6d,0x74,0x20,0x14,0x00,0x00,0x00,
    0x11,0x00,0x01,0x00,0x40,0x1f,0x00,0x00,0xd7,0x0f,0x00,0x00,0x00,0x01,0x04,0x00,0x02,0x00,0xf9,0x01,
    0x66,0x61,0x63,0x74,0x04,0x00,0x00,0x00,0x20,0x3f,0x00,0x00,0x64,0x61,0x74,0x61,0x00,0x20,0x00,0x00,
    0xbe,0x06,0x00,0x00,0x77,0xf7,0xff,0x1f,0x77,0xf4,0x9c,0x41,0x13,0xba,0x9d,0x41,0x13,0xba,0x9d,0x41,
    0x13,0xba,0x9d,0x41,0x13,0xba,0x9d,0x41,0x13,0xba,0x9d,0x32,0x14,0xc9,0x9b,0x41,0x23,0xca,0x9c,0x41,
   ・
   ・
   ・
    0x80,0x08,0x80,0x80,0x08,0x80,0x08,0x80,0x08,0x08,0x08,0x08,0x08,0x80,0x80,0x80,0x80,0x80,0x90,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

};
PCMとIMA-ADPCM(4bit DVA-ADPCM)のRIFFヘッダファイルの比較

PCMとIMA ADPCMとの差を調べるために、RIFFのヘッダ部を表にしてみました。
若干違うのが確認できるかと思います。

・データ開始バイトの違い
 PCMは44byte目からデータで、IMA ADPCMは64byte目からです。
・60byte目のiSamp0(2byte)最初のオフセット値(IMA ADPCM特有)
・62byte目のbStepTableIndex(1byte) indexテーブルの初回値(IMA ADPCM特有)

// E233_break1.wav(PCMフォーマット)
Offset00010203040506070809
DATA0x520x490x460x460x8e0x220x000x000x570x41
内容R I F F ファイルサイズ:0x228e(8846byte)W A
Offset10111213141516171819
DATA0x560x450x660x6d0x740x200x100x000x000x00
内容V E f m t _ format定義サイズ:0x10(16d)
Offset20212223242526272829
DATA0x010x000x010x000x400x1f0x000x000x400x1f
内容formatID:0x0001channel数:0x0001 sampl rate:0x1f40:8000Hz data転送速度:0x1f40
Offset30313233343536373839
DATA0x000x00 0x010x000x080x00 0x640x610x740x61
内容. ブロックサイズ 量子化ビット数:8bit d a t a
Offset40414243444546474849
DATA0x6a0x220x000x000x840x870x870x870x860x87
内容バイト数:0x226a(8810d)......

// E233_break1_adpcm.wav(IMA ADPCMフォーマット)
Offset00010203040506070809
DATA0x520x490x460x460x340x120x000x000x570x41
内容R I F F ファイルサイズ:0x1234(4660byte)W A
Offset10111213141516171819
DATA0x560x450x660x6d0x740x200x140x000x000x00
内容V E f m t _ format定義サイズ:0x14(20d)
Offset20212223242526272829
DATA0x110x000x010x000x400x1f0x000x000xd70x0f
内容formatID:0x0011channel数:0x0001 sampl rate:0x1f40:8000Hz data転送速度:0x0fd7
Offset30313233343536373839
DATA0x000x000x000x010x040x000x020x000xf90x01
内容. バイトサンプル 量子化ビット数:4bit 拡張サイズ:2byte . .
Offset40414243444546474849
DATA0x660x610x630x740x040x000x000x000x820x23
内容f a c t チャンクサイズ:0x0004(4byte) サンプル数:0x2382
Offset50515253545556575859
DATA0x000x000x640x610x740x610x000x120x000x00
内容. d a t a データサイズ:0x1200(4608byte)
Offset60616263646566676869
DATA0x000x040x000x000x770x770x870x800xaf0x20
内容iSamp0(2byte)bStepTableIndex . . . . . .

参考

アドレスオフセットコード意味詳細
(HEX)(HEX)..
+0052"RIFF"
(ckID)
.
+0149.
+0246.
+0346.
+04
+05
+06
+07
34
12
00
00
ckSize
(4Byte)
.
+0857"WAVE"
(formType)
.
+0941.
+0A56.
+0B45.
+0C66"fmt "IMA ADPCM形式のフォーマット情報
(サイズは28Byte固定)
+0D6d
+0E74
+0F20
+10
+11
+12
+13
04
00
00
00
ckSize:0x00000004
(4Byte)
.
+14
+15
11
00
wFormatTag:0x0011
(2Byte)
(0x0011:IMA ADPCM形式)
+16
+17
01
00
nChannels:0x0001
(2Byte)
(0x0001:モノラル)
+18
+19
+1A
+1B
40
1F
00
00
nSamplesPerSec:0x00001f40
(4Byte)
(0x1f40:8kHz)
+1C
+1D
+1E
+1F
D7
0F
00
00
nAvgBytesPerSec:0x00000fd7
(4Byte)
(0x0FD7:8kHz,4bit モノラル)
+20
+21
00
01
nBlockAlign:0x0100
(2Byte)
(0x0100:256Byte)
+22
+23
04
00
wBitsPerSample:0x0004
(2Byte)
(0x0004:4bit)
+24
+25
02
00
cbSize:0x0002
(2Byte)
(0x0002:2byte)
+26
+27
F9
01
extByte[cbSize]:0x01F9
(2Byte)
.
+2866"fact"IMA ADPCM形式のフォーマット情報
(サイズは28Byte固定)
+2961
+2A63
+2B43
+2C
+2D
+2E
+2F
04
00
00
00

(4Byte)
.
+30〜
+33
 サンプル数:0x2382
(4Byte)
.
+3464"data"IMA ADPCM形式のフォーマット情報
(サイズは28Byte固定)
+3561.
+3674.
+3761.
+38〜
+3B
 データサイズ:0x1200(4608byte)
(4Byte)
.
+3C〜
+3D
 iSamp0
(2Byte)
.
+3E〜
+3F
 bStepTableIndex
(2Byte)
.
+0C〜
+27
 fmt-ck
(28Byte)
IMA ADPCM形式のフォーマット情報
(サイズは28Byte固定)
+28〜
+33
 fact-ck
(12Byte)
チャンネル単位のブロックあたりの
サンプル数

RIFFフォーマット拡張仕様(マイクロソフトサイト)
Multimedia Data Standards Update April 15, 1994 Page 32 of 74 より

■Header
This is a C structure that defines the DVI ADPCM block header.
これは、DVIのADPCMブロックヘッダを定義するCの構造体です。
typedef struct dvi_adpcmblockheader_tag {
    int  iSamp0;
    BYTE bStepTableIndex; 
    BYTE bReserved;
} DVI_ADPCMBLOCKHEADER;
FieldDescription
iSamp0The first sample value of the block. When decoding, this will be used as the previous sample to start decoding with.
ブロックの最初のサンプル値。デコードするとき、これはとデコードを開始する前のサンプルとして使用されます。
bStepTableIndexThe current index into the step table array. (0 - 88)
ステップ テーブル配列に現在のインデックス。
bReservedThis byte is reserved for future use.
このバイトは、将来の使用のために予約されています。


■Data
The data words are interpreted differently depending on the number of bits per sample selected.
For 4 bit DVI ADPCM (where is equal to four) each data word contains eight sample codes as shown in the following diagram.
データワードが選択されたサンプル当たりのビット数に応じて異なって解釈されています。
次の図に示すように(は4に等しい)4ビットのDVI ADPCMの場合は、各データ・ワードは、8つのサンプルコードが含まれています。

アドレスオフセット(Data Word1)コード意味詳細
(HEX)(HEX)..
+0000iSamp0:0x0400LoByte
+0104HiByte
+0200bStepTableIndex:0x00
(1Byte)
.
+0300bReserved:0x00.
+0477Byte0:0x77.
+0577Byte1:0x00.
+ n**Byten:0x**.
+FE9DByte251:0x9D.
+FF41Byte252:0x41.



アドレスオフセット(Data Word2)コード意味詳細
+007AiSamp0:0x3F7ALoByte
+013FHiByte
+0253bStepTableIndex:0x53
(1Byte)
.
+0300bReserved:0x00.
+0491Byte0:0x77.
+05ACByte1:0x00.
+ n**Byten:0x**.


ADPCMスケッチについて

コマンドステーション DCS50K用に若干手を加えてあります。
F1,F2,F3,F4 にサウンドを割り当ててあります。
β2のダウンロード

既知の問題(^^;
・AudacityのIMA ADPCM変換なのか、またはデコーダプログラムの処理が足りないのかわからないのですが、
 IMA ADPCMからPCM変換時に、レベルが大きく狂い【プチ】音が発生してしまう。
・フェードアウト処理は未確認
・ソースが汚いw
・デコード部分が大きい
・再生時にSerial.print()を使うと音程が低くなりますw

わかっていること
・1kHzのサイン波を再生させると、途中途中で波形が崩れることがわかっています。
 赤:PCMデータ
 青:IMA ADPCMデータ(極性bit+振幅data)なので、波形にするには適しませんが・・・

【クリックすると画像が大きくなります】

・IMA ADPCMデータに0xffが入っていると、デコードさせると上図の様な波形が出来てしまう。

【クリックすると画像が大きくなります】

 Audacityの波形表示ではちゃんと表示されているようですし・・・再生させてもちゃんと再生できるし
 デコーダプログラムに足りないアルゴリズムがあるのか?
 手っ取り早く、エンコーダーを変えてみようかなーと考え中。

この辺の事情に詳しい方がおられましたら、あやのブログ、掲示板にてアドバイスいただけると助かります(^_^)

プチ音調査1

1、Audacityによる波形表示による比較
  振幅が所々変わるようです。だけど、Audacityで再生させてもプチ音は発生しません。

上側が8bit PCMで、下側がIMA ADPCMの波形です。


【クリックすると画像が大きくなります】

2、エンコーダソフトを変えてみる
 WindowsXP付属のサウンドサウンドレコーダーが必要ですが、IMA ADPCMに変換することができます。

【クリックすると画像が大きくなります】
 似たように 0xFFが含まれるエンコード結果でした・・・

3、IMA ADPCMデータを細かく見てみる
IMA ADPCM検証データ←エクセルのデータです。

【クリックすると画像が大きくなります】

【クリックすると画像が大きくなります】

IMA ADPCMフォーマットを最近結構みているので、各信号の意味がわかりつつありますが、
初見の人用には説明を書いてないのでわかりにくいと思いますが・・・
結果がわかりやすくする為に検証データは1kHzのサイン波のデータを使用しています。
512ポイント毎にscale_change_tblが[8]になるようである(ピンクのグラフ)
その結果、stepテーブルが増えるため、デコード値が大きく増加してしまう。
・・・
デコードするときに512ポイント毎にブロックの最初のサンプル値に改めてリロードする処理を
入れるのかな?

プチ音調査2

IMA ADPCMは、データ内部に256byte毎にサンプル値とステップ(量子化テーブル)が埋め込まれているため、
再設定する必要がある事がわかりました。
カウンタを追加して、サンプル値とステップ(量子化テーブル)を読み取るプログラムを追加しました。
本来はRIFFで定義されている値を取り出してスマートに書くと良いですw
ソースファイルも若干整理しなおしました。
とりあえず、IMA ADPCMから16bit PCM を生成して、8bitに間引くタイプはこれでクローズにしようかと思います。

β4のダウンロード

1kHzのサイン波をプロットすると、256Byte毎のつなぎ目が無くなり、きれいなサイン波になりました。


【クリックすると画像が大きくなります】

フェードアウト処理がデータ上、フェードアウト途中で終わっている様であるとブログに書きましたが実際波形をプロット
してみるとこんな感じでした。


【クリックすると画像が大きくなります】



2015/11/7 初版
2015/11/8 ちょっと調べた内容を追加
2015/11/9 ちょっと調べた内容を追加、画像を縮小させました。