Is a pink noise generator possible?

I am new to the forum and pretty new to Arduino in general. In my searching through the googlenet :smiley: I have not found anything on pink noise generation using an Arduino. So, my question is, is it possible and if possible, how?

That would depend on the bandwidth, but I would say there is no point in using Arduino. The standard Arduino is too slow and limited to do much in the way of even audio signal processing.

Lots of randomness, lots of different frequencies transposed on one another, lots of high frequencies - doesn't sound like an Arduino job at all.
There must be plenty of cheap devices that can do just that in hardware based upon amplifying thermal noise or so. A quick Google search turned up many simple looking circuits for producing noise.

Apart from an hardware solution, It seems that one can generate pink noise from a white noise generator :

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

An arduino Due can be a white noise generator throughout its True Random Number Generator (TRNG).

I would highly suggest on using something else to make the job easier.

Hardik... :slight_smile:

The audio library for the Teensy 3.2, 3.5 and 3.6 has a pinknoise generator object. I haven't tried it but it might be worth looking into. The audio library source is open.

Pete

JustLookin:
I am new to the forum and pretty new to Arduino in general. In my searching through the googlenet :smiley: I have not found anything on pink noise generation using an Arduino. So, my question is, is it possible and if possible, how?

Bit late to the party, but recently I've been looking at pink noise, and I'm glad to say yes, you can
generate pink noise even on a lowly ATmega328.

There's a very cunning algorithm due to Stefan Stenzel: http://http://stenzel.waldorfmusic.de/post/pink/ which pulls several tricks to produce very spectrally
accurate pink noise using only integer operations and much cleverness.

Its actually the one used in the Teensy library referenced above in fact, although that version isn't optimized
for code size and likely wouldn't fit on an Uno.

So I've coded proof-of-concept version that is much smaller.
http://sphinx.mythic-beasts.com/~markt/stenzel_pink.zip

Its a zip as there's a header file too, but here's the main code to give a flavour - it uses 62.5kHz PWM on pin 11 or 10 (board dependent), being the timer2 A channel pin. Add a DC-blocking cap and RC filter to the relevant pin and instant pink noise source.

/*
  Arduino pink noise generator -- Mark Tillotson, 2018-12-09

  Works for Uno/Pro Mini/Mega, using pin 11 or 10 depending on board (the one driven by timer2 output A)

  Uses the highly efficient algorithm from Stenzel, see:
     http://stenzel.waldorfmusic.de/post/pink/
     https://github.com/Stenzel/newshadeofpink

  Note his request that if used in a commercial product, he is sent one of the units...

 */

// define the OCR2A timer output pin, depends on which board - only support ATmega328 and ATmega1280/2560
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#  define output_pin 10
#else
#  define output_pin 11
#endif


void setup() 
{
  build_revtab() ;
  analogWrite (output_pin, 125) ;  // enable the output pin and its timer, set to 50%
  TCCR2A = 0xB3 ;  // configure as fast 8-bit PWM (mode 011, clock prescale = 1)
  TCCR2B = 0x01 ;
  TIMSK2 = 0x01;   // timer2 overflow interrupt enabled every 256 cycles (62.5 kHz for a 16MHz ATmega)
}

void loop()  // nothing here, all driven by ISR
{
}

volatile byte outsampl = 0x80 ;  // cache last sample for ISR
volatile byte phase = 0 ;  // oscillates between 0 <-> 1 for each interrupt
volatile int error = 0 ;   // trick to reduce quantization noise

// output the latest sample, regenerate new sample every two interrupts (since it takes longer than 16us)
// Thus PWM is 62.5 kHz, actual sample rate 31.25 kSPS, so a hardware anti-aliasing filter with cutoff below
// 15kHz is recommended
// Since only 8 bit samples are output, the quantization noise probably pollutes the higher frequencies
ISR (TIMER2_OVF_vect)
{
  if (phase == 0)
  {
    OCR2A = outsampl + 0x80 ;    // PWM output, offset is 50% duty cycle for 8 bit timer2
    int samp = stenzel_pink_sample () + error ;
    outsampl = samp >> 8 ;
    error = samp - (outsample<<8) ;  // recalculate error
  }
  phase = 1 - phase ;
}

byte bitrev (byte a)  // reverse a 6 bit value
{
  byte r = 0 ;
  for (byte i = 0 ; i < 6 ; i++)
  {
    r <<= 1 ;
    r |= a & 1 ;
    a >>= 1 ;
  }
  return r ;
}

byte bitrevtab[0x40];  // caches bit reversal at cost of 64 bytes RAM

void build_revtab()
{
  for (byte i = 0 ; i < 0x40 ; i++)
    bitrevtab [i] = bitrev (i) ;
}


// Stenzel "new shade of pink" algorithm state

long lfsr = 0x5EED41F5 ;  // feedback RNG
int inc = 0xCCC ;         // incrementing bit per octave
int dec = 0xCCC ;         // decrementing bit per octave
int accum = 0 ;           // accumulator
int counter = 0xAAA ;     // used to chose bit position

void seed_stenzel (long seed)
{
  lfsr += seed ;
}

#include "firtables.h"    // 12 tap FIR filter using 2 lookups on last 12 random bits

int stenzel_pink_sample ()
{
  int bit = lfsr < 0 ? 0xFFF : 0x000 ;  // step the RNG, keeping latest bit as a mask in variable 'bit'
  lfsr += lfsr ;
  if (bit)
    lfsr ^= 0x46000001L ;
    
  counter += 1;
  counter &= 0xFFF ;                    // step counter
  int bitmask = counter & -counter ;    // get lowest bit
  bitmask = bitrevtab [bitmask & 0x3F] ;// reverse using 2 lookups
  bitmask <<= 6 ;
  bitmask |= bitrevtab [bitmask >> 6] ;
  
  dec &= ~bitmask ;         // clear the dec bit at position given by bitmask
  dec |= inc & bitmask ;    // copy the bit at that position from inc to dec (cancelling the effect of inc)
  inc ^= bit & bitmask ;    // depending on latest random bit, perhaps flip the inc bit at that position
  accum += inc - dec ;      // difference calculates the linear interpolations for all 12 generators simultaneously
  int result = accum ;

  int twelve_bits = ((int) lfsr) & 0xFFF ;   // extract last 12 random bits generated and filter to correct spectral
  result += highfirtab [twelve_bits >> 6] ;  // power density at the higher end of spectrum
  result += lowfirtab  [twelve_bits & 0x3F] ;
  
  return result ;
}
1 Like