A pure white audio noise generator using an 8 pin PIC 12F675
This pages describes a simple digital noise source that produces pure white noise over the whole audio band – out to more than 40Khz, in fact. With filtering, you can derive pink noise and other colours. The circuit diagram included below was developed from the famous Polyfusion noise generator, and can produce white, pink and infra-red (low frequency) noise.
The noise generator 12F675 PIC microprocessor uses a dual LFSR algorithm which produces a pseudo-random stream of bits at over 90KHz. The bitstream shouldn’t repeat for 1428 years, but obviously I haven’t checked!
Replacement noise generator for Sequential Prophet 5 / Korg MonoPoly / Oberheim OB-X
I arranged the chip to be as close a replacement as I could for the notorious MM5837 digital noise generator which appears in early Korg MonoPolys, the Sequential Prophet 5, and the Oberheim OB-X. This chip has a very short LFSR (only 17-bit) which means that the noise it produces repeats every few seconds. This is audible, and has been described as sounding like a steam train puffing, rather than smooth white noise.
The only difference between the two chips (and one I couldn’t do anything about) is the supply voltage. This also means the new chip is quieter.
The datasheet includes some details about replacing the MM5837 in these synths.
- White noise generator PIC 12F675 ASM code
- Assembled HEX code from above file
- Circuit Diagram
- Noise Generator datasheet (includes circuit diagram and chip pinout)
A potential pitfall
The chip uses both the internal oscillator and the internal MCLR pullup. This combination causes problems with some PIC programmers. My own ICD2 clone complains that it doesn’t support it, and there are many reports online of problems with the JDM programmer. I programmed my chip successfully using the PICKit1 programmer, and it’s been reported to me that the P16pro programmer and Picallw software work too.
16 thoughts on “White Noise Source”
in your 12F675 noise chip you use a 21-bit lfsr, here’s a 23-bit one with also 9 cycles:
I took taps 23 and 14, same maximal length as 23+18 (18/14/9/5):
rlf sr2,w; get bit 14
rlf sr3,w; get bit 23
rlf temp,f; into carry
we could also shift first and take taps+1: (again, 9 cycles)
rlf sr2,w; get bit 14 (15)
xorwf sr3,w; xor witht bit 23 (24)
best, mike from switzerland
Nice work! Thanks for sharing it.
Hey Tom! Great work, as always. Would the circuit diagram given in the datasheet work at lower voltage? For example, +9, +4.5, 0 instead of +15, 0, -15. Trying to integrate it with some existing stuff without re-engineering the power system.
Yeah, it should do, as long as you’re careful about the op-amps you use.
I’d like to share two even faster maximal length LFSR’s for PIC:
31bit (taps 31,24):
15bit (taps 15,8):
There’s a flaw in the logic of the algorithm. Alternating the output between two LFSRs doesn’t “couple” them but is instead equivalent to just (somewhat poorly) mixing two independent signals. Thus you have two pseudo noises mixed together, one with a period of 21 seconds and the other with a period of 6 hours. Audibly this probably still works ok as the brain can’t track 20+ second periodicity, but you’d probably get the same or better results using just the single 31 bit LFSR.
Given that the two LFSRs are alternated, the period of even the shorter one is around 45 seconds. Not only can the brain not follow even a 20 second period of white noise, but we also know that the very best source for masking other sounds is…white noise! So even if any apparent periodicity in the shorter LFSR were perceptible, it would be totally lost with the masking effect of the longer LFSR.
Running two shorter LFSRs alternately like this enables us to increase the output sample rate. A longer 32-bit LFSR (for example) would take a few more instructions and give a lower sample rate. Another side effect of running two shorter LFSRs instead of one longer one is that we don’t hit the problem of long runs of identical samples, which starts to be significant as you get into longer LFSR lengths.
Alternating between them is a red herring.
It’s exactly equivalent to upsampling the two half-rate random streams by 2x (thus mirroring the spectrum of each separately), shifting one by one sample and then mixing them linearly together. Thus the effect as far as repetition goes is the same as just doubling the period of the shorter LFSR, not multiplying or even adding the periods. If there is an audible pattern in the 21-bit LFSR, it will be there even after you add other signal on top of that 21-bit LFSR noise.
This is why switching to having just a single 31-bit LFSR, instead of having both 31-bit & 21-bit, would be beneficial (because 31 > 21*2). The code doesn’t seem to have a meaningful difference in cycle count between the 21 & 31-bit ones, so using just the 31-bit alone would have no practical effect on the samplerate but would remove the 21-bit repetition cycle entirely.
I agree that alternating the two LFSRs doesn’t improve the length, which was my motivation for doing it when I wrote this code some ten or fifteen years ago. Maybe it does make sense to go simpler and just have the single 31-bit generator, but there’s certainly no audible cycle. I was still learning!
@Antti – I’m not convinced that you’re right about the output signal being a mix of two independent signals. The reason is that the signals aren’t mixed, but are multiplexed in the time domain. That’s an important difference. I’ll admit that you can achieve a similar result with a single LFSR of appropriate length, but I think that the length of the LFSR that would provide equivalent pseudo-randomness might be the sum of the two.
However, my main concern is that the LFSR output contains long sequences where the output doesn’t change. The longest such sequence is the LFSR length, though for obvious reasons the longest string of 0s is one less than the longest string of 1s These sequences appear to be what gives the MM5837 its regular “chuffing” artifact. Any “full range” LFSR will have the same artifacts, although with a longer LFSR the artifact might not happen until a long time in the future.
I think Antti is right. If you took the two outputs, added blank samples in-between each random sample, and then offset one output by a single sample, you can see that you can create the final output string by simply summing the two outputs together. The important question is whether this matters. I don’t think it does. Neither register has an audible repeat, and when they are mixed, this is masked even more. The resulting mixture doesn’t repeat for years.
It’s true that you get sequences when the output doesn’t change, but that’s only really a problem with much bigger registers and much lower sample rates. A sequence of 32 ones at 100KHz only lasts a third of a millisecond. That’s just a random “speckle”.
I’m still not convinced. The sequence of bits that you get is A0,B0, A1, B1, A2, B2,… The A and B sequences repeat at different intervals. Lets take a trivial example: A is generated by a 2-bit LFSR (length 3) and B is generated by a 3-bit LFSR (length 7). So you get:
The length of the resulting sequence is 3 * 7 * 2. However, that only happens when len(A) and len(B) are relatively prime. In general, the resulting sequence has a length of 2*LCM(len(A), len(B)). For example: 3-bit and 6-bit LFSRs combined like this result in a shorter sequence because 63 divides exactly by 7
In your case (assuming that your LFSRs are maximum length) the two lengths are 2^31-1 and 2^21-1. These numbers are relatively prime, so the length of the resulting sequence is (2^31-1)*(2^21-1)*2, which is a bit less than 2^53. Your 23-bit LFSR is also relatively prime, resulting in an effective length of a bit less than 2^55.
(LCM –> lowest common multiple)
Yes, I agree about this, but that’s not Antti’s point. The point is whether that longer sequence removes repetition. Antti argues that it doesn’t, because effectively what we’ve done is overlay two cycling sequences on top of each other. And that part is true as well, we have.
Of course, the whole thing is extremely academic, since you (a) can’t hear the repetition in either of the sub-sequences and (b) white noise is extremely good at masking, so the idea that you’d be able to pick out the repetition in one noise sequence placed against a background of another white noise sequence is totally crazy.
For these reasons, I’ve never felt the need to alter it. The noise signal you ultimately get doesn’t repeat for many years because of the two cycles not lining up as you suggest, even if there are groups of sub-samples within it that repeat on a shorter (but still not audible) cycle. Ultimately it works well for the purpose for which I designed it (audio noise) so I’m leaving it alone.
I’m not suggesting that you should redesign your noise generator. The motto “if it ain’t broke, don’t fix it” applies.
In any case, my admittedly mathematical analysis of the bitstream shows that your sequence is definitely longer than either of the LFSR sequences that generate it. It may be that your resulting system is non-linear.
I’ve been looking into the sequences a bit more, and I’ve spotted an interesting characteristic: by combining two sequences, you reduce the longest constant run, compared with a similar LFSR. In the combined sequence, the longest run is twice the longest run of the shorter LFSR, plus one – in your case, 21*2+1 = 42. Whereas, for a LFSR of similar sequence length, the longest run in your case would be 52 or 53.
The other interesting characteristic is that the combination has more 1s than 0s in the output. Every max-length LFSR has one more 1 than 0 in the output; the combination of two LFSRs with sequence lengths j and k has j+k more 1s than 0s in the output.
What effect this has on the noise is beyond my capabilities to analyse, except that it results in a DC offset in the signal (which is irrelevant in most cases).
From the software side, the combination may be more efficient to compute than a single LFSR of equivalent length, especially on an 8-bit microcontroller. A single LFSR needs to shift 6 bytes per output bit, whereas a double LFSR only needs to shift 4 bytes. If you write in C, you end up with an 8-byte shift if your compiler supports 64-bit integers.
Disclaimer: I’m a mathematician by education and a software developer by profession. I’m a rank beginner in the world of analogue electronics, but that doesn’t discourage me from trying. 😉
If you’re interested: I hacked a noise generator based on an ATtiny13 running at 9.6 MHz (fastest internal clock). It’s written in C and uses a 64-bit LFSR. The output bit rate is about 240 k bits per second, resulting in a maximum square wave frequency of about 120 kHz. Subjectively, the hiss is nice and regular. If you’re interested in playing around: https://gitlab.com/TheLancashireman/synthesiser/-/tree/master/noise-generator
“If it ain’t broke, don’t fix it” is exactly what I thought!
Your analysis of why I went with the interlaced LFSRs instead of one long one is spot on, too. It’s generally quicker to process a shorter LFSR, and I specifically chose tap sequences that minimised awkward bitshifting operations. So by interlacing two shorter LFSRs, I can get a new random bit out (for example) every ten instructions instead of every fourteen, while simultaneously increasing the length of the random sequence. It seemed like a good idea at the time.
Oops – a typo. 21*2+1 is 43, of course.