audioop --- 處理原始音頻數據?
audioop 模塊包含針對聲音片段的一些有用操作。它操作的聲音片段由 8、16、24 或 32 位寬的有符號整型樣本組成,存儲在 類字節串對象 中。除非特別說明,否則所有標量項目均為整數。
在 3.4 版更改: 增加了對 24 位樣本的支持。現在,所有函數都接受任何 類字節串對象。而傳入字符串會立即導致錯誤。
本模塊提供對 a-LAW、u-LAW 和 Intel/DVI ADPCM 編碼的支持。
部分更復雜的操作僅接受 16 位樣本,而其他操作始終需要樣本大小(以字節為單位)作為該操作的參數。
此模塊定義了下列變量和函數:
-
exception
audioop.error? 所有錯誤都會拋出此異常,比如樣本的字節數未知等等。
-
audioop.add(fragment1, fragment2, width)? 兩個樣本作為參數傳入,返回一個片段,該片段是兩個樣本的和。width 是樣本寬度(以字節為單位),可以取
1,2,3或4。兩個片段的長度應相同。如果發生溢出,較長的樣本將被截斷。
-
audioop.adpcm2lin(adpcmfragment, width, state)? 將 Intel/DVI ADPCM 編碼的片段解碼為線性片段。關于 ADPCM 編碼的詳情請參閱
lin2adpcm()的描述。返回一個元組(sample, newstate),其中 sample 的位寬由 width 指定。
-
audioop.alaw2lin(fragment, width)? 將 a-LAW 編碼的聲音片段轉換為線性編碼聲音片段。由于 a-LAW 編碼樣本始終為 8 位,因此這里的 width 僅指輸出片段的樣本位寬。
-
audioop.avg(fragment, width)? 返回片段中所有樣本的平均值。
-
audioop.avgpp(fragment, width)? 返回片段中所有樣本的平均峰峰值。由于沒有進行過濾,因此該例程的實用性尚存疑。
-
audioop.bias(fragment, width, bias)? 返回一個片段,該片段由原始片段中的每個樣本加上偏差組成。在溢出時樣本會回卷 (wrap around)。
-
audioop.byteswap(fragment, width)? “按字節交換”片段中的所有樣本,返回修改后的片段。將大端序樣本轉換為小端序樣本,反之亦然。
3.4 新版功能.
-
audioop.cross(fragment, width)? 將片段作為參數傳入,返回其中過零點的數量。
-
audioop.findfactor(fragment, reference)? 返回一個系數 F 使得
rms(add(fragment, mul(reference, -F)))最小,即返回乘以 reference 后最匹配 fragment 的那個乘數。兩個片段都應包含 2 字節寬的樣本。本例程所需的時間與
len(fragment)成正比。
-
audioop.findfit(fragment, reference)? 盡可能嘗試讓 reference 匹配 fragment 的一部分(fragment 應較長)。從概念上講,完成這些靠從 fragment 中取出切片,使用
findfactor()計算最佳匹配,并最小化結果。兩個片段都應包含 2 字節寬的樣本。返回一個元組(offset, factor),其中 offset 是在 fragment 中的偏移量(整數),表示從此處開始最佳匹配,而 factor 是由findfactor()定義的因數(浮點數)。
-
audioop.findmax(fragment, length)? 在 fragment 中搜索所有長度為 length 的樣本切片(不是字節!)中,能量最大的那一個切片,即返回 i 使得
rms(fragment[i*2:(i+length)*2])最大。兩個片段都應包含 2 字節寬的樣本。本例程所需的時間與
len(fragment)成正比。
-
audioop.getsample(fragment, width, index)? 返回片段中樣本索引 index 的值。
-
audioop.lin2adpcm(fragment, width, state)? 將樣本轉換為 4 位 Intel/DVI ADPCM 編碼。ADPCM 編碼是一種自適應編碼方案,其中每個 4 比特數字是一個采樣值與下一個采樣值之間的差除以(不定的)步長。IMA 已選擇使用 Intel/DVI ADPCM 算法,因此它很可能成為標準。
state 是一個表示編碼器狀態的元組。編碼器返回一個元組
(adpcmfrag, newstate),而 newstate 要在下一次調用lin2adpcm()時傳入。在初始調用中,可以將None作為 state 傳遞。adpcmfrag 是 ADPCM 編碼的片段,每個字節打包了 2 個 4 比特值。
-
audioop.lin2alaw(fragment, width)? 將音頻片段中的采樣值轉換為 a-LAW 編碼,并將其作為字節對象返回。a-LAW 是一種音頻編碼格式,僅使用 8 位樣本即可獲得大約 13 位的動態范圍。Sun 音頻硬件等使用該編碼。
-
audioop.lin2lin(fragment, width, newwidth)? 將采樣在 1、2、3 和 4 字節格式之間轉換。
注解
在某些音頻格式(如 .WAV 文件)中,16、24 和 32 位采樣是有符號的,但 8 位采樣是無符號的。因此,當將這些格式轉換為 8 位寬采樣時,還需使結果加上 128:
new_frames = audioop.lin2lin(frames, old_width, 1) new_frames = audioop.bias(new_frames, 1, 128)
反之,將 8 位寬的采樣轉換為 16、24 或 32 位時,必須采用相同的處理。
-
audioop.lin2ulaw(fragment, width)? 將音頻片段中的采樣值轉換為 u-LAW 編碼,并將其作為字節對象返回。u-LAW 是一種音頻編碼格式,僅使用 8 位采樣即可獲得大約 14 位的動態范圍。Sun 音頻硬件等使用該編碼。
-
audioop.max(fragment, width)? 返回片段中所有采樣值的最大 絕對值。
-
audioop.maxpp(fragment, width)? 返回聲音片段中的最大峰峰值。
-
audioop.minmax(fragment, width)? 返回聲音片段中所有采樣值的最小值和最大值組成的元組。
-
audioop.mul(fragment, width, factor)? 返回一個片段,該片段由原始片段中的每個采樣值乘以浮點值 factor 組成。如果發生溢出,采樣將被截斷。
-
audioop.ratecv(fragment, width, nchannels, inrate, outrate, state[, weightA[, weightB]])? 轉換輸入片段的幀速率。
state 是一個表示轉換器狀態的元組。轉換器返回一個元組
(newfragment, newstate),而 newstate 要在下一次調用ratecv()時傳入。初始調用應傳入None作為 state。參數 weightA 和 weightB 是簡單數字濾波器的參數,默認分別為
1和0。
-
audioop.reverse(fragment, width)? 將片段中的采樣值反轉,返回修改后的片段。
-
audioop.rms(fragment, width)? 返回片段的均方根值,即
sqrt(sum(S_i^2)/n)。測量音頻信號的能量。
-
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 編碼器構造網絡數據包,并且希望協議是無狀態的(即能夠容忍數據包丟失),則不僅需要傳輸數據,還應該傳輸狀態。請注意,必須將*初始*狀態(傳入 lin2adpcm() 的狀態)發送給解碼器,不能發送最終狀態(編碼器返回的狀態)。如果要使用 struct.Struct 以二進制保存狀態,可以將第一個元素(預測值)用 16 位編碼,將第二個元素(增量索引)用 8 位編碼。
本 ADPCM 編碼器從不與其他 ADPCM 編碼器對立,僅針對自身。本開發者可能會誤讀標準,這種情況下它們將無法與相應標準互操作。
乍看之下 find*() 例程可能有些可笑。它們主要是用于回聲消除,一種快速有效的方法是選取輸出樣本中能量最高的片段,在輸入樣本中定位該片段,然后從輸入樣本中減去整個輸出樣本:
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)
