Introduction
Due to a new project in the company I’ve been working in, we have been engaged in a project which should implement pattern recognition to detect whether an in use gearbox works well or not. The first step was to analyze the gearbox vibration which captured by an ordinary vibration sensor generated analog signals. With the assumption of resembling outcome of a microphone and a vibration sensor, we connect the sensor to the microphone input directly.
Note: If you have a problem with downloading, you can use this or this link. If the problem persists, leave your mail id so it can be sent to you directly!
Code Explanation
After creating the project and adding PORT.DLL and SoundAnalysis.dll references, it's needed to define Sound related function at header which has been encapsulated in PORT.DLL.
Private Declare Function SOUNDIS Lib "Port.Dll" () As Integer
It's called to detect whether a sound card is available or not. If there is no sound card, the return value will be 0
.
Private Declare Function SOUNDGETRATE Lib "Port.Dll" () As Integer
It's called to detect the current sampling rate. Default sampling rate is 11025 which is equal to 11KHz. Other possible sampling rate is 22050 (22KHz) and 44100 (44KHz).
Private Declare Function SOUNDGETBYTES Lib "Port.Dll" () As Integer
It's called to detect the current resolution whether may be 8 bits (1 Byte) or 16 bits (2 Bytes). Default resolution is 8 bits (1 Byte).
Private Declare Function SOUNDSETRATE Lib "Port.Dll" (ByVal Rate As Long) As Long
It's called to set the sampling rate. Accepted passing values are 11025, 22050 and 44100 and the return will be exactly equal to set value. In case of passing other values, resulted error is unknown.
Private Declare Function SOUNDSETBYTES Lib "Port.Dll" (ByVal Rate As Long) As Long
It's called to set the sampling resolution. Accepted passing values are 8 and 16 how return will be exactly equal to set value. I did not check its related error in case of other values.
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
It's one of the API functions so called my trace in programming which I have been using for making form printing possible. There are two functions in the program (Printing region) that clearly describe how it can be implemented.
Recording Timing NumericUpDown
determines the sampling period in milliseconds which sets the timer interval.
Window Filter Size NumericUpDown
allocates the length of window filter when 1 means no window filter will be implemented.
There are also two other NumericUpDown
shown Freq 01 and Freq 02. These two set the frequency of two intentional noise added to the main signal string to check the correctness of FFT function which can be removed or disabled by just inserting a comment sign in _Data_Capturing()
function.
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
In Timer
events, four functions are called which I can say the program relies on _Data_Capturing()
function.
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)
_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)
_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
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
is an array being used to store captured data on it, depending on data resolution its size varied between _Size
or _Size * 2
.
Using SOUNDIN
, the captured data is stored in _Samples
. There are two variables named _Temp_01
and _Temp_02
that determine points of two intentional noise.
The _Samples_Refined
is used to store refined data. Depending on resolution, two different methods are enacted.
_Samples_Refined(I) = CDbl(CInt(_Samples(I)) - 128) +
(Math.Sin(2 * Math.PI * _Temp_01) * 32) +
(Math.Sin(2 * Math.PI * _Temp_02) * 32)
The above is being used if 8 bit resolution is selected. In this case, the returned sampled value varies from 0 to 255 in which 128 means silent, so after casting data to a 32 bit integer, -128 added to, removes its offset. " + (Math.Sin(2 * Math.PI * _Temp_01) * 32) + (Math.Sin(2 * Math.PI * _Temp_02) * 32) " is the effect of intentional noise that can be omitted by just adding an ' before.
_Samples_Refined(I) = ((_Samples_Refined(I) / 128) * _Gain) * 128
It's used to implement user defined gain to refined signal.
It's important to note the returned captured data in 16 bits resolutions varies from -32768 to +32765 so removing the offset is not implemented.
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
It's just a simple window filtering implemented before FFT function is called.
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
saves the returned FFT array using SoundAnalysis.FftAlgorithm.Calculate()
function.
It's needed to pay attention that the returned FFT array length is nearest upper 2's power of input array length, e.g., if input array length is 100 (2^6 < 100 < 2^7), output length will be 128 (2^7). Finding the related frequency in an array may seem a bit strange. First of all, note just first half elements are usable and the other second half is exactly the mirror reflection of elements. For calculation related frequency, you have to use this formula:
_Size * _I / _Arr_Length
where _Size
is equal to number of samples, _Arr_Length
is returned FFT array (here is _Arr_Spec
) and _I
is elements index is FFT returned array which have to be between 0 and (_Arr_Length/2) .
If (_Size * _I / _Arr_Length >= 0) And (
_Size * _I / _Arr_Length < 1) Then
_Arr_Spec(_I) = 0
End If
The above piece of code is implemented to remove any effective frequency amoung 0 and 1. The same code:
If (_Size * _I / _Arr_Length > (_Removed_Freq - 1)) And (
_Size * _I / _Arr_Length < (_Removed_Freq + 1)) Then
_Arr_Spec(_I) = 0
End If
is doing exactly the same which removed frequency determined by related value in Removed Frequency NumericUpDown
.
There are two other functions in Timer
event that crystal clear using to draw captured signal and FFT signal in related picture boxes.
Points of Interest & Acknowledgements
The project I’ve made is a simple voice spectrum using a DLL as voice in capturing device meanwhile implementing FFT method helping to convert it to frequency. The project structure is simply trying to avoid usual showing off programming complexity. I have to appreciate Mr. Burkhard Kainka for his useful (but a bit CPU grappling!!) .DLL and also thank notmasteryet for very useful FFT convertor.
I would also like to give special thanks to Richard and Sean.
The project is just a simple VB.NET based initiative representation, hopefully the main has to be based on VC++ .NET using DirectSound.
Don’t hesitate to ask if you encounter any problems or have any questions.
History
- 12th February, 2010: Initial post
- 16th February, 2010: Updated source code