Added pulse audio fix that was just applied to master.
[rtaudio.git] / RtAudio.cpp
index 3f3c5598ac4566ac9b4bd68fa22fe085f1f25894..29acb059973f9279064c5e92a9443552839540cf 100644 (file)
@@ -39,7 +39,7 @@
 */
 /************************************************************************/
 
-// RtAudio: Version 5.1.0
+// RtAudio: Version 6.0.0beta1
 
 #include "RtAudio.h"
 #include <iostream>
@@ -150,6 +150,7 @@ extern "C" const RtAudio::Api rtaudio_compiled_apis[] = {
 #endif
   RtAudio::UNSPECIFIED,
 };
+
 extern "C" const unsigned int rtaudio_num_compiled_apis =
   sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1;
 }
@@ -235,18 +236,27 @@ void RtAudio :: openRtApi( RtAudio::Api api )
 #endif
 }
 
-RtAudio :: RtAudio( RtAudio::Api api )
+RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback errorCallback )
 {
   rtapi_ = 0;
-  
+
+  std::string errorMessage;
   if ( api != UNSPECIFIED ) {
     // Attempt to open the specified API.
     openRtApi( api );
-    if ( rtapi_ ) return;
 
-    // No compiled support for specified API value.  Issue a debug
-    // warning and continue as if no API was specified.
-    std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
+    if ( rtapi_ ) {
+      if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
+      return;
+    }
+
+    // No compiled support for specified API value.  Issue a warning
+    // and continue as if no API was specified.
+    errorMessage = "RtAudio: no compiled support for specified API argument!";
+    if ( errorCallback )
+      errorCallback( RTAUDIO_INVALID_USE, errorMessage );
+    else
+      std::cerr << '\n' << errorMessage << '\n' << std::endl;
   }
 
   // Iterate through the compiled APIs and return as soon as we find
@@ -258,14 +268,21 @@ RtAudio :: RtAudio( RtAudio::Api api )
     if ( rtapi_ && rtapi_->getDeviceCount() ) break;
   }
 
-  if ( rtapi_ ) return;
+  if ( rtapi_ ) {
+    if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
+    return;
+  }
 
   // It should not be possible to get here because the preprocessor
-  // definition __RTAUDIO_DUMMY__ is automatically defined if no
-  // API-specific definitions are passed to the compiler. But just in
-  // case something weird happens, we'll thow an error.
-  std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";
-  throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );
+  // definition __RTAUDIO_DUMMY__ is automatically defined in RtAudio.h
+  // if no API-specific definitions are passed to the compiler. But just
+  // in case something weird happens, issue an error message and abort.
+  errorMessage = "RtAudio: no compiled API support found ... critical error!";
+  if ( errorCallback )
+    errorCallback( RTAUDIO_INVALID_USE, errorMessage );
+  else
+    std::cerr << '\n' << errorMessage << '\n' << std::endl;
+  abort();
 }
 
 RtAudio :: ~RtAudio()
@@ -274,17 +291,16 @@ RtAudio :: ~RtAudio()
     delete rtapi_;
 }
 
-void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
-                            RtAudio::StreamParameters *inputParameters,
-                            RtAudioFormat format, unsigned int sampleRate,
-                            unsigned int *bufferFrames,
-                            RtAudioCallback callback, void *userData,
-                            RtAudio::StreamOptions *options,
-                            RtAudioErrorCallback errorCallback )
+RtAudioErrorType RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
+                                        RtAudio::StreamParameters *inputParameters,
+                                        RtAudioFormat format, unsigned int sampleRate,
+                                        unsigned int *bufferFrames,
+                                        RtAudioCallback callback, void *userData,
+                                        RtAudio::StreamOptions *options )
 {
   return rtapi_->openStream( outputParameters, inputParameters, format,
                              sampleRate, bufferFrames, callback,
-                             userData, options, errorCallback );
+                             userData, options );
 }
 
 // *************************************************** //
@@ -298,8 +314,8 @@ RtApi :: RtApi()
 {
   clearStreamInfo();
   MUTEX_INITIALIZE( &stream_.mutex );
+  errorCallback_ = 0;
   showWarnings_ = true;
-  firstErrorOccurred_ = false;
 }
 
 RtApi :: ~RtApi()
@@ -307,18 +323,16 @@ RtApi :: ~RtApi()
   MUTEX_DESTROY( &stream_.mutex );
 }
 
-void RtApi :: openStream( RtAudio::StreamParameters *oParams,
-                          RtAudio::StreamParameters *iParams,
-                          RtAudioFormat format, unsigned int sampleRate,
-                          unsigned int *bufferFrames,
-                          RtAudioCallback callback, void *userData,
-                          RtAudio::StreamOptions *options,
-                          RtAudioErrorCallback errorCallback )
+RtAudioErrorType RtApi :: openStream( RtAudio::StreamParameters *oParams,
+                                      RtAudio::StreamParameters *iParams,
+                                      RtAudioFormat format, unsigned int sampleRate,
+                                      unsigned int *bufferFrames,
+                                      RtAudioCallback callback, void *userData,
+                                      RtAudio::StreamOptions *options )
 {
   if ( stream_.state != STREAM_CLOSED ) {
     errorText_ = "RtApi::openStream: a stream is already open!";
-    error( RtAudioError::INVALID_USE );
-    return;
+    return error( RTAUDIO_INVALID_USE );
   }
 
   // Clear stream information potentially left from a previously open stream.
@@ -326,26 +340,22 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
 
   if ( oParams && oParams->nChannels < 1 ) {
     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
-    error( RtAudioError::INVALID_USE );
-    return;
+    return error( RTAUDIO_INVALID_USE );
   }
 
   if ( iParams && iParams->nChannels < 1 ) {
     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
-    error( RtAudioError::INVALID_USE );
-    return;
+    return error( RTAUDIO_INVALID_USE );
   }
 
   if ( oParams == NULL && iParams == NULL ) {
     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
-    error( RtAudioError::INVALID_USE );
-    return;
+    return error( RTAUDIO_INVALID_USE );
   }
 
   if ( formatBytes(format) == 0 ) {
     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
-    error( RtAudioError::INVALID_USE );
-    return;
+    return error( RTAUDIO_INVALID_USE );
   }
 
   unsigned int nDevices = getDeviceCount();
@@ -354,8 +364,7 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     oChannels = oParams->nChannels;
     if ( oParams->deviceId >= nDevices ) {
       errorText_ = "RtApi::openStream: output device parameter value is invalid.";
-      error( RtAudioError::INVALID_USE );
-      return;
+      return error( RTAUDIO_INVALID_USE );
     }
   }
 
@@ -364,8 +373,7 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     iChannels = iParams->nChannels;
     if ( iParams->deviceId >= nDevices ) {
       errorText_ = "RtApi::openStream: input device parameter value is invalid.";
-      error( RtAudioError::INVALID_USE );
-      return;
+      return error( RTAUDIO_INVALID_USE );
     }
   }
 
@@ -376,8 +384,7 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
                               sampleRate, format, bufferFrames, options );
     if ( result == false ) {
-      error( RtAudioError::SYSTEM_ERROR );
-      return;
+      return error( RTAUDIO_SYSTEM_ERROR );
     }
   }
 
@@ -386,18 +393,16 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
                               sampleRate, format, bufferFrames, options );
     if ( result == false ) {
-      if ( oChannels > 0 ) closeStream();
-      error( RtAudioError::SYSTEM_ERROR );
-      return;
+      return error( RTAUDIO_SYSTEM_ERROR );
     }
   }
 
   stream_.callbackInfo.callback = (void *) callback;
   stream_.callbackInfo.userData = userData;
-  stream_.callbackInfo.errorCallback = (void *) errorCallback;
 
   if ( options ) options->numberOfBuffers = stream_.nBuffers;
   stream_.state = STREAM_STOPPED;
+  return RTAUDIO_NO_ERROR;
 }
 
 unsigned int RtApi :: getDefaultInputDevice( void )
@@ -478,8 +483,6 @@ double RtApi :: getStreamTime( void )
 
 void RtApi :: setStreamTime( double time )
 {
-  //  verifyStream();
-
   if ( time >= 0.0 )
     stream_.streamTime = time;
   /*
@@ -489,14 +492,11 @@ void RtApi :: setStreamTime( double time )
   */
 }
 
-/*
 unsigned int RtApi :: getStreamSampleRate( void )
 {
-  verifyStream();
-
- return stream_.sampleRate;
+  if ( isStreamOpen() ) return stream_.sampleRate;
+  else return 0;
 }
-*/
 
 
 // *************************************************** //
@@ -536,9 +536,11 @@ struct CoreHandle {
   pthread_cond_t condition;
   int drainCounter;       // Tracks callback counts when draining
   bool internalDrain;     // Indicates if stop is initiated from callback or not.
+  bool xrunListenerAdded[2];
+  bool disconnectListenerAdded[2];
 
   CoreHandle()
-    :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+    :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; procId[0] = 0; procId[1] = 0; xrun[0] = false; xrun[1] = false; xrunListenerAdded[0] = false; xrunListenerAdded[1] = false; disconnectListenerAdded[0] = false; disconnectListenerAdded[1] = false; }
 };
 
 RtApiCore:: RtApiCore()
@@ -555,7 +557,7 @@ RtApiCore:: RtApiCore()
   OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
   if ( result != noErr ) {
     errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
   }
 #endif
 }
@@ -576,7 +578,7 @@ unsigned int RtApiCore :: getDeviceCount( void )
   OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
     return 0;
   }
 
@@ -594,7 +596,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
     return 0;
   }
 
@@ -604,7 +606,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
     return 0;
   }
 
@@ -612,7 +614,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
     if ( id == deviceList[i] ) return i;
 
   errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
-  error( RtAudioError::WARNING );
+  error( RTAUDIO_WARNING );
   return 0;
 }
 
@@ -627,7 +629,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void )
   OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
     return 0;
   }
 
@@ -637,7 +639,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void )
   result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_SYSTEM_ERROR );
     return 0;
   }
 
@@ -645,7 +647,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void )
     if ( id == deviceList[i] ) return i;
 
   errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
-  error( RtAudioError::WARNING );
+  error( RTAUDIO_WARNING );
   return 0;
 }
 
@@ -658,13 +660,13 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   unsigned int nDevices = getDeviceCount();
   if ( nDevices == 0 ) {
     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
-    error( RtAudioError::INVALID_USE );
+    error( RTAUDIO_INVALID_USE );
     return info;
   }
 
   if ( device >= nDevices ) {
     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
-    error( RtAudioError::INVALID_USE );
+    error( RTAUDIO_INVALID_USE );
     return info;
   }
 
@@ -677,7 +679,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
                                                 0, NULL, &dataSize, (void *) &deviceList );
   if ( result != noErr ) {
     errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -692,12 +694,12 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != noErr ) {
     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
-  int length = CFStringGetLength(cfname);
+  long length = CFStringGetLength(cfname);
   char *mname = (char *)malloc(length * 3 + 1);
 #if defined( UNICODE ) || defined( _UNICODE )
   CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);
@@ -714,7 +716,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != noErr ) {
     errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -740,7 +742,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != noErr || dataSize == 0 ) {
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -748,7 +750,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   bufferList = (AudioBufferList *) malloc( dataSize );
   if ( bufferList == NULL ) {
     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -757,7 +759,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
     free( bufferList );
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -773,7 +775,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != noErr || dataSize == 0 ) {
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -781,7 +783,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   bufferList = (AudioBufferList *) malloc( dataSize );
   if ( bufferList == NULL ) {
     errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -790,7 +792,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
     free( bufferList );
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -815,7 +817,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != kAudioHardwareNoError || dataSize == 0 ) {
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -825,7 +827,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( result != kAudioHardwareNoError ) {
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -872,7 +874,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( info.sampleRates.size() == 0 ) {
     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
     errorText_ = errorStream_.str();
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -1045,7 +1047,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
 
   // Look for a single stream meeting our needs.
-  UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
+  UInt32 firstStream = 0, streamCount = 1, streamChannels = 0, channelOffset = 0;
   for ( iStream=0; iStream<nStreams; iStream++ ) {
     streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
     if ( streamChannels >= channels + offsetCounter ) {
@@ -1096,9 +1098,9 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     return FAILURE;
   }
 
-  if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
-  else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
-  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+  if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMinimum;
+  else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMaximum;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned int) bufferRange.mMinimum;
 
   // Set the buffer size.  For multiple streams, I'm assuming we only
   // need to make this setting for the master channel.
@@ -1297,7 +1299,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     else {
       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
       errorText_ = errorStream_.str();
-      error( RtAudioError::WARNING );
+      error( RTAUDIO_WARNING );
     }
   }
 
@@ -1362,9 +1364,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // Allocate necessary internal buffers.
   unsigned long bufferBytes;
   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
-  //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
   stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );
-  memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );
   if ( stream_.userBuffer[mode] == NULL ) {
     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
     goto error;
@@ -1387,7 +1387,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     if ( makeBuffer ) {
       bufferBytes *= *bufferSize;
       if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
-      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, sizeof(char) );
       if ( stream_.deviceBuffer == NULL ) {
         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
@@ -1407,7 +1407,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
 
   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
-    // Only one callback procedure per device.
+    // Only one callback procedure and property listener per device.
     stream_.mode = DUPLEX;
   else {
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
@@ -1425,51 +1425,34 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       stream_.mode = DUPLEX;
     else
       stream_.mode = mode;
-  }
 
-  // Setup the device property listener for over/underload.
-  property.mSelector = kAudioDeviceProcessorOverload;
-  property.mScope = kAudioObjectPropertyScopeGlobal;
-  result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
+    // Setup the device property listener for over/underload.
+    property.mSelector = kAudioDeviceProcessorOverload;
+    property.mScope = kAudioObjectPropertyScopeGlobal;
+    result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
     if ( result != noErr ) {
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error setting xrun listener for device (" << device << ").";
-    errorText_ = errorStream_.str();
-    goto error;
-  }
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error setting xrun listener for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      goto error;
+    }
+    handle->xrunListenerAdded[mode] = true;
 
-  // Setup a listener to detect a possible device disconnect.
-  property.mSelector = kAudioDevicePropertyDeviceIsAlive;
-  property.mScope = kAudioObjectPropertyScopeGlobal;
-  result = AudioObjectAddPropertyListener( id , &property, disconnectListener, (void *) &stream_.callbackInfo );
-  if ( result != noErr ) {
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << device << ").";
-    errorText_ = errorStream_.str();
-    goto error;
+    // Setup a listener to detect a possible device disconnect.
+    property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+    result = AudioObjectAddPropertyListener( id , &property, disconnectListener, (void *) &stream_.callbackInfo );
+    if ( result != noErr ) {
+      AudioObjectRemovePropertyListener( id, &property, xrunListener, (void *) handle );
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      goto error;
+    }
+    handle->disconnectListenerAdded[mode] = true;
   }
 
   return SUCCESS;
 
  error:
-  if ( handle ) {
-    pthread_cond_destroy( &handle->condition );
-    delete handle;
-    stream_.apiHandle = 0;
-  }
-
-  for ( int i=0; i<2; i++ ) {
-    if ( stream_.userBuffer[i] ) {
-      free( stream_.userBuffer[i] );
-      stream_.userBuffer[i] = 0;
-    }
-  }
-
-  if ( stream_.deviceBuffer ) {
-    free( stream_.deviceBuffer );
-    stream_.deviceBuffer = 0;
-  }
-
-  clearStreamInfo();
-  //stream_.state = STREAM_CLOSED;
+  closeStream(); // this should safely clear out procedures, listeners and memory, even for duplex stream
   return FAILURE;
 }
 
@@ -1477,55 +1460,81 @@ void RtApiCore :: closeStream( void )
 {
   if ( stream_.state == STREAM_CLOSED ) {
     errorText_ = "RtApiCore::closeStream(): no open stream to close!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return;
   }
 
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-    if (handle) {
+    if ( handle ) {
       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
         kAudioObjectPropertyScopeGlobal,
         kAudioObjectPropertyElementMaster };
 
-      property.mSelector = kAudioDeviceProcessorOverload;
-      property.mScope = kAudioObjectPropertyScopeGlobal;
-      if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
-        errorText_ = "RtApiCore::closeStream(): error removing property listener!";
-        error( RtAudioError::WARNING );
+      if ( handle->xrunListenerAdded[0] ) {
+        property.mSelector = kAudioDeviceProcessorOverload;
+        if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
+          errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!";
+          error( RTAUDIO_WARNING );
+        }
       }
-    }
-    if ( stream_.state == STREAM_RUNNING )
-      AudioDeviceStop( handle->id[0], callbackHandler );
+      if ( handle->disconnectListenerAdded[0] ) {
+        property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+        if (AudioObjectRemovePropertyListener( handle->id[0], &property, disconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
+          errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
+          error( RTAUDIO_WARNING );
+        }
+      }
+
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
-    AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
-#else
-    // deprecated in favor of AudioDeviceDestroyIOProcID()
-    AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+      if ( handle->procId[0] ) {
+        if ( stream_.state == STREAM_RUNNING )
+          AudioDeviceStop( handle->id[0], handle-procId[0] );
+        AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
+      }
+#else // deprecated behaviour
+      if ( stream_.state == STREAM_RUNNING )
+        AudioDeviceStop( handle->id[0], callbackHandler );
+      AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
 #endif
+      }
+    }
   }
 
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
-    if (handle) {
+    if ( handle ) {
       AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
         kAudioObjectPropertyScopeGlobal,
         kAudioObjectPropertyElementMaster };
 
-      property.mSelector = kAudioDeviceProcessorOverload;
-      property.mScope = kAudioObjectPropertyScopeGlobal;
-      if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
-        errorText_ = "RtApiCore::closeStream(): error removing property listener!";
-        error( RtAudioError::WARNING );
+      if ( handle->xrunListenerAdded[1] ) {
+        property.mSelector = kAudioDeviceProcessorOverload;
+        if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
+          errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!";
+          error( RTAUDIO_WARNING );
+        }
       }
-    }
-    if ( stream_.state == STREAM_RUNNING )
-      AudioDeviceStop( handle->id[1], callbackHandler );
+
+      if ( handle->disconnectListenerAdded[0] ) {
+        property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+        if (AudioObjectRemovePropertyListener( handle->id[1], &property, disconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
+          errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
+          error( RTAUDIO_WARNING );
+        }
+      }
+
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
-    AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
-#else
-    // deprecated in favor of AudioDeviceDestroyIOProcID()
-    AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+      if ( handle->procId[1] ) {
+        if ( stream_.state == STREAM_RUNNING )
+          AudioDeviceStop( handle->id[1], handle->procId[1] );
+        AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
+      }
+#else // deprecated behaviour
+      if ( stream_.state == STREAM_RUNNING )
+        AudioDeviceStop( handle->id[1], callbackHandler );
+      AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
 #endif
+    }
   }
 
   for ( int i=0; i<2; i++ ) {
@@ -1549,7 +1558,7 @@ void RtApiCore :: closeStream( void )
   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
   if ( info->deviceDisconnected ) {
     errorText_ = "RtApiCore: the stream device was disconnected (and closed)!";
-    error( RtAudioError::DEVICE_DISCONNECT );
+    error( RTAUDIO_DEVICE_DISCONNECT );
   }
   
   clearStreamInfo();
@@ -1557,16 +1566,14 @@ void RtApiCore :: closeStream( void )
   //stream_.state = STREAM_CLOSED;
 }
 
-void RtApiCore :: startStream( void )
+RtAudioErrorType RtApiCore :: startStream( void )
 {
-  //verifyStream();
   if ( stream_.state != STREAM_STOPPED ) {
     if ( stream_.state == STREAM_RUNNING )
       errorText_ = "RtApiCore::startStream(): the stream is already running!";
     else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
       errorText_ = "RtApiCore::startStream(): the stream is stopping or closed!";
-    error( RtAudioError::WARNING );
-    return;
+    return error( RTAUDIO_WARNING );
   }
 
   /*
@@ -1579,7 +1586,11 @@ void RtApiCore :: startStream( void )
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
-    result = AudioDeviceStart( handle->id[0], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceStart( handle->id[0], handle->procId[0] );
+#else // deprecated behaviour
+  result = AudioDeviceStart( handle->id[0], callbackHandler );
+#endif
     if ( result != noErr ) {
       errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
       errorText_ = errorStream_.str();
@@ -1590,7 +1601,16 @@ void RtApiCore :: startStream( void )
   if ( stream_.mode == INPUT ||
        ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
 
+    // Clear user input buffer
+    unsigned long bufferBytes;
+    bufferBytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
+    memset( stream_.userBuffer[1], 0, bufferBytes * sizeof(char) );
+
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceStart( handle->id[1], handle->procId[1] );
+#else // deprecated behaviour
     result = AudioDeviceStart( handle->id[1], callbackHandler );
+#endif
     if ( result != noErr ) {
       errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
       errorText_ = errorStream_.str();
@@ -1603,20 +1623,18 @@ void RtApiCore :: startStream( void )
   stream_.state = STREAM_RUNNING;
 
  unlock:
-  if ( result == noErr ) return;
-  error( RtAudioError::SYSTEM_ERROR );
+  if ( result == noErr ) return RTAUDIO_NO_ERROR;
+  return error( RTAUDIO_SYSTEM_ERROR );
 }
 
-void RtApiCore :: stopStream( void )
+RtAudioErrorType RtApiCore :: stopStream( void )
 {
-  //verifyStream();
   if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) {
     if ( stream_.state == STREAM_STOPPED )
       errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
     else if ( stream_.state == STREAM_CLOSED )
       errorText_ = "RtApiCore::stopStream(): the stream is closed!";
-    error( RtAudioError::WARNING );
-    return;
+    return error( RTAUDIO_WARNING );
   }
 
   OSStatus result = noErr;
@@ -1628,7 +1646,11 @@ void RtApiCore :: stopStream( void )
       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
 
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceStop( handle->id[0], handle->procId[0] );
+#else
     result = AudioDeviceStop( handle->id[0], callbackHandler );
+#endif
     if ( result != noErr ) {
       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
       errorText_ = errorStream_.str();
@@ -1637,8 +1659,11 @@ void RtApiCore :: stopStream( void )
   }
 
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
-
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceStop( handle->id[1], handle->procId[1] );
+#else
     result = AudioDeviceStop( handle->id[1], callbackHandler );
+#endif
     if ( result != noErr ) {
       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
       errorText_ = errorStream_.str();
@@ -1647,30 +1672,28 @@ void RtApiCore :: stopStream( void )
   }
 
   stream_.state = STREAM_STOPPED;
-  // set stream time to zero?
 
  unlock:
-  if ( result == noErr ) return;
-  error( RtAudioError::SYSTEM_ERROR );
+  if ( result == noErr ) return RTAUDIO_NO_ERROR;
+  return error( RTAUDIO_SYSTEM_ERROR );
 }
 
-void RtApiCore :: abortStream( void )
+RtAudioErrorType RtApiCore :: abortStream( void )
 {
-  //verifyStream();
   if ( stream_.state != STREAM_RUNNING ) {
     if ( stream_.state == STREAM_STOPPED )
       errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
     else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
       errorText_ = "RtApiCore::abortStream(): the stream is stopping or closed!";
-    error( RtAudioError::WARNING );
-    return;
+    return error( RTAUDIO_WARNING );
+    //return;
   }
 
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
   handle->drainCounter = 2;
 
   stream_.state = STREAM_STOPPING;
-  stopStream();
+  return stopStream();
 }
 
 // This function will be called by a spawned thread when the user
@@ -1694,7 +1717,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
   if ( stream_.state == STREAM_CLOSED ) {
     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return FAILURE;
   }
 
@@ -1939,11 +1962,15 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
   }
 
  unlock:
-  //MUTEX_UNLOCK( &stream_.mutex );
 
   // Make sure to only tick duplex stream time once if using two devices
-  if ( stream_.mode != DUPLEX || (stream_.mode == DUPLEX && handle->id[0] != handle->id[1] && deviceId == handle->id[0] ) )
-    RtApi::tickStreamTime();
+  if ( stream_.mode == DUPLEX ) {
+    if ( handle->id[0] == handle->id[1] ) // same device, only one callback
+      RtApi::tickStreamTime();
+    else if ( deviceId == handle->id[0] )
+      RtApi::tickStreamTime(); // two devices, only tick on the output callback
+  } else
+    RtApi::tickStreamTime(); // input or output stream only
   
   return SUCCESS;
 }
@@ -1996,9 +2023,10 @@ const char* RtApiCore :: getErrorCode( OSStatus code )
 #if defined(__UNIX_JACK__)
 
 // JACK is a low-latency audio server, originally written for the
-// GNU/Linux operating system and now also ported to OS-X. It can
-// connect a number of different applications to an audio device, as
-// well as allowing them to share audio between themselves.
+// GNU/Linux operating system and now also ported to OS-X and
+// Windows. It can connect a number of different applications to an
+// audio device, as well as allowing them to share audio between
+// themselves.
 //
 // When using JACK with RtAudio, "devices" refer to JACK clients that
 // have ports connected to the server.  The JACK server is typically
@@ -2104,7 +2132,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
   if ( client == 0 ) {
     errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -2133,7 +2161,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   if ( device >= nDevices ) {
     jack_client_close( client );
     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
-    error( RtAudioError::INVALID_USE );
+    error( RTAUDIO_INVALID_USE );
     return info;
   }
 
@@ -2165,7 +2193,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
     jack_client_close(client);
     errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return info;
   }
 
@@ -2206,10 +2234,11 @@ static void *jackCloseStream( void *ptr )
   CallbackInfo *info = (CallbackInfo *) ptr;
   RtApiJack *object = (RtApiJack *) info->object;
 
+  info->deviceDisconnected = true;
   object->closeStream();
-
   pthread_exit( NULL );
 }
+
 static void jackShutdown( void *infoPointer )
 {
   CallbackInfo *info = (CallbackInfo *) infoPointer;
@@ -2224,7 +2253,6 @@ static void jackShutdown( void *infoPointer )
 
   ThreadHandle threadId;
   pthread_create( &threadId, NULL, jackCloseStream, info );
-  std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
 }
 
 static int jackXrun( void *infoPointer )
@@ -2255,7 +2283,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       client = jack_client_open( "RtApiJack", jackoptions, status );
     if ( client == 0 ) {
       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
-      error( RtAudioError::WARNING );
+      error( RTAUDIO_WARNING );
       return FAILURE;
     }
   }
@@ -2496,13 +2524,12 @@ void RtApiJack :: closeStream( void )
 {
   if ( stream_.state == STREAM_CLOSED ) {
     errorText_ = "RtApiJack::closeStream(): no open stream to close!";
-    error( RtAudioError::WARNING );
+    error( RTAUDIO_WARNING );
     return;
   }
 
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
   if ( handle ) {
-
     if ( stream_.state == STREAM_RUNNING )
       jack_deactivate( handle->client );
 
@@ -2517,6 +2544,12 @@ void RtApiJack :: closeStream( void )
     stream_.apiHandle = 0;
   }
 
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  if ( info->deviceDisconnected ) {
+    errorText_ = "RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!";
+    error( RTAUDIO_DEVICE_DISCONNECT );
+  }
+
   for ( int i=0; i<2; i++ ) {
     if ( stream_.userBuffer[i] ) {
       free( stream_.userBuffer[i] );
@@ -2529,22 +2562,26 @@ void RtApiJack :: closeStream( void )
     stream_.deviceBuffer = 0;
   }
 
-  stream_.mode = UNINITIALIZED;
-  stream_.state = STREAM_CLOSED;
+  clearStreamInfo();
+  //stream_.mode = UNINITIALIZED;
+  //stream_.state = STREAM_CLOSED;
 }
 
-void RtApiJack :: startStream( void )
+RtAudioErrorType RtApiJack :: startStream( void )
 {
-  verifyStream();
-  if ( stream_.state == STREAM_RUNNING ) {
-    errorText_ = "RtApiJack::startStream(): the stream is already running!";
-    error( RtAudioError::WARNING );
-    return;
+  if ( stream_.state != STREAM_STOPPED ) {
+    if ( stream_.state == STREAM_RUNNING )
+      errorText_ = "RtApiJack::startStream(): the stream is already running!";
+    else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+      errorText_ = "RtApiJack::startStream(): the stream is stopping or closed!";
+    return error( RTAUDIO_WARNING );
   }
 
+  /*
   #if defined( HAVE_GETTIMEOFDAY )
   gettimeofday( &stream_.lastTickTimestamp, NULL );
   #endif
+  */
 
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
   int result = jack_activate( handle->client );
@@ -2607,17 +2644,18 @@ void RtApiJack :: startStream( void )
   stream_.state = STREAM_RUNNING;
 
  unlock:
-  if ( result == 0 ) return;
-  error( RtAudioError::SYSTEM_ERROR );
+  if ( result == 0 ) return RTAUDIO_NO_ERROR;
+  return error( RTAUDIO_SYSTEM_ERROR );
 }
 
-void RtApiJack :: stopStream( void )
+RtAudioErrorType  RtApiJack :: stopStream( void )
 {
-  verifyStream();
-  if ( stream_.state == STREAM_STOPPED ) {
-    errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
-    error( RtAudioError::WARNING );
-    return;
+  if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) {
+    if ( stream_.state == STREAM_STOPPED )
+      errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+    else if ( stream_.state == STREAM_CLOSED )
+      errorText_ = "RtApiJack::stopStream(): the stream is closed!";
+    return error( RTAUDIO_WARNING );
   }
 
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
@@ -2631,21 +2669,23 @@ void RtApiJack :: stopStream( void )
 
   jack_deactivate( handle->client );
   stream_.state = STREAM_STOPPED;
+  return RTAUDIO_NO_ERROR;
 }
 
-void RtApiJack :: abortStream( void )
+RtAudioErrorType RtApiJack :: abortStream( void )
 {
-  verifyStream();
-  if ( stream_.state == STREAM_STOPPED ) {
-    errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
-    error( RtAudioError::WARNING );
-    return;
+  if ( stream_.state != STREAM_RUNNING ) {
+    if ( stream_.state == STREAM_STOPPED )
+      errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+    else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+      errorText_ = "RtApiJack::abortStream(): the stream is stopping or closed!";
+    return error( RTAUDIO_WARNING );
   }
 
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
   handle->drainCounter = 2;
 
-  stopStream();
+  return stopStream();
 }
 
 // This function will be called by a spawned thread when the user
@@ -2666,13 +2706,13 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
 {
   if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
   if ( stream_.state == STREAM_CLOSED ) {
-    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
-    error( RtAudioError::WARNING );
+    errorText_ = "RtApiJack::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RTAUDIO_WARNING );
     return FAILURE;
   }
   if ( stream_.bufferSize != nframes ) {
-    errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
-    error( RtAudioError::WARNING );
+    errorText_ = "RtApiJack::callbackEvent(): the JACK buffer size has changed ... cannot process!";
+    error( RTAUDIO_WARNING );
     return FAILURE;
   }
 
@@ -2686,7 +2726,7 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
     stream_.state = STREAM_STOPPING;
     if ( handle->internalDrain == true )
       pthread_create( &threadId, NULL, jackStopStream, info );
-    else
+    else // external call to stopStream()
       pthread_cond_signal( &handle->condition );
     return SUCCESS;
   }
@@ -8714,15 +8754,18 @@ void RtApiPulse::stopStream( void )
   stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
-  if ( pah && pah->s_play ) {
-    int pa_error;
-    if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
-      errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
-        pa_strerror( pa_error ) << ".";
-      errorText_ = errorStream_.str();
-      MUTEX_UNLOCK( &stream_.mutex );
-      error( RtAudioError::SYSTEM_ERROR );
-      return;
+  if ( pah } {
+    pah->runnable = false;
+    if ( pah->s_play ) {
+      int pa_error;
+      if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
+        errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
+          pa_strerror( pa_error ) << ".";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
     }
   }
 
@@ -8748,15 +8791,18 @@ void RtApiPulse::abortStream( void )
   stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
-  if ( pah && pah->s_play ) {
-    int pa_error;
-    if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
-      errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
-        pa_strerror( pa_error ) << ".";
-      errorText_ = errorStream_.str();
-      MUTEX_UNLOCK( &stream_.mutex );
-      error( RtAudioError::SYSTEM_ERROR );
-      return;
+  if ( pah ) {
+    pah->runnable = false;
+    if ( pah->s_play ) {
+      int pa_error;
+      if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
+        errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
+          pa_strerror( pa_error ) << ".";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
     }
   }
 
@@ -9972,38 +10018,23 @@ static void *ossCallbackHandler( void *ptr )
 
 // This method can be modified to control the behavior of error
 // message printing.
-void RtApi :: error( RtAudioError::Type type )
+RtAudioErrorType RtApi :: error( RtAudioErrorType type )
 {
-  errorStream_.str(""); // clear the ostringstream
-
-  RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;
-  if ( errorCallback ) {
-    // abortStream() can generate new error messages. Ignore them. Just keep original one.
+  errorStream_.str(""); // clear the ostringstream to avoid repeated messages
 
-    //if ( firstErrorOccurred_ ) return;
-
-    //firstErrorOccurred_ = true;
+  // Don't output warnings if showWarnings_ is false
+  if ( type == RTAUDIO_WARNING && showWarnings_ == false ) return type;
+  
+  if ( errorCallback_ ) {
     const std::string errorMessage = errorText_;
-
-    /*
-    if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {
-      stream_.callbackInfo.isRunning = false; // exit from the thread
-      abortStream();
-    }
-    */
-
-    errorCallback( type, errorMessage );
-    //firstErrorOccurred_ = false;
-    return;
+    errorCallback_( type, errorMessage );
   }
-
-  //if ( type == RtAudioError::WARNING && showWarnings_ == true )
-  if ( showWarnings_ == true )
+  else
     std::cerr << '\n' << errorText_ << "\n\n";
-  //else if ( type != RtAudioError::WARNING )
-  //  throw( RtAudioError( errorText_, type ) );
+  return type;
 }
 
+/*
 void RtApi :: verifyStream()
 {
   if ( stream_.state == STREAM_CLOSED ) {
@@ -10011,6 +10042,7 @@ void RtApi :: verifyStream()
     error( RtAudioError::INVALID_USE );
   }
 }
+*/
 
 void RtApi :: clearStreamInfo()
 {
@@ -10027,7 +10059,7 @@ void RtApi :: clearStreamInfo()
   stream_.callbackInfo.callback = 0;
   stream_.callbackInfo.userData = 0;
   stream_.callbackInfo.isRunning = false;
-  stream_.callbackInfo.errorCallback = 0;
+  stream_.callbackInfo.deviceDisconnected = false;
   for ( int i=0; i<2; i++ ) {
     stream_.device[i] = 11111;
     stream_.doConvertBuffer[i] = false;
@@ -10063,7 +10095,7 @@ unsigned int RtApi :: formatBytes( RtAudioFormat format )
     return 1;
 
   errorText_ = "RtApi::formatBytes: undefined format.";
-  error( RtAudioError::WARNING );
+  error( RTAUDIO_WARNING );
 
   return 0;
 }