使用Python编辑WAV文件

5

在wav文件中,每个单词之间都有完整的静音(我用Hex Workshop检查过了,静音用0表示)。

我该如何剪切非静音声音?

我正在使用Python进行编程。

谢谢!


你应该考虑稍微澄清一下你的问题。我想知道你是想去除静音,还是将音频分成单独的块? - Soviut
6个回答

20

Python有一个wav模块。您可以使用它来打开WAV文件以进行读取,并使用`getframes(1)'命令逐帧遍历该文件。

import wave
w = wave.open('beeps.wav', 'r')
for i in range():
frame = w.readframes(1)

返回的帧将是一个包含十六进制值的字节字符串。如果文件是立体声,结果将类似于这样(4个字节):

'\xe2\xff\xe2\xff'

如果是单声道,它将只有一半的数据(2个字节):

'\xe2\xff'
每个通道的长度为2个字节,因为音频是16位的。如果是8位,则每个通道只有一个字节。您可以使用getsampwidth()方法来确定这一点。另外,getchannels()将确定它是否为单声道或立体声。
您可以循环遍历这些字节,查看它们是否都等于零,表示两个通道都是静音的。在下面的示例中,我使用ord()函数将'\xe2'十六进制值转换为整数。
import wave
w = wave.open('beeps.wav', 'r')
for i in range(w.getnframes()):
    ### read 1 frame and the position will updated ###
    frame = w.readframes(1)

    all_zero = True
    for j in range(len(frame)):
        # check if amplitude is greater than 0
        if ord(frame[j]) > 0:
            all_zero = False
            break

    if all_zero:
        # perform your cut here
        print 'silence found at frame %s' % w.tell()
        print 'silence found at second %s' % (w.tell()/w..getframerate())

值得注意的是,单个静音帧并不一定表示空白区域,因为振幅可能会在常规频率下穿过0标记。因此,在决定该区域是否真正静音之前,建议观察一定数量的0帧。


1
当穿过0时,纯正的正弦波中可能会出现\0字节! 想象一下在11025Hz正弦波上进行44100Hz采样的文件,就会明白我的意思。 我不会-1,因为答案的其余部分非常好。 - Paweł Polewicz
1
readframes(i) 返回 i 个连续的帧,而不是在位置 i 处返回 1 帧。因此,您的循环将返回 1 帧,然后是 2 帧,然后是 3 帧等等。应该使用 readframes(1) 而不是 readframes(i)。 - gravitron
1
已修复,虽然我相信你的话。 - Soviut
请记住,8位wav文件是无符号的,而16位wav文件是有符号的。不确定Python的wave如何处理此问题。我宁愿使用像audiolab这样的更高级别的库。 - endolith
@Soviut ord(frame[j]) 是什么?它是波的振幅吗? - Jaseem
@Jaseem,是振幅。实际频率是通过随时间/帧数/样本振荡振幅来实现的。 - Soviut

7

我正在为一个项目进行一些研究,涉及IT技术相关内容。然而,我发现了解决方案中存在一些问题,主要是判断静默的方法不正确。一个“更正确”的实现方式如下:

import struct
import wave

wave_file = wave.open("sound_file.wav", "r")

for i in range(wave_file.getnframes()):
    # read a single frame and advance to next frame
    current_frame = wave_file.readframes(1)

    # check for silence
    silent = True
    # wave frame samples are stored in little endian**
    # this example works for a single channel 16-bit per sample encoding
    unpacked_signed_value = struct.unpack("<h", current_frame) # *
    if abs(unpacked_signed_value[0]) > 500:
        silent = False

    if silent:
        print "Frame %s is silent." % wave_file.tell()
    else
        print "Frame %s is not silent." % wave_file.tell()

参考资料和有用链接

*Struct Unpacking 在这里非常有用:https://docs.python.org/2/library/struct.html

**我发现一篇很好的参考资料,它解释了针对不同大小位编码和多个通道处理wave文件的格式:http://www.piclist.com/techref/io/serial/midi/wave.html

在Python中使用内置的ord()函数处理readframes(x)方法返回的字符串对象的第一个元素将无法正常工作。

另一个关键点是多通道音频是交错的,因此需要一些额外的逻辑来处理通道。同样,上面的链接详细介绍了这一点。

希望这能帮助未来的某个人。

以下是链接中的一些更重要的要点以及我发现有用的信息。

数据组织


所有数据都存储在8位字节中,以Intel 80x86(即小端)格式排列。多字节值的字节以低阶(即最不重要)字节为先存储。数据位如下所示(即在顶部显示位号):

         7  6  5  4  3  2  1  0
       +-----------------------+
 char: | lsb               msb |
       +-----------------------+

         7  6  5  4  3  2  1  0 15 14 13 12 11 10  9  8
       +-----------------------+-----------------------+
short: | lsb     byte 0        |       byte 1      msb |
       +-----------------------+-----------------------+

         7  6  5  4  3  2  1  0 15 14 13 12 11 10  9  8 23 22 21 20 19 18 17 16 31 30 29 28 27 26 25 24
       +-----------------------+-----------------------+-----------------------+-----------------------+
 long: | lsb     byte 0        |       byte 1          |         byte 2        |       byte 3      msb |
       +-----------------------+-----------------------+-----------------------+-----------------------+

交错


对于多通道声音(例如立体声波形),每个通道的单个采样点会被交错存储。例如,假设有一个立体声(即2个通道)波形。与其先存储左通道的所有采样点,然后再存储右通道的所有采样点,你需要将两个通道的采样点“混合”在一起。你需要存储左通道的第一个采样点,接着存储右通道的第一个采样点,然后存储左通道的第二个采样点,接着存储右通道的第二个采样点,以此类推,交替存储每个通道的下一个采样点。这就是所谓的交错数据;你按顺序存储每个通道的下一个采样点,以便同时“播放”(即发送到DAC)的采样点被连续存储。



1

我对此没有经验,但可以看看标准库中的wave模块。那可能会做你想要的事情。否则,您将不得不将文件读取为字节流,并剪切出0字节序列(但您不能仅剪切所有0字节,因为那样会使文件无效...)


1

您可能想尝试使用sox,这是一个命令行音频处理工具。它有许多模式之一是silence

silence:从声音文件的开头、中间或结尾删除静音。静音是低于指定阈值的任何声音。

它支持多种音频格式,并且速度相当快,因此解析大型文件不应该是问题。

要从文件中间删除静音,请指定一个负的below_periods。然后将此值视为正值,并且还用于指示效果应按照above_periods指定的方式重新启动处理,使其适用于删除声音文件中间的静音段落。

我没有找到任何libsox的Python构建,但是您可以像在Python中使用所有命令行程序一样使用它(或者您可以重写它-使用sox源代码作为指导)。


1

在剪切之前,您需要想出一些连续零的最小数量的阈值。否则,您将从正常音频数据的中间删除完全有效的零。您可以遍历波形文件,复制任何非零值,并缓冲零值。当您缓冲零并最终遇到下一个非零时,如果缓冲区的样本少于阈值,请将它们复制过去,否则请丢弃它。

然而,Python不是这种任务的好工具。 :(


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