Skip to main content

22.1. audioop —处理原始音频数据


audioop 模块包含对声音片段的一些有用的操作。它对存储在 字节状对象 中的由有符号整数样本8,16,24或32位宽组成的声音片段进行操作。除非另有说明,所有标量项都是整数。

在 3.4 版更改: 增加了对24位样本的支持。所有功能现在接受任何 bytes-like object。字符串输入现在会导致立即错误。

此模块支持a-LAW,u-LAW和Intel/DVI ADPCM编码。

一些更复杂的操作只需要16位采样,否则采样大小(以字节为单位)总是操作的参数。

模块定义以下变量和函数:

exception audioop.error

对所有错误都会引发此异常,例如每个样本的未知字节数等。

audioop.add(fragment1, fragment2, width)

返回一个片段,它是作为参数传递的两个样本的相加。 width 是以字节为单位的样本宽度,即 1234。两个片段应具有相同的长度。在溢出的情况下截取样本。

audioop.adpcm2lin(adpcmfragment, width, state)

将Intel/DVI ADPCM编码片段解码为线性片段。有关ADPCM编码的详细信息,请参阅 lin2adpcm() 的描述。返回元组 (sample, newstate),其中样本具有在 width 中指定的宽度。

audioop.alaw2lin(fragment, width)

将a-LAW编码中的声音片段转换为线性编码的声音片段。 a-LAW编码总是使用8位样本,因此 width 仅指这里的输出片段的样本宽度。

audioop.avg(fragment, width)

返回片段中所有样本的平均值。

audioop.avgpp(fragment, width)

返回片段中所有样本的平均峰 - 峰值。没有进行过滤,所以这个例程的有用性是有问题的。

audioop.bias(fragment, width, bias)

返回作为原始片段的片段,并向每个样本添加偏差。样品在溢出的情况下包裹。

audioop.byteswap(fragment, width)

“Byteswap”片段中的所有样本,并返回修改的片段。将大尾数样本转换为小尾数法,反之亦然。

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 中取出切片,使用 findfactor() 计算最佳匹配,并最小化结果。片段都应包含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使用,因此它很可能成为一个标准。

state 是包含编码器状态的元组。编码器返回一个元组 (adpcmfrag, newstate),并且 newstate 应该传递给 lin2adpcm() 的下一个调用。在初始调用中,None 可以作为状态传递。 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 作为状态。

weightAweightB 参数是简单数字滤波器的参数,默认分别为 10

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)

从单声道片段生成立体声片段。从单声道采样计算立体声片段中的每对采样,由此通过 rfactor 将左声道采样乘以 lfactor 和右声道采样。

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 来存储二进制的状态,你可以编码第一个元素(预测值)在16位和第二个(delta索引)在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)