# 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)