使用python过滤wav文件

Posted

技术标签:

【中文标题】使用python过滤wav文件【英文标题】:Filtering a wav file using python 【发布时间】:2014-09-15 05:16:12 【问题描述】:

所以我最近成功地构建了一个系统,它将完全使用 python 录制、绘制和播放音频 wav 文件。现在,我试图在我录制和开始绘制文件并将文件输出到扬声器之间进行一些过滤和音频混合。但是,我不知道从哪里开始。现在我要读入一个初始 wav 文件,应用一个低通滤波器,然后将新过滤的数据重新打包到一个新的 wav 文件中。这是我记录后用来绘制初始数据的代码。

import matplotlib.pyplot as plt
import numpy as np
import wave
import sys

spf = wave.open('wavfile.wav','r')

#Extract Raw Audio from Wav File
signal = spf.readframes(-1)
signal = np.fromstring(signal, 'Int16')

plt.figure(1)
plt.title('Signal Wave...')
plt.plot(signal)

这是我用来生成单音测试音频文件的一些代码:

import numpy as np
import wave
import struct

freq = 440.0
data_size = 40000
fname = "High_A.wav"
frate = 11025.0  
amp = 64000.0    

sine_list_x = []
for x in range(data_size):
    sine_list_x.append(np.sin(2*np.pi*freq*(x/frate)))

wav_file = wave.open(fname, "w")

nchannels = 1
sampwidth = 2
framerate = int(frate)
nframes = data_size
comptype = "NONE"
compname = "not compressed"

wav_file.setparams((nchannels, sampwidth, framerate, nframes,
comptype, compname))

for s in sine_list_x:
    wav_file.writeframes(struct.pack('h', int(s*amp/2)))

wav_file.close()

不过,我不确定如何应用所述音频过滤器并重新打包。非常感谢您提供的任何帮助和/或建议。

【问题讨论】:

你试过查看scipy的lfilter吗? 你想要sine_signal = np.sin(2*np.pi*freq*(np.arange(data_size)/frate))之类的东西,而不是生成正弦曲线的for循环,然后是wav_file.writeframes((sine_signal*amp/2).astype('h').tostring())之类的东西。 【参考方案1】:

第一步:你需要什么样的音频过滤器?

选择过滤波段

Low-pass Filter :从您的音频信号中移除最高频率 High-pass Filter :从您的音频信号中删除最低频率 Band-pass Filter :从音频信号中删除最高和最低频率

对于以下步骤,我假设您需要一个低通滤波器

选择您的截止频率

Cutoff frequency 是信号衰减 -3dB 的频率。

您的示例信号是 440Hz,所以让我们选择 400Hz 的 Cutoff frequency。然后,您的 440Hz 信号会被低通 400Hz 滤波器衰减(超过 -3dB)。

选择您的过滤器类型

根据this other *** answer

滤波器设计超出了 Stack Overflow 的范围 - 那是一个 DSP 问题,不是编程问题。过滤器设计涵盖了任何 DSP 教科书 - 去你的图书馆。我喜欢 Proakis 和 Manolakis 数字信号处理。 (Ifeachor 和 Jervis 的数字信号 处理也不错。)

进入一个简单的例子,我建议使用移动平均滤波器(用于简单的低通滤波器)。

见Moving average

在数学上,移动平均是一种卷积,因此可以将其视为信号处理中使用的低通滤波器的示例

这个移动平均低通滤波器是一个基本的滤波器,非常容易使用和理解。

移动平均线的参数是窗口长度

moving average 窗口长度和Cutoff frequency 之间的关系需要一点数学知识,并解释了here

代码将是

import math

sampleRate = 11025.0 
cutOffFrequency = 400.0
freqRatio = cutOffFrequency / sampleRate

N = int(math.sqrt(0.196201 + freqRatio**2) / freqRatio)

因此,在示例中,窗口长度将为 12

第二步:编码过滤器

手工移动平均线

见specific discussion on how to create a moving average in python

Alleo 的解决方案是

def running_mean(x, windowSize):
   cumsum = numpy.cumsum(numpy.insert(x, 0, 0)) 
   return (cumsum[windowSize:] - cumsum[:-windowSize]) / windowSize 

filtered = running_mean(signal, N)

使用 lfilter

或者,正如dpwilson 所建议的,我们也可以使用lfilter

win = numpy.ones(N)
win *= 1.0/N
filtered = scipy.signal.lfilter(win, [1], signal).astype(channels.dtype)

第三步:让我们把它们放在一起

import matplotlib.pyplot as plt
import numpy as np
import wave
import sys
import math
import contextlib

fname = 'test.wav'
outname = 'filtered.wav'

cutOffFrequency = 400.0

# from http://***.com/questions/13728392/moving-average-or-running-mean
def running_mean(x, windowSize):
  cumsum = np.cumsum(np.insert(x, 0, 0)) 
  return (cumsum[windowSize:] - cumsum[:-windowSize]) / windowSize

# from http://***.com/questions/2226853/interpreting-wav-data/2227174#2227174
def interpret_wav(raw_bytes, n_frames, n_channels, sample_width, interleaved = True):

    if sample_width == 1:
        dtype = np.uint8 # unsigned char
    elif sample_width == 2:
        dtype = np.int16 # signed 2-byte short
    else:
        raise ValueError("Only supports 8 and 16 bit audio formats.")

    channels = np.fromstring(raw_bytes, dtype=dtype)

    if interleaved:
        # channels are interleaved, i.e. sample N of channel M follows sample N of channel M-1 in raw data
        channels.shape = (n_frames, n_channels)
        channels = channels.T
    else:
        # channels are not interleaved. All samples from channel M occur before all samples from channel M-1
        channels.shape = (n_channels, n_frames)

    return channels

with contextlib.closing(wave.open(fname,'rb')) as spf:
    sampleRate = spf.getframerate()
    ampWidth = spf.getsampwidth()
    nChannels = spf.getnchannels()
    nFrames = spf.getnframes()

    # Extract Raw Audio from multi-channel Wav File
    signal = spf.readframes(nFrames*nChannels)
    spf.close()
    channels = interpret_wav(signal, nFrames, nChannels, ampWidth, True)

    # get window size
    # from http://dsp.stackexchange.com/questions/9966/what-is-the-cut-off-frequency-of-a-moving-average-filter
    freqRatio = (cutOffFrequency/sampleRate)
    N = int(math.sqrt(0.196196 + freqRatio**2)/freqRatio)

    # Use moviung average (only on first channel)
    filtered = running_mean(channels[0], N).astype(channels.dtype)

    wav_file = wave.open(outname, "w")
    wav_file.setparams((1, ampWidth, sampleRate, nFrames, spf.getcomptype(), spf.getcompname()))
    wav_file.writeframes(filtered.tobytes('C'))
    wav_file.close()

【讨论】:

您好,我认为您的代码不小心变成了高通滤波器。我在 wav 音频上使用它并最终移除了低音。我需要低通和高通滤波器,您能指出需要更改的部分吗? 我确认它对我来说是一个低通行证。可以分享一下你正在使用的wav文件吗? 对不起,我的错。它作为 LPF 工作。显然,低音的频率高于前景。 win, [1], signal中的[1]是什么意思? @user13107 参见nfilter 中的docs.scipy.org/doc/scipy/reference/generated/…,我们使用更通用的IIR 或FIR 滤波器概念。这是一个多项式定义的滤波器,每个方程a[0]*y[n] = b[0]*x[n] + b[1]*x[n-1] + ... + b[M]*x[n-M] - a[1]*y[n-1] - ... - a[N]*y[n-N] 我们使用b = win, a = [1], x = signal 然后方程是1*y[n] = win[0]*signal[n] + win[1]*signal[n-1] + ... + win[M]*signal[n-M] 这就像用大锤敲碎坚果,但它有效!【参考方案2】:

sox library可用于去静噪。

我发现this gist 有一些有用的命令作为示例

【讨论】:

以上是关于使用python过滤wav文件的主要内容,如果未能解决你的问题,请参考以下文章

Python NumPy 将 FFT 转换为文件

如何使用 python 连接两个 wav 文件?

创建并使用 WAV 文件作为对象 Python

在 python 中使用 scipy 和 librosa 读取 wav 文件

使用 Python 将 WEBM 文件转换为 WAV 文件 [关闭]

如何使用python [duplicate]连接多个.wav文件