Your assignment is to write a program that will determine, given a sample of sound from a piano key, what key (1-88) and note (A-G#) it is.
The x axis is the number of seconds from the time the key was
struck; as
you can see, we are considering only 1/20 of a second of time.
This piano key happens to be the A above middle C, tuned at 440 Hertz
(abbreviated Hz). That means the string vibrates 440 times per second.
The y axis plots the amplitude of the compression of air around a
microphone
connected to my computer; you can think of it as a voltage level.
As you can see from the plot, the voltage level, or signal, spikes
once every 1/440 of a second.
Sound is a continous signal, so how do we reprent it inside the computer?
One way is to take a sample of the amplitude of
the signal once every, say, 1/8000 of a second. We can store
these samples in a large array. Whenever we want to look at the signal
at time t, where t is in seconds, we just multiply
t by 8000,
find the closest integer, and look in the array at that point. In fact,
this is exactly the way sound is stored on an ordinary CD, with samples
taken every 1/44,000 of a second.
How do you determine the frequency of a sampled sound? This situation
is complicated by the fact that in most situations, including the piano
key strike, there are many frequencies superimposed on each other. For
example, the A above also vibrates at 880 Hz, 1,320 Hz, and other frequencies,
although these show up at a lower amplitude. These harmonic
frequencies are what give the piano,
other instruments, your voice, and pretty much every other sound its
characteristic texture. However, we are now only interested in determining
the fundamental frequency.
The data are given to us in the time domain, i.e., we know, for any
given time, what the amplitude will be. It would be nicer if the data
were in the frequency domain, where we would know, for any given
frequency, what the amplitude will be. Then we can just look for the
frequency with the highest amplitude, and that's the fundamental frequency
(usually; it works for the middle part of the piano, at least).
The Fourier Transform of a function takes a function y(t)
in the time domain and ``transforms'' it into a function Y(f) in the
frequency domain. It works for any periodic continuous function.
The Fourier Transform, for your amusement, looks like this:
If you don't know what this means, don't worry; you don't have to.
piano < 1.datYour program should read the number into an array of doubles.
The library file libfft.a on the main machines in /home/dj/cs2073 contains a precompiled version of the Discrete Real Fourier Transform as a C void function. You should declare it in your program after the #includes and before main like this:
void rfft (double [], int);You can apply this FFT to your double array of length 16384 using the following C function call:
rfft (array, 16384)where array is the name of your array. Your time-domain data will be replaced with the frequency domain data. Compile your program with:
cc -o piano piano.c -L/home/dj/cs2073 -lfft -lmThe output of your program should be give the maximum amplitude, the fundamental frequency, the key number, and the symbol for the note. So, your program should:
% ./piano < 1.dat maximum amplitude is 4097.06 at 196.29 Hz, key = 35 key letter is G % ./piano < 2.dat maximum amplitude is 6109.61 at 622.56 Hz, key = 55 key letter is D# % ./piano < 3.dat maximum amplitude is 2902.94 at 154.79 Hz, key = 31 key letter is D#Don't worry about the amplitudes matching exactly; the important thing is that the frequencies and key numbers match. To turn in this program, do exactly this from the prompt on the main machines, after having compiled your program:
% cat piano.c > output % ./piano < ~dj/cs2073/1.dat >> output % ./piano < ~dj/cs2073/2.dat >> output % ./piano < ~dj/cs2073/3.dat >> output % ./piano < ~dj/cs2073/4.dat >> output % ./piano < ~dj/cs2073/5.dat >> output % ./piano < ~dj/cs2073/6.dat >> output % ./piano < ~dj/cs2073/7.dat >> output % ./piano < ~dj/cs2073/8.dat >> output % ./piano < ~dj/cs2073/9.dat >> output % ./piano < ~dj/cs2073/10.dat >> output % ./piano < ~dj/cs2073/11.dat >> output % ./piano < ~dj/cs2073/12.dat >> output % ./piano < ~dj/cs2073/13.dat >> output % ./piano < ~dj/cs2073/14.dat >> output % ./piano < ~dj/cs2073/15.dat >> output % ./piano < ~dj/cs2073/16.dat >> output % ./piano < ~dj/cs2073/17.dat >> output % ./piano < ~dj/cs2073/18.dat >> output % ./piano < ~dj/cs2073/19.dat >> output % ./piano < ~dj/cs2073/20.dat >> outputThis will run your program on all the files and put the result into a file called output. Make sure to use the double "greater than" signs (i.e. >>) so that the output of the program will be appended to the output file. After you have done this, send the file called output to the teaching assistant.