Updates for release 4.0.9, including OS-X fixes for 10.6 and 10.7 (gs).
authorGary Scavone <gary@music.mcgill.ca>
Thu, 11 Aug 2011 16:33:05 +0000 (16:33 +0000)
committerStephen Sinclair <sinclair@music.mcgill.ca>
Thu, 10 Oct 2013 23:38:28 +0000 (01:38 +0200)
RtAudio.cpp
RtAudio.h
doc/doxygen/tutorial.txt
doc/release.txt

index 7dd1460f1e5608728bca88bd442e0ca10b72fe76..ab50cbb36d2a1d8020c70a8956013247a98d979f 100644 (file)
@@ -38,7 +38,7 @@
 */\r
 /************************************************************************/\r
 \r
-// RtAudio: Version 4.0.8\r
+// RtAudio: Version 4.0.9\r
 \r
 #include "RtAudio.h"\r
 #include <iostream>\r
@@ -392,12 +392,6 @@ unsigned int RtApi :: getStreamSampleRate( void )
 // quite a bit of extra code and most likely, a user program wouldn't\r
 // be prepared for the result anyway.  However, we do provide a flag\r
 // to the client callback function to inform of an over/underrun.\r
-//\r
-// The mechanism for querying and setting system parameters was\r
-// updated (and perhaps simplified) in OS-X version 10.4.  However,\r
-// since 10.4 support is not necessarily available to all users, I've\r
-// decided not to update the respective code at this time.  Perhaps\r
-// this will happen when Apple makes 10.4 free for everyone. :-)\r
 \r
 // A structure to hold various information related to the CoreAudio API\r
 // implementation.\r
@@ -418,9 +412,23 @@ struct CoreHandle {
     :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }\r
 };\r
 \r
-RtApiCore :: RtApiCore()\r
+RtApiCore:: RtApiCore()\r
 {\r
-  // Nothing to do here.\r
+#if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )\r
+  // This is a largely undocumented but absolutely necessary\r
+  // requirement starting with OS-X 10.6.  If not called, queries and\r
+  // updates to various audio device properties are not handled\r
+  // correctly.\r
+  CFRunLoopRef theRunLoop = NULL;\r
+  AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,\r
+                                          kAudioObjectPropertyScopeGlobal,\r
+                                          kAudioObjectPropertyElementMaster };\r
+  OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);\r
+  if ( result != noErr ) {\r
+    errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";\r
+    error( RtError::WARNING );\r
+  }\r
+#endif\r
 }\r
 \r
 RtApiCore :: ~RtApiCore()\r
@@ -732,7 +740,7 @@ OSStatus callbackHandler( AudioDeviceID inDevice,
     return kAudioHardwareNoError;\r
 }\r
 \r
-OSStatus deviceListener( AudioObjectID inDevice,\r
+OSStatus xrunListener( AudioObjectID inDevice,\r
                          UInt32 nAddresses,\r
                          const AudioObjectPropertyAddress properties[],\r
                          void* handlePointer )\r
@@ -750,6 +758,21 @@ OSStatus deviceListener( AudioObjectID inDevice,
   return kAudioHardwareNoError;\r
 }\r
 \r
+OSStatus rateListener( AudioObjectID inDevice,\r
+                       UInt32 nAddresses,\r
+                       const AudioObjectPropertyAddress properties[],\r
+                       void* ratePointer )\r
+{\r
+\r
+  Float64 *rate = (Float64 *) ratePointer;\r
+  UInt32 dataSize = sizeof( Float64 );\r
+  AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,\r
+                                          kAudioObjectPropertyScopeGlobal,\r
+                                          kAudioObjectPropertyElementMaster };\r
+  AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );\r
+  return kAudioHardwareNoError;\r
+}\r
+\r
 bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,\r
                                    unsigned int firstChannel, unsigned int sampleRate,\r
                                    RtAudioFormat format, unsigned int *bufferSize,\r
@@ -924,30 +947,6 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   stream_.bufferSize = *bufferSize;\r
   stream_.nBuffers = 1;\r
 \r
-  // Check and if necessary, change the sample rate for the device.\r
-  Float64 nominalRate;\r
-  dataSize = sizeof( Float64 );\r
-  property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
-  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
-\r
-  if ( result != noErr ) {\r
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
-    errorText_ = errorStream_.str();\r
-    return FAILURE;\r
-  }\r
-\r
-  // Only change the sample rate if off by more than 1 Hz.\r
-  if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
-    nominalRate = (Float64) sampleRate;\r
-    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
-\r
-    if ( result != noErr ) {\r
-      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
-      errorText_ = errorStream_.str();\r
-      return FAILURE;\r
-    }\r
-  }\r
-\r
   // Try to set "hog" mode ... it's not clear to me this is working.\r
   if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {\r
     pid_t hog_pid;\r
@@ -971,126 +970,159 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }\r
   }\r
 \r
-  // Get the stream ID(s) so we can set the stream format.\r
-  AudioStreamID streamIDs[ nStreams ];\r
-  dataSize = nStreams * sizeof( AudioStreamID );\r
-  property.mSelector = kAudioDevicePropertyStreams;\r
-  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &streamIDs );\r
+  // Check and if necessary, change the sample rate for the device.\r
+  Float64 nominalRate;\r
+  dataSize = sizeof( Float64 );\r
+  property.mSelector = kAudioDevicePropertyNominalSampleRate;\r
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );\r
 \r
   if ( result != noErr ) {\r
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream ID(s) for device (" << device << ").";\r
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";\r
     errorText_ = errorStream_.str();\r
     return FAILURE;\r
   }\r
 \r
-  // Now set the stream format for each stream.  Also, check the\r
-  // physical format of the device and change that if necessary.\r
-  AudioStreamBasicDescription  description;\r
-  dataSize = sizeof( AudioStreamBasicDescription );\r
-\r
-  bool updateFormat;\r
-  for ( UInt32 i=0; i<streamCount; i++ ) {\r
-\r
-    property.mSelector = kAudioStreamPropertyVirtualFormat;\r
-    result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, &dataSize, &description );\r
+  // Only change the sample rate if off by more than 1 Hz.\r
+  if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {\r
 \r
+    // Set a property listener for the sample rate change\r
+    Float64 reportedRate = 0.0;\r
+    AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };\r
+    result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
     if ( result != noErr ) {\r
-      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";\r
       errorText_ = errorStream_.str();\r
       return FAILURE;\r
     }\r
 \r
-    // Set the sample rate and data format id.  However, only make the\r
-    // change if the sample rate is not within 1.0 of the desired\r
-    // rate and the format is not linear pcm.\r
-    updateFormat = false;\r
-    if ( fabs( description.mSampleRate - (double)sampleRate ) > 1.0 ) {\r
-      description.mSampleRate = (double) sampleRate;\r
-      updateFormat = true;\r
-    }\r
+    nominalRate = (Float64) sampleRate;\r
+    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );\r
 \r
-    if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
-      description.mFormatID = kAudioFormatLinearPCM;\r
-      updateFormat = true;\r
+    if ( result != noErr ) {\r
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";\r
+      errorText_ = errorStream_.str();\r
+      return FAILURE;\r
     }\r
 \r
-    if ( updateFormat ) {\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &description );\r
-      if ( result != noErr ) {\r
-        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
-        errorText_ = errorStream_.str();\r
-        return FAILURE;\r
-      }\r
+    // Now wait until the reported nominal rate is what we just set.\r
+    UInt32 microCounter = 0;\r
+    while ( reportedRate != nominalRate ) {\r
+      microCounter += 5000;\r
+      if ( microCounter > 5000000 ) break;\r
+      usleep( 5000 );\r
     }\r
 \r
-    // Now check the physical format.\r
-    property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
-    result = AudioObjectGetPropertyData( streamIDs[firstStream+i], &property, 0, NULL,  &dataSize, &description );\r
-    if ( result != noErr ) {\r
-      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
+    // Remove the property listener.\r
+    AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );\r
+\r
+    if ( microCounter > 5000000 ) {\r
+      errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";\r
       errorText_ = errorStream_.str();\r
       return FAILURE;\r
     }\r
+  }\r
 \r
-    if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 24 ) {\r
-      description.mFormatID = kAudioFormatLinearPCM;\r
-      AudioStreamBasicDescription      testDescription = description;\r
-      unsigned long formatFlags;\r
+  // Now set the stream format for all streams.  Also, check the\r
+  // physical format of the device and change that if necessary.\r
+  AudioStreamBasicDescription  description;\r
+  dataSize = sizeof( AudioStreamBasicDescription );\r
+  property.mSelector = kAudioStreamPropertyVirtualFormat;\r
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );\r
+  if ( result != noErr ) {\r
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";\r
+    errorText_ = errorStream_.str();\r
+    return FAILURE;\r
+  }\r
 \r
-      // We'll try higher bit rates first and then work our way down.\r
-      testDescription.mBitsPerChannel = 32;\r
-      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
-      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
-      formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;\r
-      testDescription.mFormatFlags = formatFlags;\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );\r
-      if ( result == noErr ) continue;\r
+  // Set the sample rate and data format id.  However, only make the\r
+  // change if the sample rate is not within 1.0 of the desired\r
+  // rate and the format is not linear pcm.\r
+  bool updateFormat = false;\r
+  if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {\r
+    description.mSampleRate = (Float64) sampleRate;\r
+    updateFormat = true;\r
+  }\r
 \r
-      testDescription = description;\r
-      testDescription.mBitsPerChannel = 32;\r
-      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
-      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
-      formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger) & ~kLinearPCMFormatFlagIsFloat;\r
-      testDescription.mFormatFlags = formatFlags;\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );\r
-      if ( result == noErr ) continue;\r
+  if ( description.mFormatID != kAudioFormatLinearPCM ) {\r
+    description.mFormatID = kAudioFormatLinearPCM;\r
+    updateFormat = true;\r
+  }\r
 \r
-      testDescription = description;\r
-      testDescription.mBitsPerChannel = 24;\r
-      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
-      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
-      testDescription.mFormatFlags = formatFlags;\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );\r
-      if ( result == noErr ) continue;\r
+  if ( updateFormat ) {\r
+    result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );\r
+    if ( result != noErr ) {\r
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";\r
+      errorText_ = errorStream_.str();\r
+      return FAILURE;\r
+    }\r
+  }\r
 \r
-      testDescription = description;\r
-      testDescription.mBitsPerChannel = 16;\r
-      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
-      testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
-      testDescription.mFormatFlags = formatFlags;\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );\r
-      if ( result == noErr ) continue;\r
+  // Now check the physical format.\r
+  property.mSelector = kAudioStreamPropertyPhysicalFormat;\r
+  result = AudioObjectGetPropertyData( id, &property, 0, NULL,  &dataSize, &description );\r
+  if ( result != noErr ) {\r
+    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";\r
+    errorText_ = errorStream_.str();\r
+    return FAILURE;\r
+  }\r
 \r
+  //std::cout << "Current physical stream format:" << std::endl;\r
+  //std::cout << "   mBitsPerChan = " << description.mBitsPerChannel << std::endl;\r
+  //std::cout << "   aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
+  //std::cout << "   bytesPerFrame = " << description.mBytesPerFrame << std::endl;\r
+  //std::cout << "   sample rate = " << description.mSampleRate << std::endl;\r
+\r
+  if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {\r
+    description.mFormatID = kAudioFormatLinearPCM;\r
+    //description.mSampleRate = (Float64) sampleRate;\r
+    AudioStreamBasicDescription        testDescription = description;\r
+    UInt32 formatFlags;\r
+\r
+    // We'll try higher bit rates first and then work our way down.\r
+    std::vector< std::pair<UInt32, UInt32>  > physicalFormats;\r
+    formatFlags = description.mFormatFlags | kLinearPCMFormatFlagIsFloat & ~kLinearPCMFormatFlagIsSignedInteger;\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
+    formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) );   // 24-bit packed\r
+    formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low\r
+    formatFlags |= kAudioFormatFlagIsAlignedHigh;\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high\r
+    formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );\r
+    physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );\r
+\r
+    bool setPhysicalFormat = false;\r
+    for( unsigned int i=0; i<physicalFormats.size(); i++ ) {\r
       testDescription = description;\r
-      testDescription.mBitsPerChannel = 8;\r
-      testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
+      testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;\r
+      testDescription.mFormatFlags = physicalFormats[i].second;\r
+      if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )\r
+        testDescription.mBytesPerFrame =  4 * testDescription.mChannelsPerFrame;\r
+      else\r
+        testDescription.mBytesPerFrame =  testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;\r
       testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;\r
-      testDescription.mFormatFlags = formatFlags;\r
-      result = AudioObjectSetPropertyData( streamIDs[firstStream+i], &property, 0, NULL, dataSize, &testDescription );\r
-      if ( result != noErr ) {\r
-        errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
-        errorText_ = errorStream_.str();\r
-        return FAILURE;\r
+      result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );\r
+      if ( result == noErr ) {\r
+        setPhysicalFormat = true;\r
+        //std::cout << "Updated physical stream format:" << std::endl;\r
+        //std::cout << "   mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;\r
+        //std::cout << "   aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;\r
+        //std::cout << "   bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;\r
+        //std::cout << "   sample rate = " << testDescription.mSampleRate << std::endl;\r
+        break;\r
       }\r
     }\r
-  }\r
 \r
+    if ( !setPhysicalFormat ) {\r
+      errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";\r
+      errorText_ = errorStream_.str();\r
+      return FAILURE;\r
+    }\r
+  } // done setting virtual/physical formats.\r
 \r
-  // Get the stream latency.  There can be latency in both the device\r
-  // and the stream.  First, attempt to get the device latency on the\r
-  // master channel or the first open channel.  Errors that might\r
-  // occur here are not deemed critical.\r
-\r
+  // Get the stream / device latency.\r
   UInt32 latency;\r
   dataSize = sizeof( UInt32 );\r
   property.mSelector = kAudioDevicePropertyLatency;\r
@@ -1104,16 +1136,6 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     }\r
   }\r
 \r
-  // Now try to get the stream latency.  For multiple streams, I assume the\r
-  // latency is equal for each.\r
-  result = AudioObjectGetPropertyData( streamIDs[firstStream], &property, 0, NULL, &dataSize, &latency );\r
-  if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] += latency;\r
-  else {\r
-    errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream latency for device (" << device << ").";\r
-    errorText_ = errorStream_.str();\r
-    error( RtError::WARNING );\r
-  }\r
-\r
   // Byte-swapping: According to AudioHardware.h, the stream data will\r
   // always be presented in native-endian format, so we should never\r
   // need to byte swap.\r
@@ -1176,7 +1198,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
   unsigned long bufferBytes;\r
   bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );\r
   //  stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );\r
-  stream_.userBuffer[mode] = (char *) malloc( bufferBytes );\r
+  stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );\r
+  memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );\r
   if ( stream_.userBuffer[mode] == NULL ) {\r
     errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";\r
     goto error;\r
@@ -1241,7 +1264,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
   // Setup the device property listener for over/underload.\r
   property.mSelector = kAudioDeviceProcessorOverload;\r
-  result = AudioObjectAddPropertyListener( id, &property, deviceListener, (void *) handle );\r
+  result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );\r
 \r
   return SUCCESS;\r
 \r
@@ -2742,14 +2765,14 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     return FAILURE;\r
   }\r
 \r
-  // The getDeviceInfo() function will not work when a stream is open\r
-  // because ASIO does not allow multiple devices to run at the same\r
-  // time.  Thus, we'll probe the system before opening a stream and\r
-  // save the results for use by getDeviceInfo().\r
-  this->saveDeviceInfo();\r
-\r
   // Only load the driver once for duplex stream.\r
   if ( mode != INPUT || stream_.mode != OUTPUT ) {\r
+    // The getDeviceInfo() function will not work when a stream is open\r
+    // because ASIO does not allow multiple devices to run at the same\r
+    // time.  Thus, we'll probe the system before opening a stream and\r
+    // save the results for use by getDeviceInfo().\r
+    this->saveDeviceInfo();\r
+\r
     if ( !drivers.loadDriver( driverName ) ) {\r
       errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";\r
       errorText_ = errorStream_.str();\r
@@ -3804,7 +3827,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
     }\r
     if ( found == false ) info.sampleRates.push_back( rates[i] );\r
   }\r
-  sort( info.sampleRates.begin(), info.sampleRates.end() );\r
+  std::sort( info.sampleRates.begin(), info.sampleRates.end() );\r
 \r
   // If device opens for both playback and capture, we determine the channels.\r
   if ( info.outputChannels > 0 && info.inputChannels > 0 )\r
@@ -4969,16 +4992,12 @@ extern "C" unsigned __stdcall callbackHandler( void *ptr )
 \r
 std::string convertTChar( LPCTSTR name )\r
 {\r
-  std::string s;\r
-\r
 #if defined( UNICODE ) || defined( _UNICODE )\r
-  // Yes, this conversion doesn't make sense for two-byte characters\r
-  // but RtAudio is currently written to return an std::string of\r
-  // one-byte chars for the device name.\r
-  for ( unsigned int i=0; i<wcslen( name ); i++ )\r
-    s.push_back( name[i] );\r
+  int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);\r
+  std::string s( length, 0 );\r
+  length = WideCharToMultiByte(CP_UTF8, 0, name, wcslen(name), &s[0], length, NULL, NULL);\r
 #else\r
-  s.append( std::string( name ) );\r
+  std::string s( name );\r
 #endif\r
 \r
   return s;\r
@@ -5128,10 +5147,11 @@ struct AlsaHandle {
   snd_pcm_t *handles[2];\r
   bool synchronized;\r
   bool xrun[2];\r
-  pthread_cond_t runnable;\r
+  pthread_cond_t runnable_cv;\r
+  bool runnable;\r
 \r
   AlsaHandle()\r
-    :synchronized(false) { xrun[0] = false; xrun[1] = false; }\r
+    :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }\r
 };\r
 \r
 extern "C" void *alsaCallbackHandler( void * ptr );\r
@@ -5819,7 +5839,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
       goto error;\r
     }\r
 \r
-    if ( pthread_cond_init( &apiInfo->runnable, NULL ) ) {\r
+    if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {\r
       errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";\r
       goto error;\r
     }\r
@@ -5932,7 +5952,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
 \r
  error:\r
   if ( apiInfo ) {\r
-    pthread_cond_destroy( &apiInfo->runnable );\r
+    pthread_cond_destroy( &apiInfo->runnable_cv );\r
     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
     delete apiInfo;\r
@@ -5965,8 +5985,10 @@ void RtApiAlsa :: closeStream()
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
   stream_.callbackInfo.isRunning = false;\r
   MUTEX_LOCK( &stream_.mutex );\r
-  if ( stream_.state == STREAM_STOPPED )\r
-    pthread_cond_signal( &apiInfo->runnable );\r
+  if ( stream_.state == STREAM_STOPPED ) {\r
+    apiInfo->runnable = true;\r
+    pthread_cond_signal( &apiInfo->runnable_cv );\r
+  }\r
   MUTEX_UNLOCK( &stream_.mutex );\r
   pthread_join( stream_.callbackInfo.thread, NULL );\r
 \r
@@ -5979,7 +6001,7 @@ void RtApiAlsa :: closeStream()
   }\r
 \r
   if ( apiInfo ) {\r
-    pthread_cond_destroy( &apiInfo->runnable );\r
+    pthread_cond_destroy( &apiInfo->runnable_cv );\r
     if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );\r
     if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );\r
     delete apiInfo;\r
@@ -6046,10 +6068,10 @@ void RtApiAlsa :: startStream()
   stream_.state = STREAM_RUNNING;\r
 \r
  unlock:\r
+  apiInfo->runnable = true;\r
+  pthread_cond_signal( &apiInfo->runnable_cv );\r
   MUTEX_UNLOCK( &stream_.mutex );\r
 \r
-  pthread_cond_signal( &apiInfo->runnable );\r
-\r
   if ( result >= 0 ) return;\r
   error( RtError::SYSTEM_ERROR );\r
 }\r
@@ -6154,7 +6176,9 @@ void RtApiAlsa :: callbackEvent()
   AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;\r
   if ( stream_.state == STREAM_STOPPED ) {\r
     MUTEX_LOCK( &stream_.mutex );\r
-    pthread_cond_wait( &apiInfo->runnable, &stream_.mutex );\r
+    while ( !apiInfo->runnable )\r
+      pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );\r
+\r
     if ( stream_.state != STREAM_RUNNING ) {\r
       MUTEX_UNLOCK( &stream_.mutex );\r
       return;\r
index d8591c066c51957fb191b1881c11aa892c6bfa23..6032368adc1c3b575c504d757b819400e68dc7ca 100644 (file)
--- a/RtAudio.h
+++ b/RtAudio.h
@@ -42,7 +42,7 @@
   \file RtAudio.h
  */
 
-// RtAudio: Version 4.0.8
+// RtAudio: Version 4.0.9
 
 #ifndef __RTAUDIO_H
 #define __RTAUDIO_H
index 1c0ecd1c8416b7500c19089a7ad5cf3c0b50c623..3ed7a9e11e1d0254fa226da84c58e245be0f9bf3 100644 (file)
@@ -32,7 +32,7 @@ Devices are now re-enumerated every time the RtAudio::getDeviceCount(), RtAudio:
 
 \section download Download
 
-Latest Release (12 April 2011): <A href="http://www.music.mcgill.ca/~gary/rtaudio/release/rtaudio-4.0.8.tar.gz">Version 4.0.8</A>
+Latest Release (11 August 2011): <A href="http://www.music.mcgill.ca/~gary/rtaudio/release/rtaudio-4.0.9.tar.gz">Version 4.0.9</A>
 
 \section documentation Documentation Links
 
index 512c5831be3bcedd55f15886949dbff4e8552e8e..ae3025ef4ba84ed420b4f4721489d1542eabbdaf 100644 (file)
@@ -2,6 +2,13 @@ RtAudio - a set of C++ classes that provide a common API for realtime audio inpu
 
 By Gary P. Scavone, 2001-2011.
 
+v4.0.9: (?? August 2011)
+- fix for ASIO problem enumerating devices after opening duplex stream (Oliver Larkin)
+- fix for OS-X problems setting sample rate and bits-per-sample
+- updates for OS-X "Lion"
+- updates for wide character support in Windows DS (UNICODE)
+- fix for possible ALSA callback thread hang (thanks to Tristan Matthews)
+
 v4.0.8: (12 April 2011)
 - fix for MinGW4 problem enumerating and setting sample rates (iasiothiscallresolver, Dmitry Kostjuchenko)
 - fix for OS-X problem handling device names in some languages (CFString conversion, Vincent BĂ©nony)