Version 3.0.2
authorGary Scavone <gary@music.mcgill.ca>
Wed, 9 Oct 2013 21:49:32 +0000 (23:49 +0200)
committerStephen Sinclair <sinclair@music.mcgill.ca>
Wed, 9 Oct 2013 23:15:42 +0000 (01:15 +0200)
24 files changed:
RtAudio.cpp
RtAudio.h
RtError.h
configure.ac
doc/doxygen/footer.html
doc/doxygen/tutorial.txt
doc/release.txt
install
readme
tests/Windows/rtaudiotest/Release/.placeholder [new file with mode: 0644]
tests/Windows/rtaudiotest/StdOpt.cpp [new file with mode: 0644]
tests/Windows/rtaudiotest/StdOpt.h [new file with mode: 0644]
tests/Windows/rtaudiotest/readme.txt [new file with mode: 0644]
tests/Windows/rtaudiotest/rtaudiotest.cpp [new file with mode: 0644]
tests/Windows/rtaudiotest/rtaudiotest.dsp [new file with mode: 0644]
tests/Windows/rtaudiotest/rtaudiotest.dsw [new file with mode: 0644]
tests/call_inout.cpp
tests/call_saw.cpp
tests/in_out.cpp
tests/info.cpp
tests/play_raw.cpp
tests/play_saw.cpp
tests/record_raw.cpp
tests/twostreams.cpp

index 767908d2a75e780ab92aa8bd3f5be036c94487e1..1f6a597d4ee1c082aaec6e74ff0824fc834e7330 100644 (file)
@@ -9,8 +9,8 @@
 
     RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/
 
-    RtAudio: a realtime audio i/o C++ class
-    Copyright (c) 2001-2004 Gary P. Scavone
+    RtAudio: realtime audio i/o C++ classes
+    Copyright (c) 2001-2005 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 3.0.1, 22 March 2004
+// RtAudio: Version 3.0.2 (14 October 2005)
+
+// Modified by Robin Davies, 1 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.
 
 #include "RtAudio.h"
 #include <iostream>
+#include <stdio.h>
 
 // Static variable definitions.
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
@@ -92,6 +99,26 @@ RtAudio :: RtAudio( int outputDevice, int outputChannels,
   }
 }
 
+RtAudio :: RtAudio( int outputDevice, int outputChannels,
+                    int inputDevice, int inputChannels,
+                    RtAudioFormat format, int sampleRate,
+                    int *bufferSize, int *numberOfBuffers, RtAudioApi api )
+{
+  initialize( api );
+
+  try {
+    rtapi_->openStream( outputDevice, outputChannels,
+                        inputDevice, inputChannels,
+                        format, sampleRate,
+                        bufferSize, numberOfBuffers );
+  }
+  catch (RtError &exception) {
+    // Deallocate the RtApi instance.
+    delete rtapi_;
+    throw exception;
+  }
+}
+
 RtAudio :: ~RtAudio()
 {
   delete rtapi_;
@@ -107,6 +134,16 @@ void RtAudio :: openStream( int outputDevice, int outputChannels,
                       bufferSize, numberOfBuffers );
 }
 
+void RtAudio :: openStream( int outputDevice, int outputChannels,
+                            int inputDevice, int inputChannels,
+                            RtAudioFormat format, int sampleRate,
+                            int *bufferSize, int *numberOfBuffers )
+{
+  rtapi_->openStream( outputDevice, outputChannels, inputDevice,
+                      inputChannels, format, sampleRate,
+                      bufferSize, *numberOfBuffers );
+}
+
 void RtAudio::initialize( RtAudioApi api )
 {
   rtapi_ = 0;
@@ -216,6 +253,7 @@ void RtAudio::initialize( RtAudioApi api )
 RtApi :: RtApi()
 {
   stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_STOPPED;
   stream_.apiHandle = 0;
   MUTEX_INITIALIZE(&stream_.mutex);
 }
@@ -225,6 +263,17 @@ RtApi :: ~RtApi()
   MUTEX_DESTROY(&stream_.mutex);
 }
 
+void RtApi :: openStream( int outputDevice, int outputChannels,
+                         int inputDevice, int inputChannels,
+                         RtAudioFormat format, int sampleRate,
+                         int *bufferSize, int *numberOfBuffers )
+{
+  this->openStream( outputDevice, outputChannels, inputDevice,
+                    inputChannels, format, sampleRate,
+                    bufferSize, *numberOfBuffers );
+  *numberOfBuffers = stream_.nBuffers;
+}
+
 void RtApi :: openStream( int outputDevice, int outputChannels,
                          int inputDevice, int inputChannels,
                          RtAudioFormat format, int sampleRate,
@@ -259,6 +308,7 @@ void RtApi :: openStream( int outputDevice, int outputChannels,
     }
   }
 
+  std::string errorMessages;
   clearStreamInfo();
   bool result = FAILURE;
   int device, defaultDevice = 0;
@@ -281,7 +331,7 @@ void RtApi :: openStream( int outputDevice, int outputChannels,
         if ( i == defaultDevice ) continue;
         device = i;
       }
-      if (devices_[device].probed == false) {
+      if ( devices_[device].probed == false ) {
         // If the device wasn't successfully probed before, try it
         // (again) now.
         clearDeviceInfo(&devices_[device]);
@@ -291,6 +341,9 @@ void RtApi :: openStream( int outputDevice, int outputChannels,
         result = probeDeviceOpen(device, mode, channels, sampleRate,
                                  format, bufferSize, numberOfBuffers);
       if ( result == SUCCESS ) break;
+      errorMessages.append( "    " );
+      errorMessages.append( message_ );
+      errorMessages.append( "\n" );
       if ( outputDevice > 0 ) break;
       clearStreamInfo();
     }
@@ -308,22 +361,25 @@ void RtApi :: openStream( int outputDevice, int outputChannels,
     else
       device = inputDevice - 1;
 
-    for (int i=-1; i<nDevices_; i++) {
+    for ( int i=-1; i<nDevices_; i++ ) {
       if (i >= 0 ) { 
         if ( i == defaultDevice ) continue;
         device = i;
       }
-      if (devices_[device].probed == false) {
+      if ( devices_[device].probed == false ) {
         // If the device wasn't successfully probed before, try it
         // (again) now.
         clearDeviceInfo(&devices_[device]);
         probeDeviceInfo(&devices_[device]);
       }
       if ( devices_[device].probed )
-        result = probeDeviceOpen(device, mode, channels, sampleRate,
-                                 format, bufferSize, numberOfBuffers);
-      if (result == SUCCESS) break;
-      if ( outputDevice > 0 ) break;
+        result = probeDeviceOpen( device, mode, channels, sampleRate,
+                                  format, bufferSize, numberOfBuffers );
+      if ( result == SUCCESS ) break;
+      errorMessages.append( "    " );
+      errorMessages.append( message_ );
+      errorMessages.append( "\n" );
+      if ( inputDevice > 0 ) break;
     }
   }
 
@@ -336,9 +392,11 @@ void RtApi :: openStream( int outputDevice, int outputChannels,
   clearStreamInfo();
   if ( ( outputDevice == 0 && outputChannels > 0 )
        || ( inputDevice == 0 && inputChannels > 0 ) )
-    sprintf(message_,"RtApi: no devices found for given stream parameters.");
+    sprintf(message_,"RtApi: no devices found for given stream parameters: \n%s",
+            errorMessages.c_str());
   else
-    sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters.");
+    sprintf(message_,"RtApi: unable to open specified device(s) with given stream parameters: \n%s",
+            errorMessages.c_str());
   error(RtError::INVALID_PARAMETER);
 
   return;
@@ -349,6 +407,11 @@ int RtApi :: getDeviceCount(void)
   return devices_.size();
 }
 
+RtApi::StreamState RtApi :: getStreamState( void ) const
+{
+  return stream_.state;
+}
+
 RtAudioDeviceInfo RtApi :: getDeviceInfo( int device )
 {
   if (device > (int) devices_.size() || device < 1) {
@@ -1130,6 +1193,49 @@ bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels,
   else
     stream_.mode = mode;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
@@ -1145,7 +1251,7 @@ bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -1313,8 +1419,8 @@ void RtApiOss :: tickStream()
 
     // Setup parameters and do buffer conversion if necessary.
     if (stream_.doConvertBuffer[0]) {
-      convertStreamBuffer(OUTPUT);
       buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
       samples = stream_.bufferSize * stream_.nDeviceChannels[0];
       format = stream_.deviceFormat[0];
     }
@@ -1369,7 +1475,7 @@ void RtApiOss :: tickStream()
 
     // Do buffer conversion if necessary.
     if (stream_.doConvertBuffer[1])
-      convertStreamBuffer(INPUT);
+      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
   }
 
  unlock:
@@ -1476,7 +1582,7 @@ extern "C" void *ossCallbackHandler(void *ptr)
 // quite a bit of extra code and most likely, a user program wouldn't
 // be prepared for the result anyway.
 
-// A structure to hold various information related to the CoreAuio API
+// A structure to hold various information related to the CoreAudio API
 // implementation.
 struct CoreHandle {
   UInt32 index[2];
@@ -1860,13 +1966,13 @@ void RtApiCore :: probeDeviceInfo( RtApiDevice *info )
   info->probed = true;
 }
 
-OSStatus callbackHandler(AudioDeviceID inDevice,
-                         const AudioTimeStamp* inNow,
-                         const AudioBufferList* inInputData,
-                         const AudioTimeStamp* inInputTime,
-                         AudioBufferList* outOutputData,
-                         const AudioTimeStamp* inOutputTime, 
-                         void* infoPointer)
+OSStatus callbackHandler( AudioDeviceID inDevice,
+                          const AudioTimeStamp* inNow,
+                          const AudioBufferList* inInputData,
+                          const AudioTimeStamp* inInputTime,
+                          AudioBufferList* outOutputData,
+                          const AudioTimeStamp* inOutputTime, 
+                          void* infoPointer )
 {
   CallbackInfo *info = (CallbackInfo *) infoPointer;
 
@@ -1882,11 +1988,11 @@ OSStatus callbackHandler(AudioDeviceID inDevice,
   return kAudioHardwareNoError;
 }
 
-OSStatus deviceListener(AudioDeviceID inDevice,
-                        UInt32 channel,
-                        Boolean isInput,
-                        AudioDevicePropertyID propertyID,
-                        void* handlePointer)
+OSStatus deviceListener( AudioDeviceID inDevice,
+                         UInt32 channel,
+                         Boolean isInput,
+                         AudioDevicePropertyID propertyID,
+                         void* handlePointer )
 {
   CoreHandle *handle = (CoreHandle *) handlePointer;
   if ( propertyID == kAudioDeviceProcessorOverload ) {
@@ -2110,7 +2216,7 @@ bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels,
     stream_.apiHandle = (void *) handle;
   }
   else
-     handle = (CoreHandle *) stream_.apiHandle;
+    handle = (CoreHandle *) stream_.apiHandle;
   handle->index[mode] = iStream;
 
   // Allocate necessary internal buffers.
@@ -2171,6 +2277,49 @@ bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels,
   stream_.state = STREAM_STOPPED;
   stream_.callbackInfo.object = (void *) this;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   if ( stream_.mode == OUTPUT && mode == INPUT && stream_.device[0] == device )
     // Only one callback procedure per device.
     stream_.mode = DUPLEX;
@@ -2206,7 +2355,7 @@ bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -2407,7 +2556,7 @@ void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *out
       else
         stream_.deviceBuffer = handle->deviceBuffer;
 
-      convertStreamBuffer(OUTPUT);
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] );
       if ( stream_.doByteSwap[0] )
         byteSwapBuffer(stream_.deviceBuffer,
                        stream_.bufferSize * stream_.nDeviceChannels[0],
@@ -2434,6 +2583,7 @@ void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *out
     }
   }
 
+  id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId );
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) {
 
     if (stream_.doConvertBuffer[1]) {
@@ -2453,7 +2603,7 @@ void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *out
         byteSwapBuffer(stream_.deviceBuffer,
                        stream_.bufferSize * stream_.nDeviceChannels[1],
                        stream_.deviceFormat[1]);
-      convertStreamBuffer(INPUT);
+      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
 
     }
     else {
@@ -2526,9 +2676,10 @@ void RtApiCore :: cancelStreamCallback()
 //
 // .jackd -d alsa -d hw:0
 //
-// Many of the parameters normally set for a stream are fixed by the
-// JACK server and can be specified when the JACK server is started.
-// In particular,
+// or through an interface program such as qjackctl.  Many of the
+// parameters normally set for a stream are fixed by the JACK server
+// and can be specified when the JACK server is started.  In
+// particular,
 //
 // .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
 //
@@ -2645,7 +2796,7 @@ void RtApiJack :: probeDeviceInfo(RtApiDevice *info)
   if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) {
     jack_client_close(client);
     sprintf(message_, "RtApiJack: error determining jack input/output channels!");
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -2671,7 +2822,7 @@ void RtApiJack :: probeDeviceInfo(RtApiDevice *info)
   if (info->nativeFormats == 0) {
     jack_client_close(client);
     sprintf(message_, "RtApiJack: error determining jack server data format!");
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -2700,6 +2851,14 @@ void jackShutdown(void *infoPointer)
   JackHandle *handle = (JackHandle *) info->apiInfo;
   handle->clientOpen = false;
   RtApiJack *object = (RtApiJack *) info->object;
+
+  // Check current stream state.  If stopped, then we'll assume this
+  // was called as a result of a call to RtApiJack::stopStream (the
+  // deactivation of a client handle causes this function to be called).
+  // If not, we'll assume the Jack server is shutting down or some
+  // other problem occurred and we should close the stream.
+  if ( object->getStreamState() == RtApi::STREAM_STOPPED ) return;
+
   try {
     object->closeStream();
   }
@@ -2708,7 +2867,7 @@ void jackShutdown(void *infoPointer)
     return;
   }
 
-  fprintf(stderr, "\nRtApiJack: the Jack server is shutting down ... stream stopped and closed!!!\n\n");
+  fprintf(stderr, "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!!\n\n");
 }
 
 int jackXrun( void * )
@@ -2877,6 +3036,49 @@ bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels,
     jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
   }
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
@@ -2897,7 +3099,7 @@ bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -3093,11 +3295,11 @@ void RtApiJack :: callbackEvent( unsigned long nframes )
   }
 
   jack_default_audio_sample_t *jackbuffer;
-  long bufferBytes = nframes * sizeof (jack_default_audio_sample_t);
+  long bufferBytes = nframes * sizeof(jack_default_audio_sample_t);
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
     if (stream_.doConvertBuffer[0]) {
-      convertStreamBuffer(OUTPUT);
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] );
 
       for ( int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
         jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i],
@@ -3120,7 +3322,7 @@ void RtApiJack :: callbackEvent( unsigned long nframes )
                                                                         (jack_nframes_t) nframes);
       memcpy(&stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
     }
-    convertStreamBuffer(INPUT);
+    convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
     }
     else { // single channel only
       jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][0],
@@ -3178,6 +3380,17 @@ void RtApiJack :: cancelStreamCallback()
 #include <unistd.h>
 #include <ctype.h>
 
+// A structure to hold various information related to the ALSA API
+// implementation.
+struct AlsaHandle {
+  snd_pcm_t *handles[2];
+  bool synchronized;
+  char *tempBuffer;
+
+  AlsaHandle()
+    :synchronized(false), tempBuffer(0) {}
+};
+
 extern "C" void *alsaCallbackHandler(void * ptr);
 
 RtApiAlsa :: RtApiAlsa()
@@ -3319,7 +3532,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto capture_probe;
   }
 
@@ -3330,7 +3543,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware minimum channel probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto capture_probe;
   }
   info->minOutputChannels = value;
@@ -3340,7 +3553,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware maximum channel probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto capture_probe;
   }
   info->maxOutputChannels = value;
@@ -3391,7 +3604,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     if (info->maxOutputChannels > 0)
       goto probe_parameters;
     else
@@ -3404,7 +3617,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware minimum in channel probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     if (info->maxOutputChannels > 0)
       goto probe_parameters;
     else
@@ -3417,7 +3630,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware maximum in channel probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     if (info->maxOutputChannels > 0)
       goto probe_parameters;
     else
@@ -3453,7 +3666,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
   if (err < 0) {
     sprintf(message_, "RtApiAlsa: pcm (%s) won't reopen during probe: %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -3463,7 +3676,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: hardware reopen probe error (%s): %s.",
             info->name.c_str(), snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -3509,7 +3722,7 @@ void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: pcm device (%s) data format not supported by RtAudio.",
             info->name.c_str());
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -3544,7 +3757,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   if (err < 0) {
     sprintf(message_,"RtApiAlsa: pcm device (%s) won't open: %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3556,7 +3769,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error getting parameter handle (%s): %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3576,14 +3789,14 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   else {
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: device (%s) access not supported by RtAudio.", name);
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
   if (err < 0) {
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting access ( (%s): %s.", name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3649,7 +3862,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   // If we get here, no supported format was found.
   sprintf(message_,"RtApiAlsa: pcm device (%s) data format not supported by RtAudio.", name);
   snd_pcm_close(handle);
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 
  set_format:
@@ -3658,7 +3871,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting format (%s): %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3672,7 +3885,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
       snd_pcm_close(handle);
       sprintf(message_, "RtApiAlsa: error getting format endian-ness (%s): %s.",
               name, snd_strerror(err));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
   }
@@ -3683,7 +3896,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting sample rate (%d) on device (%s): %s.",
             sampleRate, name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3697,7 +3910,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: channels (%d) not supported by device (%s).",
             channels, name);
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3705,7 +3918,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   if (err < 0 ) {
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error getting min channels count on device (%s).", name);
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
   device_channels = value;
@@ -3718,7 +3931,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting channels (%d) on device (%s): %s.",
             device_channels, name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3727,54 +3940,26 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   unsigned int periods = numberOfBuffers;
   // Even though the hardware might allow 1 buffer, it won't work reliably.
   if (periods < 2) periods = 2;
-  err = snd_pcm_hw_params_get_periods_min(hw_params, &value, &dir);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error getting min periods on device (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
-  if (value > periods) periods = value;
-  err = snd_pcm_hw_params_get_periods_max(hw_params, &value, &dir);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error getting max periods on device (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
-  if (value < periods) periods = value;
-
-  err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0);
+  err = snd_pcm_hw_params_set_periods_near(handle, hw_params, &periods, &dir);
   if (err < 0) {
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting periods (%s): %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
   // Set the buffer (or period) size.
-  snd_pcm_uframes_t period_size;
-  err = snd_pcm_hw_params_get_period_size_min(hw_params, &period_size, &dir);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error getting period size (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::WARNING);
-    return FAILURE;
-  }
-  if (*bufferSize < (int) period_size) *bufferSize = (int) period_size;
-
-  err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0);
+  snd_pcm_uframes_t period_size = *bufferSize;
+  err = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, &dir);
   if (err < 0) {
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error setting period size (%s): %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
+  *bufferSize = period_size;
 
   // If attempting to setup a duplex stream, the bufferSize parameter
   // MUST be the same in both directions!
@@ -3793,7 +3978,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     snd_pcm_close(handle);
     sprintf(message_, "RtApiAlsa: error installing hardware configuration (%s): %s.",
             name, snd_strerror(err));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -3802,23 +3987,40 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
   snd_pcm_hw_params_dump(hw_params, out);
 #endif
 
-  // Allocate the stream handle if necessary and then save.
-  snd_pcm_t **handles;
+  // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
+  snd_pcm_sw_params_t *sw_params = NULL;
+  snd_pcm_sw_params_alloca( &sw_params );
+  snd_pcm_sw_params_current( handle, sw_params );
+  snd_pcm_sw_params_set_start_threshold( handle, sw_params, *bufferSize );
+  snd_pcm_sw_params_set_stop_threshold( handle, sw_params, 0x7fffffff );
+  snd_pcm_sw_params_set_silence_threshold( handle, sw_params, 0 );
+  snd_pcm_sw_params_set_silence_size( handle, sw_params, INT_MAX );
+  err = snd_pcm_sw_params( handle, sw_params );
+  if (err < 0) {
+    snd_pcm_close(handle);
+    sprintf(message_, "RtAudio: ALSA error installing software configuration (%s): %s.",
+            name, snd_strerror(err));
+    error(RtError::DEBUG_WARNING);
+    return FAILURE;
+  }
+
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
+  snd_pcm_sw_params_dump(sw_params, out);
+#endif
+
+  // Allocate the ApiHandle if necessary and then save.
+  AlsaHandle *apiInfo = 0;
   if ( stream_.apiHandle == 0 ) {
-    handles = (snd_pcm_t **) calloc(2, sizeof(snd_pcm_t *));
-    if ( handle == NULL ) {
-      sprintf(message_, "RtApiAlsa: error allocating handle memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
-    }
-    stream_.apiHandle = (void *) handles;
-    handles[0] = 0;
-    handles[1] = 0;
+    apiInfo = (AlsaHandle *) new AlsaHandle;
+    stream_.apiHandle = (void *) apiInfo;
+    apiInfo->handles[0] = 0;
+    apiInfo->handles[1] = 0;
   }
   else {
-    handles = (snd_pcm_t **) stream_.apiHandle;
+    apiInfo = (AlsaHandle *) stream_.apiHandle;
   }
-  handles[mode] = handle;
+  apiInfo->handles[mode] = handle;
 
   // Set flags for buffer conversion
   stream_.doConvertBuffer[mode] = false;
@@ -3840,8 +4042,10 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
 
     buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
     if (stream_.userBuffer) free(stream_.userBuffer);
+    if (apiInfo->tempBuffer) free(apiInfo->tempBuffer);
     stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
+    apiInfo->tempBuffer = (char *) calloc(buffer_bytes, 1);
+    if ( stream_.userBuffer == NULL || apiInfo->tempBuffer == NULL ) {
       sprintf(message_, "RtApiAlsa: error allocating user buffer memory (%s).",
               devices_[device].name.c_str());
       goto error;
@@ -3876,23 +4080,77 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
 
   stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
-  if ( stream_.mode == OUTPUT && mode == INPUT )
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
     // We had already set up an output stream.
     stream_.mode = DUPLEX;
+    // Link the streams if possible.
+    apiInfo->synchronized = false;
+    if (snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0)
+      apiInfo->synchronized = true;
+    else {
+      sprintf(message_, "RtApiAlsa: unable to synchronize input and output streams (%s).",
+              devices_[device].name.c_str());
+      error(RtError::DEBUG_WARNING);
+    }
+  }
   else
     stream_.mode = mode;
   stream_.nBuffers = periods;
   stream_.sampleRate = sampleRate;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
-  if (handles) {
-    if (handles[0])
-      snd_pcm_close(handles[0]);
-    if (handles[1])
-      snd_pcm_close(handles[1]);
-    free(handles);
+  if (apiInfo) {
+    if (apiInfo->handles[0])
+      snd_pcm_close(apiInfo->handles[0]);
+    if (apiInfo->handles[1])
+      snd_pcm_close(apiInfo->handles[1]);
+    if ( apiInfo->tempBuffer ) free(apiInfo->tempBuffer);
+    delete apiInfo;
     stream_.apiHandle = 0;
   }
 
@@ -3901,7 +4159,7 @@ bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -3916,12 +4174,12 @@ void RtApiAlsa :: closeStream()
     return;
   }
 
-  snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
   if (stream_.state == STREAM_RUNNING) {
     if (stream_.mode == OUTPUT || stream_.mode == DUPLEX)
-      snd_pcm_drop(handle[0]);
+      snd_pcm_drop(apiInfo->handles[0]);
     if (stream_.mode == INPUT || stream_.mode == DUPLEX)
-      snd_pcm_drop(handle[1]);
+      snd_pcm_drop(apiInfo->handles[1]);
     stream_.state = STREAM_STOPPED;
   }
 
@@ -3930,11 +4188,12 @@ void RtApiAlsa :: closeStream()
     pthread_join(stream_.callbackInfo.thread, NULL);
   }
 
-  if (handle) {
-    if (handle[0]) snd_pcm_close(handle[0]);
-    if (handle[1]) snd_pcm_close(handle[1]);
-    free(handle);
-    handle = 0;
+  if (apiInfo) {
+    if (apiInfo->handles[0]) snd_pcm_close(apiInfo->handles[0]);
+    if (apiInfo->handles[1]) snd_pcm_close(apiInfo->handles[1]);
+    free(apiInfo->tempBuffer);
+    delete apiInfo;
+    stream_.apiHandle = 0;
   }
 
   if (stream_.userBuffer) {
@@ -3961,7 +4220,8 @@ void RtApiAlsa :: startStream()
 
   int err;
   snd_pcm_state_t state;
-  snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
     state = snd_pcm_state(handle[0]);
     if (state != SND_PCM_STATE_PREPARED) {
@@ -3975,7 +4235,7 @@ void RtApiAlsa :: startStream()
     }
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) {
     state = snd_pcm_state(handle[1]);
     if (state != SND_PCM_STATE_PREPARED) {
       err = snd_pcm_prepare(handle[1]);
@@ -4003,7 +4263,8 @@ void RtApiAlsa :: stopStream()
   MUTEX_LOCK(&stream_.mutex);
 
   int err;
-  snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
     err = snd_pcm_drain(handle[0]);
     if (err < 0) {
@@ -4014,7 +4275,7 @@ void RtApiAlsa :: stopStream()
     }
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) {
     err = snd_pcm_drain(handle[1]);
     if (err < 0) {
       sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.",
@@ -4038,7 +4299,8 @@ void RtApiAlsa :: abortStream()
   MUTEX_LOCK(&stream_.mutex);
 
   int err;
-  snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
     err = snd_pcm_drop(handle[0]);
     if (err < 0) {
@@ -4049,7 +4311,7 @@ void RtApiAlsa :: abortStream()
     }
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if ( (stream_.mode == INPUT || stream_.mode == DUPLEX) && !apiInfo->synchronized ) {
     err = snd_pcm_drop(handle[1]);
     if (err < 0) {
       sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.",
@@ -4070,7 +4332,8 @@ int RtApiAlsa :: streamWillBlock()
   MUTEX_LOCK(&stream_.mutex);
 
   int err = 0, frames = 0;
-  snd_pcm_t **handle = (snd_pcm_t **) stream_.apiHandle;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
     err = snd_pcm_avail_update(handle[0]);
     if (err < 0) {
@@ -4124,49 +4387,56 @@ void RtApiAlsa :: tickStream()
   int err;
   char *buffer;
   int channels;
+  AlsaHandle *apiInfo;
   snd_pcm_t **handle;
   RtAudioFormat format;
-  handle = (snd_pcm_t **) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+  apiInfo = (AlsaHandle *) stream_.apiHandle;
+  handle = (snd_pcm_t **) apiInfo->handles;
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[0]) {
-      convertStreamBuffer(OUTPUT);
+  if ( stream_.mode == DUPLEX ) {
+    // In duplex mode, we need to make the snd_pcm_read call before
+    // the snd_pcm_write call in order to avoid under/over runs.  So,
+    // copy the userData to our temporary buffer.
+    int bufferBytes;
+    bufferBytes = stream_.bufferSize * stream_.nUserChannels[0] * formatBytes(stream_.userFormat);
+    memcpy( apiInfo->tempBuffer, stream_.userBuffer, bufferBytes );
+  }
+
+  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+
+    // Setup parameters.
+    if (stream_.doConvertBuffer[1]) {
       buffer = stream_.deviceBuffer;
-      channels = stream_.nDeviceChannels[0];
-      format = stream_.deviceFormat[0];
+      channels = stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
     }
     else {
       buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[0];
+      channels = stream_.nUserChannels[1];
       format = stream_.userFormat;
     }
 
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[0])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
-
-    // Write samples to device in interleaved/non-interleaved format.
-    if (stream_.deInterleave[0]) {
+    // Read samples from device in interleaved/non-interleaved format.
+    if (stream_.deInterleave[1]) {
       void *bufs[channels];
       size_t offset = stream_.bufferSize * formatBytes(format);
       for (int i=0; i<channels; i++)
         bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_writen(handle[0], bufs, stream_.bufferSize);
+      err = snd_pcm_readn(handle[1], bufs, stream_.bufferSize);
     }
     else
-      err = snd_pcm_writei(handle[0], buffer, stream_.bufferSize);
+      err = snd_pcm_readi(handle[1], buffer, stream_.bufferSize);
 
     if (err < stream_.bufferSize) {
       // Either an error or underrun occured.
       if (err == -EPIPE) {
-        snd_pcm_state_t state = snd_pcm_state(handle[0]);
+        snd_pcm_state_t state = snd_pcm_state(handle[1]);
         if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message_, "RtApiAlsa: underrun detected.");
+          sprintf(message_, "RtApiAlsa: overrun detected.");
           error(RtError::WARNING);
-          err = snd_pcm_prepare(handle[0]);
+          err = snd_pcm_prepare(handle[1]);
           if (err < 0) {
-            sprintf(message_, "RtApiAlsa: error preparing handle after underrun: %s.",
+            sprintf(message_, "RtApiAlsa: error preparing handle after overrun: %s.",
                     snd_strerror(err));
             MUTEX_UNLOCK(&stream_.mutex);
             error(RtError::DRIVER_ERROR);
@@ -4181,49 +4451,68 @@ void RtApiAlsa :: tickStream()
         goto unlock;
       }
       else {
-        sprintf(message_, "RtApiAlsa: audio write error for device (%s): %s.",
-                devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
+        sprintf(message_, "RtApiAlsa: audio read error for device (%s): %s.",
+                devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
         MUTEX_UNLOCK(&stream_.mutex);
         error(RtError::DRIVER_ERROR);
       }
     }
-  }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+    // Do byte swapping if necessary.
+    if (stream_.doByteSwap[1])
+      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
 
-    // Setup parameters.
-    if (stream_.doConvertBuffer[1]) {
+    // Do buffer conversion if necessary.
+    if (stream_.doConvertBuffer[1])
+      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+  }
+
+  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+
+    // Setup parameters and do buffer conversion if necessary.
+    if (stream_.doConvertBuffer[0]) {
       buffer = stream_.deviceBuffer;
-      channels = stream_.nDeviceChannels[1];
-      format = stream_.deviceFormat[1];
+      if ( stream_.mode == DUPLEX )
+        convertBuffer( buffer, apiInfo->tempBuffer, stream_.convertInfo[0] );
+      else
+        convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
+      channels = stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
     }
     else {
-      buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[1];
+      if ( stream_.mode == DUPLEX )
+        buffer = apiInfo->tempBuffer;
+      else
+        buffer = stream_.userBuffer;
+      channels = stream_.nUserChannels[0];
       format = stream_.userFormat;
     }
 
-    // Read samples from device in interleaved/non-interleaved format.
-    if (stream_.deInterleave[1]) {
+    // Do byte swapping if necessary.
+    if (stream_.doByteSwap[0])
+      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
+
+    // Write samples to device in interleaved/non-interleaved format.
+    if (stream_.deInterleave[0]) {
       void *bufs[channels];
       size_t offset = stream_.bufferSize * formatBytes(format);
       for (int i=0; i<channels; i++)
         bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_readn(handle[1], bufs, stream_.bufferSize);
+      err = snd_pcm_writen(handle[0], bufs, stream_.bufferSize);
     }
     else
-      err = snd_pcm_readi(handle[1], buffer, stream_.bufferSize);
+      err = snd_pcm_writei(handle[0], buffer, stream_.bufferSize);
 
     if (err < stream_.bufferSize) {
       // Either an error or underrun occured.
       if (err == -EPIPE) {
-        snd_pcm_state_t state = snd_pcm_state(handle[1]);
+        snd_pcm_state_t state = snd_pcm_state(handle[0]);
         if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message_, "RtApiAlsa: overrun detected.");
+          sprintf(message_, "RtApiAlsa: underrun detected.");
           error(RtError::WARNING);
-          err = snd_pcm_prepare(handle[1]);
+          err = snd_pcm_prepare(handle[0]);
           if (err < 0) {
-            sprintf(message_, "RtApiAlsa: error preparing handle after overrun: %s.",
+            sprintf(message_, "RtApiAlsa: error preparing handle after underrun: %s.",
                     snd_strerror(err));
             MUTEX_UNLOCK(&stream_.mutex);
             error(RtError::DRIVER_ERROR);
@@ -4238,20 +4527,12 @@ void RtApiAlsa :: tickStream()
         goto unlock;
       }
       else {
-        sprintf(message_, "RtApiAlsa: audio read error for device (%s): %s.",
-                devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
+        sprintf(message_, "RtApiAlsa: audio write error for device (%s): %s.",
+                devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
         MUTEX_UNLOCK(&stream_.mutex);
         error(RtError::DRIVER_ERROR);
       }
     }
-
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[1])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
-
-    // Do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[1])
-      convertStreamBuffer(INPUT);
   }
 
  unlock:
@@ -4375,8 +4656,34 @@ struct AsioHandle {
     :stopStream(false), bufferInfos(0) {}
 };
 
+static const char*GetAsioErrorString(ASIOError result)
+{
+  struct Messages 
+  {
+    ASIOError value;
+    const char*message;
+  };
+  static Messages m[] = 
+  {
+    {   ASE_NotPresent,    "Hardware input or output is not present or available." },
+    {   ASE_HWMalfunction,  "Hardware is malfunctioning." },
+    {   ASE_InvalidParameter, "Invalid input parameter." },
+    {   ASE_InvalidMode,      "Invalid mode." },
+    {   ASE_SPNotAdvancing,     "Sample position not advancing." },
+    {   ASE_NoClock,            "Sample clock or rate cannot be determined or is not present." },
+    {   ASE_NoMemory,           "Not enough memory to complete the request." }
+  };
+
+  for (int i = 0; i < sizeof(m)/sizeof(m[0]); ++i)
+  {
+    if (m[i].value == result) return m[i].message;
+  }
+  return "Unknown error.";
+}
+
 RtApiAsio :: RtApiAsio()
 {
+  this->coInitialized = false;
   this->initialize();
 
   if (nDevices_ <= 0) {
@@ -4388,10 +4695,26 @@ RtApiAsio :: RtApiAsio()
 RtApiAsio :: ~RtApiAsio()
 {
   if ( stream_.mode != UNINITIALIZED ) closeStream();
+  if ( coInitialized )
+  {
+    CoUninitialize();
+  }
+
 }
 
 void RtApiAsio :: initialize(void)
 {
+
+  // ASIO cannot run on a multi-threaded appartment. You can call CoInitialize beforehand, but it must be 
+  // for appartment threading (in which case, CoInitilialize will return S_FALSE here).
+  coInitialized = false;
+  HRESULT hr = CoInitialize(NULL); 
+  if (FAILED(hr))
+  {
+    sprintf(message_,"RtApiAsio: ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)");
+  }
+  coInitialized = true;
+
   nDevices_ = drivers.asioGetNumDev();
   if (nDevices_ <= 0) return;
 
@@ -4435,16 +4758,8 @@ void RtApiAsio :: probeDeviceInfo(RtApiDevice *info)
 
   ASIOError result = ASIOInit( &driverInfo );
   if ( result != ASE_OK ) {
-    char details[32];
-    if ( result == ASE_HWMalfunction )
-      sprintf(details, "hardware malfunction");
-    else if ( result == ASE_NoMemory )
-      sprintf(details, "no memory");
-    else if ( result == ASE_NotPresent )
-      sprintf(details, "driver/hardware not present");
-    else
-      sprintf(details, "unspecified");
-    sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", details, info->name.c_str());
+    sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", 
+      GetAsioErrorString(result), info->name.c_str());
     error(RtError::DEBUG_WARNING);
     return;
   }
@@ -4454,7 +4769,9 @@ void RtApiAsio :: probeDeviceInfo(RtApiDevice *info)
   result = ASIOGetChannels( &inputChannels, &outputChannels );
   if ( result != ASE_OK ) {
     drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error getting input/output channel count (%s).", info->name.c_str());
+    sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).", 
+      GetAsioErrorString(result), 
+      info->name.c_str());
     error(RtError::DEBUG_WARNING);
     return;
   }
@@ -4497,7 +4814,9 @@ void RtApiAsio :: probeDeviceInfo(RtApiDevice *info)
   result = ASIOGetChannelInfo( &channelInfo );
   if ( result != ASE_OK ) {
     drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error getting driver (%s) channel information.", info->name.c_str());
+    sprintf(message_, "RtApiAsio: error (%s) getting driver (%s) channel information.", 
+      GetAsioErrorString(result), 
+      info->name.c_str());
     error(RtError::DEBUG_WARNING);
     return;
   }
@@ -4641,23 +4960,16 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
   ASIOError result;
   if ( mode != INPUT || stream_.mode != OUTPUT ) {
     if ( !drivers.loadDriver( (char *)devices_[device].name.c_str() ) ) {
-      sprintf(message_, "RtApiAsio: error loading driver (%s).", devices_[device].name.c_str());
+      sprintf(message_, "RtApiAsio: error loading driver (%s).", 
+        devices_[device].name.c_str());
       error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
     result = ASIOInit( &driverInfo );
     if ( result != ASE_OK ) {
-      char details[32];
-      if ( result == ASE_HWMalfunction )
-        sprintf(details, "hardware malfunction");
-      else if ( result == ASE_NoMemory )
-        sprintf(details, "no memory");
-      else if ( result == ASE_NotPresent )
-        sprintf(details, "driver/hardware not present");
-      else
-        sprintf(details, "unspecified");
-      sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", details, devices_[device].name.c_str());
+      sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", 
+        GetAsioErrorString(result), devices_[device].name.c_str());
       error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
@@ -4668,8 +4980,9 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
   result = ASIOGetChannels( &inputChannels, &outputChannels );
   if ( result != ASE_OK ) {
     drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error getting input/output channel count (%s).",
-            devices_[device].name.c_str());
+    sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).",
+      GetAsioErrorString(result), 
+      devices_[device].name.c_str());
     error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
@@ -4755,8 +5068,9 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
   if ( result != ASE_OK ) {
     drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) error getting buffer size.",
-            devices_[device].name.c_str());
+    sprintf(message_, "RtApiAsio: error (%s) on driver (%s) error getting buffer size.",
+        GetAsioErrorString(result), 
+        devices_[device].name.c_str());
     error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
@@ -4770,8 +5084,14 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
     if ( *bufferSize < minSize ) *bufferSize = minSize;
     else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
     else *bufferSize = preferSize;
+  } else if (granularity != 0)
+  {
+    // to an even multiple of granularity, rounding up.
+    *bufferSize = (*bufferSize + granularity-1)/granularity*granularity;
   }
 
+
+
   if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize )
     std::cerr << "Possible input/output buffersize discrepancy!" << std::endl;
 
@@ -4794,10 +5114,10 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
     }
     handle->bufferInfos = 0;
     // Create a manual-reset event.
-    handle->condition = CreateEvent(NULL,  // no security
-                                    TRUE,  // manual-reset
-                                    FALSE, // non-signaled initially
-                                    NULL); // unnamed
+    handle->condition = CreateEvent( NULL,  // no security
+                                     TRUE,  // manual-reset
+                                     FALSE, // non-signaled initially
+                                     NULL ); // unnamed
     stream_.apiHandle = (void *) handle;
   }
 
@@ -4837,8 +5157,9 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
   asioCallbacks.bufferSwitchTimeInfo = NULL;
   result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks);
   if ( result != ASE_OK ) {
-    sprintf(message_, "RtApiAsio: driver (%s) error creating buffers.",
-            devices_[device].name.c_str());
+    sprintf(message_, "RtApiAsio: eror (%s) on driver (%s) error creating buffers.",
+      GetAsioErrorString(result), 
+      devices_[device].name.c_str());
     goto error;
   }
 
@@ -4864,8 +5185,9 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
     if (stream_.userBuffer) free(stream_.userBuffer);
     stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
     if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiAsio: error allocating user buffer memory (%s).",
-              devices_[device].name.c_str());
+      sprintf(message_, "RtApiAsio: error (%s) allocating user buffer memory (%s).",
+        GetAsioErrorString(result), 
+        devices_[device].name.c_str());
       goto error;
     }
   }
@@ -4889,7 +5211,8 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
       if (stream_.deviceBuffer) free(stream_.deviceBuffer);
       stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
       if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiAsio: error allocating device buffer memory (%s).",
+        sprintf(message_, "RtApiAsio: error (%s) allocating device buffer memory (%s).",
+          GetAsioErrorString(result), 
                 devices_[device].name.c_str());
         goto error;
       }
@@ -4907,6 +5230,49 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
   asioCallbackInfo = &stream_.callbackInfo;
   stream_.callbackInfo.object = (void *) this;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
@@ -4926,7 +5292,7 @@ bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -5108,7 +5474,7 @@ void RtApiAsio :: callbackEvent(long bufferIndex)
     bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[0]);
     if (stream_.doConvertBuffer[0]) {
 
-      convertStreamBuffer(OUTPUT);
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] );
       if ( stream_.doByteSwap[0] )
         byteSwapBuffer(stream_.deviceBuffer,
                        stream_.bufferSize * stream_.nDeviceChannels[0],
@@ -5156,7 +5522,7 @@ void RtApiAsio :: callbackEvent(long bufferIndex)
         byteSwapBuffer(stream_.deviceBuffer,
                        stream_.bufferSize * stream_.nDeviceChannels[1],
                        stream_.deviceFormat[1]);
-      convertStreamBuffer(INPUT);
+      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
 
     }
     else { // single channel only
@@ -5179,6 +5545,11 @@ void RtApiAsio :: callbackEvent(long bufferIndex)
   if ( !info->usingCallback )
     SetEvent( handle->condition );
 
+  // The following call was suggested by Malte Clasen.  While the API
+  // documentation indicates it should not be required, some device
+  // drivers apparently do not function correctly without it.
+  ASIOOutputReady();
+
   MUTEX_UNLOCK(&stream_.mutex);
 }
 
@@ -5187,7 +5558,63 @@ void RtApiAsio :: callbackEvent(long bufferIndex)
 
 #if defined(__WINDOWS_DS__) // Windows DirectSound API
 
+
 #include <dsound.h>
+#include <assert.h>
+
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768
+
+
+#ifdef _MSC_VER // if Microsoft Visual C++
+#pragma comment(lib,"winmm.lib") // then, auto-link winmm.lib. Otherwise, it has to be added manually.
+#endif
+
+
+static inline DWORD 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;
+  if (laterPointer < earlierPointer)
+  {
+    laterPointer += bufferSize;
+  }
+  if (pointer < earlierPointer)
+  {
+    pointer += bufferSize;
+  }
+  return pointer >= earlierPointer && pointer < laterPointer;
+}
+
+
+#undef GENERATE_DEBUG_LOG // Define this to generate a debug timing log file in c:/rtaudiolog.txt"
+#ifdef GENERATE_DEBUG_LOG
+
+#include "mmsystem.h"
+#include "fstream"
+
+struct TTickRecord
+{
+  DWORD currentReadPointer, safeReadPointer;
+  DWORD currentWritePointer, safeWritePointer;
+  DWORD readTime, writeTime;
+  DWORD nextWritePointer, nextReadPointer;
+};
+
+int currentDebugLogEntry = 0;
+std::vector<TTickRecord> debugLog(2000);
+
+
+#endif
 
 // A structure to hold various information related to the DirectSound
 // API implementation.
@@ -5195,8 +5622,33 @@ struct DsHandle {
   void *object;
   void *buffer;
   UINT bufferPointer;  
+  DWORD dsBufferSize;
+  DWORD dsPointerLeadTime; // the number of bytes ahead of the safe pointer to lead by.
 };
 
+
+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,
@@ -5232,6 +5684,14 @@ struct enum_info {
 
 RtApiDs :: RtApiDs()
 {
+  // Dsound will run both-threaded. If CoInitialize fails, then just accept whatever the mainline 
+  // chose for a threading model.
+  coInitialized = false;
+  HRESULT hr = CoInitialize(NULL);
+  if (!FAILED(hr)) {
+    coInitialized = true;
+  }
+
   this->initialize();
 
   if (nDevices_ <= 0) {
@@ -5242,6 +5702,10 @@ RtApiDs :: RtApiDs()
 
 RtApiDs :: ~RtApiDs()
 {
+  if (coInitialized)
+  {
+    CoUninitialize(); // balanced call.
+  }
   if ( stream_.mode != UNINITIALIZED ) closeStream();
 }
 
@@ -5364,7 +5828,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
   if ( FAILED(result) ) {
     sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.",
             getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -5377,7 +5841,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
   if ( FAILED(result) ) {
     sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.",
             info->name.c_str(), getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto playback_probe;
   }
 
@@ -5388,7 +5852,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
     input->Release();
     sprintf(message_, "RtApiDs: Could not get capture capabilities (%s): %s.",
             info->name.c_str(), getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto playback_probe;
   }
 
@@ -5449,7 +5913,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
   if ( FAILED(result) ) {
     sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.",
             getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return;
   }
 
@@ -5463,7 +5927,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
   if ( FAILED(result) ) {
     sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.",
             info->name.c_str(), getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto check_parameters;
   }
 
@@ -5473,7 +5937,7 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
     output->Release();
     sprintf(message_, "RtApiDs: Could not get playback capabilities (%s): %s.",
             info->name.c_str(), getErrorString(result));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     goto check_parameters;
   }
 
@@ -5485,17 +5949,31 @@ void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
   // if it exists.
   if ( info->sampleRates.size() == 0 ) {
     info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate );
-    info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate );
+    if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate )
+      info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate );
   }
   else {
-    // Check input rates against output rate range.
-    for ( int i=info->sampleRates.size()-1; i>=0; i-- ) {
-      if ( (unsigned int) info->sampleRates[i] > out_caps.dwMaxSecondarySampleRate )
-        info->sampleRates.erase( info->sampleRates.begin() + i );
+    // Check input rates against output rate range.  If there's an
+    // inconsistency (such as a duplex-capable device which reports a
+    // single output rate of 48000 Hz), we'll go with the output
+    // rate(s) since the DirectSoundCapture API is stupid and broken.
+    // Note that the probed sample rate values are NOT used when
+    // opening the device.  Thanks to Tue Andersen for reporting this.
+    if ( info->sampleRates.back() < (int) out_caps.dwMinSecondarySampleRate ) {
+      info->sampleRates.clear();
+      info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate );
+      if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate )
+        info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate );
     }
-    while ( info->sampleRates.size() > 0 &&
-            ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) {
-      info->sampleRates.erase( info->sampleRates.begin() );
+    else {
+      for ( int i=info->sampleRates.size()-1; i>=0; i-- ) {
+        if ( (unsigned int) info->sampleRates[i] > out_caps.dwMaxSecondarySampleRate )
+          info->sampleRates.erase( info->sampleRates.begin() + i );
+      }
+      while ( info->sampleRates.size() > 0 &&
+              ((unsigned int) info->sampleRates[0] < out_caps.dwMinSecondarySampleRate) ) {
+        info->sampleRates.erase( info->sampleRates.begin() );
+      }
     }
   }
 
@@ -5595,11 +6073,32 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
   waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
   waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
 
+  // Determine the device buffer size. By default, 32k, 
+  // but we will grow it to make allowances for very large softare buffer sizes.
+  DWORD dsBufferSize = 0;
+  DWORD dsPointerLeadTime = 0;
+
+  buffer_size = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
+
+
+  // poisonously large buffer lead time? Then increase the device buffer size accordingly.
+  while (dsPointerLeadTime *2U > (DWORD)buffer_size)
+  {
+    buffer_size *= 2;
+  }
+
+
+
   enum_info dsinfo;
   void *ohandle = 0, *bhandle = 0;
   strncpy( dsinfo.name, devices_[device].name.c_str(), 64 );
   dsinfo.isValid = false;
   if ( mode == OUTPUT ) {
+    dsPointerLeadTime = (numberOfBuffers) * 
+      (*bufferSize) * 
+      (waveFormat.wBitsPerSample / 8)
+      *channels;
+
 
     if ( devices_[device].maxOutputChannels < channels ) {
       sprintf(message_, "RtApiDs: requested channels (%d) > than supported (%d) by device (%s).",
@@ -5642,7 +6141,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       object->Release();
       sprintf(message_, "RtApiDs: Unable to set cooperative level (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5659,7 +6158,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       object->Release();
       sprintf(message_, "RtApiDs: Unable to access primary buffer (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5669,12 +6168,12 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       object->Release();
       sprintf(message_, "RtApiDs: Unable to set primary buffer format (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
     // Setup the secondary DS buffer description.
-    buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
+    dsBufferSize = (DWORD)buffer_size;
     ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
     bufferDescription.dwSize = sizeof(DSBUFFERDESC);
     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
@@ -5695,7 +6194,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
         object->Release();
         sprintf(message_, "RtApiDs: Unable to create secondary DS buffer (%s): %s.",
                 devices_[device].name.c_str(), getErrorString(result));
-        error(RtError::WARNING);
+        error(RtError::DEBUG_WARNING);
         return FAILURE;
       }
     }
@@ -5713,7 +6212,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       buffer->Release();
       sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5727,7 +6226,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       buffer->Release();
       sprintf(message_, "RtApiDs: Unable to unlock buffer(%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5738,8 +6237,11 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
 
   if ( mode == INPUT ) {
 
-    if ( devices_[device].maxInputChannels < channels )
+    if ( devices_[device].maxInputChannels < channels ) {
+      sprintf(message_, "RtAudioDS: device (%s) does not support %d channels.", devices_[device].name.c_str(), channels);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
+    }
 
     // Enumerate through input devices to find the id (if it exists).
     result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
@@ -5765,12 +6267,12 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
     if ( FAILED(result) ) {
       sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
     // Setup the secondary DS buffer description.
-    buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
+    dsBufferSize = buffer_size;
     ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC));
     bufferDescription.dwSize = sizeof(DSCBUFFERDESC);
     bufferDescription.dwFlags = 0;
@@ -5784,7 +6286,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       object->Release();
       sprintf(message_, "RtApiDs: Unable to create capture buffer (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5795,7 +6297,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       buffer->Release();
       sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5809,7 +6311,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
       buffer->Release();
       sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.",
               devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -5824,7 +6326,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
   else
     stream_.deviceFormat[mode] = RTAUDIO_SINT16;
   stream_.nUserChannels[mode] = channels;
-  *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8);
+
   stream_.bufferSize = *bufferSize;
 
   // Set flags for buffer conversion
@@ -5896,6 +6398,8 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
     handles = (DsHandle *) stream_.apiHandle;
   handles[mode].object = ohandle;
   handles[mode].buffer = bhandle;
+  handles[mode].dsBufferSize = dsBufferSize;
+  handles[mode].dsPointerLeadTime = dsPointerLeadTime;
 
   stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
@@ -5907,6 +6411,49 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
   stream_.nBuffers = nBuffers;
   stream_.sampleRate = sampleRate;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
@@ -5932,7 +6479,7 @@ bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -6048,11 +6595,41 @@ void RtApiDs :: startStream()
   verifyStream();
   if (stream_.state == STREAM_RUNNING) 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);
 
-  HRESULT result;
+  
   DsHandle *handles = (DsHandle *) stream_.apiHandle;
+
+  timeBeginPeriod(1); 
+
+
+  memset(&statistics,0,sizeof(statistics));
+  statistics.sampleRate = stream_.sampleRate;
+  statistics.writeDeviceBufferLeadBytes = handles[0].dsPointerLeadTime ;
+
+  buffersRolling = false;
+  duplexPrerollBytes = 0;
+
+  if (stream_.mode == DUPLEX)
+  {
+    // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
+    duplexPrerollBytes = (int)(0.5*stream_.sampleRate*formatBytes( stream_.deviceFormat[1])*stream_.nDeviceChannels[1]);
+  }
+
+#ifdef GENERATE_DEBUG_LOG
+  currentDebugLogEntry = 0;
+#endif  
+
+  HRESULT result;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+      statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0])
+                                  *stream_.nDeviceChannels[0];
+
+
     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
     result = buffer->Play(0, 0, DSBPLAY_LOOPING );
     if ( FAILED(result) ) {
@@ -6063,6 +6640,9 @@ void RtApiDs :: startStream()
   }
 
   if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+    statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1])
+                                  *stream_.nDeviceChannels[1];
+
     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
     result = buffer->Start(DSCBSTART_LOOPING );
     if ( FAILED(result) ) {
@@ -6081,11 +6661,31 @@ void RtApiDs :: stopStream()
   verifyStream();
   if (stream_.state == STREAM_STOPPED) return;
 
+
   // Change the state before the lock to improve shutdown response
   // when using a callback.
   stream_.state = STREAM_STOPPED;
   MUTEX_LOCK(&stream_.mutex);
 
+
+  timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows.
+
+#ifdef GENERATE_DEBUG_LOG
+  // write the timing log to a .TSV file for analysis in Excel.
+  unlink("c:/rtaudiolog.txt");
+  std::ofstream os("c:/rtaudiolog.txt");
+  os << "writeTime\treadDelay\tnextWritePointer\tnextReadPointer\tcurrentWritePointer\tsafeWritePointer\tcurrentReadPointer\tsafeReadPointer" << std::endl;
+  for (int i = 0; i < currentDebugLogEntry ; ++i)
+  {
+    TTickRecord &r = debugLog[i];
+    os 
+      << r.writeTime-debugLog[0].writeTime << "\t" << (r.readTime-r.writeTime) << "\t"
+      << r.nextWritePointer % BUFFER_SIZE << "\t" << r.nextReadPointer % BUFFER_SIZE 
+      << "\t" << r.currentWritePointer % BUFFER_SIZE << "\t" << r.safeWritePointer % BUFFER_SIZE 
+      << "\t" << r.currentReadPointer % BUFFER_SIZE << "\t" << r.safeReadPointer % BUFFER_SIZE << std::endl;
+  }
+#endif
+
   // There is no specific DirectSound API call to "drain" a buffer
   // before stopping.  We can hack this for playback by writing zeroes
   // for another bufferSize * nBuffers frames.  For capture, the
@@ -6101,12 +6701,13 @@ void RtApiDs :: stopStream()
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
 
     DWORD currentPos, safePos;
-    long buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0];
-    buffer_bytes *= formatBytes(stream_.deviceFormat[0]);
+    long buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0]
+                      * formatBytes(stream_.deviceFormat[0]);
+
 
     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    UINT nextWritePos = handles[0].bufferPointer;
-    dsBufferSize = buffer_bytes * stream_.nBuffers;
+    long nextWritePos = handles[0].bufferPointer;
+    dsBufferSize = handles[0].dsBufferSize;
 
     // Write zeroes for nBuffer counts.
     for (int i=0; i<stream_.nBuffers; i++) {
@@ -6118,14 +6719,15 @@ void RtApiDs :: stopStream()
                 devices_[stream_.device[0]].name.c_str(), getErrorString(result));
         error(RtError::DRIVER_ERROR);
       }
+      // Chase nextWritePos.
 
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
+      if ( currentPos < (DWORD)nextWritePos ) currentPos += dsBufferSize; // unwrap offset
       DWORD endWrite = nextWritePos + buffer_bytes;
 
       // Check whether the entire write region is behind the play pointer.
       while ( currentPos < endWrite ) {
         double millis = (endWrite - currentPos) * 900.0;
-        millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.sampleRate);
+        millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] *stream_.sampleRate);
         if ( millis < 1.0 ) millis = 1.0;
         Sleep( (DWORD) millis );
 
@@ -6136,7 +6738,7 @@ void RtApiDs :: stopStream()
                   devices_[stream_.device[0]].name.c_str(), getErrorString(result));
           error(RtError::DRIVER_ERROR);
         }
-        if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
+        if ( currentPos < (DWORD)nextWritePos ) currentPos += dsBufferSize; // unwrap offset
       }
 
       // Lock free space in the buffer
@@ -6168,6 +6770,7 @@ void RtApiDs :: stopStream()
   }
 
   if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+
     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
     buffer1 = NULL;
     bufferSize1 = 0;
@@ -6179,8 +6782,7 @@ void RtApiDs :: stopStream()
       error(RtError::DRIVER_ERROR);
     }
 
-    dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers;
+    dsBufferSize = handles[1].dsBufferSize;
 
     // Lock the buffer and clear it so that if we start to play again,
     // we won't have old data playing.
@@ -6225,6 +6827,7 @@ void RtApiDs :: abortStream()
   DWORD dataLen;
   DsHandle *handles = (DsHandle *) stream_.apiHandle;
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
     result = buffer->Stop();
     if ( FAILED(result) ) {
@@ -6233,8 +6836,7 @@ void RtApiDs :: abortStream()
       error(RtError::DRIVER_ERROR);
     }
 
-    dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[0];
-    dsBufferSize *= formatBytes(stream_.deviceFormat[0]) * stream_.nBuffers;
+    dsBufferSize = handles[0].dsBufferSize;
 
     // Lock the buffer and clear it so that if we start to play again,
     // we won't have old data playing.
@@ -6272,8 +6874,7 @@ void RtApiDs :: abortStream()
       error(RtError::DRIVER_ERROR);
     }
 
-    dsBufferSize = stream_.bufferSize * stream_.nDeviceChannels[1];
-    dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers;
+    dsBufferSize = handles[1].dsBufferSize;
 
     // Lock the buffer and clear it so that if we start to play again,
     // we won't have old data playing.
@@ -6320,8 +6921,7 @@ int RtApiDs :: streamWillBlock()
     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
     UINT nextWritePos = handles[0].bufferPointer;
     channels = stream_.nDeviceChannels[0];
-    DWORD dsBufferSize = stream_.bufferSize * channels;
-    dsBufferSize *= formatBytes(stream_.deviceFormat[0]) * stream_.nBuffers;
+    DWORD dsBufferSize = handles[0].dsBufferSize;
 
     // Find out where the read and "safe write" pointers are.
     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
@@ -6331,18 +6931,23 @@ int RtApiDs :: streamWillBlock()
       error(RtError::DRIVER_ERROR);
     }
 
-    if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    frames = currentPos - nextWritePos;
+    DWORD leadPos = safePos + handles[0].dsPointerLeadTime;
+    if (leadPos > dsBufferSize) {
+      leadPos -= dsBufferSize;
+    }
+    if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
+
+    frames = (leadPos - nextWritePos);
     frames /= channels * formatBytes(stream_.deviceFormat[0]);
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if (stream_.mode == INPUT ) {
+      // note that we don't block on DUPLEX input anymore. We run lockstep with the write pointer instead.
 
     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
     UINT nextReadPos = handles[1].bufferPointer;
     channels = stream_.nDeviceChannels[1];
-    DWORD dsBufferSize = stream_.bufferSize * channels;
-    dsBufferSize *= formatBytes(stream_.deviceFormat[1]) * stream_.nBuffers;
+    DWORD dsBufferSize = handles[1].dsBufferSize;
 
     // Find out where the write and "safe read" pointers are.
     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
@@ -6352,18 +6957,10 @@ int RtApiDs :: streamWillBlock()
       error(RtError::DRIVER_ERROR);
     }
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
+    if ( safePos < (DWORD)nextReadPos ) safePos += dsBufferSize; // unwrap offset
 
-    if (stream_.mode == DUPLEX ) {
-      // Take largest value of the two.
-      int temp = safePos - nextReadPos;
-      temp /= channels * formatBytes(stream_.deviceFormat[1]);
-      frames = ( temp > frames ) ? temp : frames;
-    }
-    else {
-      frames = safePos - nextReadPos;
-      frames /= channels * formatBytes(stream_.deviceFormat[1]);
-    }
+    frames = (int)(safePos - nextReadPos);
+    frames /= channels * formatBytes(stream_.deviceFormat[1]);
   }
 
   frames = stream_.bufferSize - frames;
@@ -6396,20 +6993,96 @@ void RtApiDs :: tickStream()
   }
 
   HRESULT result;
-  DWORD currentPos, safePos;
+  DWORD currentWritePos, safeWritePos;
+  DWORD currentReadPos, safeReadPos;
+  DWORD leadPos;
+  UINT nextWritePos;
+
+#ifdef GENERATE_DEBUG_LOG
+  DWORD writeTime, readTime;
+#endif
   LPVOID buffer1 = NULL;
   LPVOID buffer2 = NULL;
   DWORD bufferSize1 = 0;
   DWORD bufferSize2 = 0;
+
   char *buffer;
   long buffer_bytes;
   DsHandle *handles = (DsHandle *) stream_.apiHandle;
+
+  if (stream_.mode == DUPLEX && !buffersRolling)
+  {
+    assert(handles[0].dsBufferSize == handles[1].dsBufferSize);
+
+    // it takes a while for the devices to get rolling. As a result, there's 
+    // no guarantee that the capture and write device pointers will move in lockstep.
+    // Wait here for both devices to start rolling, and then set our buffer pointers accordingly.
+    // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600 bytes later than the write
+    // buffer.
+
+    // Stub: a serious risk of having a pre-emptive scheduling round take place between 
+    // the two GetCurrentPosition calls... but I'm really not sure how to solve the problem.
+    // Temporarily boost to Realtime priority, maybe; but I'm not sure what priority the 
+    // directsound service threads run at. We *should* be roughly within a ms or so of correct.
+
+    LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
+    LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
+
+
+    DWORD initialWritePos, initialSafeWritePos;
+    DWORD initialReadPos, initialSafeReadPos;;
+
+
+    result = dsWriteBuffer->GetCurrentPosition(&initialWritePos, &initialSafeWritePos);
+    if ( FAILED(result) ) {
+      sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.",
+              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
+      error(RtError::DRIVER_ERROR);
+    }
+    result = dsCaptureBuffer->GetCurrentPosition(&initialReadPos, &initialSafeReadPos);
+    if ( FAILED(result) ) {
+      sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.",
+              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
+      error(RtError::DRIVER_ERROR);
+    }
+    while (true)
+    {
+      result = dsWriteBuffer->GetCurrentPosition(&currentWritePos, &safeWritePos);
+      if ( FAILED(result) ) {
+        sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.",
+                devices_[stream_.device[0]].name.c_str(), getErrorString(result));
+        error(RtError::DRIVER_ERROR);
+      }
+      result = dsCaptureBuffer->GetCurrentPosition(&currentReadPos, &safeReadPos);
+      if ( FAILED(result) ) {
+        sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.",
+                devices_[stream_.device[1]].name.c_str(), getErrorString(result));
+        error(RtError::DRIVER_ERROR);
+      }
+      if (safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos)
+      {
+        break;
+      }
+      Sleep(1);
+    }
+
+    assert(handles[0].dsBufferSize == handles[1].dsBufferSize);
+
+    UINT writeBufferLead = (safeWritePos-safeReadPos + handles[0].dsBufferSize) % handles[0].dsBufferSize;
+    buffersRolling = true;
+    handles[0].bufferPointer = (safeWritePos + handles[0].dsPointerLeadTime);
+    handles[1].bufferPointer = safeReadPos;
+
+  }
+
   if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+    
+    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
 
     // Setup parameters and do buffer conversion if necessary.
     if (stream_.doConvertBuffer[0]) {
-      convertStreamBuffer(OUTPUT);
       buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
       buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0];
       buffer_bytes *= formatBytes(stream_.deviceFormat[0]);
     }
@@ -6421,23 +7094,39 @@ void RtApiDs :: tickStream()
 
     // No byte swapping necessary in DirectSound implementation.
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    UINT nextWritePos = handles[0].bufferPointer;
-    DWORD dsBufferSize = buffer_bytes * stream_.nBuffers;
+    // Ahhh ... windoze.  16-bit data is signed but 8-bit data is
+    // unsigned.  So, we need to convert our signed 8-bit data here to
+    // unsigned.
+    if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
+      for ( int i=0; i<buffer_bytes; i++ ) buffer[i] = (unsigned char) (buffer[i] + 128);
 
-    // Find out where the read and "safe write" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+    DWORD dsBufferSize = handles[0].dsBufferSize;
+         nextWritePos = handles[0].bufferPointer;
 
-    if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-    DWORD endWrite = nextWritePos + buffer_bytes;
+    DWORD endWrite;
+    while (true)
+    {
+      // Find out where the read and "safe write" pointers are.
+      result = dsBuffer->GetCurrentPosition(&currentWritePos, &safeWritePos);
+      if ( FAILED(result) ) {
+        sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.",
+                devices_[stream_.device[0]].name.c_str(), getErrorString(result));
+        error(RtError::DRIVER_ERROR);
+      }
+
+      leadPos = safeWritePos + handles[0].dsPointerLeadTime;
+      if (leadPos > dsBufferSize) {
+          leadPos -= dsBufferSize;
+      }
+      if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
+
+
+      endWrite = nextWritePos + buffer_bytes;
 
     // Check whether the entire write region is behind the play pointer.
-    while ( currentPos < endWrite ) {
+
+      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
@@ -6447,21 +7136,38 @@ void RtApiDs :: tickStream()
       // 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 - currentPos) * 900.0;
-      millis /= ( formatBytes(stream_.deviceFormat[0]) * stream_.sampleRate);
+      double millis = (endWrite - leadPos) * 900.0;
+      millis /= ( formatBytes(stream_.deviceFormat[0]) *stream_.nDeviceChannels[0]* stream_.sampleRate);
       if ( millis < 1.0 ) millis = 1.0;
-      Sleep( (DWORD) millis );
-
-      // Wake up, find out where we are now
-      result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-      if ( FAILED(result) ) {
-        sprintf(message_, "RtApiDs: Unable to get current position (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-        error(RtError::DRIVER_ERROR);
+      if (millis > 50.0) {
+        static int nOverruns = 0;
+        ++nOverruns;
       }
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
+      Sleep( (DWORD) millis );
+      // Sleep( (DWORD) 2);
     }
-
+#ifdef GENERATE_DEBUG_LOG
+    writeTime = timeGetTime();
+#endif
+    if (statistics.writeDeviceSafeLeadBytes < dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize))
+    {
+      statistics.writeDeviceSafeLeadBytes = dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize);
+    }
+
+    if (
+      dsPointerBetween(nextWritePos,safeWritePos,currentWritePos,dsBufferSize)
+      || dsPointerBetween(endWrite,safeWritePos,currentWritePos,dsBufferSize)
+    )
+    { 
+      // we've strayed into the forbidden zone. 
+      // resync the read pointer.
+      ++statistics.numberOfWriteUnderruns;
+      nextWritePos = safeWritePos + handles[0].dsPointerLeadTime-buffer_bytes+dsBufferSize;
+      while (nextWritePos >= dsBufferSize) nextWritePos-= dsBufferSize;
+      handles[0].bufferPointer = nextWritePos;
+      endWrite = nextWritePos + buffer_bytes;
+    }
+    
     // Lock free space in the buffer
     result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,
                              &bufferSize1, &buffer2, &bufferSize2, 0);
@@ -6499,39 +7205,93 @@ void RtApiDs :: tickStream()
       buffer_bytes = stream_.bufferSize * stream_.nUserChannels[1];
       buffer_bytes *= formatBytes(stream_.userFormat);
     }
-
     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    UINT nextReadPos = handles[1].bufferPointer;
-    DWORD dsBufferSize = buffer_bytes * stream_.nBuffers;
+    long nextReadPos = handles[1].bufferPointer;
+    DWORD dsBufferSize = handles[1].dsBufferSize;
 
     // Find out where the write and "safe read" pointers are.
-    result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
+    result = dsBuffer->GetCurrentPosition(&currentReadPos, &safeReadPos);
     if ( FAILED(result) ) {
       sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.",
               devices_[stream_.device[1]].name.c_str(), getErrorString(result));
       error(RtError::DRIVER_ERROR);
     }
 
-    if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
+    if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
     DWORD endRead = nextReadPos + buffer_bytes;
 
-    // Check whether the entire write region is behind the play pointer.
-    while ( safePos < endRead ) {
-      // See comments for playback.
-      double millis = (endRead - safePos) * 900.0;
-      millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.sampleRate);
-      if ( millis < 1.0 ) millis = 1.0;
-      Sleep( (DWORD) millis );
+    // Handling depends on whether we are INPUT or DUPLEX. 
+    // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
+    // then a wait here will drag the write pointers into the forbidden zone.
+    // 
+    // In DUPLEX mode, rather than wait, we will back off the read pointer until 
+    // it's in a safe position. This causes dropouts, but it seems to be the only 
+    // practical way to sync up the read and write pointers reliably, given the 
+    // the very complex relationship between phase and increment of the read and write 
+    // pointers.
+    //
+    // In order to minimize audible dropouts in DUPLEX mode, we will provide a pre-roll 
+    //  period of 0.5 seconds
+    // in which we return zeros from the read buffer while the pointers sync up.
+
+    if (stream_.mode == DUPLEX)
+    {
+      if (safeReadPos < endRead) 
+      {
+        if (duplexPrerollBytes <= 0)
+        {
+          // pre-roll time over. Be more agressive.
+          int adjustment = endRead-safeReadPos;
+
+          ++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*buffer_bytes)  
+          {
+            nextReadPos = safeReadPos-2*buffer_bytes;
+          } else 
+          {
+            nextReadPos = safeReadPos-buffer_bytes-adjustment;
+          }
+          statistics.readDeviceSafeLeadBytes =  currentReadPos-nextReadPos;
+          if (statistics.readDeviceSafeLeadBytes  < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
 
-      // Wake up, find out where we are now
-      result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
-      if ( FAILED(result) ) {
-        sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.",
-                devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-        error(RtError::DRIVER_ERROR);
+          if (nextReadPos < 0) nextReadPos += dsBufferSize;
+
+        } else {
+          // in pre=roll time. Just do it.
+          nextReadPos = safeReadPos-buffer_bytes;
+          while (nextReadPos < 0) nextReadPos += dsBufferSize;
+        }
+        endRead = nextReadPos + buffer_bytes;
       }
+    } else {
+      while ( safeReadPos < endRead ) {
+        // See comments for playback.
+        double millis = (endRead - safeReadPos) * 900.0;
+        millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
+        if ( millis < 1.0 ) millis = 1.0;
+        Sleep( (DWORD) millis );
+
+        // Wake up, find out where we are now
+        result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+        if ( FAILED(result) ) {
+          sprintf(message_, "RtApiDs: Unable to get current capture position (%s): %s.",
+                  devices_[stream_.device[1]].name.c_str(), getErrorString(result));
+          error(RtError::DRIVER_ERROR);
+        }
       
-      if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
+        if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+      }
+    }
+#ifdef GENERATE_DEBUG_LOG
+    readTime = timeGetTime();
+#endif
+    if (statistics.readDeviceSafeLeadBytes < dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize))
+    {
+      statistics.readDeviceSafeLeadBytes = dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize);
     }
 
     // Lock free space in the buffer
@@ -6543,9 +7303,16 @@ void RtApiDs :: tickStream()
       error(RtError::DRIVER_ERROR);
     }
 
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer, buffer1, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);
+    if (duplexPrerollBytes <= 0)
+    {
+      // Copy our buffer into the DS buffer
+      CopyMemory(buffer, buffer1, bufferSize1);
+      if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);
+    } else {
+      memset(buffer,0,bufferSize1);
+      if (buffer2 != NULL) memset(buffer+bufferSize1,0,bufferSize2);
+      duplexPrerollBytes -= bufferSize1 + bufferSize2;
+    }
 
     // Update our buffer offset and unlock sound buffer
     nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize;
@@ -6557,19 +7324,38 @@ void RtApiDs :: tickStream()
     }
     handles[1].bufferPointer = nextReadPos;
 
+
     // No byte swapping necessary in DirectSound implementation.
 
+    // If necessary, convert 8-bit data from unsigned to signed.
+    if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
+      for ( int j=0; j<buffer_bytes; j++ ) buffer[j] = (signed char) (buffer[j] - 128);
+
     // Do buffer conversion if necessary.
     if (stream_.doConvertBuffer[1])
-      convertStreamBuffer(INPUT);
+      convertBuffer( stream_.userBuffer, 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
+
 
   MUTEX_UNLOCK(&stream_.mutex);
 
   if (stream_.callbackInfo.usingCallback && stopStream)
     this->stopStream();
 }
-
 // Definitions for utility functions and callbacks
 // specific to the DirectSound implementation.
 
@@ -6687,52 +7473,52 @@ static char* getErrorString(int code)
        switch (code) {
 
   case DSERR_ALLOCATED:
-    return "Direct Sound already allocated";
+    return "Already allocated.";
 
   case DSERR_CONTROLUNAVAIL:
-    return "Direct Sound control unavailable";
+    return "Control unavailable.";
 
   case DSERR_INVALIDPARAM:
-    return "Direct Sound invalid parameter";
+    return "Invalid parameter.";
 
   case DSERR_INVALIDCALL:
-    return "Direct Sound invalid call";
+    return "Invalid call.";
 
   case DSERR_GENERIC:
-    return "Direct Sound generic error";
+    return "Generic error.";
 
   case DSERR_PRIOLEVELNEEDED:
-    return "Direct Sound Priority level needed";
+    return "Priority level needed";
 
   case DSERR_OUTOFMEMORY:
-    return "Direct Sound out of memory";
+    return "Out of memory";
 
   case DSERR_BADFORMAT:
-    return "Direct Sound bad format";
+    return "The sample rate or the channel format is not supported.";
 
   case DSERR_UNSUPPORTED:
-    return "Direct Sound unsupported error";
+    return "Not supported.";
 
   case DSERR_NODRIVER:
-    return "Direct Sound no driver error";
+    return "No driver.";
 
   case DSERR_ALREADYINITIALIZED:
-    return "Direct Sound already initialized";
+    return "Already initialized.";
 
   case DSERR_NOAGGREGATION:
-    return "Direct Sound no aggregation";
+    return "No aggregation.";
 
   case DSERR_BUFFERLOST:
-    return "Direct Sound buffer lost";
+    return "Buffer lost.";
 
   case DSERR_OTHERAPPHASPRIO:
-    return "Direct Sound other app has priority";
+    return "Another application already has priority.";
 
   case DSERR_UNINITIALIZED:
-    return "Direct Sound uninitialized";
+    return "Uninitialized.";
 
   default:
-    return "Direct Sound unknown error";
+    return "DirectSound unknown error";
        }
 }
 
@@ -6905,7 +7691,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info)
     if (result < 0) {
       sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.",
               info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
     }
     else {
       info->maxOutputChannels = value.i;
@@ -6916,7 +7702,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info)
     if (result < 0) {
       sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.",
               info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
     }
     else {
       info->sampleRates.clear();
@@ -6939,7 +7725,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info)
     if (result < 0) {
       sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.",
               info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
     }
     else {
       info->maxInputChannels = value.i;
@@ -6950,7 +7736,7 @@ void RtApiAl :: probeDeviceInfo(RtApiDevice *info)
     if (result < 0) {
       sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.",
               info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
     }
     else {
       // In the case of the default device, these values will
@@ -7007,7 +7793,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
   if ( !al_config ) {
     sprintf(message_,"RtApiAl: can't get AL config: %s.",
             alGetErrorString(oserror()));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -7017,7 +7803,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
     alFreeConfig(al_config);
     sprintf(message_,"RtApiAl: can't set %d channels in AL config: %s.",
             channels, alGetErrorString(oserror()));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -7039,7 +7825,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: can't set buffer size (%ld) in AL config: %s.",
               buffer_size, alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
     *bufferSize = buffer_size / nBuffers;
@@ -7078,7 +7864,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
     alFreeConfig(al_config);
     sprintf(message_,"RtApiAl: error setting sample format in AL config: %s.",
             alGetErrorString(oserror()));
-    error(RtError::WARNING);
+    error(RtError::DEBUG_WARNING);
     return FAILURE;
   }
 
@@ -7094,7 +7880,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.",
               devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -7104,7 +7890,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error opening output port: %s.",
               alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -7119,7 +7905,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.",
               sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
   }
@@ -7135,7 +7921,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.",
               devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -7145,7 +7931,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error opening input port: %s.",
               alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
 
@@ -7160,7 +7946,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
       alFreeConfig(al_config);
       sprintf(message_,"RtApiAl: error setting sample rate (%d) for device (%s): %s.",
               sampleRate, devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::WARNING);
+      error(RtError::DEBUG_WARNING);
       return FAILURE;
     }
   }
@@ -7246,6 +8032,49 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
   stream_.bufferSize = *bufferSize;
   stream_.sampleRate = sampleRate;
 
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) {
+    if (mode == INPUT) { // convert device to user buffer
+      stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
+      stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
+      stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
+      stream_.convertInfo[mode].outFormat = stream_.userFormat;
+    }
+    else { // convert user to device buffer
+      stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
+      stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
+      stream_.convertInfo[mode].inFormat = stream_.userFormat;
+      stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
+    }
+
+    if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
+    else
+      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+
+    // Set up the interleave/deinterleave offsets.
+    if ( mode == INPUT && stream_.deInterleave[1] ) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else if (mode == OUTPUT && stream_.deInterleave[0]) {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+    else {
+      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+  }
+
   return SUCCESS;
 
  error:
@@ -7263,7 +8092,7 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
     stream_.userBuffer = 0;
   }
 
-  error(RtError::WARNING);
+  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
@@ -7449,8 +8278,8 @@ void RtApiAl :: tickStream()
 
     // Setup parameters and do buffer conversion if necessary.
     if (stream_.doConvertBuffer[0]) {
-      convertStreamBuffer(OUTPUT);
       buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
       channels = stream_.nDeviceChannels[0];
       format = stream_.deviceFormat[0];
     }
@@ -7491,7 +8320,7 @@ void RtApiAl :: tickStream()
 
     // Do buffer conversion if necessary.
     if (stream_.doConvertBuffer[1])
-      convertStreamBuffer(INPUT);
+      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
   }
 
  unlock:
@@ -7608,7 +8437,7 @@ void RtApi :: error(RtError::Type type)
 void RtApi :: verifyStream()
 {
   if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtAudio: a stream was not previously opened!");
+    sprintf(message_, "RtAudio: stream is not open!");
     error(RtError::INVALID_STREAM);
   }
 }
@@ -7666,470 +8495,424 @@ int RtApi :: formatBytes(RtAudioFormat format)
   return 0;
 }
 
-void RtApi :: convertStreamBuffer( StreamMode mode )
+void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
 {
-  // This method does format conversion, input/output channel compensation, and
+  // This function does format conversion, input/output channel compensation, and
   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy
   // the upper three bytes of a 32-bit integer.
 
-  int j, jump_in, jump_out, channels;
-  RtAudioFormat format_in, format_out;
-  char *input, *output;
-
-  if (mode == INPUT) { // convert device to user buffer
-    input = stream_.deviceBuffer;
-    output = stream_.userBuffer;
-    jump_in = stream_.nDeviceChannels[1];
-    jump_out = stream_.nUserChannels[1];
-    format_in = stream_.deviceFormat[1];
-    format_out = stream_.userFormat;
-  }
-  else { // convert user to device buffer
-    input = stream_.userBuffer;
-    output = stream_.deviceBuffer;
-    jump_in = stream_.nUserChannels[0];
-    jump_out = stream_.nDeviceChannels[0];
-    format_in = stream_.userFormat;
-    format_out = stream_.deviceFormat[0];
-
-    // clear our device buffer when in/out duplex device channels are different
-    if ( stream_.mode == DUPLEX &&
-         stream_.nDeviceChannels[0] != stream_.nDeviceChannels[1] )
-      memset(output, 0, stream_.bufferSize * jump_out * formatBytes(format_out));
-  }
-
-  channels = (jump_in < jump_out) ? jump_in : jump_out;
-
-  // Set up the interleave/deinterleave offsets
-  std::vector<int> offset_in(channels);
-  std::vector<int> offset_out(channels);
-  if (mode == INPUT && stream_.deInterleave[1]) {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k * stream_.bufferSize;
-      offset_out[k] = k;
-      jump_in = 1;
-    }
-  }
-  else if (mode == OUTPUT && stream_.deInterleave[0]) {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k;
-      offset_out[k] = k * stream_.bufferSize;
-      jump_out = 1;
-    }
-  }
-  else {
-    for (int k=0; k<channels; k++) {
-      offset_in[k] = k;
-      offset_out[k] = k;
-    }
-  }
+  // Clear our device buffer when in/out duplex device channels are different
+  if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
+       stream_.nDeviceChannels[0] != stream_.nDeviceChannels[1] )
+    memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
 
-  if (format_out == RTAUDIO_FLOAT64) {
+  int j;
+  if (info.outFormat == RTAUDIO_FLOAT64) {
     Float64 scale;
-    Float64 *out = (Float64 *)output;
+    Float64 *out = (Float64 *)outBuffer;
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       scale = 1.0 / 128.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      Int16 *in = (Int16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float64) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0xffffff00);
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float64) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      Float32 *in = (Float32 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float64) in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
       // Channel compensation and/or (de)interleaving only.
-      Float64 *in = (Float64 *)input;
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
-  else if (format_out == RTAUDIO_FLOAT32) {
+  else if (info.outFormat == RTAUDIO_FLOAT32) {
     Float32 scale;
-    Float32 *out = (Float32 *)output;
+    Float32 *out = (Float32 *)outBuffer;
 
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       scale = 1.0 / 128.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      Int16 *in = (Int16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float32) (in[offset_in[j]] & 0xffffff00);
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0xffffff00);
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float32) in[offset_in[j]];
-          out[offset_out[j]] *= scale;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
+          out[info.outOffset[j]] *= scale;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
       // Channel compensation and/or (de)interleaving only.
-      Float32 *in = (Float32 *)input;
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      Float64 *in = (Float64 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Float32) in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT32) {
-    Int32 *out = (Int32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+  else if (info.outFormat == RTAUDIO_SINT32) {
+    Int32 *out = (Int32 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 24;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      Int16 *in = (Int16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) in[offset_in[j]];
-          out[offset_out[j]] <<= 16;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 16;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
+    else if (info.inFormat == RTAUDIO_SINT32) {
       // Channel compensation and/or (de)interleaving only.
-      Int32 *in = (Int32 *)input;
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      Float32 *in = (Float32 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) (in[offset_in[j]] * 2147483647.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      Float64 *in = (Float64 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) (in[offset_in[j]] * 2147483647.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT24) {
-    Int32 *out = (Int32 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+  else if (info.outFormat == RTAUDIO_SINT24) {
+    Int32 *out = (Int32 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) in[offset_in[j]];
-          out[offset_out[j]] <<= 24;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 24;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
-      Int16 *in = (Int16 *)input;
+    else if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) in[offset_in[j]];
-          out[offset_out[j]] <<= 16;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 16;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
+    else if (info.inFormat == RTAUDIO_SINT24) {
       // Channel compensation and/or (de)interleaving only.
-      Int32 *in = (Int32 *)input;
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) (in[offset_in[j]] & 0xffffff00);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] & 0xffffff00);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      Float32 *in = (Float32 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) (in[offset_in[j]] * 2147483647.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      Float64 *in = (Float64 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int32) (in[offset_in[j]] * 2147483647.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT16) {
-    Int16 *out = (Int16 *)output;
-    if (format_in == RTAUDIO_SINT8) {
-      signed char *in = (signed char *)input;
+  else if (info.outFormat == RTAUDIO_SINT16) {
+    Int16 *out = (Int16 *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
+      signed char *in = (signed char *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int16) in[offset_in[j]];
-          out[offset_out[j]] <<= 8;
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 8;
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT16) {
+    else if (info.inFormat == RTAUDIO_SINT16) {
       // Channel compensation and/or (de)interleaving only.
-      Int16 *in = (Int16 *)input;
+      Int16 *in = (Int16 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      Float32 *in = (Float32 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int16) (in[offset_in[j]] * 32767.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      Float64 *in = (Float64 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (Int16) (in[offset_in[j]] * 32767.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
-  else if (format_out == RTAUDIO_SINT8) {
-    signed char *out = (signed char *)output;
-    if (format_in == RTAUDIO_SINT8) {
+  else if (info.outFormat == RTAUDIO_SINT8) {
+    signed char *out = (signed char *)outBuffer;
+    if (info.inFormat == RTAUDIO_SINT8) {
       // Channel compensation and/or (de)interleaving only.
-      signed char *in = (signed char *)input;
+      signed char *in = (signed char *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = in[offset_in[j]];
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = in[info.inOffset[j]];
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    if (format_in == RTAUDIO_SINT16) {
-      Int16 *in = (Int16 *)input;
+    if (info.inFormat == RTAUDIO_SINT16) {
+      Int16 *in = (Int16 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 8) & 0x00ff);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT24) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT24) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_SINT32) {
-      Int32 *in = (Int32 *)input;
+    else if (info.inFormat == RTAUDIO_SINT32) {
+      Int32 *in = (Int32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT32) {
-      Float32 *in = (Float32 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT32) {
+      Float32 *in = (Float32 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
-    else if (format_in == RTAUDIO_FLOAT64) {
-      Float64 *in = (Float64 *)input;
+    else if (info.inFormat == RTAUDIO_FLOAT64) {
+      Float64 *in = (Float64 *)inBuffer;
       for (int i=0; i<stream_.bufferSize; i++) {
-        for (j=0; j<channels; j++) {
-          out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
+        for (j=0; j<info.channels; j++) {
+          out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
         }
-        in += jump_in;
-        out += jump_out;
+        in += info.inJump;
+        out += info.outJump;
       }
     }
   }
index fa2d494d297949d2b37b08e357895293d75cde2e..e26f81d186d54e2b7299f0e0290a214577af9a68 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
@@ -9,8 +9,8 @@
 
     RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/
 
-    RtAudio: a realtime audio i/o C++ class
-    Copyright (c) 2001-2004 Gary P. Scavone
+    RtAudio: realtime audio i/o C++ classes
+    Copyright (c) 2001-2005 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -37,7 +37,7 @@
 */
 /************************************************************************/
 
-// RtAudio: Version 3.0.1, 22 March 2004
+// RtAudio: Version 3.0.2 (14 October 2005)
 
 #ifndef __RTAUDIO_H
 #define __RTAUDIO_H
@@ -127,17 +127,27 @@ class RtApi
 {
 public:
 
+  enum StreamState {
+    STREAM_STOPPED,
+    STREAM_RUNNING
+  };
+
   RtApi();
   virtual ~RtApi();
   void openStream( int outputDevice, int outputChannels,
                    int inputDevice, int inputChannels,
                    RtAudioFormat format, int sampleRate,
                    int *bufferSize, int numberOfBuffers );
+  void openStream( int outputDevice, int outputChannels,
+                   int inputDevice, int inputChannels,
+                   RtAudioFormat format, int sampleRate,
+                   int *bufferSize, int *numberOfBuffers );
   virtual void setStreamCallback( RtAudioCallback callback, void *userData ) = 0;
   virtual void cancelStreamCallback() = 0;
   int getDeviceCount(void);
   RtAudioDeviceInfo getDeviceInfo( int device );
   char * const getStreamBuffer();
+  RtApi::StreamState getStreamState() const;
   virtual void tickStream() = 0;
   virtual void closeStream();
   virtual void startStream() = 0;
@@ -158,9 +168,13 @@ protected:
     UNINITIALIZED = -75
   };
 
-  enum StreamState {
-    STREAM_STOPPED,
-    STREAM_RUNNING
+  // A protected structure used for buffer conversion.
+  struct ConvertInfo {
+    int channels;
+    int inJump, outJump;
+    RtAudioFormat inFormat, outFormat;
+    std::vector<int> inOffset;
+    std::vector<int> outOffset;
   };
 
   // A protected structure for audio streams.
@@ -183,10 +197,10 @@ protected:
     RtAudioFormat deviceFormat[2]; // Playback and record, respectively.
     StreamMutex mutex;
     CallbackInfo callbackInfo;
+    ConvertInfo convertInfo[2];
 
     RtApiStream()
       :apiHandle(0), userBuffer(0), deviceBuffer(0) {}
-    //      mode(UNINITIALIZED), state(STREAM_STOPPED),
   };
 
   // A protected device structure for audio devices.
@@ -217,7 +231,7 @@ protected:
   typedef float Float32;
   typedef double Float64;
 
-  char message_[256];
+  char message_[1024];
   int nDevices_;
   std::vector<RtApiDevice> devices_;
   RtApiStream stream_;
@@ -281,7 +295,7 @@ protected:
     Protected method used to perform format, channel number, and/or interleaving
     conversions between the user and device buffers.
   */
-  void convertStreamBuffer( StreamMode mode );
+  void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info );
 
   //! Protected common method used to perform byte-swapping on buffers.
   void byteSwapBuffer( char *buffer, int samples, RtAudioFormat format );
@@ -350,6 +364,20 @@ public:
            RtAudioFormat format, int sampleRate,
            int *bufferSize, int numberOfBuffers, RtAudioApi api=UNSPECIFIED );
 
+  //! An overloaded constructor which opens a stream and also returns \c numberOfBuffers parameter via pointer argument.
+  /*!
+    See the previous constructor call for details.  This overloaded
+    version differs only in that it takes a pointer argument for the
+    \c numberOfBuffers parameter and returns the value used by the
+    audio device (which may be different from that requested).  Note
+    that the \c numberofBuffers parameter is not used with the Linux
+    Jack, Macintosh CoreAudio, and Windows ASIO APIs.
+  */
+  RtAudio( int outputDevice, int outputChannels,
+           int inputDevice, int inputChannels,
+           RtAudioFormat format, int sampleRate,
+           int *bufferSize, int *numberOfBuffers, RtAudioApi api=UNSPECIFIED );
+
   //! The destructor.
   /*!
     Stops and closes an open stream and devices and deallocates
@@ -389,6 +417,20 @@ public:
                    RtAudioFormat format, int sampleRate,
                    int *bufferSize, int numberOfBuffers );
 
+  //! A public method for opening a stream and also returning \c numberOfBuffers parameter via pointer argument.
+  /*!
+    See the previous function call for details.  This overloaded
+    version differs only in that it takes a pointer argument for the
+    \c numberOfBuffers parameter and returns the value used by the
+    audio device (which may be different from that requested).  Note
+    that the \c numberofBuffers parameter is not used with the Linux
+    Jack, Macintosh CoreAudio, and Windows ASIO APIs.
+  */
+  void openStream( int outputDevice, int outputChannels,
+                   int inputDevice, int inputChannels,
+                   RtAudioFormat format, int sampleRate,
+                   int *bufferSize, int *numberOfBuffers );
+
   //! A public method which sets a user-defined callback function for a given stream.
   /*!
     This method assigns a callback function to a previously opened
@@ -634,6 +676,53 @@ public:
   void setStreamCallback( RtAudioCallback callback, void *userData );
   void cancelStreamCallback();
 
+  public:
+  // \brief Internal structure that provide debug information on the state of a running DSound device.
+  struct RtDsStatistics {
+    // \brief Sample Rate.
+    long sampleRate;
+    // \brief The size of one sample * number of channels on the input device.
+    int inputFrameSize; 
+    // \brief The size of one sample * number of channels on the output device.
+    int outputFrameSize; 
+    /* \brief The number of times the read pointer had to be adjusted to avoid reading from an unsafe buffer position.
+     *
+     * This field is only used when running in DUPLEX mode. INPUT mode devices just wait until the data is 
+     * available.
+     */
+    int numberOfReadOverruns;
+    // \brief The number of times the write pointer had to be adjusted to avoid writing in an unsafe buffer position.
+    int numberOfWriteUnderruns;
+    // \brief Number of bytes by attribute to buffer configuration by which writing must lead the current write pointer.
+    int writeDeviceBufferLeadBytes;
+    // \brief Number of bytes by attributable to the device driver by which writing must lead the current write pointer on this output device.
+    unsigned long writeDeviceSafeLeadBytes;
+    // \brief Number of bytes by which reading must trail the current read pointer on this input device.
+    unsigned long readDeviceSafeLeadBytes; 
+    /* \brief Estimated latency in seconds. 
+    *
+    * For INPUT mode devices, based the latency of the device's safe read pointer, plus one buffer's
+    * worth of additional latency.
+    *
+    * For OUTPUT mode devices, the latency of the device's safe write pointer, plus N buffers of 
+    * additional buffer latency.
+    *
+    * For DUPLEX devices, the sum of latencies for both input and output devices. DUPLEX devices
+    * also back off the read pointers an additional amount in order to maintain synchronization 
+    * between out-of-phase read and write pointers. This time is also included.
+    *
+    * Note that most software packages report latency between the safe write pointer 
+    * and the software lead pointer, excluding the hardware device's safe write pointer 
+    * latency. Figures of 1 or 2ms of latency on Windows audio devices are invariably of this type.
+    * The reality is that hardware devices often have latencies of 30ms or more (often much 
+    * higher for duplex operation).
+    */
+
+    double latency;
+  };
+  // \brief Report on the current state of a running DSound device.
+  static RtDsStatistics getDsStatistics();
+
   private:
 
   void initialize(void);
@@ -641,6 +730,12 @@ public:
   bool probeDeviceOpen( int device, StreamMode mode, int channels, 
                         int sampleRate, RtAudioFormat format,
                         int *bufferSize, int numberOfBuffers );
+
+  bool coInitialized;
+  bool buffersRolling;
+  long duplexPrerollBytes;
+  static RtDsStatistics statistics;
+
 };
 
 #endif
@@ -674,6 +769,9 @@ public:
   bool probeDeviceOpen( int device, StreamMode mode, int channels, 
                         int sampleRate, RtAudioFormat format,
                         int *bufferSize, int numberOfBuffers );
+
+  bool coInitialized;
+
 };
 
 #endif
index 465cd754c2bb92790527fbb0bf5b07ef0077273e..cb1283df33fea56067162b079d5734d2d7382982 100644 (file)
--- a/RtError.h
+++ b/RtError.h
@@ -39,7 +39,7 @@ protected:
 
 public:
   //! The constructor.
-  RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type){}
+  RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {}
 
   //! The destructor.
   virtual ~RtError(void) {};
index 0ad7b372c02e4fb43184aca187d2122eea35c056..83723fd5ce98c5f83231cbeecac81ebe19a1bb88 100644 (file)
@@ -80,7 +80,6 @@ case $host in
     [AC_SUBST( audio_apis, [-D__MACOSX_CORE__] )],
     [AC_MSG_ERROR(CoreAudio header files not found!)] )
   AC_SUBST( frameworks, ["-framework CoreAudio"] )
-  AC_CHECK_LIB(stdc++, printf, , AC_MSG_ERROR(RtAudio requires the C++ library!) )
   ;;
 
   *)
index 888ebe6421630a3cdb19e19744e94c4793697d27..d05cf2767c962054675542f89f47bd48e638e21d 100644 (file)
@@ -1,7 +1,7 @@
 <HR>
 
 <table><tr><td><img src="../images/mcgill.gif" width=165></td>
-  <td>&copy;2001-2004 Gary P. Scavone, McGill University. All Rights Reserved.<br>
+  <td>&copy;2001-2005 Gary P. Scavone, McGill University. All Rights Reserved.<br>
   Maintained by Gary P. Scavone, <a href="mailto:gary@music.mcgill.ca">gary@music.mcgill.ca</a></td></tr>
 </table>
 
index 9b45bbc5d9d07ef9cd97b011b72b6ed7ece54b7b..2d79952bc39aa6a5545fe544a4ce70f4cdbb89b0 100644 (file)
@@ -1,8 +1,6 @@
 /*! \mainpage The RtAudio Tutorial
 
-<BODY BGCOLOR="white">
-
-<CENTER>\ref intro &nbsp;&nbsp; \ref changes &nbsp;&nbsp;\ref download &nbsp;&nbsp; \ref start &nbsp;&nbsp; \ref error &nbsp;&nbsp; \ref probing &nbsp;&nbsp; \ref settings &nbsp;&nbsp; \ref playbackb &nbsp;&nbsp; \ref playbackc &nbsp;&nbsp; \ref recording &nbsp;&nbsp; \ref duplex &nbsp;&nbsp; \ref multi &nbsp;&nbsp; \ref methods &nbsp;&nbsp; \ref compiling &nbsp;&nbsp; \ref debug &nbsp;&nbsp; \ref apinotes &nbsp;&nbsp; \ref acknowledge &nbsp;&nbsp; \ref license</CENTER>
+<CENTER>\ref intro &nbsp;&nbsp; \ref changes &nbsp;&nbsp;\ref download &nbsp;&nbsp; \ref start &nbsp;&nbsp; \ref error &nbsp;&nbsp; \ref probing &nbsp;&nbsp; \ref settings &nbsp;&nbsp; \ref playbackb &nbsp;&nbsp; \ref playbackc &nbsp;&nbsp; \ref recording &nbsp;&nbsp; \ref duplex &nbsp;&nbsp; \ref multi &nbsp;&nbsp; \ref methods &nbsp;&nbsp; \ref compiling &nbsp;&nbsp; \ref debug &nbsp;&nbsp; \ref apinotes &nbsp;&nbsp; \ref wishlist &nbsp;&nbsp; \ref acknowledge &nbsp;&nbsp; \ref license</CENTER>
 
 \section intro Introduction
 
@@ -38,7 +36,7 @@ The RtError class declaration and definition have been extracted to a separate f
 
 \section download Download
 
-Latest Release (22 March 2004): <A href="http://music.mcgill.ca/~gary/rtaudio/release/rtaudio-3.0.1.tar.gz">Version 3.0.1 (200 kB tar/gzipped)</A>
+Latest Release (14 October 2005): <A href="http://music.mcgill.ca/~gary/rtaudio/release/rtaudio-3.0.2.tar.gz">Version 3.0.2</A>
 
 \section start Getting Started
 
@@ -52,7 +50,7 @@ The first thing that must be done when using RtAudio is to create an instance of
 
 int main()
 {
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Default RtAudio constructor
   try {
@@ -89,7 +87,7 @@ A programmer may wish to query the available audio device capabilities before de
 
 int main()
 {
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Default RtAudio constructor
   try {
@@ -176,7 +174,7 @@ int main()
   int bufferSize = 256;  // 256 sample frames
   int nBuffers = 4;      // number of internal buffers used by device
   int device = 0;        // 0 indicates the default or first available device
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Instantiate RtAudio and open a stream within a try/catch block
   try {
@@ -230,7 +228,7 @@ int main()
   int nBuffers = 4;      // number of internal buffers used by device
   float *buffer;
   int device = 0;        // 0 indicates the default or first available device
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Open a stream during RtAudio instantiation
   try {
@@ -333,7 +331,7 @@ int main()
   int device = 0;        // 0 indicates the default or first available device
   double data[2];
   char input;
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Open a stream during RtAudio instantiation
   try {
@@ -401,7 +399,7 @@ int main()
   int nBuffers = 4;      // number of internal buffers used by device
   float *buffer;
   int device = 0;        // 0 indicates the default or first available device
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Instantiate RtAudio and open a stream.
   try {
@@ -496,7 +494,7 @@ int main()
   int nBuffers = 4;      // number of internal buffers used by device
   int device = 0;        // 0 indicates the default or first available device
   char input;
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // Open a stream during RtAudio instantiation
   try {
@@ -683,17 +681,51 @@ The Steinberg ASIO audio API is based on a callback scheme.  In addition, the AP
 
 A number of ASIO source and header files are required for use with RtAudio.  Specifically, an RtAudio project must include the following files: <TT>asio.h,cpp; asiodrivers.h,cpp; asiolist.h,cpp; asiodrvr.h; asiosys.h; ginclude.h; iasiodrv.h</TT>.  The Visual C++ projects found in <TT>/tests/Windows/</TT> compile both ASIO and DirectSound support.
 
+\section wishlist Possible Future Changes
 
-\section acknowledge Acknowledgements
+There are a few issues that still need to be addressed in future versions of RtAudio, including:
 
-The RtAudio API incorporates many of the concepts developed in the <A href="http://www.portaudio.com/">PortAudio</A> project by Phil Burk and Ross Bencina.  Early development also incorporated ideas from Bill Schottstaedt's <A href="http://www-ccrma.stanford.edu/software/snd/sndlib/">sndlib</A>.  The CCRMA <A href="http://www-ccrma.stanford.edu/groups/soundwire/">SoundWire group</A> provided valuable feedback during the API proposal stages.
+<ul>
+<li>Provide a mechanism so the user can "pre-fill" audio output buffers to allow precise measurement of an acoustic response;</li>
+<li>Allow the user to read / write non-interleaved data to / from the audio buffer;</li>
+<li>Further support in Windows OS for multi-channel (>2) input / output.  This is currently only possible with ASIO interface (in large part due to limitations with the DirectSound API).  But perhaps a port to the WinMM API should be investigated?</li>
+<li>Investigate the possibility of allowing the user to select specific channels of a soundcard.  For example, if an audio device supports 8 channels and the user wishes to send data out channels 7-8 only, it is currently necessary to open all 8 channels and write the two channels of output data to the correct positions in each audio frame of an interleaved data buffer.</li>
+</ul>
+
+\section acknowledge Acknowledgements
 
-The early 2.0 version of RtAudio was slowly developed over the course of many months while in residence at the <A href="http://www.iua.upf.es/">Institut Universitari de L'Audiovisual (IUA)</A> in Barcelona, Spain and the <A href="http://www.acoustics.hut.fi/">Laboratory of Acoustics and Audio Signal Processing</A> at the Helsinki University of Technology, Finland.  Much subsequent development happened while working at the <A href="http://www-ccrma.stanford.edu/">Center for Computer Research in Music and Acoustics (CCRMA)</A> at <A href="http://www.stanford.edu/">Stanford University</A>.  The most recent version of RtAudio was finished while working as an assistant professor of <a href="http://www.music.mcgill.ca/musictech/">Music Technology</a> at <a href="http://www.mcgill.ca/">McGill University</a>.  This work was supported in part by the United States Air Force Office of Scientific Research (grant \#F49620-99-1-0293).
+Thanks to Robin Davies for a number of bug fixes and improvements to
+the DirectSound and ASIO implementations in the 3.0.2 release!
+
+The RtAudio API incorporates many of the concepts developed in the <A
+href="http://www.portaudio.com/">PortAudio</A> project by Phil Burk
+and Ross Bencina.  Early development also incorporated ideas from Bill
+Schottstaedt's <A
+href="http://www-ccrma.stanford.edu/software/snd/sndlib/">sndlib</A>.
+The CCRMA <A
+href="http://www-ccrma.stanford.edu/groups/soundwire/">SoundWire
+group</A> provided valuable feedback during the API proposal stages.
+
+The early 2.0 version of RtAudio was slowly developed over the course
+of many months while in residence at the <A
+href="http://www.iua.upf.es/">Institut Universitari de L'Audiovisual
+(IUA)</A> in Barcelona, Spain and the <A
+href="http://www.acoustics.hut.fi/">Laboratory of Acoustics and Audio
+Signal Processing</A> at the Helsinki University of Technology,
+Finland.  Much subsequent development happened while working at the <A
+href="http://www-ccrma.stanford.edu/">Center for Computer Research in
+Music and Acoustics (CCRMA)</A> at <A
+href="http://www.stanford.edu/">Stanford University</A>.  The most
+recent version of RtAudio was finished while working as an assistant
+professor of <a href="http://www.music.mcgill.ca/musictech/">Music
+Technology</a> at <a href="http://www.mcgill.ca/">McGill
+University</a>.  This work was supported in part by the United States
+Air Force Office of Scientific Research (grant \#F49620-99-1-0293).
 
 \section license License
 
     RtAudio: a realtime audio i/o C++ classes<BR>
-    Copyright (c) 2001-2004 Gary P. Scavone
+    Copyright (c) 2001-2005 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
index ad9c8e982196421cb9597240b7f9fa4f10d2f8bc..ba72cc33ce5716000c665ac072668ca67bd0f73c 100644 (file)
@@ -1,6 +1,20 @@
 RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2004.
+By Gary P. Scavone, 2001-2005.
+
+v3.0.2: (14 October 2005)
+- modification of ALSA read/write order to fix duplex under/overruns
+- added synchronization of input/output devices for ALSA duplex operation
+- cleaned up and improved error reporting throughout
+- bug fix in Windows DirectSound support for 8-bit audio
+- bug fix in Windows DirectSound support during device capture query
+- added ASIOOutputReady() call near end of callbackEvent to fix some driver behavior
+- added #include <stdio.h> to RtAudio.cpp
+- fixed bug in RtApiCore for duplex operation with different I/O devices
+- improvements to DirectX pointer chasing (by Robin Davies)
+- backdoor RtDsStatistics hook provides DirectX performance information (by Robin Davies)
+- bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30 (by Robin Davies)
+- auto-call CoInitialize for DSOUND and ASIO platforms (by Robin Davies)
 
 v3.0.1: (22 March 2004)
 - bug fix in Windows DirectSound support for cards with output only
diff --git a/install b/install
index 7aab632fce9fdcf0001d970b295cff358425bfc4..b2f086b2cd3d310941413419e2c42b80c73b48be 100644 (file)
--- a/install
+++ b/install
@@ -1,6 +1,6 @@
 RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2004.
+By Gary P. Scavone, 2001-2005.
 
 To configure and compile (on Unix systems):
 
@@ -18,7 +18,7 @@ A few options can be passed to configure, including:
   --with-jack = choose JACK server support (linux only)
   --with-oss = choose OSS API support (linux only)
 
-Typing "./configure --help" will display all the available options.
+Typing "./configure --help" will display all the available options.  Note that you can provide more than one "--with-" flag to the configure script to enable multiple API support.
 
 If you wish to use a different compiler than that selected by configure, specify that compiler in the command line (ex. to use CC):
 
diff --git a/readme b/readme
index ae40510ce24dace09b75172b7ba2e6973f32893a..12025320e58628c5420156d3ca3d0f7a987fed9f 100644 (file)
--- a/readme
+++ b/readme
@@ -1,6 +1,6 @@
 RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2004.
+By Gary P. Scavone, 2001-2005.
 
 This distribution of RtAudio contains the following:
 
@@ -38,7 +38,7 @@ LEGAL AND ETHICAL:
 The RtAudio license is similar to the the MIT License, with the added "feature" that modifications be sent to the developer.
 
     RtAudio: a set of realtime audio i/o C++ classes
-    Copyright (c) 2001-2004 Gary P. Scavone
+    Copyright (c) 2001-2005 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
diff --git a/tests/Windows/rtaudiotest/Release/.placeholder b/tests/Windows/rtaudiotest/Release/.placeholder
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/Windows/rtaudiotest/StdOpt.cpp b/tests/Windows/rtaudiotest/StdOpt.cpp
new file mode 100644 (file)
index 0000000..02c8eea
--- /dev/null
@@ -0,0 +1,91 @@
+/************************************************************************/\r
+/*! \class CommandLine\r
+    \brief Command-line option parser.\r
+\r
+    Copyright (c) 2005 Robin Davies.\r
+\r
+    Permission is hereby granted, free of charge, to any person\r
+    obtaining a copy of this software and associated documentation files\r
+    (the "Software"), to deal in the Software without restriction,\r
+    including without limitation the rights to use, copy, modify, merge,\r
+    publish, distribute, sublicense, and/or sell copies of the Software,\r
+    and to permit persons to whom the Software is furnished to do so,\r
+    subject to the following conditions:\r
+\r
+    The above copyright notice and this permission notice shall be\r
+    included in all copies or substantial portions of the Software.\r
+\r
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+*/\r
+/************************************************************************/\r
+\r
+#include ".\stdopt.h"\r
+\r
+using namespace stdopt;\r
+\r
+CommandLine::CommandLine()\r
+{\r
+}\r
+\r
+void CommandLine::ProcessCommandLine(int argc, char**argv)\r
+{\r
+       std::vector<std::string> cmdline;\r
+       for (int i = 0; i < argc; ++i) {\r
+               cmdline.push_back(argv[i]);\r
+       }\r
+       ProcessCommandLine(cmdline);\r
+}\r
+\r
+const CommandLine::COptionHandlerBase*CommandLine::GetOptionHandler(const std::string &name) const\r
+{\r
+       // Return excact matches only.\r
+       for (size_t i = 0; i < optionHandlers.size(); ++i)\r
+       {\r
+               if (optionHandlers[i]->getName() == name) {\r
+                       return (optionHandlers[i]);\r
+               }\r
+       }\r
+       return NULL;\r
+}\r
+\r
+void CommandLine::ProcessCommandLine(const std::vector<std::string>& cmdline)\r
+{\r
+       for (size_t i = 1; i < cmdline.size(); ++i)\r
+    {\r
+      if (cmdline[i].length() != 0 && cmdline[i][0] == L'/' || (cmdline[i][0] == '-')) {\r
+        std::string arg = cmdline[i].substr(1);\r
+        const COptionHandlerBase *pHandler = GetOptionHandler(arg);\r
+        if (pHandler == NULL) {\r
+          throw CommandLineException(std::string("Unknown option: ") + arg);\r
+        }\r
+        if (pHandler->HasArgument())\r
+          {\r
+            std::string strArg;\r
+            if (i+1 < cmdline.size()) {\r
+              ++i;\r
+              strArg = cmdline[i];\r
+            }\r
+            pHandler->Process(strArg.c_str());\r
+          } else {\r
+          pHandler->Process(NULL);\r
+        }\r
+      } else {\r
+        args.push_back(cmdline[i]);\r
+      }\r
+    }\r
+}\r
+\r
+CommandLine::~CommandLine(void)\r
+{\r
+       for (size_t i = 0; i < optionHandlers.size(); ++i)\r
+       {\r
+               delete optionHandlers[i];\r
+       }\r
+       optionHandlers.resize(0);\r
+}\r
diff --git a/tests/Windows/rtaudiotest/StdOpt.h b/tests/Windows/rtaudiotest/StdOpt.h
new file mode 100644 (file)
index 0000000..4de660a
--- /dev/null
@@ -0,0 +1,186 @@
+/************************************************************************/\r
+/*! \class CommandLine\r
+    \brief Command-line opition parser.\r
+\r
+    Copyright (c) 2005 Robin Davies.\r
+\r
+    Permission is hereby granted, free of charge, to any person\r
+    obtaining a copy of this software and associated documentation files\r
+    (the "Software"), to deal in the Software without restriction,\r
+    including without limitation the rights to use, copy, modify, merge,\r
+    publish, distribute, sublicense, and/or sell copies of the Software,\r
+    and to permit persons to whom the Software is furnished to do so,\r
+    subject to the following conditions:\r
+\r
+    The above copyright notice and this permission notice shall be\r
+    included in all copies or substantial portions of the Software.\r
+\r
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+*/\r
+/************************************************************************/\r
+\r
+#ifndef STDOPT_H\r
+#define STDOPT_H\r
+\r
+#include <vector>\r
+#include <string>\r
+#include <sstream>\r
+#include <exception>\r
+\r
+namespace stdopt\r
+{\r
+\r
+       class CommandLineException: public std::exception {\r
+       public: \r
+               CommandLineException(const std::string &error)\r
+               {\r
+                       s = error;\r
+               }\r
+               const char*what() { return s.c_str(); }\r
+       private:\r
+               std::string s;\r
+       };\r
+\r
+       class CommandLine\r
+       {\r
+       public:\r
+               CommandLine();\r
+               virtual ~CommandLine();\r
+\r
+               void ProcessCommandLine(int argc, char**argv);\r
+               void ProcessCommandLine(const std::vector<std::string>& cmdline);\r
+\r
+               template <class TVAL> void AddOption(const char*name,TVAL*pResult, TVAL defaultValue)\r
+               {\r
+                       this->optionHandlers.push_back(new COptionHandler<TVAL>(name,pResult));\r
+            *pResult = defaultValue;\r
+\r
+               }\r
+               template <class TVAL> void AddOption(const char*name,TVAL*pResult)\r
+        {\r
+                       this->optionHandlers.push_back(new COptionHandler<TVAL>(name,pResult));\r
+        }\r
+               const std::vector<std::string> &GetArguments() { return args; }\r
+    template <class T> void GetArgument(size_t arg, T*pVal)\r
+    {\r
+      if (arg >= args.size()) { \r
+        std::stringstream os;\r
+        os << "Argument " << (arg+1) << " not provided.";\r
+        throw CommandLineException(os.str());\r
+      }\r
+      std::stringstream is(args[arg]);\r
+      T value;\r
+      is >> value;\r
+      if (!is.fail() && is.eof())\r
+      {\r
+        *pVal = value;\r
+      } else {\r
+        std::stringstream os;\r
+        os << "Argument " << (arg+1) << " was not in the correct format.";\r
+        throw CommandLineException(os.str());\r
+      }\r
+    }\r
+    void GetArgument(size_t arg, std::string*pVal)\r
+    {\r
+      if (arg >= args.size()) { \r
+        std::stringstream os;\r
+        os << "Argument " << (arg+1) << " not provided.";\r
+        throw CommandLineException(os.str());\r
+      }\r
+      *pVal = args[arg];\r
+    }\r
+\r
+\r
+       private:\r
+\r
+               class COptionHandlerBase {\r
+               public:\r
+                       COptionHandlerBase(const std::string & name) { this->name = name;}\r
+                       virtual ~COptionHandlerBase() { };\r
+                       const std::string &getName() const { return name; }\r
+                       virtual bool HasArgument()const  = 0; \r
+                       virtual void Process(const char* value) const = 0;\r
+               private:\r
+                       std::string name;\r
+               };\r
+               template <class T> class COptionHandler: public COptionHandlerBase {\r
+               public: \r
+                       COptionHandler(const std::string &name,T *result, T defaultValue = T())\r
+                               : COptionHandlerBase(name)\r
+                       {\r
+                               _pResult = result;\r
+                               *_pResult = defaultValue;\r
+\r
+                       }\r
+                       virtual bool HasArgument() const;\r
+\r
+                       virtual void Process(const char *strValue) const {\r
+                               std::stringstream is(strValue);\r
+                               T value;\r
+        is >> value;\r
+                               if (!is.fail() && is.eof())\r
+                               {\r
+                                       *_pResult = value;\r
+                               } else {\r
+                                       std::stringstream os;\r
+                                       os << "Invalid value provided for option '" << getName() << "'.";\r
+                                       throw CommandLineException(os.str().c_str());\r
+                               }\r
+                       }\r
+\r
+               private:\r
+                       std::string strName;\r
+                       T*_pResult;\r
+               };\r
+               const CommandLine::COptionHandlerBase*GetOptionHandler(const std::string &name) const;\r
+\r
+               std::vector<std::string > args;\r
+               std::vector<COptionHandlerBase*> optionHandlers;\r
+       };\r
+\r
+       // Argument specializations for bool.\r
+       template <class T> bool CommandLine::COptionHandler<T>::HasArgument()const {\r
+               return true; \r
+       };\r
+       inline bool CommandLine::COptionHandler<bool>::HasArgument() const {\r
+               return false;\r
+       }\r
+       inline void CommandLine::COptionHandler<bool>::Process(const char*strValue) const {\r
+    if (strValue == NULL)\r
+    {\r
+      *_pResult = true;\r
+      return;\r
+    }\r
+               switch (*strValue)\r
+               {\r
+               case '\0':\r
+               case '+':\r
+                       *_pResult = true;\r
+                       break;\r
+               case '-':\r
+                       *_pResult = false;\r
+                       break;\r
+               default:\r
+                       throw CommandLineException("Please specify '+' or '-' for boolean options.");\r
+               }\r
+       }\r
+       // specializations for std::string.\r
+       inline void  CommandLine::COptionHandler<std::string >::Process(const char*strValue)const  {\r
+               *_pResult  = strValue;\r
+       }\r
+\r
+       // specializations for std::vector<std::string>\r
+       inline void  CommandLine::COptionHandler<std::vector<std::string> >::Process(const char*strValue)const  {\r
+               _pResult->push_back(strValue); \r
+       }\r
+};\r
+\r
+\r
+\r
+#endif\r
diff --git a/tests/Windows/rtaudiotest/readme.txt b/tests/Windows/rtaudiotest/readme.txt
new file mode 100644 (file)
index 0000000..d883384
--- /dev/null
@@ -0,0 +1,8 @@
+rtaudiotest - Test rtaudio devices.\r
+\r
+rtaudiotest is a small tool that allows easy testing of rtaudio devices\r
+and configurations.\r
+\r
+Run rtaudiotest.exe for a description of how to run it.\r
+\r
+rtaudiotest is currently only supported for the Windows platform.\r
diff --git a/tests/Windows/rtaudiotest/rtaudiotest.cpp b/tests/Windows/rtaudiotest/rtaudiotest.cpp
new file mode 100644 (file)
index 0000000..90d8c2c
--- /dev/null
@@ -0,0 +1,386 @@
+/************************************************************************/\r
+/*! \brief Interactively Test RtAudio parameters.\r
+\r
+    RtAudio is a command-line utility that allows users to enumerate \r
+    installed devices, and to test input, output and duplex operation \r
+    of RtAudio devices with various buffer and buffer-size \r
+    configurations.\r
+\r
+    Copyright (c) 2005 Robin Davies.\r
+\r
+    Permission is hereby granted, free of charge, to any person\r
+    obtaining a copy of this software and associated documentation files\r
+    (the "Software"), to deal in the Software without restriction,\r
+    including without limitation the rights to use, copy, modify, merge,\r
+    publish, distribute, sublicense, and/or sell copies of the Software,\r
+    and to permit persons to whom the Software is furnished to do so,\r
+    subject to the following conditions:\r
+\r
+    The above copyright notice and this permission notice shall be\r
+    included in all copies or substantial portions of the Software.\r
+\r
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+*/\r
+/************************************************************************/\r
+\r
+#include "RtAudio.h"\r
+#include "FileWvOut.h"\r
+\r
+#include <cmath>\r
+#include <iostream>\r
+#include <stdopt.h>\r
+\r
+using namespace std;\r
+using namespace stdopt;\r
+\r
+#ifdef _WIN32\r
+// Correct windows.h standards violation.\r
+#undef min\r
+#undef max\r
+#endif\r
+\r
+#define DSOUND 1\r
+\r
+RtAudio::RtAudioApi rtApi = RtAudio::WINDOWS_DS;\r
+\r
+void DisplayHelp(std::ostream &os)\r
+{\r
+  os \r
+    << "rtaudiotest - Test rtaudio devices." << endl\r
+    << endl\r
+    << "Syntax:" << endl\r
+    << "   rtaudiotest [options]* enum" << endl\r
+    << "                              - Display installed devices." << endl\r
+    << "   rtaudiotest [options]* inputtest <devicenum> [<filename>]" << endl\r
+    << "                              - Capture audio to a .wav file." << endl\r
+    << "   rtaudiotest [options]* outputtest <devicenum>" << endl\r
+    << "                              - Generate a test signal on the device.." << endl\r
+    << "   rtaudiotest [options]* duplextest <inputDevicenum> <outputdevicenum>" << endl\r
+    << "                              - Echo input to output." << endl\r
+\r
+    << "Options:" << endl\r
+    << "   -h -?        Display this message." << endl\r
+    << "   -dsound      Use DirectX drivers." << endl\r
+    << "   -asio        Use ASIO drivers." << endl\r
+    << "   -buffers N   Use N buffers." << endl\r
+    << "   -size N      Use buffers of size N." << endl\r
+    << "   -srate N     Use a sample-rate of N (defaults to 44100)." << endl\r
+    << "   -channels N  Use N channels (defaults to 2)." << endl\r
+    << "   -seconds N   Run the test for N seconds (default 5)." << endl\r
+    << "Description: " << endl\r
+    << "  RtAudio is a command-line utility that allows users to enumerate " << endl\r
+    << "  installed devices, and to test input, output and duplex operation " << endl\r
+    << "  of RtAudio devices with various buffer and buffer-size " << endl\r
+    << "  configurations." << endl\r
+    << "Examples:"  << endl\r
+    << "      rtaudio -asio enum" << endl\r
+    << "      rtaudio -dsound -buffers 4 -size 128 -seconds 3 inputtest 0 test.wav" << endl\r
+    ;\r
+\r
+}\r
+\r
+void EnumerateDevices(RtAudio::RtAudioApi api)\r
+{\r
+  RtAudio rt(api);\r
+  for (int i = 1; i <= rt.getDeviceCount(); ++i)\r
+  {\r
+    RtAudioDeviceInfo info = rt.getDeviceInfo(i);\r
+    cout << "Device " << i << ": " << info.name << endl;\r
+  }\r
+}\r
+\r
+struct TestConfiguration \r
+{\r
+  long srate;\r
+  int channels;\r
+  int bufferSize;\r
+  int buffers;\r
+  int seconds;\r
+};\r
+\r
+\r
+bool DisplayStats(RtAudio::RtAudioApi api)\r
+{\r
+#ifdef __WINDOWS_DS__\r
+  // Display latency results for Windows DSound drivers.\r
+  if (api == RtAudio::WINDOWS_DS)\r
+  {\r
+    RtApiDs::RtDsStatistics s = RtApiDs::getDsStatistics();\r
+\r
+    cout << "   Latency: " << s.latency*1000.0 << "ms" << endl;\r
+    if (s.inputFrameSize)\r
+    {\r
+      cout << "   Read overruns: " << s.numberOfReadOverruns << endl;\r
+    }\r
+    if (s.outputFrameSize)\r
+    {\r
+      cout << "   Write underruns: " << s.numberOfWriteUnderruns << endl;\r
+    }\r
+\r
+    if (s.inputFrameSize)\r
+    {\r
+      cout << "   Read lead time in sample frames (device): " << s.readDeviceSafeLeadBytes/ s.inputFrameSize << endl;\r
+    }\r
+    if (s.outputFrameSize)\r
+    {\r
+      cout << "   Write lead time in sample frames (device): " << s.writeDeviceSafeLeadBytes / s.outputFrameSize << endl;\r
+      cout << "   Write lead time in sample frames (buffer): " << s.writeDeviceBufferLeadBytes / s.outputFrameSize << endl;\r
+\r
+    }\r
+\r
+  }\r
+#endif\r
+  return true;\r
+}\r
+\r
+void InputTest( RtAudio::RtAudioApi api, \r
+                int inputDevice,\r
+                const std::string &fileName,\r
+                TestConfiguration &configuration )\r
+{\r
+  RtAudio rt(api);\r
+\r
+  int bufferSize = configuration.bufferSize;\r
+\r
+  RtAudioDeviceInfo info = rt.getDeviceInfo(inputDevice);\r
+  cout << "Reading from device " << inputDevice << " (" << info.name << ")\n";\r
+\r
+  rt.openStream(0,0,inputDevice,configuration.channels, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);\r
+  if (bufferSize != configuration.bufferSize)\r
+    {\r
+      cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;\r
+      configuration.bufferSize = bufferSize;\r
+\r
+    }\r
+\r
+  int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);\r
+\r
+  if (fileName.length() == 0) \r
+    {\r
+      // just run the stream.\r
+      rt.startStream();\r
+      for (int i = 0; i < nTicks; ++i)\r
+        {\r
+          rt.tickStream();\r
+        }\r
+      rt.stopStream();\r
+    } else \r
+    {\r
+      if (configuration.seconds > 10) {\r
+        throw CommandLineException("Capture of more than 10 seconds of data is not supported.");\r
+      }\r
+      std::vector<short> data;\r
+      // we could be smarter, but the point here is to capture data without interfering with the stream.\r
+      // File writes while ticking the stream is not cool. \r
+      data.resize(nTicks*configuration.bufferSize*configuration.channels); // potentially very big. That's why we restrict capture to 10 seconds.\r
+      short *pData = &data[0];\r
+\r
+      rt.startStream();\r
+      int i;\r
+      for (i = 0; i < nTicks; ++i)\r
+        {\r
+          rt.tickStream();\r
+          short *streamBuffer = (short*)rt.getStreamBuffer();\r
+          for (int samples = 0; samples < configuration.bufferSize; ++samples)\r
+            {\r
+              for (int channel = 0; channel < configuration.channels; ++channel)\r
+                {\r
+                  *pData ++ = *streamBuffer++;\r
+                }\r
+            }\r
+        }\r
+      rt.stopStream();\r
+      remove(fileName.c_str());\r
+      FileWvOut wvOut;\r
+      wvOut.openFile( fileName.c_str(), configuration.channels, FileWrite::FILE_WAV );\r
+\r
+      StkFrames frame(1,configuration.channels,false);\r
+      pData = &data[0];\r
+\r
+      for (i = 0; i < nTicks; ++i) {\r
+        for (int samples = 0; samples < configuration.bufferSize; ++samples) {\r
+          for (int channel = 0; channel < configuration.channels; ++channel) {\r
+            frame[channel] = (float)( *pData++*( 1.0 / 32768.0 ) );\r
+          }\r
+          wvOut.tickFrame(frame);\r
+        }\r
+      }\r
+      wvOut.closeFile();\r
+    }\r
+  rt.closeStream();\r
+\r
+  if (DisplayStats(api)) {\r
+    cout << "Test succeeded." << endl;\r
+  }\r
+}\r
+\r
+void OutputTest( RtAudio::RtAudioApi api, \r
+                 int outputDevice,\r
+                 TestConfiguration &configuration )\r
+{\r
+  RtAudio rt(api);\r
+  int bufferSize = configuration.bufferSize;\r
+\r
+  RtAudioDeviceInfo info = rt.getDeviceInfo(outputDevice);\r
+  cout << "Writing to " << info.name << "...\n";\r
+\r
+  rt.openStream(outputDevice,configuration.channels, 0,0, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);\r
+  if (bufferSize != configuration.bufferSize) {\r
+    cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;\r
+    configuration.bufferSize = bufferSize;\r
+  }\r
+\r
+  rt.startStream();\r
+  short *pBuffer = (short*)rt.getStreamBuffer();\r
+  int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);\r
+\r
+  double phase = 0;\r
+  double deltaPhase = 880.0/configuration.srate;\r
+  for (int i = 0; i < nTicks; ++i) {\r
+    short *p = pBuffer;\r
+    for (int samp = 0; samp < configuration.bufferSize; ++samp) {\r
+      short val = (short)(sin(phase)*(32768/4)); // sin()*0.25 magnitude. Audible, but not damaging to ears or speakers.\r
+      phase += deltaPhase;\r
+\r
+      for (int chan = 0; chan < configuration.channels; ++chan) {\r
+        *p++ = val;\r
+      }\r
+    }\r
+    rt.tickStream();\r
+  }\r
+  rt.stopStream();\r
+  rt.closeStream();\r
+\r
+  if ( DisplayStats(api) ) {\r
+    cout << "Test succeeded." << endl;\r
+  }\r
+}\r
+\r
+void DuplexTest( RtAudio::RtAudioApi api, \r
+                 int inputDevice,\r
+                 int outputDevice,\r
+                 TestConfiguration &configuration )\r
+{\r
+  RtAudio rt(api);\r
+  int bufferSize = configuration.bufferSize;\r
+\r
+  RtAudioDeviceInfo info = rt.getDeviceInfo(inputDevice);\r
+  cout << "Reading from  " << info.name << ", " << endl;\r
+  info = rt.getDeviceInfo(outputDevice);\r
+  cout << "Writing to  " << info.name << "..." << endl;\r
+\r
+  rt.openStream(outputDevice,configuration.channels, inputDevice,configuration.channels, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);\r
+  if (bufferSize != configuration.bufferSize)\r
+    {\r
+      cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;\r
+      configuration.bufferSize = bufferSize;\r
+    }\r
+\r
+  rt.startStream();\r
+  short *pBuffer = (short*)rt.getStreamBuffer();\r
+  int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);\r
+\r
+  for (int i = 0; i < nTicks; ++i) {\r
+    rt.tickStream();\r
+  }\r
+  rt.stopStream();\r
+  rt.closeStream();\r
+\r
+  if ( DisplayStats(api) ) {\r
+    cout << "Test succeeded." << endl;\r
+  }\r
+}\r
+\r
+int main(int argc, char **argv)\r
+{\r
+  try\r
+    {\r
+      CommandLine commandLine;\r
+\r
+      TestConfiguration configuration;\r
+      bool displayHelp;\r
+      bool useDsound;\r
+      bool useAsio;\r
+\r
+      commandLine.AddOption("h",&displayHelp);\r
+      commandLine.AddOption("?",&displayHelp);\r
+      commandLine.AddOption("dsound",&useDsound);\r
+      commandLine.AddOption("asio",&useAsio);\r
+      commandLine.AddOption("srate",&configuration.srate,44100L);\r
+      commandLine.AddOption("channels",&configuration.channels,2);\r
+      commandLine.AddOption("seconds",&configuration.seconds,5);\r
+      commandLine.AddOption("buffers",&configuration.buffers,2);\r
+      commandLine.AddOption("size",&configuration.bufferSize,128);\r
+\r
+      commandLine.ProcessCommandLine(argc,argv);\r
+\r
+      if (displayHelp || commandLine.GetArguments().size() == 0)\r
+        {\r
+          DisplayHelp(cout);\r
+          return 0;\r
+        }\r
+      if (useDsound) \r
+        {\r
+          rtApi = RtAudio::WINDOWS_DS;\r
+        } else if (useAsio)\r
+        {\r
+          rtApi = RtAudio::WINDOWS_ASIO;\r
+        } else {\r
+        throw CommandLineException("Please specify an API to use: '-dsound', or '-asio'");\r
+      }\r
+\r
+      std::string testName;\r
+      commandLine.GetArgument(0,&testName);\r
+      if (testName == "enum")\r
+        {\r
+          EnumerateDevices(rtApi);\r
+        } else if (testName == "inputtest")\r
+        {\r
+          int inputDevice;\r
+          std::string fileName;\r
+          commandLine.GetArgument(1,&inputDevice);\r
+          if (commandLine.GetArguments().size() >= 2)\r
+            {\r
+              commandLine.GetArgument(2,&fileName);\r
+            }\r
+          InputTest(rtApi,inputDevice,fileName,configuration);\r
+        } else if (testName == "outputtest")\r
+        {\r
+          int inputDevice;\r
+          commandLine.GetArgument(1,&inputDevice);\r
+          OutputTest(rtApi,inputDevice,configuration);\r
+        } else if (testName == "duplextest")\r
+        {\r
+          int inputDevice;\r
+          int outputDevice;\r
+          commandLine.GetArgument(1,&inputDevice);\r
+          commandLine.GetArgument(2,&outputDevice);\r
+          DuplexTest(rtApi,inputDevice,outputDevice,configuration);\r
+        } else {\r
+        throw CommandLineException("Not a valid test name.");\r
+      }\r
+\r
+    } catch (CommandLineException &e)\r
+    {\r
+      cerr << e.what() << endl << endl;\r
+      cerr << "Run 'rtaudiotest -h' to see the commandline syntax." << endl;\r
+      return 3;\r
+    } catch (RtError &e)\r
+    {\r
+      cerr << e.getMessage() << endl;\r
+      return 3;\r
+\r
+    } catch (std::exception &e)\r
+    {\r
+      cerr << "Error: " << e.what() << endl;\r
+      return 3;\r
+\r
+    }\r
+  return 0;\r
+}\r
diff --git a/tests/Windows/rtaudiotest/rtaudiotest.dsp b/tests/Windows/rtaudiotest/rtaudiotest.dsp
new file mode 100644 (file)
index 0000000..519756b
--- /dev/null
@@ -0,0 +1,146 @@
+# Microsoft Developer Studio Project File - Name="rtaudiotest" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=rtaudiotest - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rtaudiotest.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rtaudiotest.mak" CFG="rtaudiotest - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "rtaudiotest - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "rtaudiotest - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "rtaudiotest - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MT /W3 /GX /Zi /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__LITTLE_ENDIAN__" /YX /FD /c\r
+# ADD BASE RSC /l 0x409 /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib winmm.lib /nologo /subsystem:console /debug /machine:I386\r
+\r
+!ELSEIF  "$(CFG)" == "rtaudiotest - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__LITTLE_ENDIAN__" /FR /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x409 /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib dsound.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "rtaudiotest - Win32 Release"\r
+# Name "rtaudiotest - Win32 Debug"\r
+# Begin Group "Source Files"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\asio\asio.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\asio\asiodrivers.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\asio\asiolist.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\RtAudio.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\rtaudiotest.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\StdOpt.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\Stk.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\src\WvOut.cpp\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header Files"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# Begin Source File\r
+\r
+SOURCE=..\..\include\RtAudio.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\StdOpt.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\include\Stk.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\..\include\WvOut.h\r
+# End Source File\r
+# End Group\r
+# Begin Group "Resource Files"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/tests/Windows/rtaudiotest/rtaudiotest.dsw b/tests/Windows/rtaudiotest/rtaudiotest.dsw
new file mode 100644 (file)
index 0000000..eabbccd
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
+\r
+###############################################################################\r
+\r
+Project: "rtaudiotest"=".\rtaudiotest.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
index e941e1e3f92280c0eefef52bfa06c9fd9eaeb7a3..f51ad6f96d16549aa77d47710e1096a6f37baed9 100644 (file)
@@ -53,7 +53,7 @@ int inout(char *buffer, int buffer_size, void *)
 int main(int argc, char *argv[])
 {
   int chans, fs, device = 0;
-  RtAudio *audio;
+  RtAudio *audio = 0;
   char input;
 
   // minimal command-line checking
@@ -67,8 +67,8 @@ int main(int argc, char *argv[])
   // Open the realtime output device
   int buffer_size = 512;
   try {
-    audio = new RtAudio(device, chans, device, chans,
-                        FORMAT, fs, &buffer_size, 8);
+    audio = new RtAudio( device, chans, device, chans,
+                        FORMAT, fs, &buffer_size, 8 );
   }
   catch (RtError &error) {
     error.printMessage();
index 57f225c91f17cfbc29ed5daa845b0c628759b280..b37841b81352e4413f16d4b142862a1ecbb966c1 100644 (file)
@@ -76,8 +76,8 @@ int saw(char *buffer, int buffer_size, void *data)
 int main(int argc, char *argv[])
 {
   int buffer_size, fs, device = 0;
-  RtAudio *audio;
-  double *data;
+  RtAudio *audio = 0;
+  double *data = 0;
   char input;
 
   // minimal command-line checking
index bba4d737d4c94e86d9d52a58a04628268f459449..18fa8c18918e7d7aaaa3470961d4fe0665af5e4b 100644 (file)
@@ -51,7 +51,7 @@ int main(int argc, char *argv[])
   int chans, fs, buffer_size, device = 0;
   long frames, counter = 0;
   MY_TYPE *buffer;
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // minimal command-line checking
   if (argc != 3 && argc != 4 ) usage();
index 81a4234d68f6def3a3e2ecc19c600c131e5f77ee..113efb071230a13b8cd608f1501388ae07bb78d4 100644 (file)
@@ -12,7 +12,7 @@
 
 int main(int argc, char *argv[])
 {
-  RtAudio *audio;
+  RtAudio *audio = 0;
   RtAudioDeviceInfo info;
   try {
     audio = new RtAudio();
index fcc921f08383f111216dac94c3c524e4b95701e9..6dd477d587dc934ae2d9db454f6cce08dfc605e3 100644 (file)
@@ -60,7 +60,7 @@ int main(int argc, char *argv[])
   MY_TYPE *buffer;
   char *file;
   FILE *fd;
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // minimal command-line checking
   if (argc != 4 && argc != 5 ) usage();
index 4b2cbdde6af113f4ce9174c01f124ee60ac651f6..b99248fdcc5eebca3053ce573536c09d692dbb45 100644 (file)
@@ -58,7 +58,7 @@ int main(int argc, char *argv[])
   int chans, fs, buffer_size, device = 0;
   long frames, counter = 0, i, j;
   MY_TYPE *buffer;
-  RtAudio *audio;
+  RtAudio *audio = 0;
   double *data = 0;
 
   // minimal command-line checking
index 717ce2c1fea7571bbb83dbba0db3830a08b70ab1..3a207f9e2a10e7803c40c6f652b365c0c4ead30b 100644 (file)
@@ -54,7 +54,7 @@ int main(int argc, char *argv[])
   long frames, counter = 0;
   MY_TYPE *buffer;
   FILE *fd;
-  RtAudio *audio;
+  RtAudio *audio = 0;
 
   // minimal command-line checking
   if (argc != 3 && argc != 4 ) usage();
index 508cf4d42dc23dc8eeb3c789688821c9886354a0..17badd09279deaab82ca22e5e3a991b95caca301 100644 (file)
@@ -62,7 +62,7 @@ int main(int argc, char *argv[])
   int chans, fs, buffer_size, device = 0;
   long frames, counter = 0, i, j;
   MY_TYPE *buffer1, *buffer2;
-  RtAudio *stream1, *stream2;
+  RtAudio *stream1 = 0, *stream2 = 0;
   FILE *fd;
   double *data = 0;