65.9K
CodeProject 正在变化。 阅读更多。
Home

声音扫描仪和 FFT 分析仪

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (19投票s)

2010年2月12日

CDDL

5分钟阅读

viewsIcon

283771

downloadIcon

8270

扫描模拟输入,以及FFT转换和分析。

引言

由于公司新项目,我们正在进行一项应该实现模式识别的项目,以检测使用的齿轮箱是否工作正常。第一步是分析由普通振动传感器捕捉到的齿轮箱振动,该传感器产生模拟信号。假设与麦克风和振动传感器的结果相似,我们将传感器直接连接到麦克风输入。

注意:如果您在下载时遇到问题,可以使用此链接此链接。如果问题仍然存在,请留下您的邮箱地址,以便直接发送给您!

代码解释

在创建项目并添加PORT.DLLSoundAnalysis.dll引用后,需要在头文件中定义封装在PORT.DLL中的声音相关函数。

Private Declare Function SOUNDIS Lib "Port.Dll" () As Integer

它用于检测是否存在声卡。如果没有声卡,返回值将是0

Private Declare Function SOUNDGETRATE Lib "Port.Dll" () As Integer

它用于检测当前的采样率。默认采样率是11025,等于11KHz。其他可能的采样率是22050(22KHz)和44100(44KHz)。

Private Declare Function SOUNDGETBYTES Lib "Port.Dll" () As Integer

它用于检测当前的分辨率,可能是8位(1字节)或16位(2字节)。默认分辨率是8位(1字节)。

Private Declare Function SOUNDSETRATE Lib "Port.Dll" (ByVal Rate As Long) As Long

它用于设置采样率。接受的值为11025、22050和44100,返回值将与设定值完全相等。如果传入其他值,则结果错误未知。

Private Declare Function SOUNDSETBYTES Lib "Port.Dll" (ByVal Rate As Long) As Long

它用于设置采样分辨率。接受的值为8和16,返回值将与设定值完全相等。我没有检查其他值相关的错误。

Private Declare Auto Function BitBlt Lib "gdi32.dll" (ByVal hdcDest As IntPtr,
    ByVal nXDest As Integer, ByVal nYDest As Integer, ByVal nWidth As Integer,
    ByVal nHeight As Integer, ByVal hdcSrc As IntPtr, ByVal nXSrc As Integer,
    ByVal nYSrc As Integer, ByVal dwRop As System.Int32) As Boolean

这是API函数之一,我称之为“跟踪”,在我编程中用于实现表单打印。程序中有两个函数(打印区域)清楚地描述了如何实现这一点。

录音计时NumericUpDown决定了以毫秒为单位的采样周期,它设置了定时器间隔。

窗口滤波器大小NumericUpDown分配了窗口滤波器的长度,其中1表示不实现窗口滤波器。

还有两个NumericUpDown显示为Freq 01和Freq 02。这两个用于设置添加到主信号字符串的两个附加噪声的频率,以检查FFT功能的正确性,可以通过在_Data_Capturing()函数中插入注释符号来删除或禁用。

Private Sub Tmr_Runner_Tick(ByVal sender As System.Object,
    ByVal e As System.EventArgs) Handles Tmr_Runner.Tick
    Try
        If Not Working Then
            Call _Stop()
            Exit Try
        End If
        Call _Data_Capturing()
        Call _Fourier_Transformation()
        Call Picture_Redrawing_Wave(_Samples_Refined)
        Call Draw_Fourier(_Arr_Spec, _Width_Fourier, _Heigth_Fourier / 2)
    Catch ex As Exception
        Call _Stop()
        MsgBox(ex.ToString, MsgBoxStyle.Critical, "FKR_Sound Scan")
    End Try
End Sub

Timer事件中,调用了四个函数,可以说程序依赖于_Data_Capturing()函数。

Private Function _Data_Capturing() As Boolean
    Try
        Dim I As Integer, J As Integer
        Dim Str_Out As String = ""
        Dim _Sum As Double = 0
        Dim _X_Unit_01 As Single = _Width / _Freq_01
        Dim _X_Unit_02 As Single = _Width / _Freq_02
        Dim _Step_01 As Single = _Width / _Size
        Dim _Temp_01 As Single = 0
        Dim _Temp_02 As Single = 0
        If _Bits = _Sample_Bit.Mono Then
            ReDim _Samples(_Size - 1)
        Else
            ReDim _Samples((_Size * 2) - 1)
        End If
        SOUNDIN(_Samples, CLng(_Size))
        If _Bits = _Sample_Bit.Mono Then
            Str_Out = ""
            ReDim _Samples_Refined(_Size - 2)
            For I = 0 To (UBound(_Samples) - 1)
                'The Range is amoung 0 to 255
                _Temp_01 = (I * _Step_01 * 100) Mod (_X_Unit_01 * 100)
                _Temp_01 = _Temp_01 / (100 * _X_Unit_01)
                _Temp_02 = (I * _Step_01 * 100) Mod (_X_Unit_02 * 100)
                _Temp_02 = _Temp_02 / (100 * _X_Unit_02)
                _Samples_Refined(I) = CDbl(CInt(_Samples(I)) - 128) + 
                   (Math.Sin(2 * Math.PI * _Temp_01) * 32) + 
                   (Math.Sin(2 * Math.PI * _Temp_02) * 32)
                _Samples_Refined(I) = ((_Samples_Refined(I) / 128) * _Gain) * 128
            Next
        Else
            ReDim _Samples_Refined(_Size - 1)
            Dim _Byte_arr(1) As Byte
            For I = 0 To UBound(_Samples) Step 2
                _Byte_arr(0) = _Samples(I)
                _Byte_arr(1) = _Samples(I + 1)
                'The Range changed to 0 to 65535
                _Temp_01 = (I * _Step_01 * 50) Mod (_X_Unit_01 * 100)
                _Temp_01 = _Temp_01 / (100 * _X_Unit_01)
                _Temp_02 = (I * _Step_01 * 50) Mod (_X_Unit_02 * 100)
                _Temp_02 = _Temp_02 / (100 * _X_Unit_02)
                _Samples_Refined(I / 2) = CDbl(CInt(BitConverter.ToInt16(_Byte_arr,
                    0))) + (Math.Sin(2 * Math.PI * _Temp_01) * 512) + 
                    (Math.Sin(2 * Math.PI * _Temp_02) * 512)
                _Samples_Refined(I / 2) = ((_Samples_Refined(I / 2) / 32768) *
                    _Gain) * 32768
            Next
        End If
        'Window Filter Implementing
        ReDim _WindowFilter(_Window_Size - 1)
        For I = 0 To UBound(_Samples_Refined)
            _WindowFilter(I Mod _Window_Size) = _Samples_Refined(I)
            _Sum = 0
            For J = 0 To UBound(_WindowFilter)
                _Sum += _WindowFilter(J)
            Next
            _Samples_Refined(I) = (_Sum / _Window_Size)
        Next
    Catch ex As Exception
        MsgBox(ex.Message, MsgBoxStyle.Critical, "FKR_Sound Scan")
        Call _Stop()
    End Try
End Function

_Samples是一个用于存储捕获数据的数组,根据数据分辨率,其大小在_Size_Size * 2之间变化。

使用SOUNDIN,捕获的数据存储在_Samples中。有两个变量名为_Temp_01_Temp_02,它们确定了两个附加噪声的点。

_Samples_Refined用于存储精炼后的数据。根据分辨率,会执行两种不同的方法。

_Samples_Refined(I) = CDbl(CInt(_Samples(I)) - 128) + 
                      (Math.Sin(2 * Math.PI * _Temp_01) * 32) + 
                      (Math.Sin(2 * Math.PI * _Temp_02) * 32)

以上用于8位分辨率。在这种情况下,返回的采样值范围是0到255,其中128表示静默,因此在将数据转换为32位整数后,加上-128可以去除其偏移。“ + (Math.Sin(2 * Math.PI * _Temp_01) * 32) + (Math.Sin(2 * Math.PI * _Temp_02) * 32) ”是附加噪声的效果,可以通过在前面加上一个“来省略。

_Samples_Refined(I) = ((_Samples_Refined(I) / 128) * _Gain) * 128

它用于实现用户定义的增益到精炼信号。

重要的是要注意,在16位分辨率下返回的捕获数据范围是-32768到+32765,因此没有实现去除偏移。

'Window Filter Implementing
ReDim _WindowFilter(_Window_Size - 1)
For I = 0 To UBound(_Samples_Refined)
     _WindowFilter(I Mod _Window_Size) = _Samples_Refined(I)
     _Sum = 0
     For J = 0 To UBound(_WindowFilter)
         _Sum += _WindowFilter(J)
     Next
    _Samples_Refined(I) = (_Sum / _Window_Size)
Next

这是在调用FFT函数之前实现的简单窗口滤波。

Private Function _Fourier_Transformation() As Boolean
            .
            .
            .
        _Arr_Spec = SoundAnalysis.FftAlgorithm.Calculate(_Samples_Refined)
        For _I = 0 To CInt(UBound(_Arr_Spec) * 0.95)
            If (_Size * _I / _Arr_Length >= 0) And _
			(_Size * _I / _Arr_Length < 1) Then
                _Arr_Spec(_I) = 0
            End If 
            If (_Size * _I / _Arr_Length > (_Removed_Freq - 1)) And (
                _Size * _I / _Arr_Length < (_Removed_Freq + 1)) Then
                _Arr_Spec(_I) = 0
            End If
            If _Max < _Arr_Spec(_I) Then
                _Max = _Arr_Spec(_I)
                _Index = _I
            End If
        Next
            .
            .
            .
End Function

_Arr_Spec保存使用SoundAnalysis.FftAlgorithm.Calculate()函数返回的FFT数组。

需要注意的是,返回的FFT数组的长度是输入数组长度的最近的2的幂的上限。例如,如果输入数组长度为100(2^6 < 100 < 2^7),则输出长度将为128(2^7)。在数组中查找相关频率可能有点奇怪。首先,请注意只有一半的元素是可用的,另一半是元素的精确镜像。要计算相关频率,您必须使用此公式

_Size * _I / _Arr_Length 

其中_Size等于样本数,_Arr_Length是返回的FFT数组(此处为_Arr_Spec),_I是FFT返回数组中的元素索引,它必须在0和(_Arr_Length/2)之间。

If (_Size * _I / _Arr_Length >= 0) And (
    _Size * _I / _Arr_Length < 1) Then
    _Arr_Spec(_I) = 0
End If

上面的代码片段用于去除0和1之间的任何有效频率。相同的代码

If (_Size * _I / _Arr_Length > (_Removed_Freq - 1)) And (
   _Size * _I / _Arr_Length < (_Removed_Freq + 1)) Then

   _Arr_Spec(_I) = 0

End If

做的事情完全一样,它移除了由“Removed Frequency”NumericUpDown中的相关值确定的频率。

Timer事件中还有两个函数,它们清晰地用于在相关的picture box中绘制捕获的信号和FFT信号。

兴趣点与致谢

我创建的项目是一个简单的语音频谱,使用DLL作为捕获设备的语音,同时实现FFT方法来将其转换为频率。项目结构力求避免通常炫耀的编程复杂性。我必须感谢Burkhard Kainka先生提供的有用的(但有点耗费CPU的!).DLL,并且感谢notmasteryet提供的非常有用的FFT转换器

我也要特别感谢Richard和Sean。

该项目只是一个简单的基于VB.NET的倡议性演示,希望主要部分能基于VC++ .NET和DirectSound。

如果您遇到任何问题或有任何疑问,请随时提出。

历史

  • 2010年2月12日:首次发布
  • 2010年2月16日:更新源代码
© . All rights reserved.