12,298,927 members (51,409 online)
Add your own
alternative version

473.8K views
17.2K downloads
256 bookmarked
Posted

# FFT of waveIn audio signals

, 3 Apr 2007 CPOL
 Rate this:
Please Sign up or sign in to vote.
An article on using the Fast Fourier Transform on audio signals.

## Introduction

The Fast Fourier Transform (FFT) allows users to view the spectrum content of an audio signal. The FFT code presented here was written by Don Cross, his homepage appears to have subsequently been taken down. Rather than explain the mathematical theory of the FFT, I will attempt to explain its usefulness as it relates to audio signals.

The FFT allows users to obtain the spectral makeup of an audio signal, obtain the decibels of its various frequencies, or obtain the intensity of its various frequencies. Spectral viewers (shown in the image above), Equalizers, or VU-Meters may all use the FFT in order to display their results. The difference between them then depends upon one of a couple of equations that take the real and imaginary components of the FFT, and return either the intensity or decibel levels to be used in the graphed result. The following code takes both the real and imaginary components of the FFT result, and returns the intensity and decibels.

```inline double GetFrequencyIntensity(double re, double im)
{
return sqrt((re*re)+(im*im));
}
#define mag_sqrd(re,im) (re*re+im*im)
#define Decibels(re,im) ((re == 0 && im == 0) ? (0) :
10.0 * log10(double(mag_sqrd(re,im))))
#define Amplitude(re,im,len) (GetFrequencyIntensity(re,im)/(len))
#define AmplitudeScaled(re,im,len,scale) ((int)Amplitude(re,im,len)%scale)```

The FFT uses the audio signal as its real component, and uses a `NULL` pointer for its imaginary component indicating that the imaginary data does not exist. Upon its return, the FFT will return both the real and imaginary data components based upon the data given as the real component. The is mirrored with the return samples so that `0-FFT_LEN/2` contains the data, and `FFT_LEN/2` to `FFT_LEN` contains a reverse of the data. This mistake was corrected in my code. The code that performs the FFT follows:

```DWORD nCount = 0;
for (DWORD dw = 0; dw < FFT_LEN; dw++)
{
{
//copy audio signal to fft real component for left channel
finleft[nCount] = (double)((short*)pwh->lpData)[dw++];
//copy audio signal to fft real component for right channel
finright[nCount++] = (double)((short*)pwh->lpData)[dw];
}
}
//Perform FFT on left channel
fft_double(FFT_LEN/2,0,finleft,NULL,fout,foutimg);
float re,im,fmax=-99999.9f,fmin=99999.9f;
for(int i=1;i < FFT_LEN/4-1;i++)
//Use FFT_LEN/4 since the data is mirrored within the array.
{
re = fout[i];
im = foutimg[i];
//get amplitude and scale to 0..256 range
fdraw[i]=AmplitudeScaled(re,im,FFT_LEN/2,256);
if (fdraw[i] > fmax)
{
fmax = fdraw[i];
}
if (fdraw[i] < fmin)
{
fmin = fdraw[i];
}
}
//Use this to send the average band amplitude to something
int nAvg, nBars=16, nCur = 0;
for(int i=1;i < FFT_LEN/4;i++)
{
nAvg = 0;
for (int n=0; n < nBars; n++)
{
nAvg += (int)fdraw[i];
}
nAvg /= nBars;
//Send data here to something,
//nothing to send it to so we print it.
TRACE("Average for Bar#%d is %d\n",nCur++,nAvg);
i+=nBars-1;
}
DataHolder* pDataHolder = (DataHolder*)lpData;
// Draw left channel
CFrequencyGraph* pPeak = (CFrequencyGraph*)pDataHolder->pData;
if (::IsWindow(pPeak->GetSafeHwnd()))
{
pPeak->SetYRange(0,256);
pPeak->Update(FFT_LEN/4,fdraw);
}

// Perform FFT on right channel
fmax=-99999.9f,fmin=99999.9f;
fft_double(FFT_LEN/2,0,finright,NULL,fout,foutimg);
fdraw[0] = fdraw[FFT_LEN/4] = 0;
for(i=1;i < FFT_LEN/4-1;i++)
//Use FFT_LEN/4 since the data is mirrored within the array.
{
re = fout[i];
im = foutimg[i];
//get Decibels in 0-110 range
fdraw[i] = Decibels(re,im);
if (fdraw[i] > fmax)
{
fmax = fdraw[i];
}
if (fdraw[i] < fmin)
{
fmin = fdraw[i];
}
}
//Draw right channel
CFrequencyGraph* pPeak2 = (CFrequencyGraph*)pDataHolder->pData2;
if (::IsWindow(pPeak2->GetSafeHwnd()))
{
pPeak2->SetNumberOfSteps(50);
//Use updated dynamic range for scaling
pPeak2->SetYRange((int)fmin,(int)fmax);
pPeak2->Update(FFT_LEN/4,fdraw);
}
```

This code is contained in a callback function that is called every time the `waveIn` functions return with updated audio signal data. The code that actually performs the FFT looks like:

```void fft_double (unsigned int p_nSamples, bool p_bInverseTransform,
double *p_lpRealIn, double *p_lpImagIn,
double *p_lpRealOut, double *p_lpImagOut)
{

if(!p_lpRealIn || !p_lpRealOut || !p_lpImagOut) return;

unsigned int NumBits;
unsigned int i, j, k, n;
unsigned int BlockSize, BlockEnd;

double angle_numerator = 2.0 * PI;
double tr, ti;

if( !IsPowerOfTwo(p_nSamples) )
{
return;
}

if( p_bInverseTransform ) angle_numerator = -angle_numerator;

NumBits = NumberOfBitsNeeded ( p_nSamples );

for( i=0; i < p_nSamples; i++ )
{
j = ReverseBits ( i, NumBits );
p_lpRealOut[j] = p_lpRealIn[i];
p_lpImagOut[j] = (p_lpImagIn == NULL) ? 0.0 : p_lpImagIn[i];
}

BlockEnd = 1;
for( BlockSize = 2; BlockSize <= p_nSamples; BlockSize <<= 1 )
{
double delta_angle = angle_numerator / (double)BlockSize;
double sm2 = sin ( -2 * delta_angle );
double sm1 = sin ( -delta_angle );
double cm2 = cos ( -2 * delta_angle );
double cm1 = cos ( -delta_angle );
double w = 2 * cm1;
double ar[3], ai[3];

for( i=0; i < p_nSamples; i += BlockSize )
{

ar[2] = cm2;
ar[1] = cm1;

ai[2] = sm2;
ai[1] = sm1;

for ( j=i, n=0; n < BlockEnd; j++, n++ )
{

ar[0] = w*ar[1] - ar[2];
ar[2] = ar[1];
ar[1] = ar[0];

ai[0] = w*ai[1] - ai[2];
ai[2] = ai[1];
ai[1] = ai[0];

k = j + BlockEnd;
tr = ar[0]*p_lpRealOut[k] - ai[0]*p_lpImagOut[k];
ti = ar[0]*p_lpImagOut[k] + ai[0]*p_lpRealOut[k];

p_lpRealOut[k] = p_lpRealOut[j] - tr;
p_lpImagOut[k] = p_lpImagOut[j] - ti;

p_lpRealOut[j] += tr;
p_lpImagOut[j] += ti;

}
}

BlockEnd = BlockSize;

}

if( p_bInverseTransform )
{
double denom = (double)p_nSamples;

for ( i=0; i < p_nSamples; i++ )
{
p_lpRealOut[i] /= denom;
p_lpImagOut[i] /= denom;
}
}

}```

And it requires the following supporting functions:

```///////////////////////////////////////////////////////////
// check is a number is a power of 2
///////////////////////////////////////////////////////////

bool IsPowerOfTwo( unsigned int p_nX )
{

if( p_nX < 2 ) return false;

if( p_nX & (p_nX-1) ) return false;

return true;

}

///////////////////////////////////////////////////////////
// return needed bits for fft
///////////////////////////////////////////////////////////

unsigned int NumberOfBitsNeeded( unsigned int p_nSamples )
{

int i;

if( p_nSamples < 2 )
{
return 0;
}

for ( i=0; ; i++ )
{
if( p_nSamples & (1 << i) ) return i;
}

}

///////////////////////////////////////////////////////////
// ?
///////////////////////////////////////////////////////////

unsigned int ReverseBits(unsigned int p_nIndex, unsigned int p_nBits)
{

unsigned int i, rev;

for(i=rev=0; i < p_nBits; i++)
{
rev = (rev << 1) | (p_nIndex & 1);
p_nIndex >>= 1;
}

return rev;
}

///////////////////////////////////////////////////////////
// return a frequency from the basefreq and num of samples
///////////////////////////////////////////////////////////

double Index_to_frequency(unsigned int p_nBaseFreq,
unsigned int p_nSamples, unsigned int p_nIndex)
{

if(p_nIndex >= p_nSamples)
{
return 0.0;
}
else if(p_nIndex <= p_nSamples/2)
{
return ( (double)p_nIndex /
(double)p_nSamples * p_nBaseFreq );
}
else
{
return ( -(double)(p_nSamples-p_nIndex) /
(double)p_nSamples * p_nBaseFreq );
}

}```

The included sample class, `CFrequencyGraph`, will draw a sample EQ, peak meter, and spectral graph using the frequency intensity. Hopefully, this serves as a decent introduction to the uses and basics of the Fast Fourier Transform for audio signals. Other functions of the FFT include using it in combination with a Beat Detection Algorithm to detect beats in an audio signal. Another page with useful FFT information is located at FFT Spectrum Analyser.

This article uses `waveIn*` functions to retrieve the soundcard's data from the playing source. Therefore, you must manually open your soundcard properties and change the recording source to use mono/stereo mix or waveIn as the selected recording line. Also, if you set the playback wave option to `false`, this will override the recording options and no sound will appear. For information about doing an inverse FFT for transforming frequencies into an audio signal, please do a Google search on "Inverse FFT" or "IFFT".

## History

• Version 1.3: Fixed Spectrum drawing, Changed Pixelgram color, changed Process routine to match new Recording parameters.
• Version 1.2: Fixed many drawing bugs, changed to use amplitude scaled and decibels.

## License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

## About the Author

 Web Developer United States
Programming using MFC and ATL for almost 12 years now. Currently studying Operating System implementation as well as Image processing. Previously worked on DSP and the use of FFT for audio application. Programmed using ADO, ODBC, ATL, COM, MFC for shell interfacing, databasing tasks, Internet items, and customization programs.

## Comments and Discussions

 First PrevNext
 Migration Member 1208672825-Oct-15 20:15 Member 12086728 25-Oct-15 20:15
 Code contribution pigeonb25-Mar-13 15:39 pigeonb 25-Mar-13 15:39
 I want to know FFT with Flow to detect Pitch.... Mrugrajsinh24-Oct-12 4:48 Mrugrajsinh 24-Oct-12 4:48
 Re: I want to know FFT with Flow to detect Pitch.... Fred Ackers9-Nov-12 10:30 Fred Ackers 9-Nov-12 10:30
 Re: I want to know FFT with Flow to detect Pitch.... Mrugrajsinh14-Nov-12 4:36 Mrugrajsinh 14-Nov-12 4:36
 DIRECT SOUND ''Live'' LuizSp Antonio13-Aug-12 18:29 LuizSp Antonio 13-Aug-12 18:29
 Re: DIRECT SOUND ''Live'' Fred Ackers14-Aug-12 8:57 Fred Ackers 14-Aug-12 8:57
 Re: DIRECT SOUND ''Live'' LuizSp Antonio15-Aug-12 10:29 LuizSp Antonio 15-Aug-12 10:29
 Re: DIRECT SOUND ''Live'' Fred Ackers18-Oct-12 12:12 Fred Ackers 18-Oct-12 12:12
 Assertion failure at line 183 hahakimkim27-May-12 15:09 hahakimkim 27-May-12 15:09
 Re: Assertion failure at line 183 Fred Ackers28-May-12 19:43 Fred Ackers 28-May-12 19:43
 Re: Assertion failure at line 183 hahakimkim3-Jun-12 4:06 hahakimkim 3-Jun-12 4:06
 Re: Assertion failure at line 183 Fred Ackers18-Oct-12 12:15 Fred Ackers 18-Oct-12 12:15
 Doesn't satisfy Parseval Member 773975324-Apr-12 7:49 Member 7739753 24-Apr-12 7:49
 Re: Doesn't satisfy Parseval Fred Ackers28-May-12 19:53 Fred Ackers 28-May-12 19:53
 Trying to get this done in C# stuck here..did I translate this right? mangotj1-Mar-11 7:37 mangotj 1-Mar-11 7:37
 My vote of 5 dspmx26-Oct-10 3:37 dspmx 26-Oct-10 3:37
 Decibels Value! mobailwang18-Oct-10 23:51 mobailwang 18-Oct-10 23:51
 My vote of 5 mrk1000017-Oct-10 17:51 mrk10000 17-Oct-10 17:51
 sampling frequency smart_dummies30-Mar-10 22:51 smart_dummies 30-Mar-10 22:51
 Measure units for Real and Ima. Data juanjo.montero21-Oct-09 9:11 juanjo.montero 21-Oct-09 9:11
 conver to C# enenegn13-May-09 3:42 enenegn 13-May-09 3:42
 Decibels explained? jweston15-Apr-09 11:53 jweston 15-Apr-09 11:53
 Re: Decibels explained? RichardMant1-Jun-10 4:41 RichardMant 1-Jun-10 4:41
 Frequency sarlacc17-Mar-09 3:15 sarlacc 17-Mar-09 3:15
 Internet archive for Don Cross' original page jo0ls4-Jan-09 7:51 jo0ls 4-Jan-09 7:51
 a few question about play wave file with DirectSound and display spectrum. jacky_zz4-Sep-08 20:23 jacky_zz 4-Sep-08 20:23
 Question about scaling the amplitude tabor2514-Jul-08 2:36 tabor25 14-Jul-08 2:36
 Re: Question about scaling the amplitude LLSS_China10-Mar-09 1:13 LLSS_China 10-Mar-09 1:13
 Re: Question about scaling the amplitude RichardMant1-Jun-10 4:46 RichardMant 1-Jun-10 4:46
 Re: Question about scaling the amplitude skydre1-Jul-10 3:50 skydre 1-Jul-10 3:50
 Re: Question about scaling the amplitude RichardMant1-Jul-10 4:42 RichardMant 1-Jul-10 4:42
 [Message Deleted] Danny Rodriguez27-Jan-08 9:09 Danny Rodriguez 27-Jan-08 9:09
 xcode version eoghancunneen9-Jan-08 3:25 eoghancunneen 9-Jan-08 3:25
 for another condition aa_esa23-Nov-07 18:07 aa_esa 23-Nov-07 18:07
 use many cpu percenge shallopquan13-Aug-07 0:15 shallopquan 13-Aug-07 0:15
 re Leon.Minos2-Aug-07 18:39 Leon.Minos 2-Aug-07 18:39
 VisualC++ Error edguardian1-Aug-07 11:45 edguardian 1-Aug-07 11:45
 Re: VisualC++ Error Fred Ackers4-Aug-07 8:19 Fred Ackers 4-Aug-07 8:19
 Re: VisualC++ Error AlexanderGao1-Apr-10 2:55 AlexanderGao 1-Apr-10 2:55
 Poor audio quality ChrisMitchell_26-May-07 19:23 ChrisMitchell_ 26-May-07 19:23
 Re: Poor audio quality Fred Ackers29-May-07 12:32 Fred Ackers 29-May-07 12:32
 Re: Poor audio quality [modified] ChrisMitchell_30-May-07 8:59 ChrisMitchell_ 30-May-07 8:59
 Re: Poor audio quality Fred Ackers31-May-07 12:51 Fred Ackers 31-May-07 12:51
 Re: Poor audio quality bquintero27-Jun-07 18:27 bquintero 27-Jun-07 18:27
 Thank you DanMax5-Apr-07 23:05 DanMax 5-Apr-07 23:05
 Number of channels? jakeparks30-Mar-07 8:08 jakeparks 30-Mar-07 8:08
 Re: Number of channels? Fred Ackers31-Mar-07 13:03 Fred Ackers 31-Mar-07 13:03
 Re: Number of channels? jakeparks2-Apr-07 13:50 jakeparks 2-Apr-07 13:50
 Re: Number of channels? Fred Ackers3-Apr-07 10:02 Fred Ackers 3-Apr-07 10:02
 Last Visit: 31-Dec-99 18:00     Last Update: 28-May-16 6:55 Refresh 1234 Next »

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160525.2 | Last Updated 3 Apr 2007
Article Copyright 2004 by Fred Ackers
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid