From: Phil Burk Date: Mon, 2 May 2016 01:08:38 +0000 (-0700) Subject: pa_mac_core: stop stream instead of asserting X-Git-Tag: pa_stable_v190600_20161030~19^2~1 X-Git-Url: https://andrewgundersen.net/repos?a=commitdiff_plain;h=09588cc5d6f62571d540048fee5e8a4346441fe4;p=portaudio pa_mac_core: stop stream instead of asserting Bug: 158 --- diff --git a/.gitignore b/.gitignore index 1658be3..892ddfc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ *.o *.obj +# annoying files +**/.DS_Store + # Generated by configure and make Makefile bin-stamp diff --git a/src/hostapi/coreaudio/pa_mac_core.c b/src/hostapi/coreaudio/pa_mac_core.c index 58c7b62..87f483c 100644 --- a/src/hostapi/coreaudio/pa_mac_core.c +++ b/src/hostapi/coreaudio/pa_mac_core.c @@ -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 index 0000000..b48f7ff --- /dev/null +++ b/test/patest_unplug.c @@ -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 +#include +#include +#include +#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; inumChannels; 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; idefaultLowInputLatency; + 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; +}