Another build fix.
[rtaudio-cdist.git] / RtAudio.cpp
index 470677bd2a723e5dbb5aee34566369935e0b3481..539cdc273a069c2424c8ea2030575bc4e75d1665 100644 (file)
@@ -10,7 +10,7 @@
     RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/\r
 \r
     RtAudio: realtime audio i/o C++ classes\r
-    Copyright (c) 2001-2014 Gary P. Scavone\r
+    Copyright (c) 2001-2016 Gary P. Scavone\r
 \r
     Permission is hereby granted, free of charge, to any person\r
     obtaining a copy of this software and associated documentation files\r
@@ -38,7 +38,7 @@
 */\r
 /************************************************************************/\r
 \r
-// RtAudio: Version 4.1.1\r
+// RtAudio: Version 4.1.2\r
 \r
 #include "RtAudio.h"\r
 #include <iostream>\r
@@ -46,6 +46,7 @@
 #include <cstring>\r
 #include <climits>\r
 #include <algorithm>\r
+#include <cmath>\r
 \r
 // Static variable definitions.\r
 const unsigned int RtApi::MAX_SAMPLE_RATES = 14;\r
@@ -408,7 +409,9 @@ double RtApi :: getStreamTime( void )
   struct timeval then;\r
   struct timeval now;\r
 \r
-  if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )\r
+  // If lastTickTimestamp is 0 it means we haven't had a "last tick" since\r
+  // we started the stream.\r
+  if ( stream_.state != STREAM_RUNNING || (stream_.lastTickTimestamp.tv_sec == 0 && stream_.lastTickTimestamp.tv_usec == 0) )\r
     return stream_.streamTime;\r
 \r
   gettimeofday( &now, NULL );\r
@@ -436,6 +439,14 @@ unsigned int RtApi :: getStreamSampleRate( void )
  return stream_.sampleRate;\r
 }\r
 \r
+void RtApi :: startStream( void )\r
+{\r
+#if defined( HAVE_GETTIMEOFDAY )\r
+  stream_.lastTickTimestamp.tv_sec = 0;\r
+  stream_.lastTickTimestamp.tv_usec = 0;\r
+#endif\r
+}\r
+\r
 \r
 // *************************************************** //\r
 //\r
@@ -1406,6 +1417,18 @@ void RtApiCore :: closeStream( void )
 \r
   CoreHandle *handle = (CoreHandle *) stream_.apiHandle;\r
   if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {\r
+    if (handle) {\r
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+        kAudioObjectPropertyScopeGlobal,\r
+        kAudioObjectPropertyElementMaster };\r
+\r
+      property.mSelector = kAudioDeviceProcessorOverload;\r
+      property.mScope = kAudioObjectPropertyScopeGlobal;\r
+      if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {\r
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
+        error( RtAudioError::WARNING );\r
+      }\r
+    }\r
     if ( stream_.state == STREAM_RUNNING )\r
       AudioDeviceStop( handle->id[0], callbackHandler );\r
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
@@ -1417,6 +1440,18 @@ void RtApiCore :: closeStream( void )
   }\r
 \r
   if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {\r
+    if (handle) {\r
+      AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,\r
+        kAudioObjectPropertyScopeGlobal,\r
+        kAudioObjectPropertyElementMaster };\r
+\r
+      property.mSelector = kAudioDeviceProcessorOverload;\r
+      property.mScope = kAudioObjectPropertyScopeGlobal;\r
+      if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {\r
+        errorText_ = "RtApiCore::closeStream(): error removing property listener!";\r
+        error( RtAudioError::WARNING );\r
+      }\r
+    }\r
     if ( stream_.state == STREAM_RUNNING )\r
       AudioDeviceStop( handle->id[1], callbackHandler );\r
 #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )\r
@@ -1451,6 +1486,7 @@ void RtApiCore :: closeStream( void )
 void RtApiCore :: startStream( void )\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiCore::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -2403,6 +2439,7 @@ void RtApiJack :: closeStream( void )
 void RtApiJack :: startStream( void )\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiJack::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -3012,7 +3049,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
     *bufferSize = stream_.bufferSize;\r
 \r
   } else {\r
-    if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
+    if ( *bufferSize == 0 ) *bufferSize = preferSize;\r
+    else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;\r
     else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;\r
     else if ( granularity == -1 ) {\r
       // Make sure bufferSize is a power of two.\r
@@ -3281,6 +3319,7 @@ bool stopThreadCalled = false;
 void RtApiAsio :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiAsio::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -3657,7 +3696,7 @@ static const char* getAsioErrorString( ASIOError result )
 #include <audioclient.h>\r
 #include <avrt.h>\r
 #include <mmdeviceapi.h>\r
-#include <functiondiscoverykeys_devpkey.h>\r
+#include <FunctionDiscoveryKeys_devpkey.h>\r
 \r
 //=============================================================================\r
 \r
@@ -4292,7 +4331,8 @@ void RtApiWasapi::closeStream( void )
 void RtApiWasapi::startStream( void )\r
 {\r
   verifyStream();\r
-\r
+  RtApi::startStream();\r
+  \r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiWasapi::startStream: The stream is already running.";\r
     error( RtAudioError::WARNING );\r
@@ -5133,10 +5173,10 @@ void RtApiWasapi::wasapiThread()
     // if the callback buffer was pushed renderBuffer reset callbackPulled flag\r
     if ( callbackPushed ) {\r
       callbackPulled = false;\r
+      // tick stream time\r
+      RtApi::tickStreamTime();\r
     }\r
 \r
-    // tick stream time\r
-    RtApi::tickStreamTime();\r
   }\r
 \r
 Exit:\r
@@ -5294,14 +5334,11 @@ unsigned int RtApiDs :: getDeviceCount( void )
     error( RtAudioError::WARNING );\r
   }\r
 \r
-  // Clean out any devices that may have disappeared.\r
-  std::vector< int > indices;\r
-  for ( unsigned int i=0; i<dsDevices.size(); i++ )\r
-    if ( dsDevices[i].found == false ) indices.push_back( i );\r
-  //unsigned int nErased = 0;\r
-  for ( unsigned int i=0; i<indices.size(); i++ )\r
-    dsDevices.erase( dsDevices.begin()+indices[i] );\r
-  //dsDevices.erase( dsDevices.begin()-nErased++ );\r
+  // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).\r
+  for ( unsigned int i=0; i<dsDevices.size(); ) {\r
+    if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );\r
+    else i++;\r
+  }\r
 \r
   return static_cast<unsigned int>(dsDevices.size());\r
 }\r
@@ -6044,6 +6081,8 @@ void RtApiDs :: closeStream()
 void RtApiDs :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
+  \r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiDs::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -6317,6 +6356,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6324,6 +6364,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6332,6 +6373,7 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
@@ -6339,6 +6381,7 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
@@ -6360,6 +6403,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6411,6 +6455,7 @@ void RtApiDs :: callbackEvent()
       if ( FAILED( result ) ) {\r
         errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";\r
         errorText_ = errorStream_.str();\r
+        MUTEX_UNLOCK( &stream_.mutex );\r
         error( RtAudioError::SYSTEM_ERROR );\r
         return;\r
       }\r
@@ -6452,6 +6497,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6465,6 +6511,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6501,6 +6548,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6562,6 +6610,7 @@ void RtApiDs :: callbackEvent()
         if ( FAILED( result ) ) {\r
           errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";\r
           errorText_ = errorStream_.str();\r
+          MUTEX_UNLOCK( &stream_.mutex );\r
           error( RtAudioError::SYSTEM_ERROR );\r
           return;\r
         }\r
@@ -6576,6 +6625,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -6597,6 +6647,7 @@ void RtApiDs :: callbackEvent()
     if ( FAILED( result ) ) {\r
       errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";\r
       errorText_ = errorStream_.str();\r
+      MUTEX_UNLOCK( &stream_.mutex );\r
       error( RtAudioError::SYSTEM_ERROR );\r
       return;\r
     }\r
@@ -7709,6 +7760,7 @@ void RtApiAlsa :: startStream()
   // This method calls snd_pcm_prepare if the device isn't already in that state.\r
 \r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiAlsa::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -8002,6 +8054,8 @@ void RtApiAlsa :: callbackEvent()
             errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";\r
             errorText_ = errorStream_.str();\r
           }\r
+          else\r
+            errorText_ =  "RtApiAlsa::callbackEvent: audio write error, underrun.";\r
         }\r
         else {\r
           errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";\r
@@ -8035,7 +8089,7 @@ static void *alsaCallbackHandler( void *ptr )
   bool *isRunning = &info->isRunning;\r
 \r
 #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)\r
-  if ( &info->doRealtime ) {\r
+  if ( info->doRealtime ) {\r
     pthread_t tID = pthread_self();     // ID of this thread\r
     sched_param prio = { info->priority }; // scheduling priority of thread\r
     pthread_setschedparam( tID, SCHED_RR, &prio );\r
@@ -8257,12 +8311,22 @@ void RtApiPulse::callbackEvent( void )
   MUTEX_UNLOCK( &stream_.mutex );\r
   RtApi::tickStreamTime();\r
 \r
+  if (pah->s_play) {\r
+    int e = 0;\r
+    pa_usec_t const lat = pa_simple_get_latency(pah->s_play, &e);\r
+    if (e == 0) {\r
+      stream_.latency[0] = lat * stream_.sampleRate / 1000000;\r
+    }\r
+  }\r
+\r
   if ( doStopStream == 1 )\r
     stopStream();\r
 }\r
 \r
 void RtApiPulse::startStream( void )\r
 {\r
+  RtApi::startStream();\r
+  \r
   PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );\r
 \r
   if ( stream_.state == STREAM_CLOSED ) {\r
@@ -8301,6 +8365,7 @@ void RtApiPulse::stopStream( void )
   }\r
 \r
   stream_.state = STREAM_STOPPED;\r
+  pah->runnable = false;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
   if ( pah && pah->s_play ) {\r
@@ -8335,6 +8400,7 @@ void RtApiPulse::abortStream( void )
   }\r
 \r
   stream_.state = STREAM_STOPPED;\r
+  pah->runnable = false;\r
   MUTEX_LOCK( &stream_.mutex );\r
 \r
   if ( pah && pah->s_play ) {\r
@@ -8487,7 +8553,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
     }\r
     break;\r
   case OUTPUT:\r
-    pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
+    pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );\r
     if ( !pah->s_play ) {\r
       errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";\r
       goto error;\r
@@ -9199,6 +9265,7 @@ void RtApiOss :: closeStream()
 void RtApiOss :: startStream()\r
 {\r
   verifyStream();\r
+  RtApi::startStream();\r
   if ( stream_.state == STREAM_RUNNING ) {\r
     errorText_ = "RtApiOss::startStream(): the stream is already running!";\r
     error( RtAudioError::WARNING );\r
@@ -10107,8 +10174,8 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
 \r
 void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )\r
 {\r
-  register char val;\r
-  register char *ptr;\r
+  char val;\r
+  char *ptr;\r
 \r
   ptr = buffer;\r
   if ( format == RTAUDIO_SINT16 ) {\r