summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Scavone <gary@music.mcgill.ca>2019-08-22 18:24:11 -0400
committerGary Scavone <gary@music.mcgill.ca>2019-08-22 18:24:11 -0400
commit10977977730415518802eb37c570af4ac18138de (patch)
treec721f72fd79b0d5043977f9c9ef6dbcfe2dac350
parente4b398ae9565d7680f5a2e8250a7d2d3fc9d4660 (diff)
Modified CoreAudio cleanup in case of error during initialization, fixed duplex streamtime bug for single device in CoreAudio, updated playsaw.cpp with interrupt handler instead of cin.get() function.
-rw-r--r--RtAudio.cpp135
-rw-r--r--tests/duplex.cpp31
-rw-r--r--tests/playsaw.cpp26
3 files changed, 100 insertions, 92 deletions
diff --git a/RtAudio.cpp b/RtAudio.cpp
index d0827fd..d01c4e2 100644
--- a/RtAudio.cpp
+++ b/RtAudio.cpp
@@ -393,7 +393,6 @@ RtAudioErrorType RtApi :: openStream( RtAudio::StreamParameters *oParams,
result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
sampleRate, format, bufferFrames, options );
if ( result == false ) {
- if ( oChannels > 0 ) closeStream();
return error( RTAUDIO_SYSTEM_ERROR );
}
}
@@ -537,9 +536,11 @@ struct CoreHandle {
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()
@@ -1406,7 +1407,7 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
}
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 )
@@ -1424,51 +1425,34 @@ bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
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 );
+ // 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;
- }
+ errorStream_ << "RtApiCore::probeDeviceOpen: system error setting xrun listener for device (" << device << ").";
+ errorText_ = errorStream_.str();
+ goto error;
+ }
+ handle->xrunListenerAdded[mode] = true;
- // 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;
+ // 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;
}
return SUCCESS;
error:
- if ( handle ) {
- pthread_cond_destroy( &handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- clearStreamInfo();
- //stream_.state = STREAM_CLOSED;
+ closeStream(); // this should safely clear out procedures, listeners and memory, even for duplex stream
return FAILURE;
}
@@ -1482,53 +1466,67 @@ void RtApiCore :: closeStream( void )
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if (handle) {
+ if ( handle ) {
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
- 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 ( 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 );
+ }
}
- 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 ( 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 ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[0], callbackHandler );
+
+ if ( stream_.state == STREAM_RUNNING )
+ AudioDeviceStop( handle->id[0], callbackHandler );
+
#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] );
+ if ( handle->procId[0] )
+ AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
#else
- // deprecated in favor of AudioDeviceDestroyIOProcID()
- AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+ // deprecated in favor of AudioDeviceDestroyIOProcID()
+ 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;
- if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
- errorText_ = "RtApiCore::closeStream(): error removing xrun property listener!";
- error( RTAUDIO_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 );
+ }
}
- 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 ( 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 ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[1], callbackHandler );
#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] );
+ if ( handle->procId[1] )
+ AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
#else
// deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
@@ -1947,7 +1945,12 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
unlock:
// Make sure to only tick duplex stream time once if using two devices
- if ( stream_.mode != DUPLEX || (stream_.mode == DUPLEX && handle->id[0] != handle->id[1] && deviceId == handle->id[0] ) )
+ 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(); // only tick on the output callback
+ } else
RtApi::tickStreamTime();
return SUCCESS;
diff --git a/tests/duplex.cpp b/tests/duplex.cpp
index c5abc3b..33ab282 100644
--- a/tests/duplex.cpp
+++ b/tests/duplex.cpp
@@ -1,7 +1,7 @@
/******************************************/
/*
duplex.cpp
- by Gary P. Scavone, 2006-2007.
+ by Gary P. Scavone, 2006-2019.
This program opens a duplex stream and passes
input directly through to the output.
@@ -109,36 +109,29 @@ int main( int argc, char *argv[] )
iParams.deviceId = adac.getDefaultInputDevice();
if ( oDevice == 0 )
oParams.deviceId = adac.getDefaultOutputDevice();
-
+
RtAudio::StreamOptions options;
//options.flags |= RTAUDIO_NONINTERLEAVED;
bufferBytes = bufferFrames * channels * sizeof( MY_TYPE );
- try {
- adac.openStream( &oParams, &iParams, FORMAT, fs, &bufferFrames, &inout, (void *)&bufferBytes, &options );
- }
- catch ( RtAudioError& e ) {
- std::cout << '\n' << e.getMessage() << '\n' << std::endl;
- exit( 1 );
+ if ( adac.openStream( &oParams, &iParams, FORMAT, fs, &bufferFrames, &inout, (void *)&bufferBytes, &options ) ) {
+ goto cleanup;
}
+ if ( adac.isStreamOpen() == false ) goto cleanup;
+
// Test RtAudio functionality for reporting latency.
std::cout << "\nStream latency = " << adac.getStreamLatency() << " frames" << std::endl;
- try {
- adac.startStream();
+ if ( adac.startStream() ) goto cleanup;
- char input;
- std::cout << "\nRunning ... press <enter> to quit (buffer frames = " << bufferFrames << ").\n";
- std::cin.get(input);
+ char input;
+ std::cout << "\nRunning ... press <enter> to quit (buffer frames = " << bufferFrames << ").\n";
+ std::cin.get(input);
- // Stop the stream.
+ // Stop the stream.
+ if ( adac.isStreamRunning() )
adac.stopStream();
- }
- catch ( RtAudioError& e ) {
- std::cout << '\n' << e.getMessage() << '\n' << std::endl;
- goto cleanup;
- }
cleanup:
if ( adac.isStreamOpen() ) adac.closeStream();
diff --git a/tests/playsaw.cpp b/tests/playsaw.cpp
index f678d1e..a9c7891 100644
--- a/tests/playsaw.cpp
+++ b/tests/playsaw.cpp
@@ -1,7 +1,7 @@
/******************************************/
/*
playsaw.cpp
- by Gary P. Scavone, 2006
+ by Gary P. Scavone, 2006-2019.
This program will output sawtooth waveforms
of different frequencies on each channel.
@@ -11,6 +11,7 @@
#include "RtAudio.h"
#include <iostream>
#include <cstdlib>
+#include <signal.h>
/*
typedef char MY_TYPE;
@@ -49,6 +50,10 @@ typedef double MY_TYPE;
#define SLEEP( milliseconds ) usleep( (unsigned long) (milliseconds * 1000.0) )
#endif
+// Interrupt handler function
+bool done;
+static void finish( int /*ignore*/ ){ done = true; }
+
#define BASE_RATE 0.005
#define TIME 1.0
@@ -173,8 +178,9 @@ int main( int argc, char *argv[] )
double *data = (double *) calloc( channels, sizeof( double ) );
- // Tell RtAudio to output all messages, even warnings.
//dac.setErrorCallback( &errorCallback ); // could use if not set via constructor
+
+ // Tell RtAudio to output all messages, even warnings.
dac.showWarnings( true );
// Set our stream parameters for output only.
@@ -200,6 +206,8 @@ int main( int argc, char *argv[] )
goto cleanup;
if ( dac.isStreamOpen() == false ) goto cleanup;
+ //std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl;
+
// Stream is open ... now start it.
if ( dac.startStream() ) goto cleanup;
@@ -207,13 +215,17 @@ int main( int argc, char *argv[] )
while ( dac.isStreamRunning() == true ) SLEEP( 100 );
}
else {
- char input;
- //std::cout << "Stream latency = " << dac.getStreamLatency() << "\n" << std::endl;
- std::cout << "\nPlaying ... press <enter> to quit (buffer size = " << bufferFrames << ").\n";
- std::cin.get( input );
+ std::cout << "\nPlaying ... quit with Ctrl-C (buffer size = " << bufferFrames << ").\n";
+
+ // Install an interrupt handler function.
+ done = false;
+ (void) signal(SIGINT, finish);
+
+ while ( !done && dac.isStreamRunning() ) SLEEP( 100 );
// Block released ... stop the stream
- dac.stopStream(); // or could call dac.abortStream();
+ if ( dac.isStreamRunning() )
+ dac.stopStream(); // or could call dac.abortStream();
}
cleanup: