Python PyAudio + mic输入 - 特定的频率滤波器?

时间:2014-12-30 10:08:08

标签: python audio filtering pyaudio

我目前正在研究射电天文学项目,我需要随时监测音频信号的振幅。

我已经使用了user1405612建议的简化Python代码Detect tap with pyaudio from live mic,它接受​​麦克风输入并计算出RMS振幅,我添加了一个部分来简单地将值记录到CSV文件中。这工作得非常好,谢谢你必须得到ouser1405612!

但是有一种方法可以为这段代码实现一个简单的频率滤波器。例如,我对频率为19.580khz的RMS振幅感兴趣(实际上我想看看19.4hkz到19.6hkz的范围)?

有没有办法使用上面链接中的代码通过查看原始流数据或其他任何方式使用PyAudio执行此操作?我不想要任何复杂的东西,如图形,频谱分析等,只是一个简单的频率滤波器。不幸的是,在无法进行麦克风输入之前有带通滤波器,因此需要在计算机上完成。

提前致谢!

更新 - 2014年12月31日 - 她是我现在的代码:

# source https://stackoverflow.com/questions/4160175/detect-tap-with-pyaudio-from-live-mic

import pyaudio
import struct
import math
import datetime


FORMAT = pyaudio.paInt16 
SHORT_NORMALIZE = (1.0/32768.0)
CHANNELS = 1
#RATE = 44100 
RATE = 48000 
INPUT_BLOCK_TIME = 1
INPUT_FRAMES_PER_BLOCK = int(RATE*INPUT_BLOCK_TIME)
filename = 'Data.CSV'

def get_rms(block):

    count = len(block)/2
    format = "%dh"%(count)
    shorts = struct.unpack( format, block )

    # iterate over the block.
    sum_squares = 0.0
    for sample in shorts:
    # sample is a signed short in +/- 32768. 
    # normalize it to 1.0
        n = sample * SHORT_NORMALIZE
        sum_squares += n*n

    return math.sqrt( sum_squares / count )

pa = pyaudio.PyAudio()                                 

stream = pa.open(format = FORMAT,                      
         channels = CHANNELS,                          
         rate = RATE,                                  
         input = True,                                 
         frames_per_buffer = INPUT_FRAMES_PER_BLOCK)   

errorcount = 0                                                  

for i in range(1000):
    try:                                                    
        block = stream.read(INPUT_FRAMES_PER_BLOCK)         
    except IOError, e:                                      
        errorcount += 1                                     
        print( "(%d) Error recording: %s"%(errorcount,e) )  
        noisycount = 1                                      

    amplitude = get_rms(block)
    print amplitude

    #writeCSV
    i = datetime.datetime.now()
    f = open(filename,"a")
    f.write("{},{}\n".format(i,amplitude))
    f.close()

1 个答案:

答案 0 :(得分:3)

SciPy拥有数字带通信号所需的所有功能。

设计带通滤波器

对于这个例子,我将使用scipy.signal.butter设计一个三阶黄油带带通滤波器:

def design_filter(lowcut, highcut, fs, order=3):
    nyq = 0.5*fs
    low = lowcut/nyq
    high = highcut/nyq
    b,a = butter(order, [low,high], btype='band')
    return b,a

运行过滤器

函数的返回是一组可由spicy.signal.lfilter函数使用的滤波器系数。您将找到的大多数示例都是批处理数据,因此它们只调用一次该函数。既然你正在处理一个实时流,你的将会变得有点棘手。该函数将先前的过滤器状态作为参数并返回新状态。因此,您需要存储返回的状态,以便下次可以传递它。这大致是它如何适用于您现有的代码。你需要从get_rms函数中重构数据规范化,这无论如何都不是一个坏主意:

def normalize(block):
    count = len(block)/2a
    format = "%dh"%(count)
    shorts = struct.unpack( format, block )
    doubles = [x * SHORT_NORMALIZE from x in shorts]
    return doubles


def get_rms(samples):
    sum_squares = 0.0
    for sample in doubles:
        sum_squares += n*n
    return math.sqrt( sum_squares / count )


pa = pyaudio.PyAudio()                                 
stream = pa.open(format = FORMAT,                      
         channels = CHANNELS,                          
         rate = RATE,                                  
         input = True,                                 
         frames_per_buffer = INPUT_FRAMES_PER_BLOCK)   

errorcount = 0                                                  

# design the filter
b,a = design_filter(19400, 19600, 48000, 3)
# compute the initial conditions.
zi = lfilter_zi(b, a)

for i in range(1000):
    try:                                                    
        block = stream.read(INPUT_FRAMES_PER_BLOCK)         
    except IOError, e:                                      
        errorcount += 1                                     
        print( "(%d) Error recording: %s"%(errorcount,e) )  
        noisycount = 1          

    samples = normalize(block)                            

    bandpass_samples,zi = lfilter(b, a, samples, zi)

    amplitude = get_rms(samples)
    bandpass_ampl = get_rms(bandpass_samples)
    print amplitude
    print bandpass_ampl

抱歉,我无法运行此代码进行测试。有可能samples需要转换为np.array。

相关问题