21.1. "audioop" --- 生の音声データを操作する
********************************************

"audioop" モジュールは音声データの便利な操作を含んでいます。このモジュ
ールは、Python 文字列型に保存された 8,  16, 32 ビット幅の符号付き整数
でできた音声データを操作します。これは "al" および "sunaudiodev" で使
われているのと同じ形式です。特に指定の無いかぎり、スカラ量を表す要素は
すべて整数型になっています。

このモジュールはa-LAW、u-LAWそしてIntel/DVI ADPCMエンコードをサポート
しています。

複雑な操作のうちいくつかはサンプル幅が 16 ビットのデータに対してのみ働
きますが、それ以外は常にサンプル幅を操作のパラメタとして (バイト単位で
) 渡します。

このモジュールでは以下の変数と関数を定義しています:

exception audioop.error

   この例外は、未知のサンプル当たりのバイト数を指定した時など、全般的
   なエラーに対して送出されます。

audioop.add(fragment1, fragment2, width)

   パラメータとして渡された2つのサンプルの和のデータを返します。
   *width* はバイト単位のサンプル幅で、"1", "2", >>``<<4``のいずれかで
   す。両方のデータは同じ長さでなければなりません。オーバーフローした
   場合は、切り捨てされます。

audioop.adpcm2lin(adpcmfragment, width, state)

   Intel/DVI ADPCM 形式のデータをリニア (linear) 形式にデコードします
   。 ADPCM 符号化方式の詳細については "lin2adpcm()" の説明を参照して
   下さい。 "(sample, newstate)" からなるタプルを返し、サンプルは
   *width* に指定した幅になります。

audioop.alaw2lin(fragment, width)

   a-LAW形式のデータをリニア (linear) 形式に変換します。a-LAW形式は常
   に 8 ビットのサンプルを使用するので、ここでは *width* は単に出力デ
   ータのサンプル幅となります。

   バージョン 2.5 で追加.

audioop.avg(fragment, width)

   データ中の全サンプルの平均値を返します。

audioop.avgpp(fragment, width)

   データ中の全サンプルの平均 peak-peak 振幅を返します。フィルタリング
   を行っていない場合、このルーチンの有用性は疑問です。

audioop.bias(fragment, width, bias)

   元の音声データの各サンプルにバイアスを加算した音声データを返します
   。オーバーフローした場合はラップアラウンドされます。

audioop.cross(fragment, width)

   引数に渡したデータ中のゼロ交差回数を返します。

audioop.findfactor(fragment, reference)

   "rms(add(fragment, mul(reference, -F)))" を最小にするような係数 *F*
   、すなわち、*reference* に乗算したときにもっとも *fragment* に近く
   なるような値を返します。*fragment* と *reference* のサンプル幅はい
   ずれも 2バイトでなければなりません。

   このルーチンの実行に要する時間は "len(fragment)" に比例します。

audioop.findfit(fragment, reference)

   *reference* を可能な限り *fragment* に一致させようとします
   (*fragment* は *reference* より長くなければなりません)。この処理は
   (概念的には) *fragment* からスライスをいくつか取り出し、それぞれに
   ついて "findfactor()" を使って最良な一致を計算し、誤差が最小の結果
   を選ぶことで実現します。 *fragment* と *reference* のサンプル幅は両
   方とも2バイトでなければなりません。 "(offset, factor)" からなるタプ
   ルを返します。 *offset* は最適な一致箇所が始まる *fragment* のオフ
   セット値（整数）で、 *factor* は "findfactor()" の返す係数 (浮動小
   数点数) です。

audioop.findmax(fragment, length)

   *fragment* から、長さが *length* サンプル (バイトではありません!)
   で最大のエネルギーを持つスライス、すなわち、
   "rms(fragment[i*2:(i+length)* 2])" を最大にするようなスライスを探し
   、*i* を返します。データのはサンプル幅は 2バイトでなければなりませ
   ん。

   このルーチンの実行に要する時間は "len(fragment)" に比例します。

audioop.getsample(fragment, width, index)

   データ中の *index* サンプル目の値を返します。

audioop.lin2adpcm(fragment, width, state)

   データを 4 ビットの Intel/DVI ADPCM 符号化方式に変換します。ADPCM
   符号化方式とは適応符号化方式の一つで、あるサンプルと (可変の) ステ
   ップだけ離れたその次のサンプルとの差を 4 ビットの整数で表現する方式
   です。Intel/DVI ADPCMアルゴリズムは IMA  (国際MIDI協会) に採用され
   ているので、おそらく標準になるはずです。

   *state* はエンコーダの内部状態が入ったタプルです。エンコーダは
   "(adpcmfrag, newstate)" のタプルを返し、次に "lin2adpcm()" を呼び出
   す時に *newstate* を渡さねばなりません。最初に呼び出す時には
   *state* に "None" を渡してもかまいません。 *adpcmfrag* は ADPCMで符
   号化されたデータで、バイト当たり 2 つの4ビット値がパックされていま
   す。

audioop.lin2alaw(fragment, width)

   音声データのサンプルを a-LAW エンコーディングに変換し、Python 文字
   列として返します。a-LAW とは 13 ビットのダイナミックレンジを 8bit
   だけで表現できる音声エンコーディングです。Sun の音声ハードウェアな
   どで使われています。

   バージョン 2.5 で追加.

audioop.lin2lin(fragment, width, newwidth)

   サンプル幅を 1、2、4 バイト形式の間で変換します。

   注釈: .WAV のような幾つかのオーディオフォーマットでは、16bitと
     32bitのサ ンプルは符号付きですが、 8bitのサンプルは符号なしです。
     そのため、 そのようなフォーマットで8bitに変換する場合は、変換結果
     に128を足さ なければなりません:

        new_frames = audioop.lin2lin(frames, old_width, 1)
        new_frames = audioop.bias(new_frames, 1, 128)

     逆に、8bitから16bitや32bitに変換する場合も、同じことが言えます。

audioop.lin2ulaw(fragment, width)

   音声データのサンプルを u-LAW エンコーディングに変換し、Python 文字
   列として返します。u-LAW とは 14 ビットのダイナミックレンジを 8bit
   だけで表現できる音声エンコーディングです。Sun の音声ハードウェアな
   どで使われています。

audioop.max(fragment, width)

   音声データ全サンプルの *絶対値* の最大値を返します。

audioop.maxpp(fragment, width)

   音声データの最大 peak-peak 振幅を返します。

audioop.minmax(fragment, width)

   音声データ全サンプル中における最小値と最大値からなるタプルを返しま
   す。

audioop.mul(fragment, width, factor)

   元の音声データの各サンプルに浮動小数点数 *factor* を乗算した音声デ
   ータを返します。オーバーフローした場合は切り捨てられます。

audioop.ratecv(fragment, width, nchannels, inrate, outrate, state[, weightA[, weightB]])

   入力したデータのフレームレートを変換します。

   *state* は変換ルーチンの内部状態を入れたタプルです。変換ルーチンは
   "(newfragment, newstate)" を返し、次に "ratecv()" を呼び出す時には
   *newstate* を渡さなねばなりません。最初の呼び出しでは "None" を渡し
   ます。

   引数 *weightA* と *weightB* は単純なデジタルフィルタのパラメタで、
   デフォルト値はそれぞれ "1" と "0" です。

audioop.reverse(fragment, width)

   データ内のサンプルの順序を逆転し、変更されたデータを返します。

audioop.rms(fragment, width)

   データの自乗平均根(root-mean-square)、すなわち "sqrt(sum(S_i^2)/n)"
   を返します。

   これはオーディオ信号の強度 (power) を測る一つの目安です。

audioop.tomono(fragment, width, lfactor, rfactor)

   ステレオ音声データをモノラル音声データに変換します。左チャネルのデ
   ータに *lfactor*、右チャネルのデータに *rfactor* を掛けた後、二つの
   チャネルの値を加算して単一チャネルの信号を生成します。

audioop.tostereo(fragment, width, lfactor, rfactor)

   モノラル音声データをステレオ音声データに変換します。ステレオ音声デ
   ータの各サンプル対は、モノラル音声データの各サンプルをそれぞれ左チ
   ャネルは *lfactor* 倍、右チャネルは *rfactor* 倍して生成します。

audioop.ulaw2lin(fragment, width)

   u-LAW で符号化されている音声データを線形に符号化された音声データに
   変換します。u-LAW 符号化は常にサンプル当たり 8 ビットを使うため、
   *width* は出力音声データのサンプル幅にしか使われません。

"mul()" や "max()" といった操作はモノラルとステレオを区別しない、すな
わち全てのデータを平等に扱うということに注意してください。この仕様が問
題になるようなら、あらかじめステレオ音声データを二つのモノラル音声デー
タに分割しておき、操作後に再度統合してください。そのような例を以下に示
します:

   def mul_stereo(sample, width, lfactor, rfactor):
       lsample = audioop.tomono(sample, width, 1, 0)
       rsample = audioop.tomono(sample, width, 0, 1)
       lsample = audioop.mul(lsample, width, lfactor)
       rsample = audioop.mul(rsample, width, rfactor)
       lsample = audioop.tostereo(lsample, width, 1, 0)
       rsample = audioop.tostereo(rsample, width, 0, 1)
       return audioop.add(lsample, rsample, width)

もし ADPCM 符号をネットワークパケットの構築に使って独自のプロトコルを
ステートレスにしたい場合 (つまり、パケットロスを許容したい場合)は、デ
ータだけを送信して、ステートを送信すべきではありません。デコーダに従っ
て *initial* ステート ("lin2adpcm()" に渡される値) を送るべきで、最終
状態 (符号化器が返す値) を送るべきではないことに注意してください。 も
し、"struct.Struct" をバイナリでの状態保存に使いたい場合は、最初の要素
(予測値) を 16bit で符号化し、２番目の要素 (デルタインデックス) を
8bit で符号化できます。

このモジュールの ADPCM 符号のテストは自分自身に対してのみ行っており、
他の ADPCM 符号との間では行っていません。作者が仕様を誤解している部分
もあるかもしれず、それぞれの標準との間で相互運用できない場合もあり得ま
す。

"find*()" ルーチンは一見滑稽に見えるかもしれません。これらの関数の主な
目的はエコー除去 (echo cancellation) にあります。エコー除去を十分高速
に行うには、出力サンプル中から最も大きなエネルギーを持った部分を取り出
し、この部分が入力サンプル中のどこにあるかを調べ、入力サンプルから出力
サンプル自体を減算します:

   def echocancel(outputdata, inputdata):
       pos = audioop.findmax(outputdata, 800)    # one tenth second
       out_test = outputdata[pos*2:]
       in_test = inputdata[pos*2:]
       ipos, factor = audioop.findfit(in_test, out_test)
       # Optional (for better cancellation):
       # factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)],
       #              out_test)
       prefill = '\0'*(pos+ipos)*2
       postfill = '\0'*(len(inputdata)-len(prefill)-len(outputdata))
       outputdata = prefill + audioop.mul(outputdata, 2, -factor) + postfill
       return audioop.add(inputdata, outputdata, 2)
