2014-03-11 21:46:00 +01:00
/**
* @ author Gary P . SCAVONE
*
* @ copyright 2001 - 2013 Gary P . Scavone , all right reserved
*
* @ license like MIT ( see license file )
*/
# if defined(__WINDOWS_ASIO__) // ASIO API on Windows
# include <airtaudio/Interface.h>
2014-03-12 23:55:49 +01:00
# include <airtaudio/debug.h>
2014-03-11 21:46:00 +01:00
2014-03-11 22:37:22 +01:00
airtaudio : : Api * airtaudio : : api : : Asio : : Create ( void ) {
return new airtaudio : : api : : Asio ( ) ;
}
2014-03-11 21:46:00 +01:00
// The ASIO API is designed around a callback scheme, so this
// implementation is similar to that used for OS-X CoreAudio and Linux
// Jack. The primary constraint with ASIO is that it only allows
// access to a single driver at a time. Thus, it is not possible to
// have more than one simultaneous RtAudio stream.
//
// This implementation also requires a number of external ASIO files
// and a few global variables. The ASIO callback scheme does not
// allow for the passing of user data, so we must create a global
// pointer to our callbackInfo structure.
//
// On unix systems, we make use of a pthread condition variable.
// Since there is no equivalent in Windows, I hacked something based
// on information found in
// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
# include "asiosys.h"
# include "asio.h"
# include "iasiothiscallresolver.h"
# include "asiodrivers.h"
# include <cmath>
static AsioDrivers drivers ;
static ASIOCallbacks asioCallbacks ;
static ASIODriverInfo driverInfo ;
static CallbackInfo * asioCallbackInfo ;
static bool asioXRun ;
struct AsioHandle {
2014-03-13 21:16:30 +01:00
int32_t drainCounter ; // Tracks callback counts when draining
bool internalDrain ; // Indicates if stop is initiated from callback or not.
2014-03-11 21:46:00 +01:00
ASIOBufferInfo * bufferInfos ;
HANDLE condition ;
2014-03-13 21:16:30 +01:00
AsioHandle ( void ) :
drainCounter ( 0 ) ,
internalDrain ( false ) ,
bufferInfos ( 0 ) {
}
2014-03-11 21:46:00 +01:00
} ;
// Function declarations (definitions at end of section)
2014-03-13 21:16:30 +01:00
static const char * getAsioErrorString ( ASIOError _result ) ;
static void sampleRateChanged ( ASIOSampleRate _sRate ) ;
static long asioMessages ( long _selector , long _value , void * _message , double * _opt ) ;
2014-03-11 21:46:00 +01:00
airtaudio : : api : : Asio : : Asio ( void ) {
// ASIO cannot run on a multi-threaded appartment. You can call
// CoInitialize beforehand, but it must be for appartment threading
// (in which case, CoInitilialize will return S_FALSE here).
m_coInitialized = false ;
HRESULT hr = CoInitialize ( NULL ) ;
if ( FAILED ( hr ) ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED) " ) ;
2014-03-11 21:46:00 +01:00
}
m_coInitialized = true ;
drivers . removeCurrentDriver ( ) ;
driverInfo . asioVersion = 2 ;
// See note in DirectSound implementation about GetDesktopWindow().
driverInfo . sysRef = GetForegroundWindow ( ) ;
}
airtaudio : : api : : Asio : : ~ Asio ( void ) {
2014-03-13 21:16:30 +01:00
if ( m_stream . state ! = STREAM_CLOSED ) {
closeStream ( ) ;
}
if ( m_coInitialized ) {
CoUninitialize ( ) ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
uint32_t airtaudio : : api : : Asio : : getDeviceCount ( void ) {
2014-03-11 21:46:00 +01:00
return ( uint32_t ) drivers . asioGetNumDev ( ) ;
}
2014-03-13 21:16:30 +01:00
rtaudio : : DeviceInfo airtaudio : : api : : Asio : : getDeviceInfo ( uint32_t _device ) {
2014-03-11 21:46:00 +01:00
rtaudio : : DeviceInfo info ;
info . probed = false ;
// Get device ID
uint32_t nDevices = getDeviceCount ( ) ;
if ( nDevices = = 0 ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: no devices found! " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-13 21:16:30 +01:00
if ( _device > = nDevices ) {
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: device ID is invalid! " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// If a stream is already open, we cannot probe other devices. Thus, use the saved results.
if ( m_stream . state ! = STREAM_CLOSED ) {
2014-03-13 21:16:30 +01:00
if ( _device > = m_devices . size ( ) ) {
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: device ID was not present before stream was opened. " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
2014-03-13 21:16:30 +01:00
return m_devices [ _device ] ;
2014-03-11 21:46:00 +01:00
}
char driverName [ 32 ] ;
2014-03-13 21:16:30 +01:00
ASIOError result = drivers . asioGetDriverName ( ( int ) _device , driverName , 32 ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: unable to get driver name ( " < < getAsioErrorString ( result ) < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
info . name = driverName ;
if ( ! drivers . loadDriver ( driverName ) ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: unable to load driver ( " < < driverName < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
result = ASIOInit ( & driverInfo ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: error ( " < < getAsioErrorString ( result ) < < " ) initializing driver ( " < < driverName < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
// Determine the device channel information.
long inputChannels , outputChannels ;
result = ASIOGetChannels ( & inputChannels , & outputChannels ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: error ( " < < getAsioErrorString ( result ) < < " ) getting channel count ( " < < driverName < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
info . outputChannels = outputChannels ;
info . inputChannels = inputChannels ;
2014-03-13 21:16:30 +01:00
if ( info . outputChannels > 0 & & info . inputChannels > 0 ) {
2014-03-11 21:46:00 +01:00
info . duplexChannels = ( info . outputChannels > info . inputChannels ) ? info . inputChannels : info . outputChannels ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
// Determine the supported sample rates.
info . sampleRates . clear ( ) ;
for ( uint32_t i = 0 ; i < MAX_SAMPLE_RATES ; i + + ) {
result = ASIOCanSampleRate ( ( ASIOSampleRate ) SAMPLE_RATES [ i ] ) ;
2014-03-13 21:16:30 +01:00
if ( result = = ASE_OK ) {
2014-03-11 21:46:00 +01:00
info . sampleRates . push_back ( SAMPLE_RATES [ i ] ) ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
}
// Determine supported data types ... just check first channel and assume rest are the same.
ASIOChannelInfo channelInfo ;
channelInfo . channel = 0 ;
channelInfo . isInput = true ;
2014-03-13 21:16:30 +01:00
if ( info . inputChannels < = 0 ) {
channelInfo . isInput = false ;
}
2014-03-11 21:46:00 +01:00
result = ASIOGetChannelInfo ( & channelInfo ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::getDeviceInfo: error ( " < < getAsioErrorString ( result ) < < " ) getting driver channel info ( " < < driverName < < " ). " ) ;
2014-03-11 21:46:00 +01:00
return info ;
}
info . nativeFormats = 0 ;
2014-03-13 21:16:30 +01:00
if ( channelInfo . type = = ASIOSTInt16MSB
| | channelInfo . type = = ASIOSTInt16LSB ) {
2014-03-12 23:55:49 +01:00
info . nativeFormats | = SINT16 ;
2014-03-13 21:16:30 +01:00
} else if ( channelInfo . type = = ASIOSTInt32MSB
| | channelInfo . type = = ASIOSTInt32LSB ) {
2014-03-12 23:55:49 +01:00
info . nativeFormats | = SINT32 ;
2014-03-13 21:16:30 +01:00
} else if ( channelInfo . type = = ASIOSTFloat32MSB
| | channelInfo . type = = ASIOSTFloat32LSB ) {
2014-03-12 23:55:49 +01:00
info . nativeFormats | = FLOAT32 ;
2014-03-13 21:16:30 +01:00
} else if ( channelInfo . type = = ASIOSTFloat64MSB
| | channelInfo . type = = ASIOSTFloat64LSB ) {
2014-03-12 23:55:49 +01:00
info . nativeFormats | = FLOAT64 ;
2014-03-13 21:16:30 +01:00
} else if ( channelInfo . type = = ASIOSTInt24MSB
| | channelInfo . type = = ASIOSTInt24LSB ) {
2014-03-12 23:55:49 +01:00
info . nativeFormats | = SINT24 ;
2014-03-13 21:16:30 +01:00
}
if ( info . outputChannels > 0 ) {
if ( getDefaultOutputDevice ( ) = = _device ) {
info . isDefaultOutput = true ;
}
}
if ( info . inputChannels > 0 ) {
if ( getDefaultInputDevice ( ) = = _device ) {
info . isDefaultInput = true ;
}
}
2014-03-11 21:46:00 +01:00
info . probed = true ;
drivers . removeCurrentDriver ( ) ;
return info ;
}
2014-03-13 21:16:30 +01:00
static void bufferSwitch ( long _index , ASIOBool _processNow ) {
RtApiAsio * object = ( RtApiAsio * ) asioCallbackInfo - > object ;
object - > callbackEvent ( _index ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
void airtaudio : : api : : Asio : : saveDeviceInfo ( void ) {
2014-03-11 21:46:00 +01:00
m_devices . clear ( ) ;
uint32_t nDevices = getDeviceCount ( ) ;
m_devices . resize ( nDevices ) ;
2014-03-13 21:16:30 +01:00
for ( uint32_t i = 0 ; i < nDevices ; i + + ) {
2014-03-11 21:46:00 +01:00
m_devices [ i ] = getDeviceInfo ( i ) ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
bool airtaudio : : api : : Asio : : probeDeviceOpen ( uint32_t _device ,
airtaudio : : api : : StreamMode _mode ,
uint32_t _channels ,
uint32_t _firstChannel ,
uint32_t _sampleRate ,
airtaudio : : format _format ,
uint32_t * _bufferSize ,
airtaudio : : StreamOptions * _options ) {
2014-03-11 21:46:00 +01:00
// For ASIO, a duplex stream MUST use the same driver.
2014-03-13 21:16:30 +01:00
if ( _mode = = INPUT
& & m_stream . mode = = OUTPUT
& & m_stream . device [ 0 ] ! = _device ) {
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output! " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
char driverName [ 32 ] ;
2014-03-13 21:16:30 +01:00
ASIOError result = drivers . asioGetDriverName ( ( int ) _device , driverName , 32 ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: unable to get driver name ( " < < getAsioErrorString ( result ) < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
// Only load the driver once for duplex stream.
2014-03-13 21:16:30 +01:00
if ( _mode ! = INPUT
| | m_stream . mode ! = OUTPUT ) {
2014-03-11 21:46:00 +01:00
// 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
// save the results for use by getDeviceInfo().
this - > saveDeviceInfo ( ) ;
if ( ! drivers . loadDriver ( driverName ) ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: unable to load driver ( " < < driverName < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
result = ASIOInit ( & driverInfo ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error ( " < < getAsioErrorString ( result ) < < " ) initializing driver ( " < < driverName < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
}
// Check the device channel count.
long inputChannels , outputChannels ;
result = ASIOGetChannels ( & inputChannels , & outputChannels ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error ( " < < getAsioErrorString ( result ) < < " ) getting channel count ( " < < driverName < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
if ( ( _mode = = OUTPUT
& & ( _channels + _firstChannel ) > ( uint32_t ) outputChannels )
| | ( _mode = = INPUT
& & ( _channels + _firstChannel ) > ( uint32_t ) inputChannels ) ) {
2014-03-11 21:46:00 +01:00
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) does not support requested channel count ( " < < _channels < < " ) + offset ( " < < _firstChannel < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
m_stream . nDeviceChannels [ _mode ] = _channels ;
m_stream . nUserChannels [ _mode ] = _channels ;
m_stream . channelOffset [ _mode ] = _firstChannel ;
2014-03-11 21:46:00 +01:00
// Verify the sample rate is supported.
2014-03-13 21:16:30 +01:00
result = ASIOCanSampleRate ( ( ASIOSampleRate ) _sampleRate ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) does not support requested sample rate ( " < < _sampleRate < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
// Get the current sample rate
ASIOSampleRate currentRate ;
result = ASIOGetSampleRate ( & currentRate ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error getting sample rate. " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
// Set the sample rate only if necessary
2014-03-13 21:16:30 +01:00
if ( currentRate ! = _sampleRate ) {
result = ASIOSetSampleRate ( ( ASIOSampleRate ) _sampleRate ) ;
2014-03-11 21:46:00 +01:00
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error setting sample rate ( " < < _sampleRate < < " ). " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
}
// Determine the driver data type.
ASIOChannelInfo channelInfo ;
channelInfo . channel = 0 ;
2014-03-13 21:16:30 +01:00
if ( _mode = = OUTPUT ) {
channelInfo . isInput = false ;
} else {
channelInfo . isInput = true ;
}
2014-03-11 21:46:00 +01:00
result = ASIOGetChannelInfo ( & channelInfo ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error ( " < < getAsioErrorString ( result ) < < " ) getting data format. " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
// Assuming WINDOWS host is always little-endian.
2014-03-13 21:16:30 +01:00
m_stream . doByteSwap [ _mode ] = false ;
m_stream . userFormat = _format ;
m_stream . deviceFormat [ _mode ] = 0 ;
if ( channelInfo . type = = ASIOSTInt16MSB
| | channelInfo . type = = ASIOSTInt16LSB ) {
m_stream . deviceFormat [ _mode ] = RTAUDIO_SINT16 ;
if ( channelInfo . type = = ASIOSTInt16MSB ) {
m_stream . doByteSwap [ _mode ] = true ;
}
} else if ( channelInfo . type = = ASIOSTInt32MSB
| | channelInfo . type = = ASIOSTInt32LSB ) {
m_stream . deviceFormat [ _mode ] = RTAUDIO_SINT32 ;
if ( channelInfo . type = = ASIOSTInt32MSB ) {
m_stream . doByteSwap [ _mode ] = true ;
}
} else if ( channelInfo . type = = ASIOSTFloat32MSB
| | channelInfo . type = = ASIOSTFloat32LSB ) {
m_stream . deviceFormat [ _mode ] = RTAUDIO_FLOAT32 ;
if ( channelInfo . type = = ASIOSTFloat32MSB ) {
m_stream . doByteSwap [ _mode ] = true ;
}
} else if ( channelInfo . type = = ASIOSTFloat64MSB
| | channelInfo . type = = ASIOSTFloat64LSB ) {
m_stream . deviceFormat [ _mode ] = RTAUDIO_FLOAT64 ;
if ( channelInfo . type = = ASIOSTFloat64MSB ) {
m_stream . doByteSwap [ _mode ] = true ;
}
} else if ( channelInfo . type = = ASIOSTInt24MSB
| | channelInfo . type = = ASIOSTInt24LSB ) {
m_stream . deviceFormat [ _mode ] = RTAUDIO_SINT24 ;
if ( channelInfo . type = = ASIOSTInt24MSB ) {
m_stream . doByteSwap [ _mode ] = true ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
if ( m_stream . deviceFormat [ _mode ] = = 0 ) {
2014-03-11 21:46:00 +01:00
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) data format not supported by RtAudio. " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
// Set the buffer size. For a duplex stream, this will end up
// setting the buffer size based on the input constraints, which
// should be ok.
long minSize , maxSize , preferSize , granularity ;
result = ASIOGetBufferSize ( & minSize , & maxSize , & preferSize , & granularity ) ;
if ( result ! = ASE_OK ) {
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error ( " < < getAsioErrorString ( result ) < < " ) getting buffer size. " ) ;
return false ;
}
if ( * _bufferSize < ( uint32_t ) minSize ) {
* _bufferSize = ( uint32_t ) minSize ;
} else if ( * _bufferSize > ( uint32_t ) maxSize ) {
* _bufferSize = ( uint32_t ) maxSize ;
} else if ( granularity = = - 1 ) {
2014-03-11 21:46:00 +01:00
// Make sure bufferSize is a power of two.
int32_t log2_of_min_size = 0 ;
int32_t log2_of_max_size = 0 ;
for ( uint32_t i = 0 ; i < sizeof ( long ) * 8 ; i + + ) {
2014-03-13 21:16:30 +01:00
if ( minSize & ( ( long ) 1 < < i ) ) {
log2_of_min_size = i ;
}
if ( maxSize & ( ( long ) 1 < < i ) ) {
log2_of_max_size = i ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
long min_delta = std : : abs ( ( long ) * _bufferSize - ( ( long ) 1 < < log2_of_min_size ) ) ;
2014-03-11 21:46:00 +01:00
int32_t min_delta_num = log2_of_min_size ;
for ( int32_t i = log2_of_min_size + 1 ; i < = log2_of_max_size ; i + + ) {
2014-03-13 21:16:30 +01:00
long current_delta = std : : abs ( ( long ) * _bufferSize - ( ( long ) 1 < < i ) ) ;
2014-03-11 21:46:00 +01:00
if ( current_delta < min_delta ) {
min_delta = current_delta ;
min_delta_num = i ;
}
}
2014-03-13 21:16:30 +01:00
* _bufferSize = ( ( uint32_t ) 1 < < min_delta_num ) ;
if ( * _bufferSize < ( uint32_t ) {
minSize ) * _bufferSize = ( uint32_t ) minSize ;
} else if ( * _bufferSize > ( uint32_t ) maxSize ) {
* _bufferSize = ( uint32_t ) maxSize ;
}
} else if ( granularity ! = 0 ) {
2014-03-11 21:46:00 +01:00
// Set to an even multiple of granularity, rounding up.
2014-03-13 21:16:30 +01:00
* _bufferSize = ( * _bufferSize + granularity - 1 ) / granularity * granularity ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
if ( _mode = = INPUT
& & m_stream . mode = = OUTPUT
& & m_stream . bufferSize ! = * _bufferSize ) {
2014-03-11 21:46:00 +01:00
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: input/output buffersize discrepancy! " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
m_stream . bufferSize = * _bufferSize ;
2014-03-11 21:46:00 +01:00
m_stream . nBuffers = 2 ;
2014-03-13 21:16:30 +01:00
if ( _options ! = NULL
& & _options - > flags & RTAUDIO_NONINTERLEAVED ) {
m_stream . userInterleaved = false ;
} else {
m_stream . userInterleaved = true ;
}
2014-03-11 21:46:00 +01:00
// ASIO always uses non-interleaved buffers.
2014-03-13 21:16:30 +01:00
m_stream . deviceInterleaved [ _mode ] = false ;
2014-03-11 21:46:00 +01:00
// Allocate, if necessary, our AsioHandle structure for the stream.
AsioHandle * handle = ( AsioHandle * ) m_stream . apiHandle ;
2014-03-13 21:16:30 +01:00
if ( handle = = NULL ) {
handle = new AsioHandle ;
if ( handle = = NULL ) {
2014-03-11 21:46:00 +01:00
drivers . removeCurrentDriver ( ) ;
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error allocating AsioHandle memory. " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
handle - > bufferInfos = 0 ;
// Create a manual-reset event.
2014-03-13 21:16:30 +01:00
handle - > condition = CreateEvent ( NULL , // no security
TRUE , // manual-reset
FALSE , // non-signaled initially
NULL ) ; // unnamed
2014-03-11 21:46:00 +01:00
m_stream . apiHandle = ( void * ) handle ;
}
// 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 ;
2014-03-13 21:16:30 +01:00
if ( _mode = = INPUT
& & m_stream . mode = = OUTPUT ) {
2014-03-11 21:46:00 +01:00
ASIODisposeBuffers ( ) ;
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos = = NULL ) {
free ( handle - > bufferInfos ) ;
handle - > bufferInfos = NULL ;
}
2014-03-11 21:46:00 +01:00
}
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
bool buffersAllocated = false ;
uint32_t i , nChannels = m_stream . nDeviceChannels [ 0 ] + m_stream . nDeviceChannels [ 1 ] ;
handle - > bufferInfos = ( ASIOBufferInfo * ) malloc ( nChannels * sizeof ( ASIOBufferInfo ) ) ;
if ( handle - > bufferInfos = = NULL ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error allocating bufferInfo memory for driver ( " < < driverName < < " ). " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
ASIOBufferInfo * infos ;
infos = handle - > bufferInfos ;
for ( i = 0 ; i < m_stream . nDeviceChannels [ 0 ] ; i + + , infos + + ) {
infos - > isInput = ASIOFalse ;
infos - > channelNum = i + m_stream . channelOffset [ 0 ] ;
infos - > buffers [ 0 ] = infos - > buffers [ 1 ] = 0 ;
}
for ( i = 0 ; i < m_stream . nDeviceChannels [ 1 ] ; i + + , infos + + ) {
infos - > isInput = ASIOTrue ;
infos - > channelNum = i + m_stream . channelOffset [ 1 ] ;
infos - > buffers [ 0 ] = infos - > buffers [ 1 ] = 0 ;
}
// 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 , m_stream . bufferSize , & asioCallbacks ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error ( " < < getAsioErrorString ( result ) < < " ) creating buffers. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
buffersAllocated = true ;
// Set flags for buffer conversion.
2014-03-13 21:16:30 +01:00
m_stream . doConvertBuffer [ _mode ] = false ;
if ( m_stream . userFormat ! = m_stream . deviceFormat [ _mode ] ) {
m_stream . doConvertBuffer [ _mode ] = true ;
}
if ( m_stream . userInterleaved ! = m_stream . deviceInterleaved [ _mode ]
& & m_stream . nUserChannels [ _mode ] > 1 ) {
m_stream . doConvertBuffer [ _mode ] = true ;
}
2014-03-11 21:46:00 +01:00
// Allocate necessary internal buffers
uint64_t bufferBytes ;
2014-03-13 21:16:30 +01:00
bufferBytes = m_stream . nUserChannels [ _mode ] * * _bufferSize * formatBytes ( m_stream . userFormat ) ;
m_stream . userBuffer [ _mode ] = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_stream . userBuffer [ _mode ] = = NULL ) {
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error allocating user buffer memory. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
2014-03-13 21:16:30 +01:00
if ( m_stream . doConvertBuffer [ _mode ] ) {
2014-03-11 21:46:00 +01:00
bool makeBuffer = true ;
2014-03-13 21:16:30 +01:00
bufferBytes = m_stream . nDeviceChannels [ _mode ] * formatBytes ( m_stream . deviceFormat [ _mode ] ) ;
if ( _mode = = INPUT ) {
2014-03-11 21:46:00 +01:00
if ( m_stream . mode = = OUTPUT & & m_stream . deviceBuffer ) {
uint64_t bytesOut = m_stream . nDeviceChannels [ 0 ] * formatBytes ( m_stream . deviceFormat [ 0 ] ) ;
2014-03-13 21:16:30 +01:00
if ( bufferBytes < = bytesOut ) {
makeBuffer = false ;
}
2014-03-11 21:46:00 +01:00
}
}
if ( makeBuffer ) {
2014-03-13 21:16:30 +01:00
bufferBytes * = * _bufferSize ;
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = NULL ;
}
2014-03-11 21:46:00 +01:00
m_stream . deviceBuffer = ( char * ) calloc ( bufferBytes , 1 ) ;
if ( m_stream . deviceBuffer = = NULL ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: error allocating device buffer memory. " ) ;
2014-03-11 21:46:00 +01:00
goto error ;
}
}
}
2014-03-13 21:16:30 +01:00
m_stream . sampleRate = _sampleRate ;
m_stream . device [ _mode ] = _device ;
2014-03-11 21:46:00 +01:00
m_stream . state = STREAM_STOPPED ;
asioCallbackInfo = & m_stream . callbackInfo ;
2014-03-13 21:16:30 +01:00
m_stream . callbackInfo . object = ( void * ) this ;
if ( m_stream . mode = = OUTPUT
& & _mode = = INPUT ) {
2014-03-11 21:46:00 +01:00
// We had already set up an output stream.
m_stream . mode = DUPLEX ;
2014-03-13 21:16:30 +01:00
} else {
m_stream . mode = _mode ;
}
2014-03-11 21:46:00 +01:00
// Determine device latencies
result = ASIOGetLatencies ( & inputLatency , & outputLatency ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::probeDeviceOpen: driver ( " < < driverName < < " ) error ( " < < getAsioErrorString ( result ) < < " ) getting latency. " ) ;
} else {
2014-03-11 21:46:00 +01:00
m_stream . latency [ 0 ] = outputLatency ;
m_stream . latency [ 1 ] = inputLatency ;
}
// Setup the buffer conversion information structure. We don't use
// buffers to do channel offsets, so we override that parameter
// here.
2014-03-13 21:16:30 +01:00
if ( m_stream . doConvertBuffer [ _mode ] ) {
setConvertInfo ( _mode , 0 ) ;
}
return true ;
error :
if ( buffersAllocated ) {
2014-03-11 21:46:00 +01:00
ASIODisposeBuffers ( ) ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
drivers . removeCurrentDriver ( ) ;
if ( handle ) {
CloseHandle ( handle - > condition ) ;
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos ) {
2014-03-11 21:46:00 +01:00
free ( handle - > bufferInfos ) ;
2014-03-13 21:16:30 +01:00
handle - > bufferInfos = NULL ;
}
2014-03-11 21:46:00 +01:00
delete handle ;
2014-03-13 21:16:30 +01:00
handle = NULL ;
2014-03-11 21:46:00 +01:00
m_stream . apiHandle = 0 ;
}
for ( int32_t i = 0 ; i < 2 ; i + + ) {
if ( m_stream . userBuffer [ i ] ) {
free ( m_stream . userBuffer [ i ] ) ;
m_stream . userBuffer [ i ] = 0 ;
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = 0 ;
}
2014-03-13 21:16:30 +01:00
return false ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
enum airtaudio : : errorType airtaudio : : api : : Asio : : closeStream ( void ) {
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_CLOSED ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::closeStream(): no open stream to close! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
if ( m_stream . state = = STREAM_RUNNING ) {
m_stream . state = STREAM_STOPPED ;
ASIOStop ( ) ;
}
ASIODisposeBuffers ( ) ;
drivers . removeCurrentDriver ( ) ;
AsioHandle * handle = ( AsioHandle * ) m_stream . apiHandle ;
if ( handle ) {
CloseHandle ( handle - > condition ) ;
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos ) {
2014-03-11 21:46:00 +01:00
free ( handle - > bufferInfos ) ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
delete handle ;
m_stream . apiHandle = 0 ;
}
for ( int32_t i = 0 ; i < 2 ; i + + ) {
if ( m_stream . userBuffer [ i ] ) {
free ( m_stream . userBuffer [ i ] ) ;
m_stream . userBuffer [ i ] = 0 ;
}
}
if ( m_stream . deviceBuffer ) {
free ( m_stream . deviceBuffer ) ;
m_stream . deviceBuffer = 0 ;
}
m_stream . mode = UNINITIALIZED ;
m_stream . state = STREAM_CLOSED ;
2014-03-13 21:16:30 +01:00
return airtaudio : : errorNone ;
2014-03-11 21:46:00 +01:00
}
bool stopThreadCalled = false ;
2014-03-13 21:16:30 +01:00
enum airtaudio : : errorType airtaudio : : api : : Asio : : startStream ( void ) {
2014-03-12 23:55:49 +01:00
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_RUNNING ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::startStream(): the stream is already running! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
AsioHandle * handle = ( AsioHandle * ) m_stream . apiHandle ;
ASIOError result = ASIOStart ( ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::startStream: error ( " < < getAsioErrorString ( result ) < < " ) starting device. " ) ;
2014-03-11 21:46:00 +01:00
goto unlock ;
}
handle - > drainCounter = 0 ;
handle - > internalDrain = false ;
ResetEvent ( handle - > condition ) ;
m_stream . state = STREAM_RUNNING ;
asioXRun = false ;
2014-03-13 21:16:30 +01:00
unlock :
2014-03-11 21:46:00 +01:00
stopThreadCalled = false ;
2014-03-13 21:16:30 +01:00
if ( result = = ASE_OK ) {
return airtaudio : : errorNone ;
}
return airtaudio : : errorSystemError ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
enum airtaudio : : errorType airtaudio : : api : : Asio : : stopStream ( void ) {
2014-03-12 23:55:49 +01:00
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_STOPPED ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::stopStream(): the stream is already stopped! " ) ;
return airtaudio : : errorWarning ;
2014-03-11 21:46:00 +01:00
}
AsioHandle * handle = ( AsioHandle * ) m_stream . apiHandle ;
if ( m_stream . mode = = OUTPUT | | m_stream . mode = = DUPLEX ) {
if ( handle - > drainCounter = = 0 ) {
handle - > drainCounter = 2 ;
WaitForSingleObject ( handle - > condition , INFINITE ) ; // block until signaled
}
}
m_stream . state = STREAM_STOPPED ;
ASIOError result = ASIOStop ( ) ;
if ( result ! = ASE_OK ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::stopStream: error ( " < < getAsioErrorString ( result ) < < " ) stopping device. " ) ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
if ( result = = ASE_OK ) {
return airtaudio : : errorNone ;
}
return airtaudio : : errorSystemError ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
enum airtaudio : : errorType airtaudio : : api : : Asio : : abortStream ( void ) {
2014-03-12 23:55:49 +01:00
if ( verifyStream ( ) ! = airtaudio : : errorNone ) {
return airtaudio : : errorFail ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_STOPPED ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::abortStream(): the stream is already stopped! " ) ;
2014-03-11 21:46:00 +01:00
error ( airtaudio : : errorWarning ) ;
return ;
}
// The following lines were commented-out because some behavior was
// noted where the device buffers need to be zeroed to avoid
// continuing sound, even when the device buffers are completely
// disposed. So now, calling abort is the same as calling stop.
// AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
// handle->drainCounter = 2;
2014-03-13 21:16:30 +01:00
return stopStream ( ) ;
2014-03-11 21:46:00 +01:00
}
// This function will be called by a spawned thread when the user
// callback function signals that the stream should be stopped or
// aborted. It is necessary to handle it this way because the
// callbackEvent() function must return before the ASIOStop()
// function will return.
2014-03-13 21:16:30 +01:00
static unsigned __stdcall asioStopStream ( void * _ptr ) {
CallbackInfo * info = ( CallbackInfo * ) _ptr ;
RtApiAsio * object = ( RtApiAsio * ) info - > object ;
2014-03-11 21:46:00 +01:00
object - > stopStream ( ) ;
_endthreadex ( 0 ) ;
return 0 ;
}
2014-03-13 21:16:30 +01:00
bool airtaudio : : api : : Asio : : callbackEvent ( long bufferIndex ) {
if ( m_stream . state = = STREAM_STOPPED
| | m_stream . state = = STREAM_STOPPING ) {
return true ;
}
2014-03-11 21:46:00 +01:00
if ( m_stream . state = = STREAM_CLOSED ) {
2014-03-13 21:16:30 +01:00
ATA_ERROR ( " airtaudio::api::Asio::callbackEvent(): the stream is closed ... this shouldn't happen! " ) ;
return false ;
2014-03-11 21:46:00 +01:00
}
CallbackInfo * info = ( CallbackInfo * ) & m_stream . callbackInfo ;
AsioHandle * handle = ( AsioHandle * ) m_stream . apiHandle ;
// Check if we were draining the stream and signal if finished.
if ( handle - > drainCounter > 3 ) {
m_stream . state = STREAM_STOPPING ;
2014-03-13 21:16:30 +01:00
if ( handle - > internalDrain = = false ) {
2014-03-11 21:46:00 +01:00
SetEvent ( handle - > condition ) ;
2014-03-13 21:16:30 +01:00
} else { // spawn a thread to stop the stream
2014-03-11 21:46:00 +01:00
unsigned threadId ;
m_stream . callbackInfo . thread = _beginthreadex ( NULL , 0 , & asioStopStream ,
& m_stream . callbackInfo , 0 , & threadId ) ;
}
2014-03-13 21:16:30 +01:00
return true ;
2014-03-11 21:46:00 +01:00
}
// Invoke user callback to get fresh output data UNLESS we are
// draining stream.
if ( handle - > drainCounter = = 0 ) {
airtaudio : : AirTAudioCallback callback = ( airtaudio : : AirTAudioCallback ) info - > callback ;
double streamTime = getStreamTime ( ) ;
rtaudio : : streamStatus status = 0 ;
if ( m_stream . mode ! = INPUT & & asioXRun = = true ) {
status | = RTAUDIO_OUTPUT_UNDERFLOW ;
asioXRun = false ;
}
if ( m_stream . mode ! = OUTPUT & & asioXRun = = true ) {
status | = RTAUDIO_INPUT_OVERFLOW ;
asioXRun = false ;
}
2014-03-13 21:16:30 +01:00
int32_t cbReturnValue = callback ( m_stream . userBuffer [ 0 ] ,
m_stream . userBuffer [ 1 ] ,
m_stream . bufferSize ,
streamTime ,
status ,
info - > userData ) ;
2014-03-11 21:46:00 +01:00
if ( cbReturnValue = = 2 ) {
m_stream . state = STREAM_STOPPING ;
handle - > drainCounter = 2 ;
unsigned threadId ;
2014-03-13 21:16:30 +01:00
m_stream . callbackInfo . thread = _beginthreadex ( NULL ,
0 ,
& asioStopStream ,
& m_stream . callbackInfo ,
0 ,
& threadId ) ;
return true ;
} else if ( cbReturnValue = = 1 ) {
2014-03-11 21:46:00 +01:00
handle - > drainCounter = 1 ;
handle - > internalDrain = true ;
}
}
uint32_t nChannels , bufferBytes , i , j ;
nChannels = m_stream . nDeviceChannels [ 0 ] + m_stream . nDeviceChannels [ 1 ] ;
2014-03-13 21:16:30 +01:00
if ( m_stream . mode = = OUTPUT
| | m_stream . mode = = DUPLEX ) {
2014-03-11 21:46:00 +01:00
bufferBytes = m_stream . bufferSize * formatBytes ( m_stream . deviceFormat [ 0 ] ) ;
if ( handle - > drainCounter > 1 ) { // write zeros to the output stream
for ( i = 0 , j = 0 ; i < nChannels ; i + + ) {
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos [ i ] . isInput ! = ASIOTrue ) {
2014-03-11 21:46:00 +01:00
memset ( handle - > bufferInfos [ i ] . buffers [ bufferIndex ] , 0 , bufferBytes ) ;
2014-03-13 21:16:30 +01:00
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
} else if ( m_stream . doConvertBuffer [ 0 ] ) {
2014-03-11 21:46:00 +01:00
convertBuffer ( m_stream . deviceBuffer , m_stream . userBuffer [ 0 ] , m_stream . convertInfo [ 0 ] ) ;
2014-03-13 21:16:30 +01:00
if ( m_stream . doByteSwap [ 0 ] ) {
2014-03-11 21:46:00 +01:00
byteSwapBuffer ( m_stream . deviceBuffer ,
2014-03-13 21:16:30 +01:00
m_stream . bufferSize * m_stream . nDeviceChannels [ 0 ] ,
m_stream . deviceFormat [ 0 ] ) ;
}
2014-03-11 21:46:00 +01:00
for ( i = 0 , j = 0 ; i < nChannels ; i + + ) {
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos [ i ] . isInput ! = ASIOTrue ) {
2014-03-11 21:46:00 +01:00
memcpy ( handle - > bufferInfos [ i ] . buffers [ bufferIndex ] ,
2014-03-13 21:16:30 +01:00
& m_stream . deviceBuffer [ j + + * bufferBytes ] ,
bufferBytes ) ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
} else {
if ( m_stream . doByteSwap [ 0 ] ) {
2014-03-11 21:46:00 +01:00
byteSwapBuffer ( m_stream . userBuffer [ 0 ] ,
2014-03-13 21:16:30 +01:00
m_stream . bufferSize * m_stream . nUserChannels [ 0 ] ,
m_stream . userFormat ) ;
}
2014-03-11 21:46:00 +01:00
for ( i = 0 , j = 0 ; i < nChannels ; i + + ) {
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos [ i ] . isInput ! = ASIOTrue ) {
2014-03-11 21:46:00 +01:00
memcpy ( handle - > bufferInfos [ i ] . buffers [ bufferIndex ] ,
2014-03-13 21:16:30 +01:00
& m_stream . userBuffer [ 0 ] [ bufferBytes * j + + ] ,
bufferBytes ) ;
}
2014-03-11 21:46:00 +01:00
}
}
if ( handle - > drainCounter ) {
handle - > drainCounter + + ;
goto unlock ;
}
}
2014-03-13 21:16:30 +01:00
if ( m_stream . mode = = INPUT
| | m_stream . mode = = DUPLEX ) {
2014-03-11 21:46:00 +01:00
bufferBytes = m_stream . bufferSize * formatBytes ( m_stream . deviceFormat [ 1 ] ) ;
if ( m_stream . doConvertBuffer [ 1 ] ) {
// Always interleave ASIO input data.
for ( i = 0 , j = 0 ; i < nChannels ; i + + ) {
2014-03-13 21:16:30 +01:00
if ( handle - > bufferInfos [ i ] . isInput = = ASIOTrue ) {
2014-03-11 21:46:00 +01:00
memcpy ( & m_stream . deviceBuffer [ j + + * bufferBytes ] ,
2014-03-13 21:16:30 +01:00
handle - > bufferInfos [ i ] . buffers [ bufferIndex ] ,
bufferBytes ) ;
}
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
if ( m_stream . doByteSwap [ 1 ] ) {
2014-03-11 21:46:00 +01:00
byteSwapBuffer ( m_stream . deviceBuffer ,
2014-03-13 21:16:30 +01:00
m_stream . bufferSize * m_stream . nDeviceChannels [ 1 ] ,
m_stream . deviceFormat [ 1 ] ) ;
}
convertBuffer ( m_stream . userBuffer [ 1 ] ,
m_stream . deviceBuffer ,
m_stream . convertInfo [ 1 ] ) ;
} else {
2014-03-11 21:46:00 +01:00
for ( i = 0 , j = 0 ; i < nChannels ; i + + ) {
if ( handle - > bufferInfos [ i ] . isInput = = ASIOTrue ) {
memcpy ( & m_stream . userBuffer [ 1 ] [ bufferBytes * j + + ] ,
2014-03-13 21:16:30 +01:00
handle - > bufferInfos [ i ] . buffers [ bufferIndex ] ,
bufferBytes ) ;
2014-03-11 21:46:00 +01:00
}
}
2014-03-13 21:16:30 +01:00
if ( m_stream . doByteSwap [ 1 ] ) {
2014-03-11 21:46:00 +01:00
byteSwapBuffer ( m_stream . userBuffer [ 1 ] ,
2014-03-13 21:16:30 +01:00
m_stream . bufferSize * m_stream . nUserChannels [ 1 ] ,
m_stream . userFormat ) ;
}
2014-03-11 21:46:00 +01:00
}
}
2014-03-13 21:16:30 +01:00
unlock :
2014-03-11 21:46:00 +01:00
// The following call was suggested by Malte Clasen. While the API
// documentation indicates it should not be required, some device
// drivers apparently do not function correctly without it.
ASIOOutputReady ( ) ;
2014-03-13 21:16:30 +01:00
airtaudio : : Api : : tickStreamTime ( ) ;
return true ;
2014-03-11 21:46:00 +01:00
}
2014-03-13 21:16:30 +01:00
static void sampleRateChanged ( ASIOSampleRate _sRate ) {
2014-03-11 21:46:00 +01:00
// The ASIO documentation says that this usually only happens during
// external sync. Audio processing is not stopped by the driver,
// actual sample rate might not have even changed, maybe only the
// sample rate status of an AES/EBU or S/PDIF digital input at the
// audio device.
2014-03-13 21:16:30 +01:00
RtApi * object = ( RtApi * ) asioCallbackInfo - > object ;
enum airtaudio : : errorType ret = object - > stopStream ( )
if ( ret ! = airtaudio : : errorNone ) {
ATA_ERROR ( " airtaudio::api::Asio:: sampleRateChanged() error! " ) ;
} else {
ATA_ERROR ( " airtaudio::api::Asio:: driver reports sample rate changed to " < < _sRate < < " ... stream stopped!!! " ) ;
2014-03-11 21:46:00 +01:00
}
}
2014-03-13 21:16:30 +01:00
static long asioMessages ( long _selector , long _value , void * _message , double * _opt ) {
2014-03-11 21:46:00 +01:00
long ret = 0 ;
2014-03-13 21:16:30 +01:00
switch ( _selector ) {
case kAsioSelectorSupported :
if ( _value = = kAsioResetRequest
| | _value = = kAsioEngineVersion
| | _value = = kAsioResyncRequest
| | _value = = kAsioLatenciesChanged
// The following three were added for ASIO 2.0, you don't
// necessarily have to support them.
| | _value = = kAsioSupportsTimeInfo
| | _value = = kAsioSupportsTimeCode
| | _value = = kAsioSupportsInputMonitor ) {
ret = 1L ;
}
break ;
case kAsioResetRequest :
// Defer the task and perform the reset of the driver during the
// next "safe" situation. You cannot reset the driver right now,
// as this code is called from the driver. Reset the driver is
// done by completely destruct is. I.e. ASIOStop(),
// ASIODisposeBuffers(), Destruction Afterwards you initialize the
// driver again.
ATA_ERROR ( " airtaudio::api::Asio:: driver reset requested!!! " ) ;
ret = 1L ;
break ;
case kAsioResyncRequest :
// This informs the application that the driver encountered some
// non-fatal data loss. It is used for synchronization purposes
// of different media. Added mainly to work around the Win16Mutex
// problems in Windows 95/98 with the Windows Multimedia system,
// which could lose data because the Mutex was held too long by
// another thread. However a driver can issue it in other
// situations, too.
// ATA_ERROR("airtaudio::api::Asio:: driver resync requested!!!");
asioXRun = true ;
2014-03-11 21:46:00 +01:00
ret = 1L ;
2014-03-13 21:16:30 +01:00
break ;
case kAsioLatenciesChanged :
// This will inform the host application that the drivers were
// latencies changed. Beware, it this does not mean that the
// buffer sizes have changed! You might need to update internal
// delay data.
ATA_ERROR ( " airtaudio::api::Asio:: driver latency may have changed!!! " ) ;
ret = 1L ;
break ;
case kAsioEngineVersion :
// Return the supported ASIO version of the host application. If
// a host application does not implement this selector, ASIO 1.0
// is assumed by the driver.
ret = 2L ;
break ;
case kAsioSupportsTimeInfo :
// Informs the driver whether the
// asioCallbacks.bufferSwitchTimeInfo() callback is supported.
// For compatibility with ASIO 1.0 drivers the host application
// should always support the "old" bufferSwitch method, too.
ret = 0 ;
break ;
case kAsioSupportsTimeCode :
// Informs the driver whether application is interested in time
// code info. If an application does not need to know about time
// code, the driver has less work to do.
ret = 0 ;
break ;
2014-03-11 21:46:00 +01:00
}
return ret ;
}
2014-03-13 21:16:30 +01:00
static const char * getAsioErrorString ( ASIOError _result ) {
struct Messages {
2014-03-11 21:46:00 +01:00
ASIOError value ;
const char * message ;
} ;
2014-03-13 21:16:30 +01:00
static const Messages m [ ] = {
{ ASE_NotPresent , " Hardware input or output is not present or available. " } ,
{ ASE_HWMalfunction , " Hardware is malfunctioning. " } ,
{ ASE_InvalidParameter , " Invalid input parameter. " } ,
{ ASE_InvalidMode , " Invalid mode. " } ,
{ ASE_SPNotAdvancing , " Sample position not advancing. " } ,
{ ASE_NoClock , " Sample clock or rate cannot be determined or is not present. " } ,
{ ASE_NoMemory , " Not enough memory to complete the request. " }
} ;
for ( uint32_t i = 0 ; i < sizeof ( m ) / sizeof ( m [ 0 ] ) ; + + i ) {
if ( m [ i ] . value = = result ) {
return m [ i ] . message ;
}
}
2014-03-11 21:46:00 +01:00
return " Unknown error. " ;
}
2014-03-13 21:16:30 +01:00
2014-03-11 21:46:00 +01:00
# endif