To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / examples / paex_ocean_shore.c @ 162:d43aab368df9

History | View | Annotate | Download (18 KB)

1
/** @file paex_ocean_shore.c 
2
        @ingroup examples_src
3
        @brief Generate Pink Noise using Gardner method, and make "waves". Provides an example of how to
4
           post stuff to/from the audio callback using lock-free FIFOs implemented by the PA ringbuffer.
5

6
        Optimization suggested by James McCartney uses a tree
7
        to select which random value to replace.
8
<pre>
9
        x x x x x x x x x x x x x x x x 
10
        x   x   x   x   x   x   x   x   
11
        x       x       x       x       
12
         x               x               
13
           x   
14
</pre>                            
15
        Tree is generated by counting trailing zeros in an increasing index.
16
        When the index is zero, no random number is selected.
17

18
        @author Phil Burk  http://www.softsynth.com
19
            Robert Bielik
20
*/
21
/*
22
 * $Id$
23
 *
24
 * This program uses the PortAudio Portable Audio Library.
25
 * For more information see: http://www.portaudio.com
26
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
27
 *
28
 * Permission is hereby granted, free of charge, to any person obtaining
29
 * a copy of this software and associated documentation files
30
 * (the "Software"), to deal in the Software without restriction,
31
 * including without limitation the rights to use, copy, modify, merge,
32
 * publish, distribute, sublicense, and/or sell copies of the Software,
33
 * and to permit persons to whom the Software is furnished to do so,
34
 * subject to the following conditions:
35
 *
36
 * The above copyright notice and this permission notice shall be
37
 * included in all copies or substantial portions of the Software.
38
 *
39
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
42
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
43
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
44
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46
 */
47

    
48
/*
49
 * The text above constitutes the entire PortAudio license; however, 
50
 * the PortAudio community also makes the following non-binding requests:
51
 *
52
 * Any person wishing to distribute modifications to the Software is
53
 * requested to send the modifications to the original developer so that
54
 * they can be incorporated into the canonical version. It is also 
55
 * requested that these non-binding requests be included along with the 
56
 * license above.
57
 */
58

    
59
#include <stdio.h>
60
#include <stdlib.h>
61
#include <string.h>
62
#include <math.h>
63
#include <time.h>
64

    
65
#include "portaudio.h"
66
#include "pa_ringbuffer.h"
67
#include "pa_util.h"
68

    
69
#define PINK_MAX_RANDOM_ROWS   (30)
70
#define PINK_RANDOM_BITS       (24)
71
#define PINK_RANDOM_SHIFT      ((sizeof(long)*8)-PINK_RANDOM_BITS)
72

    
73
typedef struct
74
{
75
    long      pink_Rows[PINK_MAX_RANDOM_ROWS];
76
    long      pink_RunningSum;   /* Used to optimize summing of generators. */
77
    int       pink_Index;        /* Incremented each sample. */
78
    int       pink_IndexMask;    /* Index wrapped by ANDing with this mask. */
79
    float     pink_Scalar;       /* Used to scale within range of -1.0 to +1.0 */
80
}
81
PinkNoise;
82

    
83
typedef struct 
84
{
85
    float       bq_b0;
86
    float       bq_b1;
87
    float       bq_b2;
88
    float       bq_a1;
89
    float       bq_a2;
90
} BiQuad;
91

    
92
typedef enum
93
{
94
    State_kAttack,
95
    State_kPreDecay,
96
    State_kDecay,
97
    State_kCnt,
98
} EnvState;
99

    
100
typedef struct
101
{
102
    PinkNoise   wave_left;
103
    PinkNoise   wave_right;
104

    
105
    BiQuad      wave_bq_coeffs;
106
    float       wave_bq_left[2];
107
    float       wave_bq_right[2];
108

    
109
    EnvState    wave_envelope_state;
110
    float       wave_envelope_level;
111
    float       wave_envelope_max_level;
112
    float       wave_pan_left;
113
    float       wave_pan_right;
114
    float       wave_attack_incr;
115
    float       wave_decay_incr;
116

    
117
} OceanWave;
118

    
119
/* Prototypes */
120
static unsigned long GenerateRandomNumber( void );
121
void InitializePinkNoise( PinkNoise *pink, int numRows );
122
float GeneratePinkNoise( PinkNoise *pink );
123
unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames);
124

    
125
/************************************************************/
126
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
127
static unsigned long GenerateRandomNumber( void )
128
{
129
    /* Change this seed for different random sequences. */
130
    static unsigned long randSeed = 22222;
131
    randSeed = (randSeed * 196314165) + 907633515;
132
    return randSeed;
133
}
134

    
135
/************************************************************/
136
/* Setup PinkNoise structure for N rows of generators. */
137
void InitializePinkNoise( PinkNoise *pink, int numRows )
138
{
139
    int i;
140
    long pmax;
141
    pink->pink_Index = 0;
142
    pink->pink_IndexMask = (1<<numRows) - 1;
143
    /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
144
    pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
145
    pink->pink_Scalar = 1.0f / pmax;
146
    /* Initialize rows. */
147
    for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
148
    pink->pink_RunningSum = 0;
149
}
150

    
151
/* Generate Pink noise values between -1.0 and +1.0 */
152
float GeneratePinkNoise( PinkNoise *pink )
153
{
154
    long newRandom;
155
    long sum;
156
    float output;
157
    /* Increment and mask index. */
158
    pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
159
    /* If index is zero, don't update any random values. */
160
    if( pink->pink_Index != 0 )
161
    {
162
        /* Determine how many trailing zeros in PinkIndex. */
163
        /* This algorithm will hang if n==0 so test first. */
164
        int numZeros = 0;
165
        int n = pink->pink_Index;
166
        while( (n & 1) == 0 )
167
        {
168
            n = n >> 1;
169
            numZeros++;
170
        }
171
        /* Replace the indexed ROWS random value.
172
         * Subtract and add back to RunningSum instead of adding all the random
173
         * values together. Only one changes each time.
174
         */
175
        pink->pink_RunningSum -= pink->pink_Rows[numZeros];
176
        newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
177
        pink->pink_RunningSum += newRandom;
178
        pink->pink_Rows[numZeros] = newRandom;
179
    }
180

    
181
    /* Add extra white noise value. */
182
    newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
183
    sum = pink->pink_RunningSum + newRandom;
184
    /* Scale to range of -1.0 to 0.9999. */
185
    output = pink->pink_Scalar * sum;
186
    return output;
187
}
188

    
189
float ProcessBiquad(const BiQuad* coeffs, float* memory, float input)
190
{
191
    float w = input - coeffs->bq_a1 * memory[0] - coeffs->bq_a2 * memory[1];
192
    float out = coeffs->bq_b1 * memory[0] + coeffs->bq_b2 * memory[1] + coeffs->bq_b0 * w;
193
    memory[1] = memory[0];
194
    memory[0] = w;
195
    return out;
196
}
197

    
198
static const float one_over_2Q_LP = 0.3f;
199
static const float one_over_2Q_HP = 1.0f;
200

    
201
unsigned GenerateWave( OceanWave* wave, float* output, unsigned noOfFrames )
202
{
203
    unsigned retval=0,i;
204
    float targetLevel, levelIncr, currentLevel;
205
    switch (wave->wave_envelope_state)
206
    {
207
    case State_kAttack:
208
        targetLevel = noOfFrames * wave->wave_attack_incr + wave->wave_envelope_level;
209
        if (targetLevel >= wave->wave_envelope_max_level)
210
        {
211
            /* Go to decay state */
212
            wave->wave_envelope_state = State_kPreDecay;
213
            targetLevel = wave->wave_envelope_max_level;
214
        }
215
        /* Calculate lowpass biquad coeffs
216
        
217
            alpha = sin(w0)/(2*Q)
218

219
                b0 =  (1 - cos(w0))/2
220
                b1 =   1 - cos(w0)
221
                b2 =  (1 - cos(w0))/2
222
                a0 =   1 + alpha
223
                a1 =  -2*cos(w0)
224
                a2 =   1 - alpha
225

226
            w0 = [0 - pi[
227
        */
228
        {
229
            const float w0 = 3.141592654f * targetLevel / wave->wave_envelope_max_level;
230
            const float alpha = sinf(w0) * one_over_2Q_LP;
231
            const float cosw0 = cosf(w0);
232
            const float a0_fact = 1.0f / (1.0f + alpha);
233
            wave->wave_bq_coeffs.bq_b1 = (1.0f - cosw0) * a0_fact;
234
            wave->wave_bq_coeffs.bq_b0 = wave->wave_bq_coeffs.bq_b1 * 0.5f;
235
            wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
236
            wave->wave_bq_coeffs.bq_a2 = (1.0f - alpha) * a0_fact;
237
            wave->wave_bq_coeffs.bq_a1 = -2.0f * cosw0 * a0_fact;
238
        }
239
        break;
240

    
241
    case State_kPreDecay:
242
        /* Reset biquad state */
243
        memset(wave->wave_bq_left, 0, 2 * sizeof(float));
244
        memset(wave->wave_bq_right, 0, 2 * sizeof(float));
245
        wave->wave_envelope_state = State_kDecay;
246

    
247
        /* Deliberate fall-through */
248

    
249
    case State_kDecay:
250
        targetLevel = noOfFrames * wave->wave_decay_incr + wave->wave_envelope_level;
251
        if (targetLevel < 0.001f)
252
        {
253
            /* < -60 dB, we're done */
254
            wave->wave_envelope_state = 3;
255
            retval = 1;
256
        }
257
        /* Calculate highpass biquad coeffs
258

259
            alpha = sin(w0)/(2*Q)
260

261
            b0 =  (1 + cos(w0))/2
262
            b1 = -(1 + cos(w0))
263
            b2 =  (1 + cos(w0))/2
264
            a0 =   1 + alpha
265
            a1 =  -2*cos(w0)
266
            a2 =   1 - alpha
267

268
            w0 = [0 - pi/2[
269
        */
270
        {
271
            const float v = targetLevel / wave->wave_envelope_max_level;
272
            const float w0 = 1.5707963f * (1.0f - (v*v));
273
            const float alpha = sinf(w0) * one_over_2Q_HP;
274
            const float cosw0 = cosf(w0);
275
            const float a0_fact = 1.0f / (1.0f + alpha);
276
            wave->wave_bq_coeffs.bq_b1 = (float)(- (1 + cosw0) * a0_fact);
277
            wave->wave_bq_coeffs.bq_b0 = -wave->wave_bq_coeffs.bq_b1 * 0.5f;
278
            wave->wave_bq_coeffs.bq_b2 = wave->wave_bq_coeffs.bq_b0;
279
            wave->wave_bq_coeffs.bq_a2 = (float)((1.0 - alpha) * a0_fact);
280
            wave->wave_bq_coeffs.bq_a1 = (float)(-2.0 * cosw0 * a0_fact);
281
        }
282
        break;
283

    
284
    default:
285
        break;
286
    }
287

    
288
    currentLevel = wave->wave_envelope_level;
289
    wave->wave_envelope_level = targetLevel;
290
    levelIncr = (targetLevel - currentLevel) / noOfFrames;
291

    
292
    for (i = 0; i < noOfFrames; ++i, currentLevel += levelIncr)
293
    {
294
        (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_left, (GeneratePinkNoise(&wave->wave_left))) * currentLevel * wave->wave_pan_left;
295
        (*output++) += ProcessBiquad(&wave->wave_bq_coeffs, wave->wave_bq_right, (GeneratePinkNoise(&wave->wave_right))) * currentLevel * wave->wave_pan_right;
296
    }
297

    
298
    return retval;
299
}
300

    
301

    
302
/*******************************************************************/
303

    
304
/* Context for callback routine. */
305
typedef struct
306
{
307
    OceanWave*          waves[16];      /* Maximum 16 waves */
308
    unsigned            noOfActiveWaves;
309

    
310
    /* Ring buffer (FIFO) for "communicating" towards audio callback */
311
    PaUtilRingBuffer    rBufToRT;
312
    void*               rBufToRTData;
313

    
314
    /* Ring buffer (FIFO) for "communicating" from audio callback */
315
    PaUtilRingBuffer    rBufFromRT;
316
    void*               rBufFromRTData;
317
}
318
paTestData;
319

    
320
/* This routine will be called by the PortAudio engine when audio is needed.
321
** It may called at interrupt level on some machines so don't do anything
322
** that could mess up the system like calling malloc() or free().
323
*/
324
static int patestCallback(const void*                     inputBuffer,
325
                          void*                           outputBuffer,
326
                          unsigned long                   framesPerBuffer,
327
                                      const PaStreamCallbackTimeInfo* timeInfo,
328
                                      PaStreamCallbackFlags           statusFlags,
329
                          void*                           userData)
330
{
331
    int i;
332
    paTestData *data = (paTestData*)userData;
333
    float *out = (float*)outputBuffer;
334
    (void) inputBuffer; /* Prevent "unused variable" warnings. */
335

    
336
    /* Reset output data first */
337
    memset(out, 0, framesPerBuffer * 2 * sizeof(float));
338

    
339
    for (i = 0; i < 16; ++i)
340
    {
341
        /* Consume the input queue */
342
        if (data->waves[i] == 0 && PaUtil_GetRingBufferReadAvailable(&data->rBufToRT))
343
        {
344
            OceanWave* ptr = 0;
345
            PaUtil_ReadRingBuffer(&data->rBufToRT, &ptr, 1);
346
            data->waves[i] = ptr;
347
        }
348

    
349
        if (data->waves[i] != 0)
350
        {
351
            if (GenerateWave(data->waves[i], out, framesPerBuffer))
352
            {
353
                /* If wave is "done", post it back to the main thread for deletion */
354
                PaUtil_WriteRingBuffer(&data->rBufFromRT, &data->waves[i], 1);
355
                data->waves[i] = 0;
356
            }
357
        }
358
    }
359
    return paContinue;
360
}
361

    
362
#define NEW_ROW_SIZE (12 + (8*rand())/RAND_MAX)
363

    
364
OceanWave* InitializeWave(double SR, float attackInSeconds, float maxLevel, float positionLeftRight)
365
{
366
    OceanWave* wave = NULL;
367
    static unsigned lastNoOfRows = 12;
368
    unsigned newNoOfRows;
369

    
370
    wave = (OceanWave*)PaUtil_AllocateMemory(sizeof(OceanWave));
371
    if (wave != NULL)
372
    {
373
        InitializePinkNoise(&wave->wave_left, lastNoOfRows);
374
        while ((newNoOfRows = NEW_ROW_SIZE) == lastNoOfRows);
375
        InitializePinkNoise(&wave->wave_right, newNoOfRows);
376
        lastNoOfRows = newNoOfRows;
377

    
378
        wave->wave_envelope_state = State_kAttack;
379
        wave->wave_envelope_level = 0.f;
380
        wave->wave_envelope_max_level = maxLevel;
381
        wave->wave_attack_incr = wave->wave_envelope_max_level / (attackInSeconds * (float)SR);
382
        wave->wave_decay_incr = - wave->wave_envelope_max_level / (attackInSeconds * 4 * (float)SR);
383

    
384
        wave->wave_pan_left = sqrtf(1.0f - positionLeftRight);
385
        wave->wave_pan_right = sqrtf(positionLeftRight);
386
    }
387
    return wave;
388
}
389

    
390
static float GenerateFloatRandom(float minValue, float maxValue)
391
{
392
    return minValue + ((maxValue - minValue) * rand()) / RAND_MAX;
393
}
394

    
395
/*******************************************************************/
396
int main(void);
397
int main(void)
398
{
399
    PaStream*           stream;
400
    PaError             err;
401
    paTestData          data = {0};
402
    PaStreamParameters  outputParameters;
403
    double              tstamp;
404
    double              tstart;
405
    double              tdelta = 0;
406
    static const double SR  = 44100.0;
407
    static const int    FPB = 128; /* Frames per buffer: 2.9 ms buffers. */
408

    
409
    /* Initialize communication buffers (queues) */
410
    data.rBufToRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
411
    if (data.rBufToRTData == NULL)
412
    {
413
        return 1;
414
    }
415
    PaUtil_InitializeRingBuffer(&data.rBufToRT, sizeof(OceanWave*), 256, data.rBufToRTData);
416

    
417
    data.rBufFromRTData = PaUtil_AllocateMemory(sizeof(OceanWave*) * 256);
418
    if (data.rBufFromRTData == NULL)
419
    {
420
        return 1;
421
    }
422
    PaUtil_InitializeRingBuffer(&data.rBufFromRT, sizeof(OceanWave*), 256, data.rBufFromRTData);
423

    
424
    err = Pa_Initialize();
425
    if( err != paNoError ) goto error;
426

    
427
    /* Open a stereo PortAudio stream so we can hear the result. */
428
    outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
429
    if (outputParameters.device == paNoDevice) {
430
      fprintf(stderr,"Error: No default output device.\n");
431
      goto error;
432
    }
433
    outputParameters.channelCount = 2;                     /* Stereo output, most likely supported. */
434
    outputParameters.hostApiSpecificStreamInfo = NULL;
435
    outputParameters.sampleFormat = paFloat32;             /* 32 bit floating point output. */
436
    outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
437
    err = Pa_OpenStream(&stream,
438
                        NULL,                              /* No input. */
439
                        &outputParameters,
440
                        SR,                                /* Sample rate. */
441
                        FPB,                               /* Frames per buffer. */
442
                        paDitherOff,                       /* Clip but don't dither */
443
                        patestCallback,
444
                        &data);
445
    if( err != paNoError ) goto error;
446

    
447
    err = Pa_StartStream( stream );
448
    if( err != paNoError ) goto error;
449

    
450
    printf("Stereo \"ocean waves\" for one minute...\n");
451

    
452
    tstart = PaUtil_GetTime();
453
    tstamp = tstart;
454
    srand( (unsigned)time(NULL) );
455

    
456
    while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
457
    {
458
        const double tcurrent = PaUtil_GetTime();
459

    
460
        /* Delete "waves" that the callback is finished with */
461
        while (PaUtil_GetRingBufferReadAvailable(&data.rBufFromRT) > 0)
462
        {
463
            OceanWave* ptr = 0;
464
            PaUtil_ReadRingBuffer(&data.rBufFromRT, &ptr, 1);
465
            if (ptr != 0)
466
            {
467
                printf("Wave is deleted...\n");
468
                PaUtil_FreeMemory(ptr);
469
                --data.noOfActiveWaves;
470
            }
471
        }
472

    
473
        if (tcurrent - tstart < 60.0) /* Only start new "waves" during one minute */
474
        {
475
            if (tcurrent >= tstamp)
476
            {
477
                double tdelta = GenerateFloatRandom(1.0f, 4.0f);
478
                tstamp += tdelta;
479

    
480
                if (data.noOfActiveWaves<16)
481
                {
482
                    const float attackTime = GenerateFloatRandom(2.0f, 6.0f);
483
                    const float level = GenerateFloatRandom(0.1f, 1.0f);
484
                    const float pos = GenerateFloatRandom(0.0f, 1.0f);
485
                    OceanWave* p = InitializeWave(SR, attackTime, level, pos);
486
                    if (p != NULL)
487
                    {
488
                        /* Post wave to audio callback */
489
                        PaUtil_WriteRingBuffer(&data.rBufToRT, &p, 1);
490
                        ++data.noOfActiveWaves;
491

    
492
                        printf("Starting wave at level = %.2f, attack = %.2lf, pos = %.2lf\n", level, attackTime, pos);
493
                    }
494
                }
495
            }
496
        }
497
        else
498
        {
499
            if (data.noOfActiveWaves == 0)
500
            {
501
                printf("All waves finished!\n");
502
                break;
503
            }
504
        }
505

    
506
        Pa_Sleep(100);
507
    }
508
    if( err < 0 ) goto error;
509

    
510
    err = Pa_CloseStream( stream );
511
    if( err != paNoError ) goto error;
512

    
513
    if (data.rBufToRTData)
514
    {
515
        PaUtil_FreeMemory(data.rBufToRTData);
516
    }
517
    if (data.rBufFromRTData)
518
    {
519
        PaUtil_FreeMemory(data.rBufFromRTData);
520
    }
521
    
522
    Pa_Sleep(1000);
523

    
524
    Pa_Terminate();
525
    return 0;
526

    
527
error:
528
    Pa_Terminate();
529
    fprintf( stderr, "An error occured while using the portaudio stream\n" );
530
    fprintf( stderr, "Error number: %d\n", err );
531
    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
532
    return 0;
533
}