annotate src/portaudio_20140130/examples/paex_ocean_shore.c @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 7ddb4fc30dac
children
rev   line source
Chris@39 1 /** @file paex_ocean_shore.c
Chris@39 2 @ingroup examples_src
Chris@39 3 @brief Generate Pink Noise using Gardner method, and make "waves". Provides an example of how to
Chris@39 4 post stuff to/from the audio callback using lock-free FIFOs implemented by the PA ringbuffer.
Chris@39 5
Chris@39 6 Optimization suggested by James McCartney uses a tree
Chris@39 7 to select which random value to replace.
Chris@39 8 <pre>
Chris@39 9 x x x x x x x x x x x x x x x x
Chris@39 10 x x x x x x x x
Chris@39 11 x x x x
Chris@39 12 x x
Chris@39 13 x
Chris@39 14 </pre>
Chris@39 15 Tree is generated by counting trailing zeros in an increasing index.
Chris@39 16 When the index is zero, no random number is selected.
Chris@39 17
Chris@39 18 @author Phil Burk http://www.softsynth.com
Chris@39 19 Robert Bielik
Chris@39 20 */
Chris@39 21 /*
Chris@39 22 * $Id: paex_ocean_shore.c 1816 2012-02-22 12:20:26Z robiwan $
Chris@39 23 *
Chris@39 24 * This program uses the PortAudio Portable Audio Library.
Chris@39 25 * For more information see: http://www.portaudio.com
Chris@39 26 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
Chris@39 27 *
Chris@39 28 * Permission is hereby granted, free of charge, to any person obtaining
Chris@39 29 * a copy of this software and associated documentation files
Chris@39 30 * (the "Software"), to deal in the Software without restriction,
Chris@39 31 * including without limitation the rights to use, copy, modify, merge,
Chris@39 32 * publish, distribute, sublicense, and/or sell copies of the Software,
Chris@39 33 * and to permit persons to whom the Software is furnished to do so,
Chris@39 34 * subject to the following conditions:
Chris@39 35 *
Chris@39 36 * The above copyright notice and this permission notice shall be
Chris@39 37 * included in all copies or substantial portions of the Software.
Chris@39 38 *
Chris@39 39 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@39 40 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@39 41 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
Chris@39 42 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
Chris@39 43 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@39 44 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@39 45 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@39 46 */
Chris@39 47
Chris@39 48 /*
Chris@39 49 * The text above constitutes the entire PortAudio license; however,
Chris@39 50 * the PortAudio community also makes the following non-binding requests:
Chris@39 51 *
Chris@39 52 * Any person wishing to distribute modifications to the Software is
Chris@39 53 * requested to send the modifications to the original developer so that
Chris@39 54 * they can be incorporated into the canonical version. It is also
Chris@39 55 * requested that these non-binding requests be included along with the
Chris@39 56 * license above.
Chris@39 57 */
Chris@39 58
Chris@39 59 #include <stdio.h>
Chris@39 60 #include <stdlib.h>
Chris@39 61 #include <string.h>
Chris@39 62 #include <math.h>
Chris@39 63 #include <time.h>
Chris@39 64
Chris@39 65 #include "portaudio.h"
Chris@39 66 #include "pa_ringbuffer.h"
Chris@39 67 #include "pa_util.h"
Chris@39 68
Chris@39 69 #define PINK_MAX_RANDOM_ROWS (30)
Chris@39 70 #define PINK_RANDOM_BITS (24)
Chris@39 71 #define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
Chris@39 72
Chris@39 73 typedef struct
Chris@39 74 {
Chris@39 75 long pink_Rows[PINK_MAX_RANDOM_ROWS];
Chris@39 76 long pink_RunningSum; /* Used to optimize summing of generators. */
Chris@39 77 int pink_Index; /* Incremented each sample. */
Chris@39 78 int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
Chris@39 79 float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
Chris@39 80 }
Chris@39 81 PinkNoise;
Chris@39 82
Chris@39 83 typedef struct
Chris@39 84 {
Chris@39 85 float bq_b0;
Chris@39 86 float bq_b1;
Chris@39 87 float bq_b2;
Chris@39 88 float bq_a1;
Chris@39 89 float bq_a2;
Chris@39 90 } BiQuad;
Chris@39 91
Chris@39 92 typedef enum
Chris@39 93 {
Chris@39 94 State_kAttack,
Chris@39 95 State_kPreDecay,
Chris@39 96 State_kDecay,
Chris@39 97 State_kCnt,
Chris@39 98 } EnvState;
Chris@39 99
Chris@39 100 typedef struct
Chris@39 101 {
Chris@39 102 PinkNoise wave_left;
Chris@39 103 PinkNoise wave_right;
Chris@39 104
Chris@39 105 BiQuad wave_bq_coeffs;
Chris@39 106 float wave_bq_left[2];
Chris@39 107 float wave_bq_right[2];
Chris@39 108
Chris@39 109 EnvState wave_envelope_state;
Chris@39 110 float wave_envelope_level;
Chris@39 111 float wave_envelope_max_level;
Chris@39 112 float wave_pan_left;
Chris@39 113 float wave_pan_right;
Chris@39 114 float wave_attack_incr;
Chris@39 115 float wave_decay_incr;
Chris@39 116
Chris@39 117 } OceanWave;
Chris@39 118
Chris@39 119 /* Prototypes */
Chris@39 120 static unsigned long GenerateRandomNumber( void );
Chris@39 121 void InitializePinkNoise( PinkNoise *pink, int numRows );
Chris@39 122 float GeneratePinkNoise( PinkNoise *pink );
Chris@39 123 unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames);
Chris@39 124
Chris@39 125 /************************************************************/
Chris@39 126 /* Calculate pseudo-random 32 bit number based on linear congruential method. */
Chris@39 127 static unsigned long GenerateRandomNumber( void )
Chris@39 128 {
Chris@39 129 /* Change this seed for different random sequences. */
Chris@39 130 static unsigned long randSeed = 22222;
Chris@39 131 randSeed = (randSeed * 196314165) + 907633515;
Chris@39 132 return randSeed;
Chris@39 133 }
Chris@39 134
Chris@39 135 /************************************************************/
Chris@39 136 /* Setup PinkNoise structure for N rows of generators. */
Chris@39 137 void InitializePinkNoise( PinkNoise *pink, int numRows )
Chris@39 138 {
Chris@39 139 int i;
Chris@39 140 long pmax;
Chris@39 141 pink->pink_Index = 0;
Chris@39 142 pink->pink_IndexMask = (1<<numRows) - 1;
Chris@39 143 /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
Chris@39 144 pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
Chris@39 145 pink->pink_Scalar = 1.0f / pmax;
Chris@39 146 /* Initialize rows. */
Chris@39 147 for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
Chris@39 148 pink->pink_RunningSum = 0;
Chris@39 149 }
Chris@39 150
Chris@39 151 /* Generate Pink noise values between -1.0 and +1.0 */
Chris@39 152 float GeneratePinkNoise( PinkNoise *pink )
Chris@39 153 {
Chris@39 154 long newRandom;
Chris@39 155 long sum;
Chris@39 156 float output;
Chris@39 157 /* Increment and mask index. */
Chris@39 158 pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
Chris@39 159 /* If index is zero, don't update any random values. */
Chris@39 160 if( pink->pink_Index != 0 )
Chris@39 161 {
Chris@39 162 /* Determine how many trailing zeros in PinkIndex. */
Chris@39 163 /* This algorithm will hang if n==0 so test first. */
Chris@39 164 int numZeros = 0;
Chris@39 165 int n = pink->pink_Index;
Chris@39 166 while( (n & 1) == 0 )
Chris@39 167 {
Chris@39 168 n = n >> 1;
Chris@39 169 numZeros++;
Chris@39 170 }
Chris@39 171 /* Replace the indexed ROWS random value.
Chris@39 172 * Subtract and add back to RunningSum instead of adding all the random
Chris@39 173 * values together. Only one changes each time.
Chris@39 174 */
Chris@39 175 pink->pink_RunningSum -= pink->pink_Rows[numZeros];
Chris@39 176 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
Chris@39 177 pink->pink_RunningSum += newRandom;
Chris@39 178 pink->pink_Rows[numZeros] = newRandom;
Chris@39 179 }
Chris@39 180
Chris@39 181 /* Add extra white noise value. */
Chris@39 182 newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
Chris@39 183 sum = pink->pink_RunningSum + newRandom;
Chris@39 184 /* Scale to range of -1.0 to 0.9999. */
Chris@39 185 output = pink->pink_Scalar * sum;
Chris@39 186 return output;
Chris@39 187 }
Chris@39 188
Chris@39 189 float ProcessBiquad(const BiQuad* coeffs, float* memory, float input)
Chris@39 190 {
Chris@39 191 float w = input - coeffs->bq_a1 * memory[0] - coeffs->bq_a2 * memory[1];
Chris@39 192 float out = coeffs->bq_b1 * memory[0] + coeffs->bq_b2 * memory[1] + coeffs->bq_b0 * w;
Chris@39 193 memory[1] = memory[0];
Chris@39 194 memory[0] = w;
Chris@39 195 return out;
Chris@39 196 }
Chris@39 197
Chris@39 198 static const float one_over_2Q_LP = 0.3f;
Chris@39 199 static const float one_over_2Q_HP = 1.0f;
Chris@39 200
Chris@39 201 unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames )
Chris@39 202 {
Chris@39 203 unsigned retval=0,i;
Chris@39 204 float targetLevel, levelIncr, currentLevel;
Chris@39 205 switch (wave->wave_envelope_state)
Chris@39 206 {
Chris@39 207 case State_kAttack:
Chris@39 208 targetLevel = noOfFrames * wave->wave_attack_incr + wave->wave_envelope_level;
Chris@39 209 if (targetLevel >= wave->wave_envelope_max_level)
Chris@39 210 {
Chris@39 211 /* Go to decay state */
Chris@39 212 wave->wave_envelope_state = State_kPreDecay;
Chris@39 213 targetLevel = wave->wave_envelope_max_level;
Chris@39 214 }
Chris@39 215 /* Calculate lowpass biquad coeffs
Chris@39 216
Chris@39 217 alpha = sin(w0)/(2*Q)
Chris@39 218
Chris@39 219 b0 = (1 - cos(w0))/2
Chris@39 220 b1 = 1 - cos(w0)
Chris@39 221 b2 = (1 - cos(w0))/2
Chris@39 222 a0 = 1 + alpha
Chris@39 223 a1 = -2*cos(w0)
Chris@39 224 a2 = 1 - alpha
Chris@39 225
Chris@39 226 w0 = [0 - pi[
Chris@39 227 */
Chris@39 228 {
Chris@39 229 const float w0 = 3.141592654f * targetLevel / wave->wave_envelope_max_level;
Chris@39 230 const float alpha = sinf(w0) * one_over_2Q_LP;
Chris@39 231 const float cosw0 = cosf(w0);
Chris@39 232 const float a0_fact = 1.0f / (1.0f + alpha);
Chris@39 233 wave->wave_bq_coeffs.bq_b1 = (1.0f - cosw0) * a0_fact;
Chris@39 234 wave->wave_bq_coeffs.bq_b0 = wave->wave_bq_coeffs.bq_b1 * 0.5f;
Chris@39 235 wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
Chris@39 236 wave->wave_bq_coeffs.bq_a2 = (1.0f - alpha) * a0_fact;
Chris@39 237 wave->wave_bq_coeffs.bq_a1 = -2.0f * cosw0 * a0_fact;
Chris@39 238 }
Chris@39 239 break;
Chris@39 240
Chris@39 241 case State_kPreDecay:
Chris@39 242 /* Reset biquad state */
Chris@39 243 memset(wave->wave_bq_left, 0, 2 * sizeof(float));
Chris@39 244 memset(wave->wave_bq_right, 0, 2 * sizeof(float));
Chris@39 245 wave->wave_envelope_state = State_kDecay;
Chris@39 246
Chris@39 247 /* Deliberate fall-through */
Chris@39 248
Chris@39 249 case State_kDecay:
Chris@39 250 targetLevel = noOfFrames * wave->wave_decay_incr + wave->wave_envelope_level;
Chris@39 251 if (targetLevel < 0.001f)
Chris@39 252 {
Chris@39 253 /* < -60 dB, we're done */
Chris@39 254 wave->wave_envelope_state = 3;
Chris@39 255 retval = 1;
Chris@39 256 }
Chris@39 257 /* Calculate highpass biquad coeffs
Chris@39 258
Chris@39 259 alpha = sin(w0)/(2*Q)
Chris@39 260
Chris@39 261 b0 = (1 + cos(w0))/2
Chris@39 262 b1 = -(1 + cos(w0))
Chris@39 263 b2 = (1 + cos(w0))/2
Chris@39 264 a0 = 1 + alpha
Chris@39 265 a1 = -2*cos(w0)
Chris@39 266 a2 = 1 - alpha
Chris@39 267
Chris@39 268 w0 = [0 - pi/2[
Chris@39 269 */
Chris@39 270 {
Chris@39 271 const float v = targetLevel / wave->wave_envelope_max_level;
Chris@39 272 const float w0 = 1.5707963f * (1.0f - (v*v));
Chris@39 273 const float alpha = sinf(w0) * one_over_2Q_HP;
Chris@39 274 const float cosw0 = cosf(w0);
Chris@39 275 const float a0_fact = 1.0f / (1.0f + alpha);
Chris@39 276 wave->wave_bq_coeffs.bq_b1 = (float)(- (1 + cosw0) * a0_fact);
Chris@39 277 wave->wave_bq_coeffs.bq_b0 = -wave->wave_bq_coeffs.bq_b1 * 0.5f;
Chris@39 278 wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
Chris@39 279 wave->wave_bq_coeffs.bq_a2 = (float)((1.0 - alpha) * a0_fact);
Chris@39 280 wave->wave_bq_coeffs.bq_a1 = (float)(-2.0 * cosw0 * a0_fact);
Chris@39 281 }
Chris@39 282 break;
Chris@39 283
Chris@39 284 default:
Chris@39 285 break;
Chris@39 286 }
Chris@39 287
Chris@39 288 currentLevel = wave->wave_envelope_level;
Chris@39 289 wave->wave_envelope_level = targetLevel;
Chris@39 290 levelIncr = (targetLevel - currentLevel) / noOfFrames;
Chris@39 291
Chris@39 292 for (i = 0; i < noOfFrames; ++i, currentLevel += levelIncr)
Chris@39 293 {
Chris@39 294 (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_left, (GeneratePinkNoise(&wave->wave_left))) * currentLevel * wave->wave_pan_left;
Chris@39 295 (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_right, (GeneratePinkNoise(&wave->wave_right))) * currentLevel * wave->wave_pan_right;
Chris@39 296 }
Chris@39 297
Chris@39 298 return retval;
Chris@39 299 }
Chris@39 300
Chris@39 301
Chris@39 302 /*******************************************************************/
Chris@39 303
Chris@39 304 /* Context for callback routine. */
Chris@39 305 typedef struct
Chris@39 306 {
Chris@39 307 OceanWave* waves[16]; /* Maximum 16 waves */
Chris@39 308 unsigned noOfActiveWaves;
Chris@39 309
Chris@39 310 /* Ring buffer (FIFO) for "communicating" towards audio callback */
Chris@39 311 PaUtilRingBuffer rBufToRT;
Chris@39 312 void* rBufToRTData;
Chris@39 313
Chris@39 314 /* Ring buffer (FIFO) for "communicating" from audio callback */
Chris@39 315 PaUtilRingBuffer rBufFromRT;
Chris@39 316 void* rBufFromRTData;
Chris@39 317 }
Chris@39 318 paTestData;
Chris@39 319
Chris@39 320 /* This routine will be called by the PortAudio engine when audio is needed.
Chris@39 321 ** It may called at interrupt level on some machines so don't do anything
Chris@39 322 ** that could mess up the system like calling malloc() or free().
Chris@39 323 */
Chris@39 324 static int patestCallback(const void* inputBuffer,
Chris@39 325 void* outputBuffer,
Chris@39 326 unsigned long framesPerBuffer,
Chris@39 327 const PaStreamCallbackTimeInfo* timeInfo,
Chris@39 328 PaStreamCallbackFlags statusFlags,
Chris@39 329 void* userData)
Chris@39 330 {
Chris@39 331 int i;
Chris@39 332 paTestData *data = (paTestData*)userData;
Chris@39 333 float *out = (float*)outputBuffer;
Chris@39 334 (void) inputBuffer; /* Prevent "unused variable" warnings. */
Chris@39 335
Chris@39 336 /* Reset output data first */
Chris@39 337 memset(out, 0, framesPerBuffer * 2 * sizeof(float));
Chris@39 338
Chris@39 339 for (i = 0; i < 16; ++i)
Chris@39 340 {
Chris@39 341 /* Consume the input queue */
Chris@39 342 if (data->waves[i] == 0 && PaUtil_GetRingBufferReadAvailable(&data->rBufToRT))
Chris@39 343 {
Chris@39 344 OceanWave* ptr = 0;
Chris@39 345 PaUtil_ReadRingBuffer(&data->rBufToRT, &ptr, 1);
Chris@39 346 data->waves[i] = ptr;
Chris@39 347 }
Chris@39 348
Chris@39 349 if (data->waves[i] != 0)
Chris@39 350 {
Chris@39 351 if (GenerateWave(data->waves[i], out, framesPerBuffer))
Chris@39 352 {
Chris@39 353 /* If wave is "done", post it back to the main thread for deletion */
Chris@39 354 PaUtil_WriteRingBuffer(&data->rBufFromRT, &data->waves[i], 1);
Chris@39 355 data->waves[i] = 0;
Chris@39 356 }
Chris@39 357 }
Chris@39 358 }
Chris@39 359 return paContinue;
Chris@39 360 }
Chris@39 361
Chris@39 362 #define NEW_ROW_SIZE (12 + (8*rand())/RAND_MAX)
Chris@39 363
Chris@39 364 OceanWave* InitializeWave(double SR, float attackInSeconds, float maxLevel, float positionLeftRight)
Chris@39 365 {
Chris@39 366 OceanWave* wave = NULL;
Chris@39 367 static unsigned lastNoOfRows = 12;
Chris@39 368 unsigned newNoOfRows;
Chris@39 369
Chris@39 370 wave = (OceanWave*)PaUtil_AllocateMemory(sizeof(OceanWave));
Chris@39 371 if (wave != NULL)
Chris@39 372 {
Chris@39 373 InitializePinkNoise(&wave->wave_left, lastNoOfRows);
Chris@39 374 while ((newNoOfRows = NEW_ROW_SIZE) == lastNoOfRows);
Chris@39 375 InitializePinkNoise(&wave->wave_right, newNoOfRows);
Chris@39 376 lastNoOfRows = newNoOfRows;
Chris@39 377
Chris@39 378 wave->wave_envelope_state = State_kAttack;
Chris@39 379 wave->wave_envelope_level = 0.f;
Chris@39 380 wave->wave_envelope_max_level = maxLevel;
Chris@39 381 wave->wave_attack_incr = wave->wave_envelope_max_level / (attackInSeconds * (float)SR);
Chris@39 382 wave->wave_decay_incr = - wave->wave_envelope_max_level / (attackInSeconds * 4 * (float)SR);
Chris@39 383
Chris@39 384 wave->wave_pan_left = sqrtf(1.0 - positionLeftRight);
Chris@39 385 wave->wave_pan_right = sqrtf(positionLeftRight);
Chris@39 386 }
Chris@39 387 return wave;
Chris@39 388 }
Chris@39 389
Chris@39 390 static float GenerateFloatRandom(float minValue, float maxValue)
Chris@39 391 {
Chris@39 392 return minValue + ((maxValue - minValue) * rand()) / RAND_MAX;
Chris@39 393 }
Chris@39 394
Chris@39 395 /*******************************************************************/
Chris@39 396 int main(void);
Chris@39 397 int main(void)
Chris@39 398 {
Chris@39 399 PaStream* stream;
Chris@39 400 PaError err;
Chris@39 401 paTestData data = {0};
Chris@39 402 PaStreamParameters outputParameters;
Chris@39 403 double tstamp;
Chris@39 404 double tstart;
Chris@39 405 double tdelta = 0;
Chris@39 406 static const double SR = 44100.0;
Chris@39 407 static const int FPB = 128; /* Frames per buffer: 2.9 ms buffers. */
Chris@39 408
Chris@39 409 /* Initialize communication buffers (queues) */
Chris@39 410 data.rBufToRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
Chris@39 411 if (data.rBufToRTData == NULL)
Chris@39 412 {
Chris@39 413 return 1;
Chris@39 414 }
Chris@39 415 PaUtil_InitializeRingBuffer(&data.rBufToRT, sizeof(OceanWave*), 256, data.rBufToRTData);
Chris@39 416
Chris@39 417 data.rBufFromRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
Chris@39 418 if (data.rBufFromRTData == NULL)
Chris@39 419 {
Chris@39 420 return 1;
Chris@39 421 }
Chris@39 422 PaUtil_InitializeRingBuffer(&data.rBufFromRT, sizeof(OceanWave*), 256, data.rBufFromRTData);
Chris@39 423
Chris@39 424 err = Pa_Initialize();
Chris@39 425 if( err != paNoError ) goto error;
Chris@39 426
Chris@39 427 /* Open a stereo PortAudio stream so we can hear the result. */
Chris@39 428 outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
Chris@39 429 if (outputParameters.device == paNoDevice) {
Chris@39 430 fprintf(stderr,"Error: No default output device.\n");
Chris@39 431 goto error;
Chris@39 432 }
Chris@39 433 outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
Chris@39 434 outputParameters.hostApiSpecificStreamInfo = NULL;
Chris@39 435 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
Chris@39 436 outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
Chris@39 437 err = Pa_OpenStream(&stream,
Chris@39 438 NULL, /* No input. */
Chris@39 439 &outputParameters,
Chris@39 440 SR, /* Sample rate. */
Chris@39 441 FPB, /* Frames per buffer. */
Chris@39 442 paDitherOff, /* Clip but don't dither */
Chris@39 443 patestCallback,
Chris@39 444 &data);
Chris@39 445 if( err != paNoError ) goto error;
Chris@39 446
Chris@39 447 err = Pa_StartStream( stream );
Chris@39 448 if( err != paNoError ) goto error;
Chris@39 449
Chris@39 450 printf("Stereo \"ocean waves\" for one minute...\n");
Chris@39 451
Chris@39 452 tstart = PaUtil_GetTime();
Chris@39 453 tstamp = tstart;
Chris@39 454 srand( (unsigned)time(NULL) );
Chris@39 455
Chris@39 456 while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
Chris@39 457 {
Chris@39 458 const double tcurrent = PaUtil_GetTime();
Chris@39 459
Chris@39 460 /* Delete "waves" that the callback is finished with */
Chris@39 461 while (PaUtil_GetRingBufferReadAvailable(&data.rBufFromRT) > 0)
Chris@39 462 {
Chris@39 463 OceanWave* ptr = 0;
Chris@39 464 PaUtil_ReadRingBuffer(&data.rBufFromRT, &ptr, 1);
Chris@39 465 if (ptr != 0)
Chris@39 466 {
Chris@39 467 printf("Wave is deleted...\n");
Chris@39 468 PaUtil_FreeMemory(ptr);
Chris@39 469 --data.noOfActiveWaves;
Chris@39 470 }
Chris@39 471 }
Chris@39 472
Chris@39 473 if (tcurrent - tstart < 60.0) /* Only start new "waves" during one minute */
Chris@39 474 {
Chris@39 475 if (tcurrent >= tstamp)
Chris@39 476 {
Chris@39 477 double tdelta = GenerateFloatRandom(1.0f, 4.0f);
Chris@39 478 tstamp += tdelta;
Chris@39 479
Chris@39 480 if (data.noOfActiveWaves<16)
Chris@39 481 {
Chris@39 482 const float attackTime = GenerateFloatRandom(2.0f, 6.0f);
Chris@39 483 const float level = GenerateFloatRandom(0.1f, 1.0f);
Chris@39 484 const float pos = GenerateFloatRandom(0.0f, 1.0f);
Chris@39 485 OceanWave* p = InitializeWave(SR, attackTime, level, pos);
Chris@39 486 if (p != NULL)
Chris@39 487 {
Chris@39 488 /* Post wave to audio callback */
Chris@39 489 PaUtil_WriteRingBuffer(&data.rBufToRT, &p, 1);
Chris@39 490 ++data.noOfActiveWaves;
Chris@39 491
Chris@39 492 printf("Starting wave at level = %.2f, attack = %.2lf, pos = %.2lf\n", level, attackTime, pos);
Chris@39 493 }
Chris@39 494 }
Chris@39 495 }
Chris@39 496 }
Chris@39 497 else
Chris@39 498 {
Chris@39 499 if (data.noOfActiveWaves == 0)
Chris@39 500 {
Chris@39 501 printf("All waves finished!\n");
Chris@39 502 break;
Chris@39 503 }
Chris@39 504 }
Chris@39 505
Chris@39 506 Pa_Sleep(100);
Chris@39 507 }
Chris@39 508 if( err < 0 ) goto error;
Chris@39 509
Chris@39 510 err = Pa_CloseStream( stream );
Chris@39 511 if( err != paNoError ) goto error;
Chris@39 512
Chris@39 513 if (data.rBufToRTData)
Chris@39 514 {
Chris@39 515 PaUtil_FreeMemory(data.rBufToRTData);
Chris@39 516 }
Chris@39 517 if (data.rBufFromRTData)
Chris@39 518 {
Chris@39 519 PaUtil_FreeMemory(data.rBufFromRTData);
Chris@39 520 }
Chris@39 521
Chris@39 522 Pa_Sleep(1000);
Chris@39 523
Chris@39 524 Pa_Terminate();
Chris@39 525 return 0;
Chris@39 526
Chris@39 527 error:
Chris@39 528 Pa_Terminate();
Chris@39 529 fprintf( stderr, "An error occured while using the portaudio stream\n" );
Chris@39 530 fprintf( stderr, "Error number: %d\n", err );
Chris@39 531 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
Chris@39 532 return 0;
Chris@39 533 }