This project was designed to make a guitar tuner using Vivado and a 7-segment display. Once the tuner finds the frequency of the inputted sound, the tuner will compare that value to a list of hard-coded values for exact frequencies that are known as the standard frequency for the correct pitch of a note. Then the tuner will display how close or far your inputted sound is from your desired note. What is interesting is that a sound wave is a combination of multiple sinusoidal waveforms with real and imaginary components. While this may seem difficult to work with to those unfamiliar, there are a few ways that we can still analyze a wave with real and imaginary values.
Step 1: Equipment List (take a Pic of the Board and Kevin's Comp)
First we need a Basys 3 board and a computer that supports the following programs.
Garageband/Audacity or another DAW - to record through a microphone and export wavfiles
Python - able to use pylab and scipy for sampling and fft
Vivado - to connect to the Basys 3 board and visually see the results
Step 2: Overview
A tuner is made up of few important components: microphone, sampler, FFT (Fast Fourier Transform), comparator, decoder, and display. The purpose of the microphone is to capture the input waveform. The sampler receives the microphone's output signal and uses the FFT to convert the signal into an output of magnitude in frequencies. Then using the output of the FFT and finding the max magnitude and the frequency associated with it divided by 2, the frequency associated with the pitch of the waveform can be found. That value can then go into the comparator. It is then compared to a look-up table, which has already set frequency values for perfect pitches of all notes. The comparator is given an input for the desired note, which it can then match the desired note to it's correct frequency from the look-up table. Then the comparator will choose the note with the closest frequency to the maximum frequency. The comparator will compare the two values and see close the value of the frequency is to the desired one and then put that data into a signal. The comparator will send that signal to the decoder, where the decoder will choose the inputs for the anodes of the 7-segment display to show the accuracy of the note.
Step 3: Wav File
In this step, we will be taking a wav file of a pitch and try to output the frequency of that pitch.
First you need a wav file of a note. In this example we will be using a 16 bit stereo wav file with a sampling rate of 44.1kHz. This can be either created in a DAW such as Garageband or downloaded. For this example, a A4 440Hz Sine wave generated by us on Garageband can be downloaded here. https://drive.google.com/file/d/16lprsKE2wFeKruB0u...
Step 4: Python- Uses of Pylab and Scipy
We used Python library for doing “Fast Fourier transform”. Online resource allowed us to imitate and see what is useful in pylab and scipy.
1. If you haven’t install pylab or scipy, you need to do so. Or, Pycharm has a very good feature, when tried importing pylab or scipy, there is a squiggly underline telling you that you haven’t install the library yet. You can then install them directly by pressing the red light-bulb (it will appear when you put your cursor near the squiggly underline).
2. Using scipy.io.wavfile.read function, read and pull out data from sample wav file. Run through the data by pylab.fft, it will return you a list of magnitude for the power.
3. Then find the max of the power emitted from the list. Look for the list index where the max power occurs because the faster way to find what frequency associate with that power. Finally return the max frequency. Since we later need to input a binary frequency signal into VHDL code, we can convert frequency in float into binary, and return it.
Step 5: Python-Sampling and FFT (Show Code and Its Results)
In this step, full credits go to this link below for the sampling and FFT.
After pylab and scipy have been installed, wav files are able to be imported and read.
from pylab import*
from scipy.io import wavfile
sampFreq, snd = wavfile.read('440_sine.wav')
Then snd.shape represents the sample points and the number of channels. In our case, the sample points depends on how long the wavfile is and the # of channels is 2 because it is stereo.
Then snd = snd / (2.**15)...... xlabel('Time (ms)')
organizes the time signal into an array.
Then the FFT creates an array in frequency and magnitude (Power)
Then through a while loop the max magnitude and the frequency associated with it is found.
That frequency/2 represents the pitch of the wavfile.
Then using our own code, the integer representing the frequency was converted into a 12 bit binary number and a text file was created with that number in it.
Step 6: Vivado (Comparator)
In this part of the
process, we need a comparator to compare two input frequencies.
1. Created a comparator to compare whether the input (receiver) frequency is higher, lower or within the 2 Hz margin range defined note. (typical guitar tuner ranges from e2 to g5, 82 Hz to 784 Hz).
2. When creating a margin of 2 Hz, we used a RCA to add “000000000010” to the receiver frequency, and check where it is still too low for user input. If that is the case, single bit signal “high” <= ‘0’, “low” <= ‘1’. Then we add “000000000010” to the user input see if the receiver input is even higher than that. If that is the case, “high” <= ‘1’, “low” <= ‘0’. Neither the case would both return ‘0’.
3. Since the next part of the module need a specific 4-bits data to tell what the receiver note is, not only returning the 2 comparative outputs (low and high), we need to return the code associate to note, which associate with the frequency. Please refer to the chart below:
C | 0011
C# | 1011
D | 0100
D# | 1100
E | 0101
F | 0110
F# | 1110
G | 0111
G# | 1111
A | 0001
A# | 1001
B | 0010
Using several if statements to categorize them into note and encode them in to what is needed for the seven segment decoder.
Step 7: PICTURES OF BASYS 3 Board
Step 8: Vivado (7 Segment Decoder With Multiplexing)
Everything needs a display. It is an important factor that determines the value of a design. Therefore, we need to create a display using seven-segments decoder, which would allow us to demonstrate our ability to design a tuner on the B Board. Also, it would help us in testing and debugging.
A seven-segments decoder contains inputs named Note, low, high, and CLK, while outputting SSEG, AN, and Fiz_Hz. There is a picture of block diagram above to help us understand the design.
The purpose of having a two separate of low and high inputs is to provide the designer of the comparator the freedom to manipulate whether the sound (wave) frequency is higher or lower than the input frequency (Fix_Hz) the user wants to compare. In addition, the output SSEG represents the seven segments display and the dot next by while the AN represents the anodes for which set of the seven segments display to light up.
In this seven-segments decoder, the clock (CLK) plays an important role in displaying two different values on two or more different anodes. As the Board doesn't allow us to display two different values at the same time, we have to use multiplexing to display a value one at a time, while switching into another value fast enough that our eyes can't capture it. This is where the CLK input comes into play.
For more information, please refer to the source code.
Step 9: Vivado (Combining Components)
With every modules (python receiver, comparator, seven segment decoder, etc.) completed, we then put together using the a bigger module. Just like the picture under "Over view" section shown, we connect each signal accordingly. For reference, please check our source code "SW_Hz.vhd".
Thank you. Hope you enjoy.