14,269,736 members

# FFT Guitar Tuner

Rate this:
10 Aug 2010MIT
Using a Fast Fourier Transform to calculate the fundamental frequency of the captured audio sound

## Introduction

This article shows how to use a Fast Fourier Transform (FFT) algorithm to calculate the fundamental frequency of a captured audio sound. Also, we will see how to apply the algorithm to analyze live sound to build a simple guitar tuner: the code provides a solution to the problem of calculation of the fundamental frequency of the played pitch.

## Background

The computer can capture live sound/music using a microphone that is connected to the sound card. Modern sound cards can capture digital signals. A digital signal is a set of quantized sound values that were taken in uniformly spaced times. The digital signal does not provide any information about frequencies that are present in the sound. To determine that, the data need to be analyzed.

The Short-Time Fourier Transform (STFT) makes representation of the phase and magnitude of the signal. The result of the STFT can be used to produce the spectrogram of the signal: the magnitude squared over time and frequencies. We will use a Fast Fourier Transform (FFT) to generate the spectrogram of the signal of short periods of time. After the spectrogram is calculated, the fundamental frequency can be determined by finding the index of the maximum value of the magnitude squared. The improved algorithm finds several such places, candidate frequency bins, with the magnitude squared in the top of the maximum values, and further analyzes them to verify the candidate fundamental frequencies by using the signal data.

When a note is played on a musical instrument, the sound waves are generated by strings, air, or the speaker - an instrument generates a musical note. One of the characteristics of a musical note is a pitch (fundamental frequency). Traditionally musical alphabet frequencies are divided by octaves, and then by semitones. An octave has 12 named pitches: C (prime), C#, D, D#, E, F, F#, G, G#, A, A#, and B. Octaves also have names: great, small, one-lined, two-lined, etc. The "standard pitch" (A one-lined or A4) has a fundamental frequency of its sound waves equals to 440 Hz. The frequencies of two neighboring notes are different by 21/12, and frequencies of the notes with the same name in two neighboring octaves are different by 2.

Table: Notes and Their Fundamental Frequencies
Note NameTraditional Octave Names (Scientific), Hz
Great (2)Small (3)One-lined (4)Two-lined (5)
C65.4064130.8128261.6256523.2511
C#69.2957138.5913277.1826554.3653
D73.4162146.8324293.6648587.3295
D#77.7817155.5635311.1270622.2540
E82.4069164.8138329.6276659.2551
F87.3071174.6141349.2282698.4565
F#92.4986184.9972369.9944739.9888
G97.9989195.9977391.9954783.9909
G#103.8262207.6523415.3047830.6094
A110.0000220.0000440.0000880.0000
A#116.5409233.0819466.1638932.3275
B123.4708246.9417493.8833987.7666

The typical (six string) guitar normally plays pitches of great through two-lined octaves. The pitches of the open strings (E2, A2, D3, G3, B3, and E4) are selected in the table in bold.

## Using the Code

The solution contains three projects: the main windows application (FftGuitarTuner), the sound analysis library (SoundAnalysis), and the sound capture library (SoundCapture). The heart of the solution and the SoundAnalysis project is the FFT algorithm (see the `Calculate` method of the `SoundAnalysis.FftAlgorithm` class):

```// bit reversal
ComplexNumber[] data = new ComplexNumber[length];
for (int i = 0; i < x.Length; i++)
{
int j = ReverseBits(i, bitsInLength);
data[j] = new ComplexNumber(x[i]);
}

// Cooley-Tukey
for (int i = 0; i < bitsInLength; i++)
{
int m = 1 << i;
int n = m * 2;
double alpha = -(2 * Math.PI / n);

for (int k = 0; k < m; k++)
{
// e^(-2*pi/N*k)
ComplexNumber oddPartMultiplier =
new ComplexNumber(0, alpha * k).PoweredE();

for (int j = k; j < length; j += n)
{
ComplexNumber evenPart = data[j];
ComplexNumber oddPart = oddPartMultiplier * data[j + m];
data[j] = evenPart + oddPart;
data[j + m] = evenPart - oddPart;
}
}
}

// calculate spectrogram
double[] spectrogram = new double[length];
for (int i = 0; i < spectrogram.Length; i++)
{
spectrogram[i] = data[i].AbsPower2();
}```

The data for the algorithm is provided from the sound card capture buffer. The abstract `SoundCapture.SoundCaptureBase` utility class is an adapter for DirectSound's `Capture` and `CaptureBuffer` classes, that helps to encapsulate buffering and setting up the audio format parameters. The application requires Microsoft DirectX 9 runtime components for the live sound capture from the microphone.

Figure: Main Application Form

After the application is started, select the sound device and play a note. The application will capture the live sound and will calculate the current fundamental frequency of the signal. The information can be used to tune the guitar.

## Points of Interest

To calculate the Fast Fourier Transform, the Cooley-Tukey algorithm was used. It gives good performance for the required task. To challenge the algorithm, the application analyses about 22,000 sample blocks in real time: the sound is captured at a 44,100 Hz rate and a 16 bits sample size, and the analysis is performed twice a second.

The sound analysis library can be used for tone, background noise, sound, or speech detection. Series of the spectrogram of the continued sound can be displayed as a 2D (or 3D) image to present it visually.

## History

• 1st January, 2009: Initial version
• 2nd January, 2009: Added algorithm code snippet
• 3rd August, 2010: Corrected article typos; new frequency detection algorithm

## License

This article, along with any associated source code and files, is licensed under The MIT License

## About the Author

 Software Developer United States
No Biography provided

## Comments and Discussions

 What about win7 AmerSawan10-Jul-11 14:42 AmerSawan 10-Jul-11 14:42
 Re: What about win7 TheJediMaster11-Jul-11 10:44 TheJediMaster 11-Jul-11 10:44
 Re: What about win7 bmac20-Jul-13 6:02 bmac 20-Jul-13 6:02
 Here is how to display the name of the note and octave for the note. TheJediMaster8-Jul-11 3:43 TheJediMaster 8-Jul-11 3:43
 "frequencyTextBox" update TheJediMaster8-Jul-11 1:09 TheJediMaster 8-Jul-11 1:09
 Re: "frequencyTextBox" update TheJediMaster12-Jul-11 12:42 TheJediMaster 12-Jul-11 12:42
 Re: "frequencyTextBox" update notmasteryet12-Jul-11 15:00 notmasteryet 12-Jul-11 15:00
 I made some changes: Voice tuner TheJediMaster7-Jul-11 8:17 TheJediMaster 7-Jul-11 8:17
 Hi, I noticed that your program can also be used to detect the pitch of a note that someone is singing. I compared your program with "Sing & See". If you do not know this software, you can learn about it in this site: http://www.singandsee.com[^] Running the two softwares at the same time, you can see that the frequency of the note displayed by your software and the Sing& See is very similar. Well, what I want to do is use your code for training of singing. The idea is roughly this: the program will display a note in the score and the student needs to sing that note for one second. If he keeps the right note for 1 second, then a new note is displayed on the score. And so it will train your musical perception and hearing, and keeping your voice in tune. Well, I'm almost getting it. The program is displaying a random note, which for now is limited to one octave. And when I sing that note for one second, a new note appears. Example: The system displays the note "E". If I sing this note for one second, the system will display a new note. If during this period that I am singing the note "E", I sing an "E#" or any other note, the timer starts counting again. That is, I need to keep that note for one second without error. The problem I'm facing is this: if I sing this "E" for a second, no matter in what octave is that "E", the software will consider my note as correct. And I want to change this. If the "E" that the software showed is the "E4", then only will be considered correct if I sing the "E4" for one second. If I sing the "E3" or the "E5", the system should understand that the answer is wrong. I think the change needs to be done in this part of the code below, I'm not sure. I wish you would give me a help. The variable NoteNames has a full octave (12 notes), I would like this variable had several octaves, identified as follows: NoteNames As String() = {"A2", "A#2", "B/H2", "C2", "C#2", "D2", _ "D#2", "E2", "F2", "F#2", "G2", "G#2", _ "A3", "A#3", "B/H3", "C3", "C#3", "D3", _ "D#3", "E3", "F3", "F#3", "G3", "G#3", _ "A4", "A#4", "B/H4", "C4", "C#4", "D4", _ "D#4", "E4", "F4", "F#4", "G4", "G#4", _ "A5", "A#5", "B/H5", "C5", "C#5", "D5", _ "D#5", "E5", "F5", "F#5", "G5", "G#5"} And I would like the software shows exactly which note is being played or sung. I was looking for a code like yours for many years. Finally found your arriving very close to what I need. If you can help me in this issue ... thank you. Soon I will provide you the project. ====================== Shared NoteNames As String() = {"A", "A#", "B/H", "C", "C#", "D", _ "D#", "E", "F", "F#", "G", "G#"} Shared ToneStep As Double = Math.Pow(2, 1.0 / 12) Private Sub FindClosestNote(frequency As Double, _ ByRef closestFrequency As Double, ByRef noteName As String) Const AFrequency As Double = 440.0 Const ToneIndexOffsetToPositives As Integer = 120 Dim toneIndex As Integer = CInt(Math.Truncate(Math.Round(Math.Log(frequency / AFrequency, ToneStep)))) noteName = NoteNames((ToneIndexOffsetToPositives + toneIndex) Mod NoteNames.Length) closestFrequency = Math.Pow(ToneStep, toneIndex) * AFrequency End Sub
 Fundamental frequency algoritm Ivan Kochurkin1-Jul-11 8:46 Ivan Kochurkin 1-Jul-11 8:46
 I'm having problems using the VB .Net version, running on Windows 7 64-bit TheJediMaster30-Jun-11 14:24 TheJediMaster 30-Jun-11 14:24
 Re: I'm having problems using the VB .Net version, running on Windows 7 64-bit notmasteryet30-Jun-11 14:26 notmasteryet 30-Jun-11 14:26
 Re: I'm having problems using the VB .Net version, running on Windows 7 64-bit TheJediMaster5-Jul-11 11:23 TheJediMaster 5-Jul-11 11:23
 Re: I'm having problems using the VB .Net version, running on Windows 7 64-bit Member 826810726-Sep-11 10:41 Member 8268107 26-Sep-11 10:41
 Re: I'm having problems using the VB .Net version, running on Windows 7 64-bit Member 766644016-Dec-11 6:26 Member 7666440 16-Dec-11 6:26
 Re: I'm having problems using the VB .Net version, running on Windows 7 64-bit bmac20-Jul-13 6:05 bmac 20-Jul-13 6:05
 Is it easy to convert to VB. Net? TheJediMaster30-Jun-11 5:23 TheJediMaster 30-Jun-11 5:23
 Re: Is it easy to convert to VB. Net? notmasteryet30-Jun-11 14:25 notmasteryet 30-Jun-11 14:25
 Re: Is it easy to convert to VB. Net? TheJediMaster30-Jun-11 14:26 TheJediMaster 30-Jun-11 14:26
 ReverseBits function optimization Ivan Kochurkin15-May-11 3:37 Ivan Kochurkin 15-May-11 3:37
 System sound Pareen Vatani10-May-11 20:53 Pareen Vatani 10-May-11 20:53
 My vote of 5 Viktor Signaievskyi8-May-11 0:11 Viktor Signaievskyi 8-May-11 0:11
 Nice solution thanks for providing it and a ? Alasdair Macleod22-Jan-11 11:24 Alasdair Macleod 22-Jan-11 11:24
 Re: Nice solution thanks for providing it and a ? Alasdair Macleod25-Jan-11 0:14 Alasdair Macleod 25-Jan-11 0:14
 A Bug?. Given a 44100HZ wav, the FFT result repeat and shrink many times in above 11025HZ. jasonhh13-Aug-10 23:48 jasonhh 13-Aug-10 23:48
 Re: A Bug?. Given a 44100HZ wav, the FFT result repeat and shrink many times in above 11025HZ. notmasteryet14-Aug-10 2:51 notmasteryet 14-Aug-10 2:51
 Last Visit: 21-Aug-19 23:32     Last Update: 21-Aug-19 23:32 Refresh « Prev123456 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.

Article
Posted 2 Jan 2009

425.9K views
27.7K downloads
227 bookmarked