Remove OSS configuration from pyrtaudio.
[rtaudio-cdist.git] / RtAudio.cpp
index 9f1201670c8f1aa55bdcb84946d9e36479f3678a..f4bff00b0ae9d187d54f7e055d02ba466e88b48b 100644 (file)
@@ -1,4 +1,4 @@
-/************************************************************************/\r
+/************************************************************************/\r
 /*! \class RtAudio\r
     \brief Realtime audio i/o C++ classes.\r
 \r
@@ -10,7 +10,7 @@
     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
 \r
     RtAudio: realtime audio i/o C++ classes\r
-    Copyright (c) 2001-2014 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.1.1pre\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
 \r
 // Static variable definitions.\r
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
@@ -58,6 +59,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 +196,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
@@ -404,6 +421,14 @@ double RtApi :: getStreamTime( void )
 #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
@@ -753,9 +778,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
@@ -764,8 +794,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
@@ -1372,6 +1406,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
@@ -1383,6 +1429,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
@@ -1976,7 +2034,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
@@ -2756,8 +2816,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
@@ -2816,9 +2880,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
@@ -2832,7 +2899,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
@@ -2853,22 +2920,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
@@ -2877,30 +2948,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
@@ -2911,10 +2979,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
@@ -2943,10 +3010,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
@@ -2955,49 +3021,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
@@ -3009,16 +3089,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
@@ -3033,15 +3110,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
@@ -3062,18 +3138,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
+  buffersAllocated = true;  \r
+  stream_.state = STREAM_STOPPED;\r
 \r
   // Set flags for buffer conversion.\r
   stream_.doConvertBuffer[mode] = false;\r
@@ -3096,11 +3191,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
@@ -3114,18 +3207,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
@@ -3145,32 +3228,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
@@ -3622,12 +3711,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
@@ -3770,8 +3859,7 @@ private:
 // 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.\r
+// This sample rate converter works best with conversions between one rate and its multiple.\r
 void convertBufferWasapi( char* outBuffer,\r
                           const char* inBuffer,\r
                           const unsigned int& channelCount,\r
@@ -3783,40 +3871,129 @@ void convertBufferWasapi( char* outBuffer,
 {\r
   // calculate the new outSampleCount and relative sampleStep\r
   float sampleRatio = ( float ) outSampleRate / inSampleRate;\r
+  float sampleRatioInv = ( float ) 1 / sampleRatio;\r
   float sampleStep = 1.0f / sampleRatio;\r
   float inSampleFraction = 0.0f;\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
+  // if inSampleRate is a multiple of outSampleRate (or vice versa) there's no need to interpolate\r
+  if ( floor( sampleRatio ) == sampleRatio || floor( sampleRatioInv ) == sampleRatioInv )\r
   {\r
-    unsigned int inSample = ( unsigned int ) inSampleFraction;\r
-\r
-    switch ( format )\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
     {\r
-      case RTAUDIO_SINT8:\r
-        memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
-        break;\r
-      case RTAUDIO_SINT16:\r
-        memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
-        break;\r
-      case RTAUDIO_SINT24:\r
-        memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
-        break;\r
-      case RTAUDIO_SINT32:\r
-        memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
-        break;\r
-      case RTAUDIO_FLOAT32:\r
-        memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
-        break;\r
-      case RTAUDIO_FLOAT64:\r
-        memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
-        break;\r
+      unsigned int inSample = ( unsigned int ) inSampleFraction;\r
+\r
+      switch ( format )\r
+      {\r
+        case RTAUDIO_SINT8:\r
+          memcpy( &( ( char* ) outBuffer )[ outSample * channelCount ], &( ( char* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( char ) );\r
+          break;\r
+        case RTAUDIO_SINT16:\r
+          memcpy( &( ( short* ) outBuffer )[ outSample * channelCount ], &( ( short* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( short ) );\r
+          break;\r
+        case RTAUDIO_SINT24:\r
+          memcpy( &( ( S24* ) outBuffer )[ outSample * channelCount ], &( ( S24* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( S24 ) );\r
+          break;\r
+        case RTAUDIO_SINT32:\r
+          memcpy( &( ( int* ) outBuffer )[ outSample * channelCount ], &( ( int* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( int ) );\r
+          break;\r
+        case RTAUDIO_FLOAT32:\r
+          memcpy( &( ( float* ) outBuffer )[ outSample * channelCount ], &( ( float* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( float ) );\r
+          break;\r
+        case RTAUDIO_FLOAT64:\r
+          memcpy( &( ( double* ) outBuffer )[ outSample * channelCount ], &( ( double* ) inBuffer )[ inSample * channelCount ], channelCount * sizeof( double ) );\r
+          break;\r
+      }\r
+\r
+      // jump to next in sample\r
+      inSampleFraction += sampleStep;\r
     }\r
+  }\r
+  else // else interpolate\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
+    {\r
+      unsigned int inSample = ( unsigned int ) inSampleFraction;\r
+      float inSampleDec = inSampleFraction - inSample;\r
+      unsigned int frameInSample = inSample * channelCount;\r
+      unsigned int frameOutSample = outSample * channelCount;\r
 \r
-    // jump to next in sample\r
-    inSampleFraction += sampleStep;\r
+      switch ( format )\r
+      {\r
+        case RTAUDIO_SINT8:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            char fromSample = ( ( char* ) inBuffer )[ frameInSample + channel ];\r
+            char toSample = ( ( char* ) inBuffer )[ frameInSample + channelCount + channel ];\r
+            char sampleDiff = ( char ) ( ( toSample - fromSample ) * inSampleDec );\r
+            ( ( char* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+        case RTAUDIO_SINT16:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            short fromSample = ( ( short* ) inBuffer )[ frameInSample + channel ];\r
+            short toSample = ( ( short* ) inBuffer )[ frameInSample + channelCount + channel ];\r
+            short sampleDiff = ( short ) ( ( toSample - fromSample ) * inSampleDec );\r
+            ( ( short* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+        case RTAUDIO_SINT24:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            int fromSample = ( ( S24* ) inBuffer )[ frameInSample + channel ].asInt();\r
+            int toSample = ( ( S24* ) inBuffer )[ frameInSample + channelCount + channel ].asInt();\r
+            int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );\r
+            ( ( S24* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+        case RTAUDIO_SINT32:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            int fromSample = ( ( int* ) inBuffer )[ frameInSample + channel ];\r
+            int toSample = ( ( int* ) inBuffer )[ frameInSample + channelCount + channel ];\r
+            int sampleDiff = ( int ) ( ( toSample - fromSample ) * inSampleDec );\r
+            ( ( int* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+        case RTAUDIO_FLOAT32:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            float fromSample = ( ( float* ) inBuffer )[ frameInSample + channel ];\r
+            float toSample = ( ( float* ) inBuffer )[ frameInSample + channelCount + channel ];\r
+            float sampleDiff = ( toSample - fromSample ) * inSampleDec;\r
+            ( ( float* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+        case RTAUDIO_FLOAT64:\r
+        {\r
+          for ( unsigned int channel = 0; channel < channelCount; channel++ )\r
+          {\r
+            double fromSample = ( ( double* ) inBuffer )[ frameInSample + channel ];\r
+            double toSample = ( ( double* ) inBuffer )[ frameInSample + channelCount + channel ];\r
+            double sampleDiff = ( toSample - fromSample ) * inSampleDec;\r
+            ( ( double* ) outBuffer )[ frameOutSample + channel ] = fromSample + sampleDiff;\r
+          }\r
+          break;\r
+        }\r
+      }\r
+\r
+      // jump to next in sample\r
+      inSampleFraction += sampleStep;\r
+    }\r
   }\r
 }\r
 \r
@@ -3932,7 +4109,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
@@ -4035,8 +4211,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
     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
@@ -4053,8 +4228,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
     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
@@ -4097,6 +4271,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
   for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {\r
     info.sampleRates.push_back( SAMPLE_RATES[i] );\r
   }\r
+  info.preferredSampleRate = deviceFormat->nSamplesPerSec;\r
 \r
   // native format\r
   info.nativeFormats = 0;\r
@@ -5071,10 +5246,10 @@ void RtApiWasapi::wasapiThread()
     // 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
@@ -5232,14 +5407,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()+indices[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
@@ -5295,8 +5467,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
@@ -6251,6 +6427,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
@@ -6258,6 +6435,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
@@ -6266,6 +6444,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
@@ -6273,6 +6452,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
@@ -6294,6 +6474,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
@@ -6345,6 +6526,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
@@ -6386,6 +6568,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
@@ -6399,6 +6582,7 @@ 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
@@ -6435,6 +6619,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
@@ -6496,6 +6681,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
@@ -6510,6 +6696,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
@@ -6531,6 +6718,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
@@ -6569,21 +6757,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
@@ -6625,7 +6798,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
@@ -6807,6 +6980,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
@@ -7020,8 +7194,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
@@ -7946,6 +8124,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
@@ -7979,7 +8159,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
@@ -8054,6 +8234,7 @@ 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
   return info;\r
@@ -8416,7 +8597,7 @@ 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
@@ -8430,7 +8611,7 @@ 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
+    pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
     if ( !pah->s_play ) {\r
       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
       goto error;\r
@@ -8622,6 +8803,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
@@ -8630,8 +8815,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
@@ -10042,8 +10231,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