Set some flags depending on compiler and environment.
[rtaudio-cdist.git] / RtAudio.cpp
index 47e58048bfa373ff9a1da27a7c2f4a2172567371..4b600bdb7d6866227658b2d90e54ae89b71c6528 100644 (file)
@@ -10,7 +10,7 @@
     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
 \r
     RtAudio: realtime audio i/o C++ classes\r
-    Copyright (c) 2001-2012 Gary P. Scavone\r
+    Copyright (c) 2001-2013 Gary P. Scavone\r
 \r
     Permission is hereby granted, free of charge, to any person\r
     obtaining a copy of this software and associated documentation files\r
@@ -38,7 +38,7 @@
 */\r
 /************************************************************************/\r
 \r
-// RtAudio: Version 4.0.11\r
+// RtAudio: Version 4.0.12\r
 \r
 #include "RtAudio.h"\r
 #include <iostream>\r
@@ -58,7 +58,7 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)\r
-#elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
+#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)\r
   // pthread API\r
   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)\r
   #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)\r
@@ -87,6 +87,9 @@ void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
 #if defined(__LINUX_ALSA__)\r
   apis.push_back( LINUX_ALSA );\r
 #endif\r
+#if defined(__LINUX_PULSE__)\r
+  apis.push_back( LINUX_PULSE );\r
+#endif\r
 #if defined(__LINUX_OSS__)\r
   apis.push_back( LINUX_OSS );\r
 #endif\r
@@ -106,7 +109,7 @@ void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
 \r
 void RtAudio :: openRtApi( RtAudio::Api api )\r
 {\r
-  if (rtapi_)\r
+  if ( rtapi_ )\r
     delete rtapi_;\r
   rtapi_ = 0;\r
 \r
@@ -118,6 +121,10 @@ void RtAudio :: openRtApi( RtAudio::Api api )
   if ( api == LINUX_ALSA )\r
     rtapi_ = new RtApiAlsa();\r
 #endif\r
+#if defined(__LINUX_PULSE__)\r
+  if ( api == LINUX_PULSE )\r
+    rtapi_ = new RtApiPulse();\r
+#endif\r
 #if defined(__LINUX_OSS__)\r
   if ( api == LINUX_OSS )\r
     rtapi_ = new RtApiOss();\r
@@ -182,11 +189,12 @@ void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
                             RtAudioFormat format, unsigned int sampleRate,\r
                             unsigned int *bufferFrames,\r
                             RtAudioCallback callback, void *userData,\r
-                            RtAudio::StreamOptions *options )\r
+                            RtAudio::StreamOptions *options,\r
+                            RtAudioErrorCallback errorCallback )\r
 {\r
   return rtapi_->openStream( outputParameters, inputParameters, format,\r
                              sampleRate, bufferFrames, callback,\r
-                             userData, options );\r
+                             userData, options, errorCallback );\r
 }\r
 \r
 // *************************************************** //\r
@@ -217,31 +225,37 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
                           RtAudioFormat format, unsigned int sampleRate,\r
                           unsigned int *bufferFrames,\r
                           RtAudioCallback callback, void *userData,\r
-                          RtAudio::StreamOptions *options )\r
+                          RtAudio::StreamOptions *options,\r
+                          RtAudioErrorCallback errorCallback )\r
 {\r
   if ( stream_.state != STREAM_CLOSED ) {\r
     errorText_ = "RtApi::openStream: a stream is already open!";\r
     error( RtError::INVALID_USE );\r
+    return;\r
   }\r
 \r
   if ( oParams && oParams->nChannels < 1 ) {\r
     errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";\r
     error( RtError::INVALID_USE );\r
+    return;\r
   }\r
 \r
   if ( iParams && iParams->nChannels < 1 ) {\r
     errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";\r
     error( RtError::INVALID_USE );\r
+    return;\r
   }\r
 \r
   if ( oParams == NULL && iParams == NULL ) {\r
     errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";\r
     error( RtError::INVALID_USE );\r
+    return;\r
   }\r
 \r
   if ( formatBytes(format) == 0 ) {\r
     errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";\r
     error( RtError::INVALID_USE );\r
+    return;\r
   }\r
 \r
   unsigned int nDevices = getDeviceCount();\r
@@ -251,6 +265,7 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     if ( oParams->deviceId >= nDevices ) {\r
       errorText_ = "RtApi::openStream: output device parameter value is invalid.";\r
       error( RtError::INVALID_USE );\r
+      return;\r
     }\r
   }\r
 \r
@@ -260,6 +275,7 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     if ( iParams->deviceId >= nDevices ) {\r
       errorText_ = "RtApi::openStream: input device parameter value is invalid.";\r
       error( RtError::INVALID_USE );\r
+      return;\r
     }\r
   }\r
 \r
@@ -270,7 +286,10 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
 \r
     result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,\r
                               sampleRate, format, bufferFrames, options );\r
-    if ( result == false ) error( RtError::SYSTEM_ERROR );\r
+    if ( result == false ) {\r
+      error( RtError::SYSTEM_ERROR );\r
+      return;\r
+    }\r
   }\r
 \r
   if ( iChannels > 0 ) {\r
@@ -280,11 +299,13 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     if ( result == false ) {\r
       if ( oChannels > 0 ) closeStream();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
   }\r
 \r
   stream_.callbackInfo.callback = (void *) callback;\r
   stream_.callbackInfo.userData = userData;\r
+  stream_.callbackInfo.errorCallback = (void *) errorCallback;\r
 \r
   if ( options ) options->numberOfBuffers = stream_.nBuffers;\r
   stream_.state = STREAM_STOPPED;\r
@@ -308,10 +329,10 @@ void RtApi :: closeStream( void )
   return;\r
 }\r
 \r
-bool RtApi :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
-                               unsigned int firstChannel, unsigned int sampleRate,\r
-                               RtAudioFormat format, unsigned int *bufferSize,\r
-                               RtAudio::StreamOptions *options )\r
+bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,\r
+                               unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,\r
+                               RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,\r
+                               RtAudio::StreamOptions * /*options*/ )\r
 {\r
   // MUST be implemented in subclasses!\r
   return FAILURE;\r
@@ -416,8 +437,6 @@ struct 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; }\r
 };\r
 \r
-ThreadHandle threadId;\r
-\r
 RtApiCore:: RtApiCore()\r
 {\r
 #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
@@ -536,11 +555,13 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( nDevices == 0 ) {\r
     errorText_ = "RtApiCore::getDeviceInfo: no devices found!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   if ( device >= nDevices ) {\r
     errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   AudioDeviceID deviceList[ nDevices ];\r
@@ -729,13 +750,13 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   return info;\r
 }\r
 \r
-OSStatus callbackHandler( AudioDeviceID inDevice,\r
-                          const AudioTimeStamp* inNow,\r
-                          const AudioBufferList* inInputData,\r
-                          const AudioTimeStamp* inInputTime,\r
-                          AudioBufferList* outOutputData,\r
-                          const AudioTimeStamp* inOutputTime, \r
-                          void* infoPointer )\r
+static OSStatus callbackHandler( AudioDeviceID inDevice,\r
+                                 const AudioTimeStamp* /*inNow*/,\r
+                                 const AudioBufferList* inInputData,\r
+                                 const AudioTimeStamp* /*inInputTime*/,\r
+                                 AudioBufferList* outOutputData,\r
+                                 const AudioTimeStamp* /*inOutputTime*/,\r
+                                 void* infoPointer )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
 \r
@@ -746,10 +767,10 @@ OSStatus callbackHandler( AudioDeviceID inDevice,
     return kAudioHardwareNoError;\r
 }\r
 \r
-OSStatus xrunListener( AudioObjectID inDevice,\r
-                         UInt32 nAddresses,\r
-                         const AudioObjectPropertyAddress properties[],\r
-                         void* handlePointer )\r
+static OSStatus xrunListener( AudioObjectID /*inDevice*/,\r
+                              UInt32 nAddresses,\r
+                              const AudioObjectPropertyAddress properties[],\r
+                              void* handlePointer )\r
 {\r
   CoreHandle *handle = (CoreHandle *) handlePointer;\r
   for ( UInt32 i=0; i<nAddresses; i++ ) {\r
@@ -764,10 +785,10 @@ OSStatus xrunListener( AudioObjectID inDevice,
   return kAudioHardwareNoError;\r
 }\r
 \r
-OSStatus rateListener( AudioObjectID inDevice,\r
-                       UInt32 nAddresses,\r
-                       const AudioObjectPropertyAddress properties[],\r
-                       void* ratePointer )\r
+static OSStatus rateListener( AudioObjectID inDevice,\r
+                              UInt32 /*nAddresses*/,\r
+                              const AudioObjectPropertyAddress /*properties*/[],\r
+                              void* ratePointer )\r
 {\r
 \r
   Float64 *rate = (Float64 *) ratePointer;\r
@@ -1293,6 +1314,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     stream_.deviceBuffer = 0;\r
   }\r
 \r
+  stream_.state = STREAM_CLOSED;\r
   return FAILURE;\r
 }\r
 \r
@@ -1357,8 +1379,6 @@ void RtApiCore :: startStream( void )
     return;\r
   }\r
 \r
-  //MUTEX_LOCK( &stream_.mutex );\r
-\r
   OSStatus result = noErr;\r
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
@@ -1387,8 +1407,6 @@ void RtApiCore :: startStream( void )
   stream_.state = STREAM_RUNNING;\r
 \r
  unlock:\r
-  //MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   if ( result == noErr ) return;\r
   error( RtError::SYSTEM_ERROR );\r
 }\r
@@ -1402,15 +1420,6 @@ void RtApiCore :: stopStream( void )
     return;\r
   }\r
 \r
-  /*\r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return;\r
-  }\r
-  */\r
-\r
   OSStatus result = noErr;\r
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
@@ -1420,9 +1429,7 @@ void RtApiCore :: stopStream( void )
       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled\r
     }\r
 \r
-    //MUTEX_UNLOCK( &stream_.mutex );\r
     result = AudioDeviceStop( handle->id[0], callbackHandler );\r
-    //MUTEX_LOCK( &stream_.mutex );\r
     if ( result != noErr ) {\r
       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";\r
       errorText_ = errorStream_.str();\r
@@ -1432,9 +1439,7 @@ void RtApiCore :: stopStream( void )
 \r
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
 \r
-    //MUTEX_UNLOCK( &stream_.mutex );\r
     result = AudioDeviceStop( handle->id[1], callbackHandler );\r
-    //MUTEX_LOCK( &stream_.mutex );\r
     if ( result != noErr ) {\r
       errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";\r
       errorText_ = errorStream_.str();\r
@@ -1445,8 +1450,6 @@ void RtApiCore :: stopStream( void )
   stream_.state = STREAM_STOPPED;\r
 \r
  unlock:\r
-  //MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   if ( result == noErr ) return;\r
   error( RtError::SYSTEM_ERROR );\r
 }\r
@@ -1471,13 +1474,12 @@ void RtApiCore :: abortStream( void )
 // aborted.  It is better to handle it this way because the\r
 // callbackEvent() function probably should return before the AudioDeviceStop()\r
 // function is called.\r
-extern "C" void *coreStopStream( void *ptr )\r
+static void *coreStopStream( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiCore *object = (RtApiCore *) info->object;\r
 \r
   object->stopStream();\r
-\r
   pthread_exit( NULL );\r
 }\r
 \r
@@ -1497,27 +1499,16 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
 \r
   // Check if we were draining the stream and signal is finished.\r
   if ( handle->drainCounter > 3 ) {\r
+    ThreadHandle threadId;\r
 \r
-    if ( handle->internalDrain == true ) {\r
-      stream_.state = STREAM_STOPPING;\r
+    stream_.state = STREAM_STOPPING;\r
+    if ( handle->internalDrain == true )\r
       pthread_create( &threadId, NULL, coreStopStream, info );\r
-      //stopStream();\r
-    }\r
     else // external call to stopStream()\r
       pthread_cond_signal( &handle->condition );\r
     return SUCCESS;\r
   }\r
 \r
-  /*\r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  // The state might change while waiting on a mutex.\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return SUCCESS;\r
-  }\r
-  */\r
-\r
   AudioDeviceID outputDevice = handle->id[0];\r
 \r
   // Invoke user callback to get fresh output data UNLESS we are\r
@@ -1539,7 +1530,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
                                   stream_.bufferSize, streamTime, status, info->userData );\r
     if ( cbReturnValue == 2 ) {\r
-      //MUTEX_UNLOCK( &stream_.mutex );\r
+      stream_.state = STREAM_STOPPING;\r
       handle->drainCounter = 2;\r
       abortStream();\r
       return SUCCESS;\r
@@ -1846,8 +1837,7 @@ struct JackHandle {
     :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }\r
 };\r
 \r
-ThreadHandle threadId;\r
-void jackSilentError( const char * ) {};\r
+static void jackSilentError( const char * ) {};\r
 \r
 RtApiJack :: RtApiJack()\r
 {\r
@@ -1936,6 +1926,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
     jack_client_close( client );\r
     errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   // Get the current jack server sample rate.\r
@@ -1986,7 +1977,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   return info;\r
 }\r
 \r
-int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
+static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
 \r
@@ -2000,7 +1991,7 @@ int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
 // server signals that it is shutting down.  It is necessary to handle\r
 // it this way because the jackShutdown() function must return before\r
 // the jack_deactivate() function (in closeStream()) will return.\r
-extern "C" void *jackCloseStream( void *ptr )\r
+static void *jackCloseStream( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiJack *object = (RtApiJack *) info->object;\r
@@ -2009,7 +2000,7 @@ extern "C" void *jackCloseStream( void *ptr )
 \r
   pthread_exit( NULL );\r
 }\r
-void jackShutdown( void *infoPointer )\r
+static void jackShutdown( void *infoPointer )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) infoPointer;\r
   RtApiJack *object = (RtApiJack *) info->object;\r
@@ -2021,11 +2012,12 @@ void jackShutdown( void *infoPointer )
   // other problem occurred and we should close the stream.\r
   if ( object->isStreamRunning() == false ) return;\r
 \r
+  ThreadHandle threadId;\r
   pthread_create( &threadId, NULL, jackCloseStream, info );\r
   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;\r
 }\r
 \r
-int jackXrun( void *infoPointer )\r
+static int jackXrun( void *infoPointer )\r
 {\r
   JackHandle *handle = (JackHandle *) infoPointer;\r
 \r
@@ -2119,8 +2111,17 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
   // Get the latency of the JACK port.\r
   ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );\r
-  if ( ports[ firstChannel ] )\r
-    stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
+  if ( ports[ firstChannel ] ) {\r
+    // Added by Ge Wang\r
+    jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);\r
+    // the range (usually the min and max are equal)\r
+    jack_latency_range_t latrange; latrange.min = latrange.max = 0;\r
+    // get the latency range\r
+    jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );\r
+    // be optimistic, use the min!\r
+    stream_.latency[mode] = latrange.min;\r
+    //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );\r
+  }\r
   free( ports );\r
 \r
   // The jack server always uses 32-bit floating-point data.\r
@@ -2327,8 +2328,6 @@ void RtApiJack :: startStream( void )
     return;\r
   }\r
 \r
-  MUTEX_LOCK(&stream_.mutex);\r
-\r
   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
   int result = jack_activate( handle->client );\r
   if ( result ) {\r
@@ -2390,8 +2389,6 @@ void RtApiJack :: startStream( void )
   stream_.state = STREAM_RUNNING;\r
 \r
  unlock:\r
-  MUTEX_UNLOCK(&stream_.mutex);\r
-\r
   if ( result == 0 ) return;\r
   error( RtError::SYSTEM_ERROR );\r
 }\r
@@ -2405,13 +2402,6 @@ void RtApiJack :: stopStream( void )
     return;\r
   }\r
 \r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return;\r
-  }\r
-\r
   JackHandle *handle = (JackHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
 \r
@@ -2423,8 +2413,6 @@ void RtApiJack :: stopStream( void )
 \r
   jack_deactivate( handle->client );\r
   stream_.state = STREAM_STOPPED;\r
-\r
-  MUTEX_UNLOCK( &stream_.mutex );\r
 }\r
 \r
 void RtApiJack :: abortStream( void )\r
@@ -2447,19 +2435,18 @@ void RtApiJack :: abortStream( void )
 // aborted.  It is necessary to handle it this way because the\r
 // callbackEvent() function must return before the jack_deactivate()\r
 // function will return.\r
-extern "C" void *jackStopStream( void *ptr )\r
+static void *jackStopStream( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiJack *object = (RtApiJack *) info->object;\r
 \r
   object->stopStream();\r
-\r
   pthread_exit( NULL );\r
 }\r
 \r
 bool RtApiJack :: callbackEvent( unsigned long nframes )\r
 {\r
-  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;\r
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
   if ( stream_.state == STREAM_CLOSED ) {\r
     errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
     error( RtError::WARNING );\r
@@ -2476,6 +2463,9 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
 \r
   // Check if we were draining the stream and signal is finished.\r
   if ( handle->drainCounter > 3 ) {\r
+    ThreadHandle threadId;\r
+\r
+    stream_.state = STREAM_STOPPING;\r
     if ( handle->internalDrain == true )\r
       pthread_create( &threadId, NULL, jackStopStream, info );\r
     else\r
@@ -2483,14 +2473,6 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
     return SUCCESS;\r
   }\r
 \r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  // The state might change while waiting on a mutex.\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return SUCCESS;\r
-  }\r
-\r
   // Invoke user callback first, to get fresh output data.\r
   if ( handle->drainCounter == 0 ) {\r
     RtAudioCallback callback = (RtAudioCallback) info->callback;\r
@@ -2507,9 +2489,9 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
                                   stream_.bufferSize, streamTime, status, info->userData );\r
     if ( cbReturnValue == 2 ) {\r
-      MUTEX_UNLOCK( &stream_.mutex );\r
-      ThreadHandle id;\r
+      stream_.state = STREAM_STOPPING;\r
       handle->drainCounter = 2;\r
+      ThreadHandle id;\r
       pthread_create( &id, NULL, jackStopStream, info );\r
       return SUCCESS;\r
     }\r
@@ -2571,8 +2553,6 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
   }\r
 \r
  unlock:\r
-  MUTEX_UNLOCK(&stream_.mutex);\r
-\r
   RtApi::tickStreamTime();\r
   return SUCCESS;\r
 }\r
@@ -2603,11 +2583,11 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
 #include "asiodrivers.h"\r
 #include <cmath>\r
 \r
-AsioDrivers drivers;\r
-ASIOCallbacks asioCallbacks;\r
-ASIODriverInfo driverInfo;\r
-CallbackInfo *asioCallbackInfo;\r
-bool asioXRun;\r
+static AsioDrivers drivers;\r
+static ASIOCallbacks asioCallbacks;\r
+static ASIODriverInfo driverInfo;\r
+static CallbackInfo *asioCallbackInfo;\r
+static bool asioXRun;\r
 \r
 struct AsioHandle {\r
   int drainCounter;       // Tracks callback counts when draining\r
@@ -2621,8 +2601,8 @@ struct AsioHandle {
 \r
 // Function declarations (definitions at end of section)\r
 static const char* getAsioErrorString( ASIOError result );\r
-void sampleRateChanged( ASIOSampleRate sRate );\r
-long asioMessages( long selector, long value, void* message, double* opt );\r
+static void sampleRateChanged( ASIOSampleRate sRate );\r
+static long asioMessages( long selector, long value, void* message, double* opt );\r
 \r
 RtApiAsio :: RtApiAsio()\r
 {\r
@@ -2665,11 +2645,13 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
   if ( nDevices == 0 ) {\r
     errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   if ( device >= nDevices ) {\r
     errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.\r
@@ -2755,6 +2737,8 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
     info.nativeFormats |= RTAUDIO_FLOAT32;\r
   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )\r
     info.nativeFormats |= RTAUDIO_FLOAT64;\r
+  else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )\r
+    info.nativeFormats |= RTAUDIO_SINT24;\r
 \r
   if ( info.outputChannels > 0 )\r
     if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;\r
@@ -2766,7 +2750,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
   return info;\r
 }\r
 \r
-void bufferSwitch( long index, ASIOBool processNow )\r
+static void bufferSwitch( long index, ASIOBool processNow )\r
 {\r
   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
   object->callbackEvent( index );\r
@@ -2907,6 +2891,10 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;\r
     if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;\r
   }\r
+  else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {\r
+    stream_.deviceFormat[mode] = RTAUDIO_SINT24;\r
+    if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;\r
+  }\r
 \r
   if ( stream_.deviceFormat[mode] == 0 ) {\r
     drivers.removeCurrentDriver();\r
@@ -3189,8 +3177,6 @@ void RtApiAsio :: startStream()
     return;\r
   }\r
 \r
-  //MUTEX_LOCK( &stream_.mutex );\r
-\r
   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
   ASIOError result = ASIOStart();\r
   if ( result != ASE_OK ) {\r
@@ -3206,8 +3192,6 @@ void RtApiAsio :: startStream()
   asioXRun = false;\r
 \r
  unlock:\r
-  //MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   stopThreadCalled = false;\r
 \r
   if ( result == ASE_OK ) return;\r
@@ -3223,23 +3207,11 @@ void RtApiAsio :: stopStream()
     return;\r
   }\r
 \r
-  /*\r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return;\r
-  }\r
-  */\r
-\r
   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
     if ( handle->drainCounter == 0 ) {\r
       handle->drainCounter = 2;\r
-      //      MUTEX_UNLOCK( &stream_.mutex );\r
       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
-      //ResetEvent( handle->condition );\r
-      //      MUTEX_LOCK( &stream_.mutex );\r
     }\r
   }\r
 \r
@@ -3251,8 +3223,6 @@ void RtApiAsio :: stopStream()
     errorText_ = errorStream_.str();\r
   }\r
 \r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   if ( result == ASE_OK ) return;\r
   error( RtError::SYSTEM_ERROR );\r
 }\r
@@ -3280,21 +3250,19 @@ void RtApiAsio :: abortStream()
 // aborted.  It is necessary to handle it this way because the\r
 // callbackEvent() function must return before the ASIOStop()\r
 // function will return.\r
-extern "C" unsigned __stdcall asioStopStream( void *ptr )\r
+static unsigned __stdcall asioStopStream( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiAsio *object = (RtApiAsio *) info->object;\r
 \r
   object->stopStream();\r
-\r
   _endthreadex( 0 );\r
   return 0;\r
 }\r
 \r
 bool RtApiAsio :: callbackEvent( long bufferIndex )\r
 {\r
-  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;\r
-  if ( stopThreadCalled ) return SUCCESS;\r
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;\r
   if ( stream_.state == STREAM_CLOSED ) {\r
     errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";\r
     error( RtError::WARNING );\r
@@ -3306,22 +3274,18 @@ bool RtApiAsio :: callbackEvent( long bufferIndex )
 \r
   // Check if we were draining the stream and signal if finished.\r
   if ( handle->drainCounter > 3 ) {\r
+\r
+    stream_.state = STREAM_STOPPING;\r
     if ( handle->internalDrain == false )\r
       SetEvent( handle->condition );\r
     else { // spawn a thread to stop the stream\r
       unsigned threadId;\r
-      stopThreadCalled = true;\r
       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
                                                     &stream_.callbackInfo, 0, &threadId );\r
     }\r
     return SUCCESS;\r
   }\r
 \r
-  /*MUTEX_LOCK( &stream_.mutex );\r
-\r
-  // The state might change while waiting on a mutex.\r
-  if ( stream_.state == STREAM_STOPPED ) goto unlock; */\r
-\r
   // Invoke user callback to get fresh output data UNLESS we are\r
   // draining stream.\r
   if ( handle->drainCounter == 0 ) {\r
@@ -3339,11 +3303,9 @@ bool RtApiAsio :: callbackEvent( long bufferIndex )
     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
                                      stream_.bufferSize, streamTime, status, info->userData );\r
     if ( cbReturnValue == 2 ) {\r
-      //      MUTEX_UNLOCK( &stream_.mutex );\r
-      //      abortStream();\r
-      unsigned threadId;\r
-      stopThreadCalled = true;\r
+      stream_.state = STREAM_STOPPING;\r
       handle->drainCounter = 2;\r
+      unsigned threadId;\r
       stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,\r
                                                     &stream_.callbackInfo, 0, &threadId );\r
       return SUCCESS;\r
@@ -3447,13 +3409,11 @@ bool RtApiAsio :: callbackEvent( long bufferIndex )
   // drivers apparently do not function correctly without it.\r
   ASIOOutputReady();\r
 \r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   RtApi::tickStreamTime();\r
   return SUCCESS;\r
 }\r
 \r
-void sampleRateChanged( ASIOSampleRate sRate )\r
+static void sampleRateChanged( ASIOSampleRate sRate )\r
 {\r
   // The ASIO documentation says that this usually only happens during\r
   // external sync.  Audio processing is not stopped by the driver,\r
@@ -3473,7 +3433,7 @@ void sampleRateChanged( ASIOSampleRate sRate )
   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
 }\r
 \r
-long asioMessages( long selector, long value, void* message, double* opt )\r
+static long asioMessages( long selector, long value, void* message, double* opt )\r
 {\r
   long ret = 0;\r
 \r
@@ -3551,7 +3511,7 @@ static const char* getAsioErrorString( ASIOError result )
     const char*message;\r
   };\r
 \r
-  static Messages m[] = \r
+  static const Messages m[] = \r
     {\r
       {   ASE_NotPresent,    "Hardware input or output is not present or available." },\r
       {   ASE_HWMalfunction,  "Hardware is malfunctioning." },\r
@@ -3632,7 +3592,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
 \r
 static const char* getErrorString( int code );\r
 \r
-extern "C" unsigned __stdcall callbackHandler( void *ptr );\r
+static unsigned __stdcall callbackHandler( void *ptr );\r
 \r
 struct DsDevice {\r
   LPGUID id[2];\r
@@ -3644,7 +3604,10 @@ struct DsDevice {
   : found(false) { validId[0] = false; validId[1] = false; }\r
 };\r
 \r
-std::vector< DsDevice > dsDevices;\r
+struct DsProbeData {\r
+  bool isInput;\r
+  std::vector<struct DsDevice>* dsDevices;\r
+};\r
 \r
 RtApiDs :: RtApiDs()\r
 {\r
@@ -3682,8 +3645,10 @@ unsigned int RtApiDs :: getDeviceCount( void )
     dsDevices[i].found = false;\r
 \r
   // Query DirectSound devices.\r
-  bool isInput = false;\r
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );\r
+  struct DsProbeData probeInfo;\r
+  probeInfo.isInput = false;\r
+  probeInfo.dsDevices = &dsDevices;\r
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
   if ( FAILED( result ) ) {\r
     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
     errorText_ = errorStream_.str();\r
@@ -3691,8 +3656,8 @@ unsigned int RtApiDs :: getDeviceCount( void )
   }\r
 \r
   // Query DirectSoundCapture devices.\r
-  isInput = true;\r
-  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );\r
+  probeInfo.isInput = true;\r
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
   if ( FAILED( result ) ) {\r
     errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
     errorText_ = errorStream_.str();\r
@@ -3721,12 +3686,14 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
     if ( dsDevices.size() == 0 ) {\r
       errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
       error( RtError::INVALID_USE );\r
+      return info;\r
     }\r
   }\r
 \r
   if ( device >= dsDevices.size() ) {\r
     errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   HRESULT result;\r
@@ -3954,7 +3921,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   // Determine the device buffer size. By default, we'll use the value\r
   // defined above (32K), but we will grow it to make allowances for\r
   // very large software buffer sizes.\r
-  DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;;\r
+  DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;\r
   DWORD dsPointerLeadTime = 0;\r
 \r
   void *ohandle = 0, *bhandle = 0;\r
@@ -4381,6 +4348,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     stream_.deviceBuffer = 0;\r
   }\r
 \r
+  stream_.state = STREAM_CLOSED;\r
   return FAILURE;\r
 }\r
 \r
@@ -4447,8 +4415,6 @@ void RtApiDs :: startStream()
     return;\r
   }\r
 \r
-  //MUTEX_LOCK( &stream_.mutex );\r
-  \r
   DsHandle *handle = (DsHandle *) stream_.apiHandle;\r
 \r
   // Increase scheduler frequency on lesser windows (a side-effect of\r
@@ -4493,8 +4459,6 @@ void RtApiDs :: startStream()
   stream_.state = STREAM_RUNNING;\r
 \r
  unlock:\r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );\r
 }\r
 \r
@@ -4507,15 +4471,6 @@ void RtApiDs :: stopStream()
     return;\r
   }\r
 \r
-  /*\r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return;\r
-  }\r
-  */\r
-\r
   HRESULT result = 0;\r
   LPVOID audioPtr;\r
   DWORD dataLen;\r
@@ -4523,10 +4478,7 @@ void RtApiDs :: stopStream()
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
     if ( handle->drainCounter == 0 ) {\r
       handle->drainCounter = 2;\r
-      //      MUTEX_UNLOCK( &stream_.mutex );\r
       WaitForSingleObject( handle->condition, INFINITE );  // block until signaled\r
-      //ResetEvent( handle->condition );\r
-      //      MUTEX_LOCK( &stream_.mutex );\r
     }\r
 \r
     stream_.state = STREAM_STOPPED;\r
@@ -4604,8 +4556,6 @@ void RtApiDs :: stopStream()
 \r
  unlock:\r
   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.\r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );\r
 }\r
 \r
@@ -4626,7 +4576,7 @@ void RtApiDs :: abortStream()
 \r
 void RtApiDs :: callbackEvent()\r
 {\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {\r
     Sleep( 50 ); // sleep 50 milliseconds\r
     return;\r
   }\r
@@ -4642,6 +4592,8 @@ void RtApiDs :: callbackEvent()
 \r
   // Check if we were draining the stream and signal is finished.\r
   if ( handle->drainCounter > stream_.nBuffers + 2 ) {\r
+\r
+    stream_.state = STREAM_STOPPING;\r
     if ( handle->internalDrain == false )\r
       SetEvent( handle->condition );\r
     else\r
@@ -4649,16 +4601,6 @@ void RtApiDs :: callbackEvent()
     return;\r
   }\r
 \r
-  /*\r
-  MUTEX_LOCK( &stream_.mutex );\r
-\r
-  // The state might change while waiting on a mutex.\r
-  if ( stream_.state == STREAM_STOPPED ) {\r
-    MUTEX_UNLOCK( &stream_.mutex );\r
-    return;\r
-  }\r
-  */\r
-\r
   // Invoke user callback to get fresh output data UNLESS we are\r
   // draining stream.\r
   if ( handle->drainCounter == 0 ) {\r
@@ -4676,7 +4618,7 @@ void RtApiDs :: callbackEvent()
     int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],\r
                                   stream_.bufferSize, streamTime, status, info->userData );\r
     if ( cbReturnValue == 2 ) {\r
-      //      MUTEX_UNLOCK( &stream_.mutex );\r
+      stream_.state = STREAM_STOPPING;\r
       handle->drainCounter = 2;\r
       abortStream();\r
       return;\r
@@ -4728,12 +4670,14 @@ void RtApiDs :: callbackEvent()
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
         error( RtError::SYSTEM_ERROR );\r
+        return;\r
       }\r
       result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );\r
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
         errorText_ = errorStream_.str();\r
         error( RtError::SYSTEM_ERROR );\r
+        return;\r
       }\r
       while ( true ) {\r
         result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );\r
@@ -4741,12 +4685,14 @@ void RtApiDs :: callbackEvent()
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
           errorText_ = errorStream_.str();\r
           error( RtError::SYSTEM_ERROR );\r
+          return;\r
         }\r
         result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );\r
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
           error( RtError::SYSTEM_ERROR );\r
+          return;\r
         }\r
         if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;\r
         Sleep( 1 );\r
@@ -4767,6 +4713,7 @@ void RtApiDs :: callbackEvent()
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
         error( RtError::SYSTEM_ERROR );\r
+        return;\r
       }\r
       handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];\r
       if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];\r
@@ -4817,6 +4764,7 @@ void RtApiDs :: callbackEvent()
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
         error( RtError::SYSTEM_ERROR );\r
+        return;\r
       }\r
 \r
       // We will copy our output buffer into the region between\r
@@ -4857,6 +4805,7 @@ void RtApiDs :: callbackEvent()
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
 \r
     // Copy our buffer into the DS buffer\r
@@ -4869,6 +4818,7 @@ void RtApiDs :: callbackEvent()
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
     handle->bufferPointer[0] = nextWritePointer;\r
@@ -4903,6 +4853,7 @@ void RtApiDs :: callbackEvent()
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
       errorText_ = errorStream_.str();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
 \r
     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
@@ -4963,6 +4914,7 @@ void RtApiDs :: callbackEvent()
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
           error( RtError::SYSTEM_ERROR );\r
+          return;\r
         }\r
       \r
         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
@@ -4976,6 +4928,7 @@ void RtApiDs :: callbackEvent()
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
       errorText_ = errorStream_.str();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
 \r
     if ( duplexPrerollBytes <= 0 ) {\r
@@ -4996,6 +4949,7 @@ void RtApiDs :: callbackEvent()
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
       errorText_ = errorStream_.str();\r
       error( RtError::SYSTEM_ERROR );\r
+      return;\r
     }\r
     handle->bufferPointer[1] = nextReadPointer;\r
 \r
@@ -5011,15 +4965,13 @@ void RtApiDs :: callbackEvent()
   }\r
 \r
  unlock:\r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-\r
   RtApi::tickStreamTime();\r
 }\r
 \r
 // Definitions for utility functions and callbacks\r
 // specific to the DirectSound implementation.\r
 \r
-extern "C" unsigned __stdcall callbackHandler( void *ptr )\r
+static unsigned __stdcall callbackHandler( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiDs *object = (RtApiDs *) info->object;\r
@@ -5035,12 +4987,12 @@ extern "C" unsigned __stdcall callbackHandler( void *ptr )
 \r
 #include "tchar.h"\r
 \r
-std::string convertTChar( LPCTSTR name )\r
+static std::string convertTChar( LPCTSTR name )\r
 {\r
 #if defined( UNICODE ) || defined( _UNICODE )\r
   int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
-  std::string s( length, 0 );\r
-  length = WideCharToMultiByte(CP_UTF8, 0, name, wcslen(name), &s[0], length, NULL, NULL);\r
+  std::string s( length-1, '\0' );\r
+  WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
 #else\r
   std::string s( name );\r
 #endif\r
@@ -5053,11 +5005,12 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
                                           LPCTSTR module,\r
                                           LPVOID lpContext )\r
 {\r
-  bool *isInput = (bool *) lpContext;\r
+  struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
+  std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;\r
 \r
   HRESULT hr;\r
   bool validDevice = false;\r
-  if ( *isInput == true ) {\r
+  if ( probeInfo.isInput == true ) {\r
     DSCCAPS caps;\r
     LPDIRECTSOUNDCAPTURE object;\r
 \r
@@ -5089,13 +5042,14 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
 \r
   // If good device, then save its name and guid.\r
   std::string name = convertTChar( description );\r
-  if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
+  //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
+  if ( lpguid == NULL )\r
     name = "Default Device";\r
   if ( validDevice ) {\r
     for ( unsigned int i=0; i<dsDevices.size(); i++ ) {\r
       if ( dsDevices[i].name == name ) {\r
         dsDevices[i].found = true;\r
-        if ( *isInput ) {\r
+        if ( probeInfo.isInput ) {\r
           dsDevices[i].id[1] = lpguid;\r
           dsDevices[i].validId[1] = true;\r
         }\r
@@ -5110,7 +5064,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
     DsDevice device;\r
     device.name = name;\r
     device.found = true;\r
-    if ( *isInput ) {\r
+    if ( probeInfo.isInput ) {\r
       device.id[1] = lpguid;\r
       device.validId[1] = true;\r
     }\r
@@ -5199,7 +5153,7 @@ struct AlsaHandle {
     :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
 };\r
 \r
-extern "C" void *alsaCallbackHandler( void * ptr );\r
+static void *alsaCallbackHandler( void * ptr );\r
 \r
 RtApiAlsa :: RtApiAlsa()\r
 {\r
@@ -5248,6 +5202,12 @@ unsigned int RtApiAlsa :: getDeviceCount( void )
     snd_card_next( &card );\r
   }\r
 \r
+  result = snd_ctl_open( &handle, "default", 0 );\r
+  if (result == 0) {\r
+    nDevices++;\r
+    snd_ctl_close( handle );\r
+  }\r
+\r
   return nDevices;\r
 }\r
 \r
@@ -5294,14 +5254,25 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
     snd_card_next( &card );\r
   }\r
 \r
+  result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+  if ( result == 0 ) {\r
+    if ( nDevices == device ) {\r
+      strcpy( name, "default" );\r
+      goto foundDevice;\r
+    }\r
+    nDevices++;\r
+  }\r
+\r
   if ( nDevices == 0 ) {\r
     errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   if ( device >= nDevices ) {\r
     errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
  foundDevice:\r
@@ -5327,16 +5298,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   snd_pcm_hw_params_t *params;\r
   snd_pcm_hw_params_alloca( &params );\r
 \r
-  // First try for playback\r
+  // First try for playback unless default device (which has subdev -1)\r
   stream = SND_PCM_STREAM_PLAYBACK;\r
-  snd_pcm_info_set_device( pcminfo, subdevice );\r
-  snd_pcm_info_set_subdevice( pcminfo, 0 );\r
   snd_pcm_info_set_stream( pcminfo, stream );\r
+  if ( subdevice != -1 ) {\r
+    snd_pcm_info_set_device( pcminfo, subdevice );\r
+    snd_pcm_info_set_subdevice( pcminfo, 0 );\r
 \r
-  result = snd_ctl_pcm_info( chandle, pcminfo );\r
-  if ( result < 0 ) {\r
-    // Device probably doesn't support playback.\r
-    goto captureProbe;\r
+    result = snd_ctl_pcm_info( chandle, pcminfo );\r
+    if ( result < 0 ) {\r
+      // Device probably doesn't support playback.\r
+      goto captureProbe;\r
+    }\r
   }\r
 \r
   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );\r
@@ -5371,16 +5344,18 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   snd_pcm_close( phandle );\r
 \r
  captureProbe:\r
-  // Now try for capture\r
   stream = SND_PCM_STREAM_CAPTURE;\r
   snd_pcm_info_set_stream( pcminfo, stream );\r
 \r
-  result = snd_ctl_pcm_info( chandle, pcminfo );\r
-  snd_ctl_close( chandle );\r
-  if ( result < 0 ) {\r
-    // Device probably doesn't support capture.\r
-    if ( info.outputChannels == 0 ) return info;\r
-    goto probeParameters;\r
+  // Now try for capture unless default device (with subdev = -1)\r
+  if ( subdevice != -1 ) {\r
+    result = snd_ctl_pcm_info( chandle, pcminfo );\r
+    snd_ctl_close( chandle );\r
+    if ( result < 0 ) {\r
+      // Device probably doesn't support capture.\r
+      if ( info.outputChannels == 0 ) return info;\r
+      goto probeParameters;\r
+    }\r
   }\r
 \r
   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
@@ -5571,6 +5546,15 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       snd_card_next( &card );\r
     }\r
 \r
+    result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );\r
+    if ( result == 0 ) {\r
+      if ( nDevices == device ) {\r
+        strcpy( name, "default" );\r
+        goto foundDevice;\r
+      }\r
+      nDevices++;\r
+    }\r
+\r
     if ( nDevices == 0 ) {\r
       // This should not happen because a check is made before this function is called.\r
       errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";\r
@@ -5969,22 +5953,21 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     pthread_attr_t attr;\r
     pthread_attr_init( &attr );\r
     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );\r
+\r
 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
     if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {\r
-      struct sched_param param;\r
+      // We previously attempted to increase the audio callback priority\r
+      // to SCHED_RR here via the attributes.  However, while no errors\r
+      // were reported in doing so, it did not work.  So, now this is\r
+      // done in the alsaCallbackHandler function.\r
+      stream_.callbackInfo.doRealtime = true;\r
       int priority = options->priority;\r
       int min = sched_get_priority_min( SCHED_RR );\r
       int max = sched_get_priority_max( SCHED_RR );\r
       if ( priority < min ) priority = min;\r
       else if ( priority > max ) priority = max;\r
-      param.sched_priority = priority;\r
-      pthread_attr_setschedparam( &attr, &param );\r
-      pthread_attr_setschedpolicy( &attr, SCHED_RR );\r
+      stream_.callbackInfo.priority = priority;\r
     }\r
-    else\r
-      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
-#else\r
-    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );\r
 #endif\r
 \r
     stream_.callbackInfo.isRunning = true;\r
@@ -6022,6 +6005,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     stream_.deviceBuffer = 0;\r
   }\r
 \r
+  stream_.state = STREAM_CLOSED;\r
   return FAILURE;\r
 }\r
 \r
@@ -6139,11 +6123,6 @@ void RtApiAlsa :: stopStream()
   stream_.state = STREAM_STOPPED;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
-  //if ( stream_.state == STREAM_STOPPED ) {\r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-  //  return;\r
-  //}\r
-\r
   int result = 0;\r
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
@@ -6169,7 +6148,6 @@ void RtApiAlsa :: stopStream()
   }\r
 \r
  unlock:\r
-  stream_.state = STREAM_STOPPED;\r
   MUTEX_UNLOCK( &stream_.mutex );\r
 \r
   if ( result >= 0 ) return;\r
@@ -6188,11 +6166,6 @@ void RtApiAlsa :: abortStream()
   stream_.state = STREAM_STOPPED;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
-  //if ( stream_.state == STREAM_STOPPED ) {\r
-  //  MUTEX_UNLOCK( &stream_.mutex );\r
-  //  return;\r
-  //}\r
-\r
   int result = 0;\r
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
@@ -6215,7 +6188,6 @@ void RtApiAlsa :: abortStream()
   }\r
 \r
  unlock:\r
-  stream_.state = STREAM_STOPPED;\r
   MUTEX_UNLOCK( &stream_.mutex );\r
 \r
   if ( result >= 0 ) return;\r
@@ -6408,12 +6380,20 @@ void RtApiAlsa :: callbackEvent()
   if ( doStopStream == 1 ) this->stopStream();\r
 }\r
 \r
-extern "C" void *alsaCallbackHandler( void *ptr )\r
+static void *alsaCallbackHandler( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiAlsa *object = (RtApiAlsa *) info->object;\r
   bool *isRunning = &info->isRunning;\r
 \r
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
+  if ( &info->doRealtime ) {\r
+    pthread_t tID = pthread_self();     // ID of this thread\r
+    sched_param prio = { info->priority }; // scheduling priority of thread\r
+    pthread_setschedparam( tID, SCHED_RR, &prio );\r
+  }\r
+#endif\r
+\r
   while ( *isRunning == true ) {\r
     pthread_testcancel();\r
     object->callbackEvent();\r
@@ -6425,6 +6405,479 @@ extern "C" void *alsaCallbackHandler( void *ptr )
 //******************** End of __LINUX_ALSA__ *********************//\r
 #endif\r
 \r
+#if defined(__LINUX_PULSE__)\r
+\r
+// Code written by Peter Meerwald, pmeerw@pmeerw.net\r
+// and Tristan Matthews.\r
+\r
+#include <pulse/error.h>\r
+#include <pulse/simple.h>\r
+#include <cstdio>\r
+\r
+static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
+                                                      44100, 48000, 96000, 0};\r
+\r
+struct rtaudio_pa_format_mapping_t {\r
+  RtAudioFormat rtaudio_format;\r
+  pa_sample_format_t pa_format;\r
+};\r
+\r
+static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {\r
+  {RTAUDIO_SINT16, PA_SAMPLE_S16LE},\r
+  {RTAUDIO_SINT32, PA_SAMPLE_S32LE},\r
+  {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},\r
+  {0, PA_SAMPLE_INVALID}};\r
+\r
+struct PulseAudioHandle {\r
+  pa_simple *s_play;\r
+  pa_simple *s_rec;\r
+  pthread_t thread;\r
+  pthread_cond_t runnable_cv;\r
+  bool runnable;\r
+  PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }\r
+};\r
+\r
+RtApiPulse::~RtApiPulse()\r
+{\r
+  if ( stream_.state != STREAM_CLOSED )\r
+    closeStream();\r
+}\r
+\r
+unsigned int RtApiPulse::getDeviceCount( void )\r
+{\r
+  return 1;\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int device )\r
+{\r
+  RtAudio::DeviceInfo info;\r
+  info.probed = true;\r
+  info.name = "PulseAudio";\r
+  info.outputChannels = 2;\r
+  info.inputChannels = 2;\r
+  info.duplexChannels = 2;\r
+  info.isDefaultOutput = true;\r
+  info.isDefaultInput = true;\r
+\r
+  for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
+    info.sampleRates.push_back( *sr );\r
+\r
+  info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
+\r
+  return info;\r
+}\r
+\r
+static void *pulseaudio_callback( void * user )\r
+{\r
+  CallbackInfo *cbi = static_cast<CallbackInfo *>( user );\r
+  RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );\r
+  volatile bool *isRunning = &cbi->isRunning;\r
+\r
+  while ( *isRunning ) {\r
+    pthread_testcancel();\r
+    context->callbackEvent();\r
+  }\r
+\r
+  pthread_exit( NULL );\r
+}\r
+\r
+void RtApiPulse::closeStream( void )\r
+{\r
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+  stream_.callbackInfo.isRunning = false;\r
+  if ( pah ) {\r
+    MUTEX_LOCK( &stream_.mutex );\r
+    if ( stream_.state == STREAM_STOPPED ) {\r
+      pah->runnable = true;\r
+      pthread_cond_signal( &pah->runnable_cv );\r
+    }\r
+    MUTEX_UNLOCK( &stream_.mutex );\r
+\r
+    pthread_join( pah->thread, 0 );\r
+    if ( pah->s_play ) {\r
+      pa_simple_flush( pah->s_play, NULL );\r
+      pa_simple_free( pah->s_play );\r
+    }\r
+    if ( pah->s_rec )\r
+      pa_simple_free( pah->s_rec );\r
+\r
+    pthread_cond_destroy( &pah->runnable_cv );\r
+    delete pah;\r
+    stream_.apiHandle = 0;\r
+  }\r
+\r
+  if ( stream_.userBuffer[0] ) {\r
+    free( stream_.userBuffer[0] );\r
+    stream_.userBuffer[0] = 0;\r
+  }\r
+  if ( stream_.userBuffer[1] ) {\r
+    free( stream_.userBuffer[1] );\r
+    stream_.userBuffer[1] = 0;\r
+  }\r
+\r
+  stream_.state = STREAM_CLOSED;\r
+  stream_.mode = UNINITIALIZED;\r
+}\r
+\r
+void RtApiPulse::callbackEvent( void )\r
+{\r
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    MUTEX_LOCK( &stream_.mutex );\r
+    while ( !pah->runnable )\r
+      pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );\r
+\r
+    if ( stream_.state != STREAM_RUNNING ) {\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
+      return;\r
+    }\r
+    MUTEX_UNLOCK( &stream_.mutex );\r
+  }\r
+\r
+  if ( stream_.state == STREAM_CLOSED ) {\r
+    errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "\r
+      "this shouldn't happen!";\r
+    error( RtError::WARNING );\r
+    return;\r
+  }\r
+\r
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;\r
+  double streamTime = getStreamTime();\r
+  RtAudioStreamStatus status = 0;\r
+  int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],\r
+                               stream_.bufferSize, streamTime, status,\r
+                               stream_.callbackInfo.userData );\r
+\r
+  if ( doStopStream == 2 ) {\r
+    abortStream();\r
+    return;\r
+  }\r
+\r
+  MUTEX_LOCK( &stream_.mutex );\r
+  void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];\r
+  void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];\r
+\r
+  if ( stream_.state != STREAM_RUNNING )\r
+    goto unlock;\r
+\r
+  int pa_error;\r
+  size_t bytes;\r
+  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+    if ( stream_.doConvertBuffer[OUTPUT] ) {\r
+        convertBuffer( stream_.deviceBuffer,\r
+                       stream_.userBuffer[OUTPUT],\r
+                       stream_.convertInfo[OUTPUT] );\r
+        bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *\r
+                formatBytes( stream_.deviceFormat[OUTPUT] );\r
+    } else\r
+        bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *\r
+                formatBytes( stream_.userFormat );\r
+\r
+    if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {\r
+      errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<\r
+        pa_strerror( pa_error ) << ".";\r
+      errorText_ = errorStream_.str();\r
+      error( RtError::WARNING );\r
+    }\r
+  }\r
+\r
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {\r
+    if ( stream_.doConvertBuffer[INPUT] )\r
+      bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *\r
+        formatBytes( stream_.deviceFormat[INPUT] );\r
+    else\r
+      bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
+        formatBytes( stream_.userFormat );\r
+            \r
+    if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {\r
+      errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<\r
+        pa_strerror( pa_error ) << ".";\r
+      errorText_ = errorStream_.str();\r
+      error( RtError::WARNING );\r
+    }\r
+    if ( stream_.doConvertBuffer[INPUT] ) {\r
+      convertBuffer( stream_.userBuffer[INPUT],\r
+                     stream_.deviceBuffer,\r
+                     stream_.convertInfo[INPUT] );\r
+    }\r
+  }\r
+\r
+ unlock:\r
+  MUTEX_UNLOCK( &stream_.mutex );\r
+  RtApi::tickStreamTime();\r
+\r
+  if ( doStopStream == 1 )\r
+    stopStream();\r
+}\r
+\r
+void RtApiPulse::startStream( void )\r
+{\r
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+  if ( stream_.state == STREAM_CLOSED ) {\r
+    errorText_ = "RtApiPulse::startStream(): the stream is not open!";\r
+    error( RtError::INVALID_USE );\r
+    return;\r
+  }\r
+  if ( stream_.state == STREAM_RUNNING ) {\r
+    errorText_ = "RtApiPulse::startStream(): the stream is already running!";\r
+    error( RtError::WARNING );\r
+    return;\r
+  }\r
+\r
+  MUTEX_LOCK( &stream_.mutex );\r
+\r
+  stream_.state = STREAM_RUNNING;\r
+\r
+  pah->runnable = true;\r
+  pthread_cond_signal( &pah->runnable_cv );\r
+  MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+void RtApiPulse::stopStream( void )\r
+{\r
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+  if ( stream_.state == STREAM_CLOSED ) {\r
+    errorText_ = "RtApiPulse::stopStream(): the stream is not open!";\r
+    error( RtError::INVALID_USE );\r
+    return;\r
+  }\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";\r
+    error( RtError::WARNING );\r
+    return;\r
+  }\r
+\r
+  stream_.state = STREAM_STOPPED;\r
+  MUTEX_LOCK( &stream_.mutex );\r
+\r
+  if ( pah && pah->s_play ) {\r
+    int pa_error;\r
+    if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {\r
+      errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<\r
+        pa_strerror( pa_error ) << ".";\r
+      errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
+      error( RtError::SYSTEM_ERROR );\r
+      return;\r
+    }\r
+  }\r
+\r
+  stream_.state = STREAM_STOPPED;\r
+  MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+void RtApiPulse::abortStream( void )\r
+{\r
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );\r
+\r
+  if ( stream_.state == STREAM_CLOSED ) {\r
+    errorText_ = "RtApiPulse::abortStream(): the stream is not open!";\r
+    error( RtError::INVALID_USE );\r
+    return;\r
+  }\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";\r
+    error( RtError::WARNING );\r
+    return;\r
+  }\r
+\r
+  stream_.state = STREAM_STOPPED;\r
+  MUTEX_LOCK( &stream_.mutex );\r
+\r
+  if ( pah && pah->s_play ) {\r
+    int pa_error;\r
+    if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {\r
+      errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<\r
+        pa_strerror( pa_error ) << ".";\r
+      errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
+      error( RtError::SYSTEM_ERROR );\r
+      return;\r
+    }\r
+  }\r
+\r
+  stream_.state = STREAM_STOPPED;\r
+  MUTEX_UNLOCK( &stream_.mutex );\r
+}\r
+\r
+bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,\r
+                                  unsigned int channels, unsigned int firstChannel,\r
+                                  unsigned int sampleRate, RtAudioFormat format,\r
+                                  unsigned int *bufferSize, RtAudio::StreamOptions *options )\r
+{\r
+  PulseAudioHandle *pah = 0;\r
+  unsigned long bufferBytes = 0;\r
+  pa_sample_spec ss;\r
+\r
+  if ( device != 0 ) return false;\r
+  if ( mode != INPUT && mode != OUTPUT ) return false;\r
+  if ( channels != 1 && channels != 2 ) {\r
+    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";\r
+    return false;\r
+  }\r
+  ss.channels = channels;\r
+\r
+  if ( firstChannel != 0 ) return false;\r
+\r
+  bool sr_found = false;\r
+  for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {\r
+    if ( sampleRate == *sr ) {\r
+      sr_found = true;\r
+      stream_.sampleRate = sampleRate;\r
+      ss.rate = sampleRate;\r
+      break;\r
+    }\r
+  }\r
+  if ( !sr_found ) {\r
+    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";\r
+    return false;\r
+  }\r
+\r
+  bool sf_found = 0;\r
+  for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;\r
+        sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {\r
+    if ( format == sf->rtaudio_format ) {\r
+      sf_found = true;\r
+      stream_.userFormat = sf->rtaudio_format;\r
+      ss.format = sf->pa_format;\r
+      break;\r
+    }\r
+  }\r
+  if ( !sf_found ) {\r
+    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample format.";\r
+    return false;\r
+  }\r
+\r
+  // Set interleaving parameters.\r
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
+  else stream_.userInterleaved = true;\r
+  stream_.deviceInterleaved[mode] = true;\r
+  stream_.nBuffers = 1;\r
+  stream_.doByteSwap[mode] = false;\r
+  stream_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved;\r
+  stream_.deviceFormat[mode] = stream_.userFormat;\r
+  stream_.nUserChannels[mode] = channels;\r
+  stream_.nDeviceChannels[mode] = channels + firstChannel;\r
+  stream_.channelOffset[mode] = 0;\r
+\r
+  // Allocate necessary internal buffers.\r
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
+  if ( stream_.userBuffer[mode] == NULL ) {\r
+    errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";\r
+    goto error;\r
+  }\r
+  stream_.bufferSize = *bufferSize;\r
+\r
+  if ( stream_.doConvertBuffer[mode] ) {\r
+\r
+    bool makeBuffer = true;\r
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );\r
+    if ( mode == INPUT ) {\r
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {\r
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
+      }\r
+    }\r
+\r
+    if ( makeBuffer ) {\r
+      bufferBytes *= *bufferSize;\r
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );\r
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );\r
+      if ( stream_.deviceBuffer == NULL ) {\r
+        errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";\r
+        goto error;\r
+      }\r
+    }\r
+  }\r
+\r
+  stream_.device[mode] = device;\r
+\r
+  // Setup the buffer conversion information structure.\r
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );\r
+\r
+  if ( !stream_.apiHandle ) {\r
+    PulseAudioHandle *pah = new PulseAudioHandle;\r
+    if ( !pah ) {\r
+      errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";\r
+      goto error;\r
+    }\r
+\r
+    stream_.apiHandle = pah;\r
+    if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {\r
+      errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";\r
+      goto error;\r
+    }\r
+  }\r
+  pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
+\r
+  int error;\r
+  switch ( mode ) {\r
+  case INPUT:\r
+    pah->s_rec = pa_simple_new( NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &error );\r
+    if ( !pah->s_rec ) {\r
+      errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
+      goto error;\r
+    }\r
+    break;\r
+  case OUTPUT:\r
+    pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
+    if ( !pah->s_play ) {\r
+      errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
+      goto error;\r
+    }\r
+    break;\r
+  default:\r
+    goto error;\r
+  }\r
+\r
+  if ( stream_.mode == UNINITIALIZED )\r
+    stream_.mode = mode;\r
+  else if ( stream_.mode == mode )\r
+    goto error;\r
+  else\r
+    stream_.mode = DUPLEX;\r
+\r
+  if ( !stream_.callbackInfo.isRunning ) {\r
+    stream_.callbackInfo.object = this;\r
+    stream_.callbackInfo.isRunning = true;\r
+    if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {\r
+      errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";\r
+      goto error;\r
+    }\r
+  }\r
+\r
+  stream_.state = STREAM_STOPPED;\r
+  return true;\r
\r
+ error:\r
+  if ( pah && stream_.callbackInfo.isRunning ) {\r
+    pthread_cond_destroy( &pah->runnable_cv );\r
+    delete pah;\r
+    stream_.apiHandle = 0;\r
+  }\r
+\r
+  for ( int i=0; i<2; i++ ) {\r
+    if ( stream_.userBuffer[i] ) {\r
+      free( stream_.userBuffer[i] );\r
+      stream_.userBuffer[i] = 0;\r
+    }\r
+  }\r
+\r
+  if ( stream_.deviceBuffer ) {\r
+    free( stream_.deviceBuffer );\r
+    stream_.deviceBuffer = 0;\r
+  }\r
+\r
+  return FAILURE;\r
+}\r
+\r
+//******************** End of __LINUX_PULSE__ *********************//\r
+#endif\r
 \r
 #if defined(__LINUX_OSS__)\r
 \r
@@ -6436,7 +6889,7 @@ extern "C" void *alsaCallbackHandler( void *ptr )
 #include <errno.h>\r
 #include <math.h>\r
 \r
-extern "C" void *ossCallbackHandler(void * ptr);\r
+static void *ossCallbackHandler(void * ptr);\r
 \r
 // A structure to hold various information related to the OSS API\r
 // implementation.\r
@@ -6507,12 +6960,14 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
     close( mixerfd );\r
     errorText_ = "RtApiOss::getDeviceInfo: no devices found!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   if ( device >= nDevices ) {\r
     close( mixerfd );\r
     errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";\r
     error( RtError::INVALID_USE );\r
+    return info;\r
   }\r
 \r
   oss_audioinfo ainfo;\r
@@ -7343,7 +7798,7 @@ void RtApiOss :: callbackEvent()
   if ( doStopStream == 1 ) this->stopStream();\r
 }\r
 \r
-extern "C" void *ossCallbackHandler( void *ptr )\r
+static void *ossCallbackHandler( void *ptr )\r
 {\r
   CallbackInfo *info = (CallbackInfo *) ptr;\r
   RtApiOss *object = (RtApiOss *) info->object;\r
@@ -7372,6 +7827,28 @@ extern "C" void *ossCallbackHandler( void *ptr )
 void RtApi :: error( RtError::Type type )\r
 {\r
   errorStream_.str(""); // clear the ostringstream\r
+\r
+  RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;\r
+  if ( errorCallback ) {\r
+    // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
+    static bool firstErrorOccured = false;\r
+\r
+    if ( firstErrorOccured )\r
+      return;\r
+\r
+    firstErrorOccured = true;\r
+    const std::string errorMessage = errorText_;\r
+\r
+    if ( type != RtError::WARNING && stream_.state != STREAM_STOPPED) {\r
+      stream_.callbackInfo.isRunning = false; // exit from the thread\r
+      abortStream();\r
+    }\r
+\r
+    errorCallback( type, errorMessage );\r
+    firstErrorOccured = false;\r
+    return;\r
+  }\r
+\r
   if ( type == RtError::WARNING && showWarnings_ == true )\r
     std::cerr << '\n' << errorText_ << "\n\n";\r
   else if ( type != RtError::WARNING )\r
@@ -7401,6 +7878,7 @@ void RtApi :: clearStreamInfo()
   stream_.callbackInfo.callback = 0;\r
   stream_.callbackInfo.userData = 0;\r
   stream_.callbackInfo.isRunning = false;\r
+  stream_.callbackInfo.errorCallback = 0;\r
   for ( int i=0; i<2; i++ ) {\r
     stream_.device[i] = 11111;\r
     stream_.doConvertBuffer[i] = false;\r
@@ -7426,11 +7904,12 @@ unsigned int RtApi :: formatBytes( RtAudioFormat format )
 {\r
   if ( format == RTAUDIO_SINT16 )\r
     return 2;\r
-  else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||\r
-            format == RTAUDIO_FLOAT32 )\r
+  else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )\r
     return 4;\r
   else if ( format == RTAUDIO_FLOAT64 )\r
     return 8;\r
+  else if ( format == RTAUDIO_SINT24 )\r
+    return 3;\r
   else if ( format == RTAUDIO_SINT8 )\r
     return 1;\r
 \r
@@ -7563,11 +8042,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       }\r
     }\r
     else if (info.inFormat == RTAUDIO_SINT24) {\r
-      Int32 *in = (Int32 *)inBuffer;\r
+      Int24 *in = (Int24 *)inBuffer;\r
       scale = 1.0 / 8388607.5;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);\r
+          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());\r
           out[info.outOffset[j]] += 0.5;\r
           out[info.outOffset[j]] *= scale;\r
         }\r
@@ -7641,11 +8120,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       }\r
     }\r
     else if (info.inFormat == RTAUDIO_SINT24) {\r
-      Int32 *in = (Int32 *)inBuffer;\r
+      Int24 *in = (Int24 *)inBuffer;\r
       scale = (Float32) ( 1.0 / 8388607.5 );\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);\r
+          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());\r
           out[info.outOffset[j]] += 0.5;\r
           out[info.outOffset[j]] *= scale;\r
         }\r
@@ -7712,11 +8191,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
         out += info.outJump;\r
       }\r
     }\r
-    else if (info.inFormat == RTAUDIO_SINT24) { // Hmmm ... we could just leave it in the lower 3 bytes\r
-      Int32 *in = (Int32 *)inBuffer;\r
+    else if (info.inFormat == RTAUDIO_SINT24) {\r
+      Int24 *in = (Int24 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();\r
           out[info.outOffset[j]] <<= 8;\r
         }\r
         in += info.inJump;\r
@@ -7756,13 +8235,13 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }\r
   }\r
   else if (info.outFormat == RTAUDIO_SINT24) {\r
-    Int32 *out = (Int32 *)outBuffer;\r
+    Int24 *out = (Int24 *)outBuffer;\r
     if (info.inFormat == RTAUDIO_SINT8) {\r
       signed char *in = (signed char *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
-          out[info.outOffset[j]] <<= 16;\r
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);\r
+          //out[info.outOffset[j]] <<= 16;\r
         }\r
         in += info.inJump;\r
         out += info.outJump;\r
@@ -7772,8 +8251,8 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Int16 *in = (Int16 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
-          out[info.outOffset[j]] <<= 8;\r
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);\r
+          //out[info.outOffset[j]] <<= 8;\r
         }\r
         in += info.inJump;\r
         out += info.outJump;\r
@@ -7781,7 +8260,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }\r
     else if (info.inFormat == RTAUDIO_SINT24) {\r
       // Channel compensation and/or (de)interleaving only.\r
-      Int32 *in = (Int32 *)inBuffer;\r
+      Int24 *in = (Int24 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
           out[info.outOffset[j]] = in[info.inOffset[j]];\r
@@ -7794,8 +8273,8 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Int32 *in = (Int32 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];\r
-          out[info.outOffset[j]] >>= 8;\r
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);\r
+          //out[info.outOffset[j]] >>= 8;\r
         }\r
         in += info.inJump;\r
         out += info.outJump;\r
@@ -7847,10 +8326,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       }\r
     }\r
     else if (info.inFormat == RTAUDIO_SINT24) {\r
-      Int32 *in = (Int32 *)inBuffer;\r
+      Int24 *in = (Int24 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);\r
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);\r
         }\r
         in += info.inJump;\r
         out += info.outJump;\r
@@ -7911,10 +8390,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       }\r
     }\r
     else if (info.inFormat == RTAUDIO_SINT24) {\r
-      Int32 *in = (Int32 *)inBuffer;\r
+      Int24 *in = (Int24 *)inBuffer;\r
       for (unsigned int i=0; i<stream_.bufferSize; i++) {\r
         for (j=0; j<info.channels; j++) {\r
-          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);\r
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);\r
         }\r
         in += info.inJump;\r
         out += info.outJump;\r
@@ -7953,9 +8432,9 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
   }\r
 }\r
 \r
-  //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
-  //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
-  //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
+//static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }\r
+//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }\r
+//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }\r
 \r
 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
 {\r
@@ -7974,8 +8453,7 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat
       ptr += 2;\r
     }\r
   }\r
-  else if ( format == RTAUDIO_SINT24 ||\r
-            format == RTAUDIO_SINT32 ||\r
+  else if ( format == RTAUDIO_SINT32 ||\r
             format == RTAUDIO_FLOAT32 ) {\r
     for ( unsigned int i=0; i<samples; i++ ) {\r
       // Swap 1st and 4th bytes.\r
@@ -7993,6 +8471,17 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat
       ptr += 3;\r
     }\r
   }\r
+  else if ( format == RTAUDIO_SINT24 ) {\r
+    for ( unsigned int i=0; i<samples; i++ ) {\r
+      // Swap 1st and 3rd bytes.\r
+      val = *(ptr);\r
+      *(ptr) = *(ptr+2);\r
+      *(ptr+2) = val;\r
+\r
+      // Increment 2 more bytes.\r
+      ptr += 2;\r
+    }\r
+  }\r
   else if ( format == RTAUDIO_FLOAT64 ) {\r
     for ( unsigned int i=0; i<samples; i++ ) {\r
       // Swap 1st and 8th bytes\r