Close

PWM Sine wave generator

A project log for Pink Noise Generator

Digital white noise with an analog "pink" filter

stefan-antoszkoStefan Antoszko 10/12/2025 at 06:100 Comments

Last summer I backpacked along the west coast of Vancouver island. Sometimes I look back at the pictures I have of the vast shoreline, massive trees, and unique flora and fauna from that magical landscape. The pictures from that trip are pretty good, but when I really want to re-live those memories and feel like I'm back in my hiking boots, I sometimes listen back to a short 30 second voice memo of waves washing over a pebble beach that I recorded on my phone. This voice memo is one of the tools I'm using to help me along this project.

When looking at the audio waveform of the voice memo, I was looking for the period of the waves (in seconds) and what kind of shape the waves were making. The recording is pretty short, it only captures about 4 waves, and there are really loud pebble-wave sounds that probably make the waveform a little fatter than it should be, but it looks like waves crash at around 5-10 second periods and their shape is surprisingly sinusoidal. That was not what I was expecting, I cannot lie.

Anyways, I had a clever idea for generating the sinusoids on the microcontroller. Since I need the sine wave to move over time, I can just calculate it on the fly using sin/cos relationships! I thought that if I have two variables, one for sin[i] and cos[i], since calculus tells us that d/dt sin = cos and d/dt cos = -sin, then I can calculate the amount they both need to change to get sin[i+1} and cos[i+1]! Now I doubted this would work in practise because it seemed a little too simple, so I made a spreadsheet to quickly test out the idea, and it worked! But I still doubted it would work in practise because of integer precision and under/overflows so I implemented it on my host machine in python, then in C, then in C with integers, and it didn't really start breaking until I forced it to 8 bit integers in C. Here's the main idea:

// I use _ before sin and cos to avoid redefining symbol errors

uint16_t scale_nom = 1;
uint16_t scale_denom = 60;

// scale = scale_nom / scale_denom; // (I'm avoiding floats!)

// initial conditions
uint16_t _sin = 0x7000;
uint16_t dcos = (int32_t)_sin * scale_nom / scale_denom;
uint16_t _cos = 0;
uint16_t dsin = (int32_t)_cos * scale_nom / scale_denom;

// iteratively increment values with their deltas.
for(int i = 0; i < n; i++) {
    _sin += dsin;
    dcos = -(int32_t)_sin * scale_nom / scale_denom;
    _cos += dcos;
    dsin = (int32_t)_cos * scale_nom / scale_denom;
}

GIven:


Assuming its the first iteration, the first line in the for loop updates sin by 0. Then, line two calculates dcos, or how much cosine will change next. dcos is equal to -sin times a small change in time, and I use a fraction scale=scale_num/scale_denom where scale_demon >> scale_num for smooth outputs. After updating dcos, we can increment cos += dcos, and lastly calculate dsin from cos * the same scale.

In my code, I call dt `scale` and calculate it as `scale_num / scale_denom`.


The effect of the scale on the output is that the smaller scale is, the smoother the output will be, and at the same time, the longer the period. I discovered some interesting behaviours when testing out this piece of code. The algorithm often overshoots the initial condition, so I give it a little headroom (0x7000 instead of 0x7fff). Additionally, cranking the scale to be not-so-small (over 0.5) makes it overshoot more, so for real sines its best to keep scale<0.5 but out of curiousity I tested what would happen if we cranked it above 1. For a while, it looks ok, but then after around 2 it starts looking pretty chaotic.

Signal and FFT when scale = 1/60 (0.0083)
Signal and FFT when scale = 1/60 (0.0083). Nice and clean!
Signal when scale = 1.6
Signal when scale = 1.6. Starting to get a little deformed.
Signal when scale = 1.95
Signal when scale = 1.95. Uh oh!
Chaotic signal at scale = 2.03
Chaotic signal at scale = 2.03
Chaotic Signal and FFT when scale = 2.05
Chaotic Signal and FFT when scale = 2.05. The noise has a pretty gnarly frequency profile so it would probably not make for a good noise source. But it may sound good for nasty metallic drums, or something like that.
Chaotic Signal and FFT when scale = 4.32
Chaotic Signal and FFT when scale = 4.32. Compared to the plot at 2.05, this one has a little smoother noise, so it may sound a little less harsh (relatively). I'm sure it still sounds pretty painful though.

I think the Chaotic output arises from the integer under/overflows since when I tried cranking the scale >1 while using floats, not ints, the magnitude of the sine wave just blows up, no chaos. I didn't do any further analysis on the output to see how random it really is, because I don't think this is going to replace my shift-register noise gen (which I know works and is really white). I'm pretty happy with what I discovered here and I kind of want to hook it up to a knob and play it like a wavetable to hear what it sounds like!

Anyways, till next time!

Discussions