Update to RtAudio 4.11 ==> 4.12

This commit is contained in:
vsonnier 2016-06-06 19:14:21 +02:00
parent bb337ad396
commit 26e61f87a7
3 changed files with 241 additions and 148 deletions

View File

@ -10,7 +10,7 @@
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes RtAudio: realtime audio i/o C++ classes
Copyright (c) 2001-2014 Gary P. Scavone Copyright (c) 2001-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@ -38,7 +38,7 @@
*/ */
/************************************************************************/ /************************************************************************/
// RtAudio: Version 4.1.1 // RtAudio: Version 4.1.2
#include "RtAudio.h" #include "RtAudio.h"
#include <iostream> #include <iostream>
@ -59,6 +59,22 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
#define MUTEX_DESTROY(A) DeleteCriticalSection(A) #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
#define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_LOCK(A) EnterCriticalSection(A)
#define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
#include "tchar.h"
static std::string convertCharPointerToStdString(const char *text)
{
return std::string(text);
}
static std::string convertCharPointerToStdString(const wchar_t *text)
{
int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
std::string s( length-1, '\0' );
WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);
return s;
}
#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
// pthread API // pthread API
#define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
@ -762,9 +778,14 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
bool haveValueRange = false; bool haveValueRange = false;
info.sampleRates.clear(); info.sampleRates.clear();
for ( UInt32 i=0; i<nRanges; i++ ) { for ( UInt32 i=0; i<nRanges; i++ ) {
if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {
info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum ); unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;
else { info.sampleRates.push_back( tmpSr );
if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )
info.preferredSampleRate = tmpSr;
} else {
haveValueRange = true; haveValueRange = true;
if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;
if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;
@ -773,8 +794,12 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
if ( haveValueRange ) { if ( haveValueRange ) {
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
} }
@ -1381,6 +1406,18 @@ void RtApiCore :: closeStream( void )
CoreHandle *handle = (CoreHandle *) stream_.apiHandle; CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
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 ( stream_.state == STREAM_RUNNING ) if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[0], callbackHandler ); 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 ) #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
@ -1392,6 +1429,18 @@ void RtApiCore :: closeStream( void )
} }
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
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 ( stream_.state == STREAM_RUNNING ) if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[1], callbackHandler ); 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 ) #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
@ -1985,7 +2034,9 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
// Get the current jack server sample rate. // Get the current jack server sample rate.
info.sampleRates.clear(); info.sampleRates.clear();
info.sampleRates.push_back( jack_get_sample_rate( client ) );
info.preferredSampleRate = jack_get_sample_rate( client );
info.sampleRates.push_back( info.preferredSampleRate );
// Count the available ports containing the client name as device // Count the available ports containing the client name as device
// channels. Jack "input ports" equal RtAudio output channels. // channels. Jack "input ports" equal RtAudio output channels.
@ -2765,8 +2816,12 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] ); result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
if ( result == ASE_OK ) if ( result == ASE_OK ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[i];
}
} }
// Determine supported data types ... just check first channel and assume rest are the same. // Determine supported data types ... just check first channel and assume rest are the same.
@ -2825,9 +2880,12 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
unsigned int firstChannel, unsigned int sampleRate, unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize, RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options ) RtAudio::StreamOptions *options )
{ {////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool isDuplexInput = mode == INPUT && stream_.mode == OUTPUT;
// For ASIO, a duplex stream MUST use the same driver. // For ASIO, a duplex stream MUST use the same driver.
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) { if ( isDuplexInput && stream_.device[0] != device ) {
errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!"; errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
return FAILURE; return FAILURE;
} }
@ -2841,7 +2899,7 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
// Only load the driver once for duplex stream. // Only load the driver once for duplex stream.
if ( mode != INPUT || stream_.mode != OUTPUT ) { if ( !isDuplexInput ) {
// The getDeviceInfo() function will not work when a stream is open // The getDeviceInfo() function will not work when a stream is open
// because ASIO does not allow multiple devices to run at the same // because ASIO does not allow multiple devices to run at the same
// time. Thus, we'll probe the system before opening a stream and // time. Thus, we'll probe the system before opening a stream and
@ -2862,22 +2920,26 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
} }
// keep them before any "goto error", they are used for error cleanup + goto device boundary checks
bool buffersAllocated = false;
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
unsigned int nChannels;
// Check the device channel count. // Check the device channel count.
long inputChannels, outputChannels; long inputChannels, outputChannels;
result = ASIOGetChannels( &inputChannels, &outputChannels ); result = ASIOGetChannels( &inputChannels, &outputChannels );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
stream_.nDeviceChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels;
stream_.nUserChannels[mode] = channels; stream_.nUserChannels[mode] = channels;
@ -2886,30 +2948,27 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
// Verify the sample rate is supported. // Verify the sample rate is supported.
result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Get the current sample rate // Get the current sample rate
ASIOSampleRate currentRate; ASIOSampleRate currentRate;
result = ASIOGetSampleRate( &currentRate ); result = ASIOGetSampleRate( &currentRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Set the sample rate only if necessary // Set the sample rate only if necessary
if ( currentRate != sampleRate ) { if ( currentRate != sampleRate ) {
result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
} }
@ -2920,10 +2979,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
else channelInfo.isInput = true; else channelInfo.isInput = true;
result = ASIOGetChannelInfo( &channelInfo ); result = ASIOGetChannelInfo( &channelInfo );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Assuming WINDOWS host is always little-endian. // Assuming WINDOWS host is always little-endian.
@ -2952,10 +3010,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
if ( stream_.deviceFormat[mode] == 0 ) { if ( stream_.deviceFormat[mode] == 0 ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Set the buffer size. For a duplex stream, this will end up // Set the buffer size. For a duplex stream, this will end up
@ -2964,49 +3021,63 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
long minSize, maxSize, preferSize, granularity; long minSize, maxSize, preferSize, granularity;
result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; if ( isDuplexInput ) {
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; // When this is the duplex input (output was opened before), then we have to use the same
else if ( granularity == -1 ) { // buffersize as the output, because it might use the preferred buffer size, which most
// Make sure bufferSize is a power of two. // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,
int log2_of_min_size = 0; // So instead of throwing an error, make them equal. The caller uses the reference
int log2_of_max_size = 0; // to the "bufferSize" param as usual to set up processing buffers.
for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { *bufferSize = stream_.bufferSize;
if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
}
long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); } else {
int min_delta_num = log2_of_min_size; if ( *bufferSize == 0 ) *bufferSize = preferSize;
else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
if (current_delta < min_delta) {
min_delta = current_delta;
min_delta_num = i;
}
}
*bufferSize = ( (unsigned int)1 << min_delta_num );
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
} else if ( granularity == -1 ) {
else if ( granularity != 0 ) { // Make sure bufferSize is a power of two.
// Set to an even multiple of granularity, rounding up. int log2_of_min_size = 0;
*bufferSize = (*bufferSize + granularity-1) / granularity * granularity; int log2_of_max_size = 0;
for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
}
long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
int min_delta_num = log2_of_min_size;
for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
if (current_delta < min_delta) {
min_delta = current_delta;
min_delta_num = i;
}
}
*bufferSize = ( (unsigned int)1 << min_delta_num );
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
}
else if ( granularity != 0 ) {
// Set to an even multiple of granularity, rounding up.
*bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
}
} }
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) { /*
drivers.removeCurrentDriver(); // we don't use it anymore, see above!
// Just left it here for the case...
if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {
errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
return FAILURE; goto error;
} }
*/
stream_.bufferSize = *bufferSize; stream_.bufferSize = *bufferSize;
stream_.nBuffers = 2; stream_.nBuffers = 2;
@ -3018,16 +3089,13 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
stream_.deviceInterleaved[mode] = false; stream_.deviceInterleaved[mode] = false;
// Allocate, if necessary, our AsioHandle structure for the stream. // Allocate, if necessary, our AsioHandle structure for the stream.
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
if ( handle == 0 ) { if ( handle == 0 ) {
try { try {
handle = new AsioHandle; handle = new AsioHandle;
} }
catch ( std::bad_alloc& ) { catch ( std::bad_alloc& ) {
//if ( handle == NULL ) {
drivers.removeCurrentDriver();
errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
return FAILURE; goto error;
} }
handle->bufferInfos = 0; handle->bufferInfos = 0;
@ -3042,15 +3110,14 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
// Create the ASIO internal buffers. Since RtAudio sets up input // Create the ASIO internal buffers. Since RtAudio sets up input
// and output separately, we'll have to dispose of previously // and output separately, we'll have to dispose of previously
// created output buffers for a duplex stream. // created output buffers for a duplex stream.
long inputLatency, outputLatency;
if ( mode == INPUT && stream_.mode == OUTPUT ) { if ( mode == INPUT && stream_.mode == OUTPUT ) {
ASIODisposeBuffers(); ASIODisposeBuffers();
if ( handle->bufferInfos ) free( handle->bufferInfos ); if ( handle->bufferInfos ) free( handle->bufferInfos );
} }
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
bool buffersAllocated = false; unsigned int i;
unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
if ( handle->bufferInfos == NULL ) { if ( handle->bufferInfos == NULL ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
@ -3071,18 +3138,37 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
infos->buffers[0] = infos->buffers[1] = 0; infos->buffers[0] = infos->buffers[1] = 0;
} }
// prepare for callbacks
stream_.sampleRate = sampleRate;
stream_.device[mode] = device;
stream_.mode = isDuplexInput ? DUPLEX : mode;
// store this class instance before registering callbacks, that are going to use it
asioCallbackInfo = &stream_.callbackInfo;
stream_.callbackInfo.object = (void *) this;
// Set up the ASIO callback structure and create the ASIO data buffers. // Set up the ASIO callback structure and create the ASIO data buffers.
asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages; asioCallbacks.asioMessage = &asioMessages;
asioCallbacks.bufferSwitchTimeInfo = NULL; asioCallbacks.bufferSwitchTimeInfo = NULL;
result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
if ( result != ASE_OK ) {
// Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges
// but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver
// in that case, let's be naïve and try that instead
*bufferSize = preferSize;
stream_.bufferSize = *bufferSize;
result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
}
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
goto error; goto error;
} }
buffersAllocated = true; buffersAllocated = true;
stream_.state = STREAM_STOPPED;
// Set flags for buffer conversion. // Set flags for buffer conversion.
stream_.doConvertBuffer[mode] = false; stream_.doConvertBuffer[mode] = false;
@ -3105,11 +3191,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
bool makeBuffer = true; bool makeBuffer = true;
bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
if ( mode == INPUT ) { if ( isDuplexInput && stream_.deviceBuffer ) {
if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false;
if ( bufferBytes <= bytesOut ) makeBuffer = false;
}
} }
if ( makeBuffer ) { if ( makeBuffer ) {
@ -3123,18 +3207,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
} }
stream_.sampleRate = sampleRate;
stream_.device[mode] = device;
stream_.state = STREAM_STOPPED;
asioCallbackInfo = &stream_.callbackInfo;
stream_.callbackInfo.object = (void *) this;
if ( stream_.mode == OUTPUT && mode == INPUT )
// We had already set up an output stream.
stream_.mode = DUPLEX;
else
stream_.mode = mode;
// Determine device latencies // Determine device latencies
long inputLatency, outputLatency;
result = ASIOGetLatencies( &inputLatency, &outputLatency ); result = ASIOGetLatencies( &inputLatency, &outputLatency );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
@ -3154,32 +3228,38 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
return SUCCESS; return SUCCESS;
error: error:
if ( buffersAllocated ) if ( !isDuplexInput ) {
ASIODisposeBuffers(); // the cleanup for error in the duplex input, is done by RtApi::openStream
drivers.removeCurrentDriver(); // So we clean up for single channel only
if ( handle ) { if ( buffersAllocated )
CloseHandle( handle->condition ); ASIODisposeBuffers();
if ( handle->bufferInfos )
free( handle->bufferInfos );
delete handle;
stream_.apiHandle = 0;
}
for ( int i=0; i<2; i++ ) { drivers.removeCurrentDriver();
if ( stream_.userBuffer[i] ) {
free( stream_.userBuffer[i] ); if ( handle ) {
stream_.userBuffer[i] = 0; CloseHandle( handle->condition );
if ( handle->bufferInfos )
free( handle->bufferInfos );
delete handle;
stream_.apiHandle = 0;
}
if ( stream_.userBuffer[mode] ) {
free( stream_.userBuffer[mode] );
stream_.userBuffer[mode] = 0;
}
if ( stream_.deviceBuffer ) {
free( stream_.deviceBuffer );
stream_.deviceBuffer = 0;
} }
} }
if ( stream_.deviceBuffer ) {
free( stream_.deviceBuffer );
stream_.deviceBuffer = 0;
}
return FAILURE; return FAILURE;
} }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void RtApiAsio :: closeStream() void RtApiAsio :: closeStream()
{ {
@ -3631,12 +3711,12 @@ public:
outIndex_( 0 ) {} outIndex_( 0 ) {}
~WasapiBuffer() { ~WasapiBuffer() {
delete buffer_; free( buffer_ );
} }
// sets the length of the internal ring buffer // sets the length of the internal ring buffer
void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {
delete buffer_; free( buffer_ );
buffer_ = ( char* ) calloc( bufferSize, formatBytes ); buffer_ = ( char* ) calloc( bufferSize, formatBytes );
@ -3795,7 +3875,7 @@ void convertBufferWasapi( char* outBuffer,
float sampleStep = 1.0f / sampleRatio; float sampleStep = 1.0f / sampleRatio;
float inSampleFraction = 0.0f; float inSampleFraction = 0.0f;
outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio ); outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio );
// frame-by-frame, copy each relative input sample into it's corresponding output sample // frame-by-frame, copy each relative input sample into it's corresponding output sample
for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
@ -3941,7 +4021,6 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
unsigned int captureDeviceCount = 0; unsigned int captureDeviceCount = 0;
unsigned int renderDeviceCount = 0; unsigned int renderDeviceCount = 0;
std::wstring deviceName;
std::string defaultDeviceName; std::string defaultDeviceName;
bool isCaptureDevice = false; bool isCaptureDevice = false;
@ -4044,8 +4123,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit; goto Exit;
} }
deviceName = defaultDeviceNameProp.pwszVal; defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);
defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );
// name // name
hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore ); hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
@ -4062,8 +4140,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit; goto Exit;
} }
deviceName = deviceNameProp.pwszVal; info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);
info.name = std::string( deviceName.begin(), deviceName.end() );
// is default // is default
if ( isCaptureDevice ) { if ( isCaptureDevice ) {
@ -4106,6 +4183,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
} }
info.preferredSampleRate = deviceFormat->nSamplesPerSec;
// native format // native format
info.nativeFormats = 0; info.nativeFormats = 0;
@ -5080,10 +5158,10 @@ void RtApiWasapi::wasapiThread()
// if the callback buffer was pushed renderBuffer reset callbackPulled flag // if the callback buffer was pushed renderBuffer reset callbackPulled flag
if ( callbackPushed ) { if ( callbackPushed ) {
callbackPulled = false; callbackPulled = false;
// tick stream time
RtApi::tickStreamTime();
} }
// tick stream time
RtApi::tickStreamTime();
} }
Exit: Exit:
@ -5241,14 +5319,11 @@ unsigned int RtApiDs :: getDeviceCount( void )
error( RtAudioError::WARNING ); error( RtAudioError::WARNING );
} }
// Clean out any devices that may have disappeared. // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).
std::vector< int > indices; for ( unsigned int i=0; i<dsDevices.size(); ) {
for ( unsigned int i=0; i<dsDevices.size(); i++ ) if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );
if ( dsDevices[i].found == false ) indices.push_back( i ); else i++;
//unsigned int nErased = 0; }
for ( unsigned int i=0; i<indices.size(); i++ )
dsDevices.erase( dsDevices.begin()+indices[i] );
//dsDevices.erase( dsDevices.begin()-nErased++ );
return static_cast<unsigned int>(dsDevices.size()); return static_cast<unsigned int>(dsDevices.size());
} }
@ -5304,8 +5379,12 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate && if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
// Get format information. // Get format information.
@ -6260,6 +6339,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6267,6 +6347,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6275,6 +6356,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6282,6 +6364,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6303,6 +6386,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6354,6 +6438,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6395,6 +6480,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6408,6 +6494,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6444,6 +6531,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6505,6 +6593,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6519,6 +6608,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6540,6 +6630,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@ -6578,21 +6669,6 @@ static unsigned __stdcall callbackHandler( void *ptr )
return 0; return 0;
} }
#include "tchar.h"
static std::string convertTChar( LPCTSTR name )
{
#if defined( UNICODE ) || defined( _UNICODE )
int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
std::string s( length-1, '\0' );
WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);
#else
std::string s( name );
#endif
return s;
}
static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
LPCTSTR description, LPCTSTR description,
LPCTSTR /*module*/, LPCTSTR /*module*/,
@ -6634,7 +6710,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
} }
// If good device, then save its name and guid. // If good device, then save its name and guid.
std::string name = convertTChar( description ); std::string name = convertCharPointerToStdString( description );
//if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
if ( lpguid == NULL ) if ( lpguid == NULL )
name = "Default Device"; name = "Default Device";
@ -6816,6 +6892,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Count cards and devices // Count cards and devices
card = -1; card = -1;
subdevice = -1;
snd_card_next( &card ); snd_card_next( &card );
while ( card >= 0 ) { while ( card >= 0 ) {
sprintf( name, "hw:%d", card ); sprintf( name, "hw:%d", card );
@ -7029,8 +7106,12 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Test our discrete set of sample rate values. // Test our discrete set of sample rate values.
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[i];
}
} }
if ( info.sampleRates.size() == 0 ) { if ( info.sampleRates.size() == 0 ) {
snd_pcm_close( phandle ); snd_pcm_close( phandle );
@ -7955,6 +8036,8 @@ void RtApiAlsa :: callbackEvent()
errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << "."; errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
} }
else
errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun.";
} }
else { else {
errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
@ -7988,7 +8071,7 @@ static void *alsaCallbackHandler( void *ptr )
bool *isRunning = &info->isRunning; bool *isRunning = &info->isRunning;
#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
if ( &info->doRealtime ) { if ( info->doRealtime ) {
pthread_t tID = pthread_self(); // ID of this thread pthread_t tID = pthread_self(); // ID of this thread
sched_param prio = { info->priority }; // scheduling priority of thread sched_param prio = { info->priority }; // scheduling priority of thread
pthread_setschedparam( tID, SCHED_RR, &prio ); pthread_setschedparam( tID, SCHED_RR, &prio );
@ -8063,6 +8146,7 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
info.sampleRates.push_back( *sr ); info.sampleRates.push_back( *sr );
info.preferredSampleRate = 48000;
info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
return info; return info;
@ -8425,7 +8509,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
pah = static_cast<PulseAudioHandle *>( stream_.apiHandle ); pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
int error; int error;
if ( !options->streamName.empty() ) streamName = options->streamName; if ( options && !options->streamName.empty() ) streamName = options->streamName;
switch ( mode ) { switch ( mode ) {
case INPUT: case INPUT:
pa_buffer_attr buffer_attr; pa_buffer_attr buffer_attr;
@ -8439,7 +8523,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
} }
break; break;
case OUTPUT: case OUTPUT:
pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error ); pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
if ( !pah->s_play ) { if ( !pah->s_play ) {
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server."; errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
goto error; goto error;
@ -8631,6 +8715,10 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( ainfo.rates[i] == SAMPLE_RATES[k] ) { if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
break; break;
} }
} }
@ -8639,8 +8727,12 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
else { else {
// Check min and max rate values; // Check min and max rate values;
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
} }
@ -10052,7 +10144,7 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
{ {
char val; char val;
char *ptr; char *ptr;
ptr = buffer; ptr = buffer;
if ( format == RTAUDIO_SINT16 ) { if ( format == RTAUDIO_SINT16 ) {

View File

@ -10,7 +10,7 @@
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes RtAudio: realtime audio i/o C++ classes
Copyright (c) 2001-2014 Gary P. Scavone Copyright (c) 2001-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@ -45,7 +45,7 @@
#ifndef __RTAUDIO_H #ifndef __RTAUDIO_H
#define __RTAUDIO_H #define __RTAUDIO_H
#define RTAUDIO_VERSION "4.1.1" #define RTAUDIO_VERSION "4.1.2"
#include <string> #include <string>
#include <vector> #include <vector>
@ -286,12 +286,13 @@ class RtAudio
bool isDefaultOutput; /*!< true if this is the default output device. */ bool isDefaultOutput; /*!< true if this is the default output device. */
bool isDefaultInput; /*!< true if this is the default input device. */ bool isDefaultInput; /*!< true if this is the default input device. */
std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */
RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
// Default constructor. // Default constructor.
DeviceInfo() DeviceInfo()
:probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
isDefaultOutput(false), isDefaultInput(false), nativeFormats(0) {} isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {}
}; };
//! The structure for specifying input or ouput stream parameters. //! The structure for specifying input or ouput stream parameters.

View File

@ -1,6 +1,6 @@
RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO and WASAPI) operating systems. RtAudio - a set of C++ classes that provide a common API for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X (CoreAudio and JACK), and Windows (DirectSound, ASIO and WASAPI) operating systems.
By Gary P. Scavone, 2001-2014. By Gary P. Scavone, 2001-2016.
This distribution of RtAudio contains the following: This distribution of RtAudio contains the following:
@ -11,7 +11,7 @@ tests/Windows: Visual C++ .net test program workspace and projects
OVERVIEW: OVERVIEW:
RtAudio is a set of C++ classes that provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, and OSS), Macintosh OS X, SGI, and Windows (DirectSound, ASIO and WASAPI) operating systems. RtAudio significantly simplifies the process of interacting with computer audio hardware. It was designed with the following objectives: RtAudio is a set of C++ classes that provides a common API (Application Programming Interface) for realtime audio input/output across Linux (native ALSA, JACK, PulseAudio and OSS), Macintosh OS X and Windows (DirectSound, ASIO and WASAPI) operating systems. RtAudio significantly simplifies the process of interacting with computer audio hardware. It was designed with the following objectives:
- object-oriented C++ design - object-oriented C++ design
- simple, common API across all supported platforms - simple, common API across all supported platforms
@ -34,7 +34,7 @@ LEGAL AND ETHICAL:
The RtAudio license is similar to the MIT License. The RtAudio license is similar to the MIT License.
RtAudio: a set of realtime audio i/o C++ classes RtAudio: a set of realtime audio i/o C++ classes
Copyright (c) 2001-2014 Gary P. Scavone Copyright (c) 2001-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files