音声にしろ動画にしろ,おおくのコーデックの処理は複雑で,ライブラリのお世話にならなければならない. しかし,電話などでつかわれる G.711 という ITU-T 標準のコーデックは非常にかんたんであり,ほとんどテーブル引きだけで実現することができる. ここでは Perl による G.711 の変換・逆変換のプログラムをしめす. もちろん,Perl の特殊機能はつかっていないので,他の言語に容易にかきかえることができる.
G.711 は対数圧縮によって 16 bit の音声を 8 bit に圧縮するコーデックである. G.711 には u-Law (正確には μ-Law) と a-Law という 2 種類があるが,変換のてまはあまりかわらない. ここでは u-Law だけをあつかう.
基本的には,あらかじめつぎのようなテーブルを用意しておけば,これらのテーブルをひくだけで uLaw から線形への変換ができる (ただし,添字の範囲が 0..255 からはずれないようにクリップする必要がある).
# ULAW-to-linear conversion table for upper digit
my @ULAW_H =
(130, 134, 138, 142, 146, 150, 154, 158, 162, 166,
170, 174, 178, 182, 186, 190, 193, 195, 197, 199,
201, 203, 205, 207, 209, 211, 213, 215, 217, 219,
221, 223, 224, 225, 226, 227, 228, 229, 230, 231,
232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
241, 242, 242, 243, 243, 244, 244, 245, 245, 246,
246, 247, 247, 248, 248, 248, 249, 249, 249, 249,
250, 250, 250, 250, 251, 251, 251, 251, 252, 252,
252, 252, 252, 252, 253, 253, 253, 253, 253, 253,
253, 253, 254, 254, 254, 254, 254, 254, 254, 254,
254, 254, 254, 254, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 0, 126, 121,
117, 113, 109, 105, 101, 97, 93, 89, 85, 81,
77, 73, 69, 65, 62, 60, 58, 56, 54, 52,
50, 48, 46, 44, 42, 40, 38, 36, 34, 32,
31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
21, 20, 19, 18, 17, 16, 15, 14, 14, 13,
13, 12, 12, 11, 11, 10, 10, 9, 9, 8,
8, 7, 7, 7, 6, 6, 6, 6, 5, 5,
5, 5, 4, 4, 4, 4, 3, 3, 3, 3,
3, 3, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0);
# ULAW-to-linear conversion table for lower digit
my @ULAW_L =
(0, 4, 8, 12, 17, 21, 25, 29, 33, 38,
42, 46, 50, 55, 59, 63, 66, 68, 71, 73,
75, 77, 79, 81, 83, 85, 88, 90, 92, 94,
96, 98, 228, 229, 230, 231, 232, 233, 234, 235,
236, 237, 238, 239, 240, 241, 243, 244, 180, 53,
181, 54, 182, 55, 184, 56, 185, 57, 186, 58,
187, 59, 188, 60, 157, 221, 29, 94, 158, 222,
30, 95, 159, 223, 31, 96, 160, 224, 32, 97,
145, 177, 209, 241, 17, 50, 82, 114, 146, 178,
210, 242, 18, 51, 83, 115, 139, 155, 171, 187,
203, 219, 235, 251, 11, 28, 44, 60, 76, 92,
108, 124, 136, 144, 152, 160, 168, 176, 184, 192,
200, 208, 216, 224, 232, 240, 248, 0, 0, 252,
248, 244, 239, 235, 231, 227, 223, 218, 214, 210,
206, 201, 197, 193, 190, 188, 185, 183, 181, 179,
177, 175, 173, 171, 168, 166, 164, 162, 160, 158,
28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
18, 17, 16, 15, 13, 12, 76, 203, 75, 202,
74, 201, 72, 200, 71, 199, 70, 198, 69, 197,
68, 196, 99, 35, 227, 162, 98, 34, 226, 161,
97, 33, 225, 160, 96, 32, 224, 159, 111, 79,
47, 15, 239, 206, 174, 142, 110, 78, 46, 14,
238, 205, 173, 141, 117, 101, 85, 69, 53, 37,
21, 5, 245, 228, 212, 196, 180, 164, 148, 132,
120, 112, 104, 96, 88, 80, 72, 64, 56, 48,
40, 32, 24, 16, 8, 0);
しかし,テーブルの数値をいれなくても,プログラムでそれを生成することができる. つぎにしめすのは初期設定時につぎの 2 つのサブルーティンをよびだすことによってテーブルを生成し,それをつかって変換をおこなうプログラムである.
- gen_u2l() (uLaw から線形への変換 (デコード) テーブルを生成)
- gen_l2u() (線形から uLaw への変換 (エンコード) テーブルを生成)
#=============================================================================
# ulaw <-> linear conversion table generator
#=============================================================================
my $QUANT_MASK = 0xf;
my $BIAS = 0x84;
my $SEG_MASK = 0x70;
my $SEG_SHIFT = 4;
my $SIGN_BIT = 0x80;
my (@l2u, @u2l);
### u2l($uval)
# convert ulaw value to linear value.
#
sub u2l($) {
my ($uval) = @_;
$uval = ~$uval;
my $t = (($uval & $QUANT_MASK) << 3) + $BIAS;
$t <<= ($uval & $SEG_MASK) >> $SEG_SHIFT;
return ($uval & $SIGN_BIT) ? ($BIAS - $t) : ($t - $BIAS);
}
### gen_u2l()
# generate ulaw-to-linear conversion table (@u2l)
#
sub gen_u2l() {
for (my $i = 0; $i < 256; $i++) {
$u2l[$i] = u2l($i);
};
}
### gen_l2u()
# generate linear-to-ulaw conversion table (@l2u)
# (This method might not generate an optimum converter.)
#
sub gen_l2u() {
for (my $i = 0; $i < 256; $i++) {
my $j = $u2l[$i];
if ($j < 0) {
$j += 65536;
};
$l2u[$j] = $i;
};
for (my $i = 1; $i < 65536; $i++) {
if ($l2u[$i] == 0) {
$l2u[$i] = $l2u[$i-1];
};
};
}
