# Intro to Audio Programming Part 4: Algorithms for Different Sound Waves in C#

In the last article, we saw how to synthesize a sine wave at 440Hz and save it to a working WAV file. Next, we’ll expand on that application and learn how to implement some other common waveforms.

First of all, I copied WaveFun from the last article as a foundation to work from and called it WaveFun2. The UI provides a few more configuration options, as shown here to the right.

You can now specify where you want the file to go, as well as the frequency and volume of the waveform. The code has been lightly refactored to support feeding these values from the UI. I’m not going to go into great detail on these changes in this post, but I will show you how to generate the waves.

You can pick from five total waveforms: Sine, Square, Sawtooth, Triangle and White Noise. These are very common waveforms used in synthesizing music.

Also, for the sine and square waves, we are using a variable t that represents the angular frequency (basically, the “tone” frequency in radians and adjusted for sample rate).

# Sine

We have already done some work around the sine wave. It is the smoothest sounding tone signal we can produce.

The Shape

A sine wave looks like this:

The Equation

Sample = Amplitude * sin(t*i)

where t is the angular frequency and i is a unit of time.

The Algorithm

Generating just one 16-bit channel of a sine wave (mono) is very easy.

`for (int i = 0; i < numSamples; i++){    data.shortArray[i] = Convert.ToInt16(amplitude * Math.Sin(t * i));}`

If you wanted to generate two identical, properly aligned channels of sine data, you have to write the same value twice in a row because channel data is interleaved. Further examples will show this in multichannel format, like below.

`for (int i = 0; i < numSamples - 1; i ++){    for (int channel = 0; channel < format.wChannels; channel ++)    {        data.shortArray[i + channel] = Convert.ToInt16(amplitude * Math.Sin(t * i));    }}`

# Square

The square wave is closely related to the sine wave, although it is not in sine form. The square wave produces a very harsh tone due to the abrupt rises and falloffs in the waveform:

The Equation

There are a number of ways to generate square waves, and many of them generate imperfect square waves (especially electronics). Since we are using a very precise program with nice things like for loops, we can generate a square wave that is absolutely perfect.

The equation we will be using is:

Sample = Amplitude * sgn(sin(t * i))

In this case the sgn function just tells us whether the value of the sine function is positive, negative, or zero.

The Algorithm

To generate two channels of a square wave:

`for (int i = 0; i < numSamples - 1; i++){                            for (int channel = 0; channel < format.wChannels; channel++)    {        data.shortArray[i] = Convert.ToInt16(amplitude * Math.Sign(Math.Sin(t * i)));    }}`

# Sawtooth

The sawtooth wave has tonal qualities somewhere between a sine and a square wave, almost like a saxophone. It ramps up in a linear fashion and then falls off.

The Equation

A “proper” sawtooth wave is created with additive synthesis, which we’ll get into later. Multiple sine waves are added together to create harmonics, until eventually the wave takes the shape of the sawtooth (check this nice animation from wikipedia).

Synthesizing a sawtooth in this manner is best done with an algorithm known as a fast Fourier transform, which we won’t get into just yet because it’s kinda complicated, although all kinds of filters and effects are calculated using FFT algorithms. Instead, we’ll be calculating it as if we were plotting a graph, which means we get “infinite” harmonics. This isn’t really a good thing; it will sound less smooth than a sawtooth calculated with a FFT, but whatever.

The equation for a sawtooth wave is sometimes expressed in this way:

y(t) = x – floor(x);

However, we’ll be generating the wave procedurally.

The Algorithm

The algorithm we will be using is a lot like the one we’d use just to plot this wave on a chart.

We have to determine the “step” of the y-coordinate such that we get a nice diagonal line that goes from minimum amplitude to maximum amplitude over one wavelength, and this is based on the frequency of the wave. The “step” is the difference in amplitude between adjacent samples.

`   1: // Determine the number of samples per wavelength`
`   2: int samplesPerWavelength = Convert.ToInt32(format.dwSamplesPerSec / (frequency / format.wChannels));`
`   3:  `
`   4: // Determine the amplitude step for consecutive samples`
`   5: short ampStep = Convert.ToInt16((amplitude * 2) / samplesPerWavelength);`
`   6:  `
`   7: // Temporary sample value, added to as we go through the loop`
`   8: short tempSample = (short)-amplitude;`
`   9:  `
`  10: // Total number of samples written so we know when to stop`
`  11: int totalSamplesWritten = 0;`
`  12:  `
`  13: while (totalSamplesWritten < numSamples)`
`  14: {`
`  15:     tempSample = (short)-amplitude;`
`  16:  `
`  17:     for (uint i = 0; i < samplesPerWavelength && totalSamplesWritten < numSamples; i++)`
`  18:     {`
`  19:         for (int channel = 0; channel < format.wChannels; channel++)`
`  20:         {`
`  21:             tempSample += ampStep;`
`  22:             data.shortArray[totalSamplesWritten] = tempSample;`
`  23:  `
`  24:             totalSamplesWritten++;`
`  25:         }`
`  26:     }                        `
`  27: }`

On line 2, we calculate the number of samples in 1 “tooth.”

On line 5, we calculate the amplitude step by taking the total amplitude range (amplitude * 2) and dividing it by the number of samples per saw tooth.

On line 8, we declare a temporary sample to use. This sample has the amplitude step added to it until we get to maximum amplitude.

We put this in a while loop, because our sample data might end in the middle of a sawtooth wave and we don’t want to go out of bounds.

On line 15, we reset the sample to the minimum amplitude (this happens before each saw tooth is calculated).

We use the same structure – two for loops – for this algorithm. We also add checks to make sure we’re not at the end of the sample data yet.

On line 21 we increment the temporary sample value by the amplitude step and assign it to the data array.

# Triangle

The triangle wave is like the sawtooth wave, but instead of having a sharp falloff between wavelengths, the amplitude rises and falls in a smooth linear fashion.

The triangle wave produces a slightly more coarse tone than the sine wave.

The Formula

… again, uses a fast Fourier transform to synthesize from a sine wave using odd harmonics. So let’s just create one the easy way!

The Algorithm

The algorithm we use is similar to that of the sawtooth wave shown above. All we are doing is changing the sign of the ampStep variable whenever the current sample (absolute value) is greater than the specified amplitude. So when the wave reaches the max and min, the step changes its sign so the wave goes the other way.

`for (int i = 0; i < numSamples - 1; i++){    for (int channel = 0; channel < format.wChannels; channel++)    {        // Negate ampstep whenever it hits the amplitude boundary        if (Math.Abs(tempSample) > amplitude)            ampStep = (short)-ampStep;        tempSample += ampStep;        data.shortArray[i + channel] = tempSample;    }}                    `

# White Noise

Generating white noise is probably the easiest of them all. White noise consists of totally random samples. Interestingly, white noise is sometimes used to generate random numbers.

The Equation

… There isn’t one.

The Algorithm

All you need to do is randomize every sample from –amplitude to amplitude. We don’t care about the number of channels in most cases so we just fill every sample with a new random number.

`Random rnd = new Random();short randomValue = 0;for (int i = 0; i < numSamples; i++){    randomValue = Convert.ToInt16(rnd.Next(-amplitude, amplitude));    data.shortArray[i] = randomValue;}`

# Conclusion

These are just some examples of waves that you can use to create interesting sounds. Next, we are going to learn to combine them to create more complex waveforms.

Currently Playing: Black Label Society – 1919 Eternal – Demise of Sanity

1. Joshua Smyth says:

Very interesting arrticle – can you write the wav to somekind of memory stream and play it back in real time so you can create some kind of synth keyboard thing?

2. Nish says:

Small correction: In the Square wave example, shouldn’t the two channel loop be: data.shortArray[i + channel]?

3. Patrick says:

Great post. Did some wave stuff before but lost everything with a HDD crash, these projects are a great place to start again.

Will part 5 be up anytime soon?

Thanks for the efforts so far!

4. HarryLime says:

Hi Dan, there is coding error in the sawtooth generation which is audible when volume is not 100%.

I had a close look with a wave editor. For example with volume at 42% the generated wave is one octave lower, full swing and not a sawtooth any more.

Fix of the inner loop:

Move line  21:             tempSample += ampStep;

above line 19:         for (int channel = 0; …..

Having fixed that i starting digging deeper into the code because the triangle algorithm works correctly. Although it has the same increment statement within the inner loop.

Because of the check of amplitude boundaries the wave is correct. The only minor issue is that left and right samples have not the same value.

Then i was going back to the start of your article to the sine wave generation. Your intention is to generate a stereo wave with the exact same values for left and right. That is what the inner loop does but the array content is partly overwritten by the outer loop because of i++ instead of  i+=format.wChannels.

Here is my suggestion for the sine wave:

for (int i = 0; i < numSamples; i += format.wChannels)

{

tempSample = Convert.ToInt16(amplitude * Math.Sin(t * i));

for (int channel = 0; channel < format.wChannels; channel++)

{

data.shortArray[i + channel] = tempSample;

}

}

Hope this helps.

Anyway, you wrote an very interesting intro about wave generation. Looking forward to the WPF Wave Synth.

best regards,

Harry

5. wohoo says:

thx for this !

took me ages to find out such a "walkthrough" how to make audio signals

woohoo!

6. bing says:

i read the all 4 articales that u have written cool articles, if i input a wave file and what to know the frequencies how may i approach that problem………..

ex: input a wav file with a song and capturing the frequencies in each second..

can u give me a feedback on this plz

thank you…..

7. sharon says:

very excited to find this blog !!

i am looking for exactly the same thing !

it gives me an error "Server Application Unavailable " 🙁  help ?

8. sharon says:

oh , now it works! i was able to downlaod the file!!!!!!!!!! i guess i picked a bad day last time to try and download .

this is    e x a c t l y    what i needed !!!! a-m-a-z-i-n-g

thanks!

9. dawate says:

Thanks everyone for the corrections and also I'm sorry about the outages that have affected people's downloads of the code.

I will make the corrections visible in the code in the coming weeks, but just wanted to express my thanks for the bug-hunting!

10. Gonçalo Vieira says:

This is a ver interesting document.

Is it possible to add reverbation to this wave using a delay buffer? How I can do that? do you have any ideia?

11. 192 says:

what about a sound that varies in frq. duration (vol) and pitch… you need something bettter… like what i am working on…..

it is for sale

12. dammage says:

your articles leave me speechless, just so great, thanks

13. e2020 says: