Many updates for release 4.0.7 (gs).
[rtaudio-cdist.git] / RtAudio.cpp
index 4a51baf75632e8d52038676d3214d0487c54e630..78259e947b62bd10437a6490551bb8ea41de8073 100644 (file)
@@ -1,16 +1,16 @@
 /************************************************************************/
 /*! \class RtAudio
 /************************************************************************/
 /*! \class RtAudio
-    \brief Realtime audio i/o C++ class.
+    \brief Realtime audio i/o C++ classes.
 
     RtAudio provides a common API (Application Programming Interface)
 
     RtAudio provides a common API (Application Programming Interface)
-    for realtime audio input/output across Linux (native ALSA and
-    OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound
-    and ASIO) operating systems.
+    for realtime audio input/output across Linux (native ALSA, Jack,
+    and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
+    (DirectSound 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-2010 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 4.0.7
 
 #include "RtAudio.h"
 
 #include "RtAudio.h"
-#include <vector>
-#include <stdio.h>
-#include <iostream.h>
+#include <iostream>
+#include <cstdlib>
+#include <cstring>
+#include <climits>
 
 // 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);
+  verifyStream();
 
 
-  if (stream->callbackInfo.usingCallback) {
+  long totalLatency = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+    totalLatency = stream_.latency[0];
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
+    totalLatency += stream_.latency[1];
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  return totalLatency;
+}
 
 
-    MUTEX_LOCK(&stream->mutex);
+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
+}
 
 
-    stream->callbackInfo.usingCallback = false;
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-    stream->callbackInfo.thread = 0;
-    stream->callbackInfo.callback = NULL;
-    stream->callbackInfo.userData = NULL;
+unsigned int RtApi :: getStreamSampleRate( void )
+{
+ verifyStream();
 
 
-    MUTEX_UNLOCK(&stream->mutex);
-  }
+ return stream_.sampleRate;
 }
 
 }
 
-#endif
 
 // *************************************************** //
 //
 
 // *************************************************** //
 //
@@ -362,3236 +382,2972 @@ 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
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+  AudioDeviceIOProcID procId[2];
+#endif
+  UInt32 iStream[2];      // device stream index (or first if using multiple)
+  UInt32 nStreams[2];     // number of streams to use
+  bool xrun[2];
+  char *deviceBuffer;
+  pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+
+  CoreHandle()
+    :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
 
-void RtAudio :: initialize(void)
+RtApiCore :: RtApiCore()
 {
 {
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  AudioDeviceID        *deviceList = NULL;
-  nDevices = 0;
-
-  // Find out how many audio devices there are, if any.
-  err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &dataSize, NULL);
-  if (err != noErr) {
-    sprintf(message, "RtAudio: OSX error getting device info!");
-    error(RtError::SYSTEM_ERROR);
-  }
-
-  nDevices = dataSize / sizeof(AudioDeviceID);
-  if (nDevices == 0) return;
-
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
-
-  // Make space for the devices we are about to get.
-  deviceList = (AudioDeviceID  *) malloc( dataSize );
-  if (deviceList == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
+  // 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;
+  AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
+    error( 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 );
-
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default input device." );
-    error(RtError::WARNING);
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
+    error( 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 ];
+  property.mSelector = kAudioHardwarePropertyDevices;
+  result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
+    error( 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 );
-
-  if (result != noErr) {
-    sprintf( message, "RtAudio: OSX error getting default output device." );
-    error(RtError::WARNING);
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
+    error( RtError::WARNING );
     return 0;
   }
 
     return 0;
   }
 
-  for ( int i=0; i<nDevices; i++ ) {
-    if ( id == devices[i].id[0] ) return i;
+  dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioDeviceID deviceList[ nDevices ];
+  property.mSelector = kAudioHardwarePropertyDevices;
+  result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
+    error( RtError::WARNING );
+    return 0;
   }
 
   }
 
-  return 0;
-}
-
-static bool deviceSupportsFormat( AudioDeviceID id, bool isInput,
-                                  AudioStreamBasicDescription  *desc, bool isDuplex )
-{
-  OSStatus result = noErr;
-  UInt32 dataSize = sizeof( AudioStreamBasicDescription );
-
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreamFormatSupported,
-                                   &dataSize, desc );
-
-  if (result == kAudioHardwareNoError) {
-    if ( isDuplex ) {
-      result = AudioDeviceGetProperty( id, 0, true,
-                                       kAudioDevicePropertyStreamFormatSupported,
-                                       &dataSize, desc );
-
-
-      if (result != kAudioHardwareNoError)
-        return false;
-    }
-    return true;
-  }
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
 
 
-  return false;
+  errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
+  error( RtError::WARNING );
+  return 0;
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 {
 {
-  OSStatus err = noErr;
-
-  // Get the device manufacturer and name.
-  char name[256];
-  char fullname[512];
-  UInt32 dataSize = 256;
-  err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                kAudioDevicePropertyDeviceManufacturer,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting device manufacturer." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncpy(fullname, name, 256);
-  strcat(fullname, ": " );
-
-  dataSize = 256;
-  err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                kAudioDevicePropertyDeviceName,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error getting device name." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncat(fullname, name, 254);
-  strncat(info->name, fullname, 128);
-
-  // Get output channel information.
-  unsigned int i, minChannels, maxChannels, nStreams = 0;
+  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 ( device >= nDevices ) {
+    errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
+  }
+
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
+                                                0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
+    error( RtError::WARNING );
+    return info;
+  }
+
+  AudioDeviceID id = deviceList[ device ];
+
+  // Get the device name.
+  info.name.erase();
+  CFStringRef cfname;
+  dataSize = sizeof( CFStringRef );
+  property.mSelector = kAudioObjectPropertyManufacturer;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
+  }
+
+  const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
+  info.name.append( (const char *)mname, strlen(mname) );
+  info.name.append( ": " );
+  CFRelease( cfname );
+
+  property.mSelector = kAudioObjectPropertyName;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
+  }
+
+  const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
+  info.name.append( (const char *)name, strlen(name) );
+  CFRelease( cfname );
+
+  // Get the output stream "configuration".
   AudioBufferList      *bufferList = nil;
   AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( info->id[0], 0, false,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message, "RtAudio: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return;
-    }
-
-    err = AudioDeviceGetProperty( info->id[0], 0, false,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-    if (err == noErr) {
-      maxChannels = 0;
-      minChannels = 1000;
-      nStreams = bufferList->mNumberBuffers;
-      for ( i=0; i<nStreams; i++ ) {
-        maxChannels += bufferList->mBuffers[i].mNumberChannels;
-        if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
-          minChannels = bufferList->mBuffers[i].mNumberChannels;
-      }
-    }
-  }
-  if (err != noErr || dataSize <= 0) {
-    sprintf( message, "RtAudio: OSX error getting output channels for device (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
+  property.mSelector = kAudioDevicePropertyStreamConfiguration;
+  property.mScope = kAudioDevicePropertyScopeOutput;
+  //  property.mElement = kAudioObjectPropertyElementWildcard;
+  dataSize = 0;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( 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 = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if ( result != noErr || dataSize == 0 ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( 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".
+  property.mScope = kAudioDevicePropertyScopeInput;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( 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 = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if (result != noErr || dataSize == 0) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( 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;
-  }
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  // Probe the device sample rate and data format parameters.  The
-  // core audio query mechanism is performed on a "stream"
-  // description, which can have a variable number of channels and
-  // apply to input or output only.
-
-  // Create a stream description structure.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  memset(&description, 0, sizeof(AudioStreamBasicDescription));
+  // Probe the device sample rates.
   bool isInput = false;
   bool isInput = false;
-  if ( info->maxOutputChannels == 0 ) isInput = true;
-  bool isDuplex = false;
-  if ( info->maxDuplexChannels > 0 ) isDuplex = true;
+  if ( info.outputChannels == 0 ) isInput = true;
 
   // Determine the supported sample rates.
 
   // Determine the supported sample rates.
-  info->nSampleRates = 0;
-  for (i=0; i<MAX_SAMPLE_RATES; i++) {
-    description.mSampleRate = (double) SAMPLE_RATES[i];
-    if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
-      info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i];
-  }
-
-  if (info->nSampleRates == 0) {
-    sprintf( message, "RtAudio: No supported sample rates found for OSX device (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Check for continuous sample rate support.
-  description.mSampleRate = kAudioStreamAnyRate;
-  if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) {
-    info->sampleRates[1] = info->sampleRates[info->nSampleRates-1];
-    info->nSampleRates = -1;
+  property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
+  if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != kAudioHardwareNoError || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
+    errorText_ = errorStream_.str();
+    error( 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;
-  }
-
-  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;
+  UInt32 nRanges = dataSize / sizeof( AudioValueRange );
+  AudioValueRange rangeList[ nRanges ];
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );
+  if ( result != kAudioHardwareNoError ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
+    errorText_ = errorStream_.str();
+    error( 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 ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
 
 
-  info->probed = true;
+  info.probed = true;
+  return info;
 }
 
 }
 
-OSStatus callbackHandler(AudioDeviceID inDevice,
-                         const AudioTimeStamp* inNow,
-                         const AudioBufferList* inInputData,
-                         const AudioTimeStamp* inInputTime,
-                         AudioBufferList* outOutputData,
-                         const AudioTimeStamp* inOutputTime, 
-                         void* infoPointer)
+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;
-  }
-
-  return kAudioHardwareNoError;
+  else
+    return kAudioHardwareNoError;
 }
 
 }
 
-/*
-OSStatus deviceListener(AudioDeviceID inDevice,
-                        UInt32 channel,
-                        Boolean isInput,
-                        AudioDevicePropertyID propertyID,
-                        void* infoPointer)
+OSStatus deviceListener( AudioObjectID inDevice,
+                         UInt32 nAddresses,
+                         const AudioObjectPropertyAddress properties[],
+                         void* handlePointer )
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
-
-  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;
+  CoreHandle *handle = (CoreHandle *) handlePointer;
+  for ( UInt32 i=0; i<nAddresses; i++ ) {
+    if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {
+      if ( properties[i].mScope == kAudioDevicePropertyScopeInput )
+        handle->xrun[1] = true;
+      else
+        handle->xrun[0] = true;
+    }
   }
 
   return kAudioHardwareNoError;
 }
   }
 
   return kAudioHardwareNoError;
 }
-*/
 
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
+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 )
 {
 {
-  // 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;
-    }
+  // 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;
+  }
+
+  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;
+  }
+
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
+                                          kAudioObjectPropertyScopeGlobal,
+                                          kAudioObjectPropertyElementMaster };
+  OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
+                                                0, NULL, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
+    return FAILURE;
   }
 
   }
 
+  AudioDeviceID id = deviceList[ device ];
+
   // Setup for stream mode.
   bool isInput = false;
   // Setup for stream mode.
   bool isInput = false;
-  AudioDeviceID id = devices[device].id[0];
-  if ( mode == INPUT ) isInput = true;
+  if ( mode == INPUT ) {
+    isInput = true;
+    property.mScope = kAudioDevicePropertyScopeInput;
+  }
+  else
+    property.mScope = kAudioDevicePropertyScopeOutput;
 
 
-  // Search for a stream which contains the desired number of channels.
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  unsigned int deviceChannels, nStreams;
-  UInt32 iChannel = 0, iStream = 0;
+  // Get the stream "configuration".
   AudioBufferList      *bufferList = nil;
   AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( id, 0, isInput,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message, "RtAudio: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-    err = AudioDeviceGetProperty( id, 0, isInput,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
+  dataSize = 0;
+  property.mSelector = kAudioDevicePropertyStreamConfiguration;
+  result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
+  if ( result != noErr || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    if (err == noErr) {
-      stream->deInterleave[mode] = false;
-      nStreams = bufferList->mNumberBuffers;
-      for ( iStream=0; iStream<nStreams; iStream++ ) {
-        if ( bufferList->mBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break;
-        iChannel += bufferList->mBuffers[iStream].mNumberChannels;
-      }
-      // If we didn't find a single stream above, see if we can meet
-      // the channel specification in mono mode (i.e. using separate
-      // non-interleaved buffers).  This can only work if there are N
-      // consecutive one-channel streams, where N is the number of
-      // desired channels.
-      iChannel = 0;
-      if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) {
-        int counter = 0;
-        for ( iStream=0; iStream<nStreams; iStream++ ) {
-          if ( bufferList->mBuffers[iStream].mNumberChannels == 1 )
-            counter++;
-          else
-            counter = 0;
-          if ( counter == channels ) {
-            iStream -= channels - 1;
-            iChannel -= channels - 1;
-            stream->deInterleave[mode] = true;
-            break;
-          }
-          iChannel += bufferList->mBuffers[iStream].mNumberChannels;
-        }
-      }
-    }
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
+    return FAILURE;
   }
   }
-  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);
+
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  if (iStream >= nStreams) {
-    free (bufferList);
-    sprintf( message, "RtAudio: unable to find OSX audio stream on device (%s) for requested channels (%d).",
-             devices[device].name, channels );
-    error(RtError::DEBUG_WARNING);
+  // Search for one or more streams that contain the desired number of
+  // channels. CoreAudio devices can have an arbitrary number of
+  // streams and each stream can have an arbitrary number of channels.
+  // For each stream, a single buffer of interleaved samples is
+  // provided.  RtAudio prefers the use of one stream of interleaved
+  // data or multiple consecutive single-channel streams.  However, we
+  // now support multiple consecutive multi-channel streams of
+  // interleaved data as well.
+  UInt32 iStream, offsetCounter = firstChannel;
+  UInt32 nStreams = bufferList->mNumberBuffers;
+  bool monoMode = false;
+  bool foundStream = false;
+
+  // First check that the device supports the requested number of
+  // channels.
+  UInt32 deviceChannels = 0;
+  for ( iStream=0; iStream<nStreams; iStream++ )
+    deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;
+
+  if ( deviceChannels < ( channels + firstChannel ) ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // This is ok even for mono mode ... it gets updated later.
-  deviceChannels = bufferList->mBuffers[iStream].mNumberChannels;
-  free (bufferList);
+  // Look for a single stream meeting our needs.
+  UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
+  for ( iStream=0; iStream<nStreams; iStream++ ) {
+    streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+    if ( streamChannels >= channels + offsetCounter ) {
+      firstStream = iStream;
+      channelOffset = offsetCounter;
+      foundStream = true;
+      break;
+    }
+    if ( streamChannels > offsetCounter ) break;
+    offsetCounter -= streamChannels;
+  }
+
+  // If we didn't find a single stream above, then we should be able
+  // to meet the channel specification with multiple streams.
+  if ( foundStream == false ) {
+    monoMode = true;
+    offsetCounter = firstChannel;
+    for ( iStream=0; iStream<nStreams; iStream++ ) {
+      streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+      if ( streamChannels > offsetCounter ) break;
+      offsetCounter -= streamChannels;
+    }
+
+    firstStream = iStream;
+    channelOffset = offsetCounter;
+    Int32 channelCounter = channels + offsetCounter - streamChannels;
+
+    if ( streamChannels > 1 ) monoMode = false;
+    while ( channelCounter > 0 ) {
+      streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
+      if ( streamChannels > 1 ) monoMode = false;
+      channelCounter -= streamChannels;
+      streamCount++;
+    }
+  }
+
+  free( bufferList );
 
   // Determine the buffer size.
   AudioValueRange      bufferRange;
 
   // 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 );
+  property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32);
-  if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum;
-  else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum;
-
-  // Set the buffer size.  For mono mode, I'm assuming we only need to
-  // make this setting for the first channel.
-  UInt32 theSize = (UInt32) bufferBytes;
-  dataSize = sizeof( UInt32);
-  err = AudioDeviceSetProperty(id, NULL, 0, isInput,
-                               kAudioDevicePropertyBufferSize,
-                               dataSize, &theSize);
-  if (err != noErr) {
-    sprintf( message, "RtAudio: OSX error setting the buffer size for device (%s).",
-             devices[device].name );
-    error(RtError::DEBUG_WARNING);
+  if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+  else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+
+  // Set the buffer size.  For multiple streams, I'm assuming we only
+  // need to make this setting for the master channel.
+  UInt32 theSize = (UInt32) *bufferSize;
+  dataSize = sizeof( UInt32 );
+  property.mSelector = kAudioDevicePropertyBufferFrameSize;
+  result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
   // If attempting to setup a duplex stream, the bufferSize parameter
   // MUST be the same in both directions!
     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;
 
 
-  // Set the stream format description.  Do for each channel in mono mode.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  if ( stream->deInterleave[mode] ) nStreams = channels;
-  else nStreams = 1;
-  for ( unsigned int i=0; i<nStreams; i++, iChannel++ ) {
-
-    err = AudioDeviceGetProperty( id, iChannel, isInput,
-                                  kAudioDevicePropertyStreamFormat,
-                                  &dataSize, &description );
-    if (err != noErr) {
-      sprintf( message, "RtAudio: OSX error getting stream format for device (%s).", devices[device].name );
-      error(RtError::DEBUG_WARNING);
+  // Check and if necessary, change the sample rate for the device.
+  Float64 nominalRate;
+  dataSize = sizeof( Float64 );
+  property.mSelector = kAudioDevicePropertyNominalSampleRate;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Only change the sample rate if off by more than 1 Hz.
+  if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {
+    nominalRate = (Float64) sampleRate;
+    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );
+
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate 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);
+  // Try to set "hog" mode ... it's not clear to me this is working.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {
+    pid_t hog_pid;
+    dataSize = sizeof( hog_pid );
+    property.mSelector = kAudioDevicePropertyHogMode;
+    result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
       return FAILURE;
     }
+
+    if ( hog_pid != getpid() ) {
+      hog_pid = getpid();
+      result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
   }
 
   }
 
-  // Check 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);
+  // Get the stream ID(s) so we can set the stream format.
+  AudioStreamID streamIDs[ nStreams ];
+  dataSize = nStreams * sizeof( AudioStreamID );
+  property.mSelector = kAudioDevicePropertyStreams;
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &streamIDs );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  stream->doByteSwap[mode] = false;
-  if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian )
-    stream->doByteSwap[mode] = true;
+  // Now set the stream format for each stream.  Also, check the
+  // physical format of the device and change that if necessary.
+  AudioStreamBasicDescription  description;
+  dataSize = sizeof( AudioStreamBasicDescription );
 
 
-  // From the CoreAudio documentation, PCM data must be supplied as
-  // 32-bit floats.
-  stream->userFormat = format;
-  stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
+  bool updateFormat;
+  for ( UInt32 i=0; i<streamCount; i++ ) {
 
 
-  if ( stream->deInterleave[mode] )
-    stream->nDeviceChannels[mode] = channels;
-  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;
+    property.mSelector = kAudioStreamPropertyVirtualFormat;
+    result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, &dataSize, &description );
 
 
-  // Allocate necessary internal buffers.
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
+    // 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;
+    }
 
 
-    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 ( description.mFormatID != kAudioFormatLinearPCM ) {
+      description.mFormatID = kAudioFormatLinearPCM;
+      updateFormat = true;
+    }
 
 
-  if ( stream->deInterleave[mode] ) {
+    if ( updateFormat ) {
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &description );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
 
 
-    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;
+    // Now check the physical format.
+    property.mSelector = kAudioStreamPropertyPhysicalFormat;
+    result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL,  &dataSize, &description );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    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;
+      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 32;
+      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 24;
+      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 16;
+      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 8;
+      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
+      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
       }
     }
       }
     }
+  }
 
 
-    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;
+
+  // 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;
+  dataSize = sizeof( UInt32 );
+  property.mSelector = kAudioDevicePropertyLatency;
+  if ( AudioObjectHasProperty( id, &property ) == true ) {
+    result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );
+    if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
+    else {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      error( RtError::WARNING );
+    }
+  }
+
+  // Now try to get the stream latency.  For multiple streams, I assume the
+  // latency is equal for each.
+  result = AudioObjectGetPropertyData( streamIDs[firstStream], &property, 0, NULL, &dataSize, &latency );
+  if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;
   else {
   else {
-    err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream->callbackInfo );
-    if (err != noErr) {
-      sprintf( message, "RtAudio: OSX error setting callback for device (%s).", devices[device].name );
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+  }
+
+  // Byte-swapping: According to AudioHardware.h, the stream data will
+  // always be presented in native-endian format, so we should never
+  // need to byte swap.
+  stream_.doByteSwap[mode] = false;
+
+  // From the CoreAudio documentation, PCM data must be supplied as
+  // 32-bit floats.
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+
+  if ( streamCount == 1 )
+    stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
+  else // multiple streams
+    stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = channelOffset;  // offset within a CoreAudio stream
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] = true;
+  if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;
+
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( streamCount == 1 ) {
+    if ( stream_.nUserChannels[mode] > 1 &&
+         stream_.userInterleaved != stream_.deviceInterleaved[mode] )
+      stream_.doConvertBuffer[mode] = true;
+  }
+  else if ( monoMode && stream_.userInterleaved )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate our CoreHandle structure for the stream.
+  CoreHandle *handle = 0;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new CoreHandle;
     }
     }
-    if ( stream->mode == OUTPUT && mode == INPUT )
-      stream->mode = DUPLEX;
-    else
-      stream->mode = mode;
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
+      goto error;
+    }
+
+    if ( pthread_cond_init( &handle->condition, NULL ) ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
+    stream_.apiHandle = (void *) handle;
+  }
+  else
+    handle = (CoreHandle *) stream_.apiHandle;
+  handle->iStream[mode] = firstStream;
+  handle->nStreams[mode] = streamCount;
+  handle->id[mode] = id;
+
+  // 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_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  // If we wanted to use property listeners, they would be setup here.
+  // If possible, we will make use of the CoreAudio stream buffers as
+  // "device buffers".  However, we can't do this if using multiple
+  // streams.
+  if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
 
 
-  return SUCCESS;
+    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;
+      }
+    }
 
 
- memory_error:
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+    if ( makeBuffer ) {
+      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;
+      }
+    }
   }
   }
-  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);
+  stream_.sampleRate = sampleRate;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if ( streamCount > 1 ) setConvertInfo( mode, 0 );
+    else setConvertInfo( mode, channelOffset );
+  }
 
 
-  if (stream->callbackInfo.usingCallback) {
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
+    // Only one callback procedure per device.
+    stream_.mode = DUPLEX;
+  else {
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
+#else
+    // deprecated in favor of AudioDeviceCreateIOProcID()
+    result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
+#endif
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      goto error;
+    }
+    if ( stream_.mode == OUTPUT && mode == INPUT )
+      stream_.mode = DUPLEX;
+    else
+      stream_.mode = mode;
+  }
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  // Setup the device property listener for over/underload.
+  property.mSelector = kAudioDeviceProcessorOverload;
+  result = AudioObjectAddPropertyListener( id, &property, deviceListener, (void *) handle );
 
 
-    MUTEX_LOCK(&stream->mutex);
+  return SUCCESS;
+
+ 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 );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
+#else
+    // deprecated in favor of AudioDeviceDestroyIOProcID()
+    AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+#endif
   }
 
   }
 
-  if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
-    id = devices[stream->device[1]].id[0];
-    if (stream->state == STREAM_RUNNING)
-      AudioDeviceStop( id, callbackHandler );
-    AudioDeviceRemoveIOProc( id, callbackHandler );
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[1], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+    AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
+#else
+    // deprecated in favor of AudioDeviceDestroyIOProcID()
+    AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+#endif
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  if ( stream->deInterleave[0] || stream->deInterleave[1] )
-    free(stream->callbackInfo.buffers);
+  // Destroy pthread condition variable.
+  pthread_cond_destroy( &handle->condition );
+  delete handle;
+  stream_.apiHandle = 0;
 
 
-  free(stream);
-  streams.erase(streamId);
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiCore :: startStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  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;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-  OSStatus err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  OSStatus result = noErr;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    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);
+    MUTEX_UNLOCK( &stream_.mutex );
+    result = AudioDeviceStop( handle->id[0], callbackHandler );
+    MUTEX_LOCK( &stream_.mutex );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
 
 
-    err = AudioDeviceStop(devices[stream->device[1]].id[0], callbackHandler);
-    if (err != noErr) {
-      sprintf(message, "RtAudio: OSX error stopping input callback procedure on device (%s).",
-              devices[stream->device[0]].name);
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStop( handle->id[1], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
     }
   }
 
-  stream->state = STREAM_STOPPED;
+  stream_.state = STREAM_STOPPED;
 
  unlock:
 
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  stopStream( streamId );
+  if ( result == noErr ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-// I don't know how this function can be implemented.
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiCore :: abortStream( void )
 {
 {
-  sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for OS X.");
-  error(RtError::WARNING);
-  return 0;
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
+
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
+
+  stopStream();
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
+                                 const AudioBufferList *inBufferList,
+                                 const AudioBufferList *outBufferList )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  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;
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
-    return;
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
 
 
-  if (stream->callbackInfo.usingCallback) {
-    sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
-    return;
+  // 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;
   }
 
   }
 
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    usleep(stream->callbackInfo.waitTime);
-
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  stream->callbackInfo.blockTick = true;
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return SUCCESS;
+  }
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  AudioDeviceID outputDevice = handle->id[0];
 
 
-void RtAudio :: callbackEvent( int streamId, DEVICE_ID deviceId, void *inData, void *outData )
-{
-  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;
+  // 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;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
 
 
-  if ( stream->mode == INPUT || ( stream->mode == DUPLEX && deviceId == info->device[1] ) ) {
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-    if (stream->doConvertBuffer[1]) {
+      if ( handle->nStreams[0] == 1 ) {
+        memset( outBufferList->mBuffers[handle->iStream[0]].mData,
+                0,
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else { // fill multiple streams with zeros
+        for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
+          memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  0,
+                  outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
+        }
+      }
+    }
+    else if ( handle->nStreams[0] == 1 ) {
+      if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer
+        convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,
+                       stream_.userBuffer[0], stream_.convertInfo[0] );
+      }
+      else { // copy from user buffer
+        memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
+                stream_.userBuffer[0],
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+    }
+    else { // fill multiple streams
+      Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];
+      if ( stream_.doConvertBuffer[0] ) {
+        convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+        inBuffer = (Float32 *) stream_.deviceBuffer;
+      }
 
 
-      if ( stream->deInterleave[1] ) {
-        stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
-        int bufferBytes = inBufferList->mBuffers[stream->handle[1]].mDataByteSize;
-        for ( int i=0; i<stream->nDeviceChannels[1]; i++ ) {
-          memcpy(&stream->deviceBuffer[i*bufferBytes],
-                 inBufferList->mBuffers[stream->handle[1]+i].mData, bufferBytes );
+      if ( stream_.deviceInterleaved[0] == false ) { // mono mode
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
         }
       }
         }
       }
-      else
-        stream->deviceBuffer = (char *) inBufferList->mBuffers[stream->handle[1]].mData;
+      else { // fill multiple multi-channel streams with interleaved data
+        UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;
+        Float32 *out, *in;
+
+        bool inInterleaved = ( stream_.userInterleaved ) ? true : false;
+        UInt32 inChannels = stream_.nUserChannels[0];
+        if ( stream_.doConvertBuffer[0] ) {
+          inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+          inChannels = stream_.nDeviceChannels[0];
+        }
 
 
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
+        if ( inInterleaved ) inOffset = 1;
+        else inOffset = stream_.bufferSize;
+
+        channelsLeft = inChannels;
+        for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
+          in = inBuffer;
+          out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;
+          streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
+
+          outJump = 0;
+          // Account for possible channel offset in first stream
+          if ( i == 0 && stream_.channelOffset[0] > 0 ) {
+            streamChannels -= stream_.channelOffset[0];
+            outJump = stream_.channelOffset[0];
+            out += outJump;
+          }
 
 
-    }
-    else {
-      memcpy(stream->userBuffer,
-             inBufferList->mBuffers[stream->handle[1]].mData,
-             inBufferList->mBuffers[stream->handle[1]].mDataByteSize );
+          // Account for possible unfilled channels at end of the last stream
+          if ( streamChannels > channelsLeft ) {
+            outJump = streamChannels - channelsLeft;
+            streamChannels = channelsLeft;
+          }
+
+          // Determine input buffer offsets and skips
+          if ( inInterleaved ) {
+            inJump = inChannels;
+            in += inChannels - channelsLeft;
+          }
+          else {
+            inJump = 1;
+            in += (inChannels - channelsLeft) * inOffset;
+          }
 
 
-      if (stream->doByteSwap[1])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[1],
-                       stream->userFormat);
+          for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+            for ( unsigned int j=0; j<streamChannels; j++ ) {
+              *out++ = in[j*inOffset];
+            }
+            out += outJump;
+            in += inJump;
+          }
+          channelsLeft -= streamChannels;
+        }
+      }
     }
     }
-  }
 
 
-  // Don't invoke the user callback if duplex mode, the input/output
-  // devices are different, and this function is called for the output
-  // device.
-  if ( info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    info->stopStream = callback(stream->userBuffer, stream->bufferSize, info->userData);
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
 
   }
 
-  if ( stream->mode == OUTPUT || ( stream->mode == DUPLEX && deviceId == info->device[0] ) ) {
+  AudioDeviceID inputDevice;
+  inputDevice = handle->id[1];
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
 
 
-    if (stream->doConvertBuffer[0]) {
+    if ( handle->nStreams[1] == 1 ) {
+      if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer
+        convertBuffer( stream_.userBuffer[1],
+                       (char *) inBufferList->mBuffers[handle->iStream[1]].mData,
+                       stream_.convertInfo[1] );
+      }
+      else { // copy to user buffer
+        memcpy( stream_.userBuffer[1],
+                inBufferList->mBuffers[handle->iStream[1]].mData,
+                inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+      }
+    }
+    else { // read from multiple streams
+      Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];
+      if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;
 
 
-      if ( !stream->deInterleave[0] )
-        stream->deviceBuffer = (char *) outBufferList->mBuffers[stream->handle[0]].mData;
-      else
-        stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
-
-      convertStreamBuffer(stream, OUTPUT);
-      if ( stream->doByteSwap[0] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[0],
-                       stream->deviceFormat[0]);
-
-      if ( stream->deInterleave[0] ) {
-        int bufferBytes = outBufferList->mBuffers[stream->handle[0]].mDataByteSize;
-        for ( int i=0; i<stream->nDeviceChannels[0]; i++ ) {
-          memcpy(outBufferList->mBuffers[stream->handle[0]+i].mData,
-                 &stream->deviceBuffer[i*bufferBytes], bufferBytes );
+      if ( stream_.deviceInterleaved[1] == false ) { // mono mode
+        UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+          memcpy( (void *)&outBuffer[i*stream_.bufferSize],
+                  inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
         }
       }
         }
       }
+      else { // read from multiple multi-channel streams
+        UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;
+        Float32 *out, *in;
+
+        bool outInterleaved = ( stream_.userInterleaved ) ? true : false;
+        UInt32 outChannels = stream_.nUserChannels[1];
+        if ( stream_.doConvertBuffer[1] ) {
+          outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
+          outChannels = stream_.nDeviceChannels[1];
+        }
 
 
-    }
-    else {
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
+        if ( outInterleaved ) outOffset = 1;
+        else outOffset = stream_.bufferSize;
+
+        channelsLeft = outChannels;
+        for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {
+          out = outBuffer;
+          in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;
+          streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
+
+          inJump = 0;
+          // Account for possible channel offset in first stream
+          if ( i == 0 && stream_.channelOffset[1] > 0 ) {
+            streamChannels -= stream_.channelOffset[1];
+            inJump = stream_.channelOffset[1];
+            in += inJump;
+          }
+
+          // Account for possible unread channels at end of the last stream
+          if ( streamChannels > channelsLeft ) {
+            inJump = streamChannels - channelsLeft;
+            streamChannels = channelsLeft;
+          }
+
+          // Determine output buffer offsets and skips
+          if ( outInterleaved ) {
+            outJump = outChannels;
+            out += outChannels - channelsLeft;
+          }
+          else {
+            outJump = 1;
+            out += (outChannels - channelsLeft) * outOffset;
+          }
 
 
-      memcpy(outBufferList->mBuffers[stream->handle[0]].mData,
-             stream->userBuffer,
-             outBufferList->mBuffers[stream->handle[0]].mDataByteSize );
+          for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
+            for ( unsigned int j=0; j<streamChannels; j++ ) {
+              out[j*outOffset] = *in++;
+            }
+            out += outJump;
+            in += inJump;
+          }
+          channelsLeft -= streamChannels;
+        }
+      }
+      
+      if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer
+        convertBuffer( stream_.userBuffer[1],
+                       stream_.deviceBuffer,
+                       stream_.convertInfo[1] );
+      }
     }
   }
 
     }
   }
 
-  if ( !info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) )
-    info->blockTick = false;
-
-  MUTEX_UNLOCK(&stream->mutex);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
 }
 
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
+const char* RtApiCore :: getErrorCode( OSStatus code )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  switch( code ) {
 
 
-  stream->callbackInfo.callback = (void *) callback;
-  stream->callbackInfo.userData = userData;
-  stream->callbackInfo.usingCallback = true;
-}
+  case kAudioHardwareNotRunningError:
+    return "kAudioHardwareNotRunningError";
 
 
-//******************** End of __MACOSX_CORE__ *********************//
+  case kAudioHardwareUnspecifiedError:
+    return "kAudioHardwareUnspecifiedError";
 
 
-#elif defined(__LINUX_ALSA__)
+  case kAudioHardwareUnknownPropertyError:
+    return "kAudioHardwareUnknownPropertyError";
 
 
-#define MAX_DEVICES 16
+  case kAudioHardwareBadPropertySizeError:
+    return "kAudioHardwareBadPropertySizeError";
 
 
-void RtAudio :: initialize(void)
-{
-  int card, result, device;
-  char name[32];
-  const char *cardId;
-  char deviceNames[MAX_DEVICES][32];
-  snd_ctl_t *handle;
-  snd_ctl_card_info_t *info;
-  snd_ctl_card_info_alloca(&info);
+  case kAudioHardwareIllegalOperationError:
+    return "kAudioHardwareIllegalOperationError";
 
 
-  // Count cards and devices
-  nDevices = 0;
-  card = -1;
-  snd_card_next(&card);
-  while ( card >= 0 ) {
-    sprintf(name, "hw:%d", card);
-    result = snd_ctl_open(&handle, name, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: ALSA control open (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    result = snd_ctl_card_info(handle, info);
-               if (result < 0) {
-      sprintf(message, "RtAudio: ALSA control hardware info (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    cardId = snd_ctl_card_info_get_id(info);
-               device = -1;
-               while (1) {
-      result = snd_ctl_pcm_next_device(handle, &device);
-                       if (result < 0) {
-        sprintf(message, "RtAudio: ALSA control next device (%i): %s.", card, snd_strerror(result));
-        error(RtError::DEBUG_WARNING);
-        break;
-      }
-                       if (device < 0)
-        break;
-      if ( strlen(cardId) )
-        sprintf( deviceNames[nDevices++], "hw:%s,%d", cardId, device );
-      else
-        sprintf( deviceNames[nDevices++], "hw:%d,%d", card, device );
-      if ( nDevices > MAX_DEVICES ) break;
-    }
-    if ( nDevices > MAX_DEVICES ) break;
-  next_card:
-    snd_ctl_close(handle);
-    snd_card_next(&card);
-  }
+  case kAudioHardwareBadObjectError:
+    return "kAudioHardwareBadObjectError";
 
 
-  if (nDevices == 0) return;
+  case kAudioHardwareBadDeviceError:
+    return "kAudioHardwareBadDeviceError";
 
 
-  //  Allocate the RTAUDIO_DEVICE structures.
-  devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
-  if (devices == NULL) {
-    sprintf(message, "RtAudio: memory allocation error!");
-    error(RtError::MEMORY_ERROR);
-  }
+  case kAudioHardwareBadStreamError:
+    return "kAudioHardwareBadStreamError";
+
+  case kAudioHardwareUnsupportedOperationError:
+    return "kAudioHardwareUnsupportedOperationError";
+
+  case kAudioDeviceUnsupportedFormatError:
+    return "kAudioDeviceUnsupportedFormatError";
 
 
-  // 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 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>
+#include <cstdio>
+
+// A structure to hold various information related to the Jack API
+// implementation.
+struct JackHandle {
+  jack_client_t *client;
+  jack_port_t **ports[2];
+  std::string deviceName[2];
+  bool xrun[2];
+  pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+
+  JackHandle()
+    :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
+
+ThreadHandle threadId;
+void jackSilentError( const char * ) {};
+
+RtApiJack :: RtApiJack()
 {
 {
-  // No ALSA API functions for default devices.
-  return 0;
+  // Nothing to do here.
+#if !defined(__RTAUDIO_DEBUG__)
+  // Turn off Jack's internal error reporting.
+  jack_set_error_function( &jackSilentError );
+#endif
 }
 
 }
 
-int RtAudio :: getDefaultOutputDevice(void)
+RtApiJack :: ~RtApiJack()
 {
 {
-  // No ALSA API functions for default devices.
-  return 0;
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+unsigned int RtApiJack :: getDeviceCount( void )
 {
 {
-  int err;
-  int open_mode = SND_PCM_ASYNC;
-  snd_pcm_t *handle;
-  snd_ctl_t *chandle;
-  snd_pcm_stream_t stream;
-       snd_pcm_info_t *pcminfo;
-       snd_pcm_info_alloca(&pcminfo);
-  snd_pcm_hw_params_t *params;
-  snd_pcm_hw_params_alloca(&params);
-  char name[32];
-  char *card;
-
-  // Open the control interface for this card.
-  strncpy( name, info->name, 32 );
-  card = strtok(name, ",");
-  err = snd_ctl_open(&chandle, card, 0);
-  if (err < 0) {
-    sprintf(message, "RtAudio: ALSA control open (%s): %s.", card, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return;
+  // See if we can become a jack client.
+  jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
+  jack_status_t *status = NULL;
+  jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
+  if ( client == 0 ) return 0;
+
+  const char **ports;
+  std::string port, previousPort;
+  unsigned int nChannels = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nChannels ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon + 1 );
+        if ( port != previousPort ) {
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nChannels] );
+    free( ports );
   }
   }
-  unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") );
 
 
-  // First try for playback
-  stream = SND_PCM_STREAM_PLAYBACK;
-  snd_pcm_info_set_device(pcminfo, dev);
-  snd_pcm_info_set_subdevice(pcminfo, 0);
-  snd_pcm_info_set_stream(pcminfo, stream);
+  jack_client_close( client );
+  return nDevices;
+}
 
 
-  if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) {
-    if (err == -ENOENT) {
-      sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle output!", info->name);
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) output: %s",
-              info->name, snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-    }
-    goto capture_probe;
+RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  jack_options_t options = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption
+  jack_status_t *status = NULL;
+  jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
+  if ( client == 0 ) {
+    errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
+    error( 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 (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) info.name = port;
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nPorts] );
+    free( ports );
   }
 
   }
 
-  err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK );
-  if (err < 0) {
-    if ( err == EBUSY )
-      sprintf(message, "RtAudio: ALSA pcm playback device (%s) is busy: %s.",
-              info->name, snd_strerror(err));
-    else
-      sprintf(message, "RtAudio: ALSA pcm playback open (%s) error: %s.",
-              info->name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  // We have an open device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",
-            info->name, snd_strerror(err));
-    error(RtError::WARNING);
-    goto capture_probe;
+  // Get the current jack server sample rate.
+  info.sampleRates.clear();
+  info.sampleRates.push_back( jack_get_sample_rate( client ) );
+
+  // 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;
   }
 
   }
 
-  // Get output channel information.
-  info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params);
-  info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params);
+  // 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;
+  }
+
+  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;
+  }
 
 
-  snd_pcm_close(handle);
+  // 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;
 
 
- capture_probe:
-  // Now try for capture
-  stream = SND_PCM_STREAM_CAPTURE;
-  snd_pcm_info_set_stream(pcminfo, stream);
+  // Jack always uses 32-bit floats.
+  info.nativeFormats = RTAUDIO_FLOAT32;
 
 
-  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;
-  }
+  // 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;
 
 
-  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_client_close(client);
+  info.probed = true;
+  return info;
+}
 
 
-  // Get input channel information.
-  info->minInputChannels = snd_pcm_hw_params_get_channels_min(params);
-  info->maxInputChannels = snd_pcm_hw_params_get_channels_max(params);
+int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
+{
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
 
 
-  snd_pcm_close(handle);
+  RtApiJack *object = (RtApiJack *) info->object;
+  if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
 
 
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
+  return 0;
+}
 
 
-  info->hasDuplexSupport = true;
-  info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-    info->maxInputChannels : info->maxOutputChannels;
-  info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-    info->minInputChannels : info->minOutputChannels;
+// This function will be called by a spawned thread when the Jack
+// server signals that it is shutting down.  It is necessary to handle
+// it this way because the jackShutdown() function must return before
+// the jack_deactivate() function (in closeStream()) will return.
+extern "C" void *jackCloseStream( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiJack *object = (RtApiJack *) info->object;
 
 
- 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.
+  object->closeStream();
 
 
-  if (info->maxOutputChannels >= info->maxInputChannels)
-    stream = SND_PCM_STREAM_PLAYBACK;
-  else
-    stream = SND_PCM_STREAM_CAPTURE;
+  pthread_exit( NULL );
+}
+void jackShutdown( void *infoPointer )
+{
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
+  RtApiJack *object = (RtApiJack *) info->object;
+
+  // Check current stream state.  If stopped, then we'll assume this
+  // was called as a result of a call to RtApiJack::stopStream (the
+  // deactivation of a client handle causes this function to be called).
+  // If not, we'll assume the Jack server is shutting down or some
+  // other problem occurred and we should close the stream.
+  if ( object->isStreamRunning() == false ) return;
+
+  pthread_create( &threadId, NULL, jackCloseStream, info );
+  std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
+}
 
 
-  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;
-  }
+int jackXrun( void *infoPointer )
+{
+  JackHandle *handle = (JackHandle *) infoPointer;
 
 
-  // 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;
-  }
+  if ( handle->ports[0] ) handle->xrun[0] = true;
+  if ( handle->ports[1] ) handle->xrun[1] = true;
+
+  return 0;
+}
 
 
-  // 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);
+bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+{
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+
+  // Look for jack server and try to become a client (only do once per stream).
+  jack_client_t *client = 0;
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
+    jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer | JackUseExactName ); //JackNullOption;
+    jack_status_t *status = NULL;
+    if ( options && !options->streamName.empty() )
+      client = jack_client_open( options->streamName.c_str(), jackoptions, status );
+    else
+      client = jack_client_open( "RtApiJack", jackoptions, status );
+    if ( client == 0 ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
+      error( RtError::WARNING );
+      return FAILURE;
+    }
   }
   else {
   }
   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++;
+    // The handle must have been created on an earlier pass.
+    client = handle->client;
+  }
+
+  const char **ports;
+  std::string port, previousPort, deviceName;
+  unsigned int nPorts = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    size_t iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) deviceName = port;
+          nDevices++;
+          previousPort = port;
+        }
       }
       }
-    }
-    if (info->nSampleRates == 0) {
-      snd_pcm_close(handle);
-      return;
-    }
+    } while ( ports[++nPorts] );
+    free( ports );
   }
 
   }
 
-  // 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;
-
-  // 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;
-  }
-
-  // That's all ... close the device and return
-  snd_pcm_close(handle);
-  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)
-{
-#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;
-
-  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);
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // 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;
+  // Count the available ports containing the client name as device
+  // channels.  Jack "input ports" equal RtAudio output channels.
+  unsigned int nChannels = 0;
+  unsigned long flag = JackPortIsInput;
+  if ( mode == INPUT ) flag = JackPortIsOutput;
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
   }
 
   }
 
-#if 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
-
-
-  // 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;
-  }
-  else {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA device (%s) access not supported by RtAudio.", name);
-    error(RtError::WARNING);
+  // Compare the jack ports for specified client to the requested number of channels.
+  if ( nChannels < (channels + firstChannel) ) {
+    errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message, "RtAudio: ALSA error setting access ( (%s): %s.", name, snd_strerror(err));
-    error(RtError::WARNING);
+  // Check the jack server sample rate.
+  unsigned int jackRate = jack_get_sample_rate( client );
+  if ( sampleRate != jackRate ) {
+    jack_client_close( client );
+    errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
     return FAILURE;
   }
+  stream_.sampleRate = jackRate;
 
 
-  // Determine how to set the device format.
-  stream->userFormat = format;
-  snd_pcm_format_t device_format;
-
-  if (format == RTAUDIO_SINT8)
-    device_format = SND_PCM_FORMAT_S8;
-  else if (format == RTAUDIO_SINT16)
-    device_format = SND_PCM_FORMAT_S16;
-  else if (format == RTAUDIO_SINT24)
-    device_format = SND_PCM_FORMAT_S24;
-  else if (format == RTAUDIO_SINT32)
-    device_format = SND_PCM_FORMAT_S32;
-  else if (format == RTAUDIO_FLOAT32)
-    device_format = SND_PCM_FORMAT_FLOAT;
-  else if (format == RTAUDIO_FLOAT64)
-    device_format = SND_PCM_FORMAT_FLOAT64;
-
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = format;
-    goto set_format;
-  }
-
-  // The user requested format is not natively supported by the device.
-  device_format = SND_PCM_FORMAT_FLOAT64;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
-    goto set_format;
-  }
+  // 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_FLOAT;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-    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_S32;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    goto set_format;
-  }
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
 
-  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;
-  }
+  // Jack always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
 
-  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;
-  }
+  // Jack always provides host byte-ordered data.
+  stream_.doByteSwap[mode] = false;
 
 
-  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;
-  }
+  // 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;
 
 
-  // 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;
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
 
 
- 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;
-  }
+  // 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;
+    }
 
 
-  // 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;
+    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->handle[1]) {
-    snd_pcm_close(stream->handle[1]);
-    stream->handle[1] = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 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;
+    }
+
+    // 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);
   }
 
   }
 
-  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);
+  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;
+    }
+
+    // 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_RUNNING;
+
+  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 :: stopStream(int streamId)
+void RtApiJack :: stopStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  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_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
   }
 
   }
 
-  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);
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
   }
     }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  jack_deactivate( handle->client );
+  stream_.state = STREAM_STOPPED;
+
+  MUTEX_UNLOCK( &stream_.mutex );
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+void RtApiJack :: abortStream( void )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  int err;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = snd_pcm_drop(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
-              devices[stream->device[0]].name, snd_strerror(err));
-      MUTEX_UNLOCK(&stream->mutex);
-      error(RtError::DRIVER_ERROR);
-    }
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
 
   }
 
-  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;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  stopStream();
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+// This function will be called by a spawned thread when the user
+// callback function signals that the stream should be stopped or
+// aborted.  It is necessary to handle it this way because the
+// callbackEvent() function must return before the jack_deactivate()
+// function will return.
+extern "C" void *jackStopStream( void *ptr )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiJack *object = (RtApiJack *) info->object;
 
 
-  MUTEX_LOCK(&stream->mutex);
+  object->stopStream();
 
 
-  int err = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  pthread_exit( NULL );
+}
 
 
-  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);
-    }
+bool RtApiJack :: callbackEvent( unsigned long nframes )
+{
+  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;
   }
   }
-
-  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;
+  if ( stream_.bufferSize != nframes ) {
+    errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
+    error( RtError::WARNING );
+    return FAILURE;
   }
 
   }
 
-  frames = stream->bufferSize - frames;
-  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;
+  JackHandle *handle = (JackHandle *) 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 == true ) {
+      pthread_create( &threadId, NULL, jackStopStream, info );
+    }
+    else
+      pthread_cond_signal( &handle->condition );
+    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 err;
-  char *buffer;
-  int channels;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return SUCCESS;
+  }
 
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[0];
-      format = stream->deviceFormat[0];
+  // Invoke user callback first, to get fresh output data.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
     }
     }
-    else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[0];
-      format = stream->userFormat;
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      ThreadHandle id;
+      pthread_create( &id, NULL, jackStopStream, info );
+      return SUCCESS;
     }
     }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
+  }
 
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+  jack_default_audio_sample_t *jackbuffer;
+  unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-    // 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);
+    if ( handle->drainCounter > 0 ) { // write zeros to the output stream
 
 
-    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;
-      }
-      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);
+      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 );
       }
       }
+
     }
     }
-  }
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
+      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 {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[1];
-      format = stream->userFormat;
+    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 );
+      }
     }
 
     }
 
-    // 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);
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
     }
-    else
-      err = snd_pcm_readi(stream->handle[1], buffer, stream->bufferSize);
+  }
 
 
-    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_.mode == INPUT || stream_.mode == DUPLEX ) {
+
+    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;
-  }
-
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
-
-  fd = open(info->name, O_RDWR | O_NONBLOCK);
-  if (fd == -1)
-    goto probe_parameters;
-
-  ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
-  ioctl(fd, SNDCTL_DSP_GETCAPS, &mask);
-  if (mask & DSP_CAP_DUPLEX) {
-    info->hasDuplexSupport = true;
-    // We have the device open for duplex ... see how many channels it can handle
-    for (i=MAX_CHANNELS; i>0; i--) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // as above
-      // If here, we found a working channel value
-      break;
-    }
-    info->maxDuplexChannels = i;
 
 
-    // Now find the minimum number of channels it can handle
-    for (i=1; i<=info->maxDuplexChannels; i++) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // try next channel number
-      // If here, we found the smallest working channel value
-      break;
-    }
-    info->minDuplexChannels = i;
+  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;
   }
   }
-  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;
-  }
+  info.name = driverName;
 
 
-  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;
+  if ( !drivers.loadDriver( driverName ) ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << 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;
+  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 (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 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;
   }
 
   }
 
-  // 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;
-  }
+  info.outputChannels = outputChannels;
+  info.inputChannels = inputChannels;
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
 
-  // 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;
+  // 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] );
   }
 
   }
 
-  // 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;
+  // 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;
   }
 
   }
 
-  // Probe the supported sample rates ... first get lower limit
-  int speed = 1;
-  if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
-    // If we get here, we're probably using an ALSA driver with OSS-emulation,
-    // which doesn't conform to the OSS specification.  In this case,
-    // we'll probe our predefined list of sample rates for working values.
-    info->nSampleRates = 0;
-    for (i=0; i<MAX_SAMPLE_RATES; i++) {
-      speed = SAMPLE_RATES[i];
-      if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) != -1) {
-        info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-        info->nSampleRates++;
-      }
-    }
-    if (info->nSampleRates == 0) {
-      close(fd);
-      return;
-    }
-    goto finished;
-  }
-  info->sampleRates[0] = speed;
+  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;
 
 
-  // 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;
-  }
-  info->sampleRates[1] = speed;
-  info->nSampleRates = -1;
+  if ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
 
 
- finished: // That's all ... close the device and return
-  close(fd);
-  info->probed = true;
-  return;
+  info.probed = true;
+  drivers.removeCurrentDriver();
+  return info;
 }
 
 }
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
+void bufferSwitch( long index, ASIOBool processNow )
 {
 {
-  int buffers, buffer_bytes, device_channels, device_format;
-  int srate, temp, fd;
-
-  const char *name = devices[device].name;
+  RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
+  object->callbackEvent( index );
+}
 
 
-  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);
-    }
-    else
-      fd = open(name, O_RDONLY | O_NONBLOCK);
-  }
+void RtApiAsio :: saveDeviceInfo( void )
+{
+  devices_.clear();
 
 
-  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;
-  }
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
 
 
-  // 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);
+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;
   }
 
   }
 
-  if (fd == -1) {
-    sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
-    goto error;
+  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;
   }
 
   }
 
-  // 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;
-  }
+  // 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();
 
 
-  // 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;
+  // 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;
     }
     }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT16;
-      stream->doByteSwap[mode] = true;
+
+    result = ASIOInit( &driverInfo );
+    if ( result != ASE_OK ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-#endif
   }
   }
-#if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
-  else if (format == RTAUDIO_SINT32) {
-    if (mask & AFMT_S32_NE) {
-      device_format = AFMT_S32_NE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S32_BE) {
-      device_format = AFMT_S32_BE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S32_LE) {
-      device_format = AFMT_S32_LE;
-      stream->deviceFormat[mode] = RTAUDIO_SINT32;
-      stream->doByteSwap[mode] = true;
-    }
-#endif
+
+  // 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;
   }
   }
-#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 == 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;
 
 
-  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;
+  // 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;
   }
 
   }
 
-  // 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;
+  // 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;
   }
   }
-  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;
-    }
-    else {
-      if (channels < devices[device].minInputChannels)
-        device_channels = devices[device].minInputChannels;
+
+  // 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;
     }
   }
     }
   }
-  stream->nDeviceChannels[mode] = device_channels;
 
 
-  // Attempt to set the buffer size.  According to OSS, the minimum
-  // number of buffers is two.  The supposed minimum buffer size is 16
-  // bytes, so that will be our lower bound.  The argument to this
-  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
-  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
-  // We'll check the actual value used near the end of the setup
-  // procedure.
-  buffer_bytes = *bufferSize * formatBytes(stream->deviceFormat[mode]) * device_channels;
-  if (buffer_bytes < 16) buffer_bytes = 16;
-  buffers = numberOfBuffers;
-  if (buffers < 2) buffers = 2;
-  temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0));
-  if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting fragment size for device (%s).",
-            name);
-    goto error;
+  // 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;
   }
   }
-  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;
+  // 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;
   }
   }
-
-  // Set the number of channels.
-  temp = device_channels;
-  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error setting %d channels on device (%s).",
-            temp, name);
-    goto error;
+  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-
-  // 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;
+  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-
-  // 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;
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
   }
   }
-  stream->sampleRate = sampleRate;
 
 
-  if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) {
-    close(fd);
-    sprintf(message, "RtAudio: OSS error getting buffer size for device (%s).",
-            name);
-    goto error;
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  // 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 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;
   }
 
   }
 
-  // Set flags for buffer conversion
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+  if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+  else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
+  else if ( granularity == -1 ) {
+    // Make sure bufferSize is a power of two.
+    int log2_of_min_size = 0;
+    int log2_of_max_size = 0;
 
 
-    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;
+    for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
+      if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
+      if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
     }
     }
-  }
 
 
-  if ( stream->doConvertBuffer[mode] ) {
+    long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
+    int min_delta_num = log2_of_min_size;
 
 
-    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;
+    for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
+      long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
+      if (current_delta < min_delta) {
+        min_delta = current_delta;
+        min_delta_num = i;
       }
     }
 
       }
     }
 
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL) {
-        close(fd);
-        free(stream->userBuffer);
-        sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).",
-                name);
-        goto error;
-      }
-    }
+    *bufferSize = ( (unsigned int)1 << min_delta_num );
+    if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
   }
   }
-
-  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;
+  else if ( granularity != 0 ) {
+    // Set to an even multiple of granularity, rounding up.
+    *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
   }
   }
-  else
-    stream->mode = mode;
-
-  return SUCCESS;
 
 
- error:
-  if (stream->handle[0]) {
-    close(stream->handle[0]);
-    stream->handle[0] = 0;
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
+    drivers.removeCurrentDriver();
+    errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
+    return FAILURE;
   }
   }
-  error(RtError::WARNING);
-  return FAILURE;
-}
 
 
-void RtAudio :: closeStream(int streamId)
-{
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
-    return;
-  }
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 2;
 
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
-  }
+  // ASIO always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
 
-  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);
-  }
-
-  pthread_mutex_destroy(&stream->mutex);
-
-  if (stream->handle[0])
-    close(stream->handle[0]);
-
-  if (stream->handle[1])
-    close(stream->handle[1]);
+  // Allocate, if necessary, our AsioHandle structure for the stream.
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( handle == 0 ) {
+    try {
+      handle = new AsioHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      //if ( handle == NULL ) {    
+      drivers.removeCurrentDriver();
+      errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
+      return FAILURE;
+    }
+    handle->bufferInfos = 0;
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  // 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 );
+  }
 
 
-  free(stream);
-  streams.erase(streamId);
-}
+  // 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;
+  }
 
 
-void RtAudio :: startStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  ASIOBufferInfo *infos;
+  infos = handle->bufferInfos;
+  for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
+    infos->isInput = ASIOFalse;
+    infos->channelNum = i + stream_.channelOffset[0];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
+  for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
+    infos->isInput = ASIOTrue;
+    infos->channelNum = i + stream_.channelOffset[1];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  // 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;
+  }
+  buffersAllocated = true;
 
 
-  stream->state = STREAM_RUNNING;
+  // 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;
 
 
-  // No need to do anything else here ... OSS automatically starts
-  // when fed samples.
+  // Allocate necessary internal buffers
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
+  }
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    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;
+      }
+    }
 
 
-  MUTEX_LOCK(&stream->mutex);
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
+    }
+  }
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  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
+    stream_.mode = mode;
 
 
-  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);
-    }
+  // 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 {
   }
   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);
-    }
+    stream_.latency[0] = outputLatency;
+    stream_.latency[1] = inputLatency;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  // 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 );
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  return SUCCESS;
 
 
-  MUTEX_LOCK(&stream->mutex);
+ error:
+  if ( buffersAllocated )
+    ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-  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);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
     }
   }
-  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);
-    }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  return FAILURE;
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiAsio :: closeStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  int bytes = 0, channels = 0, frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    ASIOStop();
+  }
+  ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
 
-  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];
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
+    stream_.apiHandle = 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];
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
 
     }
   }
 
-  frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0])));
-  frames -= stream->bufferSize;
-  if (frames < 0) frames = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiAsio :: startStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAsio::startStream(): the stream is already running!";
+    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);
-  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
+  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;
     goto unlock;
+  }
 
 
-  int result;
-  char *buffer;
-  int samples;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-
-    // 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];
-    }
-    else {
-      buffer = stream->userBuffer;
-      samples = stream->bufferSize * stream->nUserChannels[0];
-      format = stream->userFormat;
-    }
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
+  asioXRun = false;
 
 
-    // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, samples, format);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-    // Write samples to device.
-    result = write(stream->handle[0], buffer, samples * formatBytes(format));
+  if ( result == ASE_OK ) return;
+  error( RtError::SYSTEM_ERROR );
+}
 
 
-    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);
-    }
+void RtApiAsio :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
 
   }
 
-  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;
-    }
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // Read samples from device.
-    result = read(stream->handle[1], buffer, samples * formatBytes(format));
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-    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);
+  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 );
     }
     }
+  }
 
 
-    // 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);
+  ASIOError result = ASIOStop();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
+    errorText_ = errorStream_.str();
   }
 
   }
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  if (stream->callbackInfo.usingCallback && stopStream)
-    this->stopStream(streamId);
+  if ( result == ASE_OK ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-extern "C" void *callbackHandler(void *ptr)
+void RtApiAsio :: abortStream()
 {
 {
-  CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
-  RtAudio *object = (RtAudio *) info->object;
-  int stream = info->streamId;
-  bool *usingCallback = &info->usingCallback;
-
-  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;
-    }
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
 
   }
 
-  return 0;
+  // The following lines were commented-out because some behavior was
+  // noted where the device buffers need to be zeroed to avoid
+  // continuing sound, even when the device buffers are completely
+  // disposed.  So now, calling abort is the same as calling stop.
+  // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  // handle->drainCounter = 1;
+  stopStream();
 }
 
 }
 
+bool RtApiAsio :: callbackEvent( long bufferIndex )
+{
+  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;
+  }
 
 
-//******************** End of __LINUX_OSS__ *********************//
-
-#elif defined(__WINDOWS_ASIO__) // ASIO API on Windows
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
 
 
-// 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.
+  // 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;
+  }
 
 
-#include "asio/asiosys.h"
-#include "asio/asio.h"
-#include "asio/asiodrivers.h"
-#include <math.h>
+  MUTEX_LOCK( &stream_.mutex );
 
 
-AsioDrivers drivers;
-ASIOCallbacks asioCallbacks;
-CALLBACK_INFO *asioCallbackInfo;
-ASIODriverInfo driverInfo;
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
-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);
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && asioXRun == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      asioXRun = false;
+    }
+    if ( stream_.mode != OUTPUT && asioXRun == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      asioXRun = false;
     }
     }
+    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;
   }
 
   }
 
-  drivers.removeCurrentDriver();
-  driverInfo.asioVersion = 2;
-  // See note in DirectSound implementation about GetDesktopWindow().
-  driverInfo.sysRef = GetForegroundWindow();
-}
+  unsigned int nChannels, bufferBytes, i, j;
+  nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-int RtAudio :: getDefaultInputDevice(void)
-{
-  return 0;
-}
+    bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
 
 
-int RtAudio :: getDefaultOutputDevice(void)
-{
-  return 0;
-}
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
 
-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;
-  }
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
+      }
 
 
-  if ( !drivers.loadDriver( info->name ) ) {
-    sprintf(message, "RtAudio: ASIO error loading driver (%s).", info->name);
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+    }
+    else if ( stream_.doConvertBuffer[0] ) {
 
 
-  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;
-  }
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[0],
+                        stream_.deviceFormat[0] );
 
 
-  // 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;
-  }
+      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 );
+      }
 
 
-  info->maxOutputChannels = outputChannels;
-  if ( outputChannels > 0 ) info->minOutputChannels = 1;
+    }
+    else {
 
 
-  info->maxInputChannels = inputChannels;
-  if ( inputChannels > 0 ) info->minInputChannels = 1;
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.userBuffer[0],
+                        stream_.bufferSize * stream_.nUserChannels[0],
+                        stream_.userFormat );
 
 
-  // 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;
-  }
+      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 );
+      }
 
 
-  // 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 (info->nSampleRates == 0) {
-    drivers.removeCurrentDriver();
-    sprintf( message, "RtAudio: No supported sample rates found for ASIO driver (%s).", info->name );
-    error(RtError::DEBUG_WARNING);
-    return;
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
 
   }
 
-  // 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_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-  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;
+    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
 
 
-       // 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_.doConvertBuffer[1]) {
 
 
-  info->probed = true;
-  drivers.removeCurrentDriver();
-}
+      // 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 );
+      }
 
 
-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;
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[1],
+                        stream_.deviceFormat[1] );
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+
+    }
+    else {
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
+          memcpy( &stream_.userBuffer[1][bufferBytes*j++],
+                  handle->bufferInfos[i].buffers[bufferIndex],
+                  bufferBytes );
+        }
+      }
+
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.userBuffer[1],
+                        stream_.bufferSize * stream_.nUserChannels[1],
+                        stream_.userFormat );
+    }
   }
 
   }
 
-  return;
+ 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();
+
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  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,
@@ -3599,32 +3355,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:
@@ -3634,7 +3391,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:
@@ -3645,7 +3402,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:
@@ -3653,7 +3411,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:
@@ -3670,7 +3428,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;
@@ -3679,3327 +3437,4403 @@ 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. 
+// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
+// - Auto-call CoInitialize for DSOUND and ASIO platforms.
+// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
+// Changed device query structure for RtAudio 4.0.7, January 2010
 
 
-  // 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>
+#include <algorithm>
+
+#if defined(__MINGW32__)
+  // missing from latest mingw winapi
+#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
+#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
+#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
+#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
+#endif
 
 
-  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 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;
+}
 
 
-  // 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;
-  }
+// 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; }
+};
 
 
-  // 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;
-  }
+// Declarations for utility functions, callbacks, and structures
+// specific to the DirectSound implementation.
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext );
 
 
-  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;
-  }
+static const char* getErrorString( int code );
 
 
-  // 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;
-  }
+extern "C" unsigned __stdcall callbackHandler( void *ptr );
 
 
-  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;
-  }
+struct DsDevice {
+  LPGUID id[2];
+  bool validId[2];
+  bool found;
+  std::string name;
 
 
-  if ( mode == INPUT && stream->mode == OUTPUT && stream->bufferSize != *bufferSize )
-    cout << "possible input/output buffersize discrepancy" << endl;
+  DsDevice()
+  : found(false) { validId[0] = false; validId[1] = false; }
+};
 
 
-  stream->bufferSize = *bufferSize;
-  stream->nBuffers = 2;
+std::vector< DsDevice > dsDevices;
 
 
-  // ASIO always uses deinterleaved channels.
-  stream->deInterleave[mode] = true;
+RtApiDs :: RtApiDs()
+{
+  // 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;
+}
 
 
-  // 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;
-    }
-  }
+RtApiDs :: ~RtApiDs()
+{
+  if ( coInitialized_ ) CoUninitialize(); // balanced call.
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+}
 
 
-  // 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;
-  }
+// The DirectSound default output is always the first device.
+unsigned int RtApiDs :: getDefaultOutputDevice( void )
+{
+  return 0;
+}
 
 
-  for ( i=0; i<stream->nDeviceChannels[0]; i++, infos++ ) {
-    infos->isInput = ASIOFalse;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
-  }
+// The DirectSound default input is always the first input device,
+// which is the first capture device enumerated.
+unsigned int RtApiDs :: getDefaultInputDevice( void )
+{
+  return 0;
+}
 
 
-  // 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;
-  }
+unsigned int RtApiDs :: getDeviceCount( void )
+{
+  // Set query flag for previously found devices to false, so that we
+  // can check for any devices that have disappeared.
+  for ( unsigned int i=0; i<dsDevices.size(); i++ )
+    dsDevices[i].found = false;
 
 
-  // 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;
+  // Query DirectSound devices.
+  bool isInput = false;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+  }
 
 
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+  // Query DirectSoundCapture devices.
+  isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+  }
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
+  // Clean out any devices that may have disappeared.
+  std::vector< DsDevice > :: iterator it;
+  for ( it=dsDevices.begin(); it < dsDevices.end(); it++ )
+    if ( it->found == false ) dsDevices.erase( it );
 
 
-    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;
-  }
+  return dsDevices.size();
+}
 
 
-  if ( stream->doConvertBuffer[mode] ) {
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
+  if ( dsDevices.size() == 0 ) {
+    // Force a query of all devices
+    getDeviceCount();
+    if ( dsDevices.size() == 0 ) {
+      errorText_ = "RtApiDs::getDeviceInfo: no devices found!";
+      error( RtError::INVALID_USE );
     }
     }
+  }
 
 
-    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 ( device >= dsDevices.size() ) {
+    errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
   }
 
   }
 
-  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);
+  HRESULT result;
+  if ( dsDevices[ device ].validId[0] == false ) goto probeInput;
 
 
-  return SUCCESS;
+  LPDIRECTSOUND output;
+  DSCAPS outCaps;
+  result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    goto probeInput;
+  }
 
 
- memory_error:
-  ASIODisposeBuffers();
-  drivers.removeCurrentDriver();
+  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 );
+    goto probeInput;
+  }
 
 
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
-  stream->callbackInfo.buffers = 0;
+  // Get output channel information.
+  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
 
 
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+  // 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] );
   }
   }
-  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);
+  // Get format information.
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
 
 
-  if (stream->callbackInfo.usingCallback) {
+  output->Release();
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
 
 
-    MUTEX_LOCK(&stream->mutex);
+  if ( dsDevices[ device ].validId[1] == false ) {
+    info.name = dsDevices[ device ].name;
+    info.probed = true;
+    return info;
+  }
 
 
-    stream->callbackInfo.usingCallback = false;
-    stream->callbackInfo.userData = NULL;
-    stream->state = STREAM_STOPPED;
-    stream->callbackInfo.callback = NULL;
+ probeInput:
 
 
-    MUTEX_UNLOCK(&stream->mutex);
+  LPDIRECTSOUNDCAPTURE input;
+  result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
   }
-}
 
 
-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;
+  DSCCAPS inCaps;
+  inCaps.dwSize = sizeof( inCaps );
+  result = input->GetCaps( &inCaps );
+  if ( FAILED( result ) ) {
+    input->Release();
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";
+    errorText_ = errorStream_.str();
+    error( RtError::WARNING );
+    return info;
   }
 
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  // Get input channel information.
+  info.inputChannels = inCaps.dwChannels;
 
 
-  if (stream->state == STREAM_RUNNING)
-    ASIOStop();
+  // Get sample rate and format information.
+  std::vector<unsigned int> rates;
+  if ( inCaps.dwChannels == 2 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );
+    }
+  }
+  else if ( inCaps.dwChannels == 1 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );
+    }
+  }
+  else info.inputChannels = 0; // technically, this would be an error
 
 
-  ASIODisposeBuffers();
-  //ASIOExit();
-  drivers.removeCurrentDriver();
+  input->Release();
 
 
-  DeleteCriticalSection(&stream->mutex);
+  if ( info.inputChannels == 0 ) return info;
 
 
-  if (stream->callbackInfo.buffers)
-    free(stream->callbackInfo.buffers);
+  // Copy the supported rates to the info structure but avoid duplication.
+  bool found;
+  for ( unsigned int i=0; i<rates.size(); i++ ) {
+    found = false;
+    for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {
+      if ( rates[i] == info.sampleRates[j] ) {
+        found = true;
+        break;
+      }
+    }
+    if ( found == false ) info.sampleRates.push_back( rates[i] );
+  }
+  sort( info.sampleRates.begin(), info.sampleRates.end() );
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  // 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;
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  if ( device == 0 ) info.isDefaultInput = true;
 
 
-  free(stream);
-  streams.erase(streamId);
+  // Copy name and return.
+  info.name = dsDevices[ device ].name;
+  info.probed = true;
+  return info;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+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 )
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_RUNNING) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
+  if ( channels + firstChannel > 2 ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
+    return FAILURE;
   }
 
   }
 
-  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);
+  unsigned int nDevices = dsDevices.size();
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";
+    return FAILURE;
   }
   }
-  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;
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
   }
 
   }
 
-  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);
+  if ( mode == OUTPUT ) {
+    if ( dsDevices[ device ].validId[0] == false ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+  }
+  else { // mode == INPUT
+    if ( dsDevices[ device ].validId[1] == false ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
   }
-  stream->state = STREAM_STOPPED;
-
-  MUTEX_UNLOCK(&stream->mutex);
-}
-
-void RtAudio :: abortStream(int streamId)
-{
-  stopStream( streamId );
-}
-
-// I don't know how this function can be implemented.
-int RtAudio :: streamWillBlock(int streamId)
-{
-  sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for ASIO.");
-  error(RtError::WARNING);
-  return 0;
-}
-
-void RtAudio :: tickStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
 
 
-  if (stream->state == STREAM_STOPPED)
-    return;
+  // 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.  In the past, I had
+  // problems when using GetDesktopWindow() but it seems fine now
+  // (January 2010).  I'll leave it commented here.
+  // HWND hWnd = GetForegroundWindow();
+  HWND hWnd = GetDesktopWindow();
 
 
-  if (stream->callbackInfo.usingCallback) {
-    sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
-    return;
-  }
+  // 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.
+  int nBuffers = 0;
+  if ( options ) nBuffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
+  if ( nBuffers < 2 ) nBuffers = 3;
 
 
-  // Block waiting here until the user data is processed in callbackEvent().
-  while ( stream->callbackInfo.blockTick )
-    Sleep(stream->callbackInfo.waitTime);
+  // Check the lower range of the user-specified buffer size and set
+  // (arbitrarily) to a lower bound of 32.
+  if ( *bufferSize < 32 ) *bufferSize = 32;
 
 
-  MUTEX_LOCK(&stream->mutex);
+  // Create the wave format structure.  The data format setting will
+  // be determined later.
+  WAVEFORMATEX waveFormat;
+  ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
+  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
+  waveFormat.nChannels = channels + firstChannel;
+  waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
 
 
-  stream->callbackInfo.blockTick = true;
+  // Determine the device buffer size. By default, we'll use the value
+  // defined above (32K), but we will grow it to make allowances for
+  // very large software buffer sizes.
+  DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;;
+  DWORD dsPointerLeadTime = 0;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  void *ohandle = 0, *bhandle = 0;
+  HRESULT result;
+  if ( mode == OUTPUT ) {
 
 
-void RtAudio :: callbackEvent(int streamId, int bufferIndex, void *inData, void *outData)
-{
-  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;
-  }
+    LPDIRECTSOUND output;
+    result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-  MUTEX_LOCK(&stream->mutex);
-  int nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
-  int bufferBytes;
-  ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) info->buffers;
-  if ( stream->mode == INPUT || stream->mode == DUPLEX ) {
+    DSCAPS outCaps;
+    outCaps.dwSize = sizeof( outCaps );
+    result = output->GetCaps( &outCaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[1]);
-    if (stream->doConvertBuffer[1]) {
+    // Check channel information.
+    if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
+      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-      // Always interleave ASIO input data.
-      for ( int i=0; i<stream->nDeviceChannels[1]; i++, bufferInfos++ )
-        memcpy(&stream->deviceBuffer[i*bufferBytes], bufferInfos->buffers[bufferIndex], bufferBytes );
+    // Check format information.  Use 16-bit format unless not
+    // supported or user requests 8-bit.
+    if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
+         !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
+      waveFormat.wBitsPerSample = 16;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    }
+    else {
+      waveFormat.wBitsPerSample = 8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+    }
+    stream_.userFormat = format;
+
+    // Update wave format structure and buffer information.
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
+
+    // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
+    // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+    // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
+    result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-      if ( stream->doByteSwap[1] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[1],
-                       stream->deviceFormat[1]);
-      convertStreamBuffer(stream, INPUT);
+    // Even though we will write to the secondary buffer, we need to
+    // 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;
 
 
+    // Obtain the primary buffer
+    LPDIRECTSOUNDBUFFER buffer;
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    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);
+    // Set the primary DS buffer sound format.
+    result = buffer->SetFormat( &waveFormat );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-  }
 
 
-  if ( info->usingCallback ) {
-    RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
-    if ( callback(stream->userBuffer, stream->bufferSize, info->userData) )
-      info->stopStream = true;
-  }
+    // Setup the secondary DS buffer description.
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
+    bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                  DSBCAPS_GLOBALFOCUS |
+                                  DSBCAPS_GETCURRENTPOSITION2 |
+                                  DSBCAPS_LOCHARDWARE );  // Force hardware mixing
+    bufferDescription.dwBufferBytes = dsBufferSize;
+    bufferDescription.lpwfxFormat = &waveFormat;
 
 
-  if ( stream->mode == OUTPUT || stream->mode == DUPLEX ) {
+    // Try to create the secondary DS buffer.  If that doesn't work,
+    // try to use software mixing.  Otherwise, there's a problem.
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                    DSBCAPS_GLOBALFOCUS |
+                                    DSBCAPS_GETCURRENTPOSITION2 |
+                                    DSBCAPS_LOCSOFTWARE );  // Force software mixing
+      result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+      if ( FAILED( result ) ) {
+        output->Release();
+        errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
 
 
-    bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[0]);
-    if (stream->doConvertBuffer[0]) {
+    // Get the buffer size ... might be different from what we specified.
+    DSBCAPS dsbcaps;
+    dsbcaps.dwSize = sizeof( DSBCAPS );
+    result = buffer->GetCaps( &dsbcaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-      convertStreamBuffer(stream, OUTPUT);
-      if ( stream->doByteSwap[0] )
-        byteSwapBuffer(stream->deviceBuffer,
-                       stream->bufferSize * stream->nDeviceChannels[0],
-                       stream->deviceFormat[0]);
+    dsBufferSize = dsbcaps.dwBufferBytes;
 
 
-      // 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 );
-      }
+    // Lock the DS buffer
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
-    else { // single channel only
 
 
-      if (stream->doByteSwap[0])
-        byteSwapBuffer(stream->userBuffer,
-                       stream->bufferSize * stream->nUserChannels[0],
-                       stream->userFormat);
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
 
 
-      memcpy(bufferInfos->buffers[bufferIndex], stream->userBuffer, bufferBytes );
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
     }
+
+    ohandle = (void *) output;
+    bhandle = (void *) buffer;
   }
 
   }
 
-  if ( !info->usingCallback )
-    info->blockTick = false;
+  if ( mode == INPUT ) {
 
 
-  MUTEX_UNLOCK(&stream->mutex);
-}
+    LPDIRECTSOUNDCAPTURE input;
+    result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+    DSCCAPS inCaps;
+    inCaps.dwSize = sizeof( inCaps );
+    result = input->GetCaps( &inCaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-  stream->callbackInfo.callback = (void *) callback;
-  stream->callbackInfo.userData = userData;
-  stream->callbackInfo.usingCallback = true;
-}
+    // Check channel information.
+    if ( inCaps.dwChannels < channels + firstChannel ) {
+      errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
+      return FAILURE;
+    }
 
 
-//******************** End of __WINDOWS_ASIO__ *********************//
+    // 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;
 
 
-#elif defined(__WINDOWS_DS__) // Windows DirectSound API
+    // 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;
 
 
-#include <dsound.h>
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
 
 
-// Declarations for utility functions, callbacks, and structures
-// specific to the DirectSound implementation.
-static bool CALLBACK deviceCountCallback(LPGUID lpguid,
-                                         LPCSTR lpcstrDescription,
-                                         LPCSTR lpcstrModule,
-                                         LPVOID lpContext);
+    // Setup the secondary DS buffer description.
+    DSCBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
+    bufferDescription.dwFlags = 0;
+    bufferDescription.dwReserved = 0;
+    bufferDescription.dwBufferBytes = dsBufferSize;
+    bufferDescription.lpwfxFormat = &waveFormat;
 
 
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCSTR lpcstrDescription,
-                                        LPCSTR lpcstrModule,
-                                        LPVOID lpContext);
+    // Create the capture buffer.
+    LPDIRECTSOUNDCAPTUREBUFFER buffer;
+    result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCSTR lpcstrDescription,
-                                           LPCSTR lpcstrModule,
-                                           LPVOID lpContext);
+    // Get the buffer size ... might be different from what we specified.
+    DSCBCAPS dscbcaps;
+    dscbcaps.dwSize = sizeof( DSCBCAPS );
+    result = buffer->GetCaps( &dscbcaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCSTR lpcstrDescription,
-                                      LPCSTR lpcstrModule,
-                                      LPVOID lpContext);
+    dsBufferSize = dscbcaps.dwBufferBytes;
 
 
-static char* getErrorString(int code);
+    // NOTE: We could have a problem here if this is a duplex stream
+    // and the play and capture hardware buffer sizes are different
+    // (I'm actually not sure if that is a problem or not).
+    // Currently, we are not verifying that.
 
 
-struct enum_info {
-  char name[64];
-  LPGUID id;
-  bool isInput;
-  bool isValid;
-};
+    // Lock the capture buffer
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
 
-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);
-    return 0;
+    // Zero the buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    ohandle = (void *) input;
+    bhandle = (void *) buffer;
   }
 
   }
 
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp( devices[i].name, info.name, 64 ) == 0 ) return i;
+  // 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;
 
 
-  return 0;
-}
+  // 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;
 
 
-int RtAudio :: 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);
-    return 0;
+  // Allocate necessary internal buffers
+  long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  for ( int i=0; i<nDevices; i++ )
-    if ( strncmp(devices[i].name, info.name, 64 ) == 0 ) return i;
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-  return 0;
-}
-
-void RtAudio :: initialize(void)
-{
-  int i, ins = 0, outs = 0, count = 0;
-  HRESULT result;
-  nDevices = 0;
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
+      }
+    }
 
 
-  // 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);
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
+    }
   }
 
   }
 
-  // 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);
+  // Allocate our DsHandle structures for the stream.
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new DsHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
+      goto error;
+    }
+
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
+  }
+  else
+    handle = (DsHandle *) stream_.apiHandle;
+  handle->id[mode] = ohandle;
+  handle->buffer[mode] = bhandle;
+  handle->dsBufferSize[mode] = dsBufferSize;
+  handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
+
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up an output stream.
+    stream_.mode = DUPLEX;
+  else
+    stream_.mode = mode;
+  stream_.nBuffers = nBuffers;
+  stream_.sampleRate = sampleRate;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup the callback thread.
+  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;
   }
 
   }
 
-  count = ins + outs;
-  if (count == 0) return;
+  // Boost DS thread priority
+  SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
+  return SUCCESS;
 
 
-  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;
+ error:
+  if ( handle ) {
+    if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
+      LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
+      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      if ( buffer ) buffer->Release();
+      object->Release();
+    }
+    if ( handle->buffer[1] ) {
+      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
+      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+      if ( buffer ) buffer->Release();
+      object->Release();
+    }
+    CloseHandle( handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
   }
 
-  // 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);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
 
   }
 
-  // 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);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
   }
 
-  // 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++;
+  return FAILURE;
+}
+
+void RtApiDs :: closeStream()
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if (nDevices == 0) return;
+  // Stop the callback thread.
+  stream_.callbackInfo.isRunning = false;
+  WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
+  CloseHandle( (HANDLE) stream_.callbackInfo.thread );
 
 
-  //  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);
+  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;
   }
 
   }
 
-  // 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);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
 
   }
 
-  //for (i=0;i<nDevices; i++)
-  //probeDeviceInfo(&devices[i]);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  return;
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
+void RtApiDs :: startStream()
 {
 {
-  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);
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiDs::startStream(): the stream is already running!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  // Do capture probe first.
-  if ( dsinfo.isValid == false )
-    goto playback_probe;
+  MUTEX_LOCK( &stream_.mutex );
+  
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+
+  // Increase scheduler frequency on lesser windows (a side-effect of
+  // increasing timer accuracy).  On greater windows (Win2K or later),
+  // this is already in effect.
+  timeBeginPeriod( 1 ); 
 
 
-  LPDIRECTSOUNDCAPTURE  input;
-  result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto playback_probe;
+  buffersRolling = false;
+  duplexPrerollBytes = 0;
+
+  if ( stream_.mode == DUPLEX ) {
+    // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
+    duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
   }
 
   }
 
-  DSCCAPS in_caps;
-  in_caps.dwSize = sizeof(in_caps);
-  result = input->GetCaps( &in_caps );
-  if ( FAILED(result) ) {
-    input->Release();
-    sprintf(message, "RtAudio: Could not get DirectSound capture capabilities (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto playback_probe;
+  HRESULT result = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
   }
 
   }
 
-  // Get input channel information.
-  info->minInputChannels = 1;
-  info->maxInputChannels = in_caps.dwChannels;
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-  // 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
+    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;
+    }
+  }
 
 
-  input->Release();
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
 
 
- playback_probe:
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  dsinfo.isValid = false;
+  if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
+}
 
 
-  // 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);
+void RtApiDs :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  // Now do playback probe.
-  if ( dsinfo.isValid == false )
-    goto check_parameters;
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  LPDIRECTSOUND  output;
-  DSCAPS out_caps;
-  result = DirectSoundCreate( dsinfo.id, &output, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",
-            info->name, getErrorString(result));
-    error(RtError::WARNING);
-    goto check_parameters;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
   }
 
   }
 
-  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;
-  }
+  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::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
 
 
-  // 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;
-                       }
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    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);
+
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    else info->nSampleRates = 2;
+
+    // If we start playing again, we must begin at beginning of buffer.
+    handle->bufferPointer[0] = 0;
   }
   }
-  else {
-    // Check input rates against output rate range
-    for ( int i=info->nSampleRates-1; i>=0; i-- ) {
-      if ( info->sampleRates[i] <= out_caps.dwMaxSecondarySampleRate )
-        break;
-      info->nSampleRates--;
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    audioPtr = NULL;
+    dataLen = 0;
+
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    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;
+
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
+
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // If we start recording again, we must begin at beginning of buffer.
+    handle->bufferPointer[1] = 0;
   }
 
   }
 
-  // Get format information.
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16;
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8;
+ unlock:
+  timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  output->Release();
+  if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
+}
 
 
- check_parameters:
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
+void RtApiDs :: abortStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
     return;
     return;
-  if ( info->nSampleRates == 0 || info->nativeFormats == 0 )
+  }
+
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
+
+  stopStream();
+}
+
+void RtApiDs :: callbackEvent()
+{
+  if ( stream_.state == STREAM_STOPPED ) {
+    Sleep( 50 ); // sleep 50 milliseconds
     return;
     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 ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
 
 
-  info->probed = true;
+  // 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;
-}
+  MUTEX_LOCK( &stream_.mutex );
+
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    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;
+  }
 
 
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
   HRESULT result;
   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().
-  long buffer_size;
-  LPVOID audioPtr;
-  DWORD dataLen;
-  int nBuffers;
+  DWORD currentWritePointer, safeWritePointer;
+  DWORD currentReadPointer, safeReadPointer;
+  UINT nextWritePointer;
 
 
-  // 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;
+  LPVOID buffer1 = NULL;
+  LPVOID buffer2 = NULL;
+  DWORD bufferSize1 = 0;
+  DWORD bufferSize2 = 0;
 
 
-  // Define the wave format structure (16-bit PCM, srate, channels)
-  WAVEFORMATEX waveFormat;
-  ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
-  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
-  waveFormat.nChannels = channels;
-  waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
+  char *buffer;
+  long bufferBytes;
+
+  if ( buffersRolling == false ) {
+    if ( stream_.mode == DUPLEX ) {
+      //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+
+      // It takes a while for the devices to get rolling. As a result,
+      // there's no guarantee that the capture and write device pointers
+      // will move in lockstep.  Wait here for both devices to start
+      // rolling, and then set our buffer pointers accordingly.
+      // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
+      // bytes later than the write buffer.
+
+      // Stub: a serious risk of having a pre-emptive scheduling round
+      // take place between the two GetCurrentPosition calls... but I'm
+      // really not sure how to solve the problem.  Temporarily boost to
+      // Realtime priority, maybe; but I'm not sure what priority the
+      // DirectSound service threads run at. We *should* be roughly
+      // within a ms or so of correct.
+
+      LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+
+      DWORD startSafeWritePointer, startSafeReadPointer;
+
+      result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
+      }
+      result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );
+      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( NULL, &safeWritePointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+          errorText_ = errorStream_.str();
+          error( RtError::SYSTEM_ERROR );
+        }
+        result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          error( RtError::SYSTEM_ERROR );
+        }
+        if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;
+        Sleep( 1 );
+      }
 
 
-  // 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;
+      //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+
+      handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
+      if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
+      handle->bufferPointer[1] = safeReadPointer;
     }
     }
-    else {
-      if ( devices[device].nativeFormats & RTAUDIO_SINT16 )
-        waveFormat.wBitsPerSample = 16;
-      else
-        waveFormat.wBitsPerSample = 8;
+    else if ( stream_.mode == OUTPUT ) {
+
+      // Set the proper nextWritePosition after initial startup.
+      LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      result = dsWriteBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
+      }
+      handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
+      if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
     }
     }
+
+    buffersRolling = true;
   }
   }
-  else {
-    sprintf(message, "RtAudio: no reported data formats for DirectSound device (%s).",
-            devices[device].name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    
+    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
+      memset( stream_.userBuffer[0], 0, bufferBytes );
+    }
+
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
+      bufferBytes *= formatBytes( stream_.deviceFormat[0] );
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
+    }
+
+    // No byte swapping necessary in DirectSound implementation.
+
+    // Ahhh ... windoze.  16-bit data is signed but 8-bit data is
+    // unsigned.  So, we need to convert our signed 8-bit data here to
+    // unsigned.
+    if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
+      for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
+
+    DWORD dsBufferSize = handle->dsBufferSize[0];
+    nextWritePointer = handle->bufferPointer[0];
+
+    DWORD endWrite, leadPointer;
+    while ( true ) {
+      // Find out where the read and "safe write" pointers are.
+      result = dsBuffer->GetCurrentPosition( &currentWritePointer, &safeWritePointer );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( RtError::SYSTEM_ERROR );
+      }
+
+      // We will copy our output buffer into the region between
+      // safeWritePointer and leadPointer.  If leadPointer is not
+      // beyond the next endWrite position, wait until it is.
+      leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];
+      //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;
+      if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;
+      if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset
+      endWrite = nextWritePointer + bufferBytes;
+
+      // Check whether the entire write region is behind the play pointer.
+      if ( leadPointer >= endWrite ) break;
+
+      // If we are here, then we must wait until the leadPointer advances
+      // beyond the end of our next write region. We use the
+      // Sleep() function to suspend operation until that happens.
+      double millis = ( endWrite - leadPointer ) * 1000.0;
+      millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
+      if ( millis < 1.0 ) millis = 1.0;
+      Sleep( (DWORD) millis );
+    }
+
+    if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )
+         || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { 
+      // We've strayed into the forbidden zone ... resync the read pointer.
+      handle->xrun[0] = true;
+      nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;
+      if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;
+      handle->bufferPointer[0] = nextWritePointer;
+      endWrite = nextWritePointer + bufferBytes;
+    }
+
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_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 );
+    }
+    nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    handle->bufferPointer[0] = nextWritePointer;
+
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
 
   }
 
-  waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
-  waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
 
-  enum_info dsinfo;
-  strncpy( dsinfo.name, devices[device].name, 64 );
-  dsinfo.isValid = false;
-  if ( mode == OUTPUT ) {
+    // 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 );
+    }
 
 
-    if ( devices[device].maxOutputChannels < channels )
-      return FAILURE;
+    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    long nextReadPointer = handle->bufferPointer[1];
+    DWORD dsBufferSize = handle->dsBufferSize[1];
 
 
-    // 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);
-      return FAILURE;
+    // Find out where the write and "safe read" pointers are.
+    result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
+    }
+
+    if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
+    DWORD endRead = nextReadPointer + bufferBytes;
+
+    // Handling depends on whether we are INPUT or DUPLEX. 
+    // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
+    // then a wait here will drag the write pointers into the forbidden zone.
+    // 
+    // In DUPLEX mode, rather than wait, we will back off the read pointer until 
+    // it's in a safe position. This causes dropouts, but it seems to be the only 
+    // practical way to sync up the read and write pointers reliably, given the 
+    // the very complex relationship between phase and increment of the read and write 
+    // pointers.
+    //
+    // In order to minimize audible dropouts in DUPLEX mode, we will
+    // provide a pre-roll period of 0.5 seconds in which we return
+    // zeros from the read buffer while the pointers sync up.
+
+    if ( stream_.mode == DUPLEX ) {
+      if ( safeReadPointer < endRead ) {
+        if ( duplexPrerollBytes <= 0 ) {
+          // Pre-roll time over. Be more agressive.
+          int adjustment = endRead-safeReadPointer;
+
+          handle->xrun[1] = true;
+          // Two cases:
+          //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
+          //     and perform fine adjustments later.
+          //   - small adjustments: back off by twice as much.
+          if ( adjustment >= 2*bufferBytes )
+            nextReadPointer = safeReadPointer-2*bufferBytes;
+          else
+            nextReadPointer = safeReadPointer-bufferBytes-adjustment;
+
+          if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
+
+        }
+        else {
+          // In pre=roll time. Just do it.
+          nextReadPointer = safeReadPointer - bufferBytes;
+          while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
+        }
+        endRead = nextReadPointer + bufferBytes;
+      }
     }
     }
+    else { // mode == INPUT
+      while ( safeReadPointer < endRead ) {
+        // See comments for playback.
+        double millis = (endRead - safeReadPointer) * 1000.0;
+        millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
+        if ( millis < 1.0 ) millis = 1.0;
+        Sleep( (DWORD) millis );
 
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message, "RtAudio: DS output device (%s) id not found!", devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+        // Wake up and find out where we are now.
+        result = dsBuffer->GetCurrentPosition( &currentReadPointer, &safeReadPointer );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          error( RtError::SYSTEM_ERROR );
+        }
+      
+        if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
+      }
     }
 
     }
 
-    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;
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
     }
 
     }
 
-    // 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);
-      return FAILURE;
+    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;
     }
 
     }
 
-    // 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);
-    bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
-    // 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);
-      return FAILURE;
+    // Update our buffer offset and unlock sound buffer
+    nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( RtError::SYSTEM_ERROR );
     }
     }
+    handle->bufferPointer[1] = nextReadPointer;
 
 
-    // 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);
-      return FAILURE;
+    // No byte swapping necessary in DirectSound implementation.
+
+    // If necessary, convert 8-bit data from unsigned to signed.
+    if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
+      for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
+
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+  }
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  RtApi::tickStreamTime();
+}
+
+// Definitions for utility functions and callbacks
+// specific to the DirectSound implementation.
+
+extern "C" unsigned __stdcall callbackHandler( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiDs *object = (RtApiDs *) info->object;
+  bool* isRunning = &info->isRunning;
+
+  while ( *isRunning == true ) {
+    object->callbackEvent();
+  }
+
+  _endthreadex( 0 );
+  return 0;
+}
+
+#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 )
+{
+  bool *isInput = (bool *) lpContext;
+
+  HRESULT hr;
+  bool validDevice = false;
+  if ( *isInput == true ) {
+    DSCCAPS caps;
+    LPDIRECTSOUNDCAPTURE object;
+
+    hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return TRUE;
+
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
+        validDevice = true;
     }
     }
+    object->Release();
+  }
+  else {
+    DSCAPS caps;
+    LPDIRECTSOUND object;
+    hr = DirectSoundCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return TRUE;
 
 
-    // Setup the secondary DS buffer description.
-    buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
-    ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSBUFFERDESC);
-    bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
-                                  DSBCAPS_GETCURRENTPOSITION2 |
-                                  DSBCAPS_LOCHARDWARE );  // Force hardware mixing
-    bufferDescription.dwBufferBytes = buffer_size;
-    bufferDescription.lpwfxFormat = &waveFormat;
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
+        validDevice = true;
+    }
+    object->Release();
+  }
 
 
-    // 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) ) {
-      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);
-        return FAILURE;
+  // If good device, then save its name and guid.
+  std::string name = convertTChar( description );
+  if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
+    name = "Default Device";
+  if ( validDevice ) {
+    for ( unsigned int i=0; i<dsDevices.size(); i++ ) {
+      if ( dsDevices[i].name == name ) {
+        dsDevices[i].found = true;
+        if ( *isInput ) {
+          dsDevices[i].id[1] = lpguid;
+          dsDevices[i].validId[1] = true;
+        }
+        else {
+          dsDevices[i].id[0] = lpguid;
+          dsDevices[i].validId[0] = true;
+        }
+        return TRUE;
       }
     }
 
       }
     }
 
-    // Get the buffer size ... might be different from what we specified.
-    DSBCAPS dsbcaps;
-    dsbcaps.dwSize = sizeof(DSBCAPS);
-    buffer->GetCaps(&dsbcaps);
-    buffer_size = dsbcaps.dwBufferBytes;
+    DsDevice device;
+    device.name = name;
+    device.found = true;
+    if ( *isInput ) {
+      device.id[1] = lpguid;
+      device.validId[1] = true;
+    }
+    else {
+      device.id[0] = lpguid;
+      device.validId[0] = true;
+    }
+    dsDevices.push_back( device );
+  }
 
 
-    // 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);
-      return FAILURE;
+  return TRUE;
+}
+
+static const char* getErrorString( int code )
+{
+  switch ( code ) {
+
+  case DSERR_ALLOCATED:
+    return "Already allocated";
+
+  case DSERR_CONTROLUNAVAIL:
+    return "Control unavailable";
+
+  case DSERR_INVALIDPARAM:
+    return "Invalid parameter";
+
+  case DSERR_INVALIDCALL:
+    return "Invalid call";
+
+  case DSERR_GENERIC:
+    return "Generic error";
+
+  case DSERR_PRIOLEVELNEEDED:
+    return "Priority level needed";
+
+  case DSERR_OUTOFMEMORY:
+    return "Out of memory";
+
+  case DSERR_BADFORMAT:
+    return "The sample rate or the channel format is not supported";
+
+  case DSERR_UNSUPPORTED:
+    return "Not supported";
+
+  case DSERR_NODRIVER:
+    return "No driver";
+
+  case DSERR_ALREADYINITIALIZED:
+    return "Already initialized";
+
+  case DSERR_NOAGGREGATION:
+    return "No aggregation";
+
+  case DSERR_BUFFERLOST:
+    return "Buffer lost";
+
+  case DSERR_OTHERAPPHASPRIO:
+    return "Another application already has priority";
+
+  case DSERR_UNINITIALIZED:
+    return "Uninitialized";
+
+  default:
+    return "DirectSound unknown error";
+  }
+}
+//******************** End of __WINDOWS_DS__ *********************//
+#endif
+
+
+#if defined(__LINUX_ALSA__)
+
+#include <alsa/asoundlib.h>
+#include <unistd.h>
+
+  // A structure to hold various information related to the ALSA API
+  // implementation.
+struct AlsaHandle {
+  snd_pcm_t *handles[2];
+  bool synchronized;
+  bool xrun[2];
+  pthread_cond_t runnable;
+
+  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 );
+  }
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
+    error( RtError::INVALID_USE );
+  }
 
 
-    // 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);
-      return FAILURE;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
+  }
+
+ foundDevice:
+
+  // If a stream is already open, we cannot probe the stream devices.
+  // Thus, use the saved results.
+  if ( stream_.state != STREAM_CLOSED &&
+       ( stream_.device[0] == device || stream_.device[1] == device ) ) {
+    if ( device >= devices_.size() ) {
+      errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
+      error( RtError::WARNING );
+      return info;
     }
     }
+    return devices_[ device ];
+  }
+
+  int openMode = SND_PCM_ASYNC;
+  snd_pcm_stream_t stream;
+  snd_pcm_info_t *pcminfo;
+  snd_pcm_info_alloca( &pcminfo );
+  snd_pcm_t *phandle;
+  snd_pcm_hw_params_t *params;
+  snd_pcm_hw_params_alloca( &params );
+
+  // First try for playback
+  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;
 
 
-    stream->handle[0].object = (void *) object;
-    stream->handle[0].buffer = (void *) buffer;
-    stream->nDeviceChannels[0] = channels;
+  // 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;
   }
 
   }
 
-  if ( mode == INPUT ) {
+  // 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;
 
 
-    if ( devices[device].maxInputChannels < channels )
-      return FAILURE;
+  // That's all ... close the device and return
+  snd_pcm_close( phandle );
+  info.probed = true;
+  return info;
+}
 
 
-    // Enumerate through input devices to find the id (if it exists).
-    result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Error performing input device id enumeration: %s.",
-              getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+void RtApiAlsa :: saveDeviceInfo( void )
+{
+  devices_.clear();
 
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message, "RtAudio: DS input device (%s) id not found!", devices[device].name);
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
 
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUNDCAPTURE  object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer;
-    DSCBUFFERDESC bufferDescription;
+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 )
 
 
-    result = DirectSoundCaptureCreate( id, &object, NULL );
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+{
+#if defined(__RTAUDIO_DEBUG__)
+  snd_output_t *out;
+  snd_output_stdio_attach(&out, stderr, 0);
+#endif
 
 
-    // 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;
+  // I'm not using the "plug" interface ... too much inconsistent behavior.
 
 
-    // Create the capture buffer.
-    result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to create DS capture buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *chandle;
 
 
-    // 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);
+  // 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;
     }
       return FAILURE;
     }
-
-    // Zero the buffer
-    ZeroMemory(audioPtr, dataLen);
-
-    // Unlock the buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
-              devices[device].name, getErrorString(result));
-      error(RtError::WARNING);
-      return FAILURE;
+    subdevice = -1;
+    while( 1 ) {
+      result = snd_ctl_pcm_next_device( chandle, &subdevice );
+      if ( result < 0 ) break;
+      if ( subdevice < 0 ) break;
+      if ( nDevices == device ) {
+        sprintf( name, "hw:%d,%d", card, subdevice );
+        snd_ctl_close( chandle );
+        goto foundDevice;
+      }
+      nDevices++;
     }
     }
-
-    stream->handle[1].object = (void *) object;
-    stream->handle[1].buffer = (void *) buffer;
-    stream->nDeviceChannels[1] = channels;
+    snd_ctl_close( chandle );
+    snd_card_next( &card );
   }
 
   }
 
-  stream->userFormat = format;
-  if ( waveFormat.wBitsPerSample == 8 )
-    stream->deviceFormat[mode] = RTAUDIO_SINT8;
-  else
-    stream->deviceFormat[mode] = RTAUDIO_SINT16;
-  stream->nUserChannels[mode] = channels;
-  *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8);
-  stream->bufferSize = *bufferSize;
-
-  // Set flags for buffer conversion
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-  if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
-    stream->doConvertBuffer[mode] = true;
+  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;
+  }
 
 
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
+  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;
+  }
 
 
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
+ foundDevice:
 
 
-    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;
-  }
+  // The getDeviceInfo() function will not work for a device that is
+  // already open.  Thus, we'll probe the system before opening a
+  // stream and save the results for use by getDeviceInfo().
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
+    this->saveDeviceInfo();
 
 
-  if ( stream->doConvertBuffer[mode] ) {
+  snd_pcm_stream_t stream;
+  if ( mode == OUTPUT )
+    stream = SND_PCM_STREAM_PLAYBACK;
+  else
+    stream = SND_PCM_STREAM_CAPTURE;
 
 
-    long buffer_bytes;
-    bool makeBuffer = true;
+  snd_pcm_t *phandle;
+  int openMode = SND_PCM_ASYNC;
+  result = snd_pcm_open( &phandle, name, stream, openMode );
+  if ( result < 0 ) {
     if ( mode == OUTPUT )
     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;
-    }
+      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;
   }
 
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
-    // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->nBuffers = nBuffers;
-  stream->sampleRate = sampleRate;
+  // 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;
+  }
 
 
-  return SUCCESS;
+#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
 
 
- 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;
+  // 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;
     }
     }
-    object->Release();
-    stream->handle[0].object = NULL;
+    else
+      stream_.deviceInterleaved[mode] = false;
   }
   }
-  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;
+  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;
     }
     }
-    object->Release();
-    stream->handle[1].object = NULL;
-  }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+    else
+      stream_.deviceInterleaved[mode] =  true;
   }
   }
-  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 ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    if (stream->state == STREAM_RUNNING)
-      stopStream( streamId );
+  // 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;
+  }
 
 
-    MUTEX_LOCK(&stream->mutex);
+  // 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;
+  }
 
 
-    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;
+  deviceFormat = SND_PCM_FORMAT_FLOAT;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    goto setFormat;
+  }
 
 
-    MUTEX_UNLOCK(&stream->mutex);
+  deviceFormat = SND_PCM_FORMAT_S32;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    goto setFormat;
   }
   }
-}
 
 
-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;
+  deviceFormat = SND_PCM_FORMAT_S24;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    goto setFormat;
   }
 
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  deviceFormat = SND_PCM_FORMAT_S16;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    goto setFormat;
+  }
 
 
-  if (stream->callbackInfo.usingCallback) {
-    stream->callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream->callbackInfo.thread );
+  deviceFormat = SND_PCM_FORMAT_S8;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+    goto setFormat;
   }
 
   }
 
-  DeleteCriticalSection(&stream->mutex);
+  // 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;
 
 
-  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();
-    }
-    object->Release();
+ 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;
   }
 
   }
 
-  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();
+  // 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;
     }
     }
-    object->Release();
   }
 
   }
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
-
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  // 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;
+  }
 
 
-  free(stream);
-  streams.erase(streamId);
-}
+  // 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;
+  }
 
 
-void RtAudio :: startStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  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;
 
 
-  MUTEX_LOCK(&stream->mutex);
+  // 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;
+  }
 
 
-  if (stream->state == STREAM_RUNNING)
-    goto unlock;
+  // Set the buffer number, which in ALSA is referred to as the "period".
+  int totalSize, dir = 0;
+  unsigned int periods = 0;
+  if ( options ) periods = options->numberOfBuffers;
+  totalSize = *bufferSize * periods;
 
 
-  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);
-    }
+  // 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 (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 ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
+  else periods = totalSize / *bufferSize;
+  // 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;
   }
   }
-  stream->state = STREAM_RUNNING;
-
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
 
 
-void RtAudio :: stopStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // 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;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  stream_.bufferSize = *bufferSize;
 
 
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
-    return;
+  // 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;
   }
 
   }
 
-  // There is no specific DirectSound API call to "drain" a buffer
-  // before stopping.  We can hack this for playback by writing zeroes
-  // for another bufferSize * nBuffers frames.  For capture, the
-  // concept is less clear so we'll repeat what we do in the
-  // abortStream() case.
-  HRESULT result;
-  DWORD dsBufferSize;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
 
 
-    DWORD currentPos, safePos;
-    long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
-    buffer_bytes *= formatBytes(stream->deviceFormat[0]);
+  // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
+  snd_pcm_sw_params_t *sw_params = NULL;
+  snd_pcm_sw_params_alloca( &sw_params );
+  snd_pcm_sw_params_current( phandle, sw_params );
+  snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
+  snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
+  snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
+
+  // The following two settings were suggested by Theo Veenker
+  //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
+  //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
+
+  // here are two options for a fix
+  //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
+  snd_pcm_uframes_t val;
+  snd_pcm_sw_params_get_boundary( sw_params, &val );
+  snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
+
+  result = snd_pcm_sw_params( phandle, sw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    dsBufferSize = buffer_bytes * stream->nBuffers;
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
+  snd_pcm_sw_params_dump( sw_params, out );
+#endif
 
 
-    // Write zeroes for nBuffer counts.
-    for (int i=0; i<stream->nBuffers; i++) {
+  // 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;
+    }
 
 
-      // 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 ( pthread_cond_init( &apiInfo->runnable, NULL ) ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
 
 
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      DWORD endWrite = nextWritePos + buffer_bytes;
+    stream_.apiHandle = (void *) apiInfo;
+    apiInfo->handles[0] = 0;
+    apiInfo->handles[1] = 0;
+  }
+  else {
+    apiInfo = (AlsaHandle *) stream_.apiHandle;
+  }
+  apiInfo->handles[mode] = phandle;
 
 
-      // 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 );
+  // 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;
+  }
 
 
-        // Wake up, find out where we are now
-        result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-        if ( FAILED(result) ) {
-          sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
-                  devices[stream->device[0]].name, getErrorString(result));
-          error(RtError::DRIVER_ERROR);
-        }
-        if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      }
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-      // 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);
+    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;
       }
       }
+    }
 
 
-      // Zero the free space
-      ZeroMemory(buffer1, bufferSize1);
-      if (buffer2 != NULL) ZeroMemory(buffer2, bufferSize2);
-
-      // 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);
+    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;
       }
       }
-      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;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    buffer1 = NULL;
-    bufferSize1 = 0;
+  stream_.sampleRate = sampleRate;
+  stream_.nBuffers = periods;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
 
 
-    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);
-    }
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+  // 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 (optional).  The higher priority will only take affect
+    // if the program is run as root or suid. Note, under Linux
+    // processes with CAP_SYS_NICE privilege, a user can change
+    // scheduling policy and priority (thus need not be root). See
+    // POSIX "capabilities".
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      struct sched_param param;
+      int priority = options->priority;
+      int min = sched_get_priority_min( SCHED_RR );
+      int max = sched_get_priority_max( SCHED_RR );
+      if ( priority < min ) priority = min;
+      else if ( priority > max ) priority = max;
+      param.sched_priority = priority;
+      pthread_attr_setschedparam( &attr, &param );
+      pthread_attr_setschedpolicy( &attr, SCHED_RR );
+    }
+    else
+      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
 
 
-    // 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);
+    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;
     }
     }
+  }
 
 
-    // Zero the DS buffer
-    ZeroMemory(buffer1, bufferSize1);
+  return SUCCESS;
 
 
-    // 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);
+ error:
+  if ( apiInfo ) {
+    pthread_cond_destroy( &apiInfo->runnable );
+    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 we start recording again, we must begin at beginning of buffer.
-    stream->handle[1].bufferPointer = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
-  MUTEX_UNLOCK(&stream->mutex);
+  return FAILURE;
 }
 
 }
 
-void RtAudio :: abortStream(int streamId)
+void RtApiAlsa :: closeStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  MUTEX_LOCK(&stream->mutex);
-
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
-
-  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);
-    }
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0];
-    dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  stream_.callbackInfo.isRunning = false;
+  MUTEX_LOCK( &stream_.mutex );
+  if ( stream_.state == STREAM_STOPPED )
+    pthread_cond_signal( &apiInfo->runnable );
+  MUTEX_UNLOCK( &stream_.mutex );
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",
-              devices[stream->device[0]].name, getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  if ( 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] );
+  }
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+  if ( apiInfo ) {
+    pthread_cond_destroy( &apiInfo->runnable );
+    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;
+  }
 
 
-    // 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);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
     }
-
-    // If we start playing again, we must begin at beginning of buffer.
-    stream->handle[0].bufferPointer = 0;
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
-    audioPtr = NULL;
-    dataLen = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-    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);
-    }
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
+}
 
 
-    dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
+void RtApiAlsa :: startStream()
+{
+  // This method calls snd_pcm_prepare if the device isn't already in that state.
 
 
-    // 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);
-    }
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    // 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);
+  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 );
 
 
-int RtAudio :: streamWillBlock(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  pthread_cond_signal( &apiInfo->runnable );
 
 
-  MUTEX_LOCK(&stream->mutex);
+  if ( result >= 0 ) return;
+  error( RtError::SYSTEM_ERROR );
+}
 
 
-  int channels;
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+void RtApiAlsa :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  HRESULT result;
-  DWORD currentPos, safePos;
-  channels = 1;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  MUTEX_LOCK( &stream_.mutex );
 
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
-    UINT nextWritePos = stream->handle[0].bufferPointer;
-    channels = stream->nDeviceChannels[0];
-    DWORD dsBufferSize = stream->bufferSize * channels;
-    dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-    // 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:
+  stream_.state = STREAM_STOPPED;
+  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
+  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]);
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
+  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;
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result >= 0 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiAlsa :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_LOCK( &stream_.mutex );
+    pthread_cond_wait( &apiInfo->runnable, &stream_.mutex );
+    if ( stream_.state != STREAM_RUNNING ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      return;
+    }
+    MUTEX_UNLOCK( &stream_.mutex );
+  }
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) 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);
-  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  int doStopStream = 0;
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    apiInfo->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    apiInfo->xrun[1] = false;
+  }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
 
 
-  // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream->mutex);
+  if ( doStopStream == 2 ) {
+    abortStream();
     return;
   }
 
     return;
   }
 
-  HRESULT result;
-  DWORD currentPos, safePos;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
+  MUTEX_LOCK( &stream_.mutex );
+
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
+
+  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);
-    }
-
-    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);
+    // Read samples from device in interleaved/non-interleaved format.
+    if ( stream_.deviceInterleaved[1] )
+      result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
+    else {
+      void *bufs[channels];
+      size_t offset = stream_.bufferSize * formatBytes( format );
+      for ( int i=0; i<channels; i++ )
+        bufs[i] = (void *) (buffer + (i * offset));
+      result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
+    }
+
+    if ( result < (int) stream_.bufferSize ) {
+      // Either an error or overrun occured.
+      if ( result == -EPIPE ) {
+        snd_pcm_state_t state = snd_pcm_state( handle[1] );
+        if ( state == SND_PCM_STATE_XRUN ) {
+          apiInfo->xrun[1] = true;
+          result = snd_pcm_prepare( handle[1] );
+          if ( result < 0 ) {
+            errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
+            errorText_ = errorStream_.str();
+          }
+        }
+        else {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
+        }
+      }
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
       }
       }
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
+      error( RtError::WARNING );
+      goto tryOutput;
     }
 
     }
 
-    // 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) {
+ tryOutput:
 
 
-    // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1];
-      buffer_bytes *= formatBytes(stream->deviceFormat[1]);
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      channels = stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
     }
     else {
     }
     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();
 }
 
 }
 
-// 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;
 
 
-  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 "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;
+  pthread_cond_t runnable;
 
 
-  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;
+  close( mixerfd );
+  return sysinfo.numaudios;
 }
 
 }
 
-static char* getErrorString(int code)
+RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
 {
 {
-       switch (code) {
-
-  case DSERR_ALLOCATED:
-    return "Direct Sound already allocated";
-
-  case DSERR_CONTROLUNAVAIL:
-    return "Direct Sound control unavailable";
-
-  case DSERR_INVALIDPARAM:
-    return "Direct Sound invalid parameter";
-
-  case DSERR_INVALIDCALL:
-    return "Direct Sound invalid call";
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
 
-  case DSERR_GENERIC:
-    return "Direct Sound generic error";
-
-  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;
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
+    return FAILURE;
+  }
 
 
-  outs = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, vls, nDevices, 0, 0);
-  if (outs < 0) {
-    sprintf(message, "RtAudio: AL error getting output devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
   }
 
   }
 
-  for (i=0; i<outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      sprintf(message, "RtAudio: AL error querying output devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
-    strncpy(devices[i].name, name, 32);
-    devices[i].id[0] = vls[i].i;
+  oss_audioinfo ainfo;
+  ainfo.dev = device;
+  result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
+  close( mixerfd );
+  if ( result == -1 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  ins = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &vls[outs], nDevices-outs, 0, 0);
-  if (ins < 0) {
-    sprintf(message, "RtAudio: AL error getting input devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  // Check if device supports input or output
+  if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
+       ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
+    if ( mode == OUTPUT )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  for (i=outs; i<ins+outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      sprintf(message, "RtAudio: AL error querying input devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  int flags = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( mode == OUTPUT )
+    flags |= O_WRONLY;
+  else { // mode == INPUT
+    if (stream_.mode == OUTPUT && stream_.device[0] == device) {
+      // We just set the same device for playback ... close and reopen for duplex (OSS only).
+      close( handle->id[0] );
+      handle->id[0] = 0;
+      if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      // Check that the number previously set channels is the same.
+      if ( stream_.nUserChannels[0] != channels ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      flags |= O_RDWR;
     }
     }
-    strncpy(devices[i].name, name, 32);
-    devices[i].id[1] = vls[i].i;
+    else
+      flags |= O_RDONLY;
   }
 
   }
 
-  delete [] vls;
-
-  return;
-}
+  // Set exclusive access if specified.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
 
 
-int RtAudio :: getDefaultInputDevice(void)
-{
-  ALvalue value;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message, "RtAudio: AL error getting default input device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
-  }
-  else {
-    for ( int i=0; i<nDevices; i++ )
-      if ( devices[i].id[1] == value.i ) return i;
+  // Try to open the device.
+  int fd;
+  fd = open( ainfo.devnode, flags, 0 );
+  if ( fd == -1 ) {
+    if ( errno == EBUSY )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  return 0;
-}
+  // For duplex operation, specifically set this mode (this doesn't seem to work).
+  /*
+    if ( flags | O_RDWR ) {
+    result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
+    if ( result == -1) {
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+    }
+    }
+  */
 
 
-int RtAudio :: getDefaultOutputDevice(void)
-{
-  ALvalue value;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message, "RtAudio: AL error getting default output device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
-  }
-  else {
-    for ( int i=0; i<nDevices; i++ )
-      if ( devices[i].id[0] == value.i ) return i;
+  // Check the device channel support.
+  stream_.nUserChannels[mode] = channels;
+  if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
   }
 
-  return 0;
-}
-
-void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
-{
-  int resource, result, i;
-  ALvalue value;
-  ALparamInfo pinfo;
+  // Set the number of channels.
+  int deviceChannels = channels + firstChannel;
+  result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
+  if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.nDeviceChannels[mode] = deviceChannels;
 
 
-  // Get output resource ID if it exists.
-  resource = info->id[0];
-  if (resource > 0) {
+  // Get the data format mask
+  int mask;
+  result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
 
-    // Probe output device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  // Determine how to set the device format.
+  stream_.userFormat = format;
+  int deviceFormat = -1;
+  stream_.doByteSwap[mode] = false;
+  if ( format == RTAUDIO_SINT8 ) {
+    if ( mask & AFMT_S8 ) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
     }
-    else {
-      info->maxOutputChannels = value.i;
-      info->minOutputChannels = 1;
+  }
+  else if ( format == RTAUDIO_SINT16 ) {
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
     }
-
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
     }
     }
-    else {
-      info->nSampleRates = 0;
-      for (i=0; i<MAX_SAMPLE_RATES; i++) {
-        if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
-          info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-          info->nSampleRates++;
-        }
-      }
+  }
+  else if ( format == RTAUDIO_SINT24 ) {
+    if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    }
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
     }
     }
-
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RTAUDIO_FORMAT) 51;
   }
   }
-
-  // Now get input resource ID if it exists.
-  resource = info->id[1];
-  if (resource > 0) {
-
-    // Probe input device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  else if ( format == RTAUDIO_SINT32 ) {
+    if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
     }
     }
-    else {
-      info->maxInputChannels = value.i;
-      info->minInputChannels = 1;
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
     }
     }
+  }
 
 
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
-              info->name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+  if ( deviceFormat == -1 ) {
+    // The user requested format is not natively supported by the device.
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
     }
-    else {
-      // In the case of the default device, these values will
-      // overwrite the rates determined for the output device.  Since
-      // the input device is most likely to be more limited than the
-      // output device, this is ok.
-      info->nSampleRates = 0;
-      for (i=0; i<MAX_SAMPLE_RATES; i++) {
-        if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
-          info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
-          info->nSampleRates++;
-        }
-      }
+    else if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    }
+    else if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    }
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S8) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
     }
-
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RTAUDIO_FORMAT) 51;
   }
 
   }
 
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
-    return;
-  if ( info->nSampleRates == 0 )
-    return;
-
-  // Determine duplex status.
-  if (info->maxInputChannels < info->maxOutputChannels)
-    info->maxDuplexChannels = info->maxInputChannels;
-  else
-    info->maxDuplexChannels = info->maxOutputChannels;
-  if (info->minInputChannels < info->minOutputChannels)
-    info->minDuplexChannels = info->minInputChannels;
-  else
-    info->minDuplexChannels = info->minOutputChannels;
-
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
-
-  info->probed = true;
-
-  return;
-}
-
-bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
-                                STREAM_MODE mode, int channels, 
-                                int sampleRate, RTAUDIO_FORMAT format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  int result, resource, nBuffers;
-  ALconfig al_config;
-  ALport port;
-  ALpv pvs[2];
-
-  // Get a new ALconfig structure.
-  al_config = alNewConfig();
-  if ( !al_config ) {
-    sprintf(message,"RtAudio: can't get AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    // This really shouldn't happen ...
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Set the channels.
-  result = alSetChannels(al_config, channels);
-  if ( result < 0 ) {
-    sprintf(message,"RtAudio: can't set %d channels in AL config: %s.",
-            channels, alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  // Set the data format.
+  int temp = deviceFormat;
+  result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
+  if ( result == -1 || deviceFormat != temp ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  // Attempt to set the queue size.  The al API doesn't provide a
-  // means for querying the minimum/maximum buffer size of a device,
-  // so if the specified size doesn't work, take whatever the
-  // al_config structure returns.
-  if ( numberOfBuffers < 1 )
-    nBuffers = 1;
-  else
-    nBuffers = numberOfBuffers;
-  long buffer_size = *bufferSize * nBuffers;
-  result = alSetQueueSize(al_config, buffer_size); // in sample frames
-  if ( result < 0 ) {
-    // Get the buffer size specified by the al_config and try that.
-    buffer_size = alGetQueueSize(al_config);
-    result = alSetQueueSize(al_config, buffer_size);
-    if ( result < 0 ) {
-      sprintf(message,"RtAudio: can't set buffer size (%ld) in AL config: %s.",
-              buffer_size, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
-    *bufferSize = buffer_size / nBuffers;
+  // Attempt to set the buffer size.  According to OSS, the minimum
+  // number of buffers is two.  The supposed minimum buffer size is 16
+  // bytes, so that will be our lower bound.  The argument to this
+  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
+  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
+  // We'll check the actual value used near the end of the setup
+  // procedure.
+  int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
+  if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
+  int buffers = 0;
+  if ( options ) buffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
+  if ( buffers < 2 ) buffers = 3;
+  temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
+  result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
   }
+  stream_.nBuffers = buffers;
 
 
-  // Set the data format.
-  stream->userFormat = format;
-  stream->deviceFormat[mode] = format;
-  if (format == RTAUDIO_SINT8) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_8);
-  }
-  else if (format == RTAUDIO_SINT16) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_16);
-  }
-  else if (format == RTAUDIO_SINT24) {
-    // Our 24-bit format assumes the upper 3 bytes of a 4 byte word.
-    // The AL library uses the lower 3 bytes, so we'll need to do our
-    // own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-  }
-  else if (format == RTAUDIO_SINT32) {
-    // The AL library doesn't seem to support the 32-bit integer
-    // format, so we'll need to do our own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
-  }
-  else if (format == RTAUDIO_FLOAT32)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-  else if (format == RTAUDIO_FLOAT64)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE);
+  // Save buffer size (in sample frames).
+  *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
+  stream_.bufferSize = *bufferSize;
 
 
+  // Set the sample rate.
+  int srate = sampleRate;
+  result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
   if ( result == -1 ) {
   if ( result == -1 ) {
-    sprintf(message,"RtAudio: AL error setting sample format in AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
     return FAILURE;
   }
 
-  if (mode == OUTPUT) {
+  // Verify the sample rate setup worked.
+  if ( abs( srate - sampleRate ) > 100 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.sampleRate = sampleRate;
 
 
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_OUTPUT;
-    else
-      resource = devices[device].id[0];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
-              devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
+    // We're doing duplex setup here.
+    stream_.deviceFormat[0] = stream_.deviceFormat[1];
+    stream_.nDeviceChannels[0] = deviceChannels;
+  }
 
 
-    // Open the port.
-    port = alOpenPort("RtAudio Output Port", "w", al_config);
-    if( !port ) {
-      sprintf(message,"RtAudio: AL error opening output port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+  // Set interleaving parameters.
+  stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] =  true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
+    stream_.userInterleaved = false;
 
 
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
-              sampleRate, devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+  // Set flags for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate the stream handles if necessary and then save.
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new OssHandle;
     }
     }
-  }
-  else { // mode == INPUT
-
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_INPUT;
-    else
-      resource = devices[device].id[1];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
-              devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
+      goto error;
     }
 
     }
 
-    // Open the port.
-    port = alOpenPort("RtAudio Output Port", "r", al_config);
-    if( !port ) {
-      sprintf(message,"RtAudio: AL error opening input port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
+    if ( pthread_cond_init( &handle->runnable, NULL ) ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
     }
 
     }
 
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
-              sampleRate, devices[device].name, alGetErrorString(oserror()));
-      error(RtError::WARNING);
-      return FAILURE;
-    }
+    stream_.apiHandle = (void *) handle;
   }
   }
+  else {
+    handle = (OssHandle *) stream_.apiHandle;
+  }
+  handle->id[mode] = fd;
 
 
-  alFreeConfig(al_config);
-
-  stream->nUserChannels[mode] = channels;
-  stream->nDeviceChannels[mode] = channels;
-
-  // Set handle and flags for buffer conversion
-  stream->handle[mode] = port;
-  stream->doConvertBuffer[mode] = false;
-  if (stream->userFormat != stream->deviceFormat[mode])
-    stream->doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream->nUserChannels[0] >= stream->nUserChannels[1])
-      buffer_bytes = stream->nUserChannels[0];
-    else
-      buffer_bytes = stream->nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
-    if (stream->userBuffer) free(stream->userBuffer);
-    stream->userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream->userBuffer == NULL)
-      goto memory_error;
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   }
 
-  if ( stream->doConvertBuffer[mode] ) {
+  if ( stream_.doConvertBuffer[mode] ) {
 
 
-    long buffer_bytes;
     bool makeBuffer = true;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
-      if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
-        long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream->deviceBuffer) free(stream->deviceBuffer);
-      stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream->deviceBuffer == NULL)
-        goto memory_error;
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
     }
   }
 
-  stream->device[mode] = device;
-  stream->state = STREAM_STOPPED;
-  if ( stream->mode == OUTPUT && mode == INPUT )
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup thread if necessary.
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
     // We had already set up an output stream.
     // We had already set up an output stream.
-    stream->mode = DUPLEX;
-  else
-    stream->mode = mode;
-  stream->nBuffers = nBuffers;
-  stream->bufferSize = *bufferSize;
-  stream->sampleRate = sampleRate;
+    stream_.mode = DUPLEX;
+    if ( stream_.device[0] == device ) handle->id[0] = fd;
+  }
+  else {
+    stream_.mode = mode;
+
+    // Setup callback thread.
+    stream_.callbackInfo.object = (void *) this;
+
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority.  The higher priority will only take affect if the
+    // program is run as root or suid.
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
+      struct sched_param param;
+      int priority = options->priority;
+      int min = sched_get_priority_min( SCHED_RR );
+      int max = sched_get_priority_max( SCHED_RR );
+      if ( priority < min ) priority = min;
+      else if ( priority > max ) priority = max;
+      param.sched_priority = priority;
+      pthread_attr_setschedparam( &attr, &param );
+      pthread_attr_setschedpolicy( &attr, SCHED_RR );
+    }
+    else
+      pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
+
+    stream_.callbackInfo.isRunning = true;
+    result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
+    pthread_attr_destroy( &attr );
+    if ( result ) {
+      stream_.callbackInfo.isRunning = false;
+      errorText_ = "RtApiOss::error creating callback thread!";
+      goto error;
+    }
+  }
 
   return SUCCESS;
 
 
   return SUCCESS;
 
- memory_error:
-  if (stream->handle[0]) {
-    alClosePort(stream->handle[0]);
-    stream->handle[0] = 0;
+ error:
+  if ( handle ) {
+    pthread_cond_destroy( &handle->runnable );
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
+    stream_.apiHandle = 0;
   }
   }
-  if (stream->handle[1]) {
-    alClosePort(stream->handle[1]);
-    stream->handle[1] = 0;
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
   }
-  if (stream->userBuffer) {
-    free(stream->userBuffer);
-    stream->userBuffer = 0;
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
   }
-  sprintf(message, "RtAudio: ALSA error allocating buffer memory for device (%s).",
-          devices[device].name);
-  error(RtError::WARNING);
+
   return FAILURE;
 }
 
   return FAILURE;
 }
 
-void RtAudio :: closeStream(int streamId)
+void RtApiOss :: closeStream()
 {
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::closeStream(): no open stream to close!";
+    error( RtError::WARNING );
     return;
   }
 
     return;
   }
 
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  stream_.callbackInfo.isRunning = false;
+  MUTEX_LOCK( &stream_.mutex );
+  if ( stream_.state == STREAM_STOPPED )
+    pthread_cond_signal( &handle->runnable );
+  MUTEX_UNLOCK( &stream_.mutex );
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
 
-  if (stream->callbackInfo.usingCallback) {
-    pthread_cancel(stream->callbackInfo.thread);
-    pthread_join(stream->callbackInfo.thread, NULL);
+  if ( stream_.state == STREAM_RUNNING ) {
+    if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+      ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    else
+      ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    stream_.state = STREAM_STOPPED;
   }
 
   }
 
-  pthread_mutex_destroy(&stream->mutex);
-
-  if (stream->handle[0])
-    alClosePort(stream->handle[0]);
-
-  if (stream->handle[1])
-    alClosePort(stream->handle[1]);
+  if ( handle ) {
+    pthread_cond_destroy( &handle->runnable );
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
+    stream_.apiHandle = 0;
+  }
 
 
-  if (stream->userBuffer)
-    free(stream->userBuffer);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
 
-  if (stream->deviceBuffer)
-    free(stream->deviceBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
 
-  free(stream);
-  streams.erase(streamId);
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
 }
 
-void RtAudio :: startStream(int streamId)
+void RtApiOss :: startStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
-
-  if (stream->state == STREAM_RUNNING)
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiOss::startStream(): the stream is already running!";
+    error( 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);
+  stream_.state = STREAM_RUNNING;
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // No need to do anything else here ... OSS automatically starts
+  // when fed samples.
 
 
-  int result;
-  int buffer_size = stream->bufferSize * stream->nBuffers;
+  MUTEX_UNLOCK( &stream_.mutex );
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX)
-    alZeroFrames(stream->handle[0], buffer_size);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  pthread_cond_signal( &handle->runnable );
+}
 
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    result = alDiscardFrames(stream->handle[1], buffer_size);
-    if (result == -1) {
-      sprintf(message, "RtAudio: AL error draining stream device (%s): %s.",
-              devices[stream->device[1]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
+void RtApiOss :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
   }
   }
-  stream->state = STREAM_STOPPED;
 
 
- unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-}
+  MUTEX_LOCK( &stream_.mutex );
 
 
-void RtAudio :: abortStream(int streamId)
-{
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
 
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+    // Flush the output with zeros a few times.
+    char *buffer;
+    int samples;
+    RtAudioFormat format;
+
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
+      format = stream_.userFormat;
+    }
 
 
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+    memset( buffer, 0, samples * formatBytes(format) );
+    for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
+      if ( result == -1 ) {
+        errorText_ = "RtApiOss::stopStream: audio write error.";
+        error( 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);
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result != -1 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-int RtAudio :: streamWillBlock(int streamId)
+void RtApiOss :: abortStream()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
+    error( RtError::WARNING );
+    return;
+  }
 
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
 
-  int frames = 0;
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
 
 
-  int err = 0;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
-    err = alGetFillable(stream->handle[0]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
-              devices[stream->device[0]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
+    handle->triggered = false;
   }
 
   }
 
-  frames = err;
-
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
-    err = alGetFilled(stream->handle[1]);
-    if (err < 0) {
-      sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
-              devices[stream->device[1]].name, alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
+    result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
     }
-    if (frames > err) frames = err;
   }
 
   }
 
-  frames = stream->bufferSize - frames;
-  if (frames < 0) frames = 0;
-
  unlock:
  unlock:
-  MUTEX_UNLOCK(&stream->mutex);
-  return frames;
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result != -1 ) return;
+  error( RtError::SYSTEM_ERROR );
 }
 
 }
 
-void RtAudio :: tickStream(int streamId)
+void RtApiOss :: callbackEvent()
 {
 {
-  RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_LOCK( &stream_.mutex );
+    pthread_cond_wait( &handle->runnable, &stream_.mutex );
+    if ( stream_.state != STREAM_RUNNING ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      return;
+    }
+    MUTEX_UNLOCK( &stream_.mutex );
+  }
 
 
-  int stopStream = 0;
-  if (stream->state == STREAM_STOPPED) {
-    if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( 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;
+  if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    handle->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    handle->xrun[1] = false;
+  }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+  if ( doStopStream == 2 ) {
+    this->abortStream();
+    return;
   }
 
   }
 
-  MUTEX_LOCK(&stream->mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
   // The state might change while waiting on a mutex.
 
   // The state might change while waiting on a mutex.
-  if (stream->state == STREAM_STOPPED)
-    goto unlock;
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
 
+  int result;
   char *buffer;
   char *buffer;
-  int channels;
-  RTAUDIO_FORMAT format;
-  if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
+  int samples;
+  RtAudioFormat format;
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters and do buffer conversion if necessary.
 
     // Setup parameters and do buffer conversion if necessary.
-    if (stream->doConvertBuffer[0]) {
-      convertStreamBuffer(stream, OUTPUT);
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[0];
-      format = stream->deviceFormat[0];
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
     }
     else {
     }
     else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[0];
-      format = stream->userFormat;
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
+      format = stream_.userFormat;
     }
 
     // Do byte swapping if necessary.
     }
 
     // Do byte swapping if necessary.
-    if (stream->doByteSwap[0])
-      byteSwapBuffer(buffer, stream->bufferSize * channels, format);
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer( buffer, samples, format );
 
 
-    // Write interleaved samples to device.
-    alWriteFrames(stream->handle[0], buffer, stream->bufferSize);
+    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) );
+
+    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 );
+      // Continue on to input section.
+    }
   }
 
   }
 
-  if (stream->mode == INPUT || stream->mode == DUPLEX) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters.
 
     // Setup parameters.
-    if (stream->doConvertBuffer[1]) {
-      buffer = stream->deviceBuffer;
-      channels = stream->nDeviceChannels[1];
-      format = stream->deviceFormat[1];
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      samples = stream_.bufferSize * stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
     }
     else {
     }
     else {
-      buffer = stream->userBuffer;
-      channels = stream->nUserChannels[1];
-      format = stream->userFormat;
+      buffer = stream_.userBuffer[1];
+      samples = stream_.bufferSize * stream_.nUserChannels[1];
+      format = stream_.userFormat;
     }
 
     }
 
-    // Read interleaved samples from device.
-    alReadFrames(stream->handle[1], buffer, stream->bufferSize);
+    // Read samples from device.
+    result = read( handle->id[1], buffer, samples * formatBytes(format) );
+
+    if ( result == -1 ) {
+      // We'll assume this is an overrun, though there isn't a
+      // specific means for determining that.
+      handle->xrun[1] = true;
+      errorText_ = "RtApiOss::callbackEvent: audio read error.";
+      error( 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();
 }
 
 }
 
-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;
 
 
-  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);
-  }
+  errorStream_.str(""); // clear the ostringstream
+  if ( type == RtError::WARNING && showWarnings_ == true )
+    std::cerr << '\n' << errorText_ << "\n\n";
+  else
+    throw( RtError( errorText_, type ) );
 }
 
 }
 
-void *RtAudio :: verifyStream(int streamId)
+void RtApi :: verifyStream()
 {
 {
-  // Verify the stream key.
-  if ( streams.find( streamId ) == streams.end() ) {
-    sprintf(message, "RtAudio: invalid stream identifier!");
-    error(RtError::INVALID_STREAM);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApi:: a stream is not open!";
+    error( 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] = 11111;
+    stream_.doConvertBuffer[i] = false;
+    stream_.deviceInterleaved[i] = true;
+    stream_.doByteSwap[i] = false;
+    stream_.nUserChannels[i] = 0;
+    stream_.nDeviceChannels[i] = 0;
+    stream_.channelOffset[i] = 0;
+    stream_.deviceFormat[i] = 0;
+    stream_.latency[i] = 0;
+    stream_.userBuffer[i] = 0;
+    stream_.convertInfo[i].channels = 0;
+    stream_.convertInfo[i].inJump = 0;
+    stream_.convertInfo[i].outJump = 0;
+    stream_.convertInfo[i].inFormat = 0;
+    stream_.convertInfo[i].outFormat = 0;
+    stream_.convertInfo[i].inOffset.clear();
+    stream_.convertInfo[i].outOffset.clear();
+  }
 }
 
 }
 
-int RtAudio :: formatBytes(RTAUDIO_FORMAT format)
+unsigned int RtApi :: formatBytes( RtAudioFormat format )
 {
 {
-  if (format == RTAUDIO_SINT16)
+  if ( format == RTAUDIO_SINT16 )
     return 2;
     return 2;
-  else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
-           format == RTAUDIO_FLOAT32)
+  else if ( format == RTAUDIO_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 );
+      }
     }
   }
     }
   }
+}
 
 
-  if (format_out == RTAUDIO_FLOAT64) {
-    FLOAT64 scale;
-    FLOAT64 *out = (FLOAT64 *)output;
+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_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      scale = 1.0 / 128.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+  // Clear our device buffer when in/out duplex device channels are different
+  if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
+       ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
+    memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
+
+  int j;
+  if (info.outFormat == RTAUDIO_FLOAT64) {
+    Float64 scale;
+    Float64 *out = (Float64 *)outBuffer;
+
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      scale = 1.0 / 127.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      scale = 1.0 / 32768.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      scale = 1.0 / 32767.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = 1.0 / 8388607.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = 1.0 / 2147483647.5;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_FLOAT32) {
-    FLOAT32 scale;
-    FLOAT32 *out = (FLOAT32 *)output;
+  else if (info.outFormat == RTAUDIO_FLOAT32) {
+    Float32 scale;
+    Float32 *out = (Float32 *)outBuffer;
 
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      scale = 1.0 / 128.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      scale = (Float32) ( 1.0 / 127.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      scale = 1.0 / 32768.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      scale = (Float32) ( 1.0 / 32767.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = (Float32) ( 1.0 / 8388607.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      INT32 *in = (INT32 *)input;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
+      scale = (Float32) ( 1.0 / 2147483647.5 );
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] += 0.5;
+          out[info.outOffset[j]] *= scale;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      FLOAT32 *in = (FLOAT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Float32 *in = (Float32 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT32) {
-    INT32 *out = (INT32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+  else if (info.outFormat == RTAUDIO_SINT32) {
+    Int32 *out = (Int32 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 24;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 16;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 16;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      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.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT24) {
-    INT32 *out = (INT32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+  else if (info.outFormat == RTAUDIO_SINT24) {
+    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]] * 8388607.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT16) {
-    INT16 *out = (INT16 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) in[offset_in[j]];
-          out[offset_out[j]] <<= 8;
+  else if (info.outFormat == RTAUDIO_SINT16) {
+    Int16 *out = (Int16 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 8;
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
+    else if (info.inFormat == RTAUDIO_SINT16) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      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.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT8) {
-    signed char *out = (signed char *)output;
-    if (format_in == RTAUDIO_SINT8) {
+  else if (info.outFormat == RTAUDIO_SINT8) {
+    signed char *out = (signed char *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
       // Channel compensation and/or (de)interleaving only.
       // Channel compensation and/or (de)interleaving only.
-      signed char *in = (signed char *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+      signed char *in = (signed char *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    if (format_in == RTAUDIO_SINT16) {
-      INT16 *in = (INT16 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 8) & 0x00ff);
+    if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      INT32 *in = (INT32 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      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.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      FLOAT64 *in = (FLOAT64 *)input;
-      for (int i=0; i<stream->bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
         }
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
 }
 
       }
     }
   }
 }
 
-void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
+  //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
+  //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
+  //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
+
+void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
 {
   register char val;
   register char *ptr;
 
   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);
@@ -7009,10 +7843,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);
@@ -7024,12 +7858,12 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
-      // Increment 4 bytes.
-      ptr += 4;
+      // Increment 3 more bytes.
+      ptr += 3;
     }
   }
     }
   }
-  else if (format == RTAUDIO_FLOAT64) {
-    for (int i=0; i<samples; i++) {
+  else if ( format == RTAUDIO_FLOAT64 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 8th bytes
       val = *(ptr);
       *(ptr) = *(ptr+7);
       // Swap 1st and 8th bytes
       val = *(ptr);
       *(ptr) = *(ptr+7);
@@ -7053,30 +7887,18 @@ void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
-      // Increment 8 bytes.
-      ptr += 8;
+      // Increment 5 more bytes.
+      ptr += 5;
     }
   }
 }
 
     }
   }
 }
 
+  // Indentation settings for Vim and Emacs
+  //
+  // Local Variables:
+  // c-basic-offset: 2
+  // indent-tabs-mode: nil
+  // End:
+  //
+  // vim: et sts=2 sw=2
 
 
-// *************************************************** //
-//
-// RtError class definition.
-//
-// *************************************************** //
-
-RtError :: RtError(const char *p, TYPE tipe)
-{
-  type = tipe;
-  strncpy(error_message, p, 256);
-}
-
-RtError :: ~RtError()
-{
-}
-
-void RtError :: printMessage()
-{
-  printf("\n%s\n\n", error_message);
-}