Python中的高通滤波器

2024-09-27 07:24:30 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在用Python录制5秒钟的现场音频片段,希望将低于某个频率(例如10kHz)的所有声音都剪掉。这是我目前的脚本:

import pyaudio, wave, time, sys, os
from array import array
from scipy import signal

FORMAT=pyaudio.paInt16
CHANNELS=1
CHUNK=1024
RATE=44100 
RECORD_SECONDS=5

def butter_highpass(cutoff, fs, order=5):
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
    return b, a

def butter_highpass_filter(data, cutoff, fs, order=5):
    b, a = butter_highpass(cutoff, fs, order=order)
    y = signal.filtfilt(b, a, data)
    return y
    
audio=pyaudio.PyAudio() 

stream=audio.open(format=FORMAT,channels=CHANNELS, 
                  rate=RATE,
                  input=True,
                  frames_per_buffer=CHUNK)
while True:
    # read data
    data=stream.read(CHUNK)
    data_chunk=array('h',data)
    data_chunk = butter_highpass_filter(data_chunk,10000,RATE)
    frames=[]
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
    # write to file
    words = ["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"]
    FILE_NAME= "".join(words) 
    wavfile=wave.open(FILE_NAME,'wb')
    wavfile.setnchannels(CHANNELS)
    wavfile.setsampwidth(audio.get_sample_size(FORMAT))
    wavfile.setframerate(RATE)
    wavfile.writeframes(b''.join(frames))
    wavfile.close()

但这似乎不起作用。我想把所有(或尽可能多的)低于规定频率的声音都剪掉。为什么我使用的过滤器似乎不能将10kHz以下的声音过滤掉?我怎样才能让它工作


Tags: import声音dataframessignalrateorderarray
1条回答
网友
1楼 · 发布于 2024-09-27 07:24:30

简短的

目标是对音频应用砖墙式10 kHz高通滤波器,然后保存它。音频连续录制并保存在5秒钟的片段中,以分离.wav文件

到目前为止我们有什么

目前,当前脚本:

  • 声明一个函数以应用输出为浮点值数组的butterworth高通过滤器(butter_high-pass_filter
    • butter_high-pass_filter使用signal.filtfilt
    • 函数的输入是短格式的(错误1)
data_chunk=array('h',data)
data_chunk = butter_high-pass_filter(data_chunk,10000,RATE)
  • data_chunk从未被使用过,因此高传递的音频帧从未保存到文件中(错误2)
  • 数据读取时间为5秒音频
frames=[]
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
  • stream.read正在阻塞,因此将等待读取正确的音频量
  • 然后,收集的数据以相同的格式写入wav文件
words = ["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"]
FILE_NAME= "".join(words)
wavfile=wave.open(FILE_NAME,'wb')
wavfile.setnchannels(CHANNELS)
wavfile.setsampwidth(audio.get_sample_size(FORMAT))
wavfile.setframerate(RATE)
wavfile.writeframes(b''.join(frames))
wavfile.close()

解决方案

这里的问题是,解决方案是多方面的,需要当前脚本中缺少的多个部分。 此外,为了避免进一步的复杂化,需要采取与最初计划略有不同的方法。在保存wav样本数据之前,只需将过滤器应用于样本数据,而不是实时应用过滤器。 这个

  • 消除了处理过滤器状态连续性的需要
  • 限制在数据类型之间来回转换的需要

此外,外部永久while循环已被删除。时间和内存开始成为一个问题。程序可以简单地反复运行,直到用例覆盖更加清晰

  • 为什么音频必须经过高通滤波
  • 为什么在记录了所有数据(即应用于wav文件)后不能进行过滤
  • 为什么必须保存数据
  • 数据格式、位深度、采样率是否有限制

在这些问题得到解决之前,有太多可能的路线,每一条路线都有限制,实际上可以在一个答案中涵盖

故障

该流程的完整细分将在

  1. 声明一个函数,该函数以浮点采样数据作为输入,以浮点数据作为输出的高通滤波器
  2. 将pyaudio中5秒的字节字符串数据连接到单个变量中
  3. 将数据解包为16位(有符号短)格式的样本数组
  4. 将样本缩放为介于1.0和-1.0之间的浮点格式
  5. 将数据发送到高通滤波器
  6. 按16位(有符号短)格式范围缩放过滤器样本
  7. 将16位过滤样本打包为字节字符串
  8. 将过滤后的字节字符串数据写入wav文件

脚本

import pyaudio, wave, time, sys, os, struct
from array import array
from scipy import signal

# 1. Declare a function that takes floating point sample data as input and high pass
#    filters with floating point data as output
# 2. concatenate 5 seconds of byte-string data from PyAudio into a single variable
# 3. unpack data as a 16-bit (signed short) format array of samples
# 4. scale samples to floating point format between 1.0 and -1.0
# 5. send data to high pass filter
# 6. scale filter samples in the range of 16-bit (signed short) format
# 7. pack 16-bit filtered samples into a byte string
# 8. write filtered byte-string data to a wav file.

#                                   -
# 1. Declare High Pass Filter

def butter_highpass(cutoff: float, fs: float, order: int = 5) -> tuple:
    """
    Generate FIR and IIR coefficients for a butterworth highpass filter

    :param cutoff: cutoff frequency (hz)
    :param fs: sampling rate
    :param order: filter order
    :return: tuple of filter coefficients
    """
    nyq = 0.5 * fs
    normal_cutoff = cutoff / nyq
    b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
    return b, a


def butter_highpass_filter(data: [float], cutoff: float, fs: float, order: int = 5) -> [float]:
    """
    apply a butterworth high pass filter to sample data in floating point format

    :param data: float sample data array
    :param cutoff: filter cutoff (hz)
    :param fs: sample data sampling rate
    :param order: filter order
    :return: floating point array of filtered sample data
    """
    b, a = butter_highpass(cutoff, fs, order=order)
    y = signal.filtfilt(b, a, data)
    return y

#                                   -
# Init Global Variables

FORMAT = pyaudio.paInt16
CHANNELS = 1
CHUNK = 1024
RATE = 44100
RECORD_SECONDS = 5

audio = pyaudio.PyAudio()

stream = audio.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    frames_per_buffer=CHUNK)
#                                   -
# Main Program

if __name__ == '__main__':

    #                                   -
    # 2. concat 5 seconds of data into a single string
    frames = b''
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        frames += stream.read(CHUNK)
    #                                   -
    # 3. Unpack data as a  16 - bit
    sample_data = array('h', frames)
    #                                   -
    # 4. scale samples to floating point format between 1.0 and -1.0
    samples = [sample_datum / (2**15) for sample_datum in sample_data]
    print("Max Amplitude:", max(samples))
    #                                   -
    # 5. send data to high pass filter
    filtered_sample_data = butter_highpass_filter(samples, 10000.0, RATE)
    # filtered_sample_data = samples
    #                                   -
    # 6. scale filter samples in the range of 16-bit (signed short) format
    #    (2 ** 14) for headroom (very lazy)
    sample_data_16_bit = [int(sample * (2 ** 14)) for sample in filtered_sample_data]
    # #                                   -
    # # 7. pack 16-bit filtered samples into a byte string
    raw_data = [struct.pack('h', sample) for sample in sample_data_16_bit]
    # #                                   -
    # # 8. Write Wav
    file_name = "".join(["RECORDING-", time.strftime("%Y%m%d-%H%M%S"), ".wav"])
    wavfile = wave.open(file_name, 'wb')
    wavfile.setnchannels(CHANNELS)
    wavfile.setsampwidth(audio.get_sample_size(FORMAT))
    wavfile.setframerate(RATE)
    wavfile.writeframes(b''.join(raw_data))
    wavfile.close()

评论

潜在的附加功能可能包括用matplotlib绘制频谱、对过滤后的音频进行规范化、将过程封装到自定义函数中,但这些都是留给OP的练习

下面是归一化后音频频谱的屏幕截图。正如@marco在评论中提到的,滤波器确实有一个斜率,这是预期的。这可以通过增加滤波器的阶数来改善

Frequency Spectrum

相关问题 更多 >

    热门问题