1. Introduction
In this assignment you will write a Matlab program to generate (or synthesize) and analyze
DTMF sounds, otherwise known as Touch Tone® sounds. You will use Matlab vectors to
hold the sound data, plot some of it, and then use the Fourier Transform to take a sound
signal and extract its frequency components.
There is a lot of information in this project document. I have written a lot of explanation,
and I also give you quite a few example graph images which take up extra space. The
project does not require a lot of programming, but it does require a lot of understanding.
You will undoubtedly see some of this information again on the midterm exam. I give you
a lot of examples and ways to test your program as you develop it. Please do not skip these
parts of the assignment.
2. Value
This program is worth a maximum of 20 points. See the grading rubric at the end for a
breakdown of the values of different parts of this project.
3. Due Date
This project is due by 11:59PM on Sunday, September 30, 2012.
4. Objectives
The purpose of this assignment is to give you familiarity with:
• defining functions
• calling functions
• using vectors to store data
• processing sound
• finding peaks in a graph
• plotting / graphing
Keywords: function, vector, peak, plot, graph, DTMF, Touch Tone, sound, encode, decode,2
Fourier Transform
5. Background
In this assignment you will be generating and analyzing DTMF sound signals, otherwise
known as Touch Tone® sounds. Have a look at this web link to be sure you understand the
background:
http://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling
You don't need to memorize anything or use any of that information in your program, but
it's an interesting read nevertheless.
Interestingly, in that article under the Keypad heading there is a table of the keys, where
each key is a link to a sound file. Clicking the link will play the sound. You may find this
useful later.
5.1. if statements
You will need to use a few if statements in this project, but I haven't explained them to you
yet. Thankfully they're not that difficult to learn. Have a look at section 5.2.2 in the book
on pages 139 − 141.
5.2. Sound Waves
In class you have seen me use the sin function to generate a sine wave. A sine wave is not
just a mathematical abstraction, it is the wave that represents any pure tone. Matlab is
quite good at generating and playing tones.
The tone of middle-C is 261.626 Hz (Hz = cycles per second), so if we were able to
generate a sine wave that oscillates at that rate and have Matlab play it, you would hear
middle-C. Let's do that.
First you would need to tell Matlab how many samples per second you desire (this is how
tones are generated by digital computers). Let's choose 8192 samples per second, because
it's both a power of 2 (that will be important later) and it's a reasonable number for what
we need. We also need to determine how long we want the tone to play, so let's say 2
seconds. So the time domain is the range 0 to 2 seconds chopped into 8192 segments per
second. This is the expression in Matlab for the time domain:3
seconds = 2;
samplesPerSecond = 8192;
timeDomain = linspace(0, seconds, seconds * samplesPerSecond);
Let's have a look at the first 10 elements of the timeDomain vector:
timeDomain(1:10)
ans =
Columns 1 through 6
0 0.0001 0.0002 0.0004 0.0005 0.0006
Columns 7 through 10
0.0007 0.0009 0.0010 0.0011
This means that the sound we generate will be sampled at 0 seconds, then again at 0.0001
seconds, then at 0.0002 seconds, and so on.
We won't just take the sin of the timeDomain vector to generate a wave. Its units are
seconds, but the sin function expects numbers in an angle as some (possibly fractional)
multiple of 2π. We already know that middle-C oscillates 261.626 times per second,
meaning it goes through 261.626 x 2π radians every second. Thus, if we multiply the
timeDomain vector by this amount, we will get a frequency vector to which we can apply
the sin function.
freq = 261.626;
tone = sin(2 * pi * freq * timeDomain);
This signal is much too dense to plot, but it is perfect for playing. The sound function can
do this if you give it the tone vector and the number of samples per second of the tone
vector. However, the amplitude of the sound is quite large. Let's turn the volume down by
half first:
tone = tone * 0.5;
Now play the tone:
sound(tone, 8192)
If you're using a computer in the lab to do this, you will need to plug in earphones,
otherwise Matlab complains that there is no sound hardware installed.4
5.3. Combining Sound Waves
Let's look at a simple sound wave of 1 Hz (or 1 cycle per second) over 1 second, with 100
samples per second.
timeDomain = linspace(0, 1, 100);
wave1 = sin(2 * pi * 1 * timeDomain);
plot(wave1)
The plot shows one complete cycle of the wave. The wave is quite smooth at 100 samples
per second (or simply 100 samples, since it's exactly 1 second). Just for fun, let's try it at 8
samples instead.
plot(sin(2*pi*linspace(0,1,8)))
You can see it's much more jagged.5
Say we wanted to combine two tones, a 1Hz wave and a 3Hz wave. The way to generate
the vector of these two waves would be to add the two separate wave vectors together.
Let's have a look at the 3Hz wave.
wave2 = sin(2 * pi * 3 * timeDomain);
This is what wave2 looks like by itself:
You can see that this plot contains three complete cycles of the wave.
You would think that combining two waves causes them to somehow look like this:
But in fact this is not how waves combine. The amplitudes (heights) simply add together,
yielding a single wave that is the sum of the separate waves. This is what 1Hz + 3Hz looks
like:6
Notice how the amplitude now exceeds 1: it goes as high as 1.5 and as low as −1.5, so
this wave is louder than either of the two separate waves. Also observe in the previous plot
at the 25th sample the blue 1Hz wave is at its maximum of 1 and the red 3Hz wave is at its
minimum of −1. At that point the sum of the red and blue waves is 0. That's what the value
of this combined wave is at the 25th sample.
Consider going the opposite direction: Given a complex wave like this one, is it possible
to determine what fundamental waves make up this wave? In other words, can we perform
some analysis that tells us that in fact this wave is made of a 1Hz wave and a 3Hz wave? It
turns out that a mathematician named Joseph Fourier discovered that it is possible, and
developed an algorithm to do it. This is known as the Fourier Transform, and you'll use it in
this project.
6. Assignment
Read through all of the subsections in this section before you start, in order to understand
how you must proceed with the assignment. This assignment has two parts: in the first part,
you will write functions that synthesize DTMF sounds. In the second part, you will write
functions that analyze DTMF sounds to determine what their frequency components are.
6.1. Sound Synthesis
In this first part you will write functions to synthesize DTMF sounds, first by writing a
function that creates single pure tones, then combining the tones into a dual-tone sound.
6.1.1. Function generateTone
This function will generate a single sinusoidal pure tone of a specified frequency.7
Write a function called generateTone that has three parameters:
1. a frequency
2. the number of samples per second
3. the number of seconds of the tone duration
Name the parameters well. I would suggest freq or frequency, sampsPerSec or
samplesPerSec or samplesPerSecond, and seconds or duration or even
toneDurationSeconds.
This function must return one value: it will be the generated tone. Name this return
variable well also.
This is the outline of the function:
function ____ = generateTone(___ , ___ , ___)
___
end
You have to fill in the blanks.
In this function use the linspace function to create a time domain that goes from 0 to the
duration, having the required number of samples per second. I showed you how to do that
in the Background section.
Then apply the sin function to the time samples. Be sure to multiply the time domain by
the frequency and 2π.
Also multiply the resulting tone vector by 0.5 so that the amplitude (volume level) is
reduced a bit. Otherwise the sound will be quite loud.
After you have written the function, test it. In the background section I showed you how to
generate the tone for middle-C. Make sure this function generates the same tone:
sound(generateTone(261.626, 8192, 1))
6.1.2. Function synthDtmf
This function will generate a dual tone by using the generateTone function you just wrote.8
Write a function called synthDtmf that has two parameters that are two frequency
numbers. This function generates two tones by calling the generateTone function twice,
once for each frequency number. Finally, the two tone vectors are added together and
returned from this function. Note that as long as the vectors have the same size (and they
will) they can be added together directly, something like this:
sumVector = vector1 + vector2;
When you call the generateTone function you must specify the frequency (you know that:
it's in the first parameter variable for the first tone, the second parameter variable for the
second tone), and use 8192 as the number of samples per second, and a duration of 0.3
seconds.
The function will look like this:
function ___ = synthDtmf(___ , ___)
tone1 = ___;
tone2 = ___;
___ = ___ + ___;
end
Here's an example. The number 0 on the telephone keypad generates a sound that's the
sum of two frequencies: 941Hz and 1336Hz. This function call generates and plays a
DTMF sound for the number 0 on the telephone keypad:
sound(synthDtmf(941, 1336))
Compare it to the reference sound found here:
http://upload.wikimedia.org/wikipedia/commons/2/2d/Dtmf0.ogg
6.1.3. Function nameToFreq
This function will take the name of any one of the 16 keys on the extended telephone
keypad (0-9, A-D, *, #) and return the two frequencies that make up the DTMF sound for
that key.
Create a function called nameToFreq. This function has one parameter: the name of the
key on the keypad.9
This function returns two values. To do this, specify a vector of two variables as the
function's return variables.
function [___ , ___] = nameToFreq(___)
You must be sure to assign values to both return variables before the function ends.
In this function check if the name is equal to the character string '0'. If it is, assign the two
frequencies 941 and 1336 to the two return variables in any order. It starts like this:
if ___ == '0'
___ = 941;
___ = 1336;
Otherwise, if the name is equal to '1', the function will return 697 and 1209. It continues
like this:
elseif ___ == '1'
___ = 697;
___ = 1209;
elseif . . .
. . .
end
Here is the complete table of frequencies for the keys:
Key name Frequency 1 Frequency 2
'0' 941 1336
'1' 697 1209
'2' 697 1336
'3' 697 1477
'4' 770 1209
'5' 770 1336
'6' 770 1477
'7' 852 1209
'8' 852 1336
'9' 852 1477
'A' 697 1633
'B' 770 1633
'C' 852 163310
'D' 941 1633
'*' 941 1477
'#' 941 1477
Add the remaining key names to your function so that the function handles all 16 keys.
Test the function.
[f1 f2] = nameToFreq('0');
f1 =
941
f2 =
1336
sound(synthDtmf(f1, f2))
The sound should be the same as before. Test some of the other sounds to be sure they
work.
6.1.4. Function touchTone
This function will take the name of any one of the 16 keys on the extended telephone
keypad (0-9, A-D, *, #) and return the sound vector of the two tones that make up the
DTMF sound.
Create a new function called touchTone. Inside this function you will need to call the
nameToFreq function (remember there's a difference between defining a function and
calling a function, and inside a function that you're writing you can write a function call to
any other function), save the return values in two variables, and then call the synthDtmf
function using those two variables as arguments. The return value of the synthDtmf
function must be returned from this function.
inside touchTone:
call nameToFreq, save in two variables
call synthDtmf with those two variables
make sure the return value from synthDtmf is returned from touchTone
This is a plot of the first 100 samples of the sound generated for '0':
t0 = touchTone('0');
plot(t0(1:100))11
You are now finished with the sound synthesis portion of this assignment.
6.2. Sound analysis
Given a vector containing sound data, we can analyze it to determine what its frequency
components are by using the Fourier Transform. In general, the Fourier Transform takes a
signal in the time domain and converts it to a signal in the frequency domain. Matlab has
a Fast Fourier Transform, or FFT, function built-in already, so all we need to do is use it. I
will not discuss it here. You'll learn about it when you take an electrical engineering
course.
The hard part is that the vector returned from Matlab's FFT function requires some
manipulation in order to be made useful to us. I'll give you a lot of the Matlab code
needed to do that.
Although the sampling frequency we are using is 8192 samples per second, which in
theory lets us detect tones that go up to 8192 Hz, there are some issues with how
sampling works and how the Fourier Transform treats the data, making frequencies above
4096 (half of 8192) unimportant to us. Thus, we shall consider only those frequencies up
to half of the sampling frequency.
You can read about the Nyquist-Shannon sampling theorem here, but only if you feel like
it: http://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem
6.2.1. Function timeToFreq
Create a new function called timeToFreq. This function will convert a sound signal vector12
(which is by definition in the time domain) into a vector of power levels in the frequency
domain. You don't have to understand what this all means, you just have to understand
how to write a function.
This function will have two parameters, the sound signal and the number of samples per
second, and it must return two values, the frequency vector and the power vector.
I have written the body of the function for you already, shown below. All you need to do is
write the function header line and put the word end at the end. The variables I use here
give you an indication as to what you need to call the parameters and return variables.
count = length(signal);
y = fft(signal, count);
power = y.*conj(y)/count;
power = power(1:floor(count/2));
freq = samplesPerSec/count*(0:(floor(count/2)-1));
Test the function to be sure it works correctly.
sig = touchTone('0');
[f p] = timeToFreq(sig, 8192);
plot(f, p)
This plot shows two peaks at right about 941 and 1336, which are the two frequencies that
make up the tones for the 0 key.
6.2.2. Function peaks
This function takes two vectors: the frequency vector and the power vector, and returns a
vector of numbers that indicate what the frequencies are at the peaks of the power graph.13
It seems like it could be simple: just figure out all the locations where the power graph is
non-zero. However, this problem is made somewhat difficult by the nature of the power
data, which is hard to see on the previous plot. Let's zoom in on the first peak:
plot(f(275:290), p(275:290), '-o')
From this plot you can see that there are several non-zero data points surrounding the
941Hz location, and it even seems that the peak might be at 940 and not 941. This
complicates things a bit.
However, notice that the peak occurs at that location where the graph changes from rising
(the left slope going up to the peak) to falling (the right slope going down from the peak).
This is the definition of a peak in a graph. We need to ask Matlab: At what locations in the
vector is the point at that location greater than the point to its left and the points to its
right? It turns out that it's not hard to ask that question
If P contains all the points, then we need to construct two other vectors: one that contains
all the values to the left of P (element-by-element), and one that contains all the values to
the right of P. Here's an example:
P = [1 2 3 3 4 3 2 1]
Let's create the vector of left elements:
Left = [0 P(1:end-1)]
which is the same as:
Left = [0 1 2 3 3 4 3 2]
And the right elements:14
Right = [P(2:end) 0]
Which is the same as:
Right = [2 3 3 4 3 2 1 0]
Consider location 3. Vect(3) contains the value 3. To its left is location 2, which contains
2, and to its right is location 4, which contains 3. These same numbers are also found in
Left(3) and Right(3). But now look at location 5: Vect(5) is greater than Left(5) and Right
(5).
P Left & P Right
ans =
0 0 0 0 1 0 0 0
(The ampersand means and also.) This is the logical mask showing where the elements in P
are greater than the elements in Left, and the elements in P are greater than the elements
in Right. This expression is true in only one location. If there were multiple peaks it would
be true in multiple locations. Let's ask for the location of the peak:
find(P Left & P Right)
ans =
5
Let's get the value at that location:
P(P Left & P Right)
ans =
4
The value 4 is the peak value. This works correctly. Notice that I did not use the find
function here, instead I used a vector of logical values. This is known as logical indexing.
The vector of logical values is used as the vector index, and Matlab returns the vector
elements where the logical values are true.
Write a function called peaks that takes a vector of frequencies and a vector of power
values. This function must return the frequencies at which the peaks occur. This must be
done in several steps:
1. Determine the locations in the power vector where the peaks occur.
2. Return the values from the frequencies vector at those locations.
3. Call the round function on the vector as the last thing you do. This keeps the output
looking nicer. You can do it like this if you want to:
returnVariable = round(returnVariable);15
Now test the peaks function:
sig = touchTone('0');
[f p] = timeToFreq(sig, 8192);
peaks(f, p)
ans =
940 1337
Hm, these numbers are close to the original frequencies of 941 and 1336, but not exactly
equal. We can deal with this discrepancy in the next few functions.
6.2.3. Function closeTo
I'm tired of writing this assignment already. ;) Create a new function called closeTo, and
copy and paste the function below.
This determines if a number a is within epsilon units of another number b, where epsilon
is defined to be 5.
function result = closeTo(a, b)
epsilon = 5;
result = abs(a - b) <= epsilon;
end
This allows then inexact comparison of two numbers. Test it:
closeTo(940, 941)
ans =
1
closeTo(940, 946)
ans =
0
6.2.4. Function freqToName
I'm still tired of writing this assignment, and this is stuff you don't quite know yet. Copy
and paste this as a new function into your project, but see if you can understand how it
works.
This function converts a pair of frequencies into the name of a key on the telephone
keypad. It is the opposite of the nameToFreq function.
function name = freqToName(f1, f2)
if closeTo(f1, 697)
if closeTo(f2, 1209)16
name = '1';
elseif closeTo(f2, 1336)
name = '2';
elseif closeTo(f2, 1477)
name = '3';
elseif closeTo(f2, 1633)
name = 'A';
end
elseif closeTo(f1, 770)
if closeTo(f2, 1209)
name = '4';
elseif closeTo(f2, 1336)
name = '5';
elseif closeTo(f2, 1477)
name = '6';
elseif closeTo(f2, 1633)
name = 'B';
end
elseif closeTo(f1, 852)
if closeTo(f2, 1209)
name = '7';
elseif closeTo(f2, 1336)
name = '8';
elseif closeTo(f2, 1477)
name = '9';
elseif closeTo(f2, 1633)
name = 'C';
end
elseif closeTo(f1, 941)
if closeTo(f2, 1209)
name = '*';
elseif closeTo(f2, 1336)
name = '0';
elseif closeTo(f2, 1477)
name = '#';
elseif closeTo(f2, 1633)
name = 'D';
end
end
end
Test it:
freqToName(940,1337)
ans =
0
This looks like the value for the number 0 or even the logical value false, but in fact this is
the character string '0'. Notice how the answer is up against the left margin. That indicates
that the answer is a character string and not a number. Try different frequencies:
freqToName(943, 1630)17
ans =
D
6.3. Tie Them Together
The last function you will write will simply connect the sound synthesis portion of your
project to the sound analysis portion of your project. Thankfully, it's not too difficult.
Create a new function called encodeAndDecode. It has one parameter: this will be the
name of the tone to encode. This function also returns one value: the name of the decoded
tone. Inside the function do this:
1. Generate a touch tone signal having the given name by calling the touchTone
function.
2. Convert the signal to its frequency and power vectors by calling the timeToFreq
function.
3. Generate the vector of peaks by calling the peaks function. The function call will
return a vector having two elements in it.
4. Convert the two vector elements back into a name by calling the freqToName
function.
Test it:
encodeAndDecode('0')
ans =
0
encodeAndDecode('1')
ans =
1
encodeAndDecode('*')
ans =
*
encodeAndDecode('A')
ans =
A
7. Report
Create a Microsoft Word or Libre Word file (or some other format that your TA agrees on --
ask him or her if you are not sure). Save the file with the name Project3 with a .doc
or .docx format.18
At the beginning of the document include this information:
Tone Encoding and Decoding
CSE1010 Project 3, Fall 2012
Your name goes here T
he current date goes here
TA: Your TA's name goes here
Section: Your section number goes here
Instructor: Jeffrey A. Meunier
Be sure to replace the parts that are underlined above.
Now create the following three sections in your document.
1. Introduction
In this section copy & paste the text from the first paragraph of the introduction
section of this assignment. (It's not plagiarism if you have permission to copy something. I
give you permission.)
2. Functions
Create one subsection for each function that you wrote (even the ones I gave you). In
each section, summarize in one sentence what the function does, and show how to use
the function as best you can, and a subset of the return value or values. If the result is a
vector, insert a plot of either the whole vector or part of the vector (if a part of the vector is
more interesting than the whole vector). Here's an example:
The generateTone function generates a sinusoidal tone given some frequency,
number of samples per second, and the number of seconds the tone should last.
t = generateTone(300, 8192, 1);
t(1:10)
ans =
Columns 1 through 8
0 0.1140 0.2221 0.3184 0.3980 0.4565 0.4910 0.4996
Columns 9 through 10
0.4819 0.4387
plot(t(1:200))19
3. Source code
Copy & paste the contents of all your functions here. Insert a blank line between
functions. You do not need to write anything.
8. Submission
Submit the following two things things on HuskyCT:
1. All the .m files for this project stored in a Zip file. Do not upload each separate .m
file on HuskyCT. I have made tutorial videos available on HuskyCT describing how
to make a Zip file. These are the files you must include in the Zip file:
⁃ closeTo.m
⁃ encodeAndDecode.m
⁃ freqToName.m
⁃ generateTone.m
⁃ nameToFreq.m
⁃ peaks.m
⁃ synthDtmf.m
⁃ timeToFreq.m
⁃ touchTone.m
2. The MS Word document.
If for some reason you are not able to submit your files on HuskyCT, email your TA before
the deadline. Attach your files to the email.
9. Notes
Here are some notes about working on this project:
• Type all the examples that I provide. They are intended to teach you a lot of
material you will need to know.20
10. Grading Rubric
• (2 points) The comment block at the beginning of the program file is correct.
• (6 points) The program displays the correct answers.
• (4 points) The program uses the correct calculations to generate the answers.
• (4 points) The program is formatted neatly. Follow any example in the book, or see
the web site here: https://sites.google.com/site/jeffscourses/cse1010/matlabprogram-formatting
• (4 points) The document contains all the correct information and is formatted
neatly.