Posted by & filed under Noise, PIC/Programming, Synth DIY.

It’s been around ten years since I developed the original Druid NOISE 1B chip to produce good quality white noise. Finally it gets an update, and the new NOISE2 chip can produce pink noise as well as white noise, so there’s no need for external “pinking” filters.

Here’s what it sounds like, white noise and pink noise:

 

Here’s the spectrums for the two outputs. Note the scales on these two graphs are different, so you can’t compare one to the other directly…sorry!

Electric Druid NOISE2 White Noise output

The White noise output is flat to within 2 or 3dB over the audio range.

Electric Druid NOISE2 Pink Noise output

The Pink Noise follows the theoretical -3dB/octave slope over the audio range, and beyond, although we can’t see that here. The sharp drop-off above 20KHz and below 50Hz is due to the limitations of my digital recording set-up. The unit has steep anti-aliasing filters at 20KHz.

So how did this come about?

Adding pink noise generation to my chip was suggested by Richie Burnett, who I know from the Synth-DIY list. He suggested the “Voss-McCartney algorithm” as the best method of doing it. I hadn’t heard of this, so I went off to research it.

The Voss-McCartney Pink Noise Algorithm

The algorithm was first proposed in a 1978 paper by Voss and Clarke, and then became well known after being featured in a Martin Gardner column in Scientific American. The basic idea of the algorithm is to generate a pink noise sample by adding together lots of white noise generators that have been sampled at different rates. In the Voss algorithm, each generator is sampled at half the rate of the last one.

The topic and the algorithm came up in a discussion on the music-dip mailing list in August and September 1999, and several further refinements were added. The major one was James McCartney pointing out that by shifting the update times of the different generators, only one would need to be updated each sample. This evens out the computational load of the algorithm and makes it much more practical. It does throw up one interesting question though, which is how we efficiently generate the tree-like structure that is required to determine which output should be updated.

A further refinement is to add a white noise source at the full sample rate. This helps “fill in” some of the nulls in the frequency response and smooths the performance somewhat.

How good is the Voss-McCartney algorithm?

A theoretical pink noise source produces noise down to 0Hz and up to infinity (although exponentially decreasing), so the first thing to say is that any *actual* noise source, white or pink, is designed for a given frequency range and to given degree of accuracy. For musical purposes, we’re looking at the audio range from 20Hz to 20KHz, and while we might want our white noise to be reasonably flat across that range and our pink noise to be reasonably even, we’re not going to worry about a few decibels here or there. This is not a piece of test equipment, and having a super-high-accuracy pink noise source won’t make your synth sound better. It’s more important that it sounds good and is useful for music.

With that said, let’s have a look at the response of the Voss-McCartney algorithm. Allen Herriman points out that one way to think about the algorithm is that it is a white noise source being sampled by a sample-and-hold.

This gives us a way of getting a grip on the frequency response. The sampling-and-holding process gives a frequency response of (sin(x) / x). We can plot this response for the different octaves and see how they add together. Here’s an example at a sample rate X:

Halving the sample rate squashes the spectrum to the left. This is significant because this squashing means more of the lumpy part on the right moves down into the region we’re interested in. Here’s what happens when we reduce the sample rate to X/2:

As we move down the octaves, more of the noise power gets pushed lower and lower, but more little lumps get introduced in the higher frequencies. This is the next octave down, with a sample rate of X/4:

If we keep adding lower and lower octaves, we can build up our pink noise response. On this graph, the theoretical slope is shown in magenta, and the final response is shown in blue. This theoretical result matches our actual response shown at the top of the page rather well. The magenta vertical line is at 25KHz, the highest audio frequency we care about. The final result is basically within +/-2dB of the theoretical slope, apart from at the very highest audio frequencies, where it starts to droop a little bit by 4db or so.

It’s clear that the response is still a bit “lumpy”. Unfortunately, adding more generators doesn’t help. The lumps happen because the  generator’s sample rates are all related in neat octaves. We can avoid the lumps by setting up the generators at non-multiple sample rates. This works, but generating several prime-number sample rates is a computational nightmare. Alternatively, we could randomise the output sample rates a bit, or a lot, even. This is the basis of Larry Trammell’s Stochastic Voss-McCartney Algorithm, which I’m not going to describe here because it’s beyond what we can realistically manage, or what we realistically need. There’s a link for it in the references at the bottom, and it’s definitely worth a read.

Are there alternative approaches?

The other obvious way to generate pink noise is to copy the analog approach of filtering white noise, but to do it digitally. This can provide a very accurate pink noise response if you have a filter with enough poles and zeros. Unfortunately, there is no way a PIC is going to be able to calculate such a filter at the kind of rates necessary. This is a non-starter.

There’s another method presented by Stefan Stenzel of Waldorf Music, but again, although his method is clever and efficient on an ASIC, it’s not suitable for a low-end processor.

Can Voss-McCartney be done on a PIC?

Ok, so we’ve established that the algorithm produces decent, if not perfect, quality pink noise. The next question is “is it practical on a PIC processor?” This is not simple to work out ahead of time, but we can do some back-of-the-envelope calculations to see if it’s “easy”, “close”, or “no chance”. Let’s assume we want a 100KHz output sample rate. This gives us theoretical bandwidth out to 50KHz, which should be more than enough to keep any weirdness at the high end away from the part we care about. We’re going to need a fast processor, so I started by looking at the 16F18313 that I used for the StompLFO project since it can run with a 32MHz internal clock, giving a 8MHz instruction rate. This enables us to work out how many instructions we have available per sample: 8MHz/100KHz = 80 instructions. Ouch. That’s a few, but it’s not many, and this algorithm might eat them up pretty fast. So this definitely gets filed under “Going to be close”!

Breaking the algorithm down a bit, there are various stages:

    • Update the LFSRs
    • Update one of the outputs
    • Add a constant white noise source
    • Add up the various outputs
    • Send the result to the outside world

Simple, right?! Let’s see.

Updating the LFSRs

Firstly, it’s worth pointing out that we only need two LFSRs, despite having a lot of generators. That’s because by using the McCartney trick of shifting the updates, we only ever update one octave generator and one white noise sample each time.

The code for our two LFSRs looks like this:

;	Update the two LFSRs
;----------------------------------------------------
	
	; 31-bit LFSR with taps at 31 and 28
	;----------------------------------------------------
	; This is our white noise source, and is also the fixed white
	; noise element of the pink noise source.
	swapf	LFSR1_4, W	; Get tap 28
	movwf	TEMP
	rlf		LFSR1_4, W	; Get tap 31
	xorwf	TEMP, F
	; Shift the XORd bit into carry
	rlf		TEMP, F
	; Shift the register
	rlf		LFSR1_1, F
	rlf		LFSR1_2, F
	rlf		LFSR1_3, F
	rlf		LFSR1_4, F


	; 21-bit LFSR with taps at 21 and 19
	;----------------------------------------------------
	; This is used for the lower octaves
	rrf		LFSR2_3, W	; Get tap 19
	movwf	TEMP
	rrf		TEMP, F
	swapf	LFSR2_3, W	; Get tap 21
	xorwf	TEMP, F
	; Shift the XORd bit into carry
	rrf		TEMP, F
	; Shift the register
	rlf		LFSR2_1, F
	rlf		LFSR2_2, F
	rlf		LFSR2_3, F

Updating one of the outputs

The next problem is how to work out which one of the shifted outputs we should update. How do we generate that interesting tree-like structure?

Simple! We don’t! Whenever I see anything vaguely complicated, I go running for a look-up table, and this is no different.

;	Update one of the LFSRs
;----------------------------------------------------

; First, we need to know which one we're updating
SelectOutput:
	; Update the tree structure counter
	movlw	D'1'
	addwf	COUNT_LO, f
	movlw	D'0'
	addwfc	COUNT_HI, f
	
	; Find out which output we need to update
	movf	COUNT_LO, w
	call	TreeTable
	
	; Update the correct output
    brw
    goto	Octave0				; Updated every 2nd sample
    goto	Octave1				; Updated every 4th sample
    goto	Octave2				; Updated every 8th sample
    goto	Octave3				; etc..
    goto	Octave4
    goto	Octave5
    goto	Octave6
    goto	Octave7
    goto	Octaves8to13		; These only come around every 256th sample

    
Octave0:
	; We assume the bit is cleared, and then set it if neccessary
	bcf		OUTPUT_LO, 0
	btfsc	LFSR2_2, 0			; Doesn't really matter which LFSR bit we use
	bsf		OUTPUT_LO, 0
	; The other outputs are done in exactly the same way
	goto	Octaves8to13Compensation

Octave1:
	bcf		OUTPUT_LO, 1
	btfsc	LFSR2_2, 0		
	bsf		OUTPUT_LO, 1
	goto	Octaves8to13Compensation

You can guess what Octave2 to Octave7 look like. If you can’t, check the entire code. The Octaves8to13 routine repeats what’s above, but only gets called once every 256 samples, so it provides all the lower octaves. The fact that there’s a variable length here (Octaves 0 to 7 only do one set of branching, where Octaves 8 to 13 do two) explains the need for the Octaves8to13Compensation routine, which is just a bunch of NOP instructions to keep the two paths the same length in terms of instruction cycles.

Add the constant white noise

This is simple. We just take the output from our white noise LFSR and stick it into a spare bit in the output bytes.

;	Add white noise every sample to fill in the nulls
;----------------------------------------------------
AddWhiteNoise:
	; This uses the other LFSR, but is otherwise identical
	bcf		OUTPUT_HI, 6
	btfsc	LFSR1_4, 0		
	bsf		OUTPUT_HI, 6

Add up the various outputs

Ok, so we’ve got two bytes, OUTPUT_LO and OUTPUT_HI, containing the values of our random generators. We need to add up the output of all the generators and send it to the DAC. This is a basic “bit counting” task. I’ve organised things so that there are a maximum of 15 bits (14 octave generators and the constant white noise generator).

Again, the quickest way is a look-up table. We can use each byte as the index into a table which tells us how many bits are set in that number. We do this twice for the OUTPUT_LO and the OUTPUT_HI and add them together.

;	Add up how many of the outputs are set
;----------------------------------------------------
; We do this with an 8-bit look-up to save actual adding
AddUpBits:
	; Add up the number of bits from Octaves 0-7
	movf	OUTPUT_LO, w
	call	NumOfBitsSet
	movwf	TEMP				; Store it in TEMP
	; Add up the number of bits from Octaves 8-15
	movf	OUTPUT_HI, w
	call	NumOfBitsSet
	addwf	TEMP, f				; Add the number in TEMP to get the total
	; We have the total number of bits in TEMP

Send the result to the outside world

This is simple too. We just copy the TEMP value we just generated into the DAC output register. It’s a 5-bit DAC, so we shift it over one bit first to use the most significant bits not the least significant.

OutputToDAC:
	lslf	TEMP, w				; Shift up to 0-30 range
	movwf	DACCON1

The entire thing runs in very close to the 80 instructions we were aiming at, so we basically get our 100KHz sample rate. We can output the white noise source to a pin too, and then we have a chip which provides both white and pink noise outputs on a single 8-pin chip. Just add 5V. Actually, sometimes it’s not quite that simple. The pink noise DAC output can’t source much current, so if it’s not feeding a high impedance, it’ll need buffering. This is because the resistor string DAC used has a high output impedance (and one that depends on the value set, too).

Fantastic! I want white and pink noise!

Well, there’s the usual options for Druid chips – program your own, or we’ll have some in the shop in a few weeks.

The schematic diagram is very simple. I added simple RC roll-off at 25KHz just to turn the digital pulse white noise into something more analog-looking. It sounds just the same though, but it makes a difference if you’re modulating things with it. Both outputs have been given op-amp buffers to protect them. As mentioned above, the Pink output cannot drive significant loads, so this buffer is a good idea. For the White output, it prevents subsequent circuits affecting the filter.

Credits and Feedback

Many thanks to Richie Burnett for bringing this algorithm to my attention in the first place, and for all the help via email, both on this project and others. It’s much appreciated, Richie.

Thanks also to Robin Whittle for his excellent page about the topic, which taught me a lot, and the other people mentioned in the references below, all of which I’ve found very useful and interesting.

Any mistakes spotted, feedback, or ideas for further development are always appreciated. Please get in touch.

References:

The original Voss and Clarke paper:

Voss, R. F., & Clarke, J. (1978). “1/f noise in music: Music from 1/f noise”. Journal of the Acoustical Society of America 63: 258–263.

Martin Gardner’s article:

Gardner, M. (1978). “Mathematical Games—White and brown music, fractal curves and one-over-f fluctuations”. Scientific American 238: 16–32.

Robin Whittle’s discussion of the history of the algorithm on the music-dip list:

http://www.firstpr.com.au/dsp/pink-noise/

Larry Trammell’s description of the development of his “Stochastic Voss-McCartney Algorithm”:

http://www.ridgerat-tech.us/tech/pinkalg.htm

Stefan Stenzel’s “new shade of pink” algorithm on Github. You can also read his paper.

https://github.com/Stenzel/newshadeofpink

 

2 Responses to “NOISE2, a white and pink noise source in a single chip”

Leave a Reply

Your email address will not be published. Required fields are marked *