# 22.1. `audioop` - 处理原始音频数据

`audioop`模块包含对声音片段的一些有用的操作。它对存储在bytes-like objects中的有符号整数采样8,16,24或32位宽的声音片段进行操作。除非另有说明，否则所有标量项都是整数。

在版本3.4中已更改：添加了对24位示例的支持。所有函数现在都接受任何bytes-like object。字符串输入现在将导致立即错误。

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

一些更复杂的操作只需要16位的样本，否则样本大小（以字节为单位）总是操作的一个参数。

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

异常 `audioop。` `错误`

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

`audioop.``add`(fragment1, fragment2, width)

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

`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））） t4>`是最小的，即返回要乘以参考的因子，以使其与片段尽可能匹配。片段都应包含2字节样本。

  该例程所花费的时间与`len(fragment)`成比例。

`audioop.``findfit`(fragment, reference)

  尝试将引用与片段（应为更长的片段）的一部分匹配。（概念上）通过使用`findfactor()`从片段中取出片段来计算最佳匹配并最小化结果。片段都应包含2字节样本。Return a tuple `(offset, factor)` where offset is the (integer) offset into fragment where the optimal match started and factor is the (floating-point) factor as per `findfactor()`.

`audioop.``findmax`(fragment, length)

  搜索片段获取长度长度的切片（不是字节！）with maximum energy, i.e., return i for which `rms(fragment[i*2:(i+length)*2])` is maximal. 片段都应包含2字节样本。

  该程序需要与`len(fragment)`成正比的时间。

`audioop.``getsample`(fragment, width, index)

  返回样本index的值。

`audioop.``lin2adpcm`(fragment, width, state)

  将采样转换为4位Intel / DVI ADPCM编码。ADPCM编码是自适应编码方案，其中每个4比特数是一个采样和下一个采样之间的差除以（变化）步长。英特尔/ DVI ADPCM算法已经选择供IMA使用，因此它很可能成为一个标准。

  state是包含编码器状态的元组。编码器返回一个tuple `（adpcmfrag， newstate）`，并且newstate `lin2adpcm()`。在初始调用中，`None`可以作为状态传递。adpcmfrag是每个字节打包2个4位值的ADPCM编码片段。

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

  返回具有原始片段中的所有样本乘以浮点值因子的片段。在溢出的情况下截取样本。

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

  转换输入片段的帧速率。

  state是包含转换器状态的元组。The converter returns a tuple `(newfragment, newstate)`, and newstate should be passed to the next call of `ratecv()`. 初始调用应通过`None`作为状态。

  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)

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

`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中编写第二个（delta索引）。

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)