Fix compilation of ASIO and WASAPI backends in MingW/Ubuntu.
[rtaudio.git] / RtAudio.cpp
index 4a51baf75632e8d52038676d3214d0487c54e630..111039404af9339ab508a4efcfda6458788c6d33 100644 (file)
@@ -1,16 +1,16 @@
-/************************************************************************/
+/************************************************************************/
 /*! \class RtAudio
 /*! \class RtAudio
-    \brief Realtime audio i/o C++ class.
+    \brief Realtime audio i/o C++ classes.
 
     RtAudio provides a common API (Application Programming Interface)
 
     RtAudio provides a common API (Application Programming Interface)
-    for realtime audio input/output across Linux (native ALSA and
-    OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound
-    and ASIO) operating systems.
+    for realtime audio input/output across Linux (native ALSA, Jack,
+    and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
+    (DirectSound, ASIO and WASAPI) operating systems.
 
 
-    RtAudio WWW site: http://www-ccrma.stanford.edu/~gary/rtaudio/
+    RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
 
 
-    RtAudio: a realtime audio i/o C++ class
-    Copyright (c) 2001-2002 Gary P. Scavone
+    RtAudio: realtime audio i/o C++ classes
+    Copyright (c) 2001-2017 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -24,8 +24,9 @@
     included in all copies or substantial portions of the Software.
 
     Any person wishing to distribute modifications to the Software is
     included in all copies or substantial portions of the Software.
 
     Any person wishing to distribute modifications to the Software is
-    requested to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 */
 /************************************************************************/
 
 */
 /************************************************************************/
 
+// RtAudio: Version 5.0.0
 
 #include "RtAudio.h"
 
 #include "RtAudio.h"
-#include <vector>
-#include <stdio.h>
-#include <iostream.h>
+#include <iostream>
+#include <cstdlib>
+#include <cstring>
+#include <climits>
+#include <cmath>
+#include <algorithm>
 
 // Static variable definitions.
 
 // Static variable definitions.
-const unsigned int RtAudio :: SAMPLE_RATES[] = {
+const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
+const unsigned int RtApi::SAMPLE_RATES[] = {
   4000, 5512, 8000, 9600, 11025, 16000, 22050,
   32000, 44100, 48000, 88200, 96000, 176400, 192000
 };
   4000, 5512, 8000, 9600, 11025, 16000, 22050,
   32000, 44100, 48000, 88200, 96000, 176400, 192000
 };
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT8 = 1;
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT16 = 2;
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT24 = 4;
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT32 = 8;
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT32 = 16;
-const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT64 = 32;
-
-#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
+
+#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
+  #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)
   #define MUTEX_LOCK(A)       EnterCriticalSection(A)
   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)
   #define MUTEX_LOCK(A)       EnterCriticalSection(A)
   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)
-#else // pthread API
+
+  #include "tchar.h"
+
+  static std::string convertCharPointerToStdString(const char *text)
+  {
+    return std::string(text);
+  }
+
+  static std::string convertCharPointerToStdString(const wchar_t *text)
+  {
+    int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
+    std::string s( length-1, '\0' );
+    WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);
+    return s;
+  }
+
+#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
+  // pthread API
   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
+  #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)
   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)
   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)
   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)
   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)
+#else
+  #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions
+  #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions
 #endif
 
 // *************************************************** //
 //
 #endif
 
 // *************************************************** //
 //
-// Public common (OS-independent) methods.
+// RtAudio definitions.
 //
 // *************************************************** //
 
 //
 // *************************************************** //
 
-RtAudio :: RtAudio()
+std::string RtAudio :: getVersion( void )
+{
+  return RTAUDIO_VERSION;
+}
+
+void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis )
 {
 {
-  initialize();
+  apis.clear();
 
 
-  if (nDevices <= 0) {
-    sprintf(message, "RtAudio: no audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
+  // The order here will control the order of RtAudio's API search in
+  // the constructor.
+#if defined(__UNIX_JACK__)
+  apis.push_back( UNIX_JACK );
+#endif
+#if defined(__LINUX_ALSA__)
+  apis.push_back( LINUX_ALSA );
+#endif
+#if defined(__LINUX_PULSE__)
+  apis.push_back( LINUX_PULSE );
+#endif
+#if defined(__LINUX_OSS__)
+  apis.push_back( LINUX_OSS );
+#endif
+#if defined(__WINDOWS_ASIO__)
+  apis.push_back( WINDOWS_ASIO );
+#endif
+#if defined(__WINDOWS_WASAPI__)
+  apis.push_back( WINDOWS_WASAPI );
+#endif
+#if defined(__WINDOWS_DS__)
+  apis.push_back( WINDOWS_DS );
+#endif
+#if defined(__MACOSX_CORE__)
+  apis.push_back( MACOSX_CORE );
+#endif
+#if defined(__RTAUDIO_DUMMY__)
+  apis.push_back( RTAUDIO_DUMMY );
+#endif
 }
 
 }
 
-RtAudio :: RtAudio(int *streamId,
-                   int outputDevice, int outputChannels,
-                   int inputDevice, int inputChannels,
-                   RTAUDIO_FORMAT format, int sampleRate,
-                   int *bufferSize, int numberOfBuffers)
+void RtAudio :: openRtApi( RtAudio::Api api )
 {
 {
-  initialize();
+  if ( rtapi_ )
+    delete rtapi_;
+  rtapi_ = 0;
 
 
-  if (nDevices <= 0) {
-    sprintf(message, "RtAudio: no audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
-  }
+#if defined(__UNIX_JACK__)
+  if ( api == UNIX_JACK )
+    rtapi_ = new RtApiJack();
+#endif
+#if defined(__LINUX_ALSA__)
+  if ( api == LINUX_ALSA )
+    rtapi_ = new RtApiAlsa();
+#endif
+#if defined(__LINUX_PULSE__)
+  if ( api == LINUX_PULSE )
+    rtapi_ = new RtApiPulse();
+#endif
+#if defined(__LINUX_OSS__)
+  if ( api == LINUX_OSS )
+    rtapi_ = new RtApiOss();
+#endif
+#if defined(__WINDOWS_ASIO__)
+  if ( api == WINDOWS_ASIO )
+    rtapi_ = new RtApiAsio();
+#endif
+#if defined(__WINDOWS_WASAPI__)
+  if ( api == WINDOWS_WASAPI )
+    rtapi_ = new RtApiWasapi();
+#endif
+#if defined(__WINDOWS_DS__)
+  if ( api == WINDOWS_DS )
+    rtapi_ = new RtApiDs();
+#endif
+#if defined(__MACOSX_CORE__)
+  if ( api == MACOSX_CORE )
+    rtapi_ = new RtApiCore();
+#endif
+#if defined(__RTAUDIO_DUMMY__)
+  if ( api == RTAUDIO_DUMMY )
+    rtapi_ = new RtApiDummy();
+#endif
+}
 
 
-  try {
-    *streamId = openStream(outputDevice, outputChannels, inputDevice, inputChannels,
-                           format, sampleRate, bufferSize, numberOfBuffers);
+RtAudio :: RtAudio( RtAudio::Api api )
+{
+  rtapi_ = 0;
+
+  if ( api != UNSPECIFIED ) {
+    // Attempt to open the specified API.
+    openRtApi( api );
+    if ( rtapi_ ) return;
+
+    // No compiled support for specified API value.  Issue a debug
+    // warning and continue as if no API was specified.
+    std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
   }
   }
-  catch (RtError &exception) {
-    // deallocate the RTAUDIO_DEVICE structures
-    if (devices) free(devices);
-    throw exception;
+
+  // Iterate through the compiled APIs and return as soon as we find
+  // one with at least one device or we reach the end of the list.
+  std::vector< RtAudio::Api > apis;
+  getCompiledApi( apis );
+  for ( unsigned int i=0; i<apis.size(); i++ ) {
+    openRtApi( apis[i] );
+    if ( rtapi_ && rtapi_->getDeviceCount() ) break;
   }
   }
+
+  if ( rtapi_ ) return;
+
+  // It should not be possible to get here because the preprocessor
+  // definition __RTAUDIO_DUMMY__ is automatically defined if no
+  // API-specific definitions are passed to the compiler. But just in
+  // case something weird happens, we'll thow an error.
+  std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";
+  throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );
 }
 
 RtAudio :: ~RtAudio()
 {
 }
 
 RtAudio :: ~RtAudio()
 {
-  // close any existing streams
-  while ( streams.size() )
-    closeStream( streams.begin()->first );
+  if ( rtapi_ )
+    delete rtapi_;
+}
+
+void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
+                            RtAudio::StreamParameters *inputParameters,
+                            RtAudioFormat format, unsigned int sampleRate,
+                            unsigned int *bufferFrames,
+                            RtAudioCallback callback, void *userData,
+                            RtAudio::StreamOptions *options,
+                            RtAudioErrorCallback errorCallback )
+{
+  return rtapi_->openStream( outputParameters, inputParameters, format,
+                             sampleRate, bufferFrames, callback,
+                             userData, options, errorCallback );
+}
+
+// *************************************************** //
+//
+// Public RtApi definitions (see end of file for
+// private or protected utility functions).
+//
+// *************************************************** //
 
 
-  // deallocate the RTAUDIO_DEVICE structures
-  if (devices) free(devices);
+RtApi :: RtApi()
+{
+  stream_.state = STREAM_CLOSED;
+  stream_.mode = UNINITIALIZED;
+  stream_.apiHandle = 0;
+  stream_.userBuffer[0] = 0;
+  stream_.userBuffer[1] = 0;
+  MUTEX_INITIALIZE( &stream_.mutex );
+  showWarnings_ = true;
+  firstErrorOccurred_ = false;
 }
 
 }
 
-int RtAudio :: openStream(int outputDevice, int outputChannels,
-                          int inputDevice, int inputChannels,
-                          RTAUDIO_FORMAT format, int sampleRate,
-                          int *bufferSize, int numberOfBuffers)
+RtApi :: ~RtApi()
 {
 {
-  static int streamKey = 0; // Unique stream identifier ... OK for multiple instances.
+  MUTEX_DESTROY( &stream_.mutex );
+}
 
 
-  if (outputChannels < 1 && inputChannels < 1) {
-    sprintf(message,"RtAudio: one or both 'channel' parameters must be greater than zero.");
-    error(RtError::INVALID_PARAMETER);
+void RtApi :: openStream( RtAudio::StreamParameters *oParams,
+                          RtAudio::StreamParameters *iParams,
+                          RtAudioFormat format, unsigned int sampleRate,
+                          unsigned int *bufferFrames,
+                          RtAudioCallback callback, void *userData,
+                          RtAudio::StreamOptions *options,
+                          RtAudioErrorCallback errorCallback )
+{
+  if ( stream_.state != STREAM_CLOSED ) {
+    errorText_ = "RtApi::openStream: a stream is already open!";
+    error( RtAudioError::INVALID_USE );
+    return;
   }
 
   }
 
-  if ( formatBytes(format) == 0 ) {
-    sprintf(message,"RtAudio: 'format' parameter value is undefined.");
-    error(RtError::INVALID_PARAMETER);
-  }
+  // Clear stream information potentially left from a previously open stream.
+  clearStreamInfo();
 
 
-  if ( outputChannels > 0 ) {
-    if (outputDevice > nDevices || outputDevice < 0) {
-      sprintf(message,"RtAudio: 'outputDevice' parameter value (%d) is invalid.", outputDevice);
-      error(RtError::INVALID_PARAMETER);
-    }
+  if ( oParams && oParams->nChannels < 1 ) {
+    errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
+    error( RtAudioError::INVALID_USE );
+    return;
   }
 
   }
 
-  if ( inputChannels > 0 ) {
-    if (inputDevice > nDevices || inputDevice < 0) {
-      sprintf(message,"RtAudio: 'inputDevice' parameter value (%d) is invalid.", inputDevice);
-      error(RtError::INVALID_PARAMETER);
-    }
+  if ( iParams && iParams->nChannels < 1 ) {
+    errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
+    error( RtAudioError::INVALID_USE );
+    return;
   }
 
   }
 
-  // Allocate a new stream structure.
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) calloc(1, sizeof(RTAUDIO_STREAM));
-  if (stream == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
+  if ( oParams == NULL && iParams == NULL ) {
+    errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
+    error( RtAudioError::INVALID_USE );
+    return;
   }
   }
-  stream->mode = UNINITIALIZED;
-  MUTEX_INITIALIZE(&stream->mutex);
-
-  bool result = FAILURE;
-  int device, defaultDevice = 0;
-  STREAM_MODE mode;
-  int channels;
-  if ( outputChannels > 0 ) {
 
 
-    mode = OUTPUT;
-    channels = outputChannels;
+  if ( formatBytes(format) == 0 ) {
+    errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
+    error( RtAudioError::INVALID_USE );
+    return;
+  }
 
 
-    if ( outputDevice == 0 ) { // Try default device first.
-      defaultDevice = getDefaultOutputDevice();
-      device = defaultDevice;
+  unsigned int nDevices = getDeviceCount();
+  unsigned int oChannels = 0;
+  if ( oParams ) {
+    oChannels = oParams->nChannels;
+    if ( oParams->deviceId >= nDevices ) {
+      errorText_ = "RtApi::openStream: output device parameter value is invalid.";
+      error( RtAudioError::INVALID_USE );
+      return;
     }
     }
-    else
-      device = outputDevice - 1;
+  }
 
 
-    for (int i=-1; i<nDevices; i++) {
-      if (i >= 0 ) { 
-        if ( i == defaultDevice ) continue;
-        device = i;
-      }
-      if (devices[device].probed == false) {
-        // If the device wasn't successfully probed before, try it
-        // again now.
-        clearDeviceInfo(&devices[device]);
-        probeDeviceInfo(&devices[device]);
-      }
-      if ( devices[device].probed )
-        result = probeDeviceOpen(device, stream, mode, channels, sampleRate,
-                                 format, bufferSize, numberOfBuffers);
-      if (result == SUCCESS) break;
-      if ( outputDevice > 0 ) break;
+  unsigned int iChannels = 0;
+  if ( iParams ) {
+    iChannels = iParams->nChannels;
+    if ( iParams->deviceId >= nDevices ) {
+      errorText_ = "RtApi::openStream: input device parameter value is invalid.";
+      error( RtAudioError::INVALID_USE );
+      return;
     }
   }
 
     }
   }
 
-  if ( inputChannels > 0 && ( result == SUCCESS || outputChannels <= 0 ) ) {
+  bool result;
 
 
-    mode = INPUT;
-    channels = inputChannels;
+  if ( oChannels > 0 ) {
 
 
-    if ( inputDevice == 0 ) { // Try default device first.
-      defaultDevice = getDefaultInputDevice();
-      device = defaultDevice;
+    result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
+                              sampleRate, format, bufferFrames, options );
+    if ( result == false ) {
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
     }
     }
-    else
-      device = inputDevice - 1;
+  }
 
 
-    for (int i=-1; i<nDevices; i++) {
-      if (i >= 0 ) { 
-        if ( i == defaultDevice ) continue;
-        device = i;
-      }
-      if (devices[device].probed == false) {
-        // If the device wasn't successfully probed before, try it
-        // again now.
-        clearDeviceInfo(&devices[device]);
-        probeDeviceInfo(&devices[device]);
-      }
-      if ( devices[device].probed )
-        result = probeDeviceOpen(device, stream, mode, channels, sampleRate,
-                                 format, bufferSize, numberOfBuffers);
-      if (result == SUCCESS) break;
-      if ( outputDevice > 0 ) break;
+  if ( iChannels > 0 ) {
+
+    result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
+                              sampleRate, format, bufferFrames, options );
+    if ( result == false ) {
+      if ( oChannels > 0 ) closeStream();
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
     }
   }
 
     }
   }
 
-  streams[++streamKey] = (void *) stream;
-  if ( result == SUCCESS )
-    return streamKey;
-
-  // If we get here, all attempted probes failed.  Close any opened
-  // devices and delete the allocated stream.
-  closeStream(streamKey);
-  if ( ( outputDevice == 0 && outputChannels > 0 )
-       || ( inputDevice == 0 && inputChannels > 0 ) )
-    sprintf(message,"RtAudio: no devices found for given parameters.");
-  else
-    sprintf(message,"RtAudio: unable to open specified device(s) with given stream parameters.");
-  error(RtError::INVALID_PARAMETER);
+  stream_.callbackInfo.callback = (void *) callback;
+  stream_.callbackInfo.userData = userData;
+  stream_.callbackInfo.errorCallback = (void *) errorCallback;
 
 
-  return -1;
+  if ( options ) options->numberOfBuffers = stream_.nBuffers;
+  stream_.state = STREAM_STOPPED;
 }
 
 }
 
-int RtAudio :: getDeviceCount(void)
+unsigned int RtApi :: getDefaultInputDevice( void )
 {
 {
-  return nDevices;
+  // Should be implemented in subclasses if possible.
+  return 0;
 }
 
 }
 
-void RtAudio :: getDeviceInfo(int device, RTAUDIO_DEVICE *info)
+unsigned int RtApi :: getDefaultOutputDevice( void )
 {
 {
-  if (device > nDevices || device < 1) {
-    sprintf(message, "RtAudio: invalid device specifier (%d)!", device);
-    error(RtError::INVALID_DEVICE);
-  }
-
-  int deviceIndex = device - 1;
-
-  // If the device wasn't successfully probed before, try it now (or again).
-  if (devices[deviceIndex].probed == false) {
-    clearDeviceInfo(&devices[deviceIndex]);
-    probeDeviceInfo(&devices[deviceIndex]);
-  }
-
-  // Clear the info structure.
-  memset(info, 0, sizeof(RTAUDIO_DEVICE));
-
-  strncpy(info->name, devices[deviceIndex].name, 128);
-  info->probed = devices[deviceIndex].probed;
-  if ( info->probed == true ) {
-    info->maxOutputChannels = devices[deviceIndex].maxOutputChannels;
-    info->maxInputChannels = devices[deviceIndex].maxInputChannels;
-    info->maxDuplexChannels = devices[deviceIndex].maxDuplexChannels;
-    info->minOutputChannels = devices[deviceIndex].minOutputChannels;
-    info->minInputChannels = devices[deviceIndex].minInputChannels;
-    info->minDuplexChannels = devices[deviceIndex].minDuplexChannels;
-    info->hasDuplexSupport = devices[deviceIndex].hasDuplexSupport;
-    info->nSampleRates = devices[deviceIndex].nSampleRates;
-    if (info->nSampleRates == -1) {
-      info->sampleRates[0] = devices[deviceIndex].sampleRates[0];
-      info->sampleRates[1] = devices[deviceIndex].sampleRates[1];
-    }
-    else {
-      for (int i=0; i<info->nSampleRates; i++)
-        info->sampleRates[i] = devices[deviceIndex].sampleRates[i];
-    }
-    info->nativeFormats = devices[deviceIndex].nativeFormats;
-    if ( deviceIndex == getDefaultOutputDevice() ||
-         deviceIndex == getDefaultInputDevice() )
-      info->isDefault = true;
-  }
+  // Should be implemented in subclasses if possible.
+  return 0;
+}
 
 
+void RtApi :: closeStream( void )
+{
+  // MUST be implemented in subclasses!
   return;
 }
 
   return;
 }
 
-char * const RtAudio :: getStreamBuffer(int streamId)
+bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
+                               unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
+                               RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
+                               RtAudio::StreamOptions * /*options*/ )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  return stream->userBuffer;
+  // MUST be implemented in subclasses!
+  return FAILURE;
 }
 
 }
 
-#if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) || defined(__IRIX_AL__)
-
-extern "C" void *callbackHandler(void * ptr);
-
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
+void RtApi :: tickStreamTime( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // Subclasses that do not provide their own implementation of
+  // getStreamTime should call this function once per buffer I/O to
+  // provide basic stream time support.
 
 
-  CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message, "RtAudio: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
-  }
+  stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
+
+#if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+#endif
+}
 
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-  info->streamId = streamId;
+long RtApi :: getStreamLatency( void )
+{
+  verifyStream();
 
 
-  int err = pthread_create(&info->thread, NULL, callbackHandler, &stream->callbackInfo);
+  long totalLatency = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+    totalLatency = stream_.latency[0];
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
+    totalLatency += stream_.latency[1];
 
 
-  if (err) {
-    info->usingCallback = false;
-    sprintf(message, "RtAudio: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
-  }
+  return totalLatency;
 }
 
 }
 
-void RtAudio :: cancelStreamCallback(int streamId)
+double RtApi :: getStreamTime( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->callbackInfo.usingCallback) {
+  verifyStream();
+
+#if defined( HAVE_GETTIMEOFDAY )
+  // Return a very accurate estimate of the stream time by
+  // adding in the elapsed time since the last tick.
+  struct timeval then;
+  struct timeval now;
+
+  if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
+    return stream_.streamTime;
+
+  gettimeofday( &now, NULL );
+  then = stream_.lastTickTimestamp;
+  return stream_.streamTime +
+    ((now.tv_sec + 0.000001 * now.tv_usec) -
+     (then.tv_sec + 0.000001 * then.tv_usec));     
+#else
+  return stream_.streamTime;
+#endif
+}
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+void RtApi :: setStreamTime( double time )
+{
+  verifyStream();
 
 
-    MUTEX_LOCK(&stream->mutex);
+  if ( time >= 0.0 )
+    stream_.streamTime = time;
+#if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+#endif
+}
 
 
-    stream->callbackInfo.usingCallback = false;
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-    stream->callbackInfo.thread = 0;
-    stream->callbackInfo.callback = NULL;
-    stream->callbackInfo.userData = NULL;
+unsigned int RtApi :: getStreamSampleRate( void )
+{
+ verifyStream();
 
 
-    MUTEX_UNLOCK(&stream->mutex);
-  }
+ return stream_.sampleRate;
 }
 
 }
 
-#endif
 
 // *************************************************** //
 //
 
 // *************************************************** //
 //
@@ -362,6644 +453,9632 @@ void RtAudio :: cancelStreamCallback(int streamId)
 // procedure for each of its audio devices.  A single RtAudio duplex
 // stream using two different devices is supported here, though it
 // cannot be guaranteed to always behave correctly because we cannot
 // procedure for each of its audio devices.  A single RtAudio duplex
 // stream using two different devices is supported here, though it
 // cannot be guaranteed to always behave correctly because we cannot
-// synchronize these two callbacks.  This same functionality can be
-// achieved with better synchrony by opening two separate streams for
-// the devices and using RtAudio blocking calls (i.e. tickStream()).
-//
-// The possibility of having multiple RtAudio streams accessing the
-// same CoreAudio device is not currently supported.  The problem
-// involves the inability to install our callbackHandler function for
-// the same device more than once.  I experimented with a workaround
-// for this, but it requires an additional buffer for mixing output
-// data before filling the CoreAudio device buffer.  In the end, I
-// decided it wasn't worth supporting.
+// synchronize these two callbacks.
 //
 //
-// Property listeners are currently not used.  The issue is what could
+// A property listener is installed for over/underrun information.
+// However, no functionality is currently provided to allow property
+// listeners to trigger user handlers because it is unclear what could
 // be done if a critical stream parameter (buffer size, sample rate,
 // device disconnect) notification arrived.  The listeners entail
 // quite a bit of extra code and most likely, a user program wouldn't
 // be done if a critical stream parameter (buffer size, sample rate,
 // device disconnect) notification arrived.  The listeners entail
 // quite a bit of extra code and most likely, a user program wouldn't
-// be prepared for the result anyway.  Some initial listener code is
-// commented out.
+// be prepared for the result anyway.  However, we do provide a flag
+// to the client callback function to inform of an over/underrun.
+
+// A structure to hold various information related to the CoreAudio API
+// implementation.
+struct CoreHandle {
+  AudioDeviceID id[2];    // device ids
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+  AudioDeviceIOProcID procId[2];
+#endif
+  UInt32 iStream[2];      // device stream index (or first if using multiple)
+  UInt32 nStreams[2];     // number of streams to use
+  bool xrun[2];
+  char *deviceBuffer;
+  pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+
+  CoreHandle()
+    :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
 
-void RtAudio :: initialize(void)
+RtApiCore:: RtApiCore()
 {
 {
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  AudioDeviceID        *deviceList = NULL;
-  nDevices = 0;
-
-  // Find out how many audio devices there are, if any.
-  err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &dataSize, NULL);
-  if (err != noErr) {
-    sprintf(message, "RtAudio: OSX error getting device info!");
-    error(RtError::SYSTEM_ERROR);
-  }
-
-  nDevices = dataSize / sizeof(AudioDeviceID);
-  if (nDevices == 0) return;
-
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
-
-  // Make space for the devices we are about to get.
-  deviceList = (AudioDeviceID  *) malloc( dataSize );
-  if (deviceList == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
+#if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )
+  // This is a largely undocumented but absolutely necessary
+  // requirement starting with OS-X 10.6.  If not called, queries and
+  // updates to various audio device properties are not handled
+  // correctly.
+  CFRunLoopRef theRunLoop = NULL;
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
+    error( RtAudioError::WARNING );
   }
   }
+#endif
+}
 
 
-  // Get the array of AudioDeviceIDs.
-  err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &dataSize, (void *) deviceList);
-  if (err != noErr) {
-    free(deviceList);
-    sprintf(message, "RtAudio: OSX error getting device properties!");
-    error(RtError::SYSTEM_ERROR);
-  }
+RtApiCore :: ~RtApiCore()
+{
+  // The subclass destructor gets called before the base class
+  // destructor, so close an existing stream before deallocating
+  // apiDeviceId memory.
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+}
 
 
-  // Write device identifiers to device structures and then
-  // probe the device capabilities.
-  for (int i=0; i<nDevices; i++) {
-    devices[i].id[0] = deviceList[i];
-    //probeDeviceInfo(&devices[i]);
+unsigned int RtApiCore :: getDeviceCount( void )
+{
+  // Find out how many audio devices there are, if any.
+  UInt32 dataSize;
+  AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
+    error( RtAudioError::WARNING );
+    return 0;
   }
 
   }
 
-  free(deviceList);
+  return dataSize / sizeof( AudioDeviceID );
 }
 
 }
 
-int RtAudio :: getDefaultInputDevice(void)
+unsigned int RtApiCore :: getDefaultInputDevice( void )
 {
 {
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices <= 1 ) return 0;
+
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
-
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
-                                              &dataSize, &id );
-
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default input device." );
-    error(RtError::WARNING);
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
+    error( RtAudioError::WARNING );
     return 0;
   }
 
     return 0;
   }
 
-  for ( int i=0; i<nDevices; i++ ) {
-    if ( id == devices[i].id[0] ) return i;
+  dataSize *= nDevices;
+  AudioDeviceID deviceList[ nDevices ];
+  property.mSelector = kAudioHardwarePropertyDevices;
+  result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
+    error( RtAudioError::WARNING );
+    return 0;
   }
 
   }
 
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
+
+  errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
+  error( RtAudioError::WARNING );
   return 0;
 }
 
   return 0;
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+unsigned int RtApiCore :: getDefaultOutputDevice( void )
 {
 {
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices <= 1 ) return 0;
+
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
-
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
-                                              &dataSize, &id );
-
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default output device." );
-    error(RtError::WARNING);
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
+    error( RtAudioError::WARNING );
     return 0;
   }
 
     return 0;
   }
 
-  for ( int i=0; i<nDevices; i++ ) {
-    if ( id == devices[i].id[0] ) return i;
+  dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioDeviceID deviceList[ nDevices ];
+  property.mSelector = kAudioHardwarePropertyDevices;
+  result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
+    error( RtAudioError::WARNING );
+    return 0;
   }
 
   }
 
-  return 0;
-}
-
-static bool deviceSupportsFormat( AudioDeviceID id, bool isInput,
-                                  AudioStreamBasicDescription  *desc, bool isDuplex )
-{
-  OSStatus result = noErr;
-  UInt32 dataSize = sizeof( AudioStreamBasicDescription );
-
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreamFormatSupported,
-                                   &dataSize, desc );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
 
 
-  if (result == kAudioHardwareNoError) {
-    if ( isDuplex ) {
-      result = AudioDeviceGetProperty( id, 0, true,
-                                       kAudioDevicePropertyStreamFormatSupported,
-                                       &dataSize, desc );
-
-
-      if (result != kAudioHardwareNoError)
-        return false;
-    }
-    return true;
-  }
-
-  return false;
+  errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
+  error( RtAudioError::WARNING );
+  return 0;
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 {
 {
-  OSStatus err = noErr;
-
-  // Get the device manufacturer and name.
-  char name[256];
-  char fullname[512];
-  UInt32 dataSize = 256;
-  err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                kAudioDevicePropertyDeviceManufacturer,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting device manufacturer." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncpy(fullname, name, 256);
-  strcat(fullname, ": " );
-
-  dataSize = 256;
-  err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                kAudioDevicePropertyDeviceName,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting device name." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncat(fullname, name, 254);
-  strncat(info->name, fullname, 128);
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
+
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
+
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
+                                                0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  AudioDeviceID id = deviceList[ device ];
+
+  // Get the device name.
+  info.name.erase();
+  CFStringRef cfname;
+  dataSize = sizeof( CFStringRef );
+  property.mSelector = kAudioObjectPropertyManufacturer;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
+  int length = CFStringGetLength(cfname);
+  char *mname = (char *)malloc(length * 3 + 1);
+#if defined( UNICODE ) || defined( _UNICODE )
+  CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);
+#else
+  CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());
+#endif
+  info.name.append( (const char *)mname, strlen(mname) );
+  info.name.append( ": " );
+  CFRelease( cfname );
+  free(mname);
+
+  property.mSelector = kAudioObjectPropertyName;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
+  length = CFStringGetLength(cfname);
+  char *name = (char *)malloc(length * 3 + 1);
+#if defined( UNICODE ) || defined( _UNICODE )
+  CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);
+#else
+  CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());
+#endif
+  info.name.append( (const char *)name, strlen(name) );
+  CFRelease( cfname );
+  free(name);
 
 
-  // Get output channel information.
-  unsigned int i, minChannels, maxChannels, nStreams = 0;
+  // Get the output stream "configuration".
   AudioBufferList      *bufferList = nil;
   AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( info->id[0], 0, false,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message, "RtAudio: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return;
-    }
-
-    err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-    if (err == noErr) {
-      maxChannels = 0;
-      minChannels = 1000;
-      nStreams = bufferList->mNumberBuffers;
-      for ( i=0; i<nStreams; i++ ) {
-        maxChannels += bufferList->mBuffers[i].mNumberChannels;
-        if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
-          minChannels = bufferList->mBuffers[i].mNumberChannels;
-      }
-    }
-  }
-  if (err != noErr || dataSize <= 0) {
-    sprintf( message, "RtAudio: OSX error getting output channels for device (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
+  property.mSelector = kAudioDevicePropertyStreamConfiguration;
+  property.mScope = kAudioDevicePropertyScopeOutput;
+  //  property.mElement = kAudioObjectPropertyElementWildcard;
+  dataSize = 0;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if ( result != noErr || dataSize == 0 ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   }
 
-  free (bufferList);
-  if ( nStreams ) {
-    if ( maxChannels > 0 )
-      info->maxOutputChannels = maxChannels;
-    if ( minChannels > 0 )
-      info->minOutputChannels = minChannels;
+  // Get output channel information.
+  unsigned int i, nStreams = bufferList->mNumberBuffers;
+  for ( i=0; i<nStreams; i++ )
+    info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
+  free( bufferList );
+
+  // Get the input stream "configuration".
+  property.mScope = kAudioDevicePropertyScopeInput;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if (result != noErr || dataSize == 0) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   // Get input channel information.
   }
 
   // Get input channel information.
-  bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( info->id[0], 0, true,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message, "RtAudio: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return;
-    }
-    err = AudioDeviceGetProperty( info->id[0], 0, true,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-    if (err == noErr) {
-      maxChannels = 0;
-      minChannels = 1000;
-      nStreams = bufferList->mNumberBuffers;
-      for ( i=0; i<nStreams; i++ ) {
-        if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
-          minChannels = bufferList->mBuffers[i].mNumberChannels;
-        maxChannels += bufferList->mBuffers[i].mNumberChannels;
-      }
-    }
-  }
-  if (err != noErr || dataSize <= 0) {
-    sprintf( message, "RtAudio: OSX error getting input channels for device (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  free (bufferList);
-  if ( nStreams ) {
-    if ( maxChannels > 0 )
-      info->maxInputChannels = maxChannels;
-    if ( minChannels > 0 )
-      info->minInputChannels = minChannels;
-  }
+  nStreams = bufferList->mNumberBuffers;
+  for ( i=0; i<nStreams; i++ )
+    info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
+  free( bufferList );
 
   // If device opens for both playback and capture, we determine the channels.
 
   // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
-    info->hasDuplexSupport = true;
-    info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-      info->maxInputChannels : info->maxOutputChannels;
-    info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-      info->minInputChannels : info->minOutputChannels;
-  }
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  // Probe the device sample rate and data format parameters.  The
-  // core audio query mechanism is performed on a "stream"
-  // description, which can have a variable number of channels and
-  // apply to input or output only.
-
-  // Create a stream description structure.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  memset(&description, 0, sizeof(AudioStreamBasicDescription));
+  // Probe the device sample rates.
   bool isInput = false;
   bool isInput = false;
-  if ( info->maxOutputChannels == 0 ) isInput = true;
-  bool isDuplex = false;
-  if ( info->maxDuplexChannels > 0 ) isDuplex = true;
+  if ( info.outputChannels == 0 ) isInput = true;
 
   // Determine the supported sample rates.
 
   // Determine the supported sample rates.
-  info->nSampleRates = 0;
-  for (i=0; i<MAX_SAMPLE_RATES; i++) {
-    description.mSampleRate = (double) SAMPLE_RATES[i];
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i];
-  }
-
-  if (info->nSampleRates == 0) {
-    sprintf( message, "RtAudio: No supported sample rates found for OSX device (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Check for continuous sample rate support.
-  description.mSampleRate = kAudioStreamAnyRate;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) {
-    info->sampleRates[1] = info->sampleRates[info->nSampleRates-1];
-    info->nSampleRates = -1;
-  }
-
-  // Determine the supported data formats.
-  info->nativeFormats = 0;
-  description.mFormatID = kAudioFormatLinearPCM;
-  description.mBitsPerChannel = 8;
-  description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT8;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT8;
-  }
-
-  description.mBitsPerChannel = 16;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT16;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT16;
-  }
-
-  description.mBitsPerChannel = 32;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT32;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT32;
+  property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
+  if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != kAudioHardwareNoError || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  UInt32 nRanges = dataSize / sizeof( AudioValueRange );
+  AudioValueRange rangeList[ nRanges ];
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );
+  if ( result != kAudioHardwareNoError ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // The sample rate reporting mechanism is a bit of a mystery.  It
+  // seems that it can either return individual rates or a range of
+  // rates.  I assume that if the min / max range values are the same,
+  // then that represents a single supported rate and if the min / max
+  // range values are different, the device supports an arbitrary
+  // range of values (though there might be multiple ranges, so we'll
+  // use the most conservative range).
+  Float64 minimumRate = 1.0, maximumRate = 10000000000.0;
+  bool haveValueRange = false;
+  info.sampleRates.clear();
+  for ( UInt32 i=0; i<nRanges; i++ ) {
+    if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {
+      unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;
+      info.sampleRates.push_back( tmpSr );
+
+      if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )
+        info.preferredSampleRate = tmpSr;
+
+    } else {
+      haveValueRange = true;
+      if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;
+      if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;
+    }
+  }
+
+  if ( haveValueRange ) {
+    for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+      if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {
+        info.sampleRates.push_back( SAMPLE_RATES[k] );
+
+        if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
+          info.preferredSampleRate = SAMPLE_RATES[k];
+      }
+    }
   }
 
   }
 
-  description.mBitsPerChannel = 24;
-  description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT24;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT24;
-  }
+  // Sort and remove any redundant values
+  std::sort( info.sampleRates.begin(), info.sampleRates.end() );
+  info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );
 
 
-  description.mBitsPerChannel = 32;
-  description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_FLOAT32;
+  if ( info.sampleRates.size() == 0 ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   }
 
-  description.mBitsPerChannel = 64;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_FLOAT64;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_FLOAT64;
-  }
+  // CoreAudio always uses 32-bit floating point data for PCM streams.
+  // Thus, any other "physical" formats supported by the device are of
+  // no interest to the client.
+  info.nativeFormats = RTAUDIO_FLOAT32;
 
 
-  // Check that we have at least one supported format.
-  if (info->nativeFormats == 0) {
-    sprintf(message, "RtAudio: OSX PCM device (%s) data format not supported by RtAudio.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  if ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
 
 
-  info->probed = true;
+  info.probed = true;
+  return info;
 }
 
 }
 
-OSStatus callbackHandler(AudioDeviceID inDevice,
-                         const AudioTimeStamp* inNow,
-                         const AudioBufferList* inInputData,
-                         const AudioTimeStamp* inInputTime,
-                         AudioBufferList* outOutputData,
-                         const AudioTimeStamp* inOutputTime, 
-                         void* infoPointer)
+static OSStatus callbackHandler( AudioDeviceID inDevice,
+                                 const AudioTimeStamp* /*inNow*/,
+                                 const AudioBufferList* inInputData,
+                                 const AudioTimeStamp* /*inInputTime*/,
+                                 AudioBufferList* outOutputData,
+                                 const AudioTimeStamp* /*inOutputTime*/,
+                                 void* infoPointer )
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
 
 
-  RtAudio *object = (RtAudio *) info->object;
-  try {
-    object->callbackEvent( info->streamId, inDevice, (void *)inInputData, (void *)outOutputData );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage());
+  RtApiCore *object = (RtApiCore *) info->object;
+  if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
     return kAudioHardwareUnspecifiedError;
     return kAudioHardwareUnspecifiedError;
+  else
+    return kAudioHardwareNoError;
+}
+
+static OSStatus xrunListener( AudioObjectID /*inDevice*/,
+                              UInt32 nAddresses,
+                              const AudioObjectPropertyAddress properties[],
+                              void* handlePointer )
+{
+  CoreHandle *handle = (CoreHandle *) handlePointer;
+  for ( UInt32 i=0; i<nAddresses; i++ ) {
+    if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {
+      if ( properties[i].mScope == kAudioDevicePropertyScopeInput )
+        handle->xrun[1] = true;
+      else
+        handle->xrun[0] = true;
+    }
   }
 
   return kAudioHardwareNoError;
 }
 
   }
 
   return kAudioHardwareNoError;
 }
 
-/*
-OSStatus deviceListener(AudioDeviceID inDevice,
-                        UInt32 channel,
-                        Boolean isInput,
-                        AudioDevicePropertyID propertyID,
-                        void* infoPointer)
+static OSStatus rateListener( AudioObjectID inDevice,
+                              UInt32 /*nAddresses*/,
+                              const AudioObjectPropertyAddress /*properties*/[],
+                              void* ratePointer )
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
+  Float64 *rate = (Float64 *) ratePointer;
+  UInt32 dataSize = sizeof( Float64 );
+  AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );
+  return kAudioHardwareNoError;
+}
 
 
-  RtAudio *object = (RtAudio *) info->object;
-  try {
-    object->settingChange( info->streamId );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nDevice listener error (%s)!\n\n", exception.getMessage());
-    return kAudioHardwareUnspecifiedError;
+bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+{
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
+    return FAILURE;
   }
 
   }
 
-  return kAudioHardwareNoError;
-}
-*/
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
+  }
 
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  // Check to make sure we don't already have a stream accessing this device.
-  RTAUDIO_STREAM *streamPtr;
-  std::map<int, void *>::const_iterator i;
-  for ( i=streams.begin(); i!=streams.end(); ++i ) {
-    streamPtr = (RTAUDIO_STREAM *) i->second;
-    if ( streamPtr->device[0] == device || streamPtr->device[1] == device ) {
-      sprintf(message, "RtAudio: no current OS X support for multiple streams accessing the same device!");
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
+                                                0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
+    return FAILURE;
   }
 
   }
 
+  AudioDeviceID id = deviceList[ device ];
+
   // Setup for stream mode.
   bool isInput = false;
   // Setup for stream mode.
   bool isInput = false;
-  AudioDeviceID id = devices[device].id[0];
-  if ( mode == INPUT ) isInput = true;
+  if ( mode == INPUT ) {
+    isInput = true;
+    property.mScope = kAudioDevicePropertyScopeInput;
+  }
+  else
+    property.mScope = kAudioDevicePropertyScopeOutput;
 
 
-  // Search for a stream which contains the desired number of channels.
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  unsigned int deviceChannels, nStreams;
-  UInt32 iChannel = 0, iStream = 0;
+  // Get the stream "configuration".
   AudioBufferList      *bufferList = nil;
   AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( id, 0, isInput,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message, "RtAudio: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-    err = AudioDeviceGetProperty( id, 0, isInput,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-
-    if (err == noErr) {
-      stream->deInterleave[mode] = false;
-      nStreams = bufferList->mNumberBuffers;
-      for ( iStream=0; iStream<nStreams; iStream++ ) {
-        if ( bufferList->mBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break;
-        iChannel += bufferList->mBuffers[iStream].mNumberChannels;
-      }
-      // If we didn't find a single stream above, see if we can meet
-      // the channel specification in mono mode (i.e. using separate
-      // non-interleaved buffers).  This can only work if there are N
-      // consecutive one-channel streams, where N is the number of
-      // desired channels.
-      iChannel = 0;
-      if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) {
-        int counter = 0;
-        for ( iStream=0; iStream<nStreams; iStream++ ) {
-          if ( bufferList->mBuffers[iStream].mNumberChannels == 1 )
-            counter++;
-          else
-            counter = 0;
-          if ( counter == channels ) {
-            iStream -= channels - 1;
-            iChannel -= channels - 1;
-            stream->deInterleave[mode] = true;
-            break;
-          }
-          iChannel += bufferList->mBuffers[iStream].mNumberChannels;
-        }
-      }
-    }
-  }
-  if (err != noErr || dataSize <= 0) {
-    if ( bufferList ) free( bufferList );
-    sprintf( message, "RtAudio: OSX error getting channels for device (%s).", devices[device].name );
-    error(RtError::DEBUG_WARNING);
+  dataSize = 0;
+  property.mSelector = kAudioDevicePropertyStreamConfiguration;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  if (iStream >= nStreams) {
-    free (bufferList);
-    sprintf( message, "RtAudio: unable to find OSX audio stream on device (%s) for requested channels (%d).",
-             devices[device].name, channels );
-    error(RtError::DEBUG_WARNING);
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // This is ok even for mono mode ... it gets updated later.
-  deviceChannels = bufferList->mBuffers[iStream].mNumberChannels;
-  free (bufferList);
-
-  // Determine the buffer size.
-  AudioValueRange      bufferRange;
-  dataSize = sizeof(AudioValueRange);
-  err = AudioDeviceGetProperty( id, 0, isInput,
-                                kAudioDevicePropertyBufferSizeRange,
-                                &dataSize, &bufferRange);
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting buffer size range for device (%s).",
-             devices[device].name );
-    error(RtError::DEBUG_WARNING);
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if (result != noErr || dataSize == 0) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32);
-  if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum;
-  else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum;
-
-  // Set the buffer size.  For mono mode, I'm assuming we only need to
-  // make this setting for the first channel.
-  UInt32 theSize = (UInt32) bufferBytes;
-  dataSize = sizeof( UInt32);
-  err = AudioDeviceSetProperty(id, NULL, 0, isInput,
-                               kAudioDevicePropertyBufferSize,
-                               dataSize, &theSize);
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error setting the buffer size for device (%s).",
-             devices[device].name );
-    error(RtError::DEBUG_WARNING);
+  // Search for one or more streams that contain the desired number of
+  // channels. CoreAudio devices can have an arbitrary number of
+  // streams and each stream can have an arbitrary number of channels.
+  // For each stream, a single buffer of interleaved samples is
+  // provided.  RtAudio prefers the use of one stream of interleaved
+  // data or multiple consecutive single-channel streams.  However, we
+  // now support multiple consecutive multi-channel streams of
+  // interleaved data as well.
+  UInt32 iStream, offsetCounter = firstChannel;
+  UInt32 nStreams = bufferList->mNumberBuffers;
+  bool monoMode = false;
+  bool foundStream = false;
+
+  // First check that the device supports the requested number of
+  // channels.
+  UInt32 deviceChannels = 0;
+  for ( iStream=0; iStream<nStreams; iStream++ )
+    deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;
+
+  if ( deviceChannels < ( channels + firstChannel ) ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // If attempting to setup a duplex stream, the bufferSize parameter
-  // MUST be the same in both directions!
-  *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) );
-  if ( stream->mode == OUTPUT && mode == INPUT && *bufferSize != stream->bufferSize ) {
-    sprintf( message, "RtAudio: OSX error setting buffer size for duplex stream on device (%s).",
-             devices[device].name );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // Look for a single stream meeting our needs.
+  UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
+  for ( iStream=0; iStream<nStreams; iStream++ ) {
+    streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+    if ( streamChannels >= channels + offsetCounter ) {
+      firstStream = iStream;
+      channelOffset = offsetCounter;
+      foundStream = true;
+      break;
+    }
+    if ( streamChannels > offsetCounter ) break;
+    offsetCounter -= streamChannels;
   }
 
   }
 
-  stream->bufferSize = *bufferSize;
-  stream->nBuffers = 1;
+  // If we didn't find a single stream above, then we should be able
+  // to meet the channel specification with multiple streams.
+  if ( foundStream == false ) {
+    monoMode = true;
+    offsetCounter = firstChannel;
+    for ( iStream=0; iStream<nStreams; iStream++ ) {
+      streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+      if ( streamChannels > offsetCounter ) break;
+      offsetCounter -= streamChannels;
+    }
 
 
-  // Set the stream format description.  Do for each channel in mono mode.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  if ( stream->deInterleave[mode] ) nStreams = channels;
-  else nStreams = 1;
-  for ( unsigned int i=0; i<nStreams; i++, iChannel++ ) {
-
-    err = AudioDeviceGetProperty( id, iChannel, isInput,
-                                  kAudioDevicePropertyStreamFormat,
-                                  &dataSize, &description );
-    if (err != noErr) {
-      sprintf( message, "RtAudio: OSX error getting stream format for device (%s).", devices[device].name );
-      error(RtError::DEBUG_WARNING);
+    firstStream = iStream;
+    channelOffset = offsetCounter;
+    Int32 channelCounter = channels + offsetCounter - streamChannels;
+
+    if ( streamChannels > 1 ) monoMode = false;
+    while ( channelCounter > 0 ) {
+      streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
+      if ( streamChannels > 1 ) monoMode = false;
+      channelCounter -= streamChannels;
+      streamCount++;
+    }
+  }
+
+  free( bufferList );
+
+  // Determine the buffer size.
+  AudioValueRange      bufferRange;
+  dataSize = sizeof( AudioValueRange );
+  property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+  else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+
+  // Set the buffer size.  For multiple streams, I'm assuming we only
+  // need to make this setting for the master channel.
+  UInt32 theSize = (UInt32) *bufferSize;
+  dataSize = sizeof( UInt32 );
+  property.mSelector = kAudioDevicePropertyBufferFrameSize;
+  result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // If attempting to setup a duplex stream, the bufferSize parameter
+  // MUST be the same in both directions!
+  *bufferSize = theSize;
+  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 1;
+
+  // Try to set "hog" mode ... it's not clear to me this is working.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {
+    pid_t hog_pid;
+    dataSize = sizeof( hog_pid );
+    property.mSelector = kAudioDevicePropertyHogMode;
+    result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    if ( hog_pid != getpid() ) {
+      hog_pid = getpid();
+      result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
+  }
+
+  // Check and if necessary, change the sample rate for the device.
+  Float64 nominalRate;
+  dataSize = sizeof( Float64 );
+  property.mSelector = kAudioDevicePropertyNominalSampleRate;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Only change the sample rate if off by more than 1 Hz.
+  if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {
+
+    // Set a property listener for the sample rate change
+    Float64 reportedRate = 0.0;
+    AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+    result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    nominalRate = (Float64) sampleRate;
+    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );
+    if ( result != noErr ) {
+      AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    // Now wait until the reported nominal rate is what we just set.
+    UInt32 microCounter = 0;
+    while ( reportedRate != nominalRate ) {
+      microCounter += 5000;
+      if ( microCounter > 5000000 ) break;
+      usleep( 5000 );
+    }
+
+    // Remove the property listener.
+    AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
+
+    if ( microCounter > 5000000 ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
       return FAILURE;
     }
+  }
+
+  // Now set the stream format for all streams.  Also, check the
+  // physical format of the device and change that if necessary.
+  AudioStreamBasicDescription  description;
+  dataSize = sizeof( AudioStreamBasicDescription );
+  property.mSelector = kAudioStreamPropertyVirtualFormat;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Set the sample rate and data format id.  However, only make the
+  // change if the sample rate is not within 1.0 of the desired
+  // rate and the format is not linear pcm.
+  bool updateFormat = false;
+  if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {
+    description.mSampleRate = (Float64) sampleRate;
+    updateFormat = true;
+  }
 
 
-    // Set the sample rate and data format id.
-    description.mSampleRate = (double) sampleRate;
+  if ( description.mFormatID != kAudioFormatLinearPCM ) {
     description.mFormatID = kAudioFormatLinearPCM;
     description.mFormatID = kAudioFormatLinearPCM;
-    err = AudioDeviceSetProperty( id, NULL, iChannel, isInput,
-                                  kAudioDevicePropertyStreamFormat,
-                                  dataSize, &description );
-    if (err != noErr) {
-      sprintf( message, "RtAudio: OSX error setting sample rate or data format for device (%s).", devices[device].name );
-      error(RtError::DEBUG_WARNING);
+    updateFormat = true;
+  }
+
+  if ( updateFormat ) {
+    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
   }
 
       return FAILURE;
     }
   }
 
-  // Check whether we need byte-swapping (assuming OS X host is big-endian).
-  iChannel -= nStreams;
-  err = AudioDeviceGetProperty( id, iChannel, isInput,
-                                kAudioDevicePropertyStreamFormat,
-                                &dataSize, &description );
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting stream format for device (%s).", devices[device].name );
-    error(RtError::DEBUG_WARNING);
+  // Now check the physical format.
+  property.mSelector = kAudioStreamPropertyPhysicalFormat;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  stream->doByteSwap[mode] = false;
-  if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian )
-    stream->doByteSwap[mode] = true;
+  //std::cout << "Current physical stream format:" << std::endl;
+  //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;
+  //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
+  //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;
+  //std::cout << "   sample rate = " << description.mSampleRate << std::endl;
+
+  if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {
+    description.mFormatID = kAudioFormatLinearPCM;
+    //description.mSampleRate = (Float64) sampleRate;
+    AudioStreamBasicDescription        testDescription = description;
+    UInt32 formatFlags;
+
+    // We'll try higher bit rates first and then work our way down.
+    std::vector< std::pair<UInt32, UInt32>  > physicalFormats;
+    formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
+    formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed
+    formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low
+    formatFlags |= kAudioFormatFlagIsAlignedHigh;
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high
+    formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );
+
+    bool setPhysicalFormat = false;
+    for( unsigned int i=0; i<physicalFormats.size(); i++ ) {
+      testDescription = description;
+      testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;
+      testDescription.mFormatFlags = physicalFormats[i].second;
+      if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )
+        testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;
+      else
+        testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );
+      if ( result == noErr ) {
+        setPhysicalFormat = true;
+        //std::cout << "Updated physical stream format:" << std::endl;
+        //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;
+        //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
+        //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;
+        //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;
+        break;
+      }
+    }
+
+    if ( !setPhysicalFormat ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+  } // done setting virtual/physical formats.
+
+  // Get the stream / device latency.
+  UInt32 latency;
+  dataSize = sizeof( UInt32 );
+  property.mSelector = kAudioDevicePropertyLatency;
+  if ( AudioObjectHasProperty( id, &property ) == true ) {
+    result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );
+    if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
+    else {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      error( RtAudioError::WARNING );
+    }
+  }
+
+  // Byte-swapping: According to AudioHardware.h, the stream data will
+  // always be presented in native-endian format, so we should never
+  // need to byte swap.
+  stream_.doByteSwap[mode] = false;
 
   // From the CoreAudio documentation, PCM data must be supplied as
   // 32-bit floats.
 
   // From the CoreAudio documentation, PCM data must be supplied as
   // 32-bit floats.
-  stream->userFormat = format;
-  stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+
+  if ( streamCount == 1 )
+    stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
+  else // multiple streams
+    stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] = true;
+  if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;
 
 
-  if ( stream->deInterleave[mode] )
-    stream->nDeviceChannels[mode] = channels;
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( streamCount == 1 ) {
+    if ( stream_.nUserChannels[mode] > 1 &&
+         stream_.userInterleaved != stream_.deviceInterleaved[mode] )
+      stream_.doConvertBuffer[mode] = true;
+  }
+  else if ( monoMode && stream_.userInterleaved )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate our CoreHandle structure for the stream.
+  CoreHandle *handle = 0;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new CoreHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
+      goto error;
+    }
+
+    if ( pthread_cond_init( &handle->condition, NULL ) ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
+    stream_.apiHandle = (void *) handle;
+  }
   else
   else
-    stream->nDeviceChannels[mode] = description.mChannelsPerFrame;
-  stream->nUserChannels[mode] = channels;
-
-  // Set handle and flags for buffer conversion.
-  stream->handle[mode] = iStream;
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
-    stream->doConvertBuffer[mode] = true;
+    handle = (CoreHandle *) stream_.apiHandle;
+  handle->iStream[mode] = firstStream;
+  handle->nStreams[mode] = streamCount;
+  handle->id[mode] = id;
 
   // Allocate necessary internal buffers.
 
   // Allocate necessary internal buffers.
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );
+  memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  if ( stream->deInterleave[mode] ) {
+  // If possible, we will make use of the CoreAudio stream buffers as
+  // "device buffers".  However, we can't do this if using multiple
+  // streams.
+  if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
 
 
-    long buffer_bytes;
     bool makeBuffer = true;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
-
-      // If not de-interleaving, we point stream->deviceBuffer to the
-      // OS X supplied device buffer before doing any necessary data
-      // conversions.  This presents a problem if we have a duplex
-      // stream using one device which needs de-interleaving and
-      // another device which doesn't.  So, save a pointer to our own
-      // device buffer in the CALLBACK_INFO structure.
-      stream->callbackInfo.buffers = stream->deviceBuffer;
-    }
-  }
-
-  stream->sampleRate = sampleRate;
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  stream->callbackInfo.object = (void *) this;
-  stream->callbackInfo.waitTime = (unsigned long) (200000.0 * stream->bufferSize / stream->sampleRate);
-  stream->callbackInfo.device[mode] = id;
-  if ( stream->mode == OUTPUT && mode == INPUT && stream->device[0] == device )
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
+    }
+  }
+
+  stream_.sampleRate = sampleRate;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if ( streamCount > 1 ) setConvertInfo( mode, 0 );
+    else setConvertInfo( mode, channelOffset );
+  }
+
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
     // Only one callback procedure per device.
     // Only one callback procedure per device.
-    stream->mode = DUPLEX;
+    stream_.mode = DUPLEX;
   else {
   else {
-    err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream->callbackInfo );
-    if (err != noErr) {
-      sprintf( message, "RtAudio: OSX error setting callback for device (%s).", devices[device].name );
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
+#else
+    // deprecated in favor of AudioDeviceCreateIOProcID()
+    result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
+#endif
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      goto error;
     }
     }
-    if ( stream->mode == OUTPUT && mode == INPUT )
-      stream->mode = DUPLEX;
+    if ( stream_.mode == OUTPUT && mode == INPUT )
+      stream_.mode = DUPLEX;
     else
     else
-      stream->mode = mode;
+      stream_.mode = mode;
   }
 
   }
 
-  // If we wanted to use property listeners, they would be setup here.
+  // Setup the device property listener for over/underload.
+  property.mSelector = kAudioDeviceProcessorOverload;
+  property.mScope = kAudioObjectPropertyScopeGlobal;
+  result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
 
   return SUCCESS;
 
 
   return SUCCESS;
 
- memory_error:
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
   }
-  sprintf(message, "RtAudio: OSX error allocating buffer memory (%s).", devices[device].name);
-  error(RtError::WARNING);
-  return FAILURE;
-}
-
-void RtAudio :: cancelStreamCallback(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
 
 
-  if (stream->callbackInfo.usingCallback) {
-
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
-
-    MUTEX_LOCK(&stream->mutex);
-
-    stream->callbackInfo.usingCallback = false;
-    stream->callbackInfo.userData = NULL;
-    stream->state = STREAM_STOPPED;
-    stream->callbackInfo.callback = NULL;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-    MUTEX_UNLOCK(&stream->mutex);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
+
+  stream_.state = STREAM_CLOSED;
+  return FAILURE;
 }
 
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiCore :: closeStream( void )
 {
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if (handle) {
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster };
 
 
-  AudioDeviceID id;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    id = devices[stream->device[0]].id[0];
-    if (stream->state == STREAM_RUNNING)
-      AudioDeviceStop( id, callbackHandler );
-    AudioDeviceRemoveIOProc( id, callbackHandler );
+      property.mSelector = kAudioDeviceProcessorOverload;
+      property.mScope = kAudioObjectPropertyScopeGlobal;
+      if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";
+        error( RtAudioError::WARNING );
+      }
+    }
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[0], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
+#else
+    // deprecated in favor of AudioDeviceDestroyIOProcID()
+    AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+#endif
   }
 
   }
 
-  if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
-    id = devices[stream->device[1]].id[0];
-    if (stream->state == STREAM_RUNNING)
-      AudioDeviceStop( id, callbackHandler );
-    AudioDeviceRemoveIOProc( id, callbackHandler );
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+    if (handle) {
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+        kAudioObjectPropertyScopeGlobal,
+        kAudioObjectPropertyElementMaster };
+
+      property.mSelector = kAudioDeviceProcessorOverload;
+      property.mScope = kAudioObjectPropertyScopeGlobal;
+      if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";
+        error( RtAudioError::WARNING );
+      }
+    }
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[1], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
+#else
+    // deprecated in favor of AudioDeviceDestroyIOProcID()
+    AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+#endif
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  if ( stream->deInterleave[0] || stream->deInterleave[1] )
-    free(stream->callbackInfo.buffers);
+  // Destroy pthread condition variable.
+  pthread_cond_destroy( &handle->condition );
+  delete handle;
+  stream_.apiHandle = 0;
 
 
-  free(stream);
-  streams.erase(streamId);
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiCore :: startStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiCore::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  OSStatus err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  OSStatus result = noErr;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-    err = AudioDeviceStart(devices[stream->device[0]].id[0], callbackHandler);
-    if (err != noErr) {
-      sprintf(message, "RtAudio: OSX error starting callback procedure on device (%s).",
-              devices[stream->device[0]].name);
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStart( handle->id[0], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
+  if ( stream_.mode == INPUT ||
+       ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
 
 
-    err = AudioDeviceStart(devices[stream->device[1]].id[0], callbackHandler);
-    if (err != noErr) {
-      sprintf(message, "RtAudio: OSX error starting input callback procedure on device (%s).",
-              devices[stream->device[0]].name);
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStart( handle->id[1], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  stream->callbackInfo.streamId = streamId;
-  stream->state = STREAM_RUNNING;
-  stream->callbackInfo.blockTick = true;
-  stream->callbackInfo.stopStream = false;
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( result == noErr ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiCore :: stopStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  OSStatus result = noErr;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  OSStatus err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 2;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
+    }
 
 
-    err = AudioDeviceStop(devices[stream->device[0]].id[0], callbackHandler);
-    if (err != noErr) {
-      sprintf(message, "RtAudio: OSX error stopping callback procedure on device (%s).",
-              devices[stream->device[0]].name);
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStop( handle->id[0], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
 
 
-    err = AudioDeviceStop(devices[stream->device[1]].id[0], callbackHandler);
-    if (err != noErr) {
-      sprintf(message, "RtAudio: OSX error stopping input callback procedure on device (%s).",
-              devices[stream->device[0]].name);
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStop( handle->id[1], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  stream->state = STREAM_STOPPED;
+  stream_.state = STREAM_STOPPED;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( result == noErr ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+void RtApiCore :: abortStream( void )
 {
 {
-  stopStream( streamId );
-}
-
-// I don't know how this function can be implemented.
-int RtAudio :: streamWillBlock(int streamId)
-{
-  sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for OS X.");
-  error(RtError::WARNING);
-  return 0;
-}
-
-void RtAudio :: tickStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->state == STREAM_STOPPED)
-    return;
-
-  if (stream->callbackInfo.usingCallback) {
-    sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    usleep(stream->callbackInfo.waitTime);
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  handle->drainCounter = 2;
 
 
-  MUTEX_LOCK(&stream->mutex);
+  stopStream();
+}
 
 
-  stream->callbackInfo.blockTick = true;
+// This function will be called by a spawned thread when the user
+// callback function signals that the stream should be stopped or
+// aborted.  It is better to handle it this way because the
+// callbackEvent() function probably should return before the AudioDeviceStop()
+// function is called.
+static void *coreStopStream( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiCore *object = (RtApiCore *) info->object;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
+  object->stopStream();
+  pthread_exit( NULL );
 }
 
 }
 
-void RtAudio :: callbackEvent( int streamId, DEVICE_ID deviceId, void *inData, void *outData )
+bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
+                                 const AudioBufferList *inBufferList,
+                                 const AudioBufferList *outBufferList )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
+    return FAILURE;
+  }
 
 
-  CALLBACK_INFO *info;
-  AudioBufferList *inBufferList = (AudioBufferList *) inData;
-  AudioBufferList *outBufferList = (AudioBufferList *) outData;
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
 
 
-  if (stream->state == STREAM_STOPPED) return;
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    ThreadHandle threadId;
 
 
-  info = (CALLBACK_INFO *) &stream->callbackInfo;
-  if ( !info->usingCallback ) {
-    // Block waiting here until we get new user data in tickStream().
-    while ( !info->blockTick )
-      usleep(info->waitTime);
+    stream_.state = STREAM_STOPPING;
+    if ( handle->internalDrain == true )
+      pthread_create( &threadId, NULL, coreStopStream, info );
+    else // external call to stopStream()
+      pthread_cond_signal( &handle->condition );
+    return SUCCESS;
   }
   }
-  else if ( info->stopStream ) {
-    // Check if the stream should be stopped (via the previous user
-    // callback return value).  We stop the stream here, rather than
-    // after the function call, so that output data can first be
-    // processed.
-    this->stopStream(info->streamId);
-    return;
+
+  AudioDeviceID outputDevice = handle->id[0];
+
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream or duplex mode AND the input/output devices are
+  // different AND this function is called for the input device.
+  if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+
+    int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                  stream_.bufferSize, streamTime, status, info->userData );
+    if ( cbReturnValue == 2 ) {
+      stream_.state = STREAM_STOPPING;
+      handle->drainCounter = 2;
+      abortStream();
+      return SUCCESS;
+    }
+    else if ( cbReturnValue == 1 ) {
+      handle->drainCounter = 1;
+      handle->internalDrain = true;
+    }
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
 
 
-  if ( stream->mode == INPUT || ( stream->mode == DUPLEX && deviceId == info->device[1] ) ) {
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-    if (stream->doConvertBuffer[1]) {
+      if ( handle->nStreams[0] == 1 ) {
+        memset( outBufferList->mBuffers[handle->iStream[0]].mData,
+                0,
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else { // fill multiple streams with zeros
+        for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
+          memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  0,
+                  outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
+        }
+      }
+    }
+    else if ( handle->nStreams[0] == 1 ) {
+      if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer
+        convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,
+                       stream_.userBuffer[0], stream_.convertInfo[0] );
+      }
+      else { // copy from user buffer
+        memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
+                stream_.userBuffer[0],
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+    }
+    else { // fill multiple streams
+      Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];
+      if ( stream_.doConvertBuffer[0] ) {
+        convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+        inBuffer = (Float32 *) stream_.deviceBuffer;
+      }
 
 
-      if ( stream->deInterleave[1] ) {
-        stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
-        int bufferBytes = inBufferList->mBuffers[stream->handle[1]].mDataByteSize;
-        for ( int i=0; i<stream->nDeviceChannels[1]; i++ ) {
-          memcpy(&stream->deviceBuffer[i*bufferBytes],
-                 inBufferList->mBuffers[stream->handle[1]+i].mData, bufferBytes );
+      if ( stream_.deviceInterleaved[0] == false ) { // mono mode
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
         }
       }
         }
       }
-      else
-        stream->deviceBuffer = (char *) inBufferList->mBuffers[stream->handle[1]].mData;
+      else { // fill multiple multi-channel streams with interleaved data
+        UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;
+        Float32 *out, *in;
+
+        bool inInterleaved = ( stream_.userInterleaved ) ? true : false;
+        UInt32 inChannels = stream_.nUserChannels[0];
+        if ( stream_.doConvertBuffer[0] ) {
+          inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+          inChannels = stream_.nDeviceChannels[0];
+        }
 
 
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
+        if ( inInterleaved ) inOffset = 1;
+        else inOffset = stream_.bufferSize;
+
+        channelsLeft = inChannels;
+        for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
+          in = inBuffer;
+          out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;
+          streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
+
+          outJump = 0;
+          // Account for possible channel offset in first stream
+          if ( i == 0 && stream_.channelOffset[0] > 0 ) {
+            streamChannels -= stream_.channelOffset[0];
+            outJump = stream_.channelOffset[0];
+            out += outJump;
+          }
 
 
-    }
-    else {
-      memcpy(stream->userBuffer,
-             inBufferList->mBuffers[stream->handle[1]].mData,
-             inBufferList->mBuffers[stream->handle[1]].mDataByteSize );
+          // Account for possible unfilled channels at end of the last stream
+          if ( streamChannels > channelsLeft ) {
+            outJump = streamChannels - channelsLeft;
+            streamChannels = channelsLeft;
+          }
+
+          // Determine input buffer offsets and skips
+          if ( inInterleaved ) {
+            inJump = inChannels;
+            in += inChannels - channelsLeft;
+          }
+          else {
+            inJump = 1;
+            in += (inChannels - channelsLeft) * inOffset;
+          }
 
 
-      if (stream->doByteSwap[1])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[1],
-                       stream->userFormat);
+          for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+            for ( unsigned int j=0; j<streamChannels; j++ ) {
+              *out++ = in[j*inOffset];
+            }
+            out += outJump;
+            in += inJump;
+          }
+          channelsLeft -= streamChannels;
+        }
+      }
     }
   }
 
     }
   }
 
-  // Don't invoke the user callback if duplex mode, the input/output
-  // devices are different, and this function is called for the output
-  // device.
-  if ( info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    info->stopStream = callback(stream->userBuffer, stream->bufferSize, info->userData);
+  // Don't bother draining input
+  if ( handle->drainCounter ) {
+    handle->drainCounter++;
+    goto unlock;
   }
 
   }
 
-  if ( stream->mode == OUTPUT || ( stream->mode == DUPLEX && deviceId == info->device[0] ) ) {
+  AudioDeviceID inputDevice;
+  inputDevice = handle->id[1];
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
 
 
-    if (stream->doConvertBuffer[0]) {
+    if ( handle->nStreams[1] == 1 ) {
+      if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer
+        convertBuffer( stream_.userBuffer[1],
+                       (char *) inBufferList->mBuffers[handle->iStream[1]].mData,
+                       stream_.convertInfo[1] );
+      }
+      else { // copy to user buffer
+        memcpy( stream_.userBuffer[1],
+                inBufferList->mBuffers[handle->iStream[1]].mData,
+                inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+      }
+    }
+    else { // read from multiple streams
+      Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];
+      if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;
 
 
-      if ( !stream->deInterleave[0] )
-        stream->deviceBuffer = (char *) outBufferList->mBuffers[stream->handle[0]].mData;
-      else
-        stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
-
-      convertStreamBuffer(stream, OUTPUT);
-      if ( stream->doByteSwap[0] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[0],
-                       stream->deviceFormat[0]);
-
-      if ( stream->deInterleave[0] ) {
-        int bufferBytes = outBufferList->mBuffers[stream->handle[0]].mDataByteSize;
-        for ( int i=0; i<stream->nDeviceChannels[0]; i++ ) {
-          memcpy(outBufferList->mBuffers[stream->handle[0]+i].mData,
-                 &stream->deviceBuffer[i*bufferBytes], bufferBytes );
+      if ( stream_.deviceInterleaved[1] == false ) { // mono mode
+        UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+          memcpy( (void *)&outBuffer[i*stream_.bufferSize],
+                  inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
         }
       }
         }
       }
+      else { // read from multiple multi-channel streams
+        UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;
+        Float32 *out, *in;
+
+        bool outInterleaved = ( stream_.userInterleaved ) ? true : false;
+        UInt32 outChannels = stream_.nUserChannels[1];
+        if ( stream_.doConvertBuffer[1] ) {
+          outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+          outChannels = stream_.nDeviceChannels[1];
+        }
 
 
-    }
-    else {
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
+        if ( outInterleaved ) outOffset = 1;
+        else outOffset = stream_.bufferSize;
+
+        channelsLeft = outChannels;
+        for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {
+          out = outBuffer;
+          in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;
+          streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
+
+          inJump = 0;
+          // Account for possible channel offset in first stream
+          if ( i == 0 && stream_.channelOffset[1] > 0 ) {
+            streamChannels -= stream_.channelOffset[1];
+            inJump = stream_.channelOffset[1];
+            in += inJump;
+          }
+
+          // Account for possible unread channels at end of the last stream
+          if ( streamChannels > channelsLeft ) {
+            inJump = streamChannels - channelsLeft;
+            streamChannels = channelsLeft;
+          }
+
+          // Determine output buffer offsets and skips
+          if ( outInterleaved ) {
+            outJump = outChannels;
+            out += outChannels - channelsLeft;
+          }
+          else {
+            outJump = 1;
+            out += (outChannels - channelsLeft) * outOffset;
+          }
 
 
-      memcpy(outBufferList->mBuffers[stream->handle[0]].mData,
-             stream->userBuffer,
-             outBufferList->mBuffers[stream->handle[0]].mDataByteSize );
+          for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+            for ( unsigned int j=0; j<streamChannels; j++ ) {
+              out[j*outOffset] = *in++;
+            }
+            out += outJump;
+            in += inJump;
+          }
+          channelsLeft -= streamChannels;
+        }
+      }
+      
+      if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer
+        convertBuffer( stream_.userBuffer[1],
+                       stream_.deviceBuffer,
+                       stream_.convertInfo[1] );
+      }
     }
   }
 
     }
   }
 
-  if ( !info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) )
-    info->blockTick = false;
-
-  MUTEX_UNLOCK(&stream->mutex);
+ unlock:
+  //MUTEX_UNLOCK( &stream_.mutex );
 
 
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
 }
 
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
+const char* RtApiCore :: getErrorCode( OSStatus code )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  switch( code ) {
 
 
-  stream->callbackInfo.callback = (void *) callback;
-  stream->callbackInfo.userData = userData;
-  stream->callbackInfo.usingCallback = true;
-}
+  case kAudioHardwareNotRunningError:
+    return "kAudioHardwareNotRunningError";
 
 
-//******************** End of __MACOSX_CORE__ *********************//
+  case kAudioHardwareUnspecifiedError:
+    return "kAudioHardwareUnspecifiedError";
 
 
-#elif defined(__LINUX_ALSA__)
+  case kAudioHardwareUnknownPropertyError:
+    return "kAudioHardwareUnknownPropertyError";
 
 
-#define MAX_DEVICES 16
+  case kAudioHardwareBadPropertySizeError:
+    return "kAudioHardwareBadPropertySizeError";
 
 
-void RtAudio :: initialize(void)
-{
-  int card, result, device;
-  char name[32];
-  const char *cardId;
-  char deviceNames[MAX_DEVICES][32];
-  snd_ctl_t *handle;
-  snd_ctl_card_info_t *info;
-  snd_ctl_card_info_alloca(&info);
+  case kAudioHardwareIllegalOperationError:
+    return "kAudioHardwareIllegalOperationError";
 
 
-  // Count cards and devices
-  nDevices = 0;
-  card = -1;
-  snd_card_next(&card);
-  while ( card >= 0 ) {
-    sprintf(name, "hw:%d", card);
-    result = snd_ctl_open(&handle, name, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: ALSA control open (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    result = snd_ctl_card_info(handle, info);
-               if (result < 0) {
-      sprintf(message, "RtAudio: ALSA control hardware info (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    cardId = snd_ctl_card_info_get_id(info);
-               device = -1;
-               while (1) {
-      result = snd_ctl_pcm_next_device(handle, &device);
-                       if (result < 0) {
-        sprintf(message, "RtAudio: ALSA control next device (%i): %s.", card, snd_strerror(result));
-        error(RtError::DEBUG_WARNING);
-        break;
-      }
-                       if (device < 0)
-        break;
-      if ( strlen(cardId) )
-        sprintf( deviceNames[nDevices++], "hw:%s,%d", cardId, device );
-      else
-        sprintf( deviceNames[nDevices++], "hw:%d,%d", card, device );
-      if ( nDevices > MAX_DEVICES ) break;
-    }
-    if ( nDevices > MAX_DEVICES ) break;
-  next_card:
-    snd_ctl_close(handle);
-    snd_card_next(&card);
-  }
+  case kAudioHardwareBadObjectError:
+    return "kAudioHardwareBadObjectError";
 
 
-  if (nDevices == 0) return;
+  case kAudioHardwareBadDeviceError:
+    return "kAudioHardwareBadDeviceError";
 
 
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
+  case kAudioHardwareBadStreamError:
+    return "kAudioHardwareBadStreamError";
+
+  case kAudioHardwareUnsupportedOperationError:
+    return "kAudioHardwareUnsupportedOperationError";
 
 
-  // Write device ascii identifiers to device structures and then
-  // probe the device capabilities.
-  for (int i=0; i<nDevices; i++) {
-    strncpy(devices[i].name, deviceNames[i], 32);
-    //probeDeviceInfo(&devices[i]);
+  case kAudioDeviceUnsupportedFormatError:
+    return "kAudioDeviceUnsupportedFormatError";
+
+  case kAudioDevicePermissionsError:
+    return "kAudioDevicePermissionsError";
+
+  default:
+    return "CoreAudio unknown error";
   }
 }
 
   }
 }
 
-int RtAudio :: getDefaultInputDevice(void)
-{
-  // No ALSA API functions for default devices.
-  return 0;
+  //******************** End of __MACOSX_CORE__ *********************//
+#endif
+
+#if defined(__UNIX_JACK__)
+
+// JACK is a low-latency audio server, originally written for the
+// GNU/Linux operating system and now also ported to OS-X. It can
+// connect a number of different applications to an audio device, as
+// well as allowing them to share audio between themselves.
+//
+// When using JACK with RtAudio, "devices" refer to JACK clients that
+// have ports connected to the server.  The JACK server is typically
+// started in a terminal as follows:
+//
+// .jackd -d alsa -d hw:0
+//
+// or through an interface program such as qjackctl.  Many of the
+// parameters normally set for a stream are fixed by the JACK server
+// and can be specified when the JACK server is started.  In
+// particular,
+//
+// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
+//
+// specifies a sample rate of 44100 Hz, a buffer size of 512 sample
+// frames, and number of buffers = 4.  Once the server is running, it
+// is not possible to override these values.  If the values are not
+// specified in the command-line, the JACK server uses default values.
+//
+// The JACK server does not have to be running when an instance of
+// RtApiJack is created, though the function getDeviceCount() will
+// report 0 devices found until JACK has been started.  When no
+// devices are available (i.e., the JACK server is not running), a
+// stream cannot be opened.
+
+#include <jack/jack.h>
+#include <unistd.h>
+#include <cstdio>
+
+// A structure to hold various information related to the Jack API
+// implementation.
+struct JackHandle {
+  jack_client_t *client;
+  jack_port_t **ports[2];
+  std::string deviceName[2];
+  bool xrun[2];
+  pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+
+  JackHandle()
+    :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
+
+#if !defined(__RTAUDIO_DEBUG__)
+static void jackSilentError( const char * ) {};
+#endif
+
+RtApiJack :: RtApiJack()
+    :shouldAutoconnect_(true) {
+  // Nothing to do here.
+#if !defined(__RTAUDIO_DEBUG__)
+  // Turn off Jack's internal error reporting.
+  jack_set_error_function( &jackSilentError );
+#endif
 }
 
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+RtApiJack :: ~RtApiJack()
 {
 {
-  // No ALSA API functions for default devices.
-  return 0;
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+unsigned int RtApiJack :: getDeviceCount( void )
 {
 {
-  int err;
-  int open_mode = SND_PCM_ASYNC;
-  snd_pcm_t *handle;
-  snd_ctl_t *chandle;
-  snd_pcm_stream_t stream;
-       snd_pcm_info_t *pcminfo;
-       snd_pcm_info_alloca(&pcminfo);
-  snd_pcm_hw_params_t *params;
-  snd_pcm_hw_params_alloca(&params);
-  char name[32];
-  char *card;
-
-  // Open the control interface for this card.
-  strncpy( name, info->name, 32 );
-  card = strtok(name, ",");
-  err = snd_ctl_open(&chandle, card, 0);
-  if (err < 0) {
-    sprintf(message, "RtAudio: ALSA control open (%s): %s.", card, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return;
+  // See if we can become a jack client.
+  jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
+  jack_status_t *status = NULL;
+  jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
+  if ( client == 0 ) return 0;
+
+  const char **ports;
+  std::string port, previousPort;
+  unsigned int nChannels = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nChannels ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon + 1 );
+        if ( port != previousPort ) {
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nChannels] );
+    free( ports );
   }
   }
-  unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") );
 
 
-  // First try for playback
-  stream = SND_PCM_STREAM_PLAYBACK;
-  snd_pcm_info_set_device(pcminfo, dev);
-  snd_pcm_info_set_subdevice(pcminfo, 0);
-  snd_pcm_info_set_stream(pcminfo, stream);
+  jack_client_close( client );
+  return nDevices;
+}
 
 
-  if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) {
-    if (err == -ENOENT) {
-      sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle output!", info->name);
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) output: %s",
-              info->name, snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-    }
-    goto capture_probe;
+RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption
+  jack_status_t *status = NULL;
+  jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
+  if ( client == 0 ) {
+    errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  const char **ports;
+  std::string port, previousPort;
+  unsigned int nPorts = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) info.name = port;
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nPorts] );
+    free( ports );
   }
 
   }
 
-  err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK );
-  if (err < 0) {
-    if ( err == EBUSY )
-      sprintf(message, "RtAudio: ALSA pcm playback device (%s) is busy: %s.",
-              info->name, snd_strerror(err));
-    else
-      sprintf(message, "RtAudio: ALSA pcm playback open (%s) error: %s.",
-              info->name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
+  if ( device >= nDevices ) {
+    jack_client_close( client );
+    errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
   }
 
   }
 
-  // We have an open device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",
-            info->name, snd_strerror(err));
-    error(RtError::WARNING);
-    goto capture_probe;
-  }
+  // Get the current jack server sample rate.
+  info.sampleRates.clear();
 
 
-  // Get output channel information.
-  info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params);
-  info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params);
+  info.preferredSampleRate = jack_get_sample_rate( client );
+  info.sampleRates.push_back( info.preferredSampleRate );
 
 
-  snd_pcm_close(handle);
-
- capture_probe:
-  // Now try for capture
-  stream = SND_PCM_STREAM_CAPTURE;
-  snd_pcm_info_set_stream(pcminfo, stream);
-
-  err = snd_ctl_pcm_info(chandle, pcminfo);
-  snd_ctl_close(chandle);
-  if ( err < 0 ) {
-    if (err == -ENOENT) {
-      sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle input!", info->name);
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) input: %s",
-              info->name, snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-    }
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
+  // Count the available ports containing the client name as device
+  // channels.  Jack "input ports" equal RtAudio output channels.
+  unsigned int nChannels = 0;
+  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
+    info.outputChannels = nChannels;
   }
 
   }
 
-  err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK);
-  if (err < 0) {
-    if ( err == EBUSY )
-      sprintf(message, "RtAudio: ALSA pcm capture device (%s) is busy: %s.",
-              info->name, snd_strerror(err));
-    else
-      sprintf(message, "RtAudio: ALSA pcm capture open (%s) error: %s.",
-              info->name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
-  }
-
-  // We have an open capture device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",
-            info->name, snd_strerror(err));
-    error(RtError::WARNING);
-    if (info->maxOutputChannels > 0)
-      goto probe_parameters;
-    else
-      return;
+  // Jack "output ports" equal RtAudio input channels.
+  nChannels = 0;
+  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
+    info.inputChannels = nChannels;
   }
 
   }
 
-  // Get input channel information.
-  info->minInputChannels = snd_pcm_hw_params_get_channels_min(params);
-  info->maxInputChannels = snd_pcm_hw_params_get_channels_max(params);
-
-  snd_pcm_close(handle);
+  if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
+    jack_client_close(client);
+    errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
   // If device opens for both playback and capture, we determine the channels.
 
   // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
-
-  info->hasDuplexSupport = true;
-  info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-    info->maxInputChannels : info->maxOutputChannels;
-  info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-    info->minInputChannels : info->minOutputChannels;
-
- probe_parameters:
-  // At this point, we just need to figure out the supported data
-  // formats and sample rates.  We'll proceed by opening the device in
-  // the direction with the maximum number of channels, or playback if
-  // they are equal.  This might limit our sample rate options, but so
-  // be it.
-
-  if (info->maxOutputChannels >= info->maxInputChannels)
-    stream = SND_PCM_STREAM_PLAYBACK;
-  else
-    stream = SND_PCM_STREAM_CAPTURE;
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  err = snd_pcm_open(&handle, info->name, stream, open_mode);
-  if (err < 0) {
-    sprintf(message, "RtAudio: ALSA pcm (%s) won't reopen during probe: %s.",
-            info->name, snd_strerror(err));
-    error(RtError::WARNING);
-    return;
-  }
+  // Jack always uses 32-bit floats.
+  info.nativeFormats = RTAUDIO_FLOAT32;
 
 
-  // We have an open device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA hardware reopen probe error (%s): %s.",
-            info->name, snd_strerror(err));
-    error(RtError::WARNING);
-    return;
-  }
+  // Jack doesn't provide default devices so we'll use the first available one.
+  if ( device == 0 && info.outputChannels > 0 )
+    info.isDefaultOutput = true;
+  if ( device == 0 && info.inputChannels > 0 )
+    info.isDefaultInput = true;
 
 
-  // Test a non-standard sample rate to see if continuous rate is supported.
-  int dir = 0;
-  if (snd_pcm_hw_params_test_rate(handle, params, 35500, dir) == 0) {
-    // It appears that continuous sample rate support is available.
-    info->nSampleRates = -1;
-    info->sampleRates[0] = snd_pcm_hw_params_get_rate_min(params, &dir);
-    info->sampleRates[1] = snd_pcm_hw_params_get_rate_max(params, &dir);
-  }
-  else {
-    // No continuous rate support ... test our discrete set of sample rate values.
-    info->nSampleRates = 0;
-    for (int i=0; i<MAX_SAMPLE_RATES; i++) {
-      if (snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) {
-        info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-        info->nSampleRates++;
-      }
-    }
-    if (info->nSampleRates == 0) {
-      snd_pcm_close(handle);
-      return;
-    }
-  }
+  jack_client_close(client);
+  info.probed = true;
+  return info;
+}
 
 
-  // Probe the supported data formats ... we don't care about endian-ness just yet
-  snd_pcm_format_t format;
-  info->nativeFormats = 0;
-  format = SND_PCM_FORMAT_S8;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT8;
-  format = SND_PCM_FORMAT_S16;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT16;
-  format = SND_PCM_FORMAT_S24;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT24;
-  format = SND_PCM_FORMAT_S32;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT32;
-  format = SND_PCM_FORMAT_FLOAT;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  format = SND_PCM_FORMAT_FLOAT64;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_FLOAT64;
+static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
+{
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
 
 
-  // Check that we have at least one supported format
-  if (info->nativeFormats == 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA PCM device (%s) data format not supported by RtAudio.",
-            info->name);
-    error(RtError::WARNING);
-    return;
-  }
+  RtApiJack *object = (RtApiJack *) info->object;
+  if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
 
 
-  // That's all ... close the device and return
-  snd_pcm_close(handle);
-  info->probed = true;
-  return;
+  return 0;
 }
 
 }
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
+// This function will be called by a spawned thread when the Jack
+// server signals that it is shutting down.  It is necessary to handle
+// it this way because the jackShutdown() function must return before
+// the jack_deactivate() function (in closeStream()) will return.
+static void *jackCloseStream( void *ptr )
 {
 {
-#if defined(__RTAUDIO_DEBUG__)
-  snd_output_t *out;
-  snd_output_stdio_attach(&out, stderr, 0);
-#endif
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiJack *object = (RtApiJack *) info->object;
 
 
-  // I'm not using the "plug" interface ... too much inconsistent behavior.
-  const char *name = devices[device].name;
+  object->closeStream();
 
 
-  snd_pcm_stream_t alsa_stream;
-  if (mode == OUTPUT)
-    alsa_stream = SND_PCM_STREAM_PLAYBACK;
-  else
-    alsa_stream = SND_PCM_STREAM_CAPTURE;
-
-  int err;
-  snd_pcm_t *handle;
-  int alsa_open_mode = SND_PCM_ASYNC;
-  err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode);
-  if (err < 0) {
-    sprintf(message,"RtAudio: ALSA pcm device (%s) won't open: %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+  pthread_exit( NULL );
+}
+static void jackShutdown( void *infoPointer )
+{
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
+  RtApiJack *object = (RtApiJack *) info->object;
+
+  // Check current stream state.  If stopped, then we'll assume this
+  // was called as a result of a call to RtApiJack::stopStream (the
+  // deactivation of a client handle causes this function to be called).
+  // If not, we'll assume the Jack server is shutting down or some
+  // other problem occurred and we should close the stream.
+  if ( object->isStreamRunning() == false ) return;
+
+  ThreadHandle threadId;
+  pthread_create( &threadId, NULL, jackCloseStream, info );
+  std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
+}
 
 
-  // Fill the parameter structure.
-  snd_pcm_hw_params_t *hw_params;
-  snd_pcm_hw_params_alloca(&hw_params);
-  err = snd_pcm_hw_params_any(handle, hw_params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error getting parameter handle (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+static int jackXrun( void *infoPointer )
+{
+  JackHandle *handle = (JackHandle *) infoPointer;
 
 
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtAudio: ALSA dump hardware params just after device open:\n\n");
-  snd_pcm_hw_params_dump(hw_params, out);
-#endif
+  if ( handle->ports[0] ) handle->xrun[0] = true;
+  if ( handle->ports[1] ) handle->xrun[1] = true;
 
 
+  return 0;
+}
 
 
-  // Set access ... try interleaved access first, then non-interleaved
-  if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) {
-    err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
-  }
-  else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) {
-               err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
-    stream->deInterleave[mode] = true;
+bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+{
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+
+  // Look for jack server and try to become a client (only do once per stream).
+  jack_client_t *client = 0;
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
+    jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
+    jack_status_t *status = NULL;
+    if ( options && !options->streamName.empty() )
+      client = jack_client_open( options->streamName.c_str(), jackoptions, status );
+    else
+      client = jack_client_open( "RtApiJack", jackoptions, status );
+    if ( client == 0 ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
+      error( RtAudioError::WARNING );
+      return FAILURE;
+    }
   }
   else {
   }
   else {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA device (%s) access not supported by RtAudio.", name);
-    error(RtError::WARNING);
-    return FAILURE;
+    // The handle must have been created on an earlier pass.
+    client = handle->client;
+  }
+
+  const char **ports;
+  std::string port, previousPort, deviceName;
+  unsigned int nPorts = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) deviceName = port;
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nPorts] );
+    free( ports );
   }
 
   }
 
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting access ( (%s): %s.", name, snd_strerror(err));
-    error(RtError::WARNING);
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Determine how to set the device format.
-  stream->userFormat = format;
-  snd_pcm_format_t device_format;
-
-  if (format == RTAUDIO_SINT8)
-    device_format = SND_PCM_FORMAT_S8;
-  else if (format == RTAUDIO_SINT16)
-    device_format = SND_PCM_FORMAT_S16;
-  else if (format == RTAUDIO_SINT24)
-    device_format = SND_PCM_FORMAT_S24;
-  else if (format == RTAUDIO_SINT32)
-    device_format = SND_PCM_FORMAT_S32;
-  else if (format == RTAUDIO_FLOAT32)
-    device_format = SND_PCM_FORMAT_FLOAT;
-  else if (format == RTAUDIO_FLOAT64)
-    device_format = SND_PCM_FORMAT_FLOAT64;
-
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = format;
-    goto set_format;
-  }
-
-  // The user requested format is not natively supported by the device.
-  device_format = SND_PCM_FORMAT_FLOAT64;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
-    goto set_format;
-  }
-
-  device_format = SND_PCM_FORMAT_FLOAT;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-    goto set_format;
-  }
-
-  device_format = SND_PCM_FORMAT_S32;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    goto set_format;
-  }
-
-  device_format = SND_PCM_FORMAT_S24;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT24;
-    goto set_format;
-  }
-
-  device_format = SND_PCM_FORMAT_S16;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT16;
-    goto set_format;
-  }
-
-  device_format = SND_PCM_FORMAT_S8;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT8;
-    goto set_format;
+  // Count the available ports containing the client name as device
+  // channels.  Jack "input ports" equal RtAudio output channels.
+  unsigned int nChannels = 0;
+  unsigned long flag = JackPortIsInput;
+  if ( mode == INPUT ) flag = JackPortIsOutput;
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
   }
 
   }
 
-  // If we get here, no supported format was found.
-  sprintf(message,"RtAudio: ALSA pcm device (%s) data format not supported by RtAudio.", name);
-  snd_pcm_close(handle);
-  error(RtError::WARNING);
-  return FAILURE;
-
- set_format:
-  err = snd_pcm_hw_params_set_format(handle, hw_params, device_format);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting format (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
+  // Compare the jack ports for specified client to the requested number of channels.
+  if ( nChannels < (channels + firstChannel) ) {
+    errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Determine whether byte-swaping is necessary.
-  stream->doByteSwap[mode] = false;
-  if (device_format != SND_PCM_FORMAT_S8) {
-    err = snd_pcm_format_cpu_endian(device_format);
-    if (err == 0)
-      stream->doByteSwap[mode] = true;
-    else if (err < 0) {
-      snd_pcm_close(handle);
-      sprintf(message, "RtAudio: ALSA error getting format endian-ness (%s): %s.",
-              name, snd_strerror(err));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
-  }
-
-  // Set the sample rate.
-  err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting sample rate (%d) on device (%s): %s.",
-            sampleRate, name, snd_strerror(err));
-    error(RtError::WARNING);
+  // Check the jack server sample rate.
+  unsigned int jackRate = jack_get_sample_rate( client );
+  if ( sampleRate != jackRate ) {
+    jack_client_close( client );
+    errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
     return FAILURE;
   }
+  stream_.sampleRate = jackRate;
 
 
-  // Determine the number of channels for this device.  We support a possible
-  // minimum device channel number > than the value requested by the user.
-  stream->nUserChannels[mode] = channels;
-  int device_channels = snd_pcm_hw_params_get_channels_max(hw_params);
-  if (device_channels < channels) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: channels (%d) not supported by device (%s).",
-            channels, name);
-    error(RtError::WARNING);
-    return FAILURE;
+  // Get the latency of the JACK port.
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports[ firstChannel ] ) {
+    // Added by Ge Wang
+    jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);
+    // the range (usually the min and max are equal)
+    jack_latency_range_t latrange; latrange.min = latrange.max = 0;
+    // get the latency range
+    jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );
+    // be optimistic, use the min!
+    stream_.latency[mode] = latrange.min;
+    //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
   }
   }
+  free( ports );
 
 
-  device_channels = snd_pcm_hw_params_get_channels_min(hw_params);
-  if (device_channels < channels) device_channels = channels;
-  stream->nDeviceChannels[mode] = device_channels;
-
-  // Set the device channels.
-  err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting channels (%d) on device (%s): %s.",
-            device_channels, name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+  // The jack server always uses 32-bit floating-point data.
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+  stream_.userFormat = format;
 
 
-  // Set the buffer number, which in ALSA is referred to as the "period".
-  int dir;
-  int periods = numberOfBuffers;
-  // Even though the hardware might allow 1 buffer, it won't work reliably.
-  if (periods < 2) periods = 2;
-  err = snd_pcm_hw_params_get_periods_min(hw_params, &dir);
-  if (err > periods) periods = err;
-  err = snd_pcm_hw_params_get_periods_max(hw_params, &dir);
-  if (err < periods) periods = err;
-
-  err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting periods (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
 
-  // Set the buffer (or period) size.
-  err = snd_pcm_hw_params_get_period_size_min(hw_params, &dir);
-  if (err > *bufferSize) *bufferSize = err;
-
-  err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting period size (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+  // Jack always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
 
-  // If attempting to setup a duplex stream, the bufferSize parameter
-  // MUST be the same in both directions!
-  if ( stream->mode == OUTPUT && mode == INPUT && *bufferSize != stream->bufferSize ) {
-    sprintf( message, "RtAudio: ALSA error setting buffer size for duplex stream on device (%s).",
-             name );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  // Jack always provides host byte-ordered data.
+  stream_.doByteSwap[mode] = false;
 
 
-  stream->bufferSize = *bufferSize;
+  // Get the buffer size.  The buffer size and number of buffers
+  // (periods) is set when the jack server is started.
+  stream_.bufferSize = (int) jack_get_buffer_size( client );
+  *bufferSize = stream_.bufferSize;
 
 
-  // Install the hardware configuration
-  err = snd_pcm_hw_params(handle, hw_params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error installing hardware configuration (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
 
 
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtAudio: ALSA dump hardware params after installation:\n\n");
-  snd_pcm_hw_params_dump(hw_params, out);
-#endif
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate our JackHandle structure for the stream.
+  if ( handle == 0 ) {
+    try {
+      handle = new JackHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
+      goto error;
+    }
 
 
-  /*
-  // Install the software configuration
-  snd_pcm_sw_params_t *sw_params = NULL;
-  snd_pcm_sw_params_alloca(&sw_params);
-  snd_pcm_sw_params_current(handle, sw_params);
-  err = snd_pcm_sw_params(handle, sw_params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error installing software configuration (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
+    if ( pthread_cond_init(&handle->condition, NULL) ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
+    stream_.apiHandle = (void *) handle;
+    handle->client = client;
   }
   }
-  */
-
-  // Set handle and flags for buffer conversion
-  stream->handle[mode] = handle;
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
-    stream->doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+  handle->deviceName[mode] = deviceName;
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-    long buffer_bytes;
     bool makeBuffer = true;
     if ( mode == OUTPUT )
     bool makeBuffer = true;
     if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
+      bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
     else { // mode == INPUT
     else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+      bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
+        if ( bufferBytes < bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
-    // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->nBuffers = periods;
-  stream->sampleRate = sampleRate;
+  // Allocate memory for the Jack ports (channels) identifiers.
+  handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
+  if ( handle->ports[mode] == NULL )  {
+    errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
+    goto error;
+  }
 
 
-  return SUCCESS;
+  stream_.device[mode] = device;
+  stream_.channelOffset[mode] = firstChannel;
+  stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
 
 
- memory_error:
-  if (stream->handle[0]) {
-    snd_pcm_close(stream->handle[0]);
-    stream->handle[0] = 0;
-  }
-  if (stream->handle[1]) {
-    snd_pcm_close(stream->handle[1]);
-    stream->handle[1] = 0;
-  }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up the stream for output.
+    stream_.mode = DUPLEX;
+  else {
+    stream_.mode = mode;
+    jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
+    jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
+    jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
   }
   }
-  sprintf(message, "RtAudio: ALSA error allocating buffer memory (%s).", name);
-  error(RtError::WARNING);
-  return FAILURE;
-}
 
 
-void RtAudio :: closeStream(int streamId)
-{
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
-    return;
+  // Register our ports.
+  char label[64];
+  if ( mode == OUTPUT ) {
+    for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+      snprintf( label, 64, "outport %d", i );
+      handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
+                                                JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
+    }
+  }
+  else {
+    for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+      snprintf( label, 64, "inport %d", i );
+      handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
+                                                JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
+    }
   }
 
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  // Setup the buffer conversion information structure.  We don't use
+  // buffers to do channel offsets, so we override that parameter
+  // here.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-  }
+  if ( options && options->flags & RTAUDIO_JACK_DONT_CONNECT ) shouldAutoconnect_ = false;
 
 
-  if (stream->state == STREAM_RUNNING) {
-    if (stream->mode == OUTPUT || stream->mode == DUPLEX)
-      snd_pcm_drop(stream->handle[0]);
-    if (stream->mode == INPUT || stream->mode == DUPLEX)
-      snd_pcm_drop(stream->handle[1]);
-  }
+  return SUCCESS;
 
 
-  pthread_mutex_destroy(&stream->mutex);
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->condition );
+    jack_client_close( handle->client );
 
 
-  if (stream->handle[0])
-    snd_pcm_close(stream->handle[0]);
+    if ( handle->ports[0] ) free( handle->ports[0] );
+    if ( handle->ports[1] ) free( handle->ports[1] );
 
 
-  if (stream->handle[1])
-    snd_pcm_close(stream->handle[1]);
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  free(stream);
-  streams.erase(streamId);
+  return FAILURE;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiJack :: closeStream( void )
 {
 {
-  // This method calls snd_pcm_prepare if the device isn't already in that state.
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiJack::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( handle ) {
 
 
-  MUTEX_LOCK(&stream->mutex);
+    if ( stream_.state == STREAM_RUNNING )
+      jack_deactivate( handle->client );
 
 
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+    jack_client_close( handle->client );
+  }
 
 
-  int err;
-  snd_pcm_state_t state;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    state = snd_pcm_state(stream->handle[0]);
-    if (state != SND_PCM_STATE_PREPARED) {
-      err = snd_pcm_prepare(stream->handle[0]);
-      if (err < 0) {
-        sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",
-                devices[stream->device[0]].name, snd_strerror(err));
-        MUTEX_UNLOCK(&stream->mutex);
-        error(RtError::DRIVER_ERROR);
-      }
-    }
+  if ( handle ) {
+    if ( handle->ports[0] ) free( handle->ports[0] );
+    if ( handle->ports[1] ) free( handle->ports[1] );
+    pthread_cond_destroy( &handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    state = snd_pcm_state(stream->handle[1]);
-    if (state != SND_PCM_STATE_PREPARED) {
-      err = snd_pcm_prepare(stream->handle[1]);
-      if (err < 0) {
-        sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",
-                devices[stream->device[1]].name, snd_strerror(err));
-        MUTEX_UNLOCK(&stream->mutex);
-        error(RtError::DRIVER_ERROR);
-      }
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
     }
   }
-  stream->state = STREAM_RUNNING;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
+
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiJack :: startStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiJack::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  int result = jack_activate( handle->client );
+  if ( result ) {
+    errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
     goto unlock;
     goto unlock;
+  }
 
 
-  int err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_drain(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
-              devices[stream->device[0]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+  const char **ports;
+
+  // Get the list of available ports.
+  if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) {
+    result = 1;
+    ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
+    if ( ports == NULL) {
+      errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
+      goto unlock;
     }
     }
-  }
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_drain(stream->handle[1]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
-              devices[stream->device[1]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    // Now make the port connections.  Since RtAudio wasn't designed to
+    // allow the user to select particular channels of a device, we'll
+    // just open the first "nChannels" ports with offset.
+    for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+      result = 1;
+      if ( ports[ stream_.channelOffset[0] + i ] )
+        result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
+      if ( result ) {
+        free( ports );
+        errorText_ = "RtApiJack::startStream(): error connecting output ports!";
+        goto unlock;
+      }
     }
     }
+    free(ports);
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  int err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_drop(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
-              devices[stream->device[0]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+  if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) {
+    result = 1;
+    ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
+    if ( ports == NULL) {
+      errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
+      goto unlock;
     }
     }
-  }
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_drop(stream->handle[1]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
-              devices[stream->device[1]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    // Now make the port connections.  See note above.
+    for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+      result = 1;
+      if ( ports[ stream_.channelOffset[1] + i ] )
+        result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
+      if ( result ) {
+        free( ports );
+        errorText_ = "RtApiJack::startStream(): error connecting input ports!";
+        goto unlock;
+      }
     }
     }
+    free(ports);
   }
   }
-  stream->state = STREAM_STOPPED;
+
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( result == 0 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiJack :: stopStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  int err = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_avail_update(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",
-              devices[stream->device[0]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 2;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
   }
 
     }
   }
 
-  frames = err;
+  jack_deactivate( handle->client );
+  stream_.state = STREAM_STOPPED;
+}
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_avail_update(stream->handle[1]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",
-              devices[stream->device[1]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
-    }
-    if (frames > err) frames = err;
+void RtApiJack :: abortStream( void )
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  handle->drainCounter = 2;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  stopStream();
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+// This function will be called by a spawned thread when the user
+// callback function signals that the stream should be stopped or
+// aborted.  It is necessary to handle it this way because the
+// callbackEvent() function must return before the jack_deactivate()
+// function will return.
+static void *jackStopStream( void *ptr )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiJack *object = (RtApiJack *) info->object;
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
-    return;
+  object->stopStream();
+  pthread_exit( NULL );
+}
+
+bool RtApiJack :: callbackEvent( unsigned long nframes )
+{
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
+    return FAILURE;
   }
   }
-  else if (stream->callbackInfo.usingCallback) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
-    stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
+  if ( stream_.bufferSize != nframes ) {
+    errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
+    error( RtAudioError::WARNING );
+    return FAILURE;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
 
 
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    ThreadHandle threadId;
 
 
-  int err;
-  char *buffer;
-  int channels;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    stream_.state = STREAM_STOPPING;
+    if ( handle->internalDrain == true )
+      pthread_create( &threadId, NULL, jackStopStream, info );
+    else
+      pthread_cond_signal( &handle->condition );
+    return SUCCESS;
+  }
 
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[0];
-      format = stream->deviceFormat[0];
+  // Invoke user callback first, to get fresh output data.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[0];
-      format = stream->userFormat;
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                  stream_.bufferSize, streamTime, status, info->userData );
+    if ( cbReturnValue == 2 ) {
+      stream_.state = STREAM_STOPPING;
+      handle->drainCounter = 2;
+      ThreadHandle id;
+      pthread_create( &id, NULL, jackStopStream, info );
+      return SUCCESS;
     }
     }
+    else if ( cbReturnValue == 1 ) {
+      handle->drainCounter = 1;
+      handle->internalDrain = true;
+    }
+  }
 
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+  jack_default_audio_sample_t *jackbuffer;
+  unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
+
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memset( jackbuffer, 0, bufferBytes );
+      }
 
 
-    // Write samples to device in interleaved/non-interleaved format.
-    if (stream->deInterleave[0]) {
-      void *bufs[channels];
-      size_t offset = stream->bufferSize * formatBytes(format);
-      for (int i=0; i<channels; i++)
-        bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_writen(stream->handle[0], bufs, stream->bufferSize);
     }
     }
-    else
-      err = snd_pcm_writei(stream->handle[0], buffer, stream->bufferSize);
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-    if (err < stream->bufferSize) {
-      // Either an error or underrun occured.
-      if (err == -EPIPE) {
-        snd_pcm_state_t state = snd_pcm_state(stream->handle[0]);
-        if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message, "RtAudio: ALSA underrun detected.");
-          error(RtError::WARNING);
-          err = snd_pcm_prepare(stream->handle[0]);
-          if (err < 0) {
-            sprintf(message, "RtAudio: ALSA error preparing handle after underrun: %s.",
-                    snd_strerror(err));
-            MUTEX_UNLOCK(&stream->mutex);
-            error(RtError::DRIVER_ERROR);
-          }
-        }
-        else {
-          sprintf(message, "RtAudio: ALSA error, current state is %s.",
-                  snd_pcm_state_name(state));
-          MUTEX_UNLOCK(&stream->mutex);
-          error(RtError::DRIVER_ERROR);
-        }
-        goto unlock;
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
       }
       }
-      else {
-        sprintf(message, "RtAudio: ALSA audio write error for device (%s): %s.",
-                devices[stream->device[0]].name, snd_strerror(err));
-        MUTEX_UNLOCK(&stream->mutex);
-        error(RtError::DRIVER_ERROR);
+    }
+    else { // no buffer conversion
+      for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
       }
     }
   }
 
       }
     }
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  // Don't bother draining input
+  if ( handle->drainCounter ) {
+    handle->drainCounter++;
+    goto unlock;
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
+    if ( stream_.doConvertBuffer[1] ) {
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
+        memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
+      }
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[1];
-      format = stream->userFormat;
+    else { // no buffer conversion
+      for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
+        memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
+      }
     }
     }
+  }
 
 
-    // Read samples from device in interleaved/non-interleaved format.
-    if (stream->deInterleave[1]) {
-      void *bufs[channels];
-      size_t offset = stream->bufferSize * formatBytes(format);
-      for (int i=0; i<channels; i++)
-        bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_readn(stream->handle[1], bufs, stream->bufferSize);
-    }
-    else
-      err = snd_pcm_readi(stream->handle[1], buffer, stream->bufferSize);
+ unlock:
+  RtApi::tickStreamTime();
+  return SUCCESS;
+}
+  //******************** End of __UNIX_JACK__ *********************//
+#endif
 
 
-    if (err < stream->bufferSize) {
-      // Either an error or underrun occured.
-      if (err == -EPIPE) {
-        snd_pcm_state_t state = snd_pcm_state(stream->handle[1]);
-        if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message, "RtAudio: ALSA overrun detected.");
-          error(RtError::WARNING);
-          err = snd_pcm_prepare(stream->handle[1]);
-          if (err < 0) {
-            sprintf(message, "RtAudio: ALSA error preparing handle after overrun: %s.",
-                    snd_strerror(err));
-            MUTEX_UNLOCK(&stream->mutex);
-            error(RtError::DRIVER_ERROR);
-          }
-        }
-        else {
-          sprintf(message, "RtAudio: ALSA error, current state is %s.",
-                  snd_pcm_state_name(state));
-          MUTEX_UNLOCK(&stream->mutex);
-          error(RtError::DRIVER_ERROR);
-        }
-        goto unlock;
-      }
-      else {
-        sprintf(message, "RtAudio: ALSA audio read error for device (%s): %s.",
-                devices[stream->device[1]].name, snd_strerror(err));
-        MUTEX_UNLOCK(&stream->mutex);
-        error(RtError::DRIVER_ERROR);
-      }
-    }
+#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
 
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[1])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+// The ASIO API is designed around a callback scheme, so this
+// implementation is similar to that used for OS-X CoreAudio and Linux
+// Jack.  The primary constraint with ASIO is that it only allows
+// access to a single driver at a time.  Thus, it is not possible to
+// have more than one simultaneous RtAudio stream.
+//
+// This implementation also requires a number of external ASIO files
+// and a few global variables.  The ASIO callback scheme does not
+// allow for the passing of user data, so we must create a global
+// pointer to our callbackInfo structure.
+//
+// On unix systems, we make use of a pthread condition variable.
+// Since there is no equivalent in Windows, I hacked something based
+// on information found in
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
+
+#include "asiosys.h"
+#include "asio.h"
+#include "iasiothiscallresolver.h"
+#include "asiodrivers.h"
+#include <cmath>
+
+static AsioDrivers drivers;
+static ASIOCallbacks asioCallbacks;
+static ASIODriverInfo driverInfo;
+static CallbackInfo *asioCallbackInfo;
+static bool asioXRun;
+
+struct AsioHandle {
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+  ASIOBufferInfo *bufferInfos;
+  HANDLE condition;
+
+  AsioHandle()
+    :drainCounter(0), internalDrain(false), bufferInfos(0) {}
+};
 
 
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
+// Function declarations (definitions at end of section)
+static const char* getAsioErrorString( ASIOError result );
+static void sampleRateChanged( ASIOSampleRate sRate );
+static long asioMessages( long selector, long value, void* message, double* opt );
+
+RtApiAsio :: RtApiAsio()
+{
+  // ASIO cannot run on a multi-threaded appartment. You can call
+  // CoInitialize beforehand, but it must be for appartment threading
+  // (in which case, CoInitilialize will return S_FALSE here).
+  coInitialized_ = false;
+  HRESULT hr = CoInitialize( NULL ); 
+  if ( FAILED(hr) ) {
+    errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
+    error( RtAudioError::WARNING );
   }
   }
+  coInitialized_ = true;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  drivers.removeCurrentDriver();
+  driverInfo.asioVersion = 2;
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
+  // See note in DirectSound implementation about GetDesktopWindow().
+  driverInfo.sysRef = GetForegroundWindow();
 }
 
 }
 
-extern "C" void *callbackHandler(void *ptr)
+RtApiAsio :: ~RtApiAsio()
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+  if ( coInitialized_ ) CoUninitialize();
+}
 
 
-  while ( *usingCallback ) {
-    pthread_testcancel();
-    try {
-      object->tickStream(stream);
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessage());
-      break;
-    }
+unsigned int RtApiAsio :: getDeviceCount( void )
+{
+  return (unsigned int) drivers.asioGetNumDev();
+}
+
+RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
+    error( RtAudioError::INVALID_USE );
+    return info;
   }
 
   }
 
-  return 0;
-}
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
 
 
-//******************** End of __LINUX_ALSA__ *********************//
+  // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.
+  if ( stream_.state != STREAM_CLOSED ) {
+    if ( device >= devices_.size() ) {
+      errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";
+      error( RtAudioError::WARNING );
+      return info;
+    }
+    return devices_[ device ];
+  }
 
 
-#elif defined(__LINUX_OSS__)
+  char driverName[32];
+  ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
 
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/soundcard.h>
-#include <errno.h>
-#include <math.h>
+  info.name = driverName;
 
 
-#define DAC_NAME "/dev/dsp"
-#define MAX_DEVICES 16
-#define MAX_CHANNELS 16
+  if ( !drivers.loadDriver( driverName ) ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
 
-void RtAudio :: initialize(void)
-{
-  // Count cards and devices
-  nDevices = 0;
-
-  // We check /dev/dsp before probing devices.  /dev/dsp is supposed to
-  // be a link to the "default" audio device, of the form /dev/dsp0,
-  // /dev/dsp1, etc...  However, I've seen many cases where /dev/dsp was a
-  // real device, so we need to check for that.  Also, sometimes the
-  // link is to /dev/dspx and other times just dspx.  I'm not sure how
-  // the latter works, but it does.
-  char device_name[16];
-  struct stat dspstat;
-  int dsplink = -1;
-  int i = 0;
-  if (lstat(DAC_NAME, &dspstat) == 0) {
-    if (S_ISLNK(dspstat.st_mode)) {
-      i = readlink(DAC_NAME, device_name, sizeof(device_name));
-      if (i > 0) {
-        device_name[i] = '\0';
-        if (i > 8) { // check for "/dev/dspx"
-          if (!strncmp(DAC_NAME, device_name, 8))
-            dsplink = atoi(&device_name[8]);
-        }
-        else if (i > 3) { // check for "dspx"
-          if (!strncmp("dsp", device_name, 3))
-            dsplink = atoi(&device_name[3]);
-        }
-      }
-      else {
-        sprintf(message, "RtAudio: cannot read value of symbolic link %s.", DAC_NAME);
-        error(RtError::SYSTEM_ERROR);
-      }
-    }
+  result = ASIOInit( &driverInfo );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
   }
-  else {
-    sprintf(message, "RtAudio: cannot stat %s.", DAC_NAME);
-    error(RtError::SYSTEM_ERROR);
-  }
-
-  // The OSS API doesn't provide a routine for determining the number
-  // of devices.  Thus, we'll just pursue a brute force method.  The
-  // idea is to start with /dev/dsp(0) and continue with higher device
-  // numbers until we reach MAX_DSP_DEVICES.  This should tell us how
-  // many devices we have ... it is not a fullproof scheme, but hopefully
-  // it will work most of the time.
-
-  int fd = 0;
-  char names[MAX_DEVICES][16];
-  for (i=-1; i<MAX_DEVICES; i++) {
-
-    // Probe /dev/dsp first, since it is supposed to be the default device.
-    if (i == -1)
-      sprintf(device_name, "%s", DAC_NAME);
-    else if (i == dsplink)
-      continue; // We've aready probed this device via /dev/dsp link ... try next device.
-    else
-      sprintf(device_name, "%s%d", DAC_NAME, i);
-
-    // First try to open the device for playback, then record mode.
-    fd = open(device_name, O_WRONLY | O_NONBLOCK);
-    if (fd == -1) {
-      // Open device for playback failed ... either busy or doesn't exist.
-      if (errno != EBUSY && errno != EAGAIN) {
-        // Try to open for capture
-        fd = open(device_name, O_RDONLY | O_NONBLOCK);
-        if (fd == -1) {
-          // Open device for record failed.
-          if (errno != EBUSY && errno != EAGAIN)
-            continue;
-          else {
-            sprintf(message, "RtAudio: OSS record device (%s) is busy.", device_name);
-            error(RtError::WARNING);
-            // still count it for now
-          }
-        }
-      }
-      else {
-        sprintf(message, "RtAudio: OSS playback device (%s) is busy.", device_name);
-        error(RtError::WARNING);
-        // still count it for now
-      }
-    }
 
 
-    if (fd >= 0) close(fd);
-    strncpy(names[nDevices], device_name, 16);
-    nDevices++;
+  // Determine the device channel information.
+  long inputChannels, outputChannels;
+  result = ASIOGetChannels( &inputChannels, &outputChannels );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   }
 
-  if (nDevices == 0) return;
+  info.outputChannels = outputChannels;
+  info.inputChannels = inputChannels;
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
+
+  // Determine the supported sample rates.
+  info.sampleRates.clear();
+  for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
+    result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
+    if ( result == ASE_OK ) {
+      info.sampleRates.push_back( SAMPLE_RATES[i] );
 
 
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
+        info.preferredSampleRate = SAMPLE_RATES[i];
+    }
   }
 
   }
 
-  // Write device ascii identifiers to device control structure and then probe capabilities.
-  for (i=0; i<nDevices; i++) {
-    strncpy(devices[i].name, names[i], 16);
-    //probeDeviceInfo(&devices[i]);
+  // Determine supported data types ... just check first channel and assume rest are the same.
+  ASIOChannelInfo channelInfo;
+  channelInfo.channel = 0;
+  channelInfo.isInput = true;
+  if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
+  result = ASIOGetChannelInfo( &channelInfo );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   }
 
-  return;
+  info.nativeFormats = 0;
+  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
+    info.nativeFormats |= RTAUDIO_SINT32;
+  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
+    info.nativeFormats |= RTAUDIO_FLOAT64;
+  else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )
+    info.nativeFormats |= RTAUDIO_SINT24;
+
+  if ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
+
+  info.probed = true;
+  drivers.removeCurrentDriver();
+  return info;
 }
 
 }
 
-int RtAudio :: getDefaultInputDevice(void)
+static void bufferSwitch( long index, ASIOBool /*processNow*/ )
 {
 {
-  // No OSS API functions for default devices.
-  return 0;
+  RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
+  object->callbackEvent( index );
 }
 
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+void RtApiAsio :: saveDeviceInfo( void )
 {
 {
-  // No OSS API functions for default devices.
-  return 0;
+  devices_.clear();
+
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  int i, fd, channels, mask;
+bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+{////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 
-  // The OSS API doesn't provide a means for probing the capabilities
-  // of devices.  Thus, we'll just pursue a brute force method.
+  bool isDuplexInput =  mode == INPUT && stream_.mode == OUTPUT;
 
 
-  // First try for playback
-  fd = open(info->name, O_WRONLY | O_NONBLOCK);
-  if (fd == -1) {
-    // Open device failed ... either busy or doesn't exist
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message, "RtAudio: OSS playback device (%s) is busy and cannot be probed.",
-              info->name);
-    else
-      sprintf(message, "RtAudio: OSS playback device (%s) open error.", info->name);
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
-  }
-
-  // We have an open device ... see how many channels it can handle
-  for (i=MAX_CHANNELS; i>0; i--) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
-      // This would normally indicate some sort of hardware error, but under ALSA's
-      // OSS emulation, it sometimes indicates an invalid channel value.  Further,
-      // the returned channel value is not changed. So, we'll ignore the possible
-      // hardware error.
-      continue; // try next channel number
-    }
-    // Check to see whether the device supports the requested number of channels
-    if (channels != i ) continue; // try next channel number
-    // If here, we found the largest working channel value
-    break;
+  // For ASIO, a duplex stream MUST use the same driver.
+  if ( isDuplexInput && stream_.device[0] != device ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
+    return FAILURE;
   }
   }
-  info->maxOutputChannels = i;
 
 
-  // Now find the minimum number of channels it can handle
-  for (i=1; i<=info->maxOutputChannels; i++) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-      continue; // try next channel number
-    // If here, we found the smallest working channel value
-    break;
-  }
-  info->minOutputChannels = i;
-  close(fd);
-
- capture_probe:
-  // Now try for capture
-  fd = open(info->name, O_RDONLY | O_NONBLOCK);
-  if (fd == -1) {
-    // Open device for capture failed ... either busy or doesn't exist
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message, "RtAudio: OSS capture device (%s) is busy and cannot be probed.",
-              info->name);
-    else
-      sprintf(message, "RtAudio: OSS capture device (%s) open error.", info->name);
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
+  char driverName[32];
+  ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  // We have the device open for capture ... see how many channels it can handle
-  for (i=MAX_CHANNELS; i>0; i--) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
-      continue; // as above
+  // Only load the driver once for duplex stream.
+  if ( !isDuplexInput ) {
+    // The getDeviceInfo() function will not work when a stream is open
+    // because ASIO does not allow multiple devices to run at the same
+    // time.  Thus, we'll probe the system before opening a stream and
+    // save the results for use by getDeviceInfo().
+    this->saveDeviceInfo();
+
+    if ( !drivers.loadDriver( driverName ) ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    // If here, we found a working channel value
-    break;
-  }
-  info->maxInputChannels = i;
 
 
-  // Now find the minimum number of channels it can handle
-  for (i=1; i<=info->maxInputChannels; i++) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-      continue; // try next channel number
-    // If here, we found the smallest working channel value
-    break;
+    result = ASIOInit( &driverInfo );
+    if ( result != ASE_OK ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
   }
-  info->minInputChannels = i;
-  close(fd);
 
 
-  if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) {
-    sprintf(message, "RtAudio: OSS device (%s) reports zero channels for input and output.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  // keep them before any "goto error", they are used for error cleanup + goto device boundary checks
+  bool buffersAllocated = false;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  unsigned int nChannels;
 
 
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
-
-  fd = open(info->name, O_RDWR | O_NONBLOCK);
-  if (fd == -1)
-    goto probe_parameters;
-
-  ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
-  ioctl(fd, SNDCTL_DSP_GETCAPS, &mask);
-  if (mask & DSP_CAP_DUPLEX) {
-    info->hasDuplexSupport = true;
-    // We have the device open for duplex ... see how many channels it can handle
-    for (i=MAX_CHANNELS; i>0; i--) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // as above
-      // If here, we found a working channel value
-      break;
-    }
-    info->maxDuplexChannels = i;
 
 
-    // Now find the minimum number of channels it can handle
-    for (i=1; i<=info->maxDuplexChannels; i++) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // try next channel number
-      // If here, we found the smallest working channel value
-      break;
-    }
-    info->minDuplexChannels = i;
+  // Check the device channel count.
+  long inputChannels, outputChannels;
+  result = ASIOGetChannels( &inputChannels, &outputChannels );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    goto error;
   }
   }
-  close(fd);
-
- probe_parameters:
-  // At this point, we need to figure out the supported data formats
-  // and sample rates.  We'll proceed by openning the device in the
-  // direction with the maximum number of channels, or playback if
-  // they are equal.  This might limit our sample rate options, but so
-  // be it.
 
 
-  if (info->maxOutputChannels >= info->maxInputChannels) {
-    fd = open(info->name, O_WRONLY | O_NONBLOCK);
-    channels = info->maxOutputChannels;
+  if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
+       ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
+    errorText_ = errorStream_.str();
+    goto error;
   }
   }
-  else {
-    fd = open(info->name, O_RDONLY | O_NONBLOCK);
-    channels = info->maxInputChannels;
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = firstChannel;
+
+  // Verify the sample rate is supported.
+  result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    goto error;
   }
 
   }
 
-  if (fd == -1) {
-    // We've got some sort of conflict ... abort
-    sprintf(message, "RtAudio: OSS device (%s) won't reopen during probe.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Get the current sample rate
+  ASIOSampleRate currentRate;
+  result = ASIOGetSampleRate( &currentRate );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
+    errorText_ = errorStream_.str();
+    goto error;
   }
 
   }
 
-  // We have an open device ... set to maximum channels.
-  i = channels;
-  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
-    // We've got some sort of conflict ... abort
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) won't revert to previous channel setting.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Set the sample rate only if necessary
+  if ( currentRate != sampleRate ) {
+    result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
+    if ( result != ASE_OK ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
+      errorText_ = errorStream_.str();
+      goto error;
+    }
   }
 
   }
 
-  if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Determine the driver data type.
+  ASIOChannelInfo channelInfo;
+  channelInfo.channel = 0;
+  if ( mode == OUTPUT ) channelInfo.isInput = false;
+  else channelInfo.isInput = true;
+  result = ASIOGetChannelInfo( &channelInfo );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
+    errorText_ = errorStream_.str();
+    goto error;
   }
 
   }
 
-  // Probe the supported data formats ... we don't care about endian-ness just yet.
-  int format;
-  info->nativeFormats = 0;
-#if defined (AFMT_S32_BE)
-  // This format does not seem to be in the 2.4 kernel version of OSS soundcard.h
-  if (mask & AFMT_S32_BE) {
-    format = AFMT_S32_BE;
-    info->nativeFormats |= RTAUDIO_SINT32;
+  // Assuming WINDOWS host is always little-endian.
+  stream_.doByteSwap[mode] = false;
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = 0;
+  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-#endif
-#if defined (AFMT_S32_LE)
-  /* This format is not in the 2.4.4 kernel version of OSS soundcard.h */
-  if (mask & AFMT_S32_LE) {
-    format = AFMT_S32_LE;
-    info->nativeFormats |= RTAUDIO_SINT32;
+  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-#endif
-  if (mask & AFMT_S8) {
-    format = AFMT_S8;
-    info->nativeFormats |= RTAUDIO_SINT8;
+  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-  if (mask & AFMT_S16_BE) {
-    format = AFMT_S16_BE;
-    info->nativeFormats |= RTAUDIO_SINT16;
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-  if (mask & AFMT_S16_LE) {
-    format = AFMT_S16_LE;
-    info->nativeFormats |= RTAUDIO_SINT16;
+  else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;
   }
 
   }
 
-  // Check that we have at least one supported format
-  if (info->nativeFormats == 0) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    goto error;
   }
 
   }
 
-  // Set the format
-  i = format;
-  if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) error setting data format.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Set the buffer size.  For a duplex stream, this will end up
+  // setting the buffer size based on the input constraints, which
+  // should be ok.
+  long minSize, maxSize, preferSize, granularity;
+  result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
+    errorText_ = errorStream_.str();
+    goto error;
   }
 
   }
 
-  // Probe the supported sample rates ... first get lower limit
-  int speed = 1;
-  if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
-    // If we get here, we're probably using an ALSA driver with OSS-emulation,
-    // which doesn't conform to the OSS specification.  In this case,
-    // we'll probe our predefined list of sample rates for working values.
-    info->nSampleRates = 0;
-    for (i=0; i<MAX_SAMPLE_RATES; i++) {
-      speed = SAMPLE_RATES[i];
-      if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) != -1) {
-        info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-        info->nSampleRates++;
+  if ( isDuplexInput ) {
+    // When this is the duplex input (output was opened before), then we have to use the same
+    // buffersize as the output, because it might use the preferred buffer size, which most
+    // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,
+    // So instead of throwing an error, make them equal. The caller uses the reference
+    // to the "bufferSize" param as usual to set up processing buffers.
+
+    *bufferSize = stream_.bufferSize;
+
+  } else {
+    if ( *bufferSize == 0 ) *bufferSize = preferSize;
+    else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
+    else if ( granularity == -1 ) {
+      // Make sure bufferSize is a power of two.
+      int log2_of_min_size = 0;
+      int log2_of_max_size = 0;
+
+      for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
+        if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
+        if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
+      }
+
+      long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
+      int min_delta_num = log2_of_min_size;
+
+      for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
+        long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
+        if (current_delta < min_delta) {
+          min_delta = current_delta;
+          min_delta_num = i;
+        }
       }
       }
+
+      *bufferSize = ( (unsigned int)1 << min_delta_num );
+      if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+      else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
     }
     }
-    if (info->nSampleRates == 0) {
-      close(fd);
-      return;
+    else if ( granularity != 0 ) {
+      // Set to an even multiple of granularity, rounding up.
+      *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
     }
     }
-    goto finished;
   }
   }
-  info->sampleRates[0] = speed;
 
 
-  // Now get upper limit
-  speed = 1000000;
-  if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) error setting sample rate.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  /*
+  // we don't use it anymore, see above!
+  // Just left it here for the case...
+  if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
+    goto error;
   }
   }
-  info->sampleRates[1] = speed;
-  info->nSampleRates = -1;
+  */
 
 
- finished: // That's all ... close the device and return
-  close(fd);
-  info->probed = true;
-  return;
-}
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 2;
 
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  int buffers, buffer_bytes, device_channels, device_format;
-  int srate, temp, fd;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
 
-  const char *name = devices[device].name;
+  // ASIO always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
 
-  if (mode == OUTPUT)
-    fd = open(name, O_WRONLY | O_NONBLOCK);
-  else { // mode == INPUT
-    if (stream->mode == OUTPUT && stream->device[0] == device) {
-      // We just set the same device for playback ... close and reopen for duplex (OSS only).
-      close(stream->handle[0]);
-      stream->handle[0] = 0;
-      // First check that the number previously set channels is the same.
-      if (stream->nUserChannels[0] != channels) {
-        sprintf(message, "RtAudio: input/output channels must be equal for OSS duplex device (%s).", name);
-        goto error;
-      }
-      fd = open(name, O_RDWR | O_NONBLOCK);
+  // Allocate, if necessary, our AsioHandle structure for the stream.
+  if ( handle == 0 ) {
+    try {
+      handle = new AsioHandle;
     }
     }
-    else
-      fd = open(name, O_RDONLY | O_NONBLOCK);
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
+      goto error;
+    }
+    handle->bufferInfos = 0;
+
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
   }
 
   }
 
-  if (fd == -1) {
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message, "RtAudio: OSS device (%s) is busy and cannot be opened.",
-              name);
-    else
-      sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
+  // Create the ASIO internal buffers.  Since RtAudio sets up input
+  // and output separately, we'll have to dispose of previously
+  // created output buffers for a duplex stream.
+  if ( mode == INPUT && stream_.mode == OUTPUT ) {
+    ASIODisposeBuffers();
+    if ( handle->bufferInfos ) free( handle->bufferInfos );
+  }
+
+  // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
+  unsigned int i;
+  nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
+  if ( handle->bufferInfos == NULL ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
     goto error;
   }
 
     goto error;
   }
 
-  // Now reopen in blocking mode.
-  close(fd);
-  if (mode == OUTPUT)
-    fd = open(name, O_WRONLY | O_SYNC);
-  else { // mode == INPUT
-    if (stream->mode == OUTPUT && stream->device[0] == device)
-      fd = open(name, O_RDWR | O_SYNC);
-    else
-      fd = open(name, O_RDONLY | O_SYNC);
+  ASIOBufferInfo *infos;
+  infos = handle->bufferInfos;
+  for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
+    infos->isInput = ASIOFalse;
+    infos->channelNum = i + stream_.channelOffset[0];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
+  for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
+    infos->isInput = ASIOTrue;
+    infos->channelNum = i + stream_.channelOffset[1];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
+
+  // prepare for callbacks
+  stream_.sampleRate = sampleRate;
+  stream_.device[mode] = device;
+  stream_.mode = isDuplexInput ? DUPLEX : mode;
+
+  // store this class instance before registering callbacks, that are going to use it
+  asioCallbackInfo = &stream_.callbackInfo;
+  stream_.callbackInfo.object = (void *) this;
+
+  // Set up the ASIO callback structure and create the ASIO data buffers.
+  asioCallbacks.bufferSwitch = &bufferSwitch;
+  asioCallbacks.sampleRateDidChange = &sampleRateChanged;
+  asioCallbacks.asioMessage = &asioMessages;
+  asioCallbacks.bufferSwitchTimeInfo = NULL;
+  result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
+  if ( result != ASE_OK ) {
+    // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges
+    // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver
+    // in that case, let's be naïve and try that instead
+    *bufferSize = preferSize;
+    stream_.bufferSize = *bufferSize;
+    result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
   }
 
   }
 
-  if (fd == -1) {
-    sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
+    errorText_ = errorStream_.str();
     goto error;
   }
     goto error;
   }
+  buffersAllocated = true;  
+  stream_.state = STREAM_STOPPED;
 
 
-  // Get the sample format mask
-  int mask;
-  if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",
-            name);
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate necessary internal buffers
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
     goto error;
   }
 
     goto error;
   }
 
-  // Determine how to set the device format.
-  stream->userFormat = format;
-  device_format = -1;
-  stream->doByteSwap[mode] = false;
-  if (format == RTAUDIO_SINT8) {
-    if (mask & AFMT_S8) {
-      device_format = AFMT_S8;
-      stream->deviceFormat[mode] = RTAUDIO_SINT8;
-    }
-  }
-  else if (format == RTAUDIO_SINT16) {
-    if (mask & AFMT_S16_NE) {
-      device_format = AFMT_S16_NE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S16_BE) {
-      device_format = AFMT_S16_BE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
+  if ( stream_.doConvertBuffer[mode] ) {
+
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( isDuplexInput && stream_.deviceBuffer ) {
+      unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+      if ( bufferBytes <= bytesOut ) makeBuffer = false;
     }
     }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
+
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
     }
-#endif
   }
   }
-#if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
-  else if (format == RTAUDIO_SINT32) {
-    if (mask & AFMT_S32_NE) {
-      device_format = AFMT_S32_NE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S32_BE) {
-      device_format = AFMT_S32_BE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S32_LE) {
-      device_format = AFMT_S32_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#endif
+
+  // Determine device latencies
+  long inputLatency, outputLatency;
+  result = ASIOGetLatencies( &inputLatency, &outputLatency );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING); // warn but don't fail
+  }
+  else {
+    stream_.latency[0] = outputLatency;
+    stream_.latency[1] = inputLatency;
   }
   }
-#endif
 
 
-  if (device_format == -1) {
-    // The user requested format is not natively supported by the device.
-    if (mask & AFMT_S16_NE) {
-      device_format = AFMT_S16_NE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S16_BE) {
-      device_format = AFMT_S16_BE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
-    }
-#endif
-#if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
-    else if (mask & AFMT_S32_NE) {
-      device_format = AFMT_S32_NE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S32_BE) {
-      device_format = AFMT_S32_BE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S32_LE) {
-      device_format = AFMT_S32_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#endif
-#endif
-    else if (mask & AFMT_S8) {
-      device_format = AFMT_S8;
-      stream->deviceFormat[mode] = RTAUDIO_SINT8;
+  // Setup the buffer conversion information structure.  We don't use
+  // buffers to do channel offsets, so we override that parameter
+  // here.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
+
+  return SUCCESS;
+
+ error:
+  if ( !isDuplexInput ) {
+    // the cleanup for error in the duplex input, is done by RtApi::openStream
+    // So we clean up for single channel only
+
+    if ( buffersAllocated )
+      ASIODisposeBuffers();
+
+    drivers.removeCurrentDriver();
+
+    if ( handle ) {
+      CloseHandle( handle->condition );
+      if ( handle->bufferInfos )
+        free( handle->bufferInfos );
+
+      delete handle;
+      stream_.apiHandle = 0;
     }
     }
-  }
 
 
-  if (stream->deviceFormat[mode] == 0) {
-    // This really shouldn't happen ...
-    close(fd);
-    sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",
-            name);
-    goto error;
-  }
 
 
-  // Determine the number of channels for this device.  Note that the
-  // channel value requested by the user might be < min_X_Channels.
-  stream->nUserChannels[mode] = channels;
-  device_channels = channels;
-  if (mode == OUTPUT) {
-    if (channels < devices[device].minOutputChannels)
-      device_channels = devices[device].minOutputChannels;
-  }
-  else { // mode == INPUT
-    if (stream->mode == OUTPUT && stream->device[0] == device) {
-      // We're doing duplex setup here.
-      if (channels < devices[device].minDuplexChannels)
-        device_channels = devices[device].minDuplexChannels;
+    if ( stream_.userBuffer[mode] ) {
+      free( stream_.userBuffer[mode] );
+      stream_.userBuffer[mode] = 0;
     }
     }
-    else {
-      if (channels < devices[device].minInputChannels)
-        device_channels = devices[device].minInputChannels;
+
+    if ( stream_.deviceBuffer ) {
+      free( stream_.deviceBuffer );
+      stream_.deviceBuffer = 0;
     }
   }
     }
   }
-  stream->nDeviceChannels[mode] = device_channels;
 
 
-  // Attempt to set the buffer size.  According to OSS, the minimum
-  // number of buffers is two.  The supposed minimum buffer size is 16
-  // bytes, so that will be our lower bound.  The argument to this
-  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
-  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
-  // We'll check the actual value used near the end of the setup
-  // procedure.
-  buffer_bytes = *bufferSize * formatBytes(stream->deviceFormat[mode]) * device_channels;
-  if (buffer_bytes < 16) buffer_bytes = 16;
-  buffers = numberOfBuffers;
-  if (buffers < 2) buffers = 2;
-  temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0));
-  if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting fragment size for device (%s).",
-            name);
-    goto error;
-  }
-  stream->nBuffers = buffers;
+  return FAILURE;
+}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 
-  // Set the data format.
-  temp = device_format;
-  if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting data format for device (%s).",
-            name);
-    goto error;
+void RtApiAsio :: closeStream()
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  // Set the number of channels.
-  temp = device_channels;
-  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting %d channels on device (%s).",
-            temp, name);
-    goto error;
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    ASIOStop();
   }
   }
+  ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
 
-  // Set the sample rate.
-  srate = sampleRate;
-  temp = srate;
-  if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting sample rate = %d on device (%s).",
-            temp, name);
-    goto error;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
   }
 
-  // Verify the sample rate setup worked.
-  if (abs(srate - temp) > 100) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %d.",
-            name, temp);
-    goto error;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
   }
-  stream->sampleRate = sampleRate;
 
 
-  if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error getting buffer size for device (%s).",
-            name);
-    goto error;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
   }
 
-  // Save buffer size (in sample frames).
-  *bufferSize = buffer_bytes / (formatBytes(stream->deviceFormat[mode]) * device_channels);
-  stream->bufferSize = *bufferSize;
-
-  if (mode == INPUT && stream->mode == OUTPUT &&
-      stream->device[0] == device) {
-    // We're doing duplex setup here.
-    stream->deviceFormat[0] = stream->deviceFormat[1];
-    stream->nDeviceChannels[0] = device_channels;
-  }
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
+}
 
 
-  // Set flags for buffer conversion
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
+bool stopThreadCalled = false;
 
 
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+void RtApiAsio :: startStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAsio::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL) {
-      close(fd);
-      sprintf(message, "RtAudio: OSS error allocating user buffer memory (%s).",
-              name);
-      goto error;
-    }
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  ASIOError result = ASIOStart();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
+    errorText_ = errorStream_.str();
+    goto unlock;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  ResetEvent( handle->condition );
+  stream_.state = STREAM_RUNNING;
+  asioXRun = false;
 
 
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
-    }
+ unlock:
+  stopThreadCalled = false;
 
 
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL) {
-        close(fd);
-        free(stream->userBuffer);
-        sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).",
-                name);
-        goto error;
-      }
-    }
+  if ( result == ASE_OK ) return;
+  error( RtAudioError::SYSTEM_ERROR );
+}
+
+void RtApiAsio :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  stream->device[mode] = device;
-  stream->handle[mode] = fd;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT ) {
-    stream->mode = DUPLEX;
-    if (stream->device[0] == device)
-      stream->handle[0] = fd;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 2;
+      WaitForSingleObject( handle->condition, INFINITE );  // block until signaled
+    }
   }
   }
-  else
-    stream->mode = mode;
 
 
-  return SUCCESS;
+  stream_.state = STREAM_STOPPED;
 
 
- error:
-  if (stream->handle[0]) {
-    close(stream->handle[0]);
-    stream->handle[0] = 0;
+  ASIOError result = ASIOStop();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
+    errorText_ = errorStream_.str();
   }
   }
-  error(RtError::WARNING);
-  return FAILURE;
+
+  if ( result == ASE_OK ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiAsio :: abortStream()
 {
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  // The following lines were commented-out because some behavior was
+  // noted where the device buffers need to be zeroed to avoid
+  // continuing sound, even when the device buffers are completely
+  // disposed.  So now, calling abort is the same as calling stop.
+  // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  // handle->drainCounter = 2;
+  stopStream();
+}
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-  }
+// This function will be called by a spawned thread when the user
+// callback function signals that the stream should be stopped or
+// aborted.  It is necessary to handle it this way because the
+// callbackEvent() function must return before the ASIOStop()
+// function will return.
+static unsigned __stdcall asioStopStream( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiAsio *object = (RtApiAsio *) info->object;
+
+  object->stopStream();
+  _endthreadex( 0 );
+  return 0;
+}
 
 
-  if (stream->state == STREAM_RUNNING) {
-    if (stream->mode == OUTPUT || stream->mode == DUPLEX)
-      ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);
-    if (stream->mode == INPUT || stream->mode == DUPLEX)
-      ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);
+bool RtApiAsio :: callbackEvent( long bufferIndex )
+{
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
+    return FAILURE;
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
 
 
-  if (stream->handle[0])
-    close(stream->handle[0]);
+  // Check if we were draining the stream and signal if finished.
+  if ( handle->drainCounter > 3 ) {
 
 
-  if (stream->handle[1])
-    close(stream->handle[1]);
+    stream_.state = STREAM_STOPPING;
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else { // spawn a thread to stop the stream
+      unsigned threadId;
+      stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
+                                                    &stream_.callbackInfo, 0, &threadId );
+    }
+    return SUCCESS;
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && asioXRun == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      asioXRun = false;
+    }
+    if ( stream_.mode != OUTPUT && asioXRun == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      asioXRun = false;
+    }
+    int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( cbReturnValue == 2 ) {
+      stream_.state = STREAM_STOPPING;
+      handle->drainCounter = 2;
+      unsigned threadId;
+      stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
+                                                    &stream_.callbackInfo, 0, &threadId );
+      return SUCCESS;
+    }
+    else if ( cbReturnValue == 1 ) {
+      handle->drainCounter = 1;
+      handle->internalDrain = true;
+    }
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  unsigned int nChannels, bufferBytes, i, j;
+  nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  free(stream);
-  streams.erase(streamId);
-}
+    bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
 
 
-void RtAudio :: startStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-  MUTEX_LOCK(&stream->mutex);
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
+      }
 
 
-  stream->state = STREAM_RUNNING;
+    }
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-  // No need to do anything else here ... OSS automatically starts
-  // when fed samples.
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[0],
+                        stream_.deviceFormat[0] );
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memcpy( handle->bufferInfos[i].buffers[bufferIndex],
+                  &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
+      }
 
 
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    }
+    else {
 
 
-  MUTEX_LOCK(&stream->mutex);
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.userBuffer[0],
+                        stream_.bufferSize * stream_.nUserChannels[0],
+                        stream_.userFormat );
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memcpy( handle->bufferInfos[i].buffers[bufferIndex],
+                  &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
+      }
 
 
-  int err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = ioctl(stream->handle[0], SNDCTL_DSP_SYNC, 0);
-    if (err < -1) {
-      sprintf(message, "RtAudio: OSS error stopping device (%s).",
-              devices[stream->device[0]].name);
-      error(RtError::DRIVER_ERROR);
     }
   }
     }
   }
-  else {
-    err = ioctl(stream->handle[1], SNDCTL_DSP_SYNC, 0);
-    if (err < -1) {
-      sprintf(message, "RtAudio: OSS error stopping device (%s).",
-              devices[stream->device[1]].name);
-      error(RtError::DRIVER_ERROR);
-    }
+
+  // Don't bother draining input
+  if ( handle->drainCounter ) {
+    handle->drainCounter++;
+    goto unlock;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
 
 
-  MUTEX_LOCK(&stream->mutex);
+    if (stream_.doConvertBuffer[1]) {
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+      // Always interleave ASIO input data.
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput == ASIOTrue )
+          memcpy( &stream_.deviceBuffer[j++*bufferBytes],
+                  handle->bufferInfos[i].buffers[bufferIndex],
+                  bufferBytes );
+      }
+
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[1],
+                        stream_.deviceFormat[1] );
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
 
-  int err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);
-    if (err < -1) {
-      sprintf(message, "RtAudio: OSS error aborting device (%s).",
-              devices[stream->device[0]].name);
-      error(RtError::DRIVER_ERROR);
     }
     }
-  }
-  else {
-    err = ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);
-    if (err < -1) {
-      sprintf(message, "RtAudio: OSS error aborting device (%s).",
-              devices[stream->device[1]].name);
-      error(RtError::DRIVER_ERROR);
+    else {
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
+          memcpy( &stream_.userBuffer[1][bufferBytes*j++],
+                  handle->bufferInfos[i].buffers[bufferIndex],
+                  bufferBytes );
+        }
+      }
+
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.userBuffer[1],
+                        stream_.bufferSize * stream_.nUserChannels[1],
+                        stream_.userFormat );
     }
   }
     }
   }
-  stream->state = STREAM_STOPPED;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  // The following call was suggested by Malte Clasen.  While the API
+  // documentation indicates it should not be required, some device
+  // drivers apparently do not function correctly without it.
+  ASIOOutputReady();
+
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+static void sampleRateChanged( ASIOSampleRate sRate )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // The ASIO documentation says that this usually only happens during
+  // external sync.  Audio processing is not stopped by the driver,
+  // actual sample rate might not have even changed, maybe only the
+  // sample rate status of an AES/EBU or S/PDIF digital input at the
+  // audio device.
 
 
-  MUTEX_LOCK(&stream->mutex);
+  RtApi *object = (RtApi *) asioCallbackInfo->object;
+  try {
+    object->stopStream();
+  }
+  catch ( RtAudioError &exception ) {
+    std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
+    return;
+  }
 
 
-  int bytes = 0, channels = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
+}
 
 
-  audio_buf_info info;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    ioctl(stream->handle[0], SNDCTL_DSP_GETOSPACE, &info);
-    bytes = info.bytes;
-    channels = stream->nDeviceChannels[0];
-  }
+static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )
+{
+  long ret = 0;
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    ioctl(stream->handle[1], SNDCTL_DSP_GETISPACE, &info);
-    if (stream->mode == DUPLEX ) {
-      bytes = (bytes < info.bytes) ? bytes : info.bytes;
-      channels = stream->nDeviceChannels[0];
-    }
-    else {
-      bytes = info.bytes;
-      channels = stream->nDeviceChannels[1];
-    }
+  switch( selector ) {
+  case kAsioSelectorSupported:
+    if ( value == kAsioResetRequest
+         || value == kAsioEngineVersion
+         || value == kAsioResyncRequest
+         || value == kAsioLatenciesChanged
+         // The following three were added for ASIO 2.0, you don't
+         // necessarily have to support them.
+         || value == kAsioSupportsTimeInfo
+         || value == kAsioSupportsTimeCode
+         || value == kAsioSupportsInputMonitor)
+      ret = 1L;
+    break;
+  case kAsioResetRequest:
+    // Defer the task and perform the reset of the driver during the
+    // next "safe" situation.  You cannot reset the driver right now,
+    // as this code is called from the driver.  Reset the driver is
+    // done by completely destruct is. I.e. ASIOStop(),
+    // ASIODisposeBuffers(), Destruction Afterwards you initialize the
+    // driver again.
+    std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
+    ret = 1L;
+    break;
+  case kAsioResyncRequest:
+    // This informs the application that the driver encountered some
+    // non-fatal data loss.  It is used for synchronization purposes
+    // of different media.  Added mainly to work around the Win16Mutex
+    // problems in Windows 95/98 with the Windows Multimedia system,
+    // which could lose data because the Mutex was held too long by
+    // another thread.  However a driver can issue it in other
+    // situations, too.
+    // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
+    asioXRun = true;
+    ret = 1L;
+    break;
+  case kAsioLatenciesChanged:
+    // This will inform the host application that the drivers were
+    // latencies changed.  Beware, it this does not mean that the
+    // buffer sizes have changed!  You might need to update internal
+    // delay data.
+    std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
+    ret = 1L;
+    break;
+  case kAsioEngineVersion:
+    // Return the supported ASIO version of the host application.  If
+    // a host application does not implement this selector, ASIO 1.0
+    // is assumed by the driver.
+    ret = 2L;
+    break;
+  case kAsioSupportsTimeInfo:
+    // Informs the driver whether the
+    // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
+    // For compatibility with ASIO 1.0 drivers the host application
+    // should always support the "old" bufferSwitch method, too.
+    ret = 0;
+    break;
+  case kAsioSupportsTimeCode:
+    // Informs the driver whether application is interested in time
+    // code info.  If an application does not need to know about time
+    // code, the driver has less work to do.
+    ret = 0;
+    break;
   }
   }
+  return ret;
+}
+
+static const char* getAsioErrorString( ASIOError result )
+{
+  struct Messages 
+  {
+    ASIOError value;
+    const char*message;
+  };
+
+  static const Messages m[] = 
+    {
+      {   ASE_NotPresent,    "Hardware input or output is not present or available." },
+      {   ASE_HWMalfunction,  "Hardware is malfunctioning." },
+      {   ASE_InvalidParameter, "Invalid input parameter." },
+      {   ASE_InvalidMode,      "Invalid mode." },
+      {   ASE_SPNotAdvancing,     "Sample position not advancing." },
+      {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },
+      {   ASE_NoMemory,           "Not enough memory to complete the request." }
+    };
+
+  for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
+    if ( m[i].value == result ) return m[i].message;
+
+  return "Unknown error.";
+}
 
 
-  frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0])));
-  frames -= stream->bufferSize;
-  if (frames < 0) frames = 0;
+//******************** End of __WINDOWS_ASIO__ *********************//
+#endif
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+
+#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API
+
+// Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014
+// - Introduces support for the Windows WASAPI API
+// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required
+// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface
+// - Includes automatic internal conversion of sample rate and buffer size between hardware and the user
+
+#ifndef INITGUID
+  #define INITGUID
+#endif
+#include <audioclient.h>
+#include <avrt.h>
+#include <mmdeviceapi.h>
+#include <functiondiscoverykeys_devpkey.h>
+#include <sstream>
+
+//=============================================================================
+
+#define SAFE_RELEASE( objectPtr )\
+if ( objectPtr )\
+{\
+  objectPtr->Release();\
+  objectPtr = NULL;\
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );
+
+//-----------------------------------------------------------------------------
+
+// WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.
+// Therefore we must perform all necessary conversions to user buffers in order to satisfy these
+// requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to
+// provide intermediate storage for read / write synchronization.
+class WasapiBuffer
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+public:
+  WasapiBuffer()
+    : buffer_( NULL ),
+      bufferSize_( 0 ),
+      inIndex_( 0 ),
+      outIndex_( 0 ) {}
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
-    return;
-  }
-  else if (stream->callbackInfo.usingCallback) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
-    stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
+  ~WasapiBuffer() {
+    free( buffer_ );
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  // sets the length of the internal ring buffer
+  void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {
+    free( buffer_ );
 
 
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    buffer_ = ( char* ) calloc( bufferSize, formatBytes );
 
 
-  int result;
-  char *buffer;
-  int samples;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    bufferSize_ = bufferSize;
+    inIndex_ = 0;
+    outIndex_ = 0;
+  }
 
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      samples = stream->bufferSize * stream->nDeviceChannels[0];
-      format = stream->deviceFormat[0];
+  // attempt to push a buffer into the ring buffer at the current "in" index
+  bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )
+  {
+    if ( !buffer ||                 // incoming buffer is NULL
+         bufferSize == 0 ||         // incoming buffer has no data
+         bufferSize > bufferSize_ ) // incoming buffer too large
+    {
+      return false;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      samples = stream->bufferSize * stream->nUserChannels[0];
-      format = stream->userFormat;
+
+    unsigned int relOutIndex = outIndex_;
+    unsigned int inIndexEnd = inIndex_ + bufferSize;
+    if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {
+      relOutIndex += bufferSize_;
     }
 
     }
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, samples, format);
+    // "in" index can end on the "out" index but cannot begin at it
+    if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {
+      return false; // not enough space between "in" index and "out" index
+    }
 
 
-    // Write samples to device.
-    result = write(stream->handle[0], buffer, samples * formatBytes(format));
+    // copy buffer from external to internal
+    int fromZeroSize = inIndex_ + bufferSize - bufferSize_;
+    fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;
+    int fromInSize = bufferSize - fromZeroSize;
 
 
-    if (result == -1) {
-      // This could be an underrun, but the basic OSS API doesn't provide a means for determining that.
-      sprintf(message, "RtAudio: OSS audio write error for device (%s).",
-              devices[stream->device[0]].name);
-      error(RtError::DRIVER_ERROR);
+    switch( format )
+      {
+      case RTAUDIO_SINT8:
+        memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );
+        memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );
+        break;
+      case RTAUDIO_SINT16:
+        memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );
+        memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );
+        break;
+      case RTAUDIO_SINT24:
+        memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );
+        memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );
+        break;
+      case RTAUDIO_SINT32:
+        memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );
+        memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );
+        break;
+      case RTAUDIO_FLOAT32:
+        memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );
+        memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );
+        break;
+      case RTAUDIO_FLOAT64:
+        memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );
+        memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );
+        break;
     }
     }
+
+    // update "in" index
+    inIndex_ += bufferSize;
+    inIndex_ %= bufferSize_;
+
+    return true;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  // attempt to pull a buffer from the ring buffer from the current "out" index
+  bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )
+  {
+    if ( !buffer ||                 // incoming buffer is NULL
+         bufferSize == 0 ||         // incoming buffer has no data
+         bufferSize > bufferSize_ ) // incoming buffer too large
+    {
+      return false;
+    }
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      samples = stream->bufferSize * stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
+    unsigned int relInIndex = inIndex_;
+    unsigned int outIndexEnd = outIndex_ + bufferSize;
+    if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {
+      relInIndex += bufferSize_;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      samples = stream->bufferSize * stream->nUserChannels[1];
-      format = stream->userFormat;
+
+    // "out" index can begin at and end on the "in" index
+    if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {
+      return false; // not enough space between "out" index and "in" index
     }
 
     }
 
-    // Read samples from device.
-    result = read(stream->handle[1], buffer, samples * formatBytes(format));
+    // copy buffer from internal to external
+    int fromZeroSize = outIndex_ + bufferSize - bufferSize_;
+    fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;
+    int fromOutSize = bufferSize - fromZeroSize;
 
 
-    if (result == -1) {
-      // This could be an overrun, but the basic OSS API doesn't provide a means for determining that.
-      sprintf(message, "RtAudio: OSS audio read error for device (%s).",
-              devices[stream->device[1]].name);
-      error(RtError::DRIVER_ERROR);
+    switch( format )
+    {
+      case RTAUDIO_SINT8:
+        memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );
+        memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );
+        break;
+      case RTAUDIO_SINT16:
+        memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );
+        memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );
+        break;
+      case RTAUDIO_SINT24:
+        memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );
+        memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );
+        break;
+      case RTAUDIO_SINT32:
+        memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );
+        memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );
+        break;
+      case RTAUDIO_FLOAT32:
+        memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );
+        memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );
+        break;
+      case RTAUDIO_FLOAT64:
+        memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );
+        memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );
+        break;
     }
 
     }
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[1])
-      byteSwapBuffer(buffer, samples, format);
+    // update "out" index
+    outIndex_ += bufferSize;
+    outIndex_ %= bufferSize_;
 
 
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
+    return true;
   }
 
   }
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+private:
+  char* buffer_;
+  unsigned int bufferSize_;
+  unsigned int inIndex_;
+  unsigned int outIndex_;
+};
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
-}
+//-----------------------------------------------------------------------------
 
 
-extern "C" void *callbackHandler(void *ptr)
+// A structure to hold various information related to the WASAPI implementation.
+struct WasapiHandle
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+  IAudioClient* captureAudioClient;
+  IAudioClient* renderAudioClient;
+  IAudioCaptureClient* captureClient;
+  IAudioRenderClient* renderClient;
+  HANDLE captureEvent;
+  HANDLE renderEvent;
+
+  WasapiHandle()
+  : captureAudioClient( NULL ),
+    renderAudioClient( NULL ),
+    captureClient( NULL ),
+    renderClient( NULL ),
+    captureEvent( NULL ),
+    renderEvent( NULL ) {}
+};
 
 
-  while ( *usingCallback ) {
-    pthread_testcancel();
-    try {
-      object->tickStream(stream);
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessage());
-      break;
-    }
-  }
+//=============================================================================
 
 
-  return 0;
-}
+RtApiWasapi::RtApiWasapi()
+  : coInitialized_( false ), deviceEnumerator_( NULL )
+{
+  // WASAPI can run either apartment or multi-threaded
+  HRESULT hr = CoInitialize( NULL );
+  if ( !FAILED( hr ) )
+    coInitialized_ = true;
 
 
+  // Instantiate device enumerator
+  hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,
+                         CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),
+                         ( void** ) &deviceEnumerator_ );
 
 
-//******************** End of __LINUX_OSS__ *********************//
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::RtApiWasapi: Unable to instantiate device enumerator";
+    error( RtAudioError::DRIVER_ERROR );
+  }
+}
 
 
-#elif defined(__WINDOWS_ASIO__) // ASIO API on Windows
+//-----------------------------------------------------------------------------
 
 
-// The ASIO API is designed around a callback scheme, so this
-// implementation is similar to that used for OS X CoreAudio.  The
-// primary constraint with ASIO is that it only allows access to a
-// single driver at a time.  Thus, it is not possible to have more
-// than one simultaneous RtAudio stream.
-//
-// This implementation also requires a number of external ASIO files
-// and a few global variables.  The ASIO callback scheme does not
-// allow for the passing of user data, so we must create a global
-// pointer to our callbackInfo structure.
+RtApiWasapi::~RtApiWasapi()
+{
+  if ( stream_.state != STREAM_CLOSED )
+    closeStream();
 
 
-#include "asio/asiosys.h"
-#include "asio/asio.h"
-#include "asio/asiodrivers.h"
-#include <math.h>
+  SAFE_RELEASE( deviceEnumerator_ );
+
+  // If this object previously called CoInitialize()
+  if ( coInitialized_ )
+    CoUninitialize();
+}
 
 
-AsioDrivers drivers;
-ASIOCallbacks asioCallbacks;
-CALLBACK_INFO *asioCallbackInfo;
-ASIODriverInfo driverInfo;
+//=============================================================================
 
 
-void RtAudio :: initialize(void)
+unsigned int RtApiWasapi::getDeviceCount( void )
 {
 {
-  nDevices = drivers.asioGetNumDev();
-  if (nDevices <= 0) return;
+  unsigned int captureDeviceCount = 0;
+  unsigned int renderDeviceCount = 0;
 
 
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
+  IMMDeviceCollection* captureDevices = NULL;
+  IMMDeviceCollection* renderDevices = NULL;
+
+  // Count capture devices
+  errorText_.clear();
+  HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";
+    goto Exit;
   }
 
   }
 
-  // Write device driver names to device structures and then probe the
-  // device capabilities.
-  for (int i=0; i<nDevices; i++) {
-    if ( drivers.asioGetDriverName( i, devices[i].name, 128 ) == 0 )
-      //probeDeviceInfo(&devices[i]);
-      ;
-    else {
-      sprintf(message, "RtAudio: error getting ASIO driver name for device index %d!", i);
-      error(RtError::WARNING);
-    }
+  hr = captureDevices->GetCount( &captureDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";
+    goto Exit;
   }
 
   }
 
-  drivers.removeCurrentDriver();
-  driverInfo.asioVersion = 2;
-  // See note in DirectSound implementation about GetDesktopWindow().
-  driverInfo.sysRef = GetForegroundWindow();
-}
+  // Count render devices
+  hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";
+    goto Exit;
+  }
 
 
-int RtAudio :: getDefaultInputDevice(void)
-{
-  return 0;
-}
+  hr = renderDevices->GetCount( &renderDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";
+    goto Exit;
+  }
 
 
-int RtAudio :: getDefaultOutputDevice(void)
-{
+Exit:
+  // release all references
+  SAFE_RELEASE( captureDevices );
+  SAFE_RELEASE( renderDevices );
+
+  if ( errorText_.empty() )
+    return captureDeviceCount + renderDeviceCount;
+
+  error( RtAudioError::DRIVER_ERROR );
   return 0;
 }
 
   return 0;
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+//-----------------------------------------------------------------------------
+
+RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
 {
 {
-  // Don't probe if a stream is already open.
-  if ( streams.size() > 0 ) {
-    sprintf(message, "RtAudio: unable to probe ASIO driver while a stream is open.");
-    error(RtError::DEBUG_WARNING);
-    return;
+  RtAudio::DeviceInfo info;
+  unsigned int captureDeviceCount = 0;
+  unsigned int renderDeviceCount = 0;
+  std::string defaultDeviceName;
+  bool isCaptureDevice = false;
+
+  PROPVARIANT deviceNameProp;
+  PROPVARIANT defaultDeviceNameProp;
+
+  IMMDeviceCollection* captureDevices = NULL;
+  IMMDeviceCollection* renderDevices = NULL;
+  IMMDevice* devicePtr = NULL;
+  IMMDevice* defaultDevicePtr = NULL;
+  IAudioClient* audioClient = NULL;
+  IPropertyStore* devicePropStore = NULL;
+  IPropertyStore* defaultDevicePropStore = NULL;
+
+  WAVEFORMATEX* deviceFormat = NULL;
+  WAVEFORMATEX* closestMatchFormat = NULL;
+
+  // probed
+  info.probed = false;
+
+  // Count capture devices
+  errorText_.clear();
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
+  HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";
+    goto Exit;
   }
 
   }
 
-  if ( !drivers.loadDriver( info->name ) ) {
-    sprintf(message, "RtAudio: ASIO error loading driver (%s).", info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  hr = captureDevices->GetCount( &captureDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";
+    goto Exit;
   }
 
   }
 
-  ASIOError result = ASIOInit( &driverInfo );
-  if ( result != ASE_OK ) {
-    char details[32];
-    if ( result == ASE_HWMalfunction )
-      sprintf(details, "hardware malfunction");
-    else if ( result == ASE_NoMemory )
-      sprintf(details, "no memory");
-    else if ( result == ASE_NotPresent )
-      sprintf(details, "driver/hardware not present");
-    else
-      sprintf(details, "unspecified");
-    sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Count render devices
+  hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";
+    goto Exit;
   }
 
   }
 
-  // Determine the device channel information.
-  long inputChannels, outputChannels;
-  result = ASIOGetChannels( &inputChannels, &outputChannels );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).", info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  hr = renderDevices->GetCount( &renderDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";
+    goto Exit;
   }
 
   }
 
-  info->maxOutputChannels = outputChannels;
-  if ( outputChannels > 0 ) info->minOutputChannels = 1;
+  // validate device index
+  if ( device >= captureDeviceCount + renderDeviceCount ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";
+    errorType = RtAudioError::INVALID_USE;
+    goto Exit;
+  }
 
 
-  info->maxInputChannels = inputChannels;
-  if ( inputChannels > 0 ) info->minInputChannels = 1;
+  // determine whether index falls within capture or render devices
+  if ( device >= renderDeviceCount ) {
+    hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";
+      goto Exit;
+    }
+    isCaptureDevice = true;
+  }
+  else {
+    hr = renderDevices->Item( device, &devicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";
+      goto Exit;
+    }
+    isCaptureDevice = false;
+  }
 
 
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
-    info->hasDuplexSupport = true;
-    info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-      info->maxInputChannels : info->maxOutputChannels;
-    info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-      info->minInputChannels : info->minOutputChannels;
+  // get default device name
+  if ( isCaptureDevice ) {
+    hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";
+      goto Exit;
+    }
+  }
+  else {
+    hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";
+      goto Exit;
+    }
   }
 
   }
 
-  // Determine the supported sample rates.
-  info->nSampleRates = 0;
-  for (int i=0; i<MAX_SAMPLE_RATES; i++) {
-    result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
-    if ( result == ASE_OK )
-      info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i];
+  hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";
+    goto Exit;
   }
   }
+  PropVariantInit( &defaultDeviceNameProp );
 
 
-  if (info->nSampleRates == 0) {
-    drivers.removeCurrentDriver();
-    sprintf( message, "RtAudio: No supported sample rates found for ASIO driver (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
+  hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";
+    goto Exit;
   }
 
   }
 
-  // Determine supported data types ... just check first channel and assume rest are the same.
-  ASIOChannelInfo channelInfo;
-  channelInfo.channel = 0;
-  channelInfo.isInput = true;
-  if ( info->maxInputChannels <= 0 ) channelInfo.isInput = false;
-  result = ASIOGetChannelInfo( &channelInfo );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO error getting driver (%s) channel information.", info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);
+
+  // name
+  hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";
+    goto Exit;
   }
 
   }
 
-  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
-    info->nativeFormats |= RTAUDIO_SINT16;
-  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
-    info->nativeFormats |= RTAUDIO_SINT32;
-  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
-    info->nativeFormats |= RTAUDIO_FLOAT64;
+  PropVariantInit( &deviceNameProp );
 
 
-       // Check that we have at least one supported format.
-  if (info->nativeFormats == 0) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.",
-            info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
+  hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";
+    goto Exit;
   }
 
   }
 
-  info->probed = true;
-  drivers.removeCurrentDriver();
-}
+  info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);
 
 
-void bufferSwitch(long index, ASIOBool processNow)
-{
-  RtAudio *object = (RtAudio *) asioCallbackInfo->object;
-  try {
-    object->callbackEvent( asioCallbackInfo->streamId, index, (void *)NULL, (void *)NULL );
+  // is default
+  if ( isCaptureDevice ) {
+    info.isDefaultInput = info.name == defaultDeviceName;
+    info.isDefaultOutput = false;
   }
   }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage());
-    return;
+  else {
+    info.isDefaultInput = false;
+    info.isDefaultOutput = info.name == defaultDeviceName;
   }
 
   }
 
-  return;
-}
+  // channel count
+  hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";
+    goto Exit;
+  }
 
 
-void sampleRateChanged(ASIOSampleRate sRate)
-{
-  // The ASIO documentation says that this usually only happens during
-  // external sync.  Audio processing is not stopped by the driver,
-  // actual sample rate might not have even changed, maybe only the
-  // sample rate status of an AES/EBU or S/PDIF digital input at the
-  // audio device.
+  hr = audioClient->GetMixFormat( &deviceFormat );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";
+    goto Exit;
+  }
 
 
-  RtAudio *object = (RtAudio *) asioCallbackInfo->object;
-  try {
-    object->stopStream( asioCallbackInfo->streamId );
+  if ( isCaptureDevice ) {
+    info.inputChannels = deviceFormat->nChannels;
+    info.outputChannels = 0;
+    info.duplexChannels = 0;
   }
   }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtAudio: sampleRateChanged() error (%s)!\n\n", exception.getMessage());
-    return;
+  else {
+    info.inputChannels = 0;
+    info.outputChannels = deviceFormat->nChannels;
+    info.duplexChannels = 0;
+  }
+
+  // sample rates (WASAPI only supports the one native sample rate)
+  info.preferredSampleRate = deviceFormat->nSamplesPerSec;
+
+  info.sampleRates.clear();
+  info.sampleRates.push_back( deviceFormat->nSamplesPerSec );
+
+  // native format
+  info.nativeFormats = 0;
+
+  if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
+       ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+         ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )
+  {
+    if ( deviceFormat->wBitsPerSample == 32 ) {
+      info.nativeFormats |= RTAUDIO_FLOAT32;
+    }
+    else if ( deviceFormat->wBitsPerSample == 64 ) {
+      info.nativeFormats |= RTAUDIO_FLOAT64;
+    }
+  }
+  else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||
+           ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+             ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )
+  {
+    if ( deviceFormat->wBitsPerSample == 8 ) {
+      info.nativeFormats |= RTAUDIO_SINT8;
+    }
+    else if ( deviceFormat->wBitsPerSample == 16 ) {
+      info.nativeFormats |= RTAUDIO_SINT16;
+    }
+    else if ( deviceFormat->wBitsPerSample == 24 ) {
+      info.nativeFormats |= RTAUDIO_SINT24;
+    }
+    else if ( deviceFormat->wBitsPerSample == 32 ) {
+      info.nativeFormats |= RTAUDIO_SINT32;
+    }
   }
 
   }
 
-  fprintf(stderr, "\nRtAudio: ASIO driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate);
+  // probed
+  info.probed = true;
+
+Exit:
+  // release all references
+  PropVariantClear( &deviceNameProp );
+  PropVariantClear( &defaultDeviceNameProp );
+
+  SAFE_RELEASE( captureDevices );
+  SAFE_RELEASE( renderDevices );
+  SAFE_RELEASE( devicePtr );
+  SAFE_RELEASE( defaultDevicePtr );
+  SAFE_RELEASE( audioClient );
+  SAFE_RELEASE( devicePropStore );
+  SAFE_RELEASE( defaultDevicePropStore );
+
+  CoTaskMemFree( deviceFormat );
+  CoTaskMemFree( closestMatchFormat );
+
+  if ( !errorText_.empty() )
+    error( errorType );
+  return info;
 }
 
 }
 
-long asioMessages(long selector, long value, void* message, double* opt)
+//-----------------------------------------------------------------------------
+
+unsigned int RtApiWasapi::getDefaultOutputDevice( void )
 {
 {
-  long ret = 0;
-  switch(selector) {
-  case kAsioSelectorSupported:
-    if(value == kAsioResetRequest
-       || value == kAsioEngineVersion
-       || value == kAsioResyncRequest
-       || value == kAsioLatenciesChanged
-       // The following three were added for ASIO 2.0, you don't
-       // necessarily have to support them.
-       || value == kAsioSupportsTimeInfo
-       || value == kAsioSupportsTimeCode
-       || value == kAsioSupportsInputMonitor)
-      ret = 1L;
-    break;
-  case kAsioResetRequest:
-    // Defer the task and perform the reset of the driver during the
-    // next "safe" situation.  You cannot reset the driver right now,
-    // as this code is called from the driver.  Reset the driver is
-    // done by completely destruct is. I.e. ASIOStop(),
-    // ASIODisposeBuffers(), Destruction Afterwards you initialize the
-    // driver again.
-    fprintf(stderr, "\nRtAudio: ASIO driver reset requested!!!");
-    ret = 1L;
-    break;
-  case kAsioResyncRequest:
-    // This informs the application that the driver encountered some
-    // non-fatal data loss.  It is used for synchronization purposes
-    // of different media.  Added mainly to work around the Win16Mutex
-    // problems in Windows 95/98 with the Windows Multimedia system,
-    // which could lose data because the Mutex was held too long by
-    // another thread.  However a driver can issue it in other
-    // situations, too.
-    fprintf(stderr, "\nRtAudio: ASIO driver resync requested!!!");
-    ret = 1L;
-    break;
-  case kAsioLatenciesChanged:
-    // This will inform the host application that the drivers were
-    // latencies changed.  Beware, it this does not mean that the
-    // buffer sizes have changed!  You might need to update internal
-    // delay data.
-    fprintf(stderr, "\nRtAudio: ASIO driver latency may have changed!!!");
-    ret = 1L;
-    break;
-  case kAsioEngineVersion:
-    // Return the supported ASIO version of the host application.  If
-    // a host application does not implement this selector, ASIO 1.0
-    // is assumed by the driver.
-    ret = 2L;
-    break;
-  case kAsioSupportsTimeInfo:
-    // Informs the driver whether the
-    // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
-    // For compatibility with ASIO 1.0 drivers the host application
-    // should always support the "old" bufferSwitch method, too.
-    ret = 0;
-    break;
-  case kAsioSupportsTimeCode:
-    // Informs the driver wether application is interested in time
-    // code info.  If an application does not need to know about time
-    // code, the driver has less work to do.
-    ret = 0;
-    break;
+  for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {
+    if ( getDeviceInfo( i ).isDefaultOutput ) {
+      return i;
+    }
   }
   }
-  return ret;
+
+  return 0;
 }
 
 }
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
+//-----------------------------------------------------------------------------
+
+unsigned int RtApiWasapi::getDefaultInputDevice( void )
 {
 {
-  // Don't attempt to load another driver if a stream is already open.
-  if ( streams.size() > 0 ) {
-    sprintf(message, "RtAudio: unable to load ASIO driver while a stream is open.");
-    error(RtError::WARNING);
-    return FAILURE;
+  for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {
+    if ( getDeviceInfo( i ).isDefaultInput ) {
+      return i;
+    }
   }
 
   }
 
-  // For ASIO, a duplex stream MUST use the same driver.
-  if ( mode == INPUT && stream->mode == OUTPUT && stream->device[0] != device ) {
-    sprintf(message, "RtAudio: ASIO duplex stream must use the same device for input and output.");
-    error(RtError::WARNING);
-    return FAILURE;
+  return 0;
+}
+
+//-----------------------------------------------------------------------------
+
+void RtApiWasapi::closeStream( void )
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiWasapi::closeStream: No open stream to close.";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  // Only load the driver once for duplex stream.
-  ASIOError result;
-  if ( mode != INPUT || stream->mode != OUTPUT ) {
-    if ( !drivers.loadDriver( devices[device].name ) ) {
-      sprintf(message, "RtAudio: ASIO error loading driver (%s).", devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  if ( stream_.state != STREAM_STOPPED )
+    stopStream();
 
 
-    result = ASIOInit( &driverInfo );
-    if ( result != ASE_OK ) {
-      char details[32];
-      if ( result == ASE_HWMalfunction )
-        sprintf(details, "hardware malfunction");
-      else if ( result == ASE_NoMemory )
-        sprintf(details, "no memory");
-      else if ( result == ASE_NotPresent )
-        sprintf(details, "driver/hardware not present");
-      else
-        sprintf(details, "unspecified");
-      sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+  // clean up stream memory
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )
+
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )
+  SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )
+
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )
+    CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );
+
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )
+    CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );
+
+  delete ( WasapiHandle* ) stream_.apiHandle;
+  stream_.apiHandle = NULL;
+
+  for ( int i = 0; i < 2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
 
     }
   }
 
-  // Check the device channel count.
-  long inputChannels, outputChannels;
-  result = ASIOGetChannels( &inputChannels, &outputChannels );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
   }
 
-  if ( ( mode == OUTPUT && channels > outputChannels) ||
-       ( mode == INPUT && channels > inputChannels) ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) does not support requested channel count (%d).",
-            devices[device].name, channels);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-  stream->nDeviceChannels[mode] = channels;
-  stream->nUserChannels[mode] = channels;
+  // update stream state
+  stream_.state = STREAM_CLOSED;
+}
 
 
-  // Verify the sample rate is supported.
-  result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) does not support requested sample rate (%d).",
-            devices[device].name, sampleRate);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+//-----------------------------------------------------------------------------
 
 
-  // Set the sample rate.
-  result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) error setting sample rate (%d).",
-            devices[device].name, sampleRate);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+void RtApiWasapi::startStream( void )
+{
+  verifyStream();
 
 
-  // Determine the driver data type.
-  ASIOChannelInfo channelInfo;
-  channelInfo.channel = 0;
-  if ( mode == OUTPUT ) channelInfo.isInput = false;
-  else channelInfo.isInput = true;
-  result = ASIOGetChannelInfo( &channelInfo );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) error getting data format.",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiWasapi::startStream: The stream is already running.";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  // Assuming WINDOWS host is always little-endian.
-  stream->doByteSwap[mode] = false;
-  stream->userFormat = format;
-  stream->deviceFormat[mode] = 0;
-  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT16;
-    if ( channelInfo.type == ASIOSTInt16MSB ) stream->doByteSwap[mode] = true;
-  }
-  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    if ( channelInfo.type == ASIOSTInt32MSB ) stream->doByteSwap[mode] = true;
-  }
-  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-    if ( channelInfo.type == ASIOSTFloat32MSB ) stream->doByteSwap[mode] = true;
+  // update stream state
+  stream_.state = STREAM_RUNNING;
+
+  // create WASAPI stream thread
+  stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );
+
+  if ( !stream_.callbackInfo.thread ) {
+    errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";
+    error( RtAudioError::THREAD_ERROR );
   }
   }
-  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
-    if ( channelInfo.type == ASIOSTFloat64MSB ) stream->doByteSwap[mode] = true;
+  else {
+    SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );
+    ResumeThread( ( void* ) stream_.callbackInfo.thread );
   }
   }
+}
 
 
-  if ( stream->deviceFormat[mode] == 0 ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+//-----------------------------------------------------------------------------
 
 
-  // Set the buffer size.  For a duplex stream, this will end up
-  // setting the buffer size based on the input constraints, which
-  // should be ok.
-  long minSize, maxSize, preferSize, granularity;
-  result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) error getting buffer size.",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+void RtApiWasapi::stopStream( void )
+{
+  verifyStream();
 
 
-  if ( *bufferSize < minSize ) *bufferSize = minSize;
-  else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
-  else if ( granularity == -1 ) {
-    // Make sure bufferSize is a power of two.
-    double power = log10( *bufferSize ) / log10( 2.0 );
-    *bufferSize = pow( 2.0, floor(power+0.5) );
-    if ( *bufferSize < minSize ) *bufferSize = minSize;
-    else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
-    else *bufferSize = preferSize;
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  if ( mode == INPUT && stream->mode == OUTPUT && stream->bufferSize != *bufferSize )
-    cout << "possible input/output buffersize discrepancy" << endl;
+  // inform stream thread by setting stream state to STREAM_STOPPING
+  stream_.state = STREAM_STOPPING;
 
 
-  stream->bufferSize = *bufferSize;
-  stream->nBuffers = 2;
+  // wait until stream thread is stopped
+  while( stream_.state != STREAM_STOPPED ) {
+    Sleep( 1 );
+  }
 
 
-  // ASIO always uses deinterleaved channels.
-  stream->deInterleave[mode] = true;
+  // Wait for the last buffer to play before stopping.
+  Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
 
 
-  // Create the ASIO internal buffers.  Since RtAudio sets up input
-  // and output separately, we'll have to dispose of previously
-  // created output buffers for a duplex stream.
-  if ( mode == INPUT && stream->mode == OUTPUT ) {
-    free(stream->callbackInfo.buffers);
-    result = ASIODisposeBuffers();
-    if ( result != ASE_OK ) {
-      drivers.removeCurrentDriver();
-      sprintf(message, "RtAudio: ASIO driver (%s) error disposing previously allocated buffers.",
-              devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+  // stop capture client if applicable
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
+    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";
+      error( RtAudioError::DRIVER_ERROR );
+      return;
     }
   }
 
     }
   }
 
-  // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
-  int i, nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
-  stream->callbackInfo.buffers = 0;
-  ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
-  stream->callbackInfo.buffers = (void *) bufferInfos;
-  ASIOBufferInfo *infos = bufferInfos;
-  for ( i=0; i<stream->nDeviceChannels[1]; i++, infos++ ) {
-    infos->isInput = ASIOTrue;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
-  }
-
-  for ( i=0; i<stream->nDeviceChannels[0]; i++, infos++ ) {
-    infos->isInput = ASIOFalse;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
+  // stop render client if applicable
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
+    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";
+      error( RtAudioError::DRIVER_ERROR );
+      return;
+    }
   }
 
   }
 
-  // Set up the ASIO callback structure and create the ASIO data buffers.
-  asioCallbacks.bufferSwitch = &bufferSwitch;
-  asioCallbacks.sampleRateDidChange = &sampleRateChanged;
-  asioCallbacks.asioMessage = &asioMessages;
-  asioCallbacks.bufferSwitchTimeInfo = NULL;
-  result = ASIOCreateBuffers( bufferInfos, nChannels, stream->bufferSize, &asioCallbacks);
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message, "RtAudio: ASIO driver (%s) error creating buffers.",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // close thread handle
+  if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
+    errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
+    error( RtAudioError::THREAD_ERROR );
+    return;
   }
 
   }
 
-  // Set flags for buffer conversion.
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
-    stream->doConvertBuffer[mode] = true;
+  stream_.callbackInfo.thread = (ThreadHandle) NULL;
+}
 
 
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+//-----------------------------------------------------------------------------
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
+void RtApiWasapi::abortStream( void )
+{
+  verifyStream();
 
 
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  // inform stream thread by setting stream state to STREAM_STOPPING
+  stream_.state = STREAM_STOPPING;
 
 
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
+  // wait until stream thread is stopped
+  while ( stream_.state != STREAM_STOPPED ) {
+    Sleep( 1 );
+  }
+
+  // stop capture client if applicable
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
+    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";
+      error( RtAudioError::DRIVER_ERROR );
+      return;
     }
     }
+  }
 
 
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
+  // stop render client if applicable
+  if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
+    HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";
+      error( RtAudioError::DRIVER_ERROR );
+      return;
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
-    // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->sampleRate = sampleRate;
-  asioCallbackInfo = &stream->callbackInfo;
-  stream->callbackInfo.object = (void *) this;
-  stream->callbackInfo.waitTime = (unsigned long) (200.0 * stream->bufferSize / stream->sampleRate);
+  // close thread handle
+  if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
+    errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";
+    error( RtAudioError::THREAD_ERROR );
+    return;
+  }
 
 
-  return SUCCESS;
+  stream_.callbackInfo.thread = (ThreadHandle) NULL;
+}
 
 
- memory_error:
-  ASIODisposeBuffers();
-  drivers.removeCurrentDriver();
+//-----------------------------------------------------------------------------
 
 
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
-  stream->callbackInfo.buffers = 0;
+bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int* bufferSize,
+                                   RtAudio::StreamOptions* options )
+{
+  bool methodResult = FAILURE;
+  unsigned int captureDeviceCount = 0;
+  unsigned int renderDeviceCount = 0;
 
 
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
-  }
-  sprintf(message, "RtAudio: error allocating buffer memory (%s).",
-          devices[device].name);
-  error(RtError::WARNING);
-  return FAILURE;
-}
+  IMMDeviceCollection* captureDevices = NULL;
+  IMMDeviceCollection* renderDevices = NULL;
+  IMMDevice* devicePtr = NULL;
+  WAVEFORMATEX* deviceFormat = NULL;
+  unsigned int bufferBytes;
+  stream_.state = STREAM_STOPPED;
+  RtAudio::DeviceInfo deviceInfo;
 
 
-void RtAudio :: cancelStreamCallback(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // create API Handle if not already created
+  if ( !stream_.apiHandle )
+    stream_.apiHandle = ( void* ) new WasapiHandle();
 
 
-  if (stream->callbackInfo.usingCallback) {
+  // Count capture devices
+  errorText_.clear();
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
+  HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";
+    goto Exit;
+  }
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  hr = captureDevices->GetCount( &captureDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";
+    goto Exit;
+  }
 
 
-    MUTEX_LOCK(&stream->mutex);
+  // Count render devices
+  hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";
+    goto Exit;
+  }
 
 
-    stream->callbackInfo.usingCallback = false;
-    stream->callbackInfo.userData = NULL;
-    stream->state = STREAM_STOPPED;
-    stream->callbackInfo.callback = NULL;
+  hr = renderDevices->GetCount( &renderDeviceCount );
+  if ( FAILED( hr ) ) {
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";
+    goto Exit;
+  }
 
 
-    MUTEX_UNLOCK(&stream->mutex);
+  // validate device index
+  if ( device >= captureDeviceCount + renderDeviceCount ) {
+    errorType = RtAudioError::INVALID_USE;
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";
+    goto Exit;
   }
   }
-}
 
 
-void RtAudio :: closeStream(int streamId)
-{
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
-    return;
+  deviceInfo = getDeviceInfo( device );
+
+  // validate sample rate
+  if ( sampleRate != deviceInfo.preferredSampleRate )
+  {
+    errorType = RtAudioError::INVALID_USE;
+    std::stringstream ss;
+    ss << "RtApiWasapi::probeDeviceOpen: " << sampleRate
+       << "Hz sample rate not supported. This device only supports "
+       << deviceInfo.preferredSampleRate << "Hz.";
+    errorText_ = ss.str();
+    goto Exit;
   }
 
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  // determine whether index falls within capture or render devices
+  if ( device >= renderDeviceCount ) {
+    if ( mode != INPUT ) {
+      errorType = RtAudioError::INVALID_USE;
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";
+      goto Exit;
+    }
 
 
-  if (stream->state == STREAM_RUNNING)
-    ASIOStop();
+    // retrieve captureAudioClient from devicePtr
+    IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
 
 
-  ASIODisposeBuffers();
-  //ASIOExit();
-  drivers.removeCurrentDriver();
+    hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";
+      goto Exit;
+    }
 
 
-  DeleteCriticalSection(&stream->mutex);
+    hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
+                              NULL, ( void** ) &captureAudioClient );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
+      goto Exit;
+    }
 
 
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
+    hr = captureAudioClient->GetMixFormat( &deviceFormat );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
+      goto Exit;
+    }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+    stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
+    captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
+  }
+  else {
+    if ( mode != OUTPUT ) {
+      errorType = RtAudioError::INVALID_USE;
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Render device selected as input device.";
+      goto Exit;
+    }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+    // retrieve renderAudioClient from devicePtr
+    IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
 
 
-  free(stream);
-  streams.erase(streamId);
-}
+    hr = renderDevices->Item( device, &devicePtr );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";
+      goto Exit;
+    }
 
 
-void RtAudio :: startStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
+                              NULL, ( void** ) &renderAudioClient );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device audio client.";
+      goto Exit;
+    }
 
 
-  MUTEX_LOCK(&stream->mutex);
+    hr = renderAudioClient->GetMixFormat( &deviceFormat );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve device mix format.";
+      goto Exit;
+    }
 
 
-  if (stream->state == STREAM_RUNNING) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
+    stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
+    renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
   }
 
   }
 
-  stream->callbackInfo.blockTick = true;
-  stream->callbackInfo.stopStream = false;
-  stream->callbackInfo.streamId = streamId;
-  ASIOError result = ASIOStart();
-  if ( result != ASE_OK ) {
-    sprintf(message, "RtAudio: ASIO error starting device (%s).",
-              devices[stream->device[0]].name);
-    MUTEX_UNLOCK(&stream->mutex);
-    error(RtError::DRIVER_ERROR);
+  // fill stream data
+  if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||
+       ( stream_.mode == INPUT && mode == OUTPUT ) ) {
+    stream_.mode = DUPLEX;
+  }
+  else {
+    stream_.mode = mode;
   }
   }
-  stream->state = STREAM_RUNNING;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  stream_.device[mode] = device;
+  stream_.doByteSwap[mode] = false;
+  stream_.sampleRate = sampleRate;
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 1;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = firstChannel;
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = deviceInfo.nativeFormats;
 
 
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
+    stream_.userInterleaved = false;
+  else
+    stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] = true;
 
 
-  MUTEX_LOCK(&stream->mutex);
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] ||
+       stream_.nUserChannels != stream_.nDeviceChannels )
+    stream_.doConvertBuffer[mode] = true;
+  else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+            stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
 
 
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
-  }
+  if ( stream_.doConvertBuffer[mode] )
+    setConvertInfo( mode, 0 );
 
 
-  ASIOError result = ASIOStop();
-  if ( result != ASE_OK ) {
-    sprintf(message, "RtAudio: ASIO error stopping device (%s).",
-              devices[stream->device[0]].name);
-    MUTEX_UNLOCK(&stream->mutex);
-    error(RtError::DRIVER_ERROR);
+  // Allocate necessary internal buffers
+  bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );
+
+  stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );
+  if ( !stream_.userBuffer[mode] ) {
+    errorType = RtAudioError::MEMORY_ERROR;
+    errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";
+    goto Exit;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )
+    stream_.callbackInfo.priority = 15;
+  else
+    stream_.callbackInfo.priority = 0;
+
+  ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback
+  ///! TODO: RTAUDIO_HOG_DEVICE       // Exclusive mode
+
+  methodResult = SUCCESS;
+
+Exit:
+  //clean up
+  SAFE_RELEASE( captureDevices );
+  SAFE_RELEASE( renderDevices );
+  SAFE_RELEASE( devicePtr );
+  CoTaskMemFree( deviceFormat );
+
+  // if method failed, close the stream
+  if ( methodResult == FAILURE )
+    closeStream();
+
+  if ( !errorText_.empty() )
+    error( errorType );
+  return methodResult;
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+//=============================================================================
+
+DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )
 {
 {
-  stopStream( streamId );
+  if ( wasapiPtr )
+    ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();
+
+  return 0;
 }
 
 }
 
-// I don't know how this function can be implemented.
-int RtAudio :: streamWillBlock(int streamId)
+DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )
 {
 {
-  sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for ASIO.");
-  error(RtError::WARNING);
+  if ( wasapiPtr )
+    ( ( RtApiWasapi* ) wasapiPtr )->stopStream();
+
   return 0;
 }
 
   return 0;
 }
 
-void RtAudio :: tickStream(int streamId)
+DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( wasapiPtr )
+    ( ( RtApiWasapi* ) wasapiPtr )->abortStream();
 
 
-  if (stream->state == STREAM_STOPPED)
-    return;
+  return 0;
+}
 
 
-  if (stream->callbackInfo.usingCallback) {
-    sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
-    return;
-  }
+//-----------------------------------------------------------------------------
 
 
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    Sleep(stream->callbackInfo.waitTime);
+void RtApiWasapi::wasapiThread()
+{
+  // as this is a new thread, we must CoInitialize it
+  CoInitialize( NULL );
+
+  HRESULT hr;
+
+  IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
+  IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
+  IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;
+  IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;
+  HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;
+  HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;
+
+  WAVEFORMATEX* captureFormat = NULL;
+  WAVEFORMATEX* renderFormat = NULL;
+  WasapiBuffer captureBuffer;
+  WasapiBuffer renderBuffer;
+
+  // declare local stream variables
+  RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
+  BYTE* streamBuffer = NULL;
+  unsigned long captureFlags = 0;
+  unsigned int bufferFrameCount = 0;
+  unsigned int numFramesPadding = 0;
+  bool callbackPushed = false;
+  bool callbackPulled = false;
+  bool callbackStopped = false;
+  int callbackResult = 0;
+
+  unsigned int deviceBuffSize = 0;
+
+  errorText_.clear();
+  RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
+
+  // Attempt to assign "Pro Audio" characteristic to thread
+  HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );
+  if ( AvrtDll ) {
+    DWORD taskIndex = 0;
+    TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
+    AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );
+    FreeLibrary( AvrtDll );
+  }
+
+  // start capture stream if applicable
+  if ( captureAudioClient ) {
+    hr = captureAudioClient->GetMixFormat( &captureFormat );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
+      goto Exit;
+    }
+
+    // initialize capture stream according to desire buffer size
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / captureFormat->nSamplesPerSec );
+
+    if ( !captureClient ) {
+      hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
+                                           AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                           desiredBufferPeriod,
+                                           desiredBufferPeriod,
+                                           captureFormat,
+                                           NULL );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";
+        goto Exit;
+      }
 
 
-  MUTEX_LOCK(&stream->mutex);
+      hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),
+                                           ( void** ) &captureClient );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";
+        goto Exit;
+      }
 
 
-  stream->callbackInfo.blockTick = true;
+      // configure captureEvent to trigger on every available capture buffer
+      captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+      if ( !captureEvent ) {
+        errorType = RtAudioError::SYSTEM_ERROR;
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to create capture event.";
+        goto Exit;
+      }
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+      hr = captureAudioClient->SetEventHandle( captureEvent );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";
+        goto Exit;
+      }
 
 
-void RtAudio :: callbackEvent(int streamId, int bufferIndex, void *inData, void *outData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+      ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
+      ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;
+    }
 
 
-  CALLBACK_INFO *info = asioCallbackInfo;
-  if ( !info->usingCallback ) {
-    // Block waiting here until we get new user data in tickStream().
-    while ( !info->blockTick )
-      Sleep(info->waitTime);
-  }
-  else if ( info->stopStream ) {
-    // Check if the stream should be stopped (via the previous user
-    // callback return value).  We stop the stream here, rather than
-    // after the function call, so that output data can first be
-    // processed.
-    this->stopStream(asioCallbackInfo->streamId);
-    return;
+    unsigned int inBufferSize = 0;
+    hr = captureAudioClient->GetBufferSize( &inBufferSize );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";
+      goto Exit;
+    }
+
+    // scale outBufferSize according to stream->user sample rate ratio
+    unsigned int outBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[INPUT];
+    inBufferSize *= stream_.nDeviceChannels[INPUT];
+
+    // set captureBuffer size
+    captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );
+
+    // reset the capture stream
+    hr = captureAudioClient->Reset();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
+      goto Exit;
+    }
+
+    // start the capture stream
+    hr = captureAudioClient->Start();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
+      goto Exit;
+    }
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
-  int nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
-  int bufferBytes;
-  ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) info->buffers;
-  if ( stream->mode == INPUT || stream->mode == DUPLEX ) {
+  // start render stream if applicable
+  if ( renderAudioClient ) {
+    hr = renderAudioClient->GetMixFormat( &renderFormat );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
+      goto Exit;
+    }
+
+    // initialize render stream according to desire buffer size
+    REFERENCE_TIME desiredBufferPeriod = ( REFERENCE_TIME ) ( ( float ) stream_.bufferSize * 10000000 / renderFormat->nSamplesPerSec );
 
 
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[1]);
-    if (stream->doConvertBuffer[1]) {
+    if ( !renderClient ) {
+      hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
+                                          AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                          desiredBufferPeriod,
+                                          desiredBufferPeriod,
+                                          renderFormat,
+                                          NULL );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";
+        goto Exit;
+      }
 
 
-      // Always interleave ASIO input data.
-      for ( int i=0; i<stream->nDeviceChannels[1]; i++, bufferInfos++ )
-        memcpy(&stream->deviceBuffer[i*bufferBytes], bufferInfos->buffers[bufferIndex], bufferBytes );
+      hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),
+                                          ( void** ) &renderClient );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";
+        goto Exit;
+      }
 
 
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
+      // configure renderEvent to trigger on every available render buffer
+      renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+      if ( !renderEvent ) {
+        errorType = RtAudioError::SYSTEM_ERROR;
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to create render event.";
+        goto Exit;
+      }
+
+      hr = renderAudioClient->SetEventHandle( renderEvent );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to set render event handle.";
+        goto Exit;
+      }
+
+      ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;
+      ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;
+    }
+
+    unsigned int outBufferSize = 0;
+    hr = renderAudioClient->GetBufferSize( &outBufferSize );
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";
+      goto Exit;
+    }
+
+    // scale inBufferSize according to user->stream sample rate ratio
+    unsigned int inBufferSize = ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[OUTPUT];
+    outBufferSize *= stream_.nDeviceChannels[OUTPUT];
 
 
+    // set renderBuffer size
+    renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );
+
+    // reset the render stream
+    hr = renderAudioClient->Reset();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
+      goto Exit;
     }
     }
-    else { // single channel only
-      memcpy(stream->userBuffer, bufferInfos->buffers[bufferIndex], bufferBytes );
 
 
-      if (stream->doByteSwap[1])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[1],
-                       stream->userFormat);
+    // start the render stream
+    hr = renderAudioClient->Start();
+    if ( FAILED( hr ) ) {
+      errorText_ = "RtApiWasapi::wasapiThread: Unable to start render stream.";
+      goto Exit;
     }
   }
 
     }
   }
 
-  if ( info->usingCallback ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    if ( callback(stream->userBuffer, stream->bufferSize, info->userData) )
-      info->stopStream = true;
+  if ( stream_.mode == INPUT ) {
+    using namespace std; // for roundf
+    deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
+  }
+  else if ( stream_.mode == OUTPUT ) {
+    deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
+  }
+  else if ( stream_.mode == DUPLEX ) {
+    deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
+                               stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
+  }
+
+  stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );
+  if ( !stream_.deviceBuffer ) {
+    errorType = RtAudioError::MEMORY_ERROR;
+    errorText_ = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
+    goto Exit;
   }
 
   }
 
-  if ( stream->mode == OUTPUT || stream->mode == DUPLEX ) {
+  // stream process loop
+  while ( stream_.state != STREAM_STOPPING ) {
+    if ( !callbackPulled ) {
+      // Callback Input
+      // ==============
+      // 1. Pull callback buffer from inputBuffer
+      // 2. If 1. was successful: Convert callback buffer to user format
+
+      if ( captureAudioClient ) {
+        // Pull callback buffer from inputBuffer
+        callbackPulled = captureBuffer.pullBuffer( stream_.deviceBuffer,
+                                                   ( unsigned int ) stream_.bufferSize * stream_.nDeviceChannels[INPUT],
+                                                   stream_.deviceFormat[INPUT] );
+
+        if ( callbackPulled ) {
+          if ( stream_.doConvertBuffer[INPUT] ) {
+            // Convert callback buffer to user format
+            convertBuffer( stream_.userBuffer[INPUT],
+                           stream_.deviceBuffer,
+                           stream_.convertInfo[INPUT] );
+          }
+          else {
+            // no further conversion, simple copy deviceBuffer to userBuffer
+            memcpy( stream_.userBuffer[INPUT],
+                    stream_.deviceBuffer,
+                    stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );
+          }
+        }
+      }
+      else {
+        // if there is no capture stream, set callbackPulled flag
+        callbackPulled = true;
+      }
+
+      // Execute Callback
+      // ================
+      // 1. Execute user callback method
+      // 2. Handle return value from callback
+
+      // if callback has not requested the stream to stop
+      if ( callbackPulled && !callbackStopped ) {
+        // Execute user callback method
+        callbackResult = callback( stream_.userBuffer[OUTPUT],
+                                   stream_.userBuffer[INPUT],
+                                   stream_.bufferSize,
+                                   getStreamTime(),
+                                   captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,
+                                   stream_.callbackInfo.userData );
+
+        // Handle return value from callback
+        if ( callbackResult == 1 ) {
+          // instantiate a thread to stop this thread
+          HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );
+          if ( !threadHandle ) {
+            errorType = RtAudioError::THREAD_ERROR;
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";
+            goto Exit;
+          }
+          else if ( !CloseHandle( threadHandle ) ) {
+            errorType = RtAudioError::THREAD_ERROR;
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";
+            goto Exit;
+          }
+
+          callbackStopped = true;
+        }
+        else if ( callbackResult == 2 ) {
+          // instantiate a thread to stop this thread
+          HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );
+          if ( !threadHandle ) {
+            errorType = RtAudioError::THREAD_ERROR;
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";
+            goto Exit;
+          }
+          else if ( !CloseHandle( threadHandle ) ) {
+            errorType = RtAudioError::THREAD_ERROR;
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";
+            goto Exit;
+          }
+
+          callbackStopped = true;
+        }
+      }
+    }
+
+    // Callback Output
+    // ===============
+    // 1. Convert callback buffer to stream format
+    // 2. Push callback buffer into outputBuffer
+
+    if ( renderAudioClient && callbackPulled ) {
+      if ( stream_.doConvertBuffer[OUTPUT] ) {
+        // Convert callback buffer to stream format
+        convertBuffer( stream_.deviceBuffer,
+                       stream_.userBuffer[OUTPUT],
+                       stream_.convertInfo[OUTPUT] );
+
+      }
+
+      // Push callback buffer into outputBuffer
+      callbackPushed = renderBuffer.pushBuffer( stream_.deviceBuffer,
+                                                stream_.bufferSize * stream_.nDeviceChannels[OUTPUT],
+                                                stream_.deviceFormat[OUTPUT] );
+    }
+    else {
+      // if there is no render stream, set callbackPushed flag
+      callbackPushed = true;
+    }
+
+    // Stream Capture
+    // ==============
+    // 1. Get capture buffer from stream
+    // 2. Push capture buffer into inputBuffer
+    // 3. If 2. was successful: Release capture buffer
 
 
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[0]);
-    if (stream->doConvertBuffer[0]) {
+    if ( captureAudioClient ) {
+      // if the callback input buffer was not pulled from captureBuffer, wait for next capture event
+      if ( !callbackPulled ) {
+        WaitForSingleObject( captureEvent, INFINITE );
+      }
 
 
-      convertStreamBuffer(stream, OUTPUT);
-      if ( stream->doByteSwap[0] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[0],
-                       stream->deviceFormat[0]);
+      // Get capture buffer from stream
+      hr = captureClient->GetBuffer( &streamBuffer,
+                                     &bufferFrameCount,
+                                     &captureFlags, NULL, NULL );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";
+        goto Exit;
+      }
 
 
-      // Always de-interleave ASIO output data.
-      for ( int i=0; i<stream->nDeviceChannels[0]; i++, bufferInfos++ ) {
-        memcpy(bufferInfos->buffers[bufferIndex],
-               &stream->deviceBuffer[i*bufferBytes], bufferBytes );
+      if ( bufferFrameCount != 0 ) {
+        // Push capture buffer into inputBuffer
+        if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,
+                                       bufferFrameCount * stream_.nDeviceChannels[INPUT],
+                                       stream_.deviceFormat[INPUT] ) )
+        {
+          // Release capture buffer
+          hr = captureClient->ReleaseBuffer( bufferFrameCount );
+          if ( FAILED( hr ) ) {
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+            goto Exit;
+          }
+        }
+        else
+        {
+          // Inform WASAPI that capture was unsuccessful
+          hr = captureClient->ReleaseBuffer( 0 );
+          if ( FAILED( hr ) ) {
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+            goto Exit;
+          }
+        }
+      }
+      else
+      {
+        // Inform WASAPI that capture was unsuccessful
+        hr = captureClient->ReleaseBuffer( 0 );
+        if ( FAILED( hr ) ) {
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
+          goto Exit;
+        }
       }
     }
       }
     }
-    else { // single channel only
 
 
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
+    // Stream Render
+    // =============
+    // 1. Get render buffer from stream
+    // 2. Pull next buffer from outputBuffer
+    // 3. If 2. was successful: Fill render buffer with next buffer
+    //                          Release render buffer
+
+    if ( renderAudioClient ) {
+      // if the callback output buffer was not pushed to renderBuffer, wait for next render event
+      if ( callbackPulled && !callbackPushed ) {
+        WaitForSingleObject( renderEvent, INFINITE );
+      }
+
+      // Get render buffer from stream
+      hr = renderAudioClient->GetBufferSize( &bufferFrameCount );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";
+        goto Exit;
+      }
+
+      hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );
+      if ( FAILED( hr ) ) {
+        errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";
+        goto Exit;
+      }
+
+      bufferFrameCount -= numFramesPadding;
+
+      if ( bufferFrameCount != 0 ) {
+        hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );
+        if ( FAILED( hr ) ) {
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";
+          goto Exit;
+        }
+
+        // Pull next buffer from outputBuffer
+        // Fill render buffer with next buffer
+        if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,
+                                      bufferFrameCount * stream_.nDeviceChannels[OUTPUT],
+                                      stream_.deviceFormat[OUTPUT] ) )
+        {
+          // Release render buffer
+          hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );
+          if ( FAILED( hr ) ) {
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+            goto Exit;
+          }
+        }
+        else
+        {
+          // Inform WASAPI that render was unsuccessful
+          hr = renderClient->ReleaseBuffer( 0, 0 );
+          if ( FAILED( hr ) ) {
+            errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+            goto Exit;
+          }
+        }
+      }
+      else
+      {
+        // Inform WASAPI that render was unsuccessful
+        hr = renderClient->ReleaseBuffer( 0, 0 );
+        if ( FAILED( hr ) ) {
+          errorText_ = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
+          goto Exit;
+        }
+      }
+    }
 
 
-      memcpy(bufferInfos->buffers[bufferIndex], stream->userBuffer, bufferBytes );
+    // if the callback buffer was pushed renderBuffer reset callbackPulled flag
+    if ( callbackPushed ) {
+      callbackPulled = false;
+      // tick stream time
+      RtApi::tickStreamTime();
     }
     }
+
   }
 
   }
 
-  if ( !info->usingCallback )
-    info->blockTick = false;
+Exit:
+  // clean up
+  CoTaskMemFree( captureFormat );
+  CoTaskMemFree( renderFormat );
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  CoUninitialize();
 
 
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // update stream state
+  stream_.state = STREAM_STOPPED;
 
 
-  stream->callbackInfo.callback = (void *) callback;
-  stream->callbackInfo.userData = userData;
-  stream->callbackInfo.usingCallback = true;
+  if ( errorText_.empty() )
+    return;
+  else
+    error( errorType );
 }
 
 }
 
-//******************** End of __WINDOWS_ASIO__ *********************//
+//******************** End of __WINDOWS_WASAPI__ *********************//
+#endif
+
 
 
-#elif defined(__WINDOWS_DS__) // Windows DirectSound API
+#if defined(__WINDOWS_DS__) // Windows DirectSound API
 
 
+// Modified by Robin Davies, October 2005
+// - Improvements to DirectX pointer chasing. 
+// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
+// - Auto-call CoInitialize for DSOUND and ASIO platforms.
+// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
+// Changed device query structure for RtAudio 4.0.7, January 2010
+
+#include <mmsystem.h>
+#include <mmreg.h>
 #include <dsound.h>
 #include <dsound.h>
+#include <assert.h>
+#include <algorithm>
+
+#if defined(__MINGW32__)
+  // missing from latest mingw winapi
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
+#endif
+
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768
+
+#ifdef _MSC_VER // if Microsoft Visual C++
+#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
+#endif
+
+static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
+{
+  if ( pointer > bufferSize ) pointer -= bufferSize;
+  if ( laterPointer < earlierPointer ) laterPointer += bufferSize;
+  if ( pointer < earlierPointer ) pointer += bufferSize;
+  return pointer >= earlierPointer && pointer < laterPointer;
+}
+
+// A structure to hold various information related to the DirectSound
+// API implementation.
+struct DsHandle {
+  unsigned int drainCounter; // Tracks callback counts when draining
+  bool internalDrain;        // Indicates if stop is initiated from callback or not.
+  void *id[2];
+  void *buffer[2];
+  bool xrun[2];
+  UINT bufferPointer[2];  
+  DWORD dsBufferSize[2];
+  DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
+  HANDLE condition;
+
+  DsHandle()
+    :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; }
+};
 
 // Declarations for utility functions, callbacks, and structures
 // specific to the DirectSound implementation.
 
 // Declarations for utility functions, callbacks, and structures
 // specific to the DirectSound implementation.
-static bool CALLBACK deviceCountCallback(LPGUID lpguid,
-                                         LPCSTR lpcstrDescription,
-                                         LPCSTR lpcstrModule,
-                                         LPVOID lpContext);
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext );
 
 
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCSTR lpcstrDescription,
-                                        LPCSTR lpcstrModule,
-                                        LPVOID lpContext);
+static const char* getErrorString( int code );
 
 
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCSTR lpcstrDescription,
-                                           LPCSTR lpcstrModule,
-                                           LPVOID lpContext);
+static unsigned __stdcall callbackHandler( void *ptr );
 
 
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCSTR lpcstrDescription,
-                                      LPCSTR lpcstrModule,
-                                      LPVOID lpContext);
+struct DsDevice {
+  LPGUID id[2];
+  bool validId[2];
+  bool found;
+  std::string name;
 
 
-static char* getErrorString(int code);
+  DsDevice()
+  : found(false) { validId[0] = false; validId[1] = false; }
+};
 
 
-struct enum_info {
-  char name[64];
-  LPGUID id;
+struct DsProbeData {
   bool isInput;
   bool isInput;
-  bool isValid;
+  std::vector<struct DsDevice>* dsDevices;
 };
 
 };
 
-int RtAudio :: getDefaultInputDevice(void)
+RtApiDs :: RtApiDs()
 {
 {
-  enum_info info;
-  info.name[0] = '\0';
-
-  // Enumerate through devices to find the default output.
-  HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Error performing default input device enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return 0;
-  }
+  // Dsound will run both-threaded. If CoInitialize fails, then just
+  // accept whatever the mainline chose for a threading model.
+  coInitialized_ = false;
+  HRESULT hr = CoInitialize( NULL );
+  if ( !FAILED( hr ) ) coInitialized_ = true;
+}
 
 
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp( devices[i].name, info.name, 64 ) == 0 ) return i;
+RtApiDs :: ~RtApiDs()
+{
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+  if ( coInitialized_ ) CoUninitialize(); // balanced call.
+}
 
 
+// The DirectSound default output is always the first device.
+unsigned int RtApiDs :: getDefaultOutputDevice( void )
+{
   return 0;
 }
 
   return 0;
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+// The DirectSound default input is always the first input device,
+// which is the first capture device enumerated.
+unsigned int RtApiDs :: getDefaultInputDevice( void )
 {
 {
-  enum_info info;
-  info.name[0] = '\0';
-
-  // Enumerate through devices to find the default output.
-  HRESULT result = DirectSoundEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Error performing default output device enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return 0;
-  }
-
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp(devices[i].name, info.name, 64 ) == 0 ) return i;
-
   return 0;
 }
 
   return 0;
 }
 
-void RtAudio :: initialize(void)
+unsigned int RtApiDs :: getDeviceCount( void )
 {
 {
-  int i, ins = 0, outs = 0, count = 0;
-  HRESULT result;
-  nDevices = 0;
+  // Set query flag for previously found devices to false, so that we
+  // can check for any devices that have disappeared.
+  for ( unsigned int i=0; i<dsDevices.size(); i++ )
+    dsDevices[i].found = false;
+
+  // Query DirectSound devices.
+  struct DsProbeData probeInfo;
+  probeInfo.isInput = false;
+  probeInfo.dsDevices = &dsDevices;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+  }
+
+  // Query DirectSoundCapture devices.
+  probeInfo.isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+  }
+
+  // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).
+  for ( unsigned int i=0; i<dsDevices.size(); ) {
+    if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );
+    else i++;
+  }
+
+  return static_cast<unsigned int>(dsDevices.size());
+}
 
 
-  // Count DirectSound devices.
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &outs);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-  // Count DirectSoundCapture devices.
-  result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &ins);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
+  if ( dsDevices.size() == 0 ) {
+    // Force a query of all devices
+    getDeviceCount();
+    if ( dsDevices.size() == 0 ) {
+      errorText_ = "RtApiDs::getDeviceInfo: no devices found!";
+      error( RtAudioError::INVALID_USE );
+      return info;
+    }
   }
 
   }
 
-  count = ins + outs;
-  if (count == 0) return;
-
-  std::vector<enum_info> info(count);
-  for (i=0; i<count; i++) {
-    info[i].name[0] = '\0';
-    if (i < outs) info[i].isInput = false;
-    else info[i].isInput = true;
+  if ( device >= dsDevices.size() ) {
+    errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
   }
 
   }
 
-  // Get playback device info and check capabilities.
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
+  HRESULT result;
+  if ( dsDevices[ device ].validId[0] == false ) goto probeInput;
 
 
-  // Get capture device info and check capabilities.
-  result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
+  LPDIRECTSOUND output;
+  DSCAPS outCaps;
+  result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    goto probeInput;
   }
 
   }
 
-  // Parse the devices and check validity.  Devices are considered
-  // invalid if they cannot be opened, they report < 1 supported
-  // channels, or they report no supported data (capture only).
-  for (i=0; i<count; i++)
-    if ( info[i].isValid ) nDevices++;
+  outCaps.dwSize = sizeof( outCaps );
+  result = output->GetCaps( &outCaps );
+  if ( FAILED( result ) ) {
+    output->Release();
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    goto probeInput;
+  }
 
 
-  if (nDevices == 0) return;
+  // Get output channel information.
+  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
 
 
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
+  // Get sample rate information.
+  info.sampleRates.clear();
+  for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+    if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
+         SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {
+      info.sampleRates.push_back( SAMPLE_RATES[k] );
 
 
-  // Copy the names to our devices structures.
-  int index = 0;
-  for (i=0; i<count; i++) {
-    if ( info[i].isValid )
-      strncpy(devices[index++].name, info[i].name, 64);
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
+        info.preferredSampleRate = SAMPLE_RATES[k];
+    }
   }
 
   }
 
-  //for (i=0;i<nDevices; i++)
-  //probeDeviceInfo(&devices[i]);
+  // Get format information.
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
 
 
-  return;
-}
+  output->Release();
 
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  enum_info dsinfo;
-  strncpy( dsinfo.name, info->name, 64 );
-  dsinfo.isValid = false;
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
 
 
-  // Enumerate through input devices to find the id (if it exists).
-  HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Error performing input device id enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return;
+  if ( dsDevices[ device ].validId[1] == false ) {
+    info.name = dsDevices[ device ].name;
+    info.probed = true;
+    return info;
   }
 
   }
 
-  // Do capture probe first.
-  if ( dsinfo.isValid == false )
-    goto playback_probe;
+ probeInput:
 
 
-  LPDIRECTSOUNDCAPTURE  input;
-  result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto playback_probe;
+  LPDIRECTSOUNDCAPTURE input;
+  result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   }
 
-  DSCCAPS in_caps;
-  in_caps.dwSize = sizeof(in_caps);
-  result = input->GetCaps( &in_caps );
-  if ( FAILED(result) ) {
+  DSCCAPS inCaps;
+  inCaps.dwSize = sizeof( inCaps );
+  result = input->GetCaps( &inCaps );
+  if ( FAILED( result ) ) {
     input->Release();
     input->Release();
-    sprintf(message, "RtAudio: Could not get DirectSound capture capabilities (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto playback_probe;
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
   }
 
   // Get input channel information.
   }
 
   // Get input channel information.
-  info->minInputChannels = 1;
-  info->maxInputChannels = in_caps.dwChannels;
+  info.inputChannels = inCaps.dwChannels;
 
   // Get sample rate and format information.
 
   // Get sample rate and format information.
-  if( in_caps.dwChannels == 2 ) {
-    if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-
-    if ( info->nativeFormats & RTAUDIO_SINT16 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates[info->nSampleRates++] = 11025;
-      if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates[info->nSampleRates++] = 22050;
-      if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates[info->nSampleRates++] = 44100;
-    }
-    else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates[info->nSampleRates++] = 11025;
-      if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates[info->nSampleRates++] = 22050;
-      if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates[info->nSampleRates++] = 44100;
-    }
-  }
-  else if ( in_caps.dwChannels == 1 ) {
-    if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-
-    if ( info->nativeFormats & RTAUDIO_SINT16 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates[info->nSampleRates++] = 11025;
-      if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates[info->nSampleRates++] = 22050;
-      if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates[info->nSampleRates++] = 44100;
-    }
-    else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates[info->nSampleRates++] = 11025;
-      if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates[info->nSampleRates++] = 22050;
-      if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates[info->nSampleRates++] = 44100;
-    }
-  }
-  else info->minInputChannels = 0; // technically, this would be an error
+  std::vector<unsigned int> rates;
+  if ( inCaps.dwChannels >= 2 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );
+    }
+  }
+  else if ( inCaps.dwChannels == 1 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );
+    }
+  }
+  else info.inputChannels = 0; // technically, this would be an error
 
   input->Release();
 
 
   input->Release();
 
- playback_probe:
-
-  dsinfo.isValid = false;
+  if ( info.inputChannels == 0 ) return info;
 
 
-  // Enumerate through output devices to find the id (if it exists).
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Error performing output device id enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return;
+  // Copy the supported rates to the info structure but avoid duplication.
+  bool found;
+  for ( unsigned int i=0; i<rates.size(); i++ ) {
+    found = false;
+    for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {
+      if ( rates[i] == info.sampleRates[j] ) {
+        found = true;
+        break;
+      }
+    }
+    if ( found == false ) info.sampleRates.push_back( rates[i] );
   }
   }
+  std::sort( info.sampleRates.begin(), info.sampleRates.end() );
 
 
-  // Now do playback probe.
-  if ( dsinfo.isValid == false )
-    goto check_parameters;
+  // If device opens for both playback and capture, we determine the channels.
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  LPDIRECTSOUND  output;
-  DSCAPS out_caps;
-  result = DirectSoundCreate( dsinfo.id, &output, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto check_parameters;
-  }
+  if ( device == 0 ) info.isDefaultInput = true;
 
 
-  out_caps.dwSize = sizeof(out_caps);
-  result = output->GetCaps( &out_caps );
-  if ( FAILED(result) ) {
-    output->Release();
-    sprintf(message, "RtAudio: Could not get DirectSound playback capabilities (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto check_parameters;
+  // Copy name and return.
+  info.name = dsDevices[ device ].name;
+  info.probed = true;
+  return info;
+}
+
+bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                 unsigned int firstChannel, unsigned int sampleRate,
+                                 RtAudioFormat format, unsigned int *bufferSize,
+                                 RtAudio::StreamOptions *options )
+{
+  if ( channels + firstChannel > 2 ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
+    return FAILURE;
   }
 
   }
 
-  // Get output channel information.
-  info->minOutputChannels = 1;
-  info->maxOutputChannels = ( out_caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
-
-  // Get sample rate information.  Use capture device rate information
-  // if it exists.
-  if ( info->nSampleRates == 0 ) {
-    info->sampleRates[0] = (int) out_caps.dwMinSecondarySampleRate;
-    info->sampleRates[1] = (int) out_caps.dwMaxSecondarySampleRate;
-    if ( out_caps.dwFlags & DSCAPS_CONTINUOUSRATE )
-      info->nSampleRates = -1;
-    else if ( out_caps.dwMinSecondarySampleRate == out_caps.dwMaxSecondarySampleRate ) {
-      if ( out_caps.dwMinSecondarySampleRate == 0 ) {
-        // This is a bogus driver report ... fake the range and cross
-        // your fingers.
-        info->sampleRates[0] = 11025;
-                               info->sampleRates[1] = 48000;
-        info->nSampleRates = -1; /* continuous range */
-        sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using defaults (%s).",
-                info->name);
-        error(RtError::DEBUG_WARNING);
-      }
-      else {
-        info->nSampleRates = 1;
-                       }
-    }
-    else if ( (out_caps.dwMinSecondarySampleRate < 1000.0) &&
-              (out_caps.dwMaxSecondarySampleRate > 50000.0) ) {
-      // This is a bogus driver report ... support for only two
-      // distant rates.  We'll assume this is a range.
-      info->nSampleRates = -1;
-      sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using range (%s).",
-              info->name);
-      error(RtError::WARNING);
-    }
-    else info->nSampleRates = 2;
+  size_t nDevices = dsDevices.size();
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";
+    return FAILURE;
   }
   }
-  else {
-    // Check input rates against output rate range
-    for ( int i=info->nSampleRates-1; i>=0; i-- ) {
-      if ( info->sampleRates[i] <= out_caps.dwMaxSecondarySampleRate )
-        break;
-      info->nSampleRates--;
+
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
+  }
+
+  if ( mode == OUTPUT ) {
+    if ( dsDevices[ device ].validId[0] == false ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    while ( info->sampleRates[0] < out_caps.dwMinSecondarySampleRate ) {
-      info->nSampleRates--;
-      for ( int i=0; i<info->nSampleRates; i++)
-        info->sampleRates[i] = info->sampleRates[i+1];
-      if ( info->nSampleRates <= 0 ) break;
+  }
+  else { // mode == INPUT
+    if ( dsDevices[ device ].validId[1] == false ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
   }
 
     }
   }
 
-  // Get format information.
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16;
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8;
-
-  output->Release();
-
- check_parameters:
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
-    return;
-  if ( info->nSampleRates == 0 || info->nativeFormats == 0 )
-    return;
-
-  // Determine duplex status.
-  if (info->maxInputChannels < info->maxOutputChannels)
-    info->maxDuplexChannels = info->maxInputChannels;
-  else
-    info->maxDuplexChannels = info->maxOutputChannels;
-  if (info->minInputChannels < info->minOutputChannels)
-    info->minDuplexChannels = info->minInputChannels;
-  else
-    info->minDuplexChannels = info->minOutputChannels;
-
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
-
-  info->probed = true;
-
-  return;
-}
-
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  HRESULT result;
-  HWND hWnd = GetForegroundWindow();
   // According to a note in PortAudio, using GetDesktopWindow()
   // instead of GetForegroundWindow() is supposed to avoid problems
   // that occur when the application's window is not the foreground
   // window.  Also, if the application window closes before the
   // According to a note in PortAudio, using GetDesktopWindow()
   // instead of GetForegroundWindow() is supposed to avoid problems
   // that occur when the application's window is not the foreground
   // window.  Also, if the application window closes before the
-  // DirectSound buffer, DirectSound can crash.  However, for console
-  // applications, no sound was produced when using GetDesktopWindow().
-  long buffer_size;
-  LPVOID audioPtr;
-  DWORD dataLen;
-  int nBuffers;
+  // DirectSound buffer, DirectSound can crash.  In the past, I had
+  // problems when using GetDesktopWindow() but it seems fine now
+  // (January 2010).  I'll leave it commented here.
+  // HWND hWnd = GetForegroundWindow();
+  HWND hWnd = GetDesktopWindow();
 
   // Check the numberOfBuffers parameter and limit the lowest value to
   // two.  This is a judgement call and a value of two is probably too
   // low for capture, but it should work for playback.
 
   // Check the numberOfBuffers parameter and limit the lowest value to
   // two.  This is a judgement call and a value of two is probably too
   // low for capture, but it should work for playback.
-  if (numberOfBuffers < 2)
-    nBuffers = 2;
-  else
-    nBuffers = numberOfBuffers;
+  int nBuffers = 0;
+  if ( options ) nBuffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
+  if ( nBuffers < 2 ) nBuffers = 3;
 
 
-  // Define the wave format structure (16-bit PCM, srate, channels)
+  // Check the lower range of the user-specified buffer size and set
+  // (arbitrarily) to a lower bound of 32.
+  if ( *bufferSize < 32 ) *bufferSize = 32;
+
+  // Create the wave format structure.  The data format setting will
+  // be determined later.
   WAVEFORMATEX waveFormat;
   WAVEFORMATEX waveFormat;
-  ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
+  ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
   waveFormat.wFormatTag = WAVE_FORMAT_PCM;
   waveFormat.wFormatTag = WAVE_FORMAT_PCM;
-  waveFormat.nChannels = channels;
+  waveFormat.nChannels = channels + firstChannel;
   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
 
   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
 
-  // Determine the data format.
-  if ( devices[device].nativeFormats ) { // 8-bit and/or 16-bit support
-    if ( format == RTAUDIO_SINT8 ) {
-      if ( devices[device].nativeFormats & RTAUDIO_SINT8 )
-        waveFormat.wBitsPerSample = 8;
-      else
-        waveFormat.wBitsPerSample = 16;
-    }
-    else {
-      if ( devices[device].nativeFormats & RTAUDIO_SINT16 )
-        waveFormat.wBitsPerSample = 16;
-      else
-        waveFormat.wBitsPerSample = 8;
-    }
-  }
-  else {
-    sprintf(message, "RtAudio: no reported data formats for DirectSound device (%s).",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
-  waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+  // Determine the device buffer size. By default, we'll use the value
+  // defined above (32K), but we will grow it to make allowances for
+  // very large software buffer sizes.
+  DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;
+  DWORD dsPointerLeadTime = 0;
 
 
-  enum_info dsinfo;
-  strncpy( dsinfo.name, devices[device].name, 64 );
-  dsinfo.isValid = false;
+  void *ohandle = 0, *bhandle = 0;
+  HRESULT result;
   if ( mode == OUTPUT ) {
 
   if ( mode == OUTPUT ) {
 
-    if ( devices[device].maxOutputChannels < channels )
-      return FAILURE;
-
-    // Enumerate through output devices to find the id (if it exists).
-    result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Error performing output device id enumeration: %s.",
-              getErrorString(result));
-      error(RtError::DEBUG_WARNING);
+    LPDIRECTSOUND output;
+    result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message, "RtAudio: DS output device (%s) id not found!", devices[device].name);
-      error(RtError::DEBUG_WARNING);
+    DSCAPS outCaps;
+    outCaps.dwSize = sizeof( outCaps );
+    result = output->GetCaps( &outCaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUND  object;
-    LPDIRECTSOUNDBUFFER buffer;
-    DSBUFFERDESC bufferDescription;
-    
-    result = DirectSoundCreate( id, &object, NULL );
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::DEBUG_WARNING);
+    // Check channel information.
+    if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
+      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    // Set cooperative level to DSSCL_EXCLUSIVE
-    result = object->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to set DirectSound cooperative level (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
+    // Check format information.  Use 16-bit format unless not
+    // supported or user requests 8-bit.
+    if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
+         !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
+      waveFormat.wBitsPerSample = 16;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    }
+    else {
+      waveFormat.wBitsPerSample = 8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+    }
+    stream_.userFormat = format;
+
+    // Update wave format structure and buffer information.
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
+
+    // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
+    // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+    // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
+    result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Even though we will write to the secondary buffer, we need to
       return FAILURE;
     }
 
     // Even though we will write to the secondary buffer, we need to
-    // access the primary buffer to set the correct output format.
-    // The default is 8-bit, 22 kHz!
-    // Setup the DS primary buffer description.
-    ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSBUFFERDESC);
+    // access the primary buffer to set the correct output format
+    // (since the default is 8-bit, 22 kHz!).  Setup the DS primary
+    // buffer description.
+    DSBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
+
     // Obtain the primary buffer
     // Obtain the primary buffer
-    result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to access DS primary buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
+    LPDIRECTSOUNDBUFFER buffer;
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Set the primary DS buffer sound format.
       return FAILURE;
     }
 
     // Set the primary DS buffer sound format.
-    result = buffer->SetFormat(&waveFormat);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to set DS primary buffer format (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
+    result = buffer->SetFormat( &waveFormat );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Setup the secondary DS buffer description.
       return FAILURE;
     }
 
     // Setup the secondary DS buffer description.
-    buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
-    ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSBUFFERDESC);
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                  DSBCAPS_GLOBALFOCUS |
                                   DSBCAPS_GETCURRENTPOSITION2 |
                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
                                   DSBCAPS_GETCURRENTPOSITION2 |
                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
-    bufferDescription.dwBufferBytes = buffer_size;
+    bufferDescription.dwBufferBytes = dsBufferSize;
     bufferDescription.lpwfxFormat = &waveFormat;
 
     // Try to create the secondary DS buffer.  If that doesn't work,
     // try to use software mixing.  Otherwise, there's a problem.
     bufferDescription.lpwfxFormat = &waveFormat;
 
     // Try to create the secondary DS buffer.  If that doesn't work,
     // try to use software mixing.  Otherwise, there's a problem.
-    result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                    DSBCAPS_GLOBALFOCUS |
                                     DSBCAPS_GETCURRENTPOSITION2 |
                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing
                                     DSBCAPS_GETCURRENTPOSITION2 |
                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing
-      result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-      if ( FAILED(result) ) {
-        object->Release();
-        sprintf(message, "RtAudio: Unable to create secondary DS buffer (%s): %s.",
-                devices[device].name, getErrorString(result));
-        error(RtError::WARNING);
+      result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+      if ( FAILED( result ) ) {
+        output->Release();
+        errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";
+        errorText_ = errorStream_.str();
         return FAILURE;
       }
     }
 
     // Get the buffer size ... might be different from what we specified.
     DSBCAPS dsbcaps;
         return FAILURE;
       }
     }
 
     // Get the buffer size ... might be different from what we specified.
     DSBCAPS dsbcaps;
-    dsbcaps.dwSize = sizeof(DSBCAPS);
-    buffer->GetCaps(&dsbcaps);
-    buffer_size = dsbcaps.dwBufferBytes;
+    dsbcaps.dwSize = sizeof( DSBCAPS );
+    result = buffer->GetCaps( &dsbcaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    dsBufferSize = dsbcaps.dwBufferBytes;
 
     // Lock the DS buffer
 
     // Lock the DS buffer
-    result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Zero the DS buffer
       return FAILURE;
     }
 
     // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+    ZeroMemory( audioPtr, dataLen );
 
     // Unlock the DS buffer
 
     // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to unlock DS buffer(%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    ohandle = (void *) output;
+    bhandle = (void *) buffer;
+  }
+
+  if ( mode == INPUT ) {
+
+    LPDIRECTSOUNDCAPTURE input;
+    result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    DSCCAPS inCaps;
+    inCaps.dwSize = sizeof( inCaps );
+    result = input->GetCaps( &inCaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    // Check channel information.
+    if ( inCaps.dwChannels < channels + firstChannel ) {
+      errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
+      return FAILURE;
+    }
+
+    // Check format information.  Use 16-bit format unless user
+    // requests 8-bit.
+    DWORD deviceFormats;
+    if ( channels + firstChannel == 2 ) {
+      deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
+      if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
+        waveFormat.wBitsPerSample = 8;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+      }
+      else { // assume 16-bit is supported
+        waveFormat.wBitsPerSample = 16;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      }
+    }
+    else { // channel == 1
+      deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
+      if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
+        waveFormat.wBitsPerSample = 8;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+      }
+      else { // assume 16-bit is supported
+        waveFormat.wBitsPerSample = 16;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      }
+    }
+    stream_.userFormat = format;
+
+    // Update wave format structure and buffer information.
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
+
+    // Setup the secondary DS buffer description.
+    DSCBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
+    bufferDescription.dwFlags = 0;
+    bufferDescription.dwReserved = 0;
+    bufferDescription.dwBufferBytes = dsBufferSize;
+    bufferDescription.lpwfxFormat = &waveFormat;
+
+    // Create the capture buffer.
+    LPDIRECTSOUNDCAPTUREBUFFER buffer;
+    result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    // Get the buffer size ... might be different from what we specified.
+    DSCBCAPS dscbcaps;
+    dscbcaps.dwSize = sizeof( DSCBCAPS );
+    result = buffer->GetCaps( &dscbcaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    dsBufferSize = dscbcaps.dwBufferBytes;
+
+    // NOTE: We could have a problem here if this is a duplex stream
+    // and the play and capture hardware buffer sizes are different
+    // (I'm actually not sure if that is a problem or not).
+    // Currently, we are not verifying that.
+
+    // Lock the capture buffer
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    stream->handle[0].object = (void *) object;
-    stream->handle[0].buffer = (void *) buffer;
-    stream->nDeviceChannels[0] = channels;
+    // Zero the buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    ohandle = (void *) input;
+    bhandle = (void *) buffer;
+  }
+
+  // Set various stream parameters
+  DsHandle *handle = 0;
+  stream_.nDeviceChannels[mode] = channels + firstChannel;
+  stream_.nUserChannels[mode] = channels;
+  stream_.bufferSize = *bufferSize;
+  stream_.channelOffset[mode] = firstChannel;
+  stream_.deviceInterleaved[mode] = true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+
+  // Set flag for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
+    stream_.doConvertBuffer[mode] = true;
+  if (stream_.userFormat != stream_.deviceFormat[mode])
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate necessary internal buffers
+  long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
+  }
+
+  if ( stream_.doConvertBuffer[mode] ) {
+
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
+      }
+    }
+
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
+    }
+  }
+
+  // Allocate our DsHandle structures for the stream.
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new DsHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
+      goto error;
+    }
+
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
+  }
+  else
+    handle = (DsHandle *) stream_.apiHandle;
+  handle->id[mode] = ohandle;
+  handle->buffer[mode] = bhandle;
+  handle->dsBufferSize[mode] = dsBufferSize;
+  handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
+
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up an output stream.
+    stream_.mode = DUPLEX;
+  else
+    stream_.mode = mode;
+  stream_.nBuffers = nBuffers;
+  stream_.sampleRate = sampleRate;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup the callback thread.
+  if ( stream_.callbackInfo.isRunning == false ) {
+    unsigned threadId;
+    stream_.callbackInfo.isRunning = true;
+    stream_.callbackInfo.object = (void *) this;
+    stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
+                                                  &stream_.callbackInfo, 0, &threadId );
+    if ( stream_.callbackInfo.thread == 0 ) {
+      errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
+      goto error;
+    }
+
+    // Boost DS thread priority
+    SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
+  }
+  return SUCCESS;
+
+ error:
+  if ( handle ) {
+    if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
+      LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
+      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      if ( buffer ) buffer->Release();
+      object->Release();
+    }
+    if ( handle->buffer[1] ) {
+      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
+      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+      if ( buffer ) buffer->Release();
+      object->Release();
+    }
+    CloseHandle( handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
+
+  stream_.state = STREAM_CLOSED;
+  return FAILURE;
+}
+
+void RtApiDs :: closeStream()
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  // Stop the callback thread.
+  stream_.callbackInfo.isRunning = false;
+  WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
+  CloseHandle( (HANDLE) stream_.callbackInfo.thread );
+
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  if ( handle ) {
+    if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
+      LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
+      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      if ( buffer ) {
+        buffer->Stop();
+        buffer->Release();
+      }
+      object->Release();
+    }
+    if ( handle->buffer[1] ) {
+      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
+      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+      if ( buffer ) {
+        buffer->Stop();
+        buffer->Release();
+      }
+      object->Release();
+    }
+    CloseHandle( handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
+
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
+}
+
+void RtApiDs :: startStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiDs::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+
+  // Increase scheduler frequency on lesser windows (a side-effect of
+  // increasing timer accuracy).  On greater windows (Win2K or later),
+  // this is already in effect.
+  timeBeginPeriod( 1 ); 
+
+  buffersRolling = false;
+  duplexPrerollBytes = 0;
+
+  if ( stream_.mode == DUPLEX ) {
+    // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
+    duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
+  }
+
+  HRESULT result = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    result = buffer->Start( DSCBSTART_LOOPING );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
+
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  ResetEvent( handle->condition );
+  stream_.state = STREAM_RUNNING;
+
+ unlock:
+  if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );
+}
+
+void RtApiDs :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  HRESULT result = 0;
+  LPVOID audioPtr;
+  DWORD dataLen;
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 2;
+      WaitForSingleObject( handle->condition, INFINITE );  // block until signaled
+    }
+
+    stream_.state = STREAM_STOPPED;
+
+    MUTEX_LOCK( &stream_.mutex );
+
+    // Stop the buffer and clear memory
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // If we start playing again, we must begin at beginning of buffer.
+    handle->bufferPointer[0] = 0;
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    audioPtr = NULL;
+    dataLen = 0;
+
+    stream_.state = STREAM_STOPPED;
+
+    if ( stream_.mode != DUPLEX )
+      MUTEX_LOCK( &stream_.mutex );
+
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // If we start recording again, we must begin at beginning of buffer.
+    handle->bufferPointer[1] = 0;
+  }
+
+ unlock:
+  timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );
+}
+
+void RtApiDs :: abortStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  handle->drainCounter = 2;
+
+  stopStream();
+}
+
+void RtApiDs :: callbackEvent()
+{
+  if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {
+    Sleep( 50 ); // sleep 50 milliseconds
+    return;
+  }
+
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > stream_.nBuffers + 2 ) {
+
+    stream_.state = STREAM_STOPPING;
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else
+      stopStream();
+    return;
+  }
+
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                  stream_.bufferSize, streamTime, status, info->userData );
+    if ( cbReturnValue == 2 ) {
+      stream_.state = STREAM_STOPPING;
+      handle->drainCounter = 2;
+      abortStream();
+      return;
+    }
+    else if ( cbReturnValue == 1 ) {
+      handle->drainCounter = 1;
+      handle->internalDrain = true;
+    }
+  }
+
+  HRESULT result;
+  DWORD currentWritePointer, safeWritePointer;
+  DWORD currentReadPointer, safeReadPointer;
+  UINT nextWritePointer;
+
+  LPVOID buffer1 = NULL;
+  LPVOID buffer2 = NULL;
+  DWORD bufferSize1 = 0;
+  DWORD bufferSize2 = 0;
+
+  char *buffer;
+  long bufferBytes;
+
+  MUTEX_LOCK( &stream_.mutex );
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
+  if ( buffersRolling == false ) {
+    if ( stream_.mode == DUPLEX ) {
+      //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+
+      // It takes a while for the devices to get rolling. As a result,
+      // there's no guarantee that the capture and write device pointers
+      // will move in lockstep.  Wait here for both devices to start
+      // rolling, and then set our buffer pointers accordingly.
+      // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
+      // bytes later than the write buffer.
+
+      // Stub: a serious risk of having a pre-emptive scheduling round
+      // take place between the two GetCurrentPosition calls... but I'm
+      // really not sure how to solve the problem.  Temporarily boost to
+      // Realtime priority, maybe; but I'm not sure what priority the
+      // DirectSound service threads run at. We *should* be roughly
+      // within a ms or so of correct.
+
+      LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+
+      DWORD startSafeWritePointer, startSafeReadPointer;
+
+      result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
+      result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
+      while ( true ) {
+        result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+          errorText_ = errorStream_.str();
+          MUTEX_UNLOCK( &stream_.mutex );
+          error( RtAudioError::SYSTEM_ERROR );
+          return;
+        }
+        result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          MUTEX_UNLOCK( &stream_.mutex );
+          error( RtAudioError::SYSTEM_ERROR );
+          return;
+        }
+        if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;
+        Sleep( 1 );
+      }
+
+      //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+
+      handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
+      if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
+      handle->bufferPointer[1] = safeReadPointer;
+    }
+    else if ( stream_.mode == OUTPUT ) {
+
+      // Set the proper nextWritePosition after initial startup.
+      LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
+      handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
+      if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
+    }
+
+    buffersRolling = true;
+  }
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    
+    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
+      memset( stream_.userBuffer[0], 0, bufferBytes );
+    }
+
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
+      bufferBytes *= formatBytes( stream_.deviceFormat[0] );
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
+    }
+
+    // No byte swapping necessary in DirectSound implementation.
+
+    // Ahhh ... windoze.  16-bit data is signed but 8-bit data is
+    // unsigned.  So, we need to convert our signed 8-bit data here to
+    // unsigned.
+    if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
+      for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
+
+    DWORD dsBufferSize = handle->dsBufferSize[0];
+    nextWritePointer = handle->bufferPointer[0];
+
+    DWORD endWrite, leadPointer;
+    while ( true ) {
+      // Find out where the read and "safe write" pointers are.
+      result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        MUTEX_UNLOCK( &stream_.mutex );
+        error( RtAudioError::SYSTEM_ERROR );
+        return;
+      }
+
+      // We will copy our output buffer into the region between
+      // safeWritePointer and leadPointer.  If leadPointer is not
+      // beyond the next endWrite position, wait until it is.
+      leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];
+      //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;
+      if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;
+      if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset
+      endWrite = nextWritePointer + bufferBytes;
+
+      // Check whether the entire write region is behind the play pointer.
+      if ( leadPointer >= endWrite ) break;
+
+      // If we are here, then we must wait until the leadPointer advances
+      // beyond the end of our next write region. We use the
+      // Sleep() function to suspend operation until that happens.
+      double millis = ( endWrite - leadPointer ) * 1000.0;
+      millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
+      if ( millis < 1.0 ) millis = 1.0;
+      Sleep( (DWORD) millis );
+    }
+
+    if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )
+         || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { 
+      // We've strayed into the forbidden zone ... resync the read pointer.
+      handle->xrun[0] = true;
+      nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;
+      if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;
+      handle->bufferPointer[0] = nextWritePointer;
+      endWrite = nextWritePointer + bufferBytes;
+    }
+
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
+    }
+
+    // Copy our buffer into the DS buffer
+    CopyMemory( buffer1, buffer, bufferSize1 );
+    if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
+
+    // Update our buffer offset and unlock sound buffer
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
+    }
+    nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    handle->bufferPointer[0] = nextWritePointer;
+  }
+
+  // Don't bother draining input
+  if ( handle->drainCounter ) {
+    handle->drainCounter++;
+    goto unlock;
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+
+    // Setup parameters.
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
+      bufferBytes *= formatBytes( stream_.deviceFormat[1] );
+    }
+    else {
+      buffer = stream_.userBuffer[1];
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
+      bufferBytes *= formatBytes( stream_.userFormat );
+    }
+
+    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    long nextReadPointer = handle->bufferPointer[1];
+    DWORD dsBufferSize = handle->dsBufferSize[1];
+
+    // Find out where the write and "safe read" pointers are.
+    result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
+    }
+
+    if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
+    DWORD endRead = nextReadPointer + bufferBytes;
+
+    // Handling depends on whether we are INPUT or DUPLEX. 
+    // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
+    // then a wait here will drag the write pointers into the forbidden zone.
+    // 
+    // In DUPLEX mode, rather than wait, we will back off the read pointer until 
+    // it's in a safe position. This causes dropouts, but it seems to be the only 
+    // practical way to sync up the read and write pointers reliably, given the 
+    // the very complex relationship between phase and increment of the read and write 
+    // pointers.
+    //
+    // In order to minimize audible dropouts in DUPLEX mode, we will
+    // provide a pre-roll period of 0.5 seconds in which we return
+    // zeros from the read buffer while the pointers sync up.
+
+    if ( stream_.mode == DUPLEX ) {
+      if ( safeReadPointer < endRead ) {
+        if ( duplexPrerollBytes <= 0 ) {
+          // Pre-roll time over. Be more agressive.
+          int adjustment = endRead-safeReadPointer;
+
+          handle->xrun[1] = true;
+          // Two cases:
+          //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
+          //     and perform fine adjustments later.
+          //   - small adjustments: back off by twice as much.
+          if ( adjustment >= 2*bufferBytes )
+            nextReadPointer = safeReadPointer-2*bufferBytes;
+          else
+            nextReadPointer = safeReadPointer-bufferBytes-adjustment;
+
+          if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
+
+        }
+        else {
+          // In pre=roll time. Just do it.
+          nextReadPointer = safeReadPointer - bufferBytes;
+          while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
+        }
+        endRead = nextReadPointer + bufferBytes;
+      }
+    }
+    else { // mode == INPUT
+      while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {
+        // See comments for playback.
+        double millis = (endRead - safeReadPointer) * 1000.0;
+        millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
+        if ( millis < 1.0 ) millis = 1.0;
+        Sleep( (DWORD) millis );
+
+        // Wake up and find out where we are now.
+        result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          MUTEX_UNLOCK( &stream_.mutex );
+          error( RtAudioError::SYSTEM_ERROR );
+          return;
+        }
+      
+        if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
+      }
+    }
+
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
+    }
+
+    if ( duplexPrerollBytes <= 0 ) {
+      // Copy our buffer into the DS buffer
+      CopyMemory( buffer, buffer1, bufferSize1 );
+      if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );
+    }
+    else {
+      memset( buffer, 0, bufferSize1 );
+      if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );
+      duplexPrerollBytes -= bufferSize1 + bufferSize2;
+    }
+
+    // Update our buffer offset and unlock sound buffer
+    nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
+    }
+    handle->bufferPointer[1] = nextReadPointer;
+
+    // No byte swapping necessary in DirectSound implementation.
+
+    // If necessary, convert 8-bit data from unsigned to signed.
+    if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
+      for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
+
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+  }
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+  RtApi::tickStreamTime();
+}
+
+// Definitions for utility functions and callbacks
+// specific to the DirectSound implementation.
+
+static unsigned __stdcall callbackHandler( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiDs *object = (RtApiDs *) info->object;
+  bool* isRunning = &info->isRunning;
+
+  while ( *isRunning == true ) {
+    object->callbackEvent();
+  }
+
+  _endthreadex( 0 );
+  return 0;
+}
+
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR /*module*/,
+                                          LPVOID lpContext )
+{
+  struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;
+  std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;
+
+  HRESULT hr;
+  bool validDevice = false;
+  if ( probeInfo.isInput == true ) {
+    DSCCAPS caps;
+    LPDIRECTSOUNDCAPTURE object;
+
+    hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return TRUE;
+
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
+        validDevice = true;
+    }
+    object->Release();
+  }
+  else {
+    DSCAPS caps;
+    LPDIRECTSOUND object;
+    hr = DirectSoundCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return TRUE;
+
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
+        validDevice = true;
+    }
+    object->Release();
+  }
+
+  // If good device, then save its name and guid.
+  std::string name = convertCharPointerToStdString( description );
+  //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
+  if ( lpguid == NULL )
+    name = "Default Device";
+  if ( validDevice ) {
+    for ( unsigned int i=0; i<dsDevices.size(); i++ ) {
+      if ( dsDevices[i].name == name ) {
+        dsDevices[i].found = true;
+        if ( probeInfo.isInput ) {
+          dsDevices[i].id[1] = lpguid;
+          dsDevices[i].validId[1] = true;
+        }
+        else {
+          dsDevices[i].id[0] = lpguid;
+          dsDevices[i].validId[0] = true;
+        }
+        return TRUE;
+      }
+    }
+
+    DsDevice device;
+    device.name = name;
+    device.found = true;
+    if ( probeInfo.isInput ) {
+      device.id[1] = lpguid;
+      device.validId[1] = true;
+    }
+    else {
+      device.id[0] = lpguid;
+      device.validId[0] = true;
+    }
+    dsDevices.push_back( device );
+  }
+
+  return TRUE;
+}
+
+static const char* getErrorString( int code )
+{
+  switch ( code ) {
+
+  case DSERR_ALLOCATED:
+    return "Already allocated";
+
+  case DSERR_CONTROLUNAVAIL:
+    return "Control unavailable";
+
+  case DSERR_INVALIDPARAM:
+    return "Invalid parameter";
+
+  case DSERR_INVALIDCALL:
+    return "Invalid call";
+
+  case DSERR_GENERIC:
+    return "Generic error";
+
+  case DSERR_PRIOLEVELNEEDED:
+    return "Priority level needed";
+
+  case DSERR_OUTOFMEMORY:
+    return "Out of memory";
+
+  case DSERR_BADFORMAT:
+    return "The sample rate or the channel format is not supported";
+
+  case DSERR_UNSUPPORTED:
+    return "Not supported";
+
+  case DSERR_NODRIVER:
+    return "No driver";
+
+  case DSERR_ALREADYINITIALIZED:
+    return "Already initialized";
+
+  case DSERR_NOAGGREGATION:
+    return "No aggregation";
+
+  case DSERR_BUFFERLOST:
+    return "Buffer lost";
+
+  case DSERR_OTHERAPPHASPRIO:
+    return "Another application already has priority";
+
+  case DSERR_UNINITIALIZED:
+    return "Uninitialized";
+
+  default:
+    return "DirectSound unknown error";
+  }
+}
+//******************** End of __WINDOWS_DS__ *********************//
+#endif
+
+
+#if defined(__LINUX_ALSA__)
+
+#include <alsa/asoundlib.h>
+#include <unistd.h>
+
+  // A structure to hold various information related to the ALSA API
+  // implementation.
+struct AlsaHandle {
+  snd_pcm_t *handles[2];
+  bool synchronized;
+  bool xrun[2];
+  pthread_cond_t runnable_cv;
+  bool runnable;
+
+  AlsaHandle()
+    :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }
+};
+
+static void *alsaCallbackHandler( void * ptr );
+
+RtApiAlsa :: RtApiAlsa()
+{
+  // Nothing to do here.
+}
+
+RtApiAlsa :: ~RtApiAlsa()
+{
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+}
+
+unsigned int RtApiAlsa :: getDeviceCount( void )
+{
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *handle;
+
+  // Count cards and devices
+  card = -1;
+  snd_card_next( &card );
+  while ( card >= 0 ) {
+    sprintf( name, "hw:%d", card );
+    result = snd_ctl_open( &handle, name, 0 );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      error( RtAudioError::WARNING );
+      goto nextcard;
+    }
+    subdevice = -1;
+    while( 1 ) {
+      result = snd_ctl_pcm_next_device( handle, &subdevice );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        error( RtAudioError::WARNING );
+        break;
+      }
+      if ( subdevice < 0 )
+        break;
+      nDevices++;
+    }
+  nextcard:
+    snd_ctl_close( handle );
+    snd_card_next( &card );
+  }
+
+  result = snd_ctl_open( &handle, "default", 0 );
+  if (result == 0) {
+    nDevices++;
+    snd_ctl_close( handle );
+  }
+
+  return nDevices;
+}
+
+RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *chandle;
+
+  // Count cards and devices
+  card = -1;
+  subdevice = -1;
+  snd_card_next( &card );
+  while ( card >= 0 ) {
+    sprintf( name, "hw:%d", card );
+    result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      error( RtAudioError::WARNING );
+      goto nextcard;
+    }
+    subdevice = -1;
+    while( 1 ) {
+      result = snd_ctl_pcm_next_device( chandle, &subdevice );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        error( RtAudioError::WARNING );
+        break;
+      }
+      if ( subdevice < 0 ) break;
+      if ( nDevices == device ) {
+        sprintf( name, "hw:%d,%d", card, subdevice );
+        goto foundDevice;
+      }
+      nDevices++;
+    }
+  nextcard:
+    snd_ctl_close( chandle );
+    snd_card_next( &card );
+  }
+
+  result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );
+  if ( result == 0 ) {
+    if ( nDevices == device ) {
+      strcpy( name, "default" );
+      goto foundDevice;
+    }
+    nDevices++;
+  }
+
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
+
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
+
+ foundDevice:
+
+  // If a stream is already open, we cannot probe the stream devices.
+  // Thus, use the saved results.
+  if ( stream_.state != STREAM_CLOSED &&
+       ( stream_.device[0] == device || stream_.device[1] == device ) ) {
+    snd_ctl_close( chandle );
+    if ( device >= devices_.size() ) {
+      errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
+      error( RtAudioError::WARNING );
+      return info;
+    }
+    return devices_[ device ];
+  }
+
+  int openMode = SND_PCM_ASYNC;
+  snd_pcm_stream_t stream;
+  snd_pcm_info_t *pcminfo;
+  snd_pcm_info_alloca( &pcminfo );
+  snd_pcm_t *phandle;
+  snd_pcm_hw_params_t *params;
+  snd_pcm_hw_params_alloca( &params );
+
+  // First try for playback unless default device (which has subdev -1)
+  stream = SND_PCM_STREAM_PLAYBACK;
+  snd_pcm_info_set_stream( pcminfo, stream );
+  if ( subdevice != -1 ) {
+    snd_pcm_info_set_device( pcminfo, subdevice );
+    snd_pcm_info_set_subdevice( pcminfo, 0 );
+
+    result = snd_ctl_pcm_info( chandle, pcminfo );
+    if ( result < 0 ) {
+      // Device probably doesn't support playback.
+      goto captureProbe;
+    }
+  }
+
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    goto captureProbe;
+  }
+
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    goto captureProbe;
+  }
+
+  // Get output channel information.
+  unsigned int value;
+  result = snd_pcm_hw_params_get_channels_max( params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    goto captureProbe;
+  }
+  info.outputChannels = value;
+  snd_pcm_close( phandle );
+
+ captureProbe:
+  stream = SND_PCM_STREAM_CAPTURE;
+  snd_pcm_info_set_stream( pcminfo, stream );
+
+  // Now try for capture unless default device (with subdev = -1)
+  if ( subdevice != -1 ) {
+    result = snd_ctl_pcm_info( chandle, pcminfo );
+    snd_ctl_close( chandle );
+    if ( result < 0 ) {
+      // Device probably doesn't support capture.
+      if ( info.outputChannels == 0 ) return info;
+      goto probeParameters;
+    }
+  }
+  else
+    snd_ctl_close( chandle );
+
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
+
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
+
+  result = snd_pcm_hw_params_get_channels_max( params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
+  info.inputChannels = value;
+  snd_pcm_close( phandle );
+
+  // If device opens for both playback and capture, we determine the channels.
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
+
+  // ALSA doesn't provide default devices so we'll use the first available one.
+  if ( device == 0 && info.outputChannels > 0 )
+    info.isDefaultOutput = true;
+  if ( device == 0 && info.inputChannels > 0 )
+    info.isDefaultInput = true;
+
+ probeParameters:
+  // At this point, we just need to figure out the supported data
+  // formats and sample rates.  We'll proceed by opening the device in
+  // the direction with the maximum number of channels, or playback if
+  // they are equal.  This might limit our sample rate options, but so
+  // be it.
+
+  if ( info.outputChannels >= info.inputChannels )
+    stream = SND_PCM_STREAM_PLAYBACK;
+  else
+    stream = SND_PCM_STREAM_CAPTURE;
+  snd_pcm_info_set_stream( pcminfo, stream );
+
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Test our discrete set of sample rate values.
+  info.sampleRates.clear();
+  for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
+    if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {
+      info.sampleRates.push_back( SAMPLE_RATES[i] );
+
+      if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
+        info.preferredSampleRate = SAMPLE_RATES[i];
+    }
+  }
+  if ( info.sampleRates.size() == 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Probe the supported data formats ... we don't care about endian-ness just yet
+  snd_pcm_format_t format;
+  info.nativeFormats = 0;
+  format = SND_PCM_FORMAT_S8;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT8;
+  format = SND_PCM_FORMAT_S16;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  format = SND_PCM_FORMAT_S24;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT24;
+  format = SND_PCM_FORMAT_S32;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT32;
+  format = SND_PCM_FORMAT_FLOAT;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  format = SND_PCM_FORMAT_FLOAT64;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_FLOAT64;
+
+  // Check that we have at least one supported format
+  if ( info.nativeFormats == 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Get the device name
+  char *cardname;
+  result = snd_card_get_name( card, &cardname );
+  if ( result >= 0 ) {
+    sprintf( name, "hw:%s,%d", cardname, subdevice );
+    free( cardname );
+  }
+  info.name = name;
+
+  // That's all ... close the device and return
+  snd_pcm_close( phandle );
+  info.probed = true;
+  return info;
+}
+
+void RtApiAlsa :: saveDeviceInfo( void )
+{
+  devices_.clear();
+
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
+
+bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+
+{
+#if defined(__RTAUDIO_DEBUG__)
+  snd_output_t *out;
+  snd_output_stdio_attach(&out, stderr, 0);
+#endif
+
+  // I'm not using the "plug" interface ... too much inconsistent behavior.
+
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *chandle;
+
+  if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )
+    snprintf(name, sizeof(name), "%s", "default");
+  else {
+    // Count cards and devices
+    card = -1;
+    snd_card_next( &card );
+    while ( card >= 0 ) {
+      sprintf( name, "hw:%d", card );
+      result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      subdevice = -1;
+      while( 1 ) {
+        result = snd_ctl_pcm_next_device( chandle, &subdevice );
+        if ( result < 0 ) break;
+        if ( subdevice < 0 ) break;
+        if ( nDevices == device ) {
+          sprintf( name, "hw:%d,%d", card, subdevice );
+          snd_ctl_close( chandle );
+          goto foundDevice;
+        }
+        nDevices++;
+      }
+      snd_ctl_close( chandle );
+      snd_card_next( &card );
+    }
+
+    result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );
+    if ( result == 0 ) {
+      if ( nDevices == device ) {
+        strcpy( name, "default" );
+        goto foundDevice;
+      }
+      nDevices++;
+    }
+
+    if ( nDevices == 0 ) {
+      // This should not happen because a check is made before this function is called.
+      errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
+      return FAILURE;
+    }
+
+    if ( device >= nDevices ) {
+      // This should not happen because a check is made before this function is called.
+      errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
+      return FAILURE;
+    }
+  }
+
+ foundDevice:
+
+  // The getDeviceInfo() function will not work for a device that is
+  // already open.  Thus, we'll probe the system before opening a
+  // stream and save the results for use by getDeviceInfo().
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
+    this->saveDeviceInfo();
+
+  snd_pcm_stream_t stream;
+  if ( mode == OUTPUT )
+    stream = SND_PCM_STREAM_PLAYBACK;
+  else
+    stream = SND_PCM_STREAM_CAPTURE;
+
+  snd_pcm_t *phandle;
+  int openMode = SND_PCM_ASYNC;
+  result = snd_pcm_open( &phandle, name, stream, openMode );
+  if ( result < 0 ) {
+    if ( mode == OUTPUT )
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
+    else
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Fill the parameter structure.
+  snd_pcm_hw_params_t *hw_params;
+  snd_pcm_hw_params_alloca( &hw_params );
+  result = snd_pcm_hw_params_any( phandle, hw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
+
+  // Set access ... check user preference.
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
+    stream_.userInterleaved = false;
+    result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
+    if ( result < 0 ) {
+      result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
+      stream_.deviceInterleaved[mode] =  true;
+    }
+    else
+      stream_.deviceInterleaved[mode] = false;
+  }
+  else {
+    stream_.userInterleaved = true;
+    result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
+    if ( result < 0 ) {
+      result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
+      stream_.deviceInterleaved[mode] =  false;
+    }
+    else
+      stream_.deviceInterleaved[mode] =  true;
+  }
+
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Determine how to set the device format.
+  stream_.userFormat = format;
+  snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
+
+  if ( format == RTAUDIO_SINT8 )
+    deviceFormat = SND_PCM_FORMAT_S8;
+  else if ( format == RTAUDIO_SINT16 )
+    deviceFormat = SND_PCM_FORMAT_S16;
+  else if ( format == RTAUDIO_SINT24 )
+    deviceFormat = SND_PCM_FORMAT_S24;
+  else if ( format == RTAUDIO_SINT32 )
+    deviceFormat = SND_PCM_FORMAT_S32;
+  else if ( format == RTAUDIO_FLOAT32 )
+    deviceFormat = SND_PCM_FORMAT_FLOAT;
+  else if ( format == RTAUDIO_FLOAT64 )
+    deviceFormat = SND_PCM_FORMAT_FLOAT64;
+
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
+    stream_.deviceFormat[mode] = format;
+    goto setFormat;
+  }
+
+  // The user requested format is not natively supported by the device.
+  deviceFormat = SND_PCM_FORMAT_FLOAT64;
+  if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    goto setFormat;
+  }
+
+  deviceFormat = SND_PCM_FORMAT_FLOAT;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    goto setFormat;
+  }
+
+  deviceFormat = SND_PCM_FORMAT_S32;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    goto setFormat;
+  }
+
+  deviceFormat = SND_PCM_FORMAT_S24;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    goto setFormat;
+  }
+
+  deviceFormat = SND_PCM_FORMAT_S16;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    goto setFormat;
+  }
+
+  deviceFormat = SND_PCM_FORMAT_S8;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+    goto setFormat;
+  }
+
+  // If we get here, no supported format was found.
+  snd_pcm_close( phandle );
+  errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
+  errorText_ = errorStream_.str();
+  return FAILURE;
+
+ setFormat:
+  result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Determine whether byte-swaping is necessary.
+  stream_.doByteSwap[mode] = false;
+  if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
+    result = snd_pcm_format_cpu_endian( deviceFormat );
+    if ( result == 0 )
+      stream_.doByteSwap[mode] = true;
+    else if (result < 0) {
+      snd_pcm_close( phandle );
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+  }
+
+  // Set the sample rate.
+  result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Determine the number of channels for this device.  We support a possible
+  // minimum device channel number > than the value requested by the user.
+  stream_.nUserChannels[mode] = channels;
+  unsigned int value;
+  result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
+  unsigned int deviceChannels = value;
+  if ( result < 0 || deviceChannels < channels + firstChannel ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  deviceChannels = value;
+  if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
+  stream_.nDeviceChannels[mode] = deviceChannels;
+
+  // Set the device channels.
+  result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Set the buffer (or period) size.
+  int dir = 0;
+  snd_pcm_uframes_t periodSize = *bufferSize;
+  result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  *bufferSize = periodSize;
+
+  // Set the buffer number, which in ALSA is referred to as the "period".
+  unsigned int periods = 0;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
+  if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;
+  if ( periods < 2 ) periods = 4; // a fairly safe default value
+  result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  if ( mode == INPUT ) {
-
-    if ( devices[device].maxInputChannels < channels )
-      return FAILURE;
+  // If attempting to setup a duplex stream, the bufferSize parameter
+  // MUST be the same in both directions!
+  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    // Enumerate through input devices to find the id (if it exists).
-    result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Error performing input device id enumeration: %s.",
-              getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  stream_.bufferSize = *bufferSize;
 
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message, "RtAudio: DS input device (%s) id not found!", devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  // Install the hardware configuration
+  result = snd_pcm_hw_params( phandle, hw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUNDCAPTURE  object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer;
-    DSCBUFFERDESC bufferDescription;
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
 
 
-    result = DirectSoundCaptureCreate( id, &object, NULL );
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
+  snd_pcm_sw_params_t *sw_params = NULL;
+  snd_pcm_sw_params_alloca( &sw_params );
+  snd_pcm_sw_params_current( phandle, sw_params );
+  snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
+  snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
+  snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
+
+  // The following two settings were suggested by Theo Veenker
+  //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
+  //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
+
+  // here are two options for a fix
+  //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
+  snd_pcm_uframes_t val;
+  snd_pcm_sw_params_get_boundary( sw_params, &val );
+  snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
+
+  result = snd_pcm_sw_params( phandle, sw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    // Setup the secondary DS buffer description.
-    buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
-    ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSCBUFFERDESC);
-    bufferDescription.dwFlags = 0;
-    bufferDescription.dwReserved = 0;
-    bufferDescription.dwBufferBytes = buffer_size;
-    bufferDescription.lpwfxFormat = &waveFormat;
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
+  snd_pcm_sw_params_dump( sw_params, out );
+#endif
 
 
-    // Create the capture buffer.
-    result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to create DS capture buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
+  // Set flags for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate the ApiHandle if necessary and then save.
+  AlsaHandle *apiInfo = 0;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      apiInfo = (AlsaHandle *) new AlsaHandle;
     }
     }
-
-    // Lock the capture buffer
-    result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
+      goto error;
     }
 
     }
 
-    // Zero the buffer
-    ZeroMemory(audioPtr, dataLen);
-
-    // Unlock the buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
+    if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
     }
 
     }
 
-    stream->handle[1].object = (void *) object;
-    stream->handle[1].buffer = (void *) buffer;
-    stream->nDeviceChannels[1] = channels;
+    stream_.apiHandle = (void *) apiInfo;
+    apiInfo->handles[0] = 0;
+    apiInfo->handles[1] = 0;
   }
   }
+  else {
+    apiInfo = (AlsaHandle *) stream_.apiHandle;
+  }
+  apiInfo->handles[mode] = phandle;
+  phandle = 0;
 
 
-  stream->userFormat = format;
-  if ( waveFormat.wBitsPerSample == 8 )
-    stream->deviceFormat[mode] = RTAUDIO_SINT8;
-  else
-    stream->deviceFormat[mode] = RTAUDIO_SINT16;
-  stream->nUserChannels[mode] = channels;
-  *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8);
-  stream->bufferSize = *bufferSize;
-
-  // Set flags for buffer conversion
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-    long buffer_bytes;
     bool makeBuffer = true;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
+  stream_.sampleRate = sampleRate;
+  stream_.nBuffers = periods;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup thread if necessary.
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
     // We had already set up an output stream.
     // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->nBuffers = nBuffers;
-  stream->sampleRate = sampleRate;
+    stream_.mode = DUPLEX;
+    // Link the streams if possible.
+    apiInfo->synchronized = false;
+    if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
+      apiInfo->synchronized = true;
+    else {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
+      error( RtAudioError::WARNING );
+    }
+  }
+  else {
+    stream_.mode = mode;
+
+    // Setup callback thread.
+    stream_.callbackInfo.object = (void *) this;
+
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority (optional).  The higher priority will only take affect
+    // if the program is run as root or suid. Note, under Linux
+    // processes with CAP_SYS_NICE privilege, a user can change
+    // scheduling policy and priority (thus need not be root). See
+    // POSIX "capabilities".
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      // We previously attempted to increase the audio callback priority
+      // to SCHED_RR here via the attributes.  However, while no errors
+      // were reported in doing so, it did not work.  So, now this is
+      // done in the alsaCallbackHandler function.
+      stream_.callbackInfo.doRealtime = true;
+      int priority = options->priority;
+      int min = sched_get_priority_min( SCHED_RR );
+      int max = sched_get_priority_max( SCHED_RR );
+      if ( priority < min ) priority = min;
+      else if ( priority > max ) priority = max;
+      stream_.callbackInfo.priority = priority;
+    }
+#endif
+
+    stream_.callbackInfo.isRunning = true;
+    result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
+    pthread_attr_destroy( &attr );
+    if ( result ) {
+      stream_.callbackInfo.isRunning = false;
+      errorText_ = "RtApiAlsa::error creating callback thread!";
+      goto error;
+    }
+  }
 
   return SUCCESS;
 
 
   return SUCCESS;
 
- memory_error:
-  if (stream->handle[0].object) {
-    LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;
-    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    if (buffer) {
-      buffer->Release();
-      stream->handle[0].buffer = NULL;
-    }
-    object->Release();
-    stream->handle[0].object = NULL;
+ error:
+  if ( apiInfo ) {
+    pthread_cond_destroy( &apiInfo->runnable_cv );
+    if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
+    if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
+    delete apiInfo;
+    stream_.apiHandle = 0;
   }
   }
-  if (stream->handle[1].object) {
-    LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    if (buffer) {
-      buffer->Release();
-      stream->handle[1].buffer = NULL;
+
+  if ( phandle) snd_pcm_close( phandle );
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
     }
-    object->Release();
-    stream->handle[1].object = NULL;
   }
   }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  sprintf(message, "RtAudio: error allocating buffer memory (%s).",
-          devices[device].name);
-  error(RtError::WARNING);
+
+  stream_.state = STREAM_CLOSED;
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: cancelStreamCallback(int streamId)
+void RtApiAlsa :: closeStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  if (stream->callbackInfo.usingCallback) {
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  stream_.callbackInfo.isRunning = false;
+  MUTEX_LOCK( &stream_.mutex );
+  if ( stream_.state == STREAM_STOPPED ) {
+    apiInfo->runnable = true;
+    pthread_cond_signal( &apiInfo->runnable_cv );
+  }
+  MUTEX_UNLOCK( &stream_.mutex );
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+      snd_pcm_drop( apiInfo->handles[0] );
+    if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
+      snd_pcm_drop( apiInfo->handles[1] );
+  }
 
 
-    MUTEX_LOCK(&stream->mutex);
+  if ( apiInfo ) {
+    pthread_cond_destroy( &apiInfo->runnable_cv );
+    if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
+    if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
+    delete apiInfo;
+    stream_.apiHandle = 0;
+  }
 
 
-    stream->callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream->callbackInfo.thread );
-    stream->callbackInfo.thread = 0;
-    stream->callbackInfo.callback = NULL;
-    stream->callbackInfo.userData = NULL;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-    MUTEX_UNLOCK(&stream->mutex);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
+
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiAlsa :: startStream()
 {
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
+  // This method calls snd_pcm_prepare if the device isn't already in that state.
+
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  if (stream->callbackInfo.usingCallback) {
-    stream->callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream->callbackInfo.thread );
+  int result = 0;
+  snd_pcm_state_t state;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    state = snd_pcm_state( handle[0] );
+    if ( state != SND_PCM_STATE_PREPARED ) {
+      result = snd_pcm_prepare( handle[0] );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        goto unlock;
+      }
+    }
   }
 
   }
 
-  DeleteCriticalSection(&stream->mutex);
-
-  if (stream->handle[0].object) {
-    LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;
-    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    if (buffer) {
-      buffer->Stop();
-      buffer->Release();
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open
+    state = snd_pcm_state( handle[1] );
+    if ( state != SND_PCM_STATE_PREPARED ) {
+      result = snd_pcm_prepare( handle[1] );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        goto unlock;
+      }
     }
     }
-    object->Release();
   }
 
   }
 
-  if (stream->handle[1].object) {
-    LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    if (buffer) {
-      buffer->Stop();
-      buffer->Release();
+  stream_.state = STREAM_RUNNING;
+
+ unlock:
+  apiInfo->runnable = true;
+  pthread_cond_signal( &apiInfo->runnable_cv );
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result >= 0 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
+}
+
+void RtApiAlsa :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
+
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
+
+  int result = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( apiInfo->synchronized ) 
+      result = snd_pcm_drop( handle[0] );
+    else
+      result = snd_pcm_drain( handle[0] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    object->Release();
   }
 
   }
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    result = snd_pcm_drop( handle[1] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+ unlock:
+  apiInfo->runnable = false; // fixes high CPU usage when stopped
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  free(stream);
-  streams.erase(streamId);
+  if ( result >= 0 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiAlsa :: abortStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  HRESULT result;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    result = buffer->Play(0, 0, DSBPLAY_LOOPING );
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to start DS buffer (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  int result = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = snd_pcm_drop( handle[0] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    result = buffer->Start(DSCBSTART_LOOPING );
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to start DS capture buffer (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    result = snd_pcm_drop( handle[1] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
     }
   }
-  stream->state = STREAM_RUNNING;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  apiInfo->runnable = false; // fixes high CPU usage when stopped
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result >= 0 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiAlsa :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_LOCK( &stream_.mutex );
+    while ( !apiInfo->runnable )
+      pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );
+
+    if ( stream_.state != STREAM_RUNNING ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      return;
+    }
+    MUTEX_UNLOCK( &stream_.mutex );
+  }
 
 
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  // There is no specific DirectSound API call to "drain" a buffer
-  // before stopping.  We can hack this for playback by writing zeroes
-  // for another bufferSize * nBuffers frames.  For capture, the
-  // concept is less clear so we'll repeat what we do in the
-  // abortStream() case.
-  HRESULT result;
-  DWORD dsBufferSize;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  int doStopStream = 0;
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    apiInfo->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    apiInfo->xrun[1] = false;
+  }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
 
 
-    DWORD currentPos, safePos;
-    long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
-    buffer_bytes *= formatBytes(stream->deviceFormat[0]);
+  if ( doStopStream == 2 ) {
+    abortStream();
+    return;
+  }
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    dsBufferSize = buffer_bytes * stream->nBuffers;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // Write zeroes for nBuffer counts.
-    for (int i=0; i<stream->nBuffers; i++) {
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
-      // Find out where the read and "safe write" pointers are.
-      result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-      if ( FAILED(result) ) {
-        sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-                devices[stream->device[0]].name, getErrorString(result));
-        error(RtError::DRIVER_ERROR);
-      }
+  int result;
+  char *buffer;
+  int channels;
+  snd_pcm_t **handle;
+  snd_pcm_sframes_t frames;
+  RtAudioFormat format;
+  handle = (snd_pcm_t **) apiInfo->handles;
 
 
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      DWORD endWrite = nextWritePos + buffer_bytes;
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-      // Check whether the entire write region is behind the play pointer.
-      while ( currentPos < endWrite ) {
-        float millis = (endWrite - currentPos) * 900.0;
-        millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);
-        if ( millis < 1.0 ) millis = 1.0;
-        Sleep( (DWORD) millis );
+    // Setup parameters.
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      channels = stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
+    }
+    else {
+      buffer = stream_.userBuffer[1];
+      channels = stream_.nUserChannels[1];
+      format = stream_.userFormat;
+    }
 
 
-        // Wake up, find out where we are now
-        result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-        if ( FAILED(result) ) {
-          sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-                  devices[stream->device[0]].name, getErrorString(result));
-          error(RtError::DRIVER_ERROR);
+    // Read samples from device in interleaved/non-interleaved format.
+    if ( stream_.deviceInterleaved[1] )
+      result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
+    else {
+      void *bufs[channels];
+      size_t offset = stream_.bufferSize * formatBytes( format );
+      for ( int i=0; i<channels; i++ )
+        bufs[i] = (void *) (buffer + (i * offset));
+      result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
+    }
+
+    if ( result < (int) stream_.bufferSize ) {
+      // Either an error or overrun occured.
+      if ( result == -EPIPE ) {
+        snd_pcm_state_t state = snd_pcm_state( handle[1] );
+        if ( state == SND_PCM_STATE_XRUN ) {
+          apiInfo->xrun[1] = true;
+          result = snd_pcm_prepare( handle[1] );
+          if ( result < 0 ) {
+            errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
+            errorText_ = errorStream_.str();
+          }
+        }
+        else {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
         }
         }
-        if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
       }
       }
-
-      // Lock free space in the buffer
-      result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,
-                               &bufferSize1, &buffer2, &bufferSize2, 0);
-      if ( FAILED(result) ) {
-        sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",
-                devices[stream->device[0]].name, getErrorString(result));
-        error(RtError::DRIVER_ERROR);
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
       }
       }
+      error( RtAudioError::WARNING );
+      goto tryOutput;
+    }
 
 
-      // Zero the free space
-      ZeroMemory(buffer1, bufferSize1);
-      if (buffer2 != NULL) ZeroMemory(buffer2, bufferSize2);
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[1] )
+      byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
 
 
-      // Update our buffer offset and unlock sound buffer
-      dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
-      if ( FAILED(result) ) {
-        sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",
-                devices[stream->device[0]].name, getErrorString(result));
-        error(RtError::DRIVER_ERROR);
-      }
-      nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
-      stream->handle[0].bufferPointer = nextWritePos;
-    }
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
 
-    // If we play again, start at the beginning of the buffer.
-    stream->handle[0].bufferPointer = 0;
+    // Check stream latency
+    result = snd_pcm_delay( handle[1], &frames );
+    if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    buffer1 = NULL;
-    bufferSize1 = 0;
+ tryOutput:
 
 
-    result = buffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      channels = stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      channels = stream_.nUserChannels[0];
+      format = stream_.userFormat;
     }
 
     }
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
 
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+    // Write samples to device in interleaved/non-interleaved format.
+    if ( stream_.deviceInterleaved[0] )
+      result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
+    else {
+      void *bufs[channels];
+      size_t offset = stream_.bufferSize * formatBytes( format );
+      for ( int i=0; i<channels; i++ )
+        bufs[i] = (void *) (buffer + (i * offset));
+      result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
     }
 
     }
 
-    // Zero the DS buffer
-    ZeroMemory(buffer1, bufferSize1);
-
-    // Unlock the DS buffer
-    result = buffer->Unlock(buffer1, bufferSize1, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+    if ( result < (int) stream_.bufferSize ) {
+      // Either an error or underrun occured.
+      if ( result == -EPIPE ) {
+        snd_pcm_state_t state = snd_pcm_state( handle[0] );
+        if ( state == SND_PCM_STATE_XRUN ) {
+          apiInfo->xrun[0] = true;
+          result = snd_pcm_prepare( handle[0] );
+          if ( result < 0 ) {
+            errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
+            errorText_ = errorStream_.str();
+          }
+          else
+            errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";
+        }
+        else {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
+        }
+      }
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+      }
+      error( RtAudioError::WARNING );
+      goto unlock;
     }
 
     }
 
-    // If we start recording again, we must begin at beginning of buffer.
-    stream->handle[1].bufferPointer = 0;
+    // Check stream latency
+    result = snd_pcm_delay( handle[0], &frames );
+    if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  RtApi::tickStreamTime();
+  if ( doStopStream == 1 ) this->stopStream();
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+static void *alsaCallbackHandler( void *ptr )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiAlsa *object = (RtApiAlsa *) info->object;
+  bool *isRunning = &info->isRunning;
 
 
-  MUTEX_LOCK(&stream->mutex);
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+  if ( info->doRealtime ) {
+    pthread_t tID = pthread_self();     // ID of this thread
+    sched_param prio = { info->priority }; // scheduling priority of thread
+    pthread_setschedparam( tID, SCHED_RR, &prio );
+  }
+#endif
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  while ( *isRunning == true ) {
+    pthread_testcancel();
+    object->callbackEvent();
+  }
 
 
-  HRESULT result;
-  long dsBufferSize;
-  LPVOID audioPtr;
-  DWORD dataLen;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    result = buffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to stop DS buffer (%s): %s",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  pthread_exit( NULL );
+}
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0];
-    dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
+//******************** End of __LINUX_ALSA__ *********************//
+#endif
 
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+#if defined(__LINUX_PULSE__)
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+// Code written by Peter Meerwald, pmeerw@pmeerw.net
+// and Tristan Matthews.
 
 
-    // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to unlock DS buffer (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+#include <pulse/error.h>
+#include <pulse/simple.h>
+#include <cstdio>
 
 
-    // If we start playing again, we must begin at beginning of buffer.
-    stream->handle[0].bufferPointer = 0;
-  }
+static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
+                                                      44100, 48000, 96000, 0};
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    audioPtr = NULL;
-    dataLen = 0;
+struct rtaudio_pa_format_mapping_t {
+  RtAudioFormat rtaudio_format;
+  pa_sample_format_t pa_format;
+};
 
 
-    result = buffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
+  {RTAUDIO_SINT16, PA_SAMPLE_S16LE},
+  {RTAUDIO_SINT32, PA_SAMPLE_S32LE},
+  {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},
+  {0, PA_SAMPLE_INVALID}};
+
+struct PulseAudioHandle {
+  pa_simple *s_play;
+  pa_simple *s_rec;
+  pthread_t thread;
+  pthread_cond_t runnable_cv;
+  bool runnable;
+  PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }
+};
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+RtApiPulse::~RtApiPulse()
+{
+  if ( stream_.state != STREAM_CLOSED )
+    closeStream();
+}
 
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+unsigned int RtApiPulse::getDeviceCount( void )
+{
+  return 1;
+}
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = true;
+  info.name = "PulseAudio";
+  info.outputChannels = 2;
+  info.inputChannels = 2;
+  info.duplexChannels = 2;
+  info.isDefaultOutput = true;
+  info.isDefaultInput = true;
+
+  for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
+    info.sampleRates.push_back( *sr );
+
+  info.preferredSampleRate = 48000;
+  info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
+
+  return info;
+}
 
 
-    // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+static void *pulseaudio_callback( void * user )
+{
+  CallbackInfo *cbi = static_cast<CallbackInfo *>( user );
+  RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
+  volatile bool *isRunning = &cbi->isRunning;
 
 
-    // If we start recording again, we must begin at beginning of buffer.
-    stream->handle[1].bufferPointer = 0;
+  while ( *isRunning ) {
+    pthread_testcancel();
+    context->callbackEvent();
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  pthread_exit( NULL );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiPulse::closeStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
 
 
-  MUTEX_LOCK(&stream->mutex);
+  stream_.callbackInfo.isRunning = false;
+  if ( pah ) {
+    MUTEX_LOCK( &stream_.mutex );
+    if ( stream_.state == STREAM_STOPPED ) {
+      pah->runnable = true;
+      pthread_cond_signal( &pah->runnable_cv );
+    }
+    MUTEX_UNLOCK( &stream_.mutex );
 
 
-  int channels;
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    pthread_join( pah->thread, 0 );
+    if ( pah->s_play ) {
+      pa_simple_flush( pah->s_play, NULL );
+      pa_simple_free( pah->s_play );
+    }
+    if ( pah->s_rec )
+      pa_simple_free( pah->s_rec );
 
 
-  HRESULT result;
-  DWORD currentPos, safePos;
-  channels = 1;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    pthread_cond_destroy( &pah->runnable_cv );
+    delete pah;
+    stream_.apiHandle = 0;
+  }
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    channels = stream->nDeviceChannels[0];
-    DWORD dsBufferSize = stream->bufferSize * channels;
-    dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
+  if ( stream_.userBuffer[0] ) {
+    free( stream_.userBuffer[0] );
+    stream_.userBuffer[0] = 0;
+  }
+  if ( stream_.userBuffer[1] ) {
+    free( stream_.userBuffer[1] );
+    stream_.userBuffer[1] = 0;
+  }
+
+  stream_.state = STREAM_CLOSED;
+  stream_.mode = UNINITIALIZED;
+}
 
 
-    // Find out where the read and "safe write" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+void RtApiPulse::callbackEvent( void )
+{
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_LOCK( &stream_.mutex );
+    while ( !pah->runnable )
+      pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );
+
+    if ( stream_.state != STREAM_RUNNING ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      return;
     }
     }
+    MUTEX_UNLOCK( &stream_.mutex );
+  }
 
 
-    if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    frames = currentPos - nextWritePos;
-    frames /= channels * formatBytes(stream->deviceFormat[0]);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "
+      "this shouldn't happen!";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],
+                               stream_.bufferSize, streamTime, status,
+                               stream_.callbackInfo.userData );
 
 
-    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    UINT nextReadPos = stream->handle[1].bufferPointer;
-    channels = stream->nDeviceChannels[1];
-    DWORD dsBufferSize = stream->bufferSize * channels;
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+  if ( doStopStream == 2 ) {
+    abortStream();
+    return;
+  }
 
 
-    // Find out where the write and "safe read" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  MUTEX_LOCK( &stream_.mutex );
+  void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];
+  void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];
 
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
+  if ( stream_.state != STREAM_RUNNING )
+    goto unlock;
 
 
-    if (stream->mode == DUPLEX ) {
-      // Take largest value of the two.
-      int temp = safePos - nextReadPos;
-      temp /= channels * formatBytes(stream->deviceFormat[1]);
-      frames = ( temp > frames ) ? temp : frames;
+  int pa_error;
+  size_t bytes;
+  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( stream_.doConvertBuffer[OUTPUT] ) {
+        convertBuffer( stream_.deviceBuffer,
+                       stream_.userBuffer[OUTPUT],
+                       stream_.convertInfo[OUTPUT] );
+        bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *
+                formatBytes( stream_.deviceFormat[OUTPUT] );
+    } else
+        bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *
+                formatBytes( stream_.userFormat );
+
+    if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {
+      errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
+        pa_strerror( pa_error ) << ".";
+      errorText_ = errorStream_.str();
+      error( RtAudioError::WARNING );
+    }
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {
+    if ( stream_.doConvertBuffer[INPUT] )
+      bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *
+        formatBytes( stream_.deviceFormat[INPUT] );
+    else
+      bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *
+        formatBytes( stream_.userFormat );
+            
+    if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {
+      errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
+        pa_strerror( pa_error ) << ".";
+      errorText_ = errorStream_.str();
+      error( RtAudioError::WARNING );
     }
     }
-    else {
-      frames = safePos - nextReadPos;
-      frames /= channels * formatBytes(stream->deviceFormat[1]);
+    if ( stream_.doConvertBuffer[INPUT] ) {
+      convertBuffer( stream_.userBuffer[INPUT],
+                     stream_.deviceBuffer,
+                     stream_.convertInfo[INPUT] );
     }
   }
 
     }
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
-
  unlock:
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  MUTEX_UNLOCK( &stream_.mutex );
+  RtApi::tickStreamTime();
+
+  if ( doStopStream == 1 )
+    stopStream();
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiPulse::startStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiPulse::startStream(): the stream is not open!";
+    error( RtAudioError::INVALID_USE );
     return;
   }
     return;
   }
-  else if (stream->callbackInfo.usingCallback) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
-    stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiPulse::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
+    return;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
+  stream_.state = STREAM_RUNNING;
+
+  pah->runnable = true;
+  pthread_cond_signal( &pah->runnable_cv );
+  MUTEX_UNLOCK( &stream_.mutex );
+}
+
+void RtApiPulse::stopStream( void )
+{
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
+
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiPulse::stopStream(): the stream is not open!";
+    error( RtAudioError::INVALID_USE );
+    return;
+  }
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  HRESULT result;
-  DWORD currentPos, safePos;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
-  char *buffer;
-  long buffer_bytes;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
-      buffer_bytes *= formatBytes(stream->deviceFormat[0]);
-    }
-    else {
-      buffer = stream->userBuffer;
-      buffer_bytes = stream->bufferSize * stream->nUserChannels[0];
-      buffer_bytes *= formatBytes(stream->userFormat);
+  if ( pah && pah->s_play ) {
+    int pa_error;
+    if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
+      errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
+        pa_strerror( pa_error ) << ".";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
     }
     }
+  }
 
 
-    // No byte swapping necessary in DirectSound implementation.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+}
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    DWORD dsBufferSize = buffer_bytes * stream->nBuffers;
-
-    // Find out where the read and "safe write" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-
-    if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    DWORD endWrite = nextWritePos + buffer_bytes;
-
-    // Check whether the entire write region is behind the play pointer.
-    while ( currentPos < endWrite ) {
-      // If we are here, then we must wait until the play pointer gets
-      // beyond the write region.  The approach here is to use the
-      // Sleep() function to suspend operation until safePos catches
-      // up. Calculate number of milliseconds to wait as:
-      //   time = distance * (milliseconds/second) * fudgefactor /
-      //          ((bytes/sample) * (samples/second))
-      // A "fudgefactor" less than 1 is used because it was found
-      // that sleeping too long was MUCH worse than sleeping for
-      // several shorter periods.
-      float millis = (endWrite - currentPos) * 900.0;
-      millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);
-      if ( millis < 1.0 ) millis = 1.0;
-      Sleep( (DWORD) millis );
+void RtApiPulse::abortStream( void )
+{
+  PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );
 
 
-      // Wake up, find out where we are now
-      result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-      if ( FAILED(result) ) {
-        sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-        error(RtError::DRIVER_ERROR);
-      }
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    }
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiPulse::abortStream(): the stream is not open!";
+    error( RtAudioError::INVALID_USE );
+    return;
+  }
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-    // Lock free space in the buffer
-    result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,
-                             &bufferSize1, &buffer2, &bufferSize2, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
+
+  if ( pah && pah->s_play ) {
+    int pa_error;
+    if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
+      errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
+        pa_strerror( pa_error ) << ".";
+      errorText_ = errorStream_.str();
+      MUTEX_UNLOCK( &stream_.mutex );
+      error( RtAudioError::SYSTEM_ERROR );
+      return;
     }
     }
+  }
 
 
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer1, buffer, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+}
 
 
-    // Update our buffer offset and unlock sound buffer
-    dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-    nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
-    stream->handle[0].bufferPointer = nextWritePos;
+bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
+                                  unsigned int channels, unsigned int firstChannel,
+                                  unsigned int sampleRate, RtAudioFormat format,
+                                  unsigned int *bufferSize, RtAudio::StreamOptions *options )
+{
+  PulseAudioHandle *pah = 0;
+  unsigned long bufferBytes = 0;
+  pa_sample_spec ss;
+
+  if ( device != 0 ) return false;
+  if ( mode != INPUT && mode != OUTPUT ) return false;
+  if ( channels != 1 && channels != 2 ) {
+    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";
+    return false;
   }
   }
+  ss.channels = channels;
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  if ( firstChannel != 0 ) return false;
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1];
-      buffer_bytes *= formatBytes(stream->deviceFormat[1]);
+  bool sr_found = false;
+  for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {
+    if ( sampleRate == *sr ) {
+      sr_found = true;
+      stream_.sampleRate = sampleRate;
+      ss.rate = sampleRate;
+      break;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      buffer_bytes = stream->bufferSize * stream->nUserChannels[1];
-      buffer_bytes *= formatBytes(stream->userFormat);
+  }
+  if ( !sr_found ) {
+    errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";
+    return false;
+  }
+
+  bool sf_found = 0;
+  for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;
+        sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {
+    if ( format == sf->rtaudio_format ) {
+      sf_found = true;
+      stream_.userFormat = sf->rtaudio_format;
+      stream_.deviceFormat[mode] = stream_.userFormat;
+      ss.format = sf->pa_format;
+      break;
     }
     }
+  }
+  if ( !sf_found ) { // Use internal data format conversion.
+    stream_.userFormat = format;
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    ss.format = PA_SAMPLE_FLOAT32LE;
+  }
 
 
-    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    UINT nextReadPos = stream->handle[1].bufferPointer;
-    DWORD dsBufferSize = buffer_bytes * stream->nBuffers;
+  // Set other stream parameters.
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] = true;
+  stream_.nBuffers = 1;
+  stream_.doByteSwap[mode] = false;
+  stream_.nUserChannels[mode] = channels;
+  stream_.nDeviceChannels[mode] = channels + firstChannel;
+  stream_.channelOffset[mode] = 0;
+  std::string streamName = "RtAudio";
 
 
-    // Find out where the write and "safe read" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
 
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
-    DWORD endRead = nextReadPos + buffer_bytes;
+  // Allocate necessary internal buffers.
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
+  }
+  stream_.bufferSize = *bufferSize;
 
 
-    // Check whether the entire write region is behind the play pointer.
-    while ( safePos < endRead ) {
-      // See comments for playback.
-      float millis = (endRead - safePos) * 900.0;
-      millis /= ( formatBytes(stream->deviceFormat[1]) * stream->sampleRate);
-      if ( millis < 1.0 ) millis = 1.0;
-      Sleep( (DWORD) millis );
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-      // Wake up, find out where we are now
-      result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-      if ( FAILED(result) ) {
-        sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
-                devices[stream->device[1]].name, getErrorString(result));
-        error(RtError::DRIVER_ERROR);
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
       }
-      
-      if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
     }
 
     }
 
-    // Lock free space in the buffer
-    result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1,
-                             &bufferSize1, &buffer2, &bufferSize2, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS buffer during capture (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
     }
+  }
 
 
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer, buffer1, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);
+  stream_.device[mode] = device;
 
 
-    // Update our buffer offset and unlock sound buffer
-    nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize;
-    dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to unlock DS buffer during capture (%s): %s.",
-              devices[stream->device[1]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-    stream->handle[1].bufferPointer = nextReadPos;
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
 
 
-    // No byte swapping necessary in DirectSound implementation.
+  if ( !stream_.apiHandle ) {
+    PulseAudioHandle *pah = new PulseAudioHandle;
+    if ( !pah ) {
+      errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";
+      goto error;
+    }
 
 
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
+    stream_.apiHandle = pah;
+    if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {
+      errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";
+      goto error;
+    }
   }
   }
+  pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
-}
-
-// Definitions for utility functions and callbacks
-// specific to the DirectSound implementation.
-
-extern "C" unsigned __stdcall callbackHandler(void *ptr)
-{
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+  int error;
+  if ( options && !options->streamName.empty() ) streamName = options->streamName;
+  switch ( mode ) {
+  case INPUT:
+    pa_buffer_attr buffer_attr;
+    buffer_attr.fragsize = bufferBytes;
+    buffer_attr.maxlength = -1;
 
 
-  while ( *usingCallback ) {
-    try {
-      object->tickStream(stream);
+    pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );
+    if ( !pah->s_rec ) {
+      errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";
+      goto error;
     }
     }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessage());
-      break;
+    break;
+  case OUTPUT:
+    pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
+    if ( !pah->s_play ) {
+      errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
+      goto error;
     }
     }
+    break;
+  default:
+    goto error;
   }
 
   }
 
-  _endthreadex( 0 );
-  return 0;
-}
-
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.mode == UNINITIALIZED )
+    stream_.mode = mode;
+  else if ( stream_.mode == mode )
+    goto error;
+  else
+    stream_.mode = DUPLEX;
 
 
-  CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message, "RtAudio: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
+  if ( !stream_.callbackInfo.isRunning ) {
+    stream_.callbackInfo.object = this;
+    stream_.callbackInfo.isRunning = true;
+    if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) {
+      errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
+      goto error;
+    }
   }
 
   }
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-  info->streamId = streamId;
-
-  unsigned thread_id;
-  info->thread = _beginthreadex(NULL, 0, &callbackHandler,
-                                &stream->callbackInfo, 0, &thread_id);
-  if (info->thread == 0) {
-    info->usingCallback = false;
-    sprintf(message, "RtAudio: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
+  stream_.state = STREAM_STOPPED;
+  return true;
+ error:
+  if ( pah && stream_.callbackInfo.isRunning ) {
+    pthread_cond_destroy( &pah->runnable_cv );
+    delete pah;
+    stream_.apiHandle = 0;
   }
 
   }
 
-  // When spawning multiple threads in quick succession, it appears to be
-  // necessary to wait a bit for each to initialize ... another windoism!
-  Sleep(1);
-}
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-static bool CALLBACK deviceCountCallback(LPGUID lpguid,
-                                         LPCSTR lpcstrDescription,
-                                         LPCSTR lpcstrModule,
-                                         LPVOID lpContext)
-{
-  int *pointer = ((int *) lpContext);
-  (*pointer)++;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  return true;
+  return FAILURE;
 }
 
 }
 
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCSTR lpcstrDescription,
-                                        LPCSTR lpcstrModule,
-                                        LPVOID lpContext)
-{
-  enum_info *info = ((enum_info *) lpContext);
-  while (strlen(info->name) > 0) info++;
+//******************** End of __LINUX_PULSE__ *********************//
+#endif
 
 
-  strncpy(info->name, lpcstrDescription, 64);
-  info->id = lpguid;
+#if defined(__LINUX_OSS__)
 
 
-       HRESULT    hr;
-  info->isValid = false;
-  if (info->isInput == true) {
-    DSCCAPS               caps;
-    LPDIRECTSOUNDCAPTURE  object;
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/soundcard.h>
+#include <errno.h>
+#include <math.h>
 
 
-    hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
-    if( hr != DS_OK ) return true;
+static void *ossCallbackHandler(void * ptr);
 
 
-    caps.dwSize = sizeof(caps);
-    hr = object->GetCaps( &caps );
-    if( hr == DS_OK ) {
-      if (caps.dwChannels > 0 && caps.dwFormats > 0)
-        info->isValid = true;
-    }
-    object->Release();
-  }
-  else {
-    DSCAPS         caps;
-    LPDIRECTSOUND  object;
-    hr = DirectSoundCreate(  lpguid, &object,   NULL );
-    if( hr != DS_OK ) return true;
+// A structure to hold various information related to the OSS API
+// implementation.
+struct OssHandle {
+  int id[2];    // device ids
+  bool xrun[2];
+  bool triggered;
+  pthread_cond_t runnable;
 
 
-    caps.dwSize = sizeof(caps);
-    hr = object->GetCaps( &caps );
-    if( hr == DS_OK ) {
-      if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
-        info->isValid = true;
-    }
-    object->Release();
-  }
+  OssHandle()
+    :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
 
-  return true;
+RtApiOss :: RtApiOss()
+{
+  // Nothing to do here.
 }
 
 }
 
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCSTR lpcstrDescription,
-                                           LPCSTR lpcstrModule,
-                                           LPVOID lpContext)
+RtApiOss :: ~RtApiOss()
 {
 {
-  enum_info *info = ((enum_info *) lpContext);
-
-  if ( lpguid == NULL ) {
-    strncpy(info->name, lpcstrDescription, 64);
-    return false;
-  }
-
-  return true;
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
 }
 
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCSTR lpcstrDescription,
-                                      LPCSTR lpcstrModule,
-                                      LPVOID lpContext)
+unsigned int RtApiOss :: getDeviceCount( void )
 {
 {
-  enum_info *info = ((enum_info *) lpContext);
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
+    error( RtAudioError::WARNING );
+    return 0;
+  }
 
 
-  if ( strncmp( info->name, lpcstrDescription, 64 ) == 0 ) {
-    info->id = lpguid;
-    info->isValid = true;
-    return false;
+  oss_sysinfo sysinfo;
+  if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
+    error( RtAudioError::WARNING );
+    return 0;
   }
 
   }
 
-  return true;
+  close( mixerfd );
+  return sysinfo.numaudios;
 }
 
 }
 
-static char* getErrorString(int code)
+RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
 {
 {
-       switch (code) {
-
-  case DSERR_ALLOCATED:
-    return "Direct Sound already allocated";
-
-  case DSERR_CONTROLUNAVAIL:
-    return "Direct Sound control unavailable";
-
-  case DSERR_INVALIDPARAM:
-    return "Direct Sound invalid parameter";
-
-  case DSERR_INVALIDCALL:
-    return "Direct Sound invalid call";
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-  case DSERR_GENERIC:
-    return "Direct Sound generic error";
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
 
-  case DSERR_PRIOLEVELNEEDED:
-    return "Direct Sound Priority level needed";
+  oss_sysinfo sysinfo;
+  int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
+  if ( result == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
 
-  case DSERR_OUTOFMEMORY:
-    return "Direct Sound out of memory";
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
 
 
-  case DSERR_BADFORMAT:
-    return "Direct Sound bad format";
+  if ( device >= nDevices ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
+    error( RtAudioError::INVALID_USE );
+    return info;
+  }
 
 
-  case DSERR_UNSUPPORTED:
-    return "Direct Sound unsupported error";
+  oss_audioinfo ainfo;
+  ainfo.dev = device;
+  result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
+  close( mixerfd );
+  if ( result == -1 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
+
+  // Probe channels
+  if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
+  if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
+  if ( ainfo.caps & PCM_CAP_DUPLEX ) {
+    if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
+      info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
+  }
+
+  // Probe data formats ... do for input
+  unsigned long mask = ainfo.iformats;
+  if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  if ( mask & AFMT_S8 )
+    info.nativeFormats |= RTAUDIO_SINT8;
+  if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
+    info.nativeFormats |= RTAUDIO_SINT32;
+#ifdef AFMT_FLOAT
+  if ( mask & AFMT_FLOAT )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+#endif
+  if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
+    info.nativeFormats |= RTAUDIO_SINT24;
 
 
-  case DSERR_NODRIVER:
-    return "Direct Sound no driver error";
+  // Check that we have at least one supported format
+  if ( info.nativeFormats == 0 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+    return info;
+  }
 
 
-  case DSERR_ALREADYINITIALIZED:
-    return "Direct Sound already initialized";
+  // Probe the supported sample rates.
+  info.sampleRates.clear();
+  if ( ainfo.nrates ) {
+    for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
+      for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+        if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
+          info.sampleRates.push_back( SAMPLE_RATES[k] );
 
 
-  case DSERR_NOAGGREGATION:
-    return "Direct Sound no aggregation";
+          if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
+            info.preferredSampleRate = SAMPLE_RATES[k];
 
 
-  case DSERR_BUFFERLOST:
-    return "Direct Sound buffer lost";
+          break;
+        }
+      }
+    }
+  }
+  else {
+    // Check min and max rate values;
+    for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+      if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {
+        info.sampleRates.push_back( SAMPLE_RATES[k] );
 
 
-  case DSERR_OTHERAPPHASPRIO:
-    return "Direct Sound other app has priority";
+        if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
+          info.preferredSampleRate = SAMPLE_RATES[k];
+      }
+    }
+  }
 
 
-  case DSERR_UNINITIALIZED:
-    return "Direct Sound uninitialized";
+  if ( info.sampleRates.size() == 0 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    error( RtAudioError::WARNING );
+  }
+  else {
+    info.probed = true;
+    info.name = ainfo.name;
+  }
 
 
-  default:
-    return "Direct Sound unknown error";
-       }
+  return info;
 }
 
 }
 
-//******************** End of __WINDOWS_DS__ *********************//
-
-#elif defined(__IRIX_AL__) // SGI's AL API for IRIX
-
-#include <unistd.h>
-#include <errno.h>
 
 
-void RtAudio :: initialize(void)
+bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                  unsigned int firstChannel, unsigned int sampleRate,
+                                  RtAudioFormat format, unsigned int *bufferSize,
+                                  RtAudio::StreamOptions *options )
 {
 {
-  // Count cards and devices
-  nDevices = 0;
-
-  // Determine the total number of input and output devices.
-  nDevices = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0);
-  if (nDevices < 0) {
-    sprintf(message, "RtAudio: AL error counting devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
+    return FAILURE;
   }
 
   }
 
-  if (nDevices <= 0) return;
-
-  ALvalue *vls = (ALvalue *) new ALvalue[nDevices];
-
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
+  oss_sysinfo sysinfo;
+  int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
+  if ( result == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
+    return FAILURE;
   }
 
   }
 
-  // Write device ascii identifiers and resource ids to device info
-  // structure.
-  char name[32];
-  int outs, ins, i;
-  ALpv pvs[1];
-  pvs[0].param = AL_NAME;
-  pvs[0].value.ptr = name;
-  pvs[0].sizeIn = 32;
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
+    return FAILURE;
+  }
 
 
-  outs = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, vls, nDevices, 0, 0);
-  if (outs < 0) {
-    sprintf(message, "RtAudio: AL error getting output devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
   }
 
   }
 
-  for (i=0; i<outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      sprintf(message, "RtAudio: AL error querying output devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
-    strncpy(devices[i].name, name, 32);
-    devices[i].id[0] = vls[i].i;
+  oss_audioinfo ainfo;
+  ainfo.dev = device;
+  result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
+  close( mixerfd );
+  if ( result == -1 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  ins = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &vls[outs], nDevices-outs, 0, 0);
-  if (ins < 0) {
-    sprintf(message, "RtAudio: AL error getting input devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  // Check if device supports input or output
+  if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
+       ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
+    if ( mode == OUTPUT )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  for (i=outs; i<ins+outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      sprintf(message, "RtAudio: AL error querying input devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  int flags = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( mode == OUTPUT )
+    flags |= O_WRONLY;
+  else { // mode == INPUT
+    if (stream_.mode == OUTPUT && stream_.device[0] == device) {
+      // We just set the same device for playback ... close and reopen for duplex (OSS only).
+      close( handle->id[0] );
+      handle->id[0] = 0;
+      if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      // Check that the number previously set channels is the same.
+      if ( stream_.nUserChannels[0] != channels ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      flags |= O_RDWR;
     }
     }
-    strncpy(devices[i].name, name, 32);
-    devices[i].id[1] = vls[i].i;
+    else
+      flags |= O_RDONLY;
   }
 
   }
 
-  delete [] vls;
-
-  return;
-}
+  // Set exclusive access if specified.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
 
 
-int RtAudio :: getDefaultInputDevice(void)
-{
-  ALvalue value;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message, "RtAudio: AL error getting default input device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
-  }
-  else {
-    for ( int i=0; i<nDevices; i++ )
-      if ( devices[i].id[1] == value.i ) return i;
+  // Try to open the device.
+  int fd;
+  fd = open( ainfo.devnode, flags, 0 );
+  if ( fd == -1 ) {
+    if ( errno == EBUSY )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  return 0;
-}
+  // For duplex operation, specifically set this mode (this doesn't seem to work).
+  /*
+    if ( flags | O_RDWR ) {
+    result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
+    if ( result == -1) {
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+    }
+    }
+  */
 
 
-int RtAudio :: getDefaultOutputDevice(void)
-{
-  ALvalue value;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message, "RtAudio: AL error getting default output device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
-  }
-  else {
-    for ( int i=0; i<nDevices; i++ )
-      if ( devices[i].id[0] == value.i ) return i;
+  // Check the device channel support.
+  stream_.nUserChannels[mode] = channels;
+  if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  return 0;
-}
-
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  int resource, result, i;
-  ALvalue value;
-  ALparamInfo pinfo;
+  // Set the number of channels.
+  int deviceChannels = channels + firstChannel;
+  result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
+  if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.nDeviceChannels[mode] = deviceChannels;
 
 
-  // Get output resource ID if it exists.
-  resource = info->id[0];
-  if (resource > 0) {
+  // Get the data format mask
+  int mask;
+  result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    // Probe output device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  // Determine how to set the device format.
+  stream_.userFormat = format;
+  int deviceFormat = -1;
+  stream_.doByteSwap[mode] = false;
+  if ( format == RTAUDIO_SINT8 ) {
+    if ( mask & AFMT_S8 ) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
     }
-    else {
-      info->maxOutputChannels = value.i;
-      info->minOutputChannels = 1;
+  }
+  else if ( format == RTAUDIO_SINT16 ) {
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
     }
-
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
     }
     }
-    else {
-      info->nSampleRates = 0;
-      for (i=0; i<MAX_SAMPLE_RATES; i++) {
-        if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
-          info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-          info->nSampleRates++;
-        }
-      }
+  }
+  else if ( format == RTAUDIO_SINT24 ) {
+    if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    }
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
     }
     }
-
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RTAUDIO_FORMAT) 51;
   }
   }
-
-  // Now get input resource ID if it exists.
-  resource = info->id[1];
-  if (resource > 0) {
-
-    // Probe input device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  else if ( format == RTAUDIO_SINT32 ) {
+    if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
     }
     }
-    else {
-      info->maxInputChannels = value.i;
-      info->minInputChannels = 1;
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
     }
     }
+  }
 
 
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  if ( deviceFormat == -1 ) {
+    // The user requested format is not natively supported by the device.
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
     }
-    else {
-      // In the case of the default device, these values will
-      // overwrite the rates determined for the output device.  Since
-      // the input device is most likely to be more limited than the
-      // output device, this is ok.
-      info->nSampleRates = 0;
-      for (i=0; i<MAX_SAMPLE_RATES; i++) {
-        if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
-          info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-          info->nSampleRates++;
-        }
-      }
+    else if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    }
+    else if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    }
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S8) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
     }
-
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RTAUDIO_FORMAT) 51;
   }
 
   }
 
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
-    return;
-  if ( info->nSampleRates == 0 )
-    return;
-
-  // Determine duplex status.
-  if (info->maxInputChannels < info->maxOutputChannels)
-    info->maxDuplexChannels = info->maxInputChannels;
-  else
-    info->maxDuplexChannels = info->maxOutputChannels;
-  if (info->minInputChannels < info->minOutputChannels)
-    info->minDuplexChannels = info->minInputChannels;
-  else
-    info->minDuplexChannels = info->minOutputChannels;
-
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
-
-  info->probed = true;
-
-  return;
-}
-
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  int result, resource, nBuffers;
-  ALconfig al_config;
-  ALport port;
-  ALpv pvs[2];
-
-  // Get a new ALconfig structure.
-  al_config = alNewConfig();
-  if ( !al_config ) {
-    sprintf(message,"RtAudio: can't get AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    // This really shouldn't happen ...
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Set the channels.
-  result = alSetChannels(al_config, channels);
-  if ( result < 0 ) {
-    sprintf(message,"RtAudio: can't set %d channels in AL config: %s.",
-            channels, alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  // Set the data format.
+  int temp = deviceFormat;
+  result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
+  if ( result == -1 || deviceFormat != temp ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Attempt to set the queue size.  The al API doesn't provide a
-  // means for querying the minimum/maximum buffer size of a device,
-  // so if the specified size doesn't work, take whatever the
-  // al_config structure returns.
-  if ( numberOfBuffers < 1 )
-    nBuffers = 1;
-  else
-    nBuffers = numberOfBuffers;
-  long buffer_size = *bufferSize * nBuffers;
-  result = alSetQueueSize(al_config, buffer_size); // in sample frames
-  if ( result < 0 ) {
-    // Get the buffer size specified by the al_config and try that.
-    buffer_size = alGetQueueSize(al_config);
-    result = alSetQueueSize(al_config, buffer_size);
-    if ( result < 0 ) {
-      sprintf(message,"RtAudio: can't set buffer size (%ld) in AL config: %s.",
-              buffer_size, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
-    *bufferSize = buffer_size / nBuffers;
+  // Attempt to set the buffer size.  According to OSS, the minimum
+  // number of buffers is two.  The supposed minimum buffer size is 16
+  // bytes, so that will be our lower bound.  The argument to this
+  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
+  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
+  // We'll check the actual value used near the end of the setup
+  // procedure.
+  int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
+  if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
+  int buffers = 0;
+  if ( options ) buffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
+  if ( buffers < 2 ) buffers = 3;
+  temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
+  result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
   }
+  stream_.nBuffers = buffers;
 
 
-  // Set the data format.
-  stream->userFormat = format;
-  stream->deviceFormat[mode] = format;
-  if (format == RTAUDIO_SINT8) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_8);
-  }
-  else if (format == RTAUDIO_SINT16) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_16);
-  }
-  else if (format == RTAUDIO_SINT24) {
-    // Our 24-bit format assumes the upper 3 bytes of a 4 byte word.
-    // The AL library uses the lower 3 bytes, so we'll need to do our
-    // own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-  }
-  else if (format == RTAUDIO_SINT32) {
-    // The AL library doesn't seem to support the 32-bit integer
-    // format, so we'll need to do our own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-  }
-  else if (format == RTAUDIO_FLOAT32)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-  else if (format == RTAUDIO_FLOAT64)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE);
+  // Save buffer size (in sample frames).
+  *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
+  stream_.bufferSize = *bufferSize;
 
 
+  // Set the sample rate.
+  int srate = sampleRate;
+  result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
   if ( result == -1 ) {
   if ( result == -1 ) {
-    sprintf(message,"RtAudio: AL error setting sample format in AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  if (mode == OUTPUT) {
+  // Verify the sample rate setup worked.
+  if ( abs( srate - (int)sampleRate ) > 100 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.sampleRate = sampleRate;
 
 
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_OUTPUT;
-    else
-      resource = devices[device].id[0];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
-              devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
+    // We're doing duplex setup here.
+    stream_.deviceFormat[0] = stream_.deviceFormat[1];
+    stream_.nDeviceChannels[0] = deviceChannels;
+  }
 
 
-    // Open the port.
-    port = alOpenPort("RtAudio Output Port", "w", al_config);
-    if( !port ) {
-      sprintf(message,"RtAudio: AL error opening output port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  // Set interleaving parameters.
+  stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] =  true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
+    stream_.userInterleaved = false;
 
 
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
-              sampleRate, devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+  // Set flags for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate the stream handles if necessary and then save.
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new OssHandle;
     }
     }
-  }
-  else { // mode == INPUT
-
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_INPUT;
-    else
-      resource = devices[device].id[1];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
-              devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
+      goto error;
     }
 
     }
 
-    // Open the port.
-    port = alOpenPort("RtAudio Output Port", "r", al_config);
-    if( !port ) {
-      sprintf(message,"RtAudio: AL error opening input port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+    if ( pthread_cond_init( &handle->runnable, NULL ) ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
     }
 
     }
 
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
-              sampleRate, devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+    stream_.apiHandle = (void *) handle;
   }
   }
+  else {
+    handle = (OssHandle *) stream_.apiHandle;
+  }
+  handle->id[mode] = fd;
 
 
-  alFreeConfig(al_config);
-
-  stream->nUserChannels[mode] = channels;
-  stream->nDeviceChannels[mode] = channels;
-
-  // Set handle and flags for buffer conversion
-  stream->handle[mode] = port;
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-    long buffer_bytes;
     bool makeBuffer = true;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup thread if necessary.
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
     // We had already set up an output stream.
     // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->nBuffers = nBuffers;
-  stream->bufferSize = *bufferSize;
-  stream->sampleRate = sampleRate;
+    stream_.mode = DUPLEX;
+    if ( stream_.device[0] == device ) handle->id[0] = fd;
+  }
+  else {
+    stream_.mode = mode;
+
+    // Setup callback thread.
+    stream_.callbackInfo.object = (void *) this;
+
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority.  The higher priority will only take affect if the
+    // program is run as root or suid.
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      struct sched_param param;
+      int priority = options->priority;
+      int min = sched_get_priority_min( SCHED_RR );
+      int max = sched_get_priority_max( SCHED_RR );
+      if ( priority < min ) priority = min;
+      else if ( priority > max ) priority = max;
+      param.sched_priority = priority;
+      pthread_attr_setschedparam( &attr, &param );
+      pthread_attr_setschedpolicy( &attr, SCHED_RR );
+    }
+    else
+      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
+
+    stream_.callbackInfo.isRunning = true;
+    result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
+    pthread_attr_destroy( &attr );
+    if ( result ) {
+      stream_.callbackInfo.isRunning = false;
+      errorText_ = "RtApiOss::error creating callback thread!";
+      goto error;
+    }
+  }
 
   return SUCCESS;
 
 
   return SUCCESS;
 
- memory_error:
-  if (stream->handle[0]) {
-    alClosePort(stream->handle[0]);
-    stream->handle[0] = 0;
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->runnable );
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
+    stream_.apiHandle = 0;
   }
   }
-  if (stream->handle[1]) {
-    alClosePort(stream->handle[1]);
-    stream->handle[1] = 0;
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
   }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  sprintf(message, "RtAudio: ALSA error allocating buffer memory for device (%s).",
-          devices[device].name);
-  error(RtError::WARNING);
+
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiOss :: closeStream()
 {
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::closeStream(): no open stream to close!";
+    error( RtAudioError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  stream_.callbackInfo.isRunning = false;
+  MUTEX_LOCK( &stream_.mutex );
+  if ( stream_.state == STREAM_STOPPED )
+    pthread_cond_signal( &handle->runnable );
+  MUTEX_UNLOCK( &stream_.mutex );
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
+  if ( stream_.state == STREAM_RUNNING ) {
+    if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+      ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    else
+      ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    stream_.state = STREAM_STOPPED;
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
-
-  if (stream->handle[0])
-    alClosePort(stream->handle[0]);
-
-  if (stream->handle[1])
-    alClosePort(stream->handle[1]);
+  if ( handle ) {
+    pthread_cond_destroy( &handle->runnable );
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  free(stream);
-  streams.erase(streamId);
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiOss :: startStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->state == STREAM_RUNNING)
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiOss::startStream(): the stream is already running!";
+    error( RtAudioError::WARNING );
     return;
     return;
+  }
 
 
-  // The AL port is ready as soon as it is opened.
-  stream->state = STREAM_RUNNING;
-}
-
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  MUTEX_LOCK(&stream->mutex);
+  stream_.state = STREAM_RUNNING;
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // No need to do anything else here ... OSS automatically starts
+  // when fed samples.
 
 
-  int result;
-  int buffer_size = stream->bufferSize * stream->nBuffers;
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX)
-    alZeroFrames(stream->handle[0], buffer_size);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  pthread_cond_signal( &handle->runnable );
+}
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    result = alDiscardFrames(stream->handle[1], buffer_size);
-    if (result == -1) {
-      sprintf(message, "RtAudio: AL error draining stream device (%s): %s.",
-              devices[stream->device[1]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
+void RtApiOss :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  MUTEX_LOCK( &stream_.mutex );
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    // Flush the output with zeros a few times.
+    char *buffer;
+    int samples;
+    RtAudioFormat format;
+
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
+      format = stream_.userFormat;
+    }
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    memset( buffer, 0, samples * formatBytes(format) );
+    for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
+      if ( result == -1 ) {
+        errorText_ = "RtApiOss::stopStream: audio write error.";
+        error( RtAudioError::WARNING );
+      }
+    }
 
 
-    int buffer_size = stream->bufferSize * stream->nBuffers;
-    int result = alDiscardFrames(stream->handle[0], buffer_size);
-    if (result == -1) {
-      sprintf(message, "RtAudio: AL error aborting stream device (%s): %s.",
-              devices[stream->device[0]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+    result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
+    handle->triggered = false;
   }
 
   }
 
-  // There is no clear action to take on the input stream, since the
-  // port will continue to run in any event.
-  stream->state = STREAM_STOPPED;
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
+    result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result != -1 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiOss :: abortStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
+    error( RtAudioError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-  int err = 0;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = alGetFillable(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
-              devices[stream->device[0]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
+    handle->triggered = false;
   }
 
   }
 
-  frames = err;
-
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    err = alGetFilled(stream->handle[1]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
-              devices[stream->device[1]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
+    result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    if (frames > err) frames = err;
   }
 
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
-
  unlock:
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result != -1 ) return;
+  error( RtAudioError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiOss :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_LOCK( &stream_.mutex );
+    pthread_cond_wait( &handle->runnable, &stream_.mutex );
+    if ( stream_.state != STREAM_RUNNING ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      return;
+    }
+    MUTEX_UNLOCK( &stream_.mutex );
+  }
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtAudioError::WARNING );
     return;
   }
     return;
   }
-  else if (stream->callbackInfo.usingCallback) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
-    stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
+
+  // Invoke user callback to get fresh output data.
+  int doStopStream = 0;
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    handle->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    handle->xrun[1] = false;
+  }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+  if ( doStopStream == 2 ) {
+    this->abortStream();
+    return;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
   // The state might change while waiting on a mutex.
 
   // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
+  int result;
   char *buffer;
   char *buffer;
-  int channels;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  int samples;
+  RtAudioFormat format;
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters and do buffer conversion if necessary.
 
     // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[0];
-      format = stream->deviceFormat[0];
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
     }
     else {
     }
     else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[0];
-      format = stream->userFormat;
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
+      format = stream_.userFormat;
     }
 
     // Do byte swapping if necessary.
     }
 
     // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer( buffer, samples, format );
+
+    if ( stream_.mode == DUPLEX && handle->triggered == false ) {
+      int trig = 0;
+      ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
+      trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
+      ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
+      handle->triggered = true;
+    }
+    else
+      // Write samples to device.
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
 
 
-    // Write interleaved samples to device.
-    alWriteFrames(stream->handle[0], buffer, stream->bufferSize);
+    if ( result == -1 ) {
+      // We'll assume this is an underrun, though there isn't a
+      // specific means for determining that.
+      handle->xrun[0] = true;
+      errorText_ = "RtApiOss::callbackEvent: audio write error.";
+      error( RtAudioError::WARNING );
+      // Continue on to input section.
+    }
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters.
 
     // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      samples = stream_.bufferSize * stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
     }
     else {
     }
     else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[1];
-      format = stream->userFormat;
+      buffer = stream_.userBuffer[1];
+      samples = stream_.bufferSize * stream_.nUserChannels[1];
+      format = stream_.userFormat;
     }
 
     }
 
-    // Read interleaved samples from device.
-    alReadFrames(stream->handle[1], buffer, stream->bufferSize);
+    // Read samples from device.
+    result = read( handle->id[1], buffer, samples * formatBytes(format) );
+
+    if ( result == -1 ) {
+      // We'll assume this is an overrun, though there isn't a
+      // specific means for determining that.
+      handle->xrun[1] = true;
+      errorText_ = "RtApiOss::callbackEvent: audio read error.";
+      error( RtAudioError::WARNING );
+      goto unlock;
+    }
 
     // Do byte swapping if necessary.
 
     // Do byte swapping if necessary.
-    if (stream->doByteSwap[1])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+    if ( stream_.doByteSwap[1] )
+      byteSwapBuffer( buffer, samples, format );
 
     // Do buffer conversion if necessary.
 
     // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
   }
 
  unlock:
   }
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
+  RtApi::tickStreamTime();
+  if ( doStopStream == 1 ) this->stopStream();
 }
 
 }
 
-extern "C" void *callbackHandler(void *ptr)
+static void *ossCallbackHandler( void *ptr )
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiOss *object = (RtApiOss *) info->object;
+  bool *isRunning = &info->isRunning;
 
 
-  while ( *usingCallback ) {
+  while ( *isRunning == true ) {
     pthread_testcancel();
     pthread_testcancel();
-    try {
-      object->tickStream(stream);
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessage());
-      break;
-    }
+    object->callbackEvent();
   }
 
   }
 
-  return 0;
+  pthread_exit( NULL );
 }
 
 }
 
-//******************** End of __IRIX_AL__ *********************//
-
+//******************** End of __LINUX_OSS__ *********************//
 #endif
 
 
 // *************************************************** //
 //
 #endif
 
 
 // *************************************************** //
 //
-// Private common (OS-independent) RtAudio methods.
+// Protected common (OS-independent) RtAudio methods.
 //
 // *************************************************** //
 
 // This method can be modified to control the behavior of error
 //
 // *************************************************** //
 
 // This method can be modified to control the behavior of error
-// message reporting and throwing.
-void RtAudio :: error(RtError::TYPE type)
+// message printing.
+void RtApi :: error( RtAudioError::Type type )
 {
 {
-  if (type == RtError::WARNING) {
-    fprintf(stderr, "\n%s\n\n", message);
-  }
-  else if (type == RtError::DEBUG_WARNING) {
-#if defined(__RTAUDIO_DEBUG__)
-    fprintf(stderr, "\n%s\n\n", message);
-#endif
-  }
-  else {
-    fprintf(stderr, "\n%s\n\n", message);
-    throw RtError(message, type);
+  errorStream_.str(""); // clear the ostringstream
+
+  RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;
+  if ( errorCallback ) {
+    // abortStream() can generate new error messages. Ignore them. Just keep original one.
+
+    if ( firstErrorOccurred_ )
+      return;
+
+    firstErrorOccurred_ = true;
+    const std::string errorMessage = errorText_;
+
+    if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {
+      stream_.callbackInfo.isRunning = false; // exit from the thread
+      abortStream();
+    }
+
+    errorCallback( type, errorMessage );
+    firstErrorOccurred_ = false;
+    return;
   }
   }
+
+  if ( type == RtAudioError::WARNING && showWarnings_ == true )
+    std::cerr << '\n' << errorText_ << "\n\n";
+  else if ( type != RtAudioError::WARNING )
+    throw( RtAudioError( errorText_, type ) );
 }
 
 }
 
-void *RtAudio :: verifyStream(int streamId)
+void RtApi :: verifyStream()
 {
 {
-  // Verify the stream key.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::INVALID_STREAM);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApi:: a stream is not open!";
+    error( RtAudioError::INVALID_USE );
   }
   }
-
-  return streams[streamId];
 }
 
 }
 
-void RtAudio :: clearDeviceInfo(RTAUDIO_DEVICE *info)
+void RtApi :: clearStreamInfo()
 {
 {
-  // Don't clear the name or DEVICE_ID fields here ... they are
-  // typically set prior to a call of this function.
-  info->probed = false;
-  info->maxOutputChannels = 0;
-  info->maxInputChannels = 0;
-  info->maxDuplexChannels = 0;
-  info->minOutputChannels = 0;
-  info->minInputChannels = 0;
-  info->minDuplexChannels = 0;
-  info->hasDuplexSupport = false;
-  info->nSampleRates = 0;
-  for (int i=0; i<MAX_SAMPLE_RATES; i++)
-    info->sampleRates[i] = 0;
-  info->nativeFormats = 0;
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
+  stream_.sampleRate = 0;
+  stream_.bufferSize = 0;
+  stream_.nBuffers = 0;
+  stream_.userFormat = 0;
+  stream_.userInterleaved = true;
+  stream_.streamTime = 0.0;
+  stream_.apiHandle = 0;
+  stream_.deviceBuffer = 0;
+  stream_.callbackInfo.callback = 0;
+  stream_.callbackInfo.userData = 0;
+  stream_.callbackInfo.isRunning = false;
+  stream_.callbackInfo.errorCallback = 0;
+  for ( int i=0; i<2; i++ ) {
+    stream_.device[i] = 11111;
+    stream_.doConvertBuffer[i] = false;
+    stream_.deviceInterleaved[i] = true;
+    stream_.doByteSwap[i] = false;
+    stream_.nUserChannels[i] = 0;
+    stream_.nDeviceChannels[i] = 0;
+    stream_.channelOffset[i] = 0;
+    stream_.deviceFormat[i] = 0;
+    stream_.latency[i] = 0;
+    stream_.userBuffer[i] = 0;
+    stream_.convertInfo[i].channels = 0;
+    stream_.convertInfo[i].inJump = 0;
+    stream_.convertInfo[i].outJump = 0;
+    stream_.convertInfo[i].inFormat = 0;
+    stream_.convertInfo[i].outFormat = 0;
+    stream_.convertInfo[i].inOffset.clear();
+    stream_.convertInfo[i].outOffset.clear();
+  }
 }
 
 }
 
-int RtAudio :: formatBytes(RTAUDIO_FORMAT format)
+unsigned int RtApi :: formatBytes( RtAudioFormat format )
 {
 {
-  if (format == RTAUDIO_SINT16)
+  if ( format == RTAUDIO_SINT16 )
     return 2;
     return 2;
-  else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
-           format == RTAUDIO_FLOAT32)
+  else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )
     return 4;
     return 4;
-  else if (format == RTAUDIO_FLOAT64)
+  else if ( format == RTAUDIO_FLOAT64 )
     return 8;
     return 8;
-  else if (format == RTAUDIO_SINT8)
+  else if ( format == RTAUDIO_SINT24 )
+    return 3;
+  else if ( format == RTAUDIO_SINT8 )
     return 1;
 
     return 1;
 
-  sprintf(message,"RtAudio: undefined format in formatBytes().");
-  error(RtError::WARNING);
+  errorText_ = "RtApi::formatBytes: undefined format.";
+  error( RtAudioError::WARNING );
 
   return 0;
 }
 
 
   return 0;
 }
 
-void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode)
+void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
 {
 {
-  // This method does format conversion, input/output channel compensation, and
-  // data interleaving/deinterleaving.  24-bit integers are assumed to occupy
-  // the upper three bytes of a 32-bit integer.
-
-  int j, jump_in, jump_out, channels;
-  RTAUDIO_FORMAT format_in, format_out;
-  char *input, *output;
-
-  if (mode == INPUT) { // convert device to user buffer
-    input = stream->deviceBuffer;
-    output = stream->userBuffer;
-    jump_in = stream->nDeviceChannels[1];
-    jump_out = stream->nUserChannels[1];
-    format_in = stream->deviceFormat[1];
-    format_out = stream->userFormat;
+  if ( mode == INPUT ) { // convert device to user buffer
+    stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+    stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+    stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+    stream_.convertInfo[mode].outFormat = stream_.userFormat;
   }
   else { // convert user to device buffer
   }
   else { // convert user to device buffer
-    input = stream->userBuffer;
-    output = stream->deviceBuffer;
-    jump_in = stream->nUserChannels[0];
-    jump_out = stream->nDeviceChannels[0];
-    format_in = stream->userFormat;
-    format_out = stream->deviceFormat[0];
-
-    // clear our device buffer when in/out duplex device channels are different
-    if ( stream->mode == DUPLEX &&
-         stream->nDeviceChannels[0] != stream->nDeviceChannels[1] )
-      memset(output, 0, stream->bufferSize * jump_out * formatBytes(format_out));
+    stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+    stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+    stream_.convertInfo[mode].inFormat = stream_.userFormat;
+    stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
   }
 
   }
 
-  channels = (jump_in < jump_out) ? jump_in : jump_out;
-
-  // Set up the interleave/deinterleave offsets
-  std::vector<int> offset_in(channels);
-  std::vector<int> offset_out(channels);
-  if (mode == INPUT && stream->deInterleave[1]) {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k * stream->bufferSize;
-      offset_out[k] = k;
-      jump_in = 1;
+  if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+    stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+  else
+    stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+  // Set up the interleave/deinterleave offsets.
+  if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
+    if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
+         ( mode == INPUT && stream_.userInterleaved ) ) {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
     }
     }
-  }
-  else if (mode == OUTPUT && stream->deInterleave[0]) {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k;
-      offset_out[k] = k * stream->bufferSize;
-      jump_out = 1;
+    else {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
     }
   }
     }
   }
-  else {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k;
-      offset_out[k] = k;
+  else { // no (de)interleaving
+    if ( stream_.userInterleaved ) {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+    else {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].inJump = 1;
+        stream_.convertInfo[mode].outJump = 1;
+      }
     }
   }
 
     }
   }
 
-  if (format_out == RTAUDIO_FLOAT64) {
-    FLOAT64 scale;
-    FLOAT64 *out = (FLOAT64 *)output;
+  // Add channel offset.
+  if ( firstChannel > 0 ) {
+    if ( stream_.deviceInterleaved[mode] ) {
+      if ( mode == OUTPUT ) {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].outOffset[k] += firstChannel;
+      }
+      else {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].inOffset[k] += firstChannel;
+      }
+    }
+    else {
+      if ( mode == OUTPUT ) {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
+      }
+      else {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );
+      }
+    }
+  }
+}
 
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      scale = 1.0 / 128.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
+{
+  // This function does format conversion, input/output channel compensation, and
+  // data interleaving/deinterleaving.  24-bit integers are assumed to occupy
+  // the lower three bytes of a 32-bit integer.
+
+  // Clear our device buffer when in/out duplex device channels are different
+  if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
+       ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
+    memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
+
+  int j;
+  if (info.outFormat == RTAUDIO_FLOAT64) {
+    Float64 scale;
+    Float64 *out = (Float64 *)outBuffer;
+
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      scale = 1.0 / 127.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      scale = 1.0 / 32768.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      scale = 1.0 / 32767.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int24 *in = (Int24 *)inBuffer;
+      scale = 1.0 / 8388607.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = 1.0 / 2147483647.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_FLOAT32) {
-    FLOAT32 scale;
-    FLOAT32 *out = (FLOAT32 *)output;
+  else if (info.outFormat == RTAUDIO_FLOAT32) {
+    Float32 scale;
+    Float32 *out = (Float32 *)outBuffer;
 
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      scale = 1.0 / 128.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      scale = (Float32) ( 1.0 / 127.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      scale = 1.0 / 32768.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      scale = (Float32) ( 1.0 / 32767.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int24 *in = (Int24 *)inBuffer;
+      scale = (Float32) ( 1.0 / 8388607.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = (Float32) ( 1.0 / 2147483647.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT32) {
-    INT32 *out = (INT32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+  else if (info.outFormat == RTAUDIO_SINT32) {
+    Int32 *out = (Int32 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 24;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 16;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 16;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int24 *in = (Int24 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();
+          out[info.outOffset[j]] <<= 8;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
+    else if (info.inFormat == RTAUDIO_SINT32) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Int32 *in = (Int32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT24) {
-    INT32 *out = (INT32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+  else if (info.outFormat == RTAUDIO_SINT24) {
+    Int24 *out = (Int24 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);
+          //out[info.outOffset[j]] <<= 16;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 16;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);
+          //out[info.outOffset[j]] <<= 8;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
+    else if (info.inFormat == RTAUDIO_SINT24) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Int24 *in = (Int24 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] & 0xffffff00);
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);
+          //out[info.outOffset[j]] >>= 8;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT16) {
-    INT16 *out = (INT16 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) in[offset_in[j]];
-          out[offset_out[j]] <<= 8;
+  else if (info.outFormat == RTAUDIO_SINT16) {
+    Int16 *out = (Int16 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 8;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
+    else if (info.inFormat == RTAUDIO_SINT16) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int24 *in = (Int24 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT8) {
-    signed char *out = (signed char *)output;
-    if (format_in == RTAUDIO_SINT8) {
+  else if (info.outFormat == RTAUDIO_SINT8) {
+    signed char *out = (signed char *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 8) & 0x00ff);
+    if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int24 *in = (Int24 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
 }
 
       }
     }
   }
 }
 
-void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
+//static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
+//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
+//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
+
+void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
 {
 {
-  register char val;
-  register char *ptr;
+  char val;
+  char *ptr;
 
   ptr = buffer;
 
   ptr = buffer;
-  if (format == RTAUDIO_SINT16) {
-    for (int i=0; i<samples; i++) {
+  if ( format == RTAUDIO_SINT16 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 2nd bytes.
       val = *(ptr);
       *(ptr) = *(ptr+1);
       // Swap 1st and 2nd bytes.
       val = *(ptr);
       *(ptr) = *(ptr+1);
@@ -7009,10 +10088,9 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       ptr += 2;
     }
   }
       ptr += 2;
     }
   }
-  else if (format == RTAUDIO_SINT24 ||
-           format == RTAUDIO_SINT32 ||
-           format == RTAUDIO_FLOAT32) {
-    for (int i=0; i<samples; i++) {
+  else if ( format == RTAUDIO_SINT32 ||
+            format == RTAUDIO_FLOAT32 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 4th bytes.
       val = *(ptr);
       *(ptr) = *(ptr+3);
       // Swap 1st and 4th bytes.
       val = *(ptr);
       *(ptr) = *(ptr+3);
@@ -7024,12 +10102,23 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
-      // Increment 4 bytes.
-      ptr += 4;
+      // Increment 3 more bytes.
+      ptr += 3;
+    }
+  }
+  else if ( format == RTAUDIO_SINT24 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
+      // Swap 1st and 3rd bytes.
+      val = *(ptr);
+      *(ptr) = *(ptr+2);
+      *(ptr+2) = val;
+
+      // Increment 2 more bytes.
+      ptr += 2;
     }
   }
     }
   }
-  else if (format == RTAUDIO_FLOAT64) {
-    for (int i=0; i<samples; i++) {
+  else if ( format == RTAUDIO_FLOAT64 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 8th bytes
       val = *(ptr);
       *(ptr) = *(ptr+7);
       // Swap 1st and 8th bytes
       val = *(ptr);
       *(ptr) = *(ptr+7);
@@ -7053,30 +10142,18 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
-      // Increment 8 bytes.
-      ptr += 8;
+      // Increment 5 more bytes.
+      ptr += 5;
     }
   }
 }
 
     }
   }
 }
 
+  // Indentation settings for Vim and Emacs
+  //
+  // Local Variables:
+  // c-basic-offset: 2
+  // indent-tabs-mode: nil
+  // End:
+  //
+  // vim: et sts=2 sw=2
 
 
-// *************************************************** //
-//
-// RtError class definition.
-//
-// *************************************************** //
-
-RtError :: RtError(const char *p, TYPE tipe)
-{
-  type = tipe;
-  strncpy(error_message, p, 256);
-}
-
-RtError :: ~RtError()
-{
-}
-
-void RtError :: printMessage()
-{
-  printf("\n%s\n\n", error_message);
-}