在Python中对音频信号进行窗函数处理以实现伽马音滤波器组

1 投票
1 回答
2358 浏览
提问于 2025-04-18 16:54

我刚开始学习编程,特别是Python。我想用四阶伽马音滤波器来实现一个听觉模型。我需要把一个信号分成39个通道。当我使用一个较小的信号(大约884726位)时,代码运行得很好,但我觉得缓存区满了,所以我必须重启环境才能第二次运行代码。我尝试使用flush(),但没有成功。

所以,我决定用汉宁窗来处理信号,但也没有成功。为了更清楚,我需要把一个音频信号分成39个通道,进行整流(半波整流),然后再传入第二组四阶滤波器,这次是10个通道。我想在把信号送入第二组滤波器之前先对信号进行降采样。这段代码是用另一个函数生成的系数来实现滤波器组的,b的维度是39x4096。

def filterbank_application(input, b, verbose = False):
"""
A function to run the input through a bandpass filter bank with parameters defined by the b and a coefficients.

Parameters:
    * input (type: array-like matrix of floats) - input signal. (Required)
    * b (type: array-like matrix of floats) - the b coefficients of each filter in shape b[numOfFilters][numOfCoeffs]. (Required)

Returns:
* y (type: numpy array of floats) - an array with inner dimensions equal to that of the input and outer dimension equal to
    the length of fc (i.e. the number of bandpass filters in the bank) containing the outputs to each filter. The output 
    signal of the nth filter can be accessed using y[n].
"""

input = np.array(input)
bshape = np.shape(b)
nFilters = bshape[0]
lengthFilter = bshape[1]   

shape = (nFilters,) + (np.shape(input))
shape = np.array(shape[0:])
shape[-1] = shape[-1] + lengthFilter -1
y = np.zeros((shape))    

for i in range(nFilters):
    if(verbose):
        sys.stdout.write("\r" + str(int(np.round(100.0*i/nFilters))) + "% complete.")
        sys.stdout.flush()
    x = np.array(input)
    y[i] = signal.fftconvolve(x,b[i]) 
if(verbose): sys.stdout.write("\n")
return y

samplefreq,input = wavfile.read('sine_sweep.wav')


input = input.transpose()
input = (input[0] + input[1])/2

b_coeff1 = gammatone_filterbank(samplefreq, 39)
Output = filterbank_application(input, b_coeff1)

Rect_Output = half_rectification(Output)

我想把音频分成每段20秒的块。如果你能告诉我一个有效的方式来处理我的信号,我将非常感激,因为整个音频会比我现在使用的信号大6倍。提前谢谢你。

1 个回答

2

如果你在使用32位的Python,可能会遇到内存消耗的问题。你的代码每处理一个样本大约需要320个字节(40个缓冲区,每个样本8个字节)。可用的最大内存是2GB,这意味着信号的最大大小大约是600万个样本。如果你的文件大约有100秒长,那么你可能会开始遇到问题。

解决这个问题有两种方法(如果这真的是问题所在,我看不出你的代码还有其他明显的崩溃原因)。你可以选择使用64位的Python,或者重写你的代码,以更合理的方式使用内存。

如果我理解你的问题没错,你想要:

  1. 通过39个FIR滤波器处理信号(每个滤波器4096个点)
  2. 对处理后的信号进行半波整流
  3. 对半波整流后的信号进行降采样
  4. 用10个FIR滤波器(或者IIR滤波器?)对每个降采样后的整流信号进行滤波

这样你就会得到39 x 10个信号,这些信号能反映输入音频信号的攻击和频率响应。

我会这样做:

  1. 把原始信号保存在内存中(如果放不下,可以用一个叫做memmap的技巧解决,但如果你的信号不太长,应该是可以放下的)
  2. 取第一个伽马音滤波器,进行卷积运算(scipy.signal.fftconvolve
  3. 进行半波整流(sig = np.clip(sig, 0, None, out=sig)
  4. 对信号进行降采样(例如scipy.signal.decimate
  5. 运行10个滤波器(例如scipy.signal.fftconvolve
  6. 对所有其他伽马音滤波器重复步骤2-5

这样你就不需要在内存中保留39个滤波后信号的副本,只需保留最终结果。

在没有看到完整的应用程序和更多环境信息的情况下,很难判断你是否真的有内存问题。


有个简单的信号处理问题:为什么要进行半波整流?为什么不进行全波整流:sig = np.abs(sig)? 全波整流后的低通滤波更简单,而且音频信号应该是相对对称的。


你可能想在代码中做一些改动:

  • 你在函数的第一行就把input转换成数组——在循环中没有必要再做一次(在运行fftconvolve时直接用input代替x

  • 创建一个空的y可以用y = np.empty((b.shape[0], input.shape[0] + b.shape[1] - 1))来完成;这样会更易读,并且减少一些不必要的变量

  • input.transpose()会消耗一些时间和内存,并且不是必需的。你可以改为:input = np.average(input, axis=1) 这会对数组中的每一行进行平均(即对通道进行平均)。

你的sys.stdout.write等代码没有问题。这里使用flush是因为否则文本会写入缓冲区,只有当缓冲区满时才会显示在屏幕上。

撰写回答