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 5.1.0
#include "RtAudio.h"
#include <iostream>
errorText_ = "RtApiCore::closeStream(): error removing property listener!";
error( RtAudioError::WARNING );
}
- }
- 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] );
-#else
- // deprecated in favor of AudioDeviceDestroyIOProcID()
- AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
+ 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] ) ) {
errorText_ = "RtApiCore::closeStream(): error removing property listener!";
error( RtAudioError::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] );
-#else
- // deprecated in favor of AudioDeviceDestroyIOProcID()
- AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
+ 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++ ) {
return;
}
- #if defined( HAVE_GETTIMEOFDAY )
+#if defined( HAVE_GETTIMEOFDAY )
gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
+#endif
OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
+#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] ) ) {
+#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();
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 // deprecated behaviour
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[0], handle->procId[1] );
+#else // deprecated behaviour
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();
unlock:
//MUTEX_UNLOCK( &stream_.mutex );
- RtApi::tickStreamTime();
+ // 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] ) )
+ RtApi::tickStreamTime();
+
return SUCCESS;
}
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
+ // 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
}
#endif
}
- void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount )
+ void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount, int maxOutSampleCount = -1 )
{
unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount;
if ( _sampleRatio == 1 )
return;
}
- unsigned int outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount );
+ unsigned int outputBufferSize = 0;
+ if ( maxOutSampleCount != -1 )
+ {
+ outputBufferSize = _bytesPerSample * _channelCount * maxOutSampleCount;
+ }
+ else
+ {
+ outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount );
+ }
IMFMediaBuffer* rInBuffer;
IMFSample* rInSample;
stream_.doConvertBuffer[mode] = true;
if ( stream_.doConvertBuffer[mode] )
- setConvertInfo( mode, 0 );
+ setConvertInfo( mode, firstChannel );
// Allocate necessary internal buffers
bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );
// declare local stream variables
RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
BYTE* streamBuffer = NULL;
- unsigned long captureFlags = 0;
+ DWORD captureFlags = 0;
unsigned int bufferFrameCount = 0;
unsigned int numFramesPadding = 0;
unsigned int convBufferSize = 0;
if ( captureAudioClient )
{
int samplesToPull = ( unsigned int ) floorf( stream_.bufferSize * captureSrRatio );
- if ( captureSrRatio != 1 )
- {
- // account for remainders
- samplesToPull--;
- }
convBufferSize = 0;
while ( convBufferSize < stream_.bufferSize )
captureResampler->Convert( stream_.deviceBuffer + deviceBufferOffset,
convBuffer,
samplesToPull,
- convSamples );
+ convSamples,
+ convBufferSize == 0 ? -1 : stream_.bufferSize - convBufferSize );
convBufferSize += convSamples;
samplesToPull = 1; // now pull one sample at a time until we have stream_.bufferSize samples
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
// 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();
}
}
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;
+ }
}
}
stream_.doConvertBuffer[mode] = true;
if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
stream_.doConvertBuffer[mode] = true;
+ if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] )
+ stream_.doConvertBuffer[mode] = true;
// Allocate necessary internal buffers.
bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );