and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
(DirectSound, ASIO and WASAPI) operating systems.
+ RtAudio GitHub site: https://github.com/thestk/rtaudio
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 6.0.0beta1
#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]);
#endif
RtAudio::UNSPECIFIED,
};
+
extern "C" const unsigned int rtaudio_num_compiled_apis =
sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1;
}
#endif
}
-RtAudio :: RtAudio( RtAudio::Api api )
+RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback errorCallback )
{
rtapi_ = 0;
+ std::string errorMessage;
if ( api != UNSPECIFIED ) {
// Attempt to open the specified API.
openRtApi( api );
- if ( rtapi_ ) return;
- // No compiled support for specified API value. Issue a debug
- // warning and continue as if no API was specified.
- std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
+ if ( rtapi_ ) {
+ if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
+ return;
+ }
+
+ // No compiled support for specified API value. Issue a warning
+ // and continue as if no API was specified.
+ errorMessage = "RtAudio: no compiled support for specified API argument!";
+ if ( errorCallback )
+ errorCallback( RTAUDIO_INVALID_USE, errorMessage );
+ else
+ std::cerr << '\n' << errorMessage << '\n' << std::endl;
}
// Iterate through the compiled APIs and return as soon as we find
if ( rtapi_ && rtapi_->getDeviceCount() ) break;
}
- if ( rtapi_ ) return;
+ if ( rtapi_ ) {
+ if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
+ 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.
- std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";
- throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );
+ // 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, issue an error message and abort.
+ errorMessage = "RtAudio: no compiled API support found ... critical error!";
+ if ( errorCallback )
+ errorCallback( RTAUDIO_INVALID_USE, errorMessage );
+ else
+ std::cerr << '\n' << errorMessage << '\n' << std::endl;
+ abort();
}
RtAudio :: ~RtAudio()
delete rtapi_;
}
-void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
- RtAudio::StreamParameters *inputParameters,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames,
- RtAudioCallback callback, void *userData,
- RtAudio::StreamOptions *options,
- RtAudioErrorCallback errorCallback )
+RtAudioErrorType RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
+ RtAudio::StreamParameters *inputParameters,
+ RtAudioFormat format, unsigned int sampleRate,
+ unsigned int *bufferFrames,
+ RtAudioCallback callback, void *userData,
+ RtAudio::StreamOptions *options )
{
return rtapi_->openStream( outputParameters, inputParameters, format,
sampleRate, bufferFrames, callback,
- userData, options, errorCallback );
+ userData, options );
}
// *************************************************** //
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 );
+ errorCallback_ = 0;
showWarnings_ = true;
- firstErrorOccurred_ = false;
}
RtApi :: ~RtApi()
MUTEX_DESTROY( &stream_.mutex );
}
-void RtApi :: openStream( RtAudio::StreamParameters *oParams,
- RtAudio::StreamParameters *iParams,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames,
- RtAudioCallback callback, void *userData,
- RtAudio::StreamOptions *options,
- RtAudioErrorCallback errorCallback )
+RtAudioErrorType RtApi :: openStream( RtAudio::StreamParameters *oParams,
+ RtAudio::StreamParameters *iParams,
+ RtAudioFormat format, unsigned int sampleRate,
+ unsigned int *bufferFrames,
+ RtAudioCallback callback, void *userData,
+ RtAudio::StreamOptions *options )
{
if ( stream_.state != STREAM_CLOSED ) {
errorText_ = "RtApi::openStream: a stream is already open!";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
// Clear stream information potentially left from a previously open stream.
if ( oParams && oParams->nChannels < 1 ) {
errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
if ( iParams && iParams->nChannels < 1 ) {
errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
if ( oParams == NULL && iParams == NULL ) {
errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
if ( formatBytes(format) == 0 ) {
errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
unsigned int nDevices = getDeviceCount();
oChannels = oParams->nChannels;
if ( oParams->deviceId >= nDevices ) {
errorText_ = "RtApi::openStream: output device parameter value is invalid.";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
}
iChannels = iParams->nChannels;
if ( iParams->deviceId >= nDevices ) {
errorText_ = "RtApi::openStream: input device parameter value is invalid.";
- error( RtAudioError::INVALID_USE );
- return;
+ return error( RTAUDIO_INVALID_USE );
}
}
result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
sampleRate, format, bufferFrames, options );
if ( result == false ) {
- error( RtAudioError::SYSTEM_ERROR );
- return;
+ return error( RTAUDIO_SYSTEM_ERROR );
}
}
result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
sampleRate, format, bufferFrames, options );
if ( result == false ) {
- if ( oChannels > 0 ) closeStream();
- error( RtAudioError::SYSTEM_ERROR );
- return;
+ return error( RTAUDIO_SYSTEM_ERROR );
}
}
stream_.callbackInfo.callback = (void *) callback;
stream_.callbackInfo.userData = userData;
- stream_.callbackInfo.errorCallback = (void *) errorCallback;
if ( options ) options->numberOfBuffers = stream_.nBuffers;
stream_.state = STREAM_STOPPED;
+ return RTAUDIO_NO_ERROR;
}
unsigned int RtApi :: getDefaultInputDevice( void )
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();
-
if ( time >= 0.0 )
stream_.streamTime = time;
+ /*
#if defined( HAVE_GETTIMEOFDAY )
gettimeofday( &stream_.lastTickTimestamp, NULL );
#endif
+ */
}
unsigned int RtApi :: getStreamSampleRate( void )
{
- verifyStream();
-
- return stream_.sampleRate;
+ if ( isStreamOpen() ) return stream_.sampleRate;
+ else return 0;
}
pthread_cond_t condition;
int drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not.
+ bool xrunListenerAdded[2];
+ bool disconnectListenerAdded[2];
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; }
+ :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; procId[0] = 0; procId[1] = 0; xrun[0] = false; xrun[1] = false; xrunListenerAdded[0] = false; xrunListenerAdded[1] = false; disconnectListenerAdded[0] = false; disconnectListenerAdded[1] = false; }
};
RtApiCore:: RtApiCore()
OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
if ( result != noErr ) {
errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
}
#endif
}
OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
return 0;
}
OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
return 0;
}
result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
return 0;
}
if ( id == deviceList[i] ) return i;
errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return 0;
}
OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
return 0;
}
result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_SYSTEM_ERROR );
return 0;
}
if ( id == deviceList[i] ) return i;
errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return 0;
}
unsigned int nDevices = getDeviceCount();
if ( nDevices == 0 ) {
errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
+ error( RTAUDIO_INVALID_USE );
return info;
}
if ( device >= nDevices ) {
errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
+ error( RTAUDIO_INVALID_USE );
return info;
}
0, NULL, &dataSize, (void *) &deviceList );
if ( result != noErr ) {
errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( result != noErr ) {
errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
//const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
- int length = CFStringGetLength(cfname);
+ long length = CFStringGetLength(cfname);
char *mname = (char *)malloc(length * 3 + 1);
#if defined( UNICODE ) || defined( _UNICODE )
CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);
if ( result != noErr ) {
errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( result != noErr || dataSize == 0 ) {
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
bufferList = (AudioBufferList *) malloc( dataSize );
if ( bufferList == NULL ) {
errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
free( bufferList );
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( result != noErr || dataSize == 0 ) {
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
bufferList = (AudioBufferList *) malloc( dataSize );
if ( bufferList == NULL ) {
errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
free( bufferList );
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( result != kAudioHardwareNoError || dataSize == 0 ) {
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( result != kAudioHardwareNoError ) {
errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( info.sampleRates.size() == 0 ) {
errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
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,
}
// Look for a single stream meeting our needs.
- UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
+ UInt32 firstStream = 0, streamCount = 1, streamChannels = 0, channelOffset = 0;
for ( iStream=0; iStream<nStreams; iStream++ ) {
streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
if ( streamChannels >= channels + offsetCounter ) {
return FAILURE;
}
- if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
- else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
+ if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMinimum;
+ else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned int) bufferRange.mMaximum;
+ if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned int) bufferRange.mMinimum;
// Set the buffer size. For multiple streams, I'm assuming we only
// need to make this setting for the master channel.
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;
else {
errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
}
}
// Allocate necessary internal buffers.
unsigned long bufferBytes;
bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );
- memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );
if ( stream_.userBuffer[mode] == NULL ) {
errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
goto error;
if ( makeBuffer ) {
bufferBytes *= *bufferSize;
if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
+ stream_.deviceBuffer = (char *) calloc( bufferBytes, sizeof(char) );
if ( stream_.deviceBuffer == NULL ) {
errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
goto error;
}
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
- // Only one callback procedure per device.
+ // Only one callback procedure and property listener per device.
stream_.mode = DUPLEX;
else {
#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
stream_.mode = DUPLEX;
else
stream_.mode = mode;
- }
-
- // Setup the device property listener for over/underload.
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
-
- return SUCCESS;
- error:
- if ( handle ) {
- pthread_cond_destroy( &handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
+ // Setup the device property listener for over/underload.
+ 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;
+ }
+ handle->xrunListenerAdded[mode] = true;
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
+ // Setup a listener to detect a possible device disconnect.
+ property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+ result = AudioObjectAddPropertyListener( id , &property, disconnectListener, (void *) &stream_.callbackInfo );
+ if ( result != noErr ) {
+ AudioObjectRemovePropertyListener( id, &property, xrunListener, (void *) handle );
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << device << ").";
+ errorText_ = errorStream_.str();
+ goto error;
}
+ handle->disconnectListenerAdded[mode] = true;
}
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
+ return SUCCESS;
- stream_.state = STREAM_CLOSED;
+ error:
+ closeStream(); // this should safely clear out procedures, listeners and memory, even for duplex stream
return FAILURE;
}
{
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiCore::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return;
}
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if (handle) {
+ if ( handle ) {
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
- errorText_ = "RtApiCore::closeStream(): error removing property listener!";
- error( RtAudioError::WARNING );
+ if ( handle->xrunListenerAdded[0] ) {
+ property.mSelector = kAudioDeviceProcessorOverload;
+ if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
+ errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!";
+ error( RTAUDIO_WARNING );
+ }
}
- }
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[0], callbackHandler );
+ if ( handle->disconnectListenerAdded[0] ) {
+ property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+ if (AudioObjectRemovePropertyListener( handle->id[0], &property, disconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
+ errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
+ error( RTAUDIO_WARNING );
+ }
+ }
+
#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
-#else
- // deprecated in favor of AudioDeviceDestroyIOProcID()
- AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+ if ( handle->procId[0] ) {
+ if ( stream_.state == STREAM_RUNNING )
+ AudioDeviceStop( handle->id[0], handle-procId[0] );
+ AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
+ }
+#else // deprecated behaviour
+ if ( stream_.state == STREAM_RUNNING )
+ AudioDeviceStop( handle->id[0], callbackHandler );
+ AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
#endif
+ }
+ }
}
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
- if (handle) {
+ if ( handle ) {
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
- errorText_ = "RtApiCore::closeStream(): error removing property listener!";
- error( RtAudioError::WARNING );
+ if ( handle->xrunListenerAdded[1] ) {
+ property.mSelector = kAudioDeviceProcessorOverload;
+ if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
+ errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!";
+ error( RTAUDIO_WARNING );
+ }
}
- }
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[1], callbackHandler );
+
+ if ( handle->disconnectListenerAdded[0] ) {
+ property.mSelector = kAudioDevicePropertyDeviceIsAlive;
+ if (AudioObjectRemovePropertyListener( handle->id[1], &property, disconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
+ errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
+ error( RTAUDIO_WARNING );
+ }
+ }
+
#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
-#else
- // deprecated in favor of AudioDeviceDestroyIOProcID()
- AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+ if ( handle->procId[1] ) {
+ if ( stream_.state == STREAM_RUNNING )
+ AudioDeviceStop( handle->id[1], handle->procId[1] );
+ AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
+ }
+#else // deprecated behaviour
+ if ( stream_.state == STREAM_RUNNING )
+ AudioDeviceStop( handle->id[1], callbackHandler );
+ AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
#endif
+ }
}
for ( int i=0; i<2; i++ ) {
}
// 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( RTAUDIO_DEVICE_DISCONNECT );
+ }
+
+ clearStreamInfo();
+ //stream_.mode = UNINITIALIZED;
+ //stream_.state = STREAM_CLOSED;
}
-void RtApiCore :: startStream( void )
+RtAudioErrorType RtApiCore :: startStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiCore::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
+ 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!";
+ return error( RTAUDIO_WARNING );
}
+ /*
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+ */
+
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- result = AudioDeviceStart( handle->id[0], callbackHandler );
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ result = AudioDeviceStart( handle->id[0], handle->procId[0] );
+#else // deprecated behaviour
+ result = AudioDeviceStart( handle->id[0], callbackHandler );
+#endif
if ( result != noErr ) {
errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
errorText_ = errorStream_.str();
if ( stream_.mode == INPUT ||
( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
+ // Clear user input buffer
+ unsigned long bufferBytes;
+ bufferBytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat );
+ memset( stream_.userBuffer[1], 0, bufferBytes * sizeof(char) );
+
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ result = AudioDeviceStart( handle->id[1], handle->procId[1] );
+#else // deprecated behaviour
result = AudioDeviceStart( handle->id[1], callbackHandler );
+#endif
if ( result != noErr ) {
errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
errorText_ = errorStream_.str();
stream_.state = STREAM_RUNNING;
unlock:
- if ( result == noErr ) return;
- error( RtAudioError::SYSTEM_ERROR );
+ if ( result == noErr ) return RTAUDIO_NO_ERROR;
+ return error( RTAUDIO_SYSTEM_ERROR );
}
-void RtApiCore :: stopStream( void )
+RtAudioErrorType RtApiCore :: stopStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
+ 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!";
+ return error( RTAUDIO_WARNING );
}
OSStatus result = noErr;
pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
}
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ result = AudioDeviceStop( handle->id[0], handle->procId[0] );
+#else
result = AudioDeviceStop( handle->id[0], callbackHandler );
+#endif
if ( result != noErr ) {
errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
errorText_ = errorStream_.str();
}
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
-
+#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
+ result = AudioDeviceStop( handle->id[1], handle->procId[1] );
+#else
result = AudioDeviceStop( handle->id[1], callbackHandler );
+#endif
if ( result != noErr ) {
errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
errorText_ = errorStream_.str();
stream_.state = STREAM_STOPPED;
unlock:
- if ( result == noErr ) return;
- error( RtAudioError::SYSTEM_ERROR );
+ if ( result == noErr ) return RTAUDIO_NO_ERROR;
+ return error( RTAUDIO_SYSTEM_ERROR );
}
-void RtApiCore :: abortStream( void )
+RtAudioErrorType RtApiCore :: abortStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
+ 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!";
+ return error( RTAUDIO_WARNING );
+ //return;
}
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
handle->drainCounter = 2;
- stopStream();
+ stream_.state = STREAM_STOPPING;
+ return stopStream();
}
// This function will be called by a spawned thread when the user
if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return FAILURE;
}
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;
}
}
unlock:
- //MUTEX_UNLOCK( &stream_.mutex );
- RtApi::tickStreamTime();
+ // Make sure to only tick duplex stream time once if using two devices
+ if ( stream_.mode == DUPLEX ) {
+ if ( handle->id[0] == handle->id[1] ) // same device, only one callback
+ RtApi::tickStreamTime();
+ else if ( deviceId == handle->id[0] )
+ RtApi::tickStreamTime(); // two devices, only tick on the output callback
+ } else
+ RtApi::tickStreamTime(); // input or output stream only
+
return SUCCESS;
}
#if defined(__UNIX_JACK__)
// JACK is a low-latency audio server, originally written for the
-// GNU/Linux operating system and now also ported to OS-X. It can
-// connect a number of different applications to an audio device, as
-// well as allowing them to share audio between themselves.
+// GNU/Linux operating system and now also ported to OS-X and
+// Windows. It can connect a number of different applications to an
+// audio device, as well as allowing them to share audio between
+// themselves.
//
// When using JACK with RtAudio, "devices" refer to JACK clients that
// have ports connected to the server. The JACK server is typically
jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
if ( client == 0 ) {
errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
if ( device >= nDevices ) {
jack_client_close( client );
errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
+ error( RTAUDIO_INVALID_USE );
return info;
}
if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
jack_client_close(client);
errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return info;
}
CallbackInfo *info = (CallbackInfo *) ptr;
RtApiJack *object = (RtApiJack *) info->object;
+ info->deviceDisconnected = true;
object->closeStream();
-
pthread_exit( NULL );
}
+
static void jackShutdown( void *infoPointer )
{
CallbackInfo *info = (CallbackInfo *) infoPointer;
ThreadHandle threadId;
pthread_create( &threadId, NULL, jackCloseStream, info );
- std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
}
static int jackXrun( void *infoPointer )
client = jack_client_open( "RtApiJack", jackoptions, status );
if ( client == 0 ) {
errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return FAILURE;
}
}
{
if ( stream_.state == STREAM_CLOSED ) {
errorText_ = "RtApiJack::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return;
}
JackHandle *handle = (JackHandle *) stream_.apiHandle;
if ( handle ) {
-
if ( stream_.state == STREAM_RUNNING )
jack_deactivate( handle->client );
stream_.apiHandle = 0;
}
+ CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
+ if ( info->deviceDisconnected ) {
+ errorText_ = "RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!";
+ error( RTAUDIO_DEVICE_DISCONNECT );
+ }
+
for ( int i=0; i<2; i++ ) {
if ( stream_.userBuffer[i] ) {
free( stream_.userBuffer[i] );
stream_.deviceBuffer = 0;
}
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
+ clearStreamInfo();
+ //stream_.mode = UNINITIALIZED;
+ //stream_.state = STREAM_CLOSED;
}
-void RtApiJack :: startStream( void )
+RtAudioErrorType RtApiJack :: startStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiJack::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
+ if ( stream_.state != STREAM_STOPPED ) {
+ if ( stream_.state == STREAM_RUNNING )
+ errorText_ = "RtApiJack::startStream(): the stream is already running!";
+ else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiJack::startStream(): the stream is stopping or closed!";
+ return error( RTAUDIO_WARNING );
}
+ /*
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+ */
+
JackHandle *handle = (JackHandle *) stream_.apiHandle;
int result = jack_activate( handle->client );
if ( result ) {
stream_.state = STREAM_RUNNING;
unlock:
- if ( result == 0 ) return;
- error( RtAudioError::SYSTEM_ERROR );
+ if ( result == 0 ) return RTAUDIO_NO_ERROR;
+ return error( RTAUDIO_SYSTEM_ERROR );
}
-void RtApiJack :: stopStream( void )
+RtAudioErrorType RtApiJack :: stopStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
+ if ( stream_.state != STREAM_RUNNING && stream_.state != STREAM_STOPPING ) {
+ if ( stream_.state == STREAM_STOPPED )
+ errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
+ else if ( stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiJack::stopStream(): the stream is closed!";
+ return error( RTAUDIO_WARNING );
}
JackHandle *handle = (JackHandle *) stream_.apiHandle;
jack_deactivate( handle->client );
stream_.state = STREAM_STOPPED;
+ return RTAUDIO_NO_ERROR;
}
-void RtApiJack :: abortStream( void )
+RtAudioErrorType RtApiJack :: abortStream( void )
{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
+ if ( stream_.state != STREAM_RUNNING ) {
+ if ( stream_.state == STREAM_STOPPED )
+ errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
+ else if ( stream_.state == STREAM_STOPPING || stream_.state == STREAM_CLOSED )
+ errorText_ = "RtApiJack::abortStream(): the stream is stopping or closed!";
+ return error( RTAUDIO_WARNING );
}
JackHandle *handle = (JackHandle *) stream_.apiHandle;
handle->drainCounter = 2;
- stopStream();
+ return stopStream();
}
// This function will be called by a spawned thread when the user
{
if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
+ errorText_ = "RtApiJack::callbackEvent(): the stream is closed ... this shouldn't happen!";
+ error( RTAUDIO_WARNING );
return FAILURE;
}
if ( stream_.bufferSize != nframes ) {
- errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
- error( RtAudioError::WARNING );
+ errorText_ = "RtApiJack::callbackEvent(): the JACK buffer size has changed ... cannot process!";
+ error( RTAUDIO_WARNING );
return FAILURE;
}
stream_.state = STREAM_STOPPING;
if ( handle->internalDrain == true )
pthread_create( &threadId, NULL, jackStopStream, info );
- else
+ else // external call to stopStream()
pthread_cond_signal( &handle->condition );
return SUCCESS;
}
return;
}
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
ASIOError result = ASIOStart();
if ( result != ASE_OK ) {
relOutIndex += bufferSize_;
}
- // "in" index can end on the "out" index but cannot begin at it
- if ( inIndex_ <= relOutIndex && inIndexEnd > relOutIndex ) {
+ // the "IN" index CAN BEGIN at the "OUT" index
+ // the "IN" index CANNOT END at the "OUT" index
+ if ( inIndex_ < relOutIndex && inIndexEnd >= relOutIndex ) {
return false; // not enough space between "in" index and "out" index
}
relInIndex += bufferSize_;
}
- // "out" index can begin at and end on the "in" index
- if ( outIndex_ < relInIndex && outIndexEnd > relInIndex ) {
+ // the "OUT" index CANNOT BEGIN at the "IN" index
+ // the "OUT" index CAN END at the "IN" index
+ if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) {
return false; // not enough space between "out" index and "in" index
}
return;
}
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
// update stream state
stream_.state = STREAM_RUNNING;
// Wait for the last buffer to play before stopping.
Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
- // stop capture client if applicable
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
- HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::stopStream: Unable to stop capture stream.";
- error( RtAudioError::DRIVER_ERROR );
- return;
- }
- }
-
- // stop render client if applicable
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
- HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::stopStream: Unable to stop render stream.";
- error( RtAudioError::DRIVER_ERROR );
- return;
- }
- }
-
// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
Sleep( 1 );
}
- // stop capture client if applicable
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient ) {
- HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient->Stop();
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::abortStream: Unable to stop capture stream.";
- error( RtAudioError::DRIVER_ERROR );
- return;
- }
- }
-
- // stop render client if applicable
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient ) {
- HRESULT hr = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient->Stop();
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::abortStream: Unable to stop render stream.";
- error( RtAudioError::DRIVER_ERROR );
- return;
- }
- }
-
// close thread handle
if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";
stream_.doConvertBuffer[mode] = false;
if ( stream_.userFormat != stream_.deviceFormat[mode] ||
stream_.nUserChannels[0] != stream_.nDeviceChannels[0] ||
- stream_.nUserChannels[1] != stream_.nDeviceChannels[1] ||
- stream_.userInterleaved )
+ stream_.nUserChannels[1] != stream_.nDeviceChannels[1] )
stream_.doConvertBuffer[mode] = true;
else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
stream_.nUserChannels[mode] > 1 )
HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );
if ( AvrtDll ) {
DWORD taskIndex = 0;
- TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr = ( TAvSetMmThreadCharacteristicsPtr ) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
+ TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr =
+ ( TAvSetMmThreadCharacteristicsPtr ) (void(*)()) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );
FreeLibrary( AvrtDll );
}
}
( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
+
+ // reset the capture stream
+ hr = captureAudioClient->Reset();
+ if ( FAILED( hr ) ) {
+ errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
+ goto Exit;
+ }
+
+ // start the capture stream
+ hr = captureAudioClient->Start();
+ if ( FAILED( hr ) ) {
+ errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
+ goto Exit;
+ }
}
unsigned int inBufferSize = 0;
// set captureBuffer size
captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );
-
- // reset the capture stream
- hr = captureAudioClient->Reset();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
- goto Exit;
- }
-
- // start the capture stream
- hr = captureAudioClient->Start();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
- goto Exit;
- }
}
// start render stream if applicable
( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;
( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;
+
+ // reset the render stream
+ hr = renderAudioClient->Reset();
+ if ( FAILED( hr ) ) {
+ errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
+ goto Exit;
+ }
+
+ // start the render stream
+ hr = renderAudioClient->Start();
+ if ( FAILED( hr ) ) {
+ errorText = "RtApiWasapi::wasapiThread: Unable to start render stream.";
+ goto Exit;
+ }
}
unsigned int outBufferSize = 0;
// set renderBuffer size
renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );
-
- // reset the render stream
- hr = renderAudioClient->Reset();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
- goto Exit;
- }
-
- // start the render stream
- hr = renderAudioClient->Start();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to start render stream.";
- goto Exit;
- }
}
// malloc buffer memory
}
convBuffSize *= 2; // allow overflow for *SrRatio remainders
- convBuffer = ( char* ) malloc( convBuffSize );
- stream_.deviceBuffer = ( char* ) malloc( deviceBuffSize );
+ convBuffer = ( char* ) calloc( convBuffSize, 1 );
+ stream_.deviceBuffer = ( char* ) calloc( deviceBuffSize, 1 );
if ( !convBuffer || !stream_.deviceBuffer ) {
errorType = RtAudioError::MEMORY_ERROR;
errorText = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,
stream_.callbackInfo.userData );
+ // tick stream time
+ RtApi::tickStreamTime();
+
// Handle return value from callback
if ( callbackResult == 1 ) {
// instantiate a thread to stop this thread
stream_.convertInfo[OUTPUT] );
}
+ else {
+ // no further conversion, simple copy userBuffer to deviceBuffer
+ memcpy( stream_.deviceBuffer,
+ stream_.userBuffer[OUTPUT],
+ stream_.bufferSize * stream_.nUserChannels[OUTPUT] * formatBytes( stream_.userFormat ) );
+ }
// Convert callback buffer to stream sample rate
renderResampler->Convert( convBuffer,
// unsetting the callbackPulled flag lets the stream know that
// the audio device is ready for another callback output buffer.
callbackPulled = false;
-
- // tick stream time
- RtApi::tickStreamTime();
}
}
return;
}
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
DsHandle *handle = (DsHandle *) stream_.apiHandle;
// Increase scheduler frequency on lesser windows (a side-effect of
unsigned nDevices = 0;
int result, subdevice, card;
char name[64];
- snd_ctl_t *handle;
+ snd_ctl_t *handle = 0;
// Count cards and devices
card = -1;
sprintf( name, "hw:%d", card );
result = snd_ctl_open( &handle, name, 0 );
if ( result < 0 ) {
+ handle = 0;
errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
error( RtAudioError::WARNING );
nDevices++;
}
nextcard:
- snd_ctl_close( handle );
+ if ( handle )
+ snd_ctl_close( handle );
snd_card_next( &card );
}
unsigned nDevices = 0;
int result, subdevice, card;
char name[64];
- snd_ctl_t *chandle;
+ snd_ctl_t *chandle = 0;
// Count cards and devices
card = -1;
sprintf( name, "hw:%d", card );
result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
if ( result < 0 ) {
+ chandle = 0;
errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
error( RtAudioError::WARNING );
nDevices++;
}
nextcard:
- snd_ctl_close( chandle );
+ if ( chandle )
+ snd_ctl_close( chandle );
snd_card_next( &card );
}
MUTEX_LOCK( &stream_.mutex );
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
int result = 0;
snd_pcm_state_t state;
AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
MUTEX_LOCK( &stream_.mutex );
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
stream_.state = STREAM_RUNNING;
pah->runnable = true;
stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
- if ( pah && pah->s_play ) {
- int pa_error;
- if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
+ if ( pah } {
+ pah->runnable = false;
+ if ( pah->s_play ) {
+ int pa_error;
+ if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ MUTEX_UNLOCK( &stream_.mutex );
+ error( RtAudioError::SYSTEM_ERROR );
+ return;
+ }
}
}
stream_.state = STREAM_STOPPED;
MUTEX_LOCK( &stream_.mutex );
- if ( pah && pah->s_play ) {
- int pa_error;
- if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
+ if ( pah ) {
+ pah->runnable = false;
+ if ( pah->s_play ) {
+ int pa_error;
+ if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
+ errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
+ pa_strerror( pa_error ) << ".";
+ errorText_ = errorStream_.str();
+ MUTEX_UNLOCK( &stream_.mutex );
+ error( RtAudioError::SYSTEM_ERROR );
+ return;
+ }
}
}
MUTEX_LOCK( &stream_.mutex );
+ #if defined( HAVE_GETTIMEOFDAY )
+ gettimeofday( &stream_.lastTickTimestamp, NULL );
+ #endif
+
stream_.state = STREAM_RUNNING;
// No need to do anything else here ... OSS automatically starts
// This method can be modified to control the behavior of error
// message printing.
-void RtApi :: error( RtAudioError::Type type )
+RtAudioErrorType RtApi :: error( RtAudioErrorType type )
{
- errorStream_.str(""); // clear the ostringstream
+ errorStream_.str(""); // clear the ostringstream to avoid repeated messages
- 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;
+ // Don't output warnings if showWarnings_ is false
+ if ( type == RTAUDIO_WARNING && showWarnings_ == false ) return type;
+
+ if ( errorCallback_ ) {
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;
+ errorCallback_( type, errorMessage );
}
-
- if ( type == RtAudioError::WARNING && showWarnings_ == true )
+ else
std::cerr << '\n' << errorText_ << "\n\n";
- else if ( type != RtAudioError::WARNING )
- throw( RtAudioError( errorText_, type ) );
+ return type;
}
+/*
void RtApi :: verifyStream()
{
if ( stream_.state == STREAM_CLOSED ) {
error( RtAudioError::INVALID_USE );
}
}
+*/
void RtApi :: clearStreamInfo()
{
stream_.callbackInfo.callback = 0;
stream_.callbackInfo.userData = 0;
stream_.callbackInfo.isRunning = false;
- stream_.callbackInfo.errorCallback = 0;
+ stream_.callbackInfo.deviceDisconnected = false;
for ( int i=0; i<2; i++ ) {
stream_.device[i] = 11111;
stream_.doConvertBuffer[i] = false;
return 1;
errorText_ = "RtApi::formatBytes: undefined format.";
- error( RtAudioError::WARNING );
+ error( RTAUDIO_WARNING );
return 0;
}