Python - 下采样wav音频文件

时间:2015-06-03 12:10:28

标签: python audio wav wave downsampling

我必须在不使用任何外部python库的情况下将wav文件从44100Hz下采样到16000Hz,因此最好是wave和/或audioop。我尝试使用setframerate函数将wav文件帧速率更改为16000,但这只会减慢整个录制速度。如何将音频文件下采样到16kHz并保持相同的音频长度?

非常感谢您提前

6 个答案:

答案 0 :(得分:10)

您可以使用Librosa的load()函数,

import librosa    
y, s = librosa.load('test.wav', sr=8000) # Downsample 44.1kHz to 8kHz

安装Librosa的额外努力可能值得安心。

专业提示:在Anaconda上安装Librosa时,您还需要install ffmpeg,所以

pip install librosa
conda install -c conda-forge ffmpeg

这可以节省NoBackendError()错误。

答案 1 :(得分:4)

谢谢大家的回答。我已经找到了一个解决方案,它的工作非常好。这是整个功能。

def downsampleWav(src, dst, inrate=44100, outrate=16000, inchannels=2, outchannels=1):
    if not os.path.exists(src):
        print 'Source not found!'
        return False

    if not os.path.exists(os.path.dirname(dst)):
        os.makedirs(os.path.dirname(dst))

    try:
        s_read = wave.open(src, 'r')
        s_write = wave.open(dst, 'w')
    except:
        print 'Failed to open files!'
        return False

    n_frames = s_read.getnframes()
    data = s_read.readframes(n_frames)

    try:
        converted = audioop.ratecv(data, 2, inchannels, inrate, outrate, None)
        if outchannels == 1:
            converted = audioop.tomono(converted[0], 2, 1, 0)
    except:
        print 'Failed to downsample wav'
        return False

    try:
        s_write.setparams((outchannels, 2, outrate, 0, 'NONE', 'Uncompressed'))
        s_write.writeframes(converted)
    except:
        print 'Failed to write wav'
        return False

    try:
        s_read.close()
        s_write.close()
    except:
        print 'Failed to close wav files'
        return False

    return True

答案 2 :(得分:3)

我尝试使用Librosa,但是出于某些原因,即使在给定行y, s = librosa.load('test.wav', sr=16000)librosa.output.write_wav(filename, y, sr)之后,也无法以给定的采样率(16000,从44kHz下采样)保存声音文件。 但是pydub效果很好。 jiaaro编写的很棒的库,我使用了以下命令:

from pydub import AudioSegment as am
sound = am.from_file(filepath, format='wav', frame_rate=22050)
sound = sound.set_frame_rate(16000)
sound.export(filepath, format='wav')

上面的代码指出,我以frame_rate为22050读取的文件已更改为16000,并且export函数用新的frame_rate覆盖了此文件中的现有文件。它比librosa更好,但是我正在寻找比较两个软件包之间速度的方法,但是由于我的数据很少,所以还没有弄清楚!!!

参考:https://github.com/jiaaro/pydub/issues/232

答案 3 :(得分:2)

您可以在scipy中使用重新取样。这有点令人头疼,因为在bytestring本机到python和scipy中需要的数组之间需要进行一些类型转换。还有一个令人头痛的问题,因为在Python的wave模块中,没有办法判断数据是否已签名(仅当它是8位或16位时)。它可能(应该)适用于两者,但我没有测试过它。

这是一个小程序,它将(无符号)8和16位单声道从44.1转换为16.如果你有立体声,或使用其他格式,它应该不那么难以适应。编辑代码开头的输入/输出名称。从来没有使用命令行参数。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#  downsample.py
#  
#  Copyright 2015 John Coppens <john@jcoppens.com>
#  
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#  
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#  
#

inwave = "sine_44k.wav"
outwave = "sine_16k.wav"

import wave
import numpy as np
import scipy.signal as sps

class DownSample():
    def __init__(self):
        self.in_rate = 44100.0
        self.out_rate = 16000.0

    def open_file(self, fname):
        try:
            self.in_wav = wave.open(fname)
        except:
            print("Cannot open wav file (%s)" % fname)
            return False

        if self.in_wav.getframerate() != self.in_rate:
            print("Frame rate is not %d (it's %d)" % \
                  (self.in_rate, self.in_wav.getframerate()))
            return False

        self.in_nframes = self.in_wav.getnframes()
        print("Frames: %d" % self.in_wav.getnframes())

        if self.in_wav.getsampwidth() == 1:
            self.nptype = np.uint8
        elif self.in_wav.getsampwidth() == 2:
            self.nptype = np.uint16

        return True

    def resample(self, fname):
        self.out_wav = wave.open(fname, "w")
        self.out_wav.setframerate(self.out_rate)
        self.out_wav.setnchannels(self.in_wav.getnchannels())
        self.out_wav.setsampwidth (self.in_wav.getsampwidth())
        self.out_wav.setnframes(1)

        print("Nr output channels: %d" % self.out_wav.getnchannels())

        audio = self.in_wav.readframes(self.in_nframes)
        nroutsamples = round(len(audio) * self.out_rate/self.in_rate)
        print("Nr output samples: %d" %  nroutsamples)

        audio_out = sps.resample(np.fromstring(audio, self.nptype), nroutsamples)
        audio_out = audio_out.astype(self.nptype)

        self.out_wav.writeframes(audio_out.copy(order='C'))

        self.out_wav.close()

def main():
    ds = DownSample()
    if not ds.open_file(inwave): return 1
    ds.resample(outwave)
    return 0

if __name__ == '__main__':
    main()

答案 4 :(得分:0)

要对信号进行下采样(也称为decimate)(意味着降低采样率),或者需要在数据之间进行插值的上采样(即提高采样率)。

这个想法是,您需要以某种方式绘制点之间的曲线,然后以新的采样率从该曲线中获取值。这是因为您想了解未采样的某个时间的声波值,因此必须以一种或另一种方式猜测该值。唯一容易采样的情况是将采样率除以整数$ k $。在这种情况下,您只需要抽取$ k $样本桶,并仅保留第一个样本即可。但这不会回答您的问题。请参见下面的图片,其中有一条以两种不同比例采样的曲线。

Signal

如果您了解原理,则可以手工完成,但是我强烈建议您使用库。原因是插值正确的方法不容易,或者不明显。

您可以使用线性插值(将点与直线连接)或二项式插值(将三个点与多项式连接),或者(有时是声音的最佳选择)使用傅立叶变换并在频率空间内插值。 由于傅里叶变换不是您要手工重写的内容,因此,如果您希望获得良好的二次采样/二次采样, 有关使用scipy的不同算法的两条上采样曲线,请参见下图。 “重采样”功能使用傅立叶变换。 Resampling scipy

我确实是在加载44100Hz波形文件并需要48000Hz采样数据的情况下,所以我写了以下几行来加载数据:

    # Imports
    from scipy.io import wavfile
    import scipy.signal as sps

    # Your new sampling rate
    new_rate = 48000

    # Read file
    sampling_rate, data = wavfile.read(path)

    # Resample data
    number_of_samples = round(len(data) * float(new_rate) / sampling_rate))
    data = sps.resample(data, number_of_samples)

请注意,如果您仅进行下采样并且想要比傅立叶更快的速度,还可以使用方法decimate

答案 5 :(得分:0)

首先,您需要导入“ librosa”库 使用“ librosa.load”重新采样音频文件 librosa.load(path,sr)最初为sr(采样率)=22050。如果要保留本机采样率,请使sr = None。否则音频将被重新采样到提供的采样率