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

patest_stop_playout.c (15812B)


      1 /** @file patest_stop_playout.c
      2 	@ingroup test_src
      3 	@brief Test whether all queued samples are played when Pa_StopStream()
      4             is used with a callback or read/write stream, or when the callback
      5             returns paComplete.
      6 	@author Ross Bencina <rossb@audiomulch.com>
      7 */
      8 /*
      9  * $Id$
     10  *
     11  * This program uses the PortAudio Portable Audio Library.
     12  * For more information see: http://www.portaudio.com/
     13  * Copyright (c) 1999-2004 Ross Bencina and Phil Burk
     14  *
     15  * Permission is hereby granted, free of charge, to any person obtaining
     16  * a copy of this software and associated documentation files
     17  * (the "Software"), to deal in the Software without restriction,
     18  * including without limitation the rights to use, copy, modify, merge,
     19  * publish, distribute, sublicense, and/or sell copies of the Software,
     20  * and to permit persons to whom the Software is furnished to do so,
     21  * subject to the following conditions:
     22  *
     23  * The above copyright notice and this permission notice shall be
     24  * included in all copies or substantial portions of the Software.
     25  *
     26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     29  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
     30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     31  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     32  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     33  */
     34 
     35 /*
     36  * The text above constitutes the entire PortAudio license; however, 
     37  * the PortAudio community also makes the following non-binding requests:
     38  *
     39  * Any person wishing to distribute modifications to the Software is
     40  * requested to send the modifications to the original developer so that
     41  * they can be incorporated into the canonical version. It is also 
     42  * requested that these non-binding requests be included along with the 
     43  * license above.
     44  */
     45 #include <stdio.h>
     46 #include <math.h>
     47 #include "portaudio.h"
     48 
     49 #define SAMPLE_RATE         (44100)
     50 #define FRAMES_PER_BUFFER   (1024)
     51 
     52 #define TONE_SECONDS        (1)      /* long tone */
     53 #define TONE_FADE_SECONDS   (.04)    /* fades at start and end of long tone */
     54 #define GAP_SECONDS         (.25)     /* gap between long tone and blip */
     55 #define BLIP_SECONDS        (.035)   /* short blip */
     56 
     57 #define NUM_REPEATS         (3)
     58 
     59 #ifndef M_PI
     60 #define M_PI (3.14159265)
     61 #endif
     62 
     63 #define TABLE_SIZE          (2048)
     64 typedef struct
     65 {
     66     float sine[TABLE_SIZE+1];
     67 
     68     int repeatCount;
     69     
     70     double phase;
     71     double lowIncrement, highIncrement;
     72     
     73     int gap1Length, toneLength, toneFadesLength, gap2Length, blipLength;
     74     int gap1Countdown, toneCountdown, gap2Countdown, blipCountdown;
     75 }
     76 TestData;
     77 
     78 
     79 static void RetriggerTestSignalGenerator( TestData *data )
     80 {
     81     data->phase = 0.;
     82     data->gap1Countdown = data->gap1Length;
     83     data->toneCountdown = data->toneLength;
     84     data->gap2Countdown = data->gap2Length;
     85     data->blipCountdown = data->blipLength;
     86 }
     87 
     88 
     89 static void ResetTestSignalGenerator( TestData *data )
     90 {
     91     data->repeatCount = 0;
     92     RetriggerTestSignalGenerator( data );
     93 }
     94 
     95 
     96 static void InitTestSignalGenerator( TestData *data )
     97 {
     98     int signalLengthModBufferLength, i;
     99 
    100     /* initialise sinusoidal wavetable */
    101     for( i=0; i<TABLE_SIZE; i++ )
    102     {
    103         data->sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
    104     }
    105     data->sine[TABLE_SIZE] = data->sine[0]; /* guard point for linear interpolation */
    106 
    107 
    108     
    109     data->lowIncrement = (330. / SAMPLE_RATE) * TABLE_SIZE;
    110     data->highIncrement = (1760. / SAMPLE_RATE) * TABLE_SIZE;
    111 
    112     data->gap1Length = GAP_SECONDS * SAMPLE_RATE;
    113     data->toneLength = TONE_SECONDS * SAMPLE_RATE;
    114     data->toneFadesLength = TONE_FADE_SECONDS * SAMPLE_RATE;
    115     data->gap2Length = GAP_SECONDS * SAMPLE_RATE;
    116     data->blipLength = BLIP_SECONDS * SAMPLE_RATE;
    117 
    118     /* adjust signal length to be a multiple of the buffer length */
    119     signalLengthModBufferLength = (data->gap1Length + data->toneLength + data->gap2Length + data->blipLength) % FRAMES_PER_BUFFER;
    120     if( signalLengthModBufferLength > 0 )
    121         data->toneLength += signalLengthModBufferLength;
    122 
    123     ResetTestSignalGenerator( data );
    124 }
    125 
    126 
    127 #define MIN( a, b ) (((a)<(b))?(a):(b))
    128 
    129 static void GenerateTestSignal( TestData *data, float *stereo, int frameCount )
    130 {
    131     int framesGenerated = 0;
    132     float output;
    133     long index;
    134     float fraction;
    135     int count, i;
    136 
    137     while( framesGenerated < frameCount && data->repeatCount < NUM_REPEATS )
    138     {
    139         if( framesGenerated < frameCount && data->gap1Countdown > 0 ){
    140             count = MIN( frameCount - framesGenerated, data->gap1Countdown );
    141             for( i=0; i < count; ++i )
    142             {
    143                 *stereo++ = 0.f;
    144                 *stereo++ = 0.f;
    145             }
    146 
    147             data->gap1Countdown -= count;
    148             framesGenerated += count;
    149         }
    150     
    151         if( framesGenerated < frameCount && data->toneCountdown > 0 ){
    152             count = MIN( frameCount - framesGenerated, data->toneCountdown );
    153             for( i=0; i < count; ++i )
    154             {
    155                 /* tone with data->lowIncrement phase increment */
    156                 index = (long)data->phase;
    157                 fraction = data->phase - index;
    158                 output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
    159 
    160                 data->phase += data->lowIncrement;
    161                 while( data->phase >= TABLE_SIZE )
    162                     data->phase -= TABLE_SIZE;
    163 
    164                 /* apply fade to ends */
    165 
    166                 if( data->toneCountdown < data->toneFadesLength )
    167                 {
    168                     /* cosine-bell fade out at end */
    169                     output *= (-cos(((float)data->toneCountdown / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
    170                 }
    171                 else if( data->toneCountdown > data->toneLength - data->toneFadesLength ) 
    172                 {
    173                     /* cosine-bell fade in at start */
    174                     output *= (cos(((float)(data->toneCountdown - (data->toneLength - data->toneFadesLength)) / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
    175                 }
    176 
    177                 output *= .5; /* play tone half as loud as blip */
    178             
    179                 *stereo++ = output;
    180                 *stereo++ = output;
    181 
    182                 data->toneCountdown--;
    183             }                         
    184 
    185             framesGenerated += count;
    186         }
    187 
    188         if( framesGenerated < frameCount && data->gap2Countdown > 0 ){
    189             count = MIN( frameCount - framesGenerated, data->gap2Countdown );
    190             for( i=0; i < count; ++i )
    191             {
    192                 *stereo++ = 0.f;
    193                 *stereo++ = 0.f;
    194             }
    195 
    196             data->gap2Countdown -= count;
    197             framesGenerated += count;
    198         }
    199 
    200         if( framesGenerated < frameCount && data->blipCountdown > 0 ){
    201             count = MIN( frameCount - framesGenerated, data->blipCountdown );
    202             for( i=0; i < count; ++i )
    203             {
    204                 /* tone with data->highIncrement phase increment */
    205                 index = (long)data->phase;
    206                 fraction = data->phase - index;
    207                 output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
    208 
    209                 data->phase += data->highIncrement;
    210                 while( data->phase >= TABLE_SIZE )
    211                     data->phase -= TABLE_SIZE;
    212 
    213                 /* cosine-bell envelope over whole blip */
    214                 output *= (-cos( ((float)data->blipCountdown / (float)data->blipLength) * 2. * M_PI) + 1.) * .5;
    215                 
    216                 *stereo++ = output;
    217                 *stereo++ = output;
    218 
    219                 data->blipCountdown--;
    220             }
    221 
    222             framesGenerated += count;
    223         }
    224 
    225 
    226         if( data->blipCountdown == 0 )
    227         {
    228             RetriggerTestSignalGenerator( data );
    229             data->repeatCount++;
    230         }        
    231     }
    232 
    233     if( framesGenerated < frameCount )
    234     {
    235         count = frameCount - framesGenerated;
    236         for( i=0; i < count; ++i )
    237         {
    238             *stereo++ = 0.f;
    239             *stereo++ = 0.f;
    240         }
    241     }
    242 }
    243 
    244 
    245 static int IsTestSignalFinished( TestData *data )
    246 {
    247     if( data->repeatCount >= NUM_REPEATS )
    248         return 1;
    249     else
    250         return 0;
    251 }
    252 
    253 
    254 static int TestCallback1( const void *inputBuffer, void *outputBuffer,
    255                             unsigned long frameCount,
    256                             const PaStreamCallbackTimeInfo* timeInfo,
    257                             PaStreamCallbackFlags statusFlags,
    258                             void *userData )
    259 {
    260     (void) inputBuffer; /* Prevent unused variable warnings. */
    261     (void) timeInfo;
    262     (void) statusFlags;
    263 
    264     GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
    265 
    266     if( IsTestSignalFinished( (TestData*)userData ) )
    267         return paComplete;
    268     else
    269         return paContinue;
    270 }
    271 
    272 
    273 volatile int testCallback2Finished = 0;
    274 
    275 static int TestCallback2( const void *inputBuffer, void *outputBuffer,
    276                             unsigned long frameCount,
    277                             const PaStreamCallbackTimeInfo* timeInfo,
    278                             PaStreamCallbackFlags statusFlags,
    279                             void *userData )
    280 {
    281     (void) inputBuffer; /* Prevent unused variable warnings. */
    282     (void) timeInfo;
    283     (void) statusFlags;
    284 
    285     GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
    286 
    287     if( IsTestSignalFinished( (TestData*)userData ) )
    288         testCallback2Finished = 1;
    289    
    290     return paContinue;
    291 }
    292 
    293 /*******************************************************************/
    294 int main(void);
    295 int main(void)
    296 {
    297     PaStreamParameters outputParameters;
    298     PaStream *stream;
    299     PaError err;
    300     TestData data;
    301     float writeBuffer[ FRAMES_PER_BUFFER * 2 ];
    302     
    303     printf("PortAudio Test: check that stopping stream plays out all queued samples. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
    304 
    305     InitTestSignalGenerator( &data );
    306     
    307     err = Pa_Initialize();
    308     if( err != paNoError ) goto error;
    309 
    310     outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
    311     outputParameters.channelCount = 2;       /* stereo output */
    312     outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
    313     outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
    314     outputParameters.hostApiSpecificStreamInfo = NULL;
    315 
    316 /* test paComplete ---------------------------------------------------------- */
    317 
    318     ResetTestSignalGenerator( &data );
    319 
    320     err = Pa_OpenStream(
    321               &stream,
    322               NULL, /* no input */
    323               &outputParameters,
    324               SAMPLE_RATE,
    325               FRAMES_PER_BUFFER,
    326               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
    327               TestCallback1,
    328               &data );
    329     if( err != paNoError ) goto error;
    330 
    331     err = Pa_StartStream( stream );
    332     if( err != paNoError ) goto error;
    333 
    334     printf("\nPlaying 'tone-blip' %d times using callback, stops by returning paComplete from callback.\n", NUM_REPEATS );
    335     printf("If final blip is not intact, callback+paComplete implementation may be faulty.\n\n" );
    336 
    337     while( (err = Pa_IsStreamActive( stream )) == 1 )
    338         Pa_Sleep( 2 );
    339 
    340     if( err != 0 ) goto error;
    341 
    342     err = Pa_StopStream( stream );
    343     if( err != paNoError ) goto error;
    344 
    345     err = Pa_CloseStream( stream );
    346     if( err != paNoError ) goto error;
    347 
    348     Pa_Sleep( 500 );
    349 
    350 
    351 /* test paComplete ---------------------------------------------------------- */
    352 
    353     ResetTestSignalGenerator( &data );
    354 
    355     err = Pa_OpenStream(
    356               &stream,
    357               NULL, /* no input */
    358               &outputParameters,
    359               SAMPLE_RATE,
    360               FRAMES_PER_BUFFER,
    361               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
    362               TestCallback1,
    363               &data );
    364     if( err != paNoError ) goto error;
    365 
    366     err = Pa_StartStream( stream );
    367     if( err != paNoError ) goto error;
    368 
    369     printf("\nPlaying 'tone-blip' %d times using callback, stops by returning paComplete from callback.\n", NUM_REPEATS );
    370     printf("If final blip is not intact or is followed by garbage, callback+paComplete implementation may be faulty.\n\n" );
    371 
    372     while( (err = Pa_IsStreamActive( stream )) == 1 )
    373         Pa_Sleep( 5 );
    374 
    375     printf("Waiting 5 seconds after paComplete before stopping the stream. Tests that buffers are flushed correctly.\n");
    376     Pa_Sleep( 5000 );
    377 
    378     if( err != 0 ) goto error;
    379 
    380     err = Pa_StopStream( stream );
    381     if( err != paNoError ) goto error;
    382 
    383     err = Pa_CloseStream( stream );
    384     if( err != paNoError ) goto error;
    385 
    386     Pa_Sleep( 500 );
    387 
    388 
    389 /* test Pa_StopStream() with callback --------------------------------------- */
    390 
    391     ResetTestSignalGenerator( &data );
    392 
    393     testCallback2Finished = 0;
    394     
    395     err = Pa_OpenStream(
    396               &stream,
    397               NULL, /* no input */
    398               &outputParameters,
    399               SAMPLE_RATE,
    400               FRAMES_PER_BUFFER,
    401               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
    402               TestCallback2,
    403               &data );
    404     if( err != paNoError ) goto error;
    405 
    406     err = Pa_StartStream( stream );
    407     if( err != paNoError ) goto error;
    408 
    409 
    410     printf("\nPlaying 'tone-blip' %d times using callback, stops by calling Pa_StopStream.\n", NUM_REPEATS );
    411     printf("If final blip is not intact, callback+Pa_StopStream implementation may be faulty.\n\n" );
    412 
    413     /* note that polling a volatile flag is not a good way to synchronise with
    414         the callback, but it's the best we can do portably. */
    415     while( !testCallback2Finished )
    416         Pa_Sleep( 2 );
    417 
    418     Pa_Sleep( 500 );
    419 
    420     err = Pa_StopStream( stream );
    421     if( err != paNoError ) goto error;
    422     
    423 
    424     err = Pa_CloseStream( stream );
    425     if( err != paNoError ) goto error;
    426 
    427     Pa_Sleep( 500 );
    428 
    429 /* test Pa_StopStream() with Pa_WriteStream --------------------------------- */
    430 
    431     ResetTestSignalGenerator( &data );
    432 
    433     err = Pa_OpenStream(
    434               &stream,
    435               NULL, /* no input */
    436               &outputParameters,
    437               SAMPLE_RATE,
    438               FRAMES_PER_BUFFER,
    439               paClipOff,      /* we won't output out of range samples so don't bother clipping them */
    440               NULL, /* no callback, use blocking API */
    441               NULL ); /* no callback, so no callback userData */
    442     if( err != paNoError ) goto error;
    443 
    444     err = Pa_StartStream( stream );
    445     if( err != paNoError ) goto error;
    446 
    447 
    448     printf("\nPlaying 'tone-blip' %d times using Pa_WriteStream, stops by calling Pa_StopStream.\n", NUM_REPEATS );
    449     printf("If final blip is not intact, Pa_WriteStream+Pa_StopStream implementation may be faulty.\n\n" );
    450 
    451     do{
    452         GenerateTestSignal( &data, writeBuffer, FRAMES_PER_BUFFER );
    453         err = Pa_WriteStream( stream, writeBuffer, FRAMES_PER_BUFFER );
    454         if( err != paNoError ) goto error;
    455         
    456     }while( !IsTestSignalFinished( &data ) );
    457 
    458     err = Pa_StopStream( stream );
    459     if( err != paNoError ) goto error;
    460     
    461 
    462     err = Pa_CloseStream( stream );
    463     if( err != paNoError ) goto error;
    464 
    465 /* -------------------------------------------------------------------------- */
    466     
    467     Pa_Terminate();
    468     printf("Test finished.\n");
    469     
    470     return err;
    471     
    472 error:
    473     Pa_Terminate();
    474     fprintf( stderr, "An error occured while using the portaudio stream\n" );
    475     fprintf( stderr, "Error number: %d\n", err );
    476     fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
    477     return err;
    478 }