使用Python编辑WAV文件

5 投票
6 回答
27488 浏览
提问于 2025-04-15 11:39

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

我该怎么剪掉那些不是静音的声音呢?

我正在用python编程。

谢谢!

6 个回答

7

我最近在研究一个项目,发现了提供的解决方案中有几个问题,特别是判断静音的方法不太对。一个“更正确”的实现方法是:

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

**我找到的一个很好的参考资料,讲解了处理不同大小位编码和多个通道的波形文件格式是: 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)的采样点是连续存储的。

21

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帧后,再决定这个区域是否真的静音。

撰写回答