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  */
LookupWaveform(paTestData * data,float phase)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 */
patestCallback(const void * inputBuffer,void * outputBuffer,unsigned long framesPerBuffer,const PaStreamCallbackTimeInfo * timeInfo,PaStreamCallbackFlags statusFlags,void * userData)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);
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 
TestStopMode(paTestData * data)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 }
325