Update to RtAudio 4.11 ==> 4.12
This commit is contained in:
parent
bb337ad396
commit
26e61f87a7
|
@ -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( ¤tRate );
|
result = ASIOGetSampleRate( ¤tRate );
|
||||||
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 ) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue