如何使用Python合并两个wav文件?

34

我正在使用Python编程语言,我想将一个.wav文件连接到另一个.wav文件的末尾。

在论坛中,有一个问题建议如何合并两个wav文件即在特定偏移处添加一个wav文件的内容,但我想要将两个wav文件连接到彼此的末尾...

另外,我在使用winsound模块播放自己的wav文件时遇到了问题。我能够播放声音,但需要使用time.sleep一段时间才能播放任何Windows声音,缺点是,如果我想要播放比time.sleep(N)更长的声音,N秒后Windows声音将重叠并在N秒后播放winsound并停止。

有人可以帮助吗?请提供解决这些问题的建议...

提前感谢您

8个回答

65

Python内置了wave模块,可以完成你所需的操作。当文件的细节(单声道或立体声、帧率等)相同时,下面的示例可以正常工作:

import wave

infiles = ["sound_1.wav", "sound_2.wav"]
outfile = "sounds.wav"

data= []
for infile in infiles:
    w = wave.open(infile, 'rb')
    data.append( [w.getparams(), w.readframes(w.getnframes())] )
    w.close()
    
output = wave.open(outfile, 'wb')
output.setparams(data[0][0])
for i in range(len(data)):
    output.writeframes(data[i][1])
output.close()

当要连接的wav文件的采样率大于48kHz时,此方法会失败。 - Bruce
1
我刚试了一下合并两个96kHz 24bit的wav文件,这段代码运行良好。请注意,此方法假定文件具有相同的格式,而不匹配是它失败的常见原因。如果存在不匹配,则需要安装更高级的库(例如在另一个答案中描述的pydub)(或者至少是最简单的方法)。 - tom10

49

我是pydub的维护者,它旨在使这种事情变得容易。

from pydub import AudioSegment

sound1 = AudioSegment.from_wav("/path/to/file1.wav")
sound2 = AudioSegment.from_wav("/path/to/file2.wav")

combined_sounds = sound1 + sound2
combined_sounds.export("/output/path.wav", format="wav")

注意:pydub是对audioop的轻量级封装。因此,在幕后,它基本上正在执行Tom10提到的操作。


但是如果你正在读取一个时间戳文件呢?怎么拼接这些文件呢? - Mystery Man
1
@Elvisjames 我认为你需要安装ffprobe - pydub肯定支持Python 3。 - Jiaaro

8

Python 3 解决方案:
我们可以使用标准库来实现,就像Tom10和Eggbert的答案所示。
下面是一个更简短的版本:

  1. Only write the parameters for the first wave file. We can test the wav_out file length to see if we haven't yet written to it. If we haven't write the wave parameters once only.
  2. Then write frames to the wav_out as they are read from the wav_in.

    with wave.open(outfile, 'wb') as wav_out:
        for wav_path in infiles:
            with wave.open(wav_path, 'rb') as wav_in:
                if not wav_out.getnframes():
                    wav_out.setparams(wav_in.getparams())
                wav_out.writeframes(wav_in.readframes(wav_in.getnframes()))
    

1
请注意,此支持合并超过2个文件,而@tom10的答案仅适用于2个文件。 - Tyler Dane Hitzeman
我猜如果你在输入文件列表中包含更多的名称,Tom的答案可以适用于更多的文件? - Arpit Omprakash

5

在@tom10的回答基础上进行补充:

from contextlib import closing

with closing(wave.open(outfile, 'wb')) as output:

    # find sample rate from first file
    with closing(wave.open(wav_files[0])) as w:
        output.setparams(w.getparams())

    # write each file to output
    for infile in wav_files:
        with closing(wave.open(infile)) as w:
            output.writeframes(w.readframes(w.getnframes()))

与一次性将所有数据存储然后在最后写入不同,它逐位地写入。同时,它也使用contextlib.close以便你无需手动关闭文件。


在Python3中,根据以下内容似乎不需要使用contextlib封装器: “open()函数可以在with语句中使用。当with块完成时,将调用Wave_read.close()或Wave_write.close()方法。” https://docs.python.org/3/library/wave.html - spacether

4

我使用了pysox

wave模块和许多其他模块似乎不支持mu-law wavs。

pysox要求你安装SoX并更新PATH以包括其所在目录。

import sox    
cbn=sox.Combiner()
sounds=[]
#PROCESS SOUND PATHS TO AN ARRAY
if len(sounds)>=2:
    print(sounds)
    cbn.build(sounds,'outputfilepath.ext','concatenate')

3
您可以使用Audiolab
import audiolab, scipy
a, fs, enc = audiolab.wavread('file1.wav')
b, fs, enc = audiolab.wavread('file2.wav')
c = scipy.vstack((a,b))
audiolab.wavwrite(c, 'file3.wav', fs, enc)

我需要安装任何软件包才能使用scipy吗?我正在使用Python2.6,如果需要的话,我可以下载兼容版本。您能提供链接吗?我尝试从scipy网站上下载但遇到了一些问题。如果有安装步骤,请建议一下。谢谢您的回答。您知道如何播放声音吗?我在播放方面遇到了问题,有什么解决方法吗? - kaushik
Python 2.6可以,Numpy/Scipy网站也可以。我可能会让其他人回答你的问题并提供进一步的建议。虽然我的答案可行,但可能还有更优雅、直接的解决方案。 - Steve Tjoa
我尝试安装来自scikits的audiolab,大小约为1.4 mb,并成功安装,但在运行您的代码时,它显示导入错误:没有名为audiolab的模块。我没有安装44mb的scipy包,这是问题吗?我需要安装它还是audiolab下载本身有问题? - kaushik
我也安装了Scipy,但仍然显示导入错误。我使用的是Windows下载并安装的,但仍然存在问题,可能的原因是什么? - kaushik
1
现在,我会使用 librosa。http://librosa.github.io/librosa/generated/librosa.core.load.html#librosa.core.load - Steve Tjoa

3
我会使用librosa.loadlibrosa.write_wav。请查看此文档
import librosa
import numpy as np
import librosa.display

example_audio = librosa.util.example_audio_file()
x, sr = librosa.load(example_audio, duration=5)
print('shape of x ==> ' + str(x.shape))
y, sr = librosa.load(example_audio, duration=5)
print('shape of y ==> ' + str(y.shape))
z = np.append(x,y)
print('shape of x+y = z ==> ' + str(z.shape))
librosa.output.write_wav('joined_file.wav', z, sr)

z_loaded, sr = librosa.load('joined_file.wav')
print('shape of z loaded ==> ' + str(z_loaded.shape))

输出结果:

x的形状 ==> (110250,)

y的形状 ==> (110250,)

x+y=z的形状 ==> (220500,)

已加载z的形状 ==> (220500,)


1
@Ryan更新了答案。你可能会对这里的更多代码示例感兴趣:https://github.com/gndps/vocal-training/blob/master/7_phrasedetection.ipynb,尽管文档不太容易阅读。 - penduDev
请注意,librosa 自带 numpyscipyscikit-learn,这对于只想合并 WAV 文件的人来说可能过于复杂。 - Tyler Dane Hitzeman

0

我使用SOX [1]库,然后像这样调用它

>>> import subprocess
>>> sound_output_path = /tmp
>>> sox_filenames = ['file.wav', 'file1.wav']
>>> subprocess.call(['sox'] + sox_filenames + ['%s/out.wav' % sound_output_path])

[1] http://sox.sourceforge.net/

[1] http://sox.sourceforge.net/


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