【基于pyAudioKits的Python音频信号处理项目(一)】实现音频频谱分析仪并进行交互式滤波器设计

pyAudioKits是基于librosa和其他库的强大Python音频工作流支持。

API速查手册

通过pip安装:

pip install pyAudioKits

pyAudioKits的GitHub地址,如果这个项目帮助到了你,请为它点上一颗star,谢谢你的支持!如果你在使用过程中有任何问题,请在评论区留言或在GitHub上提issue,我将持续对该项目进行维护。

在进行音频信号有关研究时,我们常常需要用到语谱图的分析。同时,我们也需要进行降噪或者有用的频率成分的提取。

常规情况下,为了实现降噪或者有用的频率成分的提取,我们会将整个音频信号通过一个时不变滤波器。如果噪声是平稳信号,该方法可以有效滤除噪声。然而实际情况下,噪声往往是非平稳的,这就意味着我们需要在不同时间点上使用参数不同的滤波器,即使用一个时变滤波器

短时傅里叶变换可以用于生成音频信号的语谱图,语谱图上包含了信号随时间变化的频率成分信息,因此我们完全可以将时变滤波器的设置和语谱图的分析进行结合

Filter-Artist基于pyAudioKits开发,实现了一个STFT语谱图定位滤波分析系统,这个系统旨在对音频信号进行短时傅里叶变换。而且,为了更好地对语谱图进行分析,我们让用户可以借助GUI,使用鼠标,在特定时间的特定频点设置巴特沃斯滤波器,交互性地提取自己感兴趣的信号成分、屏蔽噪声,观察滤波后的波形并实时播放滤波后的音频。

该项目的GitHub地址。如果它对你有用,请为它点上一颗star,谢谢!

运行环境:

Python ≥ 3.8.3

依赖:

pyAudioKits ≥ 1.0.6

PyQt5 ≥ 5.15.4

numpy ≥ 1.22.4

opencv-python ≥ 4.5.2.52

  1. 在命令行输入:

    python main.py
    

    以执行main.py,得到窗口如下图所示。

    请添加图片描述

  2. 点按Load按钮,并读取预置的录音文件rec2_01.wav。

    请添加图片描述

    此时显示了rec2_01.wav的语谱图。

    请添加图片描述

  3. 可以选用多种窗型来显示语谱图。默认展示的是矩形窗的结果:

    请添加图片描述

    使用汉明窗:

    image-20220731054631722
  4. 可以设置巴特沃斯滤波器的阶数和通带/阻带宽度。

    image-20220731054631722
  5. 不勾选Erase Mode的情况下,设置的是带通滤波器。在语谱图上点按鼠标并拖动,则鼠标初始位置决定滤波器设置的起始时间和中心频率,鼠标释放位置决定滤波器设置的结束时间。设置一个宽度较大的高频通带以提取蜂鸣器音。

    image-20220731054631722
  6. 勾选Erase Mode的情况下,设置的是带阻滤波器。在语谱图上点按鼠标并拖动,则鼠标初始位置决定滤波器设置的起始时间和中心频率,鼠标释放位置决定滤波器设置的结束时间。设置三个宽度较小的阻带以消除蜂鸣器音。

    请添加图片描述

  7. 点击play按钮播放音频。

    请添加图片描述

  8. 点击Save按钮对音频进行保存。

    请添加图片描述

  9. 点击Redo按钮重置对音频的编辑。

    请添加图片描述

核心代码:

核心代码位于audio.py文件内

from pyAudioKits.audio import read_Audio
from pyAudioKits.analyse import FFT
from pyAudioKits.filters import lowPassButterN, highPassButterN, bandPassButterN, bandStopButterN
import matplotlib.pyplot as plt
import numpy as np

class myspectrogram:
    def __init__(self,direction,window):
        self.__wav = read_Audio(direction)	#利用pyAudioKits的api读取音频
        self.__M=0.03	#设置窗长为0.03s
        self.__R=0.015	#设置步长为0.015s

        if window=="Hamming":
            self.__Window = "hamming"
        elif window=="Rectangle":
            self.__Window = None

    def __stft(self):
        self.__freq = FFT(self.__wav.framing(self.__M, 1 - self.__R/self.__M, self.__Window))	#利用pyAudioKits的api进行短时傅里叶变换
    
    def save(self,direction):
        self.__wav.save(direction)	#利用pyAudioKits的api保存音频
    
    def play(self):
        self.__wav.sound()	#利用pyAudioKits的api播放音频

    def drawFreq(self):
        self.__stft()
        #创建子图,并取消横纵坐标
        plt.figure(figsize=(8,2))
        plt.axis('off')
        plt.gca().xaxis.set_major_locator(plt.NullLocator())
        plt.gca().yaxis.set_major_locator(plt.NullLocator())
        plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0, hspace = 0, wspace = 0)
        plt.margins(0,0)
        self.__freq.plot(ax=plt.gca(),cbar=False,freq_scale="mel",plot_type="dB")	#将子图传入pyAudioKits的频谱绘制api
        plt.savefig("tmp.png")	#保存子图,后续通过PyQt读取并显示

    def filtering(self,n,sizeFreq,erase,xposeStart,yposeStart,xposeEnd,yposeEnd):
        def freq_transform(freq):	#将鼠标在频谱上点击的y轴位置,即一个[0,1]之间的数,映射到梅尔尺度频率
            sr = self.__wav.sr
            length = int(self.__M  * sr / 2)
            max_freq_point = int(sr / 2)
            max_freq = sr / 2
            freq_points = 700 * (np.power(10, np.linspace(0, 2595 * np.log10(1 + max_freq/700), length)/2595) - 1)
            freq_point = freq_points[int(length * freq)]
            freqs = np.linspace(0,max_freq,max_freq_point)
            return freqs[int(freq_point)]

        #将鼠标在频谱上点击的x轴位置,即一个[0,1]之间的数,映射到音频时间,后续用于音频切片
        xsize=self.__R * self.__freq.shape[0]
        xposeStart=xsize*xposeStart
        xposeEnd=xsize*xposeEnd

        yposeNow=float(yposeStart)
        width=float(sizeFreq)/200	#将滤波器宽度映射到[0,1]的尺度上
		
        #计算滤波器的两个截止频率
        low=yposeNow-width
        high=yposeNow+width

        #获取滤波器并进行滤波
        if erase:
            if low>0 and high<1:
                filt = lambda x: bandStopButterN(x, n, freq_transform(low), freq_transform(high))
            if low<=0:
                filt = lambda x: highPassButterN(x, n, freq_transform(high))
            if high>=1:
                filt = lambda x: lowPassButterN(x, n, freq_transform(low))
        else:
            if low>0 and high<1:
                filt = lambda x: bandPassButterN(x, n, freq_transform(low), freq_transform(high))
            if low<=0:
                filt = lambda x: lowPassButterN(x, n, freq_transform(high))
            if high>=1:
                filt = lambda x: highPassButterN(x, n, freq_transform(low))
        if xposeStart<xposeEnd:
            self.__wav[xposeStart:xposeEnd]=filt(self.__wav[xposeStart:xposeEnd])
        if xposeEnd<xposeStart:
            self.__wav[xposeEnd:xposeStart]=filt(self.__wav[xposeEnd:xposeStart])

猜你喜欢

转载自blog.csdn.net/weixin_43441742/article/details/126093811