Python u-Law (MULAW)波形解压缩为原始波形信号

5

我已经搜索了2周,但没有找到算法或解决方案。我的一些.wav文件采用MULAW压缩,而Python似乎没有wave.py中可以成功解压它的函数。因此,我决定在Python中构建一个解码器。

我在基本元素中找到了一些关于MULAW的信息:

  1. 维基百科
  2. A-law u-Law比较
  3. 某些c-esc编解码库

所以我需要一些指导,因为我不知道如何从有符号短整数得到完整的波形信号。这是我根据迄今为止收集到的信息的初步想法:


从维基百科上我得到了u-law压缩和解压缩的公式:

压缩:compression

解压缩:enter image description here

根据压缩公式,输出似乎限制在-1到+1的float范围内,并且有符号短整数的范围为–32,768到32,767,因此看起来我需要将其从short int转换为特定范围内的float

现在,老实说,我之前听说过量化,但我不确定是否应该先尝试去量化再解压缩或者反过来,甚至在这种情况下是否是同一件事情...教程/文档可能会使用术语上的一些技巧。

我正在处理的波形文件应该包含类似于语音合成的'A'声音,我可以通过在某些音频软件和自定义波形分析器中比较2个波形来验证成功,但我真的想减少这个过程中的试错部分。

所以,我的想法是:

u = 0xff
data_chunk = b'\xe7\xe7' # -6169
data_to_r1 = unpack('h',data_chunk)[0]/0xffff # I suspect this is wrong,
#                                             # but I don't know what else

u_law = ( -1 if data_chunk<0 else 1 )*( pow( 1+u, abs(data_to_r1)) -1 )/u   

那么,在面对压缩时,我需要采取一些算法或关键步骤吗?首先:解压其次:量化第三步是什么?
因为我在 Google 上找到的所有内容都是关于如何读取 .wav PCM 调制文件类型,而不是如何处理出现的压缩情况。


2
看一下G.711,这是一个基于此的编解码器的实际示例。 - Oliver Charlesworth
lseg, linbuf, logbuf 是什么? - Danilo
3个回答

3

因此,在搜索了谷歌后,解决方案在github上找到了(想一想就知道)。我搜索了很多算法,发现有一个算法在失真压缩的误差范围内。 这个算法适用于正值30 -> 1和负值-32 -> -1的u律编码

老实说,我认为这个解决方案是足够的,但不完全符合等式,但现在它是最好的解决方案。这段代码是直接从gcc9108音频编解码器转录到Python的。

def uLaw_d(i8bit):
    bias = 33
    sign = pos = 0
    decoded = 0

    i8bit = ~i8bit
    if i8bit&0x80:
        i8bit &= ~(1<<7)
        sign = -1

    pos = ( (i8bit&0xf0) >> 4 ) + 5
    decoded = ((1 << pos) | ((i8bit & 0x0F) << (pos - 4)) | (1 << (pos - 5))) - bias
    return decoded if sign else ~decoded

def uLaw_e(i16bit):
    MAX = 0x1fff
    BIAS = 33
    mask = 0x1000
    sign = lsb = 0
    pos = 12 

    if i16bit < 0:
        i16bit = -i16bit
        sign = 0x80

    i16bit += BIAS

    if ( i16bit>MAX ): i16bit = MAX 

    for x in reversed(range(pos)):
        if i16bit&mask != mask and pos>=5:
            pos = x
            break

    lsb = ( i16bit>>(pos-4) )&0xf
    return ( ~( sign | ( pos<<4 ) | lsb ) )

测试用例:

print( 'normal :\t{0}\t|\t{0:2X}\t:\t{0:016b}'.format(0xff) )
print( 'encoded:\t{0}\t|\t{0:2X}\t:\t{0:016b}'.format(uLaw_e(0xff)) )
print( 'decoded:\t{0}\t|\t{0:2X}\t:\t{0:016b}'.format(uLaw_d(uLaw_e(0xff))) )

并输出:

normal :    255     |   FF  :   0000000011111111
encoded:    -179    |   -B3 :   -000000010110011
decoded:    263     |   107 :   0000000100000111

如您所见,263-255=8,这在范围内。当我尝试实现G.711中描述的seeemmmm方法时,友好的用户Oliver Charlesworth建议我查看数据中的最大解码值为-8036,接近uLaw规范的最大值,但我无法反向工程解码函数以获取维基百科中的二进制等效函数。

最后,我必须说我目前感到失望的是Python库不支持所有类型的压缩算法,因为它不仅是人们使用的工具,也是Python消费者学习的资源,因为大多数深入代码的数据不容易获得或理解。


编辑

经过解码数据并通过wave.py编写wav文件,我已成功地成功编写了一个新的原始线性PCM文件。这个方法很有效...尽管我一开始持怀疑态度。


编辑2: ::> 您可以在compressions.py上找到真正的解决方案。

嗨,我想将一个ulaw字节流解码成普通的wave文件,但不确定如何使用你的解决方案。我有一个ulaw编码字节的缓冲区。任何帮助都将不胜感激。 - Umesh Chaudhary
@UmeshChaudhary 在链接里有完整的实现代码文件。简单来说,你需要按照文件结构读取字节流中的每个字节。例如如果使用了ulaw 2字节整数压缩,则每对字节等于一个表示振幅的浮点数值。你可以考虑在Python中使用map功能,并将字节流作为参数传入(稍微调整一下振幅值的字节大小即可)。结果应该是浮点数类型的缓冲区。希望这能帮到你。 - Danilo

3
我发现这对于使用numpy数组进行ulaw转换非常有用。
import audioop

def numpy_audioop_helper(x, xdtype, func, width, ydtype):
    '''helper function for using audioop buffer conversion in numpy'''
    xi = np.asanyarray(x).astype(xdtype)
    if np.any(x != xi):
        xinfo = np.iinfo(xdtype)
        raise ValueError("input must be %s [%d..%d]" % (xdtype, xinfo.min, xinfo.max))
    y = np.frombuffer(func(xi.tobytes(), width), dtype=ydtype)
    return y.reshape(xi.shape)

def audioop_ulaw_compress(x):
    return numpy_audioop_helper(x, np.int16, audioop.lin2ulaw, 2, np.uint8)

def audioop_ulaw_expand(x):
    return numpy_audioop_helper(x, np.uint8, audioop.ulaw2lin, 2, np.int16)

说实话,我从未使用过numpy或pycharm或任何扩展Python的工具。我需要这样做的原因不是为了转换或处理数据,而是为了mp3 / wav压缩(以及其他)标志识别。 Python在有限的标志方面存在一些错误,如果文件使用通用、标准和广泛使用的标志编写,则Python加载时没有问题,但如果文件使用其他标志编写,则会引发错误...因此,您需要自己读取二进制结构并解压ulaw。 - Danilo

2

它确实可以,但前提是你想读取的文件具有简单压缩标志。我拿到了带有mulaw压缩标志的mp3文件,但Python无法识别它。因此,它并不适用于所有情况。 - Danilo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接