RtAudio.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 /************************************************************************/
00059 /************************************************************************/
00060 
00061 // RtAudio: Version 2.1.1, 24 October 2002
00062 
00063 #include "RtAudio.hxx"
00064 #include <vector>
00065 #include <cstdio>
00066 #include <iostream>
00067 
00068 // Static variable definitions.
00069 const unsigned int RtAudio :: SAMPLE_RATES[] = {
00070   4000, 5512, 8000, 9600, 11025, 16000, 22050,
00071   32000, 44100, 48000, 88200, 96000, 176400, 192000
00072 };
00073 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT8 = 1;
00074 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT16 = 2;
00075 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT24 = 4;
00076 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_SINT32 = 8;
00077 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT32 = 16;
00078 const RtAudio::RTAUDIO_FORMAT RtAudio :: RTAUDIO_FLOAT64 = 32;
00079 
00080 #if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__)
00081   #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
00082   #define MUTEX_LOCK(A)       EnterCriticalSection(A)
00083   #define MUTEX_UNLOCK(A)     LeaveCriticalSection(A)
00084 #else // pthread API
00085   #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
00086   #define MUTEX_LOCK(A)       pthread_mutex_lock(A)
00087   #define MUTEX_UNLOCK(A)     pthread_mutex_unlock(A)
00088 #endif
00089 
00090 // *************************************************** //
00091 //
00092 // Public common (OS-independent) methods.
00093 //
00094 // *************************************************** //
00095 
00096 RtAudio :: RtAudio()
00097 {
00098   initialize();
00099 
00100   if (nDevices <= 0) {
00101     sprintf(message, "RtAudio: no audio devices found!");
00102     error(RtError::NO_DEVICES_FOUND);
00103  }
00104 }
00105 
00106 RtAudio :: RtAudio(int *streamId,
00107                    int outputDevice, int outputChannels,
00108                    int inputDevice, int inputChannels,
00109                    RTAUDIO_FORMAT format, int sampleRate,
00110                    int *bufferSize, int numberOfBuffers)
00111 {
00112   initialize();
00113 
00114   if (nDevices <= 0) {
00115     sprintf(message, "RtAudio: no audio devices found!");
00116     error(RtError::NO_DEVICES_FOUND);
00117   }
00118 
00119   try {
00120     *streamId = openStream(outputDevice, outputChannels, inputDevice, inputChannels,
00121                            format, sampleRate, bufferSize, numberOfBuffers);
00122   }
00123   catch (RtError &exception) {
00124     // deallocate the RTAUDIO_DEVICE structures
00125     if (devices) free(devices);
00126     throw exception;
00127   }
00128 }
00129 
00130 RtAudio :: ~RtAudio()
00131 {
00132   // close any existing streams
00133   while ( streams.size() )
00134     closeStream( streams.begin()->first );
00135 
00136   // deallocate the RTAUDIO_DEVICE structures
00137   if (devices) free(devices);
00138 }
00139 
00140 int RtAudio :: openStream(int outputDevice, int outputChannels,
00141                           int inputDevice, int inputChannels,
00142                           RTAUDIO_FORMAT format, int sampleRate,
00143                           int *bufferSize, int numberOfBuffers)
00144 {
00145   static int streamKey = 0; // Unique stream identifier ... OK for multiple instances.
00146 
00147   if (outputChannels < 1 && inputChannels < 1) {
00148     sprintf(message,"RtAudio: one or both 'channel' parameters must be greater than zero.");
00149     error(RtError::INVALID_PARAMETER);
00150   }
00151 
00152   if ( formatBytes(format) == 0 ) {
00153     sprintf(message,"RtAudio: 'format' parameter value is undefined.");
00154     error(RtError::INVALID_PARAMETER);
00155   }
00156 
00157   if ( outputChannels > 0 ) {
00158     if (outputDevice > nDevices || outputDevice < 0) {
00159       sprintf(message,"RtAudio: 'outputDevice' parameter value (%d) is invalid.", outputDevice);
00160       error(RtError::INVALID_PARAMETER);
00161     }
00162   }
00163 
00164   if ( inputChannels > 0 ) {
00165     if (inputDevice > nDevices || inputDevice < 0) {
00166       sprintf(message,"RtAudio: 'inputDevice' parameter value (%d) is invalid.", inputDevice);
00167       error(RtError::INVALID_PARAMETER);
00168     }
00169   }
00170 
00171   // Allocate a new stream structure.
00172   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) calloc(1, sizeof(RTAUDIO_STREAM));
00173   if (stream == NULL) {
00174     sprintf(message, "RtAudio: memory allocation error!");
00175     error(RtError::MEMORY_ERROR);
00176   }
00177   stream->mode = UNINITIALIZED;
00178   MUTEX_INITIALIZE(&stream->mutex);
00179 
00180   bool result = FAILURE;
00181   int device, defaultDevice = 0;
00182   STREAM_MODE mode;
00183   int channels;
00184   if ( outputChannels > 0 ) {
00185 
00186     mode = OUTPUT;
00187     channels = outputChannels;
00188 
00189     if ( outputDevice == 0 ) { // Try default device first.
00190       defaultDevice = getDefaultOutputDevice();
00191       device = defaultDevice;
00192     }
00193     else
00194       device = outputDevice - 1;
00195 
00196     for (int i=-1; i<nDevices; i++) {
00197       if (i >= 0 ) { 
00198         if ( i == defaultDevice ) continue;
00199         device = i;
00200       }
00201       if (devices[device].probed == false) {
00202         // If the device wasn't successfully probed before, try it
00203         // again now.
00204         clearDeviceInfo(&devices[device]);
00205         probeDeviceInfo(&devices[device]);
00206       }
00207       if ( devices[device].probed )
00208         result = probeDeviceOpen(device, stream, mode, channels, sampleRate,
00209                                  format, bufferSize, numberOfBuffers);
00210       if (result == SUCCESS) break;
00211       if ( outputDevice > 0 ) break;
00212     }
00213   }
00214 
00215   if ( inputChannels > 0 && ( result == SUCCESS || outputChannels <= 0 ) ) {
00216 
00217     mode = INPUT;
00218     channels = inputChannels;
00219 
00220     if ( inputDevice == 0 ) { // Try default device first.
00221       defaultDevice = getDefaultInputDevice();
00222       device = defaultDevice;
00223     }
00224     else
00225       device = inputDevice - 1;
00226 
00227     for (int i=-1; i<nDevices; i++) {
00228       if (i >= 0 ) { 
00229         if ( i == defaultDevice ) continue;
00230         device = i;
00231       }
00232       if (devices[device].probed == false) {
00233         // If the device wasn't successfully probed before, try it
00234         // again now.
00235         clearDeviceInfo(&devices[device]);
00236         probeDeviceInfo(&devices[device]);
00237       }
00238       if ( devices[device].probed )
00239         result = probeDeviceOpen(device, stream, mode, channels, sampleRate,
00240                                  format, bufferSize, numberOfBuffers);
00241       if (result == SUCCESS) break;
00242       if ( outputDevice > 0 ) break;
00243     }
00244   }
00245 
00246   streams[++streamKey] = (void *) stream;
00247   if ( result == SUCCESS )
00248     return streamKey;
00249 
00250   // If we get here, all attempted probes failed.  Close any opened
00251   // devices and delete the allocated stream.
00252   closeStream(streamKey);
00253   if ( ( outputDevice == 0 && outputChannels > 0 )
00254        || ( inputDevice == 0 && inputChannels > 0 ) )
00255     sprintf(message,"RtAudio: no devices found for given parameters.");
00256   else
00257     sprintf(message,"RtAudio: unable to open specified device(s) with given stream parameters.");
00258   error(RtError::INVALID_PARAMETER);
00259 
00260   return -1;
00261 }
00262 
00263 int RtAudio :: getDeviceCount(void)
00264 {
00265   return nDevices;
00266 }
00267 
00268 void RtAudio :: getDeviceInfo(int device, RTAUDIO_DEVICE *info)
00269 {
00270   if (device > nDevices || device < 1) {
00271     sprintf(message, "RtAudio: invalid device specifier (%d)!", device);
00272     error(RtError::INVALID_DEVICE);
00273   }
00274 
00275   int deviceIndex = device - 1;
00276 
00277   // If the device wasn't successfully probed before, try it now (or again).
00278   if (devices[deviceIndex].probed == false) {
00279     clearDeviceInfo(&devices[deviceIndex]);
00280     probeDeviceInfo(&devices[deviceIndex]);
00281   }
00282 
00283   // Clear the info structure.
00284   memset(info, 0, sizeof(RTAUDIO_DEVICE));
00285 
00286   strncpy(info->name, devices[deviceIndex].name, 128);
00287   info->probed = devices[deviceIndex].probed;
00288   if ( info->probed == true ) {
00289     info->maxOutputChannels = devices[deviceIndex].maxOutputChannels;
00290     info->maxInputChannels = devices[deviceIndex].maxInputChannels;
00291     info->maxDuplexChannels = devices[deviceIndex].maxDuplexChannels;
00292     info->minOutputChannels = devices[deviceIndex].minOutputChannels;
00293     info->minInputChannels = devices[deviceIndex].minInputChannels;
00294     info->minDuplexChannels = devices[deviceIndex].minDuplexChannels;
00295     info->hasDuplexSupport = devices[deviceIndex].hasDuplexSupport;
00296     info->nSampleRates = devices[deviceIndex].nSampleRates;
00297     if (info->nSampleRates == -1) {
00298       info->sampleRates[0] = devices[deviceIndex].sampleRates[0];
00299       info->sampleRates[1] = devices[deviceIndex].sampleRates[1];
00300     }
00301     else {
00302       for (int i=0; i<info->nSampleRates; i++)
00303         info->sampleRates[i] = devices[deviceIndex].sampleRates[i];
00304     }
00305     info->nativeFormats = devices[deviceIndex].nativeFormats;
00306     if ( deviceIndex == getDefaultOutputDevice() ||
00307          deviceIndex == getDefaultInputDevice() )
00308       info->isDefault = true;
00309   }
00310 
00311   return;
00312 }
00313 
00314 char * const RtAudio :: getStreamBuffer(int streamId)
00315 {
00316   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
00317 
00318   return stream->userBuffer;
00319 }
00320 
00321 #if defined(__LINUX_ALSA__) || defined(__LINUX_OSS__) || defined(__IRIX_AL__)
00322 
00323 extern "C" void *callbackHandler(void * ptr);
00324 
00325 void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
00326 {
00327   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
00328 
00329   CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo;
00330   if ( info->usingCallback ) {
00331     sprintf(message, "RtAudio: A callback is already set for this stream!");
00332     error(RtError::WARNING);
00333     return;
00334   }
00335 
00336   info->callback = (void *) callback;
00337   info->userData = userData;
00338   info->usingCallback = true;
00339   info->object = (void *) this;
00340   info->streamId = streamId;
00341 
00342   int err = pthread_create(&info->thread, NULL, callbackHandler, &stream->callbackInfo);
00343 
00344   if (err) {
00345     info->usingCallback = false;
00346     sprintf(message, "RtAudio: error starting callback thread!");
00347     error(RtError::THREAD_ERROR);
00348   }
00349 }
00350 
00351 void RtAudio :: cancelStreamCallback(int streamId)
00352 {
00353   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
00354 
00355   if (stream->callbackInfo.usingCallback) {
00356 
00357     if (stream->state == STREAM_RUNNING)
00358       stopStream( streamId );
00359 
00360     MUTEX_LOCK(&stream->mutex);
00361 
00362     stream->callbackInfo.usingCallback = false;
00363     pthread_cancel(stream->callbackInfo.thread);
00364     pthread_join(stream->callbackInfo.thread, NULL);
00365     stream->callbackInfo.thread = 0;
00366     stream->callbackInfo.callback = NULL;
00367     stream->callbackInfo.userData = NULL;
00368 
00369     MUTEX_UNLOCK(&stream->mutex);
00370   }
00371 }
00372 
00373 #endif
00374 
00375 // *************************************************** //
00376 //
00377 // OS/API-specific methods.
00378 //
00379 // *************************************************** //
00380 
00381 #if defined(__MACOSX_CORE__)
00382 
00383 // The OS X CoreAudio API is designed to use a separate callback
00384 // procedure for each of its audio devices.  A single RtAudio duplex
00385 // stream using two different devices is supported here, though it
00386 // cannot be guaranteed to always behave correctly because we cannot
00387 // synchronize these two callbacks.  This same functionality can be
00388 // achieved with better synchrony by opening two separate streams for
00389 // the devices and using RtAudio blocking calls (i.e. tickStream()).
00390 //
00391 // The possibility of having multiple RtAudio streams accessing the
00392 // same CoreAudio device is not currently supported.  The problem
00393 // involves the inability to install our callbackHandler function for
00394 // the same device more than once.  I experimented with a workaround
00395 // for this, but it requires an additional buffer for mixing output
00396 // data before filling the CoreAudio device buffer.  In the end, I
00397 // decided it wasn't worth supporting.
00398 //
00399 // Property listeners are currently not used.  The issue is what could
00400 // be done if a critical stream parameter (buffer size, sample rate,
00401 // device disconnect) notification arrived.  The listeners entail
00402 // quite a bit of extra code and most likely, a user program wouldn't
00403 // be prepared for the result anyway.  Some initial listener code is
00404 // commented out.
00405 
00406 void RtAudio :: initialize(void)
00407 {
00408   OSStatus err = noErr;
00409   UInt32 dataSize;
00410   AudioDeviceID *deviceList = NULL;
00411   nDevices = 0;
00412 
00413   // Find out how many audio devices there are, if any.
00414   err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &dataSize, NULL);
00415   if (err != noErr) {
00416     sprintf(message, "RtAudio: OSX error getting device info!");
00417     error(RtError::SYSTEM_ERROR);
00418   }
00419 
00420   nDevices = dataSize / sizeof(AudioDeviceID);
00421   if (nDevices == 0) return;
00422 
00423   //  Allocate the RTAUDIO_DEVICE structures.
00424   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
00425   if (devices == NULL) {
00426     sprintf(message, "RtAudio: memory allocation error!");
00427     error(RtError::MEMORY_ERROR);
00428   }
00429 
00430   // Make space for the devices we are about to get.
00431   deviceList = (AudioDeviceID   *) malloc( dataSize );
00432   if (deviceList == NULL) {
00433     sprintf(message, "RtAudio: memory allocation error!");
00434     error(RtError::MEMORY_ERROR);
00435   }
00436 
00437   // Get the array of AudioDeviceIDs.
00438   err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &dataSize, (void *) deviceList);
00439   if (err != noErr) {
00440     free(deviceList);
00441     sprintf(message, "RtAudio: OSX error getting device properties!");
00442     error(RtError::SYSTEM_ERROR);
00443   }
00444 
00445   // Write device identifiers to device structures and then
00446   // probe the device capabilities.
00447   for (int i=0; i<nDevices; i++) {
00448     devices[i].id[0] = deviceList[i];
00449     //probeDeviceInfo(&devices[i]);
00450   }
00451 
00452   free(deviceList);
00453 }
00454 
00455 int RtAudio :: getDefaultInputDevice(void)
00456 {
00457   AudioDeviceID id;
00458   UInt32 dataSize = sizeof( AudioDeviceID );
00459 
00460   OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice,
00461                                               &dataSize, &id );
00462 
00463   if (result != noErr) {
00464     sprintf( message, "RtAudio: OSX error getting default input device." );
00465     error(RtError::WARNING);
00466     return 0;
00467   }
00468 
00469   for ( int i=0; i<nDevices; i++ ) {
00470     if ( id == devices[i].id[0] ) return i;
00471   }
00472 
00473   return 0;
00474 }
00475 
00476 int RtAudio :: getDefaultOutputDevice(void)
00477 {
00478   AudioDeviceID id;
00479   UInt32 dataSize = sizeof( AudioDeviceID );
00480 
00481   OSStatus result = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
00482                                               &dataSize, &id );
00483 
00484   if (result != noErr) {
00485     sprintf( message, "RtAudio: OSX error getting default output device." );
00486     error(RtError::WARNING);
00487     return 0;
00488   }
00489 
00490   for ( int i=0; i<nDevices; i++ ) {
00491     if ( id == devices[i].id[0] ) return i;
00492   }
00493 
00494   return 0;
00495 }
00496 
00497 static bool deviceSupportsFormat( AudioDeviceID id, bool isInput,
00498                                   AudioStreamBasicDescription   *desc, bool isDuplex )
00499 {
00500   OSStatus result = noErr;
00501   UInt32 dataSize = sizeof( AudioStreamBasicDescription );
00502 
00503   result = AudioDeviceGetProperty( id, 0, isInput,
00504                                    kAudioDevicePropertyStreamFormatSupported,
00505                                    &dataSize, desc );
00506 
00507   if (result == kAudioHardwareNoError) {
00508     if ( isDuplex ) {
00509       result = AudioDeviceGetProperty( id, 0, true,
00510                                        kAudioDevicePropertyStreamFormatSupported,
00511                                        &dataSize, desc );
00512 
00513 
00514       if (result != kAudioHardwareNoError)
00515         return false;
00516     }
00517     return true;
00518   }
00519 
00520   return false;
00521 }
00522 
00523 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
00524 {
00525   OSStatus err = noErr;
00526 
00527   // Get the device manufacturer and name.
00528   char  name[256];
00529   char  fullname[512];
00530   UInt32 dataSize = 256;
00531   err = AudioDeviceGetProperty( info->id[0], 0, false,
00532                                 kAudioDevicePropertyDeviceManufacturer,
00533                                 &dataSize, name );
00534   if (err != noErr) {
00535     sprintf( message, "RtAudio: OSX error getting device manufacturer." );
00536     error(RtError::DEBUG_WARNING);
00537     return;
00538   }
00539   strncpy(fullname, name, 256);
00540   strcat(fullname, ": " );
00541 
00542   dataSize = 256;
00543   err = AudioDeviceGetProperty( info->id[0], 0, false,
00544                                 kAudioDevicePropertyDeviceName,
00545                                 &dataSize, name );
00546   if (err != noErr) {
00547     sprintf( message, "RtAudio: OSX error getting device name." );
00548     error(RtError::DEBUG_WARNING);
00549     return;
00550   }
00551   strncat(fullname, name, 254);
00552   strncat(info->name, fullname, 128);
00553 
00554   // Get output channel information.
00555   unsigned int i, minChannels, maxChannels, nStreams = 0;
00556   AudioBufferList       *bufferList = nil;
00557   err = AudioDeviceGetPropertyInfo( info->id[0], 0, false,
00558                                     kAudioDevicePropertyStreamConfiguration,
00559                                     &dataSize, NULL );
00560   if (err == noErr && dataSize > 0) {
00561     bufferList = (AudioBufferList *) malloc( dataSize );
00562     if (bufferList == NULL) {
00563       sprintf(message, "RtAudio: memory allocation error!");
00564       error(RtError::DEBUG_WARNING);
00565       return;
00566     }
00567 
00568     err = AudioDeviceGetProperty( info->id[0], 0, false,
00569                                   kAudioDevicePropertyStreamConfiguration,
00570                                   &dataSize, bufferList );
00571     if (err == noErr) {
00572       maxChannels = 0;
00573       minChannels = 1000;
00574       nStreams = bufferList->mNumberBuffers;
00575       for ( i=0; i<nStreams; i++ ) {
00576         maxChannels += bufferList->mBuffers[i].mNumberChannels;
00577         if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
00578           minChannels = bufferList->mBuffers[i].mNumberChannels;
00579       }
00580     }
00581   }
00582   if (err != noErr || dataSize <= 0) {
00583     sprintf( message, "RtAudio: OSX error getting output channels for device (%s).", info->name );
00584     error(RtError::DEBUG_WARNING);
00585     return;
00586   }
00587 
00588   free (bufferList);
00589   if ( nStreams ) {
00590     if ( maxChannels > 0 )
00591       info->maxOutputChannels = maxChannels;
00592     if ( minChannels > 0 )
00593       info->minOutputChannels = minChannels;
00594   }
00595 
00596   // Get input channel information.
00597   bufferList = nil;
00598   err = AudioDeviceGetPropertyInfo( info->id[0], 0, true,
00599                                     kAudioDevicePropertyStreamConfiguration,
00600                                     &dataSize, NULL );
00601   if (err == noErr && dataSize > 0) {
00602     bufferList = (AudioBufferList *) malloc( dataSize );
00603     if (bufferList == NULL) {
00604       sprintf(message, "RtAudio: memory allocation error!");
00605       error(RtError::DEBUG_WARNING);
00606       return;
00607     }
00608     err = AudioDeviceGetProperty( info->id[0], 0, true,
00609                                   kAudioDevicePropertyStreamConfiguration,
00610                                   &dataSize, bufferList );
00611     if (err == noErr) {
00612       maxChannels = 0;
00613       minChannels = 1000;
00614       nStreams = bufferList->mNumberBuffers;
00615       for ( i=0; i<nStreams; i++ ) {
00616         if ( bufferList->mBuffers[i].mNumberChannels < minChannels )
00617           minChannels = bufferList->mBuffers[i].mNumberChannels;
00618         maxChannels += bufferList->mBuffers[i].mNumberChannels;
00619       }
00620     }
00621   }
00622   if (err != noErr || dataSize <= 0) {
00623     sprintf( message, "RtAudio: OSX error getting input channels for device (%s).", info->name );
00624     error(RtError::DEBUG_WARNING);
00625     return;
00626   }
00627 
00628   free (bufferList);
00629   if ( nStreams ) {
00630     if ( maxChannels > 0 )
00631       info->maxInputChannels = maxChannels;
00632     if ( minChannels > 0 )
00633       info->minInputChannels = minChannels;
00634   }
00635 
00636   // If device opens for both playback and capture, we determine the channels.
00637   if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
00638     info->hasDuplexSupport = true;
00639     info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
00640       info->maxInputChannels : info->maxOutputChannels;
00641     info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
00642       info->minInputChannels : info->minOutputChannels;
00643   }
00644 
00645   // Probe the device sample rate and data format parameters.  The
00646   // core audio query mechanism is performed on a "stream"
00647   // description, which can have a variable number of channels and
00648   // apply to input or output only.
00649 
00650   // Create a stream description structure.
00651   AudioStreamBasicDescription   description;
00652   dataSize = sizeof( AudioStreamBasicDescription );
00653   memset(&description, 0, sizeof(AudioStreamBasicDescription));
00654   bool isInput = false;
00655   if ( info->maxOutputChannels == 0 ) isInput = true;
00656   bool isDuplex = false;
00657   if ( info->maxDuplexChannels > 0 ) isDuplex = true;
00658 
00659   // Determine the supported sample rates.
00660   info->nSampleRates = 0;
00661   for (i=0; i<MAX_SAMPLE_RATES; i++) {
00662     description.mSampleRate = (double) SAMPLE_RATES[i];
00663     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00664       info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i];
00665   }
00666 
00667   if (info->nSampleRates == 0) {
00668     sprintf( message, "RtAudio: No supported sample rates found for OSX device (%s).", info->name );
00669     error(RtError::DEBUG_WARNING);
00670     return;
00671   }
00672 
00673   // Check for continuous sample rate support.
00674   description.mSampleRate = kAudioStreamAnyRate;
00675   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) ) {
00676     info->sampleRates[1] = info->sampleRates[info->nSampleRates-1];
00677     info->nSampleRates = -1;
00678   }
00679 
00680   // Determine the supported data formats.
00681   info->nativeFormats = 0;
00682   description.mFormatID = kAudioFormatLinearPCM;
00683   description.mBitsPerChannel = 8;
00684   description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
00685   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00686     info->nativeFormats |= RTAUDIO_SINT8;
00687   else {
00688     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00689     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00690       info->nativeFormats |= RTAUDIO_SINT8;
00691   }
00692 
00693   description.mBitsPerChannel = 16;
00694   description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
00695   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00696     info->nativeFormats |= RTAUDIO_SINT16;
00697   else {
00698     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00699     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00700       info->nativeFormats |= RTAUDIO_SINT16;
00701   }
00702 
00703   description.mBitsPerChannel = 32;
00704   description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
00705   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00706     info->nativeFormats |= RTAUDIO_SINT32;
00707   else {
00708     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00709     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00710       info->nativeFormats |= RTAUDIO_SINT32;
00711   }
00712 
00713   description.mBitsPerChannel = 24;
00714   description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsAlignedHigh | kLinearPCMFormatFlagIsBigEndian;
00715   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00716     info->nativeFormats |= RTAUDIO_SINT24;
00717   else {
00718     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00719     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00720       info->nativeFormats |= RTAUDIO_SINT24;
00721   }
00722 
00723   description.mBitsPerChannel = 32;
00724   description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsBigEndian;
00725   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00726     info->nativeFormats |= RTAUDIO_FLOAT32;
00727   else {
00728     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00729     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00730       info->nativeFormats |= RTAUDIO_FLOAT32;
00731   }
00732 
00733   description.mBitsPerChannel = 64;
00734   description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
00735   if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00736     info->nativeFormats |= RTAUDIO_FLOAT64;
00737   else {
00738     description.mFormatFlags &= ~kLinearPCMFormatFlagIsBigEndian;
00739     if ( deviceSupportsFormat( info->id[0], isInput, &description, isDuplex ) )
00740       info->nativeFormats |= RTAUDIO_FLOAT64;
00741   }
00742 
00743   // Check that we have at least one supported format.
00744   if (info->nativeFormats == 0) {
00745     sprintf(message, "RtAudio: OSX PCM device (%s) data format not supported by RtAudio.",
00746             info->name);
00747     error(RtError::DEBUG_WARNING);
00748     return;
00749   }
00750 
00751   info->probed = true;
00752 }
00753 
00754 OSStatus callbackHandler(AudioDeviceID inDevice,
00755                          const AudioTimeStamp* inNow,
00756                          const AudioBufferList* inInputData,
00757                          const AudioTimeStamp* inInputTime,
00758                          AudioBufferList* outOutputData,
00759                          const AudioTimeStamp* inOutputTime, 
00760                          void* infoPointer)
00761 {
00762   CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
00763 
00764   RtAudio *object = (RtAudio *) info->object;
00765   try {
00766     object->callbackEvent( info->streamId, inDevice, (void *)inInputData, (void *)outOutputData );
00767   }
00768   catch (RtError &exception) {
00769     fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage());
00770     return kAudioHardwareUnspecifiedError;
00771   }
00772 
00773   return kAudioHardwareNoError;
00774 }
00775 
00776 /*
00777 OSStatus deviceListener(AudioDeviceID inDevice,
00778                         UInt32 channel,
00779                         Boolean isInput,
00780                         AudioDevicePropertyID propertyID,
00781                         void* infoPointer)
00782 {
00783   CALLBACK_INFO *info = (CALLBACK_INFO *) infoPointer;
00784 
00785   RtAudio *object = (RtAudio *) info->object;
00786   try {
00787     object->settingChange( info->streamId );
00788   }
00789   catch (RtError &exception) {
00790     fprintf(stderr, "\nDevice listener error (%s)!\n\n", exception.getMessage());
00791     return kAudioHardwareUnspecifiedError;
00792   }
00793 
00794   return kAudioHardwareNoError;
00795 }
00796 */
00797 
00798 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
00799                                 STREAM_MODE mode, int channels, 
00800                                 int sampleRate, RTAUDIO_FORMAT format,
00801                                 int *bufferSize, int numberOfBuffers)
00802 {
00803   // Check to make sure we don't already have a stream accessing this device.
00804   RTAUDIO_STREAM *streamPtr;
00805   std::map<int, void *>::const_iterator i;
00806   for ( i=streams.begin(); i!=streams.end(); ++i ) {
00807     streamPtr = (RTAUDIO_STREAM *) i->second;
00808     if ( streamPtr->device[0] == device || streamPtr->device[1] == device ) {
00809       sprintf(message, "RtAudio: no current OS X support for multiple streams accessing the same device!");
00810       error(RtError::WARNING);
00811       return FAILURE;
00812     }
00813   }
00814 
00815   // Setup for stream mode.
00816   bool isInput = false;
00817   AudioDeviceID id = devices[device].id[0];
00818   if ( mode == INPUT ) isInput = true;
00819 
00820   // Search for a stream which contains the desired number of channels.
00821   OSStatus err = noErr;
00822   UInt32 dataSize;
00823   unsigned int deviceChannels, nStreams;
00824   UInt32 iChannel = 0, iStream = 0;
00825   AudioBufferList       *bufferList = nil;
00826   err = AudioDeviceGetPropertyInfo( id, 0, isInput,
00827                                     kAudioDevicePropertyStreamConfiguration,
00828                                     &dataSize, NULL );
00829 
00830   if (err == noErr && dataSize > 0) {
00831     bufferList = (AudioBufferList *) malloc( dataSize );
00832     if (bufferList == NULL) {
00833       sprintf(message, "RtAudio: memory allocation error!");
00834       error(RtError::DEBUG_WARNING);
00835       return FAILURE;
00836     }
00837     err = AudioDeviceGetProperty( id, 0, isInput,
00838                                   kAudioDevicePropertyStreamConfiguration,
00839                                   &dataSize, bufferList );
00840 
00841     if (err == noErr) {
00842       stream->deInterleave[mode] = false;
00843       nStreams = bufferList->mNumberBuffers;
00844       for ( iStream=0; iStream<nStreams; iStream++ ) {
00845         if ( bufferList->mBuffers[iStream].mNumberChannels >= (unsigned int) channels ) break;
00846         iChannel += bufferList->mBuffers[iStream].mNumberChannels;
00847       }
00848       // If we didn't find a single stream above, see if we can meet
00849       // the channel specification in mono mode (i.e. using separate
00850       // non-interleaved buffers).  This can only work if there are N
00851       // consecutive one-channel streams, where N is the number of
00852       // desired channels.
00853       iChannel = 0;
00854       if ( iStream >= nStreams && nStreams >= (unsigned int) channels ) {
00855         int counter = 0;
00856         for ( iStream=0; iStream<nStreams; iStream++ ) {
00857           if ( bufferList->mBuffers[iStream].mNumberChannels == 1 )
00858             counter++;
00859           else
00860             counter = 0;
00861           if ( counter == channels ) {
00862             iStream -= channels - 1;
00863             iChannel -= channels - 1;
00864             stream->deInterleave[mode] = true;
00865             break;
00866           }
00867           iChannel += bufferList->mBuffers[iStream].mNumberChannels;
00868         }
00869       }
00870     }
00871   }
00872   if (err != noErr || dataSize <= 0) {
00873     if ( bufferList ) free( bufferList );
00874     sprintf( message, "RtAudio: OSX error getting channels for device (%s).", devices[device].name );
00875     error(RtError::DEBUG_WARNING);
00876     return FAILURE;
00877   }
00878 
00879   if (iStream >= nStreams) {
00880     free (bufferList);
00881     sprintf( message, "RtAudio: unable to find OSX audio stream on device (%s) for requested channels (%d).",
00882              devices[device].name, channels );
00883     error(RtError::DEBUG_WARNING);
00884     return FAILURE;
00885   }
00886 
00887   // This is ok even for mono mode ... it gets updated later.
00888   deviceChannels = bufferList->mBuffers[iStream].mNumberChannels;
00889   free (bufferList);
00890 
00891   // Determine the buffer size.
00892   AudioValueRange       bufferRange;
00893   dataSize = sizeof(AudioValueRange);
00894   err = AudioDeviceGetProperty( id, 0, isInput,
00895                                 kAudioDevicePropertyBufferSizeRange,
00896                                 &dataSize, &bufferRange);
00897   if (err != noErr) {
00898     sprintf( message, "RtAudio: OSX error getting buffer size range for device (%s).",
00899              devices[device].name );
00900     error(RtError::DEBUG_WARNING);
00901     return FAILURE;
00902   }
00903 
00904   long bufferBytes = *bufferSize * deviceChannels * formatBytes(RTAUDIO_FLOAT32);
00905   if (bufferRange.mMinimum > bufferBytes) bufferBytes = (int) bufferRange.mMinimum;
00906   else if (bufferRange.mMaximum < bufferBytes) bufferBytes = (int) bufferRange.mMaximum;
00907 
00908   // Set the buffer size.  For mono mode, I'm assuming we only need to
00909   // make this setting for the first channel.
00910   UInt32 theSize = (UInt32) bufferBytes;
00911   dataSize = sizeof( UInt32);
00912   err = AudioDeviceSetProperty(id, NULL, 0, isInput,
00913                                kAudioDevicePropertyBufferSize,
00914                                dataSize, &theSize);
00915   if (err != noErr) {
00916     sprintf( message, "RtAudio: OSX error setting the buffer size for device (%s).",
00917              devices[device].name );
00918     error(RtError::DEBUG_WARNING);
00919     return FAILURE;
00920   }
00921 
00922   // If attempting to setup a duplex stream, the bufferSize parameter
00923   // MUST be the same in both directions!
00924   *bufferSize = bufferBytes / ( deviceChannels * formatBytes(RTAUDIO_FLOAT32) );
00925   if ( stream->mode == OUTPUT && mode == INPUT && *bufferSize != stream->bufferSize ) {
00926     sprintf( message, "RtAudio: OSX error setting buffer size for duplex stream on device (%s).",
00927              devices[device].name );
00928     error(RtError::DEBUG_WARNING);
00929     return FAILURE;
00930   }
00931 
00932   stream->bufferSize = *bufferSize;
00933   stream->nBuffers = 1;
00934 
00935   // Set the stream format description.  Do for each channel in mono mode.
00936   AudioStreamBasicDescription   description;
00937   dataSize = sizeof( AudioStreamBasicDescription );
00938   if ( stream->deInterleave[mode] ) nStreams = channels;
00939   else nStreams = 1;
00940   for ( unsigned int i=0; i<nStreams; i++, iChannel++ ) {
00941 
00942     err = AudioDeviceGetProperty( id, iChannel, isInput,
00943                                   kAudioDevicePropertyStreamFormat,
00944                                   &dataSize, &description );
00945     if (err != noErr) {
00946       sprintf( message, "RtAudio: OSX error getting stream format for device (%s).", devices[device].name );
00947       error(RtError::DEBUG_WARNING);
00948       return FAILURE;
00949     }
00950 
00951     // Set the sample rate and data format id.
00952     description.mSampleRate = (double) sampleRate;
00953     description.mFormatID = kAudioFormatLinearPCM;
00954     err = AudioDeviceSetProperty( id, NULL, iChannel, isInput,
00955                                   kAudioDevicePropertyStreamFormat,
00956                                   dataSize, &description );
00957     if (err != noErr) {
00958       sprintf( message, "RtAudio: OSX error setting sample rate or data format for device (%s).", devices[device].name );
00959       error(RtError::DEBUG_WARNING);
00960       return FAILURE;
00961     }
00962   }
00963 
00964   // Check whether we need byte-swapping (assuming OS X host is big-endian).
00965   iChannel -= nStreams;
00966   err = AudioDeviceGetProperty( id, iChannel, isInput,
00967                                 kAudioDevicePropertyStreamFormat,
00968                                 &dataSize, &description );
00969   if (err != noErr) {
00970     sprintf( message, "RtAudio: OSX error getting stream format for device (%s).", devices[device].name );
00971     error(RtError::DEBUG_WARNING);
00972     return FAILURE;
00973   }
00974 
00975   stream->doByteSwap[mode] = false;
00976   if ( !description.mFormatFlags & kLinearPCMFormatFlagIsBigEndian )
00977     stream->doByteSwap[mode] = true;
00978 
00979   // From the CoreAudio documentation, PCM data must be supplied as
00980   // 32-bit floats.
00981   stream->userFormat = format;
00982   stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
00983 
00984   if ( stream->deInterleave[mode] )
00985     stream->nDeviceChannels[mode] = channels;
00986   else
00987     stream->nDeviceChannels[mode] = description.mChannelsPerFrame;
00988   stream->nUserChannels[mode] = channels;
00989 
00990   // Set handle and flags for buffer conversion.
00991   stream->handle[mode] = iStream;
00992   stream->doConvertBuffer[mode] = false;
00993   if (stream->userFormat != stream->deviceFormat[mode])
00994     stream->doConvertBuffer[mode] = true;
00995   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
00996     stream->doConvertBuffer[mode] = true;
00997   if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
00998     stream->doConvertBuffer[mode] = true;
00999 
01000   // Allocate necessary internal buffers.
01001   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
01002 
01003     long buffer_bytes;
01004     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
01005       buffer_bytes = stream->nUserChannels[0];
01006     else
01007       buffer_bytes = stream->nUserChannels[1];
01008 
01009     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
01010     if (stream->userBuffer) free(stream->userBuffer);
01011     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
01012     if (stream->userBuffer == NULL)
01013       goto memory_error;
01014   }
01015 
01016   if ( stream->deInterleave[mode] ) {
01017 
01018     long buffer_bytes;
01019     bool makeBuffer = true;
01020     if ( mode == OUTPUT )
01021       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
01022     else { // mode == INPUT
01023       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
01024       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
01025         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
01026         if ( buffer_bytes < bytes_out ) makeBuffer = false;
01027       }
01028     }
01029 
01030     if ( makeBuffer ) {
01031       buffer_bytes *= *bufferSize;
01032       if (stream->deviceBuffer) free(stream->deviceBuffer);
01033       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
01034       if (stream->deviceBuffer == NULL)
01035         goto memory_error;
01036 
01037       // If not de-interleaving, we point stream->deviceBuffer to the
01038       // OS X supplied device buffer before doing any necessary data
01039       // conversions.  This presents a problem if we have a duplex
01040       // stream using one device which needs de-interleaving and
01041       // another device which doesn't.  So, save a pointer to our own
01042       // device buffer in the CALLBACK_INFO structure.
01043       stream->callbackInfo.buffers = stream->deviceBuffer;
01044     }
01045   }
01046 
01047   stream->sampleRate = sampleRate;
01048   stream->device[mode] = device;
01049   stream->state = STREAM_STOPPED;
01050   stream->callbackInfo.object = (void *) this;
01051   stream->callbackInfo.waitTime = (unsigned long) (200000.0 * stream->bufferSize / stream->sampleRate);
01052   stream->callbackInfo.device[mode] = id;
01053   if ( stream->mode == OUTPUT && mode == INPUT && stream->device[0] == device )
01054     // Only one callback procedure per device.
01055     stream->mode = DUPLEX;
01056   else {
01057     err = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream->callbackInfo );
01058     if (err != noErr) {
01059       sprintf( message, "RtAudio: OSX error setting callback for device (%s).", devices[device].name );
01060       error(RtError::DEBUG_WARNING);
01061       return FAILURE;
01062     }
01063     if ( stream->mode == OUTPUT && mode == INPUT )
01064       stream->mode = DUPLEX;
01065     else
01066       stream->mode = mode;
01067   }
01068 
01069   // If we wanted to use property listeners, they would be setup here.
01070 
01071   return SUCCESS;
01072 
01073  memory_error:
01074   if (stream->userBuffer) {
01075     free(stream->userBuffer);
01076     stream->userBuffer = 0;
01077   }
01078   sprintf(message, "RtAudio: OSX error allocating buffer memory (%s).", devices[device].name);
01079   error(RtError::WARNING);
01080   return FAILURE;
01081 }
01082 
01083 void RtAudio :: cancelStreamCallback(int streamId)
01084 {
01085   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01086 
01087   if (stream->callbackInfo.usingCallback) {
01088 
01089     if (stream->state == STREAM_RUNNING)
01090       stopStream( streamId );
01091 
01092     MUTEX_LOCK(&stream->mutex);
01093 
01094     stream->callbackInfo.usingCallback = false;
01095     stream->callbackInfo.userData = NULL;
01096     stream->state = STREAM_STOPPED;
01097     stream->callbackInfo.callback = NULL;
01098 
01099     MUTEX_UNLOCK(&stream->mutex);
01100   }
01101 }
01102 
01103 void RtAudio :: closeStream(int streamId)
01104 {
01105   // We don't want an exception to be thrown here because this
01106   // function is called by our class destructor.  So, do our own
01107   // streamId check.
01108   if ( streams.find( streamId ) == streams.end() ) {
01109     sprintf(message, "RtAudio: invalid stream identifier!");
01110     error(RtError::WARNING);
01111     return;
01112   }
01113 
01114   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
01115 
01116   AudioDeviceID id;
01117   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
01118     id = devices[stream->device[0]].id[0];
01119     if (stream->state == STREAM_RUNNING)
01120       AudioDeviceStop( id, callbackHandler );
01121     AudioDeviceRemoveIOProc( id, callbackHandler );
01122   }
01123 
01124   if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
01125     id = devices[stream->device[1]].id[0];
01126     if (stream->state == STREAM_RUNNING)
01127       AudioDeviceStop( id, callbackHandler );
01128     AudioDeviceRemoveIOProc( id, callbackHandler );
01129   }
01130 
01131   pthread_mutex_destroy(&stream->mutex);
01132 
01133   if (stream->userBuffer)
01134     free(stream->userBuffer);
01135 
01136   if ( stream->deInterleave[0] || stream->deInterleave[1] )
01137     free(stream->callbackInfo.buffers);
01138 
01139   free(stream);
01140   streams.erase(streamId);
01141 }
01142 
01143 void RtAudio :: startStream(int streamId)
01144 {
01145   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01146 
01147   MUTEX_LOCK(&stream->mutex);
01148 
01149   if (stream->state == STREAM_RUNNING)
01150     goto unlock;
01151 
01152   OSStatus err;
01153   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
01154 
01155     err = AudioDeviceStart(devices[stream->device[0]].id[0], callbackHandler);
01156     if (err != noErr) {
01157       sprintf(message, "RtAudio: OSX error starting callback procedure on device (%s).",
01158               devices[stream->device[0]].name);
01159       MUTEX_UNLOCK(&stream->mutex);
01160       error(RtError::DRIVER_ERROR);
01161     }
01162   }
01163 
01164   if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
01165 
01166     err = AudioDeviceStart(devices[stream->device[1]].id[0], callbackHandler);
01167     if (err != noErr) {
01168       sprintf(message, "RtAudio: OSX error starting input callback procedure on device (%s).",
01169               devices[stream->device[0]].name);
01170       MUTEX_UNLOCK(&stream->mutex);
01171       error(RtError::DRIVER_ERROR);
01172     }
01173   }
01174 
01175   stream->callbackInfo.streamId = streamId;
01176   stream->state = STREAM_RUNNING;
01177   stream->callbackInfo.blockTick = true;
01178   stream->callbackInfo.stopStream = false;
01179 
01180  unlock:
01181   MUTEX_UNLOCK(&stream->mutex);
01182 }
01183 
01184 void RtAudio :: stopStream(int streamId)
01185 {
01186   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01187 
01188   MUTEX_LOCK(&stream->mutex);
01189 
01190   if (stream->state == STREAM_STOPPED)
01191     goto unlock;
01192 
01193   OSStatus err;
01194   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
01195 
01196     err = AudioDeviceStop(devices[stream->device[0]].id[0], callbackHandler);
01197     if (err != noErr) {
01198       sprintf(message, "RtAudio: OSX error stopping callback procedure on device (%s).",
01199               devices[stream->device[0]].name);
01200       MUTEX_UNLOCK(&stream->mutex);
01201       error(RtError::DRIVER_ERROR);
01202     }
01203   }
01204 
01205   if (stream->mode == INPUT || ( stream->mode == DUPLEX && stream->device[0] != stream->device[1]) ) {
01206 
01207     err = AudioDeviceStop(devices[stream->device[1]].id[0], callbackHandler);
01208     if (err != noErr) {
01209       sprintf(message, "RtAudio: OSX error stopping input callback procedure on device (%s).",
01210               devices[stream->device[0]].name);
01211       MUTEX_UNLOCK(&stream->mutex);
01212       error(RtError::DRIVER_ERROR);
01213     }
01214   }
01215 
01216   stream->state = STREAM_STOPPED;
01217 
01218  unlock:
01219   MUTEX_UNLOCK(&stream->mutex);
01220 }
01221 
01222 void RtAudio :: abortStream(int streamId)
01223 {
01224   stopStream( streamId );
01225 }
01226 
01227 // I don't know how this function can be implemented.
01228 int RtAudio :: streamWillBlock(int streamId)
01229 {
01230   sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for OS X.");
01231   error(RtError::WARNING);
01232   return 0;
01233 }
01234 
01235 void RtAudio :: tickStream(int streamId)
01236 {
01237   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01238 
01239   if (stream->state == STREAM_STOPPED)
01240     return;
01241 
01242   if (stream->callbackInfo.usingCallback) {
01243     sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
01244     error(RtError::WARNING);
01245     return;
01246   }
01247 
01248   // Block waiting here until the user data is processed in callbackEvent().
01249   while ( stream->callbackInfo.blockTick )
01250     usleep(stream->callbackInfo.waitTime);
01251 
01252   MUTEX_LOCK(&stream->mutex);
01253 
01254   stream->callbackInfo.blockTick = true;
01255 
01256   MUTEX_UNLOCK(&stream->mutex);
01257 }
01258 
01259 void RtAudio :: callbackEvent( int streamId, DEVICE_ID deviceId, void *inData, void *outData )
01260 {
01261   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01262 
01263   CALLBACK_INFO *info;
01264   AudioBufferList *inBufferList = (AudioBufferList *) inData;
01265   AudioBufferList *outBufferList = (AudioBufferList *) outData;
01266 
01267   if (stream->state == STREAM_STOPPED) return;
01268 
01269   info = (CALLBACK_INFO *) &stream->callbackInfo;
01270   if ( !info->usingCallback ) {
01271     // Block waiting here until we get new user data in tickStream().
01272     while ( !info->blockTick )
01273       usleep(info->waitTime);
01274   }
01275   else if ( info->stopStream ) {
01276     // Check if the stream should be stopped (via the previous user
01277     // callback return value).  We stop the stream here, rather than
01278     // after the function call, so that output data can first be
01279     // processed.
01280     this->stopStream(info->streamId);
01281     return;
01282   }
01283 
01284   MUTEX_LOCK(&stream->mutex);
01285 
01286   // Invoke user callback first, to get fresh output data.  Don't
01287   // invoke the user callback if duplex mode, the input/output devices
01288   // are different, and this function is called for the input device.
01289   if ( info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[0] ) ) {
01290     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
01291     info->stopStream = callback(stream->userBuffer, stream->bufferSize, info->userData);
01292   }
01293 
01294   if ( stream->mode == OUTPUT || ( stream->mode == DUPLEX && deviceId == info->device[0] ) ) {
01295 
01296     if (stream->doConvertBuffer[0]) {
01297 
01298       if ( !stream->deInterleave[0] )
01299         stream->deviceBuffer = (char *) outBufferList->mBuffers[stream->handle[0]].mData;
01300       else
01301         stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
01302 
01303       convertStreamBuffer(stream, OUTPUT);
01304       if ( stream->doByteSwap[0] )
01305         byteSwapBuffer(stream->deviceBuffer,
01306                        stream->bufferSize * stream->nDeviceChannels[0],
01307                        stream->deviceFormat[0]);
01308 
01309       if ( stream->deInterleave[0] ) {
01310         int bufferBytes = outBufferList->mBuffers[stream->handle[0]].mDataByteSize;
01311         for ( int i=0; i<stream->nDeviceChannels[0]; i++ ) {
01312           memcpy(outBufferList->mBuffers[stream->handle[0]+i].mData,
01313                  &stream->deviceBuffer[i*bufferBytes], bufferBytes );
01314         }
01315       }
01316 
01317     }
01318     else {
01319       if (stream->doByteSwap[0])
01320         byteSwapBuffer(stream->userBuffer,
01321                        stream->bufferSize * stream->nUserChannels[0],
01322                        stream->userFormat);
01323 
01324       memcpy(outBufferList->mBuffers[stream->handle[0]].mData,
01325              stream->userBuffer,
01326              outBufferList->mBuffers[stream->handle[0]].mDataByteSize );
01327     }
01328   }
01329 
01330   if ( stream->mode == INPUT || ( stream->mode == DUPLEX && deviceId == info->device[1] ) ) {
01331 
01332     if (stream->doConvertBuffer[1]) {
01333 
01334       if ( stream->deInterleave[1] ) {
01335         stream->deviceBuffer = (char *) stream->callbackInfo.buffers;
01336         int bufferBytes = inBufferList->mBuffers[stream->handle[1]].mDataByteSize;
01337         for ( int i=0; i<stream->nDeviceChannels[1]; i++ ) {
01338           memcpy(&stream->deviceBuffer[i*bufferBytes],
01339                  inBufferList->mBuffers[stream->handle[1]+i].mData, bufferBytes );
01340         }
01341       }
01342       else
01343         stream->deviceBuffer = (char *) inBufferList->mBuffers[stream->handle[1]].mData;
01344 
01345       if ( stream->doByteSwap[1] )
01346         byteSwapBuffer(stream->deviceBuffer,
01347                        stream->bufferSize * stream->nDeviceChannels[1],
01348                        stream->deviceFormat[1]);
01349       convertStreamBuffer(stream, INPUT);
01350 
01351     }
01352     else {
01353       memcpy(stream->userBuffer,
01354              inBufferList->mBuffers[stream->handle[1]].mData,
01355              inBufferList->mBuffers[stream->handle[1]].mDataByteSize );
01356 
01357       if (stream->doByteSwap[1])
01358         byteSwapBuffer(stream->userBuffer,
01359                        stream->bufferSize * stream->nUserChannels[1],
01360                        stream->userFormat);
01361     }
01362   }
01363 
01364   if ( !info->usingCallback && (stream->mode != DUPLEX || deviceId == info->device[1] ) )
01365     info->blockTick = false;
01366 
01367   MUTEX_UNLOCK(&stream->mutex);
01368 
01369 }
01370 
01371 void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
01372 {
01373   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
01374 
01375   stream->callbackInfo.callback = (void *) callback;
01376   stream->callbackInfo.userData = userData;
01377   stream->callbackInfo.usingCallback = true;
01378 }
01379 
01380 //******************** End of __MACOSX_CORE__ *********************//
01381 
01382 #elif defined(__LINUX_ALSA__)
01383 
01384 #define MAX_DEVICES 16
01385 
01386 void RtAudio :: initialize(void)
01387 {
01388   int card, result, device;
01389   char name[32];
01390   const char *cardId;
01391   char deviceNames[MAX_DEVICES][32];
01392   snd_ctl_t *handle;
01393   snd_ctl_card_info_t *info;
01394   snd_ctl_card_info_alloca(&info);
01395 
01396   // Count cards and devices
01397   nDevices = 0;
01398   card = -1;
01399   snd_card_next(&card);
01400   while ( card >= 0 ) {
01401     sprintf(name, "hw:%d", card);
01402     result = snd_ctl_open(&handle, name, 0);
01403     if (result < 0) {
01404       sprintf(message, "RtAudio: ALSA control open (%i): %s.", card, snd_strerror(result));
01405       error(RtError::DEBUG_WARNING);
01406       goto next_card;
01407                 }
01408     result = snd_ctl_card_info(handle, info);
01409                 if (result < 0) {
01410       sprintf(message, "RtAudio: ALSA control hardware info (%i): %s.", card, snd_strerror(result));
01411       error(RtError::DEBUG_WARNING);
01412       goto next_card;
01413                 }
01414     cardId = snd_ctl_card_info_get_id(info);
01415                 device = -1;
01416                 while (1) {
01417       result = snd_ctl_pcm_next_device(handle, &device);
01418                         if (result < 0) {
01419         sprintf(message, "RtAudio: ALSA control next device (%i): %s.", card, snd_strerror(result));
01420         error(RtError::DEBUG_WARNING);
01421         break;
01422       }
01423                         if (device < 0)
01424         break;
01425       if ( strlen(cardId) )
01426         sprintf( deviceNames[nDevices++], "hw:%s,%d", cardId, device );
01427       else
01428         sprintf( deviceNames[nDevices++], "hw:%d,%d", card, device );
01429       if ( nDevices > MAX_DEVICES ) break;
01430     }
01431     if ( nDevices > MAX_DEVICES ) break;
01432   next_card:
01433     snd_ctl_close(handle);
01434     snd_card_next(&card);
01435   }
01436 
01437   if (nDevices == 0) return;
01438 
01439   //  Allocate the RTAUDIO_DEVICE structures.
01440   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
01441   if (devices == NULL) {
01442     sprintf(message, "RtAudio: memory allocation error!");
01443     error(RtError::MEMORY_ERROR);
01444   }
01445 
01446   // Write device ascii identifiers to device structures and then
01447   // probe the device capabilities.
01448   for (int i=0; i<nDevices; i++) {
01449     strncpy(devices[i].name, deviceNames[i], 32);
01450     //probeDeviceInfo(&devices[i]);
01451   }
01452 }
01453 
01454 int RtAudio :: getDefaultInputDevice(void)
01455 {
01456   // No ALSA API functions for default devices.
01457   return 0;
01458 }
01459 
01460 int RtAudio :: getDefaultOutputDevice(void)
01461 {
01462   // No ALSA API functions for default devices.
01463   return 0;
01464 }
01465 
01466 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
01467 {
01468   int err;
01469   int open_mode = SND_PCM_ASYNC;
01470   snd_pcm_t *handle;
01471   snd_ctl_t *chandle;
01472   snd_pcm_stream_t stream;
01473         snd_pcm_info_t *pcminfo;
01474         snd_pcm_info_alloca(&pcminfo);
01475   snd_pcm_hw_params_t *params;
01476   snd_pcm_hw_params_alloca(&params);
01477   char name[32];
01478   char *card;
01479 
01480   // Open the control interface for this card.
01481   strncpy( name, info->name, 32 );
01482   card = strtok(name, ",");
01483   err = snd_ctl_open(&chandle, card, 0);
01484   if (err < 0) {
01485     sprintf(message, "RtAudio: ALSA control open (%s): %s.", card, snd_strerror(err));
01486     error(RtError::DEBUG_WARNING);
01487     return;
01488   }
01489   unsigned int dev = (unsigned int) atoi( strtok(NULL, ",") );
01490 
01491   // First try for playback
01492   stream = SND_PCM_STREAM_PLAYBACK;
01493   snd_pcm_info_set_device(pcminfo, dev);
01494   snd_pcm_info_set_subdevice(pcminfo, 0);
01495   snd_pcm_info_set_stream(pcminfo, stream);
01496 
01497   if ((err = snd_ctl_pcm_info(chandle, pcminfo)) < 0) {
01498     if (err == -ENOENT) {
01499       sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle output!", info->name);
01500       error(RtError::DEBUG_WARNING);
01501     }
01502     else {
01503       sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) output: %s",
01504               info->name, snd_strerror(err));
01505       error(RtError::DEBUG_WARNING);
01506     }
01507     goto capture_probe;
01508   }
01509 
01510   err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK );
01511   if (err < 0) {
01512     if ( err == EBUSY )
01513       sprintf(message, "RtAudio: ALSA pcm playback device (%s) is busy: %s.",
01514               info->name, snd_strerror(err));
01515     else
01516       sprintf(message, "RtAudio: ALSA pcm playback open (%s) error: %s.",
01517               info->name, snd_strerror(err));
01518     error(RtError::DEBUG_WARNING);
01519     goto capture_probe;
01520   }
01521 
01522   // We have an open device ... allocate the parameter structure.
01523   err = snd_pcm_hw_params_any(handle, params);
01524   if (err < 0) {
01525     snd_pcm_close(handle);
01526     sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",
01527             info->name, snd_strerror(err));
01528     error(RtError::WARNING);
01529     goto capture_probe;
01530   }
01531 
01532   // Get output channel information.
01533   info->minOutputChannels = snd_pcm_hw_params_get_channels_min(params);
01534   info->maxOutputChannels = snd_pcm_hw_params_get_channels_max(params);
01535 
01536   snd_pcm_close(handle);
01537 
01538  capture_probe:
01539   // Now try for capture
01540   stream = SND_PCM_STREAM_CAPTURE;
01541   snd_pcm_info_set_stream(pcminfo, stream);
01542 
01543   err = snd_ctl_pcm_info(chandle, pcminfo);
01544   snd_ctl_close(chandle);
01545   if ( err < 0 ) {
01546     if (err == -ENOENT) {
01547       sprintf(message, "RtAudio: ALSA pcm device (%s) doesn't handle input!", info->name);
01548       error(RtError::DEBUG_WARNING);
01549     }
01550     else {
01551       sprintf(message, "RtAudio: ALSA snd_ctl_pcm_info error for device (%s) input: %s",
01552               info->name, snd_strerror(err));
01553       error(RtError::DEBUG_WARNING);
01554     }
01555     if (info->maxOutputChannels == 0)
01556       // didn't open for playback either ... device invalid
01557       return;
01558     goto probe_parameters;
01559   }
01560 
01561   err = snd_pcm_open(&handle, info->name, stream, open_mode | SND_PCM_NONBLOCK);
01562   if (err < 0) {
01563     if ( err == EBUSY )
01564       sprintf(message, "RtAudio: ALSA pcm capture device (%s) is busy: %s.",
01565               info->name, snd_strerror(err));
01566     else
01567       sprintf(message, "RtAudio: ALSA pcm capture open (%s) error: %s.",
01568               info->name, snd_strerror(err));
01569     error(RtError::DEBUG_WARNING);
01570     if (info->maxOutputChannels == 0)
01571       // didn't open for playback either ... device invalid
01572       return;
01573     goto probe_parameters;
01574   }
01575 
01576   // We have an open capture device ... allocate the parameter structure.
01577   err = snd_pcm_hw_params_any(handle, params);
01578   if (err < 0) {
01579     snd_pcm_close(handle);
01580     sprintf(message, "RtAudio: ALSA hardware probe error (%s): %s.",
01581             info->name, snd_strerror(err));
01582     error(RtError::WARNING);
01583     if (info->maxOutputChannels > 0)
01584       goto probe_parameters;
01585     else
01586       return;
01587   }
01588 
01589   // Get input channel information.
01590   info->minInputChannels = snd_pcm_hw_params_get_channels_min(params);
01591   info->maxInputChannels = snd_pcm_hw_params_get_channels_max(params);
01592 
01593   snd_pcm_close(handle);
01594 
01595   // If device opens for both playback and capture, we determine the channels.
01596   if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
01597     goto probe_parameters;
01598 
01599   info->hasDuplexSupport = true;
01600   info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
01601     info->maxInputChannels : info->maxOutputChannels;
01602   info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
01603     info->minInputChannels : info->minOutputChannels;
01604 
01605  probe_parameters:
01606   // At this point, we just need to figure out the supported data
01607   // formats and sample rates.  We'll proceed by opening the device in
01608   // the direction with the maximum number of channels, or playback if
01609   // they are equal.  This might limit our sample rate options, but so
01610   // be it.
01611 
01612   if (info->maxOutputChannels >= info->maxInputChannels)
01613     stream = SND_PCM_STREAM_PLAYBACK;
01614   else
01615     stream = SND_PCM_STREAM_CAPTURE;
01616 
01617   err = snd_pcm_open(&handle, info->name, stream, open_mode);
01618   if (err < 0) {
01619     sprintf(message, "RtAudio: ALSA pcm (%s) won't reopen during probe: %s.",
01620             info->name, snd_strerror(err));
01621     error(RtError::WARNING);
01622     return;
01623   }
01624 
01625   // We have an open device ... allocate the parameter structure.
01626   err = snd_pcm_hw_params_any(handle, params);
01627   if (err < 0) {
01628     snd_pcm_close(handle);
01629     sprintf(message, "RtAudio: ALSA hardware reopen probe error (%s): %s.",
01630             info->name, snd_strerror(err));
01631     error(RtError::WARNING);
01632     return;
01633   }
01634 
01635   // Test a non-standard sample rate to see if continuous rate is supported.
01636   int dir = 0;
01637   if (snd_pcm_hw_params_test_rate(handle, params, 35500, dir) == 0) {
01638     // It appears that continuous sample rate support is available.
01639     info->nSampleRates = -1;
01640     info->sampleRates[0] = snd_pcm_hw_params_get_rate_min(params, &dir);
01641     info->sampleRates[1] = snd_pcm_hw_params_get_rate_max(params, &dir);
01642   }
01643   else {
01644     // No continuous rate support ... test our discrete set of sample rate values.
01645     info->nSampleRates = 0;
01646     for (int i=0; i<MAX_SAMPLE_RATES; i++) {
01647       if (snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) {
01648         info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
01649         info->nSampleRates++;
01650       }
01651     }
01652     if (info->nSampleRates == 0) {
01653       snd_pcm_close(handle);
01654       return;
01655     }
01656   }
01657 
01658   // Probe the supported data formats ... we don't care about endian-ness just yet
01659   snd_pcm_format_t format;
01660   info->nativeFormats = 0;
01661   format = SND_PCM_FORMAT_S8;
01662   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01663     info->nativeFormats |= RTAUDIO_SINT8;
01664   format = SND_PCM_FORMAT_S16;
01665   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01666     info->nativeFormats |= RTAUDIO_SINT16;
01667   format = SND_PCM_FORMAT_S24;
01668   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01669     info->nativeFormats |= RTAUDIO_SINT24;
01670   format = SND_PCM_FORMAT_S32;
01671   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01672     info->nativeFormats |= RTAUDIO_SINT32;
01673   format = SND_PCM_FORMAT_FLOAT;
01674   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01675     info->nativeFormats |= RTAUDIO_FLOAT32;
01676   format = SND_PCM_FORMAT_FLOAT64;
01677   if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
01678     info->nativeFormats |= RTAUDIO_FLOAT64;
01679 
01680   // Check that we have at least one supported format
01681   if (info->nativeFormats == 0) {
01682     snd_pcm_close(handle);
01683     sprintf(message, "RtAudio: ALSA PCM device (%s) data format not supported by RtAudio.",
01684             info->name);
01685     error(RtError::WARNING);
01686     return;
01687   }
01688 
01689   // That's all ... close the device and return
01690   snd_pcm_close(handle);
01691   info->probed = true;
01692   return;
01693 }
01694 
01695 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
01696                                 STREAM_MODE mode, int channels, 
01697                                 int sampleRate, RTAUDIO_FORMAT format,
01698                                 int *bufferSize, int numberOfBuffers)
01699 {
01700 #if defined(__RTAUDIO_DEBUG__)
01701   snd_output_t *out;
01702   snd_output_stdio_attach(&out, stderr, 0);
01703 #endif
01704 
01705   // I'm not using the "plug" interface ... too much inconsistent behavior.
01706   const char *name = devices[device].name;
01707 
01708   snd_pcm_stream_t alsa_stream;
01709   if (mode == OUTPUT)
01710     alsa_stream = SND_PCM_STREAM_PLAYBACK;
01711   else
01712     alsa_stream = SND_PCM_STREAM_CAPTURE;
01713 
01714   int err;
01715   snd_pcm_t *handle;
01716   int alsa_open_mode = SND_PCM_ASYNC;
01717   err = snd_pcm_open(&handle, name, alsa_stream, alsa_open_mode);
01718   if (err < 0) {
01719     sprintf(message,"RtAudio: ALSA pcm device (%s) won't open: %s.",
01720             name, snd_strerror(err));
01721     error(RtError::WARNING);
01722     return FAILURE;
01723   }
01724 
01725   // Fill the parameter structure.
01726   snd_pcm_hw_params_t *hw_params;
01727   snd_pcm_hw_params_alloca(&hw_params);
01728   err = snd_pcm_hw_params_any(handle, hw_params);
01729   if (err < 0) {
01730     snd_pcm_close(handle);
01731     sprintf(message, "RtAudio: ALSA error getting parameter handle (%s): %s.",
01732             name, snd_strerror(err));
01733     error(RtError::WARNING);
01734     return FAILURE;
01735   }
01736 
01737 #if defined(__RTAUDIO_DEBUG__)
01738   fprintf(stderr, "\nRtAudio: ALSA dump hardware params just after device open:\n\n");
01739   snd_pcm_hw_params_dump(hw_params, out);
01740 #endif
01741 
01742 
01743   // Set access ... try interleaved access first, then non-interleaved
01744   if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) ) {
01745     err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
01746   }
01747   else if ( !snd_pcm_hw_params_test_access( handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED) ) {
01748                 err = snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
01749     stream->deInterleave[mode] = true;
01750   }
01751   else {
01752     snd_pcm_close(handle);
01753     sprintf(message, "RtAudio: ALSA device (%s) access not supported by RtAudio.", name);
01754     error(RtError::WARNING);
01755     return FAILURE;
01756   }
01757 
01758   if (err < 0) {
01759     snd_pcm_close(handle);
01760     sprintf(message, "RtAudio: ALSA error setting access ( (%s): %s.", name, snd_strerror(err));
01761     error(RtError::WARNING);
01762     return FAILURE;
01763   }
01764 
01765   // Determine how to set the device format.
01766   stream->userFormat = format;
01767   snd_pcm_format_t device_format;
01768 
01769   if (format == RTAUDIO_SINT8)
01770     device_format = SND_PCM_FORMAT_S8;
01771   else if (format == RTAUDIO_SINT16)
01772     device_format = SND_PCM_FORMAT_S16;
01773   else if (format == RTAUDIO_SINT24)
01774     device_format = SND_PCM_FORMAT_S24;
01775   else if (format == RTAUDIO_SINT32)
01776     device_format = SND_PCM_FORMAT_S32;
01777   else if (format == RTAUDIO_FLOAT32)
01778     device_format = SND_PCM_FORMAT_FLOAT;
01779   else if (format == RTAUDIO_FLOAT64)
01780     device_format = SND_PCM_FORMAT_FLOAT64;
01781 
01782   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01783     stream->deviceFormat[mode] = format;
01784     goto set_format;
01785   }
01786 
01787   // The user requested format is not natively supported by the device.
01788   device_format = SND_PCM_FORMAT_FLOAT64;
01789   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01790     stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
01791     goto set_format;
01792   }
01793 
01794   device_format = SND_PCM_FORMAT_FLOAT;
01795   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01796     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
01797     goto set_format;
01798   }
01799 
01800   device_format = SND_PCM_FORMAT_S32;
01801   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01802     stream->deviceFormat[mode] = RTAUDIO_SINT32;
01803     goto set_format;
01804   }
01805 
01806   device_format = SND_PCM_FORMAT_S24;
01807   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01808     stream->deviceFormat[mode] = RTAUDIO_SINT24;
01809     goto set_format;
01810   }
01811 
01812   device_format = SND_PCM_FORMAT_S16;
01813   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01814     stream->deviceFormat[mode] = RTAUDIO_SINT16;
01815     goto set_format;
01816   }
01817 
01818   device_format = SND_PCM_FORMAT_S8;
01819   if (snd_pcm_hw_params_test_format(handle, hw_params, device_format) == 0) {
01820     stream->deviceFormat[mode] = RTAUDIO_SINT8;
01821     goto set_format;
01822   }
01823 
01824   // If we get here, no supported format was found.
01825   sprintf(message,"RtAudio: ALSA pcm device (%s) data format not supported by RtAudio.", name);
01826   snd_pcm_close(handle);
01827   error(RtError::WARNING);
01828   return FAILURE;
01829 
01830  set_format:
01831   err = snd_pcm_hw_params_set_format(handle, hw_params, device_format);
01832   if (err < 0) {
01833     snd_pcm_close(handle);
01834     sprintf(message, "RtAudio: ALSA error setting format (%s): %s.",
01835             name, snd_strerror(err));
01836     error(RtError::WARNING);
01837     return FAILURE;
01838   }
01839 
01840   // Determine whether byte-swaping is necessary.
01841   stream->doByteSwap[mode] = false;
01842   if (device_format != SND_PCM_FORMAT_S8) {
01843     err = snd_pcm_format_cpu_endian(device_format);
01844     if (err == 0)
01845       stream->doByteSwap[mode] = true;
01846     else if (err < 0) {
01847       snd_pcm_close(handle);
01848       sprintf(message, "RtAudio: ALSA error getting format endian-ness (%s): %s.",
01849               name, snd_strerror(err));
01850       error(RtError::WARNING);
01851       return FAILURE;
01852     }
01853   }
01854 
01855   // Set the sample rate.
01856   err = snd_pcm_hw_params_set_rate(handle, hw_params, (unsigned int)sampleRate, 0);
01857   if (err < 0) {
01858     snd_pcm_close(handle);
01859     sprintf(message, "RtAudio: ALSA error setting sample rate (%d) on device (%s): %s.",
01860             sampleRate, name, snd_strerror(err));
01861     error(RtError::WARNING);
01862     return FAILURE;
01863   }
01864 
01865   // Determine the number of channels for this device.  We support a possible
01866   // minimum device channel number > than the value requested by the user.
01867   stream->nUserChannels[mode] = channels;
01868   int device_channels = snd_pcm_hw_params_get_channels_max(hw_params);
01869   if (device_channels < channels) {
01870     snd_pcm_close(handle);
01871     sprintf(message, "RtAudio: channels (%d) not supported by device (%s).",
01872             channels, name);
01873     error(RtError::WARNING);
01874     return FAILURE;
01875   }
01876 
01877   device_channels = snd_pcm_hw_params_get_channels_min(hw_params);
01878   if (device_channels < channels) device_channels = channels;
01879   stream->nDeviceChannels[mode] = device_channels;
01880 
01881   // Set the device channels.
01882   err = snd_pcm_hw_params_set_channels(handle, hw_params, device_channels);
01883   if (err < 0) {
01884     snd_pcm_close(handle);
01885     sprintf(message, "RtAudio: ALSA error setting channels (%d) on device (%s): %s.",
01886             device_channels, name, snd_strerror(err));
01887     error(RtError::WARNING);
01888     return FAILURE;
01889   }
01890 
01891   // Set the buffer number, which in ALSA is referred to as the "period".
01892   int dir;
01893   int periods = numberOfBuffers;
01894   // Even though the hardware might allow 1 buffer, it won't work reliably.
01895   if (periods < 2) periods = 2;
01896   err = snd_pcm_hw_params_get_periods_min(hw_params, &dir);
01897   if (err > periods) periods = err;
01898   err = snd_pcm_hw_params_get_periods_max(hw_params, &dir);
01899   if (err < periods) periods = err;
01900 
01901   err = snd_pcm_hw_params_set_periods(handle, hw_params, periods, 0);
01902   if (err < 0) {
01903     snd_pcm_close(handle);
01904     sprintf(message, "RtAudio: ALSA error setting periods (%s): %s.",
01905             name, snd_strerror(err));
01906     error(RtError::WARNING);
01907     return FAILURE;
01908   }
01909 
01910   // Set the buffer (or period) size.
01911   err = snd_pcm_hw_params_get_period_size_min(hw_params, &dir);
01912   if (err > *bufferSize) *bufferSize = err;
01913 
01914   err = snd_pcm_hw_params_set_period_size(handle, hw_params, *bufferSize, 0);
01915   if (err < 0) {
01916     snd_pcm_close(handle);
01917     sprintf(message, "RtAudio: ALSA error setting period size (%s): %s.",
01918             name, snd_strerror(err));
01919     error(RtError::WARNING);
01920     return FAILURE;
01921   }
01922 
01923   // If attempting to setup a duplex stream, the bufferSize parameter
01924   // MUST be the same in both directions!
01925   if ( stream->mode == OUTPUT && mode == INPUT && *bufferSize != stream->bufferSize ) {
01926     sprintf( message, "RtAudio: ALSA error setting buffer size for duplex stream on device (%s).",
01927              name );
01928     error(RtError::DEBUG_WARNING);
01929     return FAILURE;
01930   }
01931 
01932   stream->bufferSize = *bufferSize;
01933 
01934   // Install the hardware configuration
01935   err = snd_pcm_hw_params(handle, hw_params);
01936   if (err < 0) {
01937     snd_pcm_close(handle);
01938     sprintf(message, "RtAudio: ALSA error installing hardware configuration (%s): %s.",
01939             name, snd_strerror(err));
01940     error(RtError::WARNING);
01941     return FAILURE;
01942   }
01943 
01944 #if defined(__RTAUDIO_DEBUG__)
01945   fprintf(stderr, "\nRtAudio: ALSA dump hardware params after installation:\n\n");
01946   snd_pcm_hw_params_dump(hw_params, out);
01947 #endif
01948 
01949   /*
01950   // Install the software configuration
01951   snd_pcm_sw_params_t *sw_params = NULL;
01952   snd_pcm_sw_params_alloca(&sw_params);
01953   snd_pcm_sw_params_current(handle, sw_params);
01954   err = snd_pcm_sw_params(handle, sw_params);
01955   if (err < 0) {
01956     snd_pcm_close(handle);
01957     sprintf(message, "RtAudio: ALSA error installing software configuration (%s): %s.",
01958             name, snd_strerror(err));
01959     error(RtError::WARNING);
01960     return FAILURE;
01961   }
01962   */
01963 
01964   // Set handle and flags for buffer conversion
01965   stream->handle[mode] = handle;
01966   stream->doConvertBuffer[mode] = false;
01967   if (stream->userFormat != stream->deviceFormat[mode])
01968     stream->doConvertBuffer[mode] = true;
01969   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
01970     stream->doConvertBuffer[mode] = true;
01971   if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
01972     stream->doConvertBuffer[mode] = true;
01973 
01974   // Allocate necessary internal buffers
01975   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
01976 
01977     long buffer_bytes;
01978     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
01979       buffer_bytes = stream->nUserChannels[0];
01980     else
01981       buffer_bytes = stream->nUserChannels[1];
01982 
01983     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
01984     if (stream->userBuffer) free(stream->userBuffer);
01985     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
01986     if (stream->userBuffer == NULL)
01987       goto memory_error;
01988   }
01989 
01990   if ( stream->doConvertBuffer[mode] ) {
01991 
01992     long buffer_bytes;
01993     bool makeBuffer = true;
01994     if ( mode == OUTPUT )
01995       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
01996     else { // mode == INPUT
01997       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
01998       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
01999         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
02000         if ( buffer_bytes < bytes_out ) makeBuffer = false;
02001       }
02002     }
02003 
02004     if ( makeBuffer ) {
02005       buffer_bytes *= *bufferSize;
02006       if (stream->deviceBuffer) free(stream->deviceBuffer);
02007       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
02008       if (stream->deviceBuffer == NULL)
02009         goto memory_error;
02010     }
02011   }
02012 
02013   stream->device[mode] = device;
02014   stream->state = STREAM_STOPPED;
02015   if ( stream->mode == OUTPUT && mode == INPUT )
02016     // We had already set up an output stream.
02017     stream->mode = DUPLEX;
02018   else
02019     stream->mode = mode;
02020   stream->nBuffers = periods;
02021   stream->sampleRate = sampleRate;
02022 
02023   return SUCCESS;
02024 
02025  memory_error:
02026   if (stream->handle[0]) {
02027     snd_pcm_close(stream->handle[0]);
02028     stream->handle[0] = 0;
02029   }
02030   if (stream->handle[1]) {
02031     snd_pcm_close(stream->handle[1]);
02032     stream->handle[1] = 0;
02033   }
02034   if (stream->userBuffer) {
02035     free(stream->userBuffer);
02036     stream->userBuffer = 0;
02037   }
02038   sprintf(message, "RtAudio: ALSA error allocating buffer memory (%s).", name);
02039   error(RtError::WARNING);
02040   return FAILURE;
02041 }
02042 
02043 void RtAudio :: closeStream(int streamId)
02044 {
02045   // We don't want an exception to be thrown here because this
02046   // function is called by our class destructor.  So, do our own
02047   // streamId check.
02048   if ( streams.find( streamId ) == streams.end() ) {
02049     sprintf(message, "RtAudio: invalid stream identifier!");
02050     error(RtError::WARNING);
02051     return;
02052   }
02053 
02054   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
02055 
02056   if (stream->callbackInfo.usingCallback) {
02057     pthread_cancel(stream->callbackInfo.thread);
02058     pthread_join(stream->callbackInfo.thread, NULL);
02059   }
02060 
02061   if (stream->state == STREAM_RUNNING) {
02062     if (stream->mode == OUTPUT || stream->mode == DUPLEX)
02063       snd_pcm_drop(stream->handle[0]);
02064     if (stream->mode == INPUT || stream->mode == DUPLEX)
02065       snd_pcm_drop(stream->handle[1]);
02066   }
02067 
02068   pthread_mutex_destroy(&stream->mutex);
02069 
02070   if (stream->handle[0])
02071     snd_pcm_close(stream->handle[0]);
02072 
02073   if (stream->handle[1])
02074     snd_pcm_close(stream->handle[1]);
02075 
02076   if (stream->userBuffer)
02077     free(stream->userBuffer);
02078 
02079   if (stream->deviceBuffer)
02080     free(stream->deviceBuffer);
02081 
02082   free(stream);
02083   streams.erase(streamId);
02084 }
02085 
02086 void RtAudio :: startStream(int streamId)
02087 {
02088   // This method calls snd_pcm_prepare if the device isn't already in that state.
02089 
02090   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
02091 
02092   MUTEX_LOCK(&stream->mutex);
02093 
02094   if (stream->state == STREAM_RUNNING)
02095     goto unlock;
02096 
02097   int err;
02098   snd_pcm_state_t state;
02099   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
02100     state = snd_pcm_state(stream->handle[0]);
02101     if (state != SND_PCM_STATE_PREPARED) {
02102       err = snd_pcm_prepare(stream->handle[0]);
02103       if (err < 0) {
02104         sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",
02105                 devices[stream->device[0]].name, snd_strerror(err));
02106         MUTEX_UNLOCK(&stream->mutex);
02107         error(RtError::DRIVER_ERROR);
02108       }
02109     }
02110   }
02111 
02112   if (stream->mode == INPUT || stream->mode == DUPLEX) {
02113     state = snd_pcm_state(stream->handle[1]);
02114     if (state != SND_PCM_STATE_PREPARED) {
02115       err = snd_pcm_prepare(stream->handle[1]);
02116       if (err < 0) {
02117         sprintf(message, "RtAudio: ALSA error preparing pcm device (%s): %s.",
02118                 devices[stream->device[1]].name, snd_strerror(err));
02119         MUTEX_UNLOCK(&stream->mutex);
02120         error(RtError::DRIVER_ERROR);
02121       }
02122     }
02123   }
02124   stream->state = STREAM_RUNNING;
02125 
02126  unlock:
02127   MUTEX_UNLOCK(&stream->mutex);
02128 }
02129 
02130 void RtAudio :: stopStream(int streamId)
02131 {
02132   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
02133 
02134   MUTEX_LOCK(&stream->mutex);
02135 
02136   if (stream->state == STREAM_STOPPED)
02137     goto unlock;
02138 
02139   int err;
02140   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
02141     err = snd_pcm_drain(stream->handle[0]);
02142     if (err < 0) {
02143       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
02144               devices[stream->device[0]].name, snd_strerror(err));
02145       MUTEX_UNLOCK(&stream->mutex);
02146       error(RtError::DRIVER_ERROR);
02147     }
02148   }
02149 
02150   if (stream->mode == INPUT || stream->mode == DUPLEX) {
02151     err = snd_pcm_drain(stream->handle[1]);
02152     if (err < 0) {
02153       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
02154               devices[stream->device[1]].name, snd_strerror(err));
02155       MUTEX_UNLOCK(&stream->mutex);
02156       error(RtError::DRIVER_ERROR);
02157     }
02158   }
02159   stream->state = STREAM_STOPPED;
02160 
02161  unlock:
02162   MUTEX_UNLOCK(&stream->mutex);
02163 }
02164 
02165 void RtAudio :: abortStream(int streamId)
02166 {
02167   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
02168 
02169   MUTEX_LOCK(&stream->mutex);
02170 
02171   if (stream->state == STREAM_STOPPED)
02172     goto unlock;
02173 
02174   int err;
02175   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
02176     err = snd_pcm_drop(stream->handle[0]);
02177     if (err < 0) {
02178       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
02179               devices[stream->device[0]].name, snd_strerror(err));
02180       MUTEX_UNLOCK(&stream->mutex);
02181       error(RtError::DRIVER_ERROR);
02182     }
02183   }
02184 
02185   if (stream->mode == INPUT || stream->mode == DUPLEX) {
02186     err = snd_pcm_drop(stream->handle[1]);
02187     if (err < 0) {
02188       sprintf(message, "RtAudio: ALSA error draining pcm device (%s): %s.",
02189               devices[stream->device[1]].name, snd_strerror(err));
02190       MUTEX_UNLOCK(&stream->mutex);
02191       error(RtError::DRIVER_ERROR);
02192     }
02193   }
02194   stream->state = STREAM_STOPPED;
02195 
02196  unlock:
02197   MUTEX_UNLOCK(&stream->mutex);
02198 }
02199 
02200 int RtAudio :: streamWillBlock(int streamId)
02201 {
02202   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
02203 
02204   MUTEX_LOCK(&stream->mutex);
02205 
02206   int err = 0, frames = 0;
02207   if (stream->state == STREAM_STOPPED)
02208     goto unlock;
02209 
02210   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
02211     err = snd_pcm_avail_update(stream->handle[0]);
02212     if (err < 0) {
02213       sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",
02214               devices[stream->device[0]].name, snd_strerror(err));
02215       MUTEX_UNLOCK(&stream->mutex);
02216       error(RtError::DRIVER_ERROR);
02217     }
02218   }
02219 
02220   frames = err;
02221 
02222   if (stream->mode == INPUT || stream->mode == DUPLEX) {
02223     err = snd_pcm_avail_update(stream->handle[1]);
02224     if (err < 0) {
02225       sprintf(message, "RtAudio: ALSA error getting available frames for device (%s): %s.",
02226               devices[stream->device[1]].name, snd_strerror(err));
02227       MUTEX_UNLOCK(&stream->mutex);
02228       error(RtError::DRIVER_ERROR);
02229     }
02230     if (frames > err) frames = err;
02231   }
02232 
02233   frames = stream->bufferSize - frames;
02234   if (frames < 0) frames = 0;
02235 
02236  unlock:
02237   MUTEX_UNLOCK(&stream->mutex);
02238   return frames;
02239 }
02240 
02241 void RtAudio :: tickStream(int streamId)
02242 {
02243   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
02244 
02245   int stopStream = 0;
02246   if (stream->state == STREAM_STOPPED) {
02247     if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
02248     return;
02249   }
02250   else if (stream->callbackInfo.usingCallback) {
02251     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
02252     stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
02253   }
02254 
02255   MUTEX_LOCK(&stream->mutex);
02256 
02257   // The state might change while waiting on a mutex.
02258   if (stream->state == STREAM_STOPPED)
02259     goto unlock;
02260 
02261   int err;
02262   char *buffer;
02263   int channels;
02264   RTAUDIO_FORMAT format;
02265   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
02266 
02267     // Setup parameters and do buffer conversion if necessary.
02268     if (stream->doConvertBuffer[0]) {
02269       convertStreamBuffer(stream, OUTPUT);
02270       buffer = stream->deviceBuffer;
02271       channels = stream->nDeviceChannels[0];
02272       format = stream->deviceFormat[0];
02273     }
02274     else {
02275       buffer = stream->userBuffer;
02276       channels = stream->nUserChannels[0];
02277       format = stream->userFormat;
02278     }
02279 
02280     // Do byte swapping if necessary.
02281     if (stream->doByteSwap[0])
02282       byteSwapBuffer(buffer, stream->bufferSize * channels, format);
02283 
02284     // Write samples to device in interleaved/non-interleaved format.
02285     if (stream->deInterleave[0]) {
02286       void *bufs[channels];
02287       size_t offset = stream->bufferSize * formatBytes(format);
02288       for (int i=0; i<channels; i++)
02289         bufs[i] = (void *) (buffer + (i * offset));
02290       err = snd_pcm_writen(stream->handle[0], bufs, stream->bufferSize);
02291     }
02292     else
02293       err = snd_pcm_writei(stream->handle[0], buffer, stream->bufferSize);
02294 
02295     if (err < stream->bufferSize) {
02296       // Either an error or underrun occured.
02297       if (err == -EPIPE) {
02298         snd_pcm_state_t state = snd_pcm_state(stream->handle[0]);
02299         if (state == SND_PCM_STATE_XRUN) {
02300           sprintf(message, "RtAudio: ALSA underrun detected.");
02301           error(RtError::WARNING);
02302           err = snd_pcm_prepare(stream->handle[0]);
02303           if (err < 0) {
02304             sprintf(message, "RtAudio: ALSA error preparing handle after underrun: %s.",
02305                     snd_strerror(err));
02306             MUTEX_UNLOCK(&stream->mutex);
02307             error(RtError::DRIVER_ERROR);
02308           }
02309         }
02310         else {
02311           sprintf(message, "RtAudio: ALSA error, current state is %s.",
02312                   snd_pcm_state_name(state));
02313           MUTEX_UNLOCK(&stream->mutex);
02314           error(RtError::DRIVER_ERROR);
02315         }
02316         goto unlock;
02317       }
02318       else {
02319         sprintf(message, "RtAudio: ALSA audio write error for device (%s): %s.",
02320                 devices[stream->device[0]].name, snd_strerror(err));
02321         MUTEX_UNLOCK(&stream->mutex);
02322         error(RtError::DRIVER_ERROR);
02323       }
02324     }
02325   }
02326 
02327   if (stream->mode == INPUT || stream->mode == DUPLEX) {
02328 
02329     // Setup parameters.
02330     if (stream->doConvertBuffer[1]) {
02331       buffer = stream->deviceBuffer;
02332       channels = stream->nDeviceChannels[1];
02333       format = stream->deviceFormat[1];
02334     }
02335     else {
02336       buffer = stream->userBuffer;
02337       channels = stream->nUserChannels[1];
02338       format = stream->userFormat;
02339     }
02340 
02341     // Read samples from device in interleaved/non-interleaved format.
02342     if (stream->deInterleave[1]) {
02343       void *bufs[channels];
02344       size_t offset = stream->bufferSize * formatBytes(format);
02345       for (int i=0; i<channels; i++)
02346         bufs[i] = (void *) (buffer + (i * offset));
02347       err = snd_pcm_readn(stream->handle[1], bufs, stream->bufferSize);
02348     }
02349     else
02350       err = snd_pcm_readi(stream->handle[1], buffer, stream->bufferSize);
02351 
02352     if (err < stream->bufferSize) {
02353       // Either an error or underrun occured.
02354       if (err == -EPIPE) {
02355         snd_pcm_state_t state = snd_pcm_state(stream->handle[1]);
02356         if (state == SND_PCM_STATE_XRUN) {
02357           sprintf(message, "RtAudio: ALSA overrun detected.");
02358           error(RtError::WARNING);
02359           err = snd_pcm_prepare(stream->handle[1]);
02360           if (err < 0) {
02361             sprintf(message, "RtAudio: ALSA error preparing handle after overrun: %s.",
02362                     snd_strerror(err));
02363             MUTEX_UNLOCK(&stream->mutex);
02364             error(RtError::DRIVER_ERROR);
02365           }
02366         }
02367         else {
02368           sprintf(message, "RtAudio: ALSA error, current state is %s.",
02369                   snd_pcm_state_name(state));
02370           MUTEX_UNLOCK(&stream->mutex);
02371           error(RtError::DRIVER_ERROR);
02372         }
02373         goto unlock;
02374       }
02375       else {
02376         sprintf(message, "RtAudio: ALSA audio read error for device (%s): %s.",
02377                 devices[stream->device[1]].name, snd_strerror(err));
02378         MUTEX_UNLOCK(&stream->mutex);
02379         error(RtError::DRIVER_ERROR);
02380       }
02381     }
02382 
02383     // Do byte swapping if necessary.
02384     if (stream->doByteSwap[1])
02385       byteSwapBuffer(buffer, stream->bufferSize * channels, format);
02386 
02387     // Do buffer conversion if necessary.
02388     if (stream->doConvertBuffer[1])
02389       convertStreamBuffer(stream, INPUT);
02390   }
02391 
02392  unlock:
02393   MUTEX_UNLOCK(&stream->mutex);
02394 
02395   if (stream->callbackInfo.usingCallback && stopStream)
02396     this->stopStream(streamId);
02397 }
02398 
02399 extern "C" void *callbackHandler(void *ptr)
02400 {
02401   CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
02402   RtAudio *object = (RtAudio *) info->object;
02403   int stream = info->streamId;
02404   bool *usingCallback = &info->usingCallback;
02405 
02406   while ( *usingCallback ) {
02407     pthread_testcancel();
02408     try {
02409       object->tickStream(stream);
02410     }
02411     catch (RtError &exception) {
02412       fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
02413               exception.getMessage());
02414       break;
02415     }
02416   }
02417 
02418   return 0;
02419 }
02420 
02421 //******************** End of __LINUX_ALSA__ *********************//
02422 
02423 #elif defined(__LINUX_OSS__)
02424 
02425 #include <sys/stat.h>
02426 #include <sys/types.h>
02427 #include <sys/ioctl.h>
02428 #include <unistd.h>
02429 #include <fcntl.h>
02430 #include <sys/soundcard.h>
02431 #include <errno.h>
02432 #include <math.h>
02433 
02434 #define DAC_NAME "/dev/dsp"
02435 #define MAX_DEVICES 16
02436 #define MAX_CHANNELS 16
02437 
02438 void RtAudio :: initialize(void)
02439 {
02440   // Count cards and devices
02441   nDevices = 0;
02442 
02443   // We check /dev/dsp before probing devices.  /dev/dsp is supposed to
02444   // be a link to the "default" audio device, of the form /dev/dsp0,
02445   // /dev/dsp1, etc...  However, I've seen many cases where /dev/dsp was a
02446   // real device, so we need to check for that.  Also, sometimes the
02447   // link is to /dev/dspx and other times just dspx.  I'm not sure how
02448   // the latter works, but it does.
02449   char device_name[16];
02450   struct stat dspstat;
02451   int dsplink = -1;
02452   int i = 0;
02453   if (lstat(DAC_NAME, &dspstat) == 0) {
02454     if (S_ISLNK(dspstat.st_mode)) {
02455       i = readlink(DAC_NAME, device_name, sizeof(device_name));
02456       if (i > 0) {
02457         device_name[i] = '\0';
02458         if (i > 8) { // check for "/dev/dspx"
02459           if (!strncmp(DAC_NAME, device_name, 8))
02460             dsplink = atoi(&device_name[8]);
02461         }
02462         else if (i > 3) { // check for "dspx"
02463           if (!strncmp("dsp", device_name, 3))
02464             dsplink = atoi(&device_name[3]);
02465         }
02466       }
02467       else {
02468         sprintf(message, "RtAudio: cannot read value of symbolic link %s.", DAC_NAME);
02469         error(RtError::SYSTEM_ERROR);
02470       }
02471     }
02472   }
02473   else {
02474     sprintf(message, "RtAudio: cannot stat %s.", DAC_NAME);
02475     error(RtError::SYSTEM_ERROR);
02476   }
02477 
02478   // The OSS API doesn't provide a routine for determining the number
02479   // of devices.  Thus, we'll just pursue a brute force method.  The
02480   // idea is to start with /dev/dsp(0) and continue with higher device
02481   // numbers until we reach MAX_DSP_DEVICES.  This should tell us how
02482   // many devices we have ... it is not a fullproof scheme, but hopefully
02483   // it will work most of the time.
02484 
02485   int fd = 0;
02486   char names[MAX_DEVICES][16];
02487   for (i=-1; i<MAX_DEVICES; i++) {
02488 
02489     // Probe /dev/dsp first, since it is supposed to be the default device.
02490     if (i == -1)
02491       sprintf(device_name, "%s", DAC_NAME);
02492     else if (i == dsplink)
02493       continue; // We've aready probed this device via /dev/dsp link ... try next device.
02494     else
02495       sprintf(device_name, "%s%d", DAC_NAME, i);
02496 
02497     // First try to open the device for playback, then record mode.
02498     fd = open(device_name, O_WRONLY | O_NONBLOCK);
02499     if (fd == -1) {
02500       // Open device for playback failed ... either busy or doesn't exist.
02501       if (errno != EBUSY && errno != EAGAIN) {
02502         // Try to open for capture
02503         fd = open(device_name, O_RDONLY | O_NONBLOCK);
02504         if (fd == -1) {
02505           // Open device for record failed.
02506           if (errno != EBUSY && errno != EAGAIN)
02507             continue;
02508           else {
02509             sprintf(message, "RtAudio: OSS record device (%s) is busy.", device_name);
02510             error(RtError::WARNING);
02511             // still count it for now
02512           }
02513         }
02514       }
02515       else {
02516         sprintf(message, "RtAudio: OSS playback device (%s) is busy.", device_name);
02517         error(RtError::WARNING);
02518         // still count it for now
02519       }
02520     }
02521 
02522     if (fd >= 0) close(fd);
02523     strncpy(names[nDevices], device_name, 16);
02524     nDevices++;
02525   }
02526 
02527   if (nDevices == 0) return;
02528 
02529   //  Allocate the RTAUDIO_DEVICE structures.
02530   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
02531   if (devices == NULL) {
02532     sprintf(message, "RtAudio: memory allocation error!");
02533     error(RtError::MEMORY_ERROR);
02534   }
02535 
02536   // Write device ascii identifiers to device control structure and then probe capabilities.
02537   for (i=0; i<nDevices; i++) {
02538     strncpy(devices[i].name, names[i], 16);
02539     //probeDeviceInfo(&devices[i]);
02540   }
02541 
02542   return;
02543 }
02544 
02545 int RtAudio :: getDefaultInputDevice(void)
02546 {
02547   // No OSS API functions for default devices.
02548   return 0;
02549 }
02550 
02551 int RtAudio :: getDefaultOutputDevice(void)
02552 {
02553   // No OSS API functions for default devices.
02554   return 0;
02555 }
02556 
02557 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
02558 {
02559   int i, fd, channels, mask;
02560 
02561   // The OSS API doesn't provide a means for probing the capabilities
02562   // of devices.  Thus, we'll just pursue a brute force method.
02563 
02564   // First try for playback
02565   fd = open(info->name, O_WRONLY | O_NONBLOCK);
02566   if (fd == -1) {
02567     // Open device failed ... either busy or doesn't exist
02568     if (errno == EBUSY || errno == EAGAIN)
02569       sprintf(message, "RtAudio: OSS playback device (%s) is busy and cannot be probed.",
02570               info->name);
02571     else
02572       sprintf(message, "RtAudio: OSS playback device (%s) open error.", info->name);
02573     error(RtError::DEBUG_WARNING);
02574     goto capture_probe;
02575   }
02576 
02577   // We have an open device ... see how many channels it can handle
02578   for (i=MAX_CHANNELS; i>0; i--) {
02579     channels = i;
02580     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) {
02581       // This would normally indicate some sort of hardware error, but under ALSA's
02582       // OSS emulation, it sometimes indicates an invalid channel value.  Further,
02583       // the returned channel value is not changed. So, we'll ignore the possible
02584       // hardware error.
02585       continue; // try next channel number
02586     }
02587     // Check to see whether the device supports the requested number of channels
02588     if (channels != i ) continue; // try next channel number
02589     // If here, we found the largest working channel value
02590     break;
02591   }
02592   info->maxOutputChannels = i;
02593 
02594   // Now find the minimum number of channels it can handle
02595   for (i=1; i<=info->maxOutputChannels; i++) {
02596     channels = i;
02597     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
02598       continue; // try next channel number
02599     // If here, we found the smallest working channel value
02600     break;
02601   }
02602   info->minOutputChannels = i;
02603   close(fd);
02604 
02605  capture_probe:
02606   // Now try for capture
02607   fd = open(info->name, O_RDONLY | O_NONBLOCK);
02608   if (fd == -1) {
02609     // Open device for capture failed ... either busy or doesn't exist
02610     if (errno == EBUSY || errno == EAGAIN)
02611       sprintf(message, "RtAudio: OSS capture device (%s) is busy and cannot be probed.",
02612               info->name);
02613     else
02614       sprintf(message, "RtAudio: OSS capture device (%s) open error.", info->name);
02615     error(RtError::DEBUG_WARNING);
02616     if (info->maxOutputChannels == 0)
02617       // didn't open for playback either ... device invalid
02618       return;
02619     goto probe_parameters;
02620   }
02621 
02622   // We have the device open for capture ... see how many channels it can handle
02623   for (i=MAX_CHANNELS; i>0; i--) {
02624     channels = i;
02625     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
02626       continue; // as above
02627     }
02628     // If here, we found a working channel value
02629     break;
02630   }
02631   info->maxInputChannels = i;
02632 
02633   // Now find the minimum number of channels it can handle
02634   for (i=1; i<=info->maxInputChannels; i++) {
02635     channels = i;
02636     if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
02637       continue; // try next channel number
02638     // If here, we found the smallest working channel value
02639     break;
02640   }
02641   info->minInputChannels = i;
02642   close(fd);
02643 
02644   if (info->maxOutputChannels == 0 && info->maxInputChannels == 0) {
02645     sprintf(message, "RtAudio: OSS device (%s) reports zero channels for input and output.",
02646             info->name);
02647     error(RtError::DEBUG_WARNING);
02648     return;
02649   }
02650 
02651   // If device opens for both playback and capture, we determine the channels.
02652   if (info->maxOutputChannels == 0 || info->maxInputChannels == 0)
02653     goto probe_parameters;
02654 
02655   fd = open(info->name, O_RDWR | O_NONBLOCK);
02656   if (fd == -1)
02657     goto probe_parameters;
02658 
02659   ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
02660   ioctl(fd, SNDCTL_DSP_GETCAPS, &mask);
02661   if (mask & DSP_CAP_DUPLEX) {
02662     info->hasDuplexSupport = true;
02663     // We have the device open for duplex ... see how many channels it can handle
02664     for (i=MAX_CHANNELS; i>0; i--) {
02665       channels = i;
02666       if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
02667         continue; // as above
02668       // If here, we found a working channel value
02669       break;
02670     }
02671     info->maxDuplexChannels = i;
02672 
02673     // Now find the minimum number of channels it can handle
02674     for (i=1; i<=info->maxDuplexChannels; i++) {
02675       channels = i;
02676       if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i)
02677         continue; // try next channel number
02678       // If here, we found the smallest working channel value
02679       break;
02680     }
02681     info->minDuplexChannels = i;
02682   }
02683   close(fd);
02684 
02685  probe_parameters:
02686   // At this point, we need to figure out the supported data formats
02687   // and sample rates.  We'll proceed by openning the device in the
02688   // direction with the maximum number of channels, or playback if
02689   // they are equal.  This might limit our sample rate options, but so
02690   // be it.
02691 
02692   if (info->maxOutputChannels >= info->maxInputChannels) {
02693     fd = open(info->name, O_WRONLY | O_NONBLOCK);
02694     channels = info->maxOutputChannels;
02695   }
02696   else {
02697     fd = open(info->name, O_RDONLY | O_NONBLOCK);
02698     channels = info->maxInputChannels;
02699   }
02700 
02701   if (fd == -1) {
02702     // We've got some sort of conflict ... abort
02703     sprintf(message, "RtAudio: OSS device (%s) won't reopen during probe.",
02704             info->name);
02705     error(RtError::DEBUG_WARNING);
02706     return;
02707   }
02708 
02709   // We have an open device ... set to maximum channels.
02710   i = channels;
02711   if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1 || channels != i) {
02712     // We've got some sort of conflict ... abort
02713     close(fd);
02714     sprintf(message, "RtAudio: OSS device (%s) won't revert to previous channel setting.",
02715             info->name);
02716     error(RtError::DEBUG_WARNING);
02717     return;
02718   }
02719 
02720   if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
02721     close(fd);
02722     sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",
02723             info->name);
02724     error(RtError::DEBUG_WARNING);
02725     return;
02726   }
02727 
02728   // Probe the supported data formats ... we don't care about endian-ness just yet.
02729   int format;
02730   info->nativeFormats = 0;
02731 #if defined (AFMT_S32_BE)
02732   // This format does not seem to be in the 2.4 kernel version of OSS soundcard.h
02733   if (mask & AFMT_S32_BE) {
02734     format = AFMT_S32_BE;
02735     info->nativeFormats |= RTAUDIO_SINT32;
02736   }
02737 #endif
02738 #if defined (AFMT_S32_LE)
02739   /* This format is not in the 2.4.4 kernel version of OSS soundcard.h */
02740   if (mask & AFMT_S32_LE) {
02741     format = AFMT_S32_LE;
02742     info->nativeFormats |= RTAUDIO_SINT32;
02743   }
02744 #endif
02745   if (mask & AFMT_S8) {
02746     format = AFMT_S8;
02747     info->nativeFormats |= RTAUDIO_SINT8;
02748   }
02749   if (mask & AFMT_S16_BE) {
02750     format = AFMT_S16_BE;
02751     info->nativeFormats |= RTAUDIO_SINT16;
02752   }
02753   if (mask & AFMT_S16_LE) {
02754     format = AFMT_S16_LE;
02755     info->nativeFormats |= RTAUDIO_SINT16;
02756   }
02757 
02758   // Check that we have at least one supported format
02759   if (info->nativeFormats == 0) {
02760     close(fd);
02761     sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",
02762             info->name);
02763     error(RtError::DEBUG_WARNING);
02764     return;
02765   }
02766 
02767   // Set the format
02768   i = format;
02769   if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1 || format != i) {
02770     close(fd);
02771     sprintf(message, "RtAudio: OSS device (%s) error setting data format.",
02772             info->name);
02773     error(RtError::DEBUG_WARNING);
02774     return;
02775   }
02776 
02777   // Probe the supported sample rates ... first get lower limit
02778   int speed = 1;
02779   if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
02780     // If we get here, we're probably using an ALSA driver with OSS-emulation,
02781     // which doesn't conform to the OSS specification.  In this case,
02782     // we'll probe our predefined list of sample rates for working values.
02783     info->nSampleRates = 0;
02784     for (i=0; i<MAX_SAMPLE_RATES; i++) {
02785       speed = SAMPLE_RATES[i];
02786       if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) != -1) {
02787         info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
02788         info->nSampleRates++;
02789       }
02790     }
02791     if (info->nSampleRates == 0) {
02792       close(fd);
02793       return;
02794     }
02795     goto finished;
02796   }
02797   info->sampleRates[0] = speed;
02798 
02799   // Now get upper limit
02800   speed = 1000000;
02801   if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) == -1) {
02802     close(fd);
02803     sprintf(message, "RtAudio: OSS device (%s) error setting sample rate.",
02804             info->name);
02805     error(RtError::DEBUG_WARNING);
02806     return;
02807   }
02808   info->sampleRates[1] = speed;
02809   info->nSampleRates = -1;
02810 
02811  finished: // That's all ... close the device and return
02812   close(fd);
02813   info->probed = true;
02814   return;
02815 }
02816 
02817 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
02818                                 STREAM_MODE mode, int channels, 
02819                                 int sampleRate, RTAUDIO_FORMAT format,
02820                                 int *bufferSize, int numberOfBuffers)
02821 {
02822   int buffers, buffer_bytes, device_channels, device_format;
02823   int srate, temp, fd;
02824 
02825   const char *name = devices[device].name;
02826 
02827   if (mode == OUTPUT)
02828     fd = open(name, O_WRONLY | O_NONBLOCK);
02829   else { // mode == INPUT
02830     if (stream->mode == OUTPUT && stream->device[0] == device) {
02831       // We just set the same device for playback ... close and reopen for duplex (OSS only).
02832       close(stream->handle[0]);
02833       stream->handle[0] = 0;
02834       // First check that the number previously set channels is the same.
02835       if (stream->nUserChannels[0] != channels) {
02836         sprintf(message, "RtAudio: input/output channels must be equal for OSS duplex device (%s).", name);
02837         goto error;
02838       }
02839       fd = open(name, O_RDWR | O_NONBLOCK);
02840     }
02841     else
02842       fd = open(name, O_RDONLY | O_NONBLOCK);
02843   }
02844 
02845   if (fd == -1) {
02846     if (errno == EBUSY || errno == EAGAIN)
02847       sprintf(message, "RtAudio: OSS device (%s) is busy and cannot be opened.",
02848               name);
02849     else
02850       sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
02851     goto error;
02852   }
02853 
02854   // Now reopen in blocking mode.
02855   close(fd);
02856   if (mode == OUTPUT)
02857     fd = open(name, O_WRONLY | O_SYNC);
02858   else { // mode == INPUT
02859     if (stream->mode == OUTPUT && stream->device[0] == device)
02860       fd = open(name, O_RDWR | O_SYNC);
02861     else
02862       fd = open(name, O_RDONLY | O_SYNC);
02863   }
02864 
02865   if (fd == -1) {
02866     sprintf(message, "RtAudio: OSS device (%s) cannot be opened.", name);
02867     goto error;
02868   }
02869 
02870   // Get the sample format mask
02871   int mask;
02872   if (ioctl(fd, SNDCTL_DSP_GETFMTS, &mask) == -1) {
02873     close(fd);
02874     sprintf(message, "RtAudio: OSS device (%s) can't get supported audio formats.",
02875             name);
02876     goto error;
02877   }
02878 
02879   // Determine how to set the device format.
02880   stream->userFormat = format;
02881   device_format = -1;
02882   stream->doByteSwap[mode] = false;
02883   if (format == RTAUDIO_SINT8) {
02884     if (mask & AFMT_S8) {
02885       device_format = AFMT_S8;
02886       stream->deviceFormat[mode] = RTAUDIO_SINT8;
02887     }
02888   }
02889   else if (format == RTAUDIO_SINT16) {
02890     if (mask & AFMT_S16_NE) {
02891       device_format = AFMT_S16_NE;
02892       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02893     }
02894 #if BYTE_ORDER == LITTLE_ENDIAN
02895     else if (mask & AFMT_S16_BE) {
02896       device_format = AFMT_S16_BE;
02897       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02898       stream->doByteSwap[mode] = true;
02899     }
02900 #else
02901     else if (mask & AFMT_S16_LE) {
02902       device_format = AFMT_S16_LE;
02903       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02904       stream->doByteSwap[mode] = true;
02905     }
02906 #endif
02907   }
02908 #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
02909   else if (format == RTAUDIO_SINT32) {
02910     if (mask & AFMT_S32_NE) {
02911       device_format = AFMT_S32_NE;
02912       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02913     }
02914 #if BYTE_ORDER == LITTLE_ENDIAN
02915     else if (mask & AFMT_S32_BE) {
02916       device_format = AFMT_S32_BE;
02917       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02918       stream->doByteSwap[mode] = true;
02919     }
02920 #else
02921     else if (mask & AFMT_S32_LE) {
02922       device_format = AFMT_S32_LE;
02923       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02924       stream->doByteSwap[mode] = true;
02925     }
02926 #endif
02927   }
02928 #endif
02929 
02930   if (device_format == -1) {
02931     // The user requested format is not natively supported by the device.
02932     if (mask & AFMT_S16_NE) {
02933       device_format = AFMT_S16_NE;
02934       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02935     }
02936 #if BYTE_ORDER == LITTLE_ENDIAN
02937     else if (mask & AFMT_S16_BE) {
02938       device_format = AFMT_S16_BE;
02939       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02940       stream->doByteSwap[mode] = true;
02941     }
02942 #else
02943     else if (mask & AFMT_S16_LE) {
02944       device_format = AFMT_S16_LE;
02945       stream->deviceFormat[mode] = RTAUDIO_SINT16;
02946       stream->doByteSwap[mode] = true;
02947     }
02948 #endif
02949 #if defined (AFMT_S32_NE) && defined (AFMT_S32_LE) && defined (AFMT_S32_BE)
02950     else if (mask & AFMT_S32_NE) {
02951       device_format = AFMT_S32_NE;
02952       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02953     }
02954 #if BYTE_ORDER == LITTLE_ENDIAN
02955     else if (mask & AFMT_S32_BE) {
02956       device_format = AFMT_S32_BE;
02957       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02958       stream->doByteSwap[mode] = true;
02959     }
02960 #else
02961     else if (mask & AFMT_S32_LE) {
02962       device_format = AFMT_S32_LE;
02963       stream->deviceFormat[mode] = RTAUDIO_SINT32;
02964       stream->doByteSwap[mode] = true;
02965     }
02966 #endif
02967 #endif
02968     else if (mask & AFMT_S8) {
02969       device_format = AFMT_S8;
02970       stream->deviceFormat[mode] = RTAUDIO_SINT8;
02971     }
02972   }
02973 
02974   if (stream->deviceFormat[mode] == 0) {
02975     // This really shouldn't happen ...
02976     close(fd);
02977     sprintf(message, "RtAudio: OSS device (%s) data format not supported by RtAudio.",
02978             name);
02979     goto error;
02980   }
02981 
02982   // Determine the number of channels for this device.  Note that the
02983   // channel value requested by the user might be < min_X_Channels.
02984   stream->nUserChannels[mode] = channels;
02985   device_channels = channels;
02986   if (mode == OUTPUT) {
02987     if (channels < devices[device].minOutputChannels)
02988       device_channels = devices[device].minOutputChannels;
02989   }
02990   else { // mode == INPUT
02991     if (stream->mode == OUTPUT && stream->device[0] == device) {
02992       // We're doing duplex setup here.
02993       if (channels < devices[device].minDuplexChannels)
02994         device_channels = devices[device].minDuplexChannels;
02995     }
02996     else {
02997       if (channels < devices[device].minInputChannels)
02998         device_channels = devices[device].minInputChannels;
02999     }
03000   }
03001   stream->nDeviceChannels[mode] = device_channels;
03002 
03003   // Attempt to set the buffer size.  According to OSS, the minimum
03004   // number of buffers is two.  The supposed minimum buffer size is 16
03005   // bytes, so that will be our lower bound.  The argument to this
03006   // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
03007   // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
03008   // We'll check the actual value used near the end of the setup
03009   // procedure.
03010   buffer_bytes = *bufferSize * formatBytes(stream->deviceFormat[mode]) * device_channels;
03011   if (buffer_bytes < 16) buffer_bytes = 16;
03012   buffers = numberOfBuffers;
03013   if (buffers < 2) buffers = 2;
03014   temp = ((int) buffers << 16) + (int)(log10((double)buffer_bytes)/log10(2.0));
03015   if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &temp)) {
03016     close(fd);
03017     sprintf(message, "RtAudio: OSS error setting fragment size for device (%s).",
03018             name);
03019     goto error;
03020   }
03021   stream->nBuffers = buffers;
03022 
03023   // Set the data format.
03024   temp = device_format;
03025   if (ioctl(fd, SNDCTL_DSP_SETFMT, &device_format) == -1 || device_format != temp) {
03026     close(fd);
03027     sprintf(message, "RtAudio: OSS error setting data format for device (%s).",
03028             name);
03029     goto error;
03030   }
03031 
03032   // Set the number of channels.
03033   temp = device_channels;
03034   if (ioctl(fd, SNDCTL_DSP_CHANNELS, &device_channels) == -1 || device_channels != temp) {
03035     close(fd);
03036     sprintf(message, "RtAudio: OSS error setting %d channels on device (%s).",
03037             temp, name);
03038     goto error;
03039   }
03040 
03041   // Set the sample rate.
03042   srate = sampleRate;
03043   temp = srate;
03044   if (ioctl(fd, SNDCTL_DSP_SPEED, &srate) == -1) {
03045     close(fd);
03046     sprintf(message, "RtAudio: OSS error setting sample rate = %d on device (%s).",
03047             temp, name);
03048     goto error;
03049   }
03050 
03051   // Verify the sample rate setup worked.
03052   if (abs(srate - temp) > 100) {
03053     close(fd);
03054     sprintf(message, "RtAudio: OSS error ... audio device (%s) doesn't support sample rate of %d.",
03055             name, temp);
03056     goto error;
03057   }
03058   stream->sampleRate = sampleRate;
03059 
03060   if (ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &buffer_bytes) == -1) {
03061     close(fd);
03062     sprintf(message, "RtAudio: OSS error getting buffer size for device (%s).",
03063             name);
03064     goto error;
03065   }
03066 
03067   // Save buffer size (in sample frames).
03068   *bufferSize = buffer_bytes / (formatBytes(stream->deviceFormat[mode]) * device_channels);
03069   stream->bufferSize = *bufferSize;
03070 
03071   if (mode == INPUT && stream->mode == OUTPUT &&
03072       stream->device[0] == device) {
03073     // We're doing duplex setup here.
03074     stream->deviceFormat[0] = stream->deviceFormat[1];
03075     stream->nDeviceChannels[0] = device_channels;
03076   }
03077 
03078   // Set flags for buffer conversion
03079   stream->doConvertBuffer[mode] = false;
03080   if (stream->userFormat != stream->deviceFormat[mode])
03081     stream->doConvertBuffer[mode] = true;
03082   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
03083     stream->doConvertBuffer[mode] = true;
03084 
03085   // Allocate necessary internal buffers
03086   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
03087 
03088     long buffer_bytes;
03089     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
03090       buffer_bytes = stream->nUserChannels[0];
03091     else
03092       buffer_bytes = stream->nUserChannels[1];
03093 
03094     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
03095     if (stream->userBuffer) free(stream->userBuffer);
03096     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
03097     if (stream->userBuffer == NULL) {
03098       close(fd);
03099       sprintf(message, "RtAudio: OSS error allocating user buffer memory (%s).",
03100               name);
03101       goto error;
03102     }
03103   }
03104 
03105   if ( stream->doConvertBuffer[mode] ) {
03106 
03107     long buffer_bytes;
03108     bool makeBuffer = true;
03109     if ( mode == OUTPUT )
03110       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
03111     else { // mode == INPUT
03112       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
03113       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
03114         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
03115         if ( buffer_bytes < bytes_out ) makeBuffer = false;
03116       }
03117     }
03118 
03119     if ( makeBuffer ) {
03120       buffer_bytes *= *bufferSize;
03121       if (stream->deviceBuffer) free(stream->deviceBuffer);
03122       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
03123       if (stream->deviceBuffer == NULL) {
03124         close(fd);
03125         free(stream->userBuffer);
03126         sprintf(message, "RtAudio: OSS error allocating device buffer memory (%s).",
03127                 name);
03128         goto error;
03129       }
03130     }
03131   }
03132 
03133   stream->device[mode] = device;
03134   stream->handle[mode] = fd;
03135   stream->state = STREAM_STOPPED;
03136   if ( stream->mode == OUTPUT && mode == INPUT ) {
03137     stream->mode = DUPLEX;
03138     if (stream->device[0] == device)
03139       stream->handle[0] = fd;
03140   }
03141   else
03142     stream->mode = mode;
03143 
03144   return SUCCESS;
03145 
03146  error:
03147   if (stream->handle[0]) {
03148     close(stream->handle[0]);
03149     stream->handle[0] = 0;
03150   }
03151   error(RtError::WARNING);
03152   return FAILURE;
03153 }
03154 
03155 void RtAudio :: closeStream(int streamId)
03156 {
03157   // We don't want an exception to be thrown here because this
03158   // function is called by our class destructor.  So, do our own
03159   // streamId check.
03160   if ( streams.find( streamId ) == streams.end() ) {
03161     sprintf(message, "RtAudio: invalid stream identifier!");
03162     error(RtError::WARNING);
03163     return;
03164   }
03165 
03166   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
03167 
03168   if (stream->callbackInfo.usingCallback) {
03169     pthread_cancel(stream->callbackInfo.thread);
03170     pthread_join(stream->callbackInfo.thread, NULL);
03171   }
03172 
03173   if (stream->state == STREAM_RUNNING) {
03174     if (stream->mode == OUTPUT || stream->mode == DUPLEX)
03175       ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);
03176     if (stream->mode == INPUT || stream->mode == DUPLEX)
03177       ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);
03178   }
03179 
03180   pthread_mutex_destroy(&stream->mutex);
03181 
03182   if (stream->handle[0])
03183     close(stream->handle[0]);
03184 
03185   if (stream->handle[1])
03186     close(stream->handle[1]);
03187 
03188   if (stream->userBuffer)
03189     free(stream->userBuffer);
03190 
03191   if (stream->deviceBuffer)
03192     free(stream->deviceBuffer);
03193 
03194   free(stream);
03195   streams.erase(streamId);
03196 }
03197 
03198 void RtAudio :: startStream(int streamId)
03199 {
03200   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03201 
03202   MUTEX_LOCK(&stream->mutex);
03203 
03204   stream->state = STREAM_RUNNING;
03205 
03206   // No need to do anything else here ... OSS automatically starts
03207   // when fed samples.
03208 
03209   MUTEX_UNLOCK(&stream->mutex);
03210 }
03211 
03212 void RtAudio :: stopStream(int streamId)
03213 {
03214   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03215 
03216   MUTEX_LOCK(&stream->mutex);
03217 
03218   if (stream->state == STREAM_STOPPED)
03219     goto unlock;
03220 
03221   int err;
03222   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
03223     err = ioctl(stream->handle[0], SNDCTL_DSP_SYNC, 0);
03224     if (err < -1) {
03225       sprintf(message, "RtAudio: OSS error stopping device (%s).",
03226               devices[stream->device[0]].name);
03227       error(RtError::DRIVER_ERROR);
03228     }
03229   }
03230   else {
03231     err = ioctl(stream->handle[1], SNDCTL_DSP_SYNC, 0);
03232     if (err < -1) {
03233       sprintf(message, "RtAudio: OSS error stopping device (%s).",
03234               devices[stream->device[1]].name);
03235       error(RtError::DRIVER_ERROR);
03236     }
03237   }
03238   stream->state = STREAM_STOPPED;
03239 
03240  unlock:
03241   MUTEX_UNLOCK(&stream->mutex);
03242 }
03243 
03244 void RtAudio :: abortStream(int streamId)
03245 {
03246   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03247 
03248   MUTEX_LOCK(&stream->mutex);
03249 
03250   if (stream->state == STREAM_STOPPED)
03251     goto unlock;
03252 
03253   int err;
03254   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
03255     err = ioctl(stream->handle[0], SNDCTL_DSP_RESET, 0);
03256     if (err < -1) {
03257       sprintf(message, "RtAudio: OSS error aborting device (%s).",
03258               devices[stream->device[0]].name);
03259       error(RtError::DRIVER_ERROR);
03260     }
03261   }
03262   else {
03263     err = ioctl(stream->handle[1], SNDCTL_DSP_RESET, 0);
03264     if (err < -1) {
03265       sprintf(message, "RtAudio: OSS error aborting device (%s).",
03266               devices[stream->device[1]].name);
03267       error(RtError::DRIVER_ERROR);
03268     }
03269   }
03270   stream->state = STREAM_STOPPED;
03271 
03272  unlock:
03273   MUTEX_UNLOCK(&stream->mutex);
03274 }
03275 
03276 int RtAudio :: streamWillBlock(int streamId)
03277 {
03278   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03279 
03280   MUTEX_LOCK(&stream->mutex);
03281 
03282   int bytes = 0, channels = 0, frames = 0;
03283   if (stream->state == STREAM_STOPPED)
03284     goto unlock;
03285 
03286   audio_buf_info info;
03287   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
03288     ioctl(stream->handle[0], SNDCTL_DSP_GETOSPACE, &info);
03289     bytes = info.bytes;
03290     channels = stream->nDeviceChannels[0];
03291   }
03292 
03293   if (stream->mode == INPUT || stream->mode == DUPLEX) {
03294     ioctl(stream->handle[1], SNDCTL_DSP_GETISPACE, &info);
03295     if (stream->mode == DUPLEX ) {
03296       bytes = (bytes < info.bytes) ? bytes : info.bytes;
03297       channels = stream->nDeviceChannels[0];
03298     }
03299     else {
03300       bytes = info.bytes;
03301       channels = stream->nDeviceChannels[1];
03302     }
03303   }
03304 
03305   frames = (int) (bytes / (channels * formatBytes(stream->deviceFormat[0])));
03306   frames -= stream->bufferSize;
03307   if (frames < 0) frames = 0;
03308 
03309  unlock:
03310   MUTEX_UNLOCK(&stream->mutex);
03311   return frames;
03312 }
03313 
03314 void RtAudio :: tickStream(int streamId)
03315 {
03316   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03317 
03318   int stopStream = 0;
03319   if (stream->state == STREAM_STOPPED) {
03320     if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
03321     return;
03322   }
03323   else if (stream->callbackInfo.usingCallback) {
03324     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
03325     stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
03326   }
03327 
03328   MUTEX_LOCK(&stream->mutex);
03329 
03330   // The state might change while waiting on a mutex.
03331   if (stream->state == STREAM_STOPPED)
03332     goto unlock;
03333 
03334   int result;
03335   char *buffer;
03336   int samples;
03337   RTAUDIO_FORMAT format;
03338   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
03339 
03340     // Setup parameters and do buffer conversion if necessary.
03341     if (stream->doConvertBuffer[0]) {
03342       convertStreamBuffer(stream, OUTPUT);
03343       buffer = stream->deviceBuffer;
03344       samples = stream->bufferSize * stream->nDeviceChannels[0];
03345       format = stream->deviceFormat[0];
03346     }
03347     else {
03348       buffer = stream->userBuffer;
03349       samples = stream->bufferSize * stream->nUserChannels[0];
03350       format = stream->userFormat;
03351     }
03352 
03353     // Do byte swapping if necessary.
03354     if (stream->doByteSwap[0])
03355       byteSwapBuffer(buffer, samples, format);
03356 
03357     // Write samples to device.
03358     result = write(stream->handle[0], buffer, samples * formatBytes(format));
03359 
03360     if (result == -1) {
03361       // This could be an underrun, but the basic OSS API doesn't provide a means for determining that.
03362       sprintf(message, "RtAudio: OSS audio write error for device (%s).",
03363               devices[stream->device[0]].name);
03364       error(RtError::DRIVER_ERROR);
03365     }
03366   }
03367 
03368   if (stream->mode == INPUT || stream->mode == DUPLEX) {
03369 
03370     // Setup parameters.
03371     if (stream->doConvertBuffer[1]) {
03372       buffer = stream->deviceBuffer;
03373       samples = stream->bufferSize * stream->nDeviceChannels[1];
03374       format = stream->deviceFormat[1];
03375     }
03376     else {
03377       buffer = stream->userBuffer;
03378       samples = stream->bufferSize * stream->nUserChannels[1];
03379       format = stream->userFormat;
03380     }
03381 
03382     // Read samples from device.
03383     result = read(stream->handle[1], buffer, samples * formatBytes(format));
03384 
03385     if (result == -1) {
03386       // This could be an overrun, but the basic OSS API doesn't provide a means for determining that.
03387       sprintf(message, "RtAudio: OSS audio read error for device (%s).",
03388               devices[stream->device[1]].name);
03389       error(RtError::DRIVER_ERROR);
03390     }
03391 
03392     // Do byte swapping if necessary.
03393     if (stream->doByteSwap[1])
03394       byteSwapBuffer(buffer, samples, format);
03395 
03396     // Do buffer conversion if necessary.
03397     if (stream->doConvertBuffer[1])
03398       convertStreamBuffer(stream, INPUT);
03399   }
03400 
03401  unlock:
03402   MUTEX_UNLOCK(&stream->mutex);
03403 
03404   if (stream->callbackInfo.usingCallback && stopStream)
03405     this->stopStream(streamId);
03406 }
03407 
03408 extern "C" void *callbackHandler(void *ptr)
03409 {
03410   CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
03411   RtAudio *object = (RtAudio *) info->object;
03412   int stream = info->streamId;
03413   bool *usingCallback = &info->usingCallback;
03414 
03415   while ( *usingCallback ) {
03416     pthread_testcancel();
03417     try {
03418       object->tickStream(stream);
03419     }
03420     catch (RtError &exception) {
03421       fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
03422               exception.getMessage());
03423       break;
03424     }
03425   }
03426 
03427   return 0;
03428 }
03429 
03430 
03431 //******************** End of __LINUX_OSS__ *********************//
03432 
03433 #elif defined(__WINDOWS_ASIO__) // ASIO API on Windows
03434 
03435 // The ASIO API is designed around a callback scheme, so this
03436 // implementation is similar to that used for OS X CoreAudio.  The
03437 // primary constraint with ASIO is that it only allows access to a
03438 // single driver at a time.  Thus, it is not possible to have more
03439 // than one simultaneous RtAudio stream.
03440 //
03441 // This implementation also requires a number of external ASIO files
03442 // and a few global variables.  The ASIO callback scheme does not
03443 // allow for the passing of user data, so we must create a global
03444 // pointer to our callbackInfo structure.
03445 
03446 #include "asio/asiosys.h"
03447 #include "asio/asio.h"
03448 #include "asio/asiodrivers.h"
03449 #include <math.h>
03450 
03451 AsioDrivers drivers;
03452 ASIOCallbacks asioCallbacks;
03453 CALLBACK_INFO *asioCallbackInfo;
03454 ASIODriverInfo driverInfo;
03455 
03456 void RtAudio :: initialize(void)
03457 {
03458   nDevices = drivers.asioGetNumDev();
03459   if (nDevices <= 0) return;
03460 
03461   //  Allocate the RTAUDIO_DEVICE structures.
03462   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
03463   if (devices == NULL) {
03464     sprintf(message, "RtAudio: memory allocation error!");
03465     error(RtError::MEMORY_ERROR);
03466   }
03467 
03468   // Write device driver names to device structures and then probe the
03469   // device capabilities.
03470   for (int i=0; i<nDevices; i++) {
03471     if ( drivers.asioGetDriverName( i, devices[i].name, 128 ) == 0 )
03472       //probeDeviceInfo(&devices[i]);
03473       ;
03474     else {
03475       sprintf(message, "RtAudio: error getting ASIO driver name for device index %d!", i);
03476       error(RtError::WARNING);
03477     }
03478   }
03479 
03480   drivers.removeCurrentDriver();
03481   driverInfo.asioVersion = 2;
03482   // See note in DirectSound implementation about GetDesktopWindow().
03483   driverInfo.sysRef = GetForegroundWindow();
03484 }
03485 
03486 int RtAudio :: getDefaultInputDevice(void)
03487 {
03488   return 0;
03489 }
03490 
03491 int RtAudio :: getDefaultOutputDevice(void)
03492 {
03493   return 0;
03494 }
03495 
03496 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
03497 {
03498   // Don't probe if a stream is already open.
03499   if ( streams.size() > 0 ) {
03500     sprintf(message, "RtAudio: unable to probe ASIO driver while a stream is open.");
03501     error(RtError::DEBUG_WARNING);
03502     return;
03503   }
03504 
03505   if ( !drivers.loadDriver( info->name ) ) {
03506     sprintf(message, "RtAudio: ASIO error loading driver (%s).", info->name);
03507     error(RtError::DEBUG_WARNING);
03508     return;
03509   }
03510 
03511   ASIOError result = ASIOInit( &driverInfo );
03512   if ( result != ASE_OK ) {
03513     char details[32];
03514     if ( result == ASE_HWMalfunction )
03515       sprintf(details, "hardware malfunction");
03516     else if ( result == ASE_NoMemory )
03517       sprintf(details, "no memory");
03518     else if ( result == ASE_NotPresent )
03519       sprintf(details, "driver/hardware not present");
03520     else
03521       sprintf(details, "unspecified");
03522     sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, info->name);
03523     error(RtError::DEBUG_WARNING);
03524     return;
03525   }
03526 
03527   // Determine the device channel information.
03528   long inputChannels, outputChannels;
03529   result = ASIOGetChannels( &inputChannels, &outputChannels );
03530   if ( result != ASE_OK ) {
03531     drivers.removeCurrentDriver();
03532     sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).", info->name);
03533     error(RtError::DEBUG_WARNING);
03534     return;
03535   }
03536 
03537   info->maxOutputChannels = outputChannels;
03538   if ( outputChannels > 0 ) info->minOutputChannels = 1;
03539 
03540   info->maxInputChannels = inputChannels;
03541   if ( inputChannels > 0 ) info->minInputChannels = 1;
03542 
03543   // If device opens for both playback and capture, we determine the channels.
03544   if (info->maxOutputChannels > 0 && info->maxInputChannels > 0) {
03545     info->hasDuplexSupport = true;
03546     info->maxDuplexChannels = (info->maxOutputChannels > info->maxInputChannels) ?
03547       info->maxInputChannels : info->maxOutputChannels;
03548     info->minDuplexChannels = (info->minOutputChannels > info->minInputChannels) ?
03549       info->minInputChannels : info->minOutputChannels;
03550   }
03551 
03552   // Determine the supported sample rates.
03553   info->nSampleRates = 0;
03554   for (int i=0; i<MAX_SAMPLE_RATES; i++) {
03555     result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
03556     if ( result == ASE_OK )
03557       info->sampleRates[info->nSampleRates++] = SAMPLE_RATES[i];
03558   }
03559 
03560   if (info->nSampleRates == 0) {
03561     drivers.removeCurrentDriver();
03562     sprintf( message, "RtAudio: No supported sample rates found for ASIO driver (%s).", info->name );
03563     error(RtError::DEBUG_WARNING);
03564     return;
03565   }
03566 
03567   // Determine supported data types ... just check first channel and assume rest are the same.
03568   ASIOChannelInfo channelInfo;
03569   channelInfo.channel = 0;
03570   channelInfo.isInput = true;
03571   if ( info->maxInputChannels <= 0 ) channelInfo.isInput = false;
03572   result = ASIOGetChannelInfo( &channelInfo );
03573   if ( result != ASE_OK ) {
03574     drivers.removeCurrentDriver();
03575     sprintf(message, "RtAudio: ASIO error getting driver (%s) channel information.", info->name);
03576     error(RtError::DEBUG_WARNING);
03577     return;
03578   }
03579 
03580   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
03581     info->nativeFormats |= RTAUDIO_SINT16;
03582   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
03583     info->nativeFormats |= RTAUDIO_SINT32;
03584   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
03585     info->nativeFormats |= RTAUDIO_FLOAT32;
03586   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
03587     info->nativeFormats |= RTAUDIO_FLOAT64;
03588 
03589         // Check that we have at least one supported format.
03590   if (info->nativeFormats == 0) {
03591     drivers.removeCurrentDriver();
03592     sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.",
03593             info->name);
03594     error(RtError::DEBUG_WARNING);
03595     return;
03596   }
03597 
03598   info->probed = true;
03599   drivers.removeCurrentDriver();
03600 }
03601 
03602 void bufferSwitch(long index, ASIOBool processNow)
03603 {
03604   RtAudio *object = (RtAudio *) asioCallbackInfo->object;
03605   try {
03606     object->callbackEvent( asioCallbackInfo->streamId, index, (void *)NULL, (void *)NULL );
03607   }
03608   catch (RtError &exception) {
03609     fprintf(stderr, "\nCallback handler error (%s)!\n\n", exception.getMessage());
03610     return;
03611   }
03612 
03613   return;
03614 }
03615 
03616 void sampleRateChanged(ASIOSampleRate sRate)
03617 {
03618   // The ASIO documentation says that this usually only happens during
03619   // external sync.  Audio processing is not stopped by the driver,
03620   // actual sample rate might not have even changed, maybe only the
03621   // sample rate status of an AES/EBU or S/PDIF digital input at the
03622   // audio device.
03623 
03624   RtAudio *object = (RtAudio *) asioCallbackInfo->object;
03625   try {
03626     object->stopStream( asioCallbackInfo->streamId );
03627   }
03628   catch (RtError &exception) {
03629     fprintf(stderr, "\nRtAudio: sampleRateChanged() error (%s)!\n\n", exception.getMessage());
03630     return;
03631   }
03632 
03633   fprintf(stderr, "\nRtAudio: ASIO driver reports sample rate changed to %d ... stream stopped!!!", (int) sRate);
03634 }
03635 
03636 long asioMessages(long selector, long value, void* message, double* opt)
03637 {
03638   long ret = 0;
03639   switch(selector) {
03640   case kAsioSelectorSupported:
03641     if(value == kAsioResetRequest
03642        || value == kAsioEngineVersion
03643        || value == kAsioResyncRequest
03644        || value == kAsioLatenciesChanged
03645        // The following three were added for ASIO 2.0, you don't
03646        // necessarily have to support them.
03647        || value == kAsioSupportsTimeInfo
03648        || value == kAsioSupportsTimeCode
03649        || value == kAsioSupportsInputMonitor)
03650       ret = 1L;
03651     break;
03652   case kAsioResetRequest:
03653     // Defer the task and perform the reset of the driver during the
03654     // next "safe" situation.  You cannot reset the driver right now,
03655     // as this code is called from the driver.  Reset the driver is
03656     // done by completely destruct is. I.e. ASIOStop(),
03657     // ASIODisposeBuffers(), Destruction Afterwards you initialize the
03658     // driver again.
03659     fprintf(stderr, "\nRtAudio: ASIO driver reset requested!!!");
03660     ret = 1L;
03661     break;
03662   case kAsioResyncRequest:
03663     // This informs the application that the driver encountered some
03664     // non-fatal data loss.  It is used for synchronization purposes
03665     // of different media.  Added mainly to work around the Win16Mutex
03666     // problems in Windows 95/98 with the Windows Multimedia system,
03667     // which could lose data because the Mutex was held too long by
03668     // another thread.  However a driver can issue it in other
03669     // situations, too.
03670     fprintf(stderr, "\nRtAudio: ASIO driver resync requested!!!");
03671     ret = 1L;
03672     break;
03673   case kAsioLatenciesChanged:
03674     // This will inform the host application that the drivers were
03675     // latencies changed.  Beware, it this does not mean that the
03676     // buffer sizes have changed!  You might need to update internal
03677     // delay data.
03678     fprintf(stderr, "\nRtAudio: ASIO driver latency may have changed!!!");
03679     ret = 1L;
03680     break;
03681   case kAsioEngineVersion:
03682     // Return the supported ASIO version of the host application.  If
03683     // a host application does not implement this selector, ASIO 1.0
03684     // is assumed by the driver.
03685     ret = 2L;
03686     break;
03687   case kAsioSupportsTimeInfo:
03688     // Informs the driver whether the
03689     // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
03690     // For compatibility with ASIO 1.0 drivers the host application
03691     // should always support the "old" bufferSwitch method, too.
03692     ret = 0;
03693     break;
03694   case kAsioSupportsTimeCode:
03695     // Informs the driver wether application is interested in time
03696     // code info.  If an application does not need to know about time
03697     // code, the driver has less work to do.
03698     ret = 0;
03699     break;
03700   }
03701   return ret;
03702 }
03703 
03704 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
03705                                 STREAM_MODE mode, int channels, 
03706                                 int sampleRate, RTAUDIO_FORMAT format,
03707                                 int *bufferSize, int numberOfBuffers)
03708 {
03709   // Don't attempt to load another driver if a stream is already open.
03710   if ( streams.size() > 0 ) {
03711     sprintf(message, "RtAudio: unable to load ASIO driver while a stream is open.");
03712     error(RtError::WARNING);
03713     return FAILURE;
03714   }
03715 
03716   // For ASIO, a duplex stream MUST use the same driver.
03717   if ( mode == INPUT && stream->mode == OUTPUT && stream->device[0] != device ) {
03718     sprintf(message, "RtAudio: ASIO duplex stream must use the same device for input and output.");
03719     error(RtError::WARNING);
03720     return FAILURE;
03721   }
03722 
03723   // Only load the driver once for duplex stream.
03724   ASIOError result;
03725   if ( mode != INPUT || stream->mode != OUTPUT ) {
03726     if ( !drivers.loadDriver( devices[device].name ) ) {
03727       sprintf(message, "RtAudio: ASIO error loading driver (%s).", devices[device].name);
03728       error(RtError::DEBUG_WARNING);
03729       return FAILURE;
03730     }
03731 
03732     result = ASIOInit( &driverInfo );
03733     if ( result != ASE_OK ) {
03734       char details[32];
03735       if ( result == ASE_HWMalfunction )
03736         sprintf(details, "hardware malfunction");
03737       else if ( result == ASE_NoMemory )
03738         sprintf(details, "no memory");
03739       else if ( result == ASE_NotPresent )
03740         sprintf(details, "driver/hardware not present");
03741       else
03742         sprintf(details, "unspecified");
03743       sprintf(message, "RtAudio: ASIO error (%s) initializing driver (%s).", details, devices[device].name);
03744       error(RtError::DEBUG_WARNING);
03745       return FAILURE;
03746     }
03747   }
03748 
03749   // Check the device channel count.
03750   long inputChannels, outputChannels;
03751   result = ASIOGetChannels( &inputChannels, &outputChannels );
03752   if ( result != ASE_OK ) {
03753     drivers.removeCurrentDriver();
03754     sprintf(message, "RtAudio: ASIO error getting input/output channel count (%s).",
03755             devices[device].name);
03756     error(RtError::DEBUG_WARNING);
03757     return FAILURE;
03758   }
03759 
03760   if ( ( mode == OUTPUT && channels > outputChannels) ||
03761        ( mode == INPUT && channels > inputChannels) ) {
03762     drivers.removeCurrentDriver();
03763     sprintf(message, "RtAudio: ASIO driver (%s) does not support requested channel count (%d).",
03764             devices[device].name, channels);
03765     error(RtError::DEBUG_WARNING);
03766     return FAILURE;
03767   }
03768   stream->nDeviceChannels[mode] = channels;
03769   stream->nUserChannels[mode] = channels;
03770 
03771   // Verify the sample rate is supported.
03772   result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
03773   if ( result != ASE_OK ) {
03774     drivers.removeCurrentDriver();
03775     sprintf(message, "RtAudio: ASIO driver (%s) does not support requested sample rate (%d).",
03776             devices[device].name, sampleRate);
03777     error(RtError::DEBUG_WARNING);
03778     return FAILURE;
03779   }
03780 
03781   // Set the sample rate.
03782   result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
03783   if ( result != ASE_OK ) {
03784     drivers.removeCurrentDriver();
03785     sprintf(message, "RtAudio: ASIO driver (%s) error setting sample rate (%d).",
03786             devices[device].name, sampleRate);
03787     error(RtError::DEBUG_WARNING);
03788     return FAILURE;
03789   }
03790 
03791   // Determine the driver data type.
03792   ASIOChannelInfo channelInfo;
03793   channelInfo.channel = 0;
03794   if ( mode == OUTPUT ) channelInfo.isInput = false;
03795   else channelInfo.isInput = true;
03796   result = ASIOGetChannelInfo( &channelInfo );
03797   if ( result != ASE_OK ) {
03798     drivers.removeCurrentDriver();
03799     sprintf(message, "RtAudio: ASIO driver (%s) error getting data format.",
03800             devices[device].name);
03801     error(RtError::DEBUG_WARNING);
03802     return FAILURE;
03803   }
03804 
03805   // Assuming WINDOWS host is always little-endian.
03806   stream->doByteSwap[mode] = false;
03807   stream->userFormat = format;
03808   stream->deviceFormat[mode] = 0;
03809   if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
03810     stream->deviceFormat[mode] = RTAUDIO_SINT16;
03811     if ( channelInfo.type == ASIOSTInt16MSB ) stream->doByteSwap[mode] = true;
03812   }
03813   else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
03814     stream->deviceFormat[mode] = RTAUDIO_SINT32;
03815     if ( channelInfo.type == ASIOSTInt32MSB ) stream->doByteSwap[mode] = true;
03816   }
03817   else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
03818     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
03819     if ( channelInfo.type == ASIOSTFloat32MSB ) stream->doByteSwap[mode] = true;
03820   }
03821   else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
03822     stream->deviceFormat[mode] = RTAUDIO_FLOAT64;
03823     if ( channelInfo.type == ASIOSTFloat64MSB ) stream->doByteSwap[mode] = true;
03824   }
03825 
03826   if ( stream->deviceFormat[mode] == 0 ) {
03827     drivers.removeCurrentDriver();
03828     sprintf(message, "RtAudio: ASIO driver (%s) data format not supported by RtAudio.",
03829             devices[device].name);
03830     error(RtError::DEBUG_WARNING);
03831     return FAILURE;
03832   }
03833 
03834   // Set the buffer size.  For a duplex stream, this will end up
03835   // setting the buffer size based on the input constraints, which
03836   // should be ok.
03837   long minSize, maxSize, preferSize, granularity;
03838   result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
03839   if ( result != ASE_OK ) {
03840     drivers.removeCurrentDriver();
03841     sprintf(message, "RtAudio: ASIO driver (%s) error getting buffer size.",
03842             devices[device].name);
03843     error(RtError::DEBUG_WARNING);
03844     return FAILURE;
03845   }
03846 
03847   if ( *bufferSize < minSize ) *bufferSize = minSize;
03848   else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
03849   else if ( granularity == -1 ) {
03850     // Make sure bufferSize is a power of two.
03851     double power = log10( *bufferSize ) / log10( 2.0 );
03852     *bufferSize = pow( 2.0, floor(power+0.5) );
03853     if ( *bufferSize < minSize ) *bufferSize = minSize;
03854     else if ( *bufferSize > maxSize ) *bufferSize = maxSize;
03855     else *bufferSize = preferSize;
03856   }
03857 
03858   if ( mode == INPUT && stream->mode == OUTPUT && stream->bufferSize != *bufferSize )
03859     cout << "possible input/output buffersize discrepancy" << endl;
03860 
03861   stream->bufferSize = *bufferSize;
03862   stream->nBuffers = 2;
03863 
03864   // ASIO always uses deinterleaved channels.
03865   stream->deInterleave[mode] = true;
03866 
03867   // Create the ASIO internal buffers.  Since RtAudio sets up input
03868   // and output separately, we'll have to dispose of previously
03869   // created output buffers for a duplex stream.
03870   if ( mode == INPUT && stream->mode == OUTPUT ) {
03871     free(stream->callbackInfo.buffers);
03872     result = ASIODisposeBuffers();
03873     if ( result != ASE_OK ) {
03874       drivers.removeCurrentDriver();
03875       sprintf(message, "RtAudio: ASIO driver (%s) error disposing previously allocated buffers.",
03876               devices[device].name);
03877       error(RtError::DEBUG_WARNING);
03878       return FAILURE;
03879     }
03880   }
03881 
03882   // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
03883   int i, nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
03884   stream->callbackInfo.buffers = 0;
03885   ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
03886   stream->callbackInfo.buffers = (void *) bufferInfos;
03887   ASIOBufferInfo *infos = bufferInfos;
03888   for ( i=0; i<stream->nDeviceChannels[1]; i++, infos++ ) {
03889     infos->isInput = ASIOTrue;
03890     infos->channelNum = i;
03891     infos->buffers[0] = infos->buffers[1] = 0;
03892   }
03893 
03894   for ( i=0; i<stream->nDeviceChannels[0]; i++, infos++ ) {
03895     infos->isInput = ASIOFalse;
03896     infos->channelNum = i;
03897     infos->buffers[0] = infos->buffers[1] = 0;
03898   }
03899 
03900   // Set up the ASIO callback structure and create the ASIO data buffers.
03901   asioCallbacks.bufferSwitch = &bufferSwitch;
03902   asioCallbacks.sampleRateDidChange = &sampleRateChanged;
03903   asioCallbacks.asioMessage = &asioMessages;
03904   asioCallbacks.bufferSwitchTimeInfo = NULL;
03905   result = ASIOCreateBuffers( bufferInfos, nChannels, stream->bufferSize, &asioCallbacks);
03906   if ( result != ASE_OK ) {
03907     drivers.removeCurrentDriver();
03908     sprintf(message, "RtAudio: ASIO driver (%s) error creating buffers.",
03909             devices[device].name);
03910     error(RtError::DEBUG_WARNING);
03911     return FAILURE;
03912   }
03913 
03914   // Set flags for buffer conversion.
03915   stream->doConvertBuffer[mode] = false;
03916   if (stream->userFormat != stream->deviceFormat[mode])
03917     stream->doConvertBuffer[mode] = true;
03918   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
03919     stream->doConvertBuffer[mode] = true;
03920   if (stream->nUserChannels[mode] > 1 && stream->deInterleave[mode])
03921     stream->doConvertBuffer[mode] = true;
03922 
03923   // Allocate necessary internal buffers
03924   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
03925 
03926     long buffer_bytes;
03927     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
03928       buffer_bytes = stream->nUserChannels[0];
03929     else
03930       buffer_bytes = stream->nUserChannels[1];
03931 
03932     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
03933     if (stream->userBuffer) free(stream->userBuffer);
03934     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
03935     if (stream->userBuffer == NULL)
03936       goto memory_error;
03937   }
03938 
03939   if ( stream->doConvertBuffer[mode] ) {
03940 
03941     long buffer_bytes;
03942     bool makeBuffer = true;
03943     if ( mode == OUTPUT )
03944       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
03945     else { // mode == INPUT
03946       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
03947       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
03948         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
03949         if ( buffer_bytes < bytes_out ) makeBuffer = false;
03950       }
03951     }
03952 
03953     if ( makeBuffer ) {
03954       buffer_bytes *= *bufferSize;
03955       if (stream->deviceBuffer) free(stream->deviceBuffer);
03956       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
03957       if (stream->deviceBuffer == NULL)
03958         goto memory_error;
03959     }
03960   }
03961 
03962   stream->device[mode] = device;
03963   stream->state = STREAM_STOPPED;
03964   if ( stream->mode == OUTPUT && mode == INPUT )
03965     // We had already set up an output stream.
03966     stream->mode = DUPLEX;
03967   else
03968     stream->mode = mode;
03969   stream->sampleRate = sampleRate;
03970   asioCallbackInfo = &stream->callbackInfo;
03971   stream->callbackInfo.object = (void *) this;
03972   stream->callbackInfo.waitTime = (unsigned long) (200.0 * stream->bufferSize / stream->sampleRate);
03973 
03974   return SUCCESS;
03975 
03976  memory_error:
03977   ASIODisposeBuffers();
03978   drivers.removeCurrentDriver();
03979 
03980   if (stream->callbackInfo.buffers)
03981     free(stream->callbackInfo.buffers);
03982   stream->callbackInfo.buffers = 0;
03983 
03984   if (stream->userBuffer) {
03985     free(stream->userBuffer);
03986     stream->userBuffer = 0;
03987   }
03988   sprintf(message, "RtAudio: error allocating buffer memory (%s).",
03989           devices[device].name);
03990   error(RtError::WARNING);
03991   return FAILURE;
03992 }
03993 
03994 void RtAudio :: cancelStreamCallback(int streamId)
03995 {
03996   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
03997 
03998   if (stream->callbackInfo.usingCallback) {
03999 
04000     if (stream->state == STREAM_RUNNING)
04001       stopStream( streamId );
04002 
04003     MUTEX_LOCK(&stream->mutex);
04004 
04005     stream->callbackInfo.usingCallback = false;
04006     stream->callbackInfo.userData = NULL;
04007     stream->state = STREAM_STOPPED;
04008     stream->callbackInfo.callback = NULL;
04009 
04010     MUTEX_UNLOCK(&stream->mutex);
04011   }
04012 }
04013 
04014 void RtAudio :: closeStream(int streamId)
04015 {
04016   // We don't want an exception to be thrown here because this
04017   // function is called by our class destructor.  So, do our own
04018   // streamId check.
04019   if ( streams.find( streamId ) == streams.end() ) {
04020     sprintf(message, "RtAudio: invalid stream identifier!");
04021     error(RtError::WARNING);
04022     return;
04023   }
04024 
04025   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
04026 
04027   if (stream->state == STREAM_RUNNING)
04028     ASIOStop();
04029 
04030   ASIODisposeBuffers();
04031   //ASIOExit();
04032   drivers.removeCurrentDriver();
04033 
04034   DeleteCriticalSection(&stream->mutex);
04035 
04036   if (stream->callbackInfo.buffers)
04037     free(stream->callbackInfo.buffers);
04038 
04039   if (stream->userBuffer)
04040     free(stream->userBuffer);
04041 
04042   if (stream->deviceBuffer)
04043     free(stream->deviceBuffer);
04044 
04045   free(stream);
04046   streams.erase(streamId);
04047 }
04048 
04049 void RtAudio :: startStream(int streamId)
04050 {
04051   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04052 
04053   MUTEX_LOCK(&stream->mutex);
04054 
04055   if (stream->state == STREAM_RUNNING) {
04056     MUTEX_UNLOCK(&stream->mutex);
04057     return;
04058   }
04059 
04060   stream->callbackInfo.blockTick = true;
04061   stream->callbackInfo.stopStream = false;
04062   stream->callbackInfo.streamId = streamId;
04063   ASIOError result = ASIOStart();
04064   if ( result != ASE_OK ) {
04065     sprintf(message, "RtAudio: ASIO error starting device (%s).",
04066               devices[stream->device[0]].name);
04067     MUTEX_UNLOCK(&stream->mutex);
04068     error(RtError::DRIVER_ERROR);
04069   }
04070   stream->state = STREAM_RUNNING;
04071 
04072   MUTEX_UNLOCK(&stream->mutex);
04073 }
04074 
04075 void RtAudio :: stopStream(int streamId)
04076 {
04077   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04078 
04079   MUTEX_LOCK(&stream->mutex);
04080 
04081   if (stream->state == STREAM_STOPPED) {
04082     MUTEX_UNLOCK(&stream->mutex);
04083     return;
04084   }
04085 
04086   ASIOError result = ASIOStop();
04087   if ( result != ASE_OK ) {
04088     sprintf(message, "RtAudio: ASIO error stopping device (%s).",
04089               devices[stream->device[0]].name);
04090     MUTEX_UNLOCK(&stream->mutex);
04091     error(RtError::DRIVER_ERROR);
04092   }
04093   stream->state = STREAM_STOPPED;
04094 
04095   MUTEX_UNLOCK(&stream->mutex);
04096 }
04097 
04098 void RtAudio :: abortStream(int streamId)
04099 {
04100   stopStream( streamId );
04101 }
04102 
04103 // I don't know how this function can be implemented.
04104 int RtAudio :: streamWillBlock(int streamId)
04105 {
04106   sprintf(message, "RtAudio: streamWillBlock() cannot be implemented for ASIO.");
04107   error(RtError::WARNING);
04108   return 0;
04109 }
04110 
04111 void RtAudio :: tickStream(int streamId)
04112 {
04113   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04114 
04115   if (stream->state == STREAM_STOPPED)
04116     return;
04117 
04118   if (stream->callbackInfo.usingCallback) {
04119     sprintf(message, "RtAudio: tickStream() should not be used when a callback function is set!");
04120     error(RtError::WARNING);
04121     return;
04122   }
04123 
04124   // Block waiting here until the user data is processed in callbackEvent().
04125   while ( stream->callbackInfo.blockTick )
04126     Sleep(stream->callbackInfo.waitTime);
04127 
04128   MUTEX_LOCK(&stream->mutex);
04129 
04130   stream->callbackInfo.blockTick = true;
04131 
04132   MUTEX_UNLOCK(&stream->mutex);
04133 }
04134 
04135 void RtAudio :: callbackEvent(int streamId, int bufferIndex, void *inData, void *outData)
04136 {
04137   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04138 
04139   CALLBACK_INFO *info = asioCallbackInfo;
04140   if ( !info->usingCallback ) {
04141     // Block waiting here until we get new user data in tickStream().
04142     while ( !info->blockTick )
04143       Sleep(info->waitTime);
04144   }
04145   else if ( info->stopStream ) {
04146     // Check if the stream should be stopped (via the previous user
04147     // callback return value).  We stop the stream here, rather than
04148     // after the function call, so that output data can first be
04149     // processed.
04150     this->stopStream(asioCallbackInfo->streamId);
04151     return;
04152   }
04153 
04154   MUTEX_LOCK(&stream->mutex);
04155 
04156   // Invoke user callback first, to get fresh output data.
04157   if ( info->usingCallback ) {
04158     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) info->callback;
04159     if ( callback(stream->userBuffer, stream->bufferSize, info->userData) )
04160       info->stopStream = true;
04161   }
04162 
04163   int nChannels = stream->nDeviceChannels[0] + stream->nDeviceChannels[1];
04164   int bufferBytes;
04165   ASIOBufferInfo *bufferInfos = (ASIOBufferInfo *) info->buffers;
04166   if ( stream->mode == OUTPUT || stream->mode == DUPLEX ) {
04167 
04168     bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[0]);
04169     if (stream->doConvertBuffer[0]) {
04170 
04171       convertStreamBuffer(stream, OUTPUT);
04172       if ( stream->doByteSwap[0] )
04173         byteSwapBuffer(stream->deviceBuffer,
04174                        stream->bufferSize * stream->nDeviceChannels[0],
04175                        stream->deviceFormat[0]);
04176 
04177       // Always de-interleave ASIO output data.
04178       for ( int i=0; i<stream->nDeviceChannels[0]; i++, bufferInfos++ ) {
04179         memcpy(bufferInfos->buffers[bufferIndex],
04180                &stream->deviceBuffer[i*bufferBytes], bufferBytes );
04181       }
04182     }
04183     else { // single channel only
04184 
04185       if (stream->doByteSwap[0])
04186         byteSwapBuffer(stream->userBuffer,
04187                        stream->bufferSize * stream->nUserChannels[0],
04188                        stream->userFormat);
04189 
04190       memcpy(bufferInfos->buffers[bufferIndex], stream->userBuffer, bufferBytes );
04191     }
04192   }
04193 
04194   if ( stream->mode == INPUT || stream->mode == DUPLEX ) {
04195 
04196     bufferBytes = stream->bufferSize * formatBytes(stream->deviceFormat[1]);
04197     if (stream->doConvertBuffer[1]) {
04198 
04199       // Always interleave ASIO input data.
04200       for ( int i=0; i<stream->nDeviceChannels[1]; i++, bufferInfos++ )
04201         memcpy(&stream->deviceBuffer[i*bufferBytes], bufferInfos->buffers[bufferIndex], bufferBytes );
04202 
04203       if ( stream->doByteSwap[1] )
04204         byteSwapBuffer(stream->deviceBuffer,
04205                        stream->bufferSize * stream->nDeviceChannels[1],
04206                        stream->deviceFormat[1]);
04207       convertStreamBuffer(stream, INPUT);
04208 
04209     }
04210     else { // single channel only
04211       memcpy(stream->userBuffer, bufferInfos->buffers[bufferIndex], bufferBytes );
04212 
04213       if (stream->doByteSwap[1])
04214         byteSwapBuffer(stream->userBuffer,
04215                        stream->bufferSize * stream->nUserChannels[1],
04216                        stream->userFormat);
04217     }
04218   }
04219 
04220   if ( !info->usingCallback )
04221     info->blockTick = false;
04222 
04223   MUTEX_UNLOCK(&stream->mutex);
04224 }
04225 
04226 void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
04227 {
04228   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04229 
04230   stream->callbackInfo.callback = (void *) callback;
04231   stream->callbackInfo.userData = userData;
04232   stream->callbackInfo.usingCallback = true;
04233 }
04234 
04235 //******************** End of __WINDOWS_ASIO__ *********************//
04236 
04237 #elif defined(__WINDOWS_DS__) // Windows DirectSound API
04238 
04239 #include <dsound.h>
04240 
04241 // Declarations for utility functions, callbacks, and structures
04242 // specific to the DirectSound implementation.
04243 static bool CALLBACK deviceCountCallback(LPGUID lpguid,
04244                                          LPCSTR lpcstrDescription,
04245                                          LPCSTR lpcstrModule,
04246                                          LPVOID lpContext);
04247 
04248 static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
04249                                         LPCSTR lpcstrDescription,
04250                                         LPCSTR lpcstrModule,
04251                                         LPVOID lpContext);
04252 
04253 static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
04254                                            LPCSTR lpcstrDescription,
04255                                            LPCSTR lpcstrModule,
04256                                            LPVOID lpContext);
04257 
04258 static bool CALLBACK deviceIdCallback(LPGUID lpguid,
04259                                       LPCSTR lpcstrDescription,
04260                                       LPCSTR lpcstrModule,
04261                                       LPVOID lpContext);
04262 
04263 static char* getErrorString(int code);
04264 
04265 struct enum_info {
04266   char name[64];
04267   LPGUID id;
04268   bool isInput;
04269   bool isValid;
04270 };
04271 
04272 int RtAudio :: getDefaultInputDevice(void)
04273 {
04274   enum_info info;
04275   info.name[0] = '\0';
04276 
04277   // Enumerate through devices to find the default output.
04278   HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
04279   if ( FAILED(result) ) {
04280     sprintf(message, "RtAudio: Error performing default input device enumeration: %s.",
04281             getErrorString(result));
04282     error(RtError::WARNING);
04283     return 0;
04284   }
04285 
04286   for ( int i=0; i<nDevices; i++ )
04287     if ( strncmp( devices[i].name, info.name, 64 ) == 0 ) return i;
04288 
04289   return 0;
04290 }
04291 
04292 int RtAudio :: getDefaultOutputDevice(void)
04293 {
04294   enum_info info;
04295   info.name[0] = '\0';
04296 
04297   // Enumerate through devices to find the default output.
04298   HRESULT result = DirectSoundEnumerate((LPDSENUMCALLBACK)defaultDeviceCallback, &info);
04299   if ( FAILED(result) ) {
04300     sprintf(message, "RtAudio: Error performing default output device enumeration: %s.",
04301             getErrorString(result));
04302     error(RtError::WARNING);
04303     return 0;
04304   }
04305 
04306   for ( int i=0; i<nDevices; i++ )
04307     if ( strncmp(devices[i].name, info.name, 64 ) == 0 ) return i;
04308 
04309   return 0;
04310 }
04311 
04312 void RtAudio :: initialize(void)
04313 {
04314   int i, ins = 0, outs = 0, count = 0;
04315   HRESULT result;
04316   nDevices = 0;
04317 
04318   // Count DirectSound devices.
04319   result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &outs);
04320   if ( FAILED(result) ) {
04321     sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",
04322             getErrorString(result));
04323     error(RtError::DRIVER_ERROR);
04324   }
04325 
04326   // Count DirectSoundCapture devices.
04327   result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceCountCallback, &ins);
04328   if ( FAILED(result) ) {
04329     sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",
04330             getErrorString(result));
04331     error(RtError::DRIVER_ERROR);
04332   }
04333 
04334   count = ins + outs;
04335   if (count == 0) return;
04336 
04337   std::vector<enum_info> info(count);
04338   for (i=0; i<count; i++) {
04339     info[i].name[0] = '\0';
04340     if (i < outs) info[i].isInput = false;
04341     else info[i].isInput = true;
04342   }
04343 
04344   // Get playback device info and check capabilities.
04345   result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
04346   if ( FAILED(result) ) {
04347     sprintf(message, "RtAudio: Unable to enumerate through sound playback devices: %s.",
04348             getErrorString(result));
04349     error(RtError::DRIVER_ERROR);
04350   }
04351 
04352   // Get capture device info and check capabilities.
04353   result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceInfoCallback, &info[0]);
04354   if ( FAILED(result) ) {
04355     sprintf(message, "RtAudio: Unable to enumerate through sound capture devices: %s.",
04356             getErrorString(result));
04357     error(RtError::DRIVER_ERROR);
04358   }
04359 
04360   // Parse the devices and check validity.  Devices are considered
04361   // invalid if they cannot be opened, they report < 1 supported
04362   // channels, or they report no supported data (capture only).
04363   for (i=0; i<count; i++)
04364     if ( info[i].isValid ) nDevices++;
04365 
04366   if (nDevices == 0) return;
04367 
04368   //  Allocate the RTAUDIO_DEVICE structures.
04369   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
04370   if (devices == NULL) {
04371     sprintf(message, "RtAudio: memory allocation error!");
04372     error(RtError::MEMORY_ERROR);
04373   }
04374 
04375   // Copy the names to our devices structures.
04376   int index = 0;
04377   for (i=0; i<count; i++) {
04378     if ( info[i].isValid )
04379       strncpy(devices[index++].name, info[i].name, 64);
04380   }
04381 
04382   //for (i=0;i<nDevices; i++)
04383   //probeDeviceInfo(&devices[i]);
04384 
04385   return;
04386 }
04387 
04388 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
04389 {
04390   enum_info dsinfo;
04391   strncpy( dsinfo.name, info->name, 64 );
04392   dsinfo.isValid = false;
04393 
04394   // Enumerate through input devices to find the id (if it exists).
04395   HRESULT result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
04396   if ( FAILED(result) ) {
04397     sprintf(message, "RtAudio: Error performing input device id enumeration: %s.",
04398             getErrorString(result));
04399     error(RtError::WARNING);
04400     return;
04401   }
04402 
04403   // Do capture probe first.
04404   if ( dsinfo.isValid == false )
04405     goto playback_probe;
04406 
04407   LPDIRECTSOUNDCAPTURE  input;
04408   result = DirectSoundCaptureCreate( dsinfo.id, &input, NULL );
04409   if ( FAILED(result) ) {
04410     sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
04411             info->name, getErrorString(result));
04412     error(RtError::WARNING);
04413     goto playback_probe;
04414   }
04415 
04416   DSCCAPS in_caps;
04417   in_caps.dwSize = sizeof(in_caps);
04418   result = input->GetCaps( &in_caps );
04419   if ( FAILED(result) ) {
04420     input->Release();
04421     sprintf(message, "RtAudio: Could not get DirectSound capture capabilities (%s): %s.",
04422             info->name, getErrorString(result));
04423     error(RtError::WARNING);
04424     goto playback_probe;
04425   }
04426 
04427   // Get input channel information.
04428   info->minInputChannels = 1;
04429   info->maxInputChannels = in_caps.dwChannels;
04430 
04431   // Get sample rate and format information.
04432   if( in_caps.dwChannels == 2 ) {
04433     if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->nativeFormats |= RTAUDIO_SINT16;
04434     if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->nativeFormats |= RTAUDIO_SINT16;
04435     if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->nativeFormats |= RTAUDIO_SINT16;
04436     if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->nativeFormats |= RTAUDIO_SINT8;
04437     if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->nativeFormats |= RTAUDIO_SINT8;
04438     if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->nativeFormats |= RTAUDIO_SINT8;
04439 
04440     if ( info->nativeFormats & RTAUDIO_SINT16 ) {
04441       if( in_caps.dwFormats & WAVE_FORMAT_1S16 ) info->sampleRates[info->nSampleRates++] = 11025;
04442       if( in_caps.dwFormats & WAVE_FORMAT_2S16 ) info->sampleRates[info->nSampleRates++] = 22050;
04443       if( in_caps.dwFormats & WAVE_FORMAT_4S16 ) info->sampleRates[info->nSampleRates++] = 44100;
04444     }
04445     else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
04446       if( in_caps.dwFormats & WAVE_FORMAT_1S08 ) info->sampleRates[info->nSampleRates++] = 11025;
04447       if( in_caps.dwFormats & WAVE_FORMAT_2S08 ) info->sampleRates[info->nSampleRates++] = 22050;
04448       if( in_caps.dwFormats & WAVE_FORMAT_4S08 ) info->sampleRates[info->nSampleRates++] = 44100;
04449     }
04450   }
04451   else if ( in_caps.dwChannels == 1 ) {
04452     if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->nativeFormats |= RTAUDIO_SINT16;
04453     if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->nativeFormats |= RTAUDIO_SINT16;
04454     if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->nativeFormats |= RTAUDIO_SINT16;
04455     if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->nativeFormats |= RTAUDIO_SINT8;
04456     if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->nativeFormats |= RTAUDIO_SINT8;
04457     if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->nativeFormats |= RTAUDIO_SINT8;
04458 
04459     if ( info->nativeFormats & RTAUDIO_SINT16 ) {
04460       if( in_caps.dwFormats & WAVE_FORMAT_1M16 ) info->sampleRates[info->nSampleRates++] = 11025;
04461       if( in_caps.dwFormats & WAVE_FORMAT_2M16 ) info->sampleRates[info->nSampleRates++] = 22050;
04462       if( in_caps.dwFormats & WAVE_FORMAT_4M16 ) info->sampleRates[info->nSampleRates++] = 44100;
04463     }
04464     else if ( info->nativeFormats & RTAUDIO_SINT8 ) {
04465       if( in_caps.dwFormats & WAVE_FORMAT_1M08 ) info->sampleRates[info->nSampleRates++] = 11025;
04466       if( in_caps.dwFormats & WAVE_FORMAT_2M08 ) info->sampleRates[info->nSampleRates++] = 22050;
04467       if( in_caps.dwFormats & WAVE_FORMAT_4M08 ) info->sampleRates[info->nSampleRates++] = 44100;
04468     }
04469   }
04470   else info->minInputChannels = 0; // technically, this would be an error
04471 
04472   input->Release();
04473 
04474  playback_probe:
04475 
04476   dsinfo.isValid = false;
04477 
04478   // Enumerate through output devices to find the id (if it exists).
04479   result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
04480   if ( FAILED(result) ) {
04481     sprintf(message, "RtAudio: Error performing output device id enumeration: %s.",
04482             getErrorString(result));
04483     error(RtError::WARNING);
04484     return;
04485   }
04486 
04487   // Now do playback probe.
04488   if ( dsinfo.isValid == false )
04489     goto check_parameters;
04490 
04491   LPDIRECTSOUND  output;
04492   DSCAPS out_caps;
04493   result = DirectSoundCreate( dsinfo.id, &output, NULL );
04494   if ( FAILED(result) ) {
04495     sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",
04496             info->name, getErrorString(result));
04497     error(RtError::WARNING);
04498     goto check_parameters;
04499   }
04500 
04501   out_caps.dwSize = sizeof(out_caps);
04502   result = output->GetCaps( &out_caps );
04503   if ( FAILED(result) ) {
04504     output->Release();
04505     sprintf(message, "RtAudio: Could not get DirectSound playback capabilities (%s): %s.",
04506             info->name, getErrorString(result));
04507     error(RtError::WARNING);
04508     goto check_parameters;
04509   }
04510 
04511   // Get output channel information.
04512   info->minOutputChannels = 1;
04513   info->maxOutputChannels = ( out_caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
04514 
04515   // Get sample rate information.  Use capture device rate information
04516   // if it exists.
04517   if ( info->nSampleRates == 0 ) {
04518     info->sampleRates[0] = (int) out_caps.dwMinSecondarySampleRate;
04519     info->sampleRates[1] = (int) out_caps.dwMaxSecondarySampleRate;
04520     if ( out_caps.dwFlags & DSCAPS_CONTINUOUSRATE )
04521       info->nSampleRates = -1;
04522     else if ( out_caps.dwMinSecondarySampleRate == out_caps.dwMaxSecondarySampleRate ) {
04523       if ( out_caps.dwMinSecondarySampleRate == 0 ) {
04524         // This is a bogus driver report ... fake the range and cross
04525         // your fingers.
04526         info->sampleRates[0] = 11025;
04527                                 info->sampleRates[1] = 48000;
04528         info->nSampleRates = -1; /* continuous range */
04529         sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using defaults (%s).",
04530                 info->name);
04531         error(RtError::DEBUG_WARNING);
04532       }
04533       else {
04534         info->nSampleRates = 1;
04535                         }
04536     }
04537     else if ( (out_caps.dwMinSecondarySampleRate < 1000.0) &&
04538               (out_caps.dwMaxSecondarySampleRate > 50000.0) ) {
04539       // This is a bogus driver report ... support for only two
04540       // distant rates.  We'll assume this is a range.
04541       info->nSampleRates = -1;
04542       sprintf(message, "RtAudio: bogus sample rates reported by DirectSound driver ... using range (%s).",
04543               info->name);
04544       error(RtError::WARNING);
04545     }
04546     else info->nSampleRates = 2;
04547   }
04548   else {
04549     // Check input rates against output rate range
04550     for ( int i=info->nSampleRates-1; i>=0; i-- ) {
04551       if ( info->sampleRates[i] <= out_caps.dwMaxSecondarySampleRate )
04552         break;
04553       info->nSampleRates--;
04554     }
04555     while ( info->sampleRates[0] < out_caps.dwMinSecondarySampleRate ) {
04556       info->nSampleRates--;
04557       for ( int i=0; i<info->nSampleRates; i++)
04558         info->sampleRates[i] = info->sampleRates[i+1];
04559       if ( info->nSampleRates <= 0 ) break;
04560     }
04561   }
04562 
04563   // Get format information.
04564   if ( out_caps.dwFlags & DSCAPS_PRIMARY16BIT ) info->nativeFormats |= RTAUDIO_SINT16;
04565   if ( out_caps.dwFlags & DSCAPS_PRIMARY8BIT ) info->nativeFormats |= RTAUDIO_SINT8;
04566 
04567   output->Release();
04568 
04569  check_parameters:
04570   if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
04571     return;
04572   if ( info->nSampleRates == 0 || info->nativeFormats == 0 )
04573     return;
04574 
04575   // Determine duplex status.
04576   if (info->maxInputChannels < info->maxOutputChannels)
04577     info->maxDuplexChannels = info->maxInputChannels;
04578   else
04579     info->maxDuplexChannels = info->maxOutputChannels;
04580   if (info->minInputChannels < info->minOutputChannels)
04581     info->minDuplexChannels = info->minInputChannels;
04582   else
04583     info->minDuplexChannels = info->minOutputChannels;
04584 
04585   if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
04586   else info->hasDuplexSupport = false;
04587 
04588   info->probed = true;
04589 
04590   return;
04591 }
04592 
04593 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
04594                                 STREAM_MODE mode, int channels, 
04595                                 int sampleRate, RTAUDIO_FORMAT format,
04596                                 int *bufferSize, int numberOfBuffers)
04597 {
04598   HRESULT result;
04599   HWND hWnd = GetForegroundWindow();
04600   // According to a note in PortAudio, using GetDesktopWindow()
04601   // instead of GetForegroundWindow() is supposed to avoid problems
04602   // that occur when the application's window is not the foreground
04603   // window.  Also, if the application window closes before the
04604   // DirectSound buffer, DirectSound can crash.  However, for console
04605   // applications, no sound was produced when using GetDesktopWindow().
04606   long buffer_size;
04607   LPVOID audioPtr;
04608   DWORD dataLen;
04609   int nBuffers;
04610 
04611   // Check the numberOfBuffers parameter and limit the lowest value to
04612   // two.  This is a judgement call and a value of two is probably too
04613   // low for capture, but it should work for playback.
04614   if (numberOfBuffers < 2)
04615     nBuffers = 2;
04616   else
04617     nBuffers = numberOfBuffers;
04618 
04619   // Define the wave format structure (16-bit PCM, srate, channels)
04620   WAVEFORMATEX waveFormat;
04621   ZeroMemory(&waveFormat, sizeof(WAVEFORMATEX));
04622   waveFormat.wFormatTag = WAVE_FORMAT_PCM;
04623   waveFormat.nChannels = channels;
04624   waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
04625 
04626   // Determine the data format.
04627   if ( devices[device].nativeFormats ) { // 8-bit and/or 16-bit support
04628     if ( format == RTAUDIO_SINT8 ) {
04629       if ( devices[device].nativeFormats & RTAUDIO_SINT8 )
04630         waveFormat.wBitsPerSample = 8;
04631       else
04632         waveFormat.wBitsPerSample = 16;
04633     }
04634     else {
04635       if ( devices[device].nativeFormats & RTAUDIO_SINT16 )
04636         waveFormat.wBitsPerSample = 16;
04637       else
04638         waveFormat.wBitsPerSample = 8;
04639     }
04640   }
04641   else {
04642     sprintf(message, "RtAudio: no reported data formats for DirectSound device (%s).",
04643             devices[device].name);
04644     error(RtError::DEBUG_WARNING);
04645     return FAILURE;
04646   }
04647 
04648   waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
04649   waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
04650 
04651   enum_info dsinfo;
04652   strncpy( dsinfo.name, devices[device].name, 64 );
04653   dsinfo.isValid = false;
04654   if ( mode == OUTPUT ) {
04655 
04656     if ( devices[device].maxOutputChannels < channels )
04657       return FAILURE;
04658 
04659     // Enumerate through output devices to find the id (if it exists).
04660     result = DirectSoundEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
04661     if ( FAILED(result) ) {
04662       sprintf(message, "RtAudio: Error performing output device id enumeration: %s.",
04663               getErrorString(result));
04664       error(RtError::DEBUG_WARNING);
04665       return FAILURE;
04666     }
04667 
04668     if ( dsinfo.isValid == false ) {
04669       sprintf(message, "RtAudio: DS output device (%s) id not found!", devices[device].name);
04670       error(RtError::DEBUG_WARNING);
04671       return FAILURE;
04672     }
04673 
04674     LPGUID id = dsinfo.id;
04675     LPDIRECTSOUND  object;
04676     LPDIRECTSOUNDBUFFER buffer;
04677     DSBUFFERDESC bufferDescription;
04678     
04679     result = DirectSoundCreate( id, &object, NULL );
04680     if ( FAILED(result) ) {
04681       sprintf(message, "RtAudio: Could not create DirectSound playback object (%s): %s.",
04682               devices[device].name, getErrorString(result));
04683       error(RtError::DEBUG_WARNING);
04684       return FAILURE;
04685     }
04686 
04687     // Set cooperative level to DSSCL_EXCLUSIVE
04688     result = object->SetCooperativeLevel(hWnd, DSSCL_EXCLUSIVE);
04689     if ( FAILED(result) ) {
04690       object->Release();
04691       sprintf(message, "RtAudio: Unable to set DirectSound cooperative level (%s): %s.",
04692               devices[device].name, getErrorString(result));
04693       error(RtError::WARNING);
04694       return FAILURE;
04695     }
04696 
04697     // Even though we will write to the secondary buffer, we need to
04698     // access the primary buffer to set the correct output format.
04699     // The default is 8-bit, 22 kHz!
04700     // Setup the DS primary buffer description.
04701     ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
04702     bufferDescription.dwSize = sizeof(DSBUFFERDESC);
04703     bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
04704     // Obtain the primary buffer
04705     result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
04706     if ( FAILED(result) ) {
04707       object->Release();
04708       sprintf(message, "RtAudio: Unable to access DS primary buffer (%s): %s.",
04709               devices[device].name, getErrorString(result));
04710       error(RtError::WARNING);
04711       return FAILURE;
04712     }
04713 
04714     // Set the primary DS buffer sound format.
04715     result = buffer->SetFormat(&waveFormat);
04716     if ( FAILED(result) ) {
04717       object->Release();
04718       sprintf(message, "RtAudio: Unable to set DS primary buffer format (%s): %s.",
04719               devices[device].name, getErrorString(result));
04720       error(RtError::WARNING);
04721       return FAILURE;
04722     }
04723 
04724     // Setup the secondary DS buffer description.
04725     buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
04726     ZeroMemory(&bufferDescription, sizeof(DSBUFFERDESC));
04727     bufferDescription.dwSize = sizeof(DSBUFFERDESC);
04728     bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
04729                                   DSBCAPS_GETCURRENTPOSITION2 |
04730                                   DSBCAPS_LOCHARDWARE );  // Force hardware mixing
04731     bufferDescription.dwBufferBytes = buffer_size;
04732     bufferDescription.lpwfxFormat = &waveFormat;
04733 
04734     // Try to create the secondary DS buffer.  If that doesn't work,
04735     // try to use software mixing.  Otherwise, there's a problem.
04736     result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
04737     if ( FAILED(result) ) {
04738       bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
04739                                     DSBCAPS_GETCURRENTPOSITION2 |
04740                                     DSBCAPS_LOCSOFTWARE );  // Force software mixing
04741       result = object->CreateSoundBuffer(&bufferDescription, &buffer, NULL);
04742       if ( FAILED(result) ) {
04743         object->Release();
04744         sprintf(message, "RtAudio: Unable to create secondary DS buffer (%s): %s.",
04745                 devices[device].name, getErrorString(result));
04746         error(RtError::WARNING);
04747         return FAILURE;
04748       }
04749     }
04750 
04751     // Get the buffer size ... might be different from what we specified.
04752     DSBCAPS dsbcaps;
04753     dsbcaps.dwSize = sizeof(DSBCAPS);
04754     buffer->GetCaps(&dsbcaps);
04755     buffer_size = dsbcaps.dwBufferBytes;
04756 
04757     // Lock the DS buffer
04758     result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
04759     if ( FAILED(result) ) {
04760       object->Release();
04761       sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",
04762               devices[device].name, getErrorString(result));
04763       error(RtError::WARNING);
04764       return FAILURE;
04765     }
04766 
04767     // Zero the DS buffer
04768     ZeroMemory(audioPtr, dataLen);
04769 
04770     // Unlock the DS buffer
04771     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
04772     if ( FAILED(result) ) {
04773       object->Release();
04774       sprintf(message, "RtAudio: Unable to unlock DS buffer(%s): %s.",
04775               devices[device].name, getErrorString(result));
04776       error(RtError::WARNING);
04777       return FAILURE;
04778     }
04779 
04780     stream->handle[0].object = (void *) object;
04781     stream->handle[0].buffer = (void *) buffer;
04782     stream->nDeviceChannels[0] = channels;
04783   }
04784 
04785   if ( mode == INPUT ) {
04786 
04787     if ( devices[device].maxInputChannels < channels )
04788       return FAILURE;
04789 
04790     // Enumerate through input devices to find the id (if it exists).
04791     result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK)deviceIdCallback, &dsinfo);
04792     if ( FAILED(result) ) {
04793       sprintf(message, "RtAudio: Error performing input device id enumeration: %s.",
04794               getErrorString(result));
04795       error(RtError::DEBUG_WARNING);
04796       return FAILURE;
04797     }
04798 
04799     if ( dsinfo.isValid == false ) {
04800       sprintf(message, "RtAudio: DS input device (%s) id not found!", devices[device].name);
04801       error(RtError::DEBUG_WARNING);
04802       return FAILURE;
04803     }
04804 
04805     LPGUID id = dsinfo.id;
04806     LPDIRECTSOUNDCAPTURE  object;
04807     LPDIRECTSOUNDCAPTUREBUFFER buffer;
04808     DSCBUFFERDESC bufferDescription;
04809 
04810     result = DirectSoundCaptureCreate( id, &object, NULL );
04811     if ( FAILED(result) ) {
04812       sprintf(message, "RtAudio: Could not create DirectSound capture object (%s): %s.",
04813               devices[device].name, getErrorString(result));
04814       error(RtError::WARNING);
04815       return FAILURE;
04816     }
04817 
04818     // Setup the secondary DS buffer description.
04819     buffer_size = channels * *bufferSize * nBuffers * waveFormat.wBitsPerSample / 8;
04820     ZeroMemory(&bufferDescription, sizeof(DSCBUFFERDESC));
04821     bufferDescription.dwSize = sizeof(DSCBUFFERDESC);
04822     bufferDescription.dwFlags = 0;
04823     bufferDescription.dwReserved = 0;
04824     bufferDescription.dwBufferBytes = buffer_size;
04825     bufferDescription.lpwfxFormat = &waveFormat;
04826 
04827     // Create the capture buffer.
04828     result = object->CreateCaptureBuffer(&bufferDescription, &buffer, NULL);
04829     if ( FAILED(result) ) {
04830       object->Release();
04831       sprintf(message, "RtAudio: Unable to create DS capture buffer (%s): %s.",
04832               devices[device].name, getErrorString(result));
04833       error(RtError::WARNING);
04834       return FAILURE;
04835     }
04836 
04837     // Lock the capture buffer
04838     result = buffer->Lock(0, buffer_size, &audioPtr, &dataLen, NULL, NULL, 0);
04839     if ( FAILED(result) ) {
04840       object->Release();
04841       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
04842               devices[device].name, getErrorString(result));
04843       error(RtError::WARNING);
04844       return FAILURE;
04845     }
04846 
04847     // Zero the buffer
04848     ZeroMemory(audioPtr, dataLen);
04849 
04850     // Unlock the buffer
04851     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
04852     if ( FAILED(result) ) {
04853       object->Release();
04854       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
04855               devices[device].name, getErrorString(result));
04856       error(RtError::WARNING);
04857       return FAILURE;
04858     }
04859 
04860     stream->handle[1].object = (void *) object;
04861     stream->handle[1].buffer = (void *) buffer;
04862     stream->nDeviceChannels[1] = channels;
04863   }
04864 
04865   stream->userFormat = format;
04866   if ( waveFormat.wBitsPerSample == 8 )
04867     stream->deviceFormat[mode] = RTAUDIO_SINT8;
04868   else
04869     stream->deviceFormat[mode] = RTAUDIO_SINT16;
04870   stream->nUserChannels[mode] = channels;
04871   *bufferSize = buffer_size / (channels * nBuffers * waveFormat.wBitsPerSample / 8);
04872   stream->bufferSize = *bufferSize;
04873 
04874   // Set flags for buffer conversion
04875   stream->doConvertBuffer[mode] = false;
04876   if (stream->userFormat != stream->deviceFormat[mode])
04877     stream->doConvertBuffer[mode] = true;
04878   if (stream->nUserChannels[mode] < stream->nDeviceChannels[mode])
04879     stream->doConvertBuffer[mode] = true;
04880 
04881   // Allocate necessary internal buffers
04882   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
04883 
04884     long buffer_bytes;
04885     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
04886       buffer_bytes = stream->nUserChannels[0];
04887     else
04888       buffer_bytes = stream->nUserChannels[1];
04889 
04890     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
04891     if (stream->userBuffer) free(stream->userBuffer);
04892     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
04893     if (stream->userBuffer == NULL)
04894       goto memory_error;
04895   }
04896 
04897   if ( stream->doConvertBuffer[mode] ) {
04898 
04899     long buffer_bytes;
04900     bool makeBuffer = true;
04901     if ( mode == OUTPUT )
04902       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
04903     else { // mode == INPUT
04904       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
04905       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
04906         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
04907         if ( buffer_bytes < bytes_out ) makeBuffer = false;
04908       }
04909     }
04910 
04911     if ( makeBuffer ) {
04912       buffer_bytes *= *bufferSize;
04913       if (stream->deviceBuffer) free(stream->deviceBuffer);
04914       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
04915       if (stream->deviceBuffer == NULL)
04916         goto memory_error;
04917     }
04918   }
04919 
04920   stream->device[mode] = device;
04921   stream->state = STREAM_STOPPED;
04922   if ( stream->mode == OUTPUT && mode == INPUT )
04923     // We had already set up an output stream.
04924     stream->mode = DUPLEX;
04925   else
04926     stream->mode = mode;
04927   stream->nBuffers = nBuffers;
04928   stream->sampleRate = sampleRate;
04929 
04930   return SUCCESS;
04931 
04932  memory_error:
04933   if (stream->handle[0].object) {
04934     LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;
04935     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
04936     if (buffer) {
04937       buffer->Release();
04938       stream->handle[0].buffer = NULL;
04939     }
04940     object->Release();
04941     stream->handle[0].object = NULL;
04942   }
04943   if (stream->handle[1].object) {
04944     LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;
04945     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
04946     if (buffer) {
04947       buffer->Release();
04948       stream->handle[1].buffer = NULL;
04949     }
04950     object->Release();
04951     stream->handle[1].object = NULL;
04952   }
04953   if (stream->userBuffer) {
04954     free(stream->userBuffer);
04955     stream->userBuffer = 0;
04956   }
04957   sprintf(message, "RtAudio: error allocating buffer memory (%s).",
04958           devices[device].name);
04959   error(RtError::WARNING);
04960   return FAILURE;
04961 }
04962 
04963 void RtAudio :: cancelStreamCallback(int streamId)
04964 {
04965   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
04966 
04967   if (stream->callbackInfo.usingCallback) {
04968 
04969     if (stream->state == STREAM_RUNNING)
04970       stopStream( streamId );
04971 
04972     MUTEX_LOCK(&stream->mutex);
04973 
04974     stream->callbackInfo.usingCallback = false;
04975     WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
04976     CloseHandle( (HANDLE)stream->callbackInfo.thread );
04977     stream->callbackInfo.thread = 0;
04978     stream->callbackInfo.callback = NULL;
04979     stream->callbackInfo.userData = NULL;
04980 
04981     MUTEX_UNLOCK(&stream->mutex);
04982   }
04983 }
04984 
04985 void RtAudio :: closeStream(int streamId)
04986 {
04987   // We don't want an exception to be thrown here because this
04988   // function is called by our class destructor.  So, do our own
04989   // streamId check.
04990   if ( streams.find( streamId ) == streams.end() ) {
04991     sprintf(message, "RtAudio: invalid stream identifier!");
04992     error(RtError::WARNING);
04993     return;
04994   }
04995 
04996   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
04997 
04998   if (stream->callbackInfo.usingCallback) {
04999     stream->callbackInfo.usingCallback = false;
05000     WaitForSingleObject( (HANDLE)stream->callbackInfo.thread, INFINITE );
05001     CloseHandle( (HANDLE)stream->callbackInfo.thread );
05002   }
05003 
05004   DeleteCriticalSection(&stream->mutex);
05005 
05006   if (stream->handle[0].object) {
05007     LPDIRECTSOUND object = (LPDIRECTSOUND) stream->handle[0].object;
05008     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05009     if (buffer) {
05010       buffer->Stop();
05011       buffer->Release();
05012     }
05013     object->Release();
05014   }
05015 
05016   if (stream->handle[1].object) {
05017     LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) stream->handle[1].object;
05018     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05019     if (buffer) {
05020       buffer->Stop();
05021       buffer->Release();
05022     }
05023     object->Release();
05024   }
05025 
05026   if (stream->userBuffer)
05027     free(stream->userBuffer);
05028 
05029   if (stream->deviceBuffer)
05030     free(stream->deviceBuffer);
05031 
05032   free(stream);
05033   streams.erase(streamId);
05034 }
05035 
05036 void RtAudio :: startStream(int streamId)
05037 {
05038   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05039 
05040   MUTEX_LOCK(&stream->mutex);
05041 
05042   if (stream->state == STREAM_RUNNING)
05043     goto unlock;
05044 
05045   HRESULT result;
05046   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
05047     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05048     result = buffer->Play(0, 0, DSBPLAY_LOOPING );
05049     if ( FAILED(result) ) {
05050       sprintf(message, "RtAudio: Unable to start DS buffer (%s): %s.",
05051               devices[stream->device[0]].name, getErrorString(result));
05052       error(RtError::DRIVER_ERROR);
05053     }
05054   }
05055 
05056   if (stream->mode == INPUT || stream->mode == DUPLEX) {
05057     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05058     result = buffer->Start(DSCBSTART_LOOPING );
05059     if ( FAILED(result) ) {
05060       sprintf(message, "RtAudio: Unable to start DS capture buffer (%s): %s.",
05061               devices[stream->device[1]].name, getErrorString(result));
05062       error(RtError::DRIVER_ERROR);
05063     }
05064   }
05065   stream->state = STREAM_RUNNING;
05066 
05067  unlock:
05068   MUTEX_UNLOCK(&stream->mutex);
05069 }
05070 
05071 void RtAudio :: stopStream(int streamId)
05072 {
05073   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05074 
05075   MUTEX_LOCK(&stream->mutex);
05076 
05077   if (stream->state == STREAM_STOPPED) {
05078     MUTEX_UNLOCK(&stream->mutex);
05079     return;
05080   }
05081 
05082   // There is no specific DirectSound API call to "drain" a buffer
05083   // before stopping.  We can hack this for playback by writing zeroes
05084   // for another bufferSize * nBuffers frames.  For capture, the
05085   // concept is less clear so we'll repeat what we do in the
05086   // abortStream() case.
05087   HRESULT result;
05088   DWORD dsBufferSize;
05089   LPVOID buffer1 = NULL;
05090   LPVOID buffer2 = NULL;
05091   DWORD bufferSize1 = 0;
05092   DWORD bufferSize2 = 0;
05093   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
05094 
05095     DWORD currentPos, safePos;
05096     long buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
05097     buffer_bytes *= formatBytes(stream->deviceFormat[0]);
05098 
05099     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05100     UINT nextWritePos = stream->handle[0].bufferPointer;
05101     dsBufferSize = buffer_bytes * stream->nBuffers;
05102 
05103     // Write zeroes for nBuffer counts.
05104     for (int i=0; i<stream->nBuffers; i++) {
05105 
05106       // Find out where the read and "safe write" pointers are.
05107       result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
05108       if ( FAILED(result) ) {
05109         sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
05110                 devices[stream->device[0]].name, getErrorString(result));
05111         error(RtError::DRIVER_ERROR);
05112       }
05113 
05114       if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
05115       DWORD endWrite = nextWritePos + buffer_bytes;
05116 
05117       // Check whether the entire write region is behind the play pointer.
05118       while ( currentPos < endWrite ) {
05119         float millis = (endWrite - currentPos) * 900.0;
05120         millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);
05121         if ( millis < 1.0 ) millis = 1.0;
05122         Sleep( (DWORD) millis );
05123 
05124         // Wake up, find out where we are now
05125         result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
05126         if ( FAILED(result) ) {
05127           sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
05128                   devices[stream->device[0]].name, getErrorString(result));
05129           error(RtError::DRIVER_ERROR);
05130         }
05131         if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
05132       }
05133 
05134       // Lock free space in the buffer
05135       result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,
05136                                &bufferSize1, &buffer2, &bufferSize2, 0);
05137       if ( FAILED(result) ) {
05138         sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",
05139                 devices[stream->device[0]].name, getErrorString(result));
05140         error(RtError::DRIVER_ERROR);
05141       }
05142 
05143       // Zero the free space
05144       ZeroMemory(buffer1, bufferSize1);
05145       if (buffer2 != NULL) ZeroMemory(buffer2, bufferSize2);
05146 
05147       // Update our buffer offset and unlock sound buffer
05148       dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
05149       if ( FAILED(result) ) {
05150         sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",
05151                 devices[stream->device[0]].name, getErrorString(result));
05152         error(RtError::DRIVER_ERROR);
05153       }
05154       nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
05155       stream->handle[0].bufferPointer = nextWritePos;
05156     }
05157 
05158     // If we play again, start at the beginning of the buffer.
05159     stream->handle[0].bufferPointer = 0;
05160   }
05161 
05162   if (stream->mode == INPUT || stream->mode == DUPLEX) {
05163     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05164     buffer1 = NULL;
05165     bufferSize1 = 0;
05166 
05167     result = buffer->Stop();
05168     if ( FAILED(result) ) {
05169       sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",
05170               devices[stream->device[1]].name, getErrorString(result));
05171       error(RtError::DRIVER_ERROR);
05172     }
05173 
05174     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
05175     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
05176 
05177     // Lock the buffer and clear it so that if we start to play again,
05178     // we won't have old data playing.
05179     result = buffer->Lock(0, dsBufferSize, &buffer1, &bufferSize1, NULL, NULL, 0);
05180     if ( FAILED(result) ) {
05181       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
05182               devices[stream->device[1]].name, getErrorString(result));
05183       error(RtError::DRIVER_ERROR);
05184     }
05185 
05186     // Zero the DS buffer
05187     ZeroMemory(buffer1, bufferSize1);
05188 
05189     // Unlock the DS buffer
05190     result = buffer->Unlock(buffer1, bufferSize1, NULL, 0);
05191     if ( FAILED(result) ) {
05192       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
05193               devices[stream->device[1]].name, getErrorString(result));
05194       error(RtError::DRIVER_ERROR);
05195     }
05196 
05197     // If we start recording again, we must begin at beginning of buffer.
05198     stream->handle[1].bufferPointer = 0;
05199   }
05200   stream->state = STREAM_STOPPED;
05201 
05202   MUTEX_UNLOCK(&stream->mutex);
05203 }
05204 
05205 void RtAudio :: abortStream(int streamId)
05206 {
05207   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05208 
05209   MUTEX_LOCK(&stream->mutex);
05210 
05211   if (stream->state == STREAM_STOPPED)
05212     goto unlock;
05213 
05214   HRESULT result;
05215   long dsBufferSize;
05216   LPVOID audioPtr;
05217   DWORD dataLen;
05218   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
05219     LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05220     result = buffer->Stop();
05221     if ( FAILED(result) ) {
05222       sprintf(message, "RtAudio: Unable to stop DS buffer (%s): %s",
05223               devices[stream->device[0]].name, getErrorString(result));
05224       error(RtError::DRIVER_ERROR);
05225     }
05226 
05227     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[0];
05228     dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
05229 
05230     // Lock the buffer and clear it so that if we start to play again,
05231     // we won't have old data playing.
05232     result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
05233     if ( FAILED(result) ) {
05234       sprintf(message, "RtAudio: Unable to lock DS buffer (%s): %s.",
05235               devices[stream->device[0]].name, getErrorString(result));
05236       error(RtError::DRIVER_ERROR);
05237     }
05238 
05239     // Zero the DS buffer
05240     ZeroMemory(audioPtr, dataLen);
05241 
05242     // Unlock the DS buffer
05243     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
05244     if ( FAILED(result) ) {
05245       sprintf(message, "RtAudio: Unable to unlock DS buffer (%s): %s.",
05246               devices[stream->device[0]].name, getErrorString(result));
05247       error(RtError::DRIVER_ERROR);
05248     }
05249 
05250     // If we start playing again, we must begin at beginning of buffer.
05251     stream->handle[0].bufferPointer = 0;
05252   }
05253 
05254   if (stream->mode == INPUT || stream->mode == DUPLEX) {
05255     LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05256     audioPtr = NULL;
05257     dataLen = 0;
05258 
05259     result = buffer->Stop();
05260     if ( FAILED(result) ) {
05261       sprintf(message, "RtAudio: Unable to stop DS capture buffer (%s): %s",
05262               devices[stream->device[1]].name, getErrorString(result));
05263       error(RtError::DRIVER_ERROR);
05264     }
05265 
05266     dsBufferSize = stream->bufferSize * stream->nDeviceChannels[1];
05267     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
05268 
05269     // Lock the buffer and clear it so that if we start to play again,
05270     // we won't have old data playing.
05271     result = buffer->Lock(0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0);
05272     if ( FAILED(result) ) {
05273       sprintf(message, "RtAudio: Unable to lock DS capture buffer (%s): %s.",
05274               devices[stream->device[1]].name, getErrorString(result));
05275       error(RtError::DRIVER_ERROR);
05276     }
05277 
05278     // Zero the DS buffer
05279     ZeroMemory(audioPtr, dataLen);
05280 
05281     // Unlock the DS buffer
05282     result = buffer->Unlock(audioPtr, dataLen, NULL, 0);
05283     if ( FAILED(result) ) {
05284       sprintf(message, "RtAudio: Unable to unlock DS capture buffer (%s): %s.",
05285               devices[stream->device[1]].name, getErrorString(result));
05286       error(RtError::DRIVER_ERROR);
05287     }
05288 
05289     // If we start recording again, we must begin at beginning of buffer.
05290     stream->handle[1].bufferPointer = 0;
05291   }
05292   stream->state = STREAM_STOPPED;
05293 
05294  unlock:
05295   MUTEX_UNLOCK(&stream->mutex);
05296 }
05297 
05298 int RtAudio :: streamWillBlock(int streamId)
05299 {
05300   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05301 
05302   MUTEX_LOCK(&stream->mutex);
05303 
05304   int channels;
05305   int frames = 0;
05306   if (stream->state == STREAM_STOPPED)
05307     goto unlock;
05308 
05309   HRESULT result;
05310   DWORD currentPos, safePos;
05311   channels = 1;
05312   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
05313 
05314     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05315     UINT nextWritePos = stream->handle[0].bufferPointer;
05316     channels = stream->nDeviceChannels[0];
05317     DWORD dsBufferSize = stream->bufferSize * channels;
05318     dsBufferSize *= formatBytes(stream->deviceFormat[0]) * stream->nBuffers;
05319 
05320     // Find out where the read and "safe write" pointers are.
05321     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
05322     if ( FAILED(result) ) {
05323       sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
05324               devices[stream->device[0]].name, getErrorString(result));
05325       error(RtError::DRIVER_ERROR);
05326     }
05327 
05328     if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
05329     frames = currentPos - nextWritePos;
05330     frames /= channels * formatBytes(stream->deviceFormat[0]);
05331   }
05332 
05333   if (stream->mode == INPUT || stream->mode == DUPLEX) {
05334 
05335     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05336     UINT nextReadPos = stream->handle[1].bufferPointer;
05337     channels = stream->nDeviceChannels[1];
05338     DWORD dsBufferSize = stream->bufferSize * channels;
05339     dsBufferSize *= formatBytes(stream->deviceFormat[1]) * stream->nBuffers;
05340 
05341     // Find out where the write and "safe read" pointers are.
05342     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
05343     if ( FAILED(result) ) {
05344       sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
05345               devices[stream->device[1]].name, getErrorString(result));
05346       error(RtError::DRIVER_ERROR);
05347     }
05348 
05349     if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
05350 
05351     if (stream->mode == DUPLEX ) {
05352       // Take largest value of the two.
05353       int temp = safePos - nextReadPos;
05354       temp /= channels * formatBytes(stream->deviceFormat[1]);
05355       frames = ( temp > frames ) ? temp : frames;
05356     }
05357     else {
05358       frames = safePos - nextReadPos;
05359       frames /= channels * formatBytes(stream->deviceFormat[1]);
05360     }
05361   }
05362 
05363   frames = stream->bufferSize - frames;
05364   if (frames < 0) frames = 0;
05365 
05366  unlock:
05367   MUTEX_UNLOCK(&stream->mutex);
05368   return frames;
05369 }
05370 
05371 void RtAudio :: tickStream(int streamId)
05372 {
05373   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05374 
05375   int stopStream = 0;
05376   if (stream->state == STREAM_STOPPED) {
05377     if (stream->callbackInfo.usingCallback) Sleep(50); // sleep 50 milliseconds
05378     return;
05379   }
05380   else if (stream->callbackInfo.usingCallback) {
05381     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
05382     stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
05383   }
05384 
05385   MUTEX_LOCK(&stream->mutex);
05386 
05387   // The state might change while waiting on a mutex.
05388   if (stream->state == STREAM_STOPPED) {
05389     MUTEX_UNLOCK(&stream->mutex);
05390     return;
05391   }
05392 
05393   HRESULT result;
05394   DWORD currentPos, safePos;
05395   LPVOID buffer1 = NULL;
05396   LPVOID buffer2 = NULL;
05397   DWORD bufferSize1 = 0;
05398   DWORD bufferSize2 = 0;
05399   char *buffer;
05400   long buffer_bytes;
05401   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
05402 
05403     // Setup parameters and do buffer conversion if necessary.
05404     if (stream->doConvertBuffer[0]) {
05405       convertStreamBuffer(stream, OUTPUT);
05406       buffer = stream->deviceBuffer;
05407       buffer_bytes = stream->bufferSize * stream->nDeviceChannels[0];
05408       buffer_bytes *= formatBytes(stream->deviceFormat[0]);
05409     }
05410     else {
05411       buffer = stream->userBuffer;
05412       buffer_bytes = stream->bufferSize * stream->nUserChannels[0];
05413       buffer_bytes *= formatBytes(stream->userFormat);
05414     }
05415 
05416     // No byte swapping necessary in DirectSound implementation.
05417 
05418     LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) stream->handle[0].buffer;
05419     UINT nextWritePos = stream->handle[0].bufferPointer;
05420     DWORD dsBufferSize = buffer_bytes * stream->nBuffers;
05421 
05422     // Find out where the read and "safe write" pointers are.
05423     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
05424     if ( FAILED(result) ) {
05425       sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
05426               devices[stream->device[0]].name, getErrorString(result));
05427       error(RtError::DRIVER_ERROR);
05428     }
05429 
05430     if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
05431     DWORD endWrite = nextWritePos + buffer_bytes;
05432 
05433     // Check whether the entire write region is behind the play pointer.
05434     while ( currentPos < endWrite ) {
05435       // If we are here, then we must wait until the play pointer gets
05436       // beyond the write region.  The approach here is to use the
05437       // Sleep() function to suspend operation until safePos catches
05438       // up. Calculate number of milliseconds to wait as:
05439       //   time = distance * (milliseconds/second) * fudgefactor /
05440       //          ((bytes/sample) * (samples/second))
05441       // A "fudgefactor" less than 1 is used because it was found
05442       // that sleeping too long was MUCH worse than sleeping for
05443       // several shorter periods.
05444       float millis = (endWrite - currentPos) * 900.0;
05445       millis /= ( formatBytes(stream->deviceFormat[0]) * stream->sampleRate);
05446       if ( millis < 1.0 ) millis = 1.0;
05447       Sleep( (DWORD) millis );
05448 
05449       // Wake up, find out where we are now
05450       result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
05451       if ( FAILED(result) ) {
05452         sprintf(message, "RtAudio: Unable to get current DS position (%s): %s.",
05453               devices[stream->device[0]].name, getErrorString(result));
05454         error(RtError::DRIVER_ERROR);
05455       }
05456       if ( currentPos < nextWritePos ) currentPos += dsBufferSize; // unwrap offset
05457     }
05458 
05459     // Lock free space in the buffer
05460     result = dsBuffer->Lock (nextWritePos, buffer_bytes, &buffer1,
05461                              &bufferSize1, &buffer2, &bufferSize2, 0);
05462     if ( FAILED(result) ) {
05463       sprintf(message, "RtAudio: Unable to lock DS buffer during playback (%s): %s.",
05464               devices[stream->device[0]].name, getErrorString(result));
05465       error(RtError::DRIVER_ERROR);
05466     }
05467 
05468     // Copy our buffer into the DS buffer
05469     CopyMemory(buffer1, buffer, bufferSize1);
05470     if (buffer2 != NULL) CopyMemory(buffer2, buffer+bufferSize1, bufferSize2);
05471 
05472     // Update our buffer offset and unlock sound buffer
05473     dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
05474     if ( FAILED(result) ) {
05475       sprintf(message, "RtAudio: Unable to unlock DS buffer during playback (%s): %s.",
05476               devices[stream->device[0]].name, getErrorString(result));
05477       error(RtError::DRIVER_ERROR);
05478     }
05479     nextWritePos = (nextWritePos + bufferSize1 + bufferSize2) % dsBufferSize;
05480     stream->handle[0].bufferPointer = nextWritePos;
05481   }
05482 
05483   if (stream->mode == INPUT || stream->mode == DUPLEX) {
05484 
05485     // Setup parameters.
05486     if (stream->doConvertBuffer[1]) {
05487       buffer = stream->deviceBuffer;
05488       buffer_bytes = stream->bufferSize * stream->nDeviceChannels[1];
05489       buffer_bytes *= formatBytes(stream->deviceFormat[1]);
05490     }
05491     else {
05492       buffer = stream->userBuffer;
05493       buffer_bytes = stream->bufferSize * stream->nUserChannels[1];
05494       buffer_bytes *= formatBytes(stream->userFormat);
05495     }
05496 
05497     LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) stream->handle[1].buffer;
05498     UINT nextReadPos = stream->handle[1].bufferPointer;
05499     DWORD dsBufferSize = buffer_bytes * stream->nBuffers;
05500 
05501     // Find out where the write and "safe read" pointers are.
05502     result = dsBuffer->GetCurrentPosition(&currentPos, &safePos);
05503     if ( FAILED(result) ) {
05504       sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
05505               devices[stream->device[1]].name, getErrorString(result));
05506       error(RtError::DRIVER_ERROR);
05507     }
05508 
05509     if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
05510     DWORD endRead = nextReadPos + buffer_bytes;
05511 
05512     // Check whether the entire write region is behind the play pointer.
05513     while ( safePos < endRead ) {
05514       // See comments for playback.
05515       float millis = (endRead - safePos) * 900.0;
05516       millis /= ( formatBytes(stream->deviceFormat[1]) * stream->sampleRate);
05517       if ( millis < 1.0 ) millis = 1.0;
05518       Sleep( (DWORD) millis );
05519 
05520       // Wake up, find out where we are now
05521       result = dsBuffer->GetCurrentPosition( &currentPos, &safePos );
05522       if ( FAILED(result) ) {
05523         sprintf(message, "RtAudio: Unable to get current DS capture position (%s): %s.",
05524                 devices[stream->device[1]].name, getErrorString(result));
05525         error(RtError::DRIVER_ERROR);
05526       }
05527       
05528       if ( safePos < nextReadPos ) safePos += dsBufferSize; // unwrap offset
05529     }
05530 
05531     // Lock free space in the buffer
05532     result = dsBuffer->Lock (nextReadPos, buffer_bytes, &buffer1,
05533                              &bufferSize1, &buffer2, &bufferSize2, 0);
05534     if ( FAILED(result) ) {
05535       sprintf(message, "RtAudio: Unable to lock DS buffer during capture (%s): %s.",
05536               devices[stream->device[1]].name, getErrorString(result));
05537       error(RtError::DRIVER_ERROR);
05538     }
05539 
05540     // Copy our buffer into the DS buffer
05541     CopyMemory(buffer, buffer1, bufferSize1);
05542     if (buffer2 != NULL) CopyMemory(buffer+bufferSize1, buffer2, bufferSize2);
05543 
05544     // Update our buffer offset and unlock sound buffer
05545     nextReadPos = (nextReadPos + bufferSize1 + bufferSize2) % dsBufferSize;
05546     dsBuffer->Unlock (buffer1, bufferSize1, buffer2, bufferSize2);
05547     if ( FAILED(result) ) {
05548       sprintf(message, "RtAudio: Unable to unlock DS buffer during capture (%s): %s.",
05549               devices[stream->device[1]].name, getErrorString(result));
05550       error(RtError::DRIVER_ERROR);
05551     }
05552     stream->handle[1].bufferPointer = nextReadPos;
05553 
05554     // No byte swapping necessary in DirectSound implementation.
05555 
05556     // Do buffer conversion if necessary.
05557     if (stream->doConvertBuffer[1])
05558       convertStreamBuffer(stream, INPUT);
05559   }
05560 
05561   MUTEX_UNLOCK(&stream->mutex);
05562 
05563   if (stream->callbackInfo.usingCallback && stopStream)
05564     this->stopStream(streamId);
05565 }
05566 
05567 // Definitions for utility functions and callbacks
05568 // specific to the DirectSound implementation.
05569 
05570 extern "C" unsigned __stdcall callbackHandler(void *ptr)
05571 {
05572   CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
05573   RtAudio *object = (RtAudio *) info->object;
05574   int stream = info->streamId;
05575   bool *usingCallback = &info->usingCallback;
05576 
05577   while ( *usingCallback ) {
05578     try {
05579       object->tickStream(stream);
05580     }
05581     catch (RtError &exception) {
05582       fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
05583               exception.getMessage());
05584       break;
05585     }
05586   }
05587 
05588   _endthreadex( 0 );
05589   return 0;
05590 }
05591 
05592 void RtAudio :: setStreamCallback(int streamId, RTAUDIO_CALLBACK callback, void *userData)
05593 {
05594   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
05595 
05596   CALLBACK_INFO *info = (CALLBACK_INFO *) &stream->callbackInfo;
05597   if ( info->usingCallback ) {
05598     sprintf(message, "RtAudio: A callback is already set for this stream!");
05599     error(RtError::WARNING);
05600     return;
05601   }
05602 
05603   info->callback = (void *) callback;
05604   info->userData = userData;
05605   info->usingCallback = true;
05606   info->object = (void *) this;
05607   info->streamId = streamId;
05608 
05609   unsigned thread_id;
05610   info->thread = _beginthreadex(NULL, 0, &callbackHandler,
05611                                 &stream->callbackInfo, 0, &thread_id);
05612   if (info->thread == 0) {
05613     info->usingCallback = false;
05614     sprintf(message, "RtAudio: error starting callback thread!");
05615     error(RtError::THREAD_ERROR);
05616   }
05617 
05618   // When spawning multiple threads in quick succession, it appears to be
05619   // necessary to wait a bit for each to initialize ... another windoism!
05620   Sleep(1);
05621 }
05622 
05623 static bool CALLBACK deviceCountCallback(LPGUID lpguid,
05624                                          LPCSTR lpcstrDescription,
05625                                          LPCSTR lpcstrModule,
05626                                          LPVOID lpContext)
05627 {
05628   int *pointer = ((int *) lpContext);
05629   (*pointer)++;
05630 
05631   return true;
05632 }
05633 
05634 static bool CALLBACK deviceInfoCallback(LPGUID lpguid,
05635                                         LPCSTR lpcstrDescription,
05636                                         LPCSTR lpcstrModule,
05637                                         LPVOID lpContext)
05638 {
05639   enum_info *info = ((enum_info *) lpContext);
05640   while (strlen(info->name) > 0) info++;
05641 
05642   strncpy(info->name, lpcstrDescription, 64);
05643   info->id = lpguid;
05644 
05645         HRESULT    hr;
05646   info->isValid = false;
05647   if (info->isInput == true) {
05648     DSCCAPS               caps;
05649     LPDIRECTSOUNDCAPTURE  object;
05650 
05651     hr = DirectSoundCaptureCreate(  lpguid, &object,   NULL );
05652     if( hr != DS_OK ) return true;
05653 
05654     caps.dwSize = sizeof(caps);
05655     hr = object->GetCaps( &caps );
05656     if( hr == DS_OK ) {
05657       if (caps.dwChannels > 0 && caps.dwFormats > 0)
05658         info->isValid = true;
05659     }
05660     object->Release();
05661   }
05662   else {
05663     DSCAPS         caps;
05664     LPDIRECTSOUND  object;
05665     hr = DirectSoundCreate(  lpguid, &object,   NULL );
05666     if( hr != DS_OK ) return true;
05667 
05668     caps.dwSize = sizeof(caps);
05669     hr = object->GetCaps( &caps );
05670     if( hr == DS_OK ) {
05671       if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
05672         info->isValid = true;
05673     }
05674     object->Release();
05675   }
05676 
05677   return true;
05678 }
05679 
05680 static bool CALLBACK defaultDeviceCallback(LPGUID lpguid,
05681                                            LPCSTR lpcstrDescription,
05682                                            LPCSTR lpcstrModule,
05683                                            LPVOID lpContext)
05684 {
05685   enum_info *info = ((enum_info *) lpContext);
05686 
05687   if ( lpguid == NULL ) {
05688     strncpy(info->name, lpcstrDescription, 64);
05689     return false;
05690   }
05691 
05692   return true;
05693 }
05694 
05695 static bool CALLBACK deviceIdCallback(LPGUID lpguid,
05696                                       LPCSTR lpcstrDescription,
05697                                       LPCSTR lpcstrModule,
05698                                       LPVOID lpContext)
05699 {
05700   enum_info *info = ((enum_info *) lpContext);
05701 
05702   if ( strncmp( info->name, lpcstrDescription, 64 ) == 0 ) {
05703     info->id = lpguid;
05704     info->isValid = true;
05705     return false;
05706   }
05707 
05708   return true;
05709 }
05710 
05711 static char* getErrorString(int code)
05712 {
05713         switch (code) {
05714 
05715   case DSERR_ALLOCATED:
05716     return "Direct Sound already allocated";
05717 
05718   case DSERR_CONTROLUNAVAIL:
05719     return "Direct Sound control unavailable";
05720 
05721   case DSERR_INVALIDPARAM:
05722     return "Direct Sound invalid parameter";
05723 
05724   case DSERR_INVALIDCALL:
05725     return "Direct Sound invalid call";
05726 
05727   case DSERR_GENERIC:
05728     return "Direct Sound generic error";
05729 
05730   case DSERR_PRIOLEVELNEEDED:
05731     return "Direct Sound Priority level needed";
05732 
05733   case DSERR_OUTOFMEMORY:
05734     return "Direct Sound out of memory";
05735 
05736   case DSERR_BADFORMAT:
05737     return "Direct Sound bad format";
05738 
05739   case DSERR_UNSUPPORTED:
05740     return "Direct Sound unsupported error";
05741 
05742   case DSERR_NODRIVER:
05743     return "Direct Sound no driver error";
05744 
05745   case DSERR_ALREADYINITIALIZED:
05746     return "Direct Sound already initialized";
05747 
05748   case DSERR_NOAGGREGATION:
05749     return "Direct Sound no aggregation";
05750 
05751   case DSERR_BUFFERLOST:
05752     return "Direct Sound buffer lost";
05753 
05754   case DSERR_OTHERAPPHASPRIO:
05755     return "Direct Sound other app has priority";
05756 
05757   case DSERR_UNINITIALIZED:
05758     return "Direct Sound uninitialized";
05759 
05760   default:
05761     return "Direct Sound unknown error";
05762         }
05763 }
05764 
05765 //******************** End of __WINDOWS_DS__ *********************//
05766 
05767 #elif defined(__IRIX_AL__) // SGI's AL API for IRIX
05768 
05769 #include <unistd.h>
05770 #include <errno.h>
05771 
05772 void RtAudio :: initialize(void)
05773 {
05774   // Count cards and devices
05775   nDevices = 0;
05776 
05777   // Determine the total number of input and output devices.
05778   nDevices = alQueryValues(AL_SYSTEM, AL_DEVICES, 0, 0, 0, 0);
05779   if (nDevices < 0) {
05780     sprintf(message, "RtAudio: AL error counting devices: %s.",
05781             alGetErrorString(oserror()));
05782     error(RtError::DRIVER_ERROR);
05783   }
05784 
05785   if (nDevices <= 0) return;
05786 
05787   ALvalue *vls = (ALvalue *) new ALvalue[nDevices];
05788 
05789   //  Allocate the RTAUDIO_DEVICE structures.
05790   devices = (RTAUDIO_DEVICE *) calloc(nDevices, sizeof(RTAUDIO_DEVICE));
05791   if (devices == NULL) {
05792     sprintf(message, "RtAudio: memory allocation error!");
05793     error(RtError::MEMORY_ERROR);
05794   }
05795 
05796   // Write device ascii identifiers and resource ids to device info
05797   // structure.
05798   char name[32];
05799   int outs, ins, i;
05800   ALpv pvs[1];
05801   pvs[0].param = AL_NAME;
05802   pvs[0].value.ptr = name;
05803   pvs[0].sizeIn = 32;
05804 
05805   outs = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, vls, nDevices, 0, 0);
05806   if (outs < 0) {
05807     sprintf(message, "RtAudio: AL error getting output devices: %s.",
05808             alGetErrorString(oserror()));
05809     error(RtError::DRIVER_ERROR);
05810   }
05811 
05812   for (i=0; i<outs; i++) {
05813     if (alGetParams(vls[i].i, pvs, 1) < 0) {
05814       sprintf(message, "RtAudio: AL error querying output devices: %s.",
05815               alGetErrorString(oserror()));
05816       error(RtError::DRIVER_ERROR);
05817     }
05818     strncpy(devices[i].name, name, 32);
05819     devices[i].id[0] = vls[i].i;
05820   }
05821 
05822   ins = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &vls[outs], nDevices-outs, 0, 0);
05823   if (ins < 0) {
05824     sprintf(message, "RtAudio: AL error getting input devices: %s.",
05825             alGetErrorString(oserror()));
05826     error(RtError::DRIVER_ERROR);
05827   }
05828 
05829   for (i=outs; i<ins+outs; i++) {
05830     if (alGetParams(vls[i].i, pvs, 1) < 0) {
05831       sprintf(message, "RtAudio: AL error querying input devices: %s.",
05832               alGetErrorString(oserror()));
05833       error(RtError::DRIVER_ERROR);
05834     }
05835     strncpy(devices[i].name, name, 32);
05836     devices[i].id[1] = vls[i].i;
05837   }
05838 
05839   delete [] vls;
05840 
05841   return;
05842 }
05843 
05844 int RtAudio :: getDefaultInputDevice(void)
05845 {
05846   ALvalue value;
05847   int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_INPUT, &value, 1, 0, 0);
05848   if (result < 0) {
05849     sprintf(message, "RtAudio: AL error getting default input device id: %s.",
05850             alGetErrorString(oserror()));
05851     error(RtError::WARNING);
05852   }
05853   else {
05854     for ( int i=0; i<nDevices; i++ )
05855       if ( devices[i].id[1] == value.i ) return i;
05856   }
05857 
05858   return 0;
05859 }
05860 
05861 int RtAudio :: getDefaultOutputDevice(void)
05862 {
05863   ALvalue value;
05864   int result = alQueryValues(AL_SYSTEM, AL_DEFAULT_OUTPUT, &value, 1, 0, 0);
05865   if (result < 0) {
05866     sprintf(message, "RtAudio: AL error getting default output device id: %s.",
05867             alGetErrorString(oserror()));
05868     error(RtError::WARNING);
05869   }
05870   else {
05871     for ( int i=0; i<nDevices; i++ )
05872       if ( devices[i].id[0] == value.i ) return i;
05873   }
05874 
05875   return 0;
05876 }
05877 
05878 void RtAudio :: probeDeviceInfo(RTAUDIO_DEVICE *info)
05879 {
05880   int resource, result, i;
05881   ALvalue value;
05882   ALparamInfo pinfo;
05883 
05884   // Get output resource ID if it exists.
05885   resource = info->id[0];
05886   if (resource > 0) {
05887 
05888     // Probe output device parameters.
05889     result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
05890     if (result < 0) {
05891       sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
05892               info->name, alGetErrorString(oserror()));
05893       error(RtError::WARNING);
05894     }
05895     else {
05896       info->maxOutputChannels = value.i;
05897       info->minOutputChannels = 1;
05898     }
05899 
05900     result = alGetParamInfo(resource, AL_RATE, &pinfo);
05901     if (result < 0) {
05902       sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
05903               info->name, alGetErrorString(oserror()));
05904       error(RtError::WARNING);
05905     }
05906     else {
05907       info->nSampleRates = 0;
05908       for (i=0; i<MAX_SAMPLE_RATES; i++) {
05909         if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
05910           info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
05911           info->nSampleRates++;
05912         }
05913       }
05914     }
05915 
05916     // The AL library supports all our formats, except 24-bit and 32-bit ints.
05917     info->nativeFormats = (RTAUDIO_FORMAT) 51;
05918   }
05919 
05920   // Now get input resource ID if it exists.
05921   resource = info->id[1];
05922   if (resource > 0) {
05923 
05924     // Probe input device parameters.
05925     result = alQueryValues(resource, AL_CHANNELS, &value, 1, 0, 0);
05926     if (result < 0) {
05927       sprintf(message, "RtAudio: AL error getting device (%s) channels: %s.",
05928               info->name, alGetErrorString(oserror()));
05929       error(RtError::WARNING);
05930     }
05931     else {
05932       info->maxInputChannels = value.i;
05933       info->minInputChannels = 1;
05934     }
05935 
05936     result = alGetParamInfo(resource, AL_RATE, &pinfo);
05937     if (result < 0) {
05938       sprintf(message, "RtAudio: AL error getting device (%s) rates: %s.",
05939               info->name, alGetErrorString(oserror()));
05940       error(RtError::WARNING);
05941     }
05942     else {
05943       // In the case of the default device, these values will
05944       // overwrite the rates determined for the output device.  Since
05945       // the input device is most likely to be more limited than the
05946       // output device, this is ok.
05947       info->nSampleRates = 0;
05948       for (i=0; i<MAX_SAMPLE_RATES; i++) {
05949         if ( SAMPLE_RATES[i] >= pinfo.min.i && SAMPLE_RATES[i] <= pinfo.max.i ) {
05950           info->sampleRates[info->nSampleRates] = SAMPLE_RATES[i];
05951           info->nSampleRates++;
05952         }
05953       }
05954     }
05955 
05956     // The AL library supports all our formats, except 24-bit and 32-bit ints.
05957     info->nativeFormats = (RTAUDIO_FORMAT) 51;
05958   }
05959 
05960   if ( info->maxInputChannels == 0 && info->maxOutputChannels == 0 )
05961     return;
05962   if ( info->nSampleRates == 0 )
05963     return;
05964 
05965   // Determine duplex status.
05966   if (info->maxInputChannels < info->maxOutputChannels)
05967     info->maxDuplexChannels = info->maxInputChannels;
05968   else
05969     info->maxDuplexChannels = info->maxOutputChannels;
05970   if (info->minInputChannels < info->minOutputChannels)
05971     info->minDuplexChannels = info->minInputChannels;
05972   else
05973     info->minDuplexChannels = info->minOutputChannels;
05974 
05975   if ( info->maxDuplexChannels > 0 ) info->hasDuplexSupport = true;
05976   else info->hasDuplexSupport = false;
05977 
05978   info->probed = true;
05979 
05980   return;
05981 }
05982 
05983 bool RtAudio :: probeDeviceOpen(int device, RTAUDIO_STREAM *stream,
05984                                 STREAM_MODE mode, int channels, 
05985                                 int sampleRate, RTAUDIO_FORMAT format,
05986                                 int *bufferSize, int numberOfBuffers)
05987 {
05988   int result, resource, nBuffers;
05989   ALconfig al_config;
05990   ALport port;
05991   ALpv pvs[2];
05992 
05993   // Get a new ALconfig structure.
05994   al_config = alNewConfig();
05995   if ( !al_config ) {
05996     sprintf(message,"RtAudio: can't get AL config: %s.",
05997             alGetErrorString(oserror()));
05998     error(RtError::WARNING);
05999     return FAILURE;
06000   }
06001 
06002   // Set the channels.
06003   result = alSetChannels(al_config, channels);
06004   if ( result < 0 ) {
06005     sprintf(message,"RtAudio: can't set %d channels in AL config: %s.",
06006             channels, alGetErrorString(oserror()));
06007     error(RtError::WARNING);
06008     return FAILURE;
06009   }
06010 
06011   // Attempt to set the queue size.  The al API doesn't provide a
06012   // means for querying the minimum/maximum buffer size of a device,
06013   // so if the specified size doesn't work, take whatever the
06014   // al_config structure returns.
06015   if ( numberOfBuffers < 1 )
06016     nBuffers = 1;
06017   else
06018     nBuffers = numberOfBuffers;
06019   long buffer_size = *bufferSize * nBuffers;
06020   result = alSetQueueSize(al_config, buffer_size); // in sample frames
06021   if ( result < 0 ) {
06022     // Get the buffer size specified by the al_config and try that.
06023     buffer_size = alGetQueueSize(al_config);
06024     result = alSetQueueSize(al_config, buffer_size);
06025     if ( result < 0 ) {
06026       sprintf(message,"RtAudio: can't set buffer size (%ld) in AL config: %s.",
06027               buffer_size, alGetErrorString(oserror()));
06028       error(RtError::WARNING);
06029       return FAILURE;
06030     }
06031     *bufferSize = buffer_size / nBuffers;
06032   }
06033 
06034   // Set the data format.
06035   stream->userFormat = format;
06036   stream->deviceFormat[mode] = format;
06037   if (format == RTAUDIO_SINT8) {
06038     result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
06039     result = alSetWidth(al_config, AL_SAMPLE_8);
06040   }
06041   else if (format == RTAUDIO_SINT16) {
06042     result = alSetSampFmt(al_config, AL_SAMPFMT_TWOSCOMP);
06043     result = alSetWidth(al_config, AL_SAMPLE_16);
06044   }
06045   else if (format == RTAUDIO_SINT24) {
06046     // Our 24-bit format assumes the upper 3 bytes of a 4 byte word.
06047     // The AL library uses the lower 3 bytes, so we'll need to do our
06048     // own conversion.
06049     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
06050     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
06051   }
06052   else if (format == RTAUDIO_SINT32) {
06053     // The AL library doesn't seem to support the 32-bit integer
06054     // format, so we'll need to do our own conversion.
06055     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
06056     stream->deviceFormat[mode] = RTAUDIO_FLOAT32;
06057   }
06058   else if (format == RTAUDIO_FLOAT32)
06059     result = alSetSampFmt(al_config, AL_SAMPFMT_FLOAT);
06060   else if (format == RTAUDIO_FLOAT64)
06061     result = alSetSampFmt(al_config, AL_SAMPFMT_DOUBLE);
06062 
06063   if ( result == -1 ) {
06064     sprintf(message,"RtAudio: AL error setting sample format in AL config: %s.",
06065             alGetErrorString(oserror()));
06066     error(RtError::WARNING);
06067     return FAILURE;
06068   }
06069 
06070   if (mode == OUTPUT) {
06071 
06072     // Set our device.
06073     if (device == 0)
06074       resource = AL_DEFAULT_OUTPUT;
06075     else
06076       resource = devices[device].id[0];
06077     result = alSetDevice(al_config, resource);
06078     if ( result == -1 ) {
06079       sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
06080               devices[device].name, alGetErrorString(oserror()));
06081       error(RtError::WARNING);
06082       return FAILURE;
06083     }
06084 
06085     // Open the port.
06086     port = alOpenPort("RtAudio Output Port", "w", al_config);
06087     if( !port ) {
06088       sprintf(message,"RtAudio: AL error opening output port: %s.",
06089               alGetErrorString(oserror()));
06090       error(RtError::WARNING);
06091       return FAILURE;
06092     }
06093 
06094     // Set the sample rate
06095     pvs[0].param = AL_MASTER_CLOCK;
06096     pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
06097     pvs[1].param = AL_RATE;
06098     pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
06099     result = alSetParams(resource, pvs, 2);
06100     if ( result < 0 ) {
06101       alClosePort(port);
06102       sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
06103               sampleRate, devices[device].name, alGetErrorString(oserror()));
06104       error(RtError::WARNING);
06105       return FAILURE;
06106     }
06107   }
06108   else { // mode == INPUT
06109 
06110     // Set our device.
06111     if (device == 0)
06112       resource = AL_DEFAULT_INPUT;
06113     else
06114       resource = devices[device].id[1];
06115     result = alSetDevice(al_config, resource);
06116     if ( result == -1 ) {
06117       sprintf(message,"RtAudio: AL error setting device (%s) in AL config: %s.",
06118               devices[device].name, alGetErrorString(oserror()));
06119       error(RtError::WARNING);
06120       return FAILURE;
06121     }
06122 
06123     // Open the port.
06124     port = alOpenPort("RtAudio Output Port", "r", al_config);
06125     if( !port ) {
06126       sprintf(message,"RtAudio: AL error opening input port: %s.",
06127               alGetErrorString(oserror()));
06128       error(RtError::WARNING);
06129       return FAILURE;
06130     }
06131 
06132     // Set the sample rate
06133     pvs[0].param = AL_MASTER_CLOCK;
06134     pvs[0].value.i = AL_CRYSTAL_MCLK_TYPE;
06135     pvs[1].param = AL_RATE;
06136     pvs[1].value.ll = alDoubleToFixed((double)sampleRate);
06137     result = alSetParams(resource, pvs, 2);
06138     if ( result < 0 ) {
06139       alClosePort(port);
06140       sprintf(message,"RtAudio: AL error setting sample rate (%d) for device (%s): %s.",
06141               sampleRate, devices[device].name, alGetErrorString(oserror()));
06142       error(RtError::WARNING);
06143       return FAILURE;
06144     }
06145   }
06146 
06147   alFreeConfig(al_config);
06148 
06149   stream->nUserChannels[mode] = channels;
06150   stream->nDeviceChannels[mode] = channels;
06151 
06152   // Set handle and flags for buffer conversion
06153   stream->handle[mode] = port;
06154   stream->doConvertBuffer[mode] = false;
06155   if (stream->userFormat != stream->deviceFormat[mode])
06156     stream->doConvertBuffer[mode] = true;
06157 
06158   // Allocate necessary internal buffers
06159   if ( stream->nUserChannels[0] != stream->nUserChannels[1] ) {
06160 
06161     long buffer_bytes;
06162     if (stream->nUserChannels[0] >= stream->nUserChannels[1])
06163       buffer_bytes = stream->nUserChannels[0];
06164     else
06165       buffer_bytes = stream->nUserChannels[1];
06166 
06167     buffer_bytes *= *bufferSize * formatBytes(stream->userFormat);
06168     if (stream->userBuffer) free(stream->userBuffer);
06169     stream->userBuffer = (char *) calloc(buffer_bytes, 1);
06170     if (stream->userBuffer == NULL)
06171       goto memory_error;
06172   }
06173 
06174   if ( stream->doConvertBuffer[mode] ) {
06175 
06176     long buffer_bytes;
06177     bool makeBuffer = true;
06178     if ( mode == OUTPUT )
06179       buffer_bytes = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
06180     else { // mode == INPUT
06181       buffer_bytes = stream->nDeviceChannels[1] * formatBytes(stream->deviceFormat[1]);
06182       if ( stream->mode == OUTPUT && stream->deviceBuffer ) {
06183         long bytes_out = stream->nDeviceChannels[0] * formatBytes(stream->deviceFormat[0]);
06184         if ( buffer_bytes < bytes_out ) makeBuffer = false;
06185       }
06186     }
06187 
06188     if ( makeBuffer ) {
06189       buffer_bytes *= *bufferSize;
06190       if (stream->deviceBuffer) free(stream->deviceBuffer);
06191       stream->deviceBuffer = (char *) calloc(buffer_bytes, 1);
06192       if (stream->deviceBuffer == NULL)
06193         goto memory_error;
06194     }
06195   }
06196 
06197   stream->device[mode] = device;
06198   stream->state = STREAM_STOPPED;
06199   if ( stream->mode == OUTPUT && mode == INPUT )
06200     // We had already set up an output stream.
06201     stream->mode = DUPLEX;
06202   else
06203     stream->mode = mode;
06204   stream->nBuffers = nBuffers;
06205   stream->bufferSize = *bufferSize;
06206   stream->sampleRate = sampleRate;
06207 
06208   return SUCCESS;
06209 
06210  memory_error:
06211   if (stream->handle[0]) {
06212     alClosePort(stream->handle[0]);
06213     stream->handle[0] = 0;
06214   }
06215   if (stream->handle[1]) {
06216     alClosePort(stream->handle[1]);
06217     stream->handle[1] = 0;
06218   }
06219   if (stream->userBuffer) {
06220     free(stream->userBuffer);
06221     stream->userBuffer = 0;
06222   }
06223   sprintf(message, "RtAudio: ALSA error allocating buffer memory for device (%s).",
06224           devices[device].name);
06225   error(RtError::WARNING);
06226   return FAILURE;
06227 }
06228 
06229 void RtAudio :: closeStream(int streamId)
06230 {
06231   // We don't want an exception to be thrown here because this
06232   // function is called by our class destructor.  So, do our own
06233   // streamId check.
06234   if ( streams.find( streamId ) == streams.end() ) {
06235     sprintf(message, "RtAudio: invalid stream identifier!");
06236     error(RtError::WARNING);
06237     return;
06238   }
06239 
06240   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) streams[streamId];
06241 
06242   if (stream->callbackInfo.usingCallback) {
06243     pthread_cancel(stream->callbackInfo.thread);
06244     pthread_join(stream->callbackInfo.thread, NULL);
06245   }
06246 
06247   pthread_mutex_destroy(&stream->mutex);
06248 
06249   if (stream->handle[0])
06250     alClosePort(stream->handle[0]);
06251 
06252   if (stream->handle[1])
06253     alClosePort(stream->handle[1]);
06254 
06255   if (stream->userBuffer)
06256     free(stream->userBuffer);
06257 
06258   if (stream->deviceBuffer)
06259     free(stream->deviceBuffer);
06260 
06261   free(stream);
06262   streams.erase(streamId);
06263 }
06264 
06265 void RtAudio :: startStream(int streamId)
06266 {
06267   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
06268 
06269   if (stream->state == STREAM_RUNNING)
06270     return;
06271 
06272   // The AL port is ready as soon as it is opened.
06273   stream->state = STREAM_RUNNING;
06274 }
06275 
06276 void RtAudio :: stopStream(int streamId)
06277 {
06278   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
06279 
06280   MUTEX_LOCK(&stream->mutex);
06281 
06282   if (stream->state == STREAM_STOPPED)
06283     goto unlock;
06284 
06285   int result;
06286   int buffer_size = stream->bufferSize * stream->nBuffers;
06287 
06288   if (stream->mode == OUTPUT || stream->mode == DUPLEX)
06289     alZeroFrames(stream->handle[0], buffer_size);
06290 
06291   if (stream->mode == INPUT || stream->mode == DUPLEX) {
06292     result = alDiscardFrames(stream->handle[1], buffer_size);
06293     if (result == -1) {
06294       sprintf(message, "RtAudio: AL error draining stream device (%s): %s.",
06295               devices[stream->device[1]].name, alGetErrorString(oserror()));
06296       error(RtError::DRIVER_ERROR);
06297     }
06298   }
06299   stream->state = STREAM_STOPPED;
06300 
06301  unlock:
06302   MUTEX_UNLOCK(&stream->mutex);
06303 }
06304 
06305 void RtAudio :: abortStream(int streamId)
06306 {
06307   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
06308 
06309   MUTEX_LOCK(&stream->mutex);
06310 
06311   if (stream->state == STREAM_STOPPED)
06312     goto unlock;
06313 
06314   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
06315 
06316     int buffer_size = stream->bufferSize * stream->nBuffers;
06317     int result = alDiscardFrames(stream->handle[0], buffer_size);
06318     if (result == -1) {
06319       sprintf(message, "RtAudio: AL error aborting stream device (%s): %s.",
06320               devices[stream->device[0]].name, alGetErrorString(oserror()));
06321       error(RtError::DRIVER_ERROR);
06322     }
06323   }
06324 
06325   // There is no clear action to take on the input stream, since the
06326   // port will continue to run in any event.
06327   stream->state = STREAM_STOPPED;
06328 
06329  unlock:
06330   MUTEX_UNLOCK(&stream->mutex);
06331 }
06332 
06333 int RtAudio :: streamWillBlock(int streamId)
06334 {
06335   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
06336 
06337   MUTEX_LOCK(&stream->mutex);
06338 
06339   int frames = 0;
06340   if (stream->state == STREAM_STOPPED)
06341     goto unlock;
06342 
06343   int err = 0;
06344   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
06345     err = alGetFillable(stream->handle[0]);
06346     if (err < 0) {
06347       sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
06348               devices[stream->device[0]].name, alGetErrorString(oserror()));
06349       error(RtError::DRIVER_ERROR);
06350     }
06351   }
06352 
06353   frames = err;
06354 
06355   if (stream->mode == INPUT || stream->mode == DUPLEX) {
06356     err = alGetFilled(stream->handle[1]);
06357     if (err < 0) {
06358       sprintf(message, "RtAudio: AL error getting available frames for stream (%s): %s.",
06359               devices[stream->device[1]].name, alGetErrorString(oserror()));
06360       error(RtError::DRIVER_ERROR);
06361     }
06362     if (frames > err) frames = err;
06363   }
06364 
06365   frames = stream->bufferSize - frames;
06366   if (frames < 0) frames = 0;
06367 
06368  unlock:
06369   MUTEX_UNLOCK(&stream->mutex);
06370   return frames;
06371 }
06372 
06373 void RtAudio :: tickStream(int streamId)
06374 {
06375   RTAUDIO_STREAM *stream = (RTAUDIO_STREAM *) verifyStream(streamId);
06376 
06377   int stopStream = 0;
06378   if (stream->state == STREAM_STOPPED) {
06379     if (stream->callbackInfo.usingCallback) usleep(50000); // sleep 50 milliseconds
06380     return;
06381   }
06382   else if (stream->callbackInfo.usingCallback) {
06383     RTAUDIO_CALLBACK callback = (RTAUDIO_CALLBACK) stream->callbackInfo.callback;
06384     stopStream = callback(stream->userBuffer, stream->bufferSize, stream->callbackInfo.userData);
06385   }
06386 
06387   MUTEX_LOCK(&stream->mutex);
06388 
06389   // The state might change while waiting on a mutex.
06390   if (stream->state == STREAM_STOPPED)
06391     goto unlock;
06392 
06393   char *buffer;
06394   int channels;
06395   RTAUDIO_FORMAT format;
06396   if (stream->mode == OUTPUT || stream->mode == DUPLEX) {
06397 
06398     // Setup parameters and do buffer conversion if necessary.
06399     if (stream->doConvertBuffer[0]) {
06400       convertStreamBuffer(stream, OUTPUT);
06401       buffer = stream->deviceBuffer;
06402       channels = stream->nDeviceChannels[0];
06403       format = stream->deviceFormat[0];
06404     }
06405     else {
06406       buffer = stream->userBuffer;
06407       channels = stream->nUserChannels[0];
06408       format = stream->userFormat;
06409     }
06410 
06411     // Do byte swapping if necessary.
06412     if (stream->doByteSwap[0])
06413       byteSwapBuffer(buffer, stream->bufferSize * channels, format);
06414 
06415     // Write interleaved samples to device.
06416     alWriteFrames(stream->handle[0], buffer, stream->bufferSize);
06417   }
06418 
06419   if (stream->mode == INPUT || stream->mode == DUPLEX) {
06420 
06421     // Setup parameters.
06422     if (stream->doConvertBuffer[1]) {
06423       buffer = stream->deviceBuffer;
06424       channels = stream->nDeviceChannels[1];
06425       format = stream->deviceFormat[1];
06426     }
06427     else {
06428       buffer = stream->userBuffer;
06429       channels = stream->nUserChannels[1];
06430       format = stream->userFormat;
06431     }
06432 
06433     // Read interleaved samples from device.
06434     alReadFrames(stream->handle[1], buffer, stream->bufferSize);
06435 
06436     // Do byte swapping if necessary.
06437     if (stream->doByteSwap[1])
06438       byteSwapBuffer(buffer, stream->bufferSize * channels, format);
06439 
06440     // Do buffer conversion if necessary.
06441     if (stream->doConvertBuffer[1])
06442       convertStreamBuffer(stream, INPUT);
06443   }
06444 
06445  unlock:
06446   MUTEX_UNLOCK(&stream->mutex);
06447 
06448   if (stream->callbackInfo.usingCallback && stopStream)
06449     this->stopStream(streamId);
06450 }
06451 
06452 extern "C" void *callbackHandler(void *ptr)
06453 {
06454   CALLBACK_INFO *info = (CALLBACK_INFO *) ptr;
06455   RtAudio *object = (RtAudio *) info->object;
06456   int stream = info->streamId;
06457   bool *usingCallback = &info->usingCallback;
06458 
06459   while ( *usingCallback ) {
06460     pthread_testcancel();
06461     try {
06462       object->tickStream(stream);
06463     }
06464     catch (RtError &exception) {
06465       fprintf(stderr, "\nRtAudio: Callback thread error (%s) ... closing thread.\n\n",
06466               exception.getMessage());
06467       break;
06468     }
06469   }
06470 
06471   return 0;
06472 }
06473 
06474 //******************** End of __IRIX_AL__ *********************//
06475 
06476 #endif
06477 
06478 
06479 // *************************************************** //
06480 //
06481 // Private common (OS-independent) RtAudio methods.
06482 //
06483 // *************************************************** //
06484 
06485 // This method can be modified to control the behavior of error
06486 // message reporting and throwing.
06487 void RtAudio :: error(RtError::TYPE type)
06488 {
06489   if (type == RtError::WARNING) {
06490     fprintf(stderr, "\n%s\n\n", message);
06491   }
06492   else if (type == RtError::DEBUG_WARNING) {
06493 #if defined(__RTAUDIO_DEBUG__)
06494     fprintf(stderr, "\n%s\n\n", message);
06495 #endif
06496   }
06497   else {
06498     fprintf(stderr, "\n%s\n\n", message);
06499     throw RtError(message, type);
06500   }
06501 }
06502 
06503 void *RtAudio :: verifyStream(int streamId)
06504 {
06505   // Verify the stream key.
06506   if ( streams.find( streamId ) == streams.end() ) {
06507     sprintf(message, "RtAudio: invalid stream identifier!");
06508     error(RtError::INVALID_STREAM);
06509   }
06510 
06511   return streams[streamId];
06512 }
06513 
06514 void RtAudio :: clearDeviceInfo(RTAUDIO_DEVICE *info)
06515 {
06516   // Don't clear the name or DEVICE_ID fields here ... they are
06517   // typically set prior to a call of this function.
06518   info->probed = false;
06519   info->maxOutputChannels = 0;
06520   info->maxInputChannels = 0;
06521   info->maxDuplexChannels = 0;
06522   info->minOutputChannels = 0;
06523   info->minInputChannels = 0;
06524   info->minDuplexChannels = 0;
06525   info->hasDuplexSupport = false;
06526   info->nSampleRates = 0;
06527   for (int i=0; i<MAX_SAMPLE_RATES; i++)
06528     info->sampleRates[i] = 0;
06529   info->nativeFormats = 0;
06530 }
06531 
06532 int RtAudio :: formatBytes(RTAUDIO_FORMAT format)
06533 {
06534   if (format == RTAUDIO_SINT16)
06535     return 2;
06536   else if (format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 ||
06537            format == RTAUDIO_FLOAT32)
06538     return 4;
06539   else if (format == RTAUDIO_FLOAT64)
06540     return 8;
06541   else if (format == RTAUDIO_SINT8)
06542     return 1;
06543 
06544   sprintf(message,"RtAudio: undefined format in formatBytes().");
06545   error(RtError::WARNING);
06546 
06547   return 0;
06548 }
06549 
06550 void RtAudio :: convertStreamBuffer(RTAUDIO_STREAM *stream, STREAM_MODE mode)
06551 {
06552   // This method does format conversion, input/output channel compensation, and
06553   // data interleaving/deinterleaving.  24-bit integers are assumed to occupy
06554   // the upper three bytes of a 32-bit integer.
06555 
06556   int j, jump_in, jump_out, channels;
06557   RTAUDIO_FORMAT format_in, format_out;
06558   char *input, *output;
06559 
06560   if (mode == INPUT) { // convert device to user buffer
06561     input = stream->deviceBuffer;
06562     output = stream->userBuffer;
06563     jump_in = stream->nDeviceChannels[1];
06564     jump_out = stream->nUserChannels[1];
06565     format_in = stream->deviceFormat[1];
06566     format_out = stream->userFormat;
06567   }
06568   else { // convert user to device buffer
06569     input = stream->userBuffer;
06570     output = stream->deviceBuffer;
06571     jump_in = stream->nUserChannels[0];
06572     jump_out = stream->nDeviceChannels[0];
06573     format_in = stream->userFormat;
06574     format_out = stream->deviceFormat[0];
06575 
06576     // clear our device buffer when in/out duplex device channels are different
06577     if ( stream->mode == DUPLEX &&
06578          stream->nDeviceChannels[0] != stream->nDeviceChannels[1] )
06579       memset(output, 0, stream->bufferSize * jump_out * formatBytes(format_out));
06580   }
06581 
06582   channels = (jump_in < jump_out) ? jump_in : jump_out;
06583 
06584   // Set up the interleave/deinterleave offsets
06585   std::vector<int> offset_in(channels);
06586   std::vector<int> offset_out(channels);
06587   if (mode == INPUT && stream->deInterleave[1]) {
06588     for (int k=0; k<channels; k++) {
06589       offset_in[k] = k * stream->bufferSize;
06590       offset_out[k] = k;
06591       jump_in = 1;
06592     }
06593   }
06594   else if (mode == OUTPUT && stream->deInterleave[0]) {
06595     for (int k=0; k<channels; k++) {
06596       offset_in[k] = k;
06597       offset_out[k] = k * stream->bufferSize;
06598       jump_out = 1;
06599     }
06600   }
06601   else {
06602     for (int k=0; k<channels; k++) {
06603       offset_in[k] = k;
06604       offset_out[k] = k;
06605     }
06606   }
06607 
06608   if (format_out == RTAUDIO_FLOAT64) {
06609     FLOAT64 scale;
06610     FLOAT64 *out = (FLOAT64 *)output;
06611 
06612     if (format_in == RTAUDIO_SINT8) {
06613       signed char *in = (signed char *)input;
06614       scale = 1.0 / 128.0;
06615       for (int i=0; i<stream->bufferSize; i++) {
06616         for (j=0; j<channels; j++) {
06617           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
06618           out[offset_out[j]] *= scale;
06619         }
06620         in += jump_in;
06621         out += jump_out;
06622       }
06623     }
06624     else if (format_in == RTAUDIO_SINT16) {
06625       INT16 *in = (INT16 *)input;
06626       scale = 1.0 / 32768.0;
06627       for (int i=0; i<stream->bufferSize; i++) {
06628         for (j=0; j<channels; j++) {
06629           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
06630           out[offset_out[j]] *= scale;
06631         }
06632         in += jump_in;
06633         out += jump_out;
06634       }
06635     }
06636     else if (format_in == RTAUDIO_SINT24) {
06637       INT32 *in = (INT32 *)input;
06638       scale = 1.0 / 2147483648.0;
06639       for (int i=0; i<stream->bufferSize; i++) {
06640         for (j=0; j<channels; j++) {
06641           out[offset_out[j]] = (FLOAT64) (in[offset_in[j]] & 0xffffff00);
06642           out[offset_out[j]] *= scale;
06643         }
06644         in += jump_in;
06645         out += jump_out;
06646       }
06647     }
06648     else if (format_in == RTAUDIO_SINT32) {
06649       INT32 *in = (INT32 *)input;
06650       scale = 1.0 / 2147483648.0;
06651       for (int i=0; i<stream->bufferSize; i++) {
06652         for (j=0; j<channels; j++) {
06653           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
06654           out[offset_out[j]] *= scale;
06655         }
06656         in += jump_in;
06657         out += jump_out;
06658       }
06659     }
06660     else if (format_in == RTAUDIO_FLOAT32) {
06661       FLOAT32 *in = (FLOAT32 *)input;
06662       for (int i=0; i<stream->bufferSize; i++) {
06663         for (j=0; j<channels; j++) {
06664           out[offset_out[j]] = (FLOAT64) in[offset_in[j]];
06665         }
06666         in += jump_in;
06667         out += jump_out;
06668       }
06669     }
06670     else if (format_in == RTAUDIO_FLOAT64) {
06671       // Channel compensation and/or (de)interleaving only.
06672       FLOAT64 *in = (FLOAT64 *)input;
06673       for (int i=0; i<stream->bufferSize; i++) {
06674         for (j=0; j<channels; j++) {
06675           out[offset_out[j]] = in[offset_in[j]];
06676         }
06677         in += jump_in;
06678         out += jump_out;
06679       }
06680     }
06681   }
06682   else if (format_out == RTAUDIO_FLOAT32) {
06683     FLOAT32 scale;
06684     FLOAT32 *out = (FLOAT32 *)output;
06685 
06686     if (format_in == RTAUDIO_SINT8) {
06687       signed char *in = (signed char *)input;
06688       scale = 1.0 / 128.0;
06689       for (int i=0; i<stream->bufferSize; i++) {
06690         for (j=0; j<channels; j++) {
06691           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
06692           out[offset_out[j]] *= scale;
06693         }
06694         in += jump_in;
06695         out += jump_out;
06696       }
06697     }
06698     else if (format_in == RTAUDIO_SINT16) {
06699       INT16 *in = (INT16 *)input;
06700       scale = 1.0 / 32768.0;
06701       for (int i=0; i<stream->bufferSize; i++) {
06702         for (j=0; j<channels; j++) {
06703           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
06704           out[offset_out[j]] *= scale;
06705         }
06706         in += jump_in;
06707         out += jump_out;
06708       }
06709     }
06710     else if (format_in == RTAUDIO_SINT24) {
06711       INT32 *in = (INT32 *)input;
06712       scale = 1.0 / 2147483648.0;
06713       for (int i=0; i<stream->bufferSize; i++) {
06714         for (j=0; j<channels; j++) {
06715           out[offset_out[j]] = (FLOAT32) (in[offset_in[j]] & 0xffffff00);
06716           out[offset_out[j]] *= scale;
06717         }
06718         in += jump_in;
06719         out += jump_out;
06720       }
06721     }
06722     else if (format_in == RTAUDIO_SINT32) {
06723       INT32 *in = (INT32 *)input;
06724       scale = 1.0 / 2147483648.0;
06725       for (int i=0; i<stream->bufferSize; i++) {
06726         for (j=0; j<channels; j++) {
06727           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
06728           out[offset_out[j]] *= scale;
06729         }
06730         in += jump_in;
06731         out += jump_out;
06732       }
06733     }
06734     else if (format_in == RTAUDIO_FLOAT32) {
06735       // Channel compensation and/or (de)interleaving only.
06736       FLOAT32 *in = (FLOAT32 *)input;
06737       for (int i=0; i<stream->bufferSize; i++) {
06738         for (j=0; j<channels; j++) {
06739           out[offset_out[j]] = in[offset_in[j]];
06740         }
06741         in += jump_in;
06742         out += jump_out;
06743       }
06744     }
06745     else if (format_in == RTAUDIO_FLOAT64) {
06746       FLOAT64 *in = (FLOAT64 *)input;
06747       for (int i=0; i<stream->bufferSize; i++) {
06748         for (j=0; j<channels; j++) {
06749           out[offset_out[j]] = (FLOAT32) in[offset_in[j]];
06750         }
06751         in += jump_in;
06752         out += jump_out;
06753       }
06754     }
06755   }
06756   else if (format_out == RTAUDIO_SINT32) {
06757     INT32 *out = (INT32 *)output;
06758     if (format_in == RTAUDIO_SINT8) {
06759       signed char *in = (signed char *)input;
06760       for (int i=0; i<stream->bufferSize; i++) {
06761         for (j=0; j<channels; j++) {
06762           out[offset_out[j]] = (INT32) in[offset_in[j]];
06763           out[offset_out[j]] <<= 24;
06764         }
06765         in += jump_in;
06766         out += jump_out;
06767       }
06768     }
06769     else if (format_in == RTAUDIO_SINT16) {
06770       INT16 *in = (INT16 *)input;
06771       for (int i=0; i<stream->bufferSize; i++) {
06772         for (j=0; j<channels; j++) {
06773           out[offset_out[j]] = (INT32) in[offset_in[j]];
06774           out[offset_out[j]] <<= 16;
06775         }
06776         in += jump_in;
06777         out += jump_out;
06778       }
06779     }
06780     else if (format_in == RTAUDIO_SINT24) {
06781       INT32 *in = (INT32 *)input;
06782       for (int i=0; i<stream->bufferSize; i++) {
06783         for (j=0; j<channels; j++) {
06784           out[offset_out[j]] = (INT32) in[offset_in[j]];
06785         }
06786         in += jump_in;
06787         out += jump_out;
06788       }
06789     }
06790     else if (format_in == RTAUDIO_SINT32) {
06791       // Channel compensation and/or (de)interleaving only.
06792       INT32 *in = (INT32 *)input;
06793       for (int i=0; i<stream->bufferSize; i++) {
06794         for (j=0; j<channels; j++) {
06795           out[offset_out[j]] = in[offset_in[j]];
06796         }
06797         in += jump_in;
06798         out += jump_out;
06799       }
06800     }
06801     else if (format_in == RTAUDIO_FLOAT32) {
06802       FLOAT32 *in = (FLOAT32 *)input;
06803       for (int i=0; i<stream->bufferSize; i++) {
06804         for (j=0; j<channels; j++) {
06805           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
06806         }
06807         in += jump_in;
06808         out += jump_out;
06809       }
06810     }
06811     else if (format_in == RTAUDIO_FLOAT64) {
06812       FLOAT64 *in = (FLOAT64 *)input;
06813       for (int i=0; i<stream->bufferSize; i++) {
06814         for (j=0; j<channels; j++) {
06815           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
06816         }
06817         in += jump_in;
06818         out += jump_out;
06819       }
06820     }
06821   }
06822   else if (format_out == RTAUDIO_SINT24) {
06823     INT32 *out = (INT32 *)output;
06824     if (format_in == RTAUDIO_SINT8) {
06825       signed char *in = (signed char *)input;
06826       for (int i=0; i<stream->bufferSize; i++) {
06827         for (j=0; j<channels; j++) {
06828           out[offset_out[j]] = (INT32) in[offset_in[j]];
06829           out[offset_out[j]] <<= 24;
06830         }
06831         in += jump_in;
06832         out += jump_out;
06833       }
06834     }
06835     else if (format_in == RTAUDIO_SINT16) {
06836       INT16 *in = (INT16 *)input;
06837       for (int i=0; i<stream->bufferSize; i++) {
06838         for (j=0; j<channels; j++) {
06839           out[offset_out[j]] = (INT32) in[offset_in[j]];
06840           out[offset_out[j]] <<= 16;
06841         }
06842         in += jump_in;
06843         out += jump_out;
06844       }
06845     }
06846     else if (format_in == RTAUDIO_SINT24) {
06847       // Channel compensation and/or (de)interleaving only.
06848       INT32 *in = (INT32 *)input;
06849       for (int i=0; i<stream->bufferSize; i++) {
06850         for (j=0; j<channels; j++) {
06851           out[offset_out[j]] = in[offset_in[j]];
06852         }
06853         in += jump_in;
06854         out += jump_out;
06855       }
06856     }
06857     else if (format_in == RTAUDIO_SINT32) {
06858       INT32 *in = (INT32 *)input;
06859       for (int i=0; i<stream->bufferSize; i++) {
06860         for (j=0; j<channels; j++) {
06861           out[offset_out[j]] = (INT32) (in[offset_in[j]] & 0xffffff00);
06862         }
06863         in += jump_in;
06864         out += jump_out;
06865       }
06866     }
06867     else if (format_in == RTAUDIO_FLOAT32) {
06868       FLOAT32 *in = (FLOAT32 *)input;
06869       for (int i=0; i<stream->bufferSize; i++) {
06870         for (j=0; j<channels; j++) {
06871           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
06872         }
06873         in += jump_in;
06874         out += jump_out;
06875       }
06876     }
06877     else if (format_in == RTAUDIO_FLOAT64) {
06878       FLOAT64 *in = (FLOAT64 *)input;
06879       for (int i=0; i<stream->bufferSize; i++) {
06880         for (j=0; j<channels; j++) {
06881           out[offset_out[j]] = (INT32) (in[offset_in[j]] * 2147483647.0);
06882         }
06883         in += jump_in;
06884         out += jump_out;
06885       }
06886     }
06887   }
06888   else if (format_out == RTAUDIO_SINT16) {
06889     INT16 *out = (INT16 *)output;
06890     if (format_in == RTAUDIO_SINT8) {
06891       signed char *in = (signed char *)input;
06892       for (int i=0; i<stream->bufferSize; i++) {
06893         for (j=0; j<channels; j++) {
06894           out[offset_out[j]] = (INT16) in[offset_in[j]];
06895           out[offset_out[j]] <<= 8;
06896         }
06897         in += jump_in;
06898         out += jump_out;
06899       }
06900     }
06901     else if (format_in == RTAUDIO_SINT16) {
06902       // Channel compensation and/or (de)interleaving only.
06903       INT16 *in = (INT16 *)input;
06904       for (int i=0; i<stream->bufferSize; i++) {
06905         for (j=0; j<channels; j++) {
06906           out[offset_out[j]] = in[offset_in[j]];
06907         }
06908         in += jump_in;
06909         out += jump_out;
06910       }
06911     }
06912     else if (format_in == RTAUDIO_SINT24) {
06913       INT32 *in = (INT32 *)input;
06914       for (int i=0; i<stream->bufferSize; i++) {
06915         for (j=0; j<channels; j++) {
06916           out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
06917         }
06918         in += jump_in;
06919         out += jump_out;
06920       }
06921     }
06922     else if (format_in == RTAUDIO_SINT32) {
06923       INT32 *in = (INT32 *)input;
06924       for (int i=0; i<stream->bufferSize; i++) {
06925         for (j=0; j<channels; j++) {
06926           out[offset_out[j]] = (INT16) ((in[offset_in[j]] >> 16) & 0x0000ffff);
06927         }
06928         in += jump_in;
06929         out += jump_out;
06930       }
06931     }
06932     else if (format_in == RTAUDIO_FLOAT32) {
06933       FLOAT32 *in = (FLOAT32 *)input;
06934       for (int i=0; i<stream->bufferSize; i++) {
06935         for (j=0; j<channels; j++) {
06936           out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);
06937         }
06938         in += jump_in;
06939         out += jump_out;
06940       }
06941     }
06942     else if (format_in == RTAUDIO_FLOAT64) {
06943       FLOAT64 *in = (FLOAT64 *)input;
06944       for (int i=0; i<stream->bufferSize; i++) {
06945         for (j=0; j<channels; j++) {
06946           out[offset_out[j]] = (INT16) (in[offset_in[j]] * 32767.0);
06947         }
06948         in += jump_in;
06949         out += jump_out;
06950       }
06951     }
06952   }
06953   else if (format_out == RTAUDIO_SINT8) {
06954     signed char *out = (signed char *)output;
06955     if (format_in == RTAUDIO_SINT8) {
06956       // Channel compensation and/or (de)interleaving only.
06957       signed char *in = (signed char *)input;
06958       for (int i=0; i<stream->bufferSize; i++) {
06959         for (j=0; j<channels; j++) {
06960           out[offset_out[j]] = in[offset_in[j]];
06961         }
06962         in += jump_in;
06963         out += jump_out;
06964       }
06965     }
06966     if (format_in == RTAUDIO_SINT16) {
06967       INT16 *in = (INT16 *)input;
06968       for (int i=0; i<stream->bufferSize; i++) {
06969         for (j=0; j<channels; j++) {
06970           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 8) & 0x00ff);
06971         }
06972         in += jump_in;
06973         out += jump_out;
06974       }
06975     }
06976     else if (format_in == RTAUDIO_SINT24) {
06977       INT32 *in = (INT32 *)input;
06978       for (int i=0; i<stream->bufferSize; i++) {
06979         for (j=0; j<channels; j++) {
06980           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
06981         }
06982         in += jump_in;
06983         out += jump_out;
06984       }
06985     }
06986     else if (format_in == RTAUDIO_SINT32) {
06987       INT32 *in = (INT32 *)input;
06988       for (int i=0; i<stream->bufferSize; i++) {
06989         for (j=0; j<channels; j++) {
06990           out[offset_out[j]] = (signed char) ((in[offset_in[j]] >> 24) & 0x000000ff);
06991         }
06992         in += jump_in;
06993         out += jump_out;
06994       }
06995     }
06996     else if (format_in == RTAUDIO_FLOAT32) {
06997       FLOAT32 *in = (FLOAT32 *)input;
06998       for (int i=0; i<stream->bufferSize; i++) {
06999         for (j=0; j<channels; j++) {
07000           out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
07001         }
07002         in += jump_in;
07003         out += jump_out;
07004       }
07005     }
07006     else if (format_in == RTAUDIO_FLOAT64) {
07007       FLOAT64 *in = (FLOAT64 *)input;
07008       for (int i=0; i<stream->bufferSize; i++) {
07009         for (j=0; j<channels; j++) {
07010           out[offset_out[j]] = (signed char) (in[offset_in[j]] * 127.0);
07011         }
07012         in += jump_in;
07013         out += jump_out;
07014       }
07015     }
07016   }
07017 }
07018 
07019 void RtAudio :: byteSwapBuffer(char *buffer, int samples, RTAUDIO_FORMAT format)
07020 {
07021   register char val;
07022   register char *ptr;
07023 
07024   ptr = buffer;
07025   if (format == RTAUDIO_SINT16) {
07026     for (int i=0; i<samples; i++) {
07027       // Swap 1st and 2nd bytes.
07028       val = *(ptr);
07029       *(ptr) = *(ptr+1);
07030       *(ptr+1) = val;
07031 
07032       // Increment 2 bytes.
07033       ptr += 2;
07034     }
07035   }
07036   else if (format == RTAUDIO_SINT24 ||
07037            format == RTAUDIO_SINT32 ||
07038            format == RTAUDIO_FLOAT32) {
07039     for (int i=0; i<samples; i++) {
07040       // Swap 1st and 4th bytes.
07041       val = *(ptr);
07042       *(ptr) = *(ptr+3);
07043       *(ptr+3) = val;
07044 
07045       // Swap 2nd and 3rd bytes.
07046       ptr += 1;
07047       val = *(ptr);
07048       *(ptr) = *(ptr+1);
07049       *(ptr+1) = val;
07050 
07051       // Increment 4 bytes.
07052       ptr += 4;
07053     }
07054   }
07055   else if (format == RTAUDIO_FLOAT64) {
07056     for (int i=0; i<samples; i++) {
07057       // Swap 1st and 8th bytes
07058       val = *(ptr);
07059       *(ptr) = *(ptr+7);
07060       *(ptr+7) = val;
07061 
07062       // Swap 2nd and 7th bytes
07063       ptr += 1;
07064       val = *(ptr);
07065       *(ptr) = *(ptr+5);
07066       *(ptr+5) = val;
07067 
07068       // Swap 3rd and 6th bytes
07069       ptr += 1;
07070       val = *(ptr);
07071       *(ptr) = *(ptr+3);
07072       *(ptr+3) = val;
07073 
07074       // Swap 4th and 5th bytes
07075       ptr += 1;
07076       val = *(ptr);
07077       *(ptr) = *(ptr+1);
07078       *(ptr+1) = val;
07079 
07080       // Increment 8 bytes.
07081       ptr += 8;
07082     }
07083   }
07084 }
07085 
07086 
07087 // *************************************************** //
07088 //
07089 // RtError class definition.
07090 //
07091 // *************************************************** //
07092 
07093 RtError :: RtError(const char *p, TYPE tipe)
07094 {
07095   type = tipe;
07096   strncpy(error_message, p, 256);
07097 }
07098 
07099 RtError :: ~RtError()
07100 {
07101 }
07102 
07103 void RtError :: printMessage()
07104 {
07105   printf("\n%s\n\n", error_message);
07106 }
07107 
Generated by  doxygen 1.6.3