Click here to Skip to main content
Click here to Skip to main content

Sound Scanner and FFT Analyzer

By , 16 Feb 2010
 

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)
                '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 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.

'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

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

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

About the Author

Mahdi Mansouri
Engineer
Malaysia Malaysia
Member
PhD Candidate in Biomedical Eng., Control
M.Eng Electronics, Control
B.Eng Computer, Hardware
That's all ;D !

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionFast Fourier Transform on Noise wavemembermeghranjani15 Nov '12 - 19:56 
Hi Mahdi,
I was delighted to see your blog on FFT as I have been struggling to get on with it. I work in Software R&D in an automotive firm. Am looking from a scratch as to how to analyse a noise wave using FFT in VB .NET. There are FFT toolboxes which are quite expensive to buy and we want to implement one on our own. Would like to hear back from you. If you already have code samples, Kindly share them with me.
Regards,
Megha
Email: meghranjani.dobhal@tmdfriction.com
AnswerRe: Fast Fourier Transform on Noise wavememberMahdi Mansouri15 Nov '12 - 21:27 
Dear Friend,
Please check ur mail.
Best Regards,
Mehdi Smile | :) .
Questionpeaks in each (range)memberLuizSp Antonio15 Aug '12 - 17:22 
Hello, Madhi
I am beginner in program development
have a reasonable idea in delphi. I wonder how do to capture and display labels on the peaks in each (range) frequency.
 
Also got a doubt about the post below (64 bit) when you use reported (studio 11) meant you compile in Visual Studio 11 right!
 
Thanks again for your attention,
AnswerRe: peaks in each (range)memberMahdi Mansouri27 Oct '12 - 18:03 
Dear Friend
 
(1) About delphi Programming, due to years far from the last time i coded in I can not help you right now and hope you could find the solution.
(2) Is seems simple for peak capturing. Just use a simple loop to compare signals and locate the index of each frequency that shows the peak.
(3) You captured that. Studio 11 means exactly Visual Studio 11.
 
Best Regards,
Mehdi Smile | :) .
QuestionError windows 7 - 64 bitsmemberLeticia Nascimento13 Aug '12 - 18:01 
Hello, friend I'm anxious to see your project,
the project problem hangs at compile time: I am using windowns 7 to compile on 64bit error:
An attempt was made to load a program with an incorrect format. (Exception HRESULT: 0x8007000)

How do I solve'' A Brazilian hug from a friend''
Luiz Antonio

AnswerRe: Error windows 7 - 64 bitsmemberpHat201113 Aug '12 - 18:37 
The DLLs were compiled for a 32 bit processor, I believe. To fix your problem, in the project properties, set the Target CPU to x86 (32 bit).
 
In Studio 11, it's under the 'Compile' tab.
 
Thumbs Up | :thumbsup:
http://www.kwtechreno.com

QuestionFind different overtones of a violine stringmemberpeter specht1 Aug '12 - 6:40 
Hi Mahdi,
thank you very much for this code! My question is: Is it possible to find from a certain fundamental tone the different partials in Hz?
Your code:
 
Lbl_Freq1.Text = "M.E. Freq : " & CDbl((_Size * _Index / _Arr_Length) * (1000 / _Time_Interval)).ToString("0.0") & " Hz"
 
displays the fundamental frequency in Hz.
How about the overtones?
 
Thank you in advance...
Peter
AnswerRe: Find different overtones of a violine stringmemberMahdi Mansouri27 Oct '12 - 18:36 
Dear Peter
 
I have searched a lot for, but yet I have not found any suitable answer for. I hope you could find the solution as soon as possible. Please keep me informed about.
 
Best Regards,
Mehdi Smile | :) .
QuestionHELP please: How to modify this to work for 2 channels (stereo)memberyassindds23 Jun '12 - 10:44 
Wow great job... Smile | Smile | :) How should I modify this code to work for 2 channels (stereo)?
AnswerRe: HELP please: How to modify this to work for 2 channels (stereo)memberMahdi Mansouri27 Oct '12 - 18:37 
Dear Friend
 
This work basis is a mono channel DLL, so , if you are interested in dual channel I advise you to use DirectX library instead.
 
Best Regards,
Mehdi Smile | :) .
QuestionDll Error returned.memberTxon11 Jun '12 - 23:06 
Hi and thanks.
 
When trying to test your software, the following message returns.
 
Dll file not found "Port.Dll".
Cannot find specified module. HResult: 0x08007007E
 
Files are correctly downloaded.
 
Machine: 4x64 bits Intel. OS: XP-32, professional.
 
Regards, and thanks again.
AnswerRe: Dll Error returned.memberStefan Clements21 Jun '12 - 23:17 
In the soundscan folder there's a file called PORT.DLL if you copy and paste it to bin debug and release folders it will work, also I had to delete the reference to soundanalysis and re add it.
Questionport.dll problem with windows 7 64 bit, VS 2010membermarcopmp9 May '12 - 3:31 
Hi, I downloaded and tried to run in the above environment, but it gives me the "FKRSound Scan" error, saying program with incorrect format. Any idea to find the rigth PORT.DLL ?
or, some places to see on how to substitute the Dircect x library ?
Thnaks
AnswerRe: port.dll problem with windows 7 64 bit, VS 2010memberMahdi Mansouri27 Oct '12 - 18:39 
Dear Friend
 
I am so sorry for late replying. It is your kindness if just look at other similar questions and answers for. If you will still have had problem, please let me know.
 
Best Regards,
Mehdi Smile | :) .
Questionthrough speech controling gamemembermaheen shah18 Apr '12 - 7:22 
hi guyx...
iam in 1st semester of bscs and i have to make aproject...
i want to make an speech recognizing software in c language....
but i didnt find any source code like this in c language...
can any one help me???? or give me an sample code that how we can convert speech into text by using c....
plx help me plxxxxxxxxxxxxxxxxxxxxxxx
AnswerRe: through speech controling gamememberMahdi Mansouri27 Apr '12 - 8:39 
Dear Friend
It might be available, but I have not had such sample or seen it in simple C.
Regards,
Mehdi
QuestionDetecting Specific FrequenciesmemberOliver Dalton5 Mar '12 - 3:17 
Hi Mahdi,
 
I have been looking at your useful project to try to help me solve a problem.
 
I need to be able to detect specific frequencies in a series of tones, and if they are detected I need to return a corresponding number. For example, if 1981Hz is detected return '0'; if 1124Hz is detected return '2'. These tones are very short (about 40ms).
 
I have three questions:
 
1) How can I make the sample rate faster? If I reduce it below 100ms, I get an overflow error. I have looked at this, but I cannot understand why it happens.
 
2) How can I set a minimum threshold, so that 'background' noise is ignored?
 
3) How can I get the raw frequency information?
 
If you could help me or point me in the right direction I would be really grateful!
 
Many thanks,
 
Ollie Dalton
AnswerRe: Detecting Specific FrequenciesmemberMahdi Mansouri13 Mar '12 - 2:50 
Dear Friend,
I'm so sorry reply you late, recently I've been entangled in assignments, even can't wobble Wink | ;) !
The best source for giving info. about the raw frequencies, you should omit window filtering and after the process after FFT conversion, the returned array contains raw frequencies.
I wish I would find a way for reducing the sample time below 100 ms, but due to PORT.DLL incompatibility, the lowest sample rate seems unreachable how if you're still interested in taking shorter time, you can use DirectX library for (For finding a very good example please refer to FFT Guitar Tuner[^]).
For reducing the background effect it's better first take a look at white noise and detect which frequencies constitute this range, then you can easily filter hem all.
I hope the reply can help you.
Please don't hesitate to ask in any further matter.
MehdiSmile | :) .
QuestionRemarkmemberViktor Signaievskyi15 Dec '11 - 8:22 
Nice and simple application! Very useful for a beginner who are interested in fast example to signal processing.
 
My remark is below.
 
As you wrote: "Default sampling rate is 11025 which is equal to 11KHz" is not correct.
11025(Hz) != 11000(Hz).
 
Thanks!
 
Best regards,
Viktor Signaievskyi
AnswerRe: RemarkmemberMahdi Mansouri13 Mar '12 - 2:02 
U'r welcome Smile | :) Big Grin | :-D .
AnswerError in FFT RoutinememberMember 797603124 Jul '11 - 7:50 
Hello Mahdi,
I was trying to use your FFT routine in your Sound Scanner application and found an error in the code. Notice that you never set LE2. You have LE=LE/2 and it should be LE2=LE/2. Now the routine works great.
AnswerRe: Error in FFT RoutinememberMahdi Mansouri24 Jul '11 - 18:27 
Thanks,
It was a matter I have not noticed yet. I hope other who'll use the code take this point in;D.
GeneralAn attempt to load a program...memberMember 797603114 Jun '11 - 8:24 
I'm trying to use SoundScan and I have Windows7 and Visual Studio 2010 Pro. As others have said, errors come up. "An attempt to load a program with an incorrect format" error message is generated. The program seems to be FKR_Sound Scan. Is there anything I can do to correct. What is the program FKR_Sound Scan?
Thank you
GeneralRe: An attempt to load a program...memberMember 797603116 Jun '11 - 13:40 
The problem seems to be that I can't seem to add port.dll as a reference to the application. If I try to add a reference to port.dll in Windows\System32 folder, the file is not visible. However it is visible using Windows Explorer. If I try to add reference to copy in the VB application folder I get error "A reference PORT.DLL could not be added. Please make sure the file is accessible, and it is a valid assembly or COM component."
Any ideas out there?
AnswerRe: An attempt to load a program...memberMahdi Mansouri16 Jun '11 - 21:39 
I've checked the sound Scanner in both XPSP3 and Win7, having the same error, although not in WinXP SP2 nor Win2000 had some alike. Let me check the program again and find a solution.
Good LuckBig Grin | :-D .
P.S.
During recent weeks I'm too busy and not able to answer promptly;D!!!!!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 16 Feb 2010
Article Copyright 2010 by Mahdi Mansouri
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid