audioop モジュールは音声データを操作する関数を収録しています。 このモジュールは、Python 文字列型中に入っている 8, 16, 32 ビットの 符号付き整数でできた音声データ、すなわち al および sunaudiodev で使われているのと同じ形式の音声データを操作 します。特に指定の無いかぎり、スカラ量を表す要素はすべて整数型になって います。
このモジュールはa-LAW、u-LAWそしてIntel/DVI ADPCMエンコードをサポートしています。
複雑な操作のうちいくつかはサンプル幅が 16 ビットのデータに対して のみ働きますが、 それ以外は常にサンプル幅を操作のパラメタとして (バイト単位で) 渡します。
このモジュールでは以下の変数と関数を定義しています:
fragment1, fragment2, width) |
1
、2
、4
のうちいずれかです。
2 つのデータは同じサンプル幅でなければなりません。
adpcmfragment, width, state) |
(sample, newstate)
からなる
タプルを返し、サンプルはwidth に指定した幅になります。
fragment, width) |
fragment, width) |
fragment, width) |
fragment, width, bias) |
fragment, width) |
fragment, reference) |
rms(add(fragment, mul(reference, -F)))
を
最小にするような係数F 、すなわち、reference に乗算した
ときにもっとも fragment に近くなるような値を返します。
fragmentとreference のサンプル幅はいずれも 2バイト
でなければなりません。
このルーチンの実行に要する時間はlen(fragment)
に比例します。
fragment, reference) |
(offset, factor)
からなるタプルを返します。
offset は最適な一致箇所が始まるfragmentのオフセット値(整
数)で、factorはfindfactor() の返す係数
(浮動小数点数) です。
fragment, length) |
rms(fragment[i*2:(i+length)*2])
を
最大にするようなスライスを探し、 i を返します。
データのはサンプル幅は 2バイトでなければなりません。
このルーチンの実行に要する時間はlen(fragment)
に比例します。
fragment, width, index) |
fragment, width, state) |
state はエンコーダの内部状態が入ったタプルです。
エンコーダは(adpcmfrag, newstate)
のタプルを返し、
次に lin2adpcm()を呼び出す時にnewstate を渡さねば
なりません。最初に呼び出す時にはstateにNone
を渡しても
かまいません。adpcmfrag は ADPCMで符号化されたデータで、バイト
当たり 2 つの4ビット値がパックされています。
fragment, width) |
fragment, width, newwidth) |
fragment, width) |
fragment, width) |
fragment, width) |
fragment, width) |
fragment, width, factor) |
fragment, width, nchannels, inrate, outrate, state[, weightA[, weightB]]) |
state は変換ルーチンの内部状態を入れたタプルです。
変換ルーチンは(newfragment, newstate)
を返し、次にratecv()を呼び出す時にはnewstateを
渡さなねばなりません。最初の呼び出しではNone
を渡します。
引数weightAとweightB は単純なデジタルフィルタの
パラメタで、デフォルト値はそれぞれ1
と0
です。
fragment, width) |
fragment, width) |
fragment, width, lfactor, rfactor) |
fragment, width, lfactor, rfactor) |
fragment, 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 エンコーダを使って音声データの入ったネットワークパケットを 構築する際、自分のプロトコルを (パケットロスに耐えられるように) ステートレス (stateless) にしたいなら、データだけでなく状態変数 (state) も伝送せねばなりません。このとき、伝送するのはエンコード後状態 (エンコーダの返す値) ではなく、エンコーダの初期状態 (lin2adpcm() に渡した値) initial なので注意してください。 struct.struct() を使って状態変数をバイナリ形式で保存したいなら、最初の要素 (予測値) は 16 ビットで、次の値 (デルタ係数: delta index) は 8 ビット で符号化できます。
このモジュールの ADPCM 符号のテストは自分自身に対してのみ行っており、 他の ADPCM 符号との間では行っていません。作者が仕様を誤解している 部分もあるかもしれず、それぞれの標準との間で相互運用できない場合も あり得ます。
find*() ルーチンは一見滑稽に見えるかもしれません。 これらの関数の主な目的はエコー除去 (echo cancellation) にあります。 エコー除去を十分高速に行うには、出力サンプル中から最も大きな エネルギーを持った部分を取り出し、この部分が入力サンプル中の どこにあるかを調べ、入力サンプルから出力サンプル自体を減算します:
def echocancel(outputdata, inputdata): pos = audioop.findmax(outputdata, 800) # 1/10秒 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)