update rtaudio to latest version
update rtaudio from latest version availbale on github
This commit is contained in:
parent
7156aff160
commit
2f4c435bfa
2 changed files with 230 additions and 137 deletions
|
@ -46,6 +46,7 @@
|
|||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
|
||||
// Static variable definitions.
|
||||
const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
|
||||
|
@ -63,6 +64,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)
|
||||
|
@ -184,7 +201,7 @@ RtAudio :: RtAudio( RtAudio::Api api )
|
|||
getCompiledApi( apis );
|
||||
for ( unsigned int i=0; i<apis.size(); i++ ) {
|
||||
openRtApi( apis[i] );
|
||||
if ( rtapi_->getDeviceCount() ) break;
|
||||
if ( rtapi_ && rtapi_->getDeviceCount() ) break;
|
||||
}
|
||||
|
||||
if ( rtapi_ ) return;
|
||||
|
@ -766,9 +783,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;
|
||||
|
@ -777,8 +799,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1385,6 +1411,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 )
|
||||
|
@ -1396,6 +1434,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 )
|
||||
|
@ -1989,7 +2039,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.
|
||||
|
@ -2769,8 +2821,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.
|
||||
|
@ -2829,9 +2885,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;
|
||||
}
|
||||
|
@ -2845,7 +2904,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
|
||||
|
@ -2866,22 +2925,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;
|
||||
|
@ -2890,30 +2953,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( ¤tRate );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2924,10 +2984,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.
|
||||
|
@ -2956,10 +3015,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
|
||||
|
@ -2968,49 +3026,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;
|
||||
|
@ -3022,16 +3094,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;
|
||||
|
||||
|
@ -3046,15 +3115,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 << ").";
|
||||
|
@ -3075,18 +3143,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;
|
||||
stream_.state = STREAM_STOPPED;
|
||||
|
||||
// Set flags for buffer conversion.
|
||||
stream_.doConvertBuffer[mode] = false;
|
||||
|
@ -3109,11 +3196,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 ) {
|
||||
|
@ -3127,18 +3212,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.";
|
||||
|
@ -3158,32 +3233,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()
|
||||
{
|
||||
|
@ -3635,12 +3716,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 );
|
||||
|
||||
|
@ -3799,7 +3880,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++ )
|
||||
|
@ -3945,7 +4026,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;
|
||||
|
||||
|
@ -4048,8 +4128,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 );
|
||||
|
@ -4066,8 +4145,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 ) {
|
||||
|
@ -4110,6 +4188,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;
|
||||
|
@ -5245,14 +5324,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());
|
||||
}
|
||||
|
@ -5308,8 +5384,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.
|
||||
|
@ -6264,6 +6344,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;
|
||||
}
|
||||
|
@ -6271,6 +6352,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;
|
||||
}
|
||||
|
@ -6279,6 +6361,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;
|
||||
}
|
||||
|
@ -6286,6 +6369,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;
|
||||
}
|
||||
|
@ -6307,6 +6391,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;
|
||||
}
|
||||
|
@ -6399,6 +6484,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;
|
||||
}
|
||||
|
@ -6412,6 +6498,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;
|
||||
}
|
||||
|
@ -6448,6 +6535,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;
|
||||
}
|
||||
|
@ -6509,6 +6597,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;
|
||||
}
|
||||
|
@ -6523,6 +6612,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;
|
||||
}
|
||||
|
@ -6544,6 +6634,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;
|
||||
}
|
||||
|
@ -6582,21 +6673,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*/,
|
||||
|
@ -6638,7 +6714,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";
|
||||
|
@ -6820,6 +6896,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 );
|
||||
|
@ -7033,8 +7110,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 );
|
||||
|
@ -7959,6 +8040,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 ) << ".";
|
||||
|
@ -8067,6 +8150,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;
|
||||
|
@ -8429,7 +8513,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;
|
||||
|
@ -8635,6 +8719,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;
|
||||
}
|
||||
}
|
||||
|
@ -8643,8 +8731,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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -310,12 +310,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.
|
||||
|
|
Loading…
Reference in a new issue