Check in of new version 4.0.0 distribution (GS).
authorGary Scavone <gary@music.mcgill.ca>
Tue, 7 Aug 2007 14:52:05 +0000 (14:52 +0000)
committerStephen Sinclair <sinclair@music.mcgill.ca>
Thu, 10 Oct 2013 23:19:40 +0000 (01:19 +0200)
60 files changed:
Makefile.in [new file with mode: 0644]
RtAudio.cpp
RtAudio.h
RtError.h
configure.ac
doc/doxygen/Doxyfile
doc/doxygen/acknowledge.txt [new file with mode: 0644]
doc/doxygen/apinotes.txt [new file with mode: 0644]
doc/doxygen/compiling.txt [new file with mode: 0644]
doc/doxygen/duplex.txt [new file with mode: 0644]
doc/doxygen/error.txt [new file with mode: 0644]
doc/doxygen/footer.html
doc/doxygen/header.html
doc/doxygen/license.txt [new file with mode: 0644]
doc/doxygen/multi.txt [new file with mode: 0644]
doc/doxygen/playback.txt [new file with mode: 0644]
doc/doxygen/probe.txt [new file with mode: 0644]
doc/doxygen/recording.txt [new file with mode: 0644]
doc/doxygen/settings.txt [new file with mode: 0644]
doc/doxygen/tutorial.txt
doc/release.txt
install
oss/soundcard.h [new file with mode: 0644]
readme
rtaudio-config.in [new file with mode: 0644]
tests/Makefile.in
tests/Windows/Debug/.placeholder [deleted file]
tests/Windows/Release/.placeholder [deleted file]
tests/Windows/call_inout.dsp [deleted file]
tests/Windows/call_playtwo.dsp [deleted file]
tests/Windows/call_saw.dsp [deleted file]
tests/Windows/call_twostreams.dsp [deleted file]
tests/Windows/in_out.dsp [deleted file]
tests/Windows/info.dsp [deleted file]
tests/Windows/play_raw.dsp [deleted file]
tests/Windows/play_saw.dsp [deleted file]
tests/Windows/record_raw.dsp [deleted file]
tests/Windows/rtaudio.dsw [deleted file]
tests/Windows/rtaudiotest/Release/.placeholder [deleted file]
tests/Windows/rtaudiotest/StdOpt.cpp [deleted file]
tests/Windows/rtaudiotest/StdOpt.h [deleted file]
tests/Windows/rtaudiotest/readme.txt [deleted file]
tests/Windows/rtaudiotest/rtaudiotest.cpp [deleted file]
tests/Windows/rtaudiotest/rtaudiotest.dsp [deleted file]
tests/Windows/rtaudiotest/rtaudiotest.dsw [deleted file]
tests/Windows/twostreams.dsp [deleted file]
tests/call_inout.cpp [deleted file]
tests/call_saw.cpp [deleted file]
tests/duplex.cpp [new file with mode: 0644]
tests/in_out.cpp [deleted file]
tests/info.cpp [deleted file]
tests/play_raw.cpp [deleted file]
tests/play_saw.cpp [deleted file]
tests/playraw.cpp [new file with mode: 0644]
tests/playsaw.cpp [new file with mode: 0644]
tests/probe.cpp [new file with mode: 0644]
tests/record.cpp [new file with mode: 0644]
tests/record_raw.cpp [deleted file]
tests/testall.cpp [new file with mode: 0644]
tests/twostreams.cpp [deleted file]

diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..f658377
--- /dev/null
@@ -0,0 +1,43 @@
+### RtAudio library Makefile
+
+RM = /bin/rm
+
+OBJECTS        = RtAudio.o
+
+LIBRARY  = librtaudio.a
+
+CC       = @CXX@
+AR       = @AR@
+RANLIB   = @RANLIB@
+
+DEFS     = @debug@
+DEFS    += @audio_apis@
+CFLAGS   = @cflags@
+CFLAGS  += @warn@
+
+all : $(LIBRARY)
+
+tests:
+       cd tests && $(MAKE) all
+
+$(LIBRARY): $(OBJECTS)
+       $(AR) ruv $(LIBRARY) $(OBJECTS)
+       ranlib $(LIBRARY)
+
+%.o : %.cpp
+       $(CC) $(CFLAGS) $(DEFS) -c $(<) -o $@
+
+clean : 
+       -rm -f $(LIBRARY)
+       -rm -f $(OBJECTS)
+       -rm -f *~
+       cd tests && $(MAKE) clean
+
+distclean: clean
+       -rm -rf config.log autom4te.cache Makefile rtaudio-config
+       cd tests && $(MAKE) distclean
+
+strip : 
+       strip $(LIBRARY)
+       ranlib $(LIBRARY)
+       cd tests && $(MAKE) strip
index f7c918de7b8dbea0f11f12d0ac1b293344cd3cbf..71be207b5ecb339c47925bbd7245e28d8f8b51d3 100644 (file)
@@ -4,13 +4,13 @@
 
     RtAudio provides a common API (Application Programming Interface)
     for realtime audio input/output across Linux (native ALSA, Jack,
-    and OSS), SGI, Macintosh OS X (CoreAudio), and Windows
+    and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows
     (DirectSound and ASIO) operating systems.
 
-    RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/
+    RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
 
     RtAudio: realtime audio i/o C++ classes
-    Copyright (c) 2001-2005 Gary P. Scavone
+    Copyright (c) 2001-2007 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -24,8 +24,9 @@
     included in all copies or substantial portions of the Software.
 
     Any person wishing to distribute modifications to the Software is
-    requested to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 */
 /************************************************************************/
 
-// RtAudio: Version 3.0.3 (18 November 2005)
+// RtAudio: Version 4.0
 
 #include "RtAudio.h"
 #include <iostream>
-#include <stdio.h>
 
 // Static variable definitions.
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
@@ -52,101 +52,59 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
 
 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
-  #define MUTEX_DESTROY(A)    DeleteCriticalSection(A);
-  #define MUTEX_LOCK(A)      EnterCriticalSection(A)
+  #define MUTEX_DESTROY(A)    DeleteCriticalSection(A)
+  #define MUTEX_LOCK(A)       EnterCriticalSection(A)
   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)
-#else // pthread API
+#elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
+  // pthread API
   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
-  #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A);
+  #define MUTEX_DESTROY(A)    pthread_mutex_destroy(A)
   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)
   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)
+#else
+  #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions
+  #define MUTEX_DESTROY(A)    abs(*A) // dummy definitions
 #endif
 
 // *************************************************** //
 //
-// Public common (OS-independent) methods.
+// RtAudio definitions.
 //
 // *************************************************** //
 
-RtAudio :: RtAudio( RtAudioApi api )
-{
-  initialize( api );
-}
-
-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( 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_;
-}
-
-void RtAudio :: openStream( int outputDevice, int outputChannels,
-                            int inputDevice, int inputChannels,
-                            RtAudioFormat format, int sampleRate,
-                            int *bufferSize, int numberOfBuffers )
+void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis ) throw()
 {
-  rtapi_->openStream( outputDevice, outputChannels, inputDevice,
-                      inputChannels, format, sampleRate,
-                      bufferSize, numberOfBuffers );
-}
+  apis.clear();
 
-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 );
+  // The order here will control the order of RtAudio's API search in
+  // the constructor.
+#if defined(__UNIX_JACK__)
+  apis.push_back( UNIX_JACK );
+#endif
+#if defined(__LINUX_ALSA__)
+  apis.push_back( LINUX_ALSA );
+#endif
+#if defined(__LINUX_OSS__)
+  apis.push_back( LINUX_OSS );
+#endif
+#if defined(__WINDOWS_ASIO__)
+  apis.push_back( WINDOWS_ASIO );
+#endif
+#if defined(__WINDOWS_DS__)
+  apis.push_back( WINDOWS_DS );
+#endif
+#if defined(__MACOSX_CORE__)
+  apis.push_back( MACOSX_CORE );
+#endif
+#if defined(__RTAUDIO_DUMMY__)
+  apis.push_back( RTAUDIO_DUMMY );
+#endif
 }
 
-void RtAudio::initialize( RtAudioApi api )
+void RtAudio :: openRtApi( RtAudio::Api api )
 {
-  rtapi_ = 0;
-
-  // First look for a compiled match to a specified API value. If one
-  // of these constructors throws an error, it will be passed up the
-  // inheritance chain.
-#if defined(__LINUX_JACK__)
-  if ( api == LINUX_JACK )
+#if defined(__UNIX_JACK__)
+  if ( api == UNIX_JACK )
     rtapi_ = new RtApiJack();
 #endif
 #if defined(__LINUX_ALSA__)
@@ -165,314 +123,240 @@ void RtAudio::initialize( RtAudioApi api )
   if ( api == WINDOWS_DS )
     rtapi_ = new RtApiDs();
 #endif
-#if defined(__IRIX_AL__)
-  if ( api == IRIX_AL )
-    rtapi_ = new RtApiAl();
-#endif
 #if defined(__MACOSX_CORE__)
   if ( api == MACOSX_CORE )
     rtapi_ = new RtApiCore();
 #endif
+#if defined(__RTAUDIO_DUMMY__)
+  if ( api == RTAUDIO_DUMMY )
+    rtapi_ = new RtApiDummy();
+#endif
+}
 
-  if ( rtapi_ ) return;
-  if ( api > 0 ) {
-    // No compiled support for specified API value.
-    throw RtError( "RtAudio: no compiled support for specified API argument!", RtError::INVALID_PARAMETER );
-  }
+RtAudio :: RtAudio( RtAudio::Api api ) throw()
+{
+  rtapi_ = 0;
 
-  // No specified API ... search for "best" option.
-  try {
-#if defined(__LINUX_JACK__)
-    rtapi_ = new RtApiJack();
-#elif defined(__WINDOWS_ASIO__)
-    rtapi_ = new RtApiAsio();
-#elif defined(__IRIX_AL__)
-    rtapi_ = new RtApiAl();
-#elif defined(__MACOSX_CORE__)
-    rtapi_ = new RtApiCore();
-#else
-    ;
-#endif
-  }
-  catch (RtError &) {
-#if defined(__RTAUDIO_DEBUG__)
-    fprintf(stderr, "\nRtAudio: no devices found for first api option (JACK, ASIO, Al, or CoreAudio).\n\n");
-#endif
-    rtapi_ = 0;
-  }
+  if ( api != UNSPECIFIED ) {
+    // Attempt to open the specified API.
+    openRtApi( api );
+    if ( rtapi_ ) return;
 
-  if ( rtapi_ ) return;
+    // No compiled support for specified API value.  Issue a debug
+    // warning and continue as if no API was specified.
+    std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
+  }
 
-// Try second API support
-  if ( rtapi_ == 0 ) {
-    try {
-#if defined(__LINUX_ALSA__)
-      rtapi_ = new RtApiAlsa();
-#elif defined(__WINDOWS_DS__)
-      rtapi_ = new RtApiDs();
-#else
-      ;
-#endif
-    }
-    catch (RtError &) {
-#if defined(__RTAUDIO_DEBUG__)
-      fprintf(stderr, "\nRtAudio: no devices found for second api option (Alsa or DirectSound).\n\n");
-#endif
-      rtapi_ = 0;
-    }
+  // Iterate through the compiled APIs and return as soon as we find
+  // one with at least one device or we reach the end of the list.
+  std::vector< RtAudio::Api > apis;
+  getCompiledApi( apis );
+  for ( unsigned int i=0; i<apis.size(); i++ ) {
+    openRtApi( apis[i] );
+    if ( rtapi_->getDeviceCount() ) break;
   }
 
   if ( rtapi_ ) return;
 
-  // Try third API support
-  if ( rtapi_ == 0 ) {
-#if defined(__LINUX_OSS__)
-    try {
-      rtapi_ = new RtApiOss();
-    }
-    catch (RtError &error) {
-      rtapi_ = 0;
-    }
-#else
-    ;
-#endif
-  }
+  // It should not be possible to get here because the preprocessor
+  // definition __RTAUDIO_DUMMY__ is automatically defined if no
+  // API-specific definitions are passed to the compiler. But just in
+  // case something weird happens, we'll print out an error message.
+  std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n";
+}
 
-  if ( rtapi_ == 0 ) {
-    // No devices found.
-    throw RtError( "RtAudio: no devices found for compiled audio APIs!", RtError::NO_DEVICES_FOUND );
-  }
+RtAudio :: ~RtAudio() throw()
+{
+  delete rtapi_;
 }
 
+void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
+                            RtAudio::StreamParameters *inputParameters,
+                            RtAudioFormat format, unsigned int sampleRate,
+                            unsigned int *bufferFrames,
+                            RtAudioCallback callback, void *userData,
+                            RtAudio::StreamOptions *options )
+{
+  return rtapi_->openStream( outputParameters, inputParameters, format,
+                             sampleRate, bufferFrames, callback,
+                             userData, options );
+}
+
+// *************************************************** //
+//
+// Public RtApi definitions (see end of file for
+// private or protected utility functions).
+//
+// *************************************************** //
+
 RtApi :: RtApi()
 {
+  stream_.state = STREAM_CLOSED;
   stream_.mode = UNINITIALIZED;
-  stream_.state = STREAM_STOPPED;
   stream_.apiHandle = 0;
-  MUTEX_INITIALIZE(&stream_.mutex);
+  stream_.userBuffer[0] = 0;
+  stream_.userBuffer[1] = 0;
+  MUTEX_INITIALIZE( &stream_.mutex );
+  showWarnings_ = true;
 }
 
 RtApi :: ~RtApi()
 {
-  MUTEX_DESTROY(&stream_.mutex);
+  MUTEX_DESTROY( &stream_.mutex );
 }
 
-void RtApi :: openStream( int outputDevice, int outputChannels,
-                         int inputDevice, int inputChannels,
-                         RtAudioFormat format, int sampleRate,
-                         int *bufferSize, int *numberOfBuffers )
+void RtApi :: openStream( RtAudio::StreamParameters *oParams,
+                          RtAudio::StreamParameters *iParams,
+                          RtAudioFormat format, unsigned int sampleRate,
+                          unsigned int *bufferFrames,
+                          RtAudioCallback callback, void *userData,
+                          RtAudio::StreamOptions *options )
 {
-  this->openStream( outputDevice, outputChannels, inputDevice,
-                    inputChannels, format, sampleRate,
-                    bufferSize, *numberOfBuffers );
-  *numberOfBuffers = stream_.nBuffers;
-}
+  if ( stream_.state != STREAM_CLOSED ) {
+    errorText_ = "RtApi::openStream: a stream is already open!";
+    error( INVALID_CALL );
+  }
 
-void RtApi :: openStream( int outputDevice, int outputChannels,
-                         int inputDevice, int inputChannels,
-                         RtAudioFormat format, int sampleRate,
-                         int *bufferSize, int numberOfBuffers )
-{
-  if ( stream_.mode != UNINITIALIZED ) {
-    sprintf(message_, "RtApi: only one open stream allowed per class instance.");
-    error(RtError::INVALID_STREAM);
+  if ( oParams && oParams->nChannels < 1 ) {
+    errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
+    error( INVALID_CALL );
   }
 
-  if (outputChannels < 1 && inputChannels < 1) {
-    sprintf(message_,"RtApi: one or both 'channel' parameters must be greater than zero.");
-    error(RtError::INVALID_PARAMETER);
+  if ( iParams && iParams->nChannels < 1 ) {
+    errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
+    error( INVALID_CALL );
+  }
+
+  if ( oParams == NULL && iParams == NULL ) {
+    errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
+    error( INVALID_CALL );
   }
 
   if ( formatBytes(format) == 0 ) {
-    sprintf(message_,"RtApi: 'format' parameter value is undefined.");
-    error(RtError::INVALID_PARAMETER);
+    errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
+    error( INVALID_CALL );
   }
 
-  if ( outputChannels > 0 ) {
-    if (outputDevice > nDevices_ || outputDevice < 0) {
-      sprintf(message_,"RtApi: 'outputDevice' parameter value (%d) is invalid.", outputDevice);
-      error(RtError::INVALID_PARAMETER);
+  unsigned int nDevices = getDeviceCount();
+  unsigned int oChannels = 0;
+  if ( oParams ) {
+    oChannels = oParams->nChannels;
+    if ( oParams->deviceId >= nDevices ) {
+      errorText_ = "RtApi::openStream: output device parameter value is invalid.";
+      error( INVALID_CALL );
     }
   }
 
-  if ( inputChannels > 0 ) {
-    if (inputDevice > nDevices_ || inputDevice < 0) {
-      sprintf(message_,"RtApi: 'inputDevice' parameter value (%d) is invalid.", inputDevice);
-      error(RtError::INVALID_PARAMETER);
+  unsigned int iChannels = 0;
+  if ( iParams ) {
+    iChannels = iParams->nChannels;
+    if ( iParams->deviceId >= nDevices ) {
+      errorText_ = "RtApi::openStream: input device parameter value is invalid.";
+      error( INVALID_CALL );
     }
   }
 
-  std::string errorMessages;
   clearStreamInfo();
-  bool result = FAILURE;
-  int device, defaultDevice = 0;
-  StreamMode mode;
-  int channels;
-  if ( outputChannels > 0 ) {
-
-    mode = OUTPUT;
-    channels = outputChannels;
+  bool result;
 
-    if ( outputDevice == 0 ) { // Try default device first.
-      defaultDevice = getDefaultOutputDevice();
-      device = defaultDevice;
-    }
-    else
-      device = outputDevice - 1;
+  if ( oChannels > 0 ) {
 
-    for ( int i=-1; i<nDevices_; i++ ) {
-      if ( i >= 0 ) { 
-        if ( i == defaultDevice ) continue;
-        device = i;
-      }
-      if ( devices_[device].probed == false ) {
-        // If the device wasn't successfully probed before, try it
-        // (again) now.
-        clearDeviceInfo(&devices_[device]);
-        probeDeviceInfo(&devices_[device]);
-      }
-      if ( devices_[device].probed )
-        result = probeDeviceOpen(device, mode, channels, sampleRate,
-                                 format, bufferSize, numberOfBuffers);
-      if ( result == SUCCESS ) break;
-      errorMessages.append( "    " );
-      errorMessages.append( message_ );
-      errorMessages.append( "\n" );
-      if ( outputDevice > 0 ) break;
-      clearStreamInfo();
-    }
+    result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
+                              sampleRate, format, bufferFrames, options );
+    if ( result == false ) error( SYSTEM );
   }
 
-  if ( inputChannels > 0 && ( result == SUCCESS || outputChannels <= 0 ) ) {
-
-    mode = INPUT;
-    channels = inputChannels;
-
-    if ( inputDevice == 0 ) { // Try default device first.
-      defaultDevice = getDefaultInputDevice();
-      device = defaultDevice;
-    }
-    else
-      device = inputDevice - 1;
+  if ( iChannels > 0 ) {
 
-    for ( int i=-1; i<nDevices_; i++ ) {
-      if (i >= 0 ) { 
-        if ( i == defaultDevice ) continue;
-        device = i;
-      }
-      if ( devices_[device].probed == false ) {
-        // If the device wasn't successfully probed before, try it
-        // (again) now.
-        clearDeviceInfo(&devices_[device]);
-        probeDeviceInfo(&devices_[device]);
-      }
-      if ( devices_[device].probed )
-        result = probeDeviceOpen( device, mode, channels, sampleRate,
-                                  format, bufferSize, numberOfBuffers );
-      if ( result == SUCCESS ) break;
-      errorMessages.append( "    " );
-      errorMessages.append( message_ );
-      errorMessages.append( "\n" );
-      if ( inputDevice > 0 ) break;
+    result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
+                              sampleRate, format, bufferFrames, options );
+    if ( result == false ) {
+      if ( oChannels > 0 ) closeStream();
+      error( SYSTEM );
     }
   }
 
-  if ( result == SUCCESS )
-    return;
-
-  // If we get here, all attempted probes failed.  Close any opened
-  // devices and clear the stream structure.
-  if ( stream_.mode != UNINITIALIZED ) closeStream();
-  clearStreamInfo();
-  if ( ( outputDevice == 0 && outputChannels > 0 )
-       || ( inputDevice == 0 && inputChannels > 0 ) )
-    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: \n%s",
-            errorMessages.c_str());
-  error(RtError::INVALID_PARAMETER);
+  stream_.callbackInfo.callback = (void *) callback;
+  stream_.callbackInfo.userData = userData;
 
-  return;
+  if ( options ) options->numberOfBuffers = stream_.nBuffers;
+  stream_.state = STREAM_STOPPED;
 }
 
-int RtApi :: getDeviceCount(void)
+unsigned int RtApi :: getDefaultInputDevice( void )
 {
-  return devices_.size();
+  // Should be implemented in subclasses if possible.
+  return 0;
 }
 
-RtApi::StreamState RtApi :: getStreamState( void ) const
+unsigned int RtApi :: getDefaultOutputDevice( void )
 {
-  return stream_.state;
+  // Should be implemented in subclasses if possible.
+  return 0;
 }
 
-RtAudioDeviceInfo RtApi :: getDeviceInfo( int device )
+void RtApi :: closeStream( void )
 {
-  if (device > (int) devices_.size() || device < 1) {
-    sprintf(message_, "RtApi: invalid device specifier (%d)!", device);
-    error(RtError::INVALID_DEVICE);
-  }
+  // MUST be implemented in subclasses!
+  return;
+}
 
-  RtAudioDeviceInfo info;
-  int deviceIndex = device - 1;
+bool RtApi :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                               unsigned int firstChannel, unsigned int sampleRate,
+                               RtAudioFormat format, unsigned int *bufferSize,
+                               RtAudio::StreamOptions *options )
+{
+  // MUST be implemented in subclasses!
+  return FAILURE;
+}
 
-  // If the device wasn't successfully probed before, try it now (or again).
-  if (devices_[deviceIndex].probed == false) {
-    clearDeviceInfo(&devices_[deviceIndex]);
-    probeDeviceInfo(&devices_[deviceIndex]);
-  }
+void RtApi :: tickStreamTime( void )
+{
+  // Subclasses that do not provide their own implementation of
+  // getStreamTime should call this function once per buffer I/O to
+  // provide basic stream time support.
 
-  info.name.append( devices_[deviceIndex].name );
-  info.probed = devices_[deviceIndex].probed;
-  if ( info.probed == true ) {
-    info.outputChannels = devices_[deviceIndex].maxOutputChannels;
-    info.inputChannels = devices_[deviceIndex].maxInputChannels;
-    info.duplexChannels = devices_[deviceIndex].maxDuplexChannels;
-    for (unsigned int i=0; i<devices_[deviceIndex].sampleRates.size(); i++)
-      info.sampleRates.push_back( devices_[deviceIndex].sampleRates[i] );
-    info.nativeFormats = devices_[deviceIndex].nativeFormats;
-    if ( (deviceIndex == getDefaultOutputDevice()) ||
-         (deviceIndex == getDefaultInputDevice()) )
-      info.isDefault = true;
-  }
+  stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
 
-  return info;
+#if defined( HAVE_GETTIMEOFDAY )
+  gettimeofday( &stream_.lastTickTimestamp, NULL );
+#endif
 }
 
-char * const RtApi :: getStreamBuffer(void)
+long RtApi :: getStreamLatency( void )
 {
   verifyStream();
-  return stream_.userBuffer;
-}
 
-int RtApi :: getDefaultInputDevice(void)
-{
-  // Should be implemented in subclasses if appropriate.
-  return 0;
-}
+  long totalLatency = 0;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+    totalLatency = stream_.latency[0];
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
+    totalLatency += stream_.latency[1];
 
-int RtApi :: getDefaultOutputDevice(void)
-{
-  // Should be implemented in subclasses if appropriate.
-  return 0;
+  return totalLatency;
 }
 
-void RtApi :: closeStream(void)
+double RtApi :: getStreamTime( void )
 {
-  // MUST be implemented in subclasses!
-}
+  verifyStream();
 
-void RtApi :: probeDeviceInfo( RtApiDevice *info )
-{
-  // MUST be implemented in subclasses!
-}
+#if defined( HAVE_GETTIMEOFDAY )
+  // Return a very accurate estimate of the stream time by
+  // adding in the elapsed time since the last tick.
+  struct timeval then;
+  struct timeval now;
 
-bool RtApi :: probeDeviceOpen( int device, StreamMode mode, int channels, 
-                               int sampleRate, RtAudioFormat format,
-                               int *bufferSize, int numberOfBuffers )
-{
-  // MUST be implemented in subclasses!
-  return FAILURE;
+  if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
+    return stream_.streamTime;
+
+  gettimeofday( &now, NULL );
+  then = stream_.lastTickTimestamp;
+  return stream_.streamTime +
+    ((now.tv_sec + 0.000001 * now.tv_usec) -
+     (then.tv_sec + 0.000001 * then.tv_usec));     
+#else
+  return stream_.streamTime;
+#endif
 }
 
 
@@ -482,7625 +366,6069 @@ bool RtApi :: probeDeviceOpen( int device, StreamMode mode, int channels,
 //
 // *************************************************** //
 
-#if defined(__LINUX_OSS__)
+#if defined(__MACOSX_CORE__)
 
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/soundcard.h>
-#include <errno.h>
-#include <math.h>
+// The OS X CoreAudio API is designed to use a separate callback
+// procedure for each of its audio devices.  A single RtAudio duplex
+// stream using two different devices is supported here, though it
+// cannot be guaranteed to always behave correctly because we cannot
+// synchronize these two callbacks.
+//
+// A property listener is installed for over/underrun information.
+// However, no functionality is currently provided to allow property
+// listeners to trigger user handlers because it is unclear what could
+// be done if a critical stream parameter (buffer size, sample rate,
+// device disconnect) notification arrived.  The listeners entail
+// quite a bit of extra code and most likely, a user program wouldn't
+// be prepared for the result anyway.  However, we do provide a flag
+// to the client callback function to inform of an over/underrun.
+//
+// The mechanism for querying and setting system parameters was
+// updated (and perhaps simplified) in OS-X version 10.4.  However,
+// since 10.4 support is not necessarily available to all users, I've
+// decided not to update the respective code at this time.  Perhaps
+// this will happen when Apple makes 10.4 free for everyone. :-)
 
-#define DAC_NAME "/dev/dsp"
-#define MAX_DEVICES 16
-#define MAX_CHANNELS 16
+// A structure to hold various information related to the CoreAudio API
+// implementation.
+struct CoreHandle {
+  AudioDeviceID id[2];    // device ids
+  UInt32 iStream[2];      // device stream index (first for mono mode)
+  bool xrun[2];
+  char *deviceBuffer;
+  pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
 
-extern "C" void *ossCallbackHandler(void * ptr);
+  CoreHandle()
+    :deviceBuffer(0), drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
-RtApiOss :: RtApiOss()
+RtApiCore :: RtApiCore()
 {
-  this->initialize();
-
-  if (nDevices_ <= 0) {
-    sprintf(message_, "RtApiOss: no Linux OSS audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
+  // Nothing to do here.
 }
 
-RtApiOss :: ~RtApiOss()
+RtApiCore :: ~RtApiCore()
 {
-  if ( stream_.mode != UNINITIALIZED )
-    closeStream();
+  // The subclass destructor gets called before the base class
+  // destructor, so close an existing stream before deallocating
+  // apiDeviceId memory.
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
-void RtApiOss :: initialize(void)
+unsigned int RtApiCore :: getDeviceCount( void )
 {
-  // Count cards and devices
-  nDevices_ = 0;
-
-  // We check /dev/dsp before probing devices.  /dev/dsp is supposed to
-  // be a link to the "default" audio device, of the form /dev/dsp0,
-  // /dev/dsp1, etc...  However, I've seen many cases where /dev/dsp was a
-  // real device, so we need to check for that.  Also, sometimes the
-  // link is to /dev/dspx and other times just dspx.  I'm not sure how
-  // the latter works, but it does.
-  char device_name[16];
-  struct stat dspstat;
-  int dsplink = -1;
-  int i = 0;
-  if (lstat(DAC_NAME, &dspstat) == 0) {
-    if (S_ISLNK(dspstat.st_mode)) {
-      i = readlink(DAC_NAME, device_name, sizeof(device_name));
-      if (i > 0) {
-        device_name[i] = '\0';
-        if (i > 8) { // check for "/dev/dspx"
-          if (!strncmp(DAC_NAME, device_name, 8))
-            dsplink = atoi(&device_name[8]);
-        }
-        else if (i > 3) { // check for "dspx"
-          if (!strncmp("dsp", device_name, 3))
-            dsplink = atoi(&device_name[3]);
-        }
-      }
-      else {
-        sprintf(message_, "RtApiOss: cannot read value of symbolic link %s.", DAC_NAME);
-        error(RtError::SYSTEM_ERROR);
-      }
-    }
+  // Find out how many audio devices there are, if any.
+  UInt32 dataSize;
+  OSStatus result = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &dataSize, NULL );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
+    error( WARNING );
+    return 0;
   }
-  else {
-    sprintf(message_, "RtApiOss: cannot stat %s.", DAC_NAME);
-    error(RtError::SYSTEM_ERROR);
-  }
-
-  // The OSS API doesn't provide a routine for determining the number
-  // of devices.  Thus, we'll just pursue a brute force method.  The
-  // idea is to start with /dev/dsp(0) and continue with higher device
-  // numbers until we reach MAX_DSP_DEVICES.  This should tell us how
-  // many devices we have ... it is not a fullproof scheme, but hopefully
-  // it will work most of the time.
-  int fd = 0;
-  RtApiDevice device;
-  for (i=-1; i<MAX_DEVICES; i++) {
-
-    // Probe /dev/dsp first, since it is supposed to be the default device.
-    if (i == -1)
-      sprintf(device_name, "%s", DAC_NAME);
-    else if (i == dsplink)
-      continue; // We've aready probed this device via /dev/dsp link ... try next device.
-    else
-      sprintf(device_name, "%s%d", DAC_NAME, i);
-
-    // First try to open the device for playback, then record mode.
-    fd = open(device_name, O_WRONLY | O_NONBLOCK);
-    if (fd == -1) {
-      // Open device for playback failed ... either busy or doesn't exist.
-      if (errno != EBUSY && errno != EAGAIN) {
-        // Try to open for capture
-        fd = open(device_name, O_RDONLY | O_NONBLOCK);
-        if (fd == -1) {
-          // Open device for record failed.
-          if (errno != EBUSY && errno != EAGAIN)
-            continue;
-          else {
-            sprintf(message_, "RtApiOss: OSS record device (%s) is busy.", device_name);
-            error(RtError::WARNING);
-            // still count it for now
-          }
-        }
-      }
-      else {
-        sprintf(message_, "RtApiOss: OSS playback device (%s) is busy.", device_name);
-        error(RtError::WARNING);
-        // still count it for now
-      }
-    }
 
-    if (fd >= 0) close(fd);
-    device.name.erase();
-    device.name.append( (const char *)device_name, strlen(device_name)+1);
-    devices_.push_back(device);
-    nDevices_++;
-  }
+  return dataSize / sizeof( AudioDeviceID );
 }
 
-void RtApiOss :: probeDeviceInfo(RtApiDevice *info)
+unsigned int RtApiCore :: getDefaultInputDevice( void )
 {
-  int i, fd, channels, mask;
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices <= 1 ) return 0;
 
-  // The OSS API doesn't provide a means for probing the capabilities
-  // of devices.  Thus, we'll just pursue a brute force method.
+  AudioDeviceID id;
+  UInt32 dataSize = sizeof( AudioDeviceID );
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
+                                              &dataSize, &id );
 
-  // First try for playback
-  fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK);
-  if (fd == -1) {
-    // Open device failed ... either busy or doesn't exist
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message_, "RtApiOss: OSS playback device (%s) is busy and cannot be probed.",
-              info->name.c_str());
-    else
-      sprintf(message_, "RtApiOss: OSS playback device (%s) open error.", info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
-  }
-
-  // We have an open device ... see how many channels it can handle
-  for (i=MAX_CHANNELS; i>0; i--) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
-      // This would normally indicate some sort of hardware error, but under ALSA's
-      // OSS emulation, it sometimes indicates an invalid channel value.  Further,
-      // the returned channel value is not changed. So, we'll ignore the possible
-      // hardware error.
-      continue; // try next channel number
-    }
-    // Check to see whether the device supports the requested number of channels
-    if (channels != i ) continue; // try next channel number
-    // If here, we found the largest working channel value
-    break;
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
+    error( WARNING );
+    return 0;
   }
-  info->maxOutputChannels = i;
 
-  // Now find the minimum number of channels it can handle
-  for (i=1; i<=info->maxOutputChannels; i++) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-      continue; // try next channel number
-    // If here, we found the smallest working channel value
-    break;
+  dataSize *= nDevices;
+  AudioDeviceID deviceList[ nDevices ];
+  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
+    error( WARNING );
+    return 0;
   }
-  info->minOutputChannels = i;
-  close(fd);
 
- capture_probe:
-  // Now try for capture
-  fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK);
-  if (fd == -1) {
-    // Open device for capture failed ... either busy or doesn't exist
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message_, "RtApiOss: OSS capture device (%s) is busy and cannot be probed.",
-              info->name.c_str());
-    else
-      sprintf(message_, "RtApiOss: OSS capture device (%s) open error.", info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
-  }
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
 
-  // We have the device open for capture ... see how many channels it can handle
-  for (i=MAX_CHANNELS; i>0; i--) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
-      continue; // as above
-    }
-    // If here, we found a working channel value
-    break;
-  }
-  info->maxInputChannels = i;
+  errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
+  error( WARNING );
+  return 0;
+}
 
-  // Now find the minimum number of channels it can handle
-  for (i=1; i<=info->maxInputChannels; i++) {
-    channels = i;
-    if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-      continue; // try next channel number
-    // If here, we found the smallest working channel value
-    break;
+unsigned int RtApiCore :: getDefaultOutputDevice( void )
+{
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices <= 1 ) return 0;
+
+  AudioDeviceID id;
+  UInt32 dataSize = sizeof( AudioDeviceID );
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
+                                              &dataSize, &id );
+
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
+    error( WARNING );
+    return 0;
   }
-  info->minInputChannels = i;
-  close(fd);
 
-  if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) {
-    sprintf(message_, "RtApiOss: device (%s) reports zero channels for input and output.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  dataSize *= nDevices;
+  AudioDeviceID deviceList[ nDevices ];
+  result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
+    error( WARNING );
+    return 0;
   }
 
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
-
-  fd = open(info->name.c_str(), O_RDWR | O_NONBLOCK);
-  if (fd == -1)
-    goto probe_parameters;
-
-  ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
-  ioctl(fd, SNDCTL_DSP_GETCAPS, &mask);
-  if (mask & DSP_CAP_DUPLEX) {
-    info->hasDuplexSupport = true;
-    // We have the device open for duplex ... see how many channels it can handle
-    for (i=MAX_CHANNELS; i>0; i--) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // as above
-      // If here, we found a working channel value
-      break;
-    }
-    info->maxDuplexChannels = i;
+  for ( unsigned int i=0; i<nDevices; i++ )
+    if ( id == deviceList[i] ) return i;
 
-    // Now find the minimum number of channels it can handle
-    for (i=1; i<=info->maxDuplexChannels; i++) {
-      channels = i;
-      if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
-        continue; // try next channel number
-      // If here, we found the smallest working channel value
-      break;
-    }
-    info->minDuplexChannels = i;
-  }
-  close(fd);
+  errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
+  error( WARNING );
+  return 0;
+}
 
- probe_parameters:
-  // At this point, we need to figure out the supported data formats
-  // and sample rates.  We'll proceed by openning the device in the
-  // direction with the maximum number of channels, or playback if
-  // they are equal.  This might limit our sample rate options, but so
-  // be it.
+RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
-  if (info->maxOutputChannels >= info->maxInputChannels) {
-    fd = open(info->name.c_str(), O_WRONLY | O_NONBLOCK);
-    channels = info->maxOutputChannels;
-  }
-  else {
-    fd = open(info->name.c_str(), O_RDONLY | O_NONBLOCK);
-    channels = info->maxInputChannels;
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
+    error( INVALID_CALL );
   }
 
-  if (fd == -1) {
-    // We've got some sort of conflict ... abort
-    sprintf(message_, "RtApiOss: device (%s) won't reopen during probe.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
+    error( INVALID_CALL );
   }
 
-  // We have an open device ... set to maximum channels.
-  i = channels;
-  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
-    // We've got some sort of conflict ... abort
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) won't revert to previous channel setting.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
+    error( WARNING );
+    return info;
   }
 
-  if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  AudioDeviceID id = deviceList[ device ];
+
+  // Get the device name.
+  info.name.erase();
+  char name[256];
+  dataSize = 256;
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyDeviceManufacturer,
+                                   &dataSize, name );
 
-  // Probe the supported data formats ... we don't care about endian-ness just yet.
-  int format;
-  info->nativeFormats = 0;
-#if defined (AFMT_S32_BE)
-  // This format does not seem to be in the 2.4 kernel version of OSS soundcard.h
-  if (mask & AFMT_S32_BE) {
-    format = AFMT_S32_BE;
-    info->nativeFormats |= RTAUDIO_SINT32;
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
-#endif
-#if defined (AFMT_S32_LE)
-  /* This format is not in the 2.4.4 kernel version of OSS soundcard.h */
-  if (mask & AFMT_S32_LE) {
-    format = AFMT_S32_LE;
-    info->nativeFormats |= RTAUDIO_SINT32;
+  info.name.append( (const char *)name, strlen(name) + 1 );
+  info.name.append( ": " );
+
+  dataSize = 256;
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyDeviceName,
+                                   &dataSize, name );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
+  info.name.append( (const char *)name, strlen(name) + 1 );
+
+  // Get the output stream "configuration".
+  AudioBufferList      *bufferList = nil;
+  result = AudioDeviceGetPropertyInfo( id, 0, false,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
+    error( WARNING );
+    return info;
+  }
+
+  result = AudioDeviceGetProperty( id, 0, false,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
-#endif
-  if (mask & AFMT_S8) {
-    format = AFMT_S8;
-    info->nativeFormats |= RTAUDIO_SINT8;
+
+  // Get output channel information.
+  unsigned int i, nStreams = bufferList->mNumberBuffers;
+  for ( i=0; i<nStreams; i++ )
+    info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
+  free( bufferList );
+
+  // Get the input stream "configuration".
+  result = AudioDeviceGetPropertyInfo( id, 0, true,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
+
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
+    error( WARNING );
+    return info;
+  }
+
+  result = AudioDeviceGetProperty( id, 0, true,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
-  if (mask & AFMT_S16_BE) {
-    format = AFMT_S16_BE;
-    info->nativeFormats |= RTAUDIO_SINT16;
+
+  // Get input channel information.
+  nStreams = bufferList->mNumberBuffers;
+  for ( i=0; i<nStreams; i++ )
+    info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
+  free( bufferList );
+
+  // If device opens for both playback and capture, we determine the channels.
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
+
+  // Probe the device sample rates.
+  bool isInput = false;
+  if ( info.outputChannels == 0 ) isInput = true;
+
+  // Determine the supported sample rates.
+  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
+                                       kAudioDevicePropertyAvailableNominalSampleRates,
+                                       &dataSize, NULL );
+
+  if ( result != kAudioHardwareNoError || dataSize == 0 ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
-  if (mask & AFMT_S16_LE) {
-    format = AFMT_S16_LE;
-    info->nativeFormats |= RTAUDIO_SINT16;
+
+  UInt32 nRanges = dataSize / sizeof( AudioValueRange );
+  AudioValueRange rangeList[ nRanges ];
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyAvailableNominalSampleRates,
+                                   &dataSize, &rangeList );
+
+  if ( result != kAudioHardwareNoError ) {
+    errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // Check that we have at least one supported format
-  if (info->nativeFormats == 0) {
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  Float64 minimumRate = 100000000.0, maximumRate = 0.0;
+  for ( UInt32 i=0; i<nRanges; i++ ) {
+    if ( rangeList[i].mMinimum < minimumRate ) minimumRate = rangeList[i].mMinimum;
+    if ( rangeList[i].mMaximum > maximumRate ) maximumRate = rangeList[i].mMaximum;
   }
 
-  // Set the format
-  i = format;
-  if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) {
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) error setting data format.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  info.sampleRates.clear();
+  for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+    if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate )
+      info.sampleRates.push_back( SAMPLE_RATES[k] );
   }
 
-  // Probe the supported sample rates.
-  info->sampleRates.clear();
-  for (unsigned int k=0; k<MAX_SAMPLE_RATES; k++) {
-    int speed = SAMPLE_RATES[k];
-    if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) != -1 && speed == (int)SAMPLE_RATES[k])
-      info->sampleRates.push_back(speed);
-  }
-
-  if (info->sampleRates.size() == 0) {
-    close(fd);
-    sprintf(message_, "RtApiOss: no supported sample rates found for device (%s).",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
+  if ( info.sampleRates.size() == 0 ) {
+    errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // That's all ... close the device and return
-  close(fd);
-  info->probed = true;
-  return;
+  // CoreAudio always uses 32-bit floating point data for PCM streams.
+  // Thus, any other "physical" formats supported by the device are of
+  // no interest to the client.
+  info.nativeFormats = RTAUDIO_FLOAT32;
+
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
+  if ( getDefaultInputDevice() == device )
+    info.isDefaultInput = true;
+
+  info.probed = true;
+  return info;
 }
 
-bool RtApiOss :: probeDeviceOpen(int device, StreamMode mode, int channels, 
-                                int sampleRate, RtAudioFormat format,
-                                int *bufferSize, int numberOfBuffers)
+OSStatus callbackHandler( AudioDeviceID inDevice,
+                          const AudioTimeStamp* inNow,
+                          const AudioBufferList* inInputData,
+                          const AudioTimeStamp* inInputTime,
+                          AudioBufferList* outOutputData,
+                          const AudioTimeStamp* inOutputTime, 
+                          void* infoPointer )
 {
-  int buffers, buffer_bytes, device_channels, device_format;
-  int srate, temp, fd;
-  int *handle = (int *) stream_.apiHandle;
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
 
-  const char *name = devices_[device].name.c_str();
+  RtApiCore *object = (RtApiCore *) info->object;
+  if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
+    return kAudioHardwareUnspecifiedError;
+  else
+    return kAudioHardwareNoError;
+}
 
-  if (mode == OUTPUT)
-    fd = open(name, O_WRONLY | O_NONBLOCK);
-  else { // mode == INPUT
-    if (stream_.mode == OUTPUT && stream_.device[0] == device) {
-      // We just set the same device for playback ... close and reopen for duplex (OSS only).
-      close(handle[0]);
-      handle[0] = 0;
-      // First check that the number previously set channels is the same.
-      if (stream_.nUserChannels[0] != channels) {
-        sprintf(message_, "RtApiOss: input/output channels must be equal for OSS duplex device (%s).", name);
-        goto error;
-      }
-      fd = open(name, O_RDWR | O_NONBLOCK);
-    }
+OSStatus deviceListener( AudioDeviceID inDevice,
+                         UInt32 channel,
+                         Boolean isInput,
+                         AudioDevicePropertyID propertyID,
+                         void* handlePointer )
+{
+  CoreHandle *handle = (CoreHandle *) handlePointer;
+  if ( propertyID == kAudioDeviceProcessorOverload ) {
+    if ( isInput )
+      handle->xrun[1] = true;
     else
-      fd = open(name, O_RDONLY | O_NONBLOCK);
+      handle->xrun[0] = true;
   }
 
-  if (fd == -1) {
-    if (errno == EBUSY || errno == EAGAIN)
-      sprintf(message_, "RtApiOss: device (%s) is busy and cannot be opened.",
-              name);
-    else
-      sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name);
-    goto error;
-  }
+  return kAudioHardwareNoError;
+}
 
-  // Now reopen in blocking mode.
-  close(fd);
-  if (mode == OUTPUT)
-    fd = open(name, O_WRONLY | O_SYNC);
-  else { // mode == INPUT
-    if (stream_.mode == OUTPUT && stream_.device[0] == device)
-      fd = open(name, O_RDWR | O_SYNC);
-    else
-      fd = open(name, O_RDONLY | O_SYNC);
-  }
+static bool hasProperty( AudioDeviceID id, UInt32 channel, bool isInput, AudioDevicePropertyID property )
+{
+  OSStatus result = AudioDeviceGetPropertyInfo( id, channel, isInput, property, NULL, NULL );
+  return result == 0;
+}
 
-  if (fd == -1) {
-    sprintf(message_, "RtApiOss: device (%s) cannot be opened.", name);
-    goto error;
+bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
+{
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
+    return FAILURE;
   }
 
-  // Get the sample format mask
-  int mask;
-  if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) can't get supported audio formats.",
-            name);
-    goto error;
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
   }
 
-  // Determine how to set the device format.
-  stream_.userFormat = format;
-  device_format = -1;
-  stream_.doByteSwap[mode] = false;
-  if (format == RTAUDIO_SINT8) {
-    if (mask & AFMT_S8) {
-      device_format = AFMT_S8;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
-    }
-  }
-  else if (format == RTAUDIO_SINT16) {
-    if (mask & AFMT_S16_NE) {
-      device_format = AFMT_S16_NE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S16_BE) {
-      device_format = AFMT_S16_BE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-      stream_.doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-      stream_.doByteSwap[mode] = true;
-    }
-#endif
+  AudioDeviceID deviceList[ nDevices ];
+  UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
+  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &dataSize, (void *) &deviceList );
+  if ( result != noErr ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
+    return FAILURE;
   }
-#if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
-  else if (format == RTAUDIO_SINT32) {
-    if (mask & AFMT_S32_NE) {
-      device_format = AFMT_S32_NE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S32_BE) {
-      device_format = AFMT_S32_BE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-      stream_.doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S32_LE) {
-      device_format = AFMT_S32_LE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-      stream_.doByteSwap[mode] = true;
-    }
-#endif
+
+  AudioDeviceID id = deviceList[ device ];
+
+  // Setup for stream mode.
+  bool isInput = false;
+  if ( mode == INPUT ) isInput = true;
+
+  // Set or disable "hog" mode.
+  dataSize = sizeof( UInt32 );
+  UInt32 doHog = 0;
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) doHog = 1;
+  result = AudioHardwareSetProperty( kAudioHardwarePropertyHogModeIsAllowed, dataSize, &doHog );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-#endif
 
-  if (device_format == -1) {
-    // The user requested format is not natively supported by the device.
-    if (mask & AFMT_S16_NE) {
-      device_format = AFMT_S16_NE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S16_BE) {
-      device_format = AFMT_S16_BE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-      stream_.doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S16_LE) {
-      device_format = AFMT_S16_LE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-      stream_.doByteSwap[mode] = true;
-    }
-#endif
-#if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
-    else if (mask & AFMT_S32_NE) {
-      device_format = AFMT_S32_NE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-    }
-#if BYTE_ORDER == LITTLE_ENDIAN
-    else if (mask & AFMT_S32_BE) {
-      device_format = AFMT_S32_BE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-      stream_.doByteSwap[mode] = true;
-    }
-#else
-    else if (mask & AFMT_S32_LE) {
-      device_format = AFMT_S32_LE;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-      stream_.doByteSwap[mode] = true;
-    }
-#endif
-#endif
-    else if (mask & AFMT_S8) {
-      device_format = AFMT_S8;
-      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
-    }
+  // Get the stream "configuration".
+  AudioBufferList      *bufferList;
+  result = AudioDeviceGetPropertyInfo( id, 0, isInput,
+                                       kAudioDevicePropertyStreamConfiguration,
+                                       &dataSize, NULL );
+  if (result != noErr || dataSize == 0) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  if (stream_.deviceFormat[mode] == 0) {
-    // This really shouldn't happen ...
-    close(fd);
-    sprintf(message_, "RtApiOss: device (%s) data format not supported by RtAudio.",
-            name);
-    goto error;
+  // Allocate the AudioBufferList.
+  bufferList = (AudioBufferList *) malloc( dataSize );
+  if ( bufferList == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
+    return FAILURE;
   }
 
-  // Determine the number of channels for this device.  Note that the
-  // channel value requested by the user might be < min_X_Channels.
-  stream_.nUserChannels[mode] = channels;
-  device_channels = channels;
-  if (mode == OUTPUT) {
-    if (channels < devices_[device].minOutputChannels)
-      device_channels = devices_[device].minOutputChannels;
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyStreamConfiguration,
+                                   &dataSize, bufferList );
+  if ( result != noErr ) {
+    free( bufferList );
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-  else { // mode == INPUT
-    if (stream_.mode == OUTPUT && stream_.device[0] == device) {
-      // We're doing duplex setup here.
-      if (channels < devices_[device].minDuplexChannels)
-        device_channels = devices_[device].minDuplexChannels;
+
+  // Search for a stream that contains the desired number of
+  // channels. CoreAudio devices can have an arbitrary number of
+  // streams and each stream can have an arbitrary number of channels.
+  // For each stream, a single buffer of interleaved samples is
+  // provided.  RtAudio currently only supports the use of one stream
+  // of interleaved data or multiple consecutive single-channel
+  // streams.  Thus, our search below is limited to these two
+  // contexts.
+  unsigned int streamChannels = 0, nStreams = 0;
+  UInt32 iChannel = 0, iStream = 0;
+  unsigned int offsetCounter = firstChannel;
+  stream_.deviceInterleaved[mode] = true;
+  nStreams = bufferList->mNumberBuffers;
+  bool foundStream = false;
+
+  for ( iStream=0; iStream<nStreams; iStream++ ) {
+    streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+    if ( streamChannels >= channels + offsetCounter ) {
+      iChannel += offsetCounter;
+      foundStream = true;
+      break;
     }
-    else {
-      if (channels < devices_[device].minInputChannels)
-        device_channels = devices_[device].minInputChannels;
+    if ( streamChannels > offsetCounter ) break;
+    offsetCounter -= streamChannels;
+    iChannel += streamChannels;
+  }
+
+  // If we didn't find a single stream above, see if we can meet
+  // the channel specification in mono mode (i.e. using separate
+  // non-interleaved buffers).  This can only work if there are N
+  // consecutive one-channel streams, where N is the number of
+  // desired channels (+ channel offset).
+  if ( foundStream == false ) {
+    unsigned int counter = 0;
+    offsetCounter = firstChannel;
+    iChannel = 0;
+    for ( iStream=0; iStream<nStreams; iStream++ ) {
+      streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
+      if ( offsetCounter ) {
+        if ( streamChannels > offsetCounter ) break;
+        offsetCounter -= streamChannels;
+      }
+      else if ( streamChannels == 1 )
+        counter++;
+      else
+        counter = 0;
+      if ( counter == channels ) {
+        iStream -= channels - 1;
+        iChannel -= channels - 1;
+        stream_.deviceInterleaved[mode] = false;
+        foundStream = true;
+        break;
+      }
+      iChannel += streamChannels;
     }
   }
-  stream_.nDeviceChannels[mode] = device_channels;
+  free( bufferList );
 
-  // Attempt to set the buffer size.  According to OSS, the minimum
-  // number of buffers is two.  The supposed minimum buffer size is 16
-  // bytes, so that will be our lower bound.  The argument to this
-  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
-  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
-  // We'll check the actual value used near the end of the setup
-  // procedure.
-  buffer_bytes = *bufferSize * formatBytes(stream_.deviceFormat[mode]) * device_channels;
-  if (buffer_bytes < 16) buffer_bytes = 16;
-  buffers = numberOfBuffers;
-  if (buffers < 2) buffers = 2;
-  temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0));
-  if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error setting fragment size for device (%s).",
-            name);
-    goto error;
+  if ( foundStream == false ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: unable to find OS-X stream on device (" << device << ") for requested channels.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-  stream_.nBuffers = buffers;
 
-  // Set the data format.
-  temp = device_format;
-  if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error setting data format for device (%s).",
-            name);
-    goto error;
+  // Determine the buffer size.
+  AudioValueRange      bufferRange;
+  dataSize = sizeof( AudioValueRange );
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyBufferFrameSizeRange,
+                                   &dataSize, &bufferRange );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  // Set the number of channels.
-  temp = device_channels;
-  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error setting %d channels on device (%s).",
-            temp, name);
-    goto error;
-  }
+  if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+  else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
 
-  // Set the sample rate.
-  srate = sampleRate;
-  temp = srate;
-  if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error setting sample rate = %d on device (%s).",
-            temp, name);
-    goto error;
+  // Set the buffer size.  For mono mode, I'm assuming we only need to
+  // make this setting for the master channel.
+  UInt32 theSize = (UInt32) *bufferSize;
+  dataSize = sizeof( UInt32 );
+  result = AudioDeviceSetProperty( id, NULL, 0, isInput,
+                                   kAudioDevicePropertyBufferFrameSize,
+                                   dataSize, &theSize );
+
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  // Verify the sample rate setup worked.
-  if (abs(srate - temp) > 100) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error ... audio device (%s) doesn't support sample rate of %d.",
-            name, temp);
-    goto error;
+  // If attempting to setup a duplex stream, the bufferSize parameter
+  // MUST be the same in both directions!
+  *bufferSize = theSize;
+  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-  stream_.sampleRate = sampleRate;
 
-  if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) {
-    close(fd);
-    sprintf(message_, "RtApiOss: error getting buffer size for device (%s).",
-            name);
-    goto error;
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 1;
+
+  // Get the stream ID(s) so we can set the stream format.  In mono
+  // mode, we'll have to do this for each stream (channel).
+  AudioStreamID streamIDs[ nStreams ];
+  dataSize = nStreams * sizeof( AudioStreamID );
+  result = AudioDeviceGetProperty( id, 0, isInput,
+                                   kAudioDevicePropertyStreams,
+                                   &dataSize, &streamIDs );
+  if ( result != noErr ) {
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  // Save buffer size (in sample frames).
-  *bufferSize = buffer_bytes / (formatBytes(stream_.deviceFormat[mode]) * device_channels);
-  stream_.bufferSize = *bufferSize;
+  // Now set the stream format.  Also, check the physical format of the
+  // device and change that if necessary.
+  AudioStreamBasicDescription  description;
+  dataSize = sizeof( AudioStreamBasicDescription );
+  if ( stream_.deviceInterleaved[mode] ) nStreams = 1;
+  else nStreams = channels;
 
-  if (mode == INPUT && stream_.mode == OUTPUT &&
-      stream_.device[0] == device) {
-    // We're doing duplex setup here.
-    stream_.deviceFormat[0] = stream_.deviceFormat[1];
-    stream_.nDeviceChannels[0] = device_channels;
+  bool updateFormat;
+  for ( unsigned int i=0; i<nStreams; i++ ) {
+
+    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
+                                     kAudioStreamPropertyVirtualFormat,
+                                     &dataSize, &description );
+
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    // Set the sample rate and data format id.  However, only make the
+    // change if the sample rate is not within 1.0 of the desired
+    // rate and the format is not linear pcm.
+    updateFormat = false;
+    if ( fabs( description.mSampleRate - (double)sampleRate ) > 1.0 ) {
+      description.mSampleRate = (double) sampleRate;
+      updateFormat = true;
+    }
+
+    if ( description.mFormatID != kAudioFormatLinearPCM ) {
+      description.mFormatID = kAudioFormatLinearPCM;
+      updateFormat = true;
+    }
+
+    if ( updateFormat ) {
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0,
+                                       kAudioStreamPropertyVirtualFormat,
+                                       dataSize, &description );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
+
+    // Now check the physical format.
+    result = AudioStreamGetProperty( streamIDs[iStream+i], 0,
+                                     kAudioStreamPropertyPhysicalFormat,
+                                     &dataSize, &description );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 24 ) {
+      description.mFormatID = kAudioFormatLinearPCM;
+      AudioStreamBasicDescription      testDescription = description;
+      unsigned long formatFlags;
+
+      // We'll try higher bit rates first and then work our way down.
+      testDescription.mBitsPerChannel = 32;
+      formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 32;
+      formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 24;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 16;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result == noErr ) continue;
+
+      testDescription = description;
+      testDescription.mBitsPerChannel = 8;
+      testDescription.mFormatFlags = formatFlags;
+      result = AudioStreamSetProperty( streamIDs[iStream+i], NULL, 0, kAudioStreamPropertyPhysicalFormat, dataSize, &testDescription );
+      if ( result != noErr ) {
+        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
   }
 
-  // Allocate the stream handles if necessary and then save.
-  if ( stream_.apiHandle == 0 ) {
-    handle = (int *) calloc(2, sizeof(int));
-    stream_.apiHandle = (void *) handle;
-    handle[0] = 0;
-    handle[1] = 0;
+  // Get the stream latency.  There can be latency in both the device
+  // and the stream.  First, attempt to get the device latency on the
+  // master channel or the first open channel.  Errors that might
+  // occur here are not deemed critical.
+  UInt32 latency, channel = 0;
+  dataSize = sizeof( UInt32 );
+  AudioDevicePropertyID property = kAudioDevicePropertyLatency;
+  for ( int i=0; i<2; i++ ) {
+    if ( hasProperty( id, channel, isInput, property ) == true ) break;
+    channel = iChannel + 1 + i;
+  }
+  if ( channel <= iChannel + 1 ) {
+    result = AudioDeviceGetProperty( id, channel, isInput, property, &dataSize, &latency );
+    if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
+    else {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      error( WARNING );
+    }
   }
+
+  // Now try to get the stream latency.  For "mono" mode, I assume the
+  // latency is equal for all single-channel streams.
+  result = AudioStreamGetProperty( streamIDs[iStream], 0, property, &dataSize, &latency );
+  if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;
   else {
-    handle = (int *) stream_.apiHandle;
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
-  handle[mode] = fd;
 
-  // Set flags for buffer conversion
+  // Byte-swapping: According to AudioHardware.h, the stream data will
+  // always be presented in native-endian format, so we should never
+  // need to byte swap.
+  stream_.doByteSwap[mode] = false;
+
+  // From the CoreAudio documentation, PCM data must be supplied as
+  // 32-bit floats.
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+
+  if ( stream_.deviceInterleaved[mode] )
+    stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
+  else // mono mode
+    stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = iChannel;  // offset within a CoreAudio stream
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+
+  // Set flags for buffer conversion.
   stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
     stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode])
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
     stream_.doConvertBuffer[mode] = true;
 
-  // Allocate necessary internal buffers
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
+  // Allocate our CoreHandle structure for the stream.
+  CoreHandle *handle = 0;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new CoreHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
+      goto error;
+    }
 
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      close(fd);
-      sprintf(message_, "RtApiOss: error allocating user buffer memory (%s).",
-              name);
+    if ( pthread_cond_init( &handle->condition, NULL ) ) {
+      errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
       goto error;
     }
+    stream_.apiHandle = (void *) handle;
   }
+  else
+    handle = (CoreHandle *) stream_.apiHandle;
+  handle->iStream[mode] = iStream;
+  handle->id[mode] = id;
 
-  if ( stream_.doConvertBuffer[mode] ) {
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
+  }
+
+  // If possible, we will make use of the CoreAudio stream buffers as
+  // "device buffers".  However, we can't do this if the device
+  // buffers are non-interleaved ("mono" mode).
+  if ( !stream_.deviceInterleaved[mode] && stream_.doConvertBuffer[mode] ) {
 
-    long buffer_bytes;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        close(fd);
-        sprintf(message_, "RtApiOss: error allocating device buffer memory (%s).",
-                name);
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
       }
+
+      // Save a pointer to our own device buffer in the CoreHandle
+      // structure because we may need to use the stream_.deviceBuffer
+      // variable to point to the CoreAudio buffer before buffer
+      // conversion (if we have a duplex stream with two different
+      // conversion schemes).
+      handle->deviceBuffer = stream_.deviceBuffer;
     }
   }
 
+  stream_.sampleRate = sampleRate;
   stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
+  stream_.callbackInfo.object = (void *) this;
 
-  if ( stream_.mode == OUTPUT && mode == INPUT ) {
-    stream_.mode = DUPLEX;
-    if (stream_.device[0] == device)
-      handle[0] = fd;
-  }
-  else
-    stream_.mode = mode;
-
-  // Setup the buffer conversion information structure.
+  // Setup the buffer conversion information structure.  We override
+  // the channel offset value and perform our own setting for that
+  // here.
   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];
-    }
+    setConvertInfo( mode, 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;
+    // Add channel offset for interleaved channels.
+    if ( firstChannel > 0 && stream_.deviceInterleaved[mode] ) {
+      if ( mode == OUTPUT ) {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].outOffset[k] += firstChannel;
       }
-    }
-    else 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[k] += firstChannel;
       }
     }
-    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 ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
+    // Only one callback procedure per device.
+    stream_.mode = DUPLEX;
+  else {
+    result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
+      errorText_ = errorStream_.str();
+      goto error;
     }
+    if ( stream_.mode == OUTPUT && mode == INPUT )
+      stream_.mode = DUPLEX;
+    else
+      stream_.mode = mode;
   }
 
+  // Setup the device property listener for over/underload.
+  result = AudioDeviceAddPropertyListener( id, 0, isInput,
+                                           kAudioDeviceProcessorOverload,
+                                           deviceListener, (void *) handle );
+
   return SUCCESS;
 
  error:
-  if (handle) {
-    if (handle[0])
-      close(handle[0]);
-    free(handle);
+  if ( handle ) {
+    pthread_cond_destroy( &handle->condition );
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
-  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
-void RtApiOss :: closeStream()
+void RtApiCore :: closeStream( void )
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // stream check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiOss::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::closeStream(): no open stream to close!";
+    error( WARNING );
     return;
   }
 
-  int *handle = (int *) stream_.apiHandle;
-  if (stream_.state == STREAM_RUNNING) {
-    if (stream_.mode == OUTPUT || stream_.mode == DUPLEX)
-      ioctl(handle[0], SNDCTL_DSP_RESET, 0);
-    else
-      ioctl(handle[1], SNDCTL_DSP_RESET, 0);
-    stream_.state = STREAM_STOPPED;
-  }
-
-  if (stream_.callbackInfo.usingCallback) {
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[0], callbackHandler );
+    AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
   }
 
-  if (handle) {
-    if (handle[0]) close(handle[0]);
-    if (handle[1]) close(handle[1]);
-    free(handle);
-    stream_.apiHandle = 0;
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+    if ( stream_.state == STREAM_RUNNING )
+      AudioDeviceStop( handle->id[1], callbackHandler );
+    AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
 
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
+  if ( handle->deviceBuffer ) {
+    free( handle->deviceBuffer );
     stream_.deviceBuffer = 0;
   }
 
+  // Destroy pthread condition variable.
+  pthread_cond_destroy( &handle->condition );
+  delete handle;
+  stream_.apiHandle = 0;
+
   stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
-void RtApiOss :: startStream()
+void RtApiCore :: startStream( void )
 {
   verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiCore::startStream(): the stream is already running!";
+    error( WARNING );
+    return;
+  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
+  OSStatus result = noErr;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    result = AudioDeviceStart( handle->id[0], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
+
+  if ( stream_.mode == INPUT ||
+       ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+
+    result = AudioDeviceStart( handle->id[1], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
+
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
   stream_.state = STREAM_RUNNING;
 
-  // No need to do anything else here ... OSS automatically starts
-  // when fed samples.
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  if ( result == noErr ) return;
+  error( SYSTEM );
 }
 
-void RtApiOss :: stopStream()
+void RtApiCore :: stopStream( void )
 {
   verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
+
+  OSStatus result = noErr;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
+    }
 
-  int err;
-  int *handle = (int *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    err = ioctl(handle[0], SNDCTL_DSP_POST, 0);
-    //err = ioctl(handle[0], SNDCTL_DSP_SYNC, 0);
-    if (err < -1) {
-      sprintf(message_, "RtApiOss: error stopping device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      error(RtError::DRIVER_ERROR);
+    result = AudioDeviceStop( handle->id[0], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
-  else {
-    err = ioctl(handle[1], SNDCTL_DSP_POST, 0);
-    //err = ioctl(handle[1], SNDCTL_DSP_SYNC, 0);
-    if (err < -1) {
-      sprintf(message_, "RtApiOss: error stopping device (%s).",
-              devices_[stream_.device[1]].name.c_str());
-      error(RtError::DRIVER_ERROR);
+
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+
+    result = AudioDeviceStop( handle->id[1], callbackHandler );
+    if ( result != noErr ) {
+      errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
-  MUTEX_UNLOCK(&stream_.mutex);
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  stream_.state = STREAM_STOPPED;
+  if ( result == noErr ) return;
+  error( SYSTEM );
 }
 
-void RtApiOss :: abortStream()
+void RtApiCore :: abortStream( void )
 {
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
+
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
+
   stopStream();
 }
 
-int RtApiOss :: streamWillBlock()
+bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
+                                 const AudioBufferList *inBufferList,
+                                 const AudioBufferList *outBufferList )
 {
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return 0;
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
+    return FAILURE;
+  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
 
-  int bytes = 0, channels = 0, frames = 0;
-  audio_buf_info info;
-  int *handle = (int *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    ioctl(handle[0], SNDCTL_DSP_GETOSPACE, &info);
-    bytes = info.bytes;
-    channels = stream_.nDeviceChannels[0];
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      pthread_cond_signal( &handle->condition );
+    else
+      stopStream();
+    return SUCCESS;
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    ioctl(handle[1], SNDCTL_DSP_GETISPACE, &info);
-    if (stream_.mode == DUPLEX ) {
-      bytes = (bytes < info.bytes) ? bytes : info.bytes;
-      channels = stream_.nDeviceChannels[0];
+  MUTEX_LOCK( &stream_.mutex );
+
+  AudioDeviceID outputDevice = handle->id[0];
+
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream or duplex mode AND the input/output devices are
+  // different AND this function is called for the input device.
+  if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
     }
-    else {
-      bytes = info.bytes;
-      channels = stream_.nDeviceChannels[1];
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
     }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
+    }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
-  frames = (int) (bytes / (channels * formatBytes(stream_.deviceFormat[0])));
-  frames -= stream_.bufferSize;
-  if (frames < 0) frames = 0;
-
-  MUTEX_UNLOCK(&stream_.mutex);
-  return frames;
-}
+  if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
 
-void RtApiOss :: tickStream()
-{
-  verifyStream();
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
-  int stopStream = 0;
-  if (stream_.state == STREAM_STOPPED) {
-    if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
-    return;
-  }
-  else if (stream_.callbackInfo.usingCallback) {
-    RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
-    stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData);
-  }
+      if ( stream_.deviceInterleaved[0] ) {
+        memset( outBufferList->mBuffers[handle->iStream[0]].mData,
+                0,
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else {
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  0,
+                  outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
+        }
+      }
+    }
+    else if ( stream_.doConvertBuffer[0] ) {
 
-  MUTEX_LOCK(&stream_.mutex);
+      if ( stream_.deviceInterleaved[0] )
+        stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->iStream[0]].mData;
+      else
+        stream_.deviceBuffer = handle->deviceBuffer;
 
-  // The state might change while waiting on a mutex.
-  if (stream_.state == STREAM_STOPPED)
-    goto unlock;
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
 
-  int result, *handle;
-  char *buffer;
-  int samples;
-  RtAudioFormat format;
-  handle = (int *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+      if ( !stream_.deviceInterleaved[0] ) {
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
+        }
+      }
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[0]) {
-      buffer = stream_.deviceBuffer;
-      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
-      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
-      format = stream_.deviceFormat[0];
     }
     else {
-      buffer = stream_.userBuffer;
-      samples = stream_.bufferSize * stream_.nUserChannels[0];
-      format = stream_.userFormat;
+      if ( stream_.deviceInterleaved[0] ) {
+        memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
+                stream_.userBuffer[0],
+                outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
+      }
+      else {
+        UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+          memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
+                  &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
+        }
+      }
     }
 
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[0])
-      byteSwapBuffer(buffer, samples, format);
-
-    // Write samples to device.
-    result = write(handle[0], buffer, samples * formatBytes(format));
-
-    if (result == -1) {
-      // This could be an underrun, but the basic OSS API doesn't provide a means for determining that.
-      sprintf(message_, "RtApiOss: audio write error for device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      error(RtError::DRIVER_ERROR);
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-
-    // Setup parameters.
-    if (stream_.doConvertBuffer[1]) {
-      buffer = stream_.deviceBuffer;
-      samples = stream_.bufferSize * stream_.nDeviceChannels[1];
-      format = stream_.deviceFormat[1];
-    }
-    else {
-      buffer = stream_.userBuffer;
-      samples = stream_.bufferSize * stream_.nUserChannels[1];
-      format = stream_.userFormat;
-    }
+  AudioDeviceID inputDevice = handle->id[1];
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
 
-    // Read samples from device.
-    result = read(handle[1], buffer, samples * formatBytes(format));
+    if ( stream_.doConvertBuffer[1] ) {
 
-    if (result == -1) {
-      // This could be an overrun, but the basic OSS API doesn't provide a means for determining that.
-      sprintf(message_, "RtApiOss: audio read error for device (%s).",
-              devices_[stream_.device[1]].name.c_str());
-      error(RtError::DRIVER_ERROR);
-    }
+      if ( stream_.deviceInterleaved[1] )
+        stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->iStream[1]].mData;
+      else {
+        stream_.deviceBuffer = (char *) handle->deviceBuffer;
+        UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
+        for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
+          memcpy( &stream_.deviceBuffer[i*bufferBytes],
+                  inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
+        }
+      }
 
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[1])
-      byteSwapBuffer(buffer, samples, format);
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
-    // Do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[1])
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+    }
+    else {
+      memcpy( stream_.userBuffer[1],
+              inBufferList->mBuffers[handle->iStream[1]].mData,
+              inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
+    }
   }
 
  unlock:
-  MUTEX_UNLOCK(&stream_.mutex);
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  if (stream_.callbackInfo.usingCallback && stopStream)
-    this->stopStream();
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
-void RtApiOss :: setStreamCallback(RtAudioCallback callback, void *userData)
+const char* RtApiCore :: getErrorCode( OSStatus code )
 {
-  verifyStream();
-
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message_, "RtApiOss: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
-  }
+       switch( code ) {
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
+  case kAudioHardwareNotRunningError:
+    return "kAudioHardwareNotRunningError";
 
-  // Set the thread attributes for joinable and realtime scheduling
-  // priority.  The higher priority will only take affect if the
-  // program is run as root or suid.
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-  pthread_attr_setschedpolicy(&attr, SCHED_RR);
+  case kAudioHardwareUnspecifiedError:
+    return "kAudioHardwareUnspecifiedError";
 
-  int err = pthread_create(&(info->thread), &attr, ossCallbackHandler, &stream_.callbackInfo);
-  pthread_attr_destroy(&attr);
-  if (err) {
-    info->usingCallback = false;
-    sprintf(message_, "RtApiOss: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
-  }
-}
+  case kAudioHardwareUnknownPropertyError:
+    return "kAudioHardwareUnknownPropertyError";
 
-void RtApiOss :: cancelStreamCallback()
-{
-  verifyStream();
+  case kAudioHardwareBadPropertySizeError:
+    return "kAudioHardwareBadPropertySizeError";
 
-  if (stream_.callbackInfo.usingCallback) {
+  case kAudioHardwareIllegalOperationError:
+    return "kAudioHardwareIllegalOperationError";
 
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
+  case kAudioHardwareBadObjectError:
+    return "kAudioHardwareBadObjectError";
 
-    MUTEX_LOCK(&stream_.mutex);
+  case kAudioHardwareBadDeviceError:
+    return "kAudioHardwareBadDeviceError";
 
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
-    stream_.callbackInfo.thread = 0;
-    stream_.callbackInfo.callback = NULL;
-    stream_.callbackInfo.userData = NULL;
+  case kAudioHardwareBadStreamError:
+    return "kAudioHardwareBadStreamError";
 
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
-}
+  case kAudioHardwareUnsupportedOperationError:
+    return "kAudioHardwareUnsupportedOperationError";
 
-extern "C" void *ossCallbackHandler(void *ptr)
-{
-  CallbackInfo *info = (CallbackInfo *) ptr;
-  RtApiOss *object = (RtApiOss *) info->object;
-  bool *usingCallback = &info->usingCallback;
+  case kAudioDeviceUnsupportedFormatError:
+    return "kAudioDeviceUnsupportedFormatError";
 
-  while ( *usingCallback ) {
-    pthread_testcancel();
-    try {
-      object->tickStream();
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtApiOss: callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessageString());
-      break;
-    }
-  }
+  case kAudioDevicePermissionsError:
+    return "kAudioDevicePermissionsError";
 
-  return 0;
+  default:
+    return "CoreAudio unknown error";
+       }
 }
 
-//******************** End of __LINUX_OSS__ *********************//
+//******************** End of __MACOSX_CORE__ *********************//
 #endif
 
-#if defined(__MACOSX_CORE__)
-
+#if defined(__UNIX_JACK__)
 
-// The OS X CoreAudio API is designed to use a separate callback
-// procedure for each of its audio devices.  A single RtAudio duplex
-// stream using two different devices is supported here, though it
-// cannot be guaranteed to always behave correctly because we cannot
-// synchronize these two callbacks.  This same functionality can be
-// achieved with better synchrony by opening two separate streams for
-// the devices and using RtAudio blocking calls (i.e. tickStream()).
+// JACK is a low-latency audio server, originally written for the
+// GNU/Linux operating system and now also ported to OS-X. It can
+// connect a number of different applications to an audio device, as
+// well as allowing them to share audio between themselves.
 //
-// A property listener is installed for over/underrun information.
-// However, no functionality is currently provided to allow property
-// listeners to trigger user handlers because it is unclear what could
-// be done if a critical stream parameter (buffer size, sample rate,
-// device disconnect) notification arrived.  The listeners entail
-// quite a bit of extra code and most likely, a user program wouldn't
-// be prepared for the result anyway.
+// When using JACK with RtAudio, "devices" refer to JACK clients that
+// have ports connected to the server.  The JACK server is typically
+// started in a terminal as follows:
+//
+// .jackd -d alsa -d hw:0
+//
+// or through an interface program such as qjackctl.  Many of the
+// parameters normally set for a stream are fixed by the JACK server
+// and can be specified when the JACK server is started.  In
+// particular,
+//
+// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
+//
+// specifies a sample rate of 44100 Hz, a buffer size of 512 sample
+// frames, and number of buffers = 4.  Once the server is running, it
+// is not possible to override these values.  If the values are not
+// specified in the command-line, the JACK server uses default values.
+//
+// The JACK server does not have to be running when an instance of
+// RtApiJack is created, though the function getDeviceCount() will
+// report 0 devices found until JACK has been started.  When no
+// devices are available (i.e., the JACK server is not running), a
+// stream cannot be opened.
 
-// A structure to hold various information related to the CoreAudio API
+#include <jack/jack.h>
+#include <unistd.h>
+
+// A structure to hold various information related to the Jack API
 // implementation.
-struct CoreHandle {
-  UInt32 index[2];
-  bool stopStream;
-  bool xrun;
-  char *deviceBuffer;
+struct JackHandle {
+  jack_client_t *client;
+  jack_port_t **ports[2];
+  std::string deviceName[2];
+  bool xrun[2];
   pthread_cond_t condition;
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
 
-  CoreHandle()
-    :stopStream(false), xrun(false), deviceBuffer(0) {}
+  JackHandle()
+    :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
 };
 
-RtApiCore :: RtApiCore()
+RtApiJack :: RtApiJack()
 {
-  this->initialize();
-
-  if (nDevices_ <= 0) {
-    sprintf(message_, "RtApiCore: no Macintosh OS-X Core Audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
+  // Nothing to do here.
 }
 
-RtApiCore :: ~RtApiCore()
+RtApiJack :: ~RtApiJack()
 {
-  // The subclass destructor gets called before the base class
-  // destructor, so close an existing stream before deallocating
-  // apiDeviceId memory.
-  if ( stream_.mode != UNINITIALIZED ) closeStream();
-
-  // Free our allocated apiDeviceId memory.
-  AudioDeviceID *id;
-  for ( unsigned int i=0; i<devices_.size(); i++ ) {
-    id = (AudioDeviceID *) devices_[i].apiDeviceId;
-    if (id) free(id);
-  }
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
-void RtApiCore :: initialize(void)
+unsigned int RtApiJack :: getDeviceCount( void )
 {
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  AudioDeviceID        *deviceList = NULL;
-  nDevices_ = 0;
+  // See if we can become a jack client.
+  jack_client_t *client = jack_client_new( "RtApiJackCount" );
+  if ( client == 0 ) return 0;
 
-  // Find out how many audio devices there are, if any.
-  err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &dataSize, NULL);
-  if (err != noErr) {
-    sprintf(message_, "RtApiCore: OS-X error getting device info!");
-    error(RtError::SYSTEM_ERROR);
+  const char **ports;
+  std::string port, previousPort;
+  unsigned int nChannels = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    unsigned int iColon = 0;
+    do {
+      port = (char *) ports[ nChannels ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon + 1 );
+        if ( port != previousPort ) {
+          nDevices++;
+          previousPort = port;
+        }
+      }
+    } while ( ports[++nChannels] );
+    free( ports );
   }
 
-  nDevices_ = dataSize / sizeof(AudioDeviceID);
-  if (nDevices_ == 0) return;
-
-  // Make space for the devices we are about to get.
-  deviceList = (AudioDeviceID  *) malloc( dataSize );
-  if (deviceList == NULL) {
-    sprintf(message_, "RtApiCore: memory allocation error during initialization!");
-    error(RtError::MEMORY_ERROR);
-  }
+  jack_client_close( client );
+  return nDevices;
+}
 
-  // Get the array of AudioDeviceIDs.
-  err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &dataSize, (void *) deviceList);
-  if (err != noErr) {
-    free(deviceList);
-    sprintf(message_, "RtApiCore: OS-X error getting device properties!");
-    error(RtError::SYSTEM_ERROR);
-  }
+RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
-  // Create list of device structures and write device identifiers.
-  RtApiDevice device;
-  AudioDeviceID *id;
-  for (int i=0; i<nDevices_; i++) {
-    devices_.push_back(device);
-    id = (AudioDeviceID *) malloc( sizeof(AudioDeviceID) );
-    *id = deviceList[i];
-    devices_[i].apiDeviceId = (void *) id;
+  jack_client_t *client = jack_client_new( "RtApiJackInfo" );
+  if ( client == 0 ) {
+    errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
+    error( WARNING );
+    return info;
   }
 
-  free(deviceList);
-}
-
-int RtApiCore :: getDefaultInputDevice(void)
-{
-  AudioDeviceID id, *deviceId;
-  UInt32 dataSize = sizeof( AudioDeviceID );
-
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
-                                              &dataSize, &id );
-
-  if (result != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting default input device." );
-    error(RtError::WARNING);
-    return 0;
-  }
-
-  for ( int i=0; i<nDevices_; i++ ) {
-    deviceId = (AudioDeviceID *) devices_[i].apiDeviceId;
-    if ( id == *deviceId ) return i;
-  }
-
-  return 0;
-}
-
-int RtApiCore :: getDefaultOutputDevice(void)
-{
-  AudioDeviceID id, *deviceId;
-  UInt32 dataSize = sizeof( AudioDeviceID );
-
-  OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
-                                              &dataSize, &id );
-
-  if (result != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting default output device." );
-    error(RtError::WARNING);
-    return 0;
-  }
-
-  for ( int i=0; i<nDevices_; i++ ) {
-    deviceId = (AudioDeviceID *) devices_[i].apiDeviceId;
-    if ( id == *deviceId ) return i;
-  }
-
-  return 0;
-}
-
-static bool deviceSupportsFormat( AudioDeviceID id, bool isInput,
-                                  AudioStreamBasicDescription  *desc, bool isDuplex )
-{
-  OSStatus result = noErr;
-  UInt32 dataSize = sizeof( AudioStreamBasicDescription );
-
-  result = AudioDeviceGetProperty( id, 0, isInput,
-                                   kAudioDevicePropertyStreamFormatSupported,
-                                   &dataSize, desc );
-
-  if (result == kAudioHardwareNoError) {
-    if ( isDuplex ) {
-      result = AudioDeviceGetProperty( id, 0, true,
-                                       kAudioDevicePropertyStreamFormatSupported,
-                                       &dataSize, desc );
-
-
-      if (result != kAudioHardwareNoError)
-        return false;
-    }
-    return true;
-  }
-
-  return false;
-}
-
-void RtApiCore :: probeDeviceInfo( RtApiDevice *info )
-{
-  OSStatus err = noErr;
-
-  // Get the device manufacturer and name.
-  char name[256];
-  char fullname[512];
-  UInt32 dataSize = 256;
-  AudioDeviceID *id = (AudioDeviceID *) info->apiDeviceId;
-  err = AudioDeviceGetProperty( *id, 0, false,
-                                kAudioDevicePropertyDeviceManufacturer,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting device manufacturer." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncpy(fullname, name, 256);
-  strcat(fullname, ": " );
-
-  dataSize = 256;
-  err = AudioDeviceGetProperty( *id, 0, false,
-                                kAudioDevicePropertyDeviceName,
-                                &dataSize, name );
-  if (err != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting device name." );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  strncat(fullname, name, 254);
-  info->name.erase();
-  info->name.append( (const char *)fullname, strlen(fullname)+1);
-
-  // Get output channel information.
-  unsigned int i, minChannels = 0, maxChannels = 0, nStreams = 0;
-  AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( *id, 0, false,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message_, "RtApiCore: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return;
-    }
-
-    err = AudioDeviceGetProperty( *id, 0, false,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-    if (err == noErr) {
-      maxChannels = 0;
-      minChannels = 1000;
-      nStreams = bufferList->mNumberBuffers;
-      for ( i=0; i<nStreams; i++ ) {
-        maxChannels += bufferList->mBuffers[i].mNumberChannels;
-        if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
-          minChannels = bufferList->mBuffers[i].mNumberChannels;
+  const char **ports;
+  std::string port, previousPort;
+  unsigned int nPorts = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    unsigned int iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) info.name = port;
+          nDevices++;
+          previousPort = port;
+        }
       }
-    }
+    } while ( ports[++nPorts] );
+    free( ports );
   }
-  free (bufferList);
 
-  if (err != noErr || dataSize <= 0) {
-    sprintf( message_, "RtApiCore: OS-X error getting output channels for device (%s).",
-             info->name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
+    error( INVALID_CALL );
   }
 
-  if ( nStreams ) {
-    if ( maxChannels > 0 )
-      info->maxOutputChannels = maxChannels;
-    if ( minChannels > 0 )
-      info->minOutputChannels = minChannels;
-  }
+  // Get the current jack server sample rate.
+  info.sampleRates.clear();
+  info.sampleRates.push_back( jack_get_sample_rate( client ) );
 
-  // Get input channel information.
-  bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( *id, 0, true,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message_, "RtApiCore: memory allocation error!");
-      error(RtError::DEBUG_WARNING);
-      return;
-    }
-    err = AudioDeviceGetProperty( *id, 0, true,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
-    if (err == noErr) {
-      maxChannels = 0;
-      minChannels = 1000;
-      nStreams = bufferList->mNumberBuffers;
-      for ( i=0; i<nStreams; i++ ) {
-        if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
-          minChannels = bufferList->mBuffers[i].mNumberChannels;
-        maxChannels += bufferList->mBuffers[i].mNumberChannels;
-      }
-    }
+  // Count the available ports containing the client name as device
+  // channels.  Jack "input ports" equal RtAudio output channels.
+  unsigned int nChannels = 0;
+  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
+    info.outputChannels = nChannels;
   }
-  free (bufferList);
 
-  if (err != noErr || dataSize <= 0) {
-    sprintf( message_, "RtApiCore: OS-X error getting input channels for device (%s).",
-             info->name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Jack "output ports" equal RtAudio input channels.
+  nChannels = 0;
+  ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
+    info.inputChannels = nChannels;
   }
 
-  if ( nStreams ) {
-    if ( maxChannels > 0 )
-      info->maxInputChannels = maxChannels;
-    if ( minChannels > 0 )
-      info->minInputChannels = minChannels;
+  if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
+    jack_client_close(client);
+    errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
+    error( WARNING );
+    return info;
   }
 
   // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
-    info->hasDuplexSupport = true;
-    info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-      info->maxInputChannels : info->maxOutputChannels;
-    info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-      info->minInputChannels : info->minOutputChannels;
-  }
-
-  // Probe the device sample rate and data format parameters.  The
-  // core audio query mechanism is performed on a "stream"
-  // description, which can have a variable number of channels and
-  // apply to input or output only.
-
-  // Create a stream description structure.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  memset(&description, 0, sizeof(AudioStreamBasicDescription));
-  bool isInput = false;
-  if ( info->maxOutputChannels == 0 ) isInput = true;
-  bool isDuplex = false;
-  if ( info->maxDuplexChannels > 0 ) isDuplex = true;
-
-  // Determine the supported sample rates.
-  info->sampleRates.clear();
-  for (unsigned int k=0; k<MAX_SAMPLE_RATES; k++) {
-    description.mSampleRate = (double) SAMPLE_RATES[k];
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->sampleRates.push_back( SAMPLE_RATES[k] );
-  }
-
-  if (info->sampleRates.size() == 0) {
-    sprintf( message_, "RtApiCore: No supported sample rates found for OS-X device (%s).",
-             info->name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Determine the supported data formats.
-  info->nativeFormats = 0;
-  description.mFormatID = kAudioFormatLinearPCM;
-  description.mBitsPerChannel = 8;
-  description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT8;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT8;
-  }
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
-  description.mBitsPerChannel = 16;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT16;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT16;
-  }
-
-  description.mBitsPerChannel = 32;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT32;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT32;
-  }
+  // Jack always uses 32-bit floats.
+  info.nativeFormats = RTAUDIO_FLOAT32;
 
-  description.mBitsPerChannel = 24;
-  description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_SINT24;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_SINT24;
-  }
+  // Jack doesn't provide default devices so we'll use the first available one.
+  if ( device == 0 && info.outputChannels > 0 )
+    info.isDefaultOutput = true;
+  if ( device == 0 && info.inputChannels > 0 )
+    info.isDefaultInput = true;
 
-  description.mBitsPerChannel = 32;
-  description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_FLOAT32;
-  }
+  jack_client_close(client);
+  info.probed = true;
+  return info;
+}
 
-  description.mBitsPerChannel = 64;
-  description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
-  if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-    info->nativeFormats |= RTAUDIO_FLOAT64;
-  else {
-    description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
-    if ( deviceSupportsFormat( *id, isInput, &description, isDuplex ) )
-      info->nativeFormats |= RTAUDIO_FLOAT64;
-  }
+int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
+{
+  CallbackInfo *info = (CallbackInfo *) infoPointer;
 
-  // Check that we have at least one supported format.
-  if (info->nativeFormats == 0) {
-    sprintf(message_, "RtApiCore: OS-X device (%s) data format not supported by RtAudio.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  RtApiJack *object = (RtApiJack *) info->object;
+  if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
 
-  info->probed = true;
+  return 0;
 }
 
-OSStatus callbackHandler( AudioDeviceID inDevice,
-                          const AudioTimeStamp* inNow,
-                          const AudioBufferList* inInputData,
-                          const AudioTimeStamp* inInputTime,
-                          AudioBufferList* outOutputData,
-                          const AudioTimeStamp* inOutputTime, 
-                          void* infoPointer )
+void jackShutdown( void *infoPointer )
 {
   CallbackInfo *info = (CallbackInfo *) infoPointer;
+  RtApiJack *object = (RtApiJack *) info->object;
 
-  RtApiCore *object = (RtApiCore *) info->object;
-  try {
-    object->callbackEvent( inDevice, (void *)inInputData, (void *)outOutputData );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtApiCore: callback handler error (%s)!\n\n", exception.getMessageString());
-    return kAudioHardwareUnspecifiedError;
-  }
+  // Check current stream state.  If stopped, then we'll assume this
+  // was called as a result of a call to RtApiJack::stopStream (the
+  // deactivation of a client handle causes this function to be called).
+  // If not, we'll assume the Jack server is shutting down or some
+  // other problem occurred and we should close the stream.
+  if ( object->isStreamRunning() == false ) return;
 
-  return kAudioHardwareNoError;
+  object->closeStream();
+  std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
 }
 
-OSStatus deviceListener( AudioDeviceID inDevice,
-                         UInt32 channel,
-                         Boolean isInput,
-                         AudioDevicePropertyID propertyID,
-                         void* handlePointer )
+int jackXrun( void *infoPointer )
 {
-  CoreHandle *handle = (CoreHandle *) handlePointer;
-  if ( propertyID == kAudioDeviceProcessorOverload ) {
-    if ( isInput )
-      fprintf(stderr, "\nRtApiCore: OS-X audio input overrun detected!\n");
-    else
-      fprintf(stderr, "\nRtApiCore: OS-X audio output underrun detected!\n");
-    handle->xrun = true;
-  }
+  JackHandle *handle = (JackHandle *) infoPointer;
 
-  return kAudioHardwareNoError;
+  if ( handle->ports[0] ) handle->xrun[0] = true;
+  if ( handle->ports[1] ) handle->xrun[1] = true;
+
+  return 0;
 }
 
-bool RtApiCore :: probeDeviceOpen( int device, StreamMode mode, int channels, 
-                                   int sampleRate, RtAudioFormat format,
-                                   int *bufferSize, int numberOfBuffers )
+bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
 {
-  // Setup for stream mode.
-  bool isInput = false;
-  AudioDeviceID id = *((AudioDeviceID *) devices_[device].apiDeviceId);
-  if ( mode == INPUT ) isInput = true;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
 
-  // Search for a stream which contains the desired number of channels.
-  OSStatus err = noErr;
-  UInt32 dataSize;
-  unsigned int deviceChannels, nStreams = 0;
-  UInt32 iChannel = 0, iStream = 0;
-  AudioBufferList      *bufferList = nil;
-  err = AudioDeviceGetPropertyInfo( id, 0, isInput,
-                                    kAudioDevicePropertyStreamConfiguration,
-                                    &dataSize, NULL );
-
-  if (err == noErr && dataSize > 0) {
-    bufferList = (AudioBufferList *) malloc( dataSize );
-    if (bufferList == NULL) {
-      sprintf(message_, "RtApiCore: memory allocation error in probeDeviceOpen()!");
-      error(RtError::DEBUG_WARNING);
+  // Look for jack server and try to become a client (only do once per stream).
+  jack_client_t *client = 0;
+  if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
+    if ( options && !options->streamName.empty() )
+      client = jack_client_new( options->streamName.c_str() );
+    else
+      client = jack_client_new( "RtApiJack" );
+    if ( client == 0 ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
+      error( WARNING );
       return FAILURE;
     }
-    err = AudioDeviceGetProperty( id, 0, isInput,
-                                  kAudioDevicePropertyStreamConfiguration,
-                                  &dataSize, bufferList );
+  }
+  else {
+    // The handle must have been created on an earlier pass.
+    client = handle->client;
+  }
 
-    if (err == noErr) {
-      stream_.deInterleave[mode] = false;
-      nStreams = bufferList->mNumberBuffers;
-      for ( iStream=0; iStream<nStreams; iStream++ ) {
-        if ( bufferList->mBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break;
-        iChannel += bufferList->mBuffers[iStream].mNumberChannels;
-      }
-      // If we didn't find a single stream above, see if we can meet
-      // the channel specification in mono mode (i.e. using separate
-      // non-interleaved buffers).  This can only work if there are N
-      // consecutive one-channel streams, where N is the number of
-      // desired channels.
-      iChannel = 0;
-      if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) {
-        int counter = 0;
-        for ( iStream=0; iStream<nStreams; iStream++ ) {
-          if ( bufferList->mBuffers[iStream].mNumberChannels == 1 )
-            counter++;
-          else
-            counter = 0;
-          if ( counter == channels ) {
-            iStream -= channels - 1;
-            iChannel -= channels - 1;
-            stream_.deInterleave[mode] = true;
-            break;
-          }
-          iChannel += bufferList->mBuffers[iStream].mNumberChannels;
+  const char **ports;
+  std::string port, previousPort, deviceName;
+  unsigned int nPorts = 0, nDevices = 0;
+  ports = jack_get_ports( client, NULL, NULL, 0 );
+  if ( ports ) {
+    // Parse the port names up to the first colon (:).
+    unsigned int iColon = 0;
+    do {
+      port = (char *) ports[ nPorts ];
+      iColon = port.find(":");
+      if ( iColon != std::string::npos ) {
+        port = port.substr( 0, iColon );
+        if ( port != previousPort ) {
+          if ( nDevices == device ) deviceName = port;
+          nDevices++;
+          previousPort = port;
         }
       }
-    }
-  }
-  if (err != noErr || dataSize <= 0) {
-    if ( bufferList ) free( bufferList );
-    sprintf( message_, "RtApiCore: OS-X error getting channels for device (%s).",
-             devices_[device].name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+    } while ( ports[++nPorts] );
+    free( ports );
   }
 
-  if (iStream >= nStreams) {
-    free (bufferList);
-    sprintf( message_, "RtApiCore: unable to find OS-X audio stream on device (%s) for requested channels (%d).",
-             devices_[device].name.c_str(), channels );
-    error(RtError::DEBUG_WARNING);
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
     return FAILURE;
   }
 
-  // This is ok even for mono mode ... it gets updated later.
-  deviceChannels = bufferList->mBuffers[iStream].mNumberChannels;
-  free (bufferList);
-
-  // Determine the buffer size.
-  AudioValueRange      bufferRange;
-  dataSize = sizeof(AudioValueRange);
-  err = AudioDeviceGetProperty( id, 0, isInput,
-                                kAudioDevicePropertyBufferSizeRange,
-                                &dataSize, &bufferRange);
-  if (err != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting buffer size range for device (%s).",
-             devices_[device].name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // Count the available ports containing the client name as device
+  // channels.  Jack "input ports" equal RtAudio output channels.
+  unsigned int nChannels = 0;
+  unsigned long flag = JackPortIsOutput;
+  if ( mode == INPUT ) flag = JackPortIsInput;
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports ) {
+    while ( ports[ nChannels ] ) nChannels++;
+    free( ports );
   }
 
-  long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32);
-  if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum;
-  else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum;
-
-  // Set the buffer size.  For mono mode, I'm assuming we only need to
-  // make this setting for the first channel.
-  UInt32 theSize = (UInt32) bufferBytes;
-  dataSize = sizeof( UInt32);
-  err = AudioDeviceSetProperty(id, NULL, 0, isInput,
-                               kAudioDevicePropertyBufferSize,
-                               dataSize, &theSize);
-  if (err != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error setting the buffer size for device (%s).",
-             devices_[device].name.c_str() );
-    error(RtError::DEBUG_WARNING);
+  // Compare the jack ports for specified client to the requested number of channels.
+  if ( nChannels < (channels + firstChannel) ) {
+    errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
-  // If attempting to setup a duplex stream, the bufferSize parameter
-  // MUST be the same in both directions!
-  *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) );
-  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
-    sprintf( message_, "RtApiCore: OS-X error setting buffer size for duplex stream on device (%s).",
-             devices_[device].name.c_str() );
-    error(RtError::DEBUG_WARNING);
+  // Check the jack server sample rate.
+  unsigned int jackRate = jack_get_sample_rate( client );
+  if ( sampleRate != jackRate ) {
+    jack_client_close( client );
+    errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
+  stream_.sampleRate = jackRate;
 
-  stream_.bufferSize = *bufferSize;
-  stream_.nBuffers = 1;
+  // Get the latency of the JACK port.
+  ports = jack_get_ports( client, deviceName.c_str(), NULL, flag );
+  if ( ports[ firstChannel ] )
+    stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
+  free( ports );
 
-  // Set the stream format description.  Do for each channel in mono mode.
-  AudioStreamBasicDescription  description;
-  dataSize = sizeof( AudioStreamBasicDescription );
-  if ( stream_.deInterleave[mode] ) nStreams = channels;
-  else nStreams = 1;
-  for ( unsigned int i=0; i<nStreams; i++, iChannel++ ) {
-
-    err = AudioDeviceGetProperty( id, iChannel, isInput,
-                                  kAudioDevicePropertyStreamFormat,
-                                  &dataSize, &description );
-    if (err != noErr) {
-      sprintf( message_, "RtApiCore: OS-X error getting stream format for device (%s).",
-               devices_[device].name.c_str() );
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  // The jack server always uses 32-bit floating-point data.
+  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+  stream_.userFormat = format;
 
-    // Set the sample rate and data format id.
-    description.mSampleRate = (double) sampleRate;
-    description.mFormatID = kAudioFormatLinearPCM;
-    err = AudioDeviceSetProperty( id, NULL, iChannel, isInput,
-                                  kAudioDevicePropertyStreamFormat,
-                                  dataSize, &description );
-    if (err != noErr) {
-      sprintf( message_, "RtApiCore: OS-X error setting sample rate or data format for device (%s).",
-               devices_[device].name.c_str() );
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-  }
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
-  // Check whether we need byte-swapping (assuming OS-X host is big-endian).
-  iChannel -= nStreams;
-  err = AudioDeviceGetProperty( id, iChannel, isInput,
-                                kAudioDevicePropertyStreamFormat,
-                                &dataSize, &description );
-  if (err != noErr) {
-    sprintf( message_, "RtApiCore: OS-X error getting stream format for device (%s).", devices_[device].name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  // Jack always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
+  // Jack always provides host byte-ordered data.
   stream_.doByteSwap[mode] = false;
-  if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian )
-    stream_.doByteSwap[mode] = true;
 
-  // From the CoreAudio documentation, PCM data must be supplied as
-  // 32-bit floats.
-  stream_.userFormat = format;
-  stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+  // Get the buffer size.  The buffer size and number of buffers
+  // (periods) is set when the jack server is started.
+  stream_.bufferSize = (int) jack_get_buffer_size( client );
+  *bufferSize = stream_.bufferSize;
 
-  if ( stream_.deInterleave[mode] ) // mono mode
-    stream_.nDeviceChannels[mode] = channels;
-  else
-    stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
+  stream_.nDeviceChannels[mode] = channels;
   stream_.nUserChannels[mode] = channels;
 
   // Set flags for buffer conversion.
   stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
     stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] > 1 && stream_.deInterleave[mode])
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
     stream_.doConvertBuffer[mode] = true;
 
-  // Allocate our CoreHandle structure for the stream.
-  CoreHandle *handle;
-  if ( stream_.apiHandle == 0 ) {
-    handle = (CoreHandle *) calloc(1, sizeof(CoreHandle));
-    if ( handle == NULL ) {
-      sprintf(message_, "RtApiCore: OS-X error allocating coreHandle memory (%s).",
-              devices_[device].name.c_str());
+  // Allocate our JackHandle structure for the stream.
+  if ( handle == 0 ) {
+    try {
+      handle = new JackHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
       goto error;
     }
-    handle->index[0] = 0;
-    handle->index[1] = 0;
+
     if ( pthread_cond_init(&handle->condition, NULL) ) {
-      sprintf(message_, "RtApiCore: error initializing pthread condition variable (%s).",
-              devices_[device].name.c_str());
+      errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
       goto error;
     }
     stream_.apiHandle = (void *) handle;
+    handle->client = client;
   }
-  else
-    handle = (CoreHandle *) stream_.apiHandle;
-  handle->index[mode] = iStream;
+  handle->deviceName[mode] = deviceName;
 
   // Allocate necessary internal buffers.
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiCore: OS-X error allocating user buffer memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
-    }
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
-  if ( stream_.deInterleave[mode] ) {
+  if ( stream_.doConvertBuffer[mode] ) {
 
-    long buffer_bytes;
     bool makeBuffer = true;
     if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
+      bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
     else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
+      bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
+        if ( bufferBytes < bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiCore: error allocating device buffer memory (%s).",
-                devices_[device].name.c_str());
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
       }
-
-      // If not de-interleaving, we point stream_.deviceBuffer to the
-      // OS X supplied device buffer before doing any necessary data
-      // conversions.  This presents a problem if we have a duplex
-      // stream using one device which needs de-interleaving and
-      // another device which doesn't.  So, save a pointer to our own
-      // device buffer in the CallbackInfo structure.
-      handle->deviceBuffer = stream_.deviceBuffer;
     }
   }
 
-  stream_.sampleRate = sampleRate;
+  // Allocate memory for the Jack ports (channels) identifiers.
+  handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
+  if ( handle->ports[mode] == NULL )  {
+    errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
+    goto error;
+  }
+
   stream_.device[mode] = device;
+  stream_.channelOffset[mode] = firstChannel;
   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;
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up the stream for output.
+    stream_.mode = DUPLEX;
+  else {
+    stream_.mode = mode;
+    jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
+    jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle );
+    jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
+  }
+
+  // Register our ports.
+  char label[64];
+  if ( mode == OUTPUT ) {
+    for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+      snprintf( label, 64, "outport %d", i );
+      handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
+                                                JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
     }
-    else { // 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];
+  }
+  else {
+    for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+      snprintf( label, 64, "inport %d", i );
+      handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
+                                                JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
     }
+  }
 
-    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;
-  else {
-    err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
-    if (err != noErr) {
-      sprintf( message_, "RtApiCore: OS-X error setting callback for device (%s).", devices_[device].name.c_str() );
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-    if ( stream_.mode == OUTPUT && mode == INPUT )
-      stream_.mode = DUPLEX;
-    else
-      stream_.mode = mode;
-  }
-
-  // Setup the device property listener for over/underload.
-  err = AudioDeviceAddPropertyListener( id, iChannel, isInput,
-                                        kAudioDeviceProcessorOverload,
-                                        deviceListener, (void *) handle );
+  // Setup the buffer conversion information structure.  We don't use
+  // buffers to do channel offsets, so we override that parameter
+  // here.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
 
   return SUCCESS;
 
  error:
   if ( handle ) {
-    pthread_cond_destroy(&handle->condition);
-    free(handle);
+    pthread_cond_destroy( &handle->condition );
+    jack_client_close( handle->client );
+
+    if ( handle->ports[0] ) free( handle->ports[0] );
+    if ( handle->ports[1] ) free( handle->ports[1] );
+
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
-  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
-void RtApiCore :: closeStream()
+void RtApiJack :: closeStream( void )
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // stream check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiCore::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiJack::closeStream(): no open stream to close!";
+    error( WARNING );
     return;
   }
 
-  AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId );
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    if (stream_.state == STREAM_RUNNING)
-      AudioDeviceStop( id, callbackHandler );
-    AudioDeviceRemoveIOProc( id, callbackHandler );
-  }
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( handle ) {
 
-  id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId );
-  if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) {
-    if (stream_.state == STREAM_RUNNING)
-      AudioDeviceStop( id, callbackHandler );
-    AudioDeviceRemoveIOProc( id, callbackHandler );
-  }
+    if ( stream_.state == STREAM_RUNNING )
+      jack_deactivate( handle->client );
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+    jack_client_close( handle->client );
   }
 
-  if ( stream_.deInterleave[0] || stream_.deInterleave[1] ) {
-    free(stream_.deviceBuffer);
-    stream_.deviceBuffer = 0;
+  if ( handle ) {
+    if ( handle->ports[0] ) free( handle->ports[0] );
+    if ( handle->ports[1] ) free( handle->ports[1] );
+    pthread_cond_destroy( &handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
-  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
-  // Destroy pthread condition variable and free the CoreHandle structure.
-  if ( handle ) {
-    pthread_cond_destroy(&handle->condition);
-    free( handle );
-    stream_.apiHandle = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
   stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
-void RtApiCore :: startStream()
+void RtApiJack :: startStream( void )
 {
   verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiJack::startStream(): the stream is already running!";
+    error( WARNING );
+    return;
+  }
 
   MUTEX_LOCK(&stream_.mutex);
 
-  OSStatus err;
-  AudioDeviceID id;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  int result = jack_activate( handle->client );
+  if ( result ) {
+    errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
+    goto unlock;
+  }
+
+  const char **ports;
+
+  // Get the list of available ports.
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = 1;
+    ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput);
+    if ( ports == NULL) {
+      errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
+      goto unlock;
+    }
 
-    id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId );
-    err = AudioDeviceStart(id, callbackHandler);
-    if (err != noErr) {
-      sprintf(message_, "RtApiCore: OS-X error starting callback procedure on device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+    // Now make the port connections.  Since RtAudio wasn't designed to
+    // allow the user to select particular channels of a device, we'll
+    // just open the first "nChannels" ports with offset.
+    for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+      result = 1;
+      if ( ports[ stream_.channelOffset[0] + i ] )
+        result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
+      if ( result ) {
+        free( ports );
+        errorText_ = "RtApiJack::startStream(): error connecting output ports!";
+        goto unlock;
+      }
     }
+    free(ports);
   }
 
-  if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    result = 1;
+    ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput );
+    if ( ports == NULL) {
+      errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
+      goto unlock;
+    }
 
-    id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId );
-    err = AudioDeviceStart(id, callbackHandler);
-    if (err != noErr) {
-      sprintf(message_, "RtApiCore: OS-X error starting input callback procedure on device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+    // Now make the port connections.  See note above.
+    for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+      result = 1;
+      if ( ports[ stream_.channelOffset[1] + i ] )
+        result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
+      if ( result ) {
+        free( ports );
+        errorText_ = "RtApiJack::startStream(): error connecting input ports!";
+        goto unlock;
+      }
     }
+    free(ports);
   }
 
-  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
-  handle->stopStream = false;
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
   stream_.state = STREAM_RUNNING;
 
+ unlock:
   MUTEX_UNLOCK(&stream_.mutex);
+
+  if ( result == 0 ) return;
+  error( SYSTEM );
 }
 
-void RtApiCore :: stopStream()
+void RtApiJack :: stopStream( void )
 {
   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);
-
-  OSStatus err;
-  AudioDeviceID id;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-
-    id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId );
-    err = AudioDeviceStop(id, callbackHandler);
-    if (err != noErr) {
-      sprintf(message_, "RtApiCore: OS-X error stopping callback procedure on device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
-    }
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
   }
 
-  if (stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1]) ) {
+  MUTEX_LOCK( &stream_.mutex );
+
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
-    id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId );
-    err = AudioDeviceStop(id, callbackHandler);
-    if (err != noErr) {
-      sprintf(message_, "RtApiCore: OS-X error stopping input callback procedure on device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
     }
   }
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+  jack_deactivate( handle->client );
+  stream_.state = STREAM_STOPPED;
 
-void RtApiCore :: abortStream()
-{
-  stopStream();
+  MUTEX_UNLOCK( &stream_.mutex );
 }
 
-void RtApiCore :: tickStream()
+void RtApiJack :: abortStream( void )
 {
   verifyStream();
-
-  if (stream_.state == STREAM_STOPPED) return;
-
-  if (stream_.callbackInfo.usingCallback) {
-    sprintf(message_, "RtApiCore: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+    error( WARNING );
     return;
   }
 
-  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
-
-  MUTEX_LOCK(&stream_.mutex);
-
-  pthread_cond_wait(&handle->condition, &stream_.mutex);
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  stopStream();
 }
 
-void RtApiCore :: callbackEvent( AudioDeviceID deviceId, void *inData, void *outData )
+bool RtApiJack :: callbackEvent( unsigned long nframes )
 {
-  verifyStream();
-
-  if (stream_.state == STREAM_STOPPED) return;
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
+    return FAILURE;
+  }
+  if ( stream_.bufferSize != nframes ) {
+    errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
+    error( WARNING );
+    return FAILURE;
+  }
 
   CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
-  AudioBufferList *inBufferList = (AudioBufferList *) inData;
-  AudioBufferList *outBufferList = (AudioBufferList *) outData;
-
-  if ( info->usingCallback && handle->stopStream ) {
-    // Check if the stream should be stopped (via the previous user
-    // callback return value).  We stop the stream here, rather than
-    // after the function call, so that output data can first be
-    // processed.
-    this->stopStream();
-    return;
+  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      pthread_cond_signal( &handle->condition );
+    else
+      stopStream();
+    return SUCCESS;
   }
 
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
-  // Invoke user callback first, to get fresh output data.  Don't
-  // invoke the user callback if duplex mode AND the input/output devices
-  // are different AND this function is called for the input device.
-  AudioDeviceID id = *( (AudioDeviceID *) devices_[stream_.device[0]].apiDeviceId );
-  if ( info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) ) {
+  // Invoke user callback first, to get fresh output data.
+  if ( handle->drainCounter == 0 ) {
     RtAudioCallback callback = (RtAudioCallback) info->callback;
-    handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData);
-    if ( handle->xrun == true ) {
-      handle->xrun = false;
-      MUTEX_UNLOCK(&stream_.mutex);
-      return;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
     }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
+    }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
-  if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) {
-
-    if (stream_.doConvertBuffer[0]) {
+  jack_default_audio_sample_t *jackbuffer;
+  unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
-      if ( !stream_.deInterleave[0] )
-        stream_.deviceBuffer = (char *) outBufferList->mBuffers[handle->index[0]].mData;
-      else
-        stream_.deviceBuffer = handle->deviceBuffer;
+    if ( handle->drainCounter > 0 ) { // write zeros to the output stream
 
-      convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] );
-      if ( stream_.doByteSwap[0] )
-        byteSwapBuffer(stream_.deviceBuffer,
-                       stream_.bufferSize * stream_.nDeviceChannels[0],
-                       stream_.deviceFormat[0]);
-
-      if ( stream_.deInterleave[0] ) {
-        int bufferBytes = outBufferList->mBuffers[handle->index[0]].mDataByteSize;
-        for ( int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
-          memcpy(outBufferList->mBuffers[handle->index[0]+i].mData,
-                 &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
-        }
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memset( jackbuffer, 0, bufferBytes );
       }
 
     }
-    else {
-      if (stream_.doByteSwap[0])
-        byteSwapBuffer(stream_.userBuffer,
-                       stream_.bufferSize * stream_.nUserChannels[0],
-                       stream_.userFormat);
-
-      memcpy(outBufferList->mBuffers[handle->index[0]].mData,
-             stream_.userBuffer,
-             outBufferList->mBuffers[handle->index[0]].mDataByteSize );
-    }
-  }
+    else if ( stream_.doConvertBuffer[0] ) {
 
-  id = *( (AudioDeviceID *) devices_[stream_.device[1]].apiDeviceId );
-  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == id ) ) {
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
 
-    if (stream_.doConvertBuffer[1]) {
-
-      if ( stream_.deInterleave[1] ) {
-        stream_.deviceBuffer = (char *) handle->deviceBuffer;
-        int bufferBytes = inBufferList->mBuffers[handle->index[1]].mDataByteSize;
-        for ( int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
-          memcpy(&stream_.deviceBuffer[i*bufferBytes],
-                 inBufferList->mBuffers[handle->index[1]+i].mData, bufferBytes );
-        }
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
+      }
+    }
+    else { // no buffer conversion
+      for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
+        memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
       }
-      else
-        stream_.deviceBuffer = (char *) inBufferList->mBuffers[handle->index[1]].mData;
-
-      if ( stream_.doByteSwap[1] )
-        byteSwapBuffer(stream_.deviceBuffer,
-                       stream_.bufferSize * stream_.nDeviceChannels[1],
-                       stream_.deviceFormat[1]);
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
-
     }
-    else {
-      memcpy(stream_.userBuffer,
-             inBufferList->mBuffers[handle->index[1]].mData,
-             inBufferList->mBuffers[handle->index[1]].mDataByteSize );
 
-      if (stream_.doByteSwap[1])
-        byteSwapBuffer(stream_.userBuffer,
-                       stream_.bufferSize * stream_.nUserChannels[1],
-                       stream_.userFormat);
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
   }
 
-  if ( !info->usingCallback && (stream_.mode != DUPLEX || deviceId == id ) )
-    pthread_cond_signal(&handle->condition);
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
-
-void RtApiCore :: setStreamCallback(RtAudioCallback callback, void *userData)
-{
-  verifyStream();
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
-  if ( stream_.callbackInfo.usingCallback ) {
-    sprintf(message_, "RtApiCore: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
+    if ( stream_.doConvertBuffer[1] ) {
+      for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
+        memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
+      }
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
+    }
+    else { // no buffer conversion
+      for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
+        jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
+        memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
+      }
+    }
   }
 
-  stream_.callbackInfo.callback = (void *) callback;
-  stream_.callbackInfo.userData = userData;
-  stream_.callbackInfo.usingCallback = true;
-}
-
-void RtApiCore :: cancelStreamCallback()
-{
-  verifyStream();
-
-  if (stream_.callbackInfo.usingCallback) {
-
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
-
-    MUTEX_LOCK(&stream_.mutex);
-
-    stream_.callbackInfo.usingCallback = false;
-    stream_.callbackInfo.userData = NULL;
-    stream_.state = STREAM_STOPPED;
-    stream_.callbackInfo.callback = NULL;
+ unlock:
+  MUTEX_UNLOCK(&stream_.mutex);
 
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
-
-
-//******************** End of __MACOSX_CORE__ *********************//
+//******************** End of __UNIX_JACK__ *********************//
 #endif
 
-#if defined(__LINUX_JACK__)
+#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
 
-// JACK is a low-latency audio server, written primarily for the
-// GNU/Linux operating system. It can connect a number of different
-// applications to an audio device, as well as allowing them to share
-// audio between themselves.
-//
-// The JACK server must be running before RtApiJack can be instantiated.
-// RtAudio will report just a single "device", which is the JACK audio
-// server.  The JACK server is typically started in a terminal as follows:
-//
-// .jackd -d alsa -d hw:0
-//
-// or through an interface program such as qjackctl.  Many of the
-// parameters normally set for a stream are fixed by the JACK server
-// and can be specified when the JACK server is started.  In
-// particular,
+// The ASIO API is designed around a callback scheme, so this
+// implementation is similar to that used for OS-X CoreAudio and Linux
+// Jack.  The primary constraint with ASIO is that it only allows
+// access to a single driver at a time.  Thus, it is not possible to
+// have more than one simultaneous RtAudio stream.
 //
-// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
+// This implementation also requires a number of external ASIO files
+// and a few global variables.  The ASIO callback scheme does not
+// allow for the passing of user data, so we must create a global
+// pointer to our callbackInfo structure.
 //
-// specifies a sample rate of 44100 Hz, a buffer size of 512 sample
-// frames, and number of buffers = 4.  Once the server is running, it
-// is not possible to override these values.  If the values are not
-// specified in the command-line, the JACK server uses default values.
+// On unix systems, we make use of a pthread condition variable.
+// Since there is no equivalent in Windows, I hacked something based
+// on information found in
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
 
-#include <jack/jack.h>
-#include <unistd.h>
+#include "asio/asiosys.h"
+#include "asio/asio.h"
+#include "asio/iasiothiscallresolver.h"
+#include "asio/asiodrivers.h"
+#include <cmath>
 
-// A structure to hold various information related to the Jack API
-// implementation.
-struct JackHandle {
-  jack_client_t *client;
-  jack_port_t **ports[2];
-  bool clientOpen;
-  bool stopStream;
-  pthread_cond_t condition;
+AsioDrivers drivers;
+ASIOCallbacks asioCallbacks;
+ASIODriverInfo driverInfo;
+CallbackInfo *asioCallbackInfo;
+bool asioXRun;
 
-  JackHandle()
-    :client(0), clientOpen(false), stopStream(false) {}
+struct AsioHandle {
+  int drainCounter;       // Tracks callback counts when draining
+  bool internalDrain;     // Indicates if stop is initiated from callback or not.
+  ASIOBufferInfo *bufferInfos;
+  HANDLE condition;
+
+  AsioHandle()
+    :drainCounter(0), internalDrain(false), bufferInfos(0) {}
 };
 
-std::string jackmsg;
+// Function declarations (definitions at end of section)
+static const char* getAsioErrorString( ASIOError result );
+void sampleRateChanged( ASIOSampleRate sRate );
+long asioMessages( long selector, long value, void* message, double* opt );
 
-static void jackerror (const char *desc)
+RtApiAsio :: RtApiAsio()
 {
-  jackmsg.erase();
-  jackmsg.append( desc, strlen(desc)+1 );
+  // ASIO cannot run on a multi-threaded appartment. You can call
+  // CoInitialize beforehand, but it must be for appartment threading
+  // (in which case, CoInitilialize will return S_FALSE here).
+  coInitialized_ = false;
+  HRESULT hr = CoInitialize( NULL ); 
+  if ( FAILED(hr) ) {
+    errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
+    error( WARNING );
+  }
+  coInitialized_ = true;
+
+  drivers.removeCurrentDriver();
+  driverInfo.asioVersion = 2;
+
+  // See note in DirectSound implementation about GetDesktopWindow().
+  driverInfo.sysRef = GetForegroundWindow();
 }
 
-RtApiJack :: RtApiJack()
+RtApiAsio :: ~RtApiAsio()
 {
-  this->initialize();
-
-  if (nDevices_ <= 0) {
-    sprintf(message_, "RtApiJack: no Linux Jack server found or connection error (jack: %s)!",
-            jackmsg.c_str());
-    error(RtError::NO_DEVICES_FOUND);
-  }
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+  if ( coInitialized_ ) CoUninitialize();
 }
 
-RtApiJack :: ~RtApiJack()
+unsigned int RtApiAsio :: getDeviceCount( void )
 {
-  if ( stream_.mode != UNINITIALIZED ) closeStream();
+  return (unsigned int) drivers.asioGetNumDev();
 }
 
-void RtApiJack :: initialize(void)
+RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
 {
-  nDevices_ = 0;
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
-  // Tell the jack server to call jackerror() when it experiences an
-  // error.  This function saves the error message for subsequent
-  // reporting via the normal RtAudio error function.
-       jack_set_error_function( jackerror );
+  // Get device ID
+  unsigned int nDevices = getDeviceCount();
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
+    error( INVALID_CALL );
+  }
 
-  // Look for jack server and try to become a client.
-  jack_client_t *client;
-  if ( (client = jack_client_new( "RtApiJack" )) == 0)
-    return;
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
+    error( INVALID_CALL );
+  }
 
-  /*
-    RtApiDevice device;
-    // Determine the name of the device.
-    device.name = "Jack Server";
-    devices_.push_back(device);
-    nDevices_++;
-  */
-  const char **ports;
-  std::string port, prevPort;
-  unsigned int nChannels = 0;
-  ports = jack_get_ports( client, NULL, NULL, 0 );
-  if ( ports ) {
-    port = (char *) ports[ nChannels ];
-    unsigned int colonPos = 0;
-    do {
-      port = (char *) ports[ nChannels ];
-      if ( (colonPos = port.find(":")) != std::string::npos ) {
-        port = port.substr( 0, colonPos+1 );
-        if ( port != prevPort ) {
-          RtApiDevice device;
-          device.name = port;
-          devices_.push_back( device );
-          nDevices_++;
-          prevPort = port;
-        }
-      }
-    } while ( ports[++nChannels] );
-    free( ports );
+  // Don't probe if a stream is already open.
+  if ( stream_.state != STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::getDeviceInfo: unable to probe driver while a stream is open.";
+    error( WARNING );
+    return info;
   }
 
-  jack_client_close(client);
-}
-
-void RtApiJack :: probeDeviceInfo(RtApiDevice *info)
-{
-  // Look for jack server and try to become a client.
-  jack_client_t *client;
-  if ( (client = jack_client_new( "RtApiJack_Probe" )) == 0) {
-    sprintf(message_, "RtApiJack: error connecting to Linux Jack server in probeDeviceInfo() (jack: %s)!",
-            jackmsg.c_str());
-    error(RtError::WARNING);
-    return;
+  char driverName[32];
+  ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // Get the current jack server sample rate.
-  info->sampleRates.clear();
-  info->sampleRates.push_back( jack_get_sample_rate(client) );
+  info.name = driverName;
 
-  // Count the available ports as device channels.  Jack "input ports"
-  // equal RtAudio output channels.
-  const char **ports;
-  char *port;
-  unsigned int nChannels = 0;
-  ports = jack_get_ports( client, info->name.c_str(), NULL, JackPortIsInput );
-  if ( ports ) {
-    port = (char *) ports[nChannels];
-    while ( port )
-      port = (char *) ports[++nChannels];
-    free( ports );
-    info->maxOutputChannels = nChannels;
-    info->minOutputChannels = 1;
+  if ( !drivers.loadDriver( driverName ) ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // Jack "output ports" equal RtAudio input channels.
-  nChannels = 0;
-  ports = jack_get_ports( client, info->name.c_str(), NULL, JackPortIsOutput );
-  if ( ports ) {
-    port = (char *) ports[nChannels];
-    while ( port )
-      port = (char *) ports[++nChannels];
-    free( ports );
-    info->maxInputChannels = nChannels;
-    info->minInputChannels = 1;
+  result = ASIOInit( &driverInfo );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) {
-    jack_client_close(client);
-    sprintf(message_, "RtApiJack: error determining jack input/output channels!");
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Determine the device channel information.
+  long inputChannels, outputChannels;
+  result = ASIOGetChannels( &inputChannels, &outputChannels );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
-    info->hasDuplexSupport = true;
-    info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-      info->maxInputChannels : info->maxOutputChannels;
-    info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-      info->minInputChannels : info->minOutputChannels;
-  }
+  info.outputChannels = outputChannels;
+  info.inputChannels = inputChannels;
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
-  // Get the jack data format type.  There isn't much documentation
-  // regarding supported data formats in jack.  I'm assuming here that
-  // the default type will always be a floating-point type, of length
-  // equal to either 4 or 8 bytes.
-  int sample_size = sizeof( jack_default_audio_sample_t );
-  if ( sample_size == 4 )
-    info->nativeFormats = RTAUDIO_FLOAT32;
-  else if ( sample_size == 8 )
-    info->nativeFormats = RTAUDIO_FLOAT64;
+  // Determine the supported sample rates.
+  info.sampleRates.clear();
+  for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
+    result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
+    if ( result == ASE_OK )
+      info.sampleRates.push_back( SAMPLE_RATES[i] );
+  }
 
-  // Check that we have a supported format
-  if (info->nativeFormats == 0) {
-    jack_client_close(client);
-    sprintf(message_, "RtApiJack: error determining jack server data format!");
-    error(RtError::DEBUG_WARNING);
-    return;
+  // Determine supported data types ... just check first channel and assume rest are the same.
+  ASIOChannelInfo channelInfo;
+  channelInfo.channel = 0;
+  channelInfo.isInput = true;
+  if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
+  result = ASIOGetChannelInfo( &channelInfo );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  jack_client_close(client);
-  info->probed = true;
+  info.nativeFormats = 0;
+  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
+    info.nativeFormats |= RTAUDIO_SINT32;
+  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
+    info.nativeFormats |= RTAUDIO_FLOAT64;
+
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
+  if ( getDefaultInputDevice() == device )
+    info.isDefaultInput = true;
+
+  info.probed = true;
+  drivers.removeCurrentDriver();
+  return info;
 }
 
-int jackCallbackHandler(jack_nframes_t nframes, void *infoPointer)
+void bufferSwitch( long index, ASIOBool processNow )
 {
-  CallbackInfo *info = (CallbackInfo *) infoPointer;
-  RtApiJack *object = (RtApiJack *) info->object;
-  try {
-    object->callbackEvent( (unsigned long) nframes );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtApiJack: callback handler error (%s)!\n\n", exception.getMessageString());
-    return 0;
-  }
-
-  return 0;
+  RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
+  object->callbackEvent( index );
 }
 
-void jackShutdown(void *infoPointer)
+bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
 {
-  CallbackInfo *info = (CallbackInfo *) infoPointer;
-  JackHandle *handle = (JackHandle *) info->apiInfo;
-  handle->clientOpen = false;
-  RtApiJack *object = (RtApiJack *) info->object;
+  // For ASIO, a duplex stream MUST use the same driver.
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
+    return FAILURE;
+  }
 
-  // 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;
+  char driverName[32];
+  ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  try {
-    object->closeStream();
+  // Only load the driver once for duplex stream.
+  if ( mode != INPUT || stream_.mode != OUTPUT ) {
+    if ( !drivers.loadDriver( driverName ) ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+
+    result = ASIOInit( &driverInfo );
+    if ( result != ASE_OK ) {
+      errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtApiJack: jackShutdown error (%s)!\n\n", exception.getMessageString());
-    return;
+
+  // Check the device channel count.
+  long inputChannels, outputChannels;
+  result = ASIOGetChannels( &inputChannels, &outputChannels );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  fprintf(stderr, "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!!\n\n");
-}
+  if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
+       ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.nDeviceChannels[mode] = channels;
+  stream_.nUserChannels[mode] = channels;
+  stream_.channelOffset[mode] = firstChannel;
 
-int jackXrun( void * )
-{
-  fprintf(stderr, "\nRtApiJack: audio overrun/underrun reported!\n");
-  return 0;
-}
+  // Verify the sample rate is supported.
+  result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-bool RtApiJack :: probeDeviceOpen(int device, StreamMode mode, int channels, 
-                                int sampleRate, RtAudioFormat format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  // Compare the jack server channels to the requested number of channels.
-  if ( (mode == OUTPUT && devices_[device].maxOutputChannels < channels ) || 
-       (mode == INPUT && devices_[device].maxInputChannels < channels ) ) {
-    sprintf(message_, "RtApiJack: the Jack server does not support requested channels!");
-    error(RtError::DEBUG_WARNING);
+  // Set the sample rate.
+  result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
+  // Determine the driver data type.
+  ASIOChannelInfo channelInfo;
+  channelInfo.channel = 0;
+  if ( mode == OUTPUT ) channelInfo.isInput = false;
+  else channelInfo.isInput = true;
+  result = ASIOGetChannelInfo( &channelInfo );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  // Look for jack server and try to become a client (only do once per stream).
-  char label[32];
-  jack_client_t *client = 0;
-  if ( mode == OUTPUT || (mode == INPUT && stream_.mode != OUTPUT) ) {
-    snprintf(label, 32, "RtApiJack");
-    if ( (client = jack_client_new( (const char *) label )) == 0) {
-      sprintf(message_, "RtApiJack: cannot connect to Linux Jack server in probeDeviceOpen() (jack: %s)!",
-              jackmsg.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  // Assuming WINDOWS host is always little-endian.
+  stream_.doByteSwap[mode] = false;
+  stream_.userFormat = format;
+  stream_.deviceFormat[mode] = 0;
+  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
   }
-  else {
-    // The handle must have been created on an earlier pass.
-    client = handle->client;
+  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
+  }
+  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
+  }
+  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
   }
 
-  // First, check the jack server sample rate.
-  int jack_rate;
-  jack_rate = (int) jack_get_sample_rate(client);
-  if ( sampleRate != jack_rate ) {
-    jack_client_close(client);
-    sprintf( message_, "RtApiJack: the requested sample rate (%d) is different than the JACK server rate (%d).",
-             sampleRate, jack_rate );
-    error(RtError::DEBUG_WARNING);
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
-  stream_.sampleRate = jack_rate;
 
-  // The jack server seems to support just a single floating-point
-  // data type.  Since we already checked it before, just use what we
-  // found then.
-  stream_.deviceFormat[mode] = devices_[device].nativeFormats;
-  stream_.userFormat = format;
+  // Set the buffer size.  For a duplex stream, this will end up
+  // setting the buffer size based on the input constraints, which
+  // should be ok.
+  long minSize, maxSize, preferSize, granularity;
+  result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
+  if ( result != ASE_OK ) {
+    drivers.removeCurrentDriver();
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  // Jack always uses non-interleaved buffers.  We'll need to
-  // de-interleave if we have more than one channel.
-  stream_.deInterleave[mode] = false;
-  if ( channels > 1 )
-    stream_.deInterleave[mode] = true;
+  if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+  else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
+  else if ( granularity == -1 ) {
+    // Make sure bufferSize is a power of two.
+    double power = std::log10( (double) *bufferSize ) / log10( 2.0 );
+    *bufferSize = (int) pow( 2.0, floor(power+0.5) );
+    if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
+    else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
+    else *bufferSize = preferSize;
+  }
+  else if ( granularity != 0 ) {
+    // Set to an even multiple of granularity, rounding up.
+    *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
+  }
 
-  // Jack always provides host byte-ordered data.
-  stream_.doByteSwap[mode] = false;
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) {
+    drivers.removeCurrentDriver();
+    errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
+    return FAILURE;
+  }
 
-  // Get the buffer size.  The buffer size and number of buffers
-  // (periods) is set when the jack server is started.
-  stream_.bufferSize = (int) jack_get_buffer_size(client);
-  *bufferSize = stream_.bufferSize;
+  stream_.bufferSize = *bufferSize;
+  stream_.nBuffers = 2;
 
-  stream_.nDeviceChannels[mode] = channels;
-  stream_.nUserChannels[mode] = channels;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
 
-  stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.deInterleave[mode])
-    stream_.doConvertBuffer[mode] = true;
+  // ASIO always uses non-interleaved buffers.
+  stream_.deviceInterleaved[mode] = false;
 
-  // Allocate our JackHandle structure for the stream.
+  // Allocate, if necessary, our AsioHandle structure for the stream.
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
   if ( handle == 0 ) {
-    handle = (JackHandle *) calloc(1, sizeof(JackHandle));
-    if ( handle == NULL ) {
-      sprintf(message_, "RtApiJack: error allocating JackHandle memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
+    try {
+      handle = new AsioHandle;
     }
-    handle->ports[0] = 0;
-    handle->ports[1] = 0;
-    if ( pthread_cond_init(&handle->condition, NULL) ) {
-      sprintf(message_, "RtApiJack: error initializing pthread condition variable!");
-      goto error;
+    catch ( std::bad_alloc& ) {
+      //if ( handle == NULL ) {    
+      drivers.removeCurrentDriver();
+      errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
+      return FAILURE;
     }
+    handle->bufferInfos = 0;
+
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
     stream_.apiHandle = (void *) handle;
-    handle->client = client;
-    handle->clientOpen = true;
   }
 
-  // Allocate necessary internal buffers.
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
+  // Create the ASIO internal buffers.  Since RtAudio sets up input
+  // and output separately, we'll have to dispose of previously
+  // created output buffers for a duplex stream.
+  long inputLatency, outputLatency;
+  if ( mode == INPUT && stream_.mode == OUTPUT ) {
+    ASIODisposeBuffers();
+    if ( handle->bufferInfos ) free( handle->bufferInfos );
+  }
 
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiJack: error allocating user buffer memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
-    }
+  // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
+  bool buffersAllocated = false;
+  unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
+  if ( handle->bufferInfos == NULL ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
+    errorText_ = errorStream_.str();
+    goto error;
+  }
+
+  ASIOBufferInfo *infos;
+  infos = handle->bufferInfos;
+  for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
+    infos->isInput = ASIOFalse;
+    infos->channelNum = i + stream_.channelOffset[0];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
+  for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
+    infos->isInput = ASIOTrue;
+    infos->channelNum = i + stream_.channelOffset[1];
+    infos->buffers[0] = infos->buffers[1] = 0;
+  }
+
+  // Set up the ASIO callback structure and create the ASIO data buffers.
+  asioCallbacks.bufferSwitch = &bufferSwitch;
+  asioCallbacks.sampleRateDidChange = &sampleRateChanged;
+  asioCallbacks.asioMessage = &asioMessages;
+  asioCallbacks.bufferSwitchTimeInfo = NULL;
+  result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
+    errorText_ = errorStream_.str();
+    goto error;
+  }
+  buffersAllocated = true;
+
+  // Set flags for buffer conversion.
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate necessary internal buffers
+  unsigned long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   if ( stream_.doConvertBuffer[mode] ) {
 
-    long buffer_bytes;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiJack: error allocating device buffer memory (%s).",
-                devices_[device].name.c_str());
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
       }
     }
   }
 
-  // Allocate memory for the Jack ports (channels) identifiers.
-  handle->ports[mode] = (jack_port_t **) malloc (sizeof (jack_port_t *) * channels);
-  if ( handle->ports[mode] == NULL )  {
-    sprintf(message_, "RtApiJack: error allocating port handle memory (%s).",
-            devices_[device].name.c_str());
-    goto error;
-  }
-
+  stream_.sampleRate = sampleRate;
   stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
-  stream_.callbackInfo.usingCallback = false;
+  asioCallbackInfo = &stream_.callbackInfo;
   stream_.callbackInfo.object = (void *) this;
-  stream_.callbackInfo.apiInfo = (void *) handle;
-
   if ( stream_.mode == OUTPUT && mode == INPUT )
-    // We had already set up the stream for output.
+    // We had already set up an output stream.
     stream_.mode = DUPLEX;
-  else {
+  else
     stream_.mode = mode;
-    jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
-    jack_set_xrun_callback( handle->client, jackXrun, NULL );
-    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 );
-      }
-    }
+  // Determine device latencies
+  result = ASIOGetLatencies( &inputLatency, &outputLatency );
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
+    errorText_ = errorStream_.str();
+    error( WARNING); // warn but don't fail
+  }
+  else {
+    stream_.latency[0] = outputLatency;
+    stream_.latency[1] = inputLatency;
   }
 
+  // Setup the buffer conversion information structure.  We don't use
+  // buffers to do channel offsets, so we override that parameter
+  // here.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
+
   return SUCCESS;
 
  error:
-  if ( handle ) {
-    pthread_cond_destroy(&handle->condition);
-    if ( handle->clientOpen == true )
-      jack_client_close(handle->client);
-
-    if ( handle->ports[0] ) free(handle->ports[0]);
-    if ( handle->ports[1] ) free(handle->ports[1]);
+  if ( buffersAllocated )
+    ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
-    free( handle );
+  if ( handle ) {
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
-  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
-void RtApiJack :: closeStream()
+void RtApiAsio :: closeStream()
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // stream check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiJack::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
+    error( WARNING );
     return;
   }
 
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
-  if ( handle && handle->clientOpen == true ) {
-    if (stream_.state == STREAM_RUNNING)
-      jack_deactivate(handle->client);
-
-    jack_client_close(handle->client);
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    ASIOStop();
   }
+  ASIODisposeBuffers();
+  drivers.removeCurrentDriver();
 
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
   if ( handle ) {
-    if ( handle->ports[0] ) free(handle->ports[0]);
-    if ( handle->ports[1] ) free(handle->ports[1]);
-    pthread_cond_destroy(&handle->condition);
-    free( handle );
+    CloseHandle( handle->condition );
+    if ( handle->bufferInfos )
+      free( handle->bufferInfos );
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
 
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
     stream_.deviceBuffer = 0;
   }
 
   stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
-
-void RtApiJack :: startStream()
+void RtApiAsio :: startStream()
 {
   verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAsio::startStream(): the stream is already running!";
+    error( WARNING );
+    return;
+  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
-  char label[64];
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
-  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-    for ( int i=0; i<stream_.nUserChannels[0]; i++ ) {
-      snprintf(label, 64, "outport %d", i);
-      handle->ports[0][i] = jack_port_register(handle->client, (const char *)label,
-                                               JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
-    }
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  ASIOError result = ASIOStart();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
+    errorText_ = errorStream_.str();
+    goto unlock;
   }
 
-  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-    for ( int i=0; i<stream_.nUserChannels[1]; i++ ) {
-      snprintf(label, 64, "inport %d", i);
-      handle->ports[1][i] = jack_port_register(handle->client, (const char *)label,
-                                               JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
-    }
-  }
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
+  asioXRun = false;
 
-  if (jack_activate(handle->client)) {
-    sprintf(message_, "RtApiJack: unable to activate JACK client!");
-    error(RtError::SYSTEM_ERROR);
-  }
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  const char **ports;
-  int result;
-  // Get the list of available ports.
-  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-    ports = jack_get_ports(handle->client, devices_[stream_.device[0]].name.c_str(), NULL, JackPortIsInput);
-    if ( ports == NULL) {
-      sprintf(message_, "RtApiJack: error determining available jack input ports!");
-      error(RtError::SYSTEM_ERROR);
-    }
+  if ( result == ASE_OK ) return;
+  error( SYSTEM );
+}
 
-    // Now make the port connections.  Since RtAudio wasn't designed to
-    // allow the user to select particular channels of a device, we'll
-    // just open the first "nChannels" ports.
-    for ( int i=0; i<stream_.nUserChannels[0]; i++ ) {
-      result = 1;
-      if ( ports[i] )
-        result = jack_connect( handle->client, jack_port_name(handle->ports[0][i]), ports[i] );
-      if ( result ) {
-        free(ports);
-        sprintf(message_, "RtApiJack: error connecting output ports!");
-        error(RtError::SYSTEM_ERROR);
-      }
-    }
-    free(ports);
+void RtApiAsio :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
   }
 
-  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-    ports = jack_get_ports( handle->client, devices_[stream_.device[1]].name.c_str(), NULL, JackPortIsOutput );
-    if ( ports == NULL) {
-      sprintf(message_, "RtApiJack: error determining available jack output ports!");
-      error(RtError::SYSTEM_ERROR);
-    }
+  MUTEX_LOCK( &stream_.mutex );
 
-    // Now make the port connections.  See note above.
-    for ( int i=0; i<stream_.nUserChannels[1]; i++ ) {
-      result = 1;
-      if ( ports[i] )
-        result = jack_connect( handle->client, ports[i], jack_port_name(handle->ports[1][i]) );
-      if ( result ) {
-        free(ports);
-        sprintf(message_, "RtApiJack: error connecting input ports!");
-        error(RtError::SYSTEM_ERROR);
-      }
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      MUTEX_UNLOCK( &stream_.mutex );
+      WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE );  // block until signaled
+      ResetEvent( handle->condition );
+      MUTEX_LOCK( &stream_.mutex );
     }
-    free(ports);
   }
 
-  handle->stopStream = false;
-  stream_.state = STREAM_RUNNING;
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
-
-void RtApiJack :: stopStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+  ASIOError result = ASIOStop();
+  if ( result != ASE_OK ) {
+    errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
+    errorText_ = errorStream_.str();
+  }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
   stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
-
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
-  jack_deactivate(handle->client);
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+  MUTEX_UNLOCK( &stream_.mutex );
 
-void RtApiJack :: abortStream()
-{
-  stopStream();
+  if ( result == ASE_OK ) return;
+  error( SYSTEM );
 }
 
-void RtApiJack :: tickStream()
+void RtApiAsio :: abortStream()
 {
   verifyStream();
-
-  if (stream_.state == STREAM_STOPPED) return;
-
-  if (stream_.callbackInfo.usingCallback) {
-    sprintf(message_, "RtApiJack: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
+    error( WARNING );
     return;
   }
 
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
-
-  MUTEX_LOCK(&stream_.mutex);
-
-  pthread_cond_wait(&handle->condition, &stream_.mutex);
-
-  MUTEX_UNLOCK(&stream_.mutex);
+  // The following lines were commented-out because some behavior was
+  // noted where the device buffers need to be zeroed to avoid
+  // continuing sound, even when the device buffers are completed
+  // disposed.  So now, calling abort is the same as calling stop.
+  //AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
+  //handle->drainCounter = 1;
+  stopStream();
 }
 
-void RtApiJack :: callbackEvent( unsigned long nframes )
+bool RtApiAsio :: callbackEvent( long bufferIndex )
 {
-  verifyStream();
-
-  if (stream_.state == STREAM_STOPPED) return;
-
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  JackHandle *handle = (JackHandle *) stream_.apiHandle;
-  if ( info->usingCallback && handle->stopStream ) {
-    // Check if the stream should be stopped (via the previous user
-    // callback return value).  We stop the stream here, rather than
-    // after the function call, so that output data can first be
-    // processed.
-    this->stopStream();
-    return;
+  if ( stream_.state == STREAM_STOPPED ) return SUCCESS;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
+    return FAILURE;
   }
 
-  MUTEX_LOCK(&stream_.mutex);
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
 
-  // Invoke user callback first, to get fresh output data.
-  if ( info->usingCallback ) {
-    RtAudioCallback callback = (RtAudioCallback) info->callback;
-    handle->stopStream = callback(stream_.userBuffer, stream_.bufferSize, info->userData);
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > 3 ) {
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else
+      stopStream();
+    return SUCCESS;
   }
 
-  jack_default_audio_sample_t *jackbuffer;
-  long bufferBytes = nframes * sizeof(jack_default_audio_sample_t);
-  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
-    if (stream_.doConvertBuffer[0]) {
-      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],
-                                                                          (jack_nframes_t) nframes);
-        memcpy(jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
-      }
-    }
-    else { // single channel only
-      jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][0],
-                                                                        (jack_nframes_t) nframes);
-      memcpy(jackbuffer, stream_.userBuffer, bufferBytes );
-    }
-  }
+  MUTEX_LOCK( &stream_.mutex );
 
-  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
-    if (stream_.doConvertBuffer[1]) {
-    for ( int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
-      jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][i],
-                                                                        (jack_nframes_t) nframes);
-      memcpy(&stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && asioXRun == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      asioXRun = false;
     }
-    convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+    if ( stream_.mode != OUTPUT && asioXRun == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      asioXRun = false;
     }
-    else { // single channel only
-      jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][0],
-                                                                        (jack_nframes_t) nframes);
-      memcpy(stream_.userBuffer, jackbuffer, bufferBytes );
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return SUCCESS;
     }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
-  if ( !info->usingCallback )
-    pthread_cond_signal(&handle->condition);
+  unsigned int bufferBytes, i, j;
+  unsigned int nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+    bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
 
-void RtApiJack :: setStreamCallback(RtAudioCallback callback, void *userData)
-{
-  verifyStream();
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
 
-  if ( stream_.callbackInfo.usingCallback ) {
-    sprintf(message_, "RtApiJack: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
-  }
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
+      }
 
-  stream_.callbackInfo.callback = (void *) callback;
-  stream_.callbackInfo.userData = userData;
-  stream_.callbackInfo.usingCallback = true;
-}
+    }
+    else if ( stream_.doConvertBuffer[0] ) {
 
-void RtApiJack :: cancelStreamCallback()
-{
-  verifyStream();
+      convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[0],
+                        stream_.deviceFormat[0] );
+
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memcpy( handle->bufferInfos[i].buffers[bufferIndex],
+                  &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
+      }
 
-  if (stream_.callbackInfo.usingCallback) {
+    }
+    else {
 
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
+      if ( stream_.doByteSwap[0] )
+        byteSwapBuffer( stream_.userBuffer[0],
+                        stream_.bufferSize * stream_.nUserChannels[0],
+                        stream_.userFormat );
 
-    MUTEX_LOCK(&stream_.mutex);
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput != ASIOTrue )
+          memcpy( handle->bufferInfos[i].buffers[bufferIndex],
+                  &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
+      }
 
-    stream_.callbackInfo.usingCallback = false;
-    stream_.callbackInfo.userData = NULL;
-    stream_.state = STREAM_STOPPED;
-    stream_.callbackInfo.callback = NULL;
+    }
 
-    MUTEX_UNLOCK(&stream_.mutex);
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
+    }
   }
-}
-
-#endif
 
-#if defined(__LINUX_ALSA__)
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
-#include <alsa/asoundlib.h>
-#include <unistd.h>
-#include <ctype.h>
+    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
 
-// A structure to hold various information related to the ALSA API
-// implementation.
-struct AlsaHandle {
-  snd_pcm_t *handles[2];
-  bool synchronized;
-  char *tempBuffer;
+    if (stream_.doConvertBuffer[1]) {
 
-  AlsaHandle()
-    :synchronized(false), tempBuffer(0) {}
-};
+      // Always interleave ASIO input data.
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput == ASIOTrue )
+          memcpy( &stream_.deviceBuffer[j++*bufferBytes],
+                  handle->bufferInfos[i].buffers[bufferIndex],
+                  bufferBytes );
+      }
 
-extern "C" void *alsaCallbackHandler(void * ptr);
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.deviceBuffer,
+                        stream_.bufferSize * stream_.nDeviceChannels[1],
+                        stream_.deviceFormat[1] );
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
-RtApiAlsa :: RtApiAlsa()
-{
-  this->initialize();
+    }
+    else {
+      for ( i=0, j=0; i<nChannels; i++ ) {
+        if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
+          memcpy( &stream_.userBuffer[1][bufferBytes*j++],
+                  handle->bufferInfos[i].buffers[bufferIndex],
+                  bufferBytes );
+        }
+      }
 
-  if (nDevices_ <= 0) {
-    sprintf(message_, "RtApiAlsa: no Linux ALSA audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
+      if ( stream_.doByteSwap[1] )
+        byteSwapBuffer( stream_.userBuffer[1],
+                        stream_.bufferSize * stream_.nUserChannels[1],
+                        stream_.userFormat );
+    }
   }
-}
 
-RtApiAlsa :: ~RtApiAlsa()
-{
-  if ( stream_.mode != UNINITIALIZED )
-    closeStream();
-}
+ unlock:
+  // The following call was suggested by Malte Clasen.  While the API
+  // documentation indicates it should not be required, some device
+  // drivers apparently do not function correctly without it.
+  ASIOOutputReady();
 
-void RtApiAlsa :: initialize(void)
-{
-  int card, subdevice, result;
-  char name[64];
-  const char *cardId;
-  snd_ctl_t *handle;
-  snd_ctl_card_info_t *info;
-  snd_ctl_card_info_alloca(&info);
-  RtApiDevice device;
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  // Count cards and devices
-  nDevices_ = 0;
-  card = -1;
-  snd_card_next(&card);
-  while ( card >= 0 ) {
-    sprintf(name, "hw:%d", card);
-    result = snd_ctl_open(&handle, name, 0);
-    if (result < 0) {
-      sprintf(message_, "RtApiAlsa: control open (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    result = snd_ctl_card_info(handle, info);
-               if (result < 0) {
-      sprintf(message_, "RtApiAlsa: control hardware info (%i): %s.", card, snd_strerror(result));
-      error(RtError::DEBUG_WARNING);
-      goto next_card;
-               }
-    cardId = snd_ctl_card_info_get_id(info);
-               subdevice = -1;
-               while (1) {
-      result = snd_ctl_pcm_next_device(handle, &subdevice);
-                       if (result < 0) {
-        sprintf(message_, "RtApiAlsa: control next device (%i): %s.", card, snd_strerror(result));
-        error(RtError::DEBUG_WARNING);
-        break;
-      }
-                       if (subdevice < 0)
-        break;
-      sprintf( name, "hw:%d,%d", card, subdevice );
-      // If a cardId exists and it contains at least one non-numeric
-      // character, use it to identify the device.  This avoids a bug
-      // in ALSA such that a numeric string is interpreted as a device
-      // number.
-      for ( unsigned int i=0; i<strlen(cardId); i++ ) {
-        if ( !isdigit( cardId[i] ) ) {
-          sprintf( name, "hw:%s,%d", cardId, subdevice );
-          break;
-        }
-      }
-      device.name.erase();
-      device.name.append( (const char *)name, strlen(name)+1 );
-      devices_.push_back(device);
-      nDevices_++;
-    }
-  next_card:
-    snd_ctl_close(handle);
-    snd_card_next(&card);
-  }
+  RtApi::tickStreamTime();
+  return SUCCESS;
 }
 
-void RtApiAlsa :: probeDeviceInfo(RtApiDevice *info)
+void sampleRateChanged( ASIOSampleRate sRate )
 {
-  int err;
-  int open_mode = SND_PCM_ASYNC;
-  snd_pcm_t *handle;
-  snd_ctl_t *chandle;
-  snd_pcm_stream_t stream;
-       snd_pcm_info_t *pcminfo;
-       snd_pcm_info_alloca(&pcminfo);
-  snd_pcm_hw_params_t *params;
-  snd_pcm_hw_params_alloca(&params);
-  char name[64];
-  char *card;
-
-  // Open the control interface for this card.
-  strncpy( name, info->name.c_str(), 64 );
-  card = strtok(name, ",");
-  err = snd_ctl_open(&chandle, card, SND_CTL_NONBLOCK);
-  if (err < 0) {
-    sprintf(message_, "RtApiAlsa: control open (%s): %s.", card, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
+  // The ASIO documentation says that this usually only happens during
+  // external sync.  Audio processing is not stopped by the driver,
+  // actual sample rate might not have even changed, maybe only the
+  // sample rate status of an AES/EBU or S/PDIF digital input at the
+  // audio device.
+
+  RtApi *object = (RtApi *) asioCallbackInfo->object;
+  try {
+    object->stopStream();
+  }
+  catch ( RtError &exception ) {
+    std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
     return;
   }
-  unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") );
 
-  // First try for playback
-  stream = SND_PCM_STREAM_PLAYBACK;
-  snd_pcm_info_set_device(pcminfo, dev);
-  snd_pcm_info_set_subdevice(pcminfo, 0);
-  snd_pcm_info_set_stream(pcminfo, stream);
+  std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
+}
 
-  if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) {
-    if (err == -ENOENT) {
-      sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle output!", info->name.c_str());
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) output: %s",
-              info->name.c_str(), snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-    }
-    goto capture_probe;
-  }
-
-  err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK );
-  if (err < 0) {
-    if ( err == EBUSY )
-      sprintf(message_, "RtApiAlsa: pcm playback device (%s) is busy: %s.",
-              info->name.c_str(), snd_strerror(err));
-    else
-      sprintf(message_, "RtApiAlsa: pcm playback open (%s) error: %s.",
-              info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
-  }
+long asioMessages( long selector, long value, void* message, double* opt )
+{
+  long ret = 0;
 
-  // We have an open device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
+  switch( selector ) {
+  case kAsioSelectorSupported:
+    if ( value == kAsioResetRequest
+         || value == kAsioEngineVersion
+         || value == kAsioResyncRequest
+         || value == kAsioLatenciesChanged
+         // The following three were added for ASIO 2.0, you don't
+         // necessarily have to support them.
+         || value == kAsioSupportsTimeInfo
+         || value == kAsioSupportsTimeCode
+         || value == kAsioSupportsInputMonitor)
+      ret = 1L;
+    break;
+  case kAsioResetRequest:
+    // Defer the task and perform the reset of the driver during the
+    // next "safe" situation.  You cannot reset the driver right now,
+    // as this code is called from the driver.  Reset the driver is
+    // done by completely destruct is. I.e. ASIOStop(),
+    // ASIODisposeBuffers(), Destruction Afterwards you initialize the
+    // driver again.
+    std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
+    ret = 1L;
+    break;
+  case kAsioResyncRequest:
+    // This informs the application that the driver encountered some
+    // non-fatal data loss.  It is used for synchronization purposes
+    // of different media.  Added mainly to work around the Win16Mutex
+    // problems in Windows 95/98 with the Windows Multimedia system,
+    // which could lose data because the Mutex was held too long by
+    // another thread.  However a driver can issue it in other
+    // situations, too.
+    // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
+    asioXRun = true;
+    ret = 1L;
+    break;
+  case kAsioLatenciesChanged:
+    // This will inform the host application that the drivers were
+    // latencies changed.  Beware, it this does not mean that the
+    // buffer sizes have changed!  You might need to update internal
+    // delay data.
+    std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
+    ret = 1L;
+    break;
+  case kAsioEngineVersion:
+    // Return the supported ASIO version of the host application.  If
+    // a host application does not implement this selector, ASIO 1.0
+    // is assumed by the driver.
+    ret = 2L;
+    break;
+  case kAsioSupportsTimeInfo:
+    // Informs the driver whether the
+    // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
+    // For compatibility with ASIO 1.0 drivers the host application
+    // should always support the "old" bufferSwitch method, too.
+    ret = 0;
+    break;
+  case kAsioSupportsTimeCode:
+    // Informs the driver whether application is interested in time
+    // code info.  If an application does not need to know about time
+    // code, the driver has less work to do.
+    ret = 0;
+    break;
   }
+  return ret;
+}
 
-  // Get output channel information.
-  unsigned int value;
-  err = snd_pcm_hw_params_get_channels_min(params, &value);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware minimum channel probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
-  }
-  info->minOutputChannels = value;
+static const char* getAsioErrorString( ASIOError result )
+{
+  struct Messages 
+  {
+    ASIOError value;
+    const char*message;
+  };
 
-  err = snd_pcm_hw_params_get_channels_max(params, &value);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware maximum channel probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    goto capture_probe;
-  }
-  info->maxOutputChannels = value;
+  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." }
+  };
 
-  snd_pcm_close(handle);
+  for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
+    if ( m[i].value == result ) return m[i].message;
 
- capture_probe:
-  // Now try for capture
-  stream = SND_PCM_STREAM_CAPTURE;
-  snd_pcm_info_set_stream(pcminfo, stream);
+  return "Unknown error.";
+}
+//******************** End of __WINDOWS_ASIO__ *********************//
+#endif
 
-  err = snd_ctl_pcm_info(chandle, pcminfo);
-  snd_ctl_close(chandle);
-  if ( err < 0 ) {
-    if (err == -ENOENT) {
-      sprintf(message_, "RtApiAlsa: pcm device (%s) doesn't handle input!", info->name.c_str());
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      sprintf(message_, "RtApiAlsa: snd_ctl_pcm_info error for device (%s) input: %s",
-              info->name.c_str(), snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-    }
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
-  }
 
-  err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode | SND_PCM_NONBLOCK);
-  if (err < 0) {
-    if ( err == EBUSY )
-      sprintf(message_, "RtApiAlsa: pcm capture device (%s) is busy: %s.",
-              info->name.c_str(), snd_strerror(err));
-    else
-      sprintf(message_, "RtApiAlsa: pcm capture open (%s) error: %s.",
-              info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels == 0)
-      // didn't open for playback either ... device invalid
-      return;
-    goto probe_parameters;
-  }
-
-  // We have an open capture device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels > 0)
-      goto probe_parameters;
-    else
-      return;
-  }
+#if defined(__WINDOWS_DS__) // Windows DirectSound API
 
-  // Get input channel information.
-  err = snd_pcm_hw_params_get_channels_min(params, &value);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware minimum in channel probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels > 0)
-      goto probe_parameters;
-    else
-      return;
-  }
-  info->minInputChannels = value;
+// Modified by Robin Davies, October 2005
+// - Improvements to DirectX pointer chasing. 
+// - Backdoor RtDsStatistics hook provides DirectX performance information.
+// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
+// - Auto-call CoInitialize for DSOUND and ASIO platforms.
+// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
 
-  err = snd_pcm_hw_params_get_channels_max(params, &value);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware maximum in channel probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    if (info->maxOutputChannels > 0)
-      goto probe_parameters;
-    else
-      return;
-  }
-  info->maxInputChannels = value;
+#include <dsound.h>
+#include <assert.h>
 
-  snd_pcm_close(handle);
+#define MINIMUM_DEVICE_BUFFER_SIZE 32768
 
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
-    goto probe_parameters;
+#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
 
-  info->hasDuplexSupport = true;
-  info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-    info->maxInputChannels : info->maxOutputChannels;
-  info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-    info->minInputChannels : info->minOutputChannels;
+static inline DWORD dsPointerDifference( DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
+{
+  if (laterPointer > earlierPointer)
+    return laterPointer - earlierPointer;
+  else
+    return laterPointer - earlierPointer + bufferSize;
+}
 
- probe_parameters:
-  // At this point, we just need to figure out the supported data
-  // formats and sample rates.  We'll proceed by opening the device in
-  // the direction with the maximum number of channels, or playback if
-  // they are equal.  This might limit our sample rate options, but so
-  // be it.
+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;
+}
 
-  if (info->maxOutputChannels >= info->maxInputChannels)
-    stream = SND_PCM_STREAM_PLAYBACK;
-  else
-    stream = SND_PCM_STREAM_CAPTURE;
+// A structure to hold various information related to the DirectSound
+// API implementation.
+struct DsHandle {
+  unsigned int drainCounter; // Tracks callback counts when draining
+  bool internalDrain;        // Indicates if stop is initiated from callback or not.
+  void *id[2];
+  void *buffer[2];
+  bool xrun[2];
+  UINT bufferPointer[2];  
+  DWORD dsBufferSize[2];
+  DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
+  HANDLE condition;
 
-  err = snd_pcm_open(&handle, info->name.c_str(), stream, open_mode);
-  if (err < 0) {
-    sprintf(message_, "RtApiAlsa: pcm (%s) won't reopen during probe: %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  DsHandle()
+    :drainCounter(0), internalDrain(false) { id[0] = 0, id[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }
+};
 
-  // We have an open device ... allocate the parameter structure.
-  err = snd_pcm_hw_params_any(handle, params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: hardware reopen probe error (%s): %s.",
-            info->name.c_str(), snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+/*
+RtApiDs::RtDsStatistics RtApiDs::statistics;
 
-  // Test our discrete set of sample rate values.
-  int dir = 0;
-  info->sampleRates.clear();
-  for (unsigned int i=0; i<MAX_SAMPLE_RATES; i++) {
-    if (snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0)
-      info->sampleRates.push_back(SAMPLE_RATES[i]);
-  }
-  if (info->sampleRates.size() == 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: no supported sample rates found for device (%s).",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+// Provides a backdoor hook to monitor for DirectSound read overruns and write underruns.
+RtApiDs::RtDsStatistics RtApiDs::getDsStatistics()
+{
+  RtDsStatistics s = statistics;
 
-  // Probe the supported data formats ... we don't care about endian-ness just yet
-  snd_pcm_format_t format;
-  info->nativeFormats = 0;
-  format = SND_PCM_FORMAT_S8;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT8;
-  format = SND_PCM_FORMAT_S16;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT16;
-  format = SND_PCM_FORMAT_S24;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT24;
-  format = SND_PCM_FORMAT_S32;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_SINT32;
-  format = SND_PCM_FORMAT_FLOAT;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  format = SND_PCM_FORMAT_FLOAT64;
-  if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
-    info->nativeFormats |= RTAUDIO_FLOAT64;
+  // update the calculated fields.
+  if ( s.inputFrameSize != 0 )
+    s.latency += s.readDeviceSafeLeadBytes * 1.0 / s.inputFrameSize / s.sampleRate;
 
-  // Check that we have at least one supported format
-  if (info->nativeFormats == 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: pcm device (%s) data format not supported by RtAudio.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
+  if ( s.outputFrameSize != 0 )
+    s.latency += (s.writeDeviceSafeLeadBytes + s.writeDeviceBufferLeadBytes) * 1.0 / s.outputFrameSize / s.sampleRate;
 
-  // That's all ... close the device and return
-  snd_pcm_close(handle);
-  info->probed = true;
-  return;
+  return s;
 }
+*/
+
+// Declarations for utility functions, callbacks, and structures
+// specific to the DirectSound implementation.
+static bool CALLBACK deviceCountCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext );
+
+static char* getErrorString( int code );
+
+extern "C" unsigned __stdcall callbackHandler( void *ptr );
 
-bool RtApiAlsa :: probeDeviceOpen( int device, StreamMode mode, int channels, 
-                                   int sampleRate, RtAudioFormat format,
-                                   int *bufferSize, int numberOfBuffers )
+struct EnumInfo {
+  bool isInput;
+  bool getDefault;
+  bool findIndex;
+  unsigned int counter;
+  unsigned int index;
+  LPGUID id;
+  std::string name;
+
+  EnumInfo()
+    : isInput(false), getDefault(false), findIndex(false), counter(0), index(0) {}
+};
+
+RtApiDs :: RtApiDs()
 {
-#if defined(__RTAUDIO_DEBUG__)
-  snd_output_t *out;
-  snd_output_stdio_attach(&out, stderr, 0);
-#endif
+  // 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;
+}
 
-  // I'm not using the "plug" interface ... too much inconsistent behavior.
-  const char *name = devices_[device].name.c_str();
+RtApiDs :: ~RtApiDs()
+{
+  if ( coInitialized_ ) CoUninitialize(); // balanced call.
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+}
 
-  snd_pcm_stream_t alsa_stream;
-  if (mode == OUTPUT)
-    alsa_stream = SND_PCM_STREAM_PLAYBACK;
-  else
-    alsa_stream = SND_PCM_STREAM_CAPTURE;
-
-  int err;
-  snd_pcm_t *handle;
-  int alsa_open_mode = SND_PCM_ASYNC;
-  err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode);
-  if (err < 0) {
-    sprintf(message_,"RtApiAlsa: pcm device (%s) won't open: %s.",
-            name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+unsigned int RtApiDs :: getDefaultInputDevice( void )
+{
+  // Count output devices.
+  EnumInfo info;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") counting output devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return 0;
   }
 
-  // Fill the parameter structure.
-  snd_pcm_hw_params_t *hw_params;
-  snd_pcm_hw_params_alloca(&hw_params);
-  err = snd_pcm_hw_params_any(handle, hw_params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error getting parameter handle (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // Now enumerate input devices until we find the id = NULL.
+  info.isInput = true;
+  info.getDefault = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultInputDevice: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return 0;
   }
 
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n");
-  snd_pcm_hw_params_dump(hw_params, out);
-#endif
+  if ( info.counter > 0 ) return info.counter - 1;
+  return 0;
+}
 
-  // Set access ... try interleaved access first, then non-interleaved
-  if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) {
-    err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
-  }
-  else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) {
-               err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
-    stream_.deInterleave[mode] = true;
-  }
-  else {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: device (%s) access not supported by RtAudio.", name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+unsigned int RtApiDs :: getDefaultOutputDevice( void )
+{
+  // Enumerate output devices until we find the id = NULL.
+  EnumInfo info;
+  info.getDefault = true;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDefaultOutputDevice: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return 0;
   }
 
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error setting access ( (%s): %s.", name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  if ( info.counter > 0 ) return info.counter - 1;
+  return 0;
+}
 
-  // Determine how to set the device format.
-  stream_.userFormat = format;
-  snd_pcm_format_t device_format = SND_PCM_FORMAT_UNKNOWN;
-
-  if (format == RTAUDIO_SINT8)
-    device_format = SND_PCM_FORMAT_S8;
-  else if (format == RTAUDIO_SINT16)
-    device_format = SND_PCM_FORMAT_S16;
-  else if (format == RTAUDIO_SINT24)
-    device_format = SND_PCM_FORMAT_S24;
-  else if (format == RTAUDIO_SINT32)
-    device_format = SND_PCM_FORMAT_S32;
-  else if (format == RTAUDIO_FLOAT32)
-    device_format = SND_PCM_FORMAT_FLOAT;
-  else if (format == RTAUDIO_FLOAT64)
-    device_format = SND_PCM_FORMAT_FLOAT64;
-
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = format;
-    goto set_format;
+unsigned int RtApiDs :: getDeviceCount( void )
+{
+  // Count DirectSound devices.
+  EnumInfo info;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
 
-  // The user requested format is not natively supported by the device.
-  device_format = SND_PCM_FORMAT_FLOAT64;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
-    goto set_format;
+  // Count DirectSoundCapture devices.
+  info.isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &info );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
 
-  device_format = SND_PCM_FORMAT_FLOAT;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
-    goto set_format;
-  }
+  return info.counter;
+}
 
-  device_format = SND_PCM_FORMAT_S32;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-    goto set_format;
-  }
+RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
+{
+  // Because DirectSound always enumerates input and output devices
+  // separately (and because we don't attempt to combine devices
+  // internally), none of our "devices" will ever be duplex.
 
-  device_format = SND_PCM_FORMAT_S24;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT24;
-    goto set_format;
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  // Enumerate through devices to find the id (if it exists).  Note
+  // that we have to do the output enumeration first, even if this is
+  // an input device, in order for the device counter to be correct.
+  EnumInfo dsinfo;
+  dsinfo.findIndex = true;
+  dsinfo.index = device;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
 
-  device_format = SND_PCM_FORMAT_S16;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-    goto set_format;
+  if ( dsinfo.name.empty() ) goto probeInput;
+
+  LPDIRECTSOUND output;
+  DSCAPS outCaps;
+  result = DirectSoundCreate( dsinfo.id, &output, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  device_format = SND_PCM_FORMAT_S8;
-  if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT8;
-    goto set_format;
+  outCaps.dwSize = sizeof( outCaps );
+  result = output->GetCaps( &outCaps );
+  if ( FAILED( result ) ) {
+    output->Release();
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // 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::DEBUG_WARNING);
-  return FAILURE;
+  // Get output channel information.
+  info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
 
- set_format:
-  err = snd_pcm_hw_params_set_format(handle, hw_params, device_format);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error setting format (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // Get sample rate information.
+  info.sampleRates.clear();
+  for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+    if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
+         SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate )
+      info.sampleRates.push_back( SAMPLE_RATES[k] );
   }
 
-  // Determine whether byte-swaping is necessary.
-  stream_.doByteSwap[mode] = false;
-  if (device_format != SND_PCM_FORMAT_S8) {
-    err = snd_pcm_format_cpu_endian(device_format);
-    if (err == 0)
-      stream_.doByteSwap[mode] = true;
-    else if (err < 0) {
-      snd_pcm_close(handle);
-      sprintf(message_, "RtApiAlsa: error getting format endian-ness (%s): %s.",
-              name, snd_strerror(err));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-  }
+  // Get format information.
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
+  if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
 
-  // Set the sample rate.
-  err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error setting sample rate (%d) on device (%s): %s.",
-            sampleRate, name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  output->Release();
 
-  // Determine the number of channels for this device.  We support a possible
-  // minimum device channel number > than the value requested by the user.
-  stream_.nUserChannels[mode] = channels;
-  unsigned int value;
-  err = snd_pcm_hw_params_get_channels_max(hw_params, &value);
-  int device_channels = value;
-  if (err < 0 || device_channels < channels) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: channels (%d) not supported by device (%s).",
-            channels, name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  if ( getDefaultOutputDevice() == device )
+    info.isDefaultOutput = true;
 
-  err = snd_pcm_hw_params_get_channels_min(hw_params, &value);
-  if (err < 0 ) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error getting min channels count on device (%s).", name);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  // Copy name and return.
+  info.name = dsinfo.name;
+
+  info.probed = true;
+  return info;
+
+ probeInput:
+
+  dsinfo.isInput = true;
+  result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") enumerating input devices!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
-  device_channels = value;
-  if (device_channels < channels) device_channels = channels;
-  stream_.nDeviceChannels[mode] = device_channels;
 
-  // Set the device channels.
-  err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error setting channels (%d) on device (%s): %s.",
-            device_channels, name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
+  if ( dsinfo.name.empty() ) return info;
+
+  LPDIRECTSOUNDCAPTURE input;
+  result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // Set the buffer number, which in ALSA is referred to as the "period".
-  int dir;
-  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_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::DEBUG_WARNING);
-    return FAILURE;
+  DSCCAPS inCaps;
+  inCaps.dwSize = sizeof( inCaps );
+  result = input->GetCaps( &inCaps );
+  if ( FAILED( result ) ) {
+    input->Release();
+    errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsinfo.name << ")!";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  // Set the buffer (or period) size.
-  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::DEBUG_WARNING);
-    return FAILURE;
-  }
-  *bufferSize = period_size;
+  // Get input channel information.
+  info.inputChannels = inCaps.dwChannels;
 
-  // If attempting to setup a duplex stream, the bufferSize parameter
-  // MUST be the same in both directions!
-  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
-    sprintf( message_, "RtApiAlsa: error setting buffer size for duplex stream on device (%s).",
-             name );
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  // Get sample rate and format information.
+  if ( inCaps.dwChannels == 2 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.sampleRates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.sampleRates.push_back( 44100 );
+    }
+  }
+  else if ( inCaps.dwChannels == 1 ) {
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
+    if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+    if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
+
+    if ( info.nativeFormats & RTAUDIO_SINT16 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.sampleRates.push_back( 96000 );
+    }
+    else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
+      if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.sampleRates.push_back( 11025 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.sampleRates.push_back( 22050 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.sampleRates.push_back( 44100 );
+      if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.sampleRates.push_back( 96000 );
+    }
+  }
+  else info.inputChannels = 0; // technically, this would be an error
 
-  stream_.bufferSize = *bufferSize;
+  input->Release();
 
-  // Install the hardware configuration
-  err = snd_pcm_hw_params(handle, hw_params);
-  if (err < 0) {
-    snd_pcm_close(handle);
-    sprintf(message_, "RtApiAlsa: error installing hardware configuration (%s): %s.",
-            name, snd_strerror(err));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  if ( info.inputChannels == 0 ) return info;
 
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
-  snd_pcm_hw_params_dump(hw_params, out);
-#endif
+  if ( getDefaultInputDevice() == device )
+    info.isDefaultInput = true;
 
-  // 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);
+  // Copy name and return.
+  info.name = dsinfo.name;
+  info.probed = true;
+  return info;
+}
+
+bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                 unsigned int firstChannel, unsigned int sampleRate,
+                                 RtAudioFormat format, unsigned int *bufferSize,
+                                 RtAudio::StreamOptions *options )
+{
+  if ( channels + firstChannel > 2 ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
     return FAILURE;
   }
 
-#if defined(__RTAUDIO_DEBUG__)
-  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
-  snd_pcm_sw_params_dump(sw_params, out);
-#endif
+  // Enumerate through devices to find the id (if it exists).  Note
+  // that we have to do the output enumeration first, even if this is
+  // an input device, in order for the device counter to be correct.
+  EnumInfo dsinfo;
+  dsinfo.findIndex = true;
+  dsinfo.index = device;
+  HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
+  if ( FAILED( result ) ) {
+    errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating output devices!";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  // Allocate the ApiHandle if necessary and then save.
-  AlsaHandle *apiInfo = 0;
-  if ( stream_.apiHandle == 0 ) {
-    apiInfo = (AlsaHandle *) new AlsaHandle;
-    stream_.apiHandle = (void *) apiInfo;
-    apiInfo->handles[0] = 0;
-    apiInfo->handles[1] = 0;
+  if ( mode == OUTPUT ) {
+    if ( dsinfo.name.empty() ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
-  else {
-    apiInfo = (AlsaHandle *) stream_.apiHandle;
+  else { // mode == INPUT
+    dsinfo.isInput = true;
+    HRESULT result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceCountCallback, &dsinfo );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") enumerating input devices!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
+    if ( dsinfo.name.empty() ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
   }
-  apiInfo->handles[mode] = handle;
 
-  // Set flags for buffer conversion
-  stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] > 1 && stream_.deInterleave[mode])
-    stream_.doConvertBuffer[mode] = true;
+  // According to a note in PortAudio, using GetDesktopWindow()
+  // instead of GetForegroundWindow() is supposed to avoid problems
+  // that occur when the application's window is not the foreground
+  // window.  Also, if the application window closes before the
+  // DirectSound buffer, DirectSound can crash.  However, for console
+  // applications, no sound was produced when using GetDesktopWindow().
+  HWND hWnd = GetForegroundWindow();
 
-  // Allocate necessary internal buffers
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
+  // Check the numberOfBuffers parameter and limit the lowest value to
+  // two.  This is a judgement call and a value of two is probably too
+  // low for capture, but it should work for playback.
+  int nBuffers = 0;
+  if ( options ) nBuffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
+  if ( nBuffers < 2 ) nBuffers = 3;
 
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    if (apiInfo->tempBuffer) free(apiInfo->tempBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    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;
-    }
-  }
+  // Create the wave format structure.  The data format setting will
+  // be determined later.
+  WAVEFORMATEX waveFormat;
+  ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
+  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
+  waveFormat.nChannels = channels + firstChannel;
+  waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
 
-  if ( stream_.doConvertBuffer[mode] ) {
+  // Determine the device buffer size. By default, 32k, but we will
+  // grow it to make allowances for very large software buffer sizes.
+  DWORD dsBufferSize = 0;
+  DWORD dsPointerLeadTime = 0;
+  long bufferBytes = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
 
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
-      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
-    }
+  void *ohandle = 0, *bhandle = 0;
+  if ( mode == OUTPUT ) {
 
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiAlsa: error allocating device buffer memory (%s).",
-                devices_[device].name.c_str());
-        goto error;
-      }
+    LPDIRECTSOUND output;
+    result = DirectSoundCreate( dsinfo.id, &output, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
-  }
 
-  stream_.device[mode] = device;
-  stream_.state = STREAM_STOPPED;
-  if ( stream_.mode == OUTPUT && mode == INPUT ) {
-    // We had already set up an output stream.
-    stream_.mode = DUPLEX;
-    // 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);
+    DSCAPS outCaps;
+    outCaps.dwSize = sizeof( outCaps );
+    result = output->GetCaps( &outCaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
-  }
-  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];
+    // Check channel information.
+    if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
+      errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsinfo.name << ") does not support stereo playback.";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
 
-    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;
-      }
+    // Check format information.  Use 16-bit format unless not
+    // supported or user requests 8-bit.
+    if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
+         !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
+      waveFormat.wBitsPerSample = 16;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
     else {
-      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
-        stream_.convertInfo[mode].inOffset.push_back( k );
-        stream_.convertInfo[mode].outOffset.push_back( k );
-      }
+      waveFormat.wBitsPerSample = 8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
-  }
+    stream_.userFormat = format;
 
-  return SUCCESS;
+    // Update wave format structure and buffer information.
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
+    dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
 
- error:
-  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;
-  }
+    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
+    while ( dsPointerLeadTime * 2U > (DWORD) bufferBytes )
+      bufferBytes *= 2;
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
-  }
+    // Set cooperative level to DSSCL_EXCLUSIVE
+    result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  error(RtError::DEBUG_WARNING);
-  return FAILURE;
-}
+    // Even though we will write to the secondary buffer, we need to
+    // access the primary buffer to set the correct output format
+    // (since the default is 8-bit, 22 kHz!).  Setup the DS primary
+    // buffer description.
+    DSBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
+    bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
 
-void RtApiAlsa :: closeStream()
-{
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // stream check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiAlsa::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
-    return;
-  }
+    // Obtain the primary buffer
+    LPDIRECTSOUNDBUFFER buffer;
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
-  if (stream_.state == STREAM_RUNNING) {
-    if (stream_.mode == OUTPUT || stream_.mode == DUPLEX)
-      snd_pcm_drop(apiInfo->handles[0]);
-    if (stream_.mode == INPUT || stream_.mode == DUPLEX)
-      snd_pcm_drop(apiInfo->handles[1]);
-    stream_.state = STREAM_STOPPED;
-  }
+    // Set the primary DS buffer sound format.
+    result = buffer->SetFormat( &waveFormat );
+    if ( FAILED( result ) ) {
+      output->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  if (stream_.callbackInfo.usingCallback) {
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
-  }
+    // Setup the secondary DS buffer description.
+    dsBufferSize = (DWORD) bufferBytes;
+    ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSBUFFERDESC );
+    bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                  DSBCAPS_GETCURRENTPOSITION2 |
+                                  DSBCAPS_LOCHARDWARE );  // Force hardware mixing
+    bufferDescription.dwBufferBytes = bufferBytes;
+    bufferDescription.lpwfxFormat = &waveFormat;
 
-  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;
-  }
+    // Try to create the secondary DS buffer.  If that doesn't work,
+    // try to use software mixing.  Otherwise, there's a problem.
+    result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
+                                    DSBCAPS_GETCURRENTPOSITION2 |
+                                    DSBCAPS_LOCSOFTWARE );  // Force software mixing
+      result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
+      if ( FAILED( result ) ) {
+        output->Release();
+        errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsinfo.name << ")!";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+    }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
-  }
+    // Get the buffer size ... might be different from what we specified.
+    DSBCAPS dsbcaps;
+    dsbcaps.dwSize = sizeof( DSBCAPS );
+    result = buffer->GetCaps( &dsbcaps );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
-    stream_.deviceBuffer = 0;
-  }
+    bufferBytes = dsbcaps.dwBufferBytes;
 
-  stream_.mode = UNINITIALIZED;
-}
+    // Lock the DS buffer
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-// Pump a bunch of zeros into the output buffer.  This is needed only when we
-// are doing duplex operations.
-bool RtApiAlsa :: primeOutputBuffer()
-{
-  int err;
-  char *buffer;
-  int channels;
-  snd_pcm_t **handle;
-  RtAudioFormat format;
-  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
-  handle = (snd_pcm_t **) apiInfo->handles;
-  
-  if (stream_.mode == DUPLEX) {
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
 
-    // Setup parameters and do buffer conversion if necessary.
-    if ( stream_.doConvertBuffer[0] ) {
-      convertBuffer( stream_.deviceBuffer, apiInfo->tempBuffer, stream_.convertInfo[0] );
-      channels = stream_.nDeviceChannels[0];
-      format = stream_.deviceFormat[0];
-    }
-    else {
-      channels = stream_.nUserChannels[0];
-      format = stream_.userFormat;
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      output->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
 
-    buffer = new char[stream_.bufferSize * formatBytes(format) * channels];
-    bzero(buffer, stream_.bufferSize * formatBytes(format) * channels);
-    
-    for (int i=0; i<stream_.nBuffers; i++) {
-      // Write samples to device in interleaved/non-interleaved format.
-      if (stream_.deInterleave[0]) {
-        void *bufs[channels];
-        size_t offset = stream_.bufferSize * formatBytes(format);
-        for (int i=0; i<channels; i++)
-          bufs[i] = (void *) (buffer + (i * offset));
-        err = snd_pcm_writen(handle[0], bufs, stream_.bufferSize);
-      }
-      else
-        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[0]);
-          if (state == SND_PCM_STATE_XRUN) {
-            sprintf(message_, "RtApiAlsa: underrun detected while priming output buffer.");
-            return false;
-          }
-          else {
-            sprintf(message_, "RtApiAlsa: primeOutputBuffer() error, current state is %s.",
-                    snd_pcm_state_name(state));
-            return false;
-          }
-        }
-        else {
-          sprintf(message_, "RtApiAlsa: audio write error for device (%s): %s.",
-                  devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
-          return false;
-        }
-      }
-    }
+    dsBufferSize = bufferBytes;
+    ohandle = (void *) output;
+    bhandle = (void *) buffer;
   }
 
-  return true;
-}
+  if ( mode == INPUT ) {
 
-void RtApiAlsa :: startStream()
-{
-  // This method calls snd_pcm_prepare if the device isn't already in that state.
+    LPDIRECTSOUNDCAPTURE input;
+    result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+    DSCCAPS inCaps;
+    inCaps.dwSize = sizeof( inCaps );
+    result = input->GetCaps( &inCaps );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  MUTEX_LOCK(&stream_.mutex);
+    // Check channel information.
+    if ( inCaps.dwChannels < channels + firstChannel ) {
+      errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
+      return FAILURE;
+    }
 
-  int err;
-  snd_pcm_state_t state;
-  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
-  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    state = snd_pcm_state(handle[0]);
-    if (state != SND_PCM_STATE_PREPARED) {
-      err = snd_pcm_prepare(handle[0]);
-      if (err < 0) {
-        sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.",
-                devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
-        MUTEX_UNLOCK(&stream_.mutex);
-        error(RtError::DRIVER_ERROR);
+    // Check format information.  Use 16-bit format unless user
+    // requests 8-bit.
+    DWORD deviceFormats;
+    if ( channels + firstChannel == 2 ) {
+      deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
+      if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
+        waveFormat.wBitsPerSample = 8;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT8;
       }
-      // Reprime output buffer if needed
-      if ( (stream_.mode == DUPLEX) && ( !primeOutputBuffer() ) ) {
-        MUTEX_UNLOCK(&stream_.mutex);
-        error(RtError::DRIVER_ERROR);
+      else { // assume 16-bit is supported
+        waveFormat.wBitsPerSample = 16;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT16;
       }
     }
-  }
-
-  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]);
-      if (err < 0) {
-        sprintf(message_, "RtApiAlsa: error preparing pcm device (%s): %s.",
-                devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
-        MUTEX_UNLOCK(&stream_.mutex);
-        error(RtError::DRIVER_ERROR);
+    else { // channel == 1
+      deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
+      if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
+        waveFormat.wBitsPerSample = 8;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+      }
+      else { // assume 16-bit is supported
+        waveFormat.wBitsPerSample = 16;
+        stream_.deviceFormat[mode] = RTAUDIO_SINT16;
       }
     }
-  }
+    stream_.userFormat = format;
 
-  if ( (stream_.mode == DUPLEX) && ( !primeOutputBuffer() ) ) {
-    MUTEX_UNLOCK(&stream_.mutex);
-    error(RtError::DRIVER_ERROR);
-  }
-  stream_.state = STREAM_RUNNING;
+    // Update wave format structure and buffer information.
+    waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
+    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+    // Setup the secondary DS buffer description.
+    dsBufferSize = bufferBytes;
+    DSCBUFFERDESC bufferDescription;
+    ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
+    bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
+    bufferDescription.dwFlags = 0;
+    bufferDescription.dwReserved = 0;
+    bufferDescription.dwBufferBytes = bufferBytes;
+    bufferDescription.lpwfxFormat = &waveFormat;
 
-void RtApiAlsa :: stopStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+    // Create the capture buffer.
+    LPDIRECTSOUNDCAPTUREBUFFER buffer;
+    result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
+    if ( FAILED( result ) ) {
+      input->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+    // Lock the capture buffer
+    LPVOID audioPtr;
+    DWORD dataLen;
+    result = buffer->Lock( 0, bufferBytes, &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+    }
 
-  int err;
-  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) {
-      sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+    // Zero the buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      input->Release();
+      buffer->Release();
+      errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsinfo.name << ")!";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
+
+    dsBufferSize = bufferBytes;
+    ohandle = (void *) input;
+    bhandle = (void *) buffer;
+  }
+
+  // Set various stream parameters
+  stream_.nDeviceChannels[mode] = channels + firstChannel;
+  stream_.nUserChannels[mode] = channels;
+  stream_.bufferSize = *bufferSize;
+  stream_.channelOffset[mode] = firstChannel;
+  stream_.deviceInterleaved[mode] = true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
+  else stream_.userInterleaved = true;
+
+  // Set flag for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
+    stream_.doConvertBuffer[mode] = true;
+  if (stream_.userFormat != stream_.deviceFormat[mode])
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
+
+  // Allocate necessary internal buffers
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
-  if ( (stream_.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.",
-              devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.doConvertBuffer[mode] ) {
+
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
+      }
+    }
+
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
   }
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+  // Allocate our DsHandle structures for the stream.
+  DsHandle *handle;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new DsHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
+      goto error;
+    }
 
-void RtApiAlsa :: abortStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+    // Create a manual-reset event.
+    handle->condition = CreateEvent( NULL,   // no security
+                                     TRUE,   // manual-reset
+                                     FALSE,  // non-signaled initially
+                                     NULL ); // unnamed
+    stream_.apiHandle = (void *) handle;
+  }
+  else
+    handle = (DsHandle *) stream_.apiHandle;
+  handle->id[mode] = ohandle;
+  handle->buffer[mode] = bhandle;
+  handle->dsBufferSize[mode] = dsBufferSize;
+  handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
+  stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+  if ( stream_.mode == OUTPUT && mode == INPUT )
+    // We had already set up an output stream.
+    stream_.mode = DUPLEX;
+  else
+    stream_.mode = mode;
+  stream_.nBuffers = nBuffers;
+  stream_.sampleRate = sampleRate;
 
-  int err;
-  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) {
-      sprintf(message_, "RtApiAlsa: error draining pcm device (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup the callback thread.
+  unsigned threadId;
+  stream_.callbackInfo.object = (void *) this;
+  stream_.callbackInfo.isRunning = true;
+  stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
+                                                &stream_.callbackInfo, 0, &threadId );
+  if ( stream_.callbackInfo.thread == 0 ) {
+    errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
+    goto error;
+  }
+
+  return SUCCESS;
+
+ error:
+  if ( handle ) {
+    if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
+      LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
+      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      if ( buffer ) buffer->Release();
+      object->Release();
     }
+    if ( handle->buffer[1] ) {
+      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
+      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+      if ( buffer ) buffer->Release();
+      object->Release();
+    }
+    CloseHandle( handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
-  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.",
-              devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
   }
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
+
+  return FAILURE;
 }
 
-int RtApiAlsa :: streamWillBlock()
+void RtApiDs :: closeStream()
 {
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return 0;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::closeStream(): no open stream to close!";
+    error( WARNING );
+    return;
+  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  // Stop the callback thread.
+  stream_.callbackInfo.isRunning = false;
+  WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
+  CloseHandle( (HANDLE) stream_.callbackInfo.thread );
 
-  int err = 0, frames = 0;
-  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) {
-      sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  if ( handle ) {
+    if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
+      LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
+      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+      if ( buffer ) {
+        buffer->Stop();
+        buffer->Release();
+      }
+      object->Release();
+    }
+    if ( handle->buffer[1] ) {
+      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
+      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+      if ( buffer ) {
+        buffer->Stop();
+        buffer->Release();
+      }
+      object->Release();
     }
+    CloseHandle( handle->condition );
+    delete handle;
+    stream_.apiHandle = 0;
   }
 
-  frames = err;
-
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    err = snd_pcm_avail_update(handle[1]);
-    if (err < 0) {
-      sprintf(message_, "RtApiAlsa: error getting available frames for device (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), snd_strerror(err));
-      MUTEX_UNLOCK(&stream_.mutex);
-      error(RtError::DRIVER_ERROR);
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
-    if (frames > err) frames = err;
   }
 
-  frames = stream_.bufferSize - frames;
-  if (frames < 0) frames = 0;
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
-  MUTEX_UNLOCK(&stream_.mutex);
-  return frames;
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
-void RtApiAlsa :: tickStream()
+void RtApiDs :: startStream()
 {
   verifyStream();
-
-  int stopStream = 0;
-  if (stream_.state == STREAM_STOPPED) {
-    if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiDs::startStream(): the stream is already running!";
+    error( WARNING );
     return;
   }
-  else if (stream_.callbackInfo.usingCallback) {
-    RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
-    stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData);
-  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  // Increase scheduler frequency on lesser windows (a side-effect of
+  // increasing timer accuracy).  On greater windows (Win2K or later),
+  // this is already in effect.
 
-  // The state might change while waiting on a mutex.
-  if (stream_.state == STREAM_STOPPED)
-    goto unlock;
+  MUTEX_LOCK( &stream_.mutex );
+  
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
 
-  int err;
-  char *buffer;
-  int channels;
-  AlsaHandle *apiInfo;
-  snd_pcm_t **handle;
-  RtAudioFormat format;
-  apiInfo = (AlsaHandle *) stream_.apiHandle;
-  handle = (snd_pcm_t **) apiInfo->handles;
+  timeBeginPeriod( 1 ); 
+
+  /*
+  memset( &statistics, 0, sizeof( statistics ) );
+  statistics.sampleRate = stream_.sampleRate;
+  statistics.writeDeviceBufferLeadBytes = handle->dsPointerLeadTime[0];
+  */
+
+  buffersRolling = false;
+  duplexPrerollBytes = 0;
 
   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 );
+    // 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] );
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  HRESULT result;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    //statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0] ) * stream_.nDeviceChannels[0];
 
-    // Setup parameters.
-    if (stream_.doConvertBuffer[1]) {
-      buffer = stream_.deviceBuffer;
-      channels = stream_.nDeviceChannels[1];
-      format = stream_.deviceFormat[1];
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    else {
-      buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[1];
-      format = stream_.userFormat;
+  }
+
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    //statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1]) * stream_.nDeviceChannels[1];
+
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    result = buffer->Start( DSCBSTART_LOOPING );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
+  }
 
-    // Read samples from device in interleaved/non-interleaved format.
-    if (stream_.deInterleave[1]) {
-      void *bufs[channels];
-      size_t offset = stream_.bufferSize * formatBytes(format);
-      for (int i=0; i<channels; i++)
-        bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_readn(handle[1], bufs, stream_.bufferSize);
+  handle->drainCounter = 0;
+  handle->internalDrain = false;
+  stream_.state = STREAM_RUNNING;
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( FAILED( result ) ) error( SYSTEM );
+}
+
+void RtApiDs :: stopStream()
+{
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
+
+  MUTEX_LOCK( &stream_.mutex );
+
+  HRESULT result;
+  LPVOID audioPtr;
+  DWORD dataLen;
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( handle->drainCounter == 0 ) {
+      handle->drainCounter = 1;
+      MUTEX_UNLOCK( &stream_.mutex );
+      WaitForMultipleObjects( 1, &handle->condition, FALSE, INFINITE );  // block until signaled
+      ResetEvent( handle->condition );
+      MUTEX_LOCK( &stream_.mutex );
     }
-    else
-      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[1]);
-        if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message_, "RtApiAlsa: overrun detected.");
-          error(RtError::WARNING);
-          err = snd_pcm_prepare(handle[1]);
-          if (err < 0) {
-            sprintf(message_, "RtApiAlsa: error preparing handle after overrun: %s.",
-                    snd_strerror(err));
-            MUTEX_UNLOCK(&stream_.mutex);
-            error(RtError::DRIVER_ERROR);
-          }
-          // Reprime output buffer if needed.
-          if ( (stream_.mode == DUPLEX) && ( !primeOutputBuffer() ) ) {
-            MUTEX_UNLOCK(&stream_.mutex);
-            error(RtError::DRIVER_ERROR);
-          }
-        }
-        else {
-          sprintf(message_, "RtApiAlsa: tickStream() error, current state is %s.",
-                  snd_pcm_state_name(state));
-          MUTEX_UNLOCK(&stream_.mutex);
-          error(RtError::DRIVER_ERROR);
-        }
-        goto unlock;
-      }
-      else {
-        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);
-      }
+    // Stop the buffer and clear memory
+    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
 
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[1])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
 
-    // Do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[1])
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
+
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+
+    // If we start playing again, we must begin at beginning of buffer.
+    handle->bufferPointer[0] = 0;
   }
 
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
+    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    audioPtr = NULL;
+    dataLen = 0;
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[0]) {
-      buffer = stream_.deviceBuffer;
-      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];
+    result = buffer->Stop();
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") stopping input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    else {
-      if ( stream_.mode == DUPLEX )
-        buffer = apiInfo->tempBuffer;
-      else
-        buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[0];
-      format = stream_.userFormat;
+
+    // Lock the buffer and clear it so that if we start to play again,
+    // we won't have old data playing.
+    result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") locking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
 
-    // Do byte swapping if necessary.
-    if (stream_.doByteSwap[0])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
+    // Zero the DS buffer
+    ZeroMemory( audioPtr, dataLen );
 
-    // Write samples to device in interleaved/non-interleaved format.
-    if (stream_.deInterleave[0]) {
-      void *bufs[channels];
-      size_t offset = stream_.bufferSize * formatBytes(format);
-      for (int i=0; i<channels; i++)
-        bufs[i] = (void *) (buffer + (i * offset));
-      err = snd_pcm_writen(handle[0], bufs, stream_.bufferSize);
+    // Unlock the DS buffer
+    result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::abortStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    else
-      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[0]);
-        if (state == SND_PCM_STATE_XRUN) {
-          sprintf(message_, "RtApiAlsa: underrun detected.");
-          error(RtError::WARNING);
-          err = snd_pcm_prepare(handle[0]);
-          if (err < 0) {
-            sprintf(message_, "RtApiAlsa: error preparing handle after underrun: %s.",
-                    snd_strerror(err));
-            MUTEX_UNLOCK(&stream_.mutex);
-            error(RtError::DRIVER_ERROR);
-          }
-        }
-        else {
-          sprintf(message_, "RtApiAlsa: tickStream() error, current state is %s.",
-                  snd_pcm_state_name(state));
-          MUTEX_UNLOCK(&stream_.mutex);
-          error(RtError::DRIVER_ERROR);
-        }
-        goto unlock;
-      }
-      else {
-        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);
-      }
-    }
+    // If we start recording again, we must begin at beginning of buffer.
+    handle->bufferPointer[1] = 0;
   }
 
  unlock:
-  MUTEX_UNLOCK(&stream_.mutex);
-
-  if (stream_.callbackInfo.usingCallback && stopStream)
-    this->stopStream();
+  timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_UNLOCK( &stream_.mutex );
+  if ( FAILED( result ) ) error( SYSTEM );
 }
 
-void RtApiAlsa :: setStreamCallback(RtAudioCallback callback, void *userData)
+void RtApiDs :: abortStream()
 {
   verifyStream();
-
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message_, "RtApiAlsa: A callback is already set for this stream!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
+    error( WARNING );
     return;
   }
 
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-
-  // Set the thread attributes for joinable and realtime scheduling
-  // priority.  The higher priority will only take affect if the
-  // program is run as root or suid.
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-  pthread_attr_setschedpolicy(&attr, SCHED_RR);
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
+  handle->drainCounter = 1;
 
-  int err = pthread_create(&info->thread, &attr, alsaCallbackHandler, &stream_.callbackInfo);
-  pthread_attr_destroy(&attr);
-  if (err) {
-    info->usingCallback = false;
-    sprintf(message_, "RtApiAlsa: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
-  }
+  stopStream();
 }
 
-void RtApiAlsa :: cancelStreamCallback()
+void RtApiDs :: callbackEvent()
 {
-  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    Sleep(50); // sleep 50 milliseconds
+    return;
+  }
+
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
+    return;
+  }
 
-  if (stream_.callbackInfo.usingCallback) {
+  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+  DsHandle *handle = (DsHandle *) stream_.apiHandle;
 
-    if (stream_.state == STREAM_RUNNING)
+  // Check if we were draining the stream and signal is finished.
+  if ( handle->drainCounter > stream_.nBuffers + 2 ) {
+    if ( handle->internalDrain == false )
+      SetEvent( handle->condition );
+    else
       stopStream();
+    return;
+  }
 
-    MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
-    stream_.callbackInfo.thread = 0;
-    stream_.callbackInfo.callback = NULL;
-    stream_.callbackInfo.userData = NULL;
-
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
-}
-
-extern "C" void *alsaCallbackHandler(void *ptr)
-{
-  CallbackInfo *info = (CallbackInfo *) ptr;
-  RtApiAlsa *object = (RtApiAlsa *) info->object;
-  bool *usingCallback = &info->usingCallback;
-
-  while ( *usingCallback ) {
-    try {
-      object->tickStream();
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtApiAlsa: callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessageString());
-      break;
-    }
-  }
-
-  pthread_exit(NULL);
-}
-
-//******************** End of __LINUX_ALSA__ *********************//
-#endif
-
-#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
-
-// The ASIO API is designed around a callback scheme, so this
-// implementation is similar to that used for OS-X CoreAudio and Linux
-// Jack.  The primary constraint with ASIO is that it only allows
-// access to a single driver at a time.  Thus, it is not possible to
-// have more than one simultaneous RtAudio stream.
-//
-// This implementation also requires a number of external ASIO files
-// and a few global variables.  The ASIO callback scheme does not
-// allow for the passing of user data, so we must create a global
-// pointer to our callbackInfo structure.
-//
-// On unix systems, we make use of a pthread condition variable.
-// Since there is no equivalent in Windows, I hacked something based
-// on information found in
-// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
-
-#include "asio/asiosys.h"
-#include "asio/asio.h"
-#include "asio/iasiothiscallresolver.h"
-#include "asio/asiodrivers.h"
-#include <math.h>
-
-AsioDrivers drivers;
-ASIOCallbacks asioCallbacks;
-ASIODriverInfo driverInfo;
-CallbackInfo *asioCallbackInfo;
-
-struct AsioHandle {
-  bool stopStream;
-  ASIOBufferInfo *bufferInfos;
-  HANDLE condition;
-
-  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 (unsigned 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) {
-    sprintf(message_, "RtApiAsio: no Windows ASIO audio drivers found!");
-    error(RtError::NO_DEVICES_FOUND);
-  }
-}
-
-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;
-
-  // Create device structures and write device driver names to each.
-  RtApiDevice device;
-  char name[128];
-  for (int i=0; i<nDevices_; i++) {
-    if ( drivers.asioGetDriverName( i, name, 128 ) == 0 ) {
-      device.name.erase();
-      device.name.append( (const char *)name, strlen(name)+1);
-      devices_.push_back(device);
-    }
-    else {
-      sprintf(message_, "RtApiAsio: error getting driver name for device index %d!", i);
-      error(RtError::WARNING);
-    }
-  }
-
-  nDevices_ = (int) devices_.size();
-
-  drivers.removeCurrentDriver();
-  driverInfo.asioVersion = 2;
-  // See note in DirectSound implementation about GetDesktopWindow().
-  driverInfo.sysRef = GetForegroundWindow();
-}
-
-void RtApiAsio :: probeDeviceInfo(RtApiDevice *info)
-{
-  // Don't probe if a stream is already open.
-  if ( stream_.mode != UNINITIALIZED ) {
-    sprintf(message_, "RtApiAsio: unable to probe driver while a stream is open.");
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  if ( !drivers.loadDriver( (char *)info->name.c_str() ) ) {
-    sprintf(message_, "RtApiAsio: error loading driver (%s).", info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  ASIOError result = ASIOInit( &driverInfo );
-  if ( result != ASE_OK ) {
-    sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", 
-      GetAsioErrorString(result), info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Determine the device channel information.
-  long inputChannels, outputChannels;
-  result = ASIOGetChannels( &inputChannels, &outputChannels );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).", 
-      GetAsioErrorString(result), 
-      info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  info->maxOutputChannels = outputChannels;
-  if ( outputChannels > 0 ) info->minOutputChannels = 1;
-
-  info->maxInputChannels = inputChannels;
-  if ( inputChannels > 0 ) info->minInputChannels = 1;
-
-  // If device opens for both playback and capture, we determine the channels.
-  if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
-    info->hasDuplexSupport = true;
-    info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
-      info->maxInputChannels : info->maxOutputChannels;
-    info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
-      info->minInputChannels : info->minOutputChannels;
-  }
-
-  // Determine the supported sample rates.
-  info->sampleRates.clear();
-  for (unsigned int i=0; i<MAX_SAMPLE_RATES; i++) {
-    result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
-    if ( result == ASE_OK )
-      info->sampleRates.push_back( SAMPLE_RATES[i] );
-  }
-
-  if (info->sampleRates.size() == 0) {
-    drivers.removeCurrentDriver();
-    sprintf( message_, "RtApiAsio: No supported sample rates found for driver (%s).", info->name.c_str() );
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Determine supported data types ... just check first channel and assume rest are the same.
-  ASIOChannelInfo channelInfo;
-  channelInfo.channel = 0;
-  channelInfo.isInput = true;
-  if ( info->maxInputChannels <= 0 ) channelInfo.isInput = false;
-  result = ASIOGetChannelInfo( &channelInfo );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error (%s) getting driver (%s) channel information.", 
-      GetAsioErrorString(result), 
-      info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
-    info->nativeFormats |= RTAUDIO_SINT16;
-  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
-    info->nativeFormats |= RTAUDIO_SINT32;
-  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
-    info->nativeFormats |= RTAUDIO_FLOAT32;
-  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
-    info->nativeFormats |= RTAUDIO_FLOAT64;
-
-       // Check that we have at least one supported format.
-  if (info->nativeFormats == 0) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  info->probed = true;
-  drivers.removeCurrentDriver();
-}
-
-void bufferSwitch(long index, ASIOBool processNow)
-{
-  RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
-  try {
-    object->callbackEvent( index );
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtApiAsio: callback handler error (%s)!\n\n", exception.getMessageString());
-    return;
-  }
-
-  return;
-}
-
-void sampleRateChanged(ASIOSampleRate sRate)
-{
-  // The ASIO documentation says that this usually only happens during
-  // external sync.  Audio processing is not stopped by the driver,
-  // actual sample rate might not have even changed, maybe only the
-  // sample rate status of an AES/EBU or S/PDIF digital input at the
-  // audio device.
-
-  RtAudio *object = (RtAudio *) asioCallbackInfo->object;
-  try {
-    object->stopStream();
-  }
-  catch (RtError &exception) {
-    fprintf(stderr, "\nRtApiAsio: sampleRateChanged() error (%s)!\n\n", exception.getMessageString());
-    return;
-  }
-
-  fprintf(stderr, "\nRtApiAsio: driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate);
-}
-
-long asioMessages(long selector, long value, void* message, double* opt)
-{
-  long ret = 0;
-  switch(selector) {
-  case kAsioSelectorSupported:
-    if(value == kAsioResetRequest
-       || value == kAsioEngineVersion
-       || value == kAsioResyncRequest
-       || value == kAsioLatenciesChanged
-       // The following three were added for ASIO 2.0, you don't
-       // necessarily have to support them.
-       || value == kAsioSupportsTimeInfo
-       || value == kAsioSupportsTimeCode
-       || value == kAsioSupportsInputMonitor)
-      ret = 1L;
-    break;
-  case kAsioResetRequest:
-    // Defer the task and perform the reset of the driver during the
-    // next "safe" situation.  You cannot reset the driver right now,
-    // as this code is called from the driver.  Reset the driver is
-    // done by completely destruct is. I.e. ASIOStop(),
-    // ASIODisposeBuffers(), Destruction Afterwards you initialize the
-    // driver again.
-    fprintf(stderr, "\nRtApiAsio: driver reset requested!!!");
-    ret = 1L;
-    break;
-  case kAsioResyncRequest:
-    // This informs the application that the driver encountered some
-    // non-fatal data loss.  It is used for synchronization purposes
-    // of different media.  Added mainly to work around the Win16Mutex
-    // problems in Windows 95/98 with the Windows Multimedia system,
-    // which could lose data because the Mutex was held too long by
-    // another thread.  However a driver can issue it in other
-    // situations, too.
-    fprintf(stderr, "\nRtApiAsio: driver resync requested!!!");
-    ret = 1L;
-    break;
-  case kAsioLatenciesChanged:
-    // This will inform the host application that the drivers were
-    // latencies changed.  Beware, it this does not mean that the
-    // buffer sizes have changed!  You might need to update internal
-    // delay data.
-    fprintf(stderr, "\nRtApiAsio: driver latency may have changed!!!");
-    ret = 1L;
-    break;
-  case kAsioEngineVersion:
-    // Return the supported ASIO version of the host application.  If
-    // a host application does not implement this selector, ASIO 1.0
-    // is assumed by the driver.
-    ret = 2L;
-    break;
-  case kAsioSupportsTimeInfo:
-    // Informs the driver whether the
-    // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
-    // For compatibility with ASIO 1.0 drivers the host application
-    // should always support the "old" bufferSwitch method, too.
-    ret = 0;
-    break;
-  case kAsioSupportsTimeCode:
-    // Informs the driver wether application is interested in time
-    // code info.  If an application does not need to know about time
-    // code, the driver has less work to do.
-    ret = 0;
-    break;
-  }
-  return ret;
-}
-
-bool RtApiAsio :: probeDeviceOpen(int device, StreamMode mode, int channels, 
-                                  int sampleRate, RtAudioFormat format,
-                                  int *bufferSize, int numberOfBuffers)
-{
-  // For ASIO, a duplex stream MUST use the same driver.
-  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) {
-    sprintf(message_, "RtApiAsio: duplex stream must use the same device for input and output.");
-    error(RtError::WARNING);
-    return FAILURE;
-  }
-
-  // Only load the driver once for duplex stream.
-  ASIOError result;
-  if ( mode != INPUT || stream_.mode != OUTPUT ) {
-    if ( !drivers.loadDriver( (char *)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 ) {
-      sprintf(message_, "RtApiAsio: error (%s) initializing driver (%s).", 
-        GetAsioErrorString(result), devices_[device].name.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-  }
-
-  // Check the device channel count.
-  long inputChannels, outputChannels;
-  result = ASIOGetChannels( &inputChannels, &outputChannels );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error (%s) getting input/output channel count (%s).",
-      GetAsioErrorString(result), 
-      devices_[device].name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  if ( ( mode == OUTPUT && channels > outputChannels) ||
-       ( mode == INPUT && channels > inputChannels) ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) does not support requested channel count (%d).",
-            devices_[device].name.c_str(), channels);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-  stream_.nDeviceChannels[mode] = channels;
-  stream_.nUserChannels[mode] = channels;
-
-  // Verify the sample rate is supported.
-  result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) does not support requested sample rate (%d).",
-            devices_[device].name.c_str(), sampleRate);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  // Set the sample rate.
-  result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) error setting sample rate (%d).",
-            devices_[device].name.c_str(), sampleRate);
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  // Determine the driver data type.
-  ASIOChannelInfo channelInfo;
-  channelInfo.channel = 0;
-  if ( mode == OUTPUT ) channelInfo.isInput = false;
-  else channelInfo.isInput = true;
-  result = ASIOGetChannelInfo( &channelInfo );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) error getting data format.",
-            devices_[device].name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  // Assuming WINDOWS host is always little-endian.
-  stream_.doByteSwap[mode] = false;
-  stream_.userFormat = format;
-  stream_.deviceFormat[mode] = 0;
-  if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-    if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
-  }
-  else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
-    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
-    if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
-  }
-  else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
-    if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
-  }
-  else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
-    if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
-  }
-
-  if ( stream_.deviceFormat[mode] == 0 ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: driver (%s) data format not supported by RtAudio.",
-            devices_[device].name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  // Set the buffer size.  For a duplex stream, this will end up
-  // setting the buffer size based on the input constraints, which
-  // should be ok.
-  long minSize, maxSize, preferSize, granularity;
-  result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
-  if ( result != ASE_OK ) {
-    drivers.removeCurrentDriver();
-    sprintf(message_, "RtApiAsio: error (%s) on driver (%s) error getting buffer size.",
-        GetAsioErrorString(result), 
-        devices_[device].name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  if ( *bufferSize < minSize ) *bufferSize = minSize;
-  else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
-  else if ( granularity == -1 ) {
-    // Make sure bufferSize is a power of two.
-    double power = log10( (double) *bufferSize ) / log10( 2.0 );
-    *bufferSize = (int) pow( 2.0, floor(power+0.5) );
-    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;
-
-  stream_.bufferSize = *bufferSize;
-  stream_.nBuffers = 2;
-
-  // ASIO always uses deinterleaved channels.
-  stream_.deInterleave[mode] = true;
-
-  // Allocate, if necessary, our AsioHandle structure for the stream.
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-  if ( handle == 0 ) {
-    handle = (AsioHandle *) calloc(1, sizeof(AsioHandle));
-    if ( handle == NULL ) {
-      drivers.removeCurrentDriver();
-      sprintf(message_, "RtApiAsio: error allocating AsioHandle memory (%s).",
-              devices_[device].name.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-    handle->bufferInfos = 0;
-    // Create a manual-reset event.
-    handle->condition = CreateEvent( NULL,  // no security
-                                     TRUE,  // manual-reset
-                                     FALSE, // non-signaled initially
-                                     NULL ); // unnamed
-    stream_.apiHandle = (void *) handle;
-  }
-
-  // Create the ASIO internal buffers.  Since RtAudio sets up input
-  // and output separately, we'll have to dispose of previously
-  // created output buffers for a duplex stream.
-  if ( mode == INPUT && stream_.mode == OUTPUT ) {
-    ASIODisposeBuffers();
-    if ( handle->bufferInfos ) free( handle->bufferInfos );
-  }
-
-  // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
-  int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
-  handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
-  if (handle->bufferInfos == NULL) {
-    sprintf(message_, "RtApiAsio: error allocating bufferInfo memory (%s).",
-            devices_[device].name.c_str());
-    goto error;
-  }
-  ASIOBufferInfo *infos;
-  infos = handle->bufferInfos;
-  for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
-    infos->isInput = ASIOFalse;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
-  }
-  for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
-    infos->isInput = ASIOTrue;
-    infos->channelNum = i;
-    infos->buffers[0] = infos->buffers[1] = 0;
-  }
-
-  // Set up the ASIO callback structure and create the ASIO data buffers.
-  asioCallbacks.bufferSwitch = &bufferSwitch;
-  asioCallbacks.sampleRateDidChange = &sampleRateChanged;
-  asioCallbacks.asioMessage = &asioMessages;
-  asioCallbacks.bufferSwitchTimeInfo = NULL;
-  result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks);
-  if ( result != ASE_OK ) {
-    sprintf(message_, "RtApiAsio: eror (%s) on driver (%s) error creating buffers.",
-      GetAsioErrorString(result), 
-      devices_[device].name.c_str());
-    goto error;
-  }
-
-  // Set flags for buffer conversion.
-  stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] > 1 && stream_.deInterleave[mode])
-    stream_.doConvertBuffer[mode] = true;
-
-  // Allocate necessary internal buffers
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiAsio: error (%s) allocating user buffer memory (%s).",
-        GetAsioErrorString(result), 
-        devices_[device].name.c_str());
-      goto error;
-    }
-  }
-
-  if ( stream_.doConvertBuffer[mode] ) {
-
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
-      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
-    }
-
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiAsio: error (%s) allocating device buffer memory (%s).",
-          GetAsioErrorString(result), 
-                devices_[device].name.c_str());
-        goto error;
-      }
-    }
-  }
-
-  stream_.device[mode] = device;
-  stream_.state = STREAM_STOPPED;
-  if ( stream_.mode == OUTPUT && mode == INPUT )
-    // We had already set up an output stream.
-    stream_.mode = DUPLEX;
-  else
-    stream_.mode = mode;
-  stream_.sampleRate = sampleRate;
-  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:
-  ASIODisposeBuffers();
-  drivers.removeCurrentDriver();
-
-  if ( handle ) {
-    CloseHandle( handle->condition );
-    if ( handle->bufferInfos )
-      free( handle->bufferInfos );
-    free( handle );
-    stream_.apiHandle = 0;
-  }
-
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
-  }
-
-  error(RtError::DEBUG_WARNING);
-  return FAILURE;
-}
-
-void RtApiAsio :: closeStream()
-{
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiAsio::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
-    return;
-  }
-
-  if (stream_.state == STREAM_RUNNING)
-    ASIOStop();
-
-  ASIODisposeBuffers();
-  drivers.removeCurrentDriver();
-
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-  if ( handle ) {
-    CloseHandle( handle->condition );
-    if ( handle->bufferInfos )
-      free( handle->bufferInfos );
-    free( handle );
-    stream_.apiHandle = 0;
-  }
-
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
-  }
-
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
-    stream_.deviceBuffer = 0;
-  }
-
-  stream_.mode = UNINITIALIZED;
-}
-
-void RtApiAsio :: setStreamCallback(RtAudioCallback callback, void *userData)
-{
-  verifyStream();
-
-  if ( stream_.callbackInfo.usingCallback ) {
-    sprintf(message_, "RtApiAsio: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
-  }
-
-  stream_.callbackInfo.callback = (void *) callback;
-  stream_.callbackInfo.userData = userData;
-  stream_.callbackInfo.usingCallback = true;
-}
-
-void RtApiAsio :: cancelStreamCallback()
-{
-  verifyStream();
-
-  if (stream_.callbackInfo.usingCallback) {
-
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
-
-    MUTEX_LOCK(&stream_.mutex);
-
-    stream_.callbackInfo.usingCallback = false;
-    stream_.callbackInfo.userData = NULL;
-    stream_.state = STREAM_STOPPED;
-    stream_.callbackInfo.callback = NULL;
-
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
-}
-
-void RtApiAsio :: startStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
-
-  MUTEX_LOCK(&stream_.mutex);
-
-  ASIOError result = ASIOStart();
-  if ( result != ASE_OK ) {
-    sprintf(message_, "RtApiAsio: error starting device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-    MUTEX_UNLOCK(&stream_.mutex);
-    error(RtError::DRIVER_ERROR);
-  }
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-  handle->stopStream = false;
-  stream_.state = STREAM_RUNNING;
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
-
-void RtApiAsio :: 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);
-
-  ASIOError result = ASIOStop();
-  if ( result != ASE_OK ) {
-    sprintf(message_, "RtApiAsio: error stopping device (%s).",
-              devices_[stream_.device[0]].name.c_str());
-    MUTEX_UNLOCK(&stream_.mutex);
-    error(RtError::DRIVER_ERROR);
-  }
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
-
-void RtApiAsio :: abortStream()
-{
-  stopStream();
-}
-
-void RtApiAsio :: tickStream()
-{
-  verifyStream();
-
-  if (stream_.state == STREAM_STOPPED)
-    return;
-
-  if (stream_.callbackInfo.usingCallback) {
-    sprintf(message_, "RtApiAsio: tickStream() should not be used when a callback function is set!");
-    error(RtError::WARNING);
-    return;
-  }
-
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-
-  MUTEX_LOCK(&stream_.mutex);
-
-  // Release the stream_mutex here and wait for the event
-  // to become signaled by the callback process.
-  MUTEX_UNLOCK(&stream_.mutex);
-  WaitForMultipleObjects(1, &handle->condition, FALSE, INFINITE);
-  ResetEvent( handle->condition );
-}
-
-void RtApiAsio :: callbackEvent(long bufferIndex)
-{
-  verifyStream();
-
-  if (stream_.state == STREAM_STOPPED) return;
-
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-  if ( info->usingCallback && handle->stopStream ) {
-    // Check if the stream should be stopped (via the previous user
-    // callback return value).  We stop the stream here, rather than
-    // after the function call, so that output data can first be
-    // processed.
-    this->stopStream();
-    return;
-  }
-
-  MUTEX_LOCK(&stream_.mutex);
-
-  // Invoke user callback first, to get fresh output data.
-  if ( info->usingCallback ) {
-    RtAudioCallback callback = (RtAudioCallback) info->callback;
-    if ( callback(stream_.userBuffer, stream_.bufferSize, info->userData) )
-      handle->stopStream = true;
-  }
-
-  int bufferBytes, j;
-  int nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
-  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
-    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[0]);
-    if (stream_.doConvertBuffer[0]) {
-
-      convertBuffer( stream_.deviceBuffer, stream_.userBuffer, stream_.convertInfo[0] );
-      if ( stream_.doByteSwap[0] )
-        byteSwapBuffer(stream_.deviceBuffer,
-                       stream_.bufferSize * stream_.nDeviceChannels[0],
-                       stream_.deviceFormat[0]);
-
-      // Always de-interleave ASIO output data.
-      j = 0;
-      for ( int i=0; i<nChannels; i++ ) {
-        if ( handle->bufferInfos[i].isInput != ASIOTrue )
-          memcpy(handle->bufferInfos[i].buffers[bufferIndex],
-                 &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
-      }
-    }
-    else { // single channel only
-
-      if (stream_.doByteSwap[0])
-        byteSwapBuffer(stream_.userBuffer,
-                       stream_.bufferSize * stream_.nUserChannels[0],
-                       stream_.userFormat);
-
-      for ( int i=0; i<nChannels; i++ ) {
-        if ( handle->bufferInfos[i].isInput != ASIOTrue ) {
-          memcpy(handle->bufferInfos[i].buffers[bufferIndex], stream_.userBuffer, bufferBytes );
-          break;
-        }
-      }
-    }
-  }
-
-  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
-    bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
-    if (stream_.doConvertBuffer[1]) {
-
-      // Always interleave ASIO input data.
-      j = 0;
-      for ( int i=0; i<nChannels; i++ ) {
-        if ( handle->bufferInfos[i].isInput == ASIOTrue )
-          memcpy(&stream_.deviceBuffer[j++*bufferBytes],
-                 handle->bufferInfos[i].buffers[bufferIndex],
-                 bufferBytes );
-      }
-
-      if ( stream_.doByteSwap[1] )
-        byteSwapBuffer(stream_.deviceBuffer,
-                       stream_.bufferSize * stream_.nDeviceChannels[1],
-                       stream_.deviceFormat[1]);
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
-
-    }
-    else { // single channel only
-      for ( int i=0; i<nChannels; i++ ) {
-        if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
-          memcpy(stream_.userBuffer,
-                 handle->bufferInfos[i].buffers[bufferIndex],
-                 bufferBytes );
-          break;
-        }
-      }
-
-      if (stream_.doByteSwap[1])
-        byteSwapBuffer(stream_.userBuffer,
-                       stream_.bufferSize * stream_.nUserChannels[1],
-                       stream_.userFormat);
-    }
-  }
-
-  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);
-}
-
-//******************** End of __WINDOWS_ASIO__ *********************//
-#endif
-
-#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.
-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,
-                                         LPCTSTR description,
-                                         LPCTSTR module,
-                                         LPVOID lpContext);
-
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCTSTR description,
-                                        LPCTSTR module,
-                                        LPVOID lpContext);
-
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCTSTR description,
-                                           LPCTSTR module,
-                                           LPVOID lpContext);
-
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCTSTR description,
-                                      LPCTSTR module,
-                                      LPVOID lpContext);
-
-static char* getErrorString(int code);
-
-extern "C" unsigned __stdcall callbackHandler(void *ptr);
-
-struct enum_info {
-  std::string name;
-  LPGUID id;
-  bool isInput;
-  bool isValid;
-};
-
-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) {
-    sprintf(message_, "RtApiDs: no Windows DirectSound audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
-}
-
-RtApiDs :: ~RtApiDs()
-{
-  if (coInitialized)
-    CoUninitialize(); // balanced call.
-
-  if ( stream_.mode != UNINITIALIZED ) closeStream();
-}
-
-int RtApiDs :: getDefaultInputDevice(void)
-{
-  enum_info info;
-
-  // Enumerate through devices to find the default output.
-  HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Error performing default input device enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return 0;
-  }
-
-  for ( int i=0; i<nDevices_; i++ ) {
-    if ( info.name == devices_[i].name ) return i;
-  }
-
-  return 0;
-}
-
-int RtApiDs :: getDefaultOutputDevice(void)
-{
-  enum_info info;
-  info.name[0] = '\0';
-
-  // Enumerate through devices to find the default output.
-  HRESULT result = DirectSoundEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Error performing default output device enumeration: %s.",
-            getErrorString(result));
-    error(RtError::WARNING);
-    return 0;
-  }
-
-  for ( int i=0; i<nDevices_; i++ )
-    if ( info.name == devices_[i].name ) return i;
-
-  return 0;
-}
-
-void RtApiDs :: initialize(void)
-{
-  int i, ins = 0, outs = 0, count = 0;
-  HRESULT result;
-  nDevices_ = 0;
-
-  // Count DirectSound devices.
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &outs);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Unable to enumerate through sound playback devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  // Count DirectSoundCapture devices.
-  result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &ins);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Unable to enumerate through sound capture devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  count = ins + outs;
-  if (count == 0) return;
-
-  std::vector<enum_info> info(count);
-  for (i=0; i<count; i++) {
-    if (i < outs) info[i].isInput = false;
-    else info[i].isInput = true;
-  }
-
-  // Get playback device info and check capabilities.
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Unable to enumerate through sound playback devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  // Get capture device info and check capabilities.
-  result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Unable to enumerate through sound capture devices: %s.",
-            getErrorString(result));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  // Create device structures for valid devices and write device names
-  // to each.  Devices are considered invalid if they cannot be
-  // opened, they report < 1 supported channels, or they report no
-  // supported data (capture only).
-  RtApiDevice device;
-  for (i=0; i<count; i++) {
-    if ( info[i].isValid ) {
-      device.name.erase();
-      device.name = info[i].name;
-      devices_.push_back(device);
-    }
-  }
-
-  nDevices_ = devices_.size();
-  return;
-}
-
-void RtApiDs :: probeDeviceInfo(RtApiDevice *info)
-{
-  enum_info dsinfo;
-  dsinfo.name = info->name;
-  dsinfo.isValid = false;
-
-  // Enumerate through input devices to find the id (if it exists).
-  HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.",
-            getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Do capture probe first.
-  if ( dsinfo.isValid == false )
-    goto playback_probe;
-
-  LPDIRECTSOUNDCAPTURE  input;
-  result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.",
-            info->name.c_str(), getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    goto playback_probe;
-  }
-
-  DSCCAPS in_caps;
-  in_caps.dwSize = sizeof(in_caps);
-  result = input->GetCaps( &in_caps );
-  if ( FAILED(result) ) {
-    input->Release();
-    sprintf(message_, "RtApiDs: Could not get capture capabilities (%s): %s.",
-            info->name.c_str(), getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    goto playback_probe;
-  }
-
-  // Get input channel information.
-  info->minInputChannels = 1;
-  info->maxInputChannels = in_caps.dwChannels;
-
-  // Get sample rate and format information.
-  info->sampleRates.clear();
-  if( in_caps.dwChannels == 2 ) {
-    if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8;
-
-    if ( info->nativeFormats & RTAUDIO_SINT16 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates.push_back( 11025 );
-      if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates.push_back( 22050 );
-      if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates.push_back( 44100 );
-    }
-    else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates.push_back( 11025 );
-      if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates.push_back( 22050 );
-      if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates.push_back( 44100 );
-    }
-  }
-  else if ( in_caps.dwChannels == 1 ) {
-    if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->nativeFormats |= RTAUDIO_SINT16;
-    if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-    if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8;
-
-    if ( info->nativeFormats & RTAUDIO_SINT16 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates.push_back( 11025 );
-      if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates.push_back( 22050 );
-      if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates.push_back( 44100 );
-    }
-    else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
-      if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates.push_back( 11025 );
-      if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates.push_back( 22050 );
-      if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates.push_back( 44100 );
-    }
-  }
-  else info->minInputChannels = 0; // technically, this would be an error
-
-  input->Release();
-
- playback_probe:
-
-  dsinfo.isValid = false;
-
-  // Enumerate through output devices to find the id (if it exists).
-  result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.",
-            getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Now do playback probe.
-  if ( dsinfo.isValid == false )
-    goto check_parameters;
-
-  LPDIRECTSOUND  output;
-  DSCAPS out_caps;
-  result = DirectSoundCreate( dsinfo.id, &output, NULL );
-  if ( FAILED(result) ) {
-    sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.",
-            info->name.c_str(), getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    goto check_parameters;
-  }
-
-  out_caps.dwSize = sizeof(out_caps);
-  result = output->GetCaps( &out_caps );
-  if ( FAILED(result) ) {
-    output->Release();
-    sprintf(message_, "RtApiDs: Could not get playback capabilities (%s): %s.",
-            info->name.c_str(), getErrorString(result));
-    error(RtError::DEBUG_WARNING);
-    goto check_parameters;
-  }
-
-  // Get output channel information.
-  info->minOutputChannels = 1;
-  info->maxOutputChannels = ( out_caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
-
-  // Get sample rate information.  Use capture device rate information
-  // if it exists.
-  if ( info->sampleRates.size() == 0 ) {
-    info->sampleRates.push_back( (int) out_caps.dwMinSecondarySampleRate );
-    if ( out_caps.dwMaxSecondarySampleRate > out_caps.dwMinSecondarySampleRate )
-      info->sampleRates.push_back( (int) out_caps.dwMaxSecondarySampleRate );
-  }
-  else {
-    // 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 );
-    }
-    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() );
-      }
+  // Invoke user callback to get fresh output data UNLESS we are
+  // draining stream.
+  if ( handle->drainCounter == 0 ) {
+    RtAudioCallback callback = (RtAudioCallback) info->callback;
+    double streamTime = getStreamTime();
+    RtAudioStreamStatus status = 0;
+    if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+      status |= RTAUDIO_OUTPUT_UNDERFLOW;
+      handle->xrun[0] = false;
+    }
+    if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+      status |= RTAUDIO_INPUT_OVERFLOW;
+      handle->xrun[1] = false;
+    }
+    handle->drainCounter = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                                     stream_.bufferSize, streamTime, status, info->userData );
+    if ( handle->drainCounter == 2 ) {
+      MUTEX_UNLOCK( &stream_.mutex );
+      abortStream();
+      return;
     }
+    else if ( handle->drainCounter == 1 )
+      handle->internalDrain = true;
   }
 
-  // Get format information.
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16;
-  if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8;
-
-  output->Release();
-
- check_parameters:
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 ) {
-    sprintf(message_, "RtApiDs: no reported input or output channels for device (%s).",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-  if ( info->sampleRates.size() == 0 || info->nativeFormats == 0 ) {
-    sprintf(message_, "RtApiDs: no reported sample rates or data formats for device (%s).",
-            info->name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return;
-  }
-
-  // Determine duplex status.
-  if (info->maxInputChannels < info->maxOutputChannels)
-    info->maxDuplexChannels = info->maxInputChannels;
-  else
-    info->maxDuplexChannels = info->maxOutputChannels;
-  if (info->minInputChannels < info->minOutputChannels)
-    info->minDuplexChannels = info->minInputChannels;
-  else
-    info->minDuplexChannels = info->minOutputChannels;
-
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
-
-  info->probed = true;
-
-  return;
-}
-
-bool RtApiDs :: probeDeviceOpen( int device, StreamMode mode, int channels, 
-                                 int sampleRate, RtAudioFormat format,
-                                 int *bufferSize, int numberOfBuffers)
-{
   HRESULT result;
-  HWND hWnd = GetForegroundWindow();
-
-  // According to a note in PortAudio, using GetDesktopWindow()
-  // instead of GetForegroundWindow() is supposed to avoid problems
-  // that occur when the application's window is not the foreground
-  // window.  Also, if the application window closes before the
-  // DirectSound buffer, DirectSound can crash.  However, for console
-  // applications, no sound was produced when using GetDesktopWindow().
-  long buffer_size;
-  LPVOID audioPtr;
-  DWORD dataLen;
-  int nBuffers;
-
-  // Check the numberOfBuffers parameter and limit the lowest value to
-  // two.  This is a judgement call and a value of two is probably too
-  // low for capture, but it should work for playback.
-  if (numberOfBuffers < 2)
-    nBuffers = 2;
-  else
-    nBuffers = numberOfBuffers;
-
-  // Define the wave format structure (16-bit PCM, srate, channels)
-  WAVEFORMATEX waveFormat;
-  ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
-  waveFormat.wFormatTag = WAVE_FORMAT_PCM;
-  waveFormat.nChannels = channels;
-  waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
-
-  // Determine the data format.
-  if ( devices_[device].nativeFormats ) { // 8-bit and/or 16-bit support
-    if ( format == RTAUDIO_SINT8 ) {
-      if ( devices_[device].nativeFormats & RTAUDIO_SINT8 )
-        waveFormat.wBitsPerSample = 8;
-      else
-        waveFormat.wBitsPerSample = 16;
-    }
-    else {
-      if ( devices_[device].nativeFormats & RTAUDIO_SINT16 )
-        waveFormat.wBitsPerSample = 16;
-      else
-        waveFormat.wBitsPerSample = 8;
-    }
-  }
-  else {
-    sprintf(message_, "RtApiDs: no reported data formats for device (%s).",
-            devices_[device].name.c_str());
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
-
-  waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
-  waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
-
-  // Determine the device buffer size. By default, 32k, but we will
-  // grow it to make allowances for very large software buffer sizes.
-  DWORD dsBufferSize = 0;
-  DWORD dsPointerLeadTime = 0;
-
-  buffer_size = MINIMUM_DEVICE_BUFFER_SIZE; // sound cards will always *knock wood* support this
+  DWORD currentWritePos, safeWritePos;
+  DWORD currentReadPos, safeReadPos;
+  DWORD leadPos;
+  UINT nextWritePos;
 
-  enum_info dsinfo;
-  void *ohandle = 0, *bhandle = 0;
-  //  strncpy( dsinfo.name, devices_[device].name.c_str(), 64 );
-  dsinfo.name = devices_[device].name;
-  dsinfo.isValid = false;
-  if ( mode == OUTPUT ) {
+#ifdef GENERATE_DEBUG_LOG
+  DWORD writeTime, readTime;
+#endif
 
-    dsPointerLeadTime = numberOfBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
+  LPVOID buffer1 = NULL;
+  LPVOID buffer2 = NULL;
+  DWORD bufferSize1 = 0;
+  DWORD bufferSize2 = 0;
 
-    // If the user wants an even bigger buffer, increase the device buffer size accordingly.
-    while ( dsPointerLeadTime * 2U > (DWORD)buffer_size )
-      buffer_size *= 2;
+  char *buffer;
+  long bufferBytes;
 
-    if ( devices_[device].maxOutputChannels < channels ) {
-      sprintf(message_, "RtApiDs: requested channels (%d) > than supported (%d) by device (%s).",
-              channels, devices_[device].maxOutputChannels, devices_[device].name.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+  if ( stream_.mode == DUPLEX && !buffersRolling ) {
+    assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
 
-    // Enumerate through output devices to find the id (if it exists).
-    result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Error performing output device id enumeration: %s.",
-              getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    // 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.
 
-    if ( dsinfo.isValid == false ) {
-      sprintf(message_, "RtApiDs: output device (%s) id not found!", devices_[device].name.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    // 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.
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUND  object;
-    LPDIRECTSOUNDBUFFER buffer;
-    DSBUFFERDESC bufferDescription;
-    
-    result = DirectSoundCreate( id, &object, NULL );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Could not create playback object (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
+    LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
 
-    // Set cooperative level to DSSCL_EXCLUSIVE
-    result = object->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message_, "RtApiDs: Unable to set cooperative level (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    DWORD initialWritePos, initialSafeWritePos;
+    DWORD initialReadPos, initialSafeReadPos;
 
-    // Even though we will write to the secondary buffer, we need to
-    // access the primary buffer to set the correct output format
-    // (since the default is 8-bit, 22 kHz!).  Setup the DS primary
-    // buffer description.
-    ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSBUFFERDESC);
-    bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
-    // Obtain the primary buffer
-    result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message_, "RtApiDs: Unable to access primary buffer (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    result = dsWriteBuffer->GetCurrentPosition( &initialWritePos, &initialSafeWritePos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
     }
-
-    // Set the primary DS buffer sound format.
-    result = buffer->SetFormat(&waveFormat);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message_, "RtApiDs: Unable to set primary buffer format (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    result = dsCaptureBuffer->GetCurrentPosition( &initialReadPos, &initialSafeReadPos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
     }
-
-    // Setup the secondary DS buffer description.
-    dsBufferSize = (DWORD)buffer_size;
-    ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSBUFFERDESC);
-    bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
-                                  DSBCAPS_GETCURRENTPOSITION2 |
-                                  DSBCAPS_LOCHARDWARE );  // Force hardware mixing
-    bufferDescription.dwBufferBytes = buffer_size;
-    bufferDescription.lpwfxFormat = &waveFormat;
-
-    // Try to create the secondary DS buffer.  If that doesn't work,
-    // try to use software mixing.  Otherwise, there's a problem.
-    result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
-                                    DSBCAPS_GETCURRENTPOSITION2 |
-                                    DSBCAPS_LOCSOFTWARE );  // Force software mixing
-      result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
-      if ( FAILED(result) ) {
-        object->Release();
-        sprintf(message_, "RtApiDs: Unable to create secondary DS buffer (%s): %s.",
-                devices_[device].name.c_str(), getErrorString(result));
-        error(RtError::DEBUG_WARNING);
-        return FAILURE;
+    while ( true ) {
+      result = dsWriteBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( SYSTEM );
       }
+      result = dsCaptureBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+        errorText_ = errorStream_.str();
+        error( SYSTEM );
+      }
+      if ( safeWritePos != initialSafeWritePos && safeReadPos != initialSafeReadPos ) break;
+      Sleep( 1 );
     }
 
-    // Get the buffer size ... might be different from what we specified.
-    DSBCAPS dsbcaps;
-    dsbcaps.dwSize = sizeof(DSBCAPS);
-    buffer->GetCaps(&dsbcaps);
-    buffer_size = dsbcaps.dwBufferBytes;
-
-    // Lock the DS buffer
-    result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      buffer->Release();
-      sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
-
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
-
-    // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      buffer->Release();
-      sprintf(message_, "RtApiDs: Unable to unlock buffer(%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
 
-    ohandle = (void *) object;
-    bhandle = (void *) buffer;
-    stream_.nDeviceChannels[0] = channels;
+    buffersRolling = true;
+    handle->bufferPointer[0] = ( safeWritePos + handle->dsPointerLeadTime[0] );
+    handle->bufferPointer[1] = safeReadPos;
   }
 
-  if ( mode == INPUT ) {
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    
+    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
 
-    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;
+    if ( handle->drainCounter > 1 ) { // write zeros to the output stream
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
+      memset( stream_.userBuffer[0], 0, bufferBytes );
     }
 
-    // Enumerate through input devices to find the id (if it exists).
-    result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Error performing input device id enumeration: %s.",
-              getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
+      bufferBytes *= formatBytes( stream_.deviceFormat[0] );
     }
-
-    if ( dsinfo.isValid == false ) {
-      sprintf(message_, "RtAudioDS: input device (%s) id not found!", devices_[device].name.c_str());
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    else {
+      buffer = stream_.userBuffer[0];
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
+      bufferBytes *= formatBytes( stream_.userFormat );
     }
 
-    LPGUID id = dsinfo.id;
-    LPDIRECTSOUNDCAPTURE  object;
-    LPDIRECTSOUNDCAPTUREBUFFER buffer;
-    DSCBUFFERDESC bufferDescription;
+    // No byte swapping necessary in DirectSound implementation.
 
-    result = DirectSoundCaptureCreate( id, &object, NULL );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Could not create capture object (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    // Ahhh ... windoze.  16-bit data is signed but 8-bit data is
+    // unsigned.  So, we need to convert our signed 8-bit data here to
+    // unsigned.
+    if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
+      for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
 
-    // Setup the secondary DS buffer description.
-    dsBufferSize = buffer_size;
-    ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC));
-    bufferDescription.dwSize = sizeof(DSCBUFFERDESC);
-    bufferDescription.dwFlags = 0;
-    bufferDescription.dwReserved = 0;
-    bufferDescription.dwBufferBytes = buffer_size;
-    bufferDescription.lpwfxFormat = &waveFormat;
+    DWORD dsBufferSize = handle->dsBufferSize[0];
+    nextWritePos = handle->bufferPointer[0];
 
-    // Create the capture buffer.
-    result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL);
-    if ( FAILED(result) ) {
-      object->Release();
-      sprintf(message_, "RtApiDs: Unable to create capture buffer (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+    DWORD endWrite;
+    while ( true ) {
+      // Find out where the read and "safe write" pointers are.
+      result = dsBuffer->GetCurrentPosition( &currentWritePos, &safeWritePos );
+      if ( FAILED( result ) ) {
+        errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
+        errorText_ = errorStream_.str();
+        error( SYSTEM );
+      }
 
-    // Lock the capture buffer
-    result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      buffer->Release();
-      sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
-    }
+      leadPos = safeWritePos + handle->dsPointerLeadTime[0];
+      if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
+      if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
+      endWrite = nextWritePos + bufferBytes;
 
-    // Zero the buffer
-    ZeroMemory(audioPtr, dataLen);
+      // Check whether the entire write region is behind the play pointer.
+      if ( leadPos >= endWrite ) break;
 
-    // Unlock the buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      object->Release();
-      buffer->Release();
-      sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.",
-              devices_[device].name.c_str(), getErrorString(result));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+      // If we are here, then we must wait until the play pointer gets
+      // beyond the write region.  The approach here is to use the
+      // Sleep() function to suspend operation until safePos catches
+      // up. Calculate number of milliseconds to wait as:
+      //   time = distance * (milliseconds/second) * fudgefactor /
+      //          ((bytes/sample) * (samples/second))
+      // A "fudgefactor" less than 1 is used because it was found
+      // that sleeping too long was MUCH worse than sleeping for
+      // several shorter periods.
+      double millis = ( endWrite - leadPos ) * 900.0;
+      millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
+      if ( millis < 1.0 ) millis = 1.0;
+      if ( millis > 50.0 ) {
+        static int nOverruns = 0;
+        ++nOverruns;
+      }
+      Sleep( (DWORD) millis );
     }
 
-    ohandle = (void *) object;
-    bhandle = (void *) buffer;
-    stream_.nDeviceChannels[1] = channels;
-  }
+    //if ( statistics.writeDeviceSafeLeadBytes < dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] ) ) {
+    //  statistics.writeDeviceSafeLeadBytes = dsPointerDifference( safeWritePos, currentWritePos, handle->dsBufferSize[0] );
+    //}
 
-  stream_.userFormat = format;
-  if ( waveFormat.wBitsPerSample == 8 )
-    stream_.deviceFormat[mode] = RTAUDIO_SINT8;
-  else
-    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
-  stream_.nUserChannels[mode] = channels;
+    if ( dsPointerBetween( nextWritePos, safeWritePos, currentWritePos, dsBufferSize )
+         || dsPointerBetween( endWrite, safeWritePos, currentWritePos, dsBufferSize ) ) { 
+      // We've strayed into the forbidden zone ... resync the read pointer.
+      //++statistics.numberOfWriteUnderruns;
+      handle->xrun[0] = true;
+      nextWritePos = safeWritePos + handle->dsPointerLeadTime[0] - bufferBytes + dsBufferSize;
+      while ( nextWritePos >= dsBufferSize ) nextWritePos -= dsBufferSize;
+      handle->bufferPointer[0] = nextWritePos;
+      endWrite = nextWritePos + bufferBytes;
+    }
 
-  stream_.bufferSize = *bufferSize;
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextWritePos, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
+    }
 
-  // Set flags for buffer conversion
-  stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
-    stream_.doConvertBuffer[mode] = true;
-  if (stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode])
-    stream_.doConvertBuffer[mode] = true;
+    // Copy our buffer into the DS buffer
+    CopyMemory( buffer1, buffer, bufferSize1 );
+    if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
 
-  // Allocate necessary internal buffers
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
+    // Update our buffer offset and unlock sound buffer
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
+    }
+    nextWritePos = ( nextWritePos + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    handle->bufferPointer[0] = nextWritePos;
 
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiDs: error allocating user buffer memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
+    if ( handle->drainCounter ) {
+      handle->drainCounter++;
+      goto unlock;
     }
   }
 
-  if ( stream_.doConvertBuffer[mode] ) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
-    long buffer_bytes;
-    bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
-      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
-      }
+    // Setup parameters.
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
+      bufferBytes *= formatBytes( stream_.deviceFormat[1] );
     }
-
-    if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiDs: error allocating device buffer memory (%s).",
-                devices_[device].name.c_str());
-        goto error;
-      }
+    else {
+      buffer = stream_.userBuffer[1];
+      bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
+      bufferBytes *= formatBytes( stream_.userFormat );
     }
-  }
 
-  // Allocate our DsHandle structures for the stream.
-  DsHandle *handles;
-  if ( stream_.apiHandle == 0 ) {
-    handles = (DsHandle *) calloc(2, sizeof(DsHandle));
-    if ( handles == NULL ) {
-      sprintf(message_, "RtApiDs: Error allocating DsHandle memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
+    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
+    long nextReadPos = handle->bufferPointer[1];
+    DWORD dsBufferSize = handle->dsBufferSize[1];
+
+    // Find out where the write and "safe read" pointers are.
+    result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
     }
-    handles[0].object = 0;
-    handles[1].object = 0;
-    stream_.apiHandle = (void *) handles;
-  }
-  else
-    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;
-  if ( stream_.mode == OUTPUT && mode == INPUT )
-    // We had already set up an output stream.
-    stream_.mode = DUPLEX;
-  else
-    stream_.mode = mode;
-  stream_.nBuffers = nBuffers;
-  stream_.sampleRate = sampleRate;
+    if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+    DWORD endRead = nextReadPos + bufferBytes;
 
-  // 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];
-    }
+    // 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_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
-      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
-    else
-      stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
+    if ( stream_.mode == DUPLEX ) {
+      if ( safeReadPos < endRead ) {
+        if ( duplexPrerollBytes <= 0 ) {
+          // Pre-roll time over. Be more agressive.
+          int adjustment = endRead-safeReadPos;
 
-    // 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;
+          handle->xrun[1] = true;
+          //++statistics.numberOfReadOverruns;
+          // Two cases:
+          //   - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
+          //     and perform fine adjustments later.
+          //   - small adjustments: back off by twice as much.
+          if ( adjustment >= 2*bufferBytes )
+            nextReadPos = safeReadPos-2*bufferBytes;
+          else
+            nextReadPos = safeReadPos-bufferBytes-adjustment;
+
+          //statistics.readDeviceSafeLeadBytes = currentReadPos-nextReadPos;
+          //if ( statistics.readDeviceSafeLeadBytes < 0) statistics.readDeviceSafeLeadBytes += dsBufferSize;
+          if ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+
+        }
+        else {
+          // In pre=roll time. Just do it.
+          nextReadPos = safeReadPos-bufferBytes;
+          while ( nextReadPos < 0 ) nextReadPos += dsBufferSize;
+        }
+        endRead = nextReadPos + bufferBytes;
       }
     }
-    else {
-      for (int k=0; k<stream_.convertInfo[mode].channels; k++) {
-        stream_.convertInfo[mode].inOffset.push_back( k );
-        stream_.convertInfo[mode].outOffset.push_back( k );
+    else { // mode == INPUT
+      while ( safeReadPos < endRead ) {
+        // See comments for playback.
+        double millis = (endRead - safeReadPos) * 900.0;
+        millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
+        if ( millis < 1.0 ) millis = 1.0;
+        Sleep( (DWORD) millis );
+
+        // Wake up, find out where we are now
+        result = dsBuffer->GetCurrentPosition( &currentReadPos, &safeReadPos );
+        if ( FAILED( result ) ) {
+          errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
+          errorText_ = errorStream_.str();
+          error( SYSTEM );
+        }
+      
+        if ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
       }
     }
-  }
 
-  return SUCCESS;
+    //if (statistics.readDeviceSafeLeadBytes < dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize ) )
+    //  statistics.readDeviceSafeLeadBytes = dsPointerDifference( currentReadPos, nextReadPos, dsBufferSize );
 
- error:
-  if (handles) {
-    if (handles[0].object) {
-      LPDIRECTSOUND object = (LPDIRECTSOUND) handles[0].object;
-      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-      if (buffer) buffer->Release();
-      object->Release();
+    // Lock free space in the buffer
+    result = dsBuffer->Lock( nextReadPos, bufferBytes, &buffer1,
+                             &bufferSize1, &buffer2, &bufferSize2, 0 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
     }
-    if (handles[1].object) {
-      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object;
-      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-      if (buffer) buffer->Release();
-      object->Release();
+
+    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;
     }
-    free(handles);
-    stream_.apiHandle = 0;
-  }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
-  }
+    // Update our buffer offset and unlock sound buffer
+    nextReadPos = ( nextReadPos + bufferSize1 + bufferSize2 ) % dsBufferSize;
+    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
+    if ( FAILED( result ) ) {
+      errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
+      errorText_ = errorStream_.str();
+      error( SYSTEM );
+    }
+    handle->bufferPointer[1] = nextReadPos;
 
-  error(RtError::DEBUG_WARNING);
-  return FAILURE;
-}
+    // No byte swapping necessary in DirectSound implementation.
 
-void RtApiDs :: setStreamCallback(RtAudioCallback callback, void *userData)
-{
-  verifyStream();
+    // If necessary, convert 8-bit data from unsigned to signed.
+    if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
+      for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
 
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message_, "RtApiDs: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
   }
-
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-
-  unsigned thread_id;
-  info->thread = _beginthreadex(NULL, 0, &callbackHandler,
-                                &stream_.callbackInfo, 0, &thread_id);
-  if (info->thread == 0) {
-    info->usingCallback = false;
-    sprintf(message_, "RtApiDs: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
+#ifdef GENERATE_DEBUG_LOG
+  if ( currentDebugLogEntry < debugLog.size() )
+  {
+    TTickRecord &r = debugLog[currentDebugLogEntry++];
+    r.currentReadPointer = currentReadPos;
+    r.safeReadPointer = safeReadPos;
+    r.currentWritePointer = currentWritePos;
+    r.safeWritePointer = safeWritePos;
+    r.readTime = readTime;
+    r.writeTime = writeTime;
+    r.nextReadPointer = handles[1].bufferPointer;
+    r.nextWritePointer = handles[0].bufferPointer;
   }
+#endif
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  // When spawning multiple threads in quick succession, it appears to be
-  // necessary to wait a bit for each to initialize ... another windoism!
-  Sleep(1);
+  RtApi::tickStreamTime();
 }
 
-void RtApiDs :: cancelStreamCallback()
+// Definitions for utility functions and callbacks
+// specific to the DirectSound implementation.
+
+extern "C" unsigned __stdcall callbackHandler( void *ptr )
 {
-  verifyStream();
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiDs *object = (RtApiDs *) info->object;
+  bool* isRunning = &info->isRunning;
 
-  if (stream_.callbackInfo.usingCallback) {
+  while ( *isRunning == true ) {
+    object->callbackEvent();
+  }
 
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
+  _endthreadex( 0 );
+  return 0;
+}
 
-    MUTEX_LOCK(&stream_.mutex);
+#include "tchar.h"
 
-    stream_.callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream_.callbackInfo.thread );
-    stream_.callbackInfo.thread = 0;
-    stream_.callbackInfo.callback = NULL;
-    stream_.callbackInfo.userData = NULL;
+std::string convertTChar( LPCTSTR name )
+{
+  std::string s;
 
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
+#if defined( UNICODE ) || defined( _UNICODE )
+  // Yes, this conversion doesn't make sense for two-byte characters
+  // but RtAudio is currently written to return an std::string of
+  // one-byte chars for the device name.
+  for ( unsigned int i=0; i<wcslen( name ); i++ )
+    s.push_back( name[i] );
+#else
+  s.append( std::string( name ) );
+#endif
+
+  return s;
 }
 
-void RtApiDs :: closeStream()
+static bool CALLBACK deviceCountCallback( LPGUID lpguid,
+                                          LPCTSTR description,
+                                          LPCTSTR module,
+                                          LPVOID lpContext )
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiDs::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
-    return;
-  }
+  EnumInfo *info = (EnumInfo *) lpContext;
 
-  if (stream_.callbackInfo.usingCallback) {
-    stream_.callbackInfo.usingCallback = false;
-    WaitForSingleObject( (HANDLE)stream_.callbackInfo.thread, INFINITE );
-    CloseHandle( (HANDLE)stream_.callbackInfo.thread );
-  }
+  HRESULT hr;
+  if ( info->isInput == true ) {
+    DSCCAPS caps;
+    LPDIRECTSOUNDCAPTURE object;
 
-  DsHandle *handles = (DsHandle *) stream_.apiHandle;
-  if (handles) {
-    if (handles[0].object) {
-      LPDIRECTSOUND object = (LPDIRECTSOUND) handles[0].object;
-      LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-      if (buffer) {
-        buffer->Stop();
-        buffer->Release();
-      }
-      object->Release();
-    }
+    hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return true;
 
-    if (handles[1].object) {
-      LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handles[1].object;
-      LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-      if (buffer) {
-        buffer->Stop();
-        buffer->Release();
-      }
-      object->Release();
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
+        info->counter++;
     }
-    free(handles);
-    stream_.apiHandle = 0;
+    object->Release();
   }
-    
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  else {
+    DSCAPS caps;
+    LPDIRECTSOUND object;
+    hr = DirectSoundCreate(  lpguid, &object,   NULL );
+    if ( hr != DS_OK ) return true;
+
+    caps.dwSize = sizeof(caps);
+    hr = object->GetCaps( &caps );
+    if ( hr == DS_OK ) {
+      if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
+        info->counter++;
+    }
+    object->Release();
   }
 
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
-    stream_.deviceBuffer = 0;
+  if ( info->getDefault && lpguid == NULL ) return false;
+
+  if ( info->findIndex && info->counter > info->index ) {
+    info->id = lpguid;
+    info->name = convertTChar( description );
+    return false;
   }
 
-  stream_.mode = UNINITIALIZED;
+  return true;
 }
 
-void RtApiDs :: startStream()
+static char* getErrorString( int code )
 {
-  verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+       switch (code) {
 
-  // Increase scheduler frequency on lesser windows (a side-effect of
-  // increasing timer accuracy).  On greater windows (Win2K or later),
-  // this is already in effect.
+  case DSERR_ALLOCATED:
+    return "Already allocated";
 
-  MUTEX_LOCK(&stream_.mutex);
-  
-  DsHandle *handles = (DsHandle *) stream_.apiHandle;
+  case DSERR_CONTROLUNAVAIL:
+    return "Control unavailable";
 
-  timeBeginPeriod(1); 
+  case DSERR_INVALIDPARAM:
+    return "Invalid parameter";
 
-  memset(&statistics,0,sizeof(statistics));
-  statistics.sampleRate = stream_.sampleRate;
-  statistics.writeDeviceBufferLeadBytes = handles[0].dsPointerLeadTime ;
+  case DSERR_INVALIDCALL:
+    return "Invalid call";
 
-  buffersRolling = false;
-  duplexPrerollBytes = 0;
+  case DSERR_GENERIC:
+    return "Generic error";
 
-  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]);
-  }
+  case DSERR_PRIOLEVELNEEDED:
+    return "Priority level needed";
 
-#ifdef GENERATE_DEBUG_LOG
-  currentDebugLogEntry = 0;
-#endif  
+  case DSERR_OUTOFMEMORY:
+    return "Out of memory";
 
-  HRESULT result;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-      statistics.outputFrameSize = formatBytes( stream_.deviceFormat[0])
-                                  *stream_.nDeviceChannels[0];
+  case DSERR_BADFORMAT:
+    return "The sample rate or the channel format is not supported";
 
-    LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to start buffer (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-  }
+  case DSERR_UNSUPPORTED:
+    return "Not supported";
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    statistics.inputFrameSize = formatBytes( stream_.deviceFormat[1])
-                                  *stream_.nDeviceChannels[1];
+  case DSERR_NODRIVER:
+    return "No driver";
 
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    result = buffer->Start(DSCBSTART_LOOPING );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to start capture buffer (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-  }
-  stream_.state = STREAM_RUNNING;
+  case DSERR_ALREADYINITIALIZED:
+    return "Already initialized";
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+  case DSERR_NOAGGREGATION:
+    return "No aggregation";
 
-void RtApiDs :: stopStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+  case DSERR_BUFFERLOST:
+    return "Buffer lost";
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+  case DSERR_OTHERAPPHASPRIO:
+    return "Another application already has priority";
 
-  timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows.
+  case DSERR_UNINITIALIZED:
+    return "Uninitialized";
 
-#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;
-  }
+  default:
+    return "DirectSound unknown error";
+       }
+}
+//******************** End of __WINDOWS_DS__ *********************//
 #endif
 
-  // There is no specific DirectSound API call to "drain" a buffer
-  // before stopping.  We can hack this for playback by writing
-  // buffers of zeroes over the entire buffer.  For capture, the
-  // concept is less clear so we'll repeat what we do in the
-  // abortStream() case.
-  HRESULT result;
-  DWORD dsBufferSize;
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
-  DsHandle *handles = (DsHandle *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
 
-    DWORD currentPos, safePos;
-    long buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
+#if defined(__LINUX_ALSA__)
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    DWORD nextWritePos = handles[0].bufferPointer;
-    dsBufferSize = handles[0].dsBufferSize;
-    DWORD dsBytesWritten = 0;
+#include <alsa/asoundlib.h>
+#include <unistd.h>
 
-    // Write zeroes for at least dsBufferSize bytes. 
-    while ( dsBytesWritten < dsBufferSize ) {
+// A structure to hold various information related to the ALSA API
+// implementation.
+struct AlsaHandle {
+  snd_pcm_t *handles[2];
+  bool synchronized;
+  bool xrun[2];
 
-      // 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);
-      }
+  AlsaHandle()
+    :synchronized(false) { xrun[0] = false; xrun[1] = false; }
+};
 
-      // Chase nextWritePosition.
-      if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      DWORD endWrite = nextWritePos + buffer_bytes;
+extern "C" void *alsaCallbackHandler( void * ptr );
 
-      // 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_.nDeviceChannels[0] *stream_.sampleRate);
-        if ( millis < 1.0 ) millis = 1.0;
-        Sleep( (DWORD) millis );
+RtApiAlsa :: RtApiAlsa()
+{
+  // Nothing to do here.
+}
 
-        // 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);
-        }
+RtApiAlsa :: ~RtApiAlsa()
+{
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
+}
 
-        if ( currentPos < (DWORD)nextWritePos ) currentPos += dsBufferSize; // unwrap offset
-      }
+unsigned int RtApiAlsa :: getDeviceCount( void )
+{
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *handle;
 
-      // Lock free space in the buffer
-      result = dsBuffer->Lock( nextWritePos, buffer_bytes, &buffer1,
-                               &bufferSize1, &buffer2, &bufferSize2, 0);
-      if ( FAILED(result) ) {
-        sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.",
-                devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-        error(RtError::DRIVER_ERROR);
+  // Count cards and devices
+  card = -1;
+  snd_card_next( &card );
+  while ( card >= 0 ) {
+    sprintf( name, "hw:%d", card );
+    result = snd_ctl_open( &handle, name, 0 );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      error( WARNING );
+      goto nextcard;
+               }
+               subdevice = -1;
+               while( 1 ) {
+      result = snd_ctl_pcm_next_device( handle, &subdevice );
+                       if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        error( WARNING );
+        break;
       }
+                       if ( subdevice < 0 )
+        break;
+      nDevices++;
+    }
+  nextcard:
+    snd_ctl_close( handle );
+    snd_card_next( &card );
+  }
 
-      // Zero the free space
-      ZeroMemory( buffer1, bufferSize1 );
-      if (buffer2 != NULL) ZeroMemory( buffer2, bufferSize2 );
+  return nDevices;
+}
 
-      // Update our buffer offset and unlock sound buffer
-      dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
-      if ( FAILED(result) ) {
-        sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.",
-                devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-        error(RtError::DRIVER_ERROR);
-      }
-      nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
-      handles[0].bufferPointer = nextWritePos;
-      dsBytesWritten += buffer_bytes;
-    }
+RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
+{
+  RtAudio::DeviceInfo info;
+  info.probed = false;
+
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *chandle;
 
-    // OK, now stop the buffer.
-    result = dsBuffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to stop buffer (%s): %s",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  // Count cards and devices
+  card = -1;
+  snd_card_next( &card );
+  while ( card >= 0 ) {
+    sprintf( name, "hw:%d", card );
+    result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      error( WARNING );
+      goto nextcard;
+               }
+               subdevice = -1;
+               while( 1 ) {
+      result = snd_ctl_pcm_next_device( chandle, &subdevice );
+                       if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        error( WARNING );
+        break;
+      }
+                       if ( subdevice < 0 ) break;
+      if ( nDevices == device ) {
+        sprintf( name, "hw:%d,%d", card, subdevice );
+        goto foundDevice;
+      }
+      nDevices++;
     }
+  nextcard:
+    snd_ctl_close( chandle );
+    snd_card_next( &card );
+  }
 
-    // If we play again, start at the beginning of the buffer.
-    handles[0].bufferPointer = 0;
+  if ( nDevices == 0 ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
+    error( INVALID_CALL );
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if ( device >= nDevices ) {
+    errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
+    error( INVALID_CALL );
+  }
 
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    buffer1 = NULL;
-    bufferSize1 = 0;
+ foundDevice:
 
-    result = buffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  int openMode = SND_PCM_ASYNC;
+  snd_pcm_stream_t stream;
+       snd_pcm_info_t *pcminfo;
+       snd_pcm_info_alloca( &pcminfo );
+  snd_pcm_t *phandle;
+  snd_pcm_hw_params_t *params;
+  snd_pcm_hw_params_alloca( &params );
 
-    dsBufferSize = handles[1].dsBufferSize;
+  // First try for playback
+  stream = SND_PCM_STREAM_PLAYBACK;
+  snd_pcm_info_set_device( pcminfo, subdevice );
+  snd_pcm_info_set_subdevice( pcminfo, 0 );
+  snd_pcm_info_set_stream( pcminfo, stream );
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  result = snd_ctl_pcm_info( chandle, pcminfo );
+  if ( result < 0 ) {
+    // Device probably doesn't support playback.
+    goto captureProbe;
+  }
 
-    // Zero the DS buffer
-    ZeroMemory(buffer1, bufferSize1);
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    goto captureProbe;
+  }
 
-    // Unlock the DS buffer
-    result = buffer->Unlock(buffer1, bufferSize1, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    goto captureProbe;
+  }
 
-    // If we start recording again, we must begin at beginning of buffer.
-    handles[1].bufferPointer = 0;
+  // Get output channel information.
+  unsigned int value;
+  result = snd_pcm_hw_params_get_channels_max( params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    goto captureProbe;
   }
+  info.outputChannels = value;
+  snd_pcm_close( phandle );
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+ captureProbe:
+  // Now try for capture
+  stream = SND_PCM_STREAM_CAPTURE;
+  snd_pcm_info_set_stream( pcminfo, stream );
 
-void RtApiDs :: abortStream()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+  result = snd_ctl_pcm_info( chandle, pcminfo );
+  snd_ctl_close( chandle );
+  if ( result < 0 ) {
+    // Device probably doesn't support capture.
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
 
-  // Change the state before the lock to improve shutdown response
-  // when using a callback.
-  stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
 
-  timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows.
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
 
-  HRESULT result;
-  long dsBufferSize;
-  LPVOID audioPtr;
-  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) ) {
-      sprintf(message_, "RtApiDs: Unable to stop buffer (%s): %s",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  result = snd_pcm_hw_params_get_channels_max( params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    if ( info.outputChannels == 0 ) return info;
+    goto probeParameters;
+  }
+  info.inputChannels = value;
+  snd_pcm_close( phandle );
 
-    dsBufferSize = handles[0].dsBufferSize;
+  // If device opens for both playback and capture, we determine the channels.
+  if ( info.outputChannels > 0 && info.inputChannels > 0 )
+    info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
 
-    // Lock the buffer and clear it so that if we start to play again,
-    // we won't have old data playing.
-    result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to lock buffer (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  // ALSA doesn't provide default devices so we'll use the first available one.
+  if ( device == 0 && info.outputChannels > 0 )
+    info.isDefaultOutput = true;
+  if ( device == 0 && info.inputChannels > 0 )
+    info.isDefaultInput = true;
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+ probeParameters:
+  // At this point, we just need to figure out the supported data
+  // formats and sample rates.  We'll proceed by opening the device in
+  // the direction with the maximum number of channels, or playback if
+  // they are equal.  This might limit our sample rate options, but so
+  // be it.
 
-    // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to unlock buffer (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  if ( info.outputChannels >= info.inputChannels )
+    stream = SND_PCM_STREAM_PLAYBACK;
+  else
+    stream = SND_PCM_STREAM_CAPTURE;
+  snd_pcm_info_set_stream( pcminfo, stream );
 
-    // If we start playing again, we must begin at beginning of buffer.
-    handles[0].bufferPointer = 0;
+  result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
+  if ( result < 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    audioPtr = NULL;
-    dataLen = 0;
-
-    result = buffer->Stop();
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to stop capture buffer (%s): %s",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
-
-    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.
-    result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to lock capture buffer (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  // The device is open ... fill the parameter structure.
+  result = snd_pcm_hw_params_any( phandle, params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
 
-    // Zero the DS buffer
-    ZeroMemory(audioPtr, dataLen);
+  // Test our discrete set of sample rate values.
+  info.sampleRates.clear();
+  for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
+    if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 )
+      info.sampleRates.push_back( SAMPLE_RATES[i] );
+  }
+  if ( info.sampleRates.size() == 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
 
-    // Unlock the DS buffer
-    result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to unlock capture buffer (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
-    }
+  // Probe the supported data formats ... we don't care about endian-ness just yet
+  snd_pcm_format_t format;
+  info.nativeFormats = 0;
+  format = SND_PCM_FORMAT_S8;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT8;
+  format = SND_PCM_FORMAT_S16;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  format = SND_PCM_FORMAT_S24;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT24;
+  format = SND_PCM_FORMAT_S32;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_SINT32;
+  format = SND_PCM_FORMAT_FLOAT;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  format = SND_PCM_FORMAT_FLOAT64;
+  if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
+    info.nativeFormats |= RTAUDIO_FLOAT64;
 
-    // If we start recording again, we must begin at beginning of buffer.
-    handles[1].bufferPointer = 0;
+  // Check that we have at least one supported format
+  if ( info.nativeFormats == 0 ) {
+    errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
   }
 
-  MUTEX_UNLOCK(&stream_.mutex);
-}
+  // Get the device name
+  char *cardname;
+  result = snd_card_get_name( card, &cardname );
+  if ( result >= 0 )
+    sprintf( name, "hw:%s,%d", cardname, subdevice );
+  info.name = name;
 
-int RtApiDs :: streamWillBlock()
-{
-  verifyStream();
-  if (stream_.state == STREAM_STOPPED) return 0;
+  // That's all ... close the device and return
+  snd_pcm_close( phandle );
+  info.probed = true;
+  return info;
+}
 
-  MUTEX_LOCK(&stream_.mutex);
+bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                   RtAudioFormat format, unsigned int *bufferSize,
+                                   RtAudio::StreamOptions *options )
 
-  int channels;
-  int frames = 0;
-  HRESULT result;
-  DWORD currentPos, safePos;
-  channels = 1;
-  DsHandle *handles = (DsHandle *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+{
+#if defined(__RTAUDIO_DEBUG__)
+  snd_output_t *out;
+  snd_output_stdio_attach(&out, stderr, 0);
+#endif
 
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    UINT nextWritePos = handles[0].bufferPointer;
-    channels = stream_.nDeviceChannels[0];
-    DWORD dsBufferSize = handles[0].dsBufferSize;
+  // I'm not using the "plug" interface ... too much inconsistent behavior.
 
-    // 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);
-    }
+  unsigned nDevices = 0;
+  int result, subdevice, card;
+  char name[64];
+  snd_ctl_t *chandle;
 
-    DWORD leadPos = safePos + handles[0].dsPointerLeadTime;
-    if (leadPos > dsBufferSize) {
-      leadPos -= dsBufferSize;
+  // Count cards and devices
+  card = -1;
+  snd_card_next( &card );
+  while ( card >= 0 ) {
+    sprintf( name, "hw:%d", card );
+    result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      return FAILURE;
+               }
+               subdevice = -1;
+               while( 1 ) {
+      result = snd_ctl_pcm_next_device( chandle, &subdevice );
+                       if ( result < 0 ) break;
+                       if ( subdevice < 0 ) break;
+      if ( nDevices == device ) {
+        sprintf( name, "hw:%d,%d", card, subdevice );
+        goto foundDevice;
+      }
+      nDevices++;
     }
-    if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
-
-    frames = (leadPos - nextWritePos);
-    frames /= channels * formatBytes(stream_.deviceFormat[0]);
+    snd_ctl_close( chandle );
+    snd_card_next( &card );
   }
 
-  if (stream_.mode == INPUT ) {
-      // note that we don't block on DUPLEX input anymore. We run lockstep with the write pointer instead.
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
+    return FAILURE;
+  }
 
-    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    UINT nextReadPos = handles[1].bufferPointer;
-    channels = stream_.nDeviceChannels[1];
-    DWORD dsBufferSize = handles[1].dsBufferSize;
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
+  }
 
-    // Find out where the write and "safe read" pointers are.
-    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);
-    }
+ foundDevice:
 
-    if ( safePos < (DWORD)nextReadPos ) safePos += dsBufferSize; // unwrap offset
+  snd_pcm_stream_t stream;
+  if ( mode == OUTPUT )
+    stream = SND_PCM_STREAM_PLAYBACK;
+  else
+    stream = SND_PCM_STREAM_CAPTURE;
 
-    frames = (int)(safePos - nextReadPos);
-    frames /= channels * formatBytes(stream_.deviceFormat[1]);
+  snd_pcm_t *phandle;
+  int openMode = SND_PCM_ASYNC;
+  result = snd_pcm_open( &phandle, name, stream, openMode );
+  if ( result < 0 ) {
+    if ( mode == OUTPUT )
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
+    else
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  frames = stream_.bufferSize - frames;
-  if (frames < 0) frames = 0;
-
-  MUTEX_UNLOCK(&stream_.mutex);
-  return frames;
-}
+  // Fill the parameter structure.
+  snd_pcm_hw_params_t *hw_params;
+  snd_pcm_hw_params_alloca( &hw_params );
+  result = snd_pcm_hw_params_any( phandle, hw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-void RtApiDs :: tickStream()
-{
-  verifyStream();
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
 
-  int stopStream = 0;
-  if (stream_.state == STREAM_STOPPED) {
-    if (stream_.callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds
-    return;
+  // Set access ... check user preference.
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
+    stream_.userInterleaved = false;
+    result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
+    if ( result < 0 ) {
+      result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
+      stream_.deviceInterleaved[mode] =  true;
+    }
+    else
+      stream_.deviceInterleaved[mode] = false;
   }
-  else if (stream_.callbackInfo.usingCallback) {
-    RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
-    stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData);
+  else {
+    stream_.userInterleaved = true;
+    result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
+    if ( result < 0 ) {
+      result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
+      stream_.deviceInterleaved[mode] =  false;
+    }
+    else
+      stream_.deviceInterleaved[mode] =  true;
   }
 
-  MUTEX_LOCK(&stream_.mutex);
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  // The state might change while waiting on a mutex.
-  if (stream_.state == STREAM_STOPPED) {
-    MUTEX_UNLOCK(&stream_.mutex);
-    return;
+  // Determine how to set the device format.
+  stream_.userFormat = format;
+  snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
+
+  if ( format == RTAUDIO_SINT8 )
+    deviceFormat = SND_PCM_FORMAT_S8;
+  else if ( format == RTAUDIO_SINT16 )
+    deviceFormat = SND_PCM_FORMAT_S16;
+  else if ( format == RTAUDIO_SINT24 )
+    deviceFormat = SND_PCM_FORMAT_S24;
+  else if ( format == RTAUDIO_SINT32 )
+    deviceFormat = SND_PCM_FORMAT_S32;
+  else if ( format == RTAUDIO_FLOAT32 )
+    deviceFormat = SND_PCM_FORMAT_FLOAT;
+  else if ( format == RTAUDIO_FLOAT64 )
+    deviceFormat = SND_PCM_FORMAT_FLOAT64;
+
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
+    stream_.deviceFormat[mode] = format;
+    goto setFormat;
   }
 
-  HRESULT result;
-  DWORD currentWritePos, safeWritePos;
-  DWORD currentReadPos, safeReadPos;
-  DWORD leadPos;
-  UINT nextWritePos;
+  // The user requested format is not natively supported by the device.
+  deviceFormat = SND_PCM_FORMAT_FLOAT64;
+  if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
+    goto setFormat;
+  }
 
-#ifdef GENERATE_DEBUG_LOG
-  DWORD writeTime, readTime;
-#endif
+  deviceFormat = SND_PCM_FORMAT_FLOAT;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+    goto setFormat;
+  }
 
-  LPVOID buffer1 = NULL;
-  LPVOID buffer2 = NULL;
-  DWORD bufferSize1 = 0;
-  DWORD bufferSize2 = 0;
+  deviceFormat = SND_PCM_FORMAT_S32;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    goto setFormat;
+  }
 
-  char *buffer;
-  long buffer_bytes;
-  DsHandle *handles = (DsHandle *) stream_.apiHandle;
+  deviceFormat = SND_PCM_FORMAT_S24;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    goto setFormat;
+  }
 
-  if (stream_.mode == DUPLEX && !buffersRolling) {
-    assert(handles[0].dsBufferSize == handles[1].dsBufferSize);
+  deviceFormat = SND_PCM_FORMAT_S16;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    goto setFormat;
+  }
 
-    // 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.
+  deviceFormat = SND_PCM_FORMAT_S8;
+  if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
+    stream_.deviceFormat[mode] = RTAUDIO_SINT8;
+    goto setFormat;
+  }
 
-    // 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.
+  // If we get here, no supported format was found.
+  errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
+  errorText_ = errorStream_.str();
+  return FAILURE;
 
-    LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
-    LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
+ setFormat:
+  result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-    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);
+  // Determine whether byte-swaping is necessary.
+  stream_.doByteSwap[mode] = false;
+  if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
+    result = snd_pcm_format_cpu_endian( deviceFormat );
+    if ( result == 0 )
+      stream_.doByteSwap[mode] = true;
+    else if (result < 0) {
+      snd_pcm_close( phandle );
+      errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      return FAILURE;
     }
+  }
 
-    assert( handles[0].dsBufferSize == handles[1].dsBufferSize );
-
-    buffersRolling = true;
-    handles[0].bufferPointer = (safeWritePos + handles[0].dsPointerLeadTime);
-    handles[1].bufferPointer = safeReadPos;
+  // Set the sample rate.
+  result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
+  // Determine the number of channels for this device.  We support a possible
+  // minimum device channel number > than the value requested by the user.
+  stream_.nUserChannels[mode] = channels;
+  unsigned int value;
+  result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
+  unsigned int deviceChannels = value;
+  if ( result < 0 || deviceChannels < channels + firstChannel ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
 
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    
-    LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handles[0].buffer;
+  result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  deviceChannels = value;
+  if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
+  stream_.nDeviceChannels[mode] = deviceChannels;
 
-    // Setup parameters and do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[0]) {
-      buffer = stream_.deviceBuffer;
-      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
-      buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[0];
-      buffer_bytes *= formatBytes(stream_.deviceFormat[0]);
-    }
-    else {
-      buffer = stream_.userBuffer;
-      buffer_bytes = stream_.bufferSize * stream_.nUserChannels[0];
-      buffer_bytes *= formatBytes(stream_.userFormat);
-    }
+  // Set the device channels.
+  result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-    // No byte swapping necessary in DirectSound implementation.
+  // Set the buffer number, which in ALSA is referred to as the "period".
+  int dir;
+  unsigned int periods = 0;
+  if ( options ) periods = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
+  // Even though the hardware might allow 1 buffer, it won't work reliably.
+  if ( periods < 2 ) periods = 2;
+  result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-    // 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);
+  // Set the buffer (or period) size.
+  snd_pcm_uframes_t periodSize = *bufferSize;
+  result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  *bufferSize = periodSize;
 
-    DWORD dsBufferSize = handles[0].dsBufferSize;
-         nextWritePos = handles[0].bufferPointer;
+  // If attempting to setup a duplex stream, the bufferSize parameter
+  // MUST be the same in both directions!
+  if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-    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);
-      }
+  stream_.bufferSize = *bufferSize;
 
-      leadPos = safeWritePos + handles[0].dsPointerLeadTime;
-      if ( leadPos > dsBufferSize ) leadPos -= dsBufferSize;
-      if ( leadPos < nextWritePos ) leadPos += dsBufferSize; // unwrap offset
-      endWrite = nextWritePos + buffer_bytes;
+  // Install the hardware configuration
+  result = snd_pcm_hw_params( phandle, hw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-      // Check whether the entire write region is behind the play pointer.
-      if ( leadPos >= endWrite ) break;
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
+  snd_pcm_hw_params_dump( hw_params, out );
+#endif
 
-      // If we are here, then we must wait until the play pointer gets
-      // beyond the write region.  The approach here is to use the
-      // Sleep() function to suspend operation until safePos catches
-      // up. Calculate number of milliseconds to wait as:
-      //   time = distance * (milliseconds/second) * fudgefactor /
-      //          ((bytes/sample) * (samples/second))
-      // A "fudgefactor" less than 1 is used because it was found
-      // that sleeping too long was MUCH worse than sleeping for
-      // several shorter periods.
-      double millis = (endWrite - leadPos) * 900.0;
-      millis /= ( formatBytes(stream_.deviceFormat[0]) *stream_.nDeviceChannels[0]* stream_.sampleRate);
-      if ( millis < 1.0 ) millis = 1.0;
-      if ( millis > 50.0 ) {
-        static int nOverruns = 0;
-        ++nOverruns;
-      }
-      Sleep( (DWORD) millis );
-    }
+  // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
+  snd_pcm_sw_params_t *sw_params = NULL;
+  snd_pcm_sw_params_alloca( &sw_params );
+  snd_pcm_sw_params_current( phandle, sw_params );
+  snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
+  snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, 0x7fffffff );
+  snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
+  snd_pcm_sw_params_set_silence_size( phandle, sw_params, INT_MAX );
+  result = snd_pcm_sw_params( phandle, sw_params );
+  if ( result < 0 ) {
+    snd_pcm_close( phandle );
+    errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-#ifdef GENERATE_DEBUG_LOG
-    writeTime = timeGetTime();
+#if defined(__RTAUDIO_DEBUG__)
+  fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
+  snd_pcm_sw_params_dump( sw_params, out );
 #endif
 
-    if (statistics.writeDeviceSafeLeadBytes < dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize)) {
-      statistics.writeDeviceSafeLeadBytes = dsPointerDifference(safeWritePos,currentWritePos,handles[0].dsBufferSize);
-    }
+  // Set flags for buffer conversion
+  stream_.doConvertBuffer[mode] = false;
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
+    stream_.doConvertBuffer[mode] = true;
 
-    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 );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to lock buffer during playback (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  // Allocate the ApiHandle if necessary and then save.
+  AlsaHandle *apiInfo = 0;
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      apiInfo = (AlsaHandle *) new AlsaHandle;
     }
-
-    // Copy our buffer into the DS buffer
-    CopyMemory(buffer1, buffer, bufferSize1);
-    if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2);
-
-    // Update our buffer offset and unlock sound buffer
-    dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to unlock buffer during playback (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
+      goto error;
     }
-    nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
-    handles[0].bufferPointer = nextWritePos;
+    stream_.apiHandle = (void *) apiInfo;
+    apiInfo->handles[0] = 0;
+    apiInfo->handles[1] = 0;
   }
+  else {
+    apiInfo = (AlsaHandle *) stream_.apiHandle;
+  }
+  apiInfo->handles[mode] = phandle;
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-
-    // Setup parameters.
-    if (stream_.doConvertBuffer[1]) {
-      buffer = stream_.deviceBuffer;
-      buffer_bytes = stream_.bufferSize * stream_.nDeviceChannels[1];
-      buffer_bytes *= formatBytes(stream_.deviceFormat[1]);
-    }
-    else {
-      buffer = stream_.userBuffer;
-      buffer_bytes = stream_.bufferSize * stream_.nUserChannels[1];
-      buffer_bytes *= formatBytes(stream_.userFormat);
-    }
-    LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handles[1].buffer;
-    long nextReadPos = handles[1].bufferPointer;
-    DWORD dsBufferSize = handles[1].dsBufferSize;
-
-    // Find out where the write and "safe read" pointers are.
-    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 ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
-    DWORD endRead = nextReadPos + buffer_bytes;
-
-    // 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;
-
-          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 ( safeReadPos < (DWORD)nextReadPos ) safeReadPos += dsBufferSize; // unwrap offset
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
+  }
+
+  if ( stream_.doConvertBuffer[mode] ) {
+
+    bool makeBuffer = true;
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
+      if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
-#ifdef GENERATE_DEBUG_LOG
-    readTime = timeGetTime();
-#endif
-    if (statistics.readDeviceSafeLeadBytes < dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize))
-    {
-      statistics.readDeviceSafeLeadBytes = dsPointerDifference(currentReadPos,nextReadPos ,dsBufferSize);
+
+    if ( makeBuffer ) {
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
+        goto error;
+      }
     }
+  }
 
-    // Lock free space in the buffer
-    result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1,
-                             &bufferSize1, &buffer2, &bufferSize2, 0);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to lock buffer during capture (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  stream_.sampleRate = sampleRate;
+  stream_.nBuffers = periods;
+  stream_.device[mode] = device;
+  stream_.state = STREAM_STOPPED;
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup thread if necessary.
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
+    // We had already set up an output stream.
+    stream_.mode = DUPLEX;
+    // Link the streams if possible.
+    apiInfo->synchronized = false;
+    if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
+      apiInfo->synchronized = true;
+    else {
+      errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
+      error( WARNING );
     }
+  }
+  else {
+    stream_.mode = mode;
 
-    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;
+    // Setup callback thread.
+    stream_.callbackInfo.object = (void *) this;
+
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority.  The higher priority will only take affect if the
+    // program is run as root or suid.
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
+
+    stream_.callbackInfo.isRunning = true;
+    result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
+    pthread_attr_destroy( &attr );
+    if ( result ) {
+      stream_.callbackInfo.isRunning = false;
+      errorText_ = "RtApiAlsa::error creating callback thread!";
+      goto error;
     }
+  }
 
-    // Update our buffer offset and unlock sound buffer
-    nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize;
-    dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
-    if ( FAILED(result) ) {
-      sprintf(message_, "RtApiDs: Unable to unlock buffer during capture (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), getErrorString(result));
-      error(RtError::DRIVER_ERROR);
+  return SUCCESS;
+
+ error:
+  if ( apiInfo ) {
+    if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
+    if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
+    delete apiInfo;
+    stream_.apiHandle = 0;
+  }
+
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
     }
-    handles[1].bufferPointer = nextReadPos;
+  }
 
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
-    // No byte swapping necessary in DirectSound implementation.
+  return FAILURE;
+}
 
-    // 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);
+void RtApiAlsa :: closeStream()
+{
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
+    error( WARNING );
+    return;
+  }
 
-    // Do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[1])
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+  stream_.callbackInfo.isRunning = false;
+  pthread_join( stream_.callbackInfo.thread, NULL );
+
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_RUNNING ) {
+    stream_.state = STREAM_STOPPED;
+    if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+      snd_pcm_drop( apiInfo->handles[0] );
+    if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
+      snd_pcm_drop( apiInfo->handles[1] );
   }
-#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;
+
+  if ( apiInfo ) {
+    if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
+    if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
+    delete apiInfo;
+    stream_.apiHandle = 0;
   }
-#endif
 
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
+  }
 
-  if (stream_.callbackInfo.usingCallback && stopStream)
-    this->stopStream();
+  stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
-// Definitions for utility functions and callbacks
-// specific to the DirectSound implementation.
 
-extern "C" unsigned __stdcall callbackHandler(void *ptr)
+void RtApiAlsa :: startStream()
 {
-  CallbackInfo *info = (CallbackInfo *) ptr;
-  RtApiDs *object = (RtApiDs *) info->object;
-  bool *usingCallback = &info->usingCallback;
+  // This method calls snd_pcm_prepare if the device isn't already in that state.
 
-  while ( *usingCallback ) {
-    try {
-      object->tickStream();
+  verifyStream();
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
+    error( WARNING );
+    return;
+  }
+
+  MUTEX_LOCK( &stream_.mutex );
+
+  int result = 0;
+  snd_pcm_state_t state;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    state = snd_pcm_state( handle[0] );
+    if ( state != SND_PCM_STATE_PREPARED ) {
+      result = snd_pcm_prepare( handle[0] );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        goto unlock;
+      }
     }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtApiDs: callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessageString());
-      break;
+  }
+
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    state = snd_pcm_state( handle[1] );
+    if ( state != SND_PCM_STATE_PREPARED ) {
+      result = snd_pcm_prepare( handle[1] );
+      if ( result < 0 ) {
+        errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+        goto unlock;
+      }
     }
   }
 
-  _endthreadex( 0 );
-  return 0;
+  stream_.state = STREAM_RUNNING;
+
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  if ( result >= 0 ) return;
+  error( SYSTEM );
 }
 
-static bool CALLBACK deviceCountCallback(LPGUID lpguid,
-                                         LPCTSTR description,
-                                         LPCTSTR module,
-                                         LPVOID lpContext)
+void RtApiAlsa :: stopStream()
 {
-  int *pointer = ((int *) lpContext);
-  (*pointer)++;
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
 
-  return true;
-}
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
-#include "tchar.h"
+  int result = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    if ( apiInfo->synchronized ) 
+      result = snd_pcm_drop( handle[0] );
+    else
+      result = snd_pcm_drain( handle[0] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
 
-std::string convertTChar( LPCTSTR name )
-{
-  std::string s;
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    result = snd_pcm_drop( handle[1] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+  }
 
-#if defined( UNICODE ) || defined( _UNICODE )
-  // Yes, this conversion doesn't make sense for two-byte characters
-  // but RtAudio is currently written to return an std::string of
-  // one-byte chars for the device name.
-  for ( unsigned int i=0; i<wcslen( name ); i++ )
-    s.push_back( name[i] );
-#else
-  s.append( std::string( name ) );
-#endif
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  return s;
+  if ( result >= 0 ) return;
+  error( SYSTEM );
 }
 
-static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
-                                        LPCTSTR description,
-                                        LPCTSTR module,
-                                        LPVOID lpContext)
+void RtApiAlsa :: abortStream()
 {
-  enum_info *info = ((enum_info *) lpContext);
-  while ( !info->name.empty() ) info++;
-
-  info->name = convertTChar( description );
-  info->id = lpguid;
-
-  HRESULT hr;
-  info->isValid = false;
-  if (info->isInput == true) {
-    DSCCAPS caps;
-    LPDIRECTSOUNDCAPTURE object;
+  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
 
-    hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
-    if( hr != DS_OK ) return true;
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
-    caps.dwSize = sizeof(caps);
-    hr = object->GetCaps( &caps );
-    if( hr == DS_OK ) {
-      if (caps.dwChannels > 0 && caps.dwFormats > 0)
-        info->isValid = true;
+  int result = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = snd_pcm_drop( handle[0] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    object->Release();
   }
-  else {
-    DSCAPS caps;
-    LPDIRECTSOUND object;
-    hr = DirectSoundCreate(  lpguid, &object,   NULL );
-    if( hr != DS_OK ) return true;
 
-    caps.dwSize = sizeof(caps);
-    hr = object->GetCaps( &caps );
-    if( hr == DS_OK ) {
-      if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
-        info->isValid = true;
+  if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
+    result = snd_pcm_drop( handle[1] );
+    if ( result < 0 ) {
+      errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    object->Release();
   }
 
-  return true;
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
+
+  stream_.state = STREAM_STOPPED;
+  if ( result >= 0 ) return;
+  error( SYSTEM );
 }
 
-static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
-                                           LPCTSTR description,
-                                           LPCTSTR module,
-                                           LPVOID lpContext)
+void RtApiAlsa :: callbackEvent()
 {
-  enum_info *info = ((enum_info *) lpContext);
+  if ( stream_.state == STREAM_STOPPED ) {
+    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
+    return;
+  }
 
-  if ( lpguid == NULL ) {
-    info->name = convertTChar( description );
-    return false;
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
+    return;
   }
 
-  return true;
-}
+  int doStopStream = 0;
+  AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    apiInfo->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    apiInfo->xrun[1] = false;
+  }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                         stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
 
-static bool CALLBACK deviceIdCallback(LPGUID lpguid,
-                                      LPCTSTR description,
-                                      LPCTSTR module,
-                                      LPVOID lpContext)
-{
-  enum_info *info = ((enum_info *) lpContext);
+  MUTEX_LOCK( &stream_.mutex );
 
-  std::string s = convertTChar( description );
-  if ( info->name == s ) {
-    info->id = lpguid;
-    info->isValid = true;
-    return false;
-  }
+  // The state might change while waiting on a mutex.
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
-  return true;
-}
+  int result;
+  char *buffer;
+  int channels;
+  snd_pcm_t **handle;
+  snd_pcm_sframes_t frames;
+  RtAudioFormat format;
+  handle = (snd_pcm_t **) apiInfo->handles;
 
-static char* getErrorString(int code)
-{
-       switch (code) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
-  case DSERR_ALLOCATED:
-    return "Already allocated.";
+    // Setup parameters.
+    if ( stream_.doConvertBuffer[1] ) {
+      buffer = stream_.deviceBuffer;
+      channels = stream_.nDeviceChannels[1];
+      format = stream_.deviceFormat[1];
+    }
+    else {
+      buffer = stream_.userBuffer[1];
+      channels = stream_.nUserChannels[1];
+      format = stream_.userFormat;
+    }
 
-  case DSERR_CONTROLUNAVAIL:
-    return "Control unavailable.";
+    // Read samples from device in interleaved/non-interleaved format.
+    if ( stream_.deviceInterleaved[1] )
+      result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
+    else {
+      void *bufs[channels];
+      size_t offset = stream_.bufferSize * formatBytes( format );
+      for ( int i=0; i<channels; i++ )
+        bufs[i] = (void *) (buffer + (i * offset));
+      result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
+    }
 
-  case DSERR_INVALIDPARAM:
-    return "Invalid parameter.";
+    if ( result < (int) stream_.bufferSize ) {
+      // Either an error or underrun occured.
+      if ( result == -EPIPE ) {
+        snd_pcm_state_t state = snd_pcm_state( handle[1] );
+        if ( state == SND_PCM_STATE_XRUN ) {
+          apiInfo->xrun[1] = true;
+          result = snd_pcm_prepare( handle[1] );
+          if ( result < 0 ) {
+            errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
+            errorText_ = errorStream_.str();
+          }
+        }
+        else {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
+        }
+      }
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+      }
+      error( WARNING );
+      goto unlock;
+    }
 
-  case DSERR_INVALIDCALL:
-    return "Invalid call.";
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[1] )
+      byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
 
-  case DSERR_GENERIC:
-    return "Generic error.";
+    // Do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
 
-  case DSERR_PRIOLEVELNEEDED:
-    return "Priority level needed";
+    // Check stream latency
+    result = snd_pcm_delay( handle[1], &frames );
+    if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
+  }
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+
+    // Setup parameters and do buffer conversion if necessary.
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      channels = stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      channels = stream_.nUserChannels[0];
+      format = stream_.userFormat;
+    }
 
-  case DSERR_OUTOFMEMORY:
-    return "Out of memory";
+    // Do byte swapping if necessary.
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
 
-  case DSERR_BADFORMAT:
-    return "The sample rate or the channel format is not supported.";
+    // Write samples to device in interleaved/non-interleaved format.
+    if ( stream_.deviceInterleaved[0] )
+      result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
+    else {
+      void *bufs[channels];
+      size_t offset = stream_.bufferSize * formatBytes( format );
+      for ( int i=0; i<channels; i++ )
+        bufs[i] = (void *) (buffer + (i * offset));
+      result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
+    }
 
-  case DSERR_UNSUPPORTED:
-    return "Not supported.";
+    if ( result < (int) stream_.bufferSize ) {
+      // Either an error or underrun occured.
+      if ( result == -EPIPE ) {
+        snd_pcm_state_t state = snd_pcm_state( handle[0] );
+        if ( state == SND_PCM_STATE_XRUN ) {
+          apiInfo->xrun[0] = true;
+          result = snd_pcm_prepare( handle[0] );
+          if ( result < 0 ) {
+            errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
+            errorText_ = errorStream_.str();
+          }
+        }
+        else {
+          errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
+          errorText_ = errorStream_.str();
+        }
+      }
+      else {
+        errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
+        errorText_ = errorStream_.str();
+      }
+      error( WARNING );
+      goto unlock;
+    }
 
-  case DSERR_NODRIVER:
-    return "No driver.";
+    // Check stream latency
+    result = snd_pcm_delay( handle[0], &frames );
+    if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
+  }
 
 case DSERR_ALREADYINITIALIZED:
-    return "Already initialized.";
unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  case DSERR_NOAGGREGATION:
-    return "No aggregation.";
+  RtApi::tickStreamTime();
+  if ( doStopStream == 1 ) this->stopStream();
+  else if ( doStopStream == 2 ) this->abortStream();
+}
 
-  case DSERR_BUFFERLOST:
-    return "Buffer lost.";
+extern "C" void *alsaCallbackHandler( void *ptr )
+{
+  CallbackInfo *info = (CallbackInfo *) ptr;
+  RtApiAlsa *object = (RtApiAlsa *) info->object;
+  bool *isRunning = &info->isRunning;
 
-  case DSERR_OTHERAPPHASPRIO:
-    return "Another application already has priority.";
+#ifdef SCHED_RR
+  // Set a higher scheduler priority (P.J. Leonard)
+  struct sched_param param;
+  param.sched_priority = 39;   // Is this the best number?
+  sched_setscheduler( 0, SCHED_RR, &param );
+#endif
 
-  case DSERR_UNINITIALIZED:
-    return "Uninitialized.";
+  while ( *isRunning == true ) {
+    pthread_testcancel();
+    object->callbackEvent();
+  }
 
-  default:
-    return "DirectSound unknown error";
-       }
+  pthread_exit( NULL );
 }
 
-//******************** End of __WINDOWS_DS__ *********************//
+//******************** End of __LINUX_ALSA__ *********************//
 #endif
 
-#if defined(__IRIX_AL__) // SGI's AL API for IRIX
 
-#include <dmedia/audio.h>
+#if defined(__LINUX_OSS__)
+
+#include <unistd.h>
+#include <sys/ioctl.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include "oss/soundcard.h"
 #include <errno.h>
+#include <math.h>
+
+extern "C" void *ossCallbackHandler(void * ptr);
 
-extern "C" void *callbackHandler(void * ptr);
+// A structure to hold various information related to the OSS API
+// implementation.
+struct OssHandle {
+  int id[2];    // device ids
+  bool xrun[2];
+  bool triggered;
+
+  OssHandle()
+    :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
+};
 
-RtApiAl :: RtApiAl()
+RtApiOss :: RtApiOss()
 {
-  this->initialize();
+  // Nothing to do here.
+}
 
-  if (nDevices_ <= 0) {
-    sprintf(message_, "RtApiAl: no Irix AL audio devices found!");
-    error(RtError::NO_DEVICES_FOUND);
- }
+RtApiOss :: ~RtApiOss()
+{
+  if ( stream_.state != STREAM_CLOSED ) closeStream();
 }
 
-RtApiAl :: ~RtApiAl()
+unsigned int RtApiOss :: getDeviceCount( void )
 {
-  // The subclass destructor gets called before the base class
-  // destructor, so close any existing streams before deallocating
-  // apiDeviceId memory.
-  if ( stream_.mode != UNINITIALIZED ) closeStream();
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
+    error( WARNING );
+    return 0;
+  }
 
-  // Free our allocated apiDeviceId memory.
-  long *id;
-  for ( unsigned int i=0; i<devices_.size(); i++ ) {
-    id = (long *) devices_[i].apiDeviceId;
-    if (id) free(id);
+  oss_sysinfo sysinfo;
+  if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
+    error( WARNING );
+    return 0;
   }
+
+  return sysinfo.numaudios;
 }
 
-void RtApiAl :: initialize(void)
+RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
 {
-  // Count cards and devices
-  nDevices_ = 0;
+  RtAudio::DeviceInfo info;
+  info.probed = false;
 
-  // Determine the total number of input and output devices.
-  nDevices_ = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0);
-  if (nDevices_ < 0) {
-    sprintf(message_, "RtApiAl: error counting devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
+    error( WARNING );
+    return info;
   }
 
-  if (nDevices_ <= 0) return;
+  oss_sysinfo sysinfo;
+  int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
+  if ( result == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
+    error( WARNING );
+    return info;
+  }
 
-  ALvalue *vls = (ALvalue *) new ALvalue[nDevices_];
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
+    error( INVALID_CALL );
+  }
 
-  // Create our list of devices and write their ascii identifiers and resource ids.
-  char name[64];
-  int outs, ins, i;
-  ALpv pvs[1];
-  pvs[0].param = AL_NAME;
-  pvs[0].value.ptr = name;
-  pvs[0].sizeIn = 64;
-  RtApiDevice device;
-  long *id;
-
-  outs = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, vls, nDevices_, 0, 0);
-  if (outs < 0) {
-    delete [] vls;
-    sprintf(message_, "RtApiAl: error getting output devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  for (i=0; i<outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      delete [] vls;
-      sprintf(message_, "RtApiAl: error querying output devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
-    device.name.erase();
-    device.name.append( (const char *)name, strlen(name)+1);
-    devices_.push_back(device);
-    id = (long *) calloc(2, sizeof(long));
-    id[0] = vls[i].i;
-    devices_[i].apiDeviceId = (void *) id;
-  }
-
-  ins = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &vls[outs], nDevices_-outs, 0, 0);
-  if (ins < 0) {
-    delete [] vls;
-    sprintf(message_, "RtApiAl: error getting input devices: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DRIVER_ERROR);
-  }
-
-  for (i=outs; i<ins+outs; i++) {
-    if (alGetParams(vls[i].i, pvs, 1) < 0) {
-      delete [] vls;
-      sprintf(message_, "RtApiAl: error querying input devices: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
-    }
-    device.name.erase();
-    device.name.append( (const char *)name, strlen(name)+1);
-    devices_.push_back(device);
-    id = (long *) calloc(2, sizeof(long));
-    id[1] = vls[i].i;
-    devices_[i].apiDeviceId = (void *) id;
-  }
-
-  delete [] vls;
-}
+  if ( device >= nDevices ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
+    error( INVALID_CALL );
+  }
 
-int RtApiAl :: getDefaultInputDevice(void)
-{
-  ALvalue value;
-  long *id;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message_, "RtApiAl: error getting default input device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  oss_audioinfo ainfo;
+  ainfo.dev = device;
+  result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
+  close( mixerfd );
+  if ( result == -1 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
+
+  // Probe channels
+  if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
+  if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
+  if ( ainfo.caps & PCM_CAP_DUPLEX ) {
+    if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
+      info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
+  }
+
+  // Probe data formats ... do for input
+  unsigned long mask = ainfo.iformats;
+  if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
+    info.nativeFormats |= RTAUDIO_SINT16;
+  if ( mask & AFMT_S8 )
+    info.nativeFormats |= RTAUDIO_SINT8;
+  if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
+    info.nativeFormats |= RTAUDIO_SINT32;
+  if ( mask & AFMT_FLOAT )
+    info.nativeFormats |= RTAUDIO_FLOAT32;
+  if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
+    info.nativeFormats |= RTAUDIO_SINT24;
+
+  // Check that we have at least one supported format
+  if ( info.nativeFormats == 0 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    error( WARNING );
+    return info;
+  }
+
+  // Probe the supported sample rates.
+  info.sampleRates.clear();
+  if ( ainfo.nrates ) {
+    for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
+      for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+        if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
+          info.sampleRates.push_back( SAMPLE_RATES[k] );
+          break;
+        }
+      }
+    }
   }
   else {
-    for ( unsigned int i=0; i<devices_.size(); i++ ) {
-      id = (long *) devices_[i].apiDeviceId;
-      if ( id[1] == value.i ) return i;
+    // Check min and max rate values;
+    for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
+      if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] )
+        info.sampleRates.push_back( SAMPLE_RATES[k] );
     }
   }
 
-  return 0;
-}
-
-int RtApiAl :: getDefaultOutputDevice(void)
-{
-  ALvalue value;
-  long *id;
-  int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, &value, 1, 0, 0);
-  if (result < 0) {
-    sprintf(message_, "RtApiAl: error getting default output device id: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::WARNING);
+  if ( info.sampleRates.size() == 0 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    error( WARNING );
   }
   else {
-    for ( unsigned int i=0; i<devices_.size(); i++ ) {
-      id = (long *) devices_[i].apiDeviceId;
-      if ( id[0] == value.i ) return i;
-    }
+    info.probed = true;
+    info.name = ainfo.name;
   }
 
-  return 0;
+  return info;
 }
 
-void RtApiAl :: probeDeviceInfo(RtApiDevice *info)
+
+bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
+                                   unsigned int firstChannel, unsigned int sampleRate,
+                                  RtAudioFormat format, unsigned int *bufferSize,
+                                  RtAudio::StreamOptions *options )
 {
-  int result;
-  long resource;
-  ALvalue value;
-  ALparamInfo pinfo;
-
-  // Get output resource ID if it exists.
-  long *id = (long *) info->apiDeviceId;
-  resource = id[0];
-  if (resource > 0) {
-
-    // Probe output device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.",
-              info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      info->maxOutputChannels = value.i;
-      info->minOutputChannels = 1;
-    }
+  int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
+  if ( mixerfd == -1 ) {
+    errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
+    return FAILURE;
+  }
 
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.",
-              info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      info->sampleRates.clear();
-      for (unsigned int k=0; k<MAX_SAMPLE_RATES; k++) {
-        if ( SAMPLE_RATES[k] >= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i )
-          info->sampleRates.push_back( SAMPLE_RATES[k] );
-      }
-    }
+  oss_sysinfo sysinfo;
+  int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
+  if ( result == -1 ) {
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
+    return FAILURE;
+  }
 
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RtAudioFormat) 51;
+  unsigned nDevices = sysinfo.numaudios;
+  if ( nDevices == 0 ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
+    return FAILURE;
   }
 
-  // Now get input resource ID if it exists.
-  resource = id[1];
-  if (resource > 0) {
+  if ( device >= nDevices ) {
+    // This should not happen because a check is made before this function is called.
+    close( mixerfd );
+    errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
+    return FAILURE;
+  }
 
-    // Probe input device parameters.
-    result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
-    if (result < 0) {
-      sprintf(message_, "RtApiAl: error getting device (%s) channels: %s.",
-              info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      info->maxInputChannels = value.i;
-      info->minInputChannels = 1;
-    }
+  oss_audioinfo ainfo;
+  ainfo.dev = device;
+  result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
+  close( mixerfd );
+  if ( result == -1 ) {
+    errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-    result = alGetParamInfo(resource, AL_RATE, &pinfo);
-    if (result < 0) {
-      sprintf(message_, "RtApiAl: error getting device (%s) rates: %s.",
-              info->name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-    }
-    else {
-      // In the case of the default device, these values will
-      // overwrite the rates determined for the output device.  Since
-      // the input device is most likely to be more limited than the
-      // output device, this is ok.
-      info->sampleRates.clear();
-      for (unsigned int k=0; k<MAX_SAMPLE_RATES; k++) {
-        if ( SAMPLE_RATES[k] >= pinfo.min.i && SAMPLE_RATES[k] <= pinfo.max.i )
-          info->sampleRates.push_back( SAMPLE_RATES[k] );
+  // Check if device supports input or output
+  if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
+       ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
+    if ( mode == OUTPUT )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  int flags = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( mode == OUTPUT )
+    flags |= O_WRONLY;
+  else { // mode == INPUT
+    if (stream_.mode == OUTPUT && stream_.device[0] == device) {
+      // We just set the same device for playback ... close and reopen for duplex (OSS only).
+      close( handle->id[0] );
+      handle->id[0] = 0;
+      if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
+        errorText_ = errorStream_.str();
+        return FAILURE;
       }
+      // Check that the number previously set channels is the same.
+      if ( stream_.nUserChannels[0] != channels ) {
+        errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
+        errorText_ = errorStream_.str();
+        return FAILURE;
+      }
+      flags |= O_RDWR;
     }
-
-    // The AL library supports all our formats, except 24-bit and 32-bit ints.
-    info->nativeFormats = (RtAudioFormat) 51;
+    else
+      flags |= O_RDONLY;
   }
 
-  if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
-    return;
-  if ( info->sampleRates.size() == 0 )
-    return;
-
-  // Determine duplex status.
-  if (info->maxInputChannels < info->maxOutputChannels)
-    info->maxDuplexChannels = info->maxInputChannels;
-  else
-    info->maxDuplexChannels = info->maxOutputChannels;
-  if (info->minInputChannels < info->minOutputChannels)
-    info->minDuplexChannels = info->minInputChannels;
-  else
-    info->minDuplexChannels = info->minOutputChannels;
-
-  if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
-  else info->hasDuplexSupport = false;
-
-  info->probed = true;
-
-  return;
-}
-
-bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels, 
-                                int sampleRate, RtAudioFormat format,
-                                int *bufferSize, int numberOfBuffers)
-{
-  int result, nBuffers;
-  long resource;
-  ALconfig al_config;
-  ALport port;
-  ALpv pvs[2];
-  long *id = (long *) devices_[device].apiDeviceId;
-
-  // Get a new ALconfig structure.
-  al_config = alNewConfig();
-  if ( !al_config ) {
-    sprintf(message_,"RtApiAl: can't get AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DEBUG_WARNING);
-    return FAILURE;
-  }
+  // Set exclusive access if specified.
+  if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
 
-  // Set the channels.
-  result = alSetChannels(al_config, channels);
-  if ( result < 0 ) {
-    alFreeConfig(al_config);
-    sprintf(message_,"RtApiAl: can't set %d channels in AL config: %s.",
-            channels, alGetErrorString(oserror()));
-    error(RtError::DEBUG_WARNING);
+  // Try to open the device.
+  int fd;
+  fd = open( ainfo.devnode, flags, 0 );
+  if ( fd == -1 ) {
+    if ( errno == EBUSY )
+      errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
+    else
+      errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
-  // Attempt to set the queue size.  The al API doesn't provide a
-  // means for querying the minimum/maximum buffer size of a device,
-  // so if the specified size doesn't work, take whatever the
-  // al_config structure returns.
-  if ( numberOfBuffers < 1 )
-    nBuffers = 1;
-  else
-    nBuffers = numberOfBuffers;
-  long buffer_size = *bufferSize * nBuffers;
-  result = alSetQueueSize(al_config, buffer_size); // in sample frames
-  if ( result < 0 ) {
-    // Get the buffer size specified by the al_config and try that.
-    buffer_size = alGetQueueSize(al_config);
-    result = alSetQueueSize(al_config, buffer_size);
-    if ( result < 0 ) {
-      alFreeConfig(al_config);
-      sprintf(message_,"RtApiAl: can't set buffer size (%ld) in AL config: %s.",
-              buffer_size, alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
+  // For duplex operation, specifically set this mode (this doesn't seem to work).
+  /*
+  if ( flags | O_RDWR ) {
+    result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
+    if ( result == -1) {
+      errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
+      errorText_ = errorStream_.str();
       return FAILURE;
     }
-    *bufferSize = buffer_size / nBuffers;
   }
+  */
 
-  // Set the data format.
-  stream_.userFormat = format;
-  stream_.deviceFormat[mode] = format;
-  if (format == RTAUDIO_SINT8) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_8);
-  }
-  else if (format == RTAUDIO_SINT16) {
-    result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
-    result = alSetWidth(al_config, AL_SAMPLE_16);
-  }
-  else if (format == RTAUDIO_SINT24) {
-    // Our 24-bit format assumes the upper 3 bytes of a 4 byte word.
-    // The AL library uses the lower 3 bytes, so we'll need to do our
-    // own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+  // Check the device channel support.
+  stream_.nUserChannels[mode] = channels;
+  if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-  else if (format == RTAUDIO_SINT32) {
-    // The AL library doesn't seem to support the 32-bit integer
-    // format, so we'll need to do our own conversion.
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-    stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
+
+  // Set the number of channels.
+  int deviceChannels = channels + firstChannel;
+  result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
+  if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
   }
-  else if (format == RTAUDIO_FLOAT32)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
-  else if (format == RTAUDIO_FLOAT64)
-    result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE);
+  stream_.nDeviceChannels[mode] = deviceChannels;
 
+  // Get the data format mask
+  int mask;
+  result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
   if ( result == -1 ) {
-    alFreeConfig(al_config);
-    sprintf(message_,"RtApiAl: error setting sample format in AL config: %s.",
-            alGetErrorString(oserror()));
-    error(RtError::DEBUG_WARNING);
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
+    errorText_ = errorStream_.str();
     return FAILURE;
   }
 
-  if (mode == OUTPUT) {
-
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_OUTPUT;
-    else
-      resource = id[0];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      alFreeConfig(al_config);
-      sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.",
-              devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+  // Determine how to set the device format.
+  stream_.userFormat = format;
+  int deviceFormat = -1;
+  stream_.doByteSwap[mode] = false;
+  if ( format == RTAUDIO_SINT8 ) {
+    if ( mask & AFMT_S8 ) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
-
-    // Open the port.
-    port = alOpenPort("RtApiAl Output Port", "w", al_config);
-    if( !port ) {
-      alFreeConfig(al_config);
-      sprintf(message_,"RtApiAl: error opening output port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+  }
+  else if ( format == RTAUDIO_SINT16 ) {
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
     }
-
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      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::DEBUG_WARNING);
-      return FAILURE;
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
     }
   }
-  else { // mode == INPUT
-
-    // Set our device.
-    if (device == 0)
-      resource = AL_DEFAULT_INPUT;
-    else
-      resource = id[1];
-    result = alSetDevice(al_config, resource);
-    if ( result == -1 ) {
-      alFreeConfig(al_config);
-      sprintf(message_,"RtApiAl: error setting device (%s) in AL config: %s.",
-              devices_[device].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+  else if ( format == RTAUDIO_SINT24 ) {
+    if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
     }
-
-    // Open the port.
-    port = alOpenPort("RtApiAl Input Port", "r", al_config);
-    if( !port ) {
-      alFreeConfig(al_config);
-      sprintf(message_,"RtApiAl: error opening input port: %s.",
-              alGetErrorString(oserror()));
-      error(RtError::DEBUG_WARNING);
-      return FAILURE;
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
     }
+  }
+  else if ( format == RTAUDIO_SINT32 ) {
+    if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    }
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
+    }
+  }
 
-    // Set the sample rate
-    pvs[0].param = AL_MASTER_CLOCK;
-    pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
-    pvs[1].param = AL_RATE;
-    pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
-    result = alSetParams(resource, pvs, 2);
-    if ( result < 0 ) {
-      alClosePort(port);
-      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::DEBUG_WARNING);
-      return FAILURE;
+  if ( deviceFormat == -1 ) {
+    // The user requested format is not natively supported by the device.
+    if ( mask & AFMT_S16_NE ) {
+      deviceFormat = AFMT_S16_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+    }
+    else if ( mask & AFMT_S32_NE ) {
+      deviceFormat = AFMT_S32_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+    }
+    else if ( mask & AFMT_S24_NE ) {
+      deviceFormat = AFMT_S24_NE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+    }
+    else if ( mask & AFMT_S16_OE ) {
+      deviceFormat = AFMT_S16_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT16;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S32_OE ) {
+      deviceFormat = AFMT_S32_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT32;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S24_OE ) {
+      deviceFormat = AFMT_S24_OE;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT24;
+      stream_.doByteSwap[mode] = true;
+    }
+    else if ( mask & AFMT_S8) {
+      deviceFormat = AFMT_S8;
+      stream_.deviceFormat[mode] = RTAUDIO_SINT8;
     }
   }
 
-  alFreeConfig(al_config);
+  if ( stream_.deviceFormat[mode] == 0 ) {
+    // This really shouldn't happen ...
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  stream_.nUserChannels[mode] = channels;
-  stream_.nDeviceChannels[mode] = channels;
+  // Set the data format.
+  int temp = deviceFormat;
+  result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
+  if ( result == -1 || deviceFormat != temp ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
 
-  // Save stream handle.
-  ALport *handle = (ALport *) stream_.apiHandle;
-  if ( handle == 0 ) {
-    handle = (ALport *) calloc(2, sizeof(ALport));
-    if ( handle == NULL ) {
-      sprintf(message_, "RtApiAl: Irix Al error allocating handle memory (%s).",
-              devices_[device].name.c_str());
-      goto error;
-    }
-    stream_.apiHandle = (void *) handle;
-    handle[0] = 0;
-    handle[1] = 0;
+  // Attempt to set the buffer size.  According to OSS, the minimum
+  // number of buffers is two.  The supposed minimum buffer size is 16
+  // bytes, so that will be our lower bound.  The argument to this
+  // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
+  // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
+  // We'll check the actual value used near the end of the setup
+  // procedure.
+  int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
+  if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
+  int buffers = 0;
+  if ( options ) buffers = options->numberOfBuffers;
+  if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
+  if ( buffers < 2 ) buffers = 3;
+  temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
+  result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.nBuffers = buffers;
+
+  // Save buffer size (in sample frames).
+  *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
+  stream_.bufferSize = *bufferSize;
+
+  // Set the sample rate.
+  int srate = sampleRate;
+  result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
+  if ( result == -1 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+
+  // Verify the sample rate setup worked.
+  if ( abs( srate - sampleRate ) > 100 ) {
+    close( fd );
+    errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
+    errorText_ = errorStream_.str();
+    return FAILURE;
+  }
+  stream_.sampleRate = sampleRate;
+
+  if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
+    // We're doing duplex setup here.
+    stream_.deviceFormat[0] = stream_.deviceFormat[1];
+    stream_.nDeviceChannels[0] = deviceChannels;
   }
-  handle[mode] = port;
+
+  // Set interleaving parameters.
+  stream_.userInterleaved = true;
+  stream_.deviceInterleaved[mode] =  true;
+  if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
+    stream_.userInterleaved = false;
 
   // Set flags for buffer conversion
   stream_.doConvertBuffer[mode] = false;
-  if (stream_.userFormat != stream_.deviceFormat[mode])
+  if ( stream_.userFormat != stream_.deviceFormat[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
+    stream_.doConvertBuffer[mode] = true;
+  if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
+       stream_.nUserChannels[mode] > 1 )
     stream_.doConvertBuffer[mode] = true;
 
-  // Allocate necessary internal buffers
-  if ( stream_.nUserChannels[0] != stream_.nUserChannels[1] ) {
-
-    long buffer_bytes;
-    if (stream_.nUserChannels[0] >= stream_.nUserChannels[1])
-      buffer_bytes = stream_.nUserChannels[0];
-    else
-      buffer_bytes = stream_.nUserChannels[1];
-
-    buffer_bytes *= *bufferSize * formatBytes(stream_.userFormat);
-    if (stream_.userBuffer) free(stream_.userBuffer);
-    stream_.userBuffer = (char *) calloc(buffer_bytes, 1);
-    if (stream_.userBuffer == NULL) {
-      sprintf(message_, "RtApiAl: error allocating user buffer memory (%s).",
-              devices_[device].name.c_str());
+  // Allocate the stream handles if necessary and then save.
+  if ( stream_.apiHandle == 0 ) {
+    try {
+      handle = new OssHandle;
+    }
+    catch ( std::bad_alloc& ) {
+      errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
       goto error;
     }
+
+    stream_.apiHandle = (void *) handle;
+  }
+  else {
+    handle = (OssHandle *) stream_.apiHandle;
+  }
+  handle->id[mode] = fd;
+
+  // Allocate necessary internal buffers.
+  unsigned long bufferBytes;
+  bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
+  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
+  if ( stream_.userBuffer[mode] == NULL ) {
+    errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
+    goto error;
   }
 
   if ( stream_.doConvertBuffer[mode] ) {
 
-    long buffer_bytes;
     bool makeBuffer = true;
-    if ( mode == OUTPUT )
-      buffer_bytes = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-    else { // mode == INPUT
-      buffer_bytes = stream_.nDeviceChannels[1] * formatBytes(stream_.deviceFormat[1]);
+    bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
+    if ( mode == INPUT ) {
       if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
-        long bytes_out = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
-        if ( buffer_bytes < bytes_out ) makeBuffer = false;
+        unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
+        if ( bufferBytes <= bytesOut ) makeBuffer = false;
       }
     }
 
     if ( makeBuffer ) {
-      buffer_bytes *= *bufferSize;
-      if (stream_.deviceBuffer) free(stream_.deviceBuffer);
-      stream_.deviceBuffer = (char *) calloc(buffer_bytes, 1);
-      if (stream_.deviceBuffer == NULL) {
-        sprintf(message_, "RtApiAl: error allocating device buffer memory (%s).",
-                devices_[device].name.c_str());
+      bufferBytes *= *bufferSize;
+      if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
+      stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+      if ( stream_.deviceBuffer == NULL ) {
+        errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
         goto error;
       }
     }
@@ -8108,388 +6436,389 @@ bool RtApiAl :: probeDeviceOpen(int device, StreamMode mode, int channels,
 
   stream_.device[mode] = device;
   stream_.state = STREAM_STOPPED;
-  if ( stream_.mode == OUTPUT && mode == INPUT )
+
+  // Setup the buffer conversion information structure.
+  if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
+
+  // Setup thread if necessary.
+  if ( stream_.mode == OUTPUT && mode == INPUT ) {
     // We had already set up an output stream.
     stream_.mode = DUPLEX;
-  else
+    if ( stream_.device[0] == device ) handle->id[0] = fd;
+  }
+  else {
     stream_.mode = mode;
-  stream_.nBuffers = nBuffers;
-  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;
+    // Setup callback thread.
+    stream_.callbackInfo.object = (void *) this;
+
+    // Set the thread attributes for joinable and realtime scheduling
+    // priority.  The higher priority will only take affect if the
+    // program is run as root or suid.
+    pthread_attr_t attr;
+    pthread_attr_init( &attr );
+    pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
+#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
+    pthread_attr_setschedpolicy( &attr, SCHED_RR );
+#else
+    pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
+#endif
 
-    // 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 );
-      }
+    stream_.callbackInfo.isRunning = true;
+    result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
+    pthread_attr_destroy( &attr );
+    if ( result ) {
+      stream_.callbackInfo.isRunning = false;
+      errorText_ = "RtApiOss::error creating callback thread!";
+      goto error;
     }
   }
 
   return SUCCESS;
 
  error:
-  if (handle) {
-    if (handle[0])
-      alClosePort(handle[0]);
-    if (handle[1])
-      alClosePort(handle[1]);
-    free(handle);
+  if ( handle ) {
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
+  }
+
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
+    stream_.deviceBuffer = 0;
   }
 
-  error(RtError::DEBUG_WARNING);
   return FAILURE;
 }
 
-void RtApiAl :: closeStream()
+void RtApiOss :: closeStream()
 {
-  // We don't want an exception to be thrown here because this
-  // function is called by our class destructor.  So, do our own
-  // streamId check.
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtApiAl::closeStream(): no open stream to close!");
-    error(RtError::WARNING);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::closeStream(): no open stream to close!";
+    error( WARNING );
     return;
   }
 
-  ALport *handle = (ALport *) stream_.apiHandle;
-  if (stream_.state == STREAM_RUNNING) {
-    int buffer_size = stream_.bufferSize * stream_.nBuffers;
-    if (stream_.mode == OUTPUT || stream_.mode == DUPLEX)
-      alDiscardFrames(handle[0], buffer_size);
-    if (stream_.mode == INPUT || stream_.mode == DUPLEX)
-      alDiscardFrames(handle[1], buffer_size);
-    stream_.state = STREAM_STOPPED;
-  }
+  stream_.callbackInfo.isRunning = false;
+  pthread_join( stream_.callbackInfo.thread, NULL );
 
-  if (stream_.callbackInfo.usingCallback) {
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.state == STREAM_RUNNING ) {
+    if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
+      ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    else
+      ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    stream_.state = STREAM_STOPPED;
   }
 
-  if (handle) {
-    if (handle[0]) alClosePort(handle[0]);
-    if (handle[1]) alClosePort(handle[1]);
-    free(handle);
+  if ( handle ) {
+    if ( handle->id[0] ) close( handle->id[0] );
+    if ( handle->id[1] ) close( handle->id[1] );
+    delete handle;
     stream_.apiHandle = 0;
   }
 
-  if (stream_.userBuffer) {
-    free(stream_.userBuffer);
-    stream_.userBuffer = 0;
+  for ( int i=0; i<2; i++ ) {
+    if ( stream_.userBuffer[i] ) {
+      free( stream_.userBuffer[i] );
+      stream_.userBuffer[i] = 0;
+    }
   }
 
-  if (stream_.deviceBuffer) {
-    free(stream_.deviceBuffer);
+  if ( stream_.deviceBuffer ) {
+    free( stream_.deviceBuffer );
     stream_.deviceBuffer = 0;
   }
 
   stream_.mode = UNINITIALIZED;
+  stream_.state = STREAM_CLOSED;
 }
 
-void RtApiAl :: startStream()
+void RtApiOss :: startStream()
 {
   verifyStream();
-  if (stream_.state == STREAM_RUNNING) return;
+  if ( stream_.state == STREAM_RUNNING ) {
+    errorText_ = "RtApiOss::startStream(): the stream is already running!";
+    error( WARNING );
+    return;
+  }
 
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
-  // The AL port is ready as soon as it is opened.
   stream_.state = STREAM_RUNNING;
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  // No need to do anything else here ... OSS automatically starts
+  // when fed samples.
+
+  MUTEX_UNLOCK( &stream_.mutex );
 }
 
-void RtApiAl :: stopStream()
+void RtApiOss :: stopStream()
 {
   verifyStream();
-  if (stream_.state == STREAM_STOPPED) return;
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
 
   // Change the state before the lock to improve shutdown response
   // when using a callback.
   stream_.state = STREAM_STOPPED;
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
-  int result, buffer_size = stream_.bufferSize * stream_.nBuffers;
-  ALport *handle = (ALport *) stream_.apiHandle;
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX)
-    alZeroFrames(handle[0], buffer_size);
+    // Flush the output with zeros a few times.
+    char *buffer;
+    int samples;
+    RtAudioFormat format;
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    result = alDiscardFrames(handle[1], buffer_size);
-    if (result == -1) {
-      sprintf(message_, "RtApiAl: error draining stream device (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+    if ( stream_.doConvertBuffer[0] ) {
+      buffer = stream_.deviceBuffer;
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
+      format = stream_.deviceFormat[0];
+    }
+    else {
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
+      format = stream_.userFormat;
     }
-  }
-
-  MUTEX_UNLOCK(&stream_.mutex);
-}
-
-void RtApiAl :: abortStream()
-{
-  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);
+    memset( buffer, 0, samples * formatBytes(format) );
+    for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
+      if ( result == -1 ) {
+        errorText_ = "RtApiOss::stopStream: audio write error.";
+        error( WARNING );
+      }
+    }
 
-  ALport *handle = (ALport *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+    result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
+    }
+    handle->triggered = false;
+  }
 
-    int buffer_size = stream_.bufferSize * stream_.nBuffers;
-    int result = alDiscardFrames(handle[0], buffer_size);
-    if (result == -1) {
-      sprintf(message_, "RtApiAl: error aborting stream device (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
+    result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
   }
 
-  // There is no clear action to take on the input stream, since the
-  // port will continue to run in any event.
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  MUTEX_UNLOCK(&stream_.mutex);
+  stream_.state = STREAM_STOPPED;
+  if ( result != -1 ) return;
+  error( SYSTEM );
 }
 
-int RtApiAl :: streamWillBlock()
+void RtApiOss :: abortStream()
 {
   verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
+    error( WARNING );
+    return;
+  }
 
-  if (stream_.state == STREAM_STOPPED) return 0;
-
-  MUTEX_LOCK(&stream_.mutex);
+  // Change the state before the lock to improve shutdown response
+  // when using a callback.
+  stream_.state = STREAM_STOPPED;
+  MUTEX_LOCK( &stream_.mutex );
 
-  int frames = 0;
-  int err = 0;
-  ALport *handle = (ALport *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
-    err = alGetFillable(handle[0]);
-    if (err < 0) {
-      sprintf(message_, "RtApiAl: error getting available frames for stream (%s): %s.",
-              devices_[stream_.device[0]].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  int result = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+    result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
+    handle->triggered = false;
   }
 
-  frames = err;
-
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
-    err = alGetFilled(handle[1]);
-    if (err < 0) {
-      sprintf(message_, "RtApiAl: error getting available frames for stream (%s): %s.",
-              devices_[stream_.device[1]].name.c_str(), alGetErrorString(oserror()));
-      error(RtError::DRIVER_ERROR);
+  if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
+    result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
+    if ( result == -1 ) {
+      errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
+      errorText_ = errorStream_.str();
+      goto unlock;
     }
-    if (frames > err) frames = err;
   }
 
-  frames = stream_.bufferSize - frames;
-  if (frames < 0) frames = 0;
+ unlock:
+  MUTEX_UNLOCK( &stream_.mutex );
 
-  MUTEX_UNLOCK(&stream_.mutex);
-  return frames;
+  stream_.state = STREAM_STOPPED;
+  if ( result != -1 ) return;
+  error( SYSTEM );
 }
 
-void RtApiAl :: tickStream()
+void RtApiOss :: callbackEvent()
 {
-  verifyStream();
+  if ( stream_.state == STREAM_STOPPED ) {
+    if ( stream_.callbackInfo.isRunning ) usleep( 50000 ); // sleep 50 milliseconds
+    return;
+  }
 
-  int stopStream = 0;
-  if (stream_.state == STREAM_STOPPED) {
-    if (stream_.callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
+    error( WARNING );
     return;
   }
-  else if (stream_.callbackInfo.usingCallback) {
-    RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
-    stopStream = callback(stream_.userBuffer, stream_.bufferSize, stream_.callbackInfo.userData);
+
+  // Invoke user callback to get fresh output data.
+  int doStopStream = 0;
+  RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
+  double streamTime = getStreamTime();
+  RtAudioStreamStatus status = 0;
+  OssHandle *handle = (OssHandle *) stream_.apiHandle;
+  if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
+    status |= RTAUDIO_OUTPUT_UNDERFLOW;
+    handle->xrun[0] = false;
+  }
+  if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
+    status |= RTAUDIO_INPUT_OVERFLOW;
+    handle->xrun[1] = false;
   }
+  doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
+                           stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
 
-  MUTEX_LOCK(&stream_.mutex);
+  MUTEX_LOCK( &stream_.mutex );
 
   // The state might change while waiting on a mutex.
-  if (stream_.state == STREAM_STOPPED)
-    goto unlock;
+  if ( stream_.state == STREAM_STOPPED ) goto unlock;
 
+  int result;
   char *buffer;
-  int channels;
+  int samples;
   RtAudioFormat format;
-  ALport *handle = (ALport *) stream_.apiHandle;
-  if (stream_.mode == OUTPUT || stream_.mode == DUPLEX) {
+
+  if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters and do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[0]) {
+    if ( stream_.doConvertBuffer[0] ) {
       buffer = stream_.deviceBuffer;
-      convertBuffer( buffer, stream_.userBuffer, stream_.convertInfo[0] );
-      channels = stream_.nDeviceChannels[0];
+      convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
+      samples = stream_.bufferSize * stream_.nDeviceChannels[0];
       format = stream_.deviceFormat[0];
     }
     else {
-      buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[0];
+      buffer = stream_.userBuffer[0];
+      samples = stream_.bufferSize * stream_.nUserChannels[0];
       format = stream_.userFormat;
     }
 
     // Do byte swapping if necessary.
-    if (stream_.doByteSwap[0])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
+    if ( stream_.doByteSwap[0] )
+      byteSwapBuffer( buffer, samples, format );
+
+    if ( stream_.mode == DUPLEX && handle->triggered == false ) {
+      int trig = 0;
+      ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
+      trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
+      ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
+      handle->triggered = true;
+    }
+    else
+      // Write samples to device.
+      result = write( handle->id[0], buffer, samples * formatBytes(format) );
 
-    // Write interleaved samples to device.
-    alWriteFrames(handle[0], buffer, stream_.bufferSize);
+    if ( result == -1 ) {
+      // We'll assume this is an underrun, though there isn't a
+      // specific means for determining that.
+      handle->xrun[0] = true;
+      errorText_ = "RtApiOss::callbackEvent: audio write error.";
+      error( WARNING );
+      goto unlock;
+    }
   }
 
-  if (stream_.mode == INPUT || stream_.mode == DUPLEX) {
+  if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
 
     // Setup parameters.
-    if (stream_.doConvertBuffer[1]) {
+    if ( stream_.doConvertBuffer[1] ) {
       buffer = stream_.deviceBuffer;
-      channels = stream_.nDeviceChannels[1];
+      samples = stream_.bufferSize * stream_.nDeviceChannels[1];
       format = stream_.deviceFormat[1];
     }
     else {
-      buffer = stream_.userBuffer;
-      channels = stream_.nUserChannels[1];
+      buffer = stream_.userBuffer[1];
+      samples = stream_.bufferSize * stream_.nUserChannels[1];
       format = stream_.userFormat;
     }
 
-    // Read interleaved samples from device.
-    alReadFrames(handle[1], buffer, stream_.bufferSize);
+    // Read samples from device.
+    result = read( handle->id[1], buffer, samples * formatBytes(format) );
+
+    if ( result == -1 ) {
+      // We'll assume this is an overrun, though there isn't a
+      // specific means for determining that.
+      handle->xrun[1] = true;
+      errorText_ = "RtApiOss::callbackEvent: audio read error.";
+      error( WARNING );
+      goto unlock;
+    }
 
     // Do byte swapping if necessary.
-    if (stream_.doByteSwap[1])
-      byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
+    if ( stream_.doByteSwap[1] )
+      byteSwapBuffer( buffer, samples, format );
 
     // Do buffer conversion if necessary.
-    if (stream_.doConvertBuffer[1])
-      convertBuffer( stream_.userBuffer, stream_.deviceBuffer, stream_.convertInfo[1] );
+    if ( stream_.doConvertBuffer[1] )
+      convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
   }
 
  unlock:
-  MUTEX_UNLOCK(&stream_.mutex);
-
-  if (stream_.callbackInfo.usingCallback && stopStream)
-    this->stopStream();
-}
-
-void RtApiAl :: setStreamCallback(RtAudioCallback callback, void *userData)
-{
-  verifyStream();
-
-  CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
-  if ( info->usingCallback ) {
-    sprintf(message_, "RtApiAl: A callback is already set for this stream!");
-    error(RtError::WARNING);
-    return;
-  }
-
-  info->callback = (void *) callback;
-  info->userData = userData;
-  info->usingCallback = true;
-  info->object = (void *) this;
-
-  // Set the thread attributes for joinable and realtime scheduling
-  // priority.  The higher priority will only take affect if the
-  // program is run as root or suid.
-  pthread_attr_t attr;
-  pthread_attr_init(&attr);
-  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-  pthread_attr_setschedpolicy(&attr, SCHED_RR);
-
-  int err = pthread_create(&info->thread, &attr, callbackHandler, &stream_.callbackInfo);
-  pthread_attr_destroy(&attr);
-  if (err) {
-    info->usingCallback = false;
-    sprintf(message_, "RtApiAl: error starting callback thread!");
-    error(RtError::THREAD_ERROR);
-  }
-}
-
-void RtApiAl :: cancelStreamCallback()
-{
-  verifyStream();
-
-  if (stream_.callbackInfo.usingCallback) {
-
-    if (stream_.state == STREAM_RUNNING)
-      stopStream();
+  MUTEX_UNLOCK( &stream_.mutex );
 
-    MUTEX_LOCK(&stream_.mutex);
-
-    stream_.callbackInfo.usingCallback = false;
-    pthread_join(stream_.callbackInfo.thread, NULL);
-    stream_.callbackInfo.thread = 0;
-    stream_.callbackInfo.callback = NULL;
-    stream_.callbackInfo.userData = NULL;
-
-    MUTEX_UNLOCK(&stream_.mutex);
-  }
+  RtApi::tickStreamTime();
+  if ( doStopStream == 1 ) this->stopStream();
+  else if ( doStopStream == 2 ) this->abortStream();
 }
 
-extern "C" void *callbackHandler(void *ptr)
+extern "C" void *ossCallbackHandler( void *ptr )
 {
   CallbackInfo *info = (CallbackInfo *) ptr;
-  RtApiAl *object = (RtApiAl *) info->object;
-  bool *usingCallback = &info->usingCallback;
+  RtApiOss *object = (RtApiOss *) info->object;
+  bool *isRunning = &info->isRunning;
 
-  while ( *usingCallback ) {
-    try {
-      object->tickStream();
-    }
-    catch (RtError &exception) {
-      fprintf(stderr, "\nRtApiAl: callback thread error (%s) ... closing thread.\n\n",
-              exception.getMessageString());
-      break;
-    }
+#ifdef SCHED_RR
+  // Set a higher scheduler priority (P.J. Leonard)
+  struct sched_param param;
+  param.sched_priority = 39;   // Is this the best number?
+  sched_setscheduler( 0, SCHED_RR, &param );
+#endif
+
+  while ( *isRunning == true ) {
+    pthread_testcancel();
+    object->callbackEvent();
   }
 
-  return 0;
+  pthread_exit( NULL );
 }
 
-//******************** End of __IRIX_AL__ *********************//
+//******************** End of __LINUX_OSS__ *********************//
 #endif
 
 
@@ -8500,86 +6829,160 @@ extern "C" void *callbackHandler(void *ptr)
 // *************************************************** //
 
 // This method can be modified to control the behavior of error
-// message reporting and throwing.
-void RtApi :: error(RtError::Type type)
+// message printing.
+void RtApi :: error( ErrorType type )
 {
-  if (type == RtError::WARNING) {
-    fprintf(stderr, "\n%s\n\n", message_);
-  }
-  else if (type == RtError::DEBUG_WARNING) {
-#if defined(__RTAUDIO_DEBUG__)
-    fprintf(stderr, "\n%s\n\n", message_);
-#endif
-  }
-  else {
-#if defined(__RTAUDIO_DEBUG__)
-    fprintf(stderr, "\n%s\n\n", message_);
-#endif
-    throw RtError(std::string(message_), type);
-  }
+  if ( type == WARNING && showWarnings_ == true )
+    std::cerr << '\n' << errorText_ << "\n\n";
+  else if ( type == INVALID_CALL )
+    throw( RtError( errorText_, RtError::INVALID_USE ) );
+  else if ( type == SYSTEM )
+    throw( RtError( errorText_, RtError::SYSTEM_ERROR ) );
+  errorStream_.str(""); // clear the ostringstream
 }
 
 void RtApi :: verifyStream()
 {
-  if ( stream_.mode == UNINITIALIZED ) {
-    sprintf(message_, "RtAudio: stream is not open!");
-    error(RtError::INVALID_STREAM);
+  if ( stream_.state == STREAM_CLOSED ) {
+    errorText_ = "RtApi:: a stream is not open!";
+    error( INVALID_CALL );
   }
 }
 
-void RtApi :: clearDeviceInfo(RtApiDevice *info)
-{
-  // Don't clear the name or DEVICE_ID fields here ... they are
-  // typically set prior to a call of this function.
-  info->probed = false;
-  info->maxOutputChannels = 0;
-  info->maxInputChannels = 0;
-  info->maxDuplexChannels = 0;
-  info->minOutputChannels = 0;
-  info->minInputChannels = 0;
-  info->minDuplexChannels = 0;
-  info->hasDuplexSupport = false;
-  info->sampleRates.clear();
-  info->nativeFormats = 0;
-}
-
 void RtApi :: clearStreamInfo()
 {
   stream_.mode = UNINITIALIZED;
-  stream_.state = STREAM_STOPPED;
+  stream_.state = STREAM_CLOSED;
   stream_.sampleRate = 0;
   stream_.bufferSize = 0;
   stream_.nBuffers = 0;
   stream_.userFormat = 0;
+  stream_.userInterleaved = true;
+  stream_.streamTime = 0.0;
+  stream_.apiHandle = 0;
+  stream_.deviceBuffer = 0;
+  stream_.callbackInfo.callback = 0;
+  stream_.callbackInfo.userData = 0;
+  stream_.callbackInfo.isRunning = false;
   for ( int i=0; i<2; i++ ) {
     stream_.device[i] = 0;
     stream_.doConvertBuffer[i] = false;
-    stream_.deInterleave[i] = false;
+    stream_.deviceInterleaved[i] = true;
     stream_.doByteSwap[i] = false;
     stream_.nUserChannels[i] = 0;
     stream_.nDeviceChannels[i] = 0;
+    stream_.channelOffset[i] = 0;
     stream_.deviceFormat[i] = 0;
+    stream_.latency[i] = 0;
+    stream_.userBuffer[i] = 0;
+    stream_.convertInfo[i].channels = 0;
+    stream_.convertInfo[i].inJump = 0;
+    stream_.convertInfo[i].outJump = 0;
+    stream_.convertInfo[i].inFormat = 0;
+    stream_.convertInfo[i].outFormat = 0;
+    stream_.convertInfo[i].inOffset.clear();
+    stream_.convertInfo[i].outOffset.clear();
   }
 }
 
-int RtApi :: formatBytes(RtAudioFormat format)
+unsigned int RtApi :: formatBytes( RtAudioFormat format )
 {
-  if (format == RTAUDIO_SINT16)
+  if ( format == RTAUDIO_SINT16 )
     return 2;
-  else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
-           format == RTAUDIO_FLOAT32)
+  else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
+            format == RTAUDIO_FLOAT32 )
     return 4;
-  else if (format == RTAUDIO_FLOAT64)
+  else if ( format == RTAUDIO_FLOAT64 )
     return 8;
-  else if (format == RTAUDIO_SINT8)
+  else if ( format == RTAUDIO_SINT8 )
     return 1;
 
-  sprintf(message_,"RtApi: undefined format in formatBytes().");
-  error(RtError::WARNING);
+  errorText_ = "RtApi::formatBytes: undefined format.";
+  error( WARNING );
 
   return 0;
 }
 
+void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
+{
+  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 ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
+    if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
+         ( mode == INPUT && stream_.userInterleaved ) ) {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+        stream_.convertInfo[mode].inJump = 1;
+      }
+    }
+    else {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+  }
+  else { // no (de)interleaving
+    if ( stream_.userInterleaved ) {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k );
+        stream_.convertInfo[mode].outOffset.push_back( k );
+      }
+    }
+    else {
+      for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
+        stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
+        stream_.convertInfo[mode].inJump = 1;
+        stream_.convertInfo[mode].outJump = 1;
+      }
+    }
+  }
+
+  // Add channel offset.
+  if ( firstChannel > 0 ) {
+    if ( stream_.deviceInterleaved[mode] ) {
+      if ( mode == OUTPUT ) {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].outOffset[k] += firstChannel;
+      }
+      else {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].inOffset[k] += firstChannel;
+      }
+    }
+    else {
+      if ( mode == OUTPUT ) {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
+      }
+      else {
+        for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
+          stream_.convertInfo[mode].inOffset[k] += ( firstChannel  * stream_.bufferSize );
+      }
+    }
+  }
+}
+
 void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
 {
   // This function does format conversion, input/output channel compensation, and
@@ -8588,7 +6991,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
 
   // 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] )
+       ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
     memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
 
   int j;
@@ -8599,7 +7002,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
       scale = 1.0 / 128.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8611,7 +7014,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8622,10 +7025,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      scale = 1.0 / 8388608.0;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0xffffff00);
+          out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]] & 0x00ffffff);
           out[info.outOffset[j]] *= scale;
         }
         in += info.inJump;
@@ -8635,7 +7038,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8646,7 +7049,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
         }
@@ -8657,7 +7060,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       // Channel compensation and/or (de)interleaving only.
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8673,7 +7076,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
       scale = 1.0 / 128.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8685,7 +7088,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
       scale = 1.0 / 32768.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8696,10 +7099,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      scale = 1.0 / 8388608.0;
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0xffffff00);
+          out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]] & 0x00ffffff);
           out[info.outOffset[j]] *= scale;
         }
         in += info.inJump;
@@ -8709,7 +7112,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
       scale = 1.0 / 2147483648.0;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
           out[info.outOffset[j]] *= scale;
@@ -8721,7 +7124,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       // Channel compensation and/or (de)interleaving only.
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8731,7 +7134,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
         }
@@ -8744,7 +7147,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     Int32 *out = (Int32 *)outBuffer;
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
           out[info.outOffset[j]] <<= 24;
@@ -8755,7 +7158,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
           out[info.outOffset[j]] <<= 16;
@@ -8766,9 +7169,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] <<= 8;
         }
         in += info.inJump;
         out += info.outJump;
@@ -8777,7 +7181,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT32) {
       // Channel compensation and/or (de)interleaving only.
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8787,7 +7191,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
@@ -8797,7 +7201,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
@@ -8810,10 +7214,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     Int32 *out = (Int32 *)outBuffer;
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
-          out[info.outOffset[j]] <<= 24;
+          out[info.outOffset[j]] <<= 16;
         }
         in += info.inJump;
         out += info.outJump;
@@ -8821,10 +7225,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
-          out[info.outOffset[j]] <<= 16;
+          out[info.outOffset[j]] <<= 8;
         }
         in += info.inJump;
         out += info.outJump;
@@ -8833,7 +7237,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT24) {
       // Channel compensation and/or (de)interleaving only.
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8843,9 +7247,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] & 0xffffff00);
+          out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
+          out[info.outOffset[j]] >>= 8;
         }
         in += info.inJump;
         out += info.outJump;
@@ -8853,9 +7258,9 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
+          out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388608.0);
         }
         in += info.inJump;
         out += info.outJump;
@@ -8863,7 +7268,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.0);
         }
@@ -8876,7 +7281,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     Int16 *out = (Int16 *)outBuffer;
     if (info.inFormat == RTAUDIO_SINT8) {
       signed char *in = (signed char *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
           out[info.outOffset[j]] <<= 8;
@@ -8888,7 +7293,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     else if (info.inFormat == RTAUDIO_SINT16) {
       // Channel compensation and/or (de)interleaving only.
       Int16 *in = (Int16 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8898,9 +7303,9 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
+          out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 8) & 0x0000ffff);
         }
         in += info.inJump;
         out += info.outJump;
@@ -8908,7 +7313,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
         }
@@ -8918,7 +7323,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
         }
@@ -8928,7 +7333,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.0);
         }
@@ -8942,7 +7347,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     if (info.inFormat == RTAUDIO_SINT8) {
       // Channel compensation and/or (de)interleaving only.
       signed char *in = (signed char *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = in[info.inOffset[j]];
         }
@@ -8952,7 +7357,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     if (info.inFormat == RTAUDIO_SINT16) {
       Int16 *in = (Int16 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
         }
@@ -8962,9 +7367,9 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT24) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
-          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
+          out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 16) & 0x000000ff);
         }
         in += info.inJump;
         out += info.outJump;
@@ -8972,7 +7377,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_SINT32) {
       Int32 *in = (Int32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
         }
@@ -8982,7 +7387,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT32) {
       Float32 *in = (Float32 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
         }
@@ -8992,7 +7397,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
     }
     else if (info.inFormat == RTAUDIO_FLOAT64) {
       Float64 *in = (Float64 *)inBuffer;
-      for (int i=0; i<stream_.bufferSize; i++) {
+      for (unsigned int i=0; i<stream_.bufferSize; i++) {
         for (j=0; j<info.channels; j++) {
           out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.0);
         }
@@ -9003,14 +7408,14 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
   }
 }
 
-void RtApi :: byteSwapBuffer( char *buffer, int samples, RtAudioFormat format )
+void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
 {
   register char val;
   register char *ptr;
 
   ptr = buffer;
-  if (format == RTAUDIO_SINT16) {
-    for (int i=0; i<samples; i++) {
+  if ( format == RTAUDIO_SINT16 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 2nd bytes.
       val = *(ptr);
       *(ptr) = *(ptr+1);
@@ -9020,10 +7425,10 @@ void RtApi :: byteSwapBuffer( char *buffer, int samples, RtAudioFormat format )
       ptr += 2;
     }
   }
-  else if (format == RTAUDIO_SINT24 ||
-           format == RTAUDIO_SINT32 ||
-           format == RTAUDIO_FLOAT32) {
-    for (int i=0; i<samples; i++) {
+  else if ( format == RTAUDIO_SINT24 ||
+            format == RTAUDIO_SINT32 ||
+            format == RTAUDIO_FLOAT32 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 4th bytes.
       val = *(ptr);
       *(ptr) = *(ptr+3);
@@ -9039,8 +7444,8 @@ void RtApi :: byteSwapBuffer( char *buffer, int samples, RtAudioFormat format )
       ptr += 4;
     }
   }
-  else if (format == RTAUDIO_FLOAT64) {
-    for (int i=0; i<samples; i++) {
+  else if ( format == RTAUDIO_FLOAT64 ) {
+    for ( unsigned int i=0; i<samples; i++ ) {
       // Swap 1st and 8th bytes
       val = *(ptr);
       *(ptr) = *(ptr+7);
@@ -9069,3 +7474,13 @@ void RtApi :: byteSwapBuffer( char *buffer, int samples, RtAudioFormat format )
     }
   }
 }
+
+// Indentation settings for Vim and Emacs
+//
+// Local Variables:
+// c-basic-offset: 2
+// indent-tabs-mode: nil
+// End:
+//
+// vim: et sts=2 sw=2
+
index d9b8ff243994f2a154e694a00784df0d2775fc20..322b04b716b52fc761726027b6cb25dc99fed0ea 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
@@ -4,13 +4,13 @@
 
     RtAudio provides a common API (Application Programming Interface)
     for realtime audio input/output across Linux (native ALSA, Jack,
-    and OSS), SGI, Macintosh OS X (CoreAudio), and Windows
+    and OSS), SGI, Macintosh OS X (CoreAudio and Jack), and Windows
     (DirectSound and ASIO) operating systems.
 
-    RtAudio WWW site: http://music.mcgill.ca/~gary/rtaudio/
+    RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
 
     RtAudio: realtime audio i/o C++ classes
-    Copyright (c) 2001-2005 Gary P. Scavone
+    Copyright (c) 2001-2007 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -24,8 +24,9 @@
     included in all copies or substantial portions of the Software.
 
     Any person wishing to distribute modifications to the Software is
-    requested to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 */
 /************************************************************************/
 
-// RtAudio: Version 3.0.3 (18 November 2005)
+/*!
+  \file RtAudio.h
+ */
+
+// RtAudio: Version 4.0
 
 #ifndef __RTAUDIO_H
 #define __RTAUDIO_H
 
-#include "RtError.h"
 #include <string>
 #include <vector>
+#include "RtError.h"
+
+/*! \typedef typedef unsigned long RtAudioFormat;
+    \brief RtAudio data format type.
+
+    Support for signed integers and floats.  Audio data fed to/from an
+    RtAudio stream is assumed to ALWAYS be in host byte order.  The
+    internal routines will automatically take care of any necessary
+    byte-swapping between the host format and the soundcard.  Thus,
+    endian-ness is not a concern in the following format definitions.
+
+    - \e RTAUDIO_SINT8:   8-bit signed integer.
+    - \e RTAUDIO_SINT16:  16-bit signed integer.
+    - \e RTAUDIO_SINT24:  Upper 3 bytes of 32-bit signed integer.
+    - \e RTAUDIO_SINT32:  32-bit signed integer.
+    - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0.
+    - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0.
+*/
+typedef unsigned long RtAudioFormat;
+static const RtAudioFormat RTAUDIO_SINT8 = 0x1;    // 8-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT16 = 0x2;   // 16-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT24 = 0x4;   // Lower 3 bytes of 32-bit signed integer.
+static const RtAudioFormat RTAUDIO_SINT32 = 0x8;   // 32-bit signed integer.
+static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0.
+static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0.
+
+/*! \typedef typedef unsigned long RtAudioStreamFlags;
+    \brief RtAudio stream option flags.
+
+    The following flags can be OR'ed together to allow a client to
+    make changes to the default stream behavior:
+
+    - \e RTAUDIO_NONINTERLEAVED:   Use non-interleaved buffers (default = interleaved).
+    - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
+    - \e RTAUDIO_HOG_DEVICE:       Attempt grab device for exclusive use.
+
+    By default, RtAudio streams pass and receive audio data from the
+    client in an interleaved format.  By passing the
+    RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
+    data will instead be presented in non-interleaved buffers.  In
+    this case, each buffer argument in the RtAudioCallback function
+    will point to a single array of data, with \c nFrames samples for
+    each channel concatenated back-to-back.  For example, the first
+    sample of data for the second channel would be located at index \c
+    nFrames (assuming the \c buffer pointer was recast to the correct
+    data type for the stream).
+
+    Certain audio APIs offer a number of parameters that influence the
+    I/O latency of a stream.  By default, RtAudio will attempt to set
+    these parameters internally for robust (glitch-free) performance
+    (though some APIs, like Windows Direct Sound, make this difficult).
+    By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
+    function, internal stream settings will be influenced in an attempt
+    to minimize stream latency, though possibly at the expense of stream
+    performance.
+
+    If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
+    open the input and/or output stream device(s) for exclusive use.
+    Note that this is not possible with all supported audio APIs.
+*/
+typedef unsigned int RtAudioStreamFlags;
+static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1;    // Use non-interleaved buffers (default = interleaved).
+static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2;  // Attempt to set stream parameters for lowest possible latency.
+static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4;        // Attempt grab device and prevent use by others.
+
+/*! \typedef typedef unsigned long RtAudioStreamStatus;
+    \brief RtAudio stream status (over- or underflow) flags.
+
+    Notification of a stream over- or underflow is indicated by a
+    non-zero stream \c status argument in the RtAudioCallback function.
+    The stream status can be one of the following two options,
+    depending on whether the stream is open for output and/or input:
+
+    - \e RTAUDIO_INPUT_OVERFLOW:   Input data was discarded because of an overflow condition at the driver.
+    - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound.
+*/
+typedef unsigned int RtAudioStreamStatus;
+static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1;    // Input data was discarded because of an overflow condition at the driver.
+static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2;  // The output buffer ran low, likely causing a gap in the output sound.
+
+//! RtAudio callback function prototype.
+/*!
+   All RtAudio clients must create a function of type RtAudioCallback
+   to read and/or write data from/to the audio stream.  When the
+   underlying audio system is ready for new input or output data, this
+   function will be invoked.
+
+   \param outputBuffer For output (or duplex) streams, the client
+          should write \c nFrames of audio sample frames into this
+          buffer.  This argument should be recast to the datatype
+          specified when the stream was opened.  For input-only
+          streams, this argument will be NULL.
+
+   \param inputBuffer For input (or duplex) streams, this buffer will
+          hold \c nFrames of input audio sample frames.  This
+          argument should be recast to the datatype specified when the
+          stream was opened.  For output-only streams, this argument
+          will be NULL.
+
+   \param nFrames The number of sample frames of input or output
+          data in the buffers.  The actual buffer size in bytes is
+          dependent on the data type and number of channels in use.
+
+   \param streamTime The number of seconds that have elapsed since the
+          stream was started.
+
+   \param status If non-zero, this argument indicates a data overflow
+          or underflow condition for the stream.  The particular
+          condition can be determined by comparison with the
+          RtAudioStreamStatus flags.
+
+   \param userData A pointer to optional data provided by the client
+          when opening the stream (default = NULL).
+
+   To continue normal stream operation, the RtAudioCallback function
+   should return a value of zero.  To stop the stream and drain the
+   output buffer, the function should return a value of one.  To abort
+   the stream immediately, the client should return a value of two.
+ */
+typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer,
+                                unsigned int nFrames,
+                                double streamTime,
+                                RtAudioStreamStatus status,
+                                void *userData );
+
+
+// **************************************************************** //
+//
+// RtAudio class declaration.
+//
+// RtAudio is a "controller" used to select an available audio i/o
+// interface.  It presents a common API for the user to call but all
+// functionality is implemented by the class RtApi and its
+// subclasses.  RtAudio creates an instance of an RtApi subclass
+// based on the user's API choice.  If no choice is made, RtAudio
+// attempts to make a "logical" API selection.
+//
+// **************************************************************** //
+
+class RtApi;
+
+class RtAudio
+{
+ public:
+
+  //! Audio API specifier arguments.
+  enum Api {
+    UNSPECIFIED,    /*!< Search for a working compiled API. */
+    LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */
+    LINUX_OSS,      /*!< The Linux Open Sound System API. */
+    UNIX_JACK,      /*!< The Jack Low-Latency Audio Server API. */
+    MACOSX_CORE,    /*!< Macintosh OS-X Core Audio API. */
+    WINDOWS_ASIO,   /*!< The Steinberg Audio Stream I/O API. */
+    WINDOWS_DS,     /*!< The Microsoft Direct Sound API. */
+    RTAUDIO_DUMMY   /*!< A compilable but non-functional API. */
+  };
+
+  //! The public device information structure for returning queried values.
+  struct DeviceInfo {
+    bool probed;                  /*!< true if the device capabilities were successfully probed. */
+    std::string name;             /*!< Character string device identifier. */
+    unsigned int outputChannels;  /*!< Maximum output channels supported by device. */
+    unsigned int inputChannels;   /*!< Maximum input channels supported by device. */
+    unsigned int duplexChannels;  /*!< Maximum simultaneous input/output channels supported by device. */
+    bool isDefaultOutput;         /*!< true if this is the default output device. */
+    bool isDefaultInput;          /*!< true if this is the default input device. */
+    std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
+    RtAudioFormat nativeFormats;  /*!< Bit mask of supported data formats. */
+
+    // Default constructor.
+    DeviceInfo()
+      :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
+       isDefaultOutput(false), isDefaultInput(false), nativeFormats(0) {}
+  };
+
+  //! The structure for specifying input or ouput stream parameters.
+  struct StreamParameters {
+    unsigned int deviceId;     /*!< Device index (0 to getDeviceCount() - 1). */
+    unsigned int nChannels;    /*!< Number of channels. */
+    unsigned int firstChannel; /*!< First channel index on device (default = 0). */
+
+    // Default constructor.
+    StreamParameters()
+      : deviceId(0), nChannels(0), firstChannel(0) {}
+  };
+
+  //! The structure for specifying stream options.
+  /*!
+    The following flags can be OR'ed together to allow a client to
+    make changes to the default stream behavior:
+
+    - \e RTAUDIO_NONINTERLEAVED:   Use non-interleaved buffers (default = interleaved).
+    - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
+    - \e RTAUDIO_HOG_DEVICE:       Attempt grab device for exclusive use.
+
+    By default, RtAudio streams pass and receive audio data from the
+    client in an interleaved format.  By passing the
+    RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
+    data will instead be presented in non-interleaved buffers.  In
+    this case, each buffer argument in the RtAudioCallback function
+    will point to a single array of data, with \c nFrames samples for
+    each channel concatenated back-to-back.  For example, the first
+    sample of data for the second channel would be located at index \c
+    nFrames (assuming the \c buffer pointer was recast to the correct
+    data type for the stream).
+
+    Certain audio APIs offer a number of parameters that influence the
+    I/O latency of a stream.  By default, RtAudio will attempt to set
+    these parameters internally for robust (glitch-free) performance
+    (though some APIs, like Windows Direct Sound, make this difficult).
+    By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
+    function, internal stream settings will be influenced in an attempt
+    to minimize stream latency, though possibly at the expense of stream
+    performance.
+
+    If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
+    open the input and/or output stream device(s) for exclusive use.
+    Note that this is not possible with all supported audio APIs.
+
+    The \c numberOfBuffers parameter can be used to control stream
+    latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs
+    only.  A value of two is usually the smallest allowed.  Larger
+    numbers can potentially result in more robust stream performance,
+    though likely at the cost of stream latency.  The value set by the
+    user is replaced during execution of the RtAudio::openStream()
+    function by the value actually used by the system.
+
+    The \c streamName parameter can be used to set the client name
+    when using the Jack API.  By default, the client name is set to
+    RtApiJack.  However, if you wish to create multiple instances of
+    RtAudio with Jack, each instance must have a unique client name.
+  */
+  struct StreamOptions {
+    RtAudioStreamFlags flags;      /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE). */
+    unsigned int numberOfBuffers;  /*!< Number of stream buffers. */
+    std::string streamName;        /*!< A stream name (currently used only in Jack). */
+
+    // Default constructor.
+    StreamOptions()
+      : flags(0), numberOfBuffers(0) {}
+  };
+
+  //! A static function to determine the available compiled audio APIs.
+  /*!
+    The values returned in the std::vector can be compared against
+    the enumerated list values.  Note that there can be more than one
+    API compiled for certain operating systems.
+  */
+  static void getCompiledApi( std::vector<RtAudio::Api> &apis ) throw();
+
+  //! The class constructor.
+  /*!
+    The constructor performs minor initialization tasks.  No exceptions
+    can be thrown.
+
+    If no API argument is specified and multiple API support has been
+    compiled, the default order of use is JACK, ALSA, OSS (Linux
+    systems) and ASIO, DS (Windows systems).
+  */
+  RtAudio( RtAudio::Api api=UNSPECIFIED ) throw();
+
+  //! The destructor.
+  /*!
+    If a stream is running or open, it will be stopped and closed
+    automatically.
+  */
+  ~RtAudio() throw();
+
+  //! Returns the audio API specifier for the current instance of RtAudio.
+  RtAudio::Api getCurrentApi( void ) throw();
+
+  //! A public function that queries for the number of audio devices available.
+  /*!
+    This function performs a system query of available devices each time it
+    is called, thus supporting devices connected \e after instantiation. If
+    a system error occurs during processing, a warning will be issued. 
+  */
+  unsigned int getDeviceCount( void ) throw();
+
+  //! Return an RtAudio::DeviceInfo structure for a specified device number.
+  /*!
+
+    Any device integer between 0 and getDeviceCount() - 1 is valid.
+    If an invalid argument is provided, an RtError (type = INVALID_USE)
+    will be thrown.  If a device is busy or otherwise unavailable, the
+    structure member "probed" will have a value of "false" and all
+    other members are undefined.  If the specified device is the
+    current default input or output device, the corresponding
+    "isDefault" member will have a value of "true".
+  */
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+
+  //! A function that returns the index of the default output device.
+  /*!
+    If the underlying audio API does not provide a "default
+    device", or if no devices are available, the return value will be
+    0.  Note that this is a valid device identifier and it is the
+    client's responsibility to verify that a device is available
+    before attempting to open a stream.
+  */
+  unsigned int getDefaultOutputDevice( void ) throw();
+
+  //! A function that returns the index of the default input device.
+  /*!
+    If the underlying audio API does not provide a "default
+    device", or if no devices are available, the return value will be
+    0.  Note that this is a valid device identifier and it is the
+    client's responsibility to verify that a device is available
+    before attempting to open a stream.
+  */
+  unsigned int getDefaultInputDevice( void ) throw();
+
+  //! A public function for opening a stream with the specified parameters.
+  /*!
+    An RtError (type = SYSTEM_ERROR) is thrown if a stream cannot be
+    opened with the specified parameters or an error occurs during
+    processing.  An RtError (type = INVALID_USE) is thrown if any
+    invalid device ID or channel number parameters are specified.
+
+    \param outputParameters Specifies output stream parameters to use
+           when opening a stream, including a device ID, number of channels,
+           and starting channel number.  For input-only streams, this
+           argument should be NULL.  The device ID is an index value between
+           0 and getDeviceCount() - 1.
+    \param inputParameters Specifies input stream parameters to use
+           when opening a stream, including a device ID, number of channels,
+           and starting channel number.  For output-only streams, this
+           argument should be NULL.  The device ID is an index value between
+           0 and getDeviceCount() - 1.
+    \param format An RtAudioFormat specifying the desired sample data format.
+    \param sampleRate The desired sample rate (sample frames per second).
+    \param *bufferFrames A pointer to a value indicating the desired
+           internal buffer size in sample frames.  The actual value
+           used by the device is returned via the same pointer.  A
+           value of zero can be specified, in which case the lowest
+           allowable value is determined.
+    \param callback A client-defined function that will be invoked
+           when input data is available and/or output data is needed.
+    \param userData An optional pointer to data that can be accessed
+           from within the callback function.
+    \param options An optional pointer to a structure containing various
+           global stream options, including a list of OR'ed RtAudioStreamFlags
+           and a suggested number of stream buffers that can be used to 
+           control stream latency.  More buffers typically result in more
+           robust performance, though at a cost of greater latency.  If a
+           value of zero is specified, a system-specific median value is
+           chosen.  If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the
+           lowest allowable value is used.  The actual value used is
+           returned via the structure argument.  The parameter is API dependent.
+  */
+  void openStream( RtAudio::StreamParameters *outputParameters,
+                   RtAudio::StreamParameters *inputParameters,
+                   RtAudioFormat format, unsigned int sampleRate,
+                   unsigned int *bufferFrames, RtAudioCallback callback,
+                   void *userData = NULL, RtAudio::StreamOptions *options = NULL );
+
+  //! A function that closes a stream and frees any associated stream memory.
+  /*!
+    If a stream is not open, this function issues a warning and
+    returns (no exception is thrown).
+  */
+  void closeStream( void ) throw();
+
+  //! A function that starts a stream.
+  /*!
+    An RtError (type = SYSTEM_ERROR) is thrown if an error occurs
+    during processing.  An RtError (type = INVALID_USE) is thrown if a
+    stream is not open.  A warning is issued if the stream is already
+    running.
+  */
+  void startStream( void );
+
+  //! Stop a stream, allowing any samples remaining in the output queue to be played.
+  /*!
+    An RtError (type = SYSTEM_ERROR) is thrown if an error occurs
+    during processing.  An RtError (type = INVALID_USE) is thrown if a
+    stream is not open.  A warning is issued if the stream is already
+    stopped.
+  */
+  void stopStream( void );
+
+  //! Stop a stream, discarding any samples remaining in the input/output queue.
+  /*!
+    An RtError (type = SYSTEM_ERROR) is thrown if an error occurs
+    during processing.  An RtError (type = INVALID_USE) is thrown if a
+    stream is not open.  A warning is issued if the stream is already
+    stopped.
+  */
+  void abortStream( void );
+
+  //! Returns true if a stream is open and false if not.
+  bool isStreamOpen( void ) throw();
+
+  //! Returns true if the stream is running and false if it is stopped or not open.
+  bool isStreamRunning( void ) throw();
+
+  //! Returns the number of elapsed seconds since the stream was started.
+  /*!
+    If a stream is not open, an RtError (type = INVALID_USE) will be thrown.
+  */
+  double getStreamTime( void );
+
+  //! Returns the internal stream latency in sample frames.
+  /*!
+    The stream latency refers to delay in audio input and/or output
+    caused by internal buffering by the audio system and/or hardware.
+    For duplex streams, the returned value will represent the sum of
+    the input and output latencies.  If a stream is not open, an
+    RtError (type = INVALID_USE) will be thrown.  If the API does not
+    report latency, the return value will be zero.
+  */
+  long getStreamLatency( void );
+
+  //! Specify whether warning messages should be printed to stderr.
+  void showWarnings( bool value = true ) throw();
+
+ protected:
+
+  void openRtApi( RtAudio::Api api );
+  RtApi *rtapi_;
+};
 
 // Operating system dependent thread functionality.
 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
   typedef unsigned long ThreadHandle;
   typedef CRITICAL_SECTION StreamMutex;
 
-#else // Various unix flavors with pthread support.
+#elif defined(__LINUX_ALSA__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
+  // Using pthread library for various flavors of unix.
   #include <pthread.h>
 
   typedef pthread_t ThreadHandle;
   typedef pthread_mutex_t StreamMutex;
 
+#else // Setup for "dummy" behavior
+
+  #define __RTAUDIO_DUMMY__
+  typedef int ThreadHandle;
+  typedef int StreamMutex;
+
 #endif
 
 // This global structure type is used to pass callback information
 struct CallbackInfo {
   void *object;    // Used as a "this" pointer.
   ThreadHandle thread;
-  bool usingCallback;
   void *callback;
   void *userData;
   void *apiInfo;   // void pointer for API specific callback information
+  bool isRunning;
 
   // Default constructor.
   CallbackInfo()
-    :object(0), usingCallback(false), callback(0),
-     userData(0), apiInfo(0) {}
-};
-
-// Support for signed integers and floats.  Audio data fed to/from
-// the tickStream() routine is assumed to ALWAYS be in host
-// byte order.  The internal routines will automatically take care of
-// any necessary byte-swapping between the host format and the
-// soundcard.  Thus, endian-ness is not a concern in the following
-// format definitions.
-typedef unsigned long RtAudioFormat;
-static const RtAudioFormat RTAUDIO_SINT8 = 0x1;    /*!< 8-bit signed integer. */
-static const RtAudioFormat RTAUDIO_SINT16 = 0x2;   /*!< 16-bit signed integer. */
-static const RtAudioFormat RTAUDIO_SINT24 = 0x4;   /*!< Upper 3 bytes of 32-bit signed integer. */
-static const RtAudioFormat RTAUDIO_SINT32 = 0x8;   /*!< 32-bit signed integer. */
-static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; /*!< Normalized between plus/minus 1.0. */
-static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; /*!< Normalized between plus/minus 1.0. */
-
-typedef int (*RtAudioCallback)(char *buffer, int bufferSize, void *userData);
-
-//! The public device information structure for returning queried values.
-struct RtAudioDeviceInfo {
-  std::string name;      /*!< Character string device identifier. */
-  bool probed;          /*!< true if the device capabilities were successfully probed. */
-  int outputChannels;   /*!< Maximum output channels supported by device. */
-  int inputChannels;    /*!< Maximum input channels supported by device. */
-  int duplexChannels;   /*!< Maximum simultaneous input/output channels supported by device. */
-  bool isDefault;       /*!< true if this is the default output or input device. */
-  std::vector<int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
-  RtAudioFormat nativeFormats;  /*!< Bit mask of supported data formats. */
-
-  // Default constructor.
-  RtAudioDeviceInfo()
-    :probed(false), outputChannels(0), inputChannels(0),
-       duplexChannels(0), isDefault(false), nativeFormats(0) {}
+    :object(0), callback(0), userData(0), apiInfo(0), isRunning(false) {}
 };
 
 // **************************************************************** //
 //
 // RtApi class declaration.
 //
+// Subclasses of RtApi contain all API- and OS-specific code necessary
+// to fully implement the RtAudio API.
+//
 // Note that RtApi is an abstract base class and cannot be
 // explicitly instantiated.  The class RtAudio will create an
 // instance of an RtApi subclass (RtApiOss, RtApiAlsa,
@@ -123,36 +524,38 @@ struct RtAudioDeviceInfo {
 //
 // **************************************************************** //
 
+#if defined( HAVE_GETTIMEOFDAY )
+  #include <sys/time.h>
+#endif
+
+#include <sstream>
+
 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;
-  virtual void stopStream() = 0;
-  virtual void abortStream() = 0;
+  virtual RtAudio::Api getCurrentApi( void ) = 0;
+  virtual unsigned int getDeviceCount( void ) = 0;
+  virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0;
+  virtual unsigned int getDefaultInputDevice( void );
+  virtual unsigned int getDefaultOutputDevice( void );
+  void openStream( RtAudio::StreamParameters *outputParameters,
+                   RtAudio::StreamParameters *inputParameters,
+                   RtAudioFormat format, unsigned int sampleRate,
+                   unsigned int *bufferFrames, RtAudioCallback callback,
+                   void *userData, RtAudio::StreamOptions *options );
+  virtual void closeStream( void );
+  virtual void startStream( void ) = 0;
+  virtual void stopStream( void ) = 0;
+  virtual void abortStream( void ) = 0;
+  long getStreamLatency( void );
+  virtual double getStreamTime( void );
+  bool isStreamOpen( void ) { return stream_.state != STREAM_CLOSED; };
+  bool isStreamRunning( void ) { return stream_.state == STREAM_RUNNING; };
+  void showWarnings( bool value ) { showWarnings_ = value; };
+
 
 protected:
 
@@ -161,6 +564,18 @@ protected:
 
   enum { FAILURE, SUCCESS };
 
+  enum ErrorType {
+    WARNING,
+    INVALID_CALL,
+    SYSTEM
+  };
+
+  enum StreamState {
+    STREAM_STOPPED,
+    STREAM_RUNNING,
+    STREAM_CLOSED = -50
+  };
+
   enum StreamMode {
     OUTPUT,
     INPUT,
@@ -179,51 +594,36 @@ protected:
 
   // A protected structure for audio streams.
   struct RtApiStream {
-    int device[2];          // Playback and record, respectively.
-    void *apiHandle;        // void pointer for API specific stream handle information
-    StreamMode mode;         // OUTPUT, INPUT, or DUPLEX.
-    StreamState state;       // STOPPED or RUNNING
-    char *userBuffer;
+    unsigned int device[2];    // Playback and record, respectively.
+    void *apiHandle;           // void pointer for API specific stream handle information
+    StreamMode mode;           // OUTPUT, INPUT, or DUPLEX.
+    StreamState state;         // STOPPED, RUNNING, or CLOSED
+    char *userBuffer[2];       // Playback and record, respectively.
     char *deviceBuffer;
-    bool doConvertBuffer[2]; // Playback and record, respectively.
-    bool deInterleave[2];    // Playback and record, respectively.
-    bool doByteSwap[2];      // Playback and record, respectively.
-    int sampleRate;
-    int bufferSize;
-    int nBuffers;
-    int nUserChannels[2];    // Playback and record, respectively.
-    int nDeviceChannels[2];  // Playback and record channels, respectively.
+    bool doConvertBuffer[2];   // Playback and record, respectively.
+    bool userInterleaved;
+    bool deviceInterleaved[2]; // Playback and record, respectively.
+    bool doByteSwap[2];        // Playback and record, respectively.
+    unsigned int sampleRate;
+    unsigned int bufferSize;
+    unsigned int nBuffers;
+    unsigned int nUserChannels[2];    // Playback and record, respectively.
+    unsigned int nDeviceChannels[2];  // Playback and record channels, respectively.
+    unsigned int channelOffset[2];    // Playback and record, respectively.
+    unsigned long latency[2];         // Playback and record, respectively.
     RtAudioFormat userFormat;
-    RtAudioFormat deviceFormat[2]; // Playback and record, respectively.
+    RtAudioFormat deviceFormat[2];    // Playback and record, respectively.
     StreamMutex mutex;
     CallbackInfo callbackInfo;
     ConvertInfo convertInfo[2];
+    double streamTime;         // Number of elapsed seconds since the stream started.
 
-    RtApiStream()
-      :apiHandle(0), userBuffer(0), deviceBuffer(0) {}
-  };
-
-  // A protected device structure for audio devices.
-  struct RtApiDevice {
-    std::string name;      /*!< Character string device identifier. */
-    bool probed;           /*!< true if the device capabilities were successfully probed. */
-    void *apiDeviceId;     // void pointer for API specific device information
-    int maxOutputChannels; /*!< Maximum output channels supported by device. */
-    int maxInputChannels;  /*!< Maximum input channels supported by device. */
-    int maxDuplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */
-    int minOutputChannels; /*!< Minimum output channels supported by device. */
-    int minInputChannels;  /*!< Minimum input channels supported by device. */
-    int minDuplexChannels; /*!< Minimum simultaneous input/output channels supported by device. */
-    bool hasDuplexSupport; /*!< true if device supports duplex mode. */
-    bool isDefault;        /*!< true if this is the default output or input device. */
-    std::vector<int> sampleRates; /*!< Supported sample rates. */
-    RtAudioFormat nativeFormats;  /*!< Bit mask of supported data formats. */
+#if defined(HAVE_GETTIMEOFDAY)
+    struct timeval lastTickTimestamp;
+#endif
 
-    // Default constructor.
-    RtApiDevice()
-      :probed(false), apiDeviceId(0), maxOutputChannels(0), maxInputChannels(0),
-       maxDuplexChannels(0), minOutputChannels(0), minInputChannels(0),
-       minDuplexChannels(0), isDefault(false), nativeFormats(0) {}
+    RtApiStream()
+      :apiHandle(0), deviceBuffer(0) {}
   };
 
   typedef signed short Int16;
@@ -231,65 +631,37 @@ protected:
   typedef float Float32;
   typedef double Float64;
 
-  char message_[1024];
-  int nDevices_;
-  std::vector<RtApiDevice> devices_;
+  std::ostringstream errorStream_;
+  std::string errorText_;
+  bool showWarnings_;
   RtApiStream stream_;
 
   /*!
-    Protected, api-specific method to count and identify the system
-    audio devices.  This function MUST be implemented by all subclasses.
-  */
-  virtual void initialize(void) = 0;
-
-  /*!
-    Protected, api-specific method which attempts to fill an
-    RtAudioDevice structure for a given device.  This function MUST be
-    implemented by all subclasses.  If an error is encountered during
-    the probe, a "warning" message is reported and the value of
-    "probed" remains false (no exception is thrown).  A successful
-    probe is indicated by probed = true.
-  */
-  virtual void probeDeviceInfo( RtApiDevice *info );
-
-  /*!
-    Protected, api-specific method which attempts to open a device
+    Protected, api-specific method that attempts to open a device
     with the given parameters.  This function MUST be implemented by
     all subclasses.  If an error is encountered during the probe, a
-    "warning" message is reported and FAILURE is returned (no
-    exception is thrown). A successful probe is indicated by a return
-    value of SUCCESS.
+    "warning" message is reported and FAILURE is returned. A
+    successful probe is indicated by a return value of SUCCESS.
   */
-  virtual bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                                int sampleRate, RtAudioFormat format,
-                                int *bufferSize, int numberOfBuffers );
+  virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                                unsigned int firstChannel, unsigned int sampleRate,
+                                RtAudioFormat format, unsigned int *bufferSize,
+                                RtAudio::StreamOptions *options );
 
-  /*!
-    Protected method which returns the index in the devices array to
-    the default input device.
-  */
-  virtual int getDefaultInputDevice(void);
-
-  /*!
-    Protected method which returns the index in the devices array to
-    the default output device.
-  */
-  virtual int getDefaultOutputDevice(void);
-
-  //! Protected common method to clear an RtApiDevice structure.
-  void clearDeviceInfo( RtApiDevice *info );
+  //! A protected function used to increment the stream time.
+  void tickStreamTime( void );
 
   //! Protected common method to clear an RtApiStream structure.
   void clearStreamInfo();
 
-  //! Protected common error method to allow global control over error handling.
-  void error( RtError::Type type );
-
   /*!
-    Protected common method used to check whether a stream is open.
-    If not, an "invalid identifier" exception is thrown.
+    Protected common method that throws an RtError (type =
+    INVALID_USE) if a stream is not open.
   */
-  void verifyStream();
+  void verifyStream( void );
+
+  //! Protected common error method to allow global control over error handling.
+  void error( ErrorType type );
 
   /*!
     Protected method used to perform format, channel number, and/or interleaving
@@ -298,270 +670,79 @@ protected:
   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 );
+  void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format );
 
-  //! Protected common method which returns the number of bytes for a given format.
-  int formatBytes( RtAudioFormat format );
-};
+  //! Protected common method that returns the number of bytes for a given format.
+  unsigned int formatBytes( RtAudioFormat format );
 
+  //! Protected common method that sets up the parameters for buffer conversion.
+  void setConvertInfo( StreamMode mode, unsigned int firstChannel );
+};
 
 // **************************************************************** //
 //
-// RtAudio class declaration.
-//
-// RtAudio is a "controller" used to select an available audio i/o
-// interface.  It presents a common API for the user to call but all
-// functionality is implemented by the class RtAudioApi and its
-// subclasses.  RtAudio creates an instance of an RtAudioApi subclass
-// based on the user's API choice.  If no choice is made, RtAudio
-// attempts to make a "logical" API selection.
+// Inline RtAudio definitions.
 //
 // **************************************************************** //
 
-class RtAudio
-{
-public:
-
-  //! Audio API specifier arguments.
-  enum RtAudioApi {
-    UNSPECIFIED,    /*!< Search for a working compiled API. */
-    LINUX_ALSA,     /*!< The Advanced Linux Sound Architecture API. */
-    LINUX_OSS,      /*!< The Linux Open Sound System API. */
-    LINUX_JACK,     /*!< The Linux Jack Low-Latency Audio Server API. */
-    MACOSX_CORE,    /*!< Macintosh OS-X Core Audio API. */
-    IRIX_AL,        /*!< The Irix Audio Library API. */
-    WINDOWS_ASIO,   /*!< The Steinberg Audio Stream I/O API. */
-    WINDOWS_DS      /*!< The Microsoft Direct Sound API. */
-  };
-
-  //! The default class constructor.
-  /*!
-    Probes the system to make sure at least one audio input/output
-    device is available and determines the api-specific identifier for
-    each device found.  An RtError error can be thrown if no devices
-    are found or if a memory allocation error occurs.
-
-    If no API argument is specified and multiple API support has been
-    compiled, the default order of use is JACK, ALSA, OSS (Linux
-    systems) and ASIO, DS (Windows systems).
-  */
-  RtAudio( RtAudioApi api=UNSPECIFIED );
-
-  //! A constructor which can be used to open a stream during instantiation.
-  /*!
-    The specified output and/or input device identifiers correspond
-    to those enumerated via the getDeviceInfo() method.  If device =
-    0, the default or first available devices meeting the given
-    parameters is selected.  If an output or input channel value is
-    zero, the corresponding device value is ignored.  When a stream is
-    successfully opened, its identifier is returned via the "streamId"
-    pointer.  An RtError can be thrown if no devices are found
-    for the given parameters, if a memory allocation error occurs, or
-    if a driver error occurs. \sa openStream()
-  */
-  RtAudio( int outputDevice, int outputChannels,
-           int inputDevice, int inputChannels,
-           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
-    buffer and structure memory.
-  */
-  ~RtAudio();
-
-  //! A public method for opening a stream with the specified parameters.
-  /*!
-    An RtError is thrown if a stream cannot be opened.
-
-    \param outputDevice: If equal to 0, the default or first device
-           found meeting the given parameters is opened.  Otherwise, the
-           device number should correspond to one of those enumerated via
-           the getDeviceInfo() method.
-    \param outputChannels: The desired number of output channels.  If
-           equal to zero, the outputDevice identifier is ignored.
-    \param inputDevice: If equal to 0, the default or first device
-           found meeting the given parameters is opened.  Otherwise, the
-           device number should correspond to one of those enumerated via
-           the getDeviceInfo() method.
-    \param inputChannels: The desired number of input channels.  If
-           equal to zero, the inputDevice identifier is ignored.
-    \param format: An RtAudioFormat specifying the desired sample data format.
-    \param sampleRate: The desired sample rate (sample frames per second).
-    \param *bufferSize: A pointer value indicating the desired internal buffer
-           size in sample frames.  The actual value used by the device is
-           returned via the same pointer.  A value of zero can be specified,
-           in which case the lowest allowable value is determined.
-    \param numberOfBuffers: A value which can be used to help control device
-           latency.  More buffers typically result in more robust performance,
-           though at a cost of greater latency.  A value of zero can be
-           specified, in which case the lowest allowable value is used.
-  */
-  void openStream( int outputDevice, int outputChannels,
-                   int inputDevice, int inputChannels,
-                   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
-    stream for non-blocking stream functionality.  A separate process
-    is initiated, though the user function is called only when the
-    stream is "running" (between calls to the startStream() and
-    stopStream() methods, respectively).  The callback process remains
-    active for the duration of the stream and is automatically
-    shutdown when the stream is closed (via the closeStream() method
-    or by object destruction).  The callback process can also be
-    shutdown and the user function de-referenced through an explicit
-    call to the cancelStreamCallback() method.  Note that the stream
-    can use only blocking or callback functionality at a particular
-    time, though it is possible to alternate modes on the same stream
-    through the use of the setStreamCallback() and
-    cancelStreamCallback() methods (the blocking tickStream() method
-    can be used before a callback is set and/or after a callback is
-    cancelled).  An RtError will be thrown if called when no stream is
-    open or a thread errors occurs.
-  */
-  void setStreamCallback(RtAudioCallback callback, void *userData) { rtapi_->setStreamCallback( callback, userData ); };
-
-  //! A public method which cancels a callback process and function for the stream.
-  /*!
-    This method shuts down a callback process and de-references the
-    user function for the stream.  Callback functionality can
-    subsequently be restarted on the stream via the
-    setStreamCallback() method.  An RtError will be thrown if called
-    when no stream is open.
-   */
-  void cancelStreamCallback() { rtapi_->cancelStreamCallback(); };
-
-  //! A public method which returns the number of audio devices found.
-  int getDeviceCount(void) { return rtapi_->getDeviceCount(); };
-
-  //! Return an RtAudioDeviceInfo structure for a specified device number.
-  /*!
-    Any device integer between 1 and getDeviceCount() is valid.  If
-    a device is busy or otherwise unavailable, the structure member
-    "probed" will have a value of "false" and all other members are
-    undefined.  If the specified device is the current default input
-    or output device, the "isDefault" member will have a value of
-    "true".  An RtError will be thrown for an invalid device argument.
-  */
-  RtAudioDeviceInfo getDeviceInfo(int device) { return rtapi_->getDeviceInfo( device ); };
-
-  //! A public method which returns a pointer to the buffer for an open stream.
-  /*!
-    The user should fill and/or read the buffer data in interleaved format
-    and then call the tickStream() method.  An RtError will be
-    thrown if called when no stream is open.
-  */
-  char * const getStreamBuffer() { return rtapi_->getStreamBuffer(); };
-
-  //! Public method used to trigger processing of input/output data for a stream.
-  /*!
-    This method blocks until all buffer data is read/written.  An
-    RtError will be thrown if a driver error occurs or if called when
-    no stream is open.
-  */
-  void tickStream() { rtapi_->tickStream(); };
-
-  //! Public method which closes a stream and frees any associated buffers.
-  /*!
-    If a stream is not open, this method issues a warning and
-    returns (an RtError is not thrown).
-  */
-  void closeStream()  { rtapi_->closeStream(); };
-
-  //! Public method which starts a stream.
-  /*!
-    An RtError will be thrown if a driver error occurs or if called
-    when no stream is open.
-  */
-  void startStream() { rtapi_->startStream(); };
-
-  //! Stop a stream, allowing any samples remaining in the queue to be played out and/or read in.
-  /*!
-    An RtError will be thrown if a driver error occurs or if called
-    when no stream is open.
-  */
-  void stopStream() { rtapi_->stopStream(); };
-
-  //! Stop a stream, discarding any samples remaining in the input/output queue.
-  /*!
-    An RtError will be thrown if a driver error occurs or if called
-    when no stream is open.
-  */
-  void abortStream() { rtapi_->abortStream(); };
-
-
- protected:
-
-  void initialize( RtAudioApi api );
-
-  RtApi *rtapi_;
-};
-
+inline RtAudio::Api RtAudio :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); };
+inline unsigned int RtAudio :: getDeviceCount( void ) throw() { return rtapi_->getDeviceCount(); };
+inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); };
+inline unsigned int RtAudio :: getDefaultInputDevice( void ) throw() { return rtapi_->getDefaultInputDevice(); };
+inline unsigned int RtAudio :: getDefaultOutputDevice( void ) throw() { return rtapi_->getDefaultOutputDevice(); };
+inline void RtAudio :: closeStream( void ) throw() { return rtapi_->closeStream(); };
+inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); };
+inline void RtAudio :: stopStream( void )  { return rtapi_->stopStream(); };
+inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); };
+inline bool RtAudio :: isStreamOpen( void ) throw() { return rtapi_->isStreamOpen(); };
+inline bool RtAudio :: isStreamRunning( void ) throw() { return rtapi_->isStreamRunning(); };
+inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); };
+inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); };
+inline void RtAudio :: showWarnings( bool value ) throw() { rtapi_->showWarnings( value ); };
 
 // RtApi Subclass prototypes.
 
-#if defined(__LINUX_ALSA__)
+#if defined(__MACOSX_CORE__)
 
-class RtApiAlsa: public RtApi
+#include <CoreAudio/AudioHardware.h>
+
+class RtApiCore: public RtApi
 {
 public:
 
-  RtApiAlsa();
-  ~RtApiAlsa();
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  int streamWillBlock();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtApiCore();
+  ~RtApiCore();
+  RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; };
+  unsigned int getDeviceCount( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  unsigned int getDefaultOutputDevice( void );
+  unsigned int getDefaultInputDevice( void );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
+  long getStreamLatency( void );
+
+  // This function is intended for internal use only.  It must be
+  // public because it is called by the internal callback handler,
+  // which is not a member of RtAudio.  External use of this function
+  // will most likely produce highly undesireable results!
+  bool callbackEvent( AudioDeviceID deviceId,
+                      const AudioBufferList *inBufferList,
+                      const AudioBufferList *outBufferList );
 
   private:
 
-  void initialize(void);
-  bool primeOutputBuffer();
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
+  static const char* getErrorCode( OSStatus code );
 };
 
 #endif
 
-#if defined(__LINUX_JACK__)
+#if defined(__UNIX_JACK__)
 
 class RtApiJack: public RtApi
 {
@@ -569,245 +750,199 @@ public:
 
   RtApiJack();
   ~RtApiJack();
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; };
+  unsigned int getDeviceCount( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
+  long getStreamLatency( void );
+
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
   // which is not a member of RtAudio.  External use of this function
   // will most likely produce highly undesireable results!
-  void callbackEvent( unsigned long nframes );
+  bool callbackEvent( unsigned long nframes );
 
   private:
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
 };
 
 #endif
 
-#if defined(__LINUX_OSS__)
+#if defined(__WINDOWS_ASIO__)
 
-class RtApiOss: public RtApi
+class RtApiAsio: public RtApi
 {
 public:
 
-  RtApiOss();
-  ~RtApiOss();
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  int streamWillBlock();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtApiAsio();
+  ~RtApiAsio();
+  RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; };
+  unsigned int getDeviceCount( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
+  long getStreamLatency( void );
+
+  // This function is intended for internal use only.  It must be
+  // public because it is called by the internal callback handler,
+  // which is not a member of RtAudio.  External use of this function
+  // will most likely produce highly undesireable results!
+  bool callbackEvent( long bufferIndex );
 
   private:
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  bool coInitialized_;
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
 };
 
 #endif
 
-#if defined(__MACOSX_CORE__)
-
-#include <CoreAudio/AudioHardware.h>
+#if defined(__WINDOWS_DS__)
 
-class RtApiCore: public RtApi
+class RtApiDs: public RtApi
 {
 public:
 
-  RtApiCore();
-  ~RtApiCore();
-  int getDefaultOutputDevice(void);
-  int getDefaultInputDevice(void);
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtApiDs();
+  ~RtApiDs();
+  RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; };
+  unsigned int getDeviceCount( void );
+  unsigned int getDefaultOutputDevice( void );
+  unsigned int getDefaultInputDevice( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
+  long getStreamLatency( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
   // which is not a member of RtAudio.  External use of this function
   // will most likely produce highly undesireable results!
-  void callbackEvent( AudioDeviceID deviceId, void *inData, void *outData );
+  void callbackEvent( void );
 
   private:
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  bool coInitialized_;
+  bool buffersRolling;
+  long duplexPrerollBytes;
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
 };
 
 #endif
 
-#if defined(__WINDOWS_DS__)
+#if defined(__LINUX_ALSA__)
 
-class RtApiDs: public RtApi
+class RtApiAlsa: public RtApi
 {
 public:
 
-  RtApiDs();
-  ~RtApiDs();
-  int getDefaultOutputDevice(void);
-  int getDefaultInputDevice(void);
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  int streamWillBlock();
-  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:
+  RtApiAlsa();
+  ~RtApiAlsa();
+  RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; };
+  unsigned int getDeviceCount( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  // This function is intended for internal use only.  It must be
+  // public because it is called by the internal callback handler,
+  // which is not a member of RtAudio.  External use of this function
+  // will most likely produce highly undesireable results!
+  void callbackEvent( void );
 
-  bool coInitialized;
-  bool buffersRolling;
-  long duplexPrerollBytes;
-  static RtDsStatistics statistics;
+  private:
 
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
 };
 
 #endif
 
-#if defined(__WINDOWS_ASIO__)
+#if defined(__LINUX_OSS__)
 
-class RtApiAsio: public RtApi
+class RtApiOss: public RtApi
 {
 public:
 
-  RtApiAsio();
-  ~RtApiAsio();
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtApiOss();
+  ~RtApiOss();
+  RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; };
+  unsigned int getDeviceCount( void );
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
+  void closeStream( void );
+  void startStream( void );
+  void stopStream( void );
+  void abortStream( void );
 
   // This function is intended for internal use only.  It must be
   // public because it is called by the internal callback handler,
   // which is not a member of RtAudio.  External use of this function
   // will most likely produce highly undesireable results!
-  void callbackEvent( long bufferIndex );
+  void callbackEvent( void );
 
   private:
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
-
-  bool coInitialized;
-
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options );
 };
 
 #endif
 
-#if defined(__IRIX_AL__)
+#if defined(__RTAUDIO_DUMMY__)
 
-class RtApiAl: public RtApi
+class RtApiDummy: public RtApi
 {
 public:
 
-  RtApiAl();
-  ~RtApiAl();
-  int getDefaultOutputDevice(void);
-  int getDefaultInputDevice(void);
-  void tickStream();
-  void closeStream();
-  void startStream();
-  void stopStream();
-  void abortStream();
-  int streamWillBlock();
-  void setStreamCallback( RtAudioCallback callback, void *userData );
-  void cancelStreamCallback();
+  RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( WARNING ); };
+  RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; };
+  unsigned int getDeviceCount( void ) { return 0; };
+  RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) { RtAudio::DeviceInfo info; return info; };
+  void closeStream( void ) {};
+  void startStream( void ) {};
+  void stopStream( void ) {};
+  void abortStream( void ) {};
 
   private:
 
-  void initialize(void);
-  void probeDeviceInfo( RtApiDevice *info );
-  bool probeDeviceOpen( int device, StreamMode mode, int channels, 
-                        int sampleRate, RtAudioFormat format,
-                        int *bufferSize, int numberOfBuffers );
+  bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, 
+                        unsigned int firstChannel, unsigned int sampleRate,
+                        RtAudioFormat format, unsigned int *bufferSize,
+                        RtAudio::StreamOptions *options ) { return false; };
 };
 
 #endif
 
-// Define the following flag to have extra information spewed to stderr.
-//#define __RTAUDIO_DEBUG__
-
 #endif
+
+// Indentation settings for Vim and Emacs
+//
+// Local Variables:
+// c-basic-offset: 2
+// indent-tabs-mode: nil
+// End:
+//
+// vim: et sts=2 sw=2
index cb1283df33fea56067162b079d5734d2d7382982..4c4f3c4178708f0f9dfe2e3a592e73eec8ab7dc5 100644 (file)
--- a/RtError.h
+++ b/RtError.h
 #ifndef RTERROR_H
 #define RTERROR_H
 
+#include <exception>
 #include <iostream>
 #include <string>
 
-class RtError
+class RtError : public std::exception
 {
-public:
+ public:
   //! Defined RtError types.
   enum Type {
-    WARNING,           /*!< A non-critical error. */
-    DEBUG_WARNING,     /*!< A non-critical error which might be useful for debugging. */
     UNSPECIFIED,       /*!< The default, unspecified error type. */
     NO_DEVICES_FOUND,  /*!< No devices found on system. */
     INVALID_DEVICE,    /*!< An invalid device ID was specified. */
-    INVALID_STREAM,    /*!< An invalid stream ID was specified. */
     MEMORY_ERROR,      /*!< An error occured during memory allocation. */
     INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */
+    INVALID_USE,       /*!< The function was called incorrectly. */
     DRIVER_ERROR,      /*!< A system driver error occured. */
     SYSTEM_ERROR,      /*!< A system error occured. */
     THREAD_ERROR       /*!< A thread error occured. */
   };
 
-protected:
-  std::string message_;
-  Type type_;
-
-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 ) throw() : message_(message), type_(type) {}
   //! The destructor.
-  virtual ~RtError(void) {};
+  virtual ~RtError( void ) throw() {}
 
   //! Prints thrown error message to stderr.
-  virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; }
+  virtual void printMessage( void ) throw() { std::cerr << '\n' << message_ << "\n\n"; }
 
   //! Returns the thrown error message type.
-  virtual const Type& getType(void) { return type_; }
+  virtual const Type& getType(void) throw() { return type_; }
 
   //! Returns the thrown error message string.
-  virtual const std::string& getMessage(void) { return message_; }
+  virtual const std::string& getMessage(void) throw() { return message_; }
 
-  //! Returns the thrown error message as a C string.
-  virtual const char *getMessageString(void) { return message_.c_str(); }
+  //! Returns the thrown error message as a c-style string.
+  virtual const char* what( void ) const throw() { return message_.c_str(); }
+
+ protected:
+  std::string message_;
+  Type type_;
 };
 
 #endif
index 83723fd5ce98c5f83231cbeecac81ebe19a1bb88..7141656d6019a776e193b88ab1f1e4f26342ee33 100644 (file)
@@ -1,7 +1,7 @@
 # Process this file with autoconf to produce a configure script.
-AC_INIT(RtAudio, 3.0, gary@ccrma.stanford.edu, rtaudio)
+AC_INIT(RtAudio, 4.0, gary@music.mcgill.ca, rtaudio)
 AC_CONFIG_SRCDIR(RtAudio.cpp)
-AC_CONFIG_FILES(tests/Makefile)
+AC_CONFIG_FILES([rtaudio-config Makefile tests/Makefile])
 
 # Fill GXX with something before test.
 AC_SUBST( GXX, ["no"] )
@@ -9,10 +9,17 @@ AC_SUBST( GXX, ["no"] )
 # Checks for programs.
 AC_PROG_CC
 AC_PROG_CXX(g++ CC c++ cxx)
+AC_PROG_RANLIB
+AC_PATH_PROG(AR, ar, no)
+if [[ $AR = "no" ]] ; then
+    AC_MSG_ERROR("Could not find ar - needed to create a library");
+fi
 
 # Checks for libraries.
 AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR(RtAudio requires the pthread library!))
 
+
+
 # Checks for header files.
 AC_HEADER_STDC
 AC_CHECK_HEADERS(sys/ioctl.h unistd.h)
@@ -27,6 +34,9 @@ AC_ARG_ENABLE(debug,
   [AC_SUBST( debug, [-D__RTAUDIO_DEBUG__] ) AC_SUBST( cflags, [-g] ) AC_SUBST( object_path, [Debug] ) AC_MSG_RESULT(yes)],
   [AC_SUBST( debug, [] ) AC_SUBST( cflags, [-O2] ) AC_SUBST( object_path, [Release] ) AC_MSG_RESULT(no)])
 
+# Checks for functions
+AC_CHECK_FUNC(gettimeofday, [cflags=$cflags" -DHAVE_GETTIMEOFDAY"], )
+
 # Check compiler and use -Wall if gnu.
 if [test $GXX = "yes" ;] then
   AC_SUBST( warn, [-Wall] )
@@ -36,15 +46,22 @@ fi
 AC_CANONICAL_HOST
 AC_MSG_CHECKING(for audio API)
 case $host in
+  *-*-netbsd*)
+    AC_SUBST( sound_api, [-D__LINUX_OSS__] )
+    AC_MSG_RESULT(using OSS)
+    AC_SUBST( audio_apis, [-D__LINUX_OSS__] )
+    cflags=$cflags" -lossaudio"
+  ;;
+
   *-*-linux*)
   AC_SUBST( sound_api, [_NO_API_] )
-  AC_ARG_WITH(jack, [  --with-jack = choose JACK server support (linux only)], [AC_SUBST( sound_api, [-D__LINUX_JACK__] ) AC_MSG_RESULT(using JACK)], )
-  if [test $sound_api = -D__LINUX_JACK__;] then
+  AC_ARG_WITH(jack, [  --with-jack = choose JACK server support (mac and linux only)], [AC_SUBST( sound_api, [-D__UNIX_JACK__] ) AC_MSG_RESULT(using JACK)], )
+  if [test $sound_api = -D__UNIX_JACK__;] then
     TEMP_LIBS=$LIBS
     AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!))
     AC_CHECK_LIB(asound, snd_pcm_open, , AC_MSG_ERROR(Jack support also requires the asound library!))
     LIBS="`pkg-config --cflags --libs jack` $TEMP_LIBS -lasound"
-    audio_apis="-D__LINUX_JACK__"
+    audio_apis="-D__UNIX_JACK__"
   fi
 
   # Look for Alsa flag
@@ -75,11 +92,30 @@ case $host in
   ;;
 
   *-apple*)
-  # Check for CoreAudio framework
-  AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
-    [AC_SUBST( audio_apis, [-D__MACOSX_CORE__] )],
-    [AC_MSG_ERROR(CoreAudio header files not found!)] )
-  AC_SUBST( frameworks, ["-framework CoreAudio"] )
+  AC_SUBST( sound_api, [_NO_API_] )
+  AC_ARG_WITH(jack, [  --with-jack = choose JACK server support (unix only)], [AC_SUBST( sound_api, [-D__UNIX_JACK__] ) AC_MSG_RESULT(using JACK)], )
+  if [test $sound_api = -D__UNIX_JACK__;] then
+    AC_CHECK_LIB(jack, jack_client_new, , AC_MSG_ERROR(JACK support requires the jack library!))
+    audio_apis="-D__UNIX_JACK__"
+  fi
+
+  # Look for Core flag
+  AC_ARG_WITH(core, [  --with-core = choose CoreAudio API support (mac only)], [AC_SUBST( sound_api, [-D__MACOSX_CORE__] ) AC_MSG_RESULT(using CoreAudio)], )
+  if test $sound_api = -D__MACOSX_CORE__; then
+    AC_CHECK_HEADER(CoreAudio/CoreAudio.h, [], [AC_MSG_ERROR(CoreAudio header files not found!)] )
+    AC_SUBST( frameworks, ["-framework CoreAudio -framework CoreFoundation"] )
+    audio_apis="-D__MACOSX_CORE__ $audio_apis"
+  fi
+
+  # If no audio api flags specified, use CoreAudio
+  if [test $sound_api = _NO_API_;] then
+    AC_SUBST( sound_api, [-D__MACOSX_CORE__] )
+    AC_MSG_RESULT(using CoreAudio)
+    AC_CHECK_HEADER(CoreAudio/CoreAudio.h,
+      [AC_SUBST( audio_apis, [-D__MACOSX_CORE__] )],
+      [AC_MSG_ERROR(CoreAudio header files not found!)] )
+    AC_SUBST( frameworks, ["-framework CoreAudio -framework CoreFoundation"] )
+  fi
   ;;
 
   *)
@@ -92,3 +128,5 @@ esac
 AC_PROG_GCC_TRADITIONAL
 
 AC_OUTPUT
+
+chmod oug+x rtaudio-config
index a104bd48b4e05c1a257bd0be8d4155238573f38f..e4b0cef9487a912b621b1dd471e87484992c2dc4 100644 (file)
-# Doxyfile 1.2.6
+# Doxyfile 1.3.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
 
 #---------------------------------------------------------------------------
-# General configuration options
+# Project related configuration options
 #---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
 PROJECT_NAME           = RtAudio
-PROJECT_NUMBER         = 
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+PROJECT_NUMBER         = 4.0
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
 OUTPUT_DIRECTORY       = .
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, 
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en 
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese, 
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
 OUTPUT_LANGUAGE        = English
-EXTRACT_ALL            = NO
-EXTRACT_PRIVATE        = NO
-EXTRACT_STATIC         = NO
-HIDE_UNDOC_MEMBERS     = YES
-HIDE_UNDOC_CLASSES     = NO
+
+# This tag can be used to specify the encoding used in the generated output. 
+# The encoding is not always determined by the language that is chosen, 
+# but also whether or not the output is meant for Windows or non-Windows users. 
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES 
+# forces the Windows encoding (this is the default for the Windows binary), 
+# whereas setting the tag to NO uses a Unix-style encoding (the default for 
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
 BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
 REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
 ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited 
+# members of a class in the documentation of that class as if those members were 
+# ordinary class members. Constructors, destructors and assignment operators of 
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
 FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. It is allowed to use relative paths in the argument list.
+
 STRIP_FROM_PATH        = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like the Qt-style comments (thus requiring an 
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen 
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member 
+# documentation.
+
+DETAILS_AT_TOP         = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 8
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources 
+# only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources 
+# only. Doxygen will then generate output that is more tailored for Java. 
+# For instance, namespaces will be presented as packages, qualified scopes 
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = YES
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = YES
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
 INTERNAL_DOCS          = NO
-CLASS_DIAGRAMS         = YES
-SOURCE_BROWSER         = NO
-INLINE_SOURCES         = NO
-STRIP_CODE_COMMENTS    = YES
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are advised to set this option to NO.
+
 CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
 HIDE_SCOPE_NAMES       = NO
-VERBATIM_HEADERS       = YES
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
 SHOW_INCLUDE_FILES     = YES
-JAVADOC_AUTOBRIEF      = NO
-INHERIT_DOCS           = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
 INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
 SORT_MEMBER_DOCS       = NO
-DISTRIBUTE_GROUP_DOC   = NO
-TAB_SIZE               = 8
-ENABLED_SECTIONS       = 
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
 GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
 GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
 GENERATE_BUGLIST       = YES
-ALIASES                = 
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
 MAX_INITIALIZER_LINES  = 30
-OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
 SHOW_USED_FILES        = YES
+
 #---------------------------------------------------------------------------
 # configuration options related to warning and progress messages
 #---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
 QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
 WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
 WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
 WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
 WARN_LOGFILE           = 
+
 #---------------------------------------------------------------------------
 # configuration options related to the input files
 #---------------------------------------------------------------------------
-INPUT                  = tutorial.txt ../../RtAudio.h ../../RtError.h
-FILE_PATTERNS          = 
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = . \
+                         ../../RtAudio.h \
+                         ../../RtError.h
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp 
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS          = *.txt
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
 RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
 EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
 EXCLUDE_PATTERNS       = 
-EXAMPLE_PATH           = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = ../../tests/
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
 EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
 IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
 INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
 FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
 #---------------------------------------------------------------------------
 # configuration options related to the alphabetical class index
 #---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
 ALPHABETICAL_INDEX     = NO
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
 COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
 IGNORE_PREFIX          = 
+
 #---------------------------------------------------------------------------
 # configuration options related to the HTML output
 #---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
 GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
 HTML_OUTPUT            = ../html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
 HTML_HEADER            = header.html
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
 HTML_FOOTER            = footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
 HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
 HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
 GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
 GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
 BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
 TOC_EXPAND             = NO
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
 DISABLE_INDEX          = YES
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
 ENUM_VALUES_PER_LINE   = 4
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
 GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
 TREEVIEW_WIDTH         = 250
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
 GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
 LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
 COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
 PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
 EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
 LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
 PDF_HYPERLINKS         = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
 USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
 LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
 #---------------------------------------------------------------------------
 # configuration options related to the RTF output
 #---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
 GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
 RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
 COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
 RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
 RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
 #---------------------------------------------------------------------------
 # configuration options related to the man page output
 #---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
 GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
 MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
 MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
 #---------------------------------------------------------------------------
 # Configuration options related to the preprocessor   
 #---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
 ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
 MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
 EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
 SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
 INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
 INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed.
+
 PREDEFINED             = 
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
 EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
 #---------------------------------------------------------------------------
 # Configuration::addtions related to external references   
 #---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
 TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
 GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
 ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
 PERL_PATH              = /usr/bin/perl
+
 #---------------------------------------------------------------------------
 # Configuration options related to the dot tool   
 #---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or 
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
 HAVE_DOT               = NO
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
 CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
 COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
 INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
 INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
 GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
 DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
 MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
 MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
 GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
 DOT_CLEANUP            = YES
+
 #---------------------------------------------------------------------------
 # Configuration::addtions related to the search engine   
 #---------------------------------------------------------------------------
-SEARCHENGINE           = NO
-CGI_NAME               = search.cgi
-CGI_URL                = 
-DOC_URL                = 
-DOC_ABSPATH            = 
-BIN_ABSPATH            = /usr/local/bin/
-EXT_DOC_PATHS          = 
 
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/doc/doxygen/acknowledge.txt b/doc/doxygen/acknowledge.txt
new file mode 100644 (file)
index 0000000..1c42191
--- /dev/null
@@ -0,0 +1,15 @@
+/*! \page acknowledge Acknowledgements
+
+Many thanks to the following people for providing bug fixes and improvements:
+<UL>
+<LI>Robin Davies (Windows DS and ASIO)</LI>
+<LI>Ryan Williams (Windows non-MS compiler ASIO support)</LI>
+<LI>Ed Wildgoose (Linux ALSA and Jack)</LI>
+<LI>Dominic Mazzoni</LI>
+</UL>
+
+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>.  All recent versions of RtAudio have been completed 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).
+
+*/
diff --git a/doc/doxygen/apinotes.txt b/doc/doxygen/apinotes.txt
new file mode 100644 (file)
index 0000000..73e473e
--- /dev/null
@@ -0,0 +1,37 @@
+/*! \page apinotes API Notes
+
+RtAudio is designed to provide a common API across the various supported operating systems and audio libraries.  Despite that, some issues should be mentioned with regard to each.
+
+\section linux Linux:
+
+RtAudio for Linux was developed under Redhat distributions 7.0 - Fedora.  Three different audio APIs are supported on Linux platforms: <A href="http://www.opensound.com/oss.html">OSS</A> (versions >= 4.0), <A href="http://www.alsa-project.org/">ALSA</A>, and <A href="http://jackit.sourceforge.net/">Jack</A>.  Note that RtAudio now only supports the newer version 4.0 OSS API.  The ALSA API is now part of the Linux kernel and offers significantly better functionality than the OSS API.  RtAudio provides support for the 1.0 and higher versions of ALSA.  Jack is a low-latency audio server written primarily for the GNU/Linux operating system. It can connect a number of different applications to an audio device, as well as allow them to share audio between themselves.  Input/output latency on the order of 15 milliseconds can typically be achieved using any of the Linux APIs by fine-tuning the RtAudio buffer parameters (without kernel modifications).  Latencies on the order of 5 milliseconds or less can be achieved using a low-latency kernel patch and increasing FIFO scheduling priority.  The pthread library, which is used for callback functionality, is a standard component of all Linux distributions.
+
+The ALSA library includes OSS emulation support.  That means that you can run programs compiled for the OSS API even when using the ALSA drivers and library.  It should be noted however that OSS emulation under ALSA is not perfect.  Specifically, channel number queries seem to consistently produce invalid results.  While OSS emulation is successful for the majority of RtAudio tests, it is recommended that the native ALSA implementation of RtAudio be used on systems which have ALSA drivers installed.
+
+The ALSA implementation of RtAudio makes no use of the ALSA "plug" interface.  All necessary data format conversions, channel compensation, de-interleaving, and byte-swapping is handled by internal RtAudio routines.
+
+At the moment, only one RtAudio instance can be connected to the Jack server.
+
+\section macosx Macintosh OS-X (CoreAudio and Jack):
+
+The Apple CoreAudio API is designed to use a separate callback procedure for each of its audio devices.  A single RtAudio duplex stream using two different devices is supported, though it cannot be guaranteed to always behave correctly because we cannot synchronize these two callbacks.  The <I>numberOfBuffers</I> parameter to the RtAudio::openStream() function has no affect in this implementation.
+
+It is not possible to have multiple instances of RtAudio accessing the same CoreAudio device.
+
+The RtAudio Jack support can be compiled on Macintosh OS-X systems, as well as in Linux.
+
+\section windowsds Windows (DirectSound):
+
+In order to compile RtAudio under Windows for the DirectSound API, you must have the header and source files for DirectSound version 5.0 or higher.  As far as I know, there is no DirectSoundCapture support for Windows NT.  Audio output latency with DirectSound can be reasonably good, especially since RtAudio version 3.0.2.  Input audio latency still tends to be bad but better since version 3.0.2.  RtAudio was originally developed with Visual C++ version 6.0 but has been tested with .NET.
+
+The DirectSound version of RtAudio can be compiled with or without the UNICODE preprocessor definition.
+
+\section windowsasio Windows (ASIO):
+
+The Steinberg ASIO audio API allows only a single device driver to be loaded and accessed at a time.  ASIO device drivers must be supplied by audio hardware manufacturers, though ASIO emulation is possible on top of systems with DirectSound drivers.  The <I>numberOfBuffers</I> parameter to the RtAudio::openStream() function has no affect in this implementation.
+
+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; iasiothiscallresolver.h,cpp</TT>.  The Visual C++ projects found in <TT>/tests/Windows/</TT> compile both ASIO and DirectSound support.
+
+The Steinberg provided <TT>asiolist</TT> class does not compile when the preprocessor definition UNICODE is defined.  Note that this could be an issue when using RtAudio with Qt, though Qt programs appear to compile without the UNICODE definition (try <tt>DEFINES -= UNICODE</tt> in your .pro file).  RtAudio with ASIO support has been tested using the MinGW compiler under Windows XP, as well as in the Visual Studio environment.
+
+*/
diff --git a/doc/doxygen/compiling.txt b/doc/doxygen/compiling.txt
new file mode 100644 (file)
index 0000000..4cc769f
--- /dev/null
@@ -0,0 +1,76 @@
+/*! \page compiling Debugging & Compiling
+
+\section debug Debugging
+
+If you are having problems getting RtAudio to run on your system, make sure to pass a value of \e true to the RtAudio::showWarnings() function (this is the default setting).  A variety of warning messages will be displayed which may help in determining the problem.  Also, try using the programs included in the <tt>tests</tt> directory.  The program <tt>probe</tt> displays the queried capabilities of all hardware devices found for all APIs compiled.  When using the ALSA API, further information can be displayed by defining the preprocessor definition __RTAUDIO_DEBUG__.
+
+\section compile Compiling
+
+In order to compile RtAudio for a specific OS and audio API, it is necessary to supply the appropriate preprocessor definition and library within the compiler statement:
+<P>
+
+<TABLE BORDER=2 COLS=5 WIDTH="100%">
+<TR BGCOLOR="beige">
+  <TD WIDTH="5%"><B>OS:</B></TD>
+  <TD WIDTH="5%"><B>Audio API:</B></TD>
+  <TD WIDTH="5%"><B>C++ Class:</B></TD>
+  <TD WIDTH="5%"><B>Preprocessor Definition:</B></TD>
+  <TD WIDTH="5%"><B>Library or Framework:</B></TD>
+  <TD><B>Example Compiler Statement:</B></TD>
+</TR>
+<TR>
+  <TD>Linux</TD>
+  <TD>ALSA</TD>
+  <TD>RtApiAlsa</TD>
+  <TD>__LINUX_ALSA__</TD>
+  <TD><TT>asound, pthread</TT></TD>
+  <TD><TT>g++ -Wall -D__LINUX_ALSA__ -o probe probe.cpp RtAudio.cpp -lasound -lpthread</TT></TD>
+</TR>
+<TR>
+  <TD>Linux</TD>
+  <TD>OSS</TD>
+  <TD>RtApiOss</TD>
+  <TD>__LINUX_OSS__</TD>
+  <TD><TT>pthread</TT></TD>
+  <TD><TT>g++ -Wall -D__LINUX_OSS__ -o probe probe.cpp RtAudio.cpp -lpthread</TT></TD>
+</TR>
+<TR>
+  <TD>Linux or Macintosh OS-X</TD>
+  <TD>Jack Audio Server</TD>
+  <TD>RtApiJack</TD>
+  <TD>__UNIX_JACK__</TD>
+  <TD><TT>jack, pthread</TT></TD>
+  <TD><TT>g++ -Wall -D__UNIX_JACK__ -o probe probe.cpp RtAudio.cpp `pkg-config --cflags --libs jack` -lpthread</TT></TD>
+</TR>
+
+<TR>
+  <TD>Macintosh OS-X</TD>
+  <TD>CoreAudio</TD>
+  <TD>RtApiCore</TD>
+  <TD>__MACOSX_CORE__</TD>
+  <TD><TT>pthread, CoreAudio</TT></TD>
+  <TD><TT>g++ -Wall -D__MACOSX_CORE__ -o probe probe.cpp RtAudio.cpp -framework CoreAudio -lpthread</TT></TD>
+</TR>
+<TR>
+  <TD>Windows</TD>
+  <TD>Direct Sound</TD>
+  <TD>RtApiDs</TD>
+  <TD>__WINDOWS_DS__</TD>
+  <TD><TT>dsound.lib (ver. 5.0 or higher), multithreaded</TT></TD>
+  <TD><I>compiler specific</I></TD>
+</TR>
+<TR>
+  <TD>Windows</TD>
+  <TD>ASIO</TD>
+  <TD>RtApiAsio</TD>
+  <TD>__WINDOWS_ASIO__</TD>
+  <TD><I>various ASIO header and source files</I></TD>
+  <TD><I>compiler specific</I></TD>
+</TR>
+</TABLE>
+<P>
+
+The example compiler statements above could be used to compile the <TT>probe.cpp</TT> example file, assuming that <TT>probe.cpp</TT>, <TT>RtAudio.h</TT>, <tt>RtError.h</tt>, and <TT>RtAudio.cpp</TT> all exist in the same directory.
+
+
+*/
diff --git a/doc/doxygen/duplex.txt b/doc/doxygen/duplex.txt
new file mode 100644 (file)
index 0000000..f060602
--- /dev/null
@@ -0,0 +1,74 @@
+/*! \page duplex Duplex Mode
+
+Finally, it is easy to use RtAudio for simultaneous audio input/output, or duplex operation.  In this example, we simply pass the input data back to the output.
+
+\code
+#include "RtAudio.h"
+#include <iostream>
+
+// Pass-through function.
+int inout( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+           double streamTime, RtAudioStreamStatus status, void *data )
+{
+  // Since the number of input and output channels is equal, we can do
+  // a simple buffer copy operation here.
+  if ( status ) std::cout << "Stream over/underflow detected." << std::endl;
+
+  unsigned long *bytes = (unsigned long *) data;
+  memcpy( outputBuffer, inputBuffer, *bytes );
+  return 0;
+}
+
+int main()
+{
+ RtAudio adac;
+  if ( adac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  // Set the same number of channels for both input and output.
+  unsigned int bufferBytes, bufferFrames = 512;
+  RtAudio::StreamParameters iParams, oParams;
+  iParams.deviceId = 0; // first available device
+  iParams.nChannels = 2;
+  oParams.deviceId = 0; // first available device
+  oParams.nChannels = 2;
+
+  try {
+    adac.openStream( &oParams, &iParams, RTAUDIO_SINT32, 44100, &bufferFrames, &inout, (void *)&bufferBytes );
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    exit( 0 );
+  }
+
+  bufferBytes = bufferFrames * 2 * 4;
+
+  try {
+    adac.startStream();
+
+    char input;
+    std::cout << "\nRunning ... press <enter> to quit.\n";
+    std::cin.get(input);
+
+    // Stop the stream.
+    adac.stopStream();
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    goto cleanup;
+  }
+
+ cleanup:
+  if ( adac.isStreamOpen() ) adac.closeStream();
+
+  return 0;
+}
+\endcode
+
+In this example, audio recorded by the stream input will be played out during the next round of audio processing.
+
+Note that a duplex stream can make use of two different devices (except when using the Linux Jack and Windows ASIO APIs).  However, this may cause timing problems due to possible device clock variations, unless a common external "sync" is provided.
+
+*/
diff --git a/doc/doxygen/error.txt b/doc/doxygen/error.txt
new file mode 100644 (file)
index 0000000..514c1c4
--- /dev/null
@@ -0,0 +1,5 @@
+/*! \page errors Error Handling
+
+RtAudio makes restrained use of C++ exceptions.  That is, exceptions are thrown only when system errors occur that prevent further class operation or when the user makes invalid function calls.  In other cases, a warning message may be displayed and an appropriate value is returned.  For example, if a system error occurs when processing the RtAudio::getDeviceCount() function, the return value is zero.  In such a case, the user cannot expect to make use of most other RtAudio functions because no devices are available (and thus a stream cannot be opened).  A client can call the function RtAudio::showWarnings() with a boolean argument to enable or disable the printing of warning messages to <tt>stderr</tt>.  By default, warning messages are displayed.  There is a protected RtAudio method, error(), that can be modified to globally control how these messages are handled and reported.
+
+*/
index d05cf2767c962054675542f89f47bd48e638e21d..c10c2ddba2ec2af6cf6ca6ccf7b3a5d071482c98 100644 (file)
@@ -1,8 +1,7 @@
 <HR>
 
 <table><tr><td><img src="../images/mcgill.gif" width=165></td>
-  <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>
+  <td>&copy;2001-2007 Gary P. Scavone, McGill University. All Rights Reserved.<br>Maintained by <a href="http://www.music.mcgill.ca/~gary/">Gary P. Scavone</a>.</td></tr>
 </table>
 
 </BODY>
index a368db28f3429ef756be2a4a68414058a52bc737..031694adff4ca656b48fcdf43a0ce50e7abeb61f 100644 (file)
@@ -1,9 +1,10 @@
 <HTML>
 <HEAD>
-<TITLE>The RtAudio Tutorial</TITLE>
+<TITLE>The RtAudio Home Page</TITLE>
 <LINK HREF="doxygen.css" REL="stylesheet" TYPE="text/css">
+<LINK REL="SHORTCUT ICON" HREF="http://www.music.mcgill.ca/~gary/favicon.ico">
 </HEAD>
 <BODY BGCOLOR="#FFFFFF">
 <CENTER>
-<a class="qindex" href="index.html">Tutorial</a> &nbsp; <a class="qindex" href="annotated.html">Class/Enum List</a> &nbsp; <a class="qindex" href="files.html">File List</a> &nbsp; <a class="qindex" href="functions.html">Compound Members</a> &nbsp; </CENTER>
+<a class="qindex" href="index.html">Home</a> &nbsp; <a class="qindex" href="annotated.html">Class/Enum List</a> &nbsp; <a class="qindex" href="files.html">File List</a> &nbsp; <a class="qindex" href="functions.html">Compound Members</a> &nbsp; </CENTER>
 <HR>
diff --git a/doc/doxygen/license.txt b/doc/doxygen/license.txt
new file mode 100644 (file)
index 0000000..abcdc26
--- /dev/null
@@ -0,0 +1,30 @@
+/*! \page license License
+
+    RtAudio: a set of realtime audio i/o C++ classes<BR>
+    Copyright (c) 2001-2007 Gary P. Scavone
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation files
+    (the "Software"), to deal in the Software without restriction,
+    including without limitation the rights to use, copy, modify, merge,
+    publish, distribute, sublicense, and/or sell copies of the Software,
+    and to permit persons to whom the Software is furnished to do so,
+    subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    Any person wishing to distribute modifications to the Software is
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
diff --git a/doc/doxygen/multi.txt b/doc/doxygen/multi.txt
new file mode 100644 (file)
index 0000000..85a3e48
--- /dev/null
@@ -0,0 +1,7 @@
+/*! \page multi Using Simultaneous Multiple APIs
+
+Because support for each audio API is encapsulated in a specific RtApi subclass, it is possible to compile and instantiate multiple API-specific subclasses on a given operating system.  For example, one can compile both the RtApiDs and RtApiAsio classes on Windows operating systems by providing the appropriate preprocessor definitions, include files, and libraries for each.  In a run-time situation, one might first attempt to determine whether any ASIO device drivers exist.  This can be done by specifying the api argument RtAudio::WINDOWS_ASIO when attempting to create an instance of RtAudio.  If no available devices are found, then an instance of RtAudio with the api argument RtAudio::WINDOWS_DS can be created.  Alternately, if no api argument is specified, RtAudio will first look for an ASIO instance and then a DirectSound instance (on Linux systems, the default API search order is Jack, Alsa, and finally OSS).  In theory, it should also be possible to have separate instances of RtAudio open at the same time with different underlying audio API support, though this has not been tested.  It is difficult to know how well different audio APIs can simultaneously coexist on a given operating system.  In particular, it is unlikely that the same device could be simultaneously controlled with two different audio APIs.
+
+The static function RtAudio::getCompiledApi() is provided to determine the available compiled API support.  The function RtAudio::getCurrentApi() indicates the API selected for a given RtAudio instance.
+
+*/
diff --git a/doc/doxygen/playback.txt b/doc/doxygen/playback.txt
new file mode 100644 (file)
index 0000000..4d5a793
--- /dev/null
@@ -0,0 +1,81 @@
+/*! \page playback Playback
+
+In this example, we provide a complete program that demonstrates the use of RtAudio for audio playback.  Our program produces a two-channel sawtooth waveform for output.
+
+\code
+#include "RtAudio.h"
+#include <iostream>
+
+// Two-channel sawtooth wave generator.
+int saw( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+         double streamTime, RtAudioStreamStatus status, void *userData )
+{
+  unsigned int i, j;
+  double *buffer = (double *) outputBuffer;
+  double *lastValues = (double *) userData;
+
+  if ( status )
+    std::cout << "Stream underflow detected!" << std::endl;
+
+  // Write interleaved audio data.
+  for ( i=0; i<nBufferFrames; i++ ) {
+    for ( j=0; j<2; j++ ) {
+      *buffer++ = lastValues[j];
+
+      lastValues[j] += 0.005 * (j+1+(j*0.1));
+      if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
+    }
+  }
+
+  return 0;
+}
+
+int main()
+{
+  RtAudio dac;
+  if ( dac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  RtAudio::StreamParameters parameters;
+  parameters.deviceId = dac.getDefaultOutputDevice();
+  parameters.nChannels = 2;
+  parameters.firstChannel = 0;
+  unsigned int sampleRate = 44100;
+  unsigned int bufferFrames = 256; // 256 sample frames
+  double data[2];
+
+  try {
+    dac.openStream( &parameters, NULL, RTAUDIO_FLOAT64,
+                    sampleRate, &bufferFrames, &saw, (void *)&data );
+    dac.startStream();
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    exit( 0 );
+  }
+  
+  char input;
+  std::cout << "\nPlaying ... press <enter> to quit.\n";
+  std::cin.get( input );
+
+  try {
+    // Stop the stream
+    dac.stopStream();
+  }
+  catch (RtError& e) {
+    e.printMessage();
+  }
+
+  if ( dac.isStreamOpen() ) dac.closeStream();
+
+  return 0;
+}
+\endcode
+
+We open the stream in exactly the same way as the previous example (except with a data format change) and specify the address of our callback function \e "saw()". The callback function will automatically be invoked when the underlying audio system needs data for output.  Note that the callback function is called only when the stream is "running" (between calls to the RtAudio::startStream() and RtAudio::stopStream() functions).  We can also pass a pointer value to the RtAudio::openStream() function that is made available in the callback function.  In this way, it is possible to gain access to arbitrary data created in our \e main() function from within the globally defined callback function.
+
+In this example, we stop the stream with an explicit call to RtAudio::stopStream().  It is also possible to stop a stream by returning a non-zero value from the callback function.  A return value of 1 will cause the stream to finish draining its internal buffers and then halt (equivalent to calling the RtAudio::stopStream() function).   A return value of 2 will cause the stream to stop immediately (equivalent to calling the RtAudio::abortStream() function).
+
+*/
diff --git a/doc/doxygen/probe.txt b/doc/doxygen/probe.txt
new file mode 100644 (file)
index 0000000..761ae34
--- /dev/null
@@ -0,0 +1,70 @@
+/*! \page probe Probing Device Capabilities
+
+A programmer may wish to query the available audio device capabilities before deciding which to use.  The following example outlines how this can be done.
+
+\code
+
+// probe.cpp
+
+#include <iostream>
+#include "RtAudio.h"
+
+int main()
+{
+  RtAudio audio;
+
+  // Determine the number of devices available
+  unsigned int devices = audio.getDeviceCount();
+
+  // Scan through devices for various capabilities
+  RtAudio::DeviceInfo info;
+  for ( unsigned int i=1; i<=devices; i++ ) {
+
+    info = audio.getDeviceInfo( i );
+
+    if ( info.probed == true ) {
+      // Print, for example, the maximum number of output channels for each device
+      std::cout << "device = " << i;
+      std::cout << ": maximum output channels = " << info.outputChannels << "\n";
+    }
+  }
+
+  return 0;
+}
+\endcode
+
+The RtAudio::DeviceInfo structure is defined in RtAudio.h and provides a variety of information useful in assessing the capabilities of a device:
+
+\code
+  typedef struct RtAudio::DeviceInfo {
+    bool probed;                  // true if the device capabilities were successfully probed.
+    std::string name;             // Character string device identifier.
+    int outputChannels;           // Maximum output channels supported by device.
+    int inputChannels;            // Maximum input channels supported by device.
+    int duplexChannels;           // Maximum simultaneous input/output channels supported by device.
+    bool isDefaultOutput;         // true if this is the default output device.
+    bool isDefaultInput;          // true if this is the default input device.
+    std::vector<int> sampleRates; // Supported sample rates.
+    RtAudioFormat nativeFormats;  // Bit mask of supported data formats.
+  };
+\endcode
+
+The following data formats are defined and fully supported by RtAudio:
+
+\code
+  typedef unsigned long RtAudioFormat;
+  static const RtAudioFormat  RTAUDIO_SINT8;   // Signed 8-bit integer
+  static const RtAudioFormat  RTAUDIO_SINT16;  // Signed 16-bit integer
+  static const RtAudioFormat  RTAUDIO_SINT24;  // Signed 24-bit integer (lower 3 bytes of 32-bit signed integer.)
+  static const RtAudioFormat  RTAUDIO_SINT32;  // Signed 32-bit integer
+  static const RtAudioFormat  RTAUDIO_FLOAT32; // 32-bit float normalized between +/- 1.0
+  static const RtAudioFormat  RTAUDIO_FLOAT64; // 64-bit double normalized between +/- 1.0
+\endcode
+
+The \c nativeFormats member of the RtAudio::DeviceInfo structure is a bit mask of the above formats which are natively supported by the device.  However, RtAudio will automatically provide format conversion if a particular format is not natively supported.  When the \c probed member of the RtAudio::DeviceInfo structure is false, the remaining structure members are undefined and the device is probably unusable.
+
+Some audio devices may require a minimum channel value greater than one.  RtAudio will provide automatic channel number compensation when the number of channels set by the user is less than that required by the device.  Channel compensation is <I>NOT</I> possible when the number of channels set by the user is greater than that supported by the device.
+
+It should be noted that the capabilities reported by a device driver or underlying audio API are not always accurate and/or may be dependent on a combination of device settings.  For this reason, RtAudio does not rely on the queried values when attempting to open a stream.
+
+*/
diff --git a/doc/doxygen/recording.txt b/doc/doxygen/recording.txt
new file mode 100644 (file)
index 0000000..6316a5c
--- /dev/null
@@ -0,0 +1,66 @@
+/*! \page recording Recording
+
+
+Using RtAudio for audio input is almost identical to the way it is used for playback.  Here's the blocking playback example rewritten for recording:
+
+\code
+#include "RtAudio.h"
+#include <iostream>
+
+int record( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+         double streamTime, RtAudioStreamStatus status, void *userData )
+{
+  if ( status )
+    std::cout << "Stream overflow detected!" << std::endl;
+
+  // Do something with the data in the "inputBuffer" buffer.
+
+  return 0;
+}
+
+int main()
+{
+  RtAudio adc;
+  if ( adc.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  RtAudio::StreamParameters parameters;
+  parameters.deviceId = adc.getDefaultInputDevice();
+  parameters.nChannels = 2;
+  parameters.firstChannel = 0;
+  unsigned int sampleRate = 44100;
+  unsigned int bufferFrames = 256; // 256 sample frames
+
+  try {
+    adc.openStream( NULL, &parameters, RTAUDIO_SINT16,
+                    sampleRate, &bufferFrames, &record );
+    adc.startStream();
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    exit( 0 );
+  }
+  
+  char input;
+  std::cout << "\nRecording ... press <enter> to quit.\n";
+  std::cin.get( input );
+
+  try {
+    // Stop the stream
+    adc.stopStream();
+  }
+  catch (RtError& e) {
+    e.printMessage();
+  }
+
+  if ( adc.isStreamOpen() ) adc.closeStream();
+
+  return 0;
+}
+\endcode
+
+In this example, we pass the address of the stream parameter structure as the second argument of the RtAudio::openStream() function and pass a NULL value for the output stream parameters.  In this example, the \e record() callback function performs no specific operations.
+
+*/
diff --git a/doc/doxygen/settings.txt b/doc/doxygen/settings.txt
new file mode 100644 (file)
index 0000000..d7cc1d2
--- /dev/null
@@ -0,0 +1,48 @@
+/*! \page settings Device Settings
+
+The next step in using RtAudio is to open a stream with particular device and parameter settings.
+
+\code
+
+#include "RtAudio.h"
+
+int main()
+{
+  RtAudio dac;
+  if ( dac.getDeviceCount() == 0 ) exit( 0 );
+
+  RtAudio::StreamParameters parameters;
+  parameters.deviceId = dac.getDefaultOutputDevice();
+  parameters.nChannels = 2;
+  unsigned int sampleRate = 44100;
+  unsigned int bufferFrames = 256; // 256 sample frames
+
+  RtAudio::StreamOptions options;
+  options.flags = RTAUDIO_NONINTERLEAVED;
+
+  try {
+    dac.openStream( &parameters, NULL, RTAUDIO_FLOAT32,
+                    sampleRate, &bufferFrames, &myCallback, NULL, &options );
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    exit( 0 );
+  }
+  
+  return 0;
+}
+\endcode
+
+The RtAudio::openStream() function attempts to open a stream with a specified set of parameter values.  In the above example, we attempt to open a two channel playback stream using the default output device, 32-bit floating point data, a sample rate of 44100 Hz, and a frame rate of 256 sample frames per output buffer.  If the user specifies an invalid parameter value (such as a device id greater than or equal to the number of enumerated devices), an RtError is thrown of type = INVALID_USE.   If a system error occurs or the device does not support the specified parameter values, an RtError of type = SYSTEM_ERROR is thrown.  In either case, a descriptive error message is bundled with the exception and can be queried with the RtError::getMessage() or RtError::what() functions.
+
+RtAudio provides four signed integer and two floating point data formats which can be specified using the RtAudioFormat parameter values mentioned earlier.  If the opened device does not natively support the given format, RtAudio will automatically perform the necessary data format conversion.
+
+The \c bufferFrames parameter specifies the desired number of sample frames that will be written to and/or read from a device per write/read operation.  This parameter can be used to control stream latency though there is no guarantee that the passed value will be that used by a device.  In general, a lower \c bufferFrames value will produce less latency but perhaps less robust performance.  A value of zero can be specified, in which case the smallest allowable value will be used.  The \c bufferFrames parameter is passed as a pointer and the actual value used by the stream is set during the device setup procedure.  \c bufferFrames values should be a power of two.  Optimal and allowable buffer values tend to vary between systems and devices.  Stream latency can also be controlled via the optional RtAudio::StreamOptions member \c numberOfBuffers (not used in the example above), though this tends to be more system dependent.  In particular, the \c numberOfBuffers parameter is ignored when using the OS-X Core Audio, Jack, and the Windows ASIO APIs.
+
+As noted earlier, the device capabilities reported by a driver or underlying audio API are not always accurate and/or may be dependent on a combination of device settings.  Because of this, RtAudio does not attempt to query a device's capabilities or use previously reported values when opening a device.  Instead, RtAudio simply attempts to set the given parameters on a specified device and then checks whether the setup is successful or not.
+
+The RtAudioCallback parameter above is a pointer to a user-defined function that will be called whenever the audio system is ready for new output data or has new input data to be read.  Further details on the use of a callback function are provided in the next section.
+
+Several stream options are available to fine-tune the behavior of an audio stream.  In the example above, we specify that data will be written by the user in a \e non-interleaved format via the RtAudio::StreamOptions member \c flags.  That is, all \c bufferFrames of the first channel should be written consecutively, followed by all \c bufferFrames of the second channel.  By default (when no option is specified), RtAudio expects data to be written in an \e interleaved format.
+
+*/
index 802fe24c4f58e534c49490f27d9589e4bb7ff780..8e6b63d7b115e5e1c5e67dcac0d3693dfa739335 100644 (file)
-/*! \mainpage The RtAudio Tutorial
+/*! \mainpage The RtAudio Home Page
 
-<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
-
-RtAudio is a set of C++ classes which provide a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X, SGI, and Windows (DirectSound and ASIO) operating systems.  RtAudio significantly simplifies the process of interacting with computer audio hardware.  It was designed with the following goals:
+RtAudio is a set of C++ classes that provide a common API (Application Programming Interface) for realtime audio input/output across Linux, Macintosh OS-X and Windows (DirectSound and ASIO) operating systems.  RtAudio significantly simplifies the process of interacting with computer audio hardware.  It was designed with the following objectives:
 
 <UL>
-  <LI>object oriented C++ design</LI>
+  <LI>object-oriented C++ design</LI>
   <LI>simple, common API across all supported platforms</LI>
-  <LI>only two header files and one source file for easy inclusion in programming projects</LI>
+  <LI>only one source and two header files for easy inclusion in programming projects</LI>
   <LI>allow simultaneous multi-api support</LI>
-  <LI>blocking functionality</LI>
-  <LI>callback functionality</LI>
-  <LI>extensive audio device parameter control</LI>
-  <LI>audio device capability probing</LI>
-  <LI>automatic internal conversion for data format, channel number compensation, de-interleaving, and byte-swapping</LI>
+  <LI>support dynamic connection of devices</LI>
+  <LI>provide extensive audio device parameter control</LI>
+  <LI>allow audio device capability probing</LI>
+  <LI>automatic internal conversion for data format, channel number compensation, (de)interleaving, and byte-swapping</LI>
 </UL>
 
 RtAudio incorporates the concept of audio streams, which represent audio output (playback) and/or input (recording).  Available audio devices and their capabilities can be enumerated and then specified when opening a stream.  Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance.  See the \ref apinotes section for information specific to each of the supported audio APIs.
 
-The RtAudio API provides both blocking (synchronous) and callback (asynchronous) functionality.  Callbacks are typically used in conjunction with graphical user interfaces (GUI).  Blocking functionality is often necessary for explicit control of multiple input/output stream synchronization or when audio must be synchronized with other system events.
-
-\section changes What's New (Version 3.0)
-
-RtAudio now allows simultaneous multi-api support.  For example, you can compile RtAudio to provide both DirectSound and ASIO support on Windows platforms or ALSA, JACK, and OSS support on Linux platforms.  This was accomplished by creating an abstract base class, RtApi, with subclasses for each supported API (RtApiAlsa, RtApiJack, RtApiOss, RtApiDs, RtApiAsio, RtApiCore, and RtApiAl).  The class RtAudio is now a "controller" which creates an instance of an RtApi subclass based on the user's API choice via an optional RtAudio::RtAudioApi instantiation argument.  If no API is specified, RtAudio attempts to make a "logical" API selection.
-
-Support for the JACK low-latency audio server has been added with this version of RtAudio.  It is necessary to have the JACK server running before creating an instance of RtAudio.
-
-Several API changes have been made in version 3.0 of RtAudio in an effort to provide more consistent behavior across all supported audio APIs.  The most significant of these changes is that multiple stream support from a single RtAudio instance has been discontinued.  As a result, stream identifier input arguments are no longer required.  Also, the RtAudio::streamWillBlock() function was poorly supported by most APIs and has been deprecated (though the function still exists in those subclasses of RtApi that do allow it to be implemented).
+\section whatsnew What's New (Version 4.0)
 
-The RtAudio::getDeviceInfo() function was modified to return a globally defined RtAudioDeviceInfo structure.  This structure is a simplified version of the previous RTAUDIO_DEVICE structure.  In addition, the RTAUDIO_FORMAT structure was renamed RtAudioFormat and defined globally within RtAudio.h.  These changes were made for clarity and to better conform with standard C++ programming practices.
+RtAudio V4 represents a significant rewrite of the code and includes a number of API and functionality changes form previous versions. A partial list of the changes includes:
+- new support for non-interleaved user data
+- additional input/output parameter specifications, including channel offset
+- new support for dynamic connection of devices
+- new support for stream time
+- revised callback arguments, including separate input and output buffer arguments
+- revised C++ exception handling
+- updated support for OSS version 4.0
+- discontinued support of blocking functionality
+- discontinued support of SGI
 
-The RtError class declaration and definition have been extracted to a separate file (RtError.h).  This was done in preparation for a new release of the RtMidi class (planned for Summer 2004).
+Devices are now re-enumerated every time the RtAudio::getDeviceCount(), RtAudio::getDeviceInfo(), and RtAudio::openStream() functions are called.  This allows for the proper identification of hot-pluggable (USB, Firewire, ...) devices while a given RtAudio instance exists.
 
 \section download Download
 
-Latest Release (18 November 2005): <A href="http://music.mcgill.ca/~gary/rtaudio/release/rtaudio-3.0.3.tar.gz">Version 3.0.3</A>
-
-\section start Getting Started
-
-With version 3.0, it is now possible to compile multiple API support on a given platform and to specify an API choice during class instantiation.  In the examples that follow, no API will be specified (in which case, RtAudio attempts to select the most "logical" available API).
-
-The first thing that must be done when using RtAudio is to create an instance of the class.  The default constructor scans the underlying audio system to verify that at least one device is available.  RtAudio often uses C++ exceptions to report errors, necessitating try/catch blocks around most member functions.  The following code example demonstrates default object construction and destruction:
-
-\code
-
-#include "RtAudio.h"
-
-int main()
-{
-  RtAudio *audio = 0;
-
-  // Default RtAudio constructor
-  try {
-    audio = new RtAudio();
-  }
-  catch (RtError &error) {
-    // Handle the exception here
-    error.printMessage();
-  }
-
-  // Clean up
-  delete audio;
-}
-\endcode
-
-Obviously, this example doesn't demonstrate any of the real functionality of RtAudio.  However, all uses of RtAudio must begin with a constructor (either default or overloaded varieties) and must end with class destruction.  Further, it is necessary that all class methods that can throw a C++ exception be called within a try/catch block.
-
-
-\section error Error Handling
-
-RtAudio uses a C++ exception handler called RtError, which is declared and defined in RtError.h.  The RtError class is quite simple but it does allow errors to be "caught" by RtError::Type.  Almost all RtAudio methods can "throw" an RtError, most typically if a driver error occurs or a stream function is called when no stream is open.  There are a number of cases within RtAudio where warning messages may be displayed but an exception is not thrown.  There is a protected RtAudio method, error(), that can be modified to globally control how these messages are handled and reported.  By default, error messages are not automatically displayed in RtAudio unless the preprocessor definition __RTAUDIO_DEBUG__ is defined.  Messages associated with caught exceptions can be displayed with, for example, the RtError::printMessage() function.
-
-
-\section probing Probing Device Capabilities
-
-A programmer may wish to query the available audio device capabilities before deciding which to use.  The following example outlines how this can be done.
-
-\code
-
-// probe.cpp
-
-#include <iostream>
-#include "RtAudio.h"
-
-int main()
-{
-  RtAudio *audio = 0;
-
-  // Default RtAudio constructor
-  try {
-    audio = new RtAudio();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  // Determine the number of devices available
-  int devices = audio->getDeviceCount();
-
-  // Scan through devices for various capabilities
-  RtAudioDeviceInfo info;
-  for (int i=1; i<=devices; i++) {
-
-    try {
-      info = audio->getDeviceInfo(i);
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      break;
-    }
-
-    // Print, for example, the maximum number of output channels for each device
-    std::cout << "device = " << i;
-    std::cout << ": maximum output channels = " << info.outputChannels << "\n";
-  }
-
-  // Clean up
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-The RtAudioDeviceInfo structure is defined in RtAudio.h and provides a variety of information useful in assessing the capabilities of a device:
-
-\code
-  typedef struct RtAudioDeviceInfo{
-    std::string name;             // Character string device identifier.
-    bool probed;                  // true if the device capabilities were successfully probed.
-    int outputChannels;           // Maximum output channels supported by device.
-    int inputChannels;            // Maximum input channels supported by device.
-    int duplexChannels;           // Maximum simultaneous input/output channels supported by device.
-    bool isDefault;               // true if this is the default output or input device.
-    std::vector<int> sampleRates; // Supported sample rates.
-    RtAudioFormat nativeFormats;  // Bit mask of supported data formats.
-  };
-\endcode
-
-The following data formats are defined and fully supported by RtAudio:
-
-\code
-  typedef unsigned long RtAudioFormat;
-  static const RtAudioFormat  RTAUDIO_SINT8;   // Signed 8-bit integer
-  static const RtAudioFormat  RTAUDIO_SINT16;  // Signed 16-bit integer
-  static const RtAudioFormat  RTAUDIO_SINT24;  // Signed 24-bit integer (upper 3 bytes of 32-bit signed integer.)
-  static const RtAudioFormat  RTAUDIO_SINT32;  // Signed 32-bit integer
-  static const RtAudioFormat  RTAUDIO_FLOAT32; // 32-bit float normalized between +/- 1.0
-  static const RtAudioFormat  RTAUDIO_FLOAT64; // 64-bit double normalized between +/- 1.0
-\endcode
-
-The <I>nativeFormats</I> member of the RtAudioDeviceInfo structure is a bit mask of the above formats that are natively supported by the device.  However, RtAudio will automatically provide format conversion if a particular format is not natively supported.  When the <I>probed</I> member of the RtAudioDeviceInfo structure is false, the remaining structure members are undefined and the device is probably unuseable.
-
-While some audio devices may require a minimum channel value greater than one, RtAudio will provide automatic channel number compensation when the number of channels set by the user is less than that required by the device.  Channel compensation is <I>NOT</I> possible when the number of channels set by the user is greater than that supported by the device.
-
-It should be noted that the capabilities reported by a device driver or underlying audio API are not always accurate and/or may be dependent on a combination of device settings.  For this reason, RtAudio does not typically rely on the queried values when attempting to open a stream.
-
-
-\section settings Device Settings
-
-The next step in using RtAudio is to open a stream with particular device and parameter settings.
-
-\code
-
-#include "RtAudio.h"
-
-int main()
-{
-  int channels = 2;
-  int sampleRate = 44100;
-  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 = 0;
-
-  // Instantiate RtAudio and open a stream within a try/catch block
-  try {
-    audio = new RtAudio();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    audio->openStream(device, channels, 0, 0, RTAUDIO_FLOAT32,
-                      sampleRate, &bufferSize, nBuffers);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    // Perhaps try other parameters?
-  }
-
-  // Clean up
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-The RtAudio::openStream() method attempts to open a stream with a specified set of parameter values.  In this case, we attempt to open a two channel playback stream with the default output device, 32-bit floating point data, a sample rate of 44100 Hz, a frame rate of 256 sample frames per read/write, and 4 internal device buffers.  When device = 0, RtAudio first attempts to open the default audio device with the given parameters.  If that attempt fails, RtAudio searches through the remaining available devices in an effort to find a device that will meet the given parameters.  If all attempts are unsuccessful, an RtError is thrown.  When a non-zero device value is specified, an attempt is made to open that device \e ONLY (device = 1 specifies the first identified device, as reported by RtAudio::getDeviceInfo()).
-
-RtAudio provides four signed integer and two floating point data formats that can be specified using the RtAudioFormat parameter values mentioned earlier.  If the opened device does not natively support the given format, RtAudio will automatically perform the necessary data format conversion.
-
-The <I>bufferSize</I> parameter specifies the desired number of sample frames that will be written to and/or read from a device per write/read operation.  The <I>nBuffers</I> parameter is used in setting the underlying device buffer parameters.  Both the <I>bufferSize</I> and <I>nBuffers</I> parameters can be used to control stream latency though there is no guarantee that the passed values will be those used by a device (the <I>nBuffers</I> parameter is ignored when using the OS X CoreAudio, Linux Jack, and the Windows ASIO APIs).  In general, lower values for both parameters will produce less latency but perhaps less robust performance.  Both parameters can be specified with values of zero, in which case the smallest allowable values will be used.  The <I>bufferSize</I> parameter is passed as a pointer and the actual value used by the stream is set during the device setup procedure.  <I>bufferSize</I> values should be a power of two.  Optimal and allowable buffer values tend to vary between systems and devices.  Check the \ref apinotes section for general guidelines.
-
-As noted earlier, the device capabilities reported by a driver or underlying audio API are not always accurate and/or may be dependent on a combination of device settings.  Because of this, RtAudio does not attempt to query a device's capabilities or use previously reported values when opening a device.  Instead, RtAudio simply attempts to set the given parameters on a specified device and then checks whether the setup is successful or not.
-
-
-\section playbackb Playback (blocking functionality)
-
-Once the device is open for playback, there are only a few final steps necessary for realtime audio output.  We'll first provide an example (blocking functionality) and then discuss the details.
-
-\code
-// playback.cpp
-
-#include "RtAudio.h"
-
-int main()
-{
-  int count;
-  int channels = 2;
-  int sampleRate = 44100;
-  int bufferSize = 256;  // 256 sample frames
-  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 = 0;
-
-  // Open a stream during RtAudio instantiation
-  try {
-    audio = new RtAudio(device, channels, 0, 0, RTAUDIO_FLOAT32,
-                        sampleRate, &bufferSize, nBuffers);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    // Get a pointer to the stream buffer
-    buffer = (float *) audio->getStreamBuffer();
-
-    // Start the stream
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  // An example loop that runs for 40000 sample frames
-  count = 0;
-  while (count < 40000) {
-    // Generate your samples and fill the buffer with bufferSize sample frames of data
-    ...
-
-    // Trigger the output of the data buffer
-    try {
-      audio->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    count += bufferSize;
-  }
-
-  try {
-    // Stop and close the stream
-    audio->stopStream();
-    audio->closeStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-The first thing to notice in this example is that we attempt to open a stream during class instantiation with an overloaded constructor.  This constructor simply combines the functionality of the default constructor, used earlier, and the RtAudio::openStream() method.  Again, we have specified a device value of 0, indicating that the default or first available device meeting the given parameters should be used.  An attempt is made to open the stream with the specified <I>bufferSize</I> value.  However, it is possible that the device will not accept this value, in which case the closest allowable size is used and returned via the pointer value.   The constructor can fail if no available devices are found, or a memory allocation or device driver error occurs.  Note that you should not call the RtAudio destructor if an exception is thrown during instantiation.
-
-Assuming the constructor is successful, it is necessary to get a pointer to the buffer, provided by RtAudio, for use in feeding data to/from the opened stream.  Note that the user should <I>NOT</I> attempt to deallocate the stream buffer memory ... memory management for the stream buffer will be automatically controlled by RtAudio.  After starting the stream with RtAudio::startStream(), one simply fills that buffer, which is of length equal to the returned <I>bufferSize</I> value, with interleaved audio data (in the specified format) for playback.  Finally, a call to the RtAudio::tickStream() routine triggers a blocking write call for the stream.
-
-In general, one should call the RtAudio::stopStream() and RtAudio::closeStream() methods after finishing with a stream.  However, both methods will implicitly be called during object destruction if necessary.
-
-
-\section playbackc Playback (callback functionality)
-
-The primary difference in using RtAudio with callback functionality involves the creation of a user-defined callback function.  Here is an example that produces a sawtooth waveform for playback.
-
-\code
-
-#include <iostream>
-#include "RtAudio.h"
-
-// Two-channel sawtooth wave generator.
-int sawtooth(char *buffer, int bufferSize, void *data)
-{
-  int i, j;
-  double *my_buffer = (double *) buffer;
-  double *my_data = (double *) data;
-
-  // Write interleaved audio data.
-  for (i=0; i<bufferSize; i++) {
-    for (j=0; j<2; j++) {
-      *my_buffer++ = my_data[j];
-
-      my_data[j] += 0.005 * (j+1+(j*0.1));
-      if (my_data[j] >= 1.0) my_data[j] -= 2.0;
-    }
-  }
-
-  return 0;
-}
-
-int main()
-{
-  int channels = 2;
-  int sampleRate = 44100;
-  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
-  double data[2];
-  char input;
-  RtAudio *audio = 0;
-
-  // Open a stream during RtAudio instantiation
-  try {
-    audio = new RtAudio(device, channels, 0, 0, RTAUDIO_FLOAT64,
-                        sampleRate, &bufferSize, nBuffers);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    // Set the stream callback function
-    audio->setStreamCallback(&sawtooth, (void *)data);
-
-    // Start the stream
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nPlaying ... press <enter> to quit.\n";
-  std::cin.get(input);
-
-  try {
-    // Stop and close the stream
-    audio->stopStream();
-    audio->closeStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-After opening the device in exactly the same way as the previous example (except with a data format change), we must set our callback function for the stream using RtAudio::setStreamCallback().  When the underlying audio API uses blocking calls (OSS, ALSA, SGI, and Windows DirectSound), this method will spawn a new process (or thread) that automatically calls the callback function when more data is needed.  Callback-based audio APIs (OS X CoreAudio Linux Jack, and ASIO) implement their own event notification schemes.  Note that the callback function is called only when the stream is "running" (between calls to the RtAudio::startStream() and RtAudio::stopStream() methods).  The last argument to RtAudio::setStreamCallback() is a pointer to arbitrary data that you wish to access from within your callback function.
-
-In this example, we stop the stream with an explicit call to RtAudio::stopStream().  When using callback functionality, it is also possible to stop a stream by returning a non-zero value from the callback function.
-
-Once set with RtAudio::setStreamCallback, the callback process exists for the life of the stream (until the stream is closed with RtAudio::closeStream() or the RtAudio instance is deleted).  It is possible to disassociate a callback function and cancel its process for an open stream using the RtAudio::cancelStreamCallback() method.  The stream can then be used with blocking functionality or a new callback can be associated with it.
-
-
-\section recording Recording
-
-Using RtAudio for audio input is almost identical to the way it is used for playback.  Here's the blocking playback example rewritten for recording:
-
-\code
-// record.cpp
-
-#include "RtAudio.h"
-
-int main()
-{
-  int count;
-  int channels = 2;
-  int sampleRate = 44100;
-  int bufferSize = 256;  // 256 sample frames
-  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 = 0;
-
-  // Instantiate RtAudio and open a stream.
-  try {
-    audio = new RtAudio(&stream, 0, 0, device, channels,
-                        RTAUDIO_FLOAT32, sampleRate, &bufferSize, nBuffers);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    // Get a pointer to the stream buffer
-    buffer = (float *) audio->getStreamBuffer();
-
-    // Start the stream
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  // An example loop that runs for about 40000 sample frames
-  count = 0;
-  while (count < 40000) {
-
-    // Read a buffer of data
-    try {
-      audio->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    // Process the input samples (bufferSize sample frames) that were read
-    ...
-
-    count += bufferSize;
-  }
-
-  try {
-    // Stop the stream
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-In this example, the stream was opened for recording with a non-zero <I>inputChannels</I> value.  The only other difference between this example and that for playback involves the order of data processing in the loop, where it is necessary to first read a buffer of input data before manipulating it.
-
-
-\section duplex Duplex Mode
-
-Finally, it is easy to use RtAudio for simultaneous audio input/output, or duplex operation.  In this example, we use a callback function and simply scale the input data before sending it back to the output.
-
-\code
-// duplex.cpp
-
-#include <iostream>
-#include "RtAudio.h"
-
-// Pass-through function.
-int scale(char *buffer, int bufferSize, void *)
-{
-  // Note: do nothing here for pass through.
-  double *my_buffer = (double *) buffer;
-
-  // Scale input data for output.
-  for (int i=0; i<bufferSize; i++) {
-    // Do for two channels.
-    *my_buffer++ *= 0.5;
-    *my_buffer++ *= 0.5;
-  }
-
-  return 0;
-}
-
-int main()
-{
-  int channels = 2;
-  int sampleRate = 44100;
-  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
-  char input;
-  RtAudio *audio = 0;
-
-  // Open a stream during RtAudio instantiation
-  try {
-    audio = new RtAudio(device, channels, device, channels, RTAUDIO_FLOAT64,
-                        sampleRate, &bufferSize, nBuffers);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    // Set the stream callback function
-    audio->setStreamCallback(&scale, NULL);
-
-    // Start the stream
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nRunning duplex ... press <enter> to quit.\n";
-  std::cin.get(input);
-
-  try {
-    // Stop and close the stream
-    audio->stopStream();
-    audio->closeStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  delete audio;
-
-  return 0;
-}
-\endcode
-
-When an RtAudio stream is running in duplex mode (nonzero input <I>AND</I> output channels), the audio write (playback) operation always occurs before the audio read (record) operation.  This sequence allows the use of a single buffer to store both output and input data.
-
-As we see with this example, the write-read sequence of operations does not preclude the use of RtAudio in situations where input data is first processed and then output through a duplex stream.  When the stream buffer is first allocated, it is initialized with zeros, which produces no audible result when output to the device.  In this example, anything recorded by the audio stream input will be scaled and played out during the next round of audio processing.
-
-Note that duplex operation can also be achieved by opening one output stream instance and one input stream instance using the same or different devices.  However, there may be timing problems when attempting to use two different devices, due to possible device clock variations, unless a common external "sync" is provided.  This becomes even more difficult to achieve using two separate callback streams because it is not possible to <I>explicitly</I> control the calling order of the callback functions.
-
-
-\section multi Using Simultaneous Multiple APIs
-
-Because support for each audio API is encapsulated in a specific RtApi subclass, it is possible to compile and instantiate multiple API-specific subclasses on a given operating system.  For example, one can compile both the RtApiDs and RtApiAsio classes on Windows operating systems by providing the appropriate preprocessor definitions, include files, and libraries for each.  In a run-time situation, one might first attempt to determine whether any ASIO device drivers exist.  This can be done by specifying the api argument RtAudio::WINDOWS_ASIO when attempting to create an instance of RtAudio.  If an RtError is thrown (indicating no available drivers), then an instance of RtAudio with the api argument RtAudio::WINDOWS_DS can be created.  Alternately, if no api argument is specified, RtAudio will first look for ASIO drivers and then DirectSound drivers (on Linux systems, the default API search order is Jack, Alsa, and finally OSS).  In theory, it should also be possible to have separate instances of RtAudio open at the same time with different underlying audio API support, though this has not been tested.  It is difficult to know how well different audio APIs can simultaneously coexist on a given operating system.  In particular, it is most unlikely that the same device could be simultaneously controlled with two different audio APIs.
-
-
-\section methods Summary of Methods
-
-The following is a short summary of public methods (not including constructors and the destructor) provided by RtAudio:
-
-<UL>
-<LI>RtAudio::openStream(): opens a stream with the specified parameters.</LI>
-<LI>RtAudio::setStreamCallback(): sets a user-defined callback function for the stream.</LI>
-<LI>RtAudio::cancelStreamCallback(): cancels a callback process and function for the stream.</LI>
-<LI>RtAudio::getDeviceCount(): returns the number of audio devices available.</LI>
-<LI>RtAudio::getDeviceInfo(): returns an RtAudioDeviceInfo structure for a specified device.</LI>
-<LI>RtAudio::getStreamBuffer(): returns a pointer to the stream buffer.</LI>
-<LI>RtAudio::tickStream(): triggers processing of input/output data for the stream (blocking).</LI>
-<LI>RtAudio::closeStream(): closes the stream (implicitly called during object destruction).</LI>
-<LI>RtAudio::startStream(): (re)starts the stream, typically after it has been stopped with either stopStream() or abortStream() or after first opening the stream.</LI>
-<LI>RtAudio::stopStream(): stops the stream, allowing any remaining samples in the queue to be played out and/or read in.  This does not implicitly call RtAudio::closeStream().</LI>
-<LI>RtAudio::abortStream(): stops the stream, discarding any remaining samples in the queue.  This does not implicitly call closeStream().</LI>
-</UL>
-
-
-\section compiling Compiling
-
-In order to compile RtAudio for a specific OS and audio API, it is necessary to supply the appropriate preprocessor definition and library within the compiler statement:
-<P>
-
-<TABLE BORDER=2 COLS=5 WIDTH="100%">
-<TR BGCOLOR="beige">
-  <TD WIDTH="5%"><B>OS:</B></TD>
-  <TD WIDTH="5%"><B>Audio API:</B></TD>
-  <TD WIDTH="5%"><B>C++ Class:</B></TD>
-  <TD WIDTH="5%"><B>Preprocessor Definition:</B></TD>
-  <TD WIDTH="5%"><B>Library or Framework:</B></TD>
-  <TD><B>Example Compiler Statement:</B></TD>
-</TR>
-<TR>
-  <TD>Linux</TD>
-  <TD>ALSA</TD>
-  <TD>RtApiAlsa</TD>
-  <TD>__LINUX_ALSA__</TD>
-  <TD><TT>asound, pthread</TT></TD>
-  <TD><TT>g++ -Wall -D__LINUX_ALSA__ -o probe probe.cpp RtAudio.cpp -lasound -lpthread</TT></TD>
-</TR>
-<TR>
-  <TD>Linux</TD>
-  <TD>Jack Audio Server</TD>
-  <TD>RtApiJack</TD>
-  <TD>__LINUX_JACK__</TD>
-  <TD><TT>jack, pthread</TT></TD>
-  <TD><TT>g++ -Wall -D__LINUX_JACK__ -o probe probe.cpp RtAudio.cpp `pkg-config --cflags --libs jack` -lpthread</TT></TD>
-</TR>
-<TR>
-  <TD>Linux</TD>
-  <TD>OSS</TD>
-  <TD>RtApiOss</TD>
-  <TD>__LINUX_OSS__</TD>
-  <TD><TT>pthread</TT></TD>
-  <TD><TT>g++ -Wall -D__LINUX_OSS__ -o probe probe.cpp RtAudio.cpp -lpthread</TT></TD>
-</TR>
-<TR>
-  <TD>Macintosh OS X</TD>
-  <TD>CoreAudio</TD>
-  <TD>RtApiCore</TD>
-  <TD>__MACOSX_CORE__</TD>
-  <TD><TT>pthread, stdc++, CoreAudio</TT></TD>
-  <TD><TT>g++ -Wall -D__MACOSX_CORE__ -o probe probe.cpp RtAudio.cpp -framework CoreAudio -lpthread</TT></TD>
-</TR>
-<TR>
-  <TD>Irix</TD>
-  <TD>AL</TD>
-  <TD>RtApiAl</TD>
-  <TD>__IRIX_AL__</TD>
-  <TD><TT>audio, pthread</TT></TD>
-  <TD><TT>CC -Wall -D__IRIX_AL__ -o probe probe.cpp RtAudio.cpp -laudio -lpthread</TT></TD>
-</TR>
-<TR>
-  <TD>Windows</TD>
-  <TD>Direct Sound</TD>
-  <TD>RtApiDs</TD>
-  <TD>__WINDOWS_DS__</TD>
-  <TD><TT>dsound.lib (ver. 5.0 or higher), multithreaded</TT></TD>
-  <TD><I>compiler specific</I></TD>
-</TR>
-<TR>
-  <TD>Windows</TD>
-  <TD>ASIO</TD>
-  <TD>RtApiAsio</TD>
-  <TD>__WINDOWS_ASIO__</TD>
-  <TD><I>various ASIO header and source files</I></TD>
-  <TD><I>compiler specific</I></TD>
-</TR>
-</TABLE>
-<P>
-
-The example compiler statements above could be used to compile the <TT>probe.cpp</TT> example file, assuming that <TT>probe.cpp</TT>, <TT>RtAudio.h</TT>, <tt>RtError.h</tt>, and <TT>RtAudio.cpp</TT> all exist in the same directory.
-
-\section debug Debugging
-
-If you are having problems getting RtAudio to run on your system, try passing the preprocessor definition <TT>__RTAUDIO_DEBUG__</TT> to the compiler (or uncomment the definition at the bottom of RtAudio.h).  A variety of warning messages will be displayed that may help in determining the problem.  Also try using the programs included in the <tt>test</tt> directory.  The program <tt>info</tt> displays the queried capabilities of all hardware devices found.
-
-\section apinotes API Notes
-
-RtAudio is designed to provide a common API across the various supported operating systems and audio libraries.  Despite that, some issues should be mentioned with regard to each.
-
-\subsection linux Linux:
-
-RtAudio for Linux was developed under Redhat distributions 7.0 - Fedora.  Three different audio APIs are supported on Linux platforms: OSS, <A href="http://www.alsa-project.org/">ALSA</A>, and <A href="http://jackit.sourceforge.net/">Jack</A>.  The OSS API has existed for at least 6 years and the Linux kernel is distributed with free versions of OSS audio drivers.  Therefore, a generic Linux system is most likely to have OSS support (though the availability and quality of OSS drivers for new hardware is decreasing).  The ALSA API, although relatively new, is now part of the Linux development kernel and offers significantly better functionality than the OSS API.  RtAudio provides support for the 1.0 and higher versions of ALSA.  Jack, which is still in development, is a low-latency audio server, written primarily for the GNU/Linux operating system. It can connect a number of different applications to an audio device, as well as allow them to share audio between themselves.  Input/output latency on the order of 15 milliseconds can typically be achieved using any of the Linux APIs by fine-tuning the RtAudio buffer parameters (without kernel modifications).  Latencies on the order of 5 milliseconds or less can be achieved using a low-latency kernel patch and increasing FIFO scheduling priority.  The pthread library, which is used for callback functionality, is a standard component of all Linux distributions.
-
-The ALSA library includes OSS emulation support.  That means that you can run programs compiled for the OSS API even when using the ALSA drivers and library.  It should be noted however that OSS emulation under ALSA is not perfect.  Specifically, channel number queries seem to consistently produce invalid results.  While OSS emulation is successful for the majority of RtAudio tests, it is recommended that the native ALSA implementation of RtAudio be used on systems that have ALSA drivers installed.
-
-The ALSA implementation of RtAudio makes no use of the ALSA "plug" interface.  All necessary data format conversions, channel compensation, de-interleaving, and byte-swapping is handled by internal RtAudio routines.
-
-The Jack API is based on a callback scheme.  RtAudio provides blocking functionality, in addition to callback functionality, within the context of that behavior.  It should be noted, however, that the best performance is achieved when using RtAudio's callback functionality with the Jack API.  At the moment, only one RtAudio instance can be connected to the Jack server.  Because RtAudio does not provide a mechanism for allowing the user to specify particular channels (or ports) of a device, it simply opens the first <I>N</I> enumerated Jack ports for input/output.
-
-\subsection macosx Macintosh OS X (CoreAudio):
-
-The Apple CoreAudio API is based on a callback scheme.  RtAudio provides blocking functionality, in addition to callback functionality, within the context of that behavior.  CoreAudio is designed to use a separate callback procedure for each of its audio devices.  A single RtAudio duplex stream using two different devices is supported, though it cannot be guaranteed to always behave correctly because we cannot synchronize these two callbacks.  This same functionality might be achieved with better synchrony by creating separate instances of RtAudio for each device and making use of RtAudio blocking calls (i.e. RtAudio::tickStream()).  The <I>numberOfBuffers</I> parameter to the RtAudio::openStream() function has no affect in this implementation.
-
-It is not possible to have multiple instances of RtAudio accessing the same CoreAudio device.
-
-\subsection irix Irix (SGI):
-
-The Irix version of RtAudio was written and tested on an SGI Indy running Irix version 6.5.4 and the newer "al" audio library.  RtAudio does not compile under Irix version 6.3, mainly because the C++ compiler is too old.  Despite the relatively slow speed of the Indy, RtAudio was found to behave quite well and input/output latency was very good.  No problems were found with respect to using the pthread library.
-
-\subsection windowsds Windows (DirectSound):
-
-In order to compile RtAudio under Windows for the DirectSound API, you must have the header and source files for DirectSound version 5.0 or higher.  As far as I know, there is no DirectSoundCapture support for Windows NT.  Audio output latency with DirectSound can be reasonably good, especially since RtAudio version 3.0.2.  Input audio latency still tends to be bad but better since version 3.0.2.  RtAudio was originally developed with Visual C++ version 6.0 but has been tested with .NET.
-
-The DirectSound version of RtAudio can be compiled with or without the UNICODE preprocessor definition.
-
-\subsection windowsasio Windows (ASIO):
-
-The Steinberg ASIO audio API is based on a callback scheme.  In addition, the API allows only a single device driver to be loaded and accessed at a time.  ASIO device drivers must be supplied by audio hardware manufacturers, though ASIO emulation is possible on top of systems with DirectSound drivers.  The <I>numberOfBuffers</I> parameter to the RtAudio::openStream() function has no affect in this implementation.
-
-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; iasiothiscallresolver.h,cpp</TT>.  The Visual C++ projects found in <TT>/tests/Windows/</TT> compile both ASIO and DirectSound support.
-
-The Steinberg provided <TT>asiolist</TT> class does not compile when the preprocessor definition UNICODE is defined.  Note that this could be an issue when using RtAudio with Qt, though Qt programs appear to compile without the UNICODE definition (try <tt>DEFINES -= UNICODE</tt> in your .pro file).  RtAudio with ASIO support has been tested using the MinGW compiler under Windows XP, as well as in the Visual Studio environment.
-
-\section wishlist Possible Future Changes
-
-There are a few issues that still need to be addressed in future versions of RtAudio, including:
-
-<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
-
-Many thanks to the following people for providing bug fixes and improvements:
-<UL>
-<LI>Robin Davies (Windows DS and ASIO)</LI>
-<LI>Ryan Williams (Windows non-MS compiler ASIO support)</LI>
-<LI>Ed Wildgoose (Linux ALSA and Jack)</LI>
-</UL>
-
-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-2005 Gary P. Scavone
-
-    Permission is hereby granted, free of charge, to any person
-    obtaining a copy of this software and associated documentation files
-    (the "Software"), to deal in the Software without restriction,
-    including without limitation the rights to use, copy, modify, merge,
-    publish, distribute, sublicense, and/or sell copies of the Software,
-    and to permit persons to whom the Software is furnished to do so,
-    subject to the following conditions:
-
-    The above copyright notice and this permission notice shall be
-    included in all copies or substantial portions of the Software.
-
-    Any person wishing to distribute modifications to the Software is
-    requested to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.
-
-    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
-    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+Latest Release (7 August 2007): <A href="http://music.mcgill.ca/~gary/rtaudio/release/rtaudio-4.0.0.tar.gz">Version 4.0.0</A>
+
+\section documentation Documentation Links
+
+-# \ref errors
+-# \ref probe
+-# \ref settings
+-# \ref playback
+-# \ref recording
+-# \ref duplex
+-# \ref multi
+-# \ref compiling
+-# \ref apinotes
+-# \ref acknowledge
+-# \ref license
+-# <A href="bugs.html">Bug Tracker</A>
+-# <A href="updates.html">Possible Updates</A>
+-# <A href="http://sourceforge.net/projects/rtaudio">RtAudio at SourceForge</A>
 
 */
index 939501ca5cf3a0dac36625e4bd3dfefd35ac861d..522d01216450488c7e4ce9574df03c8b5fee2f65 100644 (file)
@@ -1,12 +1,27 @@
-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.
+RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2005.
+By Gary P. Scavone, 2001-2007.
 
-v3.0.3: ( November 2005)
+v4.0.0: (7 August 2007)
+- new support for non-interleaved user data
+- additional input/output parameter specifications, including channel offset
+- new support for dynamic connection of devices
+- new support for stream time
+- revised callback arguments, including separate input and output buffer arguments
+- revised C++ exception handling
+- revised OSS support for version 4.0
+- discontinued support of blocking functionality
+- discontinued support of SGI
+- Windows DirectSound API bug fix
+- NetBSD support (using OSS API) by Emmanuel Dreyfus
+- changed default pthread scheduling priority to SCHED_RR when defined in the system
+- new getCompiledApi() static function
+- new getCurrentApi(), getStreamTime(), getStreamLatency(), and isStreamRunning() functions
+- modified RtAudioDeviceInfo structure to distinguish default input and output devices
+
+v3.0.3: (18 November 2005)
 - UNICODE fix for Windows DirectSound API
 - MinGW compiler fix for ASIO API
-- jack support to create virtual devices for each jack plugin
-- support to prefill output buffer with zeroes in duplex mode
 
 v3.0.2: (14 October 2005)
 - modification of ALSA read/write order to fix duplex under/overruns
diff --git a/install b/install
index b2f086b2cd3d310941413419e2c42b80c73b48be..c161713dc855713aaa8206394d50a9f6ba2ca556 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.
+RtAudio - a set of C++ classes which provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2005.
+By Gary P. Scavone, 2001-2007.
 
 To configure and compile (on Unix systems):
 
@@ -15,8 +15,9 @@ A few options can be passed to configure, including:
 
   --enable-debug = enable various debug output
   --with-alsa = choose native ALSA API support (linux only)
-  --with-jack = choose JACK server support (linux only)
   --with-oss = choose OSS API support (linux only)
+  --with-jack = choose JACK server support (linux or Macintosh OS-X)
+  --with-core = choose CoreAudio API support (Macintosh OS-X only)
 
 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.
 
@@ -27,4 +28,4 @@ If you wish to use a different compiler than that selected by configure, specify
 
 For Windows Users:
 
-Visual C++ 6.0 project files are included for the test programs in the /tests/Windows/ directory.  These projects compile API support for both ASIO and DirectSound.
+Visual C++ 6.0 project files are included for the test programs in the /tests/Windows/ directory.  These projects compile API support for both ASIO and DirectSound.  Version 4.0 of RtAudio was tested with the .net compiler and it will not compile in Visual C++ 6.0 because of its non-conformance to modern C++ standards.
diff --git a/oss/soundcard.h b/oss/soundcard.h
new file mode 100644 (file)
index 0000000..e8fc9f6
--- /dev/null
@@ -0,0 +1,2061 @@
+#ifndef SOUNDCARD_H
+#define SOUNDCARD_H
+/*
+ ****************************************************************************
+ * Copyright by 4Front Technologies 1993-2006
+ *
+ ******************************************************************************
+ * Modifications to this file are NOT allowed. This header file controls the
+ * OSS API. For compatibility reasons only 4Front Technologies can make changes
+ * to the definitions. If you have any questions, please contact
+ * hannu@opensound.com.
+ ******************************************************************************
+ *
+ * Redistribution and use in source and binary forms, without
+ * modification, are permitted provided that the following conditions are
+ * met: 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer. 2.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ ****************************************************************************
+ */
+/*
+ * Purpose: The C/C++ header file that defines the OSS API.
+ * Description:
+ * This header file contains all the declarations required to compile OSS
+ * programs. The latest version is always installed together with OSS
+ * use of the latest version is strongly recommended.
+ *
+ * {!notice This header file contains many obsolete definitions
+ * (for compatibility with older applications that still ned them).
+ * Do not use this file as a reference manual of OSS.
+ * Please check the OSS Programmer's guide for descriptions
+ * of the supported API details (http://www.opensound.com/pguide).}
+ */
+
+#if defined(__cplusplus)
+#define EXTERNC extern "C"
+#else
+#define EXTERNC extern
+#endif /* EXTERN_C_WRAPPERS */
+
+#define OSS_VERSION    0x040002
+
+#define SOUND_VERSION  OSS_VERSION
+#define OPEN_SOUND_SYSTEM
+
+#if defined(__hpux) && !defined(_HPUX_SOURCE)
+#      error "-D_HPUX_SOURCE must be used when compiling OSS applications"
+#endif
+
+#ifdef __hpux
+#include <sys/ioctl.h>
+#endif
+
+#ifdef linux
+/* In Linux we need to be prepared for cross compiling */
+#include <linux/ioctl.h>
+#else
+# ifdef __FreeBSD__
+#    include <sys/ioccom.h>
+# else
+#    include <sys/ioctl.h>
+# endif
+#endif
+
+#ifndef __SIOWR
+#if defined(__hpux) || (defined(_IOWR) && (defined(_AIX) || (!defined(sun) && !defined(sparc) && !defined(__INCioctlh) && !defined(__Lynx__))))
+
+/* 
+ * Make sure the ioctl macros are compatible with the ones already used
+ * by this operating system.
+ */
+#define        SIOCPARM_MASK   IOCPARM_MASK
+#define        SIOC_VOID       IOC_VOID
+#define        SIOC_OUT        IOC_OUT
+#define        SIOC_IN         IOC_IN
+#define        SIOC_INOUT      IOC_INOUT
+#define __SIOC_SIZE    _IOC_SIZE
+#define __SIOC_DIR     _IOC_DIR
+#define __SIOC_NONE    _IOC_NONE
+#define __SIOC_READ    _IOC_READ
+#define __SIOC_WRITE   _IOC_WRITE
+#define        __SIO           _IO
+#define        __SIOR          _IOR
+#define        __SIOW          _IOW
+#define        __SIOWR         _IOWR
+#else
+
+/* #define     SIOCTYPE                (0xff<<8) */
+#define        SIOCPARM_MASK   0x1fff  /* parameters must be < 8192 bytes */
+#define        SIOC_VOID       0x00000000      /* no parameters */
+#define        SIOC_OUT        0x20000000      /* copy out parameters */
+#define        SIOC_IN         0x40000000      /* copy in parameters */
+#define        SIOC_INOUT      (SIOC_IN|SIOC_OUT)
+
+#define        __SIO(x,y)      ((int)(SIOC_VOID|(x<<8)|y))
+#define        __SIOR(x,y,t)   ((int)(SIOC_OUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define        __SIOW(x,y,t)   ((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define        __SIOWR(x,y,t)  ((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define __SIOC_SIZE(x) ((x>>16)&SIOCPARM_MASK)
+#define __SIOC_DIR(x)  (x & 0xf0000000)
+#define __SIOC_NONE    SIOC_VOID
+#define __SIOC_READ    SIOC_OUT
+#define __SIOC_WRITE   SIOC_IN
+#  endif /* _IOWR */
+#endif /* !__SIOWR */
+
+#define OSS_LONGNAME_SIZE      64
+#define OSS_LABEL_SIZE         16
+#define OSS_DEVNODE_SIZE       32
+typedef char oss_longname_t[OSS_LONGNAME_SIZE];
+typedef char oss_label_t[OSS_LABEL_SIZE];
+typedef char oss_devnode_t[OSS_DEVNODE_SIZE];
+
+#ifndef DISABLE_SEQUENCER
+/*
+ ****************************************************************************
+ * IOCTL Commands for /dev/sequencer and /dev/music (AKA /dev/sequencer2)
+ *
+ * Note that this interface is obsolete and no longer developed. New
+ * applications should use /dev/midi instead.
+ ****************************************************************************/
+#define SNDCTL_SEQ_RESET               __SIO  ('Q', 0)
+#define SNDCTL_SEQ_SYNC                        __SIO  ('Q', 1)
+#define SNDCTL_SYNTH_INFO              __SIOWR('Q', 2, struct synth_info)
+#define SNDCTL_SEQ_CTRLRATE            __SIOWR('Q', 3, int)    /* Set/get timer resolution (HZ) */
+#define SNDCTL_SEQ_GETOUTCOUNT         __SIOR ('Q', 4, int)
+#define SNDCTL_SEQ_GETINCOUNT          __SIOR ('Q', 5, int)
+#define SNDCTL_SEQ_PERCMODE            __SIOW ('Q', 6, int)
+#define SNDCTL_FM_LOAD_INSTR           __SIOW ('Q', 7, struct sbi_instrument)  /* Obsolete. Don't use!!!!!! */
+#define SNDCTL_SEQ_TESTMIDI            __SIOW ('Q', 8, int)
+#define SNDCTL_SEQ_RESETSAMPLES                __SIOW ('Q', 9, int)
+#define SNDCTL_SEQ_NRSYNTHS            __SIOR ('Q',10, int)
+#define SNDCTL_SEQ_NRMIDIS             __SIOR ('Q',11, int)
+#define SNDCTL_MIDI_INFO               __SIOWR('Q',12, struct midi_info)       /* OBSOLETE - use SNDCTL_MIDIINFO instead */
+#define SNDCTL_SEQ_THRESHOLD           __SIOW ('Q',13, int)
+#define SNDCTL_SYNTH_MEMAVL            __SIOWR('Q',14, int)    /* in=dev#, out=memsize */
+#define SNDCTL_FM_4OP_ENABLE           __SIOW ('Q',15, int)    /* in=dev# */
+#define SNDCTL_SEQ_PANIC               __SIO  ('Q',17)
+#define SNDCTL_SEQ_OUTOFBAND           __SIOW ('Q',18, struct seq_event_rec)
+#define SNDCTL_SEQ_GETTIME             __SIOR ('Q',19, int)
+#define SNDCTL_SYNTH_ID                        __SIOWR('Q',20, struct synth_info)
+#define SNDCTL_SYNTH_CONTROL           __SIOWR('Q',21, struct synth_control)
+#define SNDCTL_SYNTH_REMOVESAMPLE      __SIOWR('Q',22, struct remove_sample)   /* Reserved for future use */
+#define SNDCTL_SEQ_TIMING_ENABLE       __SIO  ('Q', 23)        /* Enable incoming MIDI timing messages */
+#define SNDCTL_SEQ_ACTSENSE_ENABLE     __SIO  ('Q', 24)        /* Enable incoming active sensing messages */
+#define SNDCTL_SEQ_RT_ENABLE           __SIO  ('Q', 25)        /* Enable other incoming realtime messages */
+
+typedef struct synth_control
+{
+  int devno;                   /* Synthesizer # */
+  char data[4000];             /* Device spesific command/data record */
+} synth_control;
+
+typedef struct remove_sample
+{
+  int devno;                   /* Synthesizer # */
+  int bankno;                  /* MIDI bank # (0=General MIDI) */
+  int instrno;                 /* MIDI instrument number */
+} remove_sample;
+
+typedef struct seq_event_rec
+{
+  unsigned char arr[8];
+} seq_event_rec;
+
+#define SNDCTL_TMR_TIMEBASE            __SIOWR('T', 1, int)
+#define SNDCTL_TMR_START               __SIO  ('T', 2)
+#define SNDCTL_TMR_STOP                        __SIO  ('T', 3)
+#define SNDCTL_TMR_CONTINUE            __SIO  ('T', 4)
+#define SNDCTL_TMR_TEMPO               __SIOWR('T', 5, int)
+#define SNDCTL_TMR_SOURCE              __SIOWR('T', 6, int)
+#      define TMR_INTERNAL             0x00000001
+#      define TMR_EXTERNAL             0x00000002
+#              define TMR_MODE_MIDI    0x00000010
+#              define TMR_MODE_FSK     0x00000020
+#              define TMR_MODE_CLS     0x00000040
+#              define TMR_MODE_SMPTE   0x00000080
+#define SNDCTL_TMR_METRONOME           __SIOW ('T', 7, int)
+#define SNDCTL_TMR_SELECT              __SIOW ('T', 8, int)
+
+/*
+ * Sample loading mechanism for internal synthesizers (/dev/sequencer)
+ * (for the .PAT format).
+ */
+
+struct patch_info
+{
+  unsigned short key;          /* Use WAVE_PATCH here */
+#define WAVE_PATCH     _PATCHKEY(0x04)
+#define GUS_PATCH      WAVE_PATCH
+#define WAVEFRONT_PATCH _PATCHKEY(0x06)
+
+  short device_no;             /* Synthesizer number */
+  short instr_no;              /* Midi pgm# */
+
+  unsigned int mode;
+/*
+ * The least significant byte has the same format than the GUS .PAT
+ * files
+ */
+#define WAVE_16_BITS   0x01    /* bit 0 = 8 or 16 bit wave data. */
+#define WAVE_UNSIGNED  0x02    /* bit 1 = Signed - Unsigned data. */
+#define WAVE_LOOPING   0x04    /* bit 2 = looping enabled-1. */
+#define WAVE_BIDIR_LOOP        0x08    /* bit 3 = Set is bidirectional looping. */
+#define WAVE_LOOP_BACK 0x10    /* bit 4 = Set is looping backward. */
+#define WAVE_SUSTAIN_ON        0x20    /* bit 5 = Turn sustaining on. (Env. pts. 3) */
+#define WAVE_ENVELOPES 0x40    /* bit 6 = Enable envelopes - 1 */
+#define WAVE_FAST_RELEASE 0x80 /* bit 7 = Shut off immediately after note off */
+  /*  (use the env_rate/env_offs fields). */
+/* Linux specific bits */
+#define WAVE_VIBRATO   0x00010000      /* The vibrato info is valid */
+#define WAVE_TREMOLO   0x00020000      /* The tremolo info is valid */
+#define WAVE_SCALE     0x00040000      /* The scaling info is valid */
+#define WAVE_FRACTIONS 0x00080000      /* Fraction information is valid */
+/* Reserved bits */
+#define WAVE_ROM       0x40000000      /* For future use */
+#define WAVE_MULAW     0x20000000      /* For future use */
+/* Other bits must be zeroed */
+
+  int len;                     /* Size of the wave data in bytes */
+  int loop_start, loop_end;    /* Byte offsets from the beginning */
+
+/* 
+ * The base_freq and base_note fields are used when computing the
+ * playback speed for a note. The base_note defines the tone frequency
+ * which is heard if the sample is played using the base_freq as the
+ * playback speed.
+ *
+ * The low_note and high_note fields define the minimum and maximum note
+ * frequencies for which this sample is valid. It is possible to define
+ * more than one samples for an instrument number at the same time. The
+ * low_note and high_note fields are used to select the most suitable one.
+ *
+ * The fields base_note, high_note and low_note should contain
+ * the note frequency multiplied by 1000. For example value for the
+ * middle A is 440*1000.
+ */
+
+  unsigned int base_freq;
+  unsigned int base_note;
+  unsigned int high_note;
+  unsigned int low_note;
+  int panning;                 /* -128=left, 127=right */
+  int detuning;
+
+  /* Envelope. Enabled by mode bit WAVE_ENVELOPES  */
+  unsigned char env_rate[6];   /* GUS HW ramping rate */
+  unsigned char env_offset[6]; /* 255 == 100% */
+
+  /* 
+   * The tremolo, vibrato and scale info are not supported yet.
+   * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
+   * WAVE_SCALE
+   */
+
+  unsigned char tremolo_sweep;
+  unsigned char tremolo_rate;
+  unsigned char tremolo_depth;
+
+  unsigned char vibrato_sweep;
+  unsigned char vibrato_rate;
+  unsigned char vibrato_depth;
+
+  int scale_frequency;
+  unsigned int scale_factor;   /* from 0 to 2048 or 0 to 2 */
+
+  int volume;
+  int fractions;
+  int reserved1;
+  int spare[2];
+  char data[1];                        /* The waveform data starts here */
+};
+
+struct sysex_info
+{
+  short key;                   /* Use SYSEX_PATCH or MAUI_PATCH here */
+#define SYSEX_PATCH    _PATCHKEY(0x05)
+#define MAUI_PATCH     _PATCHKEY(0x06)
+  short device_no;             /* Synthesizer number */
+  int len;                     /* Size of the sysex data in bytes */
+  unsigned char data[1];       /* Sysex data starts here */
+};
+
+/*
+ * /dev/sequencer input events.
+ *
+ * The data written to the /dev/sequencer is a stream of events. Events
+ * are records of 4 or 8 bytes. The first byte defines the size. 
+ * Any number of events can be written with a write call. There
+ * is a set of macros for sending these events. Use these macros if you
+ * want to maximize portability of your program.
+ *
+ * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
+ * (All input events are currently 4 bytes long. Be prepared to support
+ * 8 byte events also. If you receive any event having first byte >= 128,
+ * it's a 8 byte event.
+ *
+ * The events are documented at the end of this file.
+ *
+ * Normal events (4 bytes)
+ * There is also a 8 byte version of most of the 4 byte events. The
+ * 8 byte one is recommended.
+ *
+ * NOTE! All 4 byte events are now obsolete. Applications should not write
+ *       them. However 4 byte events are still used as inputs from
+ *       /dev/sequencer (/dev/music uses only 8 byte ones).
+ */
+#define SEQ_NOTEOFF            0
+#define SEQ_FMNOTEOFF          SEQ_NOTEOFF     /* Just old name */
+#define SEQ_NOTEON             1
+#define        SEQ_FMNOTEON            SEQ_NOTEON
+#define SEQ_WAIT               TMR_WAIT_ABS
+#define SEQ_PGMCHANGE          3
+#define SEQ_FMPGMCHANGE                SEQ_PGMCHANGE
+#define SEQ_SYNCTIMER          TMR_START
+#define SEQ_MIDIPUTC           5
+#define SEQ_DRUMON             6               /*** OBSOLETE ***/
+#define SEQ_DRUMOFF            7               /*** OBSOLETE ***/
+#define SEQ_ECHO               TMR_ECHO        /* For synching programs with output */
+#define SEQ_AFTERTOUCH         9
+#define SEQ_CONTROLLER         10
+#define SEQ_BALANCE            11
+#define SEQ_VOLMODE             12
+
+/************************************
+ *     Midi controller numbers     *
+ ************************************/
+/*
+ * Controllers 0 to 31 (0x00 to 0x1f) and
+ * 32 to 63 (0x20 to 0x3f) are continuous
+ * controllers.
+ * In the MIDI 1.0 these controllers are sent using
+ * two messages. Controller numbers 0 to 31 are used
+ * to send the MSB and the controller numbers 32 to 63
+ * are for the LSB. Note that just 7 bits are used in MIDI bytes.
+ */
+
+#define           CTL_BANK_SELECT              0x00
+#define           CTL_MODWHEEL                 0x01
+#define    CTL_BREATH                  0x02
+/*             undefined               0x03 */
+#define    CTL_FOOT                    0x04
+#define    CTL_PORTAMENTO_TIME         0x05
+#define    CTL_DATA_ENTRY              0x06
+#define    CTL_MAIN_VOLUME             0x07
+#define    CTL_BALANCE                 0x08
+/*             undefined               0x09 */
+#define    CTL_PAN                     0x0a
+#define    CTL_EXPRESSION              0x0b
+/*             undefined               0x0c */
+/*             undefined               0x0d */
+/*             undefined               0x0e */
+/*             undefined               0x0f */
+#define    CTL_GENERAL_PURPOSE1                0x10
+#define    CTL_GENERAL_PURPOSE2                0x11
+#define    CTL_GENERAL_PURPOSE3                0x12
+#define    CTL_GENERAL_PURPOSE4                0x13
+/*             undefined               0x14 - 0x1f */
+
+/*             undefined               0x20 */
+/* The controller numbers 0x21 to 0x3f are reserved for the */
+/* least significant bytes of the controllers 0x00 to 0x1f. */
+/* These controllers are not recognised by the driver. */
+
+/* Controllers 64 to 69 (0x40 to 0x45) are on/off switches. */
+/* 0=OFF and 127=ON (intermediate values are possible) */
+#define    CTL_DAMPER_PEDAL            0x40
+#define    CTL_SUSTAIN                 0x40    /* Alias */
+#define    CTL_HOLD                    0x40    /* Alias */
+#define    CTL_PORTAMENTO              0x41
+#define    CTL_SOSTENUTO               0x42
+#define    CTL_SOFT_PEDAL              0x43
+/*             undefined               0x44 */
+#define    CTL_HOLD2                   0x45
+/*             undefined               0x46 - 0x4f */
+
+#define    CTL_GENERAL_PURPOSE5                0x50
+#define    CTL_GENERAL_PURPOSE6                0x51
+#define    CTL_GENERAL_PURPOSE7                0x52
+#define    CTL_GENERAL_PURPOSE8                0x53
+/*             undefined               0x54 - 0x5a */
+#define    CTL_EXT_EFF_DEPTH           0x5b
+#define    CTL_TREMOLO_DEPTH           0x5c
+#define    CTL_CHORUS_DEPTH            0x5d
+#define    CTL_DETUNE_DEPTH            0x5e
+#define    CTL_CELESTE_DEPTH           0x5e    /* Alias for the above one */
+#define    CTL_PHASER_DEPTH            0x5f
+#define    CTL_DATA_INCREMENT          0x60
+#define    CTL_DATA_DECREMENT          0x61
+#define    CTL_NONREG_PARM_NUM_LSB     0x62
+#define    CTL_NONREG_PARM_NUM_MSB     0x63
+#define    CTL_REGIST_PARM_NUM_LSB     0x64
+#define    CTL_REGIST_PARM_NUM_MSB     0x65
+/*             undefined               0x66 - 0x78 */
+/*             reserved                0x79 - 0x7f */
+
+/* Pseudo controllers (not midi compatible) */
+#define    CTRL_PITCH_BENDER           255
+#define    CTRL_PITCH_BENDER_RANGE     254
+#define    CTRL_EXPRESSION             253     /* Obsolete */
+#define    CTRL_MAIN_VOLUME            252     /* Obsolete */
+
+/*
+ * Volume mode defines how volumes are used
+ */
+
+#define VOL_METHOD_ADAGIO      1
+#define VOL_METHOD_LINEAR      2
+
+/*
+ * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
+ *      input events.
+ */
+
+/*
+ * Event codes 0xf0 to 0xfc are reserved for future extensions.
+ */
+
+#define SEQ_FULLSIZE           0xfd    /* Long events */
+/*
+ * SEQ_FULLSIZE events are used for loading patches/samples to the
+ * synthesizer devices. These events are passed directly to the driver
+ * of the associated synthesizer device. There is no limit to the size
+ * of the extended events. These events are not queued but executed
+ * immediately when the write() is called (execution can take several
+ * seconds of time). 
+ *
+ * When a SEQ_FULLSIZE message is written to the device, it must
+ * be written using exactly one write() call. Other events cannot
+ * be mixed to the same write.
+ *     
+ * For FM synths (YM3812/OPL3) use struct sbi_instrument and write it to the 
+ * /dev/sequencer. Don't write other data together with the instrument structure
+ * Set the key field of the structure to FM_PATCH. The device field is used to
+ * route the patch to the corresponding device.
+ *
+ * For wave table use struct patch_info. Initialize the key field
+ * to WAVE_PATCH.
+ */
+#define SEQ_PRIVATE            0xfe    /* Low level HW dependent events (8 bytes) */
+#define SEQ_EXTENDED           0xff    /* Extended events (8 bytes) OBSOLETE */
+
+/*
+ * Record for FM patches
+ */
+
+typedef unsigned char sbi_instr_data[32];
+
+struct sbi_instrument
+{
+  unsigned short key;          /* FM_PATCH or OPL3_PATCH */
+#define FM_PATCH       _PATCHKEY(0x01)
+#define OPL3_PATCH     _PATCHKEY(0x03)
+  short device;                        /*  Synth# (0-4)    */
+  int channel;                 /*  Program# to be initialized  */
+  sbi_instr_data operators;    /*  Register settings for operator cells (.SBI format)  */
+};
+
+struct synth_info
+{                              /* Read only */
+  char name[30];
+  int device;                  /* 0-N. INITIALIZE BEFORE CALLING */
+  int synth_type;
+#define SYNTH_TYPE_FM                  0
+#define SYNTH_TYPE_SAMPLE              1
+#define SYNTH_TYPE_MIDI                        2       /* Midi interface */
+
+  int synth_subtype;
+#define FM_TYPE_ADLIB                  0x00
+#define FM_TYPE_OPL3                   0x01
+#define MIDI_TYPE_MPU401               0x401
+
+#define SAMPLE_TYPE_BASIC              0x10
+#define SAMPLE_TYPE_GUS                        SAMPLE_TYPE_BASIC
+#define SAMPLE_TYPE_WAVEFRONT          0x11
+
+  int perc_mode;               /* No longer supported */
+  int nr_voices;
+  int nr_drums;                        /* Obsolete field */
+  int instr_bank_size;
+  unsigned int capabilities;
+#define SYNTH_CAP_PERCMODE     0x00000001      /* No longer used */
+#define SYNTH_CAP_OPL3         0x00000002      /* Set if OPL3 supported */
+#define SYNTH_CAP_INPUT                0x00000004      /* Input (MIDI) device */
+  int dummies[19];             /* Reserve space */
+};
+
+struct sound_timer_info
+{
+  char name[32];
+  int caps;
+};
+
+struct midi_info               /* OBSOLETE */
+{
+  char name[30];
+  int device;                  /* 0-N. INITIALIZE BEFORE CALLING */
+  unsigned int capabilities;   /* To be defined later */
+  int dev_type;
+  int dummies[18];             /* Reserve space */
+};
+
+/*
+ * Level 2 event types for /dev/sequencer
+ */
+
+/*
+ * The 4 most significant bits of byte 0 specify the class of
+ * the event: 
+ *
+ *     0x8X = system level events,
+ *     0x9X = device/port specific events, event[1] = device/port,
+ *             The last 4 bits give the subtype:
+ *                     0x02    = Channel event (event[3] = chn).
+ *                     0x01    = note event (event[4] = note).
+ *                     (0x01 is not used alone but always with bit 0x02).
+ *            event[2] = MIDI message code (0x80=note off etc.)
+ *
+ */
+
+#define EV_SEQ_LOCAL           0x80
+#define EV_TIMING              0x81
+#define EV_CHN_COMMON          0x92
+#define EV_CHN_VOICE           0x93
+#define EV_SYSEX               0x94
+#define EV_SYSTEM              0x95    /* MIDI system and real time messages (input only) */
+/*
+ * Event types 200 to 220 are reserved for application use.
+ * These numbers will not be used by the driver.
+ */
+
+/*
+ * Events for event type EV_CHN_VOICE
+ */
+
+#define MIDI_NOTEOFF           0x80
+#define MIDI_NOTEON            0x90
+#define MIDI_KEY_PRESSURE      0xA0
+
+/*
+ * Events for event type EV_CHN_COMMON
+ */
+
+#define MIDI_CTL_CHANGE                0xB0
+#define MIDI_PGM_CHANGE                0xC0
+#define MIDI_CHN_PRESSURE      0xD0
+#define MIDI_PITCH_BEND                0xE0
+
+#define MIDI_SYSTEM_PREFIX     0xF0
+
+/*
+ * Timer event types
+ */
+#define TMR_WAIT_REL           1       /* Time relative to the prev time */
+#define TMR_WAIT_ABS           2       /* Absolute time since TMR_START */
+#define TMR_STOP               3
+#define TMR_START              4
+#define TMR_CONTINUE           5
+#define TMR_TEMPO              6
+#define TMR_ECHO               8
+#define TMR_CLOCK              9       /* MIDI clock */
+#define TMR_SPP                        10      /* Song position pointer */
+#define TMR_TIMESIG            11      /* Time signature */
+
+/*
+ *     Local event types
+ */
+#define LOCL_STARTAUDIO                1
+#define LOCL_STARTAUDIO2       2
+#define LOCL_STARTAUDIO3       3
+#define LOCL_STARTAUDIO4       4
+
+#if (!defined(__KERNEL__) && !defined(KERNEL) && !defined(INKERNEL) && !defined(_KERNEL)) || defined(USE_SEQ_MACROS)
+/*
+ * Some convenience macros to simplify programming of the
+ * /dev/sequencer interface
+ *
+ * These macros define the API which should be used when possible.
+ */
+#define SEQ_DECLAREBUF()               SEQ_USE_EXTBUF()
+
+void seqbuf_dump (void);       /* This function must be provided by programs */
+
+EXTERNC int OSS_init (int seqfd, int buflen);
+EXTERNC void OSS_seqbuf_dump (int fd, unsigned char *buf, int buflen);
+EXTERNC void OSS_seq_advbuf (int len, int fd, unsigned char *buf, int buflen);
+EXTERNC void OSS_seq_needbuf (int len, int fd, unsigned char *buf,
+                             int buflen);
+EXTERNC void OSS_patch_caching (int dev, int chn, int patch, int fd,
+                               unsigned char *buf, int buflen);
+EXTERNC void OSS_drum_caching (int dev, int chn, int patch, int fd,
+                              unsigned char *buf, int buflen);
+EXTERNC void OSS_write_patch (int fd, unsigned char *buf, int len);
+EXTERNC int OSS_write_patch2 (int fd, unsigned char *buf, int len);
+
+#define SEQ_PM_DEFINES int __foo_bar___
+#ifdef OSSLIB
+#  define SEQ_USE_EXTBUF() \
+               EXTERNC unsigned char *_seqbuf; \
+               EXTERNC int _seqbuflen;EXTERNC int _seqbufptr
+#  define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len
+#  define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen)
+
+#  define SEQ_LOAD_GMINSTR(dev, instr) \
+               OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_LOAD_GMDRUM(dev, drum) \
+               OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen)
+#else /* !OSSLIB */
+
+#  define SEQ_LOAD_GMINSTR(dev, instr)
+#  define SEQ_LOAD_GMDRUM(dev, drum)
+
+#  define SEQ_USE_EXTBUF() \
+               EXTERNC unsigned char _seqbuf[]; \
+               EXTERNC int _seqbuflen;EXTERNC int _seqbufptr
+
+#ifndef USE_SIMPLE_MACROS
+/* Sample seqbuf_dump() implementation:
+ *
+ *     SEQ_DEFINEBUF (2048);   -- Defines a buffer for 2048 bytes
+ *
+ *     int seqfd;              -- The file descriptor for /dev/sequencer.
+ *
+ *     void
+ *     seqbuf_dump ()
+ *     {
+ *       if (_seqbufptr)
+ *         if (write (seqfd, _seqbuf, _seqbufptr) == -1)
+ *           {
+ *             perror ("write /dev/sequencer");
+ *             exit (-1);
+ *           }
+ *       _seqbufptr = 0;
+ *     }
+ */
+
+#define SEQ_DEFINEBUF(len) \
+       unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
+#define _SEQ_NEEDBUF(len) \
+       if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
+#define _SEQ_ADVBUF(len) _seqbufptr += len
+#define SEQ_DUMPBUF seqbuf_dump
+#else
+/*
+ * This variation of the sequencer macros is used just to format one event
+ * using fixed buffer.
+ * 
+ * The program using the macro library must define the following macros before
+ * using this library.
+ *
+ * #define _seqbuf              name of the buffer (unsigned char[]) 
+ * #define _SEQ_ADVBUF(len)     If the applic needs to know the exact
+ *                              size of the event, this macro can be used.
+ *                              Otherwise this must be defined as empty.
+ * #define _seqbufptr           Define the name of index variable or 0 if
+ *                              not required. 
+ */
+#define _SEQ_NEEDBUF(len)      /* empty */
+#endif
+#endif /* !OSSLIB */
+
+#define SEQ_VOLUME_MODE(dev, mode) \
+                               {_SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+                               _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
+                               _seqbuf[_seqbufptr+2] = (dev);\
+                               _seqbuf[_seqbufptr+3] = (mode);\
+                               _seqbuf[_seqbufptr+4] = 0;\
+                               _seqbuf[_seqbufptr+5] = 0;\
+                               _seqbuf[_seqbufptr+6] = 0;\
+                               _seqbuf[_seqbufptr+7] = 0;\
+                               _SEQ_ADVBUF(8);}
+
+/*
+ * Midi voice messages
+ */
+
+#define _CHN_VOICE(dev, event, chn, note, parm) \
+                               {_SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr] = EV_CHN_VOICE;\
+                               _seqbuf[_seqbufptr+1] = (dev);\
+                               _seqbuf[_seqbufptr+2] = (event);\
+                               _seqbuf[_seqbufptr+3] = (chn);\
+                               _seqbuf[_seqbufptr+4] = (note);\
+                               _seqbuf[_seqbufptr+5] = (parm);\
+                               _seqbuf[_seqbufptr+6] = (0);\
+                               _seqbuf[_seqbufptr+7] = 0;\
+                               _SEQ_ADVBUF(8);}
+
+#define SEQ_START_NOTE(dev, chn, note, vol) \
+                       _CHN_VOICE(dev, MIDI_NOTEON, chn, note, vol)
+
+#define SEQ_STOP_NOTE(dev, chn, note, vol) \
+                       _CHN_VOICE(dev, MIDI_NOTEOFF, chn, note, vol)
+
+#define SEQ_KEY_PRESSURE(dev, chn, note, pressure) \
+                       _CHN_VOICE(dev, MIDI_KEY_PRESSURE, chn, note, pressure)
+
+/*
+ * Midi channel messages
+ */
+
+#define _CHN_COMMON(dev, event, chn, p1, p2, w14) \
+                               {_SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr] = EV_CHN_COMMON;\
+                               _seqbuf[_seqbufptr+1] = (dev);\
+                               _seqbuf[_seqbufptr+2] = (event);\
+                               _seqbuf[_seqbufptr+3] = (chn);\
+                               _seqbuf[_seqbufptr+4] = (p1);\
+                               _seqbuf[_seqbufptr+5] = (p2);\
+                               *(short *)&_seqbuf[_seqbufptr+6] = (w14);\
+                               _SEQ_ADVBUF(8);}
+/*
+ * SEQ_SYSEX permits sending of sysex messages. (It may look that it permits
+ * sending any MIDI bytes but it's absolutely not possible. Trying to do
+ * so _will_ cause problems with MPU401 intelligent mode).
+ *
+ * Sysex messages are sent in blocks of 1 to 6 bytes. Longer messages must be 
+ * sent by calling SEQ_SYSEX() several times (there must be no other events
+ * between them). First sysex fragment must have 0xf0 in the first byte
+ * and the last byte (buf[len-1] of the last fragment must be 0xf7. No byte
+ * between these sysex start and end markers cannot be larger than 0x7f. Also
+ * lengths of each fragments (except the last one) must be 6.
+ *
+ * Breaking the above rules may work with some MIDI ports but is likely to
+ * cause fatal problems with some other devices (such as MPU401).
+ */
+#define SEQ_SYSEX(dev, buf, len) \
+                               {int ii, ll=(len); \
+                                unsigned char *bufp=buf;\
+                                if (ll>6)ll=6;\
+                               _SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr] = EV_SYSEX;\
+                               _seqbuf[_seqbufptr+1] = (dev);\
+                               for(ii=0;ii<ll;ii++)\
+                                  _seqbuf[_seqbufptr+ii+2] = bufp[ii];\
+                               for(ii=ll;ii<6;ii++)\
+                                  _seqbuf[_seqbufptr+ii+2] = 0xff;\
+                               _SEQ_ADVBUF(8);}
+
+#define SEQ_CHN_PRESSURE(dev, chn, pressure) \
+               _CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
+
+#define SEQ_SET_PATCH SEQ_PGM_CHANGE
+#ifdef OSSLIB
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
+               {OSS_patch_caching(dev, chn, patch, seqfd, _seqbuf, _seqbuflen); \
+                _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0);}
+#else
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
+               _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
+#endif
+
+#define SEQ_CONTROL(dev, chn, controller, value) \
+               _CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
+
+#define SEQ_BENDER(dev, chn, value) \
+               _CHN_COMMON(dev, MIDI_PITCH_BEND, chn, 0, 0, value)
+
+#define SEQ_V2_X_CONTROL(dev, voice, controller, value)        \
+                               {_SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
+                               _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
+                               _seqbuf[_seqbufptr+2] = (dev);\
+                               _seqbuf[_seqbufptr+3] = (voice);\
+                               _seqbuf[_seqbufptr+4] = (controller);\
+                               _seqbuf[_seqbufptr+5] = ((value)&0xff);\
+                               _seqbuf[_seqbufptr+6] = ((value>>8)&0xff);\
+                               _seqbuf[_seqbufptr+7] = 0;\
+                               _SEQ_ADVBUF(8);}
+/*
+ * The following 5 macros are incorrectly implemented and obsolete.
+ * Use SEQ_BENDER and SEQ_CONTROL (with proper controller) instead.
+ */
+#define SEQ_PITCHBEND(dev, voice, value) \
+       SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
+#define SEQ_BENDER_RANGE(dev, voice, value) \
+       SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
+#define SEQ_EXPRESSION(dev, voice, value) \
+       SEQ_CONTROL(dev, voice, CTL_EXPRESSION, value*128)
+#define SEQ_MAIN_VOLUME(dev, voice, value) \
+       SEQ_CONTROL(dev, voice, CTL_MAIN_VOLUME, (value*16383)/100)
+#define SEQ_PANNING(dev, voice, pos) \
+       SEQ_CONTROL(dev, voice, CTL_PAN, (pos+128) / 2)
+
+/*
+ * Timing and syncronization macros
+ */
+
+#define _TIMER_EVENT(ev, parm) {_SEQ_NEEDBUF(8);\
+                               _seqbuf[_seqbufptr+0] = EV_TIMING; \
+                               _seqbuf[_seqbufptr+1] = (ev); \
+                               _seqbuf[_seqbufptr+2] = 0;\
+                               _seqbuf[_seqbufptr+3] = 0;\
+                               *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \
+                               _SEQ_ADVBUF(8);}
+
+#define SEQ_START_TIMER()              _TIMER_EVENT(TMR_START, 0)
+#define SEQ_STOP_TIMER()               _TIMER_EVENT(TMR_STOP, 0)
+#define SEQ_CONTINUE_TIMER()           _TIMER_EVENT(TMR_CONTINUE, 0)
+#define SEQ_WAIT_TIME(ticks)           _TIMER_EVENT(TMR_WAIT_ABS, ticks)
+#define SEQ_DELTA_TIME(ticks)          _TIMER_EVENT(TMR_WAIT_REL, ticks)
+#define SEQ_ECHO_BACK(key)             _TIMER_EVENT(TMR_ECHO, key)
+#define SEQ_SET_TEMPO(value)           _TIMER_EVENT(TMR_TEMPO, value)
+#define SEQ_SONGPOS(pos)               _TIMER_EVENT(TMR_SPP, pos)
+#define SEQ_TIME_SIGNATURE(sig)                _TIMER_EVENT(TMR_TIMESIG, sig)
+
+/*
+ * Local control events
+ */
+
+#define _LOCAL_EVENT(ev, parm)         {_SEQ_NEEDBUF(8);\
+                                       _seqbuf[_seqbufptr+0] = EV_SEQ_LOCAL; \
+                                       _seqbuf[_seqbufptr+1] = (ev); \
+                                       _seqbuf[_seqbufptr+2] = 0;\
+                                       _seqbuf[_seqbufptr+3] = 0;\
+                                       *(unsigned int *)&_seqbuf[_seqbufptr+4] = (parm); \
+                                       _SEQ_ADVBUF(8);}
+
+#define SEQ_PLAYAUDIO(devmask)         _LOCAL_EVENT(LOCL_STARTAUDIO, devmask)
+#define SEQ_PLAYAUDIO2(devmask)                _LOCAL_EVENT(LOCL_STARTAUDIO2, devmask)
+#define SEQ_PLAYAUDIO3(devmask)                _LOCAL_EVENT(LOCL_STARTAUDIO3, devmask)
+#define SEQ_PLAYAUDIO4(devmask)                _LOCAL_EVENT(LOCL_STARTAUDIO4, devmask)
+/*
+ * Events for the level 1 interface only 
+ */
+
+#define SEQ_MIDIOUT(device, byte)      {_SEQ_NEEDBUF(4);\
+                                       _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
+                                       _seqbuf[_seqbufptr+1] = (byte);\
+                                       _seqbuf[_seqbufptr+2] = (device);\
+                                       _seqbuf[_seqbufptr+3] = 0;\
+                                       _SEQ_ADVBUF(4);}
+
+/*
+ * Patch loading.
+ */
+#ifdef OSSLIB
+#   define SEQ_WRPATCH(patchx, len) \
+               OSS_write_patch(seqfd, (char*)(patchx), len)
+#   define SEQ_WRPATCH2(patchx, len) \
+               OSS_write_patch2(seqfd, (char*)(patchx), len)
+#else
+#   define SEQ_WRPATCH(patchx, len) \
+               {if (_seqbufptr) SEQ_DUMPBUF();\
+                if (write(seqfd, (char*)(patchx), len)==-1) \
+                   perror("Write patch: /dev/sequencer");}
+#   define SEQ_WRPATCH2(patchx, len) \
+               (SEQ_DUMPBUF(), write(seqfd, (char*)(patchx), len))
+#endif
+
+#endif
+#endif /* ifndef DISABLE_SEQUENCER */
+
+/*
+ ****************************************************************************
+ * ioctl commands for the /dev/midi## 
+ ****************************************************************************/
+#define SNDCTL_MIDI_PRETIME    __SIOWR('m', 0, int)
+
+#if 0
+/*
+ * The SNDCTL_MIDI_MPUMODE and SNDCTL_MIDI_MPUCMD calls
+ * are completely obsolete. The hardware device (MPU-401 "intelligent mode"
+ * and compatibles) has disappeared from the market 10 years ago so there 
+ * is no need for this stuff. The MPU-401 "UART" mode devices don't support
+ * this stuff.
+ */
+typedef struct
+{
+  unsigned char cmd;
+  char nr_args, nr_returns;
+  unsigned char data[30];
+} mpu_command_rec;
+
+#define SNDCTL_MIDI_MPUMODE    __SIOWR('m', 1, int)
+#define SNDCTL_MIDI_MPUCMD     __SIOWR('m', 2, mpu_command_rec)
+#endif
+
+/*
+ * SNDCTL_MIDI_MTCINPUT turns on a mode where OSS automatically inserts
+ * MTC quarter frame messages (F1 xx) to the input.
+ * The argument is the MTC mode:
+ *
+ *     -1 = Turn MTC messages OFF (default)
+ *     24 = 24 FPS 
+ *     25 = 25 FPS 
+ *     29 = 30 FPS drop frame
+ *     30 = 30 FPS 
+ *
+ * Note that 25 FPS mode is probably the only mode that is supported. Other
+ * modes may be supported in the future versions of OSS, 25 FPS is handy 
+ * because it generates 25*4=100 quarter frame messages per second which
+ * matches the usual 100 HZ system timer rate).
+ *
+ * The quarter frame timer will be reset to 0:00:00:00.0 at the moment this
+ * ioctl is made.
+ */
+#define SNDCTL_MIDI_MTCINPUT   __SIOWR('m', 3, int)
+
+/*
+ * MTC/SMPTE time code record (for future use)
+ */
+typedef struct
+{
+  unsigned char hours, minutes, seconds, frames, qframes;
+  char direction;
+#define MTC_DIR_STOPPED                 0
+#define MTC_DIR_FORWARD                 1
+#define MTC_DIR_BACKWARD       -1
+  unsigned char time_code_type;
+  unsigned int flags;
+} oss_mtc_data_t;
+
+#define SNDCTL_MIDI_SETMODE    __SIOWR('m', 6, int)
+#      define MIDI_MODE_TRADITIONAL    0
+#      define MIDI_MODE_TIMED          1       /* Input times are in MIDI ticks */
+#      define MIDI_MODE_TIMED_ABS      2       /* Input times are absolute (usecs) */
+
+/*
+ * Packet header for MIDI_MODE_TIMED and MIDI_MODE_TIMED_ABS
+ */
+typedef unsigned long long oss_midi_time_t;    /* Variable type for MIDI time (clock ticks) */
+
+typedef struct
+{
+  int magic;                   /* Initialize to MIDI_HDR_MAGIC */
+#define MIDI_HDR_MAGIC -1
+  unsigned short event_type;
+#define MIDI_EV_WRITE                  0       /* Write or read (with payload) */
+#define MIDI_EV_TEMPO                  1
+#define MIDI_EV_ECHO                   2
+#define MIDI_EV_START                  3
+#define MIDI_EV_STOP                   4
+#define MIDI_EV_CONTINUE               5
+#define MIDI_EV_XPRESSWRITE            6
+#define MIDI_EV_TIMEBASE               7
+#define MIDI_EV_DEVCTL                 8       /* Device control read/write */
+  unsigned short options;
+#define MIDI_OPT_NONE                  0x0000
+#define MIDI_OPT_TIMED                 0x0001
+#define MIDI_OPT_CONTINUATION          0x0002
+#define MIDI_OPT_USECTIME              0x0004  /* Time is absolute (in usecs) */
+#define MIDI_OPT_BUSY                  0x0008  /* Reserved for internal use */
+  oss_midi_time_t time;
+  int parm;
+  int filler[3];               /* Fur future expansion - init to zeros */
+} midi_packet_header_t;
+/* 
+ * MIDI_PAYLOAD_SIZE is the maximum size of one MIDI input chunk. It must be
+ * less (or equal) than 1024 which is the read size recommended in the 
+ * documentation. TODO: Explain this better.
+ */
+#define MIDI_PAYLOAD_SIZE              1000
+
+typedef struct
+{
+  midi_packet_header_t hdr;
+  unsigned char payload[MIDI_PAYLOAD_SIZE];
+} midi_packet_t;
+
+#define SNDCTL_MIDI_TIMEBASE           __SIOWR('m', 7, int)
+#define SNDCTL_MIDI_TEMPO              __SIOWR('m', 8, int)
+/*
+ * User land MIDI servers (synths) can use SNDCTL_MIDI_SET_LATENCY
+ * to request MIDI events to be sent to them in advance. The parameter
+ * (in microseconds) tells how much before the events are submitted.
+ *
+ * This feature is only valid for loopback devices and possibly some other
+ * types of virtual devices.
+ */
+#define SNDCTL_MIDI_SET_LATENCY                __SIOW ('m', 9, int)
+/*
+ ****************************************************************************
+ * IOCTL commands for /dev/dsp
+ ****************************************************************************/
+
+#define SNDCTL_DSP_HALT                        __SIO  ('P', 0)
+#define SNDCTL_DSP_RESET               SNDCTL_DSP_HALT /* Old name */
+#define SNDCTL_DSP_SYNC                        __SIO  ('P', 1)
+#define SNDCTL_DSP_SPEED               __SIOWR('P', 2, int)
+
+/* SNDCTL_DSP_STEREO is obsolete - use SNDCTL_DSP_CHANNELS instead */
+#define SNDCTL_DSP_STEREO              __SIOWR('P', 3, int)
+/* SNDCTL_DSP_STEREO is obsolete - use SNDCTL_DSP_CHANNELS instead */
+
+#define SNDCTL_DSP_GETBLKSIZE          __SIOWR('P', 4, int)
+#define SNDCTL_DSP_SAMPLESIZE          SNDCTL_DSP_SETFMT
+#define SNDCTL_DSP_CHANNELS            __SIOWR('P', 6, int)
+#define SNDCTL_DSP_POST                        __SIO  ('P', 8)
+#define SNDCTL_DSP_SUBDIVIDE           __SIOWR('P', 9, int)
+#define SNDCTL_DSP_SETFRAGMENT         __SIOWR('P',10, int)
+
+/* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */
+#define SNDCTL_DSP_GETFMTS             __SIOR ('P',11, int)    /* Returns a mask */
+#define SNDCTL_DSP_SETFMT              __SIOWR('P',5, int)     /* Selects ONE fmt */
+#      define AFMT_QUERY       0x00000000      /* Return current fmt */
+#      define AFMT_MU_LAW      0x00000001
+#      define AFMT_A_LAW       0x00000002
+#      define AFMT_IMA_ADPCM   0x00000004
+#      define AFMT_U8          0x00000008
+#      define AFMT_S16_LE      0x00000010      /* Little endian signed 16 */
+#      define AFMT_S16_BE      0x00000020      /* Big endian signed 16 */
+#      define AFMT_S8          0x00000040
+#      define AFMT_U16_LE      0x00000080      /* Little endian U16 */
+#      define AFMT_U16_BE      0x00000100      /* Big endian U16 */
+#      define AFMT_MPEG        0x00000200      /* MPEG (2) audio */
+
+/* AC3 _compressed_ bitstreams (See Programmer's Guide for details). */
+#      define AFMT_AC3         0x00000400
+/* Ogg Vorbis _compressed_ bit streams */
+#      define AFMT_VORBIS      0x00000800
+
+/* 32 bit formats (MSB aligned) formats */
+#      define AFMT_S32_LE      0x00001000
+#      define AFMT_S32_BE      0x00002000
+
+/* Reserved for _native_ endian double precision IEEE floating point */
+#      define AFMT_FLOAT       0x00004000
+
+/* 24 bit formats (LSB aligned in 32 bit word) formats */
+#      define AFMT_S24_LE      0x00008000
+#      define AFMT_S24_BE      0x00010000
+
+/*
+ * S/PDIF raw format. In this format the S/PDIF frames (including all
+ * control and user bits) are included in the data stream. Each sample
+ * is stored in a 32 bit frame (see IEC-958 for more info). This format
+ * is supported by very few devices and it's only usable for purposes
+ * where full access to the control/user bits is required (real time control).
+ */
+#      define AFMT_SPDIF_RAW   0x00020000
+
+/* 24 bit packed (3 byte) little endian format (USB compatibility) */
+#      define AFMT_S24_PACKED  0x00040000
+
+
+/*
+ * Some big endian/little endian handling macros (native endian and opposite
+ * endian formats). The usage of these macros is described in the OSS
+ * Programmer's Manual.
+ */
+
+#if defined(_AIX) || defined(AIX) || defined(sparc) || defined(__hppa) || defined(PPC) || defined(__powerpc__) && !defined(i386) && !defined(__i386) && !defined(__i386__)
+
+/* Big endian machines */
+#  define _PATCHKEY(id) (0xfd00|id)
+#  define AFMT_S16_NE AFMT_S16_BE
+#  define AFMT_U16_NE AFMT_U16_BE
+#  define AFMT_S32_NE AFMT_S32_BE
+#  define AFMT_S24_NE AFMT_S24_BE
+#  define AFMT_S16_OE AFMT_S16_LE
+#  define AFMT_S32_OE AFMT_S32_LE
+#  define AFMT_S24_OE AFMT_S24_LE
+#else
+#  define _PATCHKEY(id) ((id<<8)|0xfd)
+#  define AFMT_S16_NE AFMT_S16_LE
+#  define AFMT_U16_NE AFMT_U16_LE
+#  define AFMT_S32_NE AFMT_S32_LE
+#  define AFMT_S24_NE AFMT_S24_LE
+#  define AFMT_S16_OE AFMT_S16_BE
+#  define AFMT_S32_OE AFMT_S32_BE
+#  define AFMT_S24_OE AFMT_S24_BE
+#endif
+/*
+ * Buffer status queries.
+ */
+typedef struct audio_buf_info
+{
+  int fragments;               /* # of available fragments (partially usend ones not counted) */
+  int fragstotal;              /* Total # of fragments allocated */
+  int fragsize;                        /* Size of a fragment in bytes */
+  int bytes;                   /* Available space in bytes (includes partially used fragments) */
+  /* Note! 'bytes' could be more than fragments*fragsize */
+} audio_buf_info;
+
+#define SNDCTL_DSP_GETOSPACE           __SIOR ('P',12, audio_buf_info)
+#define SNDCTL_DSP_GETISPACE           __SIOR ('P',13, audio_buf_info)
+#define SNDCTL_DSP_GETCAPS             __SIOR ('P',15, int)
+#      define PCM_CAP_REVISION         0x000000ff      /* Bits for revision level (0 to 255) */
+#      define PCM_CAP_DUPLEX           0x00000100      /* Full duplex record/playback */
+#      define PCM_CAP_REALTIME         0x00000200      /* Not in use */
+#      define PCM_CAP_BATCH            0x00000400      /* Device has some kind of */
+                                                       /* internal buffers which may */
+                                                       /* cause some delays and */
+                                                       /* decrease precision of timing */
+#      define PCM_CAP_COPROC           0x00000800      /* Has a coprocessor */
+                                                       /* Sometimes it's a DSP */
+                                                       /* but usually not */
+#      define PCM_CAP_TRIGGER          0x00001000      /* Supports SETTRIGGER */
+#      define PCM_CAP_MMAP             0x00002000      /* Supports mmap() */
+#      define PCM_CAP_MULTI            0x00004000      /* Supports multiple open */
+#      define PCM_CAP_BIND             0x00008000      /* Supports binding to front/rear/center/lfe */
+#      define PCM_CAP_INPUT            0x00010000      /* Supports recording */
+#      define PCM_CAP_OUTPUT           0x00020000      /* Supports playback */
+#      define PCM_CAP_VIRTUAL          0x00040000      /* Virtuial device */
+/* 0x00040000 and 0x00080000 reserved for future use */
+
+/* Analog/digital control capabilities */
+#      define PCM_CAP_ANALOGOUT        0x00100000
+#      define PCM_CAP_ANALOGIN         0x00200000
+#      define PCM_CAP_DIGITALOUT       0x00400000
+#      define PCM_CAP_DIGITALIN        0x00800000
+#      define PCM_CAP_ADMASK           0x00f00000
+/*
+ * NOTE! (capabilities & PCM_CAP_ADMASK)==0 means just that the
+ * digital/analog interface control features are not supported by the 
+ * device/driver. However the device still supports analog, digital or
+ * both inputs/outputs (depending on the device). See the OSS Programmer's
+ * Guide for full details.
+ */
+#      define PCM_CAP_SHADOW           0x01000000      /* "Shadow" device */
+
+/*
+ * Preferred channel usage. These bits can be used to
+ * give recommendations to the application. Used by few drivers.
+ * For example if ((caps & DSP_CH_MASK) == DSP_CH_MONO) means that
+ * the device works best in mono mode. However it doesn't necessarily mean
+ * that the device cannot be used in stereo. These bits should only be used
+ * special applications such as multi track hard disk recorders to find out
+ * the initial setup. However the user should be able to override this
+ * selection.
+ *
+ * To find out which modes are actually supported the application should 
+ * try to select them using SNDCTL_DSP_CHANNELS.
+ */
+#      define DSP_CH_MASK              0x06000000      /* Mask */
+#      define DSP_CH_ANY               0x00000000      /* No preferred mode */
+#      define DSP_CH_MONO              0x02000000
+#      define DSP_CH_STEREO            0x04000000
+#      define DSP_CH_MULTI             0x06000000      /* More than two channels */
+
+#      define PCM_CAP_HIDDEN           0x08000000      /* Hidden device */
+#      define PCM_CAP_FREERATE         0x10000000
+#      define PCM_CAP_MODEM            0x20000000      /* Modem device */
+#      define PCM_CAP_DEFAULT          0x40000000      /* "Default" device */
+
+/*
+ * The PCM_CAP_* capability names were known as DSP_CAP_* prior OSS 4.0
+ * so it's necessary to define the older names too.
+ */
+#define DSP_CAP_ADMASK         PCM_CAP_ADMASK
+#define DSP_CAP_ANALOGIN       PCM_CAP_ANALOGIN
+#define DSP_CAP_ANALOGOUT      PCM_CAP_ANALOGOUT
+#define DSP_CAP_BATCH          PCM_CAP_BATCH
+#define DSP_CAP_BIND           PCM_CAP_BIND
+#define DSP_CAP_COPROC         PCM_CAP_COPROC
+#define DSP_CAP_DEFAULT                PCM_CAP_DEFAULT
+#define DSP_CAP_DIGITALIN      PCM_CAP_DIGITALIN
+#define DSP_CAP_DIGITALOUT     PCM_CAP_DIGITALOUT
+#define DSP_CAP_DUPLEX         PCM_CAP_DUPLEX
+#define DSP_CAP_FREERATE       PCM_CAP_FREERATE
+#define DSP_CAP_HIDDEN         PCM_CAP_HIDDEN
+#define DSP_CAP_INPUT          PCM_CAP_INPUT
+#define DSP_CAP_MMAP           PCM_CAP_MMAP
+#define DSP_CAP_MODEM          PCM_CAP_MODEM
+#define DSP_CAP_MULTI          PCM_CAP_MULTI
+#define DSP_CAP_OUTPUT         PCM_CAP_OUTPUT
+#define DSP_CAP_REALTIME       PCM_CAP_REALTIME
+#define DSP_CAP_REVISION       PCM_CAP_REVISION
+#define DSP_CAP_SHADOW         PCM_CAP_SHADOW
+#define DSP_CAP_TRIGGER                PCM_CAP_TRIGGER
+#define DSP_CAP_VIRTUAL                PCM_CAP_VIRTUAL
+
+#define SNDCTL_DSP_GETTRIGGER          __SIOR ('P',16, int)
+#define SNDCTL_DSP_SETTRIGGER          __SIOW ('P',16, int)
+#      define PCM_ENABLE_INPUT         0x00000001
+#      define PCM_ENABLE_OUTPUT        0x00000002
+
+typedef struct count_info
+{
+  unsigned int bytes;          /* Total # of bytes processed */
+  int blocks;                  /* # of fragment transitions since last time */
+  int ptr;                     /* Current DMA pointer value */
+} count_info;
+
+#define SNDCTL_DSP_GETIPTR             __SIOR ('P',17, count_info)
+#define SNDCTL_DSP_GETOPTR             __SIOR ('P',18, count_info)
+
+typedef struct buffmem_desc
+{
+  unsigned *buffer;
+  int size;
+} buffmem_desc;
+#define SNDCTL_DSP_SETSYNCRO           __SIO  ('P', 21)
+#define SNDCTL_DSP_SETDUPLEX           __SIO  ('P', 22)
+
+#define SNDCTL_DSP_PROFILE             __SIOW ('P', 23, int)   /* OBSOLETE */
+#define          APF_NORMAL    0       /* Normal applications */
+#define          APF_NETWORK   1       /* Underruns probably caused by an "external" delay */
+#define   APF_CPUINTENS 2      /* Underruns probably caused by "overheating" the CPU */
+
+#define SNDCTL_DSP_GETODELAY           __SIOR ('P', 23, int)
+
+typedef struct audio_errinfo
+{
+  int play_underruns;
+  int rec_overruns;
+  unsigned int play_ptradjust;
+  unsigned int rec_ptradjust;
+  int play_errorcount;
+  int rec_errorcount;
+  int play_lasterror;
+  int rec_lasterror;
+  int play_errorparm;
+  int rec_errorparm;
+  int filler[16];
+} audio_errinfo;
+
+#define SNDCTL_DSP_GETPLAYVOL          __SIOR ('P', 24, int)
+#define SNDCTL_DSP_SETPLAYVOL          __SIOWR('P', 24, int)
+#define SNDCTL_DSP_GETERROR            __SIOR ('P', 25, audio_errinfo)
+/*
+ ****************************************************************************
+ * Digital interface (S/PDIF) control interface
+ */
+
+typedef struct oss_digital_control
+{
+  unsigned int caps;
+#define DIG_CBITIN_NONE                        0x00000000
+#define DIG_CBITIN_LIMITED             0x00000001
+#define DIG_CBITIN_DATA                0x00000002
+#define DIG_CBITIN_BYTE0               0x00000004
+#define DIG_CBITIN_FULL                0x00000008
+#define DIG_CBITIN_MASK                0x0000000f
+#define DIG_CBITOUT_NONE               0x00000000
+#define DIG_CBITOUT_LIMITED            0x00000010
+#define DIG_CBITOUT_BYTE0              0x00000020
+#define DIG_CBITOUT_FULL               0x00000040
+#define DIG_CBITOUT_DATA               0x00000080
+#define DIG_CBITOUT_MASK               0x000000f0
+#define DIG_UBITIN                     0x00000100
+#define DIG_UBITOUT                    0x00000200
+#define DIG_VBITOUT                    0x00000400
+#define DIG_OUTRATE                    0x00000800
+#define DIG_INRATE                     0x00001000
+#define DIG_INBITS                     0x00002000
+#define DIG_OUTBITS                    0x00004000
+#define DIG_EXACT                      0x00010000
+#define DIG_PRO                                0x00020000
+#define DIG_CONSUMER                   0x00040000
+#define DIG_PASSTHROUGH                        0x00080000
+#define DIG_OUTSEL                     0x00100000
+
+  unsigned int valid;
+#define VAL_CBITIN                     0x00000001
+#define VAL_UBITIN                     0x00000002
+#define VAL_CBITOUT                    0x00000004
+#define VAL_UBITOUT                    0x00000008
+#define VAL_ISTATUS                    0x00000010
+#define VAL_IRATE                      0x00000020
+#define VAL_ORATE                      0x00000040
+#define VAL_INBITS                     0x00000080
+#define VAL_OUTBITS                    0x00000100
+#define VAL_REQUEST                    0x00000200
+#define VAL_OUTSEL                     0x00000400
+
+#define VAL_OUTMASK (VAL_CBITOUT|VAL_UBITOUT|VAL_ORATE|VAL_OUTBITS|VAL_OUTSEL)
+
+  unsigned int request, param;
+#define SPD_RQ_PASSTHROUGH                             1
+
+  unsigned char cbitin[24];
+  unsigned char ubitin[24];
+  unsigned char cbitout[24];
+  unsigned char ubitout[24];
+
+  unsigned int outsel;
+#define OUTSEL_DIGITAL         1
+#define OUTSEL_ANALOG          2
+#define OUTSEL_BOTH            (OUTSEL_DIGITAL|OUTSEL_ANALOG)
+
+  int in_data;                 /* Audio/data if autodetectable by the receiver */
+#define IND_UNKNOWN            0
+#define IND_AUDIO              1
+#define IND_DATA               2
+
+  int in_locked;               /* Receiver locked */
+#define LOCK_NOT_INDICATED     0
+#define LOCK_UNLOCKED          1
+#define LOCK_LOCKED            2
+
+  int in_quality;              /* Input signal quality */
+#define IN_QUAL_NOT_INDICATED  0
+#define IN_QUAL_POOR           1
+#define IN_QUAL_GOOD           2
+
+  int in_vbit, out_vbit;       /* V bits */
+#define VBIT_NOT_INDICATED     0
+#define VBIT_OFF               1
+#define VBIT_ON                        2
+
+  unsigned int in_errors;      /* Various input errro conditions */
+#define INERR_CRC              0x0001
+#define INERR_QCODE_CRC                0x0002
+#define INERR_PARITY           0x0004
+#define INERR_BIPHASE          0x0008
+
+  int srate_in, srate_out;
+  int bits_in, bits_out;
+
+  int filler[32];
+} oss_digital_control;
+
+#define SNDCTL_DSP_READCTL             __SIOWR('P', 26, oss_digital_control)
+#define SNDCTL_DSP_WRITECTL            __SIOWR('P', 27, oss_digital_control)
+
+/*
+ ****************************************************************************
+ * Sync groups for audio devices
+ */
+typedef struct oss_syncgroup
+{
+  int id;
+  int mode;
+  int filler[16];
+} oss_syncgroup;
+
+#define SNDCTL_DSP_SYNCGROUP           __SIOWR('P', 28, oss_syncgroup)
+#define SNDCTL_DSP_SYNCSTART           __SIOW ('P', 29, int)
+
+/*
+ **************************************************************************
+ * "cooked" mode enables software based conversions for sample rate, sample
+ * format (bits) and number of channels (mono/stereo). These conversions are
+ * required with some devices that support only one sample rate or just stereo
+ * to let the applications to use other formats. The cooked mode is enabled by
+ * default. However it's necessary to disable this mode when mmap() is used or
+ * when very deterministic timing is required. SNDCTL_DSP_COOKEDMODE is an
+ * optional call introduced in OSS 3.9.6f. It's _error return must be ignored_
+ * since normally this call will return erno=EINVAL.
+ *
+ * SNDCTL_DSP_COOKEDMODE must be called immediately after open before doing
+ * anything else. Otherwise the call will not have any effect.
+ */
+#define SNDCTL_DSP_COOKEDMODE          __SIOW ('P', 30, int)
+
+/*
+ **************************************************************************
+ * SNDCTL_DSP_SILENCE and SNDCTL_DSP_SKIP are new calls in OSS 3.99.0
+ * that can be used to implement pause/continue during playback (no effect
+ * on recording).
+ */
+#define SNDCTL_DSP_SILENCE             __SIO  ('P', 31)
+#define SNDCTL_DSP_SKIP                        __SIO  ('P', 32)
+/*
+ ****************************************************************************
+ * Abort transfer (reset) functions for input and output
+ */
+#define SNDCTL_DSP_HALT_INPUT          __SIO  ('P', 33)
+#define SNDCTL_DSP_RESET_INPUT SNDCTL_DSP_HALT_INPUT   /* Old name */
+#define SNDCTL_DSP_HALT_OUTPUT         __SIO  ('P', 34)
+#define SNDCTL_DSP_RESET_OUTPUT        SNDCTL_DSP_HALT_OUTPUT  /* Old name */
+/*
+ ****************************************************************************
+ * Low water level control
+ */
+#define SNDCTL_DSP_LOW_WATER           __SIOW ('P', 34, int)
+
+/*
+ ****************************************************************************
+ * 64 bit pointer support. Only available in environments that support
+ * the 64 bit (long long) integer type.
+ */
+#ifndef OSS_NO_LONG_LONG
+typedef struct
+{
+  long long samples;
+  int fifo_samples;
+  int filler[32];              /* For future use */
+} oss_count_t;
+
+#define SNDCTL_DSP_CURRENT_IPTR                __SIOR ('P', 35, oss_count_t)
+#define SNDCTL_DSP_CURRENT_OPTR                __SIOR ('P', 36, oss_count_t)
+#endif
+
+/*
+ ****************************************************************************
+ * Interface for selecting recording sources and playback output routings.
+ */
+#define SNDCTL_DSP_GET_RECSRC_NAMES    __SIOR ('P', 37, oss_mixer_enuminfo)
+#define SNDCTL_DSP_GET_RECSRC          __SIOR ('P', 38, int)
+#define SNDCTL_DSP_SET_RECSRC          __SIOWR('P', 38, int)
+
+#define SNDCTL_DSP_GET_PLAYTGT_NAMES   __SIOR ('P', 39, oss_mixer_enuminfo)
+#define SNDCTL_DSP_GET_PLAYTGT         __SIOR ('P', 40, int)
+#define SNDCTL_DSP_SET_PLAYTGT         __SIOWR('P', 40, int)
+#define SNDCTL_DSP_GETRECVOL           __SIOR ('P', 41, int)
+#define SNDCTL_DSP_SETRECVOL           __SIOWR('P', 41, int)
+
+/*
+ ***************************************************************************
+ * Some calls for setting the channel assignment with multi channel devices
+ * (see the manual for details).
+ */
+#ifndef OSS_NO_LONG_LONG
+#define SNDCTL_DSP_GET_CHNORDER                __SIOR ('P', 42, unsigned long long)
+#define SNDCTL_DSP_SET_CHNORDER                __SIOWR('P', 42, unsigned long long)
+#      define CHID_UNDEF       0
+#      define CHID_L           1
+#      define CHID_R           2
+#      define CHID_C           3
+#      define CHID_LFE         4
+#      define CHID_LS          5
+#      define CHID_RS          6
+#      define CHID_LR          7
+#      define CHID_RR          8
+#define CHNORDER_UNDEF         0x0000000000000000ULL
+#define CHNORDER_NORMAL                0x0000000087654321ULL
+#endif
+
+#define MAX_PEAK_CHANNELS      128
+typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS];
+#define SNDCTL_DSP_GETIPEAKS           __SIOR('P', 43, oss_peaks_t)
+#define SNDCTL_DSP_GETOPEAKS           __SIOR('P', 44, oss_peaks_t)
+
+#define SNDCTL_DSP_POLICY              __SIOW('P', 45, int)    /* See the manual */
+
+/*
+ ****************************************************************************
+ * Few ioctl calls that are not official parts of OSS. They have been used
+ * by few freeware implementations of OSS.
+ */
+#define SNDCTL_DSP_GETCHANNELMASK      __SIOWR('P', 64, int)
+#define SNDCTL_DSP_BIND_CHANNEL                __SIOWR('P', 65, int)
+#     define DSP_BIND_QUERY           0x00000000
+#     define DSP_BIND_FRONT           0x00000001
+#     define DSP_BIND_SURR            0x00000002
+#     define DSP_BIND_CENTER_LFE      0x00000004
+#     define DSP_BIND_HANDSET         0x00000008
+#     define DSP_BIND_MIC             0x00000010
+#     define DSP_BIND_MODEM1          0x00000020
+#     define DSP_BIND_MODEM2          0x00000040
+#     define DSP_BIND_I2S             0x00000080
+#     define DSP_BIND_SPDIF           0x00000100
+#     define DSP_BIND_REAR            0x00000200
+
+#ifndef NO_LEGACY_MIXER
+/*
+ ****************************************************************************
+ * IOCTL commands for the "legacy " /dev/mixer API (obsolete)
+ *
+ * Mixer controls
+ *
+ * There can be up to 20 different analog mixer channels. The
+ * SOUND_MIXER_NRDEVICES gives the currently supported maximum. 
+ * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
+ * the devices supported by the particular mixer.
+ *
+ * {!notice This "legacy" mixer API is obsolete. It has been superceded
+ * by a new one (see below).
+ */
+
+#define SOUND_MIXER_NRDEVICES  28
+#define SOUND_MIXER_VOLUME     0
+#define SOUND_MIXER_BASS       1
+#define SOUND_MIXER_TREBLE     2
+#define SOUND_MIXER_SYNTH      3
+#define SOUND_MIXER_PCM                4
+#define SOUND_MIXER_SPEAKER    5
+#define SOUND_MIXER_LINE       6
+#define SOUND_MIXER_MIC                7
+#define SOUND_MIXER_CD         8
+#define SOUND_MIXER_IMIX       9       /*  Recording monitor  */
+#define SOUND_MIXER_ALTPCM     10
+#define SOUND_MIXER_RECLEV     11      /* Recording level */
+#define SOUND_MIXER_IGAIN      12      /* Input gain */
+#define SOUND_MIXER_OGAIN      13      /* Output gain */
+/* 
+ * Some soundcards have three line level inputs (line, aux1 and aux2). 
+ * Since each card manufacturer has assigned different meanings to 
+ * these inputs, it's impractical to assign specific meanings 
+ * (eg line, cd, synth etc.) to them.
+ */
+#define SOUND_MIXER_LINE1      14      /* Input source 1  (aux1) */
+#define SOUND_MIXER_LINE2      15      /* Input source 2  (aux2) */
+#define SOUND_MIXER_LINE3      16      /* Input source 3  (line) */
+#define SOUND_MIXER_DIGITAL1   17      /* Digital I/O 1 */
+#define SOUND_MIXER_DIGITAL2   18      /* Digital I/O 2 */
+#define SOUND_MIXER_DIGITAL3   19      /* Digital I/O 3 */
+#define SOUND_MIXER_PHONE      20      /* Phone */
+#define SOUND_MIXER_MONO       21      /* Mono Output */
+#define SOUND_MIXER_VIDEO      22      /* Video/TV (audio) in */
+#define SOUND_MIXER_RADIO      23      /* Radio in */
+#define SOUND_MIXER_DEPTH      24      /* Surround depth */
+#define SOUND_MIXER_REARVOL    25      /* Rear/Surround speaker vol */
+#define SOUND_MIXER_CENTERVOL  26      /* Center/LFE speaker vol */
+#define SOUND_MIXER_SIDEVOL    27      /* Side-Surround (8speaker) vol */
+
+/*
+ * Warning: SOUND_MIXER_SURRVOL is an old name of SOUND_MIXER_SIDEVOL.
+ *          They are both assigned to the same mixer control. Don't
+ *          use both control names in the same program/driver.
+ */
+#define SOUND_MIXER_SURRVOL    SOUND_MIXER_SIDEVOL
+
+/* Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX) */
+/* Not counted to SOUND_MIXER_NRDEVICES, but use the same number space */
+#define SOUND_ONOFF_MIN                28
+#define SOUND_ONOFF_MAX                30
+
+/* Note!       Number 31 cannot be used since the sign bit is reserved */
+#define SOUND_MIXER_NONE       31
+
+/*
+ * The following unsupported macros are no longer functional.
+ * Use SOUND_MIXER_PRIVATE# macros in future.
+ */
+#define SOUND_MIXER_ENHANCE    SOUND_MIXER_NONE
+#define SOUND_MIXER_MUTE       SOUND_MIXER_NONE
+#define SOUND_MIXER_LOUD       SOUND_MIXER_NONE
+
+#define SOUND_DEVICE_LABELS \
+       {"Vol  ", "Bass ", "Treble", "Synth", "Pcm  ", "Speaker ", "Line ", \
+        "Mic  ", "CD   ", "Mix  ", "Pcm2 ", "Rec  ", "IGain", "OGain", \
+        "Aux1", "Aux2", "Aux3", "Digital1", "Digital2", "Digital3", \
+        "Phone", "Mono", "Video", "Radio", "Depth", \
+        "Rear", "Center", "Side"}
+
+#define SOUND_DEVICE_NAMES \
+       {"vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
+        "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \
+        "aux1", "aux2", "aux3", "dig1", "dig2", "dig3", \
+        "phone", "mono", "video", "radio", "depth", \
+        "rear", "center", "side"}
+
+/*     Device bitmask identifiers      */
+
+#define SOUND_MIXER_RECSRC     0xff    /* Arg contains a bit for each recording source */
+#define SOUND_MIXER_DEVMASK    0xfe    /* Arg contains a bit for each supported device */
+#define SOUND_MIXER_RECMASK    0xfd    /* Arg contains a bit for each supported recording source */
+#define SOUND_MIXER_CAPS       0xfc
+#      define SOUND_CAP_EXCL_INPUT     0x00000001      /* Only one recording source at a time */
+#      define SOUND_CAP_NOLEGACY       0x00000004      /* For internal use only */
+#      define SOUND_CAP_NORECSRC       0x00000008
+#define SOUND_MIXER_STEREODEVS 0xfb    /* Mixer channels supporting stereo */
+
+/* OSS/Free ONLY */
+#define SOUND_MIXER_OUTSRC    0xfa     /* Arg contains a bit for each input source to output */
+#define SOUND_MIXER_OUTMASK   0xf9     /* Arg contains a bit for each supported input source to output */
+/* OSS/Free ONLY */
+
+/*     Device mask bits        */
+
+#define SOUND_MASK_VOLUME      (1 << SOUND_MIXER_VOLUME)
+#define SOUND_MASK_BASS                (1 << SOUND_MIXER_BASS)
+#define SOUND_MASK_TREBLE      (1 << SOUND_MIXER_TREBLE)
+#define SOUND_MASK_SYNTH       (1 << SOUND_MIXER_SYNTH)
+#define SOUND_MASK_PCM         (1 << SOUND_MIXER_PCM)
+#define SOUND_MASK_SPEAKER     (1 << SOUND_MIXER_SPEAKER)
+#define SOUND_MASK_LINE                (1 << SOUND_MIXER_LINE)
+#define SOUND_MASK_MIC         (1 << SOUND_MIXER_MIC)
+#define SOUND_MASK_CD          (1 << SOUND_MIXER_CD)
+#define SOUND_MASK_IMIX                (1 << SOUND_MIXER_IMIX)
+#define SOUND_MASK_ALTPCM      (1 << SOUND_MIXER_ALTPCM)
+#define SOUND_MASK_RECLEV      (1 << SOUND_MIXER_RECLEV)
+#define SOUND_MASK_IGAIN       (1 << SOUND_MIXER_IGAIN)
+#define SOUND_MASK_OGAIN       (1 << SOUND_MIXER_OGAIN)
+#define SOUND_MASK_LINE1       (1 << SOUND_MIXER_LINE1)
+#define SOUND_MASK_LINE2       (1 << SOUND_MIXER_LINE2)
+#define SOUND_MASK_LINE3       (1 << SOUND_MIXER_LINE3)
+#define SOUND_MASK_DIGITAL1    (1 << SOUND_MIXER_DIGITAL1)
+#define SOUND_MASK_DIGITAL2    (1 << SOUND_MIXER_DIGITAL2)
+#define SOUND_MASK_DIGITAL3    (1 << SOUND_MIXER_DIGITAL3)
+#define SOUND_MASK_MONO                (1 << SOUND_MIXER_MONO)
+#define SOUND_MASK_PHONE       (1 << SOUND_MIXER_PHONE)
+#define SOUND_MASK_RADIO       (1 << SOUND_MIXER_RADIO)
+#define SOUND_MASK_VIDEO       (1 << SOUND_MIXER_VIDEO)
+#define SOUND_MASK_DEPTH       (1 << SOUND_MIXER_DEPTH)
+#define SOUND_MASK_REARVOL     (1 << SOUND_MIXER_REARVOL)
+#define SOUND_MASK_CENTERVOL   (1 << SOUND_MIXER_CENTERVOL)
+#define SOUND_MASK_SIDEVOL     (1 << SOUND_MIXER_SIDEVOL)
+
+/* Note! SOUND_MASK_SURRVOL is alias of SOUND_MASK_SIDEVOL */
+#define SOUND_MASK_SURRVOL     (1 << SOUND_MIXER_SIDEVOL)
+
+/* Obsolete macros */
+#define SOUND_MASK_MUTE                (1 << SOUND_MIXER_MUTE)
+#define SOUND_MASK_ENHANCE     (1 << SOUND_MIXER_ENHANCE)
+#define SOUND_MASK_LOUD                (1 << SOUND_MIXER_LOUD)
+
+#define MIXER_READ(dev)                        __SIOR('M', dev, int)
+#define SOUND_MIXER_READ_VOLUME                MIXER_READ(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_READ_BASS          MIXER_READ(SOUND_MIXER_BASS)
+#define SOUND_MIXER_READ_TREBLE                MIXER_READ(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_READ_SYNTH         MIXER_READ(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_READ_PCM           MIXER_READ(SOUND_MIXER_PCM)
+#define SOUND_MIXER_READ_SPEAKER       MIXER_READ(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_READ_LINE          MIXER_READ(SOUND_MIXER_LINE)
+#define SOUND_MIXER_READ_MIC           MIXER_READ(SOUND_MIXER_MIC)
+#define SOUND_MIXER_READ_CD            MIXER_READ(SOUND_MIXER_CD)
+#define SOUND_MIXER_READ_IMIX          MIXER_READ(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_READ_ALTPCM                MIXER_READ(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_READ_RECLEV                MIXER_READ(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_READ_IGAIN         MIXER_READ(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_READ_OGAIN         MIXER_READ(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_READ_LINE1         MIXER_READ(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_READ_LINE2         MIXER_READ(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_READ_LINE3         MIXER_READ(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define SOUND_MIXER_READ_MUTE          MIXER_READ(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_READ_ENHANCE       MIXER_READ(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_READ_LOUD          MIXER_READ(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_READ_RECSRC                MIXER_READ(SOUND_MIXER_RECSRC)
+#define SOUND_MIXER_READ_DEVMASK       MIXER_READ(SOUND_MIXER_DEVMASK)
+#define SOUND_MIXER_READ_RECMASK       MIXER_READ(SOUND_MIXER_RECMASK)
+#define SOUND_MIXER_READ_STEREODEVS    MIXER_READ(SOUND_MIXER_STEREODEVS)
+#define SOUND_MIXER_READ_CAPS          MIXER_READ(SOUND_MIXER_CAPS)
+
+#define MIXER_WRITE(dev)               __SIOWR('M', dev, int)
+#define SOUND_MIXER_WRITE_VOLUME       MIXER_WRITE(SOUND_MIXER_VOLUME)
+#define SOUND_MIXER_WRITE_BASS         MIXER_WRITE(SOUND_MIXER_BASS)
+#define SOUND_MIXER_WRITE_TREBLE       MIXER_WRITE(SOUND_MIXER_TREBLE)
+#define SOUND_MIXER_WRITE_SYNTH                MIXER_WRITE(SOUND_MIXER_SYNTH)
+#define SOUND_MIXER_WRITE_PCM          MIXER_WRITE(SOUND_MIXER_PCM)
+#define SOUND_MIXER_WRITE_SPEAKER      MIXER_WRITE(SOUND_MIXER_SPEAKER)
+#define SOUND_MIXER_WRITE_LINE         MIXER_WRITE(SOUND_MIXER_LINE)
+#define SOUND_MIXER_WRITE_MIC          MIXER_WRITE(SOUND_MIXER_MIC)
+#define SOUND_MIXER_WRITE_CD           MIXER_WRITE(SOUND_MIXER_CD)
+#define SOUND_MIXER_WRITE_IMIX         MIXER_WRITE(SOUND_MIXER_IMIX)
+#define SOUND_MIXER_WRITE_ALTPCM       MIXER_WRITE(SOUND_MIXER_ALTPCM)
+#define SOUND_MIXER_WRITE_RECLEV       MIXER_WRITE(SOUND_MIXER_RECLEV)
+#define SOUND_MIXER_WRITE_IGAIN                MIXER_WRITE(SOUND_MIXER_IGAIN)
+#define SOUND_MIXER_WRITE_OGAIN                MIXER_WRITE(SOUND_MIXER_OGAIN)
+#define SOUND_MIXER_WRITE_LINE1                MIXER_WRITE(SOUND_MIXER_LINE1)
+#define SOUND_MIXER_WRITE_LINE2                MIXER_WRITE(SOUND_MIXER_LINE2)
+#define SOUND_MIXER_WRITE_LINE3                MIXER_WRITE(SOUND_MIXER_LINE3)
+
+/* Obsolete macros */
+#define SOUND_MIXER_WRITE_MUTE         MIXER_WRITE(SOUND_MIXER_MUTE)
+#define SOUND_MIXER_WRITE_ENHANCE      MIXER_WRITE(SOUND_MIXER_ENHANCE)
+#define SOUND_MIXER_WRITE_LOUD         MIXER_WRITE(SOUND_MIXER_LOUD)
+
+#define SOUND_MIXER_WRITE_RECSRC       MIXER_WRITE(SOUND_MIXER_RECSRC)
+
+typedef struct mixer_info      /* OBSOLETE */
+{
+  char id[16];
+  char name[32];
+  int modify_counter;
+  int card_number;
+  int port_number;
+  char handle[32];
+} mixer_info;
+
+/* SOUND_MIXER_INFO is obsolete - use SNDCTL_MIXERINFO instead */
+#define SOUND_MIXER_INFO               __SIOR ('M', 101, mixer_info)
+
+/*
+ * Two ioctls for special souncard function (OSS/Free only)
+ */
+#define SOUND_MIXER_AGC  _SIOWR('M', 103, int)
+#define SOUND_MIXER_3DSE  _SIOWR('M', 104, int)
+/*
+ * The SOUND_MIXER_PRIVATE# commands can be redefined by low level drivers.
+ * These features can be used when accessing device specific features.
+ */
+#define SOUND_MIXER_PRIVATE1           __SIOWR('M', 111, int)
+#define SOUND_MIXER_PRIVATE2           __SIOWR('M', 112, int)
+#define SOUND_MIXER_PRIVATE3           __SIOWR('M', 113, int)
+#define SOUND_MIXER_PRIVATE4           __SIOWR('M', 114, int)
+#define SOUND_MIXER_PRIVATE5           __SIOWR('M', 115, int)
+
+/* The following two controls were never implemented and they should not be used. */
+#define SOUND_MIXER_READ_MAINVOL               __SIOR ('M', 116, int)
+#define SOUND_MIXER_WRITE_MAINVOL              __SIOWR('M', 116, int)
+
+/*
+ * SOUND_MIXER_GETLEVELS and SOUND_MIXER_SETLEVELS calls can be used
+ * for querying current mixer settings from the driver and for loading
+ * default volume settings _prior_ activating the mixer (loading
+ * doesn't affect current state of the mixer hardware). These calls
+ * are for internal use by the driver software only.
+ */
+
+typedef struct mixer_vol_table
+{
+  int num;                     /* Index to volume table */
+  char name[32];
+  int levels[32];
+} mixer_vol_table;
+
+#define SOUND_MIXER_GETLEVELS          __SIOWR('M', 116, mixer_vol_table)
+#define SOUND_MIXER_SETLEVELS          __SIOWR('M', 117, mixer_vol_table)
+
+#define OSS_GETVERSION                 __SIOR ('M', 118, int)
+
+/*
+ * Calls to set/get the recording gain for the currently active
+ * recording source. These calls automatically map to the right control.
+ * Note that these calls are not supported by all drivers. In this case
+ * the call will return -1 with errno set to EINVAL
+ *
+ * The _MONGAIN work in similar way but set/get the monitoring gain for
+ * the currently selected recording source.
+ */
+#define SOUND_MIXER_READ_RECGAIN       __SIOR ('M', 119, int)
+#define SOUND_MIXER_WRITE_RECGAIN      __SIOWR('M', 119, int)
+#define SOUND_MIXER_READ_MONGAIN       __SIOR ('M', 120, int)
+#define SOUND_MIXER_WRITE_MONGAIN      __SIOWR('M', 120, int)
+
+/* The following call is for driver development time purposes. It's not
+ * present in any released drivers.
+ */
+typedef unsigned char oss_reserved_t[512];
+#define SOUND_MIXER_RESERVED           __SIOWR('M', 121, oss_reserved_t)
+#endif /* ifndef NO_LEGACY_MIXER */
+
+/*
+ *************************************************************************
+ * The "new" mixer API of OSS 4.0 and later.
+ *
+ * This improved mixer API makes it possible to access every possible feature
+ * of every possible device. However you should read the mixer programming
+ * section of the OSS API Developer's Manual. There is no chance that you
+ * could use this interface correctly just by examining this header.
+ */
+
+typedef struct oss_sysinfo
+{
+  char product[32];            /* For example OSS/Free, OSS/Linux or OSS/Solaris */
+  char version[32];            /* For example 4.0a */
+  int versionnum;              /* See OSS_GETVERSION */
+  char options[128];           /* Reserved */
+
+  int numaudios;               /* # of audio/dsp devices */
+  int openedaudio[8];          /* Bit mask telling which audio devices are busy */
+
+  int numsynths;               /* # of availavle synth devices */
+  int nummidis;                        /* # of available MIDI ports */
+  int numtimers;               /* # of available timer devices */
+  int nummixers;               /* # of mixer devices */
+
+  int openedmidi[8];           /* Bit mask telling which midi devices are busy */
+  int numcards;                        /* Number of sound cards in the system */
+  int numaudioengines;         /* Number of audio engines in the system */
+  int filler[240];             /* For future expansion (set to -1) */
+} oss_sysinfo;
+
+typedef struct oss_mixext
+{
+  int dev;                     /* Mixer device number */
+  int ctrl;                    /* Controller number */
+  int type;                    /* Entry type */
+#      define MIXT_DEVROOT      0      /* Device root entry */
+#      define MIXT_GROUP        1      /* Controller group */
+#      define MIXT_ONOFF        2      /* OFF (0) or ON (1) */
+#      define MIXT_ENUM         3      /* Enumerated (0 to maxvalue) */
+#      define MIXT_MONOSLIDER   4      /* Mono slider (0 to 255) */
+#      define MIXT_STEREOSLIDER 5      /* Stereo slider (dual 0 to 255) */
+#      define MIXT_MESSAGE      6      /* (Readable) textual message */
+#      define MIXT_MONOVU       7      /* VU meter value (mono) */
+#      define MIXT_STEREOVU     8      /* VU meter value (stereo) */
+#      define MIXT_MONOPEAK     9      /* VU meter peak value (mono) */
+#      define MIXT_STEREOPEAK  10      /* VU meter peak value (stereo) */
+#      define MIXT_RADIOGROUP  11      /* Radio button group */
+#      define MIXT_MARKER      12      /* Separator between normal and extension entries */
+#      define MIXT_VALUE       13      /* Decimal value entry */
+#      define MIXT_HEXVALUE    14      /* Hexadecimal value entry */
+#      define MIXT_MONODB      15      /* OBSOLETE */
+#      define MIXT_STEREODB    16      /* OBSOLETE */
+#      define MIXT_SLIDER      17      /* Slider (mono) with full (31 bit) postitive integer range */
+#      define MIXT_3D          18
+
+/*
+ * Sliders with range expanded to 15 bits per channel (0-32767)
+ */
+#      define MIXT_MONOSLIDER16        19
+#      define MIXT_STEREOSLIDER16      20
+
+  /* Possible value range (minvalue to maxvalue) */
+  /* Note that maxvalue may also be smaller than minvalue */
+  int maxvalue;
+  int minvalue;
+
+  int flags;
+#      define MIXF_READABLE    0x00000001      /* Has readable value */
+#      define MIXF_WRITEABLE   0x00000002      /* Has writeable value */
+#      define MIXF_POLL        0x00000004      /* May change itself */
+#      define MIXF_HZ          0x00000008      /* Herz scale */
+#      define MIXF_STRING      0x00000010      /* Use dynamic extensions for value */
+#      define MIXF_DYNAMIC     0x00000010      /* Supports dynamic extensions */
+#      define MIXF_OKFAIL      0x00000020      /* Interpret value as 1=OK, 0=FAIL */
+#      define MIXF_FLAT        0x00000040      /* Flat vertical space requirements */
+#      define MIXF_LEGACY      0x00000080      /* Legacy mixer control group */
+#      define MIXF_CENTIBEL    0x00000100      /* Centibel (0.1 dB) step size */
+#      define MIXF_DECIBEL     0x00000200      /* Step size of 1 dB */
+#      define MIXF_MAINVOL     0x00000400      /* Main volume control */
+#      define MIXF_PCMVOL      0x00000800      /* PCM output volume control */
+#      define MIXF_RECVOL      0x00001000      /* PCM recording volume control */
+  char id[16];                 /* Mnemonic ID (mainly for internal use) */
+  int parent;                  /* Entry# of parent (group) node (-1 if root) */
+
+  int dummy;                   /* Internal use */
+
+  int timestamp;
+
+  char data[64];               /* Misc data (entry type dependent) */
+  unsigned char enum_present[32];      /* Mask of allowed enum values */
+  int control_no;              /* SOUND_MIXER_VOLUME..SOUND_MIXER_MIDI */
+  /* (-1 means not indicated) */
+
+/*
+ * The desc field is reserved for internal purposes of OSS. It should not be 
+ * used by applications.
+ */
+  unsigned int desc;
+#define MIXEXT_SCOPE_MASK                      0x0000003f
+#define MIXEXT_SCOPE_OTHER                     0x00000000
+#define MIXEXT_SCOPE_INPUT                     0x00000001
+#define MIXEXT_SCOPE_OUTPUT                    0x00000002
+#define MIXEXT_SCOPE_MONITOR                   0x00000003
+#define MIXEXT_SCOPE_RECSWITCH                 0x00000004
+
+  char extname[32];
+  int update_counter;
+  int filler[7];
+} oss_mixext;
+
+typedef struct oss_mixext_root
+{
+  char id[16];
+  char name[48];
+} oss_mixext_root;
+
+typedef struct oss_mixer_value
+{
+  int dev;
+  int ctrl;
+  int value;
+  int flags;                   /* Reserved for future use. Initialize to 0 */
+  int timestamp;               /* Must be set to oss_mixext.timestamp */
+  int filler[8];               /* Reserved for future use. Initialize to 0 */
+} oss_mixer_value;
+
+#define OSS_ENUM_MAXVALUE      255
+typedef struct oss_mixer_enuminfo
+{
+  int dev;
+  int ctrl;
+  int nvalues;
+  int version;                 /* Read the manual */
+  short strindex[OSS_ENUM_MAXVALUE];
+  char strings[3000];
+} oss_mixer_enuminfo;
+
+#define OPEN_READ      PCM_ENABLE_INPUT
+#define OPEN_WRITE     PCM_ENABLE_OUTPUT
+#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
+
+typedef struct oss_audioinfo
+{
+  int dev;                     /* Audio device number */
+  char name[64];
+  int busy;                    /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
+  int pid;
+  int caps;                    /* PCM_CAP_INPUT, PCM_CAP_OUTPUT */
+  int iformats, oformats;
+  int magic;                   /* Reserved for internal use */
+  char cmd[64];                        /* Command using the device (if known) */
+  int card_number;
+  int port_number;
+  int mixer_dev;
+  int legacy_device;           /* Obsolete field. Replaced by devnode */
+  int enabled;                 /* 1=enabled, 0=device not ready at this moment */
+  int flags;                   /* For internal use only - no practical meaning */
+  int min_rate, max_rate;      /* Sample rate limits */
+  int min_channels, max_channels;      /* Number of channels supported */
+  int binding;                 /* DSP_BIND_FRONT, etc. 0 means undefined */
+  int rate_source;
+  char handle[32];
+#define OSS_MAX_SAMPLE_RATES   20      /* Cannot be changed  */
+  unsigned int nrates, rates[OSS_MAX_SAMPLE_RATES];    /* Please read the manual before using these */
+  oss_longname_t song_name;    /* Song name (if given) */
+  oss_label_t label;           /* Device label (if given) */
+  int latency;                 /* In usecs, -1=unknown */
+  oss_devnode_t devnode;       /* Device special file name (absolute path) */
+  int next_play_engine;                /* Read the documentation for more info */
+  int next_rec_engine;         /* Read the documentation for more info */
+  int filler[184];
+} oss_audioinfo;
+
+typedef struct oss_mixerinfo
+{
+  int dev;
+  char id[16];
+  char name[32];
+  int modify_counter;
+  int card_number;
+  int port_number;
+  char handle[32];
+  int magic;                   /* Reserved */
+  int enabled;                 /* Reserved */
+  int caps;
+#define MIXER_CAP_VIRTUAL      0x00000001
+#define MIXER_CAP_LAYOUT_B     0x00000002      /* For internal use only */
+#define MIXER_CAP_NARROW       0x00000004      /* Conserve horiz space */
+  int flags;                   /* Reserved */
+  int nrext;
+  /*
+   * The priority field can be used to select the default (motherboard)
+   * mixer device. The mixer with the highest priority is the
+   * most preferred one. -2 or less means that this device cannot be used
+   * as the default mixer.
+   */
+  int priority;
+  oss_devnode_t devnode;       /* Device special file name (absolute path) */
+  int legacy_device;
+  int filler[245];             /* Reserved */
+} oss_mixerinfo;
+
+typedef struct oss_midi_info
+{
+  int dev;                     /* Midi device number */
+  char name[64];
+  int busy;                    /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
+  int pid;
+  char cmd[64];                        /* Command using the device (if known) */
+  int caps;
+#define MIDI_CAP_MPU401                0x00000001      /**** OBSOLETE ****/
+#define MIDI_CAP_INPUT         0x00000002
+#define MIDI_CAP_OUTPUT                0x00000004
+#define MIDI_CAP_INOUT         (MIDI_CAP_INPUT|MIDI_CAP_OUTPUT)
+#define MIDI_CAP_VIRTUAL       0x00000008      /* Pseudo device */
+#define MIDI_CAP_MTCINPUT      0x00000010      /* Supports SNDCTL_MIDI_MTCINPUT */
+#define MIDI_CAP_CLIENT                0x00000020      /* Virtual client side device */
+#define MIDI_CAP_SERVER                0x00000040      /* Virtual server side device */
+#define MIDI_CAP_INTERNAL      0x00000080      /* Internal (synth) device */
+#define MIDI_CAP_EXTERNAL      0x00000100      /* external (MIDI port) device */
+#define MIDI_CAP_PTOP          0x00000200      /* Point to point link to one device */
+#define MIDI_CAP_MTC           0x00000400      /* MTC/SMPTE (control) device */
+  int magic;                   /* Reserved for internal use */
+  int card_number;
+  int port_number;
+  int enabled;                 /* 1=enabled, 0=device not ready at this moment */
+  int flags;                   /* For internal use only - no practical meaning */
+  char handle[32];
+  oss_longname_t song_name;    /* Song name (if known) */
+  oss_label_t label;           /* Device label (if given) */
+  int latency;                 /* In usecs, -1=unknown */
+  oss_devnode_t devnode;       /* Device special file name (absolute path) */
+  int legacy_device;           /* Legacy device mapping */
+  int filler[235];
+} oss_midi_info;
+
+typedef struct oss_card_info
+{
+  int card;
+  char shortname[16];
+  char longname[128];
+  int flags;
+  int filler[256];
+} oss_card_info;
+
+#define SNDCTL_SYSINFO         __SIOR ('X', 1, oss_sysinfo)
+#define OSS_SYSINFO            SNDCTL_SYSINFO  /* Old name */
+
+#define SNDCTL_MIX_NRMIX       __SIOR ('X', 2, int)
+#define SNDCTL_MIX_NREXT       __SIOWR('X', 3, int)
+#define SNDCTL_MIX_EXTINFO     __SIOWR('X', 4, oss_mixext)
+#define SNDCTL_MIX_READ                __SIOWR('X', 5, oss_mixer_value)
+#define SNDCTL_MIX_WRITE       __SIOWR('X', 6, oss_mixer_value)
+
+#define SNDCTL_AUDIOINFO       __SIOWR('X', 7, oss_audioinfo)
+#define SNDCTL_MIX_ENUMINFO    __SIOWR('X', 8, oss_mixer_enuminfo)
+#define SNDCTL_MIDIINFO                __SIOWR('X', 9, oss_midi_info)
+#define SNDCTL_MIXERINFO       __SIOWR('X',10, oss_mixerinfo)
+#define SNDCTL_CARDINFO                __SIOWR('X',11, oss_card_info)
+#define SNDCTL_ENGINEINFO      __SIOWR('X',12, oss_audioinfo)
+#define SNDCTL_AUDIOINFO_EX    __SIOWR('X',13, oss_audioinfo)
+
+/* ioctl codes 'X', 200-255 are reserved for internal use */
+
+/*
+ * Few more "globally" available ioctl calls.
+ */
+#define SNDCTL_SETSONG         __SIOW ('Y', 2, oss_longname_t)
+#define SNDCTL_GETSONG         __SIOR ('Y', 2, oss_longname_t)
+#define SNDCTL_SETNAME         __SIOW ('Y', 3, oss_longname_t)
+#define SNDCTL_SETLABEL                __SIOW ('Y', 4, oss_label_t)
+#define SNDCTL_GETLABEL                __SIOR ('Y', 4, oss_label_t)
+/*
+ * The "new" mixer API definitions end here.
+ ***************************************
+ */
+
+/*
+ *********************************************************
+ * Few routines that are included in -lOSSlib
+ *
+ * At this moment this interface is not used. OSSlib contains just
+ * stubs that call the related system calls directly.
+ */
+#ifdef OSSLIB
+extern int osslib_open (const char *path, int flags, int dummy);
+extern void osslib_close (int fd);
+extern int osslib_write (int fd, const void *buf, int count);
+extern int osslib_read (int fd, void *buf, int count);
+extern int osslib_ioctl (int fd, unsigned int request, void *arg);
+#else
+#  define osslib_open  open
+#  define osslib_close close
+#  define osslib_write write
+#  define osslib_read  read
+#  define osslib_ioctl ioctl
+#endif
+
+#if 1
+#define SNDCTL_DSP_NONBLOCK            __SIO  ('P',14) /* Obsolete. Not supported any more */
+#endif
+
+#if 1
+/*
+ * Some obsolete macros that are not part of Open Sound System API.
+ */
+#define SOUND_PCM_READ_RATE             SOUND_PCM_READ_RATE_is_obsolete
+#define SOUND_PCM_READ_BITS             SOUND_PCM_READ_BITS_is_obsolete
+#define SOUND_PCM_READ_CHANNELS         SOUND_PCM_READ_CHANNELS_is_obsolete
+#define SOUND_PCM_WRITE_RATE            SOUND_PCM_WRITE_RATE_is_obsolet_use_SNDCTL_DSP_SPEED_instead
+#define SOUND_PCM_WRITE_CHANNELS        SOUND_PCM_WRITE_CHANNELS_is_obsolete_use_SNDCTL_DSP_CHANNELS_instead
+#define SOUND_PCM_WRITE_BITS            SOUND_PCM_WRITE_BITS_is_obsolete_use_SNDCTL_DSP_SETFMT_instead
+#define SOUND_PCM_POST                  SOUND_PCM_POST_is_obsolete_use_SNDCTL_DSP_POST_instead
+#define SOUND_PCM_RESET                 SOUND_PCM_RESET_is_obsolete_use_SNDCTL_DSP_HALT_instead
+#define SOUND_PCM_SYNC                  SOUND_PCM_SYNC_is_obsolete_use_SNDCTL_DSP_SYNC_instead
+#define SOUND_PCM_SUBDIVIDE             SOUND_PCM_SUBDIVIDE_is_obsolete_use_SNDCTL_DSP_SUBDIVIDE_instead
+#define SOUND_PCM_SETFRAGMENT           SOUND_PCM_SETFRAGMENT_is_obsolete_use_SNDCTL_DSP_SETFRAGMENT_instead
+#define SOUND_PCM_GETFMTS               SOUND_PCM_GETFMTS_is_obsolete_use_SNDCTL_DSP_GETFMTS_instead
+#define SOUND_PCM_SETFMT                SOUND_PCM_SETFMT_is_obsolete_use_SNDCTL_DSP_SETFMT_instead
+#define SOUND_PCM_GETOSPACE             SOUND_PCM_GETOSPACE_is_obsolete_use_SNDCTL_DSP_GETOSPACE_instead
+#define SOUND_PCM_GETISPACE             SOUND_PCM_GETISPACE_is_obsolete_use_SNDCTL_DSP_GETISPACE_instead
+#define SOUND_PCM_NONBLOCK              SOUND_PCM_NONBLOCK_is_obsolete_use_SNDCTL_DSP_NONBLOCK_instead
+#define SOUND_PCM_GETCAPS               SOUND_PCM_GETCAPS_is_obsolete_use_SNDCTL_DSP_GETCAPS_instead
+#define SOUND_PCM_GETTRIGGER            SOUND_PCM_GETTRIGGER_is_obsolete_use_SNDCTL_DSP_GETTRIGGER_instead
+#define SOUND_PCM_SETTRIGGER            SOUND_PCM_SETTRIGGER_is_obsolete_use_SNDCTL_DSP_SETTRIGGER_instead
+#define SOUND_PCM_SETSYNCRO             SOUND_PCM_SETSYNCRO_is_obsolete_use_SNDCTL_DSP_SETSYNCRO_instead
+#define SOUND_PCM_GETIPTR               SOUND_PCM_GETIPTR_is_obsolete_use_SNDCTL_DSP_GETIPTR_instead
+#define SOUND_PCM_GETOPTR               SOUND_PCM_GETOPTR_is_obsolete_use_SNDCTL_DSP_GETOPTR_instead
+#define SOUND_PCM_MAPINBUF              SOUND_PCM_MAPINBUF_is_obsolete_use_SNDCTL_DSP_MAPINBUF_instead
+#define SOUND_PCM_MAPOUTBUF             SOUND_PCM_MAPOUTBUF_is_obsolete_use_SNDCTL_DSP_MAPOUTBUF_instead
+#endif
+
+#endif
diff --git a/readme b/readme
index 12025320e58628c5420156d3ca3d0f7a987fed9f..e9279edd2531657203278210a6a374ab93063960 100644 (file)
--- a/readme
+++ b/readme
@@ -1,44 +1,40 @@
-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.
+RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound and ASIO) operating systems.
 
-By Gary P. Scavone, 2001-2005.
+By Gary P. Scavone, 2001-2007.
 
 This distribution of RtAudio contains the following:
 
 doc:      RtAudio documentation (see doc/html/index.html)
 tests:    example RtAudio programs
-asio:     header files necessary for ASIO compilation
-tests/Windows: Visual C++ 6.0 test program workspace and projects
+asio:     header and source files necessary for ASIO compilation
+tests/Windows: Visual C++ .net test program workspace and projects
 
 OVERVIEW:
 
-RtAudio is a set of C++ classes which provide a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, and OSS), SGI, Macintosh OS X (CoreAudio), and Windows (DirectSound and ASIO) operating systems. RtAudio significantly simplifies the process of interacting with computer audio hardware. It was designed with the following goals:
+RtAudio is a set of C++ classes that provide a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X, SGI, and Windows (DirectSound and ASIO) operating systems.  RtAudio significantly simplifies the process of interacting with computer audio hardware.  It was designed with the following objectives:
 
-  - object oriented C++ design
+  - object-oriented C++ design
   - simple, common API across all supported platforms
-  - only two header files and one source file for easy inclusion in programming projects
+  - only one source and two header files for easy inclusion in programming projects
   - allow simultaneous multi-api support
-  - blocking functionality
-  - callback functionality
-  - extensive audio device parameter control
-  - audio device capability probing
-  - automatic internal conversion for data format, channel number compensation, de-interleaving, and byte-swapping
-
-RtAudio incorporates the concept of audio streams, which represent audio output (playback) and/or input (recording). Available audio devices and their capabilities can be enumerated and then specified when opening a stream.  Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance.
-
-The RtAudio API provides both blocking (synchronous) and callback (asyncronous) functionality. Callbacks are typically used in conjunction with graphical user interfaces (GUI). Blocking functionality is often necessary for explicit control of multiple input/output stream synchronization or when audio must be synchronized with other system events. 
+  - support dynamic connection of devices
+  - provide extensive audio device parameter control
+  - allow audio device capability probing
+  - automatic internal conversion for data format, channel number compensation, (de)interleaving, and byte-swapping
 
+RtAudio incorporates the concept of audio streams, which represent audio output (playback) and/or input (recording).  Available audio devices and their capabilities can be enumerated and then specified when opening a stream.  Where applicable, multiple API support can be compiled and a particular API specified when creating an RtAudio instance.  See the \ref apinotes section for information specific to each of the supported audio APIs.
 
 FURTHER READING:
 
-For complete documentation on RtAudio, see the doc directory of the distribution or surf to http://music.mcgill.ca/~gary/rtaudio/.
+For complete documentation on RtAudio, see the doc directory of the distribution or surf to http://www.music.mcgill.ca/~gary/rtaudio/.
 
 
 LEGAL AND ETHICAL:
 
-The RtAudio license is similar to the the MIT License, with the added "feature" that modifications be sent to the developer.
+The RtAudio license is similar to the MIT License.
 
     RtAudio: a set of realtime audio i/o C++ classes
-    Copyright (c) 2001-2005 Gary P. Scavone
+    Copyright (c) 2001-2007 Gary P. Scavone
 
     Permission is hereby granted, free of charge, to any person
     obtaining a copy of this software and associated documentation files
@@ -52,8 +48,9 @@ The RtAudio license is similar to the the MIT License, with the added "feature"
     included in all copies or substantial portions of the Software.
 
     Any person wishing to distribute modifications to the Software is
-    requested to send the modifications to the original developer so that
-    they can be incorporated into the canonical version.
+    asked to send the modifications to the original developer so that
+    they can be incorporated into the canonical version.  This is,
+    however, not a binding provision of this license.
 
     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
diff --git a/rtaudio-config.in b/rtaudio-config.in
new file mode 100644 (file)
index 0000000..f950f4e
--- /dev/null
@@ -0,0 +1,16 @@
+#! /bin/sh
+if (test "x$#" != "x1") ; then
+  echo "Usage: $0 [--libs | --cxxflags]"
+  exit;
+fi
+
+LIBRARY="@LIBS@ @frameworks@"
+CFLAGS="@audio_apis@"
+
+if (test "x$1" == "x--libs") ; then
+  echo "$LIBRARY"
+elif (test "x$1" == "x--cflags") ; then
+  echo "$CFLAGS"
+else
+  echo "Unknown option: $1"
+fi
index 54f4d4a0304b87754beab5b4d59264cc918fc33c..be9cc8babace61fa0fa01ee545bf47bac2616f3d 100644 (file)
@@ -1,6 +1,6 @@
 ### RtAudio tests Makefile - for various flavors of unix
 
-PROGRAMS = info play_saw record_raw in_out play_raw twostreams call_saw call_inout
+PROGRAMS = probe playsaw playraw record duplex testall
 RM = /bin/rm
 SRC_PATH = ../
 INCLUDE = ../
@@ -22,34 +22,29 @@ LIBRARY += @frameworks@
 
 all : $(PROGRAMS)
 
-info : info.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o info info.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+probe : probe.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o probe probe.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-play_saw : play_saw.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o play_saw play_saw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+playsaw : playsaw.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o playsaw playsaw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-play_raw : play_raw.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o play_raw play_raw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+playraw : playraw.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o playraw playraw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-record_raw : record_raw.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o record_raw record_raw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+record : record.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o record record.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-in_out : in_out.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o in_out in_out.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+duplex : duplex.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o duplex duplex.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-twostreams : twostreams.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o twostreams twostreams.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
+testall : testall.cpp $(OBJECTS)
+       $(CC) $(CFLAGS) $(DEFS) -o testall testall.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
-call_saw : call_saw.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o call_saw call_saw.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
-
-call_inout : call_inout.cpp $(OBJECTS)
-       $(CC) $(CFLAGS) $(DEFS) -o call_inout call_inout.cpp $(OBJECT_PATH)/RtAudio.o $(LIBRARY)
 
 clean : 
        -rm $(OBJECT_PATH)/*.o
        -rm $(PROGRAMS)
-       -rm -f *~
+       -rm -f *.raw *~
 
 strip : 
        strip $(PROGRAMS)
diff --git a/tests/Windows/Debug/.placeholder b/tests/Windows/Debug/.placeholder
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/Windows/Release/.placeholder b/tests/Windows/Release/.placeholder
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/Windows/call_inout.dsp b/tests/Windows/call_inout.dsp
deleted file mode 100644 (file)
index 490dacc..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="call_inout" - 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=call_inout - 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 "call_inout.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 "call_inout.mak" CFG="call_inout - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "call_inout - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "call_inout - 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)" == "call_inout - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "call_inout - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "call_inout - Win32 Release"\r
-# Name "call_inout - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\call_inout.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/call_playtwo.dsp b/tests/Windows/call_playtwo.dsp
deleted file mode 100644 (file)
index 1c9966b..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-# Microsoft Developer Studio Project File - Name="call_playtwo" - 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=call_playtwo - 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 "call_playtwo.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 "call_playtwo.mak" CFG="call_playtwo - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "call_playtwo - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "call_playtwo - 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)" == "call_playtwo - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "call_playtwo - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "call_playtwo - Win32 Release"\r
-# Name "call_playtwo - 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=..\call_playtwo.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\RtAudio.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/call_saw.dsp b/tests/Windows/call_saw.dsp
deleted file mode 100644 (file)
index 0c1fcd9..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="call_saw" - 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=call_saw - 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 "call_saw.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 "call_saw.mak" CFG="call_saw - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "call_saw - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "call_saw - 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)" == "call_saw - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "call_saw - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "call_saw - Win32 Release"\r
-# Name "call_saw - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\call_saw.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/call_twostreams.dsp b/tests/Windows/call_twostreams.dsp
deleted file mode 100644 (file)
index 16da2e5..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-# Microsoft Developer Studio Project File - Name="call_twostreams" - 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=call_twostreams - 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 "call_twostreams.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 "call_twostreams.mak" CFG="call_twostreams - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "call_twostreams - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "call_twostreams - 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)" == "call_twostreams - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "call_twostreams - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "call_twostreams - Win32 Release"\r
-# Name "call_twostreams - 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=..\call_twostreams.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\RtAudio.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/in_out.dsp b/tests/Windows/in_out.dsp
deleted file mode 100644 (file)
index f9cfd7d..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="in_out" - 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=in_out - 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 "in_out.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 "in_out.mak" CFG="in_out - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "in_out - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "in_out - 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)" == "in_out - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "in_out - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "in_out - Win32 Release"\r
-# Name "in_out - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\in_out.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/info.dsp b/tests/Windows/info.dsp
deleted file mode 100644 (file)
index d69a64e..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="info" - 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=info - 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 "info.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 "info.mak" CFG="info - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "info - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "info - 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)" == "info - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "info - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "info - Win32 Release"\r
-# Name "info - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\info.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/play_raw.dsp b/tests/Windows/play_raw.dsp
deleted file mode 100644 (file)
index 51f2f01..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="play_raw" - 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=play_raw - 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 "play_raw.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 "play_raw.mak" CFG="play_raw - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "play_raw - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "play_raw - 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)" == "play_raw - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "play_raw - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "play_raw - Win32 Release"\r
-# Name "play_raw - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\play_raw.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/play_saw.dsp b/tests/Windows/play_saw.dsp
deleted file mode 100644 (file)
index d769d9b..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="play_saw" - 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=play_saw - 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 "play_saw.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 "play_saw.mak" CFG="play_saw - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "play_saw - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "play_saw - 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)" == "play_saw - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "play_saw - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "play_saw - Win32 Release"\r
-# Name "play_saw - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\play_saw.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/record_raw.dsp b/tests/Windows/record_raw.dsp
deleted file mode 100644 (file)
index 2092a61..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="record_raw" - 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=record_raw - 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 "record_raw.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 "record_raw.mak" CFG="record_raw - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "record_raw - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "record_raw - 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)" == "record_raw - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "record_raw - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "record_raw - Win32 Release"\r
-# Name "record_raw - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\record_raw.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/rtaudio.dsw b/tests/Windows/rtaudio.dsw
deleted file mode 100644 (file)
index 26b9d4f..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-Microsoft Developer Studio Workspace File, Format Version 6.00\r
-# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
-\r
-###############################################################################\r
-\r
-Project: "call_inout"=".\call_inout.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "call_saw"=".\call_saw.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "in_out"=".\in_out.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "info"=".\info.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "play_raw"=".\play_raw.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "play_saw"=".\play_saw.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "record_raw"=".\record_raw.dsp" - Package Owner=<4>\r
-\r
-Package=<5>\r
-{{{\r
-}}}\r
-\r
-Package=<4>\r
-{{{\r
-}}}\r
-\r
-###############################################################################\r
-\r
-Project: "twostreams"=".\twostreams.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
diff --git a/tests/Windows/rtaudiotest/Release/.placeholder b/tests/Windows/rtaudiotest/Release/.placeholder
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/Windows/rtaudiotest/StdOpt.cpp b/tests/Windows/rtaudiotest/StdOpt.cpp
deleted file mode 100644 (file)
index 02c8eea..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/************************************************************************/\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
deleted file mode 100644 (file)
index 4de660a..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/************************************************************************/\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
deleted file mode 100644 (file)
index d883384..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-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
deleted file mode 100644 (file)
index 90d8c2c..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/************************************************************************/\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
deleted file mode 100644 (file)
index 519756b..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-# 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
deleted file mode 100644 (file)
index eabbccd..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-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
diff --git a/tests/Windows/twostreams.dsp b/tests/Windows/twostreams.dsp
deleted file mode 100644 (file)
index 303de35..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-# Microsoft Developer Studio Project File - Name="twostreams" - 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=twostreams - 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 "twostreams.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 "twostreams.mak" CFG="twostreams - Win32 Debug"\r
-!MESSAGE \r
-!MESSAGE Possible choices for configuration are:\r
-!MESSAGE \r
-!MESSAGE "twostreams - Win32 Release" (based on "Win32 (x86) Console Application")\r
-!MESSAGE "twostreams - 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)" == "twostreams - 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 ""\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 /O2 /I "../../" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /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 /nologo /subsystem:console /machine:I386\r
-\r
-!ELSEIF  "$(CFG)" == "twostreams - 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 ""\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 "../../" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "__WINDOWS_DS__" /D "__WINDOWS_ASIO__" /D "__RTAUDIO_DEBUG__" /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 dsound.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
-\r
-!ENDIF \r
-\r
-# Begin Target\r
-\r
-# Name "twostreams - Win32 Release"\r
-# Name "twostreams - 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=..\..\asio\asio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.cpp\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\twostreams.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=..\..\asio\asio.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrivers.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiodrvr.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiolist.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\asiosys.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\ginclude.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiodrv.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\asio\iasiothiscallresolver.h\r
-# End Source File\r
-# Begin Source File\r
-\r
-SOURCE=..\..\RtAudio.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/call_inout.cpp b/tests/call_inout.cpp
deleted file mode 100644 (file)
index f51ad6f..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/******************************************/
-/*
-  call_inout.cpp
-  by Gary P. Scavone, 2001
-
-  Records from default input and passes it
-  through to the output.  Takes number of
-  channels and sample rate as input arguments.
-  Uses callback functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-*/
-
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: call_inout N fs device\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int inout(char *buffer, int buffer_size, void *)
-{
-  // Surprise!!  We do nothing to pass the data through.
-  return 0;
-}
-
-int main(int argc, char *argv[])
-{
-  int chans, fs, device = 0;
-  RtAudio *audio = 0;
-  char input;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  int buffer_size = 512;
-  try {
-    audio = new RtAudio( device, chans, device, chans,
-                        FORMAT, fs, &buffer_size, 8 );
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    audio->setStreamCallback(&inout, NULL);
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nRunning ... press <enter> to quit (buffer size = " << buffer_size << ").\n";
-  std::cin.get(input);
-
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-
-  return 0;
-}
diff --git a/tests/call_saw.cpp b/tests/call_saw.cpp
deleted file mode 100644 (file)
index b37841b..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/******************************************/
-/*
-  call_saw.c
-  by Gary P. Scavone, 2001
-
-  Play sawtooth waveforms of distinct frequency.
-  Takes number of channels and sample rate as
-  input arguments.  Use callback functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-#define SCALE  2147483647.0
-
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-#define SCALE  127.0
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-#define SCALE  32767.0
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-#define SCALE  2147483647.0
-*/
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-#define SCALE  1.0
-
-/*
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-#define SCALE  1.0
-*/
-
-#define BASE_RATE 0.005
-#define TIME   1.0
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: call_saw N fs <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int chans;
-
-int saw(char *buffer, int buffer_size, void *data)
-{
-  int i, j;
-  extern int chans;
-  MY_TYPE *my_buffer = (MY_TYPE *) buffer;
-  double *my_data = (double *) data;
-
-  for (i=0; i<buffer_size; i++) {
-    for (j=0; j<chans; j++) {
-      *my_buffer++ = (MY_TYPE) (my_data[j] * SCALE);
-      my_data[j] += BASE_RATE * (j+1+(j*0.1));
-      if (my_data[j] >= 1.0) my_data[j] -= 2.0;
-    }
-  }
-
-  return 0;
-}
-
-int main(int argc, char *argv[])
-{
-  int buffer_size, fs, device = 0;
-  RtAudio *audio = 0;
-  double *data = 0;
-  char input;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  buffer_size = 1024;
-  try {
-    audio = new RtAudio(device, chans, 0, 0,
-                        FORMAT, fs, &buffer_size, 4);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  data = (double *) calloc(chans, sizeof(double));
-
-  try {
-    audio->setStreamCallback(&saw, (void *)data);
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << buffer_size << ").\n";
-  std::cin.get(input);
-
-  // Stop the stream.
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-  if (data) free(data);
-
-  return 0;
-}
diff --git a/tests/duplex.cpp b/tests/duplex.cpp
new file mode 100644 (file)
index 0000000..5e63cd7
--- /dev/null
@@ -0,0 +1,131 @@
+/******************************************/
+/*
+  duplex.cpp
+  by Gary P. Scavone, 2006-2007.
+
+  This program opens a duplex stream and passes
+  input directly through to the output.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+
+/*
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT24
+
+typedef char  MY_TYPE;
+#define FORMAT RTAUDIO_SINT8
+
+typedef signed short  MY_TYPE;
+#define FORMAT RTAUDIO_SINT16
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT32
+
+typedef float  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT32
+*/
+
+typedef double  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT64
+
+void usage( void ) {
+  // Error function in case of incorrect command-line
+  // argument specifications
+  std::cout << "\nuseage: duplex N fs <iDevice> <oDevice> <iChannelOffset> <oChannelOffset>\n";
+  std::cout << "    where N = number of channels,\n";
+  std::cout << "    fs = the sample rate,\n";
+  std::cout << "    iDevice = optional input device to use (default = 0),\n";
+  std::cout << "    oDevice = optional output device to use (default = 0),\n";
+  std::cout << "    iChannelOffset = an optional input channel offset (default = 0),\n";
+  std::cout << "    and oChannelOffset = optional output channel offset (default = 0).\n\n";
+  exit( 0 );
+}
+
+int inout( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+           double streamTime, RtAudioStreamStatus status, void *data )
+{
+  // Since the number of input and output channels is equal, we can do
+  // a simple buffer copy operation here.
+  if ( status ) std::cout << "Stream over/underflow detected." << std::endl;
+
+  unsigned long *bytes = (unsigned long *) data;
+  memcpy( outputBuffer, inputBuffer, *bytes );
+  return 0;
+}
+
+int main(int argc, char *argv[])
+{
+  unsigned int channels, fs, bufferBytes, oDevice = 0, iDevice = 0, iOffset = 0, oOffset = 0;
+
+  // Minimal command-line checking
+  if (argc < 3 || argc > 7 ) usage();
+
+  RtAudio adac;
+  if ( adac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  channels = (unsigned int) atoi(argv[1]);
+  fs = (unsigned int) atoi(argv[2]);
+  if ( argc > 3 )
+    iDevice = (unsigned int) atoi(argv[3]);
+  if ( argc > 4 )
+    oDevice = (unsigned int) atoi(argv[4]);
+  if ( argc > 5 )
+    iOffset = (unsigned int) atoi(argv[5]);
+  if ( argc > 6 )
+    oOffset = (unsigned int) atoi(argv[6]);
+
+  // Let RtAudio print messages to stderr.
+  adac.showWarnings( true );
+
+  // Set the same number of channels for both input and output.
+  unsigned int bufferFrames = 512;
+  RtAudio::StreamParameters iParams, oParams;
+  iParams.deviceId = iDevice;
+  iParams.nChannels = channels;
+  iParams.firstChannel = iOffset;
+  oParams.deviceId = oDevice;
+  oParams.nChannels = channels;
+  oParams.firstChannel = oOffset;
+
+  RtAudio::StreamOptions options;
+  //options.flags |= RTAUDIO_NONINTERLEAVED;
+
+  try {
+    adac.openStream( &oParams, &iParams, FORMAT, fs, &bufferFrames, &inout, (void *)&bufferBytes, &options );
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    exit( 0 );
+  }
+
+  bufferBytes = bufferFrames * channels * sizeof( MY_TYPE );
+
+  // Test RtAudio functionality for reporting latency.
+  std::cout << "\nStream latency = " << adac.getStreamLatency() << " frames" << std::endl;
+
+  try {
+    adac.startStream();
+
+    char input;
+    std::cout << "\nRunning ... press <enter> to quit (buffer frames = " << bufferFrames << ").\n";
+    std::cin.get(input);
+
+    // Stop the stream.
+    adac.stopStream();
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    goto cleanup;
+  }
+
+ cleanup:
+  if ( adac.isStreamOpen() ) adac.closeStream();
+
+  return 0;
+}
diff --git a/tests/in_out.cpp b/tests/in_out.cpp
deleted file mode 100644 (file)
index 18fa8c1..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/******************************************/
-/*
-  in_out.c
-  by Gary P. Scavone, 2001
-
-  Records from default input and passes it
-  through to the output.  Takes number of
-  channels and sample rate as input arguments.
-  Use blocking functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-*/
-
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-
-#define TIME   4.0
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: in_out N fs <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-  int chans, fs, buffer_size, device = 0;
-  long frames, counter = 0;
-  MY_TYPE *buffer;
-  RtAudio *audio = 0;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  buffer_size = 512;
-  try {
-    audio = new RtAudio(device, chans, device, chans,
-                        FORMAT, fs, &buffer_size, 8);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  frames = (long) (fs * TIME);
-
-  try {
-    buffer = (MY_TYPE *) audio->getStreamBuffer();
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nRunning for " << TIME << " seconds ... fragment_size = " << buffer_size << std::endl;
-  while (counter < frames) {
-
-    try {
-      audio->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-    counter += buffer_size;
-  }
-
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-
-  return 0;
-}
diff --git a/tests/info.cpp b/tests/info.cpp
deleted file mode 100644 (file)
index 113efb0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/******************************************/
-/*
-  info.cpp
-  by Gary P. Scavone, 2001
-
-  Prints audio system/device info.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-int main(int argc, char *argv[])
-{
-  RtAudio *audio = 0;
-  RtAudioDeviceInfo info;
-  try {
-    audio = new RtAudio();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  int devices = audio->getDeviceCount();
-  std::cout << "\nFound " << devices << " device(s) ...\n";
-
-  for (int i=1; i<=devices; i++) {
-    try {
-      info = audio->getDeviceInfo(i);
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      break;
-    }
-
-    std::cout << "\nDevice Name = " << info.name << '\n';
-    if (info.probed == false)
-      std::cout << "Probe Status = UNsuccessful\n";
-    else {
-      std::cout << "Probe Status = Successful\n";
-      std::cout << "Output Channels = " << info.outputChannels << '\n';
-      std::cout << "Input Channels = " << info.inputChannels << '\n';
-      std::cout << "Duplex Channels = " << info.duplexChannels << '\n';
-      if (info.isDefault) std::cout << "This is the default device.\n";
-      else std::cout << "This is NOT the default device.\n";
-      if ( info.nativeFormats == 0 )
-        std::cout << "No natively supported data formats(?)!";
-      else {
-        std::cout << "Natively supported data formats:\n";
-        if ( info.nativeFormats & RTAUDIO_SINT8 )
-          std::cout << "  8-bit int\n";
-        if ( info.nativeFormats & RTAUDIO_SINT16 )
-          std::cout << "  16-bit int\n";
-        if ( info.nativeFormats & RTAUDIO_SINT24 )
-          std::cout << "  24-bit int\n";
-        if ( info.nativeFormats & RTAUDIO_SINT32 )
-          std::cout << "  32-bit int\n";
-        if ( info.nativeFormats & RTAUDIO_FLOAT32 )
-          std::cout << "  32-bit float\n";
-        if ( info.nativeFormats & RTAUDIO_FLOAT64 )
-          std::cout << "  64-bit float\n";
-      }
-      if ( info.sampleRates.size() < 1 )
-        std::cout << "No supported sample rates found!";
-      else {
-        std::cout << "Supported sample rates = ";
-        for (unsigned int j=0; j<info.sampleRates.size(); j++)
-          std::cout << info.sampleRates[j] << " ";
-      }
-      std::cout << std::endl;
-    }
-  }
-  std::cout << std::endl;
-
-  delete audio;
-  return 0;
-}
diff --git a/tests/play_raw.cpp b/tests/play_raw.cpp
deleted file mode 100644 (file)
index 6dd477d..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-/******************************************/
-/*
-  play_raw.c
-  by Gary P. Scavone, 2001
-
-  Play a raw file.  It is necessary that the
-  file be of the same format as defined below.
-  Uses blocking functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-#include <stdio.h>
-
-/*
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-#define SCALE  127.0
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-#define SCALE  32767.0
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-#define SCALE  8388607.0
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-#define SCALE  2147483647.0
-*/
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-#define SCALE  1.0;
-
-/*
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-#define SCALE  1.0;
-*/
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: play_raw N fs file <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate, \n";
-  std::cout << "    file = the raw file to play,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-  int chans, fs, buffer_size, count, device = 0;
-  long counter = 0;
-  MY_TYPE *buffer;
-  char *file;
-  FILE *fd;
-  RtAudio *audio = 0;
-
-  // minimal command-line checking
-  if (argc != 4 && argc != 5 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  file = argv[3];
-  if ( argc == 5 )
-    device = (int) atoi(argv[4]);
-
-  fd = fopen(file,"rb");
-  if (!fd) {
-    std::cout << "can't find file!\n";
-    exit(0);
-  }
-
-  // Open the realtime output device
-  buffer_size = 512;
-  try {
-    audio = new RtAudio(device, chans, 0, 0,
-                        FORMAT, fs, &buffer_size, 2);
-  }
-  catch (RtError &error) {
-    fclose(fd);
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    buffer = (MY_TYPE *) audio->getStreamBuffer();
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  while (1) {
-    count = fread(buffer, chans * sizeof(MY_TYPE), buffer_size, fd);
-
-    if (count == buffer_size) {
-      try {
-        audio->tickStream();
-      }
-      catch (RtError &error) {
-        error.printMessage();
-        goto cleanup;
-      }
-    }
-    else
-      break;
-        
-    counter += buffer_size;
-  }
-
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-  fclose(fd);
-
-  return 0;
-}
diff --git a/tests/play_saw.cpp b/tests/play_saw.cpp
deleted file mode 100644 (file)
index b99248f..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/******************************************/
-/*
-  play_saw.c
-  by Gary P. Scavone, 2001
-
-  Play sawtooth waveforms of distinct frequency.
-  Takes number of channels and sample rate as
-  input arguments.  Uses blocking functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-#define SCALE  2147483647.0
-
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-#define SCALE  127.0
-*/
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-#define SCALE  32767.0
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-#define SCALE  2147483647.0
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-#define SCALE  1.0
-
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-#define SCALE  1.0
-*/
-
-#define BASE_RATE 0.005
-#define TIME   4.0
-
-void usage(void) {
-  // Error function in case of incorrect command-line
-  // argument specifications.
-  std::cout << "\nuseage: play_saw N fs <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-  int chans, fs, buffer_size, device = 0;
-  long frames, counter = 0, i, j;
-  MY_TYPE *buffer;
-  RtAudio *audio = 0;
-  double *data = 0;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  buffer_size = 512;
-  try {
-    audio = new RtAudio(device, chans, 0, 0,
-                        FORMAT, fs, &buffer_size, 4);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  frames = (long) (fs * TIME);
-  data = (double *) calloc(chans, sizeof(double));
-
-  try {
-    buffer = (MY_TYPE *) audio->getStreamBuffer();
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nPlaying for " << TIME << " seconds ... buffer size = " << buffer_size << "." << std::endl;
-  while (counter < frames) {
-    for (i=0; i<buffer_size; i++) {
-      for (j=0; j<chans; j++) {
-        buffer[i*chans+j] = (MY_TYPE) (data[j] * SCALE);
-        data[j] += BASE_RATE * (j+1+(j*0.1));
-        if (data[j] >= 1.0) data[j] -= 2.0;
-      }
-    }
-
-    try {
-      audio->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    counter += buffer_size;
-  }
-
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-  if (data) free(data);
-
-  return 0;
-}
diff --git a/tests/playraw.cpp b/tests/playraw.cpp
new file mode 100644 (file)
index 0000000..3b70cdb
--- /dev/null
@@ -0,0 +1,146 @@
+/******************************************/
+/*
+  playraw.cpp
+  by Gary P. Scavone, 2007
+
+  Play a specified raw file.  It is necessary
+  that the file be of the same data format as
+  defined below.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+
+/*
+typedef char  MY_TYPE;
+#define FORMAT RTAUDIO_SINT8
+#define SCALE  127.0
+
+typedef signed short  MY_TYPE;
+#define FORMAT RTAUDIO_SINT16
+#define SCALE  32767.0
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT24
+#define SCALE  8388607.0
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT32
+#define SCALE  2147483647.0
+*/
+
+typedef float  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT32
+#define SCALE  1.0;
+
+/*
+typedef double  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT64
+#define SCALE  1.0;
+*/
+
+// Platform-dependent sleep routines.
+#if defined( __WINDOWS_ASIO__ ) || defined( __WINDOWS_DS__ )
+  #include <windows.h>
+  #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) 
+#else // Unix variants
+  #include <unistd.h>
+  #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
+#endif
+
+void usage( void ) {
+  // Error function in case of incorrect command-line
+  // argument specifications
+  std::cout << "\nuseage: playraw N fs file <device> <channelOffset>\n";
+  std::cout << "    where N = number of channels,\n";
+  std::cout << "    fs = the sample rate, \n";
+  std::cout << "    file = the raw file to play,\n";
+  std::cout << "    device = optional device to use (default = 0),\n";
+  std::cout << "    and channelOffset = an optional channel offset on the device (default = 0).\n\n";
+  exit(0);
+}
+
+struct OutputData {
+  FILE *fd;
+  unsigned int channels;
+};
+
+// Interleaved buffers
+int output( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+            double streamTime, RtAudioStreamStatus status, void *data )
+{
+  OutputData *oData = (OutputData*) data;
+
+  // In general, it's not a good idea to do file input in the audio
+  // callback function but I'm doing it here because I don't know the
+  // length of the file we are reading.
+  unsigned int count = fread( outputBuffer, oData->channels * sizeof( MY_TYPE ), nBufferFrames, oData->fd);
+  if ( count < nBufferFrames ) {
+    unsigned int bytes = (nBufferFrames - count) * oData->channels * sizeof( MY_TYPE );
+    unsigned int startByte = count * oData->channels * sizeof( MY_TYPE );
+    memset( (char *)(outputBuffer)+startByte, 0, bytes );
+    return 1;
+  }
+
+  return 0;
+}
+
+int main( int argc, char *argv[] )
+{
+  unsigned int channels, fs, bufferFrames, device = 0, offset = 0;
+  char *file;
+
+  // minimal command-line checking
+  if ( argc < 4 || argc > 6 ) usage();
+
+  RtAudio dac;
+  if ( dac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  channels = (unsigned int) atoi( argv[1]) ;
+  fs = (unsigned int) atoi( argv[2] );
+  file = argv[3];
+  if ( argc > 4 )
+    device = (unsigned int) atoi( argv[4] );
+  if ( argc > 5 )
+    offset = (unsigned int) atoi(argv[5]);
+
+  OutputData data;
+  data.fd = fopen( file, "rb" );
+  if ( !data.fd ) {
+    std::cout << "Unable to find or open file!\n";
+    exit( 0 );
+  }
+
+  // Set our stream parameters for output only.
+  bufferFrames = 512;
+  RtAudio::StreamParameters oParams;
+  oParams.deviceId = device;
+  oParams.nChannels = channels;
+  oParams.firstChannel = offset;
+
+  data.channels = channels;
+  try {
+    dac.openStream( &oParams, NULL, FORMAT, fs, &bufferFrames, &output, (void *)&data );
+    dac.startStream();
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    goto cleanup;
+  }
+
+  std::cout << "\nPlaying raw file " << file << " (buffer frames = " << bufferFrames << ")." << std::endl;
+  while ( 1 ) {
+    SLEEP( 100 ); // wake every 100 ms to check if we're done
+    if ( dac.isStreamRunning() == false ) break;
+  }
+
+ cleanup:
+  fclose( data.fd );
+  dac.closeStream();
+
+  return 0;
+}
diff --git a/tests/playsaw.cpp b/tests/playsaw.cpp
new file mode 100644 (file)
index 0000000..cbe4203
--- /dev/null
@@ -0,0 +1,177 @@
+/******************************************/
+/*
+  playsaw.cpp
+  by Gary P. Scavone, 2006
+
+  This program will output sawtooth waveforms
+  of different frequencies on each channel.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+
+/*
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT24
+#define SCALE  2147483647.0
+
+typedef char  MY_TYPE;
+#define FORMAT RTAUDIO_SINT8
+#define SCALE  127.0
+
+typedef signed short  MY_TYPE;
+#define FORMAT RTAUDIO_SINT16
+#define SCALE  32767.0
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT32
+#define SCALE  2147483647.0
+*/
+
+typedef float  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT32
+#define SCALE  1.0
+
+/*
+typedef double  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT64
+#define SCALE  1.0
+*/
+
+#define BASE_RATE 0.005
+#define TIME   1.0
+
+void usage( void ) {
+  // Error function in case of incorrect command-line
+  // argument specifications
+  std::cout << "\nuseage: playsaw N fs <device> <channelOffset>\n";
+  std::cout << "    where N = number of channels,\n";
+  std::cout << "    fs = the sample rate,\n";
+  std::cout << "    device = optional device to use (default = 0),\n";
+  std::cout << "    and channelOffset = an optional channel offset on the device (default = 0).\n\n";
+  exit( 0 );
+}
+
+unsigned int channels;
+RtAudio::StreamOptions options;
+
+//#define USE_INTERLEAVED
+#if defined( USE_INTERLEAVED )
+
+// Interleaved buffers
+int saw( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+         double streamTime, RtAudioStreamStatus status, void *data )
+{
+  unsigned int i, j;
+  extern unsigned int channels;
+  MY_TYPE *buffer = (MY_TYPE *) outputBuffer;
+  double *lastValues = (double *) data;
+
+  if ( status )
+    std::cout << "Stream underflow detected!" << std::endl;
+
+  for ( i=0; i<nBufferFrames; i++ ) {
+    for ( j=0; j<channels; j++ ) {
+      *buffer++ = (MY_TYPE) (lastValues[j] * SCALE);
+      lastValues[j] += BASE_RATE * (j+1+(j*0.1));
+      if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
+    }
+  }
+
+  return 0;
+}
+
+#else // Use non-interleaved buffers
+
+int saw( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+         double streamTime, RtAudioStreamStatus status, void *data )
+{
+  unsigned int i, j;
+  extern unsigned int channels;
+  MY_TYPE *buffer = (MY_TYPE *) outputBuffer;
+  double *lastValues = (double *) data;
+
+  if ( status )
+    std::cout << "Stream underflow detected!" << std::endl;
+
+  double increment;
+  for ( j=0; j<channels; j++ ) {
+    increment = BASE_RATE * (j+1+(j*0.1));
+    for ( i=0; i<nBufferFrames; i++ ) {
+      *buffer++ = (MY_TYPE) (lastValues[j] * SCALE);
+      lastValues[j] += increment;
+      if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
+    }
+  }
+
+  return 0;
+}
+#endif
+
+int main( int argc, char *argv[] )
+{
+  unsigned int bufferFrames, fs, device = 0, offset = 0;
+
+  // minimal command-line checking
+  if (argc < 3 || argc > 5 ) usage();
+
+  RtAudio dac;
+  if ( dac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  channels = (unsigned int) atoi(argv[1]);
+  fs = (unsigned int) atoi(argv[2]);
+  if ( argc > 3 )
+    device = (unsigned int) atoi(argv[3]);
+  if ( argc > 4 )
+    offset = (unsigned int) atoi(argv[4]);
+
+  double *data;
+  data = (double *) calloc( channels, sizeof( double ) );
+
+  // Let RtAudio print messages to stderr.
+  dac.showWarnings( true );
+
+  // Set our stream parameters for output only.
+  bufferFrames = 256;
+  RtAudio::StreamParameters oParams;
+  oParams.deviceId = device;
+  oParams.nChannels = channels;
+  oParams.firstChannel = offset;
+
+
+  options.flags |= RTAUDIO_HOG_DEVICE;
+#if !defined( USE_INTERLEAVED )
+  options.flags |= RTAUDIO_NONINTERLEAVED;
+#endif
+  try {
+    dac.openStream( &oParams, NULL, FORMAT, fs, &bufferFrames, &saw, (void *)data, &options );
+    dac.startStream();
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    goto cleanup;
+  }
+
+  char input;
+  std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << bufferFrames << ").\n";
+  std::cin.get( input );
+
+  try {
+    // Stop the stream
+    std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl;
+    dac.stopStream();
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+  }
+
+ cleanup:
+  if ( dac.isStreamOpen() ) dac.closeStream();
+  free( data );
+
+  return 0;
+}
diff --git a/tests/probe.cpp b/tests/probe.cpp
new file mode 100644 (file)
index 0000000..e756346
--- /dev/null
@@ -0,0 +1,86 @@
+/******************************************/
+/*
+  probe.cpp
+  by Gary P. Scavone, 2001
+
+  Probe audio system and prints device info.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+#include <map>
+
+int main()
+{
+  // Create an api map.
+  std::map<int, std::string> apiMap;
+  apiMap[RtAudio::MACOSX_CORE] = "OS-X Core Audio";
+  apiMap[RtAudio::WINDOWS_ASIO] = "Windows ASIO";
+  apiMap[RtAudio::WINDOWS_DS] = "Windows Direct Sound";
+  apiMap[RtAudio::UNIX_JACK] = "Jack Client";
+  apiMap[RtAudio::LINUX_ALSA] = "Linux ALSA";
+  apiMap[RtAudio::LINUX_OSS] = "Linux OSS";
+  apiMap[RtAudio::RTAUDIO_DUMMY] = "RtAudio Dummy";
+
+  std::vector< RtAudio::Api > apis;
+  RtAudio :: getCompiledApi( apis );
+
+  std::cout << "\nCompiled APIs:\n";
+  for ( unsigned int i=0; i<apis.size(); i++ )
+    std::cout << "  " << apiMap[ apis[i] ] << std::endl;
+
+  RtAudio audio;
+  RtAudio::DeviceInfo info;
+
+  std::cout << "\nCurrent API: " << apiMap[ audio.getCurrentApi() ] << std::endl;
+
+  unsigned int devices = audio.getDeviceCount();
+  std::cout << "\nFound " << devices << " device(s) ...\n";
+
+  for (unsigned int i=0; i<devices; i++) {
+    info = audio.getDeviceInfo(i);
+
+    std::cout << "\nDevice Name = " << info.name << '\n';
+    if ( info.probed == false )
+      std::cout << "Probe Status = UNsuccessful\n";
+    else {
+      std::cout << "Probe Status = Successful\n";
+      std::cout << "Output Channels = " << info.outputChannels << '\n';
+      std::cout << "Input Channels = " << info.inputChannels << '\n';
+      std::cout << "Duplex Channels = " << info.duplexChannels << '\n';
+      if ( info.isDefaultOutput ) std::cout << "This is the default output device.\n";
+      else std::cout << "This is NOT the default output device.\n";
+      if ( info.isDefaultInput ) std::cout << "This is the default input device.\n";
+      else std::cout << "This is NOT the default input device.\n";
+      if ( info.nativeFormats == 0 )
+        std::cout << "No natively supported data formats(?)!";
+      else {
+        std::cout << "Natively supported data formats:\n";
+        if ( info.nativeFormats & RTAUDIO_SINT8 )
+          std::cout << "  8-bit int\n";
+        if ( info.nativeFormats & RTAUDIO_SINT16 )
+          std::cout << "  16-bit int\n";
+        if ( info.nativeFormats & RTAUDIO_SINT24 )
+          std::cout << "  24-bit int\n";
+        if ( info.nativeFormats & RTAUDIO_SINT32 )
+          std::cout << "  32-bit int\n";
+        if ( info.nativeFormats & RTAUDIO_FLOAT32 )
+          std::cout << "  32-bit float\n";
+        if ( info.nativeFormats & RTAUDIO_FLOAT64 )
+          std::cout << "  64-bit float\n";
+      }
+      if ( info.sampleRates.size() < 1 )
+        std::cout << "No supported sample rates found!";
+      else {
+        std::cout << "Supported sample rates = ";
+        for (unsigned int j=0; j<info.sampleRates.size(); j++)
+          std::cout << info.sampleRates[j] << " ";
+      }
+      std::cout << std::endl;
+    }
+  }
+  std::cout << std::endl;
+
+  return 0;
+}
diff --git a/tests/record.cpp b/tests/record.cpp
new file mode 100644 (file)
index 0000000..f43db19
--- /dev/null
@@ -0,0 +1,169 @@
+/******************************************/
+/*
+  record.cpp
+  by Gary P. Scavone, 2007
+
+  This program records audio from a device and writes it to a
+  header-less binary file.  Use the 'playraw', with the same
+  parameters and format settings, to playback the audio.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+
+/*
+typedef char  MY_TYPE;
+#define FORMAT RTAUDIO_SINT8
+
+typedef signed short  MY_TYPE;
+#define FORMAT RTAUDIO_SINT16
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT24
+
+typedef signed long  MY_TYPE;
+#define FORMAT RTAUDIO_SINT32
+*/
+
+typedef float  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT32
+
+/*
+typedef double  MY_TYPE;
+#define FORMAT RTAUDIO_FLOAT64
+*/
+
+// Platform-dependent sleep routines.
+#if defined( __WINDOWS_ASIO__ ) || defined( __WINDOWS_DS__ )
+  #include <windows.h>
+  #define SLEEP( milliseconds ) Sleep( (DWORD) milliseconds ) 
+#else // Unix variants
+  #include <unistd.h>
+  #define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
+#endif
+
+void usage( void ) {
+  // Error function in case of incorrect command-line
+  // argument specifications
+  std::cout << "\nuseage: record N fs <duration> <device> <channelOffset>\n";
+  std::cout << "    where N = number of channels,\n";
+  std::cout << "    fs = the sample rate,\n";
+  std::cout << "    duration = optional time in seconds to record (default = 2.0),\n";
+  std::cout << "    device = optional device to use (default = 0),\n";
+  std::cout << "    and channelOffset = an optional channel offset on the device (default = 0).\n\n";
+  exit( 0 );
+}
+
+struct InputData {
+  MY_TYPE* buffer;
+  unsigned long bufferBytes;
+  unsigned long totalFrames;
+  unsigned long frameCounter;
+  unsigned int channels;
+};
+
+// Interleaved buffers
+int input( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+           double streamTime, RtAudioStreamStatus status, void *data )
+{
+  InputData *iData = (InputData *) data;
+
+  // Simply copy the data to our allocated buffer.
+  unsigned int frames = nBufferFrames;
+  if ( iData->frameCounter + nBufferFrames > iData->totalFrames ) {
+    frames = iData->totalFrames - iData->frameCounter;
+    iData->bufferBytes = frames * iData->channels * sizeof( MY_TYPE );
+  }
+
+  unsigned long offset = iData->frameCounter * iData->channels;
+  memcpy( iData->buffer+offset, inputBuffer, iData->bufferBytes );
+  iData->frameCounter += frames;
+
+  if ( iData->frameCounter >= iData->totalFrames ) return 2;
+  return 0;
+}
+
+int main( int argc, char *argv[] )
+{
+  unsigned int channels, fs, bufferFrames, device = 0, offset = 0;
+  double time = 2.0;
+  FILE *fd;
+
+  // minimal command-line checking
+  if ( argc < 3 || argc > 6 ) usage();
+
+  RtAudio adc;
+  if ( adc.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  channels = (unsigned int) atoi( argv[1] );
+  fs = (unsigned int) atoi( argv[2] );
+  if ( argc > 3 )
+    time = (double) atof( argv[3] );
+  if ( argc > 4 )
+    device = (unsigned int) atoi( argv[4] );
+  if ( argc > 5 )
+    offset = (unsigned int) atoi( argv[5] );
+
+  // Let RtAudio print messages to stderr.
+  adc.showWarnings( true );
+
+  // Set our stream parameters for input only.
+  bufferFrames = 512;
+  RtAudio::StreamParameters iParams;
+  iParams.deviceId = device;
+  iParams.nChannels = channels;
+  iParams.firstChannel = offset;
+
+  InputData data;
+  data.buffer = 0;
+  try {
+    adc.openStream( NULL, &iParams, FORMAT, fs, &bufferFrames, &input, (void *)&data );
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    goto cleanup;
+  }
+
+  data.bufferBytes = bufferFrames * channels * sizeof( MY_TYPE );
+  data.totalFrames = (unsigned long) (fs * time);
+  data.frameCounter = 0;
+  data.channels = channels;
+  unsigned long totalBytes;
+  totalBytes = data.totalFrames * channels * sizeof( MY_TYPE );
+
+  // Allocate the entire data buffer before starting stream.
+  data.buffer = (MY_TYPE*) malloc( totalBytes );
+  if ( data.buffer == 0 ) {
+    std::cout << "Memory allocation error ... quitting!\n";
+    goto cleanup;
+  }
+
+  try {
+    adc.startStream();
+  }
+  catch ( RtError& e ) {
+    std::cout << '\n' << e.getMessage() << '\n' << std::endl;
+    goto cleanup;
+  }
+
+  std::cout << "\nRecording for " << time << " seconds ... writing file 'record.raw' (buffer frames = " << bufferFrames << ")." << std::endl;
+  while ( 1 ) {
+    SLEEP( 100 ); // wake every 100 ms to check if we're done
+    if ( adc.isStreamRunning() == false ) break;
+  }
+
+  // Now write the entire data to the file.
+  fd = fopen( "record.raw", "wb" );
+  fwrite( data.buffer, sizeof( MY_TYPE ), data.totalFrames * channels, fd );
+  fclose(fd);
+
+ cleanup:
+  if ( adc.isStreamOpen() ) adc.closeStream();
+  if ( data.buffer ) free( data.buffer );
+
+  return 0;
+}
diff --git a/tests/record_raw.cpp b/tests/record_raw.cpp
deleted file mode 100644 (file)
index 3a207f9..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/******************************************/
-/*
-  record_raw.c
-  by Gary P. Scavone, 2001
-
-  Records from default input.  Takes
-  number of channels and sample rate
-  as input arguments. Uses blocking calls.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-#include <stdio.h>
-
-/*
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-*/
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-
-/*
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-*/
-
-#define TIME   2.0
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: record_raw N fs <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-  int chans, fs, buffer_size, device = 0;
-  long frames, counter = 0;
-  MY_TYPE *buffer;
-  FILE *fd;
-  RtAudio *audio = 0;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  buffer_size = 512;
-  try {
-    audio = new RtAudio(0, 0, device, chans,
-                        FORMAT, fs, &buffer_size, 8);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  fd = fopen("test.raw","wb");
-  frames = (long) (fs * TIME);
-
-  try {
-    buffer = (MY_TYPE *) audio->getStreamBuffer();
-    audio->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nRecording for " << TIME << " seconds ... writing file test.raw (buffer size = " << buffer_size << ")." << std::endl;
-  while (counter < frames) {
-
-    try {
-      audio->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    fwrite(buffer, sizeof(MY_TYPE), chans * buffer_size, fd);
-    counter += buffer_size;
-  }
-
-  try {
-    audio->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  audio->closeStream();
-  delete audio;
-  fclose(fd);
-
-  return 0;
-}
diff --git a/tests/testall.cpp b/tests/testall.cpp
new file mode 100644 (file)
index 0000000..af14809
--- /dev/null
@@ -0,0 +1,221 @@
+/******************************************/
+/*
+  testall.cpp
+  by Gary P. Scavone, 2007
+
+  This program will make a variety of calls
+  to extensively test RtAudio functionality.
+*/
+/******************************************/
+
+#include "RtAudio.h"
+#include <iostream>
+
+#define BASE_RATE 0.005
+#define TIME   1.0
+
+void usage( void ) {
+  // Error function in case of incorrect command-line
+  // argument specifications
+  std::cout << "\nuseage: testall N fs <device> <channelOffset>\n";
+  std::cout << "    where N = number of channels,\n";
+  std::cout << "    fs = the sample rate,\n";
+  std::cout << "    device = optional device to use (default = 0),\n";
+  std::cout << "    and channelOffset = an optional channel offset on the device (default = 0).\n\n";
+  exit( 0 );
+}
+
+unsigned int channels;
+
+// Interleaved buffers
+int sawi( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+          double streamTime, RtAudioStreamStatus status, void *data )
+{
+  unsigned int i, j;
+  extern unsigned int channels;
+  double *buffer = (double *) outputBuffer;
+  double *lastValues = (double *) data;
+
+  if ( status )
+    std::cout << "Stream underflow detected!" << std::endl;
+
+  for ( i=0; i<nBufferFrames; i++ ) {
+    for ( j=0; j<channels; j++ ) {
+      *buffer++ = (double) lastValues[j];
+      lastValues[j] += BASE_RATE * (j+1+(j*0.1));
+      if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
+    }
+  }
+
+  return 0;
+}
+
+// Non-interleaved buffers
+int sawni( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+           double streamTime, RtAudioStreamStatus status, void *data )
+{
+  unsigned int i, j;
+  extern unsigned int channels;
+  double *buffer = (double *) outputBuffer;
+  double *lastValues = (double *) data;
+
+  if ( status )
+    std::cout << "Stream underflow detected!" << std::endl;
+
+  float increment;
+  for ( j=0; j<channels; j++ ) {
+    increment = BASE_RATE * (j+1+(j*0.1));
+    for ( i=0; i<nBufferFrames; i++ ) {
+      *buffer++ = (double) lastValues[j];
+      lastValues[j] += increment;
+      if ( lastValues[j] >= 1.0 ) lastValues[j] -= 2.0;
+    }
+  }
+
+  return 0;
+}
+
+int inout( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames,
+           double streamTime, RtAudioStreamStatus status, void *data )
+{
+  // Since the number of input and output channels is equal, we can do
+  // a simple buffer copy operation here.
+  if ( status ) std::cout << "Stream over/underflow detected." << std::endl;
+
+  unsigned long *bytes = (unsigned long *) data;
+  memcpy( outputBuffer, inputBuffer, *bytes );
+  return 0;
+}
+
+int main( int argc, char *argv[] )
+{
+  unsigned int bufferFrames, fs, device = 0, offset = 0;
+  char input;
+
+  // minimal command-line checking
+  if (argc < 3 || argc > 5 ) usage();
+
+  RtAudio dac;
+  if ( dac.getDeviceCount() < 1 ) {
+    std::cout << "\nNo audio devices found!\n";
+    exit( 0 );
+  }
+
+  channels = (unsigned int) atoi(argv[1]);
+  fs = (unsigned int) atoi(argv[2]);
+  if ( argc > 3 )
+    device = (unsigned int) atoi(argv[3]);
+  if ( argc > 4 )
+    offset = (unsigned int) atoi(argv[4]);
+
+  double *data;
+  data = (double *) calloc( channels, sizeof( double ) );
+
+  // Let RtAudio print messages to stderr.
+  dac.showWarnings( true );
+
+  // Set our stream parameters for output only.
+  bufferFrames = 256;
+  RtAudio::StreamParameters oParams, iParams;
+  oParams.deviceId = device;
+  oParams.nChannels = channels;
+  oParams.firstChannel = offset;
+
+  RtAudio::StreamOptions options;
+  options.flags = RTAUDIO_HOG_DEVICE;
+  try {
+    dac.openStream( &oParams, NULL, RTAUDIO_FLOAT64, fs, &bufferFrames, &sawi, (void *)data, &options );
+    std::cout << "\nStream latency = " << dac.getStreamLatency() << std::endl;
+
+    // Start the stream
+    dac.startStream();
+    std::cout << "\nPlaying ... press <enter> to stop.\n";
+    std::cin.get( input );
+
+    // Stop the stream
+    dac.stopStream();
+
+    // Restart again
+    std::cout << "Press <enter> to restart.\n";
+    std::cin.get( input );
+    dac.startStream();
+
+    // Test abort function
+    std::cout << "Playing again ... press <enter> to abort.\n";
+    std::cin.get( input );
+    dac.abortStream();
+
+    // Restart another time
+    std::cout << "Press <enter> to restart again.\n";
+    std::cin.get( input );
+    dac.startStream();
+
+    std::cout << "Playing again ... press <enter> to close the stream.\n";
+    std::cin.get( input );
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    goto cleanup;
+  }
+
+  if ( dac.isStreamOpen() ) dac.closeStream();
+
+  // Test non-interleaved functionality
+  options.flags = RTAUDIO_NONINTERLEAVED;
+  try {
+    dac.openStream( &oParams, NULL, RTAUDIO_FLOAT64, fs, &bufferFrames, &sawni, (void *)data, &options );
+
+    std::cout << "Press <enter> to start non-interleaved playback.\n";
+    std::cin.get( input );
+
+    // Start the stream
+    dac.startStream();
+    std::cout << "\nPlaying ... press <enter> to stop.\n";
+    std::cin.get( input );
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+    goto cleanup;
+  }
+
+  if ( dac.isStreamOpen() ) dac.closeStream();
+
+  // Now open a duplex stream.
+  unsigned int bufferBytes;
+  iParams.deviceId = device;
+  iParams.nChannels = channels;
+  iParams.firstChannel = offset;
+  options.flags = RTAUDIO_NONINTERLEAVED;
+  try {
+    dac.openStream( &oParams, &iParams, RTAUDIO_SINT32, fs, &bufferFrames, &inout, (void *)&bufferBytes, &options );
+
+    bufferBytes = bufferFrames * channels * 4;
+
+    std::cout << "Press <enter> to start duplex operation.\n";
+    std::cin.get( input );
+
+    // Start the stream
+    dac.startStream();
+    std::cout << "\nRunning ... press <enter> to stop.\n";
+    std::cin.get( input );
+
+    // Stop the stream
+    dac.stopStream();
+    std::cout << "\nStopped ... press <enter> to restart.\n";
+    std::cin.get( input );
+
+    // Restart the stream
+    dac.startStream();
+    std::cout << "\nRunning ... press <enter> to stop.\n";
+    std::cin.get( input );
+  }
+  catch ( RtError& e ) {
+    e.printMessage();
+  }
+
+ cleanup:
+  if ( dac.isStreamOpen() ) dac.closeStream();
+  free( data );
+
+  return 0;
+}
diff --git a/tests/twostreams.cpp b/tests/twostreams.cpp
deleted file mode 100644 (file)
index 17badd0..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/******************************************/
-/*
-  twostreams.cpp
-  by Gary P. Scavone, 2001
-
-  Test executable for audio playback, recording,
-  duplex operation, stopping, starting, and
-  aborting operations.  Takes number of channels
-  and sample rate as input arguments.  Runs input
-  and output through two separate instances of RtAudio.
-  Uses blocking functionality.
-*/
-/******************************************/
-
-#include "RtAudio.h"
-#include <iostream>
-
-/*
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT24
-#define SCALE  2147483647.0
-
-typedef char  MY_TYPE;
-#define FORMAT RTAUDIO_SINT8
-#define SCALE  127.0
-
-typedef signed short  MY_TYPE;
-#define FORMAT RTAUDIO_SINT16
-#define SCALE  32767.0
-
-typedef signed long  MY_TYPE;
-#define FORMAT RTAUDIO_SINT32
-#define SCALE  2147483647.0
-*/
-
-typedef float  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT32
-#define SCALE  1.0
-
-/*
-typedef double  MY_TYPE;
-#define FORMAT RTAUDIO_FLOAT64
-#define SCALE  1.0
-*/
-
-#define BASE_RATE 0.005
-#define TIME 2.0
-
-void usage(void) {
-  /* Error function in case of incorrect command-line
-     argument specifications
-  */
-  std::cout << "\nuseage: twostreams N fs <device>\n";
-  std::cout << "    where N = number of channels,\n";
-  std::cout << "    fs = the sample rate,\n";
-  std::cout << "    and device = the device to use (default = 0).\n\n";
-  exit(0);
-}
-
-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 = 0, *stream2 = 0;
-  FILE *fd;
-  double *data = 0;
-
-  // minimal command-line checking
-  if (argc != 3 && argc != 4 ) usage();
-
-  chans = (int) atoi(argv[1]);
-  fs = (int) atoi(argv[2]);
-  if ( argc == 4 )
-    device = (int) atoi(argv[3]);
-
-  // Open the realtime output device
-  buffer_size = 512;
-  try {
-    stream1 = new RtAudio(device, chans, 0, 0,
-                          FORMAT, fs, &buffer_size, 8);
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    stream2 = new RtAudio(0, 0, device, chans,
-                          FORMAT, fs, &buffer_size, 8);
-  }
-  catch (RtError &error) {
-    delete stream1;
-    error.printMessage();
-    exit(EXIT_FAILURE);
-  }
-
-  try {
-    buffer1 = (MY_TYPE *) stream1->getStreamBuffer();
-    buffer2 = (MY_TYPE *) stream2->getStreamBuffer();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  frames = (long) (fs * TIME);
-  data = (double *) calloc(chans, sizeof(double));
-
-  try {
-    stream1->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  std::cout << "\nStarting sawtooth playback stream for " << TIME << " seconds." << std::endl;
-  while (counter < frames) {
-    for (i=0; i<buffer_size; i++) {
-      for (j=0; j<chans; j++) {
-        buffer1[i*chans+j] = (MY_TYPE) (data[j] * SCALE);
-        data[j] += BASE_RATE * (j+1+(j*0.1));
-        if (data[j] >= 1.0) data[j] -= 2.0;
-      }
-    }
-
-    try {
-      stream1->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    counter += buffer_size;
-  }
-
-  std::cout << "\nStopping playback stream." << std::endl;
-  try {
-    stream1->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  fd = fopen("test.raw","wb");
-
-  try {
-    stream2->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  counter = 0;
-  std::cout << "\nStarting recording stream for " << TIME << " seconds." << std::endl;
-  while (counter < frames) {
-
-    try {
-      stream2->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    fwrite(buffer2, sizeof(MY_TYPE), chans * buffer_size, fd);
-    counter += buffer_size;
-  }
-
-  fclose(fd);
-  std::cout << "\nAborting recording." << std::endl;
-
-  try {
-    stream2->abortStream();
-    stream1->startStream();
-    stream2->startStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-    goto cleanup;
-  }
-
-  counter = 0;
-  std::cout << "\nStarting playback and record streams (quasi-duplex) for " << TIME << " seconds." << std::endl;
-  while (counter < frames) {
-
-    try {
-      stream2->tickStream();
-      memcpy(buffer1, buffer2, sizeof(MY_TYPE) * chans * buffer_size);
-      stream1->tickStream();
-    }
-    catch (RtError &error) {
-      error.printMessage();
-      goto cleanup;
-    }
-
-    counter += buffer_size;
-  }
-
-  std::cout << "\nStopping both streams." << std::endl;
-  try {
-    stream1->stopStream();
-    stream2->stopStream();
-  }
-  catch (RtError &error) {
-    error.printMessage();
-  }
-
- cleanup:
-  stream1->closeStream();
-  stream2->closeStream();
-  delete stream1;
-  delete stream2;
-  if (data) free(data);
-
-  return 0;
-}