Various configure changes for MinGW (gps).
[rtaudio.git] / RtAudio.cpp
index 8e44ba2d4b54f08a070e2e4c8edb99a4f912ab86..f5b324f22539d713e55310196e747ff37dfd7902 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), SGI, Macintosh OS X (CoreAudio and Jack), and Windows
+    (DirectSound and ASIO) 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-2007 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 2.1.1, 24 October 2002
+// RtAudio: Version 4.0
 
 #include "RtAudio.h"
 
 #include "RtAudio.h"
-#include <vector>
-#include <stdio.h>
-#include <iostream.h>
+#include <iostream>
 
 // 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__)
   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
 
 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
   #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
+#elif defined(__LINUX_ALSA__) || 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()
+void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
 {
 {
-  initialize();
+  apis.clear();
+
+  // 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_OSS__)
+  apis.push_back( LINUX_OSS );
+#endif
+#if defined(__WINDOWS_ASIO__)
+  apis.push_back( WINDOWS_ASIO );
+#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
+}
 
 
-  if (nDevices <= 0) {
-    sprintf(message, "RtAudio: no audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
+void RtAudio :: openRtApi( RtAudio::Api api )
+{
+#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_OSS__)
+  if ( api == LINUX_OSS )
+    rtapi_ = new RtApiOss();
+#endif
+#if defined(__WINDOWS_ASIO__)
+  if ( api == WINDOWS_ASIO )
+    rtapi_ = new RtApiAsio();
+#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
 }
 
 }
 
-RtAudio :: RtAudio(int *streamId,
-                   int outputDevice, int outputChannels,
-                   int inputDevice, int inputChannels,
-                   RTAUDIO_FORMAT format, int sampleRate,
-                   int *bufferSize, int numberOfBuffers)
+RtAudio :: RtAudio( RtAudio::Api api ) throw()
 {
 {
-  initialize();
+  rtapi_ = 0;
 
 
-  if (nDevices <= 0) {
-    sprintf(message, "RtAudio: no audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
-  }
+  if ( api != UNSPECIFIED ) {
+    // Attempt to open the specified API.
+    openRtApi( api );
+    if ( rtapi_ ) return;
 
 
-  try {
-    *streamId = openStream(outputDevice, outputChannels, inputDevice, inputChannels,
-                           format, sampleRate, bufferSize, numberOfBuffers);
+    // 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_->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 print out an error message.
+  std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n";
+}
+
+RtAudio :: ~RtAudio() throw()
+{
+  delete rtapi_;
 }
 
 }
 
-RtAudio :: ~RtAudio()
+void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
+                            RtAudio::StreamParameters *inputParameters,
+                            RtAudioFormat format, unsigned int sampleRate,
+                            unsigned int *bufferFrames,
+                            RtAudioCallback callback, void *userData,
+                            RtAudio::StreamOptions *options )
 {
 {
-  // close any existing streams
-  while ( streams.size() )
-    closeStream( streams.begin()->first );
+  return rtapi_->openStream( outputParameters, inputParameters, format,
+                             sampleRate, bufferFrames, callback,
+                             userData, options );
+}
+
+// *************************************************** //
+//
+// 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;
 }
 
 }
 
-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 )
+{
+  if ( stream_.state != STREAM_CLOSED ) {
+    errorText_ = "RtApi::openStream: a stream is already open!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  if ( formatBytes(format) == 0 ) {
-    sprintf(message,"RtAudio: 'format' parameter value is undefined.");
-    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( RtError::INVALID_USE );
   }
 
   }
 
-  if ( outputChannels > 0 ) {
-    if (outputDevice > nDevices || outputDevice < 0) {
-      sprintf(message,"RtAudio: 'outputDevice' parameter value (%d) is invalid.", outputDevice);
-      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( RtError::INVALID_USE );
   }
 
   }
 
-  if ( inputChannels > 0 ) {
-    if (inputDevice > nDevices || inputDevice < 0) {
-      sprintf(message,"RtAudio: 'inputDevice' parameter value (%d) is invalid.", inputDevice);
-      error(RtError::INVALID_PARAMETER);
-    }
+  if ( oParams == NULL && iParams == NULL ) {
+    errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  // 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 ( formatBytes(format) == 0 ) {
+    errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
+    error( RtError::INVALID_USE );
   }
   }
-  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 ( 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( RtError::INVALID_USE );
     }
     }
-    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( RtError::INVALID_USE );
     }
   }
 
     }
   }
 
-  if ( inputChannels > 0 && ( result == SUCCESS || outputChannels <= 0 ) ) {
+  clearStreamInfo();
+  bool result;
 
 
-    mode = INPUT;
-    channels = inputChannels;
+  if ( oChannels > 0 ) {
 
 
-    if ( inputDevice == 0 ) { // Try default device first.
-      defaultDevice = getDefaultInputDevice();
-      device = defaultDevice;
-    }
-    else
-      device = inputDevice - 1;
+    result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
+                              sampleRate, format, bufferFrames, options );
+    if ( result == false ) error( RtError::SYSTEM_ERROR );
+  }
 
 
-    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( RtError::SYSTEM_ERROR );
     }
   }
 
     }
   }
 
-  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;
 
 
-  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);
-
-  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;
-  }
+  // 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.
 
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-  info->streamId = streamId;
+  stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
 
 
-  int err = pthread_create(&info->thread, NULL, callbackHandler, &stream->callbackInfo);
-
-  if (err) {
-    info->usingCallback = false;
-    sprintf(message, "RtAudio: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
-  }
+#if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+#endif
 }
 
 }
 
-void RtAudio :: cancelStreamCallback(int streamId)
+long RtApi :: getStreamLatency( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->callbackInfo.usingCallback) {
-
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
-
-    MUTEX_LOCK(&stream->mutex);
+  verifyStream();
 
 
-    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;
+  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];
 
 
-    MUTEX_UNLOCK(&stream->mutex);
-  }
+  return totalLatency;
 }
 
 }
 
+double RtApi :: getStreamTime( void )
+{
+  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
 #endif
+}
+
 
 // *************************************************** //
 //
 
 // *************************************************** //
 //
@@ -363,3236 +372,2724 @@ 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.
+//
+// The mechanism for querying and setting system parameters was
+// updated (and perhaps simplified) in OS-X version 10.4.  However,
+// since 10.4 support is not necessarily available to all users, I've
+// decided not to update the respective code at this time.  Perhaps
+// this will happen when Apple makes 10.4 free for everyone. :-)
+
+// A structure to hold various information related to the CoreAudio API
+// implementation.
+struct CoreHandle {
+  AudioDeviceID id[2];    // device ids
+  UInt32 iStream[2];      // device stream index (first for mono mode)
+  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) { 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);
-  }
+  // Nothing to do here.
+}
 
 
-  // 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;
+  OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
+    error( RtError::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 );
 
   OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
                                               &dataSize, &id );
 
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default input device." );
-    error(RtError::WARNING);
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
+    error( RtError::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 ];
+  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
+    error( RtError::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( RtError::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 );
 
   OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
                                               &dataSize, &id );
 
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default output device." );
-    error(RtError::WARNING);
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
+    error( RtError::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 ];
+  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
+    error( RtError::WARNING );
+    return 0;
   }
 
   }
 
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
+
+  errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
+  error( RtError::WARNING );
   return 0;
 }
 
   return 0;
 }
 
-static bool deviceSupportsFormat( AudioDeviceID id, bool isInput,
-                                  AudioStreamBasicDescription  *desc, bool isDuplex )
+RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 {
 {
-  OSStatus result = noErr;
-  UInt32 dataSize = sizeof( AudioStreamBasicDescription );
-
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreamFormatSupported,
-                                   &dataSize, desc );
-
-  if (result == kAudioHardwareNoError) {
-    if ( isDuplex ) {
-      result = AudioDeviceGetProperty( id, 0, true,
-                                       kAudioDevicePropertyStreamFormatSupported,
-                                       &dataSize, desc );
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
+    error( RtError::INVALID_USE );
+  }
 
 
-      if (result != kAudioHardwareNoError)
-        return false;
-    }
-    return true;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  return false;
-}
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
+    error( RtError::WARNING );
+    return info;
+  }
 
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  OSStatus err = noErr;
+  AudioDeviceID id = deviceList[ device ];
 
 
-  // Get the device manufacturer and name.
+  // Get the device name.
+  info.name.erase();
   char name[256];
   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;
   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;
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyDeviceManufacturer,
+                                   &dataSize, name );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
   }
-  strncat(fullname, name, 254);
-  strncat(info->name, fullname, 128);
+  info.name.append( (const char *)name, strlen(name) );
+  info.name.append( ": " );
 
 
-  // Get output channel information.
-  unsigned int i, minChannels, maxChannels, nStreams = 0;
+  dataSize = 256;
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyDeviceName,
+                                   &dataSize, name );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
+  }
+  info.name.append( (const char *)name, strlen(name) );
+
+  // 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;
+  result = AudioDeviceGetPropertyInfo( id, 0, false,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
+    error( RtError::WARNING );
+    return info;
+  }
+
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::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".
+  result = AudioDeviceGetPropertyInfo( id, 0, true,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
+    error( RtError::WARNING );
+    return info;
+  }
+
+  result = AudioDeviceGetProperty( id, 0, true,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::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;
-  }
-
-  // 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.
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  // 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;
-  }
+  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
+                                       kAudioDevicePropertyAvailableNominalSampleRates,
+                                       &dataSize, NULL );
 
 
-  // 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;
+  if ( result != kAudioHardwareNoError || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
 
   }
 
-  // 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;
-  }
+  UInt32 nRanges = dataSize / sizeof( AudioValueRange );
+  AudioValueRange rangeList[ nRanges ];
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyAvailableNominalSampleRates,
+                                   &dataSize, &rangeList );
 
 
-  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;
+  if ( result != kAudioHardwareNoError ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
 
   }
 
-  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;
+  Float64 minimumRate = 100000000.0, maximumRate = 0.0;
+  for ( UInt32 i=0; i<nRanges; i++ ) {
+    if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;
+    if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;
   }
 
   }
 
-  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;
+  info.sampleRates.clear();
+  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] );
   }
 
   }
 
-  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( RtError::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 ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
+  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)
+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;
+}
+
+OSStatus deviceListener( AudioDeviceID inDevice,
+                         UInt32 channel,
+                         Boolean isInput,
+                         AudioDevicePropertyID propertyID,
+                         void* handlePointer )
+{
+  CoreHandle *handle = (CoreHandle *) handlePointer;
+  if ( propertyID == kAudioDeviceProcessorOverload ) {
+    if ( isInput )
+      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 bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property )
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
+  OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL );
+  return result == 0;
+}
 
 
-  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;
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &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;
 
-  // Search for a stream which contains the desired number of channels.
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  unsigned int deviceChannels, nStreams;
+  // Set or disable "hog" mode.
+  dataSize = sizeof( UInt32 );
+  UInt32 doHog = 0;
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1;
+  result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Get the stream "configuration".
+  AudioBufferList      *bufferList;
+  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
+    return FAILURE;
+  }
+
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Search for a stream that contains 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 currently only supports the use of one stream
+  // of interleaved data or multiple consecutive single-channel
+  // streams.  Thus, our search below is limited to these two
+  // contexts.
+  unsigned int streamChannels = 0, nStreams = 0;
   UInt32 iChannel = 0, iStream = 0;
   UInt32 iChannel = 0, iStream = 0;
-  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;
+  unsigned int offsetCounter = firstChannel;
+  stream_.deviceInterleaved[mode] = true;
+  nStreams = bufferList->mNumberBuffers;
+  bool foundStream = false;
+
+  for ( iStream=0; iStream<nStreams; iStream++ ) {
+    streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+    if ( streamChannels >= channels + offsetCounter ) {
+      iChannel += offsetCounter;
+      foundStream = true;
+      break;
     }
     }
-    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 ( streamChannels > offsetCounter ) break;
+    offsetCounter -= streamChannels;
+    iChannel += streamChannels;
+  }
+
+  // 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 (+ channel offset).
+  if ( foundStream == false ) {
+    unsigned int counter = 0;
+    offsetCounter = firstChannel;
+    iChannel = 0;
+    for ( iStream=0; iStream<nStreams; iStream++ ) {
+      streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+      if ( offsetCounter ) {
+        if ( streamChannels > offsetCounter ) break;
+        offsetCounter -= streamChannels;
       }
       }
-      // 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;
-        }
+      else if ( streamChannels == 1 )
+        counter++;
+      else
+        counter = 0;
+      if ( counter == channels ) {
+        iStream -= channels - 1;
+        iChannel -= channels - 1;
+        stream_.deviceInterleaved[mode] = false;
+        foundStream = true;
+        break;
       }
       }
+      iChannel += streamChannels;
     }
   }
     }
   }
-  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);
-    return FAILURE;
-  }
+  free( bufferList );
 
 
-  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);
+  if ( foundStream == false ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
+    errorText_ = errorStream_.str();
     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;
   // 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);
+  dataSize = sizeof( AudioValueRange );
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyBufferFrameSizeRange,
+                                   &dataSize, &bufferRange );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range 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;
+  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 mono mode, I'm assuming we only need to
 
   // 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);
+  // make this setting for the master channel.
+  UInt32 theSize = (UInt32) *bufferSize;
+  dataSize = sizeof( UInt32 );
+  result = AudioDeviceSetProperty( id, NULL, 0, isInput,
+                                   kAudioDevicePropertyBufferFrameSize,
+                                   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!
     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);
+  *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;
   }
 
     return FAILURE;
   }
 
-  stream->bufferSize = *bufferSize;
-  stream->nBuffers = 1;
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 1;
+
+  // Get the stream ID(s) so we can set the stream format.  In mono
+  // mode, we'll have to do this for each stream (channel).
+  AudioStreamID streamIDs[ nStreams ];
+  dataSize = nStreams * sizeof( AudioStreamID );
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyStreams,
+                                   &dataSize, &streamIDs );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-  // Set the stream format description.  Do for each channel in mono mode.
+  // Now set the stream format.  Also, check the physical format of the
+  // device and change that if necessary.
   AudioStreamBasicDescription  description;
   dataSize = sizeof( AudioStreamBasicDescription );
   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);
+  if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
+  else nStreams = channels;
+
+  bool updateFormat;
+  for ( unsigned int i=0; i<nStreams; i++ ) {
+
+    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
+                                     kAudioStreamPropertyVirtualFormat,
+                                     &dataSize, &description );
+
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    // Set the sample rate and data format id.
-    description.mSampleRate = (double) sampleRate;
-    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);
+    // 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.
+    updateFormat = false;
+    if ( fabs( description.mSampleRate - (double)sampleRate ) > 1.0 ) {
+      description.mSampleRate = (double) sampleRate;
+      updateFormat = true;
+    }
+
+    if ( description.mFormatID != kAudioFormatLinearPCM ) {
+      description.mFormatID = kAudioFormatLinearPCM;
+      updateFormat = true;
+    }
+
+    if ( updateFormat ) {
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0,
+                                       kAudioStreamPropertyVirtualFormat,
+                                       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;
+      }
+    }
+
+    // Now check the physical format.
+    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
+                                     kAudioStreamPropertyPhysicalFormat,
+                                     &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;
     }
+
+    if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 24 ) {
+      description.mFormatID = kAudioFormatLinearPCM;
+      AudioStreamBasicDescription      testDescription = description;
+      unsigned long formatFlags;
+
+      // We'll try higher bit rates first and then work our way down.
+      testDescription.mBitsPerChannel = 32;
+      formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 32;
+      formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 24;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 16;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 8;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
+        errorText_ = errorStream_.str();
+        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);
-    return FAILURE;
+  // Get the stream latency.  There can be latency in both the device
+  // and the stream.  First, attempt to get the device latency on the
+  // master channel or the first open channel.  Errors that might
+  // occur here are not deemed critical.
+  UInt32 latency, channel = 0;
+  dataSize = sizeof( UInt32 );
+  AudioDevicePropertyID property = kAudioDevicePropertyLatency;
+  for ( int i=0; i<2; i++ ) {
+    if ( hasProperty( id, channel, isInput, property ) == true ) break;
+    channel = iChannel + 1 + i;
+  }
+  if ( channel <= iChannel + 1 ) {
+    result = AudioDeviceGetProperty( id, channel, isInput, property, &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( RtError::WARNING );
+    }
+  }
+
+  // Now try to get the stream latency.  For "mono" mode, I assume the
+  // latency is equal for all single-channel streams.
+  result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
+  if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;
+  else {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
   }
 
   }
 
-  stream->doByteSwap[mode] = false;
-  if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian )
-    stream->doByteSwap[mode] = true;
+  // 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 ( stream_.deviceInterleaved[mode] )
+    stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
+  else // mono mode
+    stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = iChannel;  // offset within a CoreAudio stream
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+
+  // 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 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 ( stream->deInterleave[mode] )
-    stream->nDeviceChannels[mode] = channels;
+    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] = iStream;
+  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 = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  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 the device
+  // buffers are non-interleaved ("mono" mode).
+  if ( !stream_.deviceInterleaved[mode] && 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;
-
-      // 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 )
-    // Only one callback procedure per device.
-    stream->mode = DUPLEX;
-  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;
+      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;
+      }
+
+      // Save a pointer to our own device buffer in the CoreHandle
+      // structure because we may need to use the stream_.deviceBuffer
+      // variable to point to the CoreAudio buffer before buffer
+      // conversion (if we have a duplex stream with two different
+      // conversion schemes).
+      handle->deviceBuffer = stream_.deviceBuffer;
     }
     }
-    if ( stream->mode == OUTPUT && mode == INPUT )
-      stream->mode = DUPLEX;
-    else
-      stream->mode = mode;
   }
 
   }
 
-  // If we wanted to use property listeners, they would be setup here.
+  stream_.sampleRate = sampleRate;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
 
 
-  return SUCCESS;
+  // Setup the buffer conversion information structure.  We override
+  // the channel offset value and perform our own setting for that
+  // here.
+  if ( stream_.doConvertBuffer[mode] ) {
+    setConvertInfo( mode, 0 );
 
 
- memory_error:
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+    // Add channel offset for interleaved channels.
+    if ( firstChannel > 0 && 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;
+      }
+    }
   }
   }
-  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 ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
+    // Only one callback procedure per device.
+    stream_.mode = DUPLEX;
+  else {
+    result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
+    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;
+    else
+      stream_.mode = mode;
+  }
 
 
-  if (stream->callbackInfo.usingCallback) {
+  // Setup the device property listener for over/underload.
+  result = AudioDeviceAddPropertyListener( id, 0, isInput,
+                                           kAudioDeviceProcessorOverload,
+                                           deviceListener, (void *) handle );
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  return SUCCESS;
 
 
-    MUTEX_LOCK(&stream->mutex);
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-    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;
   }
   }
+
+  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( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
-
-  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 );
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[0], callbackHandler );
+    AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
   }
 
   }
 
-  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 ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[1], callbackHandler );
+    AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
   }
 
   }
 
-  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 ( handle->deviceBuffer ) {
+    free( handle->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);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiCore::startStream(): the stream is already running!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  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);
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result == noErr ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiCore :: stopStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  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 = 1;
+      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;
-
  unlock:
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: abortStream(int streamId)
-{
-  stopStream( streamId );
-}
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-// 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;
+  stream_.state = STREAM_STOPPED;
+  if ( result == noErr ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiCore :: abortStream( void )
 {
 {
-  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( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    usleep(stream->callbackInfo.waitTime);
-
-  MUTEX_LOCK(&stream->mutex);
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
 
 
-  stream->callbackInfo.blockTick = true;
-
-  MUTEX_UNLOCK(&stream->mutex);
+  stopStream();
 }
 
 }
 
-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);
-
-  CALLBACK_INFO *info;
-  AudioBufferList *inBufferList = (AudioBufferList *) inData;
-  AudioBufferList *outBufferList = (AudioBufferList *) outData;
-
-  if (stream->state == STREAM_STOPPED) return;
-
-  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);
-  }
-  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;
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::WARNING );
+    return FAILURE;
+  }
+
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      pthread_cond_signal( &handle->condition );
+    else
+      stopStream();
+    return SUCCESS;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
+
+  AudioDeviceID outputDevice = handle->id[0];
 
 
-  // Invoke user callback first, to get fresh output data.  Don't
-  // invoke the user callback if duplex mode, the input/output devices
-  // are different, and this function is called for the input device.
-  if ( info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[0] ) ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    info->stopStream = callback(stream->userBuffer, stream->bufferSize, info->userData);
+  // 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;
+    }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
+    }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
   }
 
-  if ( stream->mode == OUTPUT || ( stream->mode == DUPLEX && deviceId == info->device[0] ) ) {
+  if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
+
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-    if (stream->doConvertBuffer[0]) {
+      if ( stream_.deviceInterleaved[0] ) {
+        memset( outBufferList->mBuffers[handle->iStream[0]].mData,
+                0,
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else {
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  0,
+                  outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
+        }
+      }
+    }
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-      if ( !stream->deInterleave[0] )
-        stream->deviceBuffer = (char *) outBufferList->mBuffers[stream->handle[0]].mData;
+      if ( stream_.deviceInterleaved[0] )
+        stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
       else
       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 );
+        stream_.deviceBuffer = handle->deviceBuffer;
+
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+
+      if ( !stream_.deviceInterleaved[0] ) {
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
         }
       }
 
     }
     else {
         }
       }
 
     }
     else {
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
+      if ( stream_.deviceInterleaved[0] ) {
+        memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
+                stream_.userBuffer[0],
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else {
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
+        }
+      }
+    }
 
 
-      memcpy(outBufferList->mBuffers[stream->handle[0]].mData,
-             stream->userBuffer,
-             outBufferList->mBuffers[stream->handle[0]].mDataByteSize );
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
   }
 
     }
   }
 
-  if ( stream->mode == INPUT || ( stream->mode == DUPLEX && deviceId == info->device[1] ) ) {
+  AudioDeviceID inputDevice = handle->id[1];
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
 
 
-    if (stream->doConvertBuffer[1]) {
+    if ( stream_.doConvertBuffer[1] ) {
 
 
-      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[1] )
+        stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
+      else {
+        stream_.deviceBuffer = (char *) handle->deviceBuffer;
+        UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
+          memcpy( &stream_.deviceBuffer[i*bufferBytes],
+                  inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
         }
       }
         }
       }
-      else
-        stream->deviceBuffer = (char *) inBufferList->mBuffers[stream->handle[1]].mData;
 
 
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
     }
     else {
 
     }
     else {
-      memcpy(stream->userBuffer,
-             inBufferList->mBuffers[stream->handle[1]].mData,
-             inBufferList->mBuffers[stream->handle[1]].mDataByteSize );
-
-      if (stream->doByteSwap[1])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[1],
-                       stream->userFormat);
+      memcpy( stream_.userBuffer[1],
+              inBufferList->mBuffers[handle->iStream[1]].mData,
+              inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
     }
   }
 
     }
   }
 
-  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";
 
 
-  // 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 kAudioHardwareUnsupportedOperationError:
+    return "kAudioHardwareUnsupportedOperationError";
+
+  case kAudioDeviceUnsupportedFormatError:
+    return "kAudioDeviceUnsupportedFormatError";
+
+  case kAudioDevicePermissionsError:
+    return "kAudioDevicePermissionsError";
+
+  default:
+    return "CoreAudio unknown error";
+       }
 }
 
 }
 
-int RtAudio :: getDefaultInputDevice(void)
+//******************** 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>
+
+// 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; }
+};
+
+RtApiJack :: RtApiJack()
 {
 {
-  // No ALSA API functions for default devices.
-  return 0;
+  // Nothing to do here.
 }
 
 }
 
-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_client_t *client = jack_client_new( "RtApiJackCount" );
+  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 (:).
+    unsigned int 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);
-
-  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;
-  }
+  jack_client_close( client );
+  return nDevices;
+}
 
 
-  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;
+RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  jack_client_t *client = jack_client_new( "RtApiJackInfo" );
+  if ( client == 0 ) {
+    errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
+    error( RtError::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 (:).
+    unsigned int 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 );
   }
 
   }
 
-  // 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;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  // Get output channel information.
-  info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params);
-  info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params);
-
-  snd_pcm_close(handle);
-
- capture_probe:
-  // Now try for capture
-  stream = SND_PCM_STREAM_CAPTURE;
-  snd_pcm_info_set_stream(pcminfo, stream);
+  // Get the current jack server sample rate.
+  info.sampleRates.clear();
+  info.sampleRates.push_back( jack_get_sample_rate( client ) );
 
 
-  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( RtError::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;
+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)
+void jackShutdown( void *infoPointer )
 {
 {
-#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.
-  const char *name = devices[device].name;
+  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;
+
+  object->closeStream();
+  std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
+}
 
 
-  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;
-  }
+int jackXrun( void *infoPointer )
+{
+  JackHandle *handle = (JackHandle *) infoPointer;
 
 
-  // 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;
-  }
+  if ( handle->ports[0] ) handle->xrun[0] = true;
+  if ( handle->ports[1] ) handle->xrun[1] = true;
 
 
-#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
+  return 0;
+}
 
 
+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;
 
 
-  // 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;
+  // 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 ) ) {
+    if ( options && !options->streamName.empty() )
+      client = jack_client_new( options->streamName.c_str() );
+    else
+      client = jack_client_new( "RtApiJack" );
+    if ( client == 0 ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
+      error( RtError::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 (:).
+    unsigned int 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;
+  // 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 = JackPortIsOutput;
+  if ( mode == INPUT ) flag = JackPortIsInput;
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
   }
 
   }
 
-  // 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;
+  // 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;
   }
 
   }
 
-  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;
+  // 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;
   }
   }
+  stream_.sampleRate = jackRate;
 
 
-  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;
-  }
+  // Get the latency of the JACK port.
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports[ firstChannel ] )
+    stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
+  free( ports );
 
 
-  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;
-  }
+  // The jack server always uses 32-bit floating-point data.
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+  stream_.userFormat = 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;
-  }
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
 
-  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;
-  }
+  // Jack always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
 
-  // 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;
+  // Jack always provides host byte-ordered data.
+  stream_.doByteSwap[mode] = false;
 
 
- 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);
-    return FAILURE;
-  }
+  // 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;
 
 
-  // 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;
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+
+  // 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;
+    }
+
+    if ( pthread_cond_init(&handle->condition, NULL) ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
     }
     }
+    stream_.apiHandle = (void *) handle;
+    handle->client = client;
   }
   }
+  handle->deviceName[mode] = deviceName;
 
 
-  // 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);
-    return FAILURE;
+  // 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;
   }
 
   }
 
-  // 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;
-  }
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-  device_channels = snd_pcm_hw_params_get_channels_min(hw_params);
-  if (device_channels < channels) device_channels = channels;
-  stream->nDeviceChannels[mode] = device_channels;
+    bool makeBuffer = true;
+    if ( mode == OUTPUT )
+      bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+    else { // mode == INPUT
+      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;
+      }
+    }
 
 
-  // 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;
+    if ( makeBuffer ) {
+      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;
+      }
+    }
   }
 
   }
 
-  // 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;
+  // 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;
   }
 
   }
 
-  // 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;
-  }
+  stream_.device[mode] = device;
+  stream_.channelOffset[mode] = firstChannel;
+  stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
 
 
-  // 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;
+  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 );
   }
 
   }
 
-  stream->bufferSize = *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;
+  // 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 );
+    }
   }
   }
-
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtAudio: ALSA dump hardware params after installation:\n\n");
-  snd_pcm_hw_params_dump(hw_params, out);
-#endif
-
-  /*
-  // 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;
+  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 );
+    }
   }
   }
-  */
 
 
-  // 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] ) {
+  // 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 );
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
+  return SUCCESS;
 
 
-    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;
-  }
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->condition );
+    jack_client_close( handle->client );
 
 
-  if ( stream->doConvertBuffer[mode] ) {
+    if ( handle->ports[0] ) free( handle->ports[0] );
+    if ( handle->ports[1] ) free( handle->ports[1] );
 
 
-    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;
-      }
-    }
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-    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;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
 
     }
   }
 
-  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;
-
-  return SUCCESS;
-
- memory_error:
-  if (stream->handle[0]) {
-    snd_pcm_close(stream->handle[0]);
-    stream->handle[0] = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  if (stream->handle[1]) {
-    snd_pcm_close(stream->handle[1]);
-    stream->handle[1] = 0;
-  }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
-  }
-  sprintf(message, "RtAudio: ALSA error allocating buffer memory (%s).", name);
-  error(RtError::WARNING);
+
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiJack :: 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_ = "RtApiJack::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( handle ) {
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-  }
+    if ( stream_.state == STREAM_RUNNING )
+      jack_deactivate( handle->client );
 
 
-  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]);
+    jack_client_close( handle->client );
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
-
-  if (stream->handle[0])
-    snd_pcm_close(stream->handle[0]);
-
-  if (stream->handle[1])
-    snd_pcm_close(stream->handle[1]);
+  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->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 RtApiJack :: startStream( void )
 {
 {
-  // This method calls snd_pcm_prepare if the device isn't already in that state.
-
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiJack::startStream(): the stream is already running!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK(&stream_.mutex);
 
 
-  if (stream->state == STREAM_RUNNING)
+  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;
-  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);
-      }
+  const char **ports;
+
+  // Get the list of available ports.
+  if ( 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) {
-    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);
+    // 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_RUNNING;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: stopStream(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_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);
+  if ( 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_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.  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);
+  MUTEX_UNLOCK(&stream_.mutex);
+
+  if ( result == 0 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+void RtApiJack :: stopStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  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 ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
   }
 
     }
   }
 
-  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);
-    }
-  }
-  stream->state = STREAM_STOPPED;
+  jack_deactivate( handle->client );
+  stream_.state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  MUTEX_UNLOCK( &stream_.mutex );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiJack :: abortStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  int err = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  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);
-    }
-  }
-
-  frames = err;
-
-  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;
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
 
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  stopStream();
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+bool RtApiJack :: callbackEvent( unsigned long nframes )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
-    return;
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::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( RtError::WARNING );
+    return FAILURE;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
-
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
 
 
-  int err;
-  char *buffer;
-  int channels;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      pthread_cond_signal( &handle->condition );
+    else
+      stopStream();
+    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];
-    }
-    else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[0];
-      format = stream->userFormat;
-    }
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+  // 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;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
+    }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
+  }
+
+  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 > 0 ) { // 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) {
-
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
-    }
-    else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[1];
-      format = stream->userFormat;
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
     }
+  }
 
 
-    // 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);
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-    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;
+    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 );
       }
       }
-      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);
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+    }
+    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 );
       }
     }
       }
     }
-
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[1])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
-
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
   }
 
  unlock:
   }
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  MUTEX_UNLOCK(&stream_.mutex);
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 }
+//******************** End of __UNIX_JACK__ *********************//
+#endif
 
 
-extern "C" void *callbackHandler(void *ptr)
-{
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
 
 
-  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;
-    }
-  }
+// 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.
 
 
-  return 0;
-}
+#include "asiosys.h"
+#include "asio.h"
+#include "iasiothiscallresolver.h"
+#include "asiodrivers.h"
+#include <cmath>
 
 
-//******************** End of __LINUX_ALSA__ *********************//
+AsioDrivers drivers;
+ASIOCallbacks asioCallbacks;
+ASIODriverInfo driverInfo;
+CallbackInfo *asioCallbackInfo;
+bool asioXRun;
 
 
-#elif defined(__LINUX_OSS__)
+struct AsioHandle {
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+  ASIOBufferInfo *bufferInfos;
+  HANDLE condition;
 
 
-#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>
+  AsioHandle()
+    :drainCounter(0), internalDrain(false), bufferInfos(0) {}
+};
 
 
-#define DAC_NAME "/dev/dsp"
-#define MAX_DEVICES 16
-#define MAX_CHANNELS 16
+// Function declarations (definitions at end of section)
+static const char* getAsioErrorString( ASIOError result );
+void sampleRateChanged( ASIOSampleRate sRate );
+long asioMessages( long selector, long value, void* message, double* opt );
 
 
-void RtAudio :: initialize(void)
+RtApiAsio :: RtApiAsio()
 {
 {
-  // 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);
-      }
-    }
-  }
-  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++;
-  }
-
-  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);
+  // 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( RtError::WARNING );
   }
   }
+  coInitialized_ = true;
 
 
-  // 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]);
-  }
+  drivers.removeCurrentDriver();
+  driverInfo.asioVersion = 2;
 
 
-  return;
+  // See note in DirectSound implementation about GetDesktopWindow().
+  driverInfo.sysRef = GetForegroundWindow();
 }
 
 }
 
-int RtAudio :: getDefaultInputDevice(void)
+RtApiAsio :: ~RtApiAsio()
 {
 {
-  // No OSS API functions for default devices.
-  return 0;
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+  if ( coInitialized_ ) CoUninitialize();
 }
 
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+unsigned int RtApiAsio :: getDeviceCount( void )
 {
 {
-  // No OSS API functions for default devices.
-  return 0;
+  return (unsigned int) drivers.asioGetNumDev();
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
 {
 {
-  int i, fd, channels, mask;
-
-  // The OSS API doesn't provide a means for probing the capabilities
-  // of devices.  Thus, we'll just pursue a brute force method.
-
-  // 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;
-  }
-  info->maxOutputChannels = i;
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-  // 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;
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
+    error( RtError::INVALID_USE );
   }
   }
-  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;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  // 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
+  // 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( RtError::WARNING );
+      return info;
     }
     }
-    // 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;
+    return devices_[ device ];
   }
   }
-  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;
+  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( RtError::WARNING );
+    return info;
   }
 
   }
 
-  // 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;
+  info.name = driverName;
 
 
-    // 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;
+  if ( !drivers.loadDriver( driverName ) ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
   }
-  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;
-  }
-  else {
-    fd = open(info->name, O_RDONLY | O_NONBLOCK);
-    channels = info->maxInputChannels;
+  result = ASIOInit( &driverInfo );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
 
   }
 
-  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;
+  // 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( RtError::WARNING );
+    return info;
   }
 
   }
 
-  // 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;
-  }
+  info.outputChannels = outputChannels;
+  info.inputChannels = inputChannels;
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  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 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] );
   }
 
   }
 
-  // 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;
-  }
-#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;
-  }
-#endif
-  if (mask & AFMT_S8) {
-    format = AFMT_S8;
-    info->nativeFormats |= RTAUDIO_SINT8;
-  }
-  if (mask & AFMT_S16_BE) {
-    format = AFMT_S16_BE;
-    info->nativeFormats |= RTAUDIO_SINT16;
-  }
-  if (mask & AFMT_S16_LE) {
-    format = AFMT_S16_LE;
-    info->nativeFormats |= RTAUDIO_SINT16;
+  // 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( RtError::WARNING );
+    return info;
   }
 
   }
 
-  // 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;
+  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;
+
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
+  if ( getDefaultInputDevice() == device )
+    info.isDefaultInput = true;
+
+  info.probed = true;
+  drivers.removeCurrentDriver();
+  return info;
+}
+
+void bufferSwitch( long index, ASIOBool processNow )
+{
+  RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
+  object->callbackEvent( index );
+}
+
+void RtApiAsio :: saveDeviceInfo( void )
+{
+  devices_.clear();
+
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
+
+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 )
+{
+  // For ASIO, a duplex stream MUST use the same driver.
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
+    return FAILURE;
   }
 
   }
 
-  // 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;
+  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;
   }
 
   }
 
-  // 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++;
-      }
+  // 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();
+
+  // Only load the driver once for duplex stream.
+  if ( mode != INPUT || stream_.mode != OUTPUT ) {
+    if ( !drivers.loadDriver( driverName ) ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    if (info->nSampleRates == 0) {
-      close(fd);
-      return;
+
+    result = ASIOInit( &driverInfo );
+    if ( result != ASE_OK ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    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;
+  // Check the device channel count.
+  long inputChannels, outputChannels;
+  result = ASIOGetChannels( &inputChannels, &outputChannels );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
   }
-  info->sampleRates[1] = speed;
-  info->nSampleRates = -1;
 
 
- finished: // That's all ... close the device and return
-  close(fd);
-  info->probed = true;
-  return;
-}
+  if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
+       ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = firstChannel;
 
 
-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;
+  // Verify the sample rate is supported.
+  result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-  const char *name = devices[device].name;
+  // Get the current sample rate
+  ASIOSampleRate currentRate;
+  result = ASIOGetSampleRate( &currentRate );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-  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);
+  // Set the sample rate only if necessary
+  if ( currentRate != sampleRate ) {
+    result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
+    if ( result != ASE_OK ) {
+      drivers.removeCurrentDriver();
+      errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    else
-      fd = open(name, O_RDONLY | O_NONBLOCK);
   }
 
   }
 
-  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);
-    goto error;
+  // 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();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  // 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);
+  // 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;
+  }
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
   }
 
   }
 
-  if (fd == -1) {
-    sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
-    goto error;
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  // 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);
-    goto error;
+  // 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();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  // 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;
-    }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
-    }
-#endif
+  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.
+    double power = std::log10( (double) *bufferSize ) / log10( 2.0 );
+    *bufferSize = (int) pow( 2.0, floor(power+0.5) );
+    if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
+    else *bufferSize = preferSize;
   }
   }
-#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
+  else if ( granularity != 0 ) {
+    // Set to an even multiple of granularity, rounding up.
+    *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
   }
   }
-#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;
-    }
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
+    drivers.removeCurrentDriver();
+    errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
+    return FAILURE;
   }
 
   }
 
-  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;
-  }
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 2;
 
 
-  // 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 ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+
+  // ASIO always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
+
+  // Allocate, if necessary, our AsioHandle structure for the stream.
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( handle == 0 ) {
+    try {
+      handle = new AsioHandle;
     }
     }
-    else {
-      if (channels < devices[device].minInputChannels)
-        device_channels = devices[device].minInputChannels;
+    catch ( std::bad_alloc& ) {
+      //if ( handle == NULL ) {    
+      drivers.removeCurrentDriver();
+      errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
+      return FAILURE;
     }
     }
-  }
-  stream->nDeviceChannels[mode] = device_channels;
+    handle->bufferInfos = 0;
 
 
-  // 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;
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
   }
   }
-  stream->nBuffers = buffers;
 
 
-  // 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;
+  // 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.
+  long inputLatency, outputLatency;
+  if ( mode == INPUT && stream_.mode == OUTPUT ) {
+    ASIODisposeBuffers();
+    if ( handle->bufferInfos ) free( handle->bufferInfos );
   }
 
   }
 
-  // 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);
+  // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
+  bool buffersAllocated = false;
+  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;
   }
 
-  // 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;
+  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;
   }
   }
-
-  // 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 ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
+    infos->isInput = ASIOTrue;
+    infos->channelNum = i + stream_.channelOffset[1];
+    infos->buffers[0] = infos->buffers[1] = 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);
+  // 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 ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
+    errorText_ = errorStream_.str();
     goto error;
   }
     goto error;
   }
+  buffersAllocated = true;
 
 
-  // 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;
-  }
-
-  // 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;
+  // 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
 
   // 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) {
-      close(fd);
-      sprintf(message, "RtAudio: OSS error allocating user buffer memory (%s).",
-              name);
-      goto error;
-    }
+  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;
   }
 
   }
 
-  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) {
-        close(fd);
-        free(stream->userBuffer);
-        sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).",
-                name);
+      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;
       }
     }
   }
 
         goto error;
       }
     }
   }
 
-  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;
-  }
+  stream_.sampleRate = sampleRate;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  asioCallbackInfo = &stream_.callbackInfo;
+  stream_.callbackInfo.object = (void *) this;
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up an output stream.
+    stream_.mode = DUPLEX;
   else
   else
-    stream->mode = mode;
+    stream_.mode = mode;
+
+  // Determine device latencies
+  result = ASIOGetLatencies( &inputLatency, &outputLatency );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING); // warn but don't fail
+  }
+  else {
+    stream_.latency[0] = outputLatency;
+    stream_.latency[1] = inputLatency;
+  }
+
+  // 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:
 
   return SUCCESS;
 
  error:
-  if (stream->handle[0]) {
-    close(stream->handle[0]);
-    stream->handle[0] = 0;
+  if ( buffersAllocated )
+    ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
+
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
   }
-  error(RtError::WARNING);
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
+
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiAsio :: 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_ = "RtApiAsio::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
-
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    ASIOStop();
   }
   }
+  ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
 
-  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);
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
-
-  if (stream->handle[0])
-    close(stream->handle[0]);
-
-  if (stream->handle[1])
-    close(stream->handle[1]);
-
-  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 RtApiAsio :: startStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAsio::startStream(): the stream is already running!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  stream->state = STREAM_RUNNING;
+  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;
+  }
 
 
-  // No need to do anything else here ... OSS automatically starts
-  // when fed samples.
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
+  asioXRun = false;
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( result == ASE_OK ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiAsio :: stopStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  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);
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      MUTEX_UNLOCK( &stream_.mutex );
+      WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE );  // block until signaled
+      ResetEvent( handle->condition );
+      MUTEX_LOCK( &stream_.mutex );
     }
   }
     }
   }
-  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);
-    }
+
+  ASIOError result = ASIOStop();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
+    errorText_ = errorStream_.str();
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result == ASE_OK ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+void RtApiAsio :: abortStream()
 {
 {
-  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 = 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);
-    }
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  // 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 completed
+  // disposed.  So now, calling abort is the same as calling stop.
+  //AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  //handle->drainCounter = 1;
+  stopStream();
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+bool RtApiAsio :: callbackEvent( long bufferIndex )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  int bytes = 0, channels = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  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];
-  }
-
-  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];
-    }
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::WARNING );
+    return FAILURE;
   }
 
   }
 
-  frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0])));
-  frames -= stream->bufferSize;
-  if (frames < 0) frames = 0;
-
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
-}
-
-void RtAudio :: tickStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
 
 
-  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);
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else
+      stopStream();
+    return SUCCESS;
   }
 
   }
 
-  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;
-
-  int result;
-  char *buffer;
-  int samples;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
-    // 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];
+  // 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;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      samples = stream->bufferSize * stream->nUserChannels[0];
-      format = stream->userFormat;
+    if ( stream_.mode != OUTPUT && asioXRun == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      asioXRun = false;
     }
     }
-
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, samples, format);
-
-    // Write samples to device.
-    result = write(stream->handle[0], buffer, samples * formatBytes(format));
-
-    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);
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
     }
     }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      samples = stream->bufferSize * stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
-    }
-    else {
-      buffer = stream->userBuffer;
-      samples = stream->bufferSize * stream->nUserChannels[1];
-      format = stream->userFormat;
-    }
-
-    // Read samples from device.
-    result = read(stream->handle[1], buffer, samples * formatBytes(format));
-
-    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);
-    }
-
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[1])
-      byteSwapBuffer(buffer, samples, format);
-
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
-  }
+  unsigned int nChannels, bufferBytes, i, j;
+  nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+    bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
-}
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-extern "C" void *callbackHandler(void *ptr)
-{
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
+      }
 
 
-  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;
-}
-
-
-//******************** End of __LINUX_OSS__ *********************//
-
-#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.
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-#include "asio/asiosys.h"
-#include "asio/asio.h"
-#include "asio/asiodrivers.h"
-#include <math.h>
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[0],
+                        stream_.deviceFormat[0] );
 
 
-AsioDrivers drivers;
-ASIOCallbacks asioCallbacks;
-CALLBACK_INFO *asioCallbackInfo;
-ASIODriverInfo driverInfo;
+      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 :: initialize(void)
-{
-  nDevices = drivers.asioGetNumDev();
-  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);
-  }
-
-  // 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);
     }
     }
-  }
-
-  drivers.removeCurrentDriver();
-  driverInfo.asioVersion = 2;
-  // See note in DirectSound implementation about GetDesktopWindow().
-  driverInfo.sysRef = GetForegroundWindow();
-}
-
-int RtAudio :: getDefaultInputDevice(void)
-{
-  return 0;
-}
-
-int RtAudio :: getDefaultOutputDevice(void)
-{
-  return 0;
-}
+    else {
 
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  // 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;
-  }
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.userBuffer[0],
+                        stream_.bufferSize * stream_.nUserChannels[0],
+                        stream_.userFormat );
 
 
-  if ( !drivers.loadDriver( info->name ) ) {
-    sprintf(message, "RtAudio: ASIO error loading driver (%s).", info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+      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 );
+      }
 
 
-  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;
-  }
+    }
 
 
-  // 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;
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
 
   }
 
-  info->maxOutputChannels = outputChannels;
-  if ( outputChannels > 0 ) info->minOutputChannels = 1;
-
-  info->maxInputChannels = inputChannels;
-  if ( inputChannels > 0 ) info->minInputChannels = 1;
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-  // 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;
-  }
+    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
 
 
-  // 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];
-  }
+    if (stream_.doConvertBuffer[1]) {
 
 
-  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;
-  }
+      // 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 );
+      }
 
 
-  // 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;
-  }
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[1],
+                        stream_.deviceFormat[1] );
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
 
-  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 {
+      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 );
+        }
+      }
 
 
-       // 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;
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.userBuffer[1],
+                        stream_.bufferSize * stream_.nUserChannels[1],
+                        stream_.userFormat );
+    }
   }
 
   }
 
-  info->probed = true;
-  drivers.removeCurrentDriver();
-}
+ unlock:
+  // 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();
 
 
-void bufferSwitch(long index, ASIOBool processNow)
-{
-  RtAudio *object = (RtAudio *) asioCallbackInfo->object;
-  try {
-    object->callbackEvent( asioCallbackInfo->streamId, index, (void *)NULL, (void *)NULL );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage());
-    return;
-  }
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  return;
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
 }
 
-void sampleRateChanged(ASIOSampleRate sRate)
+void sampleRateChanged( ASIOSampleRate sRate )
 {
   // The ASIO documentation says that this usually only happens during
   // external sync.  Audio processing is not stopped by the driver,
 {
   // The ASIO documentation says that this usually only happens during
   // external sync.  Audio processing is not stopped by the driver,
@@ -3600,32 +3097,33 @@ void sampleRateChanged(ASIOSampleRate sRate)
   // sample rate status of an AES/EBU or S/PDIF digital input at the
   // audio device.
 
   // sample rate status of an AES/EBU or S/PDIF digital input at the
   // audio device.
 
-  RtAudio *object = (RtAudio *) asioCallbackInfo->object;
+  RtApi *object = (RtApi *) asioCallbackInfo->object;
   try {
   try {
-    object->stopStream( asioCallbackInfo->streamId );
+    object->stopStream();
   }
   }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtAudio: sampleRateChanged() error (%s)!\n\n", exception.getMessage());
+  catch ( RtError &exception ) {
+    std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
     return;
   }
 
     return;
   }
 
-  fprintf(stderr, "\nRtAudio: ASIO driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate);
+  std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
 }
 
 }
 
-long asioMessages(long selector, long value, void* message, double* opt)
+long asioMessages( long selector, long value, void* message, double* opt )
 {
   long ret = 0;
 {
   long ret = 0;
-  switch(selector) {
+
+  switch( selector ) {
   case kAsioSelectorSupported:
   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)
+    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:
       ret = 1L;
     break;
   case kAsioResetRequest:
@@ -3635,7 +3133,7 @@ long asioMessages(long selector, long value, void* message, double* opt)
     // done by completely destruct is. I.e. ASIOStop(),
     // ASIODisposeBuffers(), Destruction Afterwards you initialize the
     // driver again.
     // done by completely destruct is. I.e. ASIOStop(),
     // ASIODisposeBuffers(), Destruction Afterwards you initialize the
     // driver again.
-    fprintf(stderr, "\nRtAudio: ASIO driver reset requested!!!");
+    std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
     ret = 1L;
     break;
   case kAsioResyncRequest:
     ret = 1L;
     break;
   case kAsioResyncRequest:
@@ -3646,7 +3144,8 @@ long asioMessages(long selector, long value, void* message, double* opt)
     // which could lose data because the Mutex was held too long by
     // another thread.  However a driver can issue it in other
     // situations, too.
     // 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!!!");
+    // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
+    asioXRun = true;
     ret = 1L;
     break;
   case kAsioLatenciesChanged:
     ret = 1L;
     break;
   case kAsioLatenciesChanged:
@@ -3654,7 +3153,7 @@ long asioMessages(long selector, long value, void* message, double* opt)
     // latencies changed.  Beware, it this does not mean that the
     // buffer sizes have changed!  You might need to update internal
     // delay data.
     // 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!!!");
+    std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
     ret = 1L;
     break;
   case kAsioEngineVersion:
     ret = 1L;
     break;
   case kAsioEngineVersion:
@@ -3671,7 +3170,7 @@ long asioMessages(long selector, long value, void* message, double* opt)
     ret = 0;
     break;
   case kAsioSupportsTimeCode:
     ret = 0;
     break;
   case kAsioSupportsTimeCode:
-    // Informs the driver wether application is interested in time
+    // 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;
     // code info.  If an application does not need to know about time
     // code, the driver has less work to do.
     ret = 0;
@@ -3680,3329 +3179,4283 @@ long asioMessages(long selector, long value, void* message, double* opt)
   return ret;
 }
 
   return ret;
 }
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
+static const char* getAsioErrorString( ASIOError result )
 {
 {
-  // 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;
-  }
+  struct Messages 
+  {
+    ASIOError value;
+    const char*message;
+  };
+
+  static 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.";
+}
+//******************** End of __WINDOWS_ASIO__ *********************//
+#endif
 
 
-  // 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;
-  }
 
 
-  // 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 defined(__WINDOWS_DS__) // Windows DirectSound API
 
 
-    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;
-    }
-  }
+// Modified by Robin Davies, October 2005
+// - Improvements to DirectX pointer chasing. 
+// - Backdoor RtDsStatistics hook provides DirectX performance information.
+// - 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
 
 
-  // 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;
-  }
+#include <dsound.h>
+#include <assert.h>
+
+#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
 
 
-  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;
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768
 
 
-  // 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;
-  }
+#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
 
 
-  // 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;
-  }
+static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
+{
+  if (laterPointer > earlierPointer)
+    return laterPointer - earlierPointer;
+  else
+    return laterPointer - earlierPointer + bufferSize;
+}
 
 
-  // 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;
-  }
+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;
+}
 
 
-  // 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;
-  }
-  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
-    if ( channelInfo.type == ASIOSTFloat64MSB ) stream->doByteSwap[mode] = true;
-  }
+// 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; }
+};
 
 
-  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;
-  }
+/*
+RtApiDs::RtDsStatistics RtApiDs::statistics;
 
 
-  // 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;
-  }
+// Provides a backdoor hook to monitor for DirectSound read overruns and write underruns.
+RtApiDs::RtDsStatistics RtApiDs::getDsStatistics()
+{
+  RtDsStatistics s = statistics;
 
 
-  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;
-  }
+  // update the calculated fields.
+  if ( s.inputFrameSize != 0 )
+    s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate;
 
 
-  if ( mode == INPUT && stream->mode == OUTPUT && stream->bufferSize != *bufferSize )
-    cout << "possible input/output buffersize discrepancy" << endl;
+  if ( s.outputFrameSize != 0 )
+    s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate;
 
 
-  stream->bufferSize = *bufferSize;
-  stream->nBuffers = 2;
+  return s;
+}
+*/
 
 
-  // ASIO always uses deinterleaved channels.
-  stream->deInterleave[mode] = true;
+// Declarations for utility functions, callbacks, and structures
+// specific to the DirectSound implementation.
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext );
 
 
-  // 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;
-    }
-  }
+static char* getErrorString( int code );
 
 
-  // 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;
-  }
+extern "C" unsigned __stdcall callbackHandler( void *ptr );
 
 
-  for ( i=0; i<stream->nDeviceChannels[0]; i++, infos++ ) {
-    infos->isInput = ASIOFalse;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
-  }
+struct EnumInfo {
+  bool isInput;
+  bool getDefault;
+  bool findIndex;
+  unsigned int counter;
+  unsigned int index;
+  LPGUID id;
+  std::string name;
 
 
-  // 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;
-  }
-
-  // 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;
-
-  // 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;
-  }
-
-  if ( stream->doConvertBuffer[mode] ) {
-
-    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;
-      }
-    }
-
-    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;
-    }
-  }
-
-  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);
-
-  return SUCCESS;
-
- memory_error:
-  ASIODisposeBuffers();
-  drivers.removeCurrentDriver();
-
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
-  stream->callbackInfo.buffers = 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;
-}
-
-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;
-
-    MUTEX_UNLOCK(&stream->mutex);
-  }
-}
-
-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;
-  }
-
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
-
-  if (stream->state == STREAM_RUNNING)
-    ASIOStop();
-
-  ASIODisposeBuffers();
-  //ASIOExit();
-  drivers.removeCurrentDriver();
-
-  DeleteCriticalSection(&stream->mutex);
-
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
-
-  if (stream->userBuffer)
-    free(stream->userBuffer);
-
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
-
-  free(stream);
-  streams.erase(streamId);
-}
-
-void RtAudio :: startStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_RUNNING) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
-  }
-
-  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);
-  }
-  stream->state = STREAM_RUNNING;
-
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
-  }
-
-  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);
-  }
-  stream->state = STREAM_STOPPED;
-
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: abortStream(int streamId)
-{
-  stopStream( streamId );
-}
+  EnumInfo()
+    : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {}
+};
 
 
-// I don't know how this function can be implemented.
-int RtAudio :: streamWillBlock(int streamId)
+RtApiDs :: RtApiDs()
 {
 {
-  sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for ASIO.");
-  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;
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+RtApiDs :: ~RtApiDs()
 {
 {
-  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);
-    return;
-  }
-
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    Sleep(stream->callbackInfo.waitTime);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  stream->callbackInfo.blockTick = true;
-
-  MUTEX_UNLOCK(&stream->mutex);
+  if ( coInitialized_ ) CoUninitialize(); // balanced call.
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
 }
 
-void RtAudio :: callbackEvent(int streamId, int bufferIndex, void *inData, void *outData)
+unsigned int RtApiDs :: getDefaultInputDevice( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  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;
-  }
-
-  MUTEX_LOCK(&stream->mutex);
-
-  // Invoke user callback first, to get fresh output data.
-  if ( info->usingCallback ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    if ( callback(stream->userBuffer, stream->bufferSize, info->userData) )
-      info->stopStream = true;
-  }
-
-  int nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
-  int bufferBytes;
-  ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) info->buffers;
-  if ( stream->mode == OUTPUT || stream->mode == DUPLEX ) {
-
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[0]);
-    if (stream->doConvertBuffer[0]) {
-
-      convertStreamBuffer(stream, OUTPUT);
-      if ( stream->doByteSwap[0] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[0],
-                       stream->deviceFormat[0]);
-
-      // 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 );
-      }
-    }
-    else { // single channel only
-
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
-
-      memcpy(bufferInfos->buffers[bufferIndex], stream->userBuffer, bufferBytes );
-    }
-  }
-
-  if ( stream->mode == INPUT || stream->mode == DUPLEX ) {
-
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[1]);
-    if (stream->doConvertBuffer[1]) {
-
-      // Always interleave ASIO input data.
-      for ( int i=0; i<stream->nDeviceChannels[1]; i++, bufferInfos++ )
-        memcpy(&stream->deviceBuffer[i*bufferBytes], bufferInfos->buffers[bufferIndex], bufferBytes );
-
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
-
-    }
-    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);
-    }
+  // Count output devices.
+  EnumInfo info;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return 0;
   }
 
   }
 
-  if ( !info->usingCallback )
-    info->blockTick = false;
-
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  stream->callbackInfo.callback = (void *) callback;
-  stream->callbackInfo.userData = userData;
-  stream->callbackInfo.usingCallback = true;
-}
-
-//******************** End of __WINDOWS_ASIO__ *********************//
-
-#elif defined(__WINDOWS_DS__) // Windows DirectSound API
-
-#include <dsound.h>
-
-// 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 deviceInfoCallback(LPGUID lpguid,
-                                        LPCSTR lpcstrDescription,
-                                        LPCSTR lpcstrModule,
-                                        LPVOID lpContext);
-
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCSTR lpcstrDescription,
-                                           LPCSTR lpcstrModule,
-                                           LPVOID lpContext);
-
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCSTR lpcstrDescription,
-                                      LPCSTR lpcstrModule,
-                                      LPVOID lpContext);
-
-static char* getErrorString(int code);
-
-struct enum_info {
-  char name[64];
-  LPGUID id;
-  bool isInput;
-  bool isValid;
-};
-
-int RtAudio :: getDefaultInputDevice(void)
-{
-  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);
+  // Now enumerate input devices until we find the id = NULL.
+  info.isInput = true;
+  info.getDefault = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
     return 0;
   }
 
     return 0;
   }
 
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp( devices[i].name, info.name, 64 ) == 0 ) return i;
-
+  if ( info.counter > 0 ) return info.counter - 1;
   return 0;
 }
 
   return 0;
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+unsigned int RtApiDs :: getDefaultOutputDevice( 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);
+  // Enumerate output devices until we find the id = NULL.
+  EnumInfo info;
+  info.getDefault = true;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
     return 0;
   }
 
     return 0;
   }
 
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp(devices[i].name, info.name, 64 ) == 0 ) return i;
-
+  if ( info.counter > 0 ) return info.counter - 1;
   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;
-
   // Count DirectSound devices.
   // 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);
+  EnumInfo info;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
   }
 
   // Count DirectSoundCapture devices.
   }
 
   // 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);
+  info.isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
   }
 
   }
 
-  count = ins + outs;
-  if (count == 0) return;
+  return info.counter;
+}
 
 
-  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;
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
+{
+  // Because DirectSound always enumerates input and output devices
+  // separately (and because we don't attempt to combine devices
+  // internally), none of our "devices" will ever be duplex.
+
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  // Enumerate through devices to find the id (if it exists).  Note
+  // that we have to do the output enumeration first, even if this is
+  // an input device, in order for the device counter to be correct.
+  EnumInfo dsinfo;
+  dsinfo.findIndex = true;
+  dsinfo.index = device;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+  }
+
+  if ( dsinfo.name.empty() ) goto probeInput;
+
+  LPDIRECTSOUND output;
+  DSCAPS outCaps;
+  result = DirectSoundCreate( dsinfo.id, &output, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    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);
+  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( RtError::WARNING );
+    return info;
   }
 
   }
 
-  // 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);
+  // Get output channel information.
+  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
+
+  // 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] );
   }
 
   }
 
-  // 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++;
+  // Get format information.
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
 
 
-  if (nDevices == 0) return;
+  output->Release();
 
 
-  //  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 ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
 
 
-  // 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);
-  }
+  // Copy name and return.
+  info.name = dsinfo.name;
 
 
-  //for (i=0;i<nDevices; i++)
-  //probeDeviceInfo(&devices[i]);
+  info.probed = true;
+  return info;
 
 
-  return;
-}
+ probeInput:
 
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  enum_info dsinfo;
-  strncpy( dsinfo.name, info->name, 64 );
-  dsinfo.isValid = false;
-
-  // 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;
+  dsinfo.isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
   }
 
   }
 
-  // Do capture probe first.
-  if ( dsinfo.isValid == false )
-    goto playback_probe;
+  if ( dsinfo.name.empty() ) return info;
 
 
-  LPDIRECTSOUNDCAPTURE  input;
+  LPDIRECTSOUNDCAPTURE input;
   result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
   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;
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::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 (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::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
+  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 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 );
+    }
+  }
+  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 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.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;
-  }
+  if ( getDefaultInputDevice() == device )
+    info.isDefaultInput = true;
 
 
-  // Now do playback probe.
-  if ( dsinfo.isValid == false )
-    goto check_parameters;
+  // Copy name and return.
+  info.name = dsinfo.name;
+  info.probed = true;
+  return info;
+}
 
 
-  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;
+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;
   }
 
   }
 
-  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;
+  // Enumerate through devices to find the id (if it exists).  Note
+  // that we have to do the output enumeration first, even if this is
+  // an input device, in order for the device counter to be correct.
+  EnumInfo dsinfo;
+  dsinfo.findIndex = true;
+  dsinfo.index = device;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    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);
+  if ( mode == OUTPUT ) {
+    if ( dsinfo.name.empty() ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    else info->nSampleRates = 2;
   }
   }
-  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--;
+  else { // mode == INPUT
+    dsinfo.isInput = true;
+    HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &dsinfo );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!";
+      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;
+    if ( dsinfo.name.empty() ) {
+      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
   // DirectSound buffer, DirectSound can crash.  However, for console
   // applications, no sound was produced when using GetDesktopWindow().
   // 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;
+  HWND hWnd = GetForegroundWindow();
 
   // 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)
+  // 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, 32k, but we will
+  // grow it to make allowances for very large software buffer sizes.
+  DWORD dsBufferSize = 0;
+  DWORD dsPointerLeadTime = 0;
+  long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
 
 
-  enum_info dsinfo;
-  strncpy( dsinfo.name, devices[device].name, 64 );
-  dsinfo.isValid = false;
+  void *ohandle = 0, *bhandle = 0;
   if ( mode == OUTPUT ) {
 
   if ( mode == OUTPUT ) {
 
-    if ( devices[device].maxOutputChannels < channels )
+    LPDIRECTSOUND output;
+    result = DirectSoundCreate( dsinfo.id, &output, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
       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);
+    DSCAPS outCaps;
+    outCaps.dwSize = sizeof( outCaps );
+    result = output->GetCaps( &outCaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.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);
+    // Check channel information.
+    if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
+      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback.";
+      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);
-      return FAILURE;
+    // 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 > (DWORD) bufferBytes )
+      bufferBytes *= 2;
 
     // Set cooperative level to DSSCL_EXCLUSIVE
 
     // 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);
+    result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.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 (" << dsinfo.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 (" << dsinfo.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);
+    dsBufferSize = (DWORD) bufferBytes;
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
                                   DSBCAPS_GETCURRENTPOSITION2 |
                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
                                   DSBCAPS_GETCURRENTPOSITION2 |
                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
-    bufferDescription.dwBufferBytes = buffer_size;
+    bufferDescription.dwBufferBytes = bufferBytes;
     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 |
                                     DSBCAPS_GETCURRENTPOSITION2 |
                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing
       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
                                     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 (" << dsinfo.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 (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    bufferBytes = 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, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsinfo.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 (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    stream->handle[0].object = (void *) object;
-    stream->handle[0].buffer = (void *) buffer;
-    stream->nDeviceChannels[0] = channels;
+    dsBufferSize = bufferBytes;
+    ohandle = (void *) output;
+    bhandle = (void *) buffer;
   }
 
   if ( mode == INPUT ) {
 
   }
 
   if ( mode == INPUT ) {
 
-    if ( devices[device].maxInputChannels < channels )
-      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);
+    LPDIRECTSOUNDCAPTURE input;
+    result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message, "RtAudio: DS input device (%s) id not found!", devices[device].name);
-      error(RtError::DEBUG_WARNING);
+    DSCCAPS inCaps;
+    inCaps.dwSize = sizeof( inCaps );
+    result = input->GetCaps( &inCaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUNDCAPTURE  object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer;
-    DSCBUFFERDESC bufferDescription;
-
-    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);
+    // Check channel information.
+    if ( inCaps.dwChannels < channels + firstChannel ) {
+      errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
       return FAILURE;
     }
 
       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;
-
+    // 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;
+
+    // Setup the secondary DS buffer description.
+    dsBufferSize = bufferBytes;
+    DSCBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
+    bufferDescription.dwFlags = 0;
+    bufferDescription.dwReserved = 0;
+    bufferDescription.dwBufferBytes = bufferBytes;
+    bufferDescription.lpwfxFormat = &waveFormat;
+
     // Create the capture buffer.
     // 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);
+    LPDIRECTSOUNDCAPTUREBUFFER buffer;
+    result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Lock the capture buffer
       return FAILURE;
     }
 
     // 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);
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Zero the buffer
       return FAILURE;
     }
 
     // Zero the buffer
-    ZeroMemory(audioPtr, dataLen);
+    ZeroMemory( audioPtr, dataLen );
 
     // Unlock the buffer
 
     // 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);
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
 
       return FAILURE;
     }
 
-    stream->handle[1].object = (void *) object;
-    stream->handle[1].buffer = (void *) buffer;
-    stream->nDeviceChannels[1] = channels;
+    dsBufferSize = bufferBytes;
+    ohandle = (void *) input;
+    bhandle = (void *) buffer;
   }
 
   }
 
-  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 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 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;
+  // 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
 
   // 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;
+  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] ) {
+  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 <= (long) 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_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
+  // 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.
     // We had already set up an output stream.
-    stream->mode = DUPLEX;
+    stream_.mode = DUPLEX;
   else
   else
-    stream->mode = mode;
-  stream->nBuffers = nBuffers;
-  stream->sampleRate = sampleRate;
+    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.
+  unsigned threadId;
+  stream_.callbackInfo.object = (void *) this;
+  stream_.callbackInfo.isRunning = true;
+  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;
+  }
 
   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;
+ 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();
     }
     }
-    object->Release();
-    stream->handle[0].object = NULL;
+    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;
   }
   }
-  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;
+
+  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);
+
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: cancelStreamCallback(int streamId)
+void RtApiDs :: closeStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->callbackInfo.usingCallback) {
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  // Stop the callback thread.
+  stream_.callbackInfo.isRunning = false;
+  WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
+  CloseHandle( (HANDLE) stream_.callbackInfo.thread );
 
 
-    MUTEX_LOCK(&stream->mutex);
+  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;
+  }
 
 
-    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 RtApiDs :: 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);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiDs::startStream(): the stream is already running!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  // Increase scheduler frequency on lesser windows (a side-effect of
+  // increasing timer accuracy).  On greater windows (Win2K or later),
+  // this is already in effect.
+
+  MUTEX_LOCK( &stream_.mutex );
+  
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+
+  timeBeginPeriod( 1 ); 
+
+  /*
+  memset( &statistics, 0, sizeof( statistics ) );
+  statistics.sampleRate = stream_.sampleRate;
+  statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0];
+  */
+
+  buffersRolling = false;
+  duplexPrerollBytes = 0;
 
 
-  if (stream->callbackInfo.usingCallback) {
-    stream->callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream->callbackInfo.thread );
+  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] );
   }
 
   }
 
-  DeleteCriticalSection(&stream->mutex);
+  HRESULT result = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0];
 
 
-  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();
+    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;
     }
     }
-    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();
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1];
+
+    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;
     }
     }
-    object->Release();
   }
 
   }
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  free(stream);
-  streams.erase(streamId);
+  if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiDs :: stopStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+  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 = 1;
+      MUTEX_UNLOCK( &stream_.mutex );
+      WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE );  // block until signaled
+      ResetEvent( handle->condition );
+      MUTEX_LOCK( &stream_.mutex );
+    }
+
+    // Stop the buffer and clear memory
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
 
 
-  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);
+    // 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::abortStream: 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::abortStream: 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) 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 ) {
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    audioPtr = NULL;
+    dataLen = 0;
+
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: 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::abortStream: 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::abortStream: 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;
   }
   }
-  stream->state = STREAM_RUNNING;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+  if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
+}
+
+void RtApiDs :: abortStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
+
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
+
+  stopStream();
 }
 
 }
 
-void RtAudio :: stopStream(int streamId)
+void RtApiDs :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.state == STREAM_STOPPED ) {
+    Sleep(50); // sleep 50 milliseconds
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
+  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 ) {
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else
+      stopStream();
     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.
+  MUTEX_LOCK( &stream_.mutex );
+
+  // 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;
+    }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return;
+    }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
+  }
+
   HRESULT result;
   HRESULT result;
-  DWORD dsBufferSize;
+  DWORD currentWritePos, safeWritePos;
+  DWORD currentReadPos, safeReadPos;
+  DWORD leadPos;
+  UINT nextWritePos;
+
+#ifdef GENERATE_DEBUG_LOG
+  DWORD writeTime, readTime;
+#endif
+
   LPVOID buffer1 = NULL;
   LPVOID buffer2 = NULL;
   DWORD bufferSize1 = 0;
   DWORD bufferSize2 = 0;
   LPVOID buffer1 = NULL;
   LPVOID buffer2 = NULL;
   DWORD bufferSize1 = 0;
   DWORD bufferSize2 = 0;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
 
 
-    DWORD currentPos, safePos;
-    long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
-    buffer_bytes *= formatBytes(stream->deviceFormat[0]);
+  char *buffer;
+  long bufferBytes;
+
+  if ( stream_.mode == DUPLEX && !buffersRolling ) {
+    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 initialWritePos, initialSafeWritePos;
+    DWORD initialReadPos, initialSafeReadPos;
+
+    result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
+    }
+    result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
+    }
+    while ( true ) {
+      result = dsWriteBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
+      }
+      result = dsCaptureBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
+      }
+      if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break;
+      Sleep( 1 );
+    }
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    dsBufferSize = buffer_bytes * stream->nBuffers;
+    assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
 
 
-    // Write zeroes for nBuffer counts.
-    for (int i=0; i<stream->nBuffers; i++) {
+    buffersRolling = true;
+    handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] );
+    handle->bufferPointer[1] = safeReadPos;
+  }
 
 
-      // 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 ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    
+    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
 
 
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      DWORD endWrite = nextWritePos + buffer_bytes;
+    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 );
+    }
 
 
-      // 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 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 );
+    }
 
 
-        // 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
-      }
+    // No byte swapping necessary in DirectSound implementation.
 
 
-      // 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);
-      }
+    // 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 );
 
 
-      // Zero the free space
-      ZeroMemory(buffer1, bufferSize1);
-      if (buffer2 != NULL) ZeroMemory(buffer2, bufferSize2);
+    DWORD dsBufferSize = handle->dsBufferSize[0];
+    nextWritePos = handle->bufferPointer[0];
 
 
-      // 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);
+    DWORD endWrite;
+    while ( true ) {
+      // Find out where the read and "safe write" pointers are.
+      result = dsBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
       }
       }
-      nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
-      stream->handle[0].bufferPointer = nextWritePos;
-    }
 
 
-    // If we play again, start at the beginning of the buffer.
-    stream->handle[0].bufferPointer = 0;
-  }
+      leadPos = safeWritePos + handle->dsPointerLeadTime[0];
+      if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
+      if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
+      endWrite = nextWritePos + bufferBytes;
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    buffer1 = NULL;
-    bufferSize1 = 0;
+      // Check whether the entire write region is behind the play pointer.
+      if ( leadPos >= endWrite ) break;
 
 
-    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 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.
+      double millis = ( endWrite - leadPos ) * 900.0;
+      millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
+      if ( millis < 1.0 ) millis = 1.0;
+      if ( millis > 50.0 ) {
+        static int nOverruns = 0;
+        ++nOverruns;
+      }
+      Sleep( (DWORD) millis );
     }
 
     }
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+    //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) {
+    //  statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] );
+    //}
 
 
-    // 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);
+    if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize )
+         || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) { 
+      // We've strayed into the forbidden zone ... resync the read pointer.
+      //++statistics.numberOfWriteUnderruns;
+      handle->xrun[0] = true;
+      nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize;
+      while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize;
+      handle->bufferPointer[0] = nextWritePos;
+      endWrite = nextWritePos + bufferBytes;
     }
 
     }
 
-    // Zero the DS buffer
-    ZeroMemory(buffer1, bufferSize1);
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextWritePos, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
+    }
 
 
-    // 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);
+    // 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();
+      error( RtError::SYSTEM_ERROR );
     }
     }
+    nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    handle->bufferPointer[0] = nextWritePos;
 
 
-    // If we start recording again, we must begin at beginning of buffer.
-    stream->handle[1].bufferPointer = 0;
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    // 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 );
+    }
 
 
-  MUTEX_LOCK(&stream->mutex);
+    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    long nextReadPos = handle->bufferPointer[1];
+    DWORD dsBufferSize = handle->dsBufferSize[1];
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    // Find out where the write and "safe read" pointers are.
+    result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
+    }
+
+    if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+    DWORD endRead = nextReadPos + 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 ( safeReadPos < endRead ) {
+        if ( duplexPrerollBytes <= 0 ) {
+          // Pre-roll time over. Be more agressive.
+          int adjustment = endRead-safeReadPos;
+
+          handle->xrun[1] = true;
+          //++statistics.numberOfReadOverruns;
+          // 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 )
+            nextReadPos = safeReadPos-2*bufferBytes;
+          else
+            nextReadPos = safeReadPos-bufferBytes-adjustment;
 
 
-  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);
+          //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos;
+          //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
+          if ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+
+        }
+        else {
+          // In pre=roll time. Just do it.
+          nextReadPos = safeReadPos-bufferBytes;
+          while ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+        }
+        endRead = nextReadPos + bufferBytes;
+      }
+    }
+    else { // mode == INPUT
+      while ( safeReadPos < endRead ) {
+        // See comments for playback.
+        double millis = (endRead - safeReadPos) * 900.0;
+        millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
+        if ( millis < 1.0 ) millis = 1.0;
+        Sleep( (DWORD) millis );
+
+        // Wake up, find out where we are now
+        result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          error( RtError::SYSTEM_ERROR );
+        }
+      
+        if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+      }
     }
 
     }
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0];
-    dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
+    //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) )
+    //  statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize );
 
 
-    // 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);
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
     }
 
     }
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+    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;
+    }
 
 
-    // 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);
+    // Update our buffer offset and unlock sound buffer
+    nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
     }
     }
+    handle->bufferPointer[1] = nextReadPos;
 
 
-    // If we start playing again, we must begin at beginning of buffer.
-    stream->handle[0].bufferPointer = 0;
+    // 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] );
+  }
+#ifdef GENERATE_DEBUG_LOG
+  if ( currentDebugLogEntry < debugLog.size() )
+  {
+    TTickRecord &r = debugLog[currentDebugLogEntry++];
+    r.currentReadPointer = currentReadPos;
+    r.safeReadPointer = safeReadPos;
+    r.currentWritePointer = currentWritePos;
+    r.safeWritePointer = safeWritePos;
+    r.readTime = readTime;
+    r.writeTime = writeTime;
+    r.nextReadPointer = handles[1].bufferPointer;
+    r.nextWritePointer = handles[0].bufferPointer;
   }
   }
+#endif
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    audioPtr = NULL;
-    dataLen = 0;
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-    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);
-    }
+  RtApi::tickStreamTime();
+}
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+// Definitions for utility functions and callbacks
+// specific to the DirectSound implementation.
 
 
-    // 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);
-    }
+extern "C" unsigned __stdcall callbackHandler( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiDs *object = (RtApiDs *) info->object;
+  bool* isRunning = &info->isRunning;
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+  while ( *isRunning == true ) {
+    object->callbackEvent();
+  }
 
 
-    // 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);
+  _endthreadex( 0 );
+  return 0;
+}
+
+#include "tchar.h"
+
+std::string convertTChar( LPCTSTR name )
+{
+  std::string s;
+
+#if defined( UNICODE ) || defined( _UNICODE )
+  // Yes, this conversion doesn't make sense for two-byte characters
+  // but RtAudio is currently written to return an std::string of
+  // one-byte chars for the device name.
+  for ( unsigned int i=0; i<wcslen( name ); i++ )
+    s.push_back( name[i] );
+#else
+  s.append( std::string( name ) );
+#endif
+
+  return s;
+}
+
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext )
+{
+  EnumInfo *info = (EnumInfo *) lpContext;
+
+  HRESULT hr;
+  if ( info->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 )
+        info->counter++;
+    }
+    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 )
+        info->counter++;
+    }
+    object->Release();
+  }
+
+  if ( info->getDefault && lpguid == NULL ) return FALSE;
+
+  if ( info->findIndex && info->counter > info->index ) {
+    info->id = lpguid;
+    info->name = convertTChar( description );
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+static 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];
+
+  AlsaHandle()
+    :synchronized(false) { xrun[0] = false; xrun[1] = false; }
+};
+
+extern "C" 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( RtError::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( RtError::WARNING );
+        break;
+      }
+                       if ( subdevice < 0 )
+        break;
+      nDevices++;
+    }
+  nextcard:
+    snd_ctl_close( handle );
+    snd_card_next( &card );
+  }
+
+  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;
+  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( RtError::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( RtError::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 );
+  }
+
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
+    error( RtError::INVALID_USE );
+  }
+
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
+  }
+
+ foundDevice:
+
+  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
+  stream = SND_PCM_STREAM_PLAYBACK;
+  snd_pcm_info_set_device( pcminfo, subdevice );
+  snd_pcm_info_set_subdevice( pcminfo, 0 );
+  snd_pcm_info_set_stream( pcminfo, stream );
+
+  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( RtError::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( RtError::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( RtError::WARNING );
+    goto captureProbe;
+  }
+  info.outputChannels = value;
+  snd_pcm_close( phandle );
+
+ captureProbe:
+  // Now try for capture
+  stream = SND_PCM_STREAM_CAPTURE;
+  snd_pcm_info_set_stream( pcminfo, stream );
+
+  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;
+  }
+
+  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( RtError::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( RtError::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( RtError::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( RtError::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( RtError::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.sampleRates.size() == 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::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 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    error( RtError::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 );
+  info.name = name;
+
+  // That's all ... close the device and return
+  snd_pcm_close( phandle );
+  info.probed = true;
+  return info;
+}
+
+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;
+
+  // 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 );
+        goto foundDevice;
+      }
+      nDevices++;
+    }
+    snd_ctl_close( chandle );
+    snd_card_next( &card );
+  }
+
+  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:
+
+  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.
+  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 number, which in ALSA is referred to as the "period".
+  int dir;
+  unsigned int periods = 0;
+  if ( options ) periods = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
+  // Even though the hardware might allow 1 buffer, it won't work reliably.
+  if ( periods < 2 ) periods = 2;
+  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;
+  }
+
+  // Set the buffer (or period) size.
+  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;
+
+  // 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 ) {
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  stream_.bufferSize = *bufferSize;
+
+  // 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;
+  }
+
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
+
+  // 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, 0x7fffffff );
+  snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
+  snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX );
+  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;
+  }
+
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
+  snd_pcm_sw_params_dump( sw_params, out );
+#endif
+
+  // 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;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
+      goto error;
+    }
+    stream_.apiHandle = (void *) apiInfo;
+    apiInfo->handles[0] = 0;
+    apiInfo->handles[1] = 0;
+  }
+  else {
+    apiInfo = (AlsaHandle *) stream_.apiHandle;
+  }
+  apiInfo->handles[mode] = phandle;
+
+  // 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] ) {
+
+    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 ( makeBuffer ) {
+      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_.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.
+    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( RtError::WARNING );
+    }
+  }
+  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)
+    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#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;
+
+ error:
+  if ( apiInfo ) {
+    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;
+  }
+
+  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;
+  }
+
+  return FAILURE;
+}
+
+void RtApiAlsa :: closeStream()
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
+    return;
+  }
+
+  stream_.callbackInfo.isRunning = false;
+  pthread_join( stream_.callbackInfo.thread, NULL );
+
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  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] );
+  }
+
+  if ( apiInfo ) {
+    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;
+  }
+
+  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 RtApiAlsa :: startStream()
+{
+  // 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( RtError::WARNING );
+    return;
+  }
+
+  MUTEX_LOCK( &stream_.mutex );
+
+  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;
+      }
     }
     }
+  }
 
 
-    // If we start recording again, we must begin at beginning of buffer.
-    stream->handle[1].bufferPointer = 0;
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    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;
+      }
+    }
   }
   }
-  stream->state = STREAM_STOPPED;
+
+  stream_.state = STREAM_RUNNING;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result >= 0 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiAlsa :: stopStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  int channels;
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  HRESULT result;
-  DWORD currentPos, safePos;
-  channels = 1;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-    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;
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // 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 = 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;
     }
     }
+  }
 
 
-    if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    frames = currentPos - nextWritePos;
-    frames /= channels * formatBytes(stream->deviceFormat[0]);
+  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->mode == INPUT || stream->mode == DUPLEX) {
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-    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 ( result >= 0 ) return;
+  error( RtError::SYSTEM_ERROR );
+}
 
 
-    // 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);
-    }
+void RtApiAlsa :: abortStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    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;
-    }
-    else {
-      frames = safePos - nextReadPos;
-      frames /= channels * formatBytes(stream->deviceFormat[1]);
+  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;
     }
   }
 
     }
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
+  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;
+    }
+  }
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  stream_.state = STREAM_STOPPED;
+  if ( result >= 0 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiAlsa :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.state == STREAM_STOPPED ) {
+    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
+    return;
+  }
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::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);
+
+  int doStopStream = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  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 );
 
 
-  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) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
-  }
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
-  HRESULT result;
-  DWORD currentPos, safePos;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
+  int result;
   char *buffer;
   char *buffer;
-  long buffer_bytes;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  int channels;
+  snd_pcm_t **handle;
+  snd_pcm_sframes_t frames;
+  RtAudioFormat format;
+  handle = (snd_pcm_t **) apiInfo->handles;
 
 
-    // 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]);
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+
+    // Setup parameters.
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      channels = stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
     }
     else {
     }
     else {
-      buffer = stream->userBuffer;
-      buffer_bytes = stream->bufferSize * stream->nUserChannels[0];
-      buffer_bytes *= formatBytes(stream->userFormat);
+      buffer = stream_.userBuffer[1];
+      channels = stream_.nUserChannels[1];
+      format = stream_.userFormat;
     }
 
     }
 
-    // No byte swapping necessary in DirectSound implementation.
-
-    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);
+    // 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 ( 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 );
-
-      // 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 ( result < (int) stream_.bufferSize ) {
+      // Either an error or underrun 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();
+        }
+      }
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
       }
       }
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
+      error( RtError::WARNING );
+      goto unlock;
     }
 
     }
 
-    // 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);
-    }
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[1] )
+      byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
 
 
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer1, buffer, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2);
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
 
-    // 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;
+    // 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) {
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1];
-      buffer_bytes *= formatBytes(stream->deviceFormat[1]);
+    // 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 {
     }
     else {
-      buffer = stream->userBuffer;
-      buffer_bytes = stream->bufferSize * stream->nUserChannels[1];
-      buffer_bytes *= formatBytes(stream->userFormat);
+      buffer = stream_.userBuffer[0];
+      channels = stream_.nUserChannels[0];
+      format = stream_.userFormat;
     }
 
     }
 
-    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    UINT nextReadPos = stream->handle[1].bufferPointer;
-    DWORD dsBufferSize = buffer_bytes * stream->nBuffers;
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
 
 
-    // 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);
+    // 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 );
     }
 
     }
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
-    DWORD endRead = nextReadPos + buffer_bytes;
-
-    // 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 );
-
-      // 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);
+    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 {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
+        }
       }
       }
-      
-      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);
-    }
-
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer, buffer1, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);
-
-    // 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);
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+      }
+      error( RtError::WARNING );
+      goto unlock;
     }
     }
-    stream->handle[1].bufferPointer = nextReadPos;
-
-    // No byte swapping necessary in DirectSound implementation.
 
 
-    // Do buffer conversion if necessary.
-    if (stream->doConvertBuffer[1])
-      convertStreamBuffer(stream, INPUT);
+    // Check stream latency
+    result = snd_pcm_delay( handle[0], &frames );
+    if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
   }
 
   }
 
-  MUTEX_UNLOCK(&stream->mutex);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
+  RtApi::tickStreamTime();
+  if ( doStopStream == 1 ) this->stopStream();
+  else if ( doStopStream == 2 ) this->abortStream();
 }
 
 }
 
-// Definitions for utility functions and callbacks
-// specific to the DirectSound implementation.
-
-extern "C" unsigned __stdcall callbackHandler(void *ptr)
+extern "C" void *alsaCallbackHandler( 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;
+  RtApiAlsa *object = (RtApiAlsa *) info->object;
+  bool *isRunning = &info->isRunning;
+
+#ifdef SCHED_RR
+  // Set a higher scheduler priority (P.J. Leonard)
+  struct sched_param param;
+  int min = sched_get_priority_min( SCHED_RR );
+  int max = sched_get_priority_max( SCHED_RR );
+  param.sched_priority = min + ( max - min ) / 2;   // Is this the best number?
+  sched_setscheduler( 0, SCHED_RR, &param );
+#endif
 
 
-  while ( *usingCallback ) {
-    try {
-      object->tickStream(stream);
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessage());
-      break;
-    }
+  while ( *isRunning == true ) {
+    pthread_testcancel();
+    object->callbackEvent();
   }
 
   }
 
-  _endthreadex( 0 );
-  return 0;
+  pthread_exit( NULL );
 }
 
 }
 
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+//******************** End of __LINUX_ALSA__ *********************//
+#endif
 
 
-  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;
-  }
 
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-  info->streamId = streamId;
+#if defined(__LINUX_OSS__)
 
 
-  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);
-  }
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "oss/soundcard.h"
+#include <errno.h>
+#include <math.h>
 
 
-  // When spawning multiple threads in quick succession, it appears to be
-  // necessary to wait a bit for each to initialize ... another windoism!
-  Sleep(1);
-}
+extern "C" void *ossCallbackHandler(void * ptr);
 
 
-static bool CALLBACK deviceCountCallback(LPGUID lpguid,
-                                         LPCSTR lpcstrDescription,
-                                         LPCSTR lpcstrModule,
-                                         LPVOID lpContext)
-{
-  int *pointer = ((int *) lpContext);
-  (*pointer)++;
+// A structure to hold various information related to the OSS API
+// implementation.
+struct OssHandle {
+  int id[2];    // device ids
+  bool xrun[2];
+  bool triggered;
 
 
-  return true;
-}
+  OssHandle()
+    :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
 
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCSTR lpcstrDescription,
-                                        LPCSTR lpcstrModule,
-                                        LPVOID lpContext)
+RtApiOss :: RtApiOss()
 {
 {
-  enum_info *info = ((enum_info *) lpContext);
-  while (strlen(info->name) > 0) info++;
-
-  strncpy(info->name, lpcstrDescription, 64);
-  info->id = lpguid;
-
-       HRESULT    hr;
-  info->isValid = false;
-  if (info->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)
-        info->isValid = 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 )
-        info->isValid = true;
-    }
-    object->Release();
-  }
-
-  return true;
+  // 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( RtError::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( RtError::WARNING );
+    return 0;
   }
 
   }
 
-  return true;
+  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";
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-  case DSERR_INVALIDPARAM:
-    return "Direct Sound invalid parameter";
-
-  case DSERR_INVALIDCALL:
-    return "Direct Sound invalid call";
-
-  case DSERR_GENERIC:
-    return "Direct Sound generic error";
-
-  case DSERR_PRIOLEVELNEEDED:
-    return "Direct Sound Priority level needed";
-
-  case DSERR_OUTOFMEMORY:
-    return "Direct Sound out of memory";
-
-  case DSERR_BADFORMAT:
-    return "Direct Sound bad format";
-
-  case DSERR_UNSUPPORTED:
-    return "Direct Sound unsupported error";
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
+    error( RtError::WARNING );
+    return info;
+  }
 
 
-  case DSERR_NODRIVER:
-    return "Direct Sound no driver error";
+  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( RtError::WARNING );
+    return info;
+  }
 
 
-  case DSERR_ALREADYINITIALIZED:
-    return "Direct Sound already initialized";
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
+    error( RtError::INVALID_USE );
+  }
 
 
-  case DSERR_NOAGGREGATION:
-    return "Direct Sound no aggregation";
+  if ( device >= nDevices ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
+  }
 
 
-  case DSERR_BUFFERLOST:
-    return "Direct Sound buffer lost";
+  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( RtError::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;
+  if ( mask & AFMT_FLOAT )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
+    info.nativeFormats |= RTAUDIO_SINT24;
 
 
-  case DSERR_OTHERAPPHASPRIO:
-    return "Direct Sound other app has priority";
+  // 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( RtError::WARNING );
+    return info;
+  }
+
+  // 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] );
+          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_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( RtError::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;
-
-  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);
+  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;
   }
 
   }
 
-  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;
+  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;
   }
 
   }
 
-  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);
+  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;
   }
 
   }
 
-  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);
-    }
-    strncpy(devices[i].name, name, 32);
-    devices[i].id[1] = vls[i].i;
+  // 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;
   }
 
   }
 
-  delete [] vls;
-
-  return;
-}
-
-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;
+  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;
+    }
+    else
+      flags |= O_RDONLY;
   }
 
   }
 
-  return 0;
-}
+  // Set exclusive access if specified.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
 
 
-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);
+  // 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;
   }
   }
-  else {
-    for ( int i=0; i<nDevices; i++ )
-      if ( devices[i].id[0] == value.i ) return i;
+
+  // 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;
+    }
   }
   }
+  */
 
 
-  return 0;
-}
+  // 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;
+  }
 
 
-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) {
-
-    // 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;
-    }
-
-    // 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;
-    }
+  // Verify the sample rate setup worked.
+  if ( abs( srate - 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 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;
-    }
+  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;
   }
   }
-  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;
-    }
+  // Set interleaving parameters.
+  stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] =  true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
+    stream_.userInterleaved = false;
 
 
-    // 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;
+  // 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;
     }
     }
-
-    // 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;
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
+      goto error;
     }
     }
-  }
-
-  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];
+    stream_.apiHandle = (void *) handle;
+  }
+  else {
+    handle = (OssHandle *) stream_.apiHandle;
+  }
+  handle->id[mode] = fd;
 
 
-    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)
+    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+#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 ) {
+    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( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  stream_.callbackInfo.isRunning = false;
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  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 ) {
+    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( RtError::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 );
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  stream_.state = STREAM_RUNNING;
 
 
-  int result;
-  int buffer_size = stream->bufferSize * stream->nBuffers;
+  // No need to do anything else here ... OSS automatically starts
+  // when fed samples.
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX)
-    alZeroFrames(stream->handle[0], buffer_size);
+  MUTEX_UNLOCK( &stream_.mutex );
+}
 
 
-  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( RtError::WARNING );
+    return;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  MUTEX_LOCK(&stream->mutex);
+    // Flush the output with zeros a few times.
+    char *buffer;
+    int samples;
+    RtAudioFormat format;
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    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( RtError::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);
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  stream_.state = STREAM_STOPPED;
+  if ( result != -1 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiOss :: abortStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  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;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  stream_.state = STREAM_STOPPED;
+  if ( result != -1 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiOss :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  if ( stream_.state == STREAM_STOPPED ) {
+    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
+    return;
+  }
 
 
-  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( RtError::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;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  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 );
 
 
-  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( RtError::WARNING );
+      goto unlock;
+    }
   }
 
   }
 
-  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( RtError::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();
+  else if ( doStopStream == 2 ) this->abortStream();
 }
 
 }
 
-extern "C" void *callbackHandler(void *ptr)
+extern "C" 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;
+
+#ifdef SCHED_RR
+  // Set a higher scheduler priority (P.J. Leonard)
+  struct sched_param param;
+  param.sched_priority = 39;   // Is this the best number?
+  sched_setscheduler( 0, SCHED_RR, &param );
+#endif
 
 
-  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( RtError::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);
-  }
+  if ( type == RtError::WARNING && showWarnings_ == true )
+    std::cerr << '\n' << errorText_ << "\n\n";
+  else
+    throw( RtError( errorText_, type ) );
+  errorStream_.str(""); // clear the ostringstream
 }
 
 }
 
-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( RtError::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;
+  for ( int i=0; i<2; i++ ) {
+    stream_.device[i] = 0;
+    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_SINT24 || 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_SINT8 )
     return 1;
 
     return 1;
 
-  sprintf(message,"RtAudio: undefined format in formatBytes().");
-  error(RtError::WARNING);
+  errorText_ = "RtApi::formatBytes: undefined format.";
+  error( RtError::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 {
+      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 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 { // 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;
+      }
     }
   }
     }
   }
-  else {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k;
-      offset_out[k] = k;
+
+  // 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 );
+      }
     }
   }
     }
   }
+}
+
+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 upper three bytes of a 32-bit integer.
 
 
-  if (format_out == RTAUDIO_FLOAT64) {
-    FLOAT64 scale;
-    FLOAT64 *out = (FLOAT64 *)output;
+  // 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 ) );
 
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+  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 / 128.0;
       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;
+      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]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
       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;
+      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]] *= 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) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = 1.0 / 8388608.0;
+      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]] & 0x00ffffff);
+          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;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       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;
+      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]] *= 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;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       scale = 1.0 / 128.0;
       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;
+      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]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
       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;
+      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]] *= 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) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = 1.0 / 8388608.0;
+      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]] & 0x00ffffff);
+          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;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       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;
+      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]] *= 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) {
+      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]];
+          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.0);
         }
         }
-        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.0);
         }
         }
-        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) {
+    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]] <<= 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]];
+          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]];
+      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_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]];
+          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]] * 8388608.0);
         }
         }
-        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.0);
         }
         }
-        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) {
+      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]] >> 8) & 0x0000ffff);
         }
         }
-        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.0);
         }
         }
-        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.0);
         }
         }
-        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) {
+      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]] >> 16) & 0x000000ff);
         }
         }
-        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.0);
         }
         }
-        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.0);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
 }
 
       }
     }
   }
 }
 
-void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
+void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
 {
   register char val;
   register char *ptr;
 
   ptr = buffer;
 {
   register char val;
   register char *ptr;
 
   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);
@@ -7012,10 +7465,10 @@ 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_SINT24 ||
+            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);
@@ -7031,8 +7484,8 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       ptr += 4;
     }
   }
       ptr += 4;
     }
   }
-  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);
@@ -7062,24 +7515,12 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
   }
 }
 
   }
 }
 
-
-// *************************************************** //
+// Indentation settings for Vim and Emacs
 //
 //
-// RtError class definition.
+// Local Variables:
+// c-basic-offset: 2
+// indent-tabs-mode: nil
+// End:
 //
 //
-// *************************************************** //
+// vim: et sts=2 sw=2
 
 
-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);
-}