Fix typo in previous.
[rtaudio-cdist.git] / RtAudio.cpp
index 202de261a46668af3e8b6e0fb857c4e41f2f793c..9b527ef6e085b0fb0cc4602922d56f4132512291 100644 (file)
@@ -5,12 +5,12 @@
     RtAudio provides a common API (Application Programming Interface)\r
     for realtime audio input/output across Linux (native ALSA, Jack,\r
     and OSS), Macintosh OS X (CoreAudio and Jack), and Windows\r
-    (DirectSound and ASIO) operating systems.\r
+    (DirectSound, ASIO and WASAPI) operating systems.\r
 \r
     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
 \r
     RtAudio: realtime audio i/o C++ classes\r
-    Copyright (c) 2001-2013 Gary P. Scavone\r
+    Copyright (c) 2001-2016 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
 */\r
 /************************************************************************/\r
 \r
-// RtAudio: Version 4.0.12\r
+// RtAudio: Version 4.1.2\r
 \r
 #include "RtAudio.h"\r
 #include <iostream>\r
 #include <cstdlib>\r
 #include <cstring>\r
 #include <climits>\r
+#include <algorithm>\r
+#include <cmath>\r
 \r
 // Static variable definitions.\r
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
@@ -58,6 +60,22 @@ 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
+\r
+  #include "tchar.h"\r
+\r
+  static std::string convertCharPointerToStdString(const char *text)\r
+  {\r
+    return std::string(text);\r
+  }\r
+\r
+  static std::string convertCharPointerToStdString(const wchar_t *text)\r
+  {\r
+    int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);\r
+    std::string s( length-1, '\0' );\r
+    WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);\r
+    return s;\r
+  }\r
+\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
@@ -179,7 +197,7 @@ RtAudio :: RtAudio( RtAudio::Api api )
   getCompiledApi( apis );\r
   for ( unsigned int i=0; i<apis.size(); i++ ) {\r
     openRtApi( apis[i] );\r
-    if ( rtapi_->getDeviceCount() ) break;\r
+    if ( rtapi_ && rtapi_->getDeviceCount() ) break;\r
   }\r
 \r
   if ( rtapi_ ) return;\r
@@ -249,6 +267,9 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     return;\r
   }\r
 \r
+  // Clear stream information potentially left from a previously open stream.\r
+  clearStreamInfo();\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( RtAudioError::INVALID_USE );\r
@@ -294,7 +315,6 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     }\r
   }\r
 \r
-  clearStreamInfo();\r
   bool result;\r
 \r
   if ( oChannels > 0 ) {\r
@@ -389,19 +409,29 @@ double RtApi :: getStreamTime( void )
   struct timeval then;\r
   struct timeval now;\r
 \r
-  if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
+  // If lastTickTimestamp is 0 it means we haven't had a "last tick" since\r
+  // we started the stream.\r
+  if ( stream_.state != STREAM_RUNNING || (stream_.lastTickTimestamp.tv_sec == 0 && stream_.lastTickTimestamp.tv_usec == 0) )\r
     return stream_.streamTime;\r
 \r
   gettimeofday( &now, NULL );\r
   then = stream_.lastTickTimestamp;\r
   return stream_.streamTime +\r
     ((now.tv_sec + 0.000001 * now.tv_usec) -\r
-     (then.tv_sec + 0.000001 * then.tv_usec));     \r
+     (then.tv_sec + 0.000001 * then.tv_usec));\r
 #else\r
   return stream_.streamTime;\r
 #endif\r
 }\r
 \r
+void RtApi :: setStreamTime( double time )\r
+{\r
+  verifyStream();\r
+\r
+  if ( time >= 0.0 )\r
+    stream_.streamTime = time;\r
+}\r
+\r
 unsigned int RtApi :: getStreamSampleRate( void )\r
 {\r
  verifyStream();\r
@@ -409,6 +439,14 @@ unsigned int RtApi :: getStreamSampleRate( void )
  return stream_.sampleRate;\r
 }\r
 \r
+void RtApi :: startStream( void )\r
+{\r
+#if defined( HAVE_GETTIMEOFDAY )\r
+  stream_.lastTickTimestamp.tv_sec = 0;\r
+  stream_.lastTickTimestamp.tv_usec = 0;\r
+#endif\r
+}\r
+\r
 \r
 // *************************************************** //\r
 //\r
@@ -751,9 +789,14 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   bool haveValueRange = false;\r
   info.sampleRates.clear();\r
   for ( UInt32 i=0; i<nRanges; i++ ) {\r
-    if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
-      info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\r
-    else {\r
+    if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {\r
+      unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;\r
+      info.sampleRates.push_back( tmpSr );\r
+\r
+      if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )\r
+        info.preferredSampleRate = tmpSr;\r
+\r
+    } else {\r
       haveValueRange = true;\r
       if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;\r
       if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;\r
@@ -762,8 +805,12 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 \r
   if ( haveValueRange ) {\r
     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
-      if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )\r
+      if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {\r
         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+\r
+        if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
+          info.preferredSampleRate = SAMPLE_RATES[k];\r
+      }\r
     }\r
   }\r
 \r
@@ -1370,6 +1417,18 @@ void RtApiCore :: closeStream( void )
 \r
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+    if (handle) {\r
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+        kAudioObjectPropertyScopeGlobal,\r
+        kAudioObjectPropertyElementMaster };\r
+\r
+      property.mSelector = kAudioDeviceProcessorOverload;\r
+      property.mScope = kAudioObjectPropertyScopeGlobal;\r
+      if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {\r
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
+        error( RtAudioError::WARNING );\r
+      }\r
+    }\r
     if ( stream_.state == STREAM_RUNNING )\r
       AudioDeviceStop( handle->id[0], callbackHandler );\r
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
@@ -1381,6 +1440,18 @@ void RtApiCore :: closeStream( void )
   }\r
 \r
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
+    if (handle) {\r
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+        kAudioObjectPropertyScopeGlobal,\r
+        kAudioObjectPropertyElementMaster };\r
+\r
+      property.mSelector = kAudioDeviceProcessorOverload;\r
+      property.mScope = kAudioObjectPropertyScopeGlobal;\r
+      if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {\r
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
+        error( RtAudioError::WARNING );\r
+      }\r
+    }\r
     if ( stream_.state == STREAM_RUNNING )\r
       AudioDeviceStop( handle->id[1], callbackHandler );\r
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
@@ -1415,6 +1486,7 @@ void RtApiCore :: closeStream( void )
 void RtApiCore :: startStream( void )\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -1680,11 +1752,12 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
         }\r
       }\r
     }\r
+  }\r
 \r
-    if ( handle->drainCounter ) {\r
-      handle->drainCounter++;\r
-      goto unlock;\r
-    }\r
+  // Don't bother draining input\r
+  if ( handle->drainCounter ) {\r
+    handle->drainCounter++;\r
+    goto unlock;\r
   }\r
 \r
   AudioDeviceID inputDevice;\r
@@ -1768,7 +1841,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
           channelsLeft -= streamChannels;\r
         }\r
       }\r
-      \r
+\r
       if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer\r
         convertBuffer( stream_.userBuffer[1],\r
                        stream_.deviceBuffer,\r
@@ -1973,7 +2046,9 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
 \r
   // Get the current jack server sample rate.\r
   info.sampleRates.clear();\r
-  info.sampleRates.push_back( jack_get_sample_rate( client ) );\r
+\r
+  info.preferredSampleRate = jack_get_sample_rate( client );\r
+  info.sampleRates.push_back( info.preferredSampleRate );\r
 \r
   // Count the available ports containing the client name as device\r
   // channels.  Jack "input ports" equal RtAudio output channels.\r
@@ -2364,6 +2439,7 @@ void RtApiJack :: closeStream( void )
 void RtApiJack :: startStream( void )\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -2570,11 +2646,12 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
         memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );\r
       }\r
     }\r
+  }\r
 \r
-    if ( handle->drainCounter ) {\r
-      handle->drainCounter++;\r
-      goto unlock;\r
-    }\r
+  // Don't bother draining input\r
+  if ( handle->drainCounter ) {\r
+    handle->drainCounter++;\r
+    goto unlock;\r
   }\r
 \r
   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
@@ -2652,7 +2729,7 @@ RtApiAsio :: RtApiAsio()
   // CoInitialize beforehand, but it must be for appartment threading\r
   // (in which case, CoInitilialize will return S_FALSE here).\r
   coInitialized_ = false;\r
-  HRESULT hr = CoInitialize( NULL ); \r
+  HRESULT hr = CoInitialize( NULL );\r
   if ( FAILED(hr) ) {\r
     errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";\r
     error( RtAudioError::WARNING );\r
@@ -2752,8 +2829,12 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
   info.sampleRates.clear();\r
   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );\r
-    if ( result == ASE_OK )\r
+    if ( result == ASE_OK ) {\r
       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+\r
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
+        info.preferredSampleRate = SAMPLE_RATES[i];\r
+    }\r
   }\r
 \r
   // Determine supported data types ... just check first channel and assume rest are the same.\r
@@ -2812,9 +2893,12 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
                                    unsigned int firstChannel, unsigned int sampleRate,\r
                                    RtAudioFormat format, unsigned int *bufferSize,\r
                                    RtAudio::StreamOptions *options )\r
-{\r
+{////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
+\r
+  bool isDuplexInput =  mode == INPUT && stream_.mode == OUTPUT;\r
+\r
   // For ASIO, a duplex stream MUST use the same driver.\r
-  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {\r
+  if ( isDuplexInput && stream_.device[0] != device ) {\r
     errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";\r
     return FAILURE;\r
   }\r
@@ -2828,7 +2912,7 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }\r
 \r
   // Only load the driver once for duplex stream.\r
-  if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
+  if ( !isDuplexInput ) {\r
     // The getDeviceInfo() function will not work when a stream is open\r
     // because ASIO does not allow multiple devices to run at the same\r
     // time.  Thus, we'll probe the system before opening a stream and\r
@@ -2849,22 +2933,26 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }\r
   }\r
 \r
+  // keep them before any "goto error", they are used for error cleanup + goto device boundary checks\r
+  bool buffersAllocated = false;\r
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
+  unsigned int nChannels;\r
+\r
+\r
   // Check the device channel count.\r
   long inputChannels, outputChannels;\r
   result = ASIOGetChannels( &inputChannels, &outputChannels );\r
   if ( result != ASE_OK ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
   if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||\r
        ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
   stream_.nDeviceChannels[mode] = channels;\r
   stream_.nUserChannels[mode] = channels;\r
@@ -2873,30 +2961,27 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // Verify the sample rate is supported.\r
   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );\r
   if ( result != ASE_OK ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
   // Get the current sample rate\r
   ASIOSampleRate currentRate;\r
   result = ASIOGetSampleRate( &currentRate );\r
   if ( result != ASE_OK ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
   // Set the sample rate only if necessary\r
   if ( currentRate != sampleRate ) {\r
     result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );\r
     if ( result != ASE_OK ) {\r
-      drivers.removeCurrentDriver();\r
       errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";\r
       errorText_ = errorStream_.str();\r
-      return FAILURE;\r
+      goto error;\r
     }\r
   }\r
 \r
@@ -2907,10 +2992,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   else channelInfo.isInput = true;\r
   result = ASIOGetChannelInfo( &channelInfo );\r
   if ( result != ASE_OK ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
   // Assuming WINDOWS host is always little-endian.\r
@@ -2939,10 +3023,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }\r
 \r
   if ( stream_.deviceFormat[mode] == 0 ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
   // Set the buffer size.  For a duplex stream, this will end up\r
@@ -2951,49 +3034,63 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   long minSize, maxSize, preferSize, granularity;\r
   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );\r
   if ( result != ASE_OK ) {\r
-    drivers.removeCurrentDriver();\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";\r
     errorText_ = errorStream_.str();\r
-    return FAILURE;\r
+    goto error;\r
   }\r
 \r
-  if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
-  else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
-  else if ( granularity == -1 ) {\r
-    // Make sure bufferSize is a power of two.\r
-    int log2_of_min_size = 0;\r
-    int log2_of_max_size = 0;\r
+  if ( isDuplexInput ) {\r
+    // When this is the duplex input (output was opened before), then we have to use the same\r
+    // buffersize as the output, because it might use the preferred buffer size, which most\r
+    // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,\r
+    // So instead of throwing an error, make them equal. The caller uses the reference\r
+    // to the "bufferSize" param as usual to set up processing buffers.\r
 \r
-    for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
-      if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
-      if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
-    }\r
+    *bufferSize = stream_.bufferSize;\r
 \r
-    long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
-    int min_delta_num = log2_of_min_size;\r
+  } else {\r
+    if ( *bufferSize == 0 ) *bufferSize = preferSize;\r
+    else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
+    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
+    else if ( granularity == -1 ) {\r
+      // Make sure bufferSize is a power of two.\r
+      int log2_of_min_size = 0;\r
+      int log2_of_max_size = 0;\r
 \r
-    for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
-      long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
-      if (current_delta < min_delta) {\r
-        min_delta = current_delta;\r
-        min_delta_num = i;\r
+      for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {\r
+        if ( minSize & ((long)1 << i) ) log2_of_min_size = i;\r
+        if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;\r
       }\r
-    }\r
 \r
-    *bufferSize = ( (unsigned int)1 << min_delta_num );\r
-    if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
-    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
-  }\r
-  else if ( granularity != 0 ) {\r
-    // Set to an even multiple of granularity, rounding up.\r
-    *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
+      long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );\r
+      int min_delta_num = log2_of_min_size;\r
+\r
+      for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {\r
+        long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );\r
+        if (current_delta < min_delta) {\r
+          min_delta = current_delta;\r
+          min_delta_num = i;\r
+        }\r
+      }\r
+\r
+      *bufferSize = ( (unsigned int)1 << min_delta_num );\r
+      if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
+      else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
+    }\r
+    else if ( granularity != 0 ) {\r
+      // Set to an even multiple of granularity, rounding up.\r
+      *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;\r
+    }\r
   }\r
 \r
-  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {\r
-    drivers.removeCurrentDriver();\r
+  /*\r
+  // we don't use it anymore, see above!\r
+  // Just left it here for the case...\r
+  if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {\r
     errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";\r
-    return FAILURE;\r
+    goto error;\r
   }\r
+  */\r
 \r
   stream_.bufferSize = *bufferSize;\r
   stream_.nBuffers = 2;\r
@@ -3005,16 +3102,13 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.deviceInterleaved[mode] = false;\r
 \r
   // Allocate, if necessary, our AsioHandle structure for the stream.\r
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;\r
   if ( handle == 0 ) {\r
     try {\r
       handle = new AsioHandle;\r
     }\r
     catch ( std::bad_alloc& ) {\r
-      //if ( handle == NULL ) {    \r
-      drivers.removeCurrentDriver();\r
       errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";\r
-      return FAILURE;\r
+      goto error;\r
     }\r
     handle->bufferInfos = 0;\r
 \r
@@ -3029,15 +3123,14 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // Create the ASIO internal buffers.  Since RtAudio sets up input\r
   // and output separately, we'll have to dispose of previously\r
   // created output buffers for a duplex stream.\r
-  long inputLatency, outputLatency;\r
   if ( mode == INPUT && stream_.mode == OUTPUT ) {\r
     ASIODisposeBuffers();\r
     if ( handle->bufferInfos ) free( handle->bufferInfos );\r
   }\r
 \r
   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.\r
-  bool buffersAllocated = false;\r
-  unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
+  unsigned int i;\r
+  nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];\r
   handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );\r
   if ( handle->bufferInfos == NULL ) {\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";\r
@@ -3058,18 +3151,37 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     infos->buffers[0] = infos->buffers[1] = 0;\r
   }\r
 \r
+  // prepare for callbacks\r
+  stream_.sampleRate = sampleRate;\r
+  stream_.device[mode] = device;\r
+  stream_.mode = isDuplexInput ? DUPLEX : mode;\r
+\r
+  // store this class instance before registering callbacks, that are going to use it\r
+  asioCallbackInfo = &stream_.callbackInfo;\r
+  stream_.callbackInfo.object = (void *) this;\r
+\r
   // Set up the ASIO callback structure and create the ASIO data buffers.\r
   asioCallbacks.bufferSwitch = &bufferSwitch;\r
   asioCallbacks.sampleRateDidChange = &sampleRateChanged;\r
   asioCallbacks.asioMessage = &asioMessages;\r
   asioCallbacks.bufferSwitchTimeInfo = NULL;\r
   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
+  if ( result != ASE_OK ) {\r
+    // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges\r
+    // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver\r
+    // in that case, let's be naïve and try that instead\r
+    *bufferSize = preferSize;\r
+    stream_.bufferSize = *bufferSize;\r
+    result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );\r
+  }\r
+\r
   if ( result != ASE_OK ) {\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";\r
     errorText_ = errorStream_.str();\r
     goto error;\r
   }\r
   buffersAllocated = true;\r
+  stream_.state = STREAM_STOPPED;\r
 \r
   // Set flags for buffer conversion.\r
   stream_.doConvertBuffer[mode] = false;\r
@@ -3092,11 +3204,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \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
+    if ( isDuplexInput && stream_.deviceBuffer ) {\r
+      unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );\r
+      if ( bufferBytes <= bytesOut ) makeBuffer = false;\r
     }\r
 \r
     if ( makeBuffer ) {\r
@@ -3110,18 +3220,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }\r
   }\r
 \r
-  stream_.sampleRate = sampleRate;\r
-  stream_.device[mode] = device;\r
-  stream_.state = STREAM_STOPPED;\r
-  asioCallbackInfo = &stream_.callbackInfo;\r
-  stream_.callbackInfo.object = (void *) this;\r
-  if ( stream_.mode == OUTPUT && mode == INPUT )\r
-    // We had already set up an output stream.\r
-    stream_.mode = DUPLEX;\r
-  else\r
-    stream_.mode = mode;\r
-\r
   // Determine device latencies\r
+  long inputLatency, outputLatency;\r
   result = ASIOGetLatencies( &inputLatency, &outputLatency );\r
   if ( result != ASE_OK ) {\r
     errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";\r
@@ -3141,32 +3241,38 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   return SUCCESS;\r
 \r
  error:\r
-  if ( buffersAllocated )\r
-    ASIODisposeBuffers();\r
-  drivers.removeCurrentDriver();\r
+  if ( !isDuplexInput ) {\r
+    // the cleanup for error in the duplex input, is done by RtApi::openStream\r
+    // So we clean up for single channel only\r
 \r
-  if ( handle ) {\r
-    CloseHandle( handle->condition );\r
-    if ( handle->bufferInfos )\r
-      free( handle->bufferInfos );\r
-    delete handle;\r
-    stream_.apiHandle = 0;\r
-  }\r
+    if ( buffersAllocated )\r
+      ASIODisposeBuffers();\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
+    drivers.removeCurrentDriver();\r
+\r
+    if ( handle ) {\r
+      CloseHandle( handle->condition );\r
+      if ( handle->bufferInfos )\r
+        free( handle->bufferInfos );\r
+\r
+      delete handle;\r
+      stream_.apiHandle = 0;\r
     }\r
-  }\r
 \r
-  if ( stream_.deviceBuffer ) {\r
-    free( stream_.deviceBuffer );\r
-    stream_.deviceBuffer = 0;\r
+\r
+    if ( stream_.userBuffer[mode] ) {\r
+      free( stream_.userBuffer[mode] );\r
+      stream_.userBuffer[mode] = 0;\r
+    }\r
+\r
+    if ( stream_.deviceBuffer ) {\r
+      free( stream_.deviceBuffer );\r
+      stream_.deviceBuffer = 0;\r
+    }\r
   }\r
 \r
   return FAILURE;\r
-}\r
+}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\r
 \r
 void RtApiAsio :: closeStream()\r
 {\r
@@ -3213,6 +3319,7 @@ bool stopThreadCalled = false;
 void RtApiAsio :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -3401,11 +3508,12 @@ bool RtApiAsio :: callbackEvent( long bufferIndex )
       }\r
 \r
     }\r
+  }\r
 \r
-    if ( handle->drainCounter ) {\r
-      handle->drainCounter++;\r
-      goto unlock;\r
-    }\r
+  // Don't bother draining input\r
+  if ( handle->drainCounter ) {\r
+    handle->drainCounter++;\r
+    goto unlock;\r
   }\r
 \r
   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
@@ -3547,13 +3655,13 @@ static long asioMessages( long selector, long value, void* /*message*/, double*
 \r
 static const char* getAsioErrorString( ASIOError result )\r
 {\r
-  struct Messages \r
+  struct Messages\r
   {\r
     ASIOError value;\r
     const char*message;\r
   };\r
 \r
-  static const 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
@@ -3576,21 +3684,22 @@ static const char* getAsioErrorString( ASIOError result )
 \r
 #if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
 \r
+// Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014\r
+// - Introduces support for the Windows WASAPI API\r
+// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required\r
+// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface\r
+// - Includes automatic internal conversion of sample rate and buffer size between hardware and the user\r
+\r
+#ifndef INITGUID\r
+  #define INITGUID\r
+#endif\r
 #include <audioclient.h>\r
 #include <avrt.h>\r
-#include <functiondiscoverykeys.h>\r
 #include <mmdeviceapi.h>\r
+#include <FunctionDiscoveryKeys_devpkey.h>\r
 \r
 //=============================================================================\r
 \r
-#define EXIT_ON_ERROR( hr, errorType, errorText )\\r
-if ( FAILED( hr ) )\\r
-{\\r
-  errorText_ = __FUNCTION__ ": " errorText;\\r
-  error( errorType );\\r
-  goto Exit;\\r
-}\r
-\r
 #define SAFE_RELEASE( objectPtr )\\r
 if ( objectPtr )\\r
 {\\r
@@ -3616,12 +3725,12 @@ public:
       outIndex_( 0 ) {}\r
 \r
   ~WasapiBuffer() {\r
-    delete buffer_;\r
+    free( buffer_ );\r
   }\r
 \r
   // sets the length of the internal ring buffer\r
   void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
-    delete buffer_;\r
+    free( buffer_ );\r
 \r
     buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
 \r
@@ -3761,16 +3870,14 @@ private:
 \r
 //-----------------------------------------------------------------------------\r
 \r
-// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate and\r
-// channel counts between HW and the user. The convertBufferWasapi function is used to perform\r
-// these conversions between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
+// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate\r
+// between HW and the user. The convertBufferWasapi function is used to perform this conversion\r
+// between HwIn->UserIn and UserOut->HwOut during the stream callback loop.\r
 // This sample rate converter favors speed over quality, and works best with conversions between\r
-// one rate and its multiple. RtApiWasapi will not populate a device's sample rate list with rates\r
-// that may cause artifacts via this conversion.\r
+// one rate and its multiple.\r
 void convertBufferWasapi( char* outBuffer,\r
                           const char* inBuffer,\r
-                          const unsigned int& inChannelCount,\r
-                          const unsigned int& outChannelCount,\r
+                          const unsigned int& channelCount,\r
                           const unsigned int& inSampleRate,\r
                           const unsigned int& outSampleRate,\r
                           const unsigned int& inSampleCount,\r
@@ -3781,9 +3888,8 @@ void convertBufferWasapi( char* outBuffer,
   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
   float sampleStep = 1.0f / sampleRatio;\r
   float inSampleFraction = 0.0f;\r
-  unsigned int commonChannelCount = min( inChannelCount, outChannelCount );\r
 \r
-  outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio );\r
+  outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio );\r
 \r
   // frame-by-frame, copy each relative input sample into it's corresponding output sample\r
   for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )\r
@@ -3793,22 +3899,22 @@ void convertBufferWasapi( char* outBuffer,
     switch ( format )\r
     {\r
       case RTAUDIO_SINT8:\r
-        memcpy( &( ( char* ) outBuffer )[ outSample * outChannelCount ], &( ( char* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( char ) );\r
+        memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
         break;\r
       case RTAUDIO_SINT16:\r
-        memcpy( &( ( short* ) outBuffer )[ outSample * outChannelCount ], &( ( short* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( short ) );\r
+        memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
         break;\r
       case RTAUDIO_SINT24:\r
-        memcpy( &( ( S24* ) outBuffer )[ outSample * outChannelCount ], &( ( S24* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( S24 ) );\r
+        memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
         break;\r
       case RTAUDIO_SINT32:\r
-        memcpy( &( ( int* ) outBuffer )[ outSample * outChannelCount ], &( ( int* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( int ) );\r
+        memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
         break;\r
       case RTAUDIO_FLOAT32:\r
-        memcpy( &( ( float* ) outBuffer )[ outSample * outChannelCount ], &( ( float* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( float ) );\r
+        memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
         break;\r
       case RTAUDIO_FLOAT64:\r
-        memcpy( &( ( double* ) outBuffer )[ outSample * outChannelCount ], &( ( double* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( double ) );\r
+        memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
         break;\r
     }\r
 \r
@@ -3845,11 +3951,10 @@ RtApiWasapi::RtApiWasapi()
 {\r
   // WASAPI can run either apartment or multi-threaded\r
   HRESULT hr = CoInitialize( NULL );\r
-\r
   if ( !FAILED( hr ) )\r
     coInitialized_ = true;\r
 \r
-  // instantiate device enumerator\r
+  // Instantiate device enumerator\r
   hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
                          CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
                          ( void** ) &deviceEnumerator_ );\r
@@ -3864,16 +3969,14 @@ RtApiWasapi::RtApiWasapi()
 \r
 RtApiWasapi::~RtApiWasapi()\r
 {\r
-  // if this object previously called CoInitialize()\r
-  if ( coInitialized_ ) {\r
-    CoUninitialize();\r
-  }\r
-\r
-  if ( stream_.state != STREAM_CLOSED ) {\r
+  if ( stream_.state != STREAM_CLOSED )\r
     closeStream();\r
-  }\r
 \r
   SAFE_RELEASE( deviceEnumerator_ );\r
+\r
+  // If this object previously called CoInitialize()\r
+  if ( coInitialized_ )\r
+    CoUninitialize();\r
 }\r
 \r
 //=============================================================================\r
@@ -3886,26 +3989,43 @@ unsigned int RtApiWasapi::getDeviceCount( void )
   IMMDeviceCollection* captureDevices = NULL;\r
   IMMDeviceCollection* renderDevices = NULL;\r
 \r
-  // count capture devices\r
+  // Count capture devices\r
+  errorText_.clear();\r
   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = captureDevices->GetCount( &captureDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";\r
+    goto Exit;\r
+  }\r
 \r
-  // count render devices\r
+  // Count render devices\r
   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = renderDevices->GetCount( &renderDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";\r
+    goto Exit;\r
+  }\r
 \r
 Exit:\r
   // release all references\r
   SAFE_RELEASE( captureDevices );\r
   SAFE_RELEASE( renderDevices );\r
 \r
-  return captureDeviceCount + renderDeviceCount;\r
+  if ( errorText_.empty() )\r
+    return captureDeviceCount + renderDeviceCount;\r
+\r
+  error( RtAudioError::DRIVER_ERROR );\r
+  return 0;\r
 }\r
 \r
 //-----------------------------------------------------------------------------\r
@@ -3915,7 +4035,6 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
   RtAudio::DeviceInfo info;\r
   unsigned int captureDeviceCount = 0;\r
   unsigned int renderDeviceCount = 0;\r
-  std::wstring deviceName;\r
   std::string defaultDeviceName;\r
   bool isCaptureDevice = false;\r
 \r
@@ -3936,73 +4055,106 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
   // probed\r
   info.probed = false;\r
 \r
-  // count capture devices\r
+  // Count capture devices\r
+  errorText_.clear();\r
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = captureDevices->GetCount( &captureDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";\r
+    goto Exit;\r
+  }\r
 \r
-  // count render devices\r
+  // Count render devices\r
   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = renderDevices->GetCount( &renderDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";\r
+    goto Exit;\r
+  }\r
 \r
   // validate device index\r
-  if ( device >= captureDeviceCount + renderDeviceCount )\r
-    EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );\r
+  if ( device >= captureDeviceCount + renderDeviceCount ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";\r
+    errorType = RtAudioError::INVALID_USE;\r
+    goto Exit;\r
+  }\r
 \r
   // determine whether index falls within capture or render devices\r
-  //if ( device < captureDeviceCount ) {\r
   if ( device >= renderDeviceCount ) {\r
-    //hr = captureDevices->Item( device, &devicePtr );\r
     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );\r
-\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";\r
+      goto Exit;\r
+    }\r
     isCaptureDevice = true;\r
   }\r
   else {\r
-    //hr = renderDevices->Item( device - captureDeviceCount, &devicePtr );\r
     hr = renderDevices->Item( device, &devicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );\r
-\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";\r
+      goto Exit;\r
+    }\r
     isCaptureDevice = false;\r
   }\r
 \r
   // get default device name\r
   if ( isCaptureDevice ) {\r
     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default render device handle" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";\r
+      goto Exit;\r
+    }\r
   }\r
   else {\r
     hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default capture device handle" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";\r
+      goto Exit;\r
+    }\r
   }\r
 \r
   hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to open default device property store" );\r
-\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";\r
+    goto Exit;\r
+  }\r
   PropVariantInit( &defaultDeviceNameProp );\r
 \r
   hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve default device property: PKEY_Device_FriendlyName" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";\r
+    goto Exit;\r
+  }\r
 \r
-  deviceName = defaultDeviceNameProp.pwszVal;\r
-  defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\r
+  defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);\r
 \r
   // name\r
   hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to open device property store" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";\r
+    goto Exit;\r
+  }\r
 \r
   PropVariantInit( &deviceNameProp );\r
 \r
   hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device property: PKEY_Device_FriendlyName" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";\r
+    goto Exit;\r
+  }\r
 \r
-  deviceName = deviceNameProp.pwszVal;\r
-  info.name = std::string( deviceName.begin(), deviceName.end() );\r
+  info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);\r
 \r
   // is default\r
   if ( isCaptureDevice ) {\r
@@ -4016,10 +4168,16 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
 \r
   // channel count\r
   hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = audioClient->GetMixFormat( &deviceFormat );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";\r
+    goto Exit;\r
+  }\r
 \r
   if ( isCaptureDevice ) {\r
     info.inputChannels = deviceFormat->nChannels;\r
@@ -4035,19 +4193,11 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
   // sample rates\r
   info.sampleRates.clear();\r
 \r
-  // allow support for sample rates that are multiples of the base rate\r
+  // allow support for all sample rates as we have a built-in sample rate converter\r
   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
-    if ( SAMPLE_RATES[i] < deviceFormat->nSamplesPerSec ) {\r
-      if ( deviceFormat->nSamplesPerSec % SAMPLE_RATES[i] == 0 ) {\r
-        info.sampleRates.push_back( SAMPLE_RATES[i] );\r
-      }\r
-    }\r
-    else {\r
-      if ( SAMPLE_RATES[i] % deviceFormat->nSamplesPerSec == 0 ) {\r
-        info.sampleRates.push_back( SAMPLE_RATES[i] );\r
-      }\r
-    }\r
+    info.sampleRates.push_back( SAMPLE_RATES[i] );\r
   }\r
+  info.preferredSampleRate = deviceFormat->nSamplesPerSec;\r
 \r
   // native format\r
   info.nativeFormats = 0;\r
@@ -4100,6 +4250,8 @@ Exit:
   CoTaskMemFree( deviceFormat );\r
   CoTaskMemFree( closestMatchFormat );\r
 \r
+  if ( !errorText_.empty() )\r
+    error( errorType );\r
   return info;\r
 }\r
 \r
@@ -4134,7 +4286,7 @@ unsigned int RtApiWasapi::getDefaultInputDevice( void )
 void RtApiWasapi::closeStream( void )\r
 {\r
   if ( stream_.state == STREAM_CLOSED ) {\r
-    errorText_ = "RtApiWasapi::closeStream: No open stream to close";\r
+    errorText_ = "RtApiWasapi::closeStream: No open stream to close.";\r
     error( RtAudioError::WARNING );\r
     return;\r
   }\r
@@ -4155,7 +4307,7 @@ void RtApiWasapi::closeStream( void )
   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
     CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
 \r
-  delete stream_.apiHandle;\r
+  delete ( WasapiHandle* ) stream_.apiHandle;\r
   stream_.apiHandle = NULL;\r
 \r
   for ( int i = 0; i < 2; i++ ) {\r
@@ -4179,9 +4331,10 @@ void RtApiWasapi::closeStream( void )
 void RtApiWasapi::startStream( void )\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
 \r
   if ( stream_.state == STREAM_RUNNING ) {\r
-    errorText_ = "RtApiWasapi::startStream: The stream is already running";\r
+    errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
     error( RtAudioError::WARNING );\r
     return;\r
   }\r
@@ -4190,10 +4343,10 @@ void RtApiWasapi::startStream( void )
   stream_.state = STREAM_RUNNING;\r
 \r
   // create WASAPI stream thread\r
-  stream_.callbackInfo.thread = ( unsigned int ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
+  stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );\r
 \r
   if ( !stream_.callbackInfo.thread ) {\r
-    errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread";\r
+    errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";\r
     error( RtAudioError::THREAD_ERROR );\r
   }\r
   else {\r
@@ -4209,7 +4362,7 @@ void RtApiWasapi::stopStream( void )
   verifyStream();\r
 \r
   if ( stream_.state == STREAM_STOPPED ) {\r
-    errorText_ = "RtApiWasapi::stopStream: The stream is already stopped";\r
+    errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";\r
     error( RtAudioError::WARNING );\r
     return;\r
   }\r
@@ -4229,8 +4382,9 @@ void RtApiWasapi::stopStream( void )
   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
     if ( FAILED( hr ) ) {\r
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream";\r
+      errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";\r
       error( RtAudioError::DRIVER_ERROR );\r
+      return;\r
     }\r
   }\r
 \r
@@ -4238,18 +4392,20 @@ void RtApiWasapi::stopStream( void )
   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
     if ( FAILED( hr ) ) {\r
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream";\r
+      errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";\r
       error( RtAudioError::DRIVER_ERROR );\r
+      return;\r
     }\r
   }\r
 \r
   // close thread handle\r
   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
-    errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread";\r
+    errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";\r
     error( RtAudioError::THREAD_ERROR );\r
+    return;\r
   }\r
 \r
-  stream_.callbackInfo.thread = NULL;\r
+  stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
 }\r
 \r
 //-----------------------------------------------------------------------------\r
@@ -4259,7 +4415,7 @@ void RtApiWasapi::abortStream( void )
   verifyStream();\r
 \r
   if ( stream_.state == STREAM_STOPPED ) {\r
-    errorText_ = "RtApiWasapi::abortStream: The stream is already stopped";\r
+    errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";\r
     error( RtAudioError::WARNING );\r
     return;\r
   }\r
@@ -4276,8 +4432,9 @@ void RtApiWasapi::abortStream( void )
   if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {\r
     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();\r
     if ( FAILED( hr ) ) {\r
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream";\r
+      errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";\r
       error( RtAudioError::DRIVER_ERROR );\r
+      return;\r
     }\r
   }\r
 \r
@@ -4285,18 +4442,20 @@ void RtApiWasapi::abortStream( void )
   if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {\r
     HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();\r
     if ( FAILED( hr ) ) {\r
-      errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream";\r
+      errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";\r
       error( RtAudioError::DRIVER_ERROR );\r
+      return;\r
     }\r
   }\r
 \r
   // close thread handle\r
   if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {\r
-    errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread";\r
+    errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";\r
     error( RtAudioError::THREAD_ERROR );\r
+    return;\r
   }\r
 \r
-  stream_.callbackInfo.thread = NULL;\r
+  stream_.callbackInfo.thread = (ThreadHandle) NULL;\r
 }\r
 \r
 //-----------------------------------------------------------------------------\r
@@ -4314,69 +4473,109 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   IMMDeviceCollection* renderDevices = NULL;\r
   IMMDevice* devicePtr = NULL;\r
   WAVEFORMATEX* deviceFormat = NULL;\r
+  unsigned int bufferBytes;\r
+  stream_.state = STREAM_STOPPED;\r
 \r
   // create API Handle if not already created\r
   if ( !stream_.apiHandle )\r
     stream_.apiHandle = ( void* ) new WasapiHandle();\r
 \r
-  // count capture devices\r
+  // Count capture devices\r
+  errorText_.clear();\r
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
   HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = captureDevices->GetCount( &captureDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";\r
+    goto Exit;\r
+  }\r
 \r
-  // count render devices\r
+  // Count render devices\r
   hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device collection" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";\r
+    goto Exit;\r
+  }\r
 \r
   hr = renderDevices->GetCount( &renderDeviceCount );\r
-  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";\r
+    goto Exit;\r
+  }\r
 \r
   // validate device index\r
-  if ( device >= captureDeviceCount + renderDeviceCount )\r
-    EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );\r
+  if ( device >= captureDeviceCount + renderDeviceCount ) {\r
+    errorType = RtAudioError::INVALID_USE;\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";\r
+    goto Exit;\r
+  }\r
 \r
   // determine whether index falls within capture or render devices\r
-  //if ( device < captureDeviceCount ) {\r
   if ( device >= renderDeviceCount ) {\r
-    if ( mode != INPUT )\r
-      EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Capture device selected as output device" );\r
+    if ( mode != INPUT ) {\r
+      errorType = RtAudioError::INVALID_USE;\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";\r
+      goto Exit;\r
+    }\r
 \r
     // retrieve captureAudioClient from devicePtr\r
     IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
 \r
-    //hr = captureDevices->Item( device, &devicePtr );\r
     hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";\r
+      goto Exit;\r
+    }\r
 \r
     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
                               NULL, ( void** ) &captureAudioClient );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
+      goto Exit;\r
+    }\r
 \r
     hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
+      goto Exit;\r
+    }\r
 \r
     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
     captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
   }\r
   else {\r
-    if ( mode != OUTPUT )\r
-      EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Render device selected as input device" );\r
+    if ( mode != OUTPUT ) {\r
+      errorType = RtAudioError::INVALID_USE;\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";\r
+      goto Exit;\r
+    }\r
 \r
     // retrieve renderAudioClient from devicePtr\r
     IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
 \r
-    //hr = renderDevices->Item( device - captureDeviceCount, &devicePtr );\r
     hr = renderDevices->Item( device, &devicePtr );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";\r
+      goto Exit;\r
+    }\r
 \r
     hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,\r
                               NULL, ( void** ) &renderAudioClient );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device audio client" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";\r
+      goto Exit;\r
+    }\r
 \r
     hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";\r
+      goto Exit;\r
+    }\r
 \r
     stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
     renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
@@ -4392,7 +4591,6 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }\r
 \r
   stream_.device[mode] = device;\r
-  stream_.state = STREAM_STOPPED;\r
   stream_.doByteSwap[mode] = false;\r
   stream_.sampleRate = sampleRate;\r
   stream_.bufferSize = *bufferSize;\r
@@ -4410,29 +4608,24 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
   // Set flags for buffer conversion.\r
   stream_.doConvertBuffer[mode] = false;\r
-  if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+  if ( stream_.userFormat != stream_.deviceFormat[mode] ||\r
+       stream_.nUserChannels != stream_.nDeviceChannels )\r
     stream_.doConvertBuffer[mode] = true;\r
-  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
-       stream_.nUserChannels[mode] > 1 )\r
+  else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&\r
+            stream_.nUserChannels[mode] > 1 )\r
     stream_.doConvertBuffer[mode] = true;\r
 \r
   if ( stream_.doConvertBuffer[mode] )\r
     setConvertInfo( mode, 0 );\r
 \r
   // Allocate necessary internal buffers\r
-  unsigned int bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
+  bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );\r
 \r
   stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );\r
-  if ( !stream_.userBuffer[mode] )\r
-    EXIT_ON_ERROR( -1, RtAudioError::MEMORY_ERROR, "Error allocating user buffer memory" );\r
-\r
-  if ( stream_.doConvertBuffer[mode] && !stream_.deviceBuffer ) {\r
-    unsigned int deviceBufferSize = max( stream_.nUserChannels[INPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ),\r
-                                         stream_.nUserChannels[OUTPUT] * stream_.bufferSize * formatBytes( stream_.userFormat ) );\r
-\r
-    stream_.deviceBuffer = ( char* ) calloc( deviceBufferSize, 1 );\r
-    if ( !stream_.deviceBuffer )\r
-      EXIT_ON_ERROR( -1, RtAudioError::MEMORY_ERROR, "Error allocating device buffer memory" );\r
+  if ( !stream_.userBuffer[mode] ) {\r
+    errorType = RtAudioError::MEMORY_ERROR;\r
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";\r
+    goto Exit;\r
   }\r
 \r
   if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
@@ -4447,17 +4640,17 @@ bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
 Exit:\r
   //clean up\r
-\r
   SAFE_RELEASE( captureDevices );\r
   SAFE_RELEASE( renderDevices );\r
   SAFE_RELEASE( devicePtr );\r
-\r
   CoTaskMemFree( deviceFormat );\r
 \r
   // if method failed, close the stream\r
   if ( methodResult == FAILURE )\r
     closeStream();\r
 \r
+  if ( !errorText_.empty() )\r
+    error( errorType );\r
   return methodResult;\r
 }\r
 \r
@@ -4510,8 +4703,28 @@ void RtApiWasapi::wasapiThread()
   WasapiBuffer captureBuffer;\r
   WasapiBuffer renderBuffer;\r
 \r
+  // declare local stream variables\r
+  RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
+  BYTE* streamBuffer = NULL;\r
+  unsigned long captureFlags = 0;\r
+  unsigned int bufferFrameCount = 0;\r
+  unsigned int numFramesPadding = 0;\r
+  unsigned int convBufferSize = 0;\r
+  bool callbackPushed = false;\r
+  bool callbackPulled = false;\r
+  bool callbackStopped = false;\r
+  int callbackResult = 0;\r
+\r
+  // convBuffer is used to store converted buffers between WASAPI and the user\r
+  char* convBuffer = NULL;\r
+  unsigned int convBuffSize = 0;\r
+  unsigned int deviceBuffSize = 0;\r
+\r
+  errorText_.clear();\r
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;\r
+\r
   // Attempt to assign "Pro Audio" characteristic to thread\r
-  HMODULE AvrtDll = LoadLibrary( "AVRT.dll" );\r
+  HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );\r
   if ( AvrtDll ) {\r
     DWORD taskIndex = 0;\r
     TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
@@ -4522,7 +4735,10 @@ void RtApiWasapi::wasapiThread()
   // start capture stream if applicable\r
   if ( captureAudioClient ) {\r
     hr = captureAudioClient->GetMixFormat( &captureFormat );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
+      goto Exit;\r
+    }\r
 \r
     captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
 \r
@@ -4537,19 +4753,31 @@ void RtApiWasapi::wasapiThread()
                                            desiredBufferPeriod,\r
                                            captureFormat,\r
                                            NULL );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize capture audio client" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";\r
+        goto Exit;\r
+      }\r
 \r
       hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),\r
                                            ( void** ) &captureClient );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture client handle" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";\r
+        goto Exit;\r
+      }\r
 \r
       // configure captureEvent to trigger on every available capture buffer\r
       captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
-      if ( !captureEvent )\r
-        EXIT_ON_ERROR( -1, RtAudioError::SYSTEM_ERROR, "Unable to create capture event" );\r
+      if ( !captureEvent ) {\r
+        errorType = RtAudioError::SYSTEM_ERROR;\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";\r
+        goto Exit;\r
+      }\r
 \r
       hr = captureAudioClient->SetEventHandle( captureEvent );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set capture event handle" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";\r
+        goto Exit;\r
+      }\r
 \r
       ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
       ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
@@ -4557,7 +4785,10 @@ void RtApiWasapi::wasapiThread()
 \r
     unsigned int inBufferSize = 0;\r
     hr = captureAudioClient->GetBufferSize( &inBufferSize );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to get capture buffer size" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";\r
+      goto Exit;\r
+    }\r
 \r
     // scale outBufferSize according to stream->user sample rate ratio\r
     unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
@@ -4568,17 +4799,26 @@ void RtApiWasapi::wasapiThread()
 \r
     // reset the capture stream\r
     hr = captureAudioClient->Reset();\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset capture stream" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";\r
+      goto Exit;\r
+    }\r
 \r
     // start the capture stream\r
     hr = captureAudioClient->Start();\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start capture stream" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";\r
+      goto Exit;\r
+    }\r
   }\r
 \r
   // start render stream if applicable\r
   if ( renderAudioClient ) {\r
     hr = renderAudioClient->GetMixFormat( &renderFormat );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";\r
+      goto Exit;\r
+    }\r
 \r
     renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
 \r
@@ -4593,19 +4833,31 @@ void RtApiWasapi::wasapiThread()
                                           desiredBufferPeriod,\r
                                           renderFormat,\r
                                           NULL );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize render audio client" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";\r
+        goto Exit;\r
+      }\r
 \r
       hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),\r
                                           ( void** ) &renderClient );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render client handle" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";\r
+        goto Exit;\r
+      }\r
 \r
       // configure renderEvent to trigger on every available render buffer\r
       renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );\r
-      if ( !renderEvent )\r
-        EXIT_ON_ERROR( -1, RtAudioError::SYSTEM_ERROR, "Unable to create render event" );\r
+      if ( !renderEvent ) {\r
+        errorType = RtAudioError::SYSTEM_ERROR;\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";\r
+        goto Exit;\r
+      }\r
 \r
       hr = renderAudioClient->SetEventHandle( renderEvent );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set render event handle" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";\r
+        goto Exit;\r
+      }\r
 \r
       ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
       ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
@@ -4613,7 +4865,10 @@ void RtApiWasapi::wasapiThread()
 \r
     unsigned int outBufferSize = 0;\r
     hr = renderAudioClient->GetBufferSize( &outBufferSize );\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to get render buffer size" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";\r
+      goto Exit;\r
+    }\r
 \r
     // scale inBufferSize according to user->stream sample rate ratio\r
     unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
@@ -4624,41 +4879,40 @@ void RtApiWasapi::wasapiThread()
 \r
     // reset the render stream\r
     hr = renderAudioClient->Reset();\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset render stream" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";\r
+      goto Exit;\r
+    }\r
 \r
     // start the render stream\r
     hr = renderAudioClient->Start();\r
-    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start render stream" );\r
+    if ( FAILED( hr ) ) {\r
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";\r
+      goto Exit;\r
+    }\r
   }\r
 \r
-  // declare local stream variables\r
-  RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;\r
-\r
-  BYTE* streamBuffer = NULL;\r
-  unsigned long captureFlags = 0;\r
-\r
-  unsigned int bufferFrameCount = 0;\r
-  unsigned int numFramesPadding = 0;\r
-  unsigned int convBufferSize = 0;\r
-\r
-  bool callbackPushed = false;\r
-  bool callbackPulled = false;\r
-  bool callbackStopped = false;\r
-\r
-  int callbackResult = 0;\r
-\r
-  // convBuffer is used to store converted buffers between WASAPI and the user\r
-  char* convBuffer = NULL;\r
-\r
   if ( stream_.mode == INPUT ) {\r
-    convBuffer = ( char* ) malloc( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ) );\r
+    convBuffSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
+    deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
   }\r
   else if ( stream_.mode == OUTPUT ) {\r
-    convBuffer = ( char* ) malloc( ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+    convBuffSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
+    deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
   }\r
   else if ( stream_.mode == DUPLEX ) {\r
-    convBuffer = ( char* ) malloc( max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
-                                        ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) ) );\r
+    convBuffSize = std::max( ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
+                             ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+    deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),\r
+                               stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+  }\r
+\r
+  convBuffer = ( char* ) malloc( convBuffSize );\r
+  stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );\r
+  if ( !convBuffer || !stream_.deviceBuffer ) {\r
+    errorType = RtAudioError::MEMORY_ERROR;\r
+    errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";\r
+    goto Exit;\r
   }\r
 \r
   // stream process loop\r
@@ -4677,11 +4931,10 @@ void RtApiWasapi::wasapiThread()
                                                    stream_.deviceFormat[INPUT] );\r
 \r
         if ( callbackPulled ) {\r
-          // Convert callback buffer to user sample rate and channel count\r
+          // Convert callback buffer to user sample rate\r
           convertBufferWasapi( stream_.deviceBuffer,\r
                                convBuffer,\r
                                stream_.nDeviceChannels[INPUT],\r
-                               stream_.nUserChannels[INPUT],\r
                                captureFormat->nSamplesPerSec,\r
                                stream_.sampleRate,\r
                                ( unsigned int ) ( stream_.bufferSize * captureSrRatio ),\r
@@ -4695,7 +4948,7 @@ void RtApiWasapi::wasapiThread()
                            stream_.convertInfo[INPUT] );\r
           }\r
           else {\r
-            // no conversion, simple copy deviceBuffer to userBuffer\r
+            // no further conversion, simple copy deviceBuffer to userBuffer\r
             memcpy( stream_.userBuffer[INPUT],\r
                     stream_.deviceBuffer,\r
                     stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
@@ -4725,26 +4978,32 @@ void RtApiWasapi::wasapiThread()
         // Handle return value from callback\r
         if ( callbackResult == 1 ) {\r
           // instantiate a thread to stop this thread\r
-          HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, NULL, NULL );\r
-\r
+          HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );\r
           if ( !threadHandle ) {\r
-            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream stop thread" );\r
+            errorType = RtAudioError::THREAD_ERROR;\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";\r
+            goto Exit;\r
           }\r
           else if ( !CloseHandle( threadHandle ) ) {\r
-            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream stop thread handle" );\r
+            errorType = RtAudioError::THREAD_ERROR;\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";\r
+            goto Exit;\r
           }\r
 \r
           callbackStopped = true;\r
         }\r
         else if ( callbackResult == 2 ) {\r
           // instantiate a thread to stop this thread\r
-          HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, NULL, NULL );\r
-\r
+          HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );\r
           if ( !threadHandle ) {\r
-            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream abort thread" );\r
+            errorType = RtAudioError::THREAD_ERROR;\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";\r
+            goto Exit;\r
           }\r
           else if ( !CloseHandle( threadHandle ) ) {\r
-            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream abort thread handle" );\r
+            errorType = RtAudioError::THREAD_ERROR;\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";\r
+            goto Exit;\r
           }\r
 \r
           callbackStopped = true;\r
@@ -4765,35 +5024,27 @@ void RtApiWasapi::wasapiThread()
                        stream_.userBuffer[OUTPUT],\r
                        stream_.convertInfo[OUTPUT] );\r
 \r
-        // Convert callback buffer to stream sample rate and channel count\r
-        convertBufferWasapi( convBuffer,\r
-                             stream_.deviceBuffer,\r
-                             stream_.nUserChannels[OUTPUT],\r
-                             stream_.nDeviceChannels[OUTPUT],\r
-                             stream_.sampleRate,\r
-                             renderFormat->nSamplesPerSec,\r
-                             stream_.bufferSize,\r
-                             convBufferSize,\r
-                             stream_.deviceFormat[OUTPUT] );\r
-      }\r
-      else {\r
-        // Convert callback buffer to stream sample rate and channel count\r
-        convertBufferWasapi( convBuffer,\r
-                             stream_.userBuffer[OUTPUT],\r
-                             stream_.nUserChannels[OUTPUT],\r
-                             stream_.nDeviceChannels[OUTPUT],\r
-                             stream_.sampleRate,\r
-                             renderFormat->nSamplesPerSec,\r
-                             stream_.bufferSize,\r
-                             convBufferSize,\r
-                             stream_.deviceFormat[OUTPUT] );\r
       }\r
 \r
+      // Convert callback buffer to stream sample rate\r
+      convertBufferWasapi( convBuffer,\r
+                           stream_.deviceBuffer,\r
+                           stream_.nDeviceChannels[OUTPUT],\r
+                           stream_.sampleRate,\r
+                           renderFormat->nSamplesPerSec,\r
+                           stream_.bufferSize,\r
+                           convBufferSize,\r
+                           stream_.deviceFormat[OUTPUT] );\r
+\r
       // Push callback buffer into outputBuffer\r
       callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
                                                 convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
                                                 stream_.deviceFormat[OUTPUT] );\r
     }\r
+    else {\r
+      // if there is no render stream, set callbackPushed flag\r
+      callbackPushed = true;\r
+    }\r
 \r
     // Stream Capture\r
     // ==============\r
@@ -4811,30 +5062,42 @@ void RtApiWasapi::wasapiThread()
       hr = captureClient->GetBuffer( &streamBuffer,\r
                                      &bufferFrameCount,\r
                                      &captureFlags, NULL, NULL );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture buffer" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";\r
+        goto Exit;\r
+      }\r
 \r
       if ( bufferFrameCount != 0 ) {\r
         // Push capture buffer into inputBuffer\r
         if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,\r
-                                      bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
-                                      stream_.deviceFormat[INPUT] ) )\r
+                                       bufferFrameCount * stream_.nDeviceChannels[INPUT],\r
+                                       stream_.deviceFormat[INPUT] ) )\r
         {\r
           // Release capture buffer\r
           hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
-          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );\r
+          if ( FAILED( hr ) ) {\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+            goto Exit;\r
+          }\r
         }\r
         else\r
         {\r
           // Inform WASAPI that capture was unsuccessful\r
           hr = captureClient->ReleaseBuffer( 0 );\r
-          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );\r
+          if ( FAILED( hr ) ) {\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+            goto Exit;\r
+          }\r
         }\r
       }\r
       else\r
       {\r
         // Inform WASAPI that capture was unsuccessful\r
         hr = captureClient->ReleaseBuffer( 0 );\r
-        EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );\r
+        if ( FAILED( hr ) ) {\r
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";\r
+          goto Exit;\r
+        }\r
       }\r
     }\r
 \r
@@ -4853,49 +5116,67 @@ void RtApiWasapi::wasapiThread()
 \r
       // Get render buffer from stream\r
       hr = renderAudioClient->GetBufferSize( &bufferFrameCount );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer size" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";\r
+        goto Exit;\r
+      }\r
 \r
       hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
-      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer padding" );\r
+      if ( FAILED( hr ) ) {\r
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";\r
+        goto Exit;\r
+      }\r
 \r
       bufferFrameCount -= numFramesPadding;\r
 \r
       if ( bufferFrameCount != 0 ) {\r
         hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );\r
-        EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer" );\r
+        if ( FAILED( hr ) ) {\r
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";\r
+          goto Exit;\r
+        }\r
 \r
         // Pull next buffer from outputBuffer\r
         // Fill render buffer with next buffer\r
         if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,\r
-                                     bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
-                                     stream_.deviceFormat[OUTPUT] ) )\r
+                                      bufferFrameCount * stream_.nDeviceChannels[OUTPUT],\r
+                                      stream_.deviceFormat[OUTPUT] ) )\r
         {\r
           // Release render buffer\r
           hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
-          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );\r
+          if ( FAILED( hr ) ) {\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+            goto Exit;\r
+          }\r
         }\r
         else\r
         {\r
           // Inform WASAPI that render was unsuccessful\r
           hr = renderClient->ReleaseBuffer( 0, 0 );\r
-          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );\r
+          if ( FAILED( hr ) ) {\r
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+            goto Exit;\r
+          }\r
         }\r
       }\r
       else\r
       {\r
         // Inform WASAPI that render was unsuccessful\r
         hr = renderClient->ReleaseBuffer( 0, 0 );\r
-        EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );\r
+        if ( FAILED( hr ) ) {\r
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";\r
+          goto Exit;\r
+        }\r
       }\r
     }\r
 \r
     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
     if ( callbackPushed ) {\r
       callbackPulled = false;\r
+      // tick stream time\r
+      RtApi::tickStreamTime();\r
     }\r
 \r
-    // tick stream time\r
-    RtApi::tickStreamTime();\r
   }\r
 \r
 Exit:\r
@@ -4903,13 +5184,17 @@ Exit:
   CoTaskMemFree( captureFormat );\r
   CoTaskMemFree( renderFormat );\r
 \r
-  //delete convBuffer;\r
   free ( convBuffer );\r
 \r
   CoUninitialize();\r
 \r
   // update stream state\r
   stream_.state = STREAM_STOPPED;\r
+\r
+  if ( errorText_.empty() )\r
+    return;\r
+  else\r
+    error( errorType );\r
 }\r
 \r
 //******************** End of __WINDOWS_WASAPI__ *********************//\r
@@ -4919,7 +5204,7 @@ Exit:
 #if defined(__WINDOWS_DS__) // Windows DirectSound API\r
 \r
 // Modified by Robin Davies, October 2005\r
-// - Improvements to DirectX pointer chasing. \r
+// - Improvements to DirectX pointer chasing.\r
 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.\r
 // - Auto-call CoInitialize for DSOUND and ASIO platforms.\r
 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007\r
@@ -4959,7 +5244,7 @@ struct DsHandle {
   void *id[2];\r
   void *buffer[2];\r
   bool xrun[2];\r
-  UINT bufferPointer[2];  \r
+  UINT bufferPointer[2];\r
   DWORD dsBufferSize[2];\r
   DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.\r
   HANDLE condition;\r
@@ -5049,13 +5334,11 @@ unsigned int RtApiDs :: getDeviceCount( void )
     error( RtAudioError::WARNING );\r
   }\r
 \r
-  // Clean out any devices that may have disappeared.\r
-  std::vector< int > indices;\r
-  for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
-    if ( dsDevices[i].found == false ) indices.push_back( i );\r
-  unsigned int nErased = 0;\r
-  for ( unsigned int i=0; i<indices.size(); i++ )\r
-    dsDevices.erase( dsDevices.begin()-nErased++ );\r
+  // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).\r
+  for ( unsigned int i=0; i<dsDevices.size(); ) {\r
+    if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );\r
+    else i++;\r
+  }\r
 \r
   return static_cast<unsigned int>(dsDevices.size());\r
 }\r
@@ -5111,8 +5394,12 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
   info.sampleRates.clear();\r
   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
-         SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )\r
+         SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {\r
       info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+\r
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
+        info.preferredSampleRate = SAMPLE_RATES[k];\r
+    }\r
   }\r
 \r
   // Get format information.\r
@@ -5794,6 +6081,8 @@ void RtApiDs :: closeStream()
 void RtApiDs :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
+\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -5805,7 +6094,7 @@ void RtApiDs :: startStream()
   // Increase scheduler frequency on lesser windows (a side-effect of\r
   // increasing timer accuracy).  On greater windows (Win2K or later),\r
   // this is already in effect.\r
-  timeBeginPeriod( 1 ); \r
+  timeBeginPeriod( 1 );\r
 \r
   buffersRolling = false;\r
   duplexPrerollBytes = 0;\r
@@ -5868,6 +6157,8 @@ void RtApiDs :: stopStream()
 \r
     stream_.state = STREAM_STOPPED;\r
 \r
+    MUTEX_LOCK( &stream_.mutex );\r
+\r
     // Stop the buffer and clear memory\r
     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
     result = buffer->Stop();\r
@@ -5908,6 +6199,9 @@ void RtApiDs :: stopStream()
 \r
     stream_.state = STREAM_STOPPED;\r
 \r
+    if ( stream_.mode != DUPLEX )\r
+      MUTEX_LOCK( &stream_.mutex );\r
+\r
     result = buffer->Stop();\r
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";\r
@@ -5941,6 +6235,8 @@ 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( RtAudioError::SYSTEM_ERROR );\r
 }\r
 \r
@@ -6027,6 +6323,12 @@ void RtApiDs :: callbackEvent()
   char *buffer;\r
   long bufferBytes;\r
 \r
+  MUTEX_LOCK( &stream_.mutex );\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    MUTEX_UNLOCK( &stream_.mutex );\r
+    return;\r
+  }\r
+\r
   if ( buffersRolling == false ) {\r
     if ( stream_.mode == DUPLEX ) {\r
       //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );\r
@@ -6054,6 +6356,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6061,6 +6364,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6069,6 +6373,7 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
@@ -6076,6 +6381,7 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
@@ -6097,6 +6403,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6108,7 +6415,7 @@ void RtApiDs :: callbackEvent()
   }\r
 \r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
-    \r
+\r
     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];\r
 \r
     if ( handle->drainCounter > 1 ) { // write zeros to the output stream\r
@@ -6148,6 +6455,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6174,7 +6482,7 @@ void RtApiDs :: callbackEvent()
     }\r
 \r
     if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )\r
-         || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { \r
+         || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) {\r
       // We've strayed into the forbidden zone ... resync the read pointer.\r
       handle->xrun[0] = true;\r
       nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;\r
@@ -6189,6 +6497,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6202,16 +6511,18 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
     nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;\r
     handle->bufferPointer[0] = nextWritePointer;\r
+  }\r
 \r
-    if ( handle->drainCounter ) {\r
-      handle->drainCounter++;\r
-      goto unlock;\r
-    }\r
+  // Don't bother draining input\r
+  if ( handle->drainCounter ) {\r
+    handle->drainCounter++;\r
+    goto unlock;\r
   }\r
 \r
   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {\r
@@ -6237,6 +6548,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6244,14 +6556,14 @@ void RtApiDs :: callbackEvent()
     if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
     DWORD endRead = nextReadPointer + bufferBytes;\r
 \r
-    // Handling depends on whether we are INPUT or DUPLEX. \r
+    // Handling depends on whether we are INPUT or DUPLEX.\r
     // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,\r
     // then a wait here will drag the write pointers into the forbidden zone.\r
-    // \r
-    // In DUPLEX mode, rather than wait, we will back off the read pointer until \r
-    // it's in a safe position. This causes dropouts, but it seems to be the only \r
-    // practical way to sync up the read and write pointers reliably, given the \r
-    // the very complex relationship between phase and increment of the read and write \r
+    //\r
+    // In DUPLEX mode, rather than wait, we will back off the read pointer until\r
+    // it's in a safe position. This causes dropouts, but it seems to be the only\r
+    // practical way to sync up the read and write pointers reliably, given the\r
+    // the very complex relationship between phase and increment of the read and write\r
     // pointers.\r
     //\r
     // In order to minimize audible dropouts in DUPLEX mode, we will\r
@@ -6298,10 +6610,11 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
-      \r
+\r
         if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset\r
       }\r
     }\r
@@ -6312,6 +6625,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6333,6 +6647,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6350,6 +6665,7 @@ void RtApiDs :: callbackEvent()
   }\r
 \r
  unlock:\r
+  MUTEX_UNLOCK( &stream_.mutex );\r
   RtApi::tickStreamTime();\r
 }\r
 \r
@@ -6370,21 +6686,6 @@ static unsigned __stdcall callbackHandler( void *ptr )
   return 0;\r
 }\r
 \r
-#include "tchar.h"\r
-\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-1, '\0' );\r
-  WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);\r
-#else\r
-  std::string s( name );\r
-#endif\r
-\r
-  return s;\r
-}\r
-\r
 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
                                           LPCTSTR description,\r
                                           LPCTSTR /*module*/,\r
@@ -6426,7 +6727,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
   }\r
 \r
   // If good device, then save its name and guid.\r
-  std::string name = convertTChar( description );\r
+  std::string name = convertCharPointerToStdString( description );\r
   //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )\r
   if ( lpguid == NULL )\r
     name = "Default Device";\r
@@ -6608,6 +6909,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
 \r
   // Count cards and devices\r
   card = -1;\r
+  subdevice = -1;\r
   snd_card_next( &card );\r
   while ( card >= 0 ) {\r
     sprintf( name, "hw:%d", card );\r
@@ -6821,8 +7123,12 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   // Test our discrete set of sample rate values.\r
   info.sampleRates.clear();\r
   for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {\r
-    if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )\r
+    if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {\r
       info.sampleRates.push_back( SAMPLE_RATES[i] );\r
+\r
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )\r
+        info.preferredSampleRate = SAMPLE_RATES[i];\r
+    }\r
   }\r
   if ( info.sampleRates.size() == 0 ) {\r
     snd_pcm_close( phandle );\r
@@ -7454,6 +7760,7 @@ void RtApiAlsa :: startStream()
   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
 \r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -7518,7 +7825,7 @@ void RtApiAlsa :: stopStream()
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
   snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
-    if ( apiInfo->synchronized ) \r
+    if ( apiInfo->synchronized )\r
       result = snd_pcm_drop( handle[0] );\r
     else\r
       result = snd_pcm_drain( handle[0] );\r
@@ -7747,6 +8054,8 @@ void RtApiAlsa :: callbackEvent()
             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
             errorText_ = errorStream_.str();\r
           }\r
+          else\r
+            errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";\r
         }\r
         else {\r
           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
@@ -7780,7 +8089,7 @@ static void *alsaCallbackHandler( void *ptr )
   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
+  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
@@ -7805,6 +8114,7 @@ static void *alsaCallbackHandler( void *ptr )
 \r
 #include <pulse/error.h>\r
 #include <pulse/simple.h>\r
+#include <pulse/pulseaudio.h>\r
 #include <cstdio>\r
 \r
 static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,\r
@@ -7841,8 +8151,33 @@ unsigned int RtApiPulse::getDeviceCount( void )
   return 1;\r
 }\r
 \r
+void RtApiPulse::sinkInfoCallback(pa_context*, const pa_sink_info* info, int, void* arg)\r
+{\r
+  RtApiPulse* api = (RtApiPulse *) arg;\r
+  if (info) {\r
+    api->channels_ = info->sample_spec.channels;\r
+  }\r
+  pa_threaded_mainloop_signal(api->mainloop_, 0);\r
+}\r
+\r
+void RtApiPulse::contextStateCallback(pa_context* c, void* arg)\r
+{\r
+  pa_threaded_mainloop* mainloop = (pa_threaded_mainloop*) arg;\r
+\r
+  switch (pa_context_get_state(c)) {\r
+  case PA_CONTEXT_READY:\r
+  case PA_CONTEXT_TERMINATED:\r
+  case PA_CONTEXT_FAILED:\r
+    pa_threaded_mainloop_signal(mainloop, 0);\r
+    break;\r
+  default:\r
+    break;\r
+  }\r
+}\r
+\r
 RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
 {\r
+  /* Set up some defaults in case we crash and burn */\r
   RtAudio::DeviceInfo info;\r
   info.probed = true;\r
   info.name = "PulseAudio";\r
@@ -7855,8 +8190,75 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
   for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )\r
     info.sampleRates.push_back( *sr );\r
 \r
+  info.preferredSampleRate = 48000;\r
   info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;\r
 \r
+  /* Get the number of output channels from pulseaudio.  A simple task, you say?\r
+     "What is your mainloop?" */\r
+  mainloop_ = pa_threaded_mainloop_new();\r
+  if (!mainloop_) {\r
+    return info;\r
+  }\r
+\r
+  pa_threaded_mainloop_start(mainloop_);\r
+  pa_threaded_mainloop_lock(mainloop_);\r
+\r
+  /* "And what is your context?" */\r
+  pa_context* context = pa_context_new(pa_threaded_mainloop_get_api(mainloop_), "RtAudio");\r
+  if (!context) {\r
+    pa_threaded_mainloop_unlock(mainloop_);\r
+    pa_threaded_mainloop_stop(mainloop_);\r
+    pa_threaded_mainloop_free(mainloop_);\r
+    mainloop_ = 0;\r
+    return info;\r
+  }\r
+\r
+  pa_context_set_state_callback(context, contextStateCallback, mainloop_);\r
+\r
+  pa_context_connect(context, 0, (pa_context_flags_t) 0, 0);\r
+\r
+  /* "And what is your favourite colour?" */\r
+  int connected = 0;\r
+  pa_context_state_t state = pa_context_get_state(context);\r
+  for (; !connected; state = pa_context_get_state(context)) {\r
+    switch (state) {\r
+    case PA_CONTEXT_READY:\r
+      connected = 1;\r
+      continue;\r
+    case PA_CONTEXT_FAILED:\r
+    case PA_CONTEXT_TERMINATED:\r
+      /* Blue! No, I mean red! */\r
+      pa_threaded_mainloop_unlock(mainloop_);\r
+      pa_context_disconnect(context);\r
+      pa_context_unref(context);\r
+      pa_threaded_mainloop_stop(mainloop_);\r
+      pa_threaded_mainloop_free(mainloop_);\r
+      mainloop_ = 0;\r
+      return info;\r
+    default:\r
+      pa_threaded_mainloop_wait(mainloop_);\r
+      break;\r
+    }\r
+  }\r
+\r
+  pa_operation* op = pa_context_get_sink_info_by_index(context, 0, sinkInfoCallback, this);\r
+\r
+  if (op) {\r
+    pa_operation_unref(op);\r
+  }\r
+\r
+  pa_threaded_mainloop_wait(mainloop_);\r
+  pa_threaded_mainloop_unlock(mainloop_);\r
+\r
+  pa_context_disconnect(context);\r
+  pa_context_unref(context);\r
+\r
+  pa_threaded_mainloop_stop(mainloop_);\r
+  pa_threaded_mainloop_free(mainloop_);\r
+  mainloop_ = 0;\r
+\r
+  info.outputChannels = channels_;\r
+\r
   return info;\r
 }\r
 \r
@@ -7983,7 +8385,7 @@ void RtApiPulse::callbackEvent( void )
     else\r
       bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *\r
         formatBytes( stream_.userFormat );\r
-            \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
@@ -8001,12 +8403,22 @@ void RtApiPulse::callbackEvent( void )
   MUTEX_UNLOCK( &stream_.mutex );\r
   RtApi::tickStreamTime();\r
 \r
+  if (pah->s_play) {\r
+    int e = 0;\r
+    pa_usec_t const lat = pa_simple_get_latency(pah->s_play, &e);\r
+    if (e == 0) {\r
+      stream_.latency[0] = lat * stream_.sampleRate / 1000000;\r
+    }\r
+  }\r
+\r
   if ( doStopStream == 1 )\r
     stopStream();\r
 }\r
 \r
 void RtApiPulse::startStream( void )\r
 {\r
+  RtApi::startStream();\r
+\r
   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
 \r
   if ( stream_.state == STREAM_CLOSED ) {\r
@@ -8045,6 +8457,7 @@ void RtApiPulse::stopStream( void )
   }\r
 \r
   stream_.state = STREAM_STOPPED;\r
+  pah->runnable = false;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
   if ( pah && pah->s_play ) {\r
@@ -8079,6 +8492,7 @@ void RtApiPulse::abortStream( void )
   }\r
 \r
   stream_.state = STREAM_STOPPED;\r
+  pah->runnable = false;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
   if ( pah && pah->s_play ) {\r
@@ -8108,10 +8522,6 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
 \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
@@ -8217,13 +8627,12 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
 \r
   int error;\r
-  if ( !options->streamName.empty() ) streamName = options->streamName;\r
+  if ( options && !options->streamName.empty() ) streamName = options->streamName;\r
   switch ( mode ) {\r
   case INPUT:\r
     pa_buffer_attr buffer_attr;\r
     buffer_attr.fragsize = bufferBytes;\r
     buffer_attr.maxlength = -1;\r
-\r
     pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );\r
     if ( !pah->s_rec ) {\r
       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";\r
@@ -8231,7 +8640,38 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     }\r
     break;\r
   case OUTPUT:\r
-    pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
+    /* XXX: hard-coded for DCP-o-matic */\r
+    pa_channel_map map;\r
+    pa_channel_map_init(&map);\r
+    /* XXX: need to check 7.1 */\r
+    map.channels = channels;\r
+\r
+    if (channels > 0) {\r
+      map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;\r
+    }\r
+    if (channels > 1) {\r
+      map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;\r
+    }\r
+    if (channels > 2) {\r
+      map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;\r
+    }\r
+    if (channels > 3) {\r
+      map.map[3] = PA_CHANNEL_POSITION_LFE;\r
+    }\r
+    if (channels > 4) {\r
+      map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;\r
+    }\r
+    if (channels > 5) {\r
+      map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;\r
+    }\r
+    if (channels > 6) {\r
+      map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;\r
+    }\r
+    if (channels > 7) {\r
+      map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;\r
+    }\r
+\r
+    pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, &map, NULL, &error );\r
     if ( !pah->s_play ) {\r
       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
       goto error;\r
@@ -8259,7 +8699,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
 \r
   stream_.state = STREAM_STOPPED;\r
   return true;\r
\r
+\r
  error:\r
   if ( pah && stream_.callbackInfo.isRunning ) {\r
     pthread_cond_destroy( &pah->runnable_cv );\r
@@ -8423,6 +8863,10 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
       for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
         if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {\r
           info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+\r
+          if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
+            info.preferredSampleRate = SAMPLE_RATES[k];\r
+\r
           break;\r
         }\r
       }\r
@@ -8431,8 +8875,12 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
   else {\r
     // Check min and max rate values;\r
     for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
-      if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )\r
+      if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {\r
         info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+\r
+        if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )\r
+          info.preferredSampleRate = SAMPLE_RATES[k];\r
+      }\r
     }\r
   }\r
 \r
@@ -8935,6 +9383,7 @@ void RtApiOss :: closeStream()
 void RtApiOss :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -9843,8 +10292,8 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
 \r
 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
 {\r
-  register char val;\r
-  register char *ptr;\r
+  char val;\r
+  char *ptr;\r
 \r
   ptr = buffer;\r
   if ( format == RTAUDIO_SINT16 ) {\r
@@ -9926,4 +10375,3 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat
   // End:\r
   //\r
   // vim: et sts=2 sw=2\r
-\r