RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes
- Copyright (c) 2001-2017 Gary P. Scavone
+ Copyright (c) 2001-2019 Gary P. Scavone
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
*/
/************************************************************************/
-// RtAudio: Version 5.0.0
+// RtAudio: Version 5.1.0
#include "RtAudio.h"
#include <iostream>
{ "ds" , "DirectSound" },
{ "dummy" , "Dummy" },
};
+
const unsigned int rtaudio_num_api_names =
sizeof(rtaudio_api_names)/sizeof(rtaudio_api_names[0]);
RtAudio :: RtAudio( RtAudio::Api api )
{
rtapi_ = 0;
-
+
if ( api != UNSPECIFIED ) {
// Attempt to open the specified API.
openRtApi( api );
if ( rtapi_ ) return;
// It should not be possible to get here because the preprocessor
- // definition __RTAUDIO_DUMMY__ is automatically defined if no
- // API-specific definitions are passed to the compiler. But just in
- // case something weird happens, we'll thow an error.
+ // definition __RTAUDIO_DUMMY__ is automatically defined in RtAudio.h
+ // if no API-specific definitions are passed to the compiler. But just
+ // in case something weird happens, we'll thow an error.
std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";
throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );
}
RtApi :: RtApi()
{
- stream_.state = STREAM_CLOSED;
- stream_.mode = UNINITIALIZED;
- stream_.apiHandle = 0;
- stream_.userBuffer[0] = 0;
- stream_.userBuffer[1] = 0;
+ clearStreamInfo();
MUTEX_INITIALIZE( &stream_.mutex );
showWarnings_ = true;
firstErrorOccurred_ = false;
stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
+ /*
#if defined( HAVE_GETTIMEOFDAY )
gettimeofday( &stream_.lastTickTimestamp, NULL );
#endif
+ */
}
long RtApi :: getStreamLatency( void )
{
- verifyStream();
-
long totalLatency = 0;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
totalLatency = stream_.latency[0];
return totalLatency;
}
+/*
double RtApi :: getStreamTime( void )
{
- verifyStream();
-
#if defined( HAVE_GETTIMEOFDAY )
// Return a very accurate estimate of the stream time by
// adding in the elapsed time since the last tick.
(then.tv_sec + 0.000001 * then.tv_usec));
#else
return stream_.streamTime;
-#endif
+ #endif
}
+*/
void RtApi :: setStreamTime( double time )
{
- verifyStream();
+ // verifyStream();
if ( time >= 0.0 )
stream_.streamTime = time;
+ /*
#if defined( HAVE_GETTIMEOFDAY )
gettimeofday( &stream_.lastTickTimestamp, NULL );
#endif
+ */
}
+/*
unsigned int RtApi :: getStreamSampleRate( void )
{
- verifyStream();
+ verifyStream();
return stream_.sampleRate;
}
+*/
// *************************************************** //
return info;
}
+ // Probe the currently configured sample rate
+ Float64 nominalRate;
+ dataSize = sizeof( Float64 );
+ property.mSelector = kAudioDevicePropertyNominalSampleRate;
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );
+ if ( result == noErr ) info.currentSampleRate = (unsigned int) nominalRate;
+
// CoreAudio always uses 32-bit floating point data for PCM streams.
// Thus, any other "physical" formats supported by the device are of
// no interest to the client.
return kAudioHardwareNoError;
}
+static OSStatus disconnectListener( AudioObjectID /*inDevice*/,
+ UInt32 nAddresses,
+ const AudioObjectPropertyAddress properties[],
+ void* infoPointer )
+{
+ for ( UInt32 i=0; i<nAddresses; i++ ) {
+ if ( properties[i].mSelector == kAudioDevicePropertyDeviceIsAlive ) {
+ CallbackInfo *info = (CallbackInfo *) infoPointer;
+ RtApiCore *object = (RtApiCore *) info->object;
+ info->deviceDisconnected = true;
+ object->closeStream();
+ return kAudioHardwareUnspecifiedError;
+ }
+ }
+
+ return kAudioHardwareNoError;
+}
+
static OSStatus xrunListener( AudioObjectID /*inDevice*/,
UInt32 nAddresses,
const AudioObjectPropertyAddress properties[],
return kAudioHardwareNoError;
}
-static OSStatus rateListener( AudioObjectID inDevice,
- UInt32 /*nAddresses*/,
- const AudioObjectPropertyAddress /*properties*/[],
- void* ratePointer )
-{
- Float64 *rate = (Float64 *) ratePointer;
- UInt32 dataSize = sizeof( Float64 );
- AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );
- return kAudioHardwareNoError;
-}
-
bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
return FAILURE;
}
- // Only change the sample rate if off by more than 1 Hz.
+ // Only try to change the sample rate if off by more than 1 Hz.
if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {
- // Set a property listener for the sample rate change
- Float64 reportedRate = 0.0;
- AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
nominalRate = (Float64) sampleRate;
result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );
if ( result != noErr ) {
- AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";
errorText_ = errorStream_.str();
return FAILURE;
// Now wait until the reported nominal rate is what we just set.
UInt32 microCounter = 0;
+ Float64 reportedRate = 0.0;
while ( reportedRate != nominalRate ) {
microCounter += 5000;
- if ( microCounter > 5000000 ) break;
+ if ( microCounter > 2000000 ) break;
usleep( 5000 );
+ result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &reportedRate );
}
- // Remove the property listener.
- AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
-
- if ( microCounter > 5000000 ) {
+ if ( microCounter > 2000000 ) {
errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";
errorText_ = errorStream_.str();
return FAILURE;
property.mSelector = kAudioDeviceProcessorOverload;
property.mScope = kAudioObjectPropertyScopeGlobal;
result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
+ if ( result != noErr ) {
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting xrun listener for device (" << device << ").";
+ errorText_ = errorStream_.str();
+ goto error;
+ }
+
+ // Setup a listener to detect a possible device disconnect.
+ property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+ property.mScope = kAudioObjectPropertyScopeGlobal;
+ result = AudioObjectAddPropertyListener( id , &property, disconnectListener, (void *) &stream_.callbackInfo );
+ if ( result != noErr ) {
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << device << ").";
+ errorText_ = errorStream_.str();
+ goto error;
+ }
return SUCCESS;
stream_.deviceBuffer = 0;
}
- stream_.state = STREAM_CLOSED;
+ clearStreamInfo();
+ //stream_.state = STREAM_CLOSED;
return FAILURE;
}
}
// Destroy pthread condition variable.
+ pthread_cond_signal( &handle->condition ); // signal condition variable in case stopStream is blocked
pthread_cond_destroy( &handle->condition );
delete handle;
stream_.apiHandle = 0;
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+ if ( info->deviceDisconnected ) {
+ errorText_ = "RtApiCore: the stream device was disconnected (and closed)!";
+ error( RtAudioError::DEVICE_DISCONNECT );
+ }
+
+ clearStreamInfo();
+ //stream_.mode = UNINITIALIZED;
+ //stream_.state = STREAM_CLOSED;
}
void RtApiCore :: startStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiCore::startStream(): the stream is already running!";
+ //verifyStream();
+ if ( stream_.state != STREAM_STOPPED ) {
+ if ( stream_.state == STREAM_RUNNING )
+ errorText_ = "RtApiCore::startStream(): the stream is already running!";
+ else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiCore::startStream(): the stream is stopping or closed!";
error( RtAudioError::WARNING );
return;
}
+ /*
#if defined( HAVE_GETTIMEOFDAY )
gettimeofday( &stream_.lastTickTimestamp, NULL );
#endif
+ */
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
void RtApiCore :: stopStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
+ //verifyStream();
+ if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) {
+ if ( stream_.state == STREAM_STOPPED )
+ errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
+ else if ( stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiCore::stopStream(): the stream is closed!";
error( RtAudioError::WARNING );
return;
}
}
stream_.state = STREAM_STOPPED;
+ // set stream time to zero?
unlock:
if ( result == noErr ) return;
void RtApiCore :: abortStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
+ //verifyStream();
+ if ( stream_.state != STREAM_RUNNING ) {
+ if ( stream_.state == STREAM_STOPPED )
+ errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
+ else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiCore::abortStream(): the stream is stopping or closed!";
error( RtAudioError::WARNING );
return;
}
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
handle->drainCounter = 2;
+ stream_.state = STREAM_STOPPING;
stopStream();
}
int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
stream_.bufferSize, streamTime, status, info->userData );
if ( cbReturnValue == 2 ) {
- stream_.state = STREAM_STOPPING;
- handle->drainCounter = 2;
abortStream();
return SUCCESS;
}
RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;
if ( errorCallback ) {
- // abortStream() can generate new error messages. Ignore them. Just keep original one.
-
- if ( firstErrorOccurred_ )
- return;
-
- firstErrorOccurred_ = true;
const std::string errorMessage = errorText_;
-
- if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {
- stream_.callbackInfo.isRunning = false; // exit from the thread
- abortStream();
- }
-
errorCallback( type, errorMessage );
- firstErrorOccurred_ = false;
- return;
}
-
- if ( type == RtAudioError::WARNING && showWarnings_ == true )
- std::cerr << '\n' << errorText_ << "\n\n";
- else if ( type != RtAudioError::WARNING )
- throw( RtAudioError( errorText_, type ) );
+ else {
+ if ( showWarnings_ == true )
+ std::cerr << '\n' << errorText_ << "\n\n";
+ }
}
+/*
void RtApi :: verifyStream()
{
if ( stream_.state == STREAM_CLOSED ) {
error( RtAudioError::INVALID_USE );
}
}
+*/
void RtApi :: clearStreamInfo()
{