paex_pink.c (9919B)
1 /** @file paex_pink.c 2 @ingroup examples_src 3 @brief Generate Pink Noise using Gardner method. 4 5 Optimization suggested by James McCartney uses a tree 6 to select which random value to replace. 7 <pre> 8 x x x x x x x x x x x x x x x x 9 x x x x x x x x 10 x x x x 11 x x 12 x 13 </pre> 14 Tree is generated by counting trailing zeros in an increasing index. 15 When the index is zero, no random number is selected. 16 17 @author Phil Burk http://www.softsynth.com 18 */ 19 /* 20 * $Id$ 21 * 22 * This program uses the PortAudio Portable Audio Library. 23 * For more information see: http://www.portaudio.com 24 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 25 * 26 * Permission is hereby granted, free of charge, to any person obtaining 27 * a copy of this software and associated documentation files 28 * (the "Software"), to deal in the Software without restriction, 29 * including without limitation the rights to use, copy, modify, merge, 30 * publish, distribute, sublicense, and/or sell copies of the Software, 31 * and to permit persons to whom the Software is furnished to do so, 32 * subject to the following conditions: 33 * 34 * The above copyright notice and this permission notice shall be 35 * included in all copies or substantial portions of the Software. 36 * 37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 38 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 39 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 40 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 41 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 42 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 43 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 44 */ 45 46 /* 47 * The text above constitutes the entire PortAudio license; however, 48 * the PortAudio community also makes the following non-binding requests: 49 * 50 * Any person wishing to distribute modifications to the Software is 51 * requested to send the modifications to the original developer so that 52 * they can be incorporated into the canonical version. It is also 53 * requested that these non-binding requests be included along with the 54 * license above. 55 */ 56 57 #include <stdio.h> 58 #include <math.h> 59 #include "portaudio.h" 60 61 #define PINK_MAX_RANDOM_ROWS (30) 62 #define PINK_RANDOM_BITS (24) 63 #define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) 64 65 typedef struct 66 { 67 long pink_Rows[PINK_MAX_RANDOM_ROWS]; 68 long pink_RunningSum; /* Used to optimize summing of generators. */ 69 int pink_Index; /* Incremented each sample. */ 70 int pink_IndexMask; /* Index wrapped by ANDing with this mask. */ 71 float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */ 72 } 73 PinkNoise; 74 75 /* Prototypes */ 76 static unsigned long GenerateRandomNumber( void ); 77 void InitializePinkNoise( PinkNoise *pink, int numRows ); 78 float GeneratePinkNoise( PinkNoise *pink ); 79 80 /************************************************************/ 81 /* Calculate pseudo-random 32 bit number based on linear congruential method. */ 82 static unsigned long GenerateRandomNumber( void ) 83 { 84 /* Change this seed for different random sequences. */ 85 static unsigned long randSeed = 22222; 86 randSeed = (randSeed * 196314165) + 907633515; 87 return randSeed; 88 } 89 90 /************************************************************/ 91 /* Setup PinkNoise structure for N rows of generators. */ 92 void InitializePinkNoise( PinkNoise *pink, int numRows ) 93 { 94 int i; 95 long pmax; 96 pink->pink_Index = 0; 97 pink->pink_IndexMask = (1<<numRows) - 1; 98 /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */ 99 pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1)); 100 pink->pink_Scalar = 1.0f / pmax; 101 /* Initialize rows. */ 102 for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0; 103 pink->pink_RunningSum = 0; 104 } 105 106 #define PINK_MEASURE 107 #ifdef PINK_MEASURE 108 float pinkMax = -999.0; 109 float pinkMin = 999.0; 110 #endif 111 112 /* Generate Pink noise values between -1.0 and +1.0 */ 113 float GeneratePinkNoise( PinkNoise *pink ) 114 { 115 long newRandom; 116 long sum; 117 float output; 118 /* Increment and mask index. */ 119 pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask; 120 /* If index is zero, don't update any random values. */ 121 if( pink->pink_Index != 0 ) 122 { 123 /* Determine how many trailing zeros in PinkIndex. */ 124 /* This algorithm will hang if n==0 so test first. */ 125 int numZeros = 0; 126 int n = pink->pink_Index; 127 while( (n & 1) == 0 ) 128 { 129 n = n >> 1; 130 numZeros++; 131 } 132 /* Replace the indexed ROWS random value. 133 * Subtract and add back to RunningSum instead of adding all the random 134 * values together. Only one changes each time. 135 */ 136 pink->pink_RunningSum -= pink->pink_Rows[numZeros]; 137 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; 138 pink->pink_RunningSum += newRandom; 139 pink->pink_Rows[numZeros] = newRandom; 140 } 141 142 /* Add extra white noise value. */ 143 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; 144 sum = pink->pink_RunningSum + newRandom; 145 /* Scale to range of -1.0 to 0.9999. */ 146 output = pink->pink_Scalar * sum; 147 #ifdef PINK_MEASURE 148 /* Check Min/Max */ 149 if( output > pinkMax ) pinkMax = output; 150 else if( output < pinkMin ) pinkMin = output; 151 #endif 152 return output; 153 } 154 155 /*******************************************************************/ 156 #define PINK_TEST 157 #ifdef PINK_TEST 158 159 /* Context for callback routine. */ 160 typedef struct 161 { 162 PinkNoise leftPink; 163 PinkNoise rightPink; 164 unsigned int sampsToGo; 165 } 166 paTestData; 167 168 /* This routine will be called by the PortAudio engine when audio is needed. 169 ** It may called at interrupt level on some machines so don't do anything 170 ** that could mess up the system like calling malloc() or free(). 171 */ 172 static int patestCallback(const void* inputBuffer, 173 void* outputBuffer, 174 unsigned long framesPerBuffer, 175 const PaStreamCallbackTimeInfo* timeInfo, 176 PaStreamCallbackFlags statusFlags, 177 void* userData) 178 { 179 int finished; 180 int i; 181 int numFrames; 182 paTestData *data = (paTestData*)userData; 183 float *out = (float*)outputBuffer; 184 (void) inputBuffer; /* Prevent "unused variable" warnings. */ 185 186 /* Are we almost at end. */ 187 if( data->sampsToGo < framesPerBuffer ) 188 { 189 numFrames = data->sampsToGo; 190 finished = 1; 191 } 192 else 193 { 194 numFrames = framesPerBuffer; 195 finished = 0; 196 } 197 for( i=0; i<numFrames; i++ ) 198 { 199 *out++ = GeneratePinkNoise( &data->leftPink ); 200 *out++ = GeneratePinkNoise( &data->rightPink ); 201 } 202 data->sampsToGo -= numFrames; 203 return finished; 204 } 205 206 /*******************************************************************/ 207 int main(void); 208 int main(void) 209 { 210 PaStream* stream; 211 PaError err; 212 paTestData data; 213 PaStreamParameters outputParameters; 214 int totalSamps; 215 static const double SR = 44100.0; 216 static const int FPB = 2048; /* Frames per buffer: 46 ms buffers. */ 217 218 /* Initialize two pink noise signals with different numbers of rows. */ 219 InitializePinkNoise( &data.leftPink, 12 ); 220 InitializePinkNoise( &data.rightPink, 16 ); 221 222 /* Look at a few values. */ 223 { 224 int i; 225 float pink; 226 for( i=0; i<20; i++ ) 227 { 228 pink = GeneratePinkNoise( &data.leftPink ); 229 printf("Pink = %f\n", pink ); 230 } 231 } 232 233 data.sampsToGo = totalSamps = (int)(60.0 * SR); /* Play a whole minute. */ 234 err = Pa_Initialize(); 235 if( err != paNoError ) goto error; 236 237 /* Open a stereo PortAudio stream so we can hear the result. */ 238 outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */ 239 if (outputParameters.device == paNoDevice) { 240 fprintf(stderr,"Error: No default output device.\n"); 241 goto error; 242 } 243 outputParameters.channelCount = 2; /* Stereo output, most likely supported. */ 244 outputParameters.hostApiSpecificStreamInfo = NULL; 245 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */ 246 outputParameters.suggestedLatency = 247 Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; 248 err = Pa_OpenStream(&stream, 249 NULL, /* No input. */ 250 &outputParameters, 251 SR, /* Sample rate. */ 252 FPB, /* Frames per buffer. */ 253 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 254 patestCallback, 255 &data); 256 if( err != paNoError ) goto error; 257 258 err = Pa_StartStream( stream ); 259 if( err != paNoError ) goto error; 260 261 printf("Stereo pink noise for one minute...\n"); 262 263 while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); 264 if( err < 0 ) goto error; 265 266 err = Pa_CloseStream( stream ); 267 if( err != paNoError ) goto error; 268 #ifdef PINK_MEASURE 269 printf("Pink min = %f, max = %f\n", pinkMin, pinkMax ); 270 #endif 271 Pa_Terminate(); 272 return 0; 273 error: 274 Pa_Terminate(); 275 fprintf( stderr, "An error occured while using the portaudio stream\n" ); 276 fprintf( stderr, "Error number: %d\n", err ); 277 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); 278 return 0; 279 } 280 #endif /* PINK_TEST */