]> Repos - portaudio/commitdiff
pa_mac_core: stop stream instead of asserting
authorPhil Burk <philburk@mobileer.com>
Mon, 2 May 2016 01:08:38 +0000 (18:08 -0700)
committerPhil Burk <philburk@mobileer.com>
Mon, 2 May 2016 01:10:23 +0000 (18:10 -0700)
Bug: 158

.gitignore
src/hostapi/coreaudio/pa_mac_core.c
test/patest_unplug.c [new file with mode: 0644]

index 1658be3a2967810355660cfd5a2ac85067c95684..892ddfce02d229653e52a4209f3768ef319b456f 100644 (file)
@@ -4,6 +4,9 @@
 *.o
 *.obj
 
+# annoying files
+**/.DS_Store
+
 # Generated by configure and make
 Makefile
 bin-stamp
index 58c7b62f2e2f5cf31a6a18165dbcb8414dc945d9..87f483c69aa12c6c8873df7d56063d59c6c03534 100644 (file)
@@ -2277,9 +2277,11 @@ static OSStatus AudioIOProc( void *inRefCon,
                     INPUT_ELEMENT,
                     inNumberFrames,
                     &stream->inputAudioBufferList );
-      /* FEEDBACK: I'm not sure what to do when this call fails. There's nothing in the PA API to
-       * do about failures in the callback system. */
-      assert( !err );
+      if(err != noErr)
+      {
+        /* We used to assert on error. Now we try to just stop the stream. */
+        goto stop_stream;
+      }
 
       PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
       PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
@@ -2370,8 +2372,12 @@ static OSStatus AudioIOProc( void *inRefCon,
                   }
                }
                ERR( err );
-               assert( !err );
-               
+               if(err != noErr)
+               {
+                 /* We used to assert on error. Now we try to just stop the stream. */
+                 goto stop_stream;
+               }
+
                PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
                PaUtil_SetInterleavedInputChannels( &(stream->bufferProcessor),
                                    0,
@@ -2477,9 +2483,13 @@ static OSStatus AudioIOProc( void *inRefCon,
          if( err == -10874 )
             inNumberFrames /= 2;
       } while( err == -10874 && inNumberFrames > 1 );
-      /* FEEDBACK: I'm not sure what to do when this call fails */
       ERR( err );
-      assert( !err );
+      if(err != noErr)
+      {
+          /* We used to assert on error. Now we try to just stop the stream. */
+          goto stop_stream;
+      }
+
       if( stream->inputSRConverter || stream->outputUnit )
       {
          /* If this is duplex or we use a converter, put the data
@@ -2522,11 +2532,11 @@ static OSStatus AudioIOProc( void *inRefCon,
           * chunks, and let the BufferProcessor deal with the rest.
           *
           */
-         /*This might be too big or small depending on SR conversion*/
+         /* This might be too big or small depending on SR conversion. */
          float data[ chan * inNumberFrames ];
          OSStatus err;
          do
-         { /*Run the buffer processor until we are out of data*/
+         { /* Run the buffer processor until we are out of data. */
             UInt32 size;
             long f;
 
@@ -2539,7 +2549,12 @@ static OSStatus AudioIOProc( void *inRefCon,
                           (void *)data );
             if( err != RING_BUFFER_EMPTY )
                ERR( err );
-            assert( err == 0 || err == RING_BUFFER_EMPTY );
+            if( err != noErr && err != RING_BUFFER_EMPTY )
+            {
+              /* We used to assert on error. Now we try to just stop the stream. */
+              goto stop_stream;
+            }
+
 
             f = size / ( chan * sizeof(float) );
             PaUtil_SetInputFrameCount( &(stream->bufferProcessor), f );
@@ -2564,19 +2579,26 @@ static OSStatus AudioIOProc( void *inRefCon,
 
    switch( callbackResult )
    {
-   case paContinue: break;
+   case paContinue:
+           break;
    case paComplete:
    case paAbort:
-      stream->state = CALLBACK_STOPPED ;
-      if( stream->outputUnit )
-         AudioOutputUnitStop(stream->outputUnit);
-      if( stream->inputUnit )
-         AudioOutputUnitStop(stream->inputUnit);
+           goto stop_stream;
       break;
    }
 
    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
    return noErr;
+
+stop_stream:
+    stream->state = CALLBACK_STOPPED ;
+    if( stream->outputUnit )
+        AudioOutputUnitStop(stream->outputUnit);
+    if( stream->inputUnit )
+        AudioOutputUnitStop(stream->inputUnit);
+
+    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+    return noErr;
 }
 
 
diff --git a/test/patest_unplug.c b/test/patest_unplug.c
new file mode 100644 (file)
index 0000000..b48f7ff
--- /dev/null
@@ -0,0 +1,243 @@
+/** @file patest_unplug.c
+       @ingroup test_src
+       @brief Debug a crash involving unplugging a USB device.
+       @author Phil Burk  http://www.softsynth.com
+*/
+/*
+ * $Id$
+ *
+ * This program uses the PortAudio Portable Audio Library.
+ * For more information see: http://www.portaudio.com
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * The text above constitutes the entire PortAudio license; however, 
+ * the PortAudio community also makes the following non-binding requests:
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version. It is also 
+ * requested that these non-binding requests be included along with the 
+ * license above.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include "portaudio.h"
+
+#define NUM_SECONDS   (8)
+#define SAMPLE_RATE   (44100)
+#ifndef M_PI
+#define M_PI  (3.14159265)
+#endif
+#define TABLE_SIZE        (200)
+#define FRAMES_PER_BUFFER  (64)
+#define MAX_CHANNELS        (8)
+
+typedef struct
+{
+    short sine[TABLE_SIZE];
+    int32_t phases[MAX_CHANNELS];
+    int32_t numChannels;
+    int32_t sampsToGo;
+}
+paTestData;
+
+
+static int inputCallback( const void *inputBuffer, void *outputBuffer,
+                          unsigned long framesPerBuffer,
+                          const PaStreamCallbackTimeInfo* timeInfo,
+                          PaStreamCallbackFlags statusFlags,
+                          void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+    (void) outputBuffer; /* Prevent "unused variable" warnings. */
+
+    data->sampsToGo -= framesPerBuffer;
+    if (data->sampsToGo <= 0)
+    {
+        data->sampsToGo = 0;
+        finished = 1;
+    }
+    return finished;
+}
+
+static int outputCallback( const void *inputBuffer, void *outputBuffer,
+                            unsigned long framesPerBuffer,
+                            const PaStreamCallbackTimeInfo* timeInfo,
+                            PaStreamCallbackFlags statusFlags,
+                            void *userData )
+{
+    paTestData *data = (paTestData*)userData;
+    short *out = (short*)outputBuffer;
+    unsigned int i;
+    int finished = 0;
+    (void) inputBuffer; /* Prevent "unused variable" warnings. */
+
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+        for (int channelIndex = 0; channelIndex < data->numChannels; channelIndex++)
+        {
+            int phase = data->phases[channelIndex];
+            *out++ = data->sine[phase];
+            phase += channelIndex + 2;
+            if( phase >= TABLE_SIZE ) phase -= TABLE_SIZE;
+            data->phases[channelIndex] = phase;
+        }
+    }
+    return finished;
+}
+
+/*******************************************************************/
+int main(int argc, char **args);
+int main(int argc, char **args)
+{
+    PaStreamParameters inputParameters;
+    PaStreamParameters outputParameters;
+    PaStream *inputStream;
+    PaStream *outputStream;
+    const PaDeviceInfo *deviceInfo;
+    PaError err;
+    paTestData data;
+    int i;
+    int totalSamps;
+    int inputDevice = -1;
+    int outputDevice = -1;
+
+    printf("Test unplugging a USB device.\n");
+
+    if( argc > 1 ) {
+       inputDevice = outputDevice = atoi( args[1] );
+       printf("Using device number %d.\n\n", inputDevice );
+    } else {
+       printf("Using default device.\n\n" );
+    }
+
+    memset(&data, 0, sizeof(data));
+
+    /* initialise sinusoidal wavetable */
+    for( i=0; i<TABLE_SIZE; i++ )
+    {
+        data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
+    }
+    data.numChannels = 2;
+    data.sampsToGo = totalSamps =  NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
+
+
+    err = Pa_Initialize();
+    if( err != paNoError ) goto error;
+
+    if( inputDevice == -1 )
+        inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
+    else
+        inputParameters.device = inputDevice ;
+
+    if (inputParameters.device == paNoDevice) {
+        fprintf(stderr,"Error: No default input device.\n");
+        goto error;
+    }
+
+    if( outputDevice == -1 )
+        outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
+    else
+        outputParameters.device = outputDevice ;
+
+    if (outputParameters.device == paNoDevice) {
+        fprintf(stderr,"Error: No default output device.\n");
+        goto error;
+    }
+
+    inputParameters.channelCount = 2;
+    inputParameters.sampleFormat = paInt16;
+    deviceInfo = Pa_GetDeviceInfo( inputParameters.device );
+    if( deviceInfo == NULL )
+    {
+        fprintf( stderr, "No matching input device.\n" );
+        goto error;
+    }
+    inputParameters.suggestedLatency = deviceInfo->defaultLowInputLatency;
+    inputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream(
+                &inputStream,
+                &inputParameters,
+                NULL,
+                SAMPLE_RATE,
+                FRAMES_PER_BUFFER,
+                0,
+                inputCallback,
+                &data );
+    if( err != paNoError ) goto error;
+
+    outputParameters.channelCount = 2;
+    outputParameters.sampleFormat = paInt16;
+    deviceInfo = Pa_GetDeviceInfo( outputParameters.device );
+    if( deviceInfo == NULL )
+    {
+        fprintf( stderr, "No matching output device.\n" );
+        goto error;
+    }
+    outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
+    outputParameters.hostApiSpecificStreamInfo = NULL;
+    err = Pa_OpenStream(
+                &outputStream,
+                NULL,
+                &outputParameters,
+                SAMPLE_RATE,
+                FRAMES_PER_BUFFER,
+                (paClipOff | paDitherOff),
+                outputCallback,
+                &data );
+    if( err != paNoError ) goto error;
+
+    err = Pa_StartStream( inputStream );
+    if( err != paNoError ) goto error;
+    err = Pa_StartStream( outputStream );
+    if( err != paNoError ) goto error;
+
+    printf("When you hear sound, unplug the USB device.\n");
+    do
+    {
+        Pa_Sleep(500);
+        printf("Frames remaining = %d\n", data.sampsToGo);
+        printf("Pa_IsStreamActive(inputStream) = %d\n", Pa_IsStreamActive(inputStream));
+        printf("Pa_IsStreamActive(outputStream) = %d\n", Pa_IsStreamActive(outputStream));
+    } while( Pa_IsStreamActive(inputStream) && Pa_IsStreamActive(outputStream) );
+
+    err = Pa_CloseStream( inputStream );
+    if( err != paNoError ) goto error;
+    err = Pa_CloseStream( outputStream );
+    if( err != paNoError ) goto error;
+    Pa_Terminate();
+    return paNoError;
+error:
+    Pa_Terminate();
+    fprintf( stderr, "An error occured while using the portaudio stream\n" );
+    fprintf( stderr, "Error number: %d\n", err );
+    fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
+    fprintf( stderr, "Host Error message: %s\n", Pa_GetLastHostErrorInfo()->errorText );
+    return err;
+}