Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to CoreAudio for changing sample rates on a stream #429

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 43 additions & 13 deletions RtAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ class RtApiCore: public RtApi
RtAudioErrorType stopStream( void ) override;
RtAudioErrorType abortStream( void ) override;

void streamSampleRateUpdated( int sampleRate );

// This function is intended for internal use only. It must be
// public because it is called by an internal callback handler,
// which is not a member of RtAudio. External use of this function
Expand Down Expand Up @@ -575,7 +577,7 @@ void RtAudio :: openRtApi( RtAudio::Api api )
#endif
}

RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback&& errorCallback )
RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback&& errorCallback, RtAudioStreamUpdateCallback&& updateCallback )
{
rtapi_ = 0;

Expand All @@ -586,6 +588,7 @@ RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback&& errorCallback )

if ( rtapi_ ) {
if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
if ( updateCallback ) rtapi_->setStreamUpdateCallback (updateCallback );
return;
}

Expand All @@ -610,6 +613,7 @@ RtAudio :: RtAudio( RtAudio::Api api, RtAudioErrorCallback&& errorCallback )

if ( rtapi_ ) {
if ( errorCallback ) rtapi_->setErrorCallback( errorCallback );
if ( updateCallback ) rtapi_->setStreamUpdateCallback (updateCallback );
return;
}

Expand Down Expand Up @@ -1092,19 +1096,36 @@ unsigned int RtApiCore :: getDefaultInputDevice( void )
}

// If a device used in an open stream is disconnected, close the stream.
static OSStatus streamDisconnectListener( AudioObjectID /*id*/,
UInt32 nAddresses,
const AudioObjectPropertyAddress properties[],
void* infoPointer )
static OSStatus streamPropertyChangeListener( AudioObjectID id,
UInt32 nAddresses,
const AudioObjectPropertyAddress properties[],
void* infoPointer )
{
CallbackInfo *info = (CallbackInfo *) infoPointer;
RtApiCore *object = (RtApiCore *) info->object;

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;
}
if ( properties[i].mSelector == kAudioDevicePropertyNominalSampleRate )
{
AudioObjectPropertyAddress property;

property.mSelector = kAudioDevicePropertyNominalSampleRate;
property.mScope = kAudioObjectPropertyScopeGlobal;
property.mElement = kAudioObjectPropertyElementMaster;

Float64 sampleRate;
UInt32 dataSize = sizeof( Float64 );

auto result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &sampleRate );

if ( result == noErr)
object->streamSampleRateUpdated( static_cast<int> (sampleRate) );
}
}

return kAudioHardwareNoError;
Expand Down Expand Up @@ -1921,8 +1942,8 @@ bool RtApiCore :: probeDeviceOpen( unsigned int deviceId, StreamMode mode, unsig
handle->xrunListenerAdded[mode] = true;

// Setup a listener to detect a possible device disconnect.
property.mSelector = kAudioDevicePropertyDeviceIsAlive;
result = AudioObjectAddPropertyListener( id , &property, streamDisconnectListener, (void *) &stream_.callbackInfo );
property.mSelector = kAudioObjectPropertySelectorWildcard;
result = AudioObjectAddPropertyListener( id , &property, streamPropertyChangeListener, (void *) &stream_.callbackInfo );
if ( result != noErr ) {
AudioObjectRemovePropertyListener( id, &property, xrunListener, (void *) handle );
errorStream_ << "RtApiCore::probeDeviceOpen: system error setting disconnect listener for device (" << deviceId << ").";
Expand Down Expand Up @@ -1961,8 +1982,8 @@ void RtApiCore :: closeStream( void )
}
}
if ( handle->disconnectListenerAdded[0] ) {
property.mSelector = kAudioDevicePropertyDeviceIsAlive;
if (AudioObjectRemovePropertyListener( handle->id[0], &property, streamDisconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
property.mSelector = kAudioObjectPropertySelectorWildcard;
if (AudioObjectRemovePropertyListener( handle->id[0], &property, streamPropertyChangeListener, (void *) &stream_.callbackInfo ) != noErr) {
errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
error( RTAUDIO_WARNING );
}
Expand Down Expand Up @@ -1997,8 +2018,8 @@ void RtApiCore :: closeStream( void )
}

if ( handle->disconnectListenerAdded[1] ) {
property.mSelector = kAudioDevicePropertyDeviceIsAlive;
if (AudioObjectRemovePropertyListener( handle->id[1], &property, streamDisconnectListener, (void *) &stream_.callbackInfo ) != noErr) {
property.mSelector = kAudioObjectPropertySelectorWildcard;
if (AudioObjectRemovePropertyListener( handle->id[1], &property, streamPropertyChangeListener, (void *) &stream_.callbackInfo ) != noErr) {
errorText_ = "RtApiCore::closeStream(): error removing disconnect property listener!";
error( RTAUDIO_WARNING );
}
Expand Down Expand Up @@ -2174,6 +2195,15 @@ RtAudioErrorType RtApiCore :: abortStream( void )
return stopStream();
}

void RtApiCore :: streamSampleRateUpdated( int sampleRate )
{
if ( sampleRate != stream_.sampleRate) {
stream_.sampleRate = sampleRate;
if ( streamUpdateCallback_ != nullptr )
streamUpdateCallback_();
}
}

// This function will be called by a spawned thread when the user
// callback function signals that the stream should be stopped or
// aborted. It is better to handle it this way because the
Expand Down
14 changes: 13 additions & 1 deletion RtAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ typedef std::function<void(RtAudioErrorType type,
const std::string &errorText )>
RtAudioErrorCallback;

//! RtAudio streamUpdate callback function prototype.
typedef std::function<void()>
RtAudioStreamUpdateCallback;

// **************************************************************** //
//
// RtAudio class declaration.
Expand Down Expand Up @@ -430,7 +434,9 @@ class RTAUDIO_DLL_PUBLIC RtAudio
An optional errorCallback function can be specified to
subsequently receive warning and error messages.
*/
RtAudio( RtAudio::Api api=UNSPECIFIED, RtAudioErrorCallback&& errorCallback=0 );
RtAudio( RtAudio::Api api=UNSPECIFIED,
RtAudioErrorCallback&& errorCallback=0,
RtAudioStreamUpdateCallback&& updateCallback=0 );

//! The destructor.
/*!
Expand Down Expand Up @@ -632,6 +638,9 @@ class RTAUDIO_DLL_PUBLIC RtAudio
//! Set a client-defined function that will be invoked when an error or warning occurs.
void setErrorCallback( RtAudioErrorCallback errorCallback );

//! Set a client-defined function that will be invoked if the stream properties change
void setStreamUpdateCallback( RtAudioStreamUpdateCallback updateCallback );

//! Specify whether warning messages should be output or not.
/*!
The default behaviour is for warning messages to be output,
Expand Down Expand Up @@ -772,6 +781,7 @@ class RTAUDIO_DLL_PUBLIC RtApi
bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; }
bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; }
void setErrorCallback( RtAudioErrorCallback errorCallback ) { errorCallback_ = errorCallback; }
void setStreamUpdateCallback( RtAudioStreamUpdateCallback updateCallback ) { streamUpdateCallback_ = updateCallback; }
void showWarnings( bool value ) { showWarnings_ = value; }


Expand Down Expand Up @@ -848,6 +858,7 @@ class RTAUDIO_DLL_PUBLIC RtApi
std::ostringstream errorStream_;
std::string errorText_;
RtAudioErrorCallback errorCallback_;
RtAudioStreamUpdateCallback streamUpdateCallback_;
bool showWarnings_;
std::vector<RtAudio::DeviceInfo> deviceList_;
unsigned int currentDeviceId_;
Expand Down Expand Up @@ -925,6 +936,7 @@ inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getS
inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); }
inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); }
inline void RtAudio :: setErrorCallback( RtAudioErrorCallback errorCallback ) { rtapi_->setErrorCallback( errorCallback ); }
inline void RtAudio :: setStreamUpdateCallback( RtAudioStreamUpdateCallback updateCallback ) { rtapi_->setStreamUpdateCallback ( updateCallback ); }
inline void RtAudio :: showWarnings( bool value ) { rtapi_->showWarnings( value ); }

#endif
Expand Down
Loading