Many updates for release 4.0.7 (gs).
[rtaudio.git] / RtAudio.cpp
index fc4418eae52b0c0b66e79f6849b6635ba8e66ba7..78259e947b62bd10437a6490551bb8ea41de8073 100644 (file)
@@ -4,13 +4,13 @@
 
     RtAudio provides a common API (Application Programming Interface)
     for realtime audio input/output across Linux (native ALSA, Jack,
-    and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows
+    and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
     (DirectSound and ASIO) operating systems.
 
     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
 
     RtAudio: realtime audio i/o C++ classes
-    Copyright (c) 2001-2007 Gary P. Scavone
+    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
 */
 /************************************************************************/
 
-// RtAudio: Version 4.0
+// RtAudio: Version 4.0.7
 
 #include "RtAudio.h"
 #include <iostream>
+#include <cstdlib>
+#include <cstring>
+#include <climits>
 
 // Static variable definitions.
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
@@ -359,6 +362,13 @@ double RtApi :: getStreamTime( void )
 #endif
 }
 
+unsigned int RtApi :: getStreamSampleRate( void )
+{
+ verifyStream();
+
+ return stream_.sampleRate;
+}
+
 
 // *************************************************** //
 //
@@ -393,7 +403,11 @@ double RtApi :: getStreamTime( void )
 // implementation.
 struct CoreHandle {
   AudioDeviceID id[2];    // device ids
-  UInt32 iStream[2];      // device stream index (first for mono mode)
+#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;
@@ -401,7 +415,7 @@ struct CoreHandle {
   bool internalDrain;     // Indicates if stop is initiated from callback or not.
 
   CoreHandle()
-    :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+    :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
 };
 
 RtApiCore :: RtApiCore()
@@ -421,7 +435,8 @@ unsigned int RtApiCore :: getDeviceCount( void )
 {
   // Find out how many audio devices there are, if any.
   UInt32 dataSize;
-  OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL );
+  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 );
@@ -438,9 +453,8 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
 
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
-                                              &dataSize, &id );
-
+  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 );
@@ -449,7 +463,8 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
 
   dataSize *= nDevices;
   AudioDeviceID deviceList[ nDevices ];
-  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  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 );
@@ -471,18 +486,18 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void )
 
   AudioDeviceID id;
   UInt32 dataSize = sizeof( AudioDeviceID );
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
-                                              &dataSize, &id );
-
+  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;
   }
 
-  dataSize *= nDevices;
+  dataSize = sizeof( AudioDeviceID ) * nDevices;
   AudioDeviceID deviceList[ nDevices ];
-  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  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 );
@@ -516,7 +531,11 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 
   AudioDeviceID deviceList[ nDevices ];
   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  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 );
@@ -527,39 +546,43 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 
   // Get the device name.
   info.name.erase();
-  char name[256];
-  dataSize = 256;
-  result = AudioDeviceGetProperty( id, 0, false,
-                                   kAudioDevicePropertyDeviceManufacturer,
-                                   &dataSize, name );
-
+  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;
   }
-  info.name.append( (const char *)name, strlen(name) );
+
+  const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
+  info.name.append( (const char *)mname, strlen(mname) );
   info.name.append( ": " );
+  CFRelease( cfname );
 
-  dataSize = 256;
-  result = AudioDeviceGetProperty( id, 0, false,
-                                   kAudioDevicePropertyDeviceName,
-                                   &dataSize, name );
+  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;
-  result = AudioDeviceGetPropertyInfo( id, 0, false,
-                                       kAudioDevicePropertyStreamConfiguration,
-                                       &dataSize, NULL );
-  if (result != noErr || dataSize == 0) {
+  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 );
@@ -574,10 +597,8 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
     return info;
   }
 
-  result = AudioDeviceGetProperty( id, 0, false,
-                                   kAudioDevicePropertyStreamConfiguration,
-                                   &dataSize, bufferList );
-  if ( result != noErr ) {
+  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();
@@ -592,10 +613,9 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   free( bufferList );
 
   // Get the input stream "configuration".
-  result = AudioDeviceGetPropertyInfo( id, 0, true,
-                                       kAudioDevicePropertyStreamConfiguration,
-                                       &dataSize, NULL );
-  if (result != noErr || dataSize == 0) {
+  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 );
@@ -610,10 +630,8 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
     return info;
   }
 
-  result = AudioDeviceGetProperty( id, 0, true,
-                                   kAudioDevicePropertyStreamConfiguration,
-                                   &dataSize, bufferList );
-  if ( result != noErr ) {
+  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();
@@ -636,10 +654,9 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   if ( info.outputChannels == 0 ) isInput = true;
 
   // Determine the supported sample rates.
-  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
-                                       kAudioDevicePropertyAvailableNominalSampleRates,
-                                       &dataSize, NULL );
-
+  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();
@@ -649,10 +666,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
 
   UInt32 nRanges = dataSize / sizeof( AudioValueRange );
   AudioValueRange rangeList[ nRanges ];
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyAvailableNominalSampleRates,
-                                   &dataSize, &rangeList );
-
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );
   if ( result != kAudioHardwareNoError ) {
     errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
     errorText_ = errorStream_.str();
@@ -684,10 +698,10 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
   // no interest to the client.
   info.nativeFormats = RTAUDIO_FLOAT32;
 
-  if ( getDefaultOutputDevice() == device )
-    info.isDefaultOutput = true;
-  if ( getDefaultInputDevice() == device )
-    info.isDefaultInput = true;
+  if ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
 
   info.probed = true;
   return info;
@@ -710,29 +724,24 @@ OSStatus callbackHandler( AudioDeviceID inDevice,
     return kAudioHardwareNoError;
 }
 
-OSStatus deviceListener( AudioDeviceID inDevice,
-                         UInt32 channel,
-                         Boolean isInput,
-                         AudioDevicePropertyID propertyID,
+OSStatus deviceListener( AudioObjectID inDevice,
+                         UInt32 nAddresses,
+                         const AudioObjectPropertyAddress properties[],
                          void* handlePointer )
 {
   CoreHandle *handle = (CoreHandle *) handlePointer;
-  if ( propertyID == kAudioDeviceProcessorOverload ) {
-    if ( isInput )
-      handle->xrun[1] = true;
-    else
-      handle->xrun[0] = true;
+  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;
 }
 
-static bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property )
-{
-  OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL );
-  return result == 0;
-}
-
 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
                                    unsigned int firstChannel, unsigned int sampleRate,
                                    RtAudioFormat format, unsigned int *bufferSize,
@@ -754,7 +763,11 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 
   AudioDeviceID deviceList[ nDevices ];
   UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  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;
@@ -764,25 +777,19 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 
   // Setup for stream mode.
   bool isInput = false;
-  if ( mode == INPUT ) isInput = true;
-
-  // Set or disable "hog" mode.
-  dataSize = sizeof( UInt32 );
-  UInt32 doHog = 0;
-  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1;
-  result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog );
-  if ( result != noErr ) {
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
-    errorText_ = errorStream_.str();
-    return FAILURE;
+  if ( mode == INPUT ) {
+    isInput = true;
+    property.mScope = kAudioDevicePropertyScopeInput;
   }
+  else
+    property.mScope = kAudioDevicePropertyScopeOutput;
 
   // Get the stream "configuration".
-  AudioBufferList      *bufferList;
-  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
-                                       kAudioDevicePropertyStreamConfiguration,
-                                       &dataSize, NULL );
-  if (result != noErr || dataSize == 0) {
+  AudioBufferList      *bufferList = nil;
+  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;
@@ -795,86 +802,85 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     return FAILURE;
   }
 
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreamConfiguration,
-                                   &dataSize, bufferList );
-  if ( result != noErr ) {
-    free( bufferList );
+  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;
   }
 
-  // Search for a stream that contains the desired number of
+  // 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 currently only supports the use of one stream
-  // of interleaved data or multiple consecutive single-channel
-  // streams.  Thus, our search below is limited to these two
-  // contexts.
-  unsigned int streamChannels = 0, nStreams = 0;
-  UInt32 iChannel = 0, iStream = 0;
-  unsigned int offsetCounter = firstChannel;
-  stream_.deviceInterleaved[mode] = true;
-  nStreams = bufferList->mNumberBuffers;
+  // 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;
+  }
+
+  // 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 ) {
-      iChannel += offsetCounter;
+      firstStream = iStream;
+      channelOffset = offsetCounter;
       foundStream = true;
       break;
     }
     if ( streamChannels > offsetCounter ) break;
     offsetCounter -= streamChannels;
-    iChannel += streamChannels;
   }
 
-  // If we didn't find a single stream above, see if we can meet
-  // the channel specification in mono mode (i.e. using separate
-  // non-interleaved buffers).  This can only work if there are N
-  // consecutive one-channel streams, where N is the number of
-  // desired channels (+ channel offset).
+  // If we didn't find a single stream above, then we should be able
+  // to meet the channel specification with multiple streams.
   if ( foundStream == false ) {
-    unsigned int counter = 0;
+    monoMode = true;
     offsetCounter = firstChannel;
-    iChannel = 0;
     for ( iStream=0; iStream<nStreams; iStream++ ) {
       streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
-      if ( offsetCounter ) {
-        if ( streamChannels > offsetCounter ) break;
-        offsetCounter -= streamChannels;
-      }
-      else if ( streamChannels == 1 )
-        counter++;
-      else
-        counter = 0;
-      if ( counter == channels ) {
-        iStream -= channels - 1;
-        iChannel -= channels - 1;
-        stream_.deviceInterleaved[mode] = false;
-        foundStream = true;
-        break;
-      }
-      iChannel += streamChannels;
+      if ( streamChannels > offsetCounter ) break;
+      offsetCounter -= streamChannels;
     }
-  }
-  free( bufferList );
 
-  if ( foundStream == false ) {
-    errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
-    errorText_ = errorStream_.str();
-    return FAILURE;
+    firstStream = iStream;
+    channelOffset = offsetCounter;
+    Int32 channelCounter = channels + offsetCounter - streamChannels;
+
+    if ( streamChannels > 1 ) monoMode = false;
+    while ( channelCounter > 0 ) {
+      streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
+      if ( streamChannels > 1 ) monoMode = false;
+      channelCounter -= streamChannels;
+      streamCount++;
+    }
   }
 
+  free( bufferList );
+
   // Determine the buffer size.
   AudioValueRange      bufferRange;
   dataSize = sizeof( AudioValueRange );
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyBufferFrameSizeRange,
-                                   &dataSize, &bufferRange );
+  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();
@@ -885,13 +891,12 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
 
-  // Set the buffer size.  For mono mode, I'm assuming we only need to
-  // make this setting for the master channel.
+  // 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 );
-  result = AudioDeviceSetProperty( id, NULL, 0, isInput,
-                                   kAudioDevicePropertyBufferFrameSize,
-                                   dataSize, &theSize );
+  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 << ").";
@@ -911,32 +916,75 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.bufferSize = *bufferSize;
   stream_.nBuffers = 1;
 
-  // Get the stream ID(s) so we can set the stream format.  In mono
-  // mode, we'll have to do this for each stream (channel).
+  // 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;
+    }
+  }
+
+  // Try to set "hog" mode ... it's not clear to me this is working.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {
+    pid_t hog_pid;
+    dataSize = sizeof( hog_pid );
+    property.mSelector = kAudioDevicePropertyHogMode;
+    result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    if ( hog_pid != getpid() ) {
+      hog_pid = getpid();
+      result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
+  }
+
+  // Get the stream ID(s) so we can set the stream format.
   AudioStreamID streamIDs[ nStreams ];
   dataSize = nStreams * sizeof( AudioStreamID );
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreams,
-                                   &dataSize, &streamIDs );
+  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;
   }
 
-  // Now set the stream format.  Also, check the physical format of the
-  // device and change that if necessary.
+  // 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 );
-  if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
-  else nStreams = channels;
 
   bool updateFormat;
-  for ( unsigned int i=0; i<nStreams; i++ ) {
+  for ( UInt32 i=0; i<streamCount; i++ ) {
 
-    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
-                                     kAudioStreamPropertyVirtualFormat,
-                                     &dataSize, &description );
+    property.mSelector = kAudioStreamPropertyVirtualFormat;
+    result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, &dataSize, &description );
 
     if ( result != noErr ) {
       errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
@@ -959,9 +1007,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }
 
     if ( updateFormat ) {
-      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0,
-                                       kAudioStreamPropertyVirtualFormat,
-                                       dataSize, &description );
+      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();
@@ -970,9 +1016,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }
 
     // Now check the physical format.
-    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
-                                     kAudioStreamPropertyPhysicalFormat,
-                                     &dataSize, &description );
+    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();
@@ -986,34 +1031,44 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 
       // 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 = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      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 = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      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 = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      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 = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      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 = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      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();
@@ -1022,19 +1077,17 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }
   }
 
+
   // Get the stream latency.  There can be latency in both the device
   // and the stream.  First, attempt to get the device latency on the
   // master channel or the first open channel.  Errors that might
   // occur here are not deemed critical.
-  UInt32 latency, channel = 0;
+
+  UInt32 latency;
   dataSize = sizeof( UInt32 );
-  AudioDevicePropertyID property = kAudioDevicePropertyLatency;
-  for ( int i=0; i<2; i++ ) {
-    if ( hasProperty( id, channel, isInput, property ) == true ) break;
-    channel = iChannel + 1 + i;
-  }
-  if ( channel <= iChannel + 1 ) {
-    result = AudioDeviceGetProperty( id, channel, isInput, property, &dataSize, &latency );
+  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 << ").";
@@ -1043,9 +1096,9 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }
   }
 
-  // Now try to get the stream latency.  For "mono" mode, I assume the
-  // latency is equal for all single-channel streams.
-  result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
+  // 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 {
     errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
@@ -1063,14 +1116,16 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.userFormat = format;
   stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
 
-  if ( stream_.deviceInterleaved[mode] )
+  if ( streamCount == 1 )
     stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
-  else // mono mode
+  else // multiple streams
     stream_.nDeviceChannels[mode] = channels;
   stream_.nUserChannels[mode] = channels;
-  stream_.channelOffset[mode] = iChannel;  // offset within a CoreAudio stream
+  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;
@@ -1078,8 +1133,12 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     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 )
+  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.
@@ -1101,11 +1160,13 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
   else
     handle = (CoreHandle *) stream_.apiHandle;
-  handle->iStream[mode] = iStream;
+  handle->iStream[mode] = firstStream;
+  handle->nStreams[mode] = streamCount;
   handle->id[mode] = id;
 
   // Allocate necessary internal buffers.
-  unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  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.";
@@ -1113,9 +1174,9 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
 
   // If possible, we will make use of the CoreAudio stream buffers as
-  // "device buffers".  However, we can't do this if the device
-  // buffers are non-interleaved ("mono" mode).
-  if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) {
+  // "device buffers".  However, we can't do this if using multiple
+  // streams.
+  if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
 
     bool makeBuffer = true;
     bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
@@ -1134,13 +1195,6 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
         errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
       }
-
-      // Save a pointer to our own device buffer in the CoreHandle
-      // structure because we may need to use the stream_.deviceBuffer
-      // variable to point to the CoreAudio buffer before buffer
-      // conversion (if we have a duplex stream with two different
-      // conversion schemes).
-      handle->deviceBuffer = stream_.deviceBuffer;
     }
   }
 
@@ -1149,30 +1203,22 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.state = STREAM_STOPPED;
   stream_.callbackInfo.object = (void *) this;
 
-  // Setup the buffer conversion information structure.  We override
-  // the channel offset value and perform our own setting for that
-  // here.
+  // Setup the buffer conversion information structure.
   if ( stream_.doConvertBuffer[mode] ) {
-    setConvertInfo( mode, 0 );
-
-    // Add channel offset for interleaved channels.
-    if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) {
-      if ( mode == OUTPUT ) {
-        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
-          stream_.convertInfo[mode].outOffset[k] += firstChannel;
-      }
-      else {
-        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
-          stream_.convertInfo[mode].inOffset[k] += firstChannel;
-      }
-    }
+    if ( streamCount > 1 ) setConvertInfo( mode, 0 );
+    else setConvertInfo( mode, channelOffset );
   }
 
   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
     // Only one callback procedure per device.
     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();
@@ -1185,9 +1231,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
 
   // Setup the device property listener for over/underload.
-  result = AudioDeviceAddPropertyListener( id, 0, isInput,
-                                           kAudioDeviceProcessorOverload,
-                                           deviceListener, (void *) handle );
+  property.mSelector = kAudioDeviceProcessorOverload;
+  result = AudioObjectAddPropertyListener( id, &property, deviceListener, (void *) handle );
 
   return SUCCESS;
 
@@ -1225,13 +1270,23 @@ void RtApiCore :: closeStream( void )
   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] ) ) {
     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
   }
 
   for ( int i=0; i<2; i++ ) {
@@ -1241,8 +1296,8 @@ void RtApiCore :: closeStream( void )
     }
   }
 
-  if ( handle->deviceBuffer ) {
-    free( handle->deviceBuffer );
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
     stream_.deviceBuffer = 0;
   }
 
@@ -1311,6 +1366,11 @@ void RtApiCore :: stopStream( void )
 
   MUTEX_LOCK( &stream_.mutex );
 
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
   OSStatus result = noErr;
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -1320,7 +1380,9 @@ void RtApiCore :: stopStream( void )
       pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
 
+    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();
@@ -1338,10 +1400,11 @@ void RtApiCore :: stopStream( void )
     }
   }
 
+  stream_.state = STREAM_STOPPED;
+
  unlock:
   MUTEX_UNLOCK( &stream_.mutex );
 
-  stream_.state = STREAM_STOPPED;
   if ( result == noErr ) return;
   error( RtError::SYSTEM_ERROR );
 }
@@ -1386,6 +1449,12 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
 
   MUTEX_LOCK( &stream_.mutex );
 
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return SUCCESS;
+  }
+
   AudioDeviceID outputDevice = handle->id[0];
 
   // Invoke user callback to get fresh output data UNLESS we are
@@ -1418,48 +1487,96 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
 
     if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
-      if ( stream_.deviceInterleaved[0] ) {
+      if ( handle->nStreams[0] == 1 ) {
         memset( outBufferList->mBuffers[handle->iStream[0]].mData,
                 0,
                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
       }
-      else {
-        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+      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 ( stream_.doConvertBuffer[0] ) {
-
-      if ( stream_.deviceInterleaved[0] )
-        stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
-      else
-        stream_.deviceBuffer = handle->deviceBuffer;
-
-      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
-
-      if ( !stream_.deviceInterleaved[0] ) {
-        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
-        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
-          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
-                  &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
-        }
+    else 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 {
-      if ( stream_.deviceInterleaved[0] ) {
+      else { // copy from user buffer
         memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
                 stream_.userBuffer[0],
                 outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
       }
-      else {
+    }
+    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_.deviceInterleaved[0] == false ) { // mono mode
         UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
-        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+        for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
           memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
-                  &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
+                  (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
+        }
+      }
+      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 ( 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;
+          }
+
+          // 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;
+          }
+
+          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;
         }
       }
     }
@@ -1470,29 +1587,93 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
     }
   }
 
-  AudioDeviceID inputDevice = handle->id[1];
+  AudioDeviceID inputDevice;
+  inputDevice = handle->id[1];
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
 
-    if ( stream_.doConvertBuffer[1] ) {
+    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_.deviceInterleaved[1] )
-        stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
-      else {
-        stream_.deviceBuffer = (char *) handle->deviceBuffer;
+      if ( stream_.deviceInterleaved[1] == false ) { // mono mode
         UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
-        for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
-          memcpy( &stream_.deviceBuffer[i*bufferBytes],
+        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];
+        }
 
-      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+        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;
+          }
 
-    }
-    else {
-      memcpy( stream_.userBuffer[1],
-              inBufferList->mBuffers[handle->iStream[1]].mData,
-              inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+          // 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;
+          }
+
+          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] );
+      }
     }
   }
 
@@ -1505,7 +1686,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
 
 const char* RtApiCore :: getErrorCode( OSStatus code )
 {
-       switch( code ) {
+  switch( code ) {
 
   case kAudioHardwareNotRunningError:
     return "kAudioHardwareNotRunningError";
@@ -1542,10 +1723,10 @@ const char* RtApiCore :: getErrorCode( OSStatus code )
 
   default:
     return "CoreAudio unknown error";
-       }
+  }
 }
 
-//******************** End of __MACOSX_CORE__ *********************//
+  //******************** End of __MACOSX_CORE__ *********************//
 #endif
 
 #if defined(__UNIX_JACK__)
@@ -1581,6 +1762,7 @@ const char* RtApiCore :: getErrorCode( OSStatus code )
 
 #include <jack/jack.h>
 #include <unistd.h>
+#include <cstdio>
 
 // A structure to hold various information related to the Jack API
 // implementation.
@@ -1597,9 +1779,16 @@ struct 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()
 {
   // Nothing to do here.
+#if !defined(__RTAUDIO_DEBUG__)
+  // Turn off Jack's internal error reporting.
+  jack_set_error_function( &jackSilentError );
+#endif
 }
 
 RtApiJack :: ~RtApiJack()
@@ -1610,7 +1799,9 @@ RtApiJack :: ~RtApiJack()
 unsigned int RtApiJack :: getDeviceCount( void )
 {
   // See if we can become a jack client.
-  jack_client_t *client = jack_client_new( "RtApiJackCount" );
+  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;
@@ -1619,7 +1810,7 @@ unsigned int RtApiJack :: getDeviceCount( void )
   ports = jack_get_ports( client, NULL, NULL, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
-    unsigned int iColon = 0;
+    size_t iColon = 0;
     do {
       port = (char *) ports[ nChannels ];
       iColon = port.find(":");
@@ -1643,7 +1834,9 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   RtAudio::DeviceInfo info;
   info.probed = false;
 
-  jack_client_t *client = jack_client_new( "RtApiJackInfo" );
+  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 );
@@ -1656,7 +1849,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
   ports = jack_get_ports( client, NULL, NULL, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
-    unsigned int iColon = 0;
+    size_t iColon = 0;
     do {
       port = (char *) ports[ nPorts ];
       iColon = port.find(":");
@@ -1735,6 +1928,19 @@ int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
   return 0;
 }
 
+// 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;
+
+  object->closeStream();
+
+  pthread_exit( NULL );
+}
 void jackShutdown( void *infoPointer )
 {
   CallbackInfo *info = (CallbackInfo *) infoPointer;
@@ -1747,7 +1953,7 @@ void jackShutdown( void *infoPointer )
   // other problem occurred and we should close the stream.
   if ( object->isStreamRunning() == false ) return;
 
-  object->closeStream();
+  pthread_create( &threadId, NULL, jackCloseStream, info );
   std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
 }
 
@@ -1771,10 +1977,12 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // 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_new( options->streamName.c_str() );
+      client = jack_client_open( options->streamName.c_str(), jackoptions, status );
     else
-      client = jack_client_new( "RtApiJack" );
+      client = jack_client_open( "RtApiJack", jackoptions, status );
     if ( client == 0 ) {
       errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
       error( RtError::WARNING );
@@ -1792,7 +2000,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   ports = jack_get_ports( client, NULL, NULL, 0 );
   if ( ports ) {
     // Parse the port names up to the first colon (:).
-    unsigned int iColon = 0;
+    size_t iColon = 0;
     do {
       port = (char *) ports[ nPorts ];
       iColon = port.find(":");
@@ -1816,8 +2024,8 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   // Count the available ports containing the client name as device
   // channels.  Jack "input ports" equal RtAudio output channels.
   unsigned int nChannels = 0;
-  unsigned long flag = JackPortIsOutput;
-  if ( mode == INPUT ) flag = JackPortIsInput;
+  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++;
@@ -2131,6 +2339,11 @@ void RtApiJack :: stopStream( void )
 
   MUTEX_LOCK( &stream_.mutex );
 
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
   JackHandle *handle = (JackHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
@@ -2161,6 +2374,21 @@ void RtApiJack :: abortStream( void )
   stopStream();
 }
 
+// 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 )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiJack *object = (RtApiJack *) info->object;
+
+  object->stopStream();
+
+  pthread_exit( NULL );
+}
+
 bool RtApiJack :: callbackEvent( unsigned long nframes )
 {
   if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
@@ -2180,15 +2408,22 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
 
   // Check if we were draining the stream and signal is finished.
   if ( handle->drainCounter > 3 ) {
-    if ( handle->internalDrain == false )
-      pthread_cond_signal( &handle->condition );
+    if ( handle->internalDrain == true ) {
+      pthread_create( &threadId, NULL, jackStopStream, info );
+    }
     else
-      stopStream();
+      pthread_cond_signal( &handle->condition );
     return SUCCESS;
   }
 
   MUTEX_LOCK( &stream_.mutex );
 
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return SUCCESS;
+  }
+
   // Invoke user callback first, to get fresh output data.
   if ( handle->drainCounter == 0 ) {
     RtAudioCallback callback = (RtAudioCallback) info->callback;
@@ -2206,7 +2441,8 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
                                      stream_.bufferSize, streamTime, status, info->userData );
     if ( handle->drainCounter == 2 ) {
       MUTEX_UNLOCK( &stream_.mutex );
-      abortStream();
+      ThreadHandle id;
+      pthread_create( &id, NULL, jackStopStream, info );
       return SUCCESS;
     }
     else if ( handle->drainCounter == 1 )
@@ -2270,7 +2506,7 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
   RtApi::tickStreamTime();
   return SUCCESS;
 }
-//******************** End of __UNIX_JACK__ *********************//
+  //******************** End of __UNIX_JACK__ *********************//
 #endif
 
 #if defined(__WINDOWS_ASIO__) // ASIO API on Windows
@@ -2291,10 +2527,10 @@ bool RtApiJack :: callbackEvent( unsigned long nframes )
 // on information found in
 // http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
 
-#include "asio/asiosys.h"
-#include "asio/asio.h"
-#include "asio/iasiothiscallresolver.h"
-#include "asio/asiodrivers.h"
+#include "asiosys.h"
+#include "asio.h"
+#include "iasiothiscallresolver.h"
+#include "asiodrivers.h"
 #include <cmath>
 
 AsioDrivers drivers;
@@ -2366,11 +2602,14 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
     error( RtError::INVALID_USE );
   }
 
-  // Don't probe if a stream is already open.
+  // If a stream is already open, we cannot probe other devices.  Thus, use the saved results.
   if ( stream_.state != STREAM_CLOSED ) {
-    errorText_ = "RtApiAsio::getDeviceInfo: unable to probe driver while a stream is open.";
-    error( RtError::WARNING );
-    return info;
+    if ( device >= devices_.size() ) {
+      errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";
+      error( RtError::WARNING );
+      return info;
+    }
+    return devices_[ device ];
   }
 
   char driverName[32];
@@ -2447,10 +2686,10 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
     info.nativeFormats |= RTAUDIO_FLOAT64;
 
-  if ( getDefaultOutputDevice() == device )
-    info.isDefaultOutput = true;
-  if ( getDefaultInputDevice() == device )
-    info.isDefaultInput = true;
+  if ( info.outputChannels > 0 )
+    if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
+  if ( info.inputChannels > 0 )
+    if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
 
   info.probed = true;
   drivers.removeCurrentDriver();
@@ -2463,6 +2702,16 @@ void bufferSwitch( long index, ASIOBool processNow )
   object->callbackEvent( index );
 }
 
+void RtApiAsio :: saveDeviceInfo( void )
+{
+  devices_.clear();
+
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
+
 bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
                                    unsigned int firstChannel, unsigned int sampleRate,
                                    RtAudioFormat format, unsigned int *bufferSize,
@@ -2482,6 +2731,12 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     return FAILURE;
   }
 
+  // The getDeviceInfo() function will not work when a stream is open
+  // because ASIO does not allow multiple devices to run at the same
+  // time.  Thus, we'll probe the system before opening a stream and
+  // save the results for use by getDeviceInfo().
+  this->saveDeviceInfo();
+
   // Only load the driver once for duplex stream.
   if ( mode != INPUT || stream_.mode != OUTPUT ) {
     if ( !drivers.loadDriver( driverName ) ) {
@@ -2606,11 +2861,28 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
   else if ( granularity == -1 ) {
     // Make sure bufferSize is a power of two.
-    double power = std::log10( (double) *bufferSize ) / log10( 2.0 );
-    *bufferSize = (int) pow( 2.0, floor(power+0.5) );
+    int log2_of_min_size = 0;
+    int log2_of_max_size = 0;
+
+    for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
+      if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
+      if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
+    }
+
+    long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
+    int min_delta_num = log2_of_min_size;
+
+    for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
+      long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
+      if (current_delta < min_delta) {
+        min_delta = current_delta;
+        min_delta_num = i;
+      }
+    }
+
+    *bufferSize = ( (unsigned int)1 << min_delta_num );
     if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
-    else *bufferSize = preferSize;
   }
   else if ( granularity != 0 ) {
     // Set to an even multiple of granularity, rounding up.
@@ -2878,6 +3150,11 @@ void RtApiAsio :: stopStream()
 
   MUTEX_LOCK( &stream_.mutex );
 
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
   AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
     if ( handle->drainCounter == 0 ) {
@@ -2913,10 +3190,10 @@ void RtApiAsio :: abortStream()
 
   // The following lines were commented-out because some behavior was
   // noted where the device buffers need to be zeroed to avoid
-  // continuing sound, even when the device buffers are completed
+  // 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;
+  // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  // handle->drainCounter = 1;
   stopStream();
 }
 
@@ -3169,15 +3446,15 @@ static const char* getAsioErrorString( ASIOError result )
   };
 
   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." }
-  };
+    {
+      {   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;
@@ -3192,16 +3469,17 @@ static const char* getAsioErrorString( ASIOError result )
 
 // Modified by Robin Davies, October 2005
 // - Improvements to DirectX pointer chasing. 
-// - Backdoor RtDsStatistics hook provides DirectX performance information.
 // - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
 // - Auto-call CoInitialize for DSOUND and ASIO platforms.
 // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
+// Changed device query structure for RtAudio 4.0.7, January 2010
 
 #include <dsound.h>
 #include <assert.h>
+#include <algorithm>
 
 #if defined(__MINGW32__)
-// missing from latest mingw winapi
+  // 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 */
@@ -3214,14 +3492,6 @@ static const char* getAsioErrorString( ASIOError result )
 #pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
 #endif
 
-static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
-{
-  if (laterPointer > earlierPointer)
-    return laterPointer - earlierPointer;
-  else
-    return laterPointer - earlierPointer + bufferSize;
-}
-
 static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
 {
   if ( pointer > bufferSize ) pointer -= bufferSize;
@@ -3247,49 +3517,29 @@ struct 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; }
 };
 
-/*
-RtApiDs::RtDsStatistics RtApiDs::statistics;
-
-// Provides a backdoor hook to monitor for DirectSound read overruns and write underruns.
-RtApiDs::RtDsStatistics RtApiDs::getDsStatistics()
-{
-  RtDsStatistics s = statistics;
-
-  // update the calculated fields.
-  if ( s.inputFrameSize != 0 )
-    s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate;
-
-  if ( s.outputFrameSize != 0 )
-    s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate;
-
-  return s;
-}
-*/
-
 // Declarations for utility functions, callbacks, and structures
 // specific to the DirectSound implementation.
-static BOOL CALLBACK deviceCountCallback( LPGUID lpguid,
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
                                           LPCTSTR description,
                                           LPCTSTR module,
                                           LPVOID lpContext );
 
-static char* getErrorString( int code );
+static const char* getErrorString( int code );
 
 extern "C" unsigned __stdcall callbackHandler( void *ptr );
 
-struct EnumInfo {
-  bool isInput;
-  bool getDefault;
-  bool findIndex;
-  unsigned int counter;
-  unsigned int index;
-  LPGUID id;
+struct DsDevice {
+  LPGUID id[2];
+  bool validId[2];
+  bool found;
   std::string name;
 
-  EnumInfo()
-    : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {}
+  DsDevice()
+  : found(false) { validId[0] = false; validId[1] = false; }
 };
 
+std::vector< DsDevice > dsDevices;
+
 RtApiDs :: RtApiDs()
 {
   // Dsound will run both-threaded. If CoInitialize fails, then just
@@ -3305,105 +3555,82 @@ RtApiDs :: ~RtApiDs()
   if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
-unsigned int RtApiDs :: getDefaultInputDevice( void )
+// The DirectSound default output is always the first device.
+unsigned int RtApiDs :: getDefaultOutputDevice( void )
 {
-  // Count output devices.
-  EnumInfo info;
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!";
-    errorText_ = errorStream_.str();
-    error( RtError::WARNING );
-    return 0;
-  }
-
-  // Now enumerate input devices until we find the id = NULL.
-  info.isInput = true;
-  info.getDefault = true;
-  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!";
-    errorText_ = errorStream_.str();
-    error( RtError::WARNING );
-    return 0;
-  }
-
-  if ( info.counter > 0 ) return info.counter - 1;
   return 0;
 }
 
-unsigned int RtApiDs :: getDefaultOutputDevice( void )
+// The DirectSound default input is always the first input device,
+// which is the first capture device enumerated.
+unsigned int RtApiDs :: getDefaultInputDevice( void )
 {
-  // Enumerate output devices until we find the id = NULL.
-  EnumInfo info;
-  info.getDefault = true;
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!";
-    errorText_ = errorStream_.str();
-    error( RtError::WARNING );
-    return 0;
-  }
-
-  if ( info.counter > 0 ) return info.counter - 1;
   return 0;
 }
 
 unsigned int RtApiDs :: getDeviceCount( void )
 {
-  // Count DirectSound devices.
-  EnumInfo info;
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  // Set query flag for previously found devices to false, so that we
+  // can check for any devices that have disappeared.
+  for ( unsigned int i=0; i<dsDevices.size(); i++ )
+    dsDevices[i].found = false;
+
+  // Query DirectSound devices.
+  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 );
   }
 
-  // Count DirectSoundCapture devices.
-  info.isInput = true;
-  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  // 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 );
   }
 
-  return info.counter;
+  // 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 );
+
+  return dsDevices.size();
 }
 
 RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
 {
-  // Because DirectSound always enumerates input and output devices
-  // separately (and because we don't attempt to combine devices
-  // internally), none of our "devices" will ever be duplex.
-
   RtAudio::DeviceInfo info;
   info.probed = false;
 
-  // Enumerate through devices to find the id (if it exists).  Note
-  // that we have to do the output enumeration first, even if this is
-  // an input device, in order for the device counter to be correct.
-  EnumInfo dsinfo;
-  dsinfo.findIndex = true;
-  dsinfo.index = device;
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!";
-    errorText_ = errorStream_.str();
-    error( RtError::WARNING );
+  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 ( dsinfo.name.empty() ) goto probeInput;
+  if ( device >= dsDevices.size() ) {
+    errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
+    error( RtError::INVALID_USE );
+  }
+
+  HRESULT result;
+  if ( dsDevices[ device ].validId[0] == false ) goto probeInput;
 
   LPDIRECTSOUND output;
   DSCAPS outCaps;
-  result = DirectSoundCreate( dsinfo.id, &output, NULL );
+  result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
   if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
     errorText_ = errorStream_.str();
     error( RtError::WARNING );
-    return info;
+    goto probeInput;
   }
 
   outCaps.dwSize = sizeof( outCaps );
@@ -3413,7 +3640,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
     errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
     errorText_ = errorStream_.str();
     error( RtError::WARNING );
-    return info;
+    goto probeInput;
   }
 
   // Get output channel information.
@@ -3436,28 +3663,18 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
   if ( getDefaultOutputDevice() == device )
     info.isDefaultOutput = true;
 
-  // Copy name and return.
-  info.name = dsinfo.name;
-
-  info.probed = true;
-  return info;
-
- probeInput:
-
-  dsinfo.isInput = true;
-  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!";
-    errorText_ = errorStream_.str();
-    error( RtError::WARNING );
+  if ( dsDevices[ device ].validId[1] == false ) {
+    info.name = dsDevices[ device ].name;
+    info.probed = true;
+    return info;
   }
 
-  if ( dsinfo.name.empty() ) return info;
+ probeInput:
 
   LPDIRECTSOUNDCAPTURE input;
-  result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
+  result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
   if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
     errorText_ = errorStream_.str();
     error( RtError::WARNING );
     return info;
@@ -3468,7 +3685,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
   result = input->GetCaps( &inCaps );
   if ( FAILED( result ) ) {
     input->Release();
-    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsinfo.name << ")!";
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";
     errorText_ = errorStream_.str();
     error( RtError::WARNING );
     return info;
@@ -3478,6 +3695,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
   info.inputChannels = inCaps.dwChannels;
 
   // 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;
@@ -3489,16 +3707,16 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
     if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
 
     if ( info.nativeFormats & RTAUDIO_SINT16 ) {
-      if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.sampleRates.push_back( 11025 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 );
+      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 ) info.sampleRates.push_back( 11025 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 );
+      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 ) {
@@ -3512,16 +3730,16 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
     if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
 
     if ( info.nativeFormats & RTAUDIO_SINT16 ) {
-      if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.sampleRates.push_back( 11025 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 );
+      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 ) info.sampleRates.push_back( 11025 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 );
-      if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.push_back( 96000 );
+      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
@@ -3530,11 +3748,28 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
 
   if ( info.inputChannels == 0 ) return info;
 
-  if ( getDefaultInputDevice() == device )
-    info.isDefaultInput = true;
+  // 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 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 ( device == 0 ) info.isDefaultInput = true;
 
   // Copy name and return.
-  info.name = dsinfo.name;
+  info.name = dsDevices[ device ].name;
   info.probed = true;
   return info;
 }
@@ -3549,35 +3784,28 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     return FAILURE;
   }
 
-  // Enumerate through devices to find the id (if it exists).  Note
-  // that we have to do the output enumeration first, even if this is
-  // an input device, in order for the device counter to be correct.
-  EnumInfo dsinfo;
-  dsinfo.findIndex = true;
-  dsinfo.index = device;
-  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
-  if ( FAILED( result ) ) {
-    errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!";
-    errorText_ = errorStream_.str();
+  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;
+  }
+
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";
     return FAILURE;
   }
 
   if ( mode == OUTPUT ) {
-    if ( dsinfo.name.empty() ) {
+    if ( dsDevices[ device ].validId[0] == false ) {
       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
   }
   else { // mode == INPUT
-    dsinfo.isInput = true;
-    HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
-    if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!";
-      errorText_ = errorStream_.str();
-      return FAILURE;
-    }
-    if ( dsinfo.name.empty() ) {
+    if ( dsDevices[ device ].validId[1] == false ) {
       errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
       errorText_ = errorStream_.str();
       return FAILURE;
@@ -3588,9 +3816,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   // 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().
-  HWND hWnd = GetForegroundWindow();
+  // DirectSound buffer, DirectSound can crash.  In the past, I had
+  // problems when using GetDesktopWindow() but it seems fine now
+  // (January 2010).  I'll leave it commented here.
+  // HWND hWnd = GetForegroundWindow();
+  HWND hWnd = GetDesktopWindow();
 
   // Check the numberOfBuffers parameter and limit the lowest value to
   // two.  This is a judgement call and a value of two is probably too
@@ -3600,6 +3830,10 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
   if ( nBuffers < 2 ) nBuffers = 3;
 
+  // Check the lower range of the user-specified buffer size and set
+  // (arbitrarily) to a lower bound of 32.
+  if ( *bufferSize < 32 ) *bufferSize = 32;
+
   // Create the wave format structure.  The data format setting will
   // be determined later.
   WAVEFORMATEX waveFormat;
@@ -3608,19 +3842,20 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   waveFormat.nChannels = channels + firstChannel;
   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
 
-  // Determine the device buffer size. By default, 32k, but we will
-  // grow it to make allowances for very large software buffer sizes.
-  DWORD dsBufferSize = 0;
+  // 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;
-  long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
 
   void *ohandle = 0, *bhandle = 0;
+  HRESULT result;
   if ( mode == OUTPUT ) {
 
     LPDIRECTSOUND output;
-    result = DirectSoundCreate( dsinfo.id, &output, NULL );
+    result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3630,14 +3865,14 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = output->GetCaps( &outCaps );
     if ( FAILED( result ) ) {
       output->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Check channel information.
     if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
-      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback.";
+      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3661,14 +3896,16 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
 
     // If the user wants an even bigger buffer, increase the device buffer size accordingly.
-    while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes )
-      bufferBytes *= 2;
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
 
-    // Set cooperative level to DSSCL_EXCLUSIVE
-    result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+    // 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 (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3687,7 +3924,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
     if ( FAILED( result ) ) {
       output->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3696,19 +3933,19 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = buffer->SetFormat( &waveFormat );
     if ( FAILED( result ) ) {
       output->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
     // Setup the secondary DS buffer description.
-    dsBufferSize = (DWORD) bufferBytes;
     ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
     bufferDescription.dwSize = sizeof( DSBUFFERDESC );
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                  DSBCAPS_GLOBALFOCUS |
                                   DSBCAPS_GETCURRENTPOSITION2 |
                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
-    bufferDescription.dwBufferBytes = bufferBytes;
+    bufferDescription.dwBufferBytes = dsBufferSize;
     bufferDescription.lpwfxFormat = &waveFormat;
 
     // Try to create the secondary DS buffer.  If that doesn't work,
@@ -3716,12 +3953,13 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     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 (" << dsinfo.name << ")!";
+        errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";
         errorText_ = errorStream_.str();
         return FAILURE;
       }
@@ -3734,21 +3972,21 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     if ( FAILED( result ) ) {
       output->Release();
       buffer->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
-    bufferBytes = dsbcaps.dwBufferBytes;
+    dsBufferSize = dsbcaps.dwBufferBytes;
 
     // Lock the DS buffer
     LPVOID audioPtr;
     DWORD dataLen;
-    result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    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 (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3761,12 +3999,11 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     if ( FAILED( result ) ) {
       output->Release();
       buffer->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
-    dsBufferSize = bufferBytes;
     ohandle = (void *) output;
     bhandle = (void *) buffer;
   }
@@ -3774,9 +4011,9 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   if ( mode == INPUT ) {
 
     LPDIRECTSOUNDCAPTURE input;
-    result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
+    result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3786,7 +4023,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = input->GetCaps( &inCaps );
     if ( FAILED( result ) ) {
       input->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3827,15 +4064,19 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     // Update wave format structure and buffer information.
     waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
     waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > dsBufferSize )
+      dsBufferSize *= 2;
 
     // Setup the secondary DS buffer description.
-    dsBufferSize = bufferBytes;
     DSCBUFFERDESC bufferDescription;
     ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
     bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
     bufferDescription.dwFlags = 0;
     bufferDescription.dwReserved = 0;
-    bufferDescription.dwBufferBytes = bufferBytes;
+    bufferDescription.dwBufferBytes = dsBufferSize;
     bufferDescription.lpwfxFormat = &waveFormat;
 
     // Create the capture buffer.
@@ -3843,19 +4084,38 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
     if ( FAILED( result ) ) {
       input->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    // Get the buffer size ... might be different from what we specified.
+    DSCBCAPS dscbcaps;
+    dscbcaps.dwSize = sizeof( DSCBCAPS );
+    result = buffer->GetCaps( &dscbcaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
+    dsBufferSize = dscbcaps.dwBufferBytes;
+
+    // NOTE: We could have a problem here if this is a duplex stream
+    // and the play and capture hardware buffer sizes are different
+    // (I'm actually not sure if that is a problem or not).
+    // Currently, we are not verifying that.
+
     // Lock the capture buffer
     LPVOID audioPtr;
     DWORD dataLen;
-    result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    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 (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
@@ -3868,17 +4128,17 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     if ( FAILED( result ) ) {
       input->Release();
       buffer->Release();
-      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!";
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";
       errorText_ = errorStream_.str();
       return FAILURE;
     }
 
-    dsBufferSize = bufferBytes;
     ohandle = (void *) input;
     bhandle = (void *) buffer;
   }
 
   // Set various stream parameters
+  DsHandle *handle = 0;
   stream_.nDeviceChannels[mode] = channels + firstChannel;
   stream_.nUserChannels[mode] = channels;
   stream_.bufferSize = *bufferSize;
@@ -3898,7 +4158,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     stream_.doConvertBuffer[mode] = true;
 
   // Allocate necessary internal buffers
-  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  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.";
@@ -3928,7 +4188,6 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
   }
 
   // Allocate our DsHandle structures for the stream.
-  DsHandle *handle;
   if ( stream_.apiHandle == 0 ) {
     try {
       handle = new DsHandle;
@@ -3976,6 +4235,8 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     goto error;
   }
 
+  // Boost DS thread priority
+  SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
   return SUCCESS;
 
  error:
@@ -4075,22 +4336,15 @@ void RtApiDs :: startStream()
     return;
   }
 
-  // Increase scheduler frequency on lesser windows (a side-effect of
-  // increasing timer accuracy).  On greater windows (Win2K or later),
-  // this is already in effect.
-
   MUTEX_LOCK( &stream_.mutex );
   
   DsHandle *handle = (DsHandle *) stream_.apiHandle;
 
+  // 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 ); 
 
-  /*
-  memset( &statistics, 0, sizeof( statistics ) );
-  statistics.sampleRate = stream_.sampleRate;
-  statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0];
-  */
-
   buffersRolling = false;
   duplexPrerollBytes = 0;
 
@@ -4099,9 +4353,8 @@ void RtApiDs :: startStream()
     duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
   }
 
-  HRESULT result;
+  HRESULT result = 0;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-    //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0];
 
     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
     result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
@@ -4113,7 +4366,6 @@ void RtApiDs :: startStream()
   }
 
   if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-    //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1];
 
     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
     result = buffer->Start( DSCBSTART_LOOPING );
@@ -4145,7 +4397,12 @@ void RtApiDs :: stopStream()
 
   MUTEX_LOCK( &stream_.mutex );
 
-  HRESULT result;
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
+  HRESULT result = 0;
   LPVOID audioPtr;
   DWORD dataLen;
   DsHandle *handle = (DsHandle *) stream_.apiHandle;
@@ -4162,7 +4419,7 @@ void RtApiDs :: stopStream()
     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
     result = buffer->Stop();
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4171,7 +4428,7 @@ void RtApiDs :: stopStream()
     // we won't have old data playing.
     result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4182,7 +4439,7 @@ void RtApiDs :: stopStream()
     // Unlock the DS buffer
     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4198,7 +4455,7 @@ void RtApiDs :: stopStream()
 
     result = buffer->Stop();
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4207,7 +4464,7 @@ void RtApiDs :: stopStream()
     // we won't have old data playing.
     result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4218,7 +4475,7 @@ void RtApiDs :: stopStream()
     // Unlock the DS buffer
     result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
     if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
+      errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
       errorText_ = errorStream_.str();
       goto unlock;
     }
@@ -4231,6 +4488,7 @@ void RtApiDs :: stopStream()
   timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
   stream_.state = STREAM_STOPPED;
   MUTEX_UNLOCK( &stream_.mutex );
+
   if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR );
 }
 
@@ -4252,7 +4510,7 @@ void RtApiDs :: abortStream()
 void RtApiDs :: callbackEvent()
 {
   if ( stream_.state == STREAM_STOPPED ) {
-    Sleep(50); // sleep 50 milliseconds
+    Sleep( 50 ); // sleep 50 milliseconds
     return;
   }
 
@@ -4276,6 +4534,12 @@ void RtApiDs :: callbackEvent()
 
   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 ) {
@@ -4302,14 +4566,9 @@ void RtApiDs :: callbackEvent()
   }
 
   HRESULT result;
-  DWORD currentWritePos, safeWritePos;
-  DWORD currentReadPos, safeReadPos;
-  DWORD leadPos;
-  UINT nextWritePos;
-
-#ifdef GENERATE_DEBUG_LOG
-  DWORD writeTime, readTime;
-#endif
+  DWORD currentWritePointer, safeWritePointer;
+  DWORD currentReadPointer, safeReadPointer;
+  UINT nextWritePointer;
 
   LPVOID buffer1 = NULL;
   LPVOID buffer2 = NULL;
@@ -4319,63 +4578,79 @@ void RtApiDs :: callbackEvent()
   char *buffer;
   long bufferBytes;
 
-  if ( stream_.mode == DUPLEX && !buffersRolling ) {
-    assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+  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.
+      // 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.
+      // 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];
+      LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
 
-    DWORD initialWritePos, initialSafeWritePos;
-    DWORD initialReadPos, initialSafeReadPos;
+      DWORD startSafeWritePointer, startSafeReadPointer;
 
-    result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos );
-    if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
-      errorText_ = errorStream_.str();
-      error( RtError::SYSTEM_ERROR );
-    }
-    result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos );
-    if ( FAILED( result ) ) {
-      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
-      errorText_ = errorStream_.str();
-      error( RtError::SYSTEM_ERROR );
-    }
-    while ( true ) {
-      result = dsWriteBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      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( &currentReadPos, &safeReadPos );
+      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 );
       }
-      if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break;
-      Sleep( 1 );
+      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 );
+      }
+
+      //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+
+      handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
+      if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
+      handle->bufferPointer[1] = safeReadPointer;
     }
+    else if ( stream_.mode == OUTPUT ) {
 
-    assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
+      // 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;
-    handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] );
-    handle->bufferPointer[1] = safeReadPos;
   }
 
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -4410,62 +4685,51 @@ void RtApiDs :: callbackEvent()
       for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
 
     DWORD dsBufferSize = handle->dsBufferSize[0];
-    nextWritePos = handle->bufferPointer[0];
+    nextWritePointer = handle->bufferPointer[0];
 
-    DWORD endWrite;
+    DWORD endWrite, leadPointer;
     while ( true ) {
       // Find out where the read and "safe write" pointers are.
-      result = dsBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      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 );
       }
 
-      leadPos = safeWritePos + handle->dsPointerLeadTime[0];
-      if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
-      if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
-      endWrite = nextWritePos + bufferBytes;
+      // 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 ( leadPos >= endWrite ) break;
-
-      // If we are here, then we must wait until the play pointer gets
-      // beyond the write region.  The approach here is to use the
-      // Sleep() function to suspend operation until safePos catches
-      // up. Calculate number of milliseconds to wait as:
-      //   time = distance * (milliseconds/second) * fudgefactor /
-      //          ((bytes/sample) * (samples/second))
-      // A "fudgefactor" less than 1 is used because it was found
-      // that sleeping too long was MUCH worse than sleeping for
-      // several shorter periods.
-      double millis = ( endWrite - leadPos ) * 900.0;
+      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;
-      if ( millis > 50.0 ) {
-        static int nOverruns = 0;
-        ++nOverruns;
-      }
       Sleep( (DWORD) millis );
     }
 
-    //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) {
-    //  statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] );
-    //}
-
-    if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize )
-         || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) { 
+    if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )
+         || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) { 
       // We've strayed into the forbidden zone ... resync the read pointer.
-      //++statistics.numberOfWriteUnderruns;
       handle->xrun[0] = true;
-      nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize;
-      while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize;
-      handle->bufferPointer[0] = nextWritePos;
-      endWrite = nextWritePos + bufferBytes;
+      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( nextWritePos, bufferBytes, &buffer1,
+    result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,
                              &bufferSize1, &buffer2, &bufferSize2, 0 );
     if ( FAILED( result ) ) {
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
@@ -4484,8 +4748,8 @@ void RtApiDs :: callbackEvent()
       errorText_ = errorStream_.str();
       error( RtError::SYSTEM_ERROR );
     }
-    nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize;
-    handle->bufferPointer[0] = nextWritePos;
+    nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    handle->bufferPointer[0] = nextWritePointer;
 
     if ( handle->drainCounter ) {
       handle->drainCounter++;
@@ -4508,19 +4772,19 @@ void RtApiDs :: callbackEvent()
     }
 
     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
-    long nextReadPos = handle->bufferPointer[1];
+    long nextReadPointer = handle->bufferPointer[1];
     DWORD dsBufferSize = handle->dsBufferSize[1];
 
     // Find out where the write and "safe read" pointers are.
-    result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+    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 ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
-    DWORD endRead = nextReadPos + bufferBytes;
+    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,
@@ -4537,60 +4801,54 @@ void RtApiDs :: callbackEvent()
     // zeros from the read buffer while the pointers sync up.
 
     if ( stream_.mode == DUPLEX ) {
-      if ( safeReadPos < endRead ) {
+      if ( safeReadPointer < endRead ) {
         if ( duplexPrerollBytes <= 0 ) {
           // Pre-roll time over. Be more agressive.
-          int adjustment = endRead-safeReadPos;
+          int adjustment = endRead-safeReadPointer;
 
           handle->xrun[1] = true;
-          //++statistics.numberOfReadOverruns;
           // Two cases:
           //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
           //     and perform fine adjustments later.
           //   - small adjustments: back off by twice as much.
           if ( adjustment >= 2*bufferBytes )
-            nextReadPos = safeReadPos-2*bufferBytes;
+            nextReadPointer = safeReadPointer-2*bufferBytes;
           else
-            nextReadPos = safeReadPos-bufferBytes-adjustment;
+            nextReadPointer = safeReadPointer-bufferBytes-adjustment;
 
-          //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos;
-          //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
-          if ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+          if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
 
         }
         else {
           // In pre=roll time. Just do it.
-          nextReadPos = safeReadPos-bufferBytes;
-          while ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+          nextReadPointer = safeReadPointer - bufferBytes;
+          while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
         }
-        endRead = nextReadPos + bufferBytes;
+        endRead = nextReadPointer + bufferBytes;
       }
     }
     else { // mode == INPUT
-      while ( safeReadPos < endRead ) {
+      while ( safeReadPointer < endRead ) {
         // See comments for playback.
-        double millis = (endRead - safeReadPos) * 900.0;
+        double millis = (endRead - safeReadPointer) * 1000.0;
         millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
         if ( millis < 1.0 ) millis = 1.0;
         Sleep( (DWORD) millis );
 
-        // Wake up, find out where we are now
-        result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+        // 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 ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+        if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
       }
     }
 
-    //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) )
-    //  statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize );
-
     // Lock free space in the buffer
-    result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1,
+    result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,
                              &bufferSize1, &buffer2, &bufferSize2, 0 );
     if ( FAILED( result ) ) {
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
@@ -4610,14 +4868,14 @@ void RtApiDs :: callbackEvent()
     }
 
     // Update our buffer offset and unlock sound buffer
-    nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    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] = nextReadPos;
+    handle->bufferPointer[1] = nextReadPointer;
 
     // No byte swapping necessary in DirectSound implementation.
 
@@ -4629,20 +4887,6 @@ void RtApiDs :: callbackEvent()
     if ( stream_.doConvertBuffer[1] )
       convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
   }
-#ifdef GENERATE_DEBUG_LOG
-  if ( currentDebugLogEntry < debugLog.size() )
-  {
-    TTickRecord &r = debugLog[currentDebugLogEntry++];
-    r.currentReadPointer = currentReadPos;
-    r.safeReadPointer = safeReadPos;
-    r.currentWritePointer = currentWritePos;
-    r.safeWritePointer = safeWritePos;
-    r.readTime = readTime;
-    r.writeTime = writeTime;
-    r.nextReadPointer = handles[1].bufferPointer;
-    r.nextWritePointer = handles[0].bufferPointer;
-  }
-#endif
 
  unlock:
   MUTEX_UNLOCK( &stream_.mutex );
@@ -4686,15 +4930,16 @@ std::string convertTChar( LPCTSTR name )
   return s;
 }
 
-static BOOL CALLBACK deviceCountCallback( LPGUID lpguid,
+static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
                                           LPCTSTR description,
                                           LPCTSTR module,
                                           LPVOID lpContext )
 {
-  EnumInfo *info = (EnumInfo *) lpContext;
+  bool *isInput = (bool *) lpContext;
 
   HRESULT hr;
-  if ( info->isInput == true ) {
+  bool validDevice = false;
+  if ( *isInput == true ) {
     DSCCAPS caps;
     LPDIRECTSOUNDCAPTURE object;
 
@@ -4705,7 +4950,7 @@ static BOOL CALLBACK deviceCountCallback( LPGUID lpguid,
     hr = object->GetCaps( &caps );
     if ( hr == DS_OK ) {
       if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
-        info->counter++;
+        validDevice = true;
     }
     object->Release();
   }
@@ -4719,25 +4964,51 @@ static BOOL CALLBACK deviceCountCallback( LPGUID lpguid,
     hr = object->GetCaps( &caps );
     if ( hr == DS_OK ) {
       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
-        info->counter++;
+        validDevice = true;
     }
     object->Release();
   }
 
-  if ( info->getDefault && lpguid == NULL ) return FALSE;
+  // 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;
+      }
+    }
 
-  if ( info->findIndex && info->counter > info->index ) {
-    info->id = lpguid;
-    info->name = convertTChar( description );
-    return FALSE;
+    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 );
   }
 
   return TRUE;
 }
 
-static char* getErrorString( int code )
+static const char* getErrorString( int code )
 {
-       switch ( code ) {
+  switch ( code ) {
 
   case DSERR_ALLOCATED:
     return "Already allocated";
@@ -4786,7 +5057,7 @@ static char* getErrorString( int code )
 
   default:
     return "DirectSound unknown error";
-       }
+  }
 }
 //******************** End of __WINDOWS_DS__ *********************//
 #endif
@@ -4797,12 +5068,13 @@ static char* getErrorString( int code )
 #include <alsa/asoundlib.h>
 #include <unistd.h>
 
-// A structure to hold various information related to the ALSA API
-// implementation.
+  // 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; }
@@ -4838,17 +5110,17 @@ unsigned int RtApiAlsa :: getDeviceCount( void )
       errorText_ = errorStream_.str();
       error( RtError::WARNING );
       goto nextcard;
-               }
-               subdevice = -1;
-               while( 1 ) {
+    }
+    subdevice = -1;
+    while( 1 ) {
       result = snd_ctl_pcm_next_device( handle, &subdevice );
-                       if ( result < 0 ) {
+      if ( result < 0 ) {
         errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
         errorText_ = errorStream_.str();
         error( RtError::WARNING );
         break;
       }
-                       if ( subdevice < 0 )
+      if ( subdevice < 0 )
         break;
       nDevices++;
     }
@@ -4881,17 +5153,17 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
       errorText_ = errorStream_.str();
       error( RtError::WARNING );
       goto nextcard;
-               }
-               subdevice = -1;
-               while( 1 ) {
+    }
+    subdevice = -1;
+    while( 1 ) {
       result = snd_ctl_pcm_next_device( chandle, &subdevice );
-                       if ( result < 0 ) {
+      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 ( subdevice < 0 ) break;
       if ( nDevices == device ) {
         sprintf( name, "hw:%d,%d", card, subdevice );
         goto foundDevice;
@@ -4915,10 +5187,22 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
 
  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_info_t *pcminfo;
+  snd_pcm_info_alloca( &pcminfo );
   snd_pcm_t *phandle;
   snd_pcm_hw_params_t *params;
   snd_pcm_hw_params_alloca( &params );
@@ -5109,6 +5393,16 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
   return info;
 }
 
+void RtApiAlsa :: saveDeviceInfo( void )
+{
+  devices_.clear();
+
+  unsigned int nDevices = getDeviceCount();
+  devices_.resize( nDevices );
+  for ( unsigned int i=0; i<nDevices; i++ )
+    devices_[i] = getDeviceInfo( i );
+}
+
 bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
                                    unsigned int firstChannel, unsigned int sampleRate,
                                    RtAudioFormat format, unsigned int *bufferSize,
@@ -5137,14 +5431,15 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
       errorText_ = errorStream_.str();
       return FAILURE;
-               }
-               subdevice = -1;
-               while( 1 ) {
+    }
+    subdevice = -1;
+    while( 1 ) {
       result = snd_ctl_pcm_next_device( chandle, &subdevice );
-                       if ( result < 0 ) break;
-                       if ( subdevice < 0 ) break;
+      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++;
@@ -5167,6 +5462,12 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 
  foundDevice:
 
+  // The getDeviceInfo() function will not work for a device that is
+  // already open.  Thus, we'll probe the system before opening a
+  // stream and save the results for use by getDeviceInfo().
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
+    this->saveDeviceInfo();
+
   snd_pcm_stream_t stream;
   if ( mode == OUTPUT )
     stream = SND_PCM_STREAM_PLAYBACK;
@@ -5360,19 +5661,10 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
 
   // Set the buffer number, which in ALSA is referred to as the "period".
-  int dir;
+  int totalSize, dir = 0;
   unsigned int periods = 0;
   if ( options ) periods = options->numberOfBuffers;
-  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
-  // Even though the hardware might allow 1 buffer, it won't work reliably.
-  if ( periods < 2 ) periods = 2;
-  result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
-  if ( result < 0 ) {
-    snd_pcm_close( phandle );
-    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
-    errorText_ = errorStream_.str();
-    return FAILURE;
-  }
+  totalSize = *bufferSize * periods;
 
   // Set the buffer (or period) size.
   snd_pcm_uframes_t periodSize = *bufferSize;
@@ -5385,6 +5677,18 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   }
   *bufferSize = periodSize;
 
+  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;
+  }
+
   // 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 ) {
@@ -5414,9 +5718,19 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   snd_pcm_sw_params_alloca( &sw_params );
   snd_pcm_sw_params_current( phandle, sw_params );
   snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
-  snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, 0x7fffffff );
+  snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
   snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
-  snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX );
+
+  // 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 );
@@ -5450,6 +5764,12 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
       goto error;
     }
+
+    if ( pthread_cond_init( &apiInfo->runnable, NULL ) ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
+
     stream_.apiHandle = (void *) apiInfo;
     apiInfo->handles[0] = 0;
     apiInfo->handles[1] = 0;
@@ -5518,13 +5838,28 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     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.
+    // 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)
-    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+    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
@@ -5543,6 +5878,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 
  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;
@@ -5572,10 +5908,14 @@ void RtApiAlsa :: closeStream()
     return;
   }
 
+  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 );
 
-  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
   if ( stream_.state == STREAM_RUNNING ) {
     stream_.state = STREAM_STOPPED;
     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
@@ -5585,6 +5925,7 @@ void RtApiAlsa :: closeStream()
   }
 
   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;
@@ -5653,6 +5994,8 @@ void RtApiAlsa :: startStream()
  unlock:
   MUTEX_UNLOCK( &stream_.mutex );
 
+  pthread_cond_signal( &apiInfo->runnable );
+
   if ( result >= 0 ) return;
   error( RtError::SYSTEM_ERROR );
 }
@@ -5666,11 +6009,13 @@ void RtApiAlsa :: stopStream()
     return;
   }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
+  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;
@@ -5696,6 +6041,7 @@ void RtApiAlsa :: stopStream()
   }
 
  unlock:
+  stream_.state = STREAM_STOPPED;
   MUTEX_UNLOCK( &stream_.mutex );
 
   if ( result >= 0 ) return;
@@ -5711,11 +6057,13 @@ void RtApiAlsa :: abortStream()
     return;
   }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
+  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;
@@ -5738,18 +6086,24 @@ void RtApiAlsa :: abortStream()
   }
 
  unlock:
+  stream_.state = STREAM_STOPPED;
   MUTEX_UNLOCK( &stream_.mutex );
 
-  stream_.state = STREAM_STOPPED;
   if ( result >= 0 ) return;
   error( RtError::SYSTEM_ERROR );
 }
 
 void RtApiAlsa :: callbackEvent()
 {
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
   if ( stream_.state == STREAM_STOPPED ) {
-    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
-    return;
+    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 );
   }
 
   if ( stream_.state == STREAM_CLOSED ) {
@@ -5759,7 +6113,6 @@ void RtApiAlsa :: callbackEvent()
   }
 
   int doStopStream = 0;
-  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
   double streamTime = getStreamTime();
   RtAudioStreamStatus status = 0;
@@ -5772,7 +6125,12 @@ void RtApiAlsa :: callbackEvent()
     apiInfo->xrun[1] = false;
   }
   doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
-                         stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
+
+  if ( doStopStream == 2 ) {
+    abortStream();
+    return;
+  }
 
   MUTEX_LOCK( &stream_.mutex );
 
@@ -5813,7 +6171,7 @@ void RtApiAlsa :: callbackEvent()
     }
 
     if ( result < (int) stream_.bufferSize ) {
-      // Either an error or underrun occured.
+      // 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 ) {
@@ -5834,7 +6192,7 @@ void RtApiAlsa :: callbackEvent()
         errorText_ = errorStream_.str();
       }
       error( RtError::WARNING );
-      goto unlock;
+      goto tryOutput;
     }
 
     // Do byte swapping if necessary.
@@ -5850,6 +6208,8 @@ void RtApiAlsa :: callbackEvent()
     if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
   }
 
+ tryOutput:
+
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters and do buffer conversion if necessary.
@@ -5915,7 +6275,6 @@ void RtApiAlsa :: callbackEvent()
 
   RtApi::tickStreamTime();
   if ( doStopStream == 1 ) this->stopStream();
-  else if ( doStopStream == 2 ) this->abortStream();
 }
 
 extern "C" void *alsaCallbackHandler( void *ptr )
@@ -5924,15 +6283,6 @@ extern "C" void *alsaCallbackHandler( void *ptr )
   RtApiAlsa *object = (RtApiAlsa *) info->object;
   bool *isRunning = &info->isRunning;
 
-#ifdef SCHED_RR
-  // Set a higher scheduler priority (P.J. Leonard)
-  struct sched_param param;
-  int min = sched_get_priority_min( SCHED_RR );
-  int max = sched_get_priority_max( SCHED_RR );
-  param.sched_priority = min + ( max - min ) / 2;   // Is this the best number?
-  sched_setscheduler( 0, SCHED_RR, &param );
-#endif
-
   while ( *isRunning == true ) {
     pthread_testcancel();
     object->callbackEvent();
@@ -5951,7 +6301,7 @@ extern "C" void *alsaCallbackHandler( void *ptr )
 #include <sys/ioctl.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include "oss/soundcard.h"
+#include "soundcard.h"
 #include <errno.h>
 #include <math.h>
 
@@ -5963,6 +6313,7 @@ struct OssHandle {
   int id[2];    // device ids
   bool xrun[2];
   bool triggered;
+  pthread_cond_t runnable;
 
   OssHandle()
     :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
@@ -5995,6 +6346,7 @@ unsigned int RtApiOss :: getDeviceCount( void )
     return 0;
   }
 
+  close( mixerfd );
   return sysinfo.numaudios;
 }
 
@@ -6107,7 +6459,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
 
 
 bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
-                                   unsigned int firstChannel, unsigned int sampleRate,
+                                  unsigned int firstChannel, unsigned int sampleRate,
                                   RtAudioFormat format, unsigned int *bufferSize,
                                   RtAudio::StreamOptions *options )
 {
@@ -6204,14 +6556,14 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
 
   // For duplex operation, specifically set this mode (this doesn't seem to work).
   /*
-  if ( flags | O_RDWR ) {
+    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;
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+    }
     }
-  }
   */
 
   // Check the device channel support.
@@ -6419,6 +6771,11 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
       goto error;
     }
 
+    if ( pthread_cond_init( &handle->runnable, NULL ) ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
+      goto error;
+    }
+
     stream_.apiHandle = (void *) handle;
   }
   else {
@@ -6482,7 +6839,19 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
     pthread_attr_init( &attr );
     pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
-    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+    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
@@ -6501,6 +6870,7 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned
 
  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;
@@ -6530,10 +6900,14 @@ void RtApiOss :: closeStream()
     return;
   }
 
+  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 );
 
-  OssHandle *handle = (OssHandle *) stream_.apiHandle;
   if ( stream_.state == STREAM_RUNNING ) {
     if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
       ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
@@ -6543,6 +6917,7 @@ void RtApiOss :: closeStream()
   }
 
   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;
@@ -6582,6 +6957,9 @@ void RtApiOss :: startStream()
   // when fed samples.
 
   MUTEX_UNLOCK( &stream_.mutex );
+
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  pthread_cond_signal( &handle->runnable );
 }
 
 void RtApiOss :: stopStream()
@@ -6593,11 +6971,14 @@ void RtApiOss :: stopStream()
     return;
   }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
   int result = 0;
   OssHandle *handle = (OssHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -6646,9 +7027,9 @@ void RtApiOss :: stopStream()
   }
 
  unlock:
+  stream_.state = STREAM_STOPPED;
   MUTEX_UNLOCK( &stream_.mutex );
 
-  stream_.state = STREAM_STOPPED;
   if ( result != -1 ) return;
   error( RtError::SYSTEM_ERROR );
 }
@@ -6662,11 +7043,14 @@ void RtApiOss :: abortStream()
     return;
   }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
   MUTEX_LOCK( &stream_.mutex );
 
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) {
+    MUTEX_UNLOCK( &stream_.mutex );
+    return;
+  }
+
   int result = 0;
   OssHandle *handle = (OssHandle *) stream_.apiHandle;
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
@@ -6689,18 +7073,24 @@ void RtApiOss :: abortStream()
   }
 
  unlock:
+  stream_.state = STREAM_STOPPED;
   MUTEX_UNLOCK( &stream_.mutex );
 
-  stream_.state = STREAM_STOPPED;
   if ( result != -1 ) return;
   error( RtError::SYSTEM_ERROR );
 }
 
 void RtApiOss :: callbackEvent()
 {
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
   if ( stream_.state == STREAM_STOPPED ) {
-    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
-    return;
+    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 );
   }
 
   if ( stream_.state == STREAM_CLOSED ) {
@@ -6714,7 +7104,6 @@ void RtApiOss :: callbackEvent()
   RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
   double streamTime = getStreamTime();
   RtAudioStreamStatus status = 0;
-  OssHandle *handle = (OssHandle *) stream_.apiHandle;
   if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
     status |= RTAUDIO_OUTPUT_UNDERFLOW;
     handle->xrun[0] = false;
@@ -6725,6 +7114,10 @@ void RtApiOss :: callbackEvent()
   }
   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 );
 
@@ -6773,7 +7166,7 @@ void RtApiOss :: callbackEvent()
       handle->xrun[0] = true;
       errorText_ = "RtApiOss::callbackEvent: audio write error.";
       error( RtError::WARNING );
-      goto unlock;
+      // Continue on to input section.
     }
   }
 
@@ -6817,7 +7210,6 @@ void RtApiOss :: callbackEvent()
 
   RtApi::tickStreamTime();
   if ( doStopStream == 1 ) this->stopStream();
-  else if ( doStopStream == 2 ) this->abortStream();
 }
 
 extern "C" void *ossCallbackHandler( void *ptr )
@@ -6826,13 +7218,6 @@ extern "C" void *ossCallbackHandler( void *ptr )
   RtApiOss *object = (RtApiOss *) info->object;
   bool *isRunning = &info->isRunning;
 
-#ifdef SCHED_RR
-  // Set a higher scheduler priority (P.J. Leonard)
-  struct sched_param param;
-  param.sched_priority = 39;   // Is this the best number?
-  sched_setscheduler( 0, SCHED_RR, &param );
-#endif
-
   while ( *isRunning == true ) {
     pthread_testcancel();
     object->callbackEvent();
@@ -6855,11 +7240,11 @@ extern "C" void *ossCallbackHandler( void *ptr )
 // message printing.
 void RtApi :: error( RtError::Type type )
 {
+  errorStream_.str(""); // clear the ostringstream
   if ( type == RtError::WARNING && showWarnings_ == true )
     std::cerr << '\n' << errorText_ << "\n\n";
   else
     throw( RtError( errorText_, type ) );
-  errorStream_.str(""); // clear the ostringstream
 }
 
 void RtApi :: verifyStream()
@@ -6886,7 +7271,7 @@ void RtApi :: clearStreamInfo()
   stream_.callbackInfo.userData = 0;
   stream_.callbackInfo.isRunning = false;
   for ( int i=0; i<2; i++ ) {
-    stream_.device[i] = 0;
+    stream_.device[i] = 11111;
     stream_.doConvertBuffer[i] = false;
     stream_.deviceInterleaved[i] = true;
     stream_.doByteSwap[i] = false;
@@ -7022,10 +7407,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
 
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
-      scale = 1.0 / 128.0;
+      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 += info.inJump;
@@ -7034,10 +7420,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
-      scale = 1.0 / 32768.0;
+      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 += info.inJump;
@@ -7046,10 +7433,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 8388608.0;
+      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 += info.inJump;
@@ -7058,10 +7446,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 2147483648.0;
+      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 += info.inJump;
@@ -7096,10 +7485,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
 
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
-      scale = 1.0 / 128.0;
+      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 += info.inJump;
@@ -7108,10 +7498,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
-      scale = 1.0 / 32768.0;
+      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 += info.inJump;
@@ -7120,10 +7511,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 8388608.0;
+      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 += info.inJump;
@@ -7132,10 +7524,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 2147483648.0;
+      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 += info.inJump;
@@ -7214,7 +7607,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float32 *in = (Float32 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7224,7 +7617,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float64 *in = (Float64 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7281,7 +7674,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float32 *in = (Float32 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388608.0);
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7291,7 +7684,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float64 *in = (Float64 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7346,7 +7739,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float32 *in = (Float32 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7356,7 +7749,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float64 *in = (Float64 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7410,7 +7803,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float32 *in = (Float32 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7420,7 +7813,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
       Float64 *in = (Float64 *)inBuffer;
       for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
         }
         in += info.inJump;
         out += info.outJump;
@@ -7429,6 +7822,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
   }
 }
 
+  //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;
@@ -7461,8 +7858,8 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat
       *(ptr) = *(ptr+1);
       *(ptr+1) = val;
 
-      // Increment 4 bytes.
-      ptr += 4;
+      // Increment 3 more bytes.
+      ptr += 3;
     }
   }
   else if ( format == RTAUDIO_FLOAT64 ) {
@@ -7490,18 +7887,18 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat
       *(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
+  // Indentation settings for Vim and Emacs
+  //
+  // Local Variables:
+  // c-basic-offset: 2
+  // indent-tabs-mode: nil
+  // End:
+  //
+  // vim: et sts=2 sw=2