gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

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 */