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: 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
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 <iostream>
@ -59,6 +59,22 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
#define MUTEX_DESTROY(A) DeleteCriticalSection(A)
#define MUTEX_LOCK(A) EnterCriticalSection(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__)
// pthread API
#define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
@ -762,9 +778,14 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
bool haveValueRange = false;
info.sampleRates.clear();
for ( UInt32 i=0; i<nRanges; i++ ) {
if ( rangeList[i].mMinimum == rangeList[i].mMaximum )
info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum );
else {
if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {
unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;
info.sampleRates.push_back( tmpSr );
if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )
info.preferredSampleRate = tmpSr;
} else {
haveValueRange = true;
if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;
if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;
@ -773,8 +794,12 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
if ( haveValueRange ) {
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] );
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;
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 )
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 )
@ -1392,6 +1429,18 @@ void RtApiCore :: closeStream( void )
}
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 )
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 )
@ -1985,7 +2034,9 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
// Get the current jack server sample rate.
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
// channels. Jack "input ports" equal RtAudio output channels.
@ -2765,8 +2816,12 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
info.sampleRates.clear();
for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
if ( result == ASE_OK )
if ( result == ASE_OK ) {
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.
@ -2825,9 +2880,12 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options )
{
{////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool isDuplexInput = mode == INPUT && stream_.mode == OUTPUT;
// 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!";
return FAILURE;
}
@ -2841,7 +2899,7 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
}
// 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
// because ASIO does not allow multiple devices to run at the same
// 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.
long inputChannels, outputChannels;
result = ASIOGetChannels( &inputChannels, &outputChannels );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
stream_.nDeviceChannels[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.
result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
// Get the current sample rate
ASIOSampleRate currentRate;
result = ASIOGetSampleRate( &currentRate );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
// Set the sample rate only if necessary
if ( currentRate != sampleRate ) {
result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
}
@ -2920,10 +2979,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
else channelInfo.isInput = true;
result = ASIOGetChannelInfo( &channelInfo );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
// 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 ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
// 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;
result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
errorText_ = errorStream_.str();
return FAILURE;
goto error;
}
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
else if ( granularity == -1 ) {
// Make sure bufferSize is a power of two.
int log2_of_min_size = 0;
int log2_of_max_size = 0;
if ( isDuplexInput ) {
// When this is the duplex input (output was opened before), then we have to use the same
// buffersize as the output, because it might use the preferred buffer size, which most
// likely wasn't passed as input to this. The buffer sizes have to be identically anyway,
// So instead of throwing an error, make them equal. The caller uses the reference
// to the "bufferSize" param as usual to set up processing buffers.
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;
}
*bufferSize = stream_.bufferSize;
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 == 0 ) *bufferSize = preferSize;
else 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;
else if ( granularity == -1 ) {
// Make sure bufferSize is a power of two.
int log2_of_min_size = 0;
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!";
return FAILURE;
goto error;
}
*/
stream_.bufferSize = *bufferSize;
stream_.nBuffers = 2;
@ -3018,16 +3089,13 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
stream_.deviceInterleaved[mode] = false;
// Allocate, if necessary, our AsioHandle structure for the stream.
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
if ( handle == 0 ) {
try {
handle = new AsioHandle;
}
catch ( std::bad_alloc& ) {
//if ( handle == NULL ) {
drivers.removeCurrentDriver();
errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
return FAILURE;
goto error;
}
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
// and output separately, we'll have to dispose of previously
// created output buffers for a duplex stream.
long inputLatency, outputLatency;
if ( mode == INPUT && stream_.mode == OUTPUT ) {
ASIODisposeBuffers();
if ( handle->bufferInfos ) free( handle->bufferInfos );
}
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
bool buffersAllocated = false;
unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
unsigned int i;
nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
if ( handle->bufferInfos == NULL ) {
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;
}
// 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.
asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages;
asioCallbacks.bufferSwitchTimeInfo = NULL;
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 ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
errorText_ = errorStream_.str();
goto error;
}
buffersAllocated = true;
buffersAllocated = true;
stream_.state = STREAM_STOPPED;
// Set flags for buffer conversion.
stream_.doConvertBuffer[mode] = false;
@ -3105,11 +3191,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
bool makeBuffer = true;
bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
if ( mode == INPUT ) {
if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
if ( bufferBytes <= bytesOut ) makeBuffer = false;
}
if ( isDuplexInput && stream_.deviceBuffer ) {
unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
if ( bufferBytes <= bytesOut ) makeBuffer = false;
}
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
long inputLatency, outputLatency;
result = ASIOGetLatencies( &inputLatency, &outputLatency );
if ( result != ASE_OK ) {
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;
error:
if ( buffersAllocated )
ASIODisposeBuffers();
drivers.removeCurrentDriver();
if ( !isDuplexInput ) {
// the cleanup for error in the duplex input, is done by RtApi::openStream
// So we clean up for single channel only
if ( handle ) {
CloseHandle( handle->condition );
if ( handle->bufferInfos )
free( handle->bufferInfos );
delete handle;
stream_.apiHandle = 0;
}
if ( buffersAllocated )
ASIODisposeBuffers();
for ( int i=0; i<2; i++ ) {
if ( stream_.userBuffer[i] ) {
free( stream_.userBuffer[i] );
stream_.userBuffer[i] = 0;
drivers.removeCurrentDriver();
if ( handle ) {
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;
}
}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void RtApiAsio :: closeStream()
{
@ -3631,12 +3711,12 @@ public:
outIndex_( 0 ) {}
~WasapiBuffer() {
delete buffer_;
free( buffer_ );
}
// sets the length of the internal ring buffer
void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {
delete buffer_;
free( buffer_ );
buffer_ = ( char* ) calloc( bufferSize, formatBytes );
@ -3795,7 +3875,7 @@ void convertBufferWasapi( char* outBuffer,
float sampleStep = 1.0f / sampleRatio;
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
for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
@ -3941,7 +4021,6 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
RtAudio::DeviceInfo info;
unsigned int captureDeviceCount = 0;
unsigned int renderDeviceCount = 0;
std::wstring deviceName;
std::string defaultDeviceName;
bool isCaptureDevice = false;
@ -4044,8 +4123,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit;
}
deviceName = defaultDeviceNameProp.pwszVal;
defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );
defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);
// name
hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
@ -4062,8 +4140,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit;
}
deviceName = deviceNameProp.pwszVal;
info.name = std::string( deviceName.begin(), deviceName.end() );
info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);
// is default
if ( isCaptureDevice ) {
@ -4106,6 +4183,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {
info.sampleRates.push_back( SAMPLE_RATES[i] );
}
info.preferredSampleRate = deviceFormat->nSamplesPerSec;
// native format
info.nativeFormats = 0;
@ -5080,10 +5158,10 @@ void RtApiWasapi::wasapiThread()
// if the callback buffer was pushed renderBuffer reset callbackPulled flag
if ( callbackPushed ) {
callbackPulled = false;
// tick stream time
RtApi::tickStreamTime();
}
// tick stream time
RtApi::tickStreamTime();
}
Exit:
@ -5241,14 +5319,11 @@ unsigned int RtApiDs :: getDeviceCount( void )
error( RtAudioError::WARNING );
}
// Clean out any devices that may have disappeared.
std::vector< int > indices;
for ( unsigned int i=0; i<dsDevices.size(); i++ )
if ( dsDevices[i].found == false ) indices.push_back( 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++ );
// Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).
for ( unsigned int i=0; i<dsDevices.size(); ) {
if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );
else i++;
}
return static_cast<unsigned int>(dsDevices.size());
}
@ -5304,8 +5379,12 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
info.sampleRates.clear();
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
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] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
}
// Get format information.
@ -6260,6 +6339,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6267,6 +6347,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6275,6 +6356,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6282,6 +6364,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6303,6 +6386,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6354,6 +6438,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6395,6 +6480,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6408,6 +6494,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6444,6 +6531,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6505,6 +6593,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6519,6 +6608,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6540,6 +6630,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR );
return;
}
@ -6578,21 +6669,6 @@ static unsigned __stdcall callbackHandler( void *ptr )
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,
LPCTSTR description,
LPCTSTR /*module*/,
@ -6634,7 +6710,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
}
// 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 ( lpguid == NULL )
name = "Default Device";
@ -6816,6 +6892,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Count cards and devices
card = -1;
subdevice = -1;
snd_card_next( &card );
while ( card >= 0 ) {
sprintf( name, "hw:%d", card );
@ -7029,8 +7106,12 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Test our discrete set of sample rate values.
info.sampleRates.clear();
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] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[i];
}
}
if ( info.sampleRates.size() == 0 ) {
snd_pcm_close( phandle );
@ -7955,6 +8036,8 @@ void RtApiAlsa :: callbackEvent()
errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str();
}
else
errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun.";
}
else {
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;
#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
sched_param prio = { info->priority }; // scheduling priority of thread
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 )
info.sampleRates.push_back( *sr );
info.preferredSampleRate = 48000;
info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
return info;
@ -8425,7 +8509,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
int error;
if ( !options->streamName.empty() ) streamName = options->streamName;
if ( options && !options->streamName.empty() ) streamName = options->streamName;
switch ( mode ) {
case INPUT:
pa_buffer_attr buffer_attr;
@ -8439,7 +8523,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
}
break;
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 ) {
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
goto error;
@ -8631,6 +8715,10 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( ainfo.rates[i] == 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;
}
}
@ -8639,8 +8727,12 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
else {
// Check min and max rate values;
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] );
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 )
{
char val;
char *ptr;
char *ptr;
ptr = buffer;
if ( format == RTAUDIO_SINT16 ) {

View File

@ -10,7 +10,7 @@
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
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
obtaining a copy of this software and associated documentation files
@ -45,7 +45,7 @@
#ifndef __RTAUDIO_H
#define __RTAUDIO_H
#define RTAUDIO_VERSION "4.1.1"
#define RTAUDIO_VERSION "4.1.2"
#include <string>
#include <vector>
@ -286,12 +286,13 @@ class RtAudio
bool isDefaultOutput; /*!< true if this is the default output 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). */
unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */
RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
// Default constructor.
DeviceInfo()
: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.

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.
By Gary P. Scavone, 2001-2014.
By Gary P. Scavone, 2001-2016.
This distribution of RtAudio contains the following:
@ -11,7 +11,7 @@ tests/Windows: Visual C++ .net test program workspace and projects
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
- simple, common API across all supported platforms
@ -34,7 +34,7 @@ LEGAL AND ETHICAL:
The RtAudio license is similar to the MIT License.
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
obtaining a copy of this software and associated documentation files