PortAudio 2.0
|
00001 00019 /* 00020 * $Id: paex_pink.c 1752 2011-09-08 03:21:55Z philburk $ 00021 * 00022 * This program uses the PortAudio Portable Audio Library. 00023 * For more information see: http://www.portaudio.com 00024 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 00025 * 00026 * Permission is hereby granted, free of charge, to any person obtaining 00027 * a copy of this software and associated documentation files 00028 * (the "Software"), to deal in the Software without restriction, 00029 * including without limitation the rights to use, copy, modify, merge, 00030 * publish, distribute, sublicense, and/or sell copies of the Software, 00031 * and to permit persons to whom the Software is furnished to do so, 00032 * subject to the following conditions: 00033 * 00034 * The above copyright notice and this permission notice shall be 00035 * included in all copies or substantial portions of the Software. 00036 * 00037 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00038 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00039 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00040 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 00041 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 00042 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00043 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00044 */ 00045 00046 /* 00047 * The text above constitutes the entire PortAudio license; however, 00048 * the PortAudio community also makes the following non-binding requests: 00049 * 00050 * Any person wishing to distribute modifications to the Software is 00051 * requested to send the modifications to the original developer so that 00052 * they can be incorporated into the canonical version. It is also 00053 * requested that these non-binding requests be included along with the 00054 * license above. 00055 */ 00056 00057 #include <stdio.h> 00058 #include <math.h> 00059 #include "portaudio.h" 00060 00061 #define PINK_MAX_RANDOM_ROWS (30) 00062 #define PINK_RANDOM_BITS (24) 00063 #define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) 00064 00065 typedef struct 00066 { 00067 long pink_Rows[PINK_MAX_RANDOM_ROWS]; 00068 long pink_RunningSum; /* Used to optimize summing of generators. */ 00069 int pink_Index; /* Incremented each sample. */ 00070 int pink_IndexMask; /* Index wrapped by ANDing with this mask. */ 00071 float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */ 00072 } 00073 PinkNoise; 00074 00075 /* Prototypes */ 00076 static unsigned long GenerateRandomNumber( void ); 00077 void InitializePinkNoise( PinkNoise *pink, int numRows ); 00078 float GeneratePinkNoise( PinkNoise *pink ); 00079 00080 /************************************************************/ 00081 /* Calculate pseudo-random 32 bit number based on linear congruential method. */ 00082 static unsigned long GenerateRandomNumber( void ) 00083 { 00084 /* Change this seed for different random sequences. */ 00085 static unsigned long randSeed = 22222; 00086 randSeed = (randSeed * 196314165) + 907633515; 00087 return randSeed; 00088 } 00089 00090 /************************************************************/ 00091 /* Setup PinkNoise structure for N rows of generators. */ 00092 void InitializePinkNoise( PinkNoise *pink, int numRows ) 00093 { 00094 int i; 00095 long pmax; 00096 pink->pink_Index = 0; 00097 pink->pink_IndexMask = (1<<numRows) - 1; 00098 /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */ 00099 pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1)); 00100 pink->pink_Scalar = 1.0f / pmax; 00101 /* Initialize rows. */ 00102 for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0; 00103 pink->pink_RunningSum = 0; 00104 } 00105 00106 #define PINK_MEASURE 00107 #ifdef PINK_MEASURE 00108 float pinkMax = -999.0; 00109 float pinkMin = 999.0; 00110 #endif 00111 00112 /* Generate Pink noise values between -1.0 and +1.0 */ 00113 float GeneratePinkNoise( PinkNoise *pink ) 00114 { 00115 long newRandom; 00116 long sum; 00117 float output; 00118 /* Increment and mask index. */ 00119 pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask; 00120 /* If index is zero, don't update any random values. */ 00121 if( pink->pink_Index != 0 ) 00122 { 00123 /* Determine how many trailing zeros in PinkIndex. */ 00124 /* This algorithm will hang if n==0 so test first. */ 00125 int numZeros = 0; 00126 int n = pink->pink_Index; 00127 while( (n & 1) == 0 ) 00128 { 00129 n = n >> 1; 00130 numZeros++; 00131 } 00132 /* Replace the indexed ROWS random value. 00133 * Subtract and add back to RunningSum instead of adding all the random 00134 * values together. Only one changes each time. 00135 */ 00136 pink->pink_RunningSum -= pink->pink_Rows[numZeros]; 00137 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; 00138 pink->pink_RunningSum += newRandom; 00139 pink->pink_Rows[numZeros] = newRandom; 00140 } 00141 00142 /* Add extra white noise value. */ 00143 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; 00144 sum = pink->pink_RunningSum + newRandom; 00145 /* Scale to range of -1.0 to 0.9999. */ 00146 output = pink->pink_Scalar * sum; 00147 #ifdef PINK_MEASURE 00148 /* Check Min/Max */ 00149 if( output > pinkMax ) pinkMax = output; 00150 else if( output < pinkMin ) pinkMin = output; 00151 #endif 00152 return output; 00153 } 00154 00155 /*******************************************************************/ 00156 #define PINK_TEST 00157 #ifdef PINK_TEST 00158 00159 /* Context for callback routine. */ 00160 typedef struct 00161 { 00162 PinkNoise leftPink; 00163 PinkNoise rightPink; 00164 unsigned int sampsToGo; 00165 } 00166 paTestData; 00167 00168 /* This routine will be called by the PortAudio engine when audio is needed. 00169 ** It may called at interrupt level on some machines so don't do anything 00170 ** that could mess up the system like calling malloc() or free(). 00171 */ 00172 static int patestCallback(const void* inputBuffer, 00173 void* outputBuffer, 00174 unsigned long framesPerBuffer, 00175 const PaStreamCallbackTimeInfo* timeInfo, 00176 PaStreamCallbackFlags statusFlags, 00177 void* userData) 00178 { 00179 int finished; 00180 int i; 00181 int numFrames; 00182 paTestData *data = (paTestData*)userData; 00183 float *out = (float*)outputBuffer; 00184 (void) inputBuffer; /* Prevent "unused variable" warnings. */ 00185 00186 /* Are we almost at end. */ 00187 if( data->sampsToGo < framesPerBuffer ) 00188 { 00189 numFrames = data->sampsToGo; 00190 finished = 1; 00191 } 00192 else 00193 { 00194 numFrames = framesPerBuffer; 00195 finished = 0; 00196 } 00197 for( i=0; i<numFrames; i++ ) 00198 { 00199 *out++ = GeneratePinkNoise( &data->leftPink ); 00200 *out++ = GeneratePinkNoise( &data->rightPink ); 00201 } 00202 data->sampsToGo -= numFrames; 00203 return finished; 00204 } 00205 00206 /*******************************************************************/ 00207 int main(void); 00208 int main(void) 00209 { 00210 PaStream* stream; 00211 PaError err; 00212 paTestData data; 00213 PaStreamParameters outputParameters; 00214 int totalSamps; 00215 static const double SR = 44100.0; 00216 static const int FPB = 2048; /* Frames per buffer: 46 ms buffers. */ 00217 00218 /* Initialize two pink noise signals with different numbers of rows. */ 00219 InitializePinkNoise( &data.leftPink, 12 ); 00220 InitializePinkNoise( &data.rightPink, 16 ); 00221 00222 /* Look at a few values. */ 00223 { 00224 int i; 00225 float pink; 00226 for( i=0; i<20; i++ ) 00227 { 00228 pink = GeneratePinkNoise( &data.leftPink ); 00229 printf("Pink = %f\n", pink ); 00230 } 00231 } 00232 00233 data.sampsToGo = totalSamps = (int)(60.0 * SR); /* Play a whole minute. */ 00234 err = Pa_Initialize(); 00235 if( err != paNoError ) goto error; 00236 00237 /* Open a stereo PortAudio stream so we can hear the result. */ 00238 outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */ 00239 if (outputParameters.device == paNoDevice) { 00240 fprintf(stderr,"Error: No default output device.\n"); 00241 goto error; 00242 } 00243 outputParameters.channelCount = 2; /* Stereo output, most likely supported. */ 00244 outputParameters.hostApiSpecificStreamInfo = NULL; 00245 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */ 00246 outputParameters.suggestedLatency = 00247 Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency; 00248 err = Pa_OpenStream(&stream, 00249 NULL, /* No input. */ 00250 &outputParameters, 00251 SR, /* Sample rate. */ 00252 FPB, /* Frames per buffer. */ 00253 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 00254 patestCallback, 00255 &data); 00256 if( err != paNoError ) goto error; 00257 00258 err = Pa_StartStream( stream ); 00259 if( err != paNoError ) goto error; 00260 00261 printf("Stereo pink noise for one minute...\n"); 00262 00263 while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100); 00264 if( err < 0 ) goto error; 00265 00266 err = Pa_CloseStream( stream ); 00267 if( err != paNoError ) goto error; 00268 #ifdef PINK_MEASURE 00269 printf("Pink min = %f, max = %f\n", pinkMin, pinkMax ); 00270 #endif 00271 Pa_Terminate(); 00272 return 0; 00273 error: 00274 Pa_Terminate(); 00275 fprintf( stderr, "An error occured while using the portaudio stream\n" ); 00276 fprintf( stderr, "Error number: %d\n", err ); 00277 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); 00278 return 0; 00279 } 00280 #endif /* PINK_TEST */