<HTML>
<HEAD>
<TITLE>Computer Science Logo Style vol 2 ch 13: Example: Fourier Series Plotter</TITLE>
</HEAD>
<BODY>
<CITE>Computer Science Logo Style</CITE> volume 2:
<CITE>Advanced Techniques</CITE> 2/e Copyright (C) 1997 MIT
<H1>Example: Fourier Series Plotter</H1>
<TABLE width="100%"><TR><TD>
<IMG SRC="../csls2.jpg" ALT="cover photo">
<TD><TABLE>
<TR><TD align="right"><CITE><A HREF="http://www.cs.berkeley.edu/~bh/">Brian
Harvey</A><BR>University of California, Berkeley</CITE>
<TR><TD align="right"><BR>
<TR><TD align="right"><A HREF="../pdf/v2ch13.pdf">Download PDF version</A>
<TR><TD align="right"><A HREF="../v2-toc2.html">Back to Table of Contents</A>
<TR><TD align="right"><A HREF="https://people.eecs.berkeley.edu/~bh/v2ch12/v2ch12.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch14/manual.html"><STRONG>NEXT</STRONG></A>
<TR><TD align="right"><A HREF="https://mitpress.mit.edu/books/computer-science-logo-style-second-edition-volume-2">MIT
Press web page for <CITE>Computer Science Logo Style</CITE></A>
</TABLE></TABLE>
<HR><P>Program file for this chapter: <A HREF="https://people.eecs.berkeley.edu/~bh/v2ch13/plot.lg"><CODE>plot</CODE></A>
<P>A particular musical note (middle C, say) played on a piano and played
on a violin sound similar in some ways and different in other ways.
Two different notes played on the violin also have similarities and
differences. How do you hear which note is being played, and how
do you know what instrument you're listening to?
<P>To do justice to these questions would fill up an entire book. For example,
a piano produces sound when a felt-covered wooden hammer hits metal wires,
or strings. Each piano key controls one hammer, but each hammer may hit
from one to three strings. It turns out that the strings for a particular
note are not tuned to exactly the same pitch. Part of the richness of the
piano's sound comes from the interplay of slightly different pitches making
up the same note.
<P>Another contributing factor to the recognition of different instruments is
their differences in attack and decay. Does the sound of a note start
abruptly, or gradually? The differences are not only a matter of loudness,
though. A few instruments start out each note with a very pure, simple tone
like a tuning fork. Gradually, the tone becomes more complex until it
finally reaches the timbre you associate with the instrument. But a bowed
violin, a more typical example, starts out each note almost as a burst of
pure noise, as the bow hits the strings, and gradually mellows into the
sound of a particular note. If you are experimentally inclined, try tape
recording the same note as played by several instruments. Then cut out the
beginnings and ends of the notes, and retain only the middle section. Play
these to people and see how well they can identify the instruments, compared
to their ability to identify the complete recorded notes.
<P>
For this chapter, though, I'm going to ignore these complications, and
concentrate on the differences in the <EM>steady-state</EM> central part of a
note as played by a particular instrument. What all such steady
musical sounds have in common is that they are largely <EM>
periodic</EM>. This means that if you graph the air pressure produced by the
instrument over time (or the voltage when the sound is represented
electrically in a hifi system), the same pattern of high and low pressures
repeats again and again. Here is an example. In this picture, the motion
of your eye from left to right represents the passing of time.
<P><CENTER><IMG SRC="squig5.gif" ALT="figure: squig5"></CENTER>
<P>
The height of the squiggle on the page, at
any particular moment, represents the sound pressure at that moment. So
what this picture shows is that there are many small up-and-down
oscillations superimposed on one large, regular up-and-down motion. (This
one large oscillation is called the <EM>fundamental</EM> frequency.) You can
see that the entire picture consists of five repetitions of a smaller
squiggle with just one of the large oscillations.
<P>From what I've said about oscillations, you might get the impression
that this is a picture of something like a foghorn or siren, in which
you can hear an alternation of loud and soft moments. But this is
actually the picture of what sounds like a perfectly steady tone.
The entire width of the page represents about one one-hundredth of
a second. There are a few hundred repetitions of the single large
up-and-down cycle in each second of a musical note. The exact number
of repetitions is the <EM>frequency</EM> of the note,
and is the same for every instrument. For example, the note A above
middle C has a pitch of 440 cycles per second, or 440 Hertz.
<P>All instruments playing A above middle C will have a picture with
the same fundamental frequency of 440 Hertz. What is different from
one instrument to another is the exact shape of the squiggle. (By
the way, the technical name for a squiggle is a <EM>waveform.</EM> You
can see the waveform for a note by connecting a microphone to an oscilloscope,
a device that shows the waveform on a TV-like screen.)
<P>Here is a picture of the simplest, purest possible tone:
<P><CENTER><IMG SRC="sine5.gif" ALT="figure: sine5"></CENTER>
<P>This is the waveform you'd get from an ideal tuning fork,
with no impurities or bumps. It is called a <EM>sine wave.</EM> This
particular kind of oscillation turns up in many situations, not just musical
sounds. For example, suppose you write a program that starts a turtle
moving in a circle forever.
<P><PRE>to circle
fd 1
rt 1
circle
end
</PRE>
<P>Think about the motion of the turtle, and concentrate only
on its vertical position on the screen. Never mind its motion from
left to right. The up-and-down part of the turtle's motion over time
looks just like this sine wave.
<P>This says more than simply that the turtle alternates moving up and
down. For example, the turtle's vertical motion might have looked
like this over time:
<P><CENTER><IMG SRC="tri5.gif" ALT="figure: tri5"></CENTER>
<P>If
this were the picture of the turtle's motion, it would mean that
the turtle's vertical position climbed at a steady rate until it reached
the top of the circle, then abruptly turned around and started down
again. But in fact what happens is that the height of the turtle
changes most quickly when the turtle is near the "Equator"
of its circle. The turtle's vertical speed gets less and less as
the turtle gets near the "poles." This speed change corresponds
to the gradual flattening of the sine wave near the top and bottom.
(You may find it confusing when I say that the turtle's vertical motion
slows down, because the turtle's speed doesn't seem to change as it
draws. But what happens is that near the Equator, the turtle's speed
is mostly vertical; near the poles, its speed is mostly horizontal.
We aren't thinking about the horizontal aspect of its motion right
now.)
<P><CENTER><IMG SRC="motion.gif" ALT="figure: motion"></CENTER>
<P>What makes sine waves most important, though, is that <EM>any</EM> periodic
waveform can be analyzed as the sum of a bunch of sine waves of different
frequencies. (Sometimes an infinite number of since waves must be
added together.) The frequencies of the sine waves will always be
multiples of the fundamental frequency. This important mathematical
result was discovered by the French mathematician
Jean-Baptiste-Joseph Fourier (1768-1830). The representation of
a mathematical function as a sum of sine waves is called a <EM>
Fourier series.</EM>
<P>For example, when a violin plays A above middle C, the waveform that
results will include a sine wave with frequency 440 Hertz, one with
frequency 880 Hertz, one at 1320 Hertz, and so on. Not all of these
contribute equally to the complete waveform. The <EM>amplitude</EM> of
each sine wave (the amount of swing, or the vertical distance in the
picture) will be different for each. Typically, the fundamental frequency
has the largest amplitude, and the others (which are called <EM>
harmonics</EM> or <EM>overtones</EM>) have smaller amplitudes. The
precise amplitudes of each harmonic are what determine the steady-state
timbre of a particular instrument.
<P><H2>Square Waves</H2>
<P>Two traditional musical instruments, the clarinet and the
pipe organ, share a curious characteristic: their Fourier series
contain only odd harmonics. In other words, if a clarinet is
playing A above middle C, the waveform includes frequencies of 440 Hertz,
1320 Hertz (3 times 440), 2200 Hertz (5 times 440), and so on. But the
waveform does not include frequencies of 880 Hertz (2 times 440), 1760 Hertz
(4 times 440), and so on. (I'm oversimplifying a bit in the case of the
pipe organ. What I've said about only odd harmonics is true about each
pipe, but the organ can be set up to combine several pipes in order to
include even harmonics of a note.)
<P>In recent times, a third musical instrument has come to share this
peculiar Fourier series: the computer. (Perhaps you were wondering
where computers come into this.) Today there are computer-controlled
musical instruments that can generate
any possible sound. Musicians have even used computers to create
new instrument timbres that are not possible with ordinary instruments.
But the particular timbre that most people associate with
computer music is the one produced by the simplest possible
computer sound generator. Instead of a steady oscillation in sound
pressure, this simple device can only be on or off at a given moment. The
computer produces sound by flipping the device from on to off and back at a
particular rate. Such a device produces a <EM>square wave</EM>, like
this:
<P><CENTER><IMG SRC="square5.gif" ALT="figure: square5"></CENTER>
<P>No sound that occurs in nature has a waveform that turns
corners so abruptly. But what is "natural" in nature isn't necessarily
what's "natural" for a computer. For many years, computer-generated
music invariably meant square waves except in very fancy music research
centers.
<P>More recently, new integrated circuit technology has made
it relatively inexpensive to equip computers with "music chips"
that generate sine waves. The stereotyped sound of computer music
is becoming uncommon. But I still find square waves fascinating
for several reasons.
<P>One place where square waves are still used is in the hifi magazines,
in their tests of amplifiers. The testing laboratories feed a square
wave into an amplifier, and show oscilloscope pictures of the waveform
going into the amp and the waveform coming out. Here is an example:
<P>
<CENTER><IMG SRC="scope.gif" ALT="figure: scope"></CENTER>
<P>The oscillation that is visible in the output near the corners of
the input is called <EM>ringing</EM>. A lot of ringing indicates that
the amplifier doesn't have good high-frequency response.
<P>Here is why a square wave is a good test of high frequencies: The
Fourier series corresponding to the square wave includes an infinite
number of odd-harmonic sine wave components. In other words, a perfect
square wave includes infinitely high frequencies. (In practice, the
input picture isn't a perfect square wave. You can see that the vertical
segments aren't <EM>quite</EM> truly vertical, for example.) No amplifier
can reproduce infinitely high frequencies faithfully. The result
is that the output from the amplifier includes only some of the harmonics
that make up the input. It turns out that such a <EM>partial series</EM>,
with relatively few of the harmonics included, produces a waveform
in which the ringing phenomenon at the corners is clearly visible.
<P>If you think about it, that's a bit unexpected. Normally, the more
harmonics, the more complicated the waveform. For example, the simplest
waveform is the one with only the fundamental, and no added harmonics.
Yet, <EM>removing</EM> harmonics from the square wave produces a <EM>
more</EM> complicated picture. I like paradoxes like that. I wanted
to write a computer program to help me understand this one.
<P>Before you can look into the square wave in detail, you have to know
not only the fact that it uses odd harmonics, but also the amplitude
of each harmonic. A square wave with fundamental frequency <EM>f</EM>
has this formula: <P><CENTER><IMG SRC="https://people.eecs.berkeley.edu/~bh/v2ch13/math1.gif" ALT="math display"></CENTER>
<P>The dots at the end indicate that this series goes on forever.
The amplitude of each sine wave is the reciprocal of the harmonic
number (one divided by the number).
<P>This project draws pictures of waveforms containing some number of
terms of this series. (Each sine wave is called a term.) The program
allows many different ways of controlling exactly what is drawn.
<P>To start with something very simple, try this instruction:
<P><PRE>plot 1
</PRE>
<P>The effect of this command is to draw one cycle of a pure
sine wave:
<P><CENTER><IMG SRC="plot1.gif" ALT="figure: plot1"></CENTER>
<P>This
is the first term of the series for the square wave. Now try
this:
<P><PRE>plot 5
</PRE>
<P><CENTER><IMG SRC="plot5.gif" ALT="figure: plot5"></CENTER>
<P>The
input to <CODE>plot</CODE> is the harmonic number of the highest harmonic.
In this example, we've drawn three sine waves added together: the
fundamental, third harmonic, and fifth harmonic.
<P>To see a plot looking somewhat more like the pictures in the amplifier
tests, try
<P><PRE>plot 23
</PRE>
<P><CENTER><IMG SRC="plot23.gif" ALT="figure: plot23"></CENTER>
<P>This
contains the first 12 odd harmonics. (Remember to use an odd
number as input, if you want to see something that looks like a square
wave.) You can see that the result still includes some oscillation
in the horizontal sections, but does have an overall square shape.
<P>A mediocre hifi amp has a frequency response that is good to about
20,000 Hertz. This is about the 45th harmonic of 440 Hertz. To see
how A above middle C would come out on such an amplifier, try
<P><PRE>plot 45
</PRE>
<P><CENTER><IMG SRC="plot45.gif" ALT="figure: plot45"></CENTER>
<P>There
is still some ringing near the corners, but the middle of the
horizontal segment is starting to look really flat. A better amplifier
might be good to 30,000 Hertz. To see how that would look, try
<P><PRE>plot 77
</PRE>
<P><CENTER><IMG SRC="plot77.gif" ALT="figure: plot77"></CENTER>
<P>(The
drawing of the picture takes longer when you use a larger input
to <CODE>plot</CODE>, because the program has to calculate more terms of the series.)
<P>So far, we have only changed one of the possible parameters controlling
the waveform, namely the highest harmonic. The program allows you
to control several other elements of the picture. For example, try
this:
<P><PRE>plot [maxharm 77 yscale 140 deltax 1]
</PRE>
<P><CENTER><IMG SRC="https://people.eecs.berkeley.edu/~bh/v2ch13/plot77big.gif" ALT="figure: plot77big"></CENTER>
<P><CODE>
Plot</CODE> takes one input, but this time the input is a list instead of
a single number. The members of the list are used as sort of "sub-inputs."
The odd-numbered members are the <EM>names</EM> of parameters,
for which the even-numbered members provide <EM>values.</EM>
<P><CODE>Maxharm</CODE> stands for "maximum harmonic"; it is the parameter you were
setting when you used a single number as the input. <CODE>Yscale</CODE> is an
adjustment for the height of the plot. (To "scale" a bunch of numbers
means to multiply all of them by some constant value, the "scale factor.")
You may have noticed that as the
number of harmonics has increased, the pictures have been getting smaller in
the vertical direction; by increasing the value of <CODE>yscale</CODE> we can
expand the height of the plot to show more detail. Similarly, <CODE>deltax</CODE>
allows us to show more horizontal detail, not by widening the picture but by
computing the value for every dot. Ordinarily, the program saves time by
calculating every second dot. This approximation is usually good enough,
but sometimes not. (<CODE>Deltax</CODE> means "change in X." Delta is the
name of the Greek letter D (Δ), which mathematicians use to represent
a small change in something.)
<P>Here's another example:
<P><PRE>plot [11 cycles 5]
</PRE>
<P><CENTER><IMG SRC="plot11by5.gif" ALT="figure: plot11by5"></CENTER>
<P><CODE>Cycles</CODE> indicates
the number of complete cycles you want to see. By saying <CODE>cycles 5</CODE>
in this example, I drew a picture like the ones near the beginning
of this chapter, with five repetitions of the fundamental oscillation.
<P>Notice also that we didn't have to say <CODE>maxharm</CODE>. If a number
appears in the input list where a name should be, it's automatically assigned
to <CODE>maxharm</CODE>.
<P><CODE>Plot</CODE> allows you to specify any of six parameters. Each parameter
has a <EM>default</EM> value, the value that is used if you don't say
anything about it. For example, the default value for <CODE>deltax</CODE> is 2.
Here are all the parameters:
<P><TABLE>
<TR><TH>name<TH>default<TH>purpose
<TR><TD>maxharm<TD> 5<TD> highest harmonic number included in series
<TR><TD>deltax<TD> 2<TD> number of turtle steps skipped between calculations
<TR><TD>yscale<TD> 75<TD> vertical motion is multiplied by this number
<TR><TD>cycles<TD> 1<TD> number of cycles of fundamental shown
<TR><TD>xrange<TD> 230<TD> highest X coordinate allowed
<TR><TD>skip<TD> 2<TD> number of harmonics skipped between terms
</TABLE>
<P>You've already seen what <CODE>maxharm</CODE>, <CODE>yscale</CODE>, <CODE>deltax</CODE>,
and <CODE>cycles</CODE> are
for. Now I'll explain the others.
<P><CODE>Xrange</CODE> is mainly changed when moving the program from
one computer to another. Each computer allows a particular number
of turtle steps to fit on the screen in each dimension, horizontal
and vertical. <CODE>Xrange</CODE> is the largest horizontal position <CODE>plot</CODE>
is allowed
to use. This is set a little below the largest possible X coordinate,
just to make sure that there is no problem with wrapping around the
screen.
<P><CODE>Skip</CODE> is the number of harmonics skipped between terms. To get odd
harmonics, which we need for the square wave, we have to skip by 2
each time, from 1 to 3, from 3 to 5, and so on. Different values
for <CODE>skip</CODE> will give very different shapes.
<P>For example, if you are at all adventurous, you must have tried an
even value of <CODE>maxharm</CODE> a while ago, getting a result like this:
<P><PRE>plot 6
</PRE>
<P><CENTER><IMG SRC="plot6.gif" ALT="figure: plot6"></CENTER>
<P>What
you see is two cycles of an approximation to another shape, the
<EM>sawtooth</EM>:
<P><CENTER><IMG SRC="saw.gif" ALT="figure: saw"></CENTER>
<P>Why
two cycles? Well, <CODE>plot 6</CODE> uses the
second, fourth, and sixth harmonics.
Supposing that the fundamental frequency is 440 again, this means
that <CODE>plot</CODE> added frequencies of 880, 1760, and 2640 Hertz. But these
are also the fundamental, second harmonic, and third harmonic of 880
Hertz. By choosing only even harmonics, you've essentially chosen
<EM>all</EM> the harmonics of <EM>twice the fundamental frequency</EM> you
had in mind. It is this doubling of the fundamental frequency that
produces two cycles on the screen. You could get one cycle of the
same waveform by saying <CODE>plot [3 skip 1]</CODE>:
<P><CENTER><IMG SRC="plot3skip1.gif" ALT="figure: plot3skip1"></CENTER>
<P>You can see much more bizarre waveforms by using other values of <CODE>skip</CODE>.
The best one I've found is <CODE>plot [16 skip 3]</CODE>:
<P><CENTER><IMG SRC="plot16skip3.gif" ALT="figure: plot16skip3"></CENTER>
<P>I
chose a <CODE>maxharm</CODE> of 16 because it includes the fundamental plus five
additional harmonics (4, 7, 10, 13, 16). If I'd made <CODE>maxharm</CODE> 15 or
17, I wouldn't have included the fundamental.
<P><H2>Keyword Inputs</H2>
<P>There are two different points of interest about this project. One
is the whole business of waveforms and Fourier series. The second
is the use of <EM>keyword</EM> inputs, which is the name for this system
of giving information to <CODE>plot</CODE>. The more usual style of Logo programming
would have been to make <CODE>plot</CODE> a procedure with six inputs. To draw
a default graph, you would then have had to say
<P><PRE>plot 5 2 75 1 230 2
</PRE>
<P>Since most of the time you want to use the default values
for most of the inputs, all this typing would be an annoyance. It
would also be easy to make a mistake about the correct order of the
inputs. (This more usual Logo technique is called <EM>positional</EM>
inputs.) The combination of many necessary inputs with standard
values for most of them makes the keyword technique appropriate here.
It isn't always appropriate. You wouldn't want to have to say
<P><PRE>print item [index 2 list [vanilla chocolate strawberry]]
</PRE>
<P>because you have no trouble remembering which input to <CODE>item</CODE>
is which, and you always want to provide both of them.
<P>The procedure that interprets the keyword inputs is called <CODE>keyword</CODE>.
<CODE>Keyword</CODE> was written to be a general tool, not limited to this particular
program. It takes two inputs. The first is the input that you,
the user, provide. The second is a list of defaults. When <CODE>plot</CODE> invokes
<CODE>keyword</CODE>, the second input is this:
<P><PRE>[maxharm 5 deltax 2 yscale 75 cycles 1 xrange 230 skip 2]
</PRE>
<P>This input tells <CODE>keyword</CODE> the names of all the keyword inputs
as well as their default values. It's in the same form as the actual
input you give (a list of alternating names and values), and in fact
<CODE>keyword</CODE> uses a single subprocedure, first to process the default list
and then to process your input.
<P><CODE>Keyword</CODE> is actually not <EM>perfectly</EM> general because it uses the
assumption that all the values it gets are numeric. The virtue of this
assumption is that it allows <CODE>keyword</CODE> to recognize a number without a
name as implicitly referring to the <CODE>maxharm</CODE> keyword. (The name <CODE>
maxharm</CODE> is not built into the procedure. Instead, the first name in the
list of default values is used.) To use <CODE>keyword</CODE> in a context in which
non-numeric words could be values as well as names, this assumption would
have to be removed.
<P>I didn't have keyword inputs in mind from the beginning. When I started
working on this project, the only input to <CODE>plot</CODE> was what I now call
<CODE>maxharm</CODE>, the highest harmonic number to include. All the other numbers
were "wired in"; if I wanted to change something like what is now
called <CODE>:xrange</CODE>, I'd edit all the procedures and change the numbers
in the editor.
<P>Editing all the procedures wasn't too difficult, since without the
keyword-processing procedures everything fits in a single screenful.
Changing the resolution (what is now <CODE>:deltax</CODE>) was a bit annoying,
since I had to edit three different parts of the program. (You can
see that <CODE>:deltax</CODE> appears three times in the final version.) When
I finally got tired of that editing process, I decided to use keyword
inputs.
<P><H2>Making the Variables Local</H2>
<P>The job of <CODE>keyword</CODE> is to create variables, one for each keyword,
and assign a value to each variable. If the user provides a value for
a particular keyword, that's the value to use; if not, the default
value is used.
<P>When I first did this project, I wrote a version of <CODE>keyword</CODE> that
creates global variables for the keywords:
<P><PRE>to keyword :inputs :defaults
if or (wordp :inputs) (numberp first :inputs) ~
[make "inputs sentence (first :defaults) :inputs]
setup.values :defaults
setup.values :inputs
end
to setup.values :list
if emptyp :list [stop]
make first :list first butfirst :list
setup.values butfirst butfirst :list
end
</PRE>
<P><CODE>Keyword</CODE> checks for the special cases of a single number
(as in <CODE>plot 5</CODE>) or a list beginning with a number; in either case,
a new list is made with the first keyword (<CODE>maxharm</CODE>) inserted before
the number. Then the default values are assigned to all the keyword
variables, and finally the user's values are assigned to whatever keywords
the user provided, replacing the defaults.
<P>Since these keyword variables are only used within the <CODE>plot</CODE> program,
it would be cleaner to make them local to <CODE>plot</CODE>, just as ordinary
positional inputs are automatically local to a procedure. I could have
had <CODE>plot</CODE> take care of this before calling <CODE>keyword</CODE>:
<P><PRE>to plot :inputs
local [maxharm deltax yscale cycles xrange skip]
keyword :inputs [maxharm 5 deltax 2 yscale 75 cycles 1 xrange 230 skip 2]
...
</PRE>
<P>but I thought it would be unaesthetic to have to type the
names twice! What I really want is for <CODE>keyword</CODE> to be able to
make the variables local. But I can't just say
<P><PRE>to keyword :inputs :defaults
<U>local filter [not numberp ?] :defaults</U>
if or (wordp :inputs) (numberp first :inputs) ~
[make "inputs sentence (first :defaults) :inputs]
setup.values :defaults
setup.values :inputs
end
</PRE>
<P>because that would make the variables local to <CODE>keyword</CODE>
itself, not to its caller, <CODE>plot</CODE>. This is the same problem I had
in writing <CODE>localmake</CODE> in Chapter 12, and the solution is the
same: Make <CODE>keyword</CODE> a macro!
<P><PRE>.macro keyword :inputs :defaults
if or (wordp :inputs) (numberp first :inputs) ~
[make "inputs sentence (first :defaults) :inputs]
output `[local ,[filter [not numberp ?] :defaults]
setup.values ,[:defaults]
setup.values ,[:inputs]]
end
</PRE>
<P>Now it will be <CODE>plot</CODE>, instead of <CODE>keyword</CODE>, that creates the
local variables and calls <CODE>setup.values</CODE>.
<P><H2>Indirect Assignment</H2>
<P>The actual assignment of values to the keywords is a good illustration of
indirect assignment in Logo. The instruction that does the
assignment is this:
<P><PRE>make first :list first butfirst :list
</PRE>
<P>Usually the first input to <CODE>make</CODE> is an explicit quoted word,
but in this program the variable names are computed, not explicit.
This technique would be impossible in most programming languages.
<P><H2>Numeric Precision</H2>
<P>It's important that the program computes the Fourier
series starting with the higher harmonic numbers, adding in the fundamental
term last. Recall the formula for the series:
<P><CENTER><IMG SRC="https://people.eecs.berkeley.edu/~bh/v2ch13/math1.gif" ALT="math display"></CENTER><P>
The value of the sine function for each term is divided
by the harmonic number of the term. In general, this means that the
terms for higher numbered harmonics contribute smaller values to the
sum.
<P>Theoretically, it shouldn't matter in what order you add up a bunch
of numbers. But computers carry out numeric computations with only
a limited precision. Usually there is a particular number of <EM>
significant digits</EM> that the computer can handle. It doesn't matter
how big or small the number is. The numbers 1234, 1.234, and 0.00000001234
all have four significant digits.
<P>To take a slightly oversimplified case, suppose your computer can
handle six significant digits. Suppose that the value of the fundamental
term is exactly 1. Then the computer could add 0.00001 to that 1
and get 1.00001 as the result. But if you tried to add 0.000001 to
1, the result (1.000001) would require seven significant digits.
The computer would round this off to exactly 1.
<P>Now suppose that the 23rd term in some series is 0.000004, the 24th
term is 0.000003, and the 25th is 0.000002. (I just made up
these values, but the general idea
that they'd be quite small is true.) Suppose we are adding the terms
from left to right in the formula, and the sum of the first 22 terms
is 2.73. Adding the 23rd term would make it 2.730004, which is too
many significant digits. This sum would be rounded off to 2.73 again.
Similarly, the 24th and 25th terms would make absolutely no difference
to the result.
<P>But now suppose we add up the terms from right to left. The sum of
the 25th and 24th terms is 0.000005, and adding in the 23rd term give
0.000009. If we were to add this to 2.73 the result would be 2.730009.
Although this is still too many significant digits, the computer would
round it off to 2.73001. The three terms at the end <EM>would</EM> make
a small difference in the result.
<P>In the square wave series, the successive terms get smaller quite
slowly. You'd have to add very many terms before the problem I'm
describing would really be important. But other series have terms
that get smaller quickly, so that even for a small number of terms it's
important to add in the smaller terms before the larger ones.
<P>
<P>By the way, the procedure <CODE>series</CODE> that computes the value of the
series for some particular <CODE>x</CODE> value is written recursively, but
its task is iterative. I could have said
<P><PRE>to series
localmake "result 0
for [harmonic :maxharm 1 [-:skip]] ~
[make "result :result + (term :harmonic)]
output :result
end
</PRE>
<P>but the use of <CODE>make</CODE> to change the value of a variable
repeatedly isn't very good Logo style. What I really want is an <EM>
operation</EM> corresponding to <CODE>for</CODE>, analogous to <CODE>map</CODE> as the
operation corresponding to <CODE>foreach</CODE>. Then I could say
<P><PRE>to series
output accumulate "sum [harmonic :maxharm 1 [-:skip]] [term :harmonic]
end
</PRE>
<P>You might enjoy using the techniques of Chapter 10 to
implement <CODE>accumulate</CODE>.
<P><H2>Dynamic Scope</H2>
<P>One final point about the programming style of this project has to
do with the use of Logo's dynamic scope. Every procedure has access
to the variables of its superprocedures, and this project takes advantage
of the fact. Many people think it's better style if every procedure is given
all the information it needs as inputs. I didn't follow that rule
in this project because, as I've said, many of the variables were
invented late in the development process, and I did as little rewriting
as possible.
<P>For example, here is the procedure that computes one term of the
Fourier series:
<P><PRE>to term :harmonic
output (sin :xscale * :harmonic * :x) / :harmonic
end
</PRE>
<P>Of the three numbers that are used in this computation, <CODE>
:xscale</CODE> is constant throughout the program, so it's not unreasonable for it
to be used globally. But <CODE>:x</CODE> changes for every point. <CODE>X</CODE> is, in
fact, a local variable, but it's local to <CODE>plot</CODE> rather than to <CODE>
term</CODE>.
<P>
<P>Why does it matter? One reason is that people find it easier to understand
a program if it's made very clear where each variable gets its value. In
this example, it's easy to miss the fact that <CODE>x</CODE> is the index variable
in a <CODE>for</CODE> loop. The second reason is that as written, <CODE>term</CODE> can
be used only within <CODE>plot</CODE>. If we wanted to compute the value of a
Fourier series for some reason other than plotting it, we couldn't easily
use the same procedures.
<P>I decided to leave the program as it is, instead of cleaning it up
for publication, so that you could see an example of a style that,
after all, Logo makes possible by choosing to use dynamic scope.
In some ways, this style is simpler than one in which each procedure
would have several more inputs. You can decide for yourself if you
find it easier or harder to read the program this way, compared to
the officially approved style:
<P><PRE>to term :x :harmonic :xscale
output (sin :xscale * :harmonic * :x) / :harmonic
end
</PRE>
<P>In this version of the procedure, I've made <CODE>x</CODE> the first
input to <CODE>term</CODE>. This seems most natural to me, considering that <CODE>
term</CODE>'s job is to compute an algebraic function of <CODE>x</CODE>. The argument to
a function is very naturally represented in a computer program as
the input to a procedure.
<P>All I've changed in <CODE>term</CODE> itself is the title line. But of course
the invocation of <CODE>term</CODE>, which is in the procedure <CODE>series</CODE>, must
be changed also. And to be consistent, <CODE>series</CODE> should get <EM>its</EM>
needed values as explicit inputs, too:
<P><PRE>to series :x :harmonic :skip :xscale
if :harmonic < 1 [output 0]
output (term :harmonic)+(series :harmonic-:skip)
end
</PRE>
<P>We're up to four inputs, and it'd be easy to get them out of
order when <CODE>series</CODE> is invoked by <CODE>plot</CODE>. You can see why
"inheriting" variables from a procedure's caller can simplify its use.
<P><H2>Further Explorations</H2>
<P>The fact that each term in the series is divided by <CODE>:harmonic</CODE> limits this
program to a particular family of waveforms, the family that includes
square waves and sawtooth waves. In general, real musical instruments
don't have such regularity in the extent to which each term contributes
to the sum. For example, I started
by saying that clarinets and pipe
organs are made of odd harmonics, just as square waves are. But clarinets
don't sound like organs, and neither sound like square waves. There
is a family resemblance, but there are definite differences too.
The differences are due to the different "weights" that each instrument
gives to each harmonic.
<P>Instead of the <CODE>maxharm</CODE> and <CODE>skip</CODE>
variables in the program as I've written
it, you could have an input called <CODE>timbre</CODE> (a French word for
the characteristic sound of an instrument, pronounced sort of like
"tamper" with a B instead of the P) that would be a list of
weighting factors. The equivalent of <CODE>plot 5</CODE> would be this timbre
list:
<P><PRE>[1 0 0.3333 0 0.2]
</PRE>
<P>This list says that the fundamental has a weight of 1, the
second harmonic has a weight of 0 (so it's not used at all), the third
harmonic has a weight of 1/3, and so on.
<P>The <CODE>timbre</CODE> version of the program would be perfectly general. You
could create any instrument, if you could find the right weighting
factors. But so much generality makes it hard to know where to begin
exploring all the possibilities. Another thing you could do would
be to try different kinds of formulas for weighting factors. For
example, you could write this new version of <CODE>term</CODE>:
<P><PRE>to term :harmonic
op (sin :xscale * :harmonic * :x)/(:harmonic * :harmonic)
end
</PRE>
<P>What waveforms would result from this change?
<P>If you're really interested in computer-generated music, you'll want
to hear what these waveforms sound like. Unfortunately, it's hard
to do that with the standard sound generators in personal computers,
which allow little or no control of timbre. But if you have one
of the computer-controllable musical instruments that have become
available recently, you may be able to program them to reproduce
the timbre of your choice.
<P>On the other hand, you can hear the effect of different waveforms
without a computer if you visit the <A HREF="http://www.exploratorium.edu/">Exploratorium</A>
in San Francisco,
the world's best museum. Among their exhibits are several that let
you experiment with different ways of generating sounds. One of these
exhibits is a machine that does audibly the same thing we've been
doing graphically, adding up selected harmonics of a fundamental pitch.
If you don't live near San Francisco, the Exploratorium is well worth
the trip, no matter how far away you are!
<P>
<TABLE width="100%"><TR><TD><A HREF="../v2-toc2.html">(back to Table of Contents)</A>
<TD align="right"><A HREF="https://people.eecs.berkeley.edu/~bh/v2ch12/v2ch12.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch14/manual.html"><STRONG>NEXT</STRONG></A>
</TABLE>
<P><H2>Program Listing</H2>
<P>As mentioned in the text, the appropriate value of <CODE>xrange</CODE> may be
different depending on which computer you're using.
<P><P>
<P><PRE>
to plot :inputs
keyword :inputs ~
[maxharm 5 deltax 2 yscale 75 cycles 1 xrange 230 skip 2]
localmake "xscale :cycles*180/:xrange
splitscreen clearscreen hideturtle penup
setpos list (-:xrange) 0
pendown
for [x :deltax [2*:xrange] :deltax] ~
[setpos list (xcor+:deltax) (:yscale * series :maxharm)]
end
;; Compute the Fourier series values
to series :harmonic
if :harmonic < 1 [output 0]
output (term :harmonic)+(series :harmonic-:skip)
end
to term :harmonic
output (sin :xscale * :harmonic * :x) / :harmonic
end
;; Handle keyword inputs
.macro keyword :inputs :defaults
if or (wordp :inputs) (numberp first :inputs) ~
[make "inputs sentence (first :defaults) :inputs]
output `[local ,[filter [not numberp ?] :defaults]
setup.values ,[:defaults]
setup.values ,[:inputs]]
end
to setup.values :list
if emptyp :list [stop]
make first :list first butfirst :list
setup.values butfirst butfirst :list
end
</PRE><P>
<P><A HREF="../v2-toc2.html">(back to Table of Contents)</A>
<P><A HREF="https://people.eecs.berkeley.edu/~bh/v2ch12/v2ch12.html"><STRONG>BACK</STRONG></A>
chapter thread <A HREF="../v2ch14/manual.html"><STRONG>NEXT</STRONG></A>
<P>
<ADDRESS>
<A HREF="../index.html">Brian Harvey</A>,
<CODE>bh@cs.berkeley.edu</CODE>
</ADDRESS>
</BODY>
</HTML>