Update to CMake file for WASAPI; documentation update for CMake; finalized date for...
[rtaudio-cdist.git] / RtAudio.cpp
index 1562e858629f6f92e0deea89bcaf6d180aa932d8..eb3eadc411236243eded55af91ee744ff31b8048 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-2014 Gary P. Scavone\r
 \r
     Permission is hereby granted, free of charge, to any person\r
     obtaining a copy of this software and associated documentation files\r
@@ -38,7 +38,7 @@
 */\r
 /************************************************************************/\r
 \r
-// RtAudio: Version 4.0.12\r
+// RtAudio: Version 4.1.0\r
 \r
 #include "RtAudio.h"\r
 #include <iostream>\r
@@ -53,7 +53,7 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
   32000, 44100, 48000, 88200, 96000, 176400, 192000\r
 };\r
 \r
-#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)\r
+#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)\r
   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)\r
   #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)\r
   #define MUTEX_LOCK(A)       EnterCriticalSection(A)\r
@@ -75,6 +75,11 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
 //\r
 // *************************************************** //\r
 \r
+std::string RtAudio :: getVersion( void ) throw()\r
+{\r
+  return RTAUDIO_VERSION;\r
+}\r
+\r
 void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()\r
 {\r
   apis.clear();\r
@@ -96,6 +101,9 @@ void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
 #if defined(__WINDOWS_ASIO__)\r
   apis.push_back( WINDOWS_ASIO );\r
 #endif\r
+#if defined(__WINDOWS_WASAPI__)\r
+  apis.push_back( WINDOWS_WASAPI );\r
+#endif\r
 #if defined(__WINDOWS_DS__)\r
   apis.push_back( WINDOWS_DS );\r
 #endif\r
@@ -133,6 +141,10 @@ void RtAudio :: openRtApi( RtAudio::Api api )
   if ( api == WINDOWS_ASIO )\r
     rtapi_ = new RtApiAsio();\r
 #endif\r
+#if defined(__WINDOWS_WASAPI__)\r
+  if ( api == WINDOWS_WASAPI )\r
+    rtapi_ = new RtApiWasapi();\r
+#endif\r
 #if defined(__WINDOWS_DS__)\r
   if ( api == WINDOWS_DS )\r
     rtapi_ = new RtApiDs();\r
@@ -147,7 +159,7 @@ void RtAudio :: openRtApi( RtAudio::Api api )
 #endif\r
 }\r
 \r
-RtAudio :: RtAudio( RtAudio::Api api ) throw()\r
+RtAudio :: RtAudio( RtAudio::Api api )\r
 {\r
   rtapi_ = 0;\r
 \r
@@ -175,13 +187,15 @@ RtAudio :: RtAudio( RtAudio::Api api ) throw()
   // It should not be possible to get here because the preprocessor\r
   // definition __RTAUDIO_DUMMY__ is automatically defined if no\r
   // API-specific definitions are passed to the compiler. But just in\r
-  // case something weird happens, we'll print out an error message.\r
-  std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
+  // case something weird happens, we'll thow an error.\r
+  std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";\r
+  throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );\r
 }\r
 \r
 RtAudio :: ~RtAudio() throw()\r
 {\r
-  delete rtapi_;\r
+  if ( rtapi_ )\r
+    delete rtapi_;\r
 }\r
 \r
 void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,\r
@@ -213,7 +227,7 @@ RtApi :: RtApi()
   stream_.userBuffer[1] = 0;\r
   MUTEX_INITIALIZE( &stream_.mutex );\r
   showWarnings_ = true;\r
-  firstErrorOccurred = false;\r
+  firstErrorOccurred_ = false;\r
 }\r
 \r
 RtApi :: ~RtApi()\r
@@ -235,6 +249,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
@@ -280,7 +297,6 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams,
     }\r
   }\r
 \r
-  clearStreamInfo();\r
   bool result;\r
 \r
   if ( oChannels > 0 ) {\r
@@ -596,7 +612,11 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
   int length = CFStringGetLength(cfname);\r
   char *mname = (char *)malloc(length * 3 + 1);\r
+#if defined( UNICODE ) || defined( _UNICODE )\r
+  CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);\r
+#else\r
   CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());\r
+#endif\r
   info.name.append( (const char *)mname, strlen(mname) );\r
   info.name.append( ": " );\r
   CFRelease( cfname );\r
@@ -614,7 +634,11 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );\r
   length = CFStringGetLength(cfname);\r
   char *name = (char *)malloc(length * 3 + 1);\r
+#if defined( UNICODE ) || defined( _UNICODE )\r
+  CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);\r
+#else\r
   CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());\r
+#endif\r
   info.name.append( (const char *)name, strlen(name) );\r
   CFRelease( cfname );\r
   free(name);\r
@@ -718,18 +742,37 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
     return info;\r
   }\r
 \r
-  Float64 minimumRate = 100000000.0, maximumRate = 0.0;\r
+  // The sample rate reporting mechanism is a bit of a mystery.  It\r
+  // seems that it can either return individual rates or a range of\r
+  // rates.  I assume that if the min / max range values are the same,\r
+  // then that represents a single supported rate and if the min / max\r
+  // range values are different, the device supports an arbitrary\r
+  // range of values (though there might be multiple ranges, so we'll\r
+  // use the most conservative range).\r
+  Float64 minimumRate = 1.0, maximumRate = 10000000000.0;\r
+  bool haveValueRange = false;\r
+  info.sampleRates.clear();\r
   for ( UInt32 i=0; i<nRanges; i++ ) {\r
-    if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;\r
-    if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;\r
+    if ( rangeList[i].mMinimum == rangeList[i].mMaximum )\r
+      info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );\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
+    }\r
   }\r
 \r
-  info.sampleRates.clear();\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
-      info.sampleRates.push_back( SAMPLE_RATES[k] );\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
+        info.sampleRates.push_back( SAMPLE_RATES[k] );\r
+    }\r
   }\r
 \r
+  // Sort and remove any redundant values\r
+  std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
+  info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );\r
+\r
   if ( info.sampleRates.size() == 0 ) {\r
     errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";\r
     errorText_ = errorStream_.str();\r
@@ -791,7 +834,6 @@ static OSStatus rateListener( AudioObjectID inDevice,
                               const AudioObjectPropertyAddress /*properties*/[],\r
                               void* ratePointer )\r
 {\r
-\r
   Float64 *rate = (Float64 *) ratePointer;\r
   UInt32 dataSize = sizeof( Float64 );\r
   AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
@@ -863,6 +905,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );\r
   if (result != noErr || dataSize == 0) {\r
+    free( bufferList );\r
     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";\r
     errorText_ = errorStream_.str();\r
     return FAILURE;\r
@@ -1003,7 +1046,6 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   dataSize = sizeof( Float64 );\r
   property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
   result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
-\r
   if ( result != noErr ) {\r
     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
     errorText_ = errorStream_.str();\r
@@ -1025,8 +1067,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
     nominalRate = (Float64) sampleRate;\r
     result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
-\r
     if ( result != noErr ) {\r
+      AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
       errorText_ = errorStream_.str();\r
       return FAILURE;\r
@@ -1292,6 +1334,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
   // Setup the device property listener for over/underload.\r
   property.mSelector = kAudioDeviceProcessorOverload;\r
+  property.mScope = kAudioObjectPropertyScopeGlobal;\r
   result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
 \r
   return SUCCESS;\r
@@ -2751,7 +2794,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
   return info;\r
 }\r
 \r
-static void bufferSwitch( long index, ASIOBool processNow )\r
+static void bufferSwitch( long index, ASIOBool /*processNow*/ )\r
 {\r
   RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;\r
   object->callbackEvent( index );\r
@@ -3434,7 +3477,7 @@ static void sampleRateChanged( ASIOSampleRate sRate )
   std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;\r
 }\r
 \r
-static long asioMessages( long selector, long value, void* message, double* opt )\r
+static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )\r
 {\r
   long ret = 0;\r
 \r
@@ -3528,202 +3571,1535 @@ static const char* getAsioErrorString( ASIOError result )
 \r
   return "Unknown error.";\r
 }\r
+\r
 //******************** End of __WINDOWS_ASIO__ *********************//\r
 #endif\r
 \r
 \r
-#if defined(__WINDOWS_DS__) // Windows DirectSound API\r
+#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API\r
 \r
-// Modified by Robin Davies, October 2005\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
-// Changed device query structure for RtAudio 4.0.7, January 2010\r
+#include <audioclient.h>\r
+#include <avrt.h>\r
+#include <functiondiscoverykeys.h>\r
+#include <mmdeviceapi.h>\r
 \r
-#include <dsound.h>\r
-#include <assert.h>\r
-#include <algorithm>\r
+//=============================================================================\r
 \r
-#if defined(__MINGW32__)\r
-  // missing from latest mingw winapi\r
-#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
-#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
-#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
-#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
-#endif\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 MINIMUM_DEVICE_BUFFER_SIZE 32768\r
+#define SAFE_RELEASE( objectPtr )\\r
+if ( objectPtr )\\r
+{\\r
+  objectPtr->Release();\\r
+  objectPtr = NULL;\\r
+}\r
 \r
-#ifdef _MSC_VER // if Microsoft Visual C++\r
-#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
-#endif\r
+typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );\r
 \r
-static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
+//-----------------------------------------------------------------------------\r
+\r
+// WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.\r
+// Therefore we must perform all necessary conversions to user buffers in order to satisfy these\r
+// requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to\r
+// provide intermediate storage for read / write synchronization.\r
+class WasapiBuffer\r
 {\r
-  if ( pointer > bufferSize ) pointer -= bufferSize;\r
-  if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
-  if ( pointer < earlierPointer ) pointer += bufferSize;\r
-  return pointer >= earlierPointer && pointer < laterPointer;\r
-}\r
+public:\r
+  WasapiBuffer()\r
+    : buffer_( NULL ),\r
+      bufferSize_( 0 ),\r
+      inIndex_( 0 ),\r
+      outIndex_( 0 ) {}\r
 \r
-// A structure to hold various information related to the DirectSound\r
-// API implementation.\r
-struct DsHandle {\r
-  unsigned int drainCounter; // Tracks callback counts when draining\r
-  bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
-  void *id[2];\r
-  void *buffer[2];\r
-  bool xrun[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
+  ~WasapiBuffer() {\r
+    delete buffer_;\r
+  }\r
 \r
-  DsHandle()\r
-    :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }\r
-};\r
+  // sets the length of the internal ring buffer\r
+  void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {\r
+    delete buffer_;\r
 \r
-// Declarations for utility functions, callbacks, and structures\r
-// specific to the DirectSound implementation.\r
-static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
-                                          LPCTSTR description,\r
-                                          LPCTSTR module,\r
-                                          LPVOID lpContext );\r
+    buffer_ = ( char* ) calloc( bufferSize, formatBytes );\r
 \r
-static const char* getErrorString( int code );\r
+    bufferSize_ = bufferSize;\r
+    inIndex_ = 0;\r
+    outIndex_ = 0;\r
+  }\r
 \r
-static unsigned __stdcall callbackHandler( void *ptr );\r
+  // attempt to push a buffer into the ring buffer at the current "in" index\r
+  bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
+  {\r
+    if ( !buffer ||                 // incoming buffer is NULL\r
+         bufferSize == 0 ||         // incoming buffer has no data\r
+         bufferSize > bufferSize_ ) // incoming buffer too large\r
+    {\r
+      return false;\r
+    }\r
 \r
-struct DsDevice {\r
-  LPGUID id[2];\r
-  bool validId[2];\r
-  bool found;\r
-  std::string name;\r
+    unsigned int relOutIndex = outIndex_;\r
+    unsigned int inIndexEnd = inIndex_ + bufferSize;\r
+    if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {\r
+      relOutIndex += bufferSize_;\r
+    }\r
 \r
-  DsDevice()\r
-  : found(false) { validId[0] = false; validId[1] = false; }\r
-};\r
+    // "in" index can end on the "out" index but cannot begin at it\r
+    if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {\r
+      return false; // not enough space between "in" index and "out" index\r
+    }\r
 \r
-struct DsProbeData {\r
-  bool isInput;\r
-  std::vector<struct DsDevice>* dsDevices;\r
+    // copy buffer from external to internal\r
+    int fromZeroSize = inIndex_ + bufferSize - bufferSize_;\r
+    fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
+    int fromInSize = bufferSize - fromZeroSize;\r
+\r
+    switch( format )\r
+      {\r
+      case RTAUDIO_SINT8:\r
+        memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );\r
+        memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );\r
+        break;\r
+      case RTAUDIO_SINT16:\r
+        memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );\r
+        memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );\r
+        break;\r
+      case RTAUDIO_SINT24:\r
+        memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );\r
+        memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );\r
+        break;\r
+      case RTAUDIO_SINT32:\r
+        memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );\r
+        memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );\r
+        break;\r
+      case RTAUDIO_FLOAT32:\r
+        memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );\r
+        memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );\r
+        break;\r
+      case RTAUDIO_FLOAT64:\r
+        memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );\r
+        memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );\r
+        break;\r
+    }\r
+\r
+    // update "in" index\r
+    inIndex_ += bufferSize;\r
+    inIndex_ %= bufferSize_;\r
+\r
+    return true;\r
+  }\r
+\r
+  // attempt to pull a buffer from the ring buffer from the current "out" index\r
+  bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )\r
+  {\r
+    if ( !buffer ||                 // incoming buffer is NULL\r
+         bufferSize == 0 ||         // incoming buffer has no data\r
+         bufferSize > bufferSize_ ) // incoming buffer too large\r
+    {\r
+      return false;\r
+    }\r
+\r
+    unsigned int relInIndex = inIndex_;\r
+    unsigned int outIndexEnd = outIndex_ + bufferSize;\r
+    if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {\r
+      relInIndex += bufferSize_;\r
+    }\r
+\r
+    // "out" index can begin at and end on the "in" index\r
+    if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {\r
+      return false; // not enough space between "out" index and "in" index\r
+    }\r
+\r
+    // copy buffer from internal to external\r
+    int fromZeroSize = outIndex_ + bufferSize - bufferSize_;\r
+    fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;\r
+    int fromOutSize = bufferSize - fromZeroSize;\r
+\r
+    switch( format )\r
+    {\r
+      case RTAUDIO_SINT8:\r
+        memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );\r
+        memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );\r
+        break;\r
+      case RTAUDIO_SINT16:\r
+        memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );\r
+        memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );\r
+        break;\r
+      case RTAUDIO_SINT24:\r
+        memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );\r
+        memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );\r
+        break;\r
+      case RTAUDIO_SINT32:\r
+        memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );\r
+        memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );\r
+        break;\r
+      case RTAUDIO_FLOAT32:\r
+        memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );\r
+        memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );\r
+        break;\r
+      case RTAUDIO_FLOAT64:\r
+        memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );\r
+        memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );\r
+        break;\r
+    }\r
+\r
+    // update "out" index\r
+    outIndex_ += bufferSize;\r
+    outIndex_ %= bufferSize_;\r
+\r
+    return true;\r
+  }\r
+\r
+private:\r
+  char* buffer_;\r
+  unsigned int bufferSize_;\r
+  unsigned int inIndex_;\r
+  unsigned int outIndex_;\r
 };\r
 \r
-RtApiDs :: RtApiDs()\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
+// 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
+void convertBufferWasapi( char* outBuffer,\r
+                          const char* inBuffer,\r
+                          const unsigned int& inChannelCount,\r
+                          const unsigned int& outChannelCount,\r
+                          const unsigned int& inSampleRate,\r
+                          const unsigned int& outSampleRate,\r
+                          const unsigned int& inSampleCount,\r
+                          unsigned int& outSampleCount,\r
+                          const RtAudioFormat& format )\r
 {\r
-  // Dsound will run both-threaded. If CoInitialize fails, then just\r
-  // accept whatever the mainline chose for a threading model.\r
-  coInitialized_ = false;\r
-  HRESULT hr = CoInitialize( NULL );\r
-  if ( !FAILED( hr ) ) coInitialized_ = true;\r
+  // calculate the new outSampleCount and relative sampleStep\r
+  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
+\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
+\r
+    switch ( format )\r
+    {\r
+      case RTAUDIO_SINT8:\r
+        memcpy( &( ( char* ) outBuffer )[ outSample * outChannelCount ], &( ( char* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( char ) );\r
+        break;\r
+      case RTAUDIO_SINT16:\r
+        memcpy( &( ( short* ) outBuffer )[ outSample * outChannelCount ], &( ( short* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( short ) );\r
+        break;\r
+      case RTAUDIO_SINT24:\r
+        memcpy( &( ( S24* ) outBuffer )[ outSample * outChannelCount ], &( ( S24* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( S24 ) );\r
+        break;\r
+      case RTAUDIO_SINT32:\r
+        memcpy( &( ( int* ) outBuffer )[ outSample * outChannelCount ], &( ( int* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( int ) );\r
+        break;\r
+      case RTAUDIO_FLOAT32:\r
+        memcpy( &( ( float* ) outBuffer )[ outSample * outChannelCount ], &( ( float* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( float ) );\r
+        break;\r
+      case RTAUDIO_FLOAT64:\r
+        memcpy( &( ( double* ) outBuffer )[ outSample * outChannelCount ], &( ( double* ) inBuffer )[ inSample * inChannelCount ], commonChannelCount * sizeof( double ) );\r
+        break;\r
+    }\r
+\r
+    // jump to next in sample\r
+    inSampleFraction += sampleStep;\r
+  }\r
 }\r
 \r
-RtApiDs :: ~RtApiDs()\r
+//-----------------------------------------------------------------------------\r
+\r
+// A structure to hold various information related to the WASAPI implementation.\r
+struct WasapiHandle\r
 {\r
-  if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
-  if ( stream_.state != STREAM_CLOSED ) closeStream();\r
-}\r
+  IAudioClient* captureAudioClient;\r
+  IAudioClient* renderAudioClient;\r
+  IAudioCaptureClient* captureClient;\r
+  IAudioRenderClient* renderClient;\r
+  HANDLE captureEvent;\r
+  HANDLE renderEvent;\r
+\r
+  WasapiHandle()\r
+  : captureAudioClient( NULL ),\r
+    renderAudioClient( NULL ),\r
+    captureClient( NULL ),\r
+    renderClient( NULL ),\r
+    captureEvent( NULL ),\r
+    renderEvent( NULL ) {}\r
+};\r
 \r
-// The DirectSound default output is always the first device.\r
-unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
+//=============================================================================\r
+\r
+RtApiWasapi::RtApiWasapi()\r
+  : coInitialized_( false ), deviceEnumerator_( NULL )\r
 {\r
-  return 0;\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
+  hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,\r
+                         CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),\r
+                         ( void** ) &deviceEnumerator_ );\r
+\r
+  if ( FAILED( hr ) ) {\r
+    errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";\r
+    error( RtAudioError::DRIVER_ERROR );\r
+  }\r
 }\r
 \r
-// The DirectSound default input is always the first input device,\r
-// which is the first capture device enumerated.\r
-unsigned int RtApiDs :: getDefaultInputDevice( void )\r
+//-----------------------------------------------------------------------------\r
+\r
+RtApiWasapi::~RtApiWasapi()\r
 {\r
-  return 0;\r
+  // if this object previously called CoInitialize()\r
+  if ( coInitialized_ ) {\r
+    CoUninitialize();\r
+  }\r
+\r
+  if ( stream_.state != STREAM_CLOSED ) {\r
+    closeStream();\r
+  }\r
+\r
+  SAFE_RELEASE( deviceEnumerator_ );\r
 }\r
 \r
-unsigned int RtApiDs :: getDeviceCount( void )\r
+//=============================================================================\r
+\r
+unsigned int RtApiWasapi::getDeviceCount( void )\r
 {\r
-  // Set query flag for previously found devices to false, so that we\r
-  // can check for any devices that have disappeared.\r
-  for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
-    dsDevices[i].found = false;\r
+  unsigned int captureDeviceCount = 0;\r
+  unsigned int renderDeviceCount = 0;\r
 \r
-  // Query DirectSound devices.\r
-  struct DsProbeData probeInfo;\r
-  probeInfo.isInput = false;\r
-  probeInfo.dsDevices = &dsDevices;\r
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
-  if ( FAILED( result ) ) {\r
-    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
-    errorText_ = errorStream_.str();\r
-    error( RtAudioError::WARNING );\r
-  }\r
+  IMMDeviceCollection* captureDevices = NULL;\r
+  IMMDeviceCollection* renderDevices = NULL;\r
 \r
-  // Query DirectSoundCapture devices.\r
-  probeInfo.isInput = true;\r
-  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
-  if ( FAILED( result ) ) {\r
-    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
-    errorText_ = errorStream_.str();\r
-    error( RtAudioError::WARNING );\r
-  }\r
+  // count capture devices\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
 \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
+  hr = captureDevices->GetCount( &captureDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
+\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
 \r
-  return dsDevices.size();\r
+  hr = renderDevices->GetCount( &renderDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
+\r
+Exit:\r
+  // release all references\r
+  SAFE_RELEASE( captureDevices );\r
+  SAFE_RELEASE( renderDevices );\r
+\r
+  return captureDeviceCount + renderDeviceCount;\r
 }\r
 \r
-RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
+//-----------------------------------------------------------------------------\r
+\r
+RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )\r
 {\r
   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
+  PROPVARIANT deviceNameProp;\r
+  PROPVARIANT defaultDeviceNameProp;\r
+\r
+  IMMDeviceCollection* captureDevices = NULL;\r
+  IMMDeviceCollection* renderDevices = NULL;\r
+  IMMDevice* devicePtr = NULL;\r
+  IMMDevice* defaultDevicePtr = NULL;\r
+  IAudioClient* audioClient = NULL;\r
+  IPropertyStore* devicePropStore = NULL;\r
+  IPropertyStore* defaultDevicePropStore = NULL;\r
+\r
+  WAVEFORMATEX* deviceFormat = NULL;\r
+  WAVEFORMATEX* closestMatchFormat = NULL;\r
+\r
+  // probed\r
   info.probed = false;\r
 \r
-  if ( dsDevices.size() == 0 ) {\r
-    // Force a query of all devices\r
-    getDeviceCount();\r
-    if ( dsDevices.size() == 0 ) {\r
-      errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
-      error( RtAudioError::INVALID_USE );\r
-      return info;\r
-    }\r
-  }\r
+  // count capture devices\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
 \r
-  if ( device >= dsDevices.size() ) {\r
-    errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
-    error( RtAudioError::INVALID_USE );\r
-    return info;\r
-  }\r
+  hr = captureDevices->GetCount( &captureDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
 \r
-  HRESULT result;\r
-  if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\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
 \r
-  LPDIRECTSOUND output;\r
-  DSCAPS outCaps;\r
-  result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
-  if ( FAILED( result ) ) {\r
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
-    errorText_ = errorStream_.str();\r
-    error( RtAudioError::WARNING );\r
-    goto probeInput;\r
-  }\r
+  hr = renderDevices->GetCount( &renderDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
 \r
-  outCaps.dwSize = sizeof( outCaps );\r
-  result = output->GetCaps( &outCaps );\r
-  if ( FAILED( result ) ) {\r
-    output->Release();\r
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
-    errorText_ = errorStream_.str();\r
-    error( RtAudioError::WARNING );\r
-    goto probeInput;\r
+  // validate device index\r
+  if ( device >= captureDeviceCount + renderDeviceCount )\r
+    EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );\r
+\r
+  // determine whether index falls within capture or render devices\r
+  if ( device >= renderDeviceCount ) {\r
+    hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );\r
+\r
+    isCaptureDevice = true;\r
   }\r
+  else {\r
+    hr = renderDevices->Item( device, &devicePtr );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );\r
 \r
-  // Get output channel information.\r
-  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
+    isCaptureDevice = false;\r
+  }\r
 \r
-  // Get sample rate information.\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
+  }\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
+  }\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
+  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
+\r
+  deviceName = defaultDeviceNameProp.pwszVal;\r
+  defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );\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
+\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
+\r
+  deviceName = deviceNameProp.pwszVal;\r
+  info.name = std::string( deviceName.begin(), deviceName.end() );\r
+\r
+  // is default\r
+  if ( isCaptureDevice ) {\r
+    info.isDefaultInput = info.name == defaultDeviceName;\r
+    info.isDefaultOutput = false;\r
+  }\r
+  else {\r
+    info.isDefaultInput = false;\r
+    info.isDefaultOutput = info.name == defaultDeviceName;\r
+  }\r
+\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
+\r
+  hr = audioClient->GetMixFormat( &deviceFormat );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+\r
+  if ( isCaptureDevice ) {\r
+    info.inputChannels = deviceFormat->nChannels;\r
+    info.outputChannels = 0;\r
+    info.duplexChannels = 0;\r
+  }\r
+  else {\r
+    info.inputChannels = 0;\r
+    info.outputChannels = deviceFormat->nChannels;\r
+    info.duplexChannels = 0;\r
+  }\r
+\r
+  // sample rates\r
+  info.sampleRates.clear();\r
+\r
+  // allow support for sample rates that are multiples of the base rate\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
+  }\r
+\r
+  // native format\r
+  info.nativeFormats = 0;\r
+\r
+  if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||\r
+       ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
+         ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )\r
+  {\r
+    if ( deviceFormat->wBitsPerSample == 32 ) {\r
+      info.nativeFormats |= RTAUDIO_FLOAT32;\r
+    }\r
+    else if ( deviceFormat->wBitsPerSample == 64 ) {\r
+      info.nativeFormats |= RTAUDIO_FLOAT64;\r
+    }\r
+  }\r
+  else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||\r
+           ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&\r
+             ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )\r
+  {\r
+    if ( deviceFormat->wBitsPerSample == 8 ) {\r
+      info.nativeFormats |= RTAUDIO_SINT8;\r
+    }\r
+    else if ( deviceFormat->wBitsPerSample == 16 ) {\r
+      info.nativeFormats |= RTAUDIO_SINT16;\r
+    }\r
+    else if ( deviceFormat->wBitsPerSample == 24 ) {\r
+      info.nativeFormats |= RTAUDIO_SINT24;\r
+    }\r
+    else if ( deviceFormat->wBitsPerSample == 32 ) {\r
+      info.nativeFormats |= RTAUDIO_SINT32;\r
+    }\r
+  }\r
+\r
+  // probed\r
+  info.probed = true;\r
+\r
+Exit:\r
+  // release all references\r
+  PropVariantClear( &deviceNameProp );\r
+  PropVariantClear( &defaultDeviceNameProp );\r
+\r
+  SAFE_RELEASE( captureDevices );\r
+  SAFE_RELEASE( renderDevices );\r
+  SAFE_RELEASE( devicePtr );\r
+  SAFE_RELEASE( defaultDevicePtr );\r
+  SAFE_RELEASE( audioClient );\r
+  SAFE_RELEASE( devicePropStore );\r
+  SAFE_RELEASE( defaultDevicePropStore );\r
+\r
+  CoTaskMemFree( deviceFormat );\r
+  CoTaskMemFree( closestMatchFormat );\r
+\r
+  return info;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+unsigned int RtApiWasapi::getDefaultOutputDevice( void )\r
+{\r
+  for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
+    if ( getDeviceInfo( i ).isDefaultOutput ) {\r
+      return i;\r
+    }\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+unsigned int RtApiWasapi::getDefaultInputDevice( void )\r
+{\r
+  for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {\r
+    if ( getDeviceInfo( i ).isDefaultInput ) {\r
+      return i;\r
+    }\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::closeStream( void )\r
+{\r
+  if ( stream_.state == STREAM_CLOSED ) {\r
+    errorText_ = "RtApiWasapi::closeStream: No open stream to close";\r
+    error( RtAudioError::WARNING );\r
+    return;\r
+  }\r
+\r
+  if ( stream_.state != STREAM_STOPPED )\r
+    stopStream();\r
+\r
+  // clean up stream memory\r
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )\r
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )\r
+\r
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )\r
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )\r
+\r
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )\r
+    CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );\r
+\r
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )\r
+    CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );\r
+\r
+  delete stream_.apiHandle;\r
+  stream_.apiHandle = NULL;\r
+\r
+  for ( int i = 0; i < 2; i++ ) {\r
+    if ( stream_.userBuffer[i] ) {\r
+      free( stream_.userBuffer[i] );\r
+      stream_.userBuffer[i] = 0;\r
+    }\r
+  }\r
+\r
+  if ( stream_.deviceBuffer ) {\r
+    free( stream_.deviceBuffer );\r
+    stream_.deviceBuffer = 0;\r
+  }\r
+\r
+  // update stream state\r
+  stream_.state = STREAM_CLOSED;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::startStream( void )\r
+{\r
+  verifyStream();\r
+\r
+  if ( stream_.state == STREAM_RUNNING ) {\r
+    errorText_ = "RtApiWasapi::startStream: The stream is already running";\r
+    error( RtAudioError::WARNING );\r
+    return;\r
+  }\r
+\r
+  // update stream state\r
+  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
+\r
+  if ( !stream_.callbackInfo.thread ) {\r
+    errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread";\r
+    error( RtAudioError::THREAD_ERROR );\r
+  }\r
+  else {\r
+    SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );\r
+    ResumeThread( ( void* ) stream_.callbackInfo.thread );\r
+  }\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::stopStream( void )\r
+{\r
+  verifyStream();\r
+\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    errorText_ = "RtApiWasapi::stopStream: The stream is already stopped";\r
+    error( RtAudioError::WARNING );\r
+    return;\r
+  }\r
+\r
+  // inform stream thread by setting stream state to STREAM_STOPPING\r
+  stream_.state = STREAM_STOPPING;\r
+\r
+  // wait until stream thread is stopped\r
+  while( stream_.state != STREAM_STOPPED ) {\r
+    Sleep( 1 );\r
+  }\r
+\r
+  // Wait for the last buffer to play before stopping.\r
+  Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );\r
+\r
+  // stop capture client if applicable\r
+  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
+      error( RtAudioError::DRIVER_ERROR );\r
+    }\r
+  }\r
+\r
+  // stop render client if applicable\r
+  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
+      error( RtAudioError::DRIVER_ERROR );\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
+    error( RtAudioError::THREAD_ERROR );\r
+  }\r
+\r
+  stream_.callbackInfo.thread = NULL;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::abortStream( void )\r
+{\r
+  verifyStream();\r
+\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    errorText_ = "RtApiWasapi::abortStream: The stream is already stopped";\r
+    error( RtAudioError::WARNING );\r
+    return;\r
+  }\r
+\r
+  // inform stream thread by setting stream state to STREAM_STOPPING\r
+  stream_.state = STREAM_STOPPING;\r
+\r
+  // wait until stream thread is stopped\r
+  while ( stream_.state != STREAM_STOPPED ) {\r
+    Sleep( 1 );\r
+  }\r
+\r
+  // stop capture client if applicable\r
+  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
+      error( RtAudioError::DRIVER_ERROR );\r
+    }\r
+  }\r
+\r
+  // stop render client if applicable\r
+  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
+      error( RtAudioError::DRIVER_ERROR );\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
+    error( RtAudioError::THREAD_ERROR );\r
+  }\r
+\r
+  stream_.callbackInfo.thread = NULL;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
+                                   unsigned int firstChannel, unsigned int sampleRate,\r
+                                   RtAudioFormat format, unsigned int* bufferSize,\r
+                                   RtAudio::StreamOptions* options )\r
+{\r
+  bool methodResult = FAILURE;\r
+  unsigned int captureDeviceCount = 0;\r
+  unsigned int renderDeviceCount = 0;\r
+\r
+  IMMDeviceCollection* captureDevices = NULL;\r
+  IMMDeviceCollection* renderDevices = NULL;\r
+  IMMDevice* devicePtr = NULL;\r
+  WAVEFORMATEX* deviceFormat = NULL;\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
+  HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device collection" );\r
+\r
+  hr = captureDevices->GetCount( &captureDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device count" );\r
+\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
+\r
+  hr = renderDevices->GetCount( &renderDeviceCount );\r
+  EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device count" );\r
+\r
+  // validate device index\r
+  if ( device >= captureDeviceCount + renderDeviceCount )\r
+    EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Invalid device index" );\r
+\r
+  // determine whether index falls within capture or render devices\r
+  if ( device >= renderDeviceCount ) {\r
+    if ( mode != INPUT )\r
+      EXIT_ON_ERROR( -1, RtAudioError::INVALID_USE, "Capture device selected as output device" );\r
+\r
+    // retrieve captureAudioClient from devicePtr\r
+    IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
+\r
+    hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture device handle" );\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
+\r
+    hr = captureAudioClient->GetMixFormat( &deviceFormat );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\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
+\r
+    // retrieve renderAudioClient from devicePtr\r
+    IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
+\r
+    hr = renderDevices->Item( device, &devicePtr );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render device handle" );\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
+\r
+    hr = renderAudioClient->GetMixFormat( &deviceFormat );\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve device mix format" );\r
+\r
+    stream_.nDeviceChannels[mode] = deviceFormat->nChannels;\r
+    renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );\r
+  }\r
+\r
+  // fill stream data\r
+  if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||\r
+       ( stream_.mode == INPUT && mode == OUTPUT ) ) {\r
+    stream_.mode = DUPLEX;\r
+  }\r
+  else {\r
+    stream_.mode = mode;\r
+  }\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
+  stream_.nBuffers = 1;\r
+  stream_.nUserChannels[mode] = channels;\r
+  stream_.channelOffset[mode] = firstChannel;\r
+  stream_.userFormat = format;\r
+  stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;\r
+\r
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )\r
+    stream_.userInterleaved = false;\r
+  else\r
+    stream_.userInterleaved = true;\r
+  stream_.deviceInterleaved[mode] = true;\r
+\r
+  // Set flags for buffer conversion.\r
+  stream_.doConvertBuffer[mode] = false;\r
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+    stream_.doConvertBuffer[mode] = true;\r
+  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
+\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 ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )\r
+    stream_.callbackInfo.priority = 15;\r
+  else\r
+    stream_.callbackInfo.priority = 0;\r
+\r
+  ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback\r
+  ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode\r
+\r
+  methodResult = SUCCESS;\r
+\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
+  return methodResult;\r
+}\r
+\r
+//=============================================================================\r
+\r
+DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )\r
+{\r
+  if ( wasapiPtr )\r
+    ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();\r
+\r
+  return 0;\r
+}\r
+\r
+DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )\r
+{\r
+  if ( wasapiPtr )\r
+    ( ( RtApiWasapi* ) wasapiPtr )->stopStream();\r
+\r
+  return 0;\r
+}\r
+\r
+DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )\r
+{\r
+  if ( wasapiPtr )\r
+    ( ( RtApiWasapi* ) wasapiPtr )->abortStream();\r
+\r
+  return 0;\r
+}\r
+\r
+//-----------------------------------------------------------------------------\r
+\r
+void RtApiWasapi::wasapiThread()\r
+{\r
+  // as this is a new thread, we must CoInitialize it\r
+  CoInitialize( NULL );\r
+\r
+  HRESULT hr;\r
+\r
+  IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;\r
+  IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;\r
+  IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;\r
+  IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;\r
+  HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;\r
+  HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;\r
+\r
+  WAVEFORMATEX* captureFormat = NULL;\r
+  WAVEFORMATEX* renderFormat = NULL;\r
+  float captureSrRatio = 0.0f;\r
+  float renderSrRatio = 0.0f;\r
+  WasapiBuffer captureBuffer;\r
+  WasapiBuffer renderBuffer;\r
+\r
+  // Attempt to assign "Pro Audio" characteristic to thread\r
+  HMODULE AvrtDll = LoadLibrary( "AVRT.dll" );\r
+  if ( AvrtDll ) {\r
+    DWORD taskIndex = 0;\r
+    TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );\r
+    AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );\r
+    FreeLibrary( AvrtDll );\r
+  }\r
+\r
+  // 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
+\r
+    captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );\r
+\r
+    // initialize capture stream according to desire buffer size\r
+    float desiredBufferSize = stream_.bufferSize * captureSrRatio;\r
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / captureFormat->nSamplesPerSec );\r
+\r
+    if ( !captureClient ) {\r
+      hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
+                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
+                                           desiredBufferPeriod,\r
+                                           desiredBufferPeriod,\r
+                                           captureFormat,\r
+                                           NULL );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize capture audio client" );\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
+\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
+\r
+      hr = captureAudioClient->SetEventHandle( captureEvent );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set capture event handle" );\r
+\r
+      ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;\r
+      ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;\r
+    }\r
+\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
+\r
+    // scale outBufferSize according to stream->user sample rate ratio\r
+    unsigned int outBufferSize = ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];\r
+    inBufferSize *= stream_.nDeviceChannels[INPUT];\r
+\r
+    // set captureBuffer size\r
+    captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );\r
+\r
+    // reset the capture stream\r
+    hr = captureAudioClient->Reset();\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset capture stream" );\r
+\r
+    // start the capture stream\r
+    hr = captureAudioClient->Start();\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start capture stream" );\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
+\r
+    renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );\r
+\r
+    // initialize render stream according to desire buffer size\r
+    float desiredBufferSize = stream_.bufferSize * renderSrRatio;\r
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) desiredBufferSize * 10000000 / renderFormat->nSamplesPerSec );\r
+\r
+    if ( !renderClient ) {\r
+      hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,\r
+                                          AUDCLNT_STREAMFLAGS_EVENTCALLBACK,\r
+                                          desiredBufferPeriod,\r
+                                          desiredBufferPeriod,\r
+                                          renderFormat,\r
+                                          NULL );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to initialize render audio client" );\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
+\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
+\r
+      hr = renderAudioClient->SetEventHandle( renderEvent );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to set render event handle" );\r
+\r
+      ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;\r
+      ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;\r
+    }\r
+\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
+\r
+    // scale inBufferSize according to user->stream sample rate ratio\r
+    unsigned int inBufferSize = ( unsigned int ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];\r
+    outBufferSize *= stream_.nDeviceChannels[OUTPUT];\r
+\r
+    // set renderBuffer size\r
+    renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );\r
+\r
+    // reset the render stream\r
+    hr = renderAudioClient->Reset();\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to reset render stream" );\r
+\r
+    // start the render stream\r
+    hr = renderAudioClient->Start();\r
+    EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to start render stream" );\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
+  unsigned int deviceBufferSize = 0;\r
+  if ( stream_.mode == INPUT ) {\r
+    deviceBufferSize = ( size_t ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );\r
+  }\r
+  else if ( stream_.mode == OUTPUT ) {\r
+    deviceBufferSize = ( size_t ) ( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );\r
+  }\r
+  else if ( stream_.mode == DUPLEX ) {\r
+    deviceBufferSize = 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
+  }\r
+\r
+  char* convBuffer = ( char* ) malloc( deviceBufferSize );\r
+  stream_.deviceBuffer = ( char* ) malloc( deviceBufferSize );\r
+  if ( !convBuffer || !stream_.deviceBuffer )\r
+      EXIT_ON_ERROR( -1, RtAudioError::MEMORY_ERROR, "Error allocating device buffer memory" );\r
+\r
+  // stream process loop\r
+  while ( stream_.state != STREAM_STOPPING ) {\r
+    if ( !callbackPulled ) {\r
+      // Callback Input\r
+      // ==============\r
+      // 1. Pull callback buffer from inputBuffer\r
+      // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count\r
+      //                          Convert callback buffer to user format\r
+\r
+      if ( captureAudioClient ) {\r
+        // Pull callback buffer from inputBuffer\r
+        callbackPulled = captureBuffer.pullBuffer( convBuffer,\r
+                                                   ( unsigned int ) ( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT],\r
+                                                   stream_.deviceFormat[INPUT] );\r
+\r
+        if ( callbackPulled ) {\r
+          // Convert callback buffer to user sample rate and channel count\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
+                               convBufferSize,\r
+                               stream_.deviceFormat[INPUT] );\r
+\r
+          if ( stream_.doConvertBuffer[INPUT] ) {\r
+            // Convert callback buffer to user format\r
+            convertBuffer( stream_.userBuffer[INPUT],\r
+                           stream_.deviceBuffer,\r
+                           stream_.convertInfo[INPUT] );\r
+          }\r
+          else {\r
+            // no conversion, simple copy deviceBuffer to userBuffer\r
+            memcpy( stream_.userBuffer[INPUT],\r
+                    stream_.deviceBuffer,\r
+                    stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );\r
+          }\r
+        }\r
+      }\r
+      else {\r
+        // if there is no capture stream, set callbackPulled flag\r
+        callbackPulled = true;\r
+      }\r
+\r
+      // Execute Callback\r
+      // ================\r
+      // 1. Execute user callback method\r
+      // 2. Handle return value from callback\r
+\r
+      // if callback has not requested the stream to stop\r
+      if ( callbackPulled && !callbackStopped ) {\r
+        // Execute user callback method\r
+        callbackResult = callback( stream_.userBuffer[OUTPUT],\r
+                                   stream_.userBuffer[INPUT],\r
+                                   stream_.bufferSize,\r
+                                   getStreamTime(),\r
+                                   captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,\r
+                                   stream_.callbackInfo.userData );\r
+\r
+        // 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
+          if ( !threadHandle ) {\r
+            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream stop thread" );\r
+          }\r
+          else if ( !CloseHandle( threadHandle ) ) {\r
+            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream stop thread handle" );\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
+          if ( !threadHandle ) {\r
+            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to instantiate stream abort thread" );\r
+          }\r
+          else if ( !CloseHandle( threadHandle ) ) {\r
+            EXIT_ON_ERROR( -1, RtAudioError::THREAD_ERROR, "Unable to close stream abort thread handle" );\r
+          }\r
+\r
+          callbackStopped = true;\r
+        }\r
+      }\r
+    }\r
+\r
+    // Callback Output\r
+    // ===============\r
+    // 1. Convert callback buffer to stream format\r
+    // 2. Convert callback buffer to stream sample rate and channel count\r
+    // 3. Push callback buffer into outputBuffer\r
+\r
+    if ( renderAudioClient && callbackPulled ) {\r
+      if ( stream_.doConvertBuffer[OUTPUT] ) {\r
+        // Convert callback buffer to stream format\r
+        convertBuffer( stream_.deviceBuffer,\r
+                       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
+      // Push callback buffer into outputBuffer\r
+      callbackPushed = renderBuffer.pushBuffer( convBuffer,\r
+                                                convBufferSize * stream_.nDeviceChannels[OUTPUT],\r
+                                                stream_.deviceFormat[OUTPUT] );\r
+    }\r
+\r
+    // Stream Capture\r
+    // ==============\r
+    // 1. Get capture buffer from stream\r
+    // 2. Push capture buffer into inputBuffer\r
+    // 3. If 2. was successful: Release capture buffer\r
+\r
+    if ( captureAudioClient ) {\r
+      // if the callback input buffer was not pulled from captureBuffer, wait for next capture event\r
+      if ( !callbackPulled ) {\r
+        WaitForSingleObject( captureEvent, INFINITE );\r
+      }\r
+\r
+      // Get capture buffer from stream\r
+      hr = captureClient->GetBuffer( &streamBuffer,\r
+                                     &bufferFrameCount,\r
+                                     &captureFlags, NULL, NULL );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve capture buffer" );\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
+        {\r
+          // Release capture buffer\r
+          hr = captureClient->ReleaseBuffer( bufferFrameCount );\r
+          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release capture buffer" );\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
+        }\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
+      }\r
+    }\r
+\r
+    // Stream Render\r
+    // =============\r
+    // 1. Get render buffer from stream\r
+    // 2. Pull next buffer from outputBuffer\r
+    // 3. If 2. was successful: Fill render buffer with next buffer\r
+    //                          Release render buffer\r
+\r
+    if ( renderAudioClient ) {\r
+      // if the callback output buffer was not pushed to renderBuffer, wait for next render event\r
+      if ( callbackPulled && !callbackPushed ) {\r
+        WaitForSingleObject( renderEvent, INFINITE );\r
+      }\r
+\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
+\r
+      hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );\r
+      EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to retrieve render buffer padding" );\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
+\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
+        {\r
+          // Release render buffer\r
+          hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );\r
+          EXIT_ON_ERROR( hr, RtAudioError::DRIVER_ERROR, "Unable to release render buffer" );\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
+        }\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
+      }\r
+    }\r
+\r
+    // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
+    if ( callbackPushed ) {\r
+      callbackPulled = false;\r
+    }\r
+\r
+    // tick stream time\r
+    RtApi::tickStreamTime();\r
+  }\r
+\r
+Exit:\r
+  // clean up\r
+  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
+\r
+//******************** End of __WINDOWS_WASAPI__ *********************//\r
+#endif\r
+\r
+\r
+#if defined(__WINDOWS_DS__) // Windows DirectSound API\r
+\r
+// Modified by Robin Davies, October 2005\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
+// Changed device query structure for RtAudio 4.0.7, January 2010\r
+\r
+#include <dsound.h>\r
+#include <assert.h>\r
+#include <algorithm>\r
+\r
+#if defined(__MINGW32__)\r
+  // missing from latest mingw winapi\r
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */\r
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */\r
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */\r
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */\r
+#endif\r
+\r
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768\r
+\r
+#ifdef _MSC_VER // if Microsoft Visual C++\r
+#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.\r
+#endif\r
+\r
+static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )\r
+{\r
+  if ( pointer > bufferSize ) pointer -= bufferSize;\r
+  if ( laterPointer < earlierPointer ) laterPointer += bufferSize;\r
+  if ( pointer < earlierPointer ) pointer += bufferSize;\r
+  return pointer >= earlierPointer && pointer < laterPointer;\r
+}\r
+\r
+// A structure to hold various information related to the DirectSound\r
+// API implementation.\r
+struct DsHandle {\r
+  unsigned int drainCounter; // Tracks callback counts when draining\r
+  bool internalDrain;        // Indicates if stop is initiated from callback or not.\r
+  void *id[2];\r
+  void *buffer[2];\r
+  bool xrun[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
+\r
+  DsHandle()\r
+    :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }\r
+};\r
+\r
+// Declarations for utility functions, callbacks, and structures\r
+// specific to the DirectSound implementation.\r
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
+                                          LPCTSTR description,\r
+                                          LPCTSTR module,\r
+                                          LPVOID lpContext );\r
+\r
+static const char* getErrorString( int code );\r
+\r
+static unsigned __stdcall callbackHandler( void *ptr );\r
+\r
+struct DsDevice {\r
+  LPGUID id[2];\r
+  bool validId[2];\r
+  bool found;\r
+  std::string name;\r
+\r
+  DsDevice()\r
+  : found(false) { validId[0] = false; validId[1] = false; }\r
+};\r
+\r
+struct DsProbeData {\r
+  bool isInput;\r
+  std::vector<struct DsDevice>* dsDevices;\r
+};\r
+\r
+RtApiDs :: RtApiDs()\r
+{\r
+  // Dsound will run both-threaded. If CoInitialize fails, then just\r
+  // accept whatever the mainline chose for a threading model.\r
+  coInitialized_ = false;\r
+  HRESULT hr = CoInitialize( NULL );\r
+  if ( !FAILED( hr ) ) coInitialized_ = true;\r
+}\r
+\r
+RtApiDs :: ~RtApiDs()\r
+{\r
+  if ( coInitialized_ ) CoUninitialize(); // balanced call.\r
+  if ( stream_.state != STREAM_CLOSED ) closeStream();\r
+}\r
+\r
+// The DirectSound default output is always the first device.\r
+unsigned int RtApiDs :: getDefaultOutputDevice( void )\r
+{\r
+  return 0;\r
+}\r
+\r
+// The DirectSound default input is always the first input device,\r
+// which is the first capture device enumerated.\r
+unsigned int RtApiDs :: getDefaultInputDevice( void )\r
+{\r
+  return 0;\r
+}\r
+\r
+unsigned int RtApiDs :: getDeviceCount( void )\r
+{\r
+  // Set query flag for previously found devices to false, so that we\r
+  // can check for any devices that have disappeared.\r
+  for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
+    dsDevices[i].found = false;\r
+\r
+  // Query DirectSound devices.\r
+  struct DsProbeData probeInfo;\r
+  probeInfo.isInput = false;\r
+  probeInfo.dsDevices = &dsDevices;\r
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+  if ( FAILED( result ) ) {\r
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";\r
+    errorText_ = errorStream_.str();\r
+    error( RtAudioError::WARNING );\r
+  }\r
+\r
+  // Query DirectSoundCapture devices.\r
+  probeInfo.isInput = true;\r
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );\r
+  if ( FAILED( result ) ) {\r
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";\r
+    errorText_ = errorStream_.str();\r
+    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
+\r
+  return static_cast<unsigned int>(dsDevices.size());\r
+}\r
+\r
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )\r
+{\r
+  RtAudio::DeviceInfo info;\r
+  info.probed = false;\r
+\r
+  if ( dsDevices.size() == 0 ) {\r
+    // Force a query of all devices\r
+    getDeviceCount();\r
+    if ( dsDevices.size() == 0 ) {\r
+      errorText_ = "RtApiDs::getDeviceInfo: no devices found!";\r
+      error( RtAudioError::INVALID_USE );\r
+      return info;\r
+    }\r
+  }\r
+\r
+  if ( device >= dsDevices.size() ) {\r
+    errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";\r
+    error( RtAudioError::INVALID_USE );\r
+    return info;\r
+  }\r
+\r
+  HRESULT result;\r
+  if ( dsDevices[ device ].validId[0] == false ) goto probeInput;\r
+\r
+  LPDIRECTSOUND output;\r
+  DSCAPS outCaps;\r
+  result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );\r
+  if ( FAILED( result ) ) {\r
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";\r
+    errorText_ = errorStream_.str();\r
+    error( RtAudioError::WARNING );\r
+    goto probeInput;\r
+  }\r
+\r
+  outCaps.dwSize = sizeof( outCaps );\r
+  result = output->GetCaps( &outCaps );\r
+  if ( FAILED( result ) ) {\r
+    output->Release();\r
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";\r
+    errorText_ = errorStream_.str();\r
+    error( RtAudioError::WARNING );\r
+    goto probeInput;\r
+  }\r
+\r
+  // Get output channel information.\r
+  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;\r
+\r
+  // Get sample rate information.\r
   info.sampleRates.clear();\r
   for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {\r
     if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&\r
@@ -3861,7 +5237,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     return FAILURE;\r
   }\r
 \r
-  unsigned int nDevices = dsDevices.size();\r
+  size_t nDevices = dsDevices.size();\r
   if ( nDevices == 0 ) {\r
     // This should not happen because a check is made before this function is called.\r
     errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";\r
@@ -5003,7 +6379,7 @@ static std::string convertTChar( LPCTSTR name )
 \r
 static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,\r
                                           LPCTSTR description,\r
-                                          LPCTSTR module,\r
+                                          LPCTSTR /*module*/,\r
                                           LPVOID lpContext )\r
 {\r
   struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;\r
@@ -5358,6 +6734,8 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
       goto probeParameters;\r
     }\r
   }\r
+  else\r
+    snd_ctl_close( chandle );\r
 \r
   result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);\r
   if ( result < 0 ) {\r
@@ -5470,6 +6848,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
 \r
   // Check that we have at least one supported format\r
   if ( info.nativeFormats == 0 ) {\r
+    snd_pcm_close( phandle );\r
     errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";\r
     errorText_ = errorStream_.str();\r
     error( RtAudioError::WARNING );\r
@@ -5479,8 +6858,10 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   // Get the device name\r
   char *cardname;\r
   result = snd_card_get_name( card, &cardname );\r
-  if ( result >= 0 )\r
+  if ( result >= 0 ) {\r
     sprintf( name, "hw:%s,%d", cardname, subdevice );\r
+    free( cardname );\r
+  }\r
   info.name = name;\r
 \r
   // That's all ... close the device and return\r
@@ -6105,7 +7486,7 @@ void RtApiAlsa :: startStream()
   stream_.state = STREAM_RUNNING;\r
 \r
  unlock:\r
-  apiInfo->runnable = false; // fixes high CPU usage when stopped\r
+  apiInfo->runnable = true;\r
   pthread_cond_signal( &apiInfo->runnable_cv );\r
   MUTEX_UNLOCK( &stream_.mutex );\r
 \r
@@ -6191,6 +7572,7 @@ void RtApiAlsa :: abortStream()
   }\r
 \r
  unlock:\r
+  apiInfo->runnable = false; // fixes high CPU usage when stopped\r
   MUTEX_UNLOCK( &stream_.mutex );\r
 \r
   if ( result >= 0 ) return;\r
@@ -6451,7 +7833,7 @@ unsigned int RtApiPulse::getDeviceCount( void )
   return 1;\r
 }\r
 \r
-RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int device )\r
+RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )\r
 {\r
   RtAudio::DeviceInfo info;\r
   info.probed = true;\r
@@ -6746,26 +8128,34 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     if ( format == sf->rtaudio_format ) {\r
       sf_found = true;\r
       stream_.userFormat = sf->rtaudio_format;\r
+      stream_.deviceFormat[mode] = stream_.userFormat;\r
       ss.format = sf->pa_format;\r
       break;\r
     }\r
   }\r
-  if ( !sf_found ) {\r
-    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample format.";\r
-    return false;\r
+  if ( !sf_found ) { // Use internal data format conversion.\r
+    stream_.userFormat = format;\r
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;\r
+    ss.format = PA_SAMPLE_FLOAT32LE;\r
   }\r
 \r
-  // Set interleaving parameters.\r
+  // Set other stream parameters.\r
   if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;\r
   else stream_.userInterleaved = true;\r
   stream_.deviceInterleaved[mode] = true;\r
   stream_.nBuffers = 1;\r
   stream_.doByteSwap[mode] = false;\r
-  stream_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved;\r
-  stream_.deviceFormat[mode] = stream_.userFormat;\r
   stream_.nUserChannels[mode] = channels;\r
   stream_.nDeviceChannels[mode] = channels + firstChannel;\r
   stream_.channelOffset[mode] = 0;\r
+  std::string streamName = "RtAudio";\r
+\r
+  // Set flags for buffer conversion.\r
+  stream_.doConvertBuffer[mode] = false;\r
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )\r
+    stream_.doConvertBuffer[mode] = true;\r
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )\r
+    stream_.doConvertBuffer[mode] = true;\r
 \r
   // Allocate necessary internal buffers.\r
   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
@@ -6819,7 +8209,6 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
   pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
 \r
   int error;\r
-  std::string streamName = "RtAudio";\r
   if ( !options->streamName.empty() ) streamName = options->streamName;\r
   switch ( mode ) {\r
   case INPUT:\r
@@ -7841,10 +9230,10 @@ void RtApi :: error( RtAudioError::Type type )
   if ( errorCallback ) {\r
     // abortStream() can generate new error messages. Ignore them. Just keep original one.\r
 \r
-    if ( firstErrorOccurred )\r
+    if ( firstErrorOccurred_ )\r
       return;\r
 \r
-    firstErrorOccurred = true;\r
+    firstErrorOccurred_ = true;\r
     const std::string errorMessage = errorText_;\r
 \r
     if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {\r
@@ -7853,7 +9242,7 @@ void RtApi :: error( RtAudioError::Type type )
     }\r
 \r
     errorCallback( type, errorMessage );\r
-    firstErrorOccurred = false;\r
+    firstErrorOccurred_ = false;\r
     return;\r
   }\r
 \r