声音扫描仪和 FFT 分析仪






4.83/5 (19投票s)
扫描模拟输入,以及FFT转换和分析。
引言
由于公司新项目,我们正在进行一项应该实现模式识别的项目,以检测使用的齿轮箱是否工作正常。第一步是分析由普通振动传感器捕捉到的齿轮箱振动,该传感器产生模拟信号。假设与麦克风和振动传感器的结果相似,我们将传感器直接连接到麦克风输入。
注意:如果您在下载时遇到问题,可以使用此链接或此链接。如果问题仍然存在,请留下您的邮箱地址,以便直接发送给您!
代码解释
在创建项目并添加PORT.DLL和SoundAnalysis.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日:更新源代码