Browse Source

pa_mac_core: stop stream instead of asserting

Bug: 158
tags/pa_stable_v190600_20161030
Phil Burk 3 years ago
parent
commit
09588cc5d6
3 changed files with 284 additions and 16 deletions
  1. 3
    0
      .gitignore
  2. 38
    16
      src/hostapi/coreaudio/pa_mac_core.c
  3. 243
    0
      test/patest_unplug.c

+ 3
- 0
.gitignore View File

@@ -4,6 +4,9 @@
4 4
 *.o
5 5
 *.obj
6 6
 
7
+# annoying files
8
+**/.DS_Store
9
+
7 10
 # Generated by configure and make
8 11
 Makefile
9 12
 bin-stamp

+ 38
- 16
src/hostapi/coreaudio/pa_mac_core.c View File

@@ -2277,9 +2277,11 @@ static OSStatus AudioIOProc( void *inRefCon,
2277 2277
                     INPUT_ELEMENT,
2278 2278
                     inNumberFrames,
2279 2279
                     &stream->inputAudioBufferList );
2280
-      /* FEEDBACK: I'm not sure what to do when this call fails. There's nothing in the PA API to
2281
-       * do about failures in the callback system. */
2282
-      assert( !err );
2280
+      if(err != noErr)
2281
+      {
2282
+        /* We used to assert on error. Now we try to just stop the stream. */
2283
+        goto stop_stream;
2284
+      }
2283 2285
 
2284 2286
       PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2285 2287
       PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
@@ -2370,8 +2372,12 @@ static OSStatus AudioIOProc( void *inRefCon,
2370 2372
                   }
2371 2373
                }
2372 2374
                ERR( err );
2373
-               assert( !err );
2374
-               
2375
+               if(err != noErr)
2376
+               {
2377
+                 /* We used to assert on error. Now we try to just stop the stream. */
2378
+                 goto stop_stream;
2379
+               }
2380
+
2375 2381
                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
2376 2382
                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
2377 2383
                                    0,
@@ -2477,9 +2483,13 @@ static OSStatus AudioIOProc( void *inRefCon,
2477 2483
          if( err == -10874 )
2478 2484
             inNumberFrames /= 2;
2479 2485
       } while( err == -10874 && inNumberFrames > 1 );
2480
-      /* FEEDBACK: I'm not sure what to do when this call fails */
2481 2486
       ERR( err );
2482
-      assert( !err );
2487
+      if(err != noErr)
2488
+      {
2489
+          /* We used to assert on error. Now we try to just stop the stream. */
2490
+          goto stop_stream;
2491
+      }
2492
+
2483 2493
       if( stream->inputSRConverter || stream->outputUnit )
2484 2494
       {
2485 2495
          /* If this is duplex or we use a converter, put the data
@@ -2522,11 +2532,11 @@ static OSStatus AudioIOProc( void *inRefCon,
2522 2532
           * chunks, and let the BufferProcessor deal with the rest.
2523 2533
           *
2524 2534
           */
2525
-         /*This might be too big or small depending on SR conversion*/
2535
+         /* This might be too big or small depending on SR conversion. */
2526 2536
          float data[ chan * inNumberFrames ];
2527 2537
          OSStatus err;
2528 2538
          do
2529
-         { /*Run the buffer processor until we are out of data*/
2539
+         { /* Run the buffer processor until we are out of data. */
2530 2540
             UInt32 size;
2531 2541
             long f;
2532 2542
 
@@ -2539,7 +2549,12 @@ static OSStatus AudioIOProc( void *inRefCon,
2539 2549
                           (void *)data );
2540 2550
             if( err != RING_BUFFER_EMPTY )
2541 2551
                ERR( err );
2542
-            assert( err == 0 || err == RING_BUFFER_EMPTY );
2552
+            if( err != noErr && err != RING_BUFFER_EMPTY )
2553
+            {
2554
+              /* We used to assert on error. Now we try to just stop the stream. */
2555
+              goto stop_stream;
2556
+            }
2557
+
2543 2558
 
2544 2559
             f = size / ( chan * sizeof(float) );
2545 2560
             PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
@@ -2564,19 +2579,26 @@ static OSStatus AudioIOProc( void *inRefCon,
2564 2579
 
2565 2580
    switch( callbackResult )
2566 2581
    {
2567
-   case paContinue: break;
2582
+   case paContinue:
2583
+           break;
2568 2584
    case paComplete:
2569 2585
    case paAbort:
2570
-      stream->state = CALLBACK_STOPPED ;
2571
-      if( stream->outputUnit )
2572
-         AudioOutputUnitStop(stream->outputUnit);
2573
-      if( stream->inputUnit )
2574
-         AudioOutputUnitStop(stream->inputUnit);
2586
+           goto stop_stream;
2575 2587
       break;
2576 2588
    }
2577 2589
 
2578 2590
    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2579 2591
    return noErr;
2592
+
2593
+stop_stream:
2594
+    stream->state = CALLBACK_STOPPED ;
2595
+    if( stream->outputUnit )
2596
+        AudioOutputUnitStop(stream->outputUnit);
2597
+    if( stream->inputUnit )
2598
+        AudioOutputUnitStop(stream->inputUnit);
2599
+
2600
+    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
2601
+    return noErr;
2580 2602
 }
2581 2603
 
2582 2604
 

+ 243
- 0
test/patest_unplug.c View File

@@ -0,0 +1,243 @@
1
+/** @file patest_unplug.c
2
+	@ingroup test_src
3
+	@brief Debug a crash involving unplugging a USB device.
4
+	@author Phil Burk  http://www.softsynth.com
5
+*/
6
+/*
7
+ * $Id$
8
+ *
9
+ * This program uses the PortAudio Portable Audio Library.
10
+ * For more information see: http://www.portaudio.com
11
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
12
+ *
13
+ * Permission is hereby granted, free of charge, to any person obtaining
14
+ * a copy of this software and associated documentation files
15
+ * (the "Software"), to deal in the Software without restriction,
16
+ * including without limitation the rights to use, copy, modify, merge,
17
+ * publish, distribute, sublicense, and/or sell copies of the Software,
18
+ * and to permit persons to whom the Software is furnished to do so,
19
+ * subject to the following conditions:
20
+ *
21
+ * The above copyright notice and this permission notice shall be
22
+ * included in all copies or substantial portions of the Software.
23
+ *
24
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
28
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
29
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+ */
32
+
33
+/*
34
+ * The text above constitutes the entire PortAudio license; however, 
35
+ * the PortAudio community also makes the following non-binding requests:
36
+ *
37
+ * Any person wishing to distribute modifications to the Software is
38
+ * requested to send the modifications to the original developer so that
39
+ * they can be incorporated into the canonical version. It is also 
40
+ * requested that these non-binding requests be included along with the 
41
+ * license above.
42
+ */
43
+
44
+#include <stdio.h>
45
+#include <stdlib.h>
46
+#include <memory.h>
47
+#include <math.h>
48
+#include "portaudio.h"
49
+
50
+#define NUM_SECONDS   (8)
51
+#define SAMPLE_RATE   (44100)
52
+#ifndef M_PI
53
+#define M_PI  (3.14159265)
54
+#endif
55
+#define TABLE_SIZE        (200)
56
+#define FRAMES_PER_BUFFER  (64)
57
+#define MAX_CHANNELS        (8)
58
+
59
+typedef struct
60
+{
61
+    short sine[TABLE_SIZE];
62
+    int32_t phases[MAX_CHANNELS];
63
+    int32_t numChannels;
64
+    int32_t sampsToGo;
65
+}
66
+paTestData;
67
+
68
+
69
+static int inputCallback( const void *inputBuffer, void *outputBuffer,
70
+                          unsigned long framesPerBuffer,
71
+                          const PaStreamCallbackTimeInfo* timeInfo,
72
+                          PaStreamCallbackFlags statusFlags,
73
+                          void *userData )
74
+{
75
+    paTestData *data = (paTestData*)userData;
76
+    int finished = 0;
77
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
78
+    (void) outputBuffer; /* Prevent "unused variable" warnings. */
79
+
80
+    data->sampsToGo -= framesPerBuffer;
81
+    if (data->sampsToGo <= 0)
82
+    {
83
+        data->sampsToGo = 0;
84
+        finished = 1;
85
+    }
86
+    return finished;
87
+}
88
+
89
+static int outputCallback( const void *inputBuffer, void *outputBuffer,
90
+                            unsigned long framesPerBuffer,
91
+                            const PaStreamCallbackTimeInfo* timeInfo,
92
+                            PaStreamCallbackFlags statusFlags,
93
+                            void *userData )
94
+{
95
+    paTestData *data = (paTestData*)userData;
96
+    short *out = (short*)outputBuffer;
97
+    unsigned int i;
98
+    int finished = 0;
99
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
100
+
101
+    for( i=0; i<framesPerBuffer; i++ )
102
+    {
103
+        for (int channelIndex = 0; channelIndex < data->numChannels; channelIndex++)
104
+        {
105
+            int phase = data->phases[channelIndex];
106
+            *out++ = data->sine[phase];
107
+            phase += channelIndex + 2;
108
+            if( phase >= TABLE_SIZE ) phase -= TABLE_SIZE;
109
+            data->phases[channelIndex] = phase;
110
+        }
111
+    }
112
+    return finished;
113
+}
114
+
115
+/*******************************************************************/
116
+int main(int argc, char **args);
117
+int main(int argc, char **args)
118
+{
119
+    PaStreamParameters inputParameters;
120
+    PaStreamParameters outputParameters;
121
+    PaStream *inputStream;
122
+    PaStream *outputStream;
123
+    const PaDeviceInfo *deviceInfo;
124
+    PaError err;
125
+    paTestData data;
126
+    int i;
127
+    int totalSamps;
128
+    int inputDevice = -1;
129
+    int outputDevice = -1;
130
+
131
+    printf("Test unplugging a USB device.\n");
132
+
133
+    if( argc > 1 ) {
134
+       inputDevice = outputDevice = atoi( args[1] );
135
+       printf("Using device number %d.\n\n", inputDevice );
136
+    } else {
137
+       printf("Using default device.\n\n" );
138
+    }
139
+
140
+    memset(&data, 0, sizeof(data));
141
+
142
+    /* initialise sinusoidal wavetable */
143
+    for( i=0; i<TABLE_SIZE; i++ )
144
+    {
145
+        data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
146
+    }
147
+    data.numChannels = 2;
148
+    data.sampsToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
149
+
150
+
151
+    err = Pa_Initialize();
152
+    if( err != paNoError ) goto error;
153
+
154
+    if( inputDevice == -1 )
155
+        inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
156
+    else
157
+        inputParameters.device = inputDevice ;
158
+
159
+    if (inputParameters.device == paNoDevice) {
160
+        fprintf(stderr,"Error: No default input device.\n");
161
+        goto error;
162
+    }
163
+
164
+    if( outputDevice == -1 )
165
+        outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
166
+    else
167
+        outputParameters.device = outputDevice ;
168
+
169
+    if (outputParameters.device == paNoDevice) {
170
+        fprintf(stderr,"Error: No default output device.\n");
171
+        goto error;
172
+    }
173
+
174
+    inputParameters.channelCount = 2;
175
+    inputParameters.sampleFormat = paInt16;
176
+    deviceInfo = Pa_GetDeviceInfo( inputParameters.device );
177
+    if( deviceInfo == NULL )
178
+    {
179
+        fprintf( stderr, "No matching input device.\n" );
180
+        goto error;
181
+    }
182
+    inputParameters.suggestedLatency = deviceInfo->defaultLowInputLatency;
183
+    inputParameters.hostApiSpecificStreamInfo = NULL;
184
+    err = Pa_OpenStream(
185
+                &inputStream,
186
+                &inputParameters,
187
+                NULL,
188
+                SAMPLE_RATE,
189
+                FRAMES_PER_BUFFER,
190
+                0,
191
+                inputCallback,
192
+                &data );
193
+    if( err != paNoError ) goto error;
194
+
195
+    outputParameters.channelCount = 2;
196
+    outputParameters.sampleFormat = paInt16;
197
+    deviceInfo = Pa_GetDeviceInfo( outputParameters.device );
198
+    if( deviceInfo == NULL )
199
+    {
200
+        fprintf( stderr, "No matching output device.\n" );
201
+        goto error;
202
+    }
203
+    outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
204
+    outputParameters.hostApiSpecificStreamInfo = NULL;
205
+    err = Pa_OpenStream(
206
+                &outputStream,
207
+                NULL,
208
+                &outputParameters,
209
+                SAMPLE_RATE,
210
+                FRAMES_PER_BUFFER,
211
+                (paClipOff | paDitherOff),
212
+                outputCallback,
213
+                &data );
214
+    if( err != paNoError ) goto error;
215
+
216
+    err = Pa_StartStream( inputStream );
217
+    if( err != paNoError ) goto error;
218
+    err = Pa_StartStream( outputStream );
219
+    if( err != paNoError ) goto error;
220
+
221
+    printf("When you hear sound, unplug the USB device.\n");
222
+    do
223
+    {
224
+        Pa_Sleep(500);
225
+        printf("Frames remaining = %d\n", data.sampsToGo);
226
+        printf("Pa_IsStreamActive(inputStream) = %d\n", Pa_IsStreamActive(inputStream));
227
+        printf("Pa_IsStreamActive(outputStream) = %d\n", Pa_IsStreamActive(outputStream));
228
+    } while( Pa_IsStreamActive(inputStream) && Pa_IsStreamActive(outputStream) );
229
+
230
+    err = Pa_CloseStream( inputStream );
231
+    if( err != paNoError ) goto error;
232
+    err = Pa_CloseStream( outputStream );
233
+    if( err != paNoError ) goto error;
234
+    Pa_Terminate();
235
+    return paNoError;
236
+error:
237
+    Pa_Terminate();
238
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
239
+    fprintf( stderr, "Error number: %d\n", err );
240
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
241
+    fprintf( stderr, "Host Error message: %s\n", Pa_GetLastHostErrorInfo()->errorText );
242
+    return err;
243
+}

Loading…
Cancel
Save