In the previous part of this series you learned a lot about the mother of all periodic waveforms, the sine wave and how to create a simple tone generator that sounds the note A using AS3.

Yesterdays and todays synthesizers offer a variety of different sound tone generators called **oscillators**. What it actually sounds like among other factors depends on the waveform it generates. As of yet you know what a sine wave sounds like but what else is there?

There are three other common periodic waveforms, each unique in sound.

**Square**

To refresh your memory, the sine wave sweeps between -1 and +1 periodically or more generally between -amplitude and +amplitude in one cycle. One cycle goes from 0 to 2 * π.

The number of cycles it completes within one second make up the fundamental frequency of the sound.

In comparison, one cycle of a square wave looks like this:

As you can see the difference is kinda obvious. The sound as well!

Let’s hear our note A again, this time generated using a square wave.

But how can we construct this waveform?

Let’s take a look at the code for our sinewave generator we developed in part 1 of this series:

1 2 3 4 5 6 |
sample = Math.sin(phase) * volume; phase += Math.PI * 2 * frequency / sampleRate; if (phase > Math.PI * 2) { phase -= Math.PI * 2; } |

**Phase **tells us at what position of the waveform we currently are and is a value between 0 and 2 * π. If it exceeds 2 * π we finished a cycle and can jump back to the beginning.

If we take a closer look at the square wave above, we notice that between 0 and π, the amplitude stays at +1 while between π and 2 * π the amplitude suddenly goes to -1 for the rest of the cycle. That shouldn’t be too hard to put into an algorithm.

In fact, it’s as easy as 1 2 3!

Pseudo code:

If phase is smaller than π then amplitude=1

If phase is greater than π then amplitude=-1

Transfered to AS3:

1 |
sample = phase < Math.PI ? volume : -volume; |

That’s it! The remaining two waveforms are just as easy!

**Sawtooth**

Again, we should have a look at a visual representation first.

Can you imagine what it sounds like? If not, listen to this example:

Simply by looking at the graph, we can derive an algorithm. The amplitude goes from +1 to -1 gradually from 0 to 2 * π. If we remember phase being some kind of pointer, we can come up with:

1 |
sample = volume-(volume / Math.PI * phase); |

Ready for the last waveform? 🙂

**Triangle**

I know you’re surprised now. A triangle waveform is shaped kinda like a triangle!

Like a sine wave it’s very pure in sound and can be used as a basis for a flute for example.

By looking at the graph we can see that it’s amplitude rises gradually from -1 to -1 within π and descents back to -1 at 2 * π.

One way to express this as an alogrithm would be:

1 |
sample = phase < Math.PI ? (-volume + (2 * volume / Math.PI) * phase) : (3 * volume - (2 * volume / Math.PI) * phase); |

**Summing up**

Now that we know more about the common periodic waveforms sine, square, sawtooth and triangle let’s revise our tonegenerator from part 1 of the series. We want to incorporate the missing 3 oscillators!

Here’s the complete code first:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
package { import flash.display.Sprite; import flash.events.Event; import flash.media.Sound; import flash.events.SampleDataEvent; public class Main extends Sprite { private const buffer:int = 8192; private const sampleRate:int = 44100; private var frequency:Number = 440; private var phase:Number = 0; private var volume:Number = 0.1; private var oscillator:String = new String("square"); public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); var mySound:Sound = new Sound(); mySound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData); mySound.play(); } private function onSampleData(event:SampleDataEvent):void { var sample:Number; for (var a:int = 0; a < buffer; a++) { switch (oscillator) { case "sine": sample = Math.sin(phase) * volume; break; case "square": sample = phase < Math.PI ? volume : -volume; break; case "sawtooth": sample = volume - (volume / Math.PI * phase); break; case "triangle": sample = phase < Math.PI ? (-volume + (2 * volume / Math.PI) * phase) : (3 * volume - (2 * volume / Math.PI) * phase); break; } phase += Math.PI * 2 * frequency / sampleRate; if (phase > Math.PI * 2) { phase -= Math.PI * 2; } event.data.writeFloat(sample); event.data.writeFloat(sample); } } } } |

Again, this is pretty straightforward.

Line 15

We set up a new variable called **oscillator**, which makes it possible for us to select our desired oscillator. Possible values are sine, square, sawtooth and triangle.

Lines 39-53

Based on the value of oscillator, we pick one of the algorithms we’ve worked out in this tutorial.