diff options
author | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2023-08-23 07:52:19 -0400 |
commit | 562a9a52d599d9a05f871404050968a5fd282640 (patch) | |
tree | 7d3305c1252c043bfe246ccc7deff0056aa6b5ab /js/games/nluqo.github.io/~bh/v2ch13/fourie.html | |
parent | 5d012c6c011a9dedf7d0a098e456206244eb5a0f (diff) | |
download | tour-562a9a52d599d9a05f871404050968a5fd282640.tar.gz |
*
Diffstat (limited to 'js/games/nluqo.github.io/~bh/v2ch13/fourie.html')
-rw-r--r-- | js/games/nluqo.github.io/~bh/v2ch13/fourie.html | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/js/games/nluqo.github.io/~bh/v2ch13/fourie.html b/js/games/nluqo.github.io/~bh/v2ch13/fourie.html new file mode 100644 index 0000000..74ea2c4 --- /dev/null +++ b/js/games/nluqo.github.io/~bh/v2ch13/fourie.html @@ -0,0 +1,853 @@ +<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> |