patest_stop.c (10796B)
1 /** @file patest_stop.c 2 @ingroup test_src 3 @brief Test different ways of stopping audio. 4 5 Test the three ways of stopping audio: 6 - calling Pa_StopStream(), 7 - calling Pa_AbortStream(), 8 - and returning a 1 from the callback function. 9 10 A long latency is set up so that you can hear the difference. 11 Then a simple 8 note sequence is repeated twice. 12 The program will print what you should hear. 13 14 @author Phil Burk <philburk@softsynth.com> 15 */ 16 /* 17 * $Id$ 18 * 19 * This program uses the PortAudio Portable Audio Library. 20 * For more information see: http://www.portaudio.com 21 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 22 * 23 * Permission is hereby granted, free of charge, to any person obtaining 24 * a copy of this software and associated documentation files 25 * (the "Software"), to deal in the Software without restriction, 26 * including without limitation the rights to use, copy, modify, merge, 27 * publish, distribute, sublicense, and/or sell copies of the Software, 28 * and to permit persons to whom the Software is furnished to do so, 29 * subject to the following conditions: 30 * 31 * The above copyright notice and this permission notice shall be 32 * included in all copies or substantial portions of the Software. 33 * 34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 35 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 36 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 37 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 38 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 39 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 40 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 41 */ 42 43 /* 44 * The text above constitutes the entire PortAudio license; however, 45 * the PortAudio community also makes the following non-binding requests: 46 * 47 * Any person wishing to distribute modifications to the Software is 48 * requested to send the modifications to the original developer so that 49 * they can be incorporated into the canonical version. It is also 50 * requested that these non-binding requests be included along with the 51 * license above. 52 */ 53 #include <stdio.h> 54 #include <math.h> 55 #include "portaudio.h" 56 57 #define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) 58 #define SLEEP_DUR (200) 59 #define SAMPLE_RATE (44100) 60 #define FRAMES_PER_BUFFER (256) 61 #define LATENCY_SECONDS (3.f) 62 #define FRAMES_PER_NOTE (SAMPLE_RATE/2) 63 #define MAX_REPEATS (2) 64 #define FUNDAMENTAL (400.0f / SAMPLE_RATE) 65 #define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f) 66 #define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f) 67 #define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f) 68 #define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f) 69 #define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f) 70 #define MODE_FINISH (0) 71 #define MODE_STOP (1) 72 #define MODE_ABORT (2) 73 #ifndef M_PI 74 #define M_PI (3.14159265) 75 #endif 76 #define TABLE_SIZE (400) 77 78 typedef struct 79 { 80 float waveform[TABLE_SIZE + 1]; /* Add one for guard point for interpolation. */ 81 float phase_increment; 82 float phase; 83 float *tune; 84 int notesPerTune; 85 int frameCounter; 86 int noteCounter; 87 int repeatCounter; 88 PaTime outTime; 89 int stopMode; 90 int done; 91 } 92 paTestData; 93 94 /************* Prototypes *****************************/ 95 int TestStopMode( paTestData *data ); 96 float LookupWaveform( paTestData *data, float phase ); 97 98 /****************************************************** 99 * Convert phase between 0.0 and 1.0 to waveform value 100 * using linear interpolation. 101 */ 102 float LookupWaveform( paTestData *data, float phase ) 103 { 104 float fIndex = phase*TABLE_SIZE; 105 int index = (int) fIndex; 106 float fract = fIndex - index; 107 float lo = data->waveform[index]; 108 float hi = data->waveform[index+1]; 109 float val = lo + fract*(hi-lo); 110 return val; 111 } 112 113 /* This routine will be called by the PortAudio engine when audio is needed. 114 ** It may called at interrupt level on some machines so don't do anything 115 ** that could mess up the system like calling malloc() or free(). 116 */ 117 static int patestCallback( const void *inputBuffer, void *outputBuffer, 118 unsigned long framesPerBuffer, 119 const PaStreamCallbackTimeInfo* timeInfo, 120 PaStreamCallbackFlags statusFlags, 121 void *userData ) 122 { 123 paTestData *data = (paTestData*)userData; 124 float *out = (float*)outputBuffer; 125 float value; 126 unsigned int i = 0; 127 int finished = paContinue; 128 129 (void) inputBuffer; /* Prevent unused variable warnings. */ 130 (void) timeInfo; 131 (void) statusFlags; 132 133 134 /* data->outTime = outTime; */ 135 136 if( !data->done ) 137 { 138 for( i=0; i<framesPerBuffer; i++ ) 139 { 140 /* Are we done with this note? */ 141 if( data->frameCounter >= FRAMES_PER_NOTE ) 142 { 143 data->noteCounter += 1; 144 data->frameCounter = 0; 145 /* Are we done with this tune? */ 146 if( data->noteCounter >= data->notesPerTune ) 147 { 148 data->noteCounter = 0; 149 data->repeatCounter += 1; 150 /* Are we totally done? */ 151 if( data->repeatCounter >= MAX_REPEATS ) 152 { 153 data->done = 1; 154 if( data->stopMode == MODE_FINISH ) 155 { 156 finished = paComplete; 157 break; 158 } 159 } 160 } 161 data->phase_increment = data->tune[data->noteCounter]; 162 } 163 value = LookupWaveform(data, data->phase); 164 *out++ = value; /* left */ 165 *out++ = value; /* right */ 166 data->phase += data->phase_increment; 167 if( data->phase >= 1.0f ) data->phase -= 1.0f; 168 169 data->frameCounter += 1; 170 } 171 } 172 /* zero remainder of final buffer */ 173 for( ; i<framesPerBuffer; i++ ) 174 { 175 *out++ = 0; /* left */ 176 *out++ = 0; /* right */ 177 } 178 return finished; 179 } 180 /*******************************************************************/ 181 int main(void); 182 int main(void) 183 { 184 paTestData data; 185 int i; 186 float simpleTune[] = { NOTE_0, NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_3, NOTE_2, NOTE_1 }; 187 188 printf("PortAudio Test: play song and test stopping. ask for %f seconds latency\n", LATENCY_SECONDS ); 189 /* initialise sinusoidal wavetable */ 190 for( i=0; i<TABLE_SIZE; i++ ) 191 { 192 data.waveform[i] = (float) ( 193 (0.2 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. )) + 194 (0.2 * sin( ((double)(3*i)/(double)TABLE_SIZE) * M_PI * 2. )) + 195 (0.1 * sin( ((double)(5*i)/(double)TABLE_SIZE) * M_PI * 2. )) 196 ); 197 } 198 data.waveform[TABLE_SIZE] = data.waveform[0]; /* Set guard point. */ 199 data.tune = &simpleTune[0]; 200 data.notesPerTune = sizeof(simpleTune) / sizeof(float); 201 202 printf("Test MODE_FINISH - callback returns 1.\n"); 203 printf("Should hear entire %d note tune repeated twice.\n", data.notesPerTune); 204 data.stopMode = MODE_FINISH; 205 if( TestStopMode( &data ) != paNoError ) 206 { 207 printf("Test of MODE_FINISH failed!\n"); 208 goto error; 209 } 210 211 printf("Test MODE_STOP - stop when song is done.\n"); 212 printf("Should hear entire %d note tune repeated twice.\n", data.notesPerTune); 213 data.stopMode = MODE_STOP; 214 if( TestStopMode( &data ) != paNoError ) 215 { 216 printf("Test of MODE_STOP failed!\n"); 217 goto error; 218 } 219 220 printf("Test MODE_ABORT - abort immediately.\n"); 221 printf("Should hear last repetition cut short by %f seconds.\n", LATENCY_SECONDS); 222 data.stopMode = MODE_ABORT; 223 if( TestStopMode( &data ) != paNoError ) 224 { 225 printf("Test of MODE_ABORT failed!\n"); 226 goto error; 227 } 228 229 return 0; 230 231 error: 232 return 1; 233 } 234 235 int TestStopMode( paTestData *data ) 236 { 237 PaStreamParameters outputParameters; 238 PaStream *stream; 239 PaError err; 240 241 data->done = 0; 242 data->phase = 0.0; 243 data->frameCounter = 0; 244 data->noteCounter = 0; 245 data->repeatCounter = 0; 246 data->phase_increment = data->tune[data->noteCounter]; 247 248 err = Pa_Initialize(); 249 if( err != paNoError ) goto error; 250 251 outputParameters.device = OUTPUT_DEVICE; 252 if (outputParameters.device == paNoDevice) { 253 fprintf(stderr,"Error: No default output device.\n"); 254 goto error; 255 } 256 outputParameters.channelCount = 2; /* stereo output */ 257 outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */ 258 outputParameters.suggestedLatency = LATENCY_SECONDS; 259 outputParameters.hostApiSpecificStreamInfo = NULL; 260 261 err = Pa_OpenStream( 262 &stream, 263 NULL, /* no input */ 264 &outputParameters, 265 SAMPLE_RATE, 266 FRAMES_PER_BUFFER, /* frames per buffer */ 267 paClipOff, /* we won't output out of range samples so don't bother clipping them */ 268 patestCallback, 269 data ); 270 if( err != paNoError ) goto error; 271 272 err = Pa_StartStream( stream ); 273 if( err != paNoError ) goto error; 274 275 if( data->stopMode == MODE_FINISH ) 276 { 277 while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) 278 { 279 /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, 280 data->noteCounter, data->repeatCounter ); 281 fflush(stdout); */ 282 Pa_Sleep( SLEEP_DUR ); 283 } 284 if( err < 0 ) goto error; 285 } 286 else 287 { 288 while( data->repeatCounter < MAX_REPEATS ) 289 { 290 /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, 291 data->noteCounter, data->repeatCounter ); 292 fflush(stdout); */ 293 Pa_Sleep( SLEEP_DUR ); 294 } 295 } 296 297 if( data->stopMode == MODE_ABORT ) 298 { 299 printf("Call Pa_AbortStream()\n"); 300 err = Pa_AbortStream( stream ); 301 } 302 else 303 { 304 printf("Call Pa_StopStream()\n"); 305 err = Pa_StopStream( stream ); 306 } 307 if( err != paNoError ) goto error; 308 309 printf("Call Pa_CloseStream()\n"); fflush(stdout); 310 err = Pa_CloseStream( stream ); 311 if( err != paNoError ) goto error; 312 313 Pa_Terminate(); 314 printf("Test finished.\n"); 315 316 return err; 317 318 error: 319 Pa_Terminate(); 320 fprintf( stderr, "An error occured while using the portaudio stream\n" ); 321 fprintf( stderr, "Error number: %d\n", err ); 322 fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); 323 return err; 324 }