PortAudio 2.0

paex_ocean_shore.c

Go to the documentation of this file.
00001 
00021 /*
00022  * $Id: paex_ocean_shore.c 1816 2012-02-22 12:20:26Z robiwan $
00023  *
00024  * This program uses the PortAudio Portable Audio Library.
00025  * For more information see: http://www.portaudio.com
00026  * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
00027  *
00028  * Permission is hereby granted, free of charge, to any person obtaining
00029  * a copy of this software and associated documentation files
00030  * (the "Software"), to deal in the Software without restriction,
00031  * including without limitation the rights to use, copy, modify, merge,
00032  * publish, distribute, sublicense, and/or sell copies of the Software,
00033  * and to permit persons to whom the Software is furnished to do so,
00034  * subject to the following conditions:
00035  *
00036  * The above copyright notice and this permission notice shall be
00037  * included in all copies or substantial portions of the Software.
00038  *
00039  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00040  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00041  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00042  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
00043  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00044  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00045  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00046  */
00047 
00048 /*
00049  * The text above constitutes the entire PortAudio license; however, 
00050  * the PortAudio community also makes the following non-binding requests:
00051  *
00052  * Any person wishing to distribute modifications to the Software is
00053  * requested to send the modifications to the original developer so that
00054  * they can be incorporated into the canonical version. It is also 
00055  * requested that these non-binding requests be included along with the 
00056  * license above.
00057  */
00058 
00059 #include <stdio.h>
00060 #include <stdlib.h>
00061 #include <string.h>
00062 #include <math.h>
00063 #include <time.h>
00064 
00065 #include "portaudio.h"
00066 #include "pa_ringbuffer.h"
00067 #include "pa_util.h"
00068 
00069 #define PINK_MAX_RANDOM_ROWS   (30)
00070 #define PINK_RANDOM_BITS       (24)
00071 #define PINK_RANDOM_SHIFT      ((sizeof(long)*8)-PINK_RANDOM_BITS)
00072 
00073 typedef struct
00074 {
00075     long      pink_Rows[PINK_MAX_RANDOM_ROWS];
00076     long      pink_RunningSum;   /* Used to optimize summing of generators. */
00077     int       pink_Index;        /* Incremented each sample. */
00078     int       pink_IndexMask;    /* Index wrapped by ANDing with this mask. */
00079     float     pink_Scalar;       /* Used to scale within range of -1.0 to +1.0 */
00080 }
00081 PinkNoise;
00082 
00083 typedef struct 
00084 {
00085     float       bq_b0;
00086     float       bq_b1;
00087     float       bq_b2;
00088     float       bq_a1;
00089     float       bq_a2;
00090 } BiQuad;
00091 
00092 typedef enum
00093 {
00094     State_kAttack,
00095     State_kPreDecay,
00096     State_kDecay,
00097     State_kCnt,
00098 } EnvState;
00099 
00100 typedef struct
00101 {
00102     PinkNoise   wave_left;
00103     PinkNoise   wave_right;
00104 
00105     BiQuad      wave_bq_coeffs;
00106     float       wave_bq_left[2];
00107     float       wave_bq_right[2];
00108 
00109     EnvState    wave_envelope_state;
00110     float       wave_envelope_level;
00111     float       wave_envelope_max_level;
00112     float       wave_pan_left;
00113     float       wave_pan_right;
00114     float       wave_attack_incr;
00115     float       wave_decay_incr;
00116 
00117 } OceanWave;
00118 
00119 /* Prototypes */
00120 static unsigned long GenerateRandomNumber( void );
00121 void InitializePinkNoise( PinkNoise *pink, int numRows );
00122 float GeneratePinkNoise( PinkNoise *pink );
00123 unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames);
00124 
00125 /************************************************************/
00126 /* Calculate pseudo-random 32 bit number based on linear congruential method. */
00127 static unsigned long GenerateRandomNumber( void )
00128 {
00129     /* Change this seed for different random sequences. */
00130     static unsigned long randSeed = 22222;
00131     randSeed = (randSeed * 196314165) + 907633515;
00132     return randSeed;
00133 }
00134 
00135 /************************************************************/
00136 /* Setup PinkNoise structure for N rows of generators. */
00137 void InitializePinkNoise( PinkNoise *pink, int numRows )
00138 {
00139     int i;
00140     long pmax;
00141     pink->pink_Index = 0;
00142     pink->pink_IndexMask = (1<<numRows) - 1;
00143     /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
00144     pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
00145     pink->pink_Scalar = 1.0f / pmax;
00146     /* Initialize rows. */
00147     for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
00148     pink->pink_RunningSum = 0;
00149 }
00150 
00151 /* Generate Pink noise values between -1.0 and +1.0 */
00152 float GeneratePinkNoise( PinkNoise *pink )
00153 {
00154     long newRandom;
00155     long sum;
00156     float output;
00157     /* Increment and mask index. */
00158     pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
00159     /* If index is zero, don't update any random values. */
00160     if( pink->pink_Index != 0 )
00161     {
00162         /* Determine how many trailing zeros in PinkIndex. */
00163         /* This algorithm will hang if n==0 so test first. */
00164         int numZeros = 0;
00165         int n = pink->pink_Index;
00166         while( (n & 1) == 0 )
00167         {
00168             n = n >> 1;
00169             numZeros++;
00170         }
00171         /* Replace the indexed ROWS random value.
00172          * Subtract and add back to RunningSum instead of adding all the random
00173          * values together. Only one changes each time.
00174          */
00175         pink->pink_RunningSum -= pink->pink_Rows[numZeros];
00176         newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
00177         pink->pink_RunningSum += newRandom;
00178         pink->pink_Rows[numZeros] = newRandom;
00179     }
00180 
00181     /* Add extra white noise value. */
00182     newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
00183     sum = pink->pink_RunningSum + newRandom;
00184     /* Scale to range of -1.0 to 0.9999. */
00185     output = pink->pink_Scalar * sum;
00186     return output;
00187 }
00188 
00189 float ProcessBiquad(const BiQuad* coeffs, float* memory, float input)
00190 {
00191     float w = input - coeffs->bq_a1 * memory[0] - coeffs->bq_a2 * memory[1];
00192     float out = coeffs->bq_b1 * memory[0] + coeffs->bq_b2 * memory[1] + coeffs->bq_b0 * w;
00193     memory[1] = memory[0];
00194     memory[0] = w;
00195     return out;
00196 }
00197 
00198 static const float one_over_2Q_LP = 0.3f;
00199 static const float one_over_2Q_HP = 1.0f;
00200 
00201 unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames )
00202 {
00203     unsigned retval=0,i;
00204     float targetLevel, levelIncr, currentLevel;
00205     switch (wave->wave_envelope_state)
00206     {
00207     case State_kAttack:
00208         targetLevel = noOfFrames * wave->wave_attack_incr + wave->wave_envelope_level;
00209         if (targetLevel >= wave->wave_envelope_max_level)
00210         {
00211             /* Go to decay state */
00212             wave->wave_envelope_state = State_kPreDecay;
00213             targetLevel = wave->wave_envelope_max_level;
00214         }
00215         /* Calculate lowpass biquad coeffs
00216         
00217             alpha = sin(w0)/(2*Q)
00218 
00219                 b0 =  (1 - cos(w0))/2
00220                 b1 =   1 - cos(w0)
00221                 b2 =  (1 - cos(w0))/2
00222                 a0 =   1 + alpha
00223                 a1 =  -2*cos(w0)
00224                 a2 =   1 - alpha
00225 
00226             w0 = [0 - pi[
00227         */
00228         {
00229             const float w0 = 3.141592654f * targetLevel / wave->wave_envelope_max_level;
00230             const float alpha = sinf(w0) * one_over_2Q_LP;
00231             const float cosw0 = cosf(w0);
00232             const float a0_fact = 1.0f / (1.0f + alpha);
00233             wave->wave_bq_coeffs.bq_b1 = (1.0f - cosw0) * a0_fact;
00234             wave->wave_bq_coeffs.bq_b0 = wave->wave_bq_coeffs.bq_b1 * 0.5f;
00235             wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
00236             wave->wave_bq_coeffs.bq_a2 = (1.0f - alpha) * a0_fact;
00237             wave->wave_bq_coeffs.bq_a1 = -2.0f * cosw0 * a0_fact;
00238         }
00239         break;
00240 
00241     case State_kPreDecay:
00242         /* Reset biquad state */
00243         memset(wave->wave_bq_left, 0, 2 * sizeof(float));
00244         memset(wave->wave_bq_right, 0, 2 * sizeof(float));
00245         wave->wave_envelope_state = State_kDecay;
00246 
00247         /* Deliberate fall-through */
00248 
00249     case State_kDecay:
00250         targetLevel = noOfFrames * wave->wave_decay_incr + wave->wave_envelope_level;
00251         if (targetLevel < 0.001f)
00252         {
00253             /* < -60 dB, we're done */
00254             wave->wave_envelope_state = 3;
00255             retval = 1;
00256         }
00257         /* Calculate highpass biquad coeffs
00258 
00259             alpha = sin(w0)/(2*Q)
00260 
00261             b0 =  (1 + cos(w0))/2
00262             b1 = -(1 + cos(w0))
00263             b2 =  (1 + cos(w0))/2
00264             a0 =   1 + alpha
00265             a1 =  -2*cos(w0)
00266             a2 =   1 - alpha
00267 
00268             w0 = [0 - pi/2[
00269         */
00270         {
00271             const float v = targetLevel / wave->wave_envelope_max_level;
00272             const float w0 = 1.5707963f * (1.0f - (v*v));
00273             const float alpha = sinf(w0) * one_over_2Q_HP;
00274             const float cosw0 = cosf(w0);
00275             const float a0_fact = 1.0f / (1.0f + alpha);
00276             wave->wave_bq_coeffs.bq_b1 = (float)(- (1 + cosw0) * a0_fact);
00277             wave->wave_bq_coeffs.bq_b0 = -wave->wave_bq_coeffs.bq_b1 * 0.5f;
00278             wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
00279             wave->wave_bq_coeffs.bq_a2 = (float)((1.0 - alpha) * a0_fact);
00280             wave->wave_bq_coeffs.bq_a1 = (float)(-2.0 * cosw0 * a0_fact);
00281         }
00282         break;
00283 
00284     default:
00285         break;
00286     }
00287 
00288     currentLevel = wave->wave_envelope_level;
00289     wave->wave_envelope_level = targetLevel;
00290     levelIncr = (targetLevel - currentLevel) / noOfFrames;
00291 
00292     for (i = 0; i < noOfFrames; ++i, currentLevel += levelIncr)
00293     {
00294         (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_left, (GeneratePinkNoise(&wave->wave_left))) * currentLevel * wave->wave_pan_left;
00295         (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_right, (GeneratePinkNoise(&wave->wave_right))) * currentLevel * wave->wave_pan_right;
00296     }
00297 
00298     return retval;
00299 }
00300 
00301 
00302 /*******************************************************************/
00303 
00304 /* Context for callback routine. */
00305 typedef struct
00306 {
00307     OceanWave*          waves[16];      /* Maximum 16 waves */
00308     unsigned            noOfActiveWaves;
00309 
00310     /* Ring buffer (FIFO) for "communicating" towards audio callback */
00311     PaUtilRingBuffer    rBufToRT;
00312     void*               rBufToRTData;
00313 
00314     /* Ring buffer (FIFO) for "communicating" from audio callback */
00315     PaUtilRingBuffer    rBufFromRT;
00316     void*               rBufFromRTData;
00317 }
00318 paTestData;
00319 
00320 /* This routine will be called by the PortAudio engine when audio is needed.
00321 ** It may called at interrupt level on some machines so don't do anything
00322 ** that could mess up the system like calling malloc() or free().
00323 */
00324 static int patestCallback(const void*                     inputBuffer,
00325                           void*                           outputBuffer,
00326                           unsigned long                   framesPerBuffer,
00327                                       const PaStreamCallbackTimeInfo* timeInfo,
00328                                       PaStreamCallbackFlags           statusFlags,
00329                           void*                           userData)
00330 {
00331     int i;
00332     paTestData *data = (paTestData*)userData;
00333     float *out = (float*)outputBuffer;
00334     (void) inputBuffer; /* Prevent "unused variable" warnings. */
00335 
00336     /* Reset output data first */
00337     memset(out, 0, framesPerBuffer * 2 * sizeof(float));
00338 
00339     for (i = 0; i < 16; ++i)
00340     {
00341         /* Consume the input queue */
00342         if (data->waves[i] == 0 && PaUtil_GetRingBufferReadAvailable(&data->rBufToRT))
00343         {
00344             OceanWave* ptr = 0;
00345             PaUtil_ReadRingBuffer(&data->rBufToRT, &ptr, 1);
00346             data->waves[i] = ptr;
00347         }
00348 
00349         if (data->waves[i] != 0)
00350         {
00351             if (GenerateWave(data->waves[i], out, framesPerBuffer))
00352             {
00353                 /* If wave is "done", post it back to the main thread for deletion */
00354                 PaUtil_WriteRingBuffer(&data->rBufFromRT, &data->waves[i], 1);
00355                 data->waves[i] = 0;
00356             }
00357         }
00358     }
00359     return paContinue;
00360 }
00361 
00362 #define NEW_ROW_SIZE (12 + (8*rand())/RAND_MAX)
00363 
00364 OceanWave* InitializeWave(double SR, float attackInSeconds, float maxLevel, float positionLeftRight)
00365 {
00366     OceanWave* wave = NULL;
00367     static unsigned lastNoOfRows = 12;
00368     unsigned newNoOfRows;
00369 
00370     wave = (OceanWave*)PaUtil_AllocateMemory(sizeof(OceanWave));
00371     if (wave != NULL)
00372     {
00373         InitializePinkNoise(&wave->wave_left, lastNoOfRows);
00374         while ((newNoOfRows = NEW_ROW_SIZE) == lastNoOfRows);
00375         InitializePinkNoise(&wave->wave_right, newNoOfRows);
00376         lastNoOfRows = newNoOfRows;
00377 
00378         wave->wave_envelope_state = State_kAttack;
00379         wave->wave_envelope_level = 0.f;
00380         wave->wave_envelope_max_level = maxLevel;
00381         wave->wave_attack_incr = wave->wave_envelope_max_level / (attackInSeconds * (float)SR);
00382         wave->wave_decay_incr = - wave->wave_envelope_max_level / (attackInSeconds * 4 * (float)SR);
00383 
00384         wave->wave_pan_left = sqrtf(1.0 - positionLeftRight);
00385         wave->wave_pan_right = sqrtf(positionLeftRight);
00386     }
00387     return wave;
00388 }
00389 
00390 static float GenerateFloatRandom(float minValue, float maxValue)
00391 {
00392     return minValue + ((maxValue - minValue) * rand()) / RAND_MAX;
00393 }
00394 
00395 /*******************************************************************/
00396 int main(void);
00397 int main(void)
00398 {
00399     PaStream*           stream;
00400     PaError             err;
00401     paTestData          data = {0};
00402     PaStreamParameters  outputParameters;
00403     double              tstamp;
00404     double              tstart;
00405     double              tdelta = 0;
00406     static const double SR  = 44100.0;
00407     static const int    FPB = 128; /* Frames per buffer: 2.9 ms buffers. */
00408 
00409     /* Initialize communication buffers (queues) */
00410     data.rBufToRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
00411     if (data.rBufToRTData == NULL)
00412     {
00413         return 1;
00414     }
00415     PaUtil_InitializeRingBuffer(&data.rBufToRT, sizeof(OceanWave*), 256, data.rBufToRTData);
00416 
00417     data.rBufFromRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
00418     if (data.rBufFromRTData == NULL)
00419     {
00420         return 1;
00421     }
00422     PaUtil_InitializeRingBuffer(&data.rBufFromRT, sizeof(OceanWave*), 256, data.rBufFromRTData);
00423 
00424     err = Pa_Initialize();
00425     if( err != paNoError ) goto error;
00426 
00427     /* Open a stereo PortAudio stream so we can hear the result. */
00428     outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
00429     if (outputParameters.device == paNoDevice) {
00430       fprintf(stderr,"Error: No default output device.\n");
00431       goto error;
00432     }
00433     outputParameters.channelCount = 2;                     /* Stereo output, most likely supported. */
00434     outputParameters.hostApiSpecificStreamInfo = NULL;
00435     outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output. */
00436     outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
00437     err = Pa_OpenStream(&stream,
00438                         NULL,                              /* No input. */
00439                         &outputParameters,
00440                         SR,                                /* Sample rate. */
00441                         FPB,                               /* Frames per buffer. */
00442                         paDitherOff,                       /* Clip but don't dither */
00443                         patestCallback,
00444                         &data);
00445     if( err != paNoError ) goto error;
00446 
00447     err = Pa_StartStream( stream );
00448     if( err != paNoError ) goto error;
00449 
00450     printf("Stereo \"ocean waves\" for one minute...\n");
00451 
00452     tstart = PaUtil_GetTime();
00453     tstamp = tstart;
00454     srand( (unsigned)time(NULL) );
00455 
00456     while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
00457     {
00458         const double tcurrent = PaUtil_GetTime();
00459 
00460         /* Delete "waves" that the callback is finished with */
00461         while (PaUtil_GetRingBufferReadAvailable(&data.rBufFromRT) > 0)
00462         {
00463             OceanWave* ptr = 0;
00464             PaUtil_ReadRingBuffer(&data.rBufFromRT, &ptr, 1);
00465             if (ptr != 0)
00466             {
00467                 printf("Wave is deleted...\n");
00468                 PaUtil_FreeMemory(ptr);
00469                 --data.noOfActiveWaves;
00470             }
00471         }
00472 
00473         if (tcurrent - tstart < 60.0) /* Only start new "waves" during one minute */
00474         {
00475             if (tcurrent >= tstamp)
00476             {
00477                 double tdelta = GenerateFloatRandom(1.0f, 4.0f);
00478                 tstamp += tdelta;
00479 
00480                 if (data.noOfActiveWaves<16)
00481                 {
00482                     const float attackTime = GenerateFloatRandom(2.0f, 6.0f);
00483                     const float level = GenerateFloatRandom(0.1f, 1.0f);
00484                     const float pos = GenerateFloatRandom(0.0f, 1.0f);
00485                     OceanWave* p = InitializeWave(SR, attackTime, level, pos);
00486                     if (p != NULL)
00487                     {
00488                         /* Post wave to audio callback */
00489                         PaUtil_WriteRingBuffer(&data.rBufToRT, &p, 1);
00490                         ++data.noOfActiveWaves;
00491 
00492                         printf("Starting wave at level = %.2f, attack = %.2lf, pos = %.2lf\n", level, attackTime, pos);
00493                     }
00494                 }
00495             }
00496         }
00497         else
00498         {
00499             if (data.noOfActiveWaves == 0)
00500             {
00501                 printf("All waves finished!\n");
00502                 break;
00503             }
00504         }
00505 
00506         Pa_Sleep(100);
00507     }
00508     if( err < 0 ) goto error;
00509 
00510     err = Pa_CloseStream( stream );
00511     if( err != paNoError ) goto error;
00512 
00513     if (data.rBufToRTData)
00514     {
00515         PaUtil_FreeMemory(data.rBufToRTData);
00516     }
00517     if (data.rBufFromRTData)
00518     {
00519         PaUtil_FreeMemory(data.rBufFromRTData);
00520     }
00521     
00522     Pa_Sleep(1000);
00523 
00524     Pa_Terminate();
00525     return 0;
00526 
00527 error:
00528     Pa_Terminate();
00529     fprintf( stderr, "An error occured while using the portaudio stream\n" );
00530     fprintf( stderr, "Error number: %d\n", err );
00531     fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
00532     return 0;
00533 }