Wave Generators

In this tutorial, we will cover the four main waveforms, how to generate them, and how to convert them to something we can actually hear. It is important that we know about these waveforms because they are fundamental to creating sounds (synthesizing). These four waves are: Sine waves, Square waves, Sawtooth waves, and Triangle waves.

This is a picture of the four waveforms.

A waveform is a shape that repeats itself in a medium. These repeating shapes and values are determined by a wave’s attributes. These attributes include: wavelength, frequency, period, and amplitude. Together they allow us to hear different sounds with different pitches, harmonics, and timbres. Take note that the audible range of frequency for human ears is 20 Hz to 20,000 Hz. Typically, the higher the frequency, the higher the pitch sound you get. Harmonics are integer multiples of the frequency.

These are the equations we will be using to create our waveforms:

// Sine wave
math:Sine(frequency * (twoPI * (counter/sampleSize)))

// Square wave
math:Sine(frequency * (twoPI * (counter/sampleSize)))
   if(s <= 0)
       s = 0
   else
       s = 1
   end

// Triangle wave
2 / math:pi * math:InverseSine(math:Sine(frequency * twoPI * counter/sampleSize))

// Sawtooth wave
-2 / math:pi * math:InverseTangent(1 / math:Tangent(frequency * math:pi* counter/sampleSize))

Example Program

Below is an example program implementing all four waveforms. Each waveform is played for a set amount of seconds (which we set to 4) one after another.

use Libraries.Compute.Math
use Libraries.Sound.Audio
use Libraries.Sound.AudioSamples

Math math
number twoPI = math:pi * 2 // period
number frequency = 440
integer seconds = 4 // seconds we want each wave to play for

// ~~~~~ Sine wave
AudioSamples sine
sine:SetChannels(1)
sine:SetSizeInSeconds(seconds)

number sampleSize = sine:GetSamplesPerSecond()
integer counter = 0
repeat while counter < sine:GetSize()
   number s = math:Sine(frequency * (twoPI * (counter/sampleSize))) // Equation for sine wave
   sine:Set(counter, s, 0)
   counter = counter + 1
end

// ~~~~~ Square wave
AudioSamples square
square:SetChannels(1)
square:SetSizeInSeconds(seconds)

sampleSize = square:GetSamplesPerSecond()
counter = 0
repeat while counter < square:GetSize()
   number s = math:Sine(frequency * (twoPI * (counter/sampleSize))) // Equation for square wave
   if(s <= 0)
       s = -1
   else
       s = 1
   end
   square:Set(counter, s, 0)
   counter = counter + 1
end

// ~~~~~ Triangle wave
AudioSamples triangle
triangle:SetChannels(1)
triangle:SetSizeInSeconds(seconds)

sampleSize = triangle:GetSamplesPerSecond()
counter = 0
repeat while counter < triangle:GetSize()
   number s = 2 / math:pi * math:InverseSine(math:Sine(frequency * twoPI * counter/sampleSize)) // Equation for tirangle wave
   triangle:Set(counter, s, 0)
   counter = counter + 1
end

// ~~~~~ Sawtooth wave
AudioSamples sawtooth
sawtooth:SetChannels(1)
sawtooth:SetSizeInSeconds(seconds)

sampleSize = sawtooth:GetSamplesPerSecond()
counter = 0
repeat while counter < sawtooth:GetSize()
   number s = -2 / math:pi * math:InverseTangent(1 / math:Tangent(frequency * math:pi* counter/sampleSize)) // Equation for sawtooth wave
   sawtooth:Set(counter, s, 0)
   counter = counter + 1
end

// ~~~~~ Play the waveforms
Audio audio1
audio1:Load(sine)
// audio1:PlayUntilDone()

Audio audio2
audio2:Load(square)
audio2:PlayUntilDone()

Audio audio3
audio3:Load(triangle)
// audio3:PlayUntilDone()

Audio audio4
audio4:Load(sawtooth)
// audio4:PlayUntilDone()

Getting Started: Including the proper libraries

To begin, we will be using three libraries that must be included at the beginning of our project. Make sure to include the following at the top of your code:

use Libraries.Sound.Audio
use Libraries.Sound.AudioSamples
use Libraries.Compute.Math

Setting up the AudioSamples

We learned from a previous tutorial what the AudioSamples object was and how it is used to create sound. To implement the wave values, we must use the AudioSamples object. First, we create the object and then set how many channels we want and how long we would like the sound to play for. Let’s set it to one channel to produce a mono sound that will play for four seconds. Recall that with mono sound, each speaker will play the same sound from the single channel. We can do this by using the SetChannels() and SetSizeInSeconds() actions from the AudioSamples object.

AudioSamples sine // or square, triangle, sawtooth
sine:SetChannels(1)
sine:SetSizeInSeconds(4)

Sine Waves

Sine waves are the most basic of the sound waves. It produces an isolated sound without additional harmonics. Let’s take a look into the sine wave code from the above example program.

number sampleSize = sine:GetSamplesPerSecond()
integer counter = 0
repeat while counter < sine:GetSize()
   number s = math:Sine(frequency * (twoPI * (counter/sampleSize))) // Equation for sine wave
   sine:Set(counter, s, 0)
   counter = counter + 1
end

Notice that the code is very similar to the white noise generator program we previously created. The only difference here is that instead of setting a sample with a random value, we set the sample to the value of our equation. In fact, the only thing that differs from each of our waveforms’ code is the equation. Sine waves look exactly like you would expect a “wave” to look like. It’s shape comes from the standard sine equation. The line: number s = math:Sine(frequency * (twoPI * (counter/sampleSize)))

is what gives this wave its shape. We plot these values into our sine AudioSamples object to set them. As these samples are taken, you should hear an elongated beep sound. Compared to the other waveforms, the sine wave’s sound may seem dull and basic.

Square Waves, Triangle Waves, and Sawtooth Waves

Square waves, unlike sine waves, include odd [numbered] harmonics. The square wave should sound a bit louder and more rich because of these harmonics. There are only two possible values for square waves, a high level and a low level. Here is our code:

sampleSize = square:GetSamplesPerSecond()
counter = 0
repeat while counter < square:GetSize()
   number s = math:Sine(frequency * (twoPI * (counter/sampleSize))) // Equation for square wave
   if(s <= 0)
       s = -1 // Low level
   else
       s = 1 // High level
   end
   square:Set(counter, s, 0)
   counter = counter + 1
end

Notice that we set our high value to 1 and our low value to 1. The high value is the wave’s amplitude. We also use the same equation as the sine wave but map these values to our high and low levels based on if they are positive or negative.

Triangle Waves are more like square waves in the sense that they also include odd harmonics. The difference is that the harmonics of a triangle wave jump much quicker than a square wave.

sampleSize = triangle:GetSamplesPerSecond()
counter = 0
repeat while counter < triangle:GetSize()
   number s = 2 / math:pi * math:InverseSine(math:Sine(frequency * twoPI * counter/sampleSize)) // Equation for tirangle wave
   triangle:Set(counter, s, 0)
   counter = counter + 1
end

Again the code looks idential but with a different mathematical equation

Lastly, sawtooth waves contain all harmonics (even and odd) which create a fuller sound. They are shaped the way they are because they rise steadily then drop to their lowest level.

sampleSize = sawtooth:GetSamplesPerSecond()
counter = 0
repeat while counter < sawtooth:GetSize()
   number s = -2 / math:pi * math:InverseTangent(1 / math:Tangent(frequency * math:pi* counter/sampleSize)) // Equation for sawtooth wave
   sawtooth:Set(counter, s, 0)
   counter = counter + 1
end
The sound of a single sawtooth wave should sound more like a buzz rather than a beep.

If you listen closely to all these sound waves, you should be able to hear a sine wave as well. This is because each waveform can be described as an infinite set of sine waves.

Generate Different Waveforms

Next Tutorial

In the next tutorial, we will discuss AM & FM Synthesis, which describes how to synthesize your own sounds..