PortAudio  2.0
paex_pink.c
Go to the documentation of this file.
1 
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 */