/*
  patest_pink.c

  generate Pink Noise using Gardner method.
  Optimization suggested by James McCartney uses a tree
  to select which random value to replace.

  x x x x x x x x x x x x x x x x 
  x   x   x   x   x   x   x   x   
  x       x       x       x       
  x               x               
  x                               

  Tree is generated by counting trailing zeros in an increasing index.
  When the index is zero, no random number is selected.

  This program uses the Portable Audio library which is under development.
  For more information see:   http://www.audiomulch.com/portaudio/

  Author: Phil Burk, http://www.softsynth.com
	
  Revision History:

  Copyleft 1999 Phil Burk - No rights reserved.
*/

#include "aconfig.h"
#include <stdio.h>
#include <math.h>
#include "pink.h"

/************************************************************/
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
static unsigned long generate_random_number( void )
{
    static unsigned long rand_seed = 22222;  /* Change this for different random sequences. */
    rand_seed = (rand_seed * 196314165) + 907633515;
    return rand_seed;
}

/* Setup PinkNoise structure for N rows of generators. */
void initialize_pink_noise( pink_noise_t *pink, int num_rows )
{
    int i;
    long pmax;
    pink->pink_index = 0;
    pink->pink_index_mask = (1<<num_rows) - 1;
/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
    pmax = (num_rows + 1) * (1<<(PINK_RANDOM_BITS-1));
    pink->pink_scalar = 1.0f / pmax;
/* Initialize rows. */
    for( i=0; i<num_rows; i++ ) pink->pink_rows[i] = 0;
    pink->pink_running_sum = 0;
}

/* generate Pink noise values between -1.0 and +1.0 */
float generate_pink_noise_sample( pink_noise_t *pink )
{
    long new_random;
    long sum;
    float output;

/* Increment and mask index. */
    pink->pink_index = (pink->pink_index + 1) & pink->pink_index_mask;

/* If index is zero, don't update any random values. */
    if( pink->pink_index != 0 )
    {
	/* Determine how many trailing zeros in PinkIndex. */
	/* This algorithm will hang if n==0 so test first. */
	int num_zeros = 0;
	int n = pink->pink_index;
	while( (n & 1) == 0 )
	{
	    n = n >> 1;
	    num_zeros++;
	}

	/* Replace the indexed ROWS random value.
	 * Subtract and add back to Running_sum instead of adding all the random
	 * values together. Only one changes each time.
	 */
	pink->pink_running_sum -= pink->pink_rows[num_zeros];
	new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT;
	pink->pink_running_sum += new_random;
	pink->pink_rows[num_zeros] = new_random;
    }
	
/* Add extra white noise value. */
    new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT;
    sum = pink->pink_running_sum + new_random;

/* Scale to range of -1.0 to 0.9999. */
    output = pink->pink_scalar * sum;

    return output;
}
