Sequential Guitar Tuner
By Daniel Sonnenmark
Fall 2012
ECE 3551
Purpose
The purpose of this project is to create a program which can be used to tune a guitar. This is done by creating a sequential guitar tuner using the OMAPL137 Development Board. A user would upload the program to their own OMAPL137 Development Board and be able to tune their guitar with it.
Specifically, the program measures the sound of the user’s guitar. It then compares the frequency of the guitar sound to the ideal frequency stored in memory. It then indicates whether the guitar’s frequency is too high (sharp), too low (flat), or in tune. The guitar tuner continues to detect the guitar’s frequency until it is in tune. Once it is in tune, the program progresses to the next string. This process continues until all strings are in tune. The program then terminates. This process is illustrated in the diagram shown below.
For the student, this project presents a challenge. While most projects use filtering, this project is unique in that it uses functions of the CCS software not previously used within the lab. This includes using the Fast Fourier Transform as well as data processing not previously used. This project is challenging though achievable for a student.
Method
Although the process described earlier seems fairly simple, implementing it is not as elementary as one would think. There were many difficult procedures that had to be researched, designed, and implemented in CCS.
The most challenging portion of the design was detecting the pitch of the guitar. There were actually many steps needed to accomplish this along with many hours of research. Through vigorous research and testing an ideal solution was found.
The first portion that had to be researched was the pitch detection method. There are actually many ways to evaluate the frequency and pitch of a sound. All methods either occur in the time domain (amplitude vs. time) or frequency domain (amplitude vs. frequency).
When a signal is received by the development board, it is already in the time domain. Using the time domain eliminates any need to compute the complex Fourier Transform, making the code somewhat simpler. There are a few ways to evaluate the signal in this domain to obtain its pitch.
One method is the zero-crossing method. This method detects every instance of a zero in a signal; that is, where the amplitude transitions from positive to negative or vice versa. By counting every zero-crossing, the period of signal can be found, and hence the fundamental frequency can be determined. A flaw in this design is that it may not work well for a signal that has many harmonics, such as the sound emit from a guitar. This is because the zero-crossings of the harmonics may be confused for the zeros of the target fundamental frequency, resulting in skewed results.
Using this method combined with auto-correlation and other algorithms can in fact create a very accurate pitch detection method. Utilizing various methods is what is used in most commercial digital guitar tuners. Auto-correlation will be explained in detail later.
Another method is using the FFT method. The FFT computes the Discrete Fourier Transform(DFT) of a signal much quicker than using normal DFT calculations. The DFT allows signals to be displayed as functions of frequency, where the fundamental frequency (desired frequency) can be found. The most common FFT is the Cooley-Tukey method. The disadvantage of using just an FFT to derive the fundamental frequency is that the harmonics associated with the signal may interfere with the desired frequency.
Any of these methods in both the time and frequency domains can be utilized in an auto-correlation type algorithm to produce a more accurate result. Autocorrelation compares a function with itself, and is widely used to determine the fundamental frequency when noise or harmonics are present. It works by comparing the function at two different times while looking for a distinct pattern. Common types of auto-correlation methods include Cepstrum, Harmonic Product Spectrum (HPS), Maximum Likelihood, Average Mean Difference Function (AMDF), and various algorithms that build on autocorrelation.
Although using one of these auto-correlation methods would be ideal, the basic FFT and zero-crossing methods are much simpler to implement and are the best options to use for a novice CCS user.
Guitar signals are not just simple sine waves. In fact they often have arbitrary peaks and strange waveforms that make the signal difficult to analyze. The signal also contains many harmonics, which are waves of different amplitude, frequency and phase which can create confusion for any pitch detection software.
Using just the Zero-Crossing method will only work for a periodic signal with absolutely no harmonics. Any harmonics at all will interfere with the measurement of zero crossings and thus create inaccurate results.
Thus the FFT was chosen as the method that the program would use to determine the pitch of the guitar. The FFT converts the input signal from the time domain to the frequency domain. Since the system is digital, a DFT is created from this computation. The DFT is created in the form of an array of amplitudes. By indexing the element with the maximum amplitude, the fundamental frequency is found. This process will be discussed in detail in the Code Explanation section. An image of a typical DFT output can be seen below.
Figure 1- Typical FFT Transformation
After the guitar note’s fundamental frequency is established the next step is to determine whether this frequency is the correct frequency for that note. If you are not familiar with guitars, a standard guitar has six strings. Each string has a corresponding note when played in the open position, which means that the frets are not used. Each guitar string is labeled by this note. Each guitar string, its note, and that notes fundamental frequency is shown in a table below.
Guitar String / Note / Fundamental Freq.1st string / e / 329.6 Hz
2nd string / B / 246.9 Hz
3rd string / G / 196 Hz
4th string / D / 146.8 Hz
5th string / A / 110 Hz
6th string / E / 82.4Hz
Table A- Guitar Strings and Corresponding Frequencies
Since it is unlikely for the FFT to be able to be accurate enough to determine the frequency with four significant figures, it was deemed that the best way to determine the tune of the string would be to determine if its fundamental frequency falls within a specified range deemed acceptable. Through research it was deemed that a difference in 1Hz may be not make an audible difference. Thus each note has a range of length two Hz that it must fall within to be considered in tune.
An instrument’s note is flat if its frequency falls below that of the intended note and sharp if it is above the target frequency. The program is able to determine if the fundamental frequency is either flat, sharp, or in tune by using comparison methods.
Once the state of the note is determined the following happens. If the note is flat or sharp, the process repeats itself until the note is deemed in tune. If the string is in tune, the next string is evaluated. This continues until all strings are evaluated. The program then terminates itself.
The program uses LEDs to indicate the status of the guitar’s note. The OMAPL137’s usable LEDs are labeled as DS1, DS2, DS3, and DS4. For each state (flat, sharp, in tune) a different sequence of LEDs are used to indicate the state of the note.
The next section describes the code in detail and how it is implemented.
Code Explanation
The code itself is not made from scratch. In fact, there is a lab not done in class but available on the Angel website that contains the FFT algorithm. This lab is labeled Lab10, and its purpose is to allow the user to see a visual graph of the FFT of an input signal. Thus for development purposes it was easier to modify Lab 10 to suit the needs of this project. Most of the code is the same as in other labs, priming the inputs/outputs etc, and therefore will not be discussed.
The first portion that is described is the use of the FFT. Shown below are the definitions and declarations used in the FFT process.
Figure 2- FFT Definitions
The FFT is available from the “dsplib” site as a file that can be referenced through code. This file is labeled “DSPF_sp_fftSPxSP” and was referenced through the CCS linker. The two states of the program are “Passthrough”, which passes input to output, and “Complex_FFT,” which computes the DFT of the input signal. “N” is the number of samples that the FFT computes. This is used in many calculations to determine the fundamental frequency. “pCFFT_In” and the others are the different states of the FFT calculation that will be explained in further detail later.
Next shown is the bit-reversal array used in the decomposition portion of the FFT. This is used where the FFT reverses the binary data of the time-domain signal.
Figure 3- Bit-reversal Definition
The twiddle factors are an array of coefficients that are multiplied by the input data within the FFT computation. In the Cooley-Turkey algorithm, these coefficients are used to combine the DFTs that were initially broken up by the FFT algorithm. Instead of listing these as an array, the twiddle factors are created by a specific portion of the code. An image of this section is shown below.
Figure 4- Twiddle Factor Generation
The section shown below is the main processing section. The first portion, labeled “decide on radix” is what determines how the FFT will operate. This implies that the FFT is using a split-radix method, which divides the samples into either radix-2 or radix-4 portions to speed up the computation time. Each value encloses a number of samples. Given a number of input samples, previously defined by “N”, this section will coerce this value to one of these in order to compute the FFT. Sample numbers such as 512 are powers of only and thus require a radix of 2. Sample numbers such as 1024, which is used in this program, are powers of four and thus can use radix-4.
The next portion prepares the input for use in the FFT. It does this by splitting the input up into real and imaginary numbers. As shown there are two different input values, one containing only real values and the other containing the imaginary ones as well.
The computational portion simply enacts the FFT file that was described previously. This file computes the FFT so it does not have to be written in the program. The FFT is a very complex process that would be very difficult to emulate using hard code.
The last portion computes the DFT magnitudes from the FFT output. The values contained in the “pCFFT_Mag” array are amplitudes, in order from 0Hz to half of the sampling frequency (22,050 Hz). These values are used in the code created by the student to determine the fundamental frequency.
Figure 5- FFT Portion of Code
The next portion highlights the code created to determine the fundamental frequency. The first part that should be pointed out is the while loop. Each case represents a string. A while loop is used to determine whether the program should continue testing the same string (out of tune) or move on to the next one. More about this will be shown later. Also note that the portion shown below initially turns off all LEDs.
Figure 6- E-string Case
The next portion determines the fundamental frequency of the guitar signal. From the FFT we have an array of magnitudes. The element of that array with the maximum amplitude is the fundamental frequency. From finding the location of this element within the array, the fundamental frequency can be calculated.
A For loop is set up which uses the first 50 samples of the DFT array. Each element’s magnitude is compared to the “Max_Amp”, which is initially set to a very low value. When an element’s magnitude is greater than “Max_Amp”, it then becomes “Max_Amp.” This process is continued until an element cannot be surpassed, meaning that it has the maximum amplitude of all of the elements. The index of this element is multiplied by the maximum frequency divided by the number of samples. For this program, this means that every element differs by 21.5 Hz. For example, if the index of the array was 5, then the fundamental frequency would be 107.5 Hz.
Figure 7- Finding Fundamental Frequency
The last portion of code determines whether the note is in tune or not. Boundaries of the acceptable range of frequencies are set for each note. These are labeled Highthresh and Lowthresh. A series of if-else statements determine which category the note falls under. If it is flat or sharp, that state is indicated through the use of LEDs, and “check” is set to 1. As long as check is equal to 1 the loop will keep repeating, thus forcing the process to continuously repeat. If the note is in tune then check is set to 0, thus ending the loop and allowing the code to progress to the next case.
Figure 8- Determining State of Guitar Note
Troubleshooting
Unfortunately, the program would not execute in the required manner. Although the program would compile, the LEDs would not indicate anything. Using a breakpoint on the FFT output, no output was found either. This problem was never resolved; however steps were taken to fix it. When these steps failed to fix the problem, an alternate program was created using a Win32 application and in Labview. These steps are documented below.
The first step was to determine if there was an input waveform. This was confirmed by using a breakpoint and graphing pOut. The result was a perfect sine wave, as generated from a YouTube video.
Next was to determine if the FFT was working properly. A graph was generated of the FFT magnitude by placing a breakpoint after the FFT operations. The resulting graph looks nothing like an FFT should, leading to a lack of appropriate data to be processed. The resulting graph looks like this:
Figure 9- FFT Magnitude Output
Although graph settings were tinkered with, the code would simply not work. It seemed that the data processing portion was not receiving the data either, leading to not outputs.
The LED functionality was attempted to be tested, though this also failed. The LED test program was imported and attempted to run. Unfortunately, an error kept popping up indicating that the “evmomapl137bsl.lib” file could not be found. Using the linker, this file was directed to the file. This did not change anything, as the error continued to show. This seems like an internal error of the CCS program. This contradiction is shown in the image below.
Figure 10- Missing File Error Contradiction
From the lack of ability to test the functionality of different portions of the code, it was hard to troubleshoot the program. Through hours of failed attempts, it was understood that time was running out and there had to be something to show for the presentation. Thus as a back-up two programs were created which showcase the theoretical abilities of the code already discussed. These two programs will continue to be the topic of discussion as real results were obtained from these.
Alternate Solutions
The first objective was to demonstrate that the code that was written was indeed correctly written and would work as intended. The code was copied into Microsoft Visual Studio 2010 to be made into a Win32 application. The only portion of the code that was copied was the portion that was written by the user. Thus the FFT and I/O configurations were left out.
The Win32 application assumes that the FFT Magnitude array was already created and thus uses an array of values to represent that. From then everything else is exactly the same. Instead of using LEDs to indicate the state of the guitar’s tuning, strings are outputted on the Win32 window. The complete code for this is shown below.
/*
**SEQUENTIAL GUITAR TUNER**
*/
#include"stdafx.h"
#includeiostream
#includeiomanip
#includeWindows.h
usingnamespace std;
int _tmain()
{
int check = 1;
while (check == 1)
{
intpCFFT_Mag[] = {0,2.7,3.5,4.8,6.8, 4.2, 3, 1, 2};
constint N = 512;
int Max_Amp = -1;
intFund_Index;
intFund_Freq;
inti;
for(i=1; i < 8; i++)
{
if (pCFFT_Mag[i] > Max_Amp)
{
Max_Amp = pCFFT_Mag[i];
Fund_Index = i;
Fund_Freq = (22050/N)*i;
}
}
intHighthresh_E;
intLowthresh_E;
Highthresh_E = 550;
Lowthresh_E = 650;
if (Fund_Freq >= Highthresh_E)
{
check = 1;
cout"LED 1: ON ""NOT IN TUNE";
coutendl;
coutendl;
}
elseif (Fund_Freq <= Lowthresh_E)
{
cout"LED 3: ON ""NOT IN TUNE";
coutendl;
coutendl;
check = 1;
}
else
{
cout"LEDs 1,2,3: ON ""IN TUNE";
coutendl;
coutendl;
check = 0;
}
cout"Fundamental Frequency = "Fund_Freq;
coutendl;
coutendl;
Sleep(1000);
system("pause");
}
check = 1;
/*If check is = 0, the program exits the while loop. Another while loop is placed right after this, the only difference being