X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=RtAudio.cpp;h=0a7d9ebda0537975a6c247acde50b46ae201d797;hb=ee76af899f7f27f4784d3bbae27f2c63f0f1e6ce;hp=8477ef995ad7b9d7a77eb4a13bf16d95da7551cb;hpb=28fc1cfcb352d47a17078f3348f8702f221db650;p=rtaudio-cdist.git diff --git a/RtAudio.cpp b/RtAudio.cpp index 8477ef9..0a7d9eb 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -10,7 +10,7 @@ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio: realtime audio i/o C++ classes - Copyright (c) 2001-2012 Gary P. Scavone + Copyright (c) 2001-2013 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -38,7 +38,7 @@ */ /************************************************************************/ -// RtAudio: Version 4.0.11 +// RtAudio: Version 4.0.12 #include "RtAudio.h" #include @@ -75,6 +75,11 @@ const unsigned int RtApi::SAMPLE_RATES[] = { // // *************************************************** // +std::string RtAudio :: getVersion( void ) throw() +{ + return std::string( RTAUDIO_VERSION ); +} + void RtAudio :: getCompiledApi( std::vector &apis ) throw() { apis.clear(); @@ -176,7 +181,8 @@ RtAudio :: RtAudio( RtAudio::Api api ) throw() // definition __RTAUDIO_DUMMY__ is automatically defined if no // API-specific definitions are passed to the compiler. But just in // case something weird happens, we'll print out an error message. - std::cerr << "\nRtAudio: no compiled API support found ... critical error!!\n\n"; + std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n"; + throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) ); } RtAudio :: ~RtAudio() throw() @@ -189,11 +195,12 @@ void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, - RtAudio::StreamOptions *options ) + RtAudio::StreamOptions *options, + RtAudioErrorCallback errorCallback ) { return rtapi_->openStream( outputParameters, inputParameters, format, sampleRate, bufferFrames, callback, - userData, options ); + userData, options, errorCallback ); } // *************************************************** // @@ -212,6 +219,7 @@ RtApi :: RtApi() stream_.userBuffer[1] = 0; MUTEX_INITIALIZE( &stream_.mutex ); showWarnings_ = true; + firstErrorOccurred_ = false; } RtApi :: ~RtApi() @@ -224,31 +232,37 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams, RtAudioFormat format, unsigned int sampleRate, unsigned int *bufferFrames, RtAudioCallback callback, void *userData, - RtAudio::StreamOptions *options ) + RtAudio::StreamOptions *options, + RtAudioErrorCallback errorCallback ) { if ( stream_.state != STREAM_CLOSED ) { errorText_ = "RtApi::openStream: a stream is already open!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } if ( oParams && oParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one."; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } if ( iParams && iParams->nChannels < 1 ) { errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one."; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } if ( oParams == NULL && iParams == NULL ) { errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } if ( formatBytes(format) == 0 ) { errorText_ = "RtApi::openStream: 'format' parameter value is undefined."; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } unsigned int nDevices = getDeviceCount(); @@ -257,7 +271,8 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams, oChannels = oParams->nChannels; if ( oParams->deviceId >= nDevices ) { errorText_ = "RtApi::openStream: output device parameter value is invalid."; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } } @@ -266,7 +281,8 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams, iChannels = iParams->nChannels; if ( iParams->deviceId >= nDevices ) { errorText_ = "RtApi::openStream: input device parameter value is invalid."; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return; } } @@ -277,7 +293,10 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams, result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel, sampleRate, format, bufferFrames, options ); - if ( result == false ) error( RtError::SYSTEM_ERROR ); + if ( result == false ) { + error( RtAudioError::SYSTEM_ERROR ); + return; + } } if ( iChannels > 0 ) { @@ -286,12 +305,14 @@ void RtApi :: openStream( RtAudio::StreamParameters *oParams, sampleRate, format, bufferFrames, options ); if ( result == false ) { if ( oChannels > 0 ) closeStream(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } } stream_.callbackInfo.callback = (void *) callback; stream_.callbackInfo.userData = userData; + stream_.callbackInfo.errorCallback = (void *) errorCallback; if ( options ) options->numberOfBuffers = stream_.nBuffers; stream_.state = STREAM_STOPPED; @@ -315,10 +336,10 @@ void RtApi :: closeStream( void ) return; } -bool RtApi :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels, - unsigned int firstChannel, unsigned int sampleRate, - RtAudioFormat format, unsigned int *bufferSize, - RtAudio::StreamOptions *options ) +bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/, + unsigned int /*firstChannel*/, unsigned int /*sampleRate*/, + RtAudioFormat /*format*/, unsigned int * /*bufferSize*/, + RtAudio::StreamOptions * /*options*/ ) { // MUST be implemented in subclasses! return FAILURE; @@ -423,8 +444,6 @@ struct CoreHandle { :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; } }; -ThreadHandle threadId; - RtApiCore:: RtApiCore() { #if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER ) @@ -439,7 +458,7 @@ RtApiCore:: RtApiCore() OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop); if ( result != noErr ) { errorText_ = "RtApiCore::RtApiCore: error setting run loop property!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } #endif } @@ -460,7 +479,7 @@ unsigned int RtApiCore :: getDeviceCount( void ) OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -478,7 +497,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void ) OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -488,7 +507,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void ) result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -496,7 +515,7 @@ unsigned int RtApiCore :: getDefaultInputDevice( void ) if ( id == deviceList[i] ) return i; errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -511,7 +530,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void ) OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -521,7 +540,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void ) result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -529,7 +548,7 @@ unsigned int RtApiCore :: getDefaultOutputDevice( void ) if ( id == deviceList[i] ) return i; errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -542,12 +561,14 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) unsigned int nDevices = getDeviceCount(); if ( nDevices == 0 ) { errorText_ = "RtApiCore::getDeviceInfo: no devices found!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } if ( device >= nDevices ) { errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } AudioDeviceID deviceList[ nDevices ]; @@ -559,7 +580,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) 0, NULL, &dataSize, (void *) &deviceList ); if ( result != noErr ) { errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -574,7 +595,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -592,7 +613,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != noErr ) { errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -614,7 +635,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -622,7 +643,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -631,7 +652,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -647,7 +668,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != noErr || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -655,7 +676,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) bufferList = (AudioBufferList *) malloc( dataSize ); if ( bufferList == NULL ) { errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -664,7 +685,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) free( bufferList ); errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -689,7 +710,7 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != kAudioHardwareNoError || dataSize == 0 ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -699,26 +720,45 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) if ( result != kAudioHardwareNoError ) { errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } - Float64 minimumRate = 100000000.0, maximumRate = 0.0; + // The sample rate reporting mechanism is a bit of a mystery. It + // seems that it can either return individual rates or a range of + // rates. I assume that if the min / max range values are the same, + // then that represents a single supported rate and if the min / max + // range values are different, the device supports an arbitrary + // range of values (though there might be multiple ranges, so we'll + // use the most conservative range). + Float64 minimumRate = 1.0, maximumRate = 10000000000.0; + bool haveValueRange = false; + info.sampleRates.clear(); for ( UInt32 i=0; i maximumRate ) maximumRate = rangeList[i].mMaximum; + if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) + info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum ); + else { + haveValueRange = true; + if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; + if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; + } } - info.sampleRates.clear(); - for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) - info.sampleRates.push_back( SAMPLE_RATES[k] ); + if ( haveValueRange ) { + for ( unsigned int k=0; k= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) + info.sampleRates.push_back( SAMPLE_RATES[k] ); + } } + // Sort and remove any redundant values + std::sort( info.sampleRates.begin(), info.sampleRates.end() ); + info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() ); + if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -736,13 +776,13 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device ) return info; } -OSStatus callbackHandler( AudioDeviceID inDevice, - const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, - const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, - const AudioTimeStamp* inOutputTime, - void* infoPointer ) +static OSStatus callbackHandler( AudioDeviceID inDevice, + const AudioTimeStamp* /*inNow*/, + const AudioBufferList* inInputData, + const AudioTimeStamp* /*inInputTime*/, + AudioBufferList* outOutputData, + const AudioTimeStamp* /*inOutputTime*/, + void* infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; @@ -753,10 +793,10 @@ OSStatus callbackHandler( AudioDeviceID inDevice, return kAudioHardwareNoError; } -OSStatus xrunListener( AudioObjectID inDevice, - UInt32 nAddresses, - const AudioObjectPropertyAddress properties[], - void* handlePointer ) +static OSStatus xrunListener( AudioObjectID /*inDevice*/, + UInt32 nAddresses, + const AudioObjectPropertyAddress properties[], + void* handlePointer ) { CoreHandle *handle = (CoreHandle *) handlePointer; for ( UInt32 i=0; iobject; @@ -1475,7 +1515,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return FAILURE; } @@ -1484,6 +1524,7 @@ bool RtApiCore :: callbackEvent( AudioDeviceID deviceId, // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { + ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) @@ -1821,8 +1862,7 @@ struct JackHandle { :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; } }; -ThreadHandle threadId; -void jackSilentError( const char * ) {}; +static void jackSilentError( const char * ) {}; RtApiJack :: RtApiJack() { @@ -1881,7 +1921,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status ); if ( client == 0 ) { errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -1910,7 +1950,8 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) if ( device >= nDevices ) { jack_client_close( client ); errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } // Get the current jack server sample rate. @@ -1939,7 +1980,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) if ( info.outputChannels == 0 && info.inputChannels == 0 ) { jack_client_close(client); errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -1961,7 +2002,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) return info; } -int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) +static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; @@ -1975,7 +2016,7 @@ int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer ) // server signals that it is shutting down. It is necessary to handle // it this way because the jackShutdown() function must return before // the jack_deactivate() function (in closeStream()) will return. -extern "C" void *jackCloseStream( void *ptr ) +static void *jackCloseStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; @@ -1984,7 +2025,7 @@ extern "C" void *jackCloseStream( void *ptr ) pthread_exit( NULL ); } -void jackShutdown( void *infoPointer ) +static void jackShutdown( void *infoPointer ) { CallbackInfo *info = (CallbackInfo *) infoPointer; RtApiJack *object = (RtApiJack *) info->object; @@ -1996,11 +2037,12 @@ void jackShutdown( void *infoPointer ) // other problem occurred and we should close the stream. if ( object->isStreamRunning() == false ) return; + ThreadHandle threadId; pthread_create( &threadId, NULL, jackCloseStream, info ); std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl; } -int jackXrun( void *infoPointer ) +static int jackXrun( void *infoPointer ) { JackHandle *handle = (JackHandle *) infoPointer; @@ -2028,7 +2070,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne client = jack_client_open( "RtApiJack", jackoptions, status ); if ( client == 0 ) { errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return FAILURE; } } @@ -2265,7 +2307,7 @@ void RtApiJack :: closeStream( void ) { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiJack::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -2307,7 +2349,7 @@ void RtApiJack :: startStream( void ) verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiJack::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -2373,7 +2415,7 @@ void RtApiJack :: startStream( void ) unlock: if ( result == 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiJack :: stopStream( void ) @@ -2381,7 +2423,7 @@ void RtApiJack :: stopStream( void ) verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiJack::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -2403,7 +2445,7 @@ void RtApiJack :: abortStream( void ) verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiJack::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -2418,7 +2460,7 @@ void RtApiJack :: abortStream( void ) // aborted. It is necessary to handle it this way because the // callbackEvent() function must return before the jack_deactivate() // function will return. -extern "C" void *jackStopStream( void *ptr ) +static void *jackStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiJack *object = (RtApiJack *) info->object; @@ -2432,12 +2474,12 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return FAILURE; } if ( stream_.bufferSize != nframes ) { errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return FAILURE; } @@ -2446,6 +2488,7 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) // Check if we were draining the stream and signal is finished. if ( handle->drainCounter > 3 ) { + ThreadHandle threadId; stream_.state = STREAM_STOPPING; if ( handle->internalDrain == true ) @@ -2565,11 +2608,11 @@ bool RtApiJack :: callbackEvent( unsigned long nframes ) #include "asiodrivers.h" #include -AsioDrivers drivers; -ASIOCallbacks asioCallbacks; -ASIODriverInfo driverInfo; -CallbackInfo *asioCallbackInfo; -bool asioXRun; +static AsioDrivers drivers; +static ASIOCallbacks asioCallbacks; +static ASIODriverInfo driverInfo; +static CallbackInfo *asioCallbackInfo; +static bool asioXRun; struct AsioHandle { int drainCounter; // Tracks callback counts when draining @@ -2583,8 +2626,8 @@ struct AsioHandle { // Function declarations (definitions at end of section) static const char* getAsioErrorString( ASIOError result ); -void sampleRateChanged( ASIOSampleRate sRate ); -long asioMessages( long selector, long value, void* message, double* opt ); +static void sampleRateChanged( ASIOSampleRate sRate ); +static long asioMessages( long selector, long value, void* message, double* opt ); RtApiAsio :: RtApiAsio() { @@ -2595,7 +2638,7 @@ RtApiAsio :: RtApiAsio() HRESULT hr = CoInitialize( NULL ); if ( FAILED(hr) ) { errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } coInitialized_ = true; @@ -2626,19 +2669,21 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) unsigned int nDevices = getDeviceCount(); if ( nDevices == 0 ) { errorText_ = "RtApiAsio::getDeviceInfo: no devices found!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } if ( device >= nDevices ) { errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } // If a stream is already open, we cannot probe other devices. Thus, use the saved results. if ( stream_.state != STREAM_CLOSED ) { if ( device >= devices_.size() ) { errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } return devices_[ device ]; @@ -2649,7 +2694,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -2658,7 +2703,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) if ( !drivers.loadDriver( driverName ) ) { errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -2666,7 +2711,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -2677,7 +2722,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -2704,7 +2749,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) drivers.removeCurrentDriver(); errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -2730,7 +2775,7 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) return info; } -void bufferSwitch( long index, ASIOBool processNow ) +static void bufferSwitch( long index, ASIOBool processNow ) { RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object; object->callbackEvent( index ); @@ -3064,7 +3109,7 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne if ( result != ASE_OK ) { errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; errorText_ = errorStream_.str(); - error( RtError::WARNING); // warn but don't fail + error( RtAudioError::WARNING); // warn but don't fail } else { stream_.latency[0] = outputLatency; @@ -3110,7 +3155,7 @@ void RtApiAsio :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -3153,7 +3198,7 @@ void RtApiAsio :: startStream() verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiAsio::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -3175,7 +3220,7 @@ void RtApiAsio :: startStream() stopThreadCalled = false; if ( result == ASE_OK ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: stopStream() @@ -3183,7 +3228,7 @@ void RtApiAsio :: stopStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -3204,7 +3249,7 @@ void RtApiAsio :: stopStream() } if ( result == ASE_OK ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: abortStream() @@ -3212,7 +3257,7 @@ void RtApiAsio :: abortStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -3230,7 +3275,7 @@ void RtApiAsio :: abortStream() // aborted. It is necessary to handle it this way because the // callbackEvent() function must return before the ASIOStop() // function will return. -extern "C" unsigned __stdcall asioStopStream( void *ptr ) +static unsigned __stdcall asioStopStream( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAsio *object = (RtApiAsio *) info->object; @@ -3245,7 +3290,7 @@ bool RtApiAsio :: callbackEvent( long bufferIndex ) if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS; if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return FAILURE; } @@ -3393,7 +3438,7 @@ bool RtApiAsio :: callbackEvent( long bufferIndex ) return SUCCESS; } -void sampleRateChanged( ASIOSampleRate sRate ) +static void sampleRateChanged( ASIOSampleRate sRate ) { // The ASIO documentation says that this usually only happens during // external sync. Audio processing is not stopped by the driver, @@ -3405,7 +3450,7 @@ void sampleRateChanged( ASIOSampleRate sRate ) try { object->stopStream(); } - catch ( RtError &exception ) { + catch ( RtAudioError &exception ) { std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl; return; } @@ -3413,7 +3458,7 @@ void sampleRateChanged( ASIOSampleRate sRate ) std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl; } -long asioMessages( long selector, long value, void* message, double* opt ) +static long asioMessages( long selector, long value, void* message, double* opt ) { long ret = 0; @@ -3491,7 +3536,7 @@ static const char* getAsioErrorString( ASIOError result ) const char*message; }; - static Messages m[] = + static const Messages m[] = { { ASE_NotPresent, "Hardware input or output is not present or available." }, { ASE_HWMalfunction, "Hardware is malfunctioning." }, @@ -3572,7 +3617,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, static const char* getErrorString( int code ); -extern "C" unsigned __stdcall callbackHandler( void *ptr ); +static unsigned __stdcall callbackHandler( void *ptr ); struct DsDevice { LPGUID id[2]; @@ -3584,7 +3629,10 @@ struct DsDevice { : found(false) { validId[0] = false; validId[1] = false; } }; -std::vector< DsDevice > dsDevices; +struct DsProbeData { + bool isInput; + std::vector* dsDevices; +}; RtApiDs :: RtApiDs() { @@ -3622,21 +3670,23 @@ unsigned int RtApiDs :: getDeviceCount( void ) dsDevices[i].found = false; // Query DirectSound devices. - bool isInput = false; - HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput ); + struct DsProbeData probeInfo; + probeInfo.isInput = false; + probeInfo.dsDevices = &dsDevices; + HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } // Query DirectSoundCapture devices. - isInput = true; - result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &isInput ); + probeInfo.isInput = true; + result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } // Clean out any devices that may have disappeared. @@ -3660,13 +3710,15 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) getDeviceCount(); if ( dsDevices.size() == 0 ) { errorText_ = "RtApiDs::getDeviceInfo: no devices found!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } } if ( device >= dsDevices.size() ) { errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } HRESULT result; @@ -3678,7 +3730,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto probeInput; } @@ -3688,7 +3740,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) output->Release(); errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto probeInput; } @@ -3725,7 +3777,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) if ( FAILED( result ) ) { errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -3736,7 +3788,7 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) input->Release(); errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!"; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -4321,6 +4373,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -4328,7 +4381,7 @@ void RtApiDs :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4383,7 +4436,7 @@ void RtApiDs :: startStream() verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiDs::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4431,7 +4484,7 @@ void RtApiDs :: startStream() stream_.state = STREAM_RUNNING; unlock: - if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR ); + if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); } void RtApiDs :: stopStream() @@ -4439,7 +4492,7 @@ void RtApiDs :: stopStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4528,7 +4581,7 @@ void RtApiDs :: stopStream() unlock: timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows. - if ( FAILED( result ) ) error( RtError::SYSTEM_ERROR ); + if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR ); } void RtApiDs :: abortStream() @@ -4536,7 +4589,7 @@ void RtApiDs :: abortStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiDs::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4555,7 +4608,7 @@ void RtApiDs :: callbackEvent() if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4641,26 +4694,30 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } while ( true ) { result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer ); if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break; Sleep( 1 ); @@ -4680,7 +4737,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0]; @@ -4730,7 +4788,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } // We will copy our output buffer into the region between @@ -4770,7 +4829,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } // Copy our buffer into the DS buffer @@ -4782,7 +4842,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize; handle->bufferPointer[0] = nextWritePointer; @@ -4816,7 +4877,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset @@ -4876,7 +4938,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset @@ -4889,7 +4952,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } if ( duplexPrerollBytes <= 0 ) { @@ -4909,7 +4973,8 @@ void RtApiDs :: callbackEvent() if ( FAILED( result ) ) { errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorText_ = errorStream_.str(); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } handle->bufferPointer[1] = nextReadPointer; @@ -4931,7 +4996,7 @@ void RtApiDs :: callbackEvent() // Definitions for utility functions and callbacks // specific to the DirectSound implementation. -extern "C" unsigned __stdcall callbackHandler( void *ptr ) +static unsigned __stdcall callbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiDs *object = (RtApiDs *) info->object; @@ -4947,12 +5012,12 @@ extern "C" unsigned __stdcall callbackHandler( void *ptr ) #include "tchar.h" -std::string convertTChar( LPCTSTR name ) +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, 0 ); - length = WideCharToMultiByte(CP_UTF8, 0, name, wcslen(name), &s[0], length, 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 @@ -4965,11 +5030,12 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR module, LPVOID lpContext ) { - bool *isInput = (bool *) lpContext; + struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext; + std::vector& dsDevices = *probeInfo.dsDevices; HRESULT hr; bool validDevice = false; - if ( *isInput == true ) { + if ( probeInfo.isInput == true ) { DSCCAPS caps; LPDIRECTSOUNDCAPTURE object; @@ -5001,13 +5067,14 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, // If good device, then save its name and guid. std::string name = convertTChar( description ); - if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) + //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) + if ( lpguid == NULL ) name = "Default Device"; if ( validDevice ) { for ( unsigned int i=0; i= nDevices ) { errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } foundDevice: @@ -5225,7 +5309,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_ctl_close( chandle ); if ( device >= devices_.size() ) { errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } return devices_[ device ]; @@ -5239,23 +5323,25 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca( ¶ms ); - // First try for playback + // First try for playback unless default device (which has subdev -1) stream = SND_PCM_STREAM_PLAYBACK; - snd_pcm_info_set_device( pcminfo, subdevice ); - snd_pcm_info_set_subdevice( pcminfo, 0 ); snd_pcm_info_set_stream( pcminfo, stream ); + if ( subdevice != -1 ) { + snd_pcm_info_set_device( pcminfo, subdevice ); + snd_pcm_info_set_subdevice( pcminfo, 0 ); - result = snd_ctl_pcm_info( chandle, pcminfo ); - if ( result < 0 ) { - // Device probably doesn't support playback. - goto captureProbe; + result = snd_ctl_pcm_info( chandle, pcminfo ); + if ( result < 0 ) { + // Device probably doesn't support playback. + goto captureProbe; + } } result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK ); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto captureProbe; } @@ -5265,7 +5351,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto captureProbe; } @@ -5276,30 +5362,34 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto captureProbe; } info.outputChannels = value; snd_pcm_close( phandle ); captureProbe: - // Now try for capture stream = SND_PCM_STREAM_CAPTURE; snd_pcm_info_set_stream( pcminfo, stream ); - result = snd_ctl_pcm_info( chandle, pcminfo ); - snd_ctl_close( chandle ); - if ( result < 0 ) { - // Device probably doesn't support capture. - if ( info.outputChannels == 0 ) return info; - goto probeParameters; + // Now try for capture unless default device (with subdev = -1) + if ( subdevice != -1 ) { + result = snd_ctl_pcm_info( chandle, pcminfo ); + snd_ctl_close( chandle ); + if ( result < 0 ) { + // Device probably doesn't support capture. + if ( info.outputChannels == 0 ) return info; + goto probeParameters; + } } + else + snd_ctl_close( chandle ); result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK); if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } @@ -5310,7 +5400,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } @@ -5320,7 +5410,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); if ( info.outputChannels == 0 ) return info; goto probeParameters; } @@ -5354,7 +5444,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) if ( result < 0 ) { errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -5364,7 +5454,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -5378,7 +5468,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -5406,17 +5496,20 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device ) // Check that we have at least one supported format if ( info.nativeFormats == 0 ) { + snd_pcm_close( phandle ); errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } // Get the device name char *cardname; result = snd_card_get_name( card, &cardname ); - if ( result >= 0 ) + if ( result >= 0 ) { sprintf( name, "hw:%s,%d", cardname, subdevice ); + free( cardname ); + } info.name = name; // That's all ... close the device and return @@ -5483,6 +5576,15 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne snd_card_next( &card ); } + result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK ); + if ( result == 0 ) { + if ( nDevices == device ) { + strcpy( name, "default" ); + goto foundDevice; + } + nDevices++; + } + if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!"; @@ -5863,7 +5965,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne apiInfo->synchronized = true; else { errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } } else { @@ -5881,22 +5983,21 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne pthread_attr_t attr; pthread_attr_init( &attr ); pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - struct sched_param param; + // We previously attempted to increase the audio callback priority + // to SCHED_RR here via the attributes. However, while no errors + // were reported in doing so, it did not work. So, now this is + // done in the alsaCallbackHandler function. + stream_.callbackInfo.doRealtime = true; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); int max = sched_get_priority_max( SCHED_RR ); if ( priority < min ) priority = min; else if ( priority > max ) priority = max; - param.sched_priority = priority; - pthread_attr_setschedpolicy( &attr, SCHED_RR ); - pthread_attr_setschedparam( &attr, ¶m ); + stream_.callbackInfo.priority = priority; } - else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; @@ -5934,6 +6035,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -5941,7 +6043,7 @@ void RtApiAlsa :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -5994,7 +6096,7 @@ void RtApiAlsa :: startStream() verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiAlsa::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6017,6 +6119,7 @@ void RtApiAlsa :: startStream() } if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) { + result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open state = snd_pcm_state( handle[1] ); if ( state != SND_PCM_STATE_PREPARED ) { result = snd_pcm_prepare( handle[1] ); @@ -6031,12 +6134,12 @@ void RtApiAlsa :: startStream() stream_.state = STREAM_RUNNING; unlock: - apiInfo->runnable = true; + apiInfo->runnable = false; // fixes high CPU usage when stopped pthread_cond_signal( &apiInfo->runnable_cv ); MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: stopStream() @@ -6044,7 +6147,7 @@ void RtApiAlsa :: stopStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6076,10 +6179,11 @@ void RtApiAlsa :: stopStream() } unlock: + apiInfo->runnable = false; // fixes high CPU usage when stopped MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: abortStream() @@ -6087,7 +6191,7 @@ void RtApiAlsa :: abortStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6119,7 +6223,7 @@ void RtApiAlsa :: abortStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: callbackEvent() @@ -6139,7 +6243,7 @@ void RtApiAlsa :: callbackEvent() if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6222,7 +6326,7 @@ void RtApiAlsa :: callbackEvent() errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto tryOutput; } @@ -6292,7 +6396,7 @@ void RtApiAlsa :: callbackEvent() errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto unlock; } @@ -6308,12 +6412,20 @@ void RtApiAlsa :: callbackEvent() if ( doStopStream == 1 ) this->stopStream(); } -extern "C" void *alsaCallbackHandler( void *ptr ) +static void *alsaCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiAlsa *object = (RtApiAlsa *) info->object; bool *isRunning = &info->isRunning; +#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) + if ( &info->doRealtime ) { + pthread_t tID = pthread_self(); // ID of this thread + sched_param prio = { info->priority }; // scheduling priority of thread + pthread_setschedparam( tID, SCHED_RR, &prio ); + } +#endif + while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent(); @@ -6334,9 +6446,8 @@ extern "C" void *alsaCallbackHandler( void *ptr ) #include #include -namespace { -const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, - 44100, 48000, 96000, 0}; } +static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000, + 44100, 48000, 96000, 0}; struct rtaudio_pa_format_mapping_t { RtAudioFormat rtaudio_format; @@ -6388,7 +6499,7 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int device ) return info; } -extern "C" void *pulseaudio_callback( void * user ) +static void *pulseaudio_callback( void * user ) { CallbackInfo *cbi = static_cast( user ); RtApiPulse *context = static_cast( cbi->object ); @@ -6460,14 +6571,14 @@ void RtApiPulse::callbackEvent( void ) if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... " "this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback; double streamTime = getStreamTime(); RtAudioStreamStatus status = 0; - int doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1], + int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT], stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData ); @@ -6477,50 +6588,52 @@ void RtApiPulse::callbackEvent( void ) } MUTEX_LOCK( &stream_.mutex ); + void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT]; + void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT]; if ( stream_.state != STREAM_RUNNING ) goto unlock; int pa_error; size_t bytes; - switch ( stream_.mode ) { - case INPUT: - bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) { - errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); - } - break; - case OUTPUT: - bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0 ) { + if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { + if ( stream_.doConvertBuffer[OUTPUT] ) { + convertBuffer( stream_.deviceBuffer, + stream_.userBuffer[OUTPUT], + stream_.convertInfo[OUTPUT] ); + bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize * + formatBytes( stream_.deviceFormat[OUTPUT] ); + } else + bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize * + formatBytes( stream_.userFormat ); + + if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } - break; - case DUPLEX: - bytes = stream_.nUserChannels[1] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_read( pah->s_rec, stream_.userBuffer[1], bytes, &pa_error ) < 0 ) { + } + + if ( stream_.mode == INPUT || stream_.mode == DUPLEX) { + if ( stream_.doConvertBuffer[INPUT] ) + bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize * + formatBytes( stream_.deviceFormat[INPUT] ); + else + bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize * + formatBytes( stream_.userFormat ); + + if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) { errorStream_ << "RtApiPulse::callbackEvent: audio read error, " << pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } - bytes = stream_.nUserChannels[0] * stream_.bufferSize * formatBytes( stream_.userFormat ); - if ( pa_simple_write( pah->s_play, stream_.userBuffer[0], bytes, &pa_error ) < 0) { - errorStream_ << "RtApiPulse::callbackEvent: audio write error, " << - pa_strerror( pa_error ) << "."; - errorText_ = errorStream_.str(); - error( RtError::WARNING ); + if ( stream_.doConvertBuffer[INPUT] ) { + convertBuffer( stream_.userBuffer[INPUT], + stream_.deviceBuffer, + stream_.convertInfo[INPUT] ); } - break; - default: - // ERROR - break; } unlock: @@ -6537,12 +6650,12 @@ void RtApiPulse::startStream( void ) if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::startStream(): the stream is not open!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiPulse::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6561,12 +6674,12 @@ void RtApiPulse::stopStream( void ) if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::stopStream(): the stream is not open!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6580,7 +6693,8 @@ void RtApiPulse::stopStream( void ) pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } } @@ -6594,12 +6708,12 @@ void RtApiPulse::abortStream( void ) if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiPulse::abortStream(): the stream is not open!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); return; } if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -6613,7 +6727,8 @@ void RtApiPulse::abortStream( void ) pa_strerror( pa_error ) << "."; errorText_ = errorStream_.str(); MUTEX_UNLOCK( &stream_.mutex ); - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); + return; } } @@ -6669,20 +6784,16 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, return false; } - if ( options && ( options->flags & RTAUDIO_NONINTERLEAVED ) ) { - errorText_ = "RtApiPulse::probeDeviceOpen: only interleaved audio data supported."; - return false; - } - - stream_.userInterleaved = true; - stream_.nBuffers = 1; - + // Set interleaving parameters. + if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false; + else stream_.userInterleaved = true; stream_.deviceInterleaved[mode] = true; + stream_.nBuffers = 1; stream_.doByteSwap[mode] = false; - stream_.doConvertBuffer[mode] = false; + stream_.doConvertBuffer[mode] = channels > 1 && !stream_.userInterleaved; stream_.deviceFormat[mode] = stream_.userFormat; stream_.nUserChannels[mode] = channels; - stream_.nDeviceChannels[mode] = channels; + stream_.nDeviceChannels[mode] = channels + firstChannel; stream_.channelOffset[mode] = 0; // Allocate necessary internal buffers. @@ -6694,6 +6805,33 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, } stream_.bufferSize = *bufferSize; + if ( stream_.doConvertBuffer[mode] ) { + + bool makeBuffer = true; + bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); + if ( mode == INPUT ) { + if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { + unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); + if ( bufferBytes <= bytesOut ) makeBuffer = false; + } + } + + if ( makeBuffer ) { + bufferBytes *= *bufferSize; + if ( stream_.deviceBuffer ) free( stream_.deviceBuffer ); + stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 ); + if ( stream_.deviceBuffer == NULL ) { + errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory."; + goto error; + } + } + } + + stream_.device[mode] = device; + + // Setup the buffer conversion information structure. + if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel ); + if ( !stream_.apiHandle ) { PulseAudioHandle *pah = new PulseAudioHandle; if ( !pah ) { @@ -6710,9 +6848,15 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, pah = static_cast( stream_.apiHandle ); int error; + std::string streamName = "RtAudio"; + if ( !options->streamName.empty() ) streamName = options->streamName; switch ( mode ) { case INPUT: - pah->s_rec = pa_simple_new( NULL, "RtAudio", PA_STREAM_RECORD, NULL, "Record", &ss, NULL, NULL, &error ); + pa_buffer_attr buffer_attr; + buffer_attr.fragsize = bufferBytes; + buffer_attr.maxlength = -1; + + pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error ); if ( !pah->s_rec ) { errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server."; goto error; @@ -6736,8 +6880,6 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, else stream_.mode = DUPLEX; - stream_.state = STREAM_STOPPED; - if ( !stream_.callbackInfo.isRunning ) { stream_.callbackInfo.object = this; stream_.callbackInfo.isRunning = true; @@ -6746,11 +6888,30 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, goto error; } } + + stream_.state = STREAM_STOPPED; return true; error: - closeStream(); - return false; + if ( pah && stream_.callbackInfo.isRunning ) { + pthread_cond_destroy( &pah->runnable_cv ); + delete pah; + stream_.apiHandle = 0; + } + + for ( int i=0; i<2; i++ ) { + if ( stream_.userBuffer[i] ) { + free( stream_.userBuffer[i] ); + stream_.userBuffer[i] = 0; + } + } + + if ( stream_.deviceBuffer ) { + free( stream_.deviceBuffer ); + stream_.deviceBuffer = 0; + } + + return FAILURE; } //******************** End of __LINUX_PULSE__ *********************// @@ -6762,11 +6923,11 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, #include #include #include -#include "soundcard.h" +#include #include #include -extern "C" void *ossCallbackHandler(void * ptr); +static void *ossCallbackHandler(void * ptr); // A structure to hold various information related to the OSS API // implementation. @@ -6795,7 +6956,7 @@ unsigned int RtApiOss :: getDeviceCount( void ) int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -6803,7 +6964,7 @@ unsigned int RtApiOss :: getDeviceCount( void ) if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -6819,7 +6980,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) int mixerfd = open( "/dev/mixer", O_RDWR, 0 ); if ( mixerfd == -1 ) { errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -6828,7 +6989,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) if ( result == -1 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -6836,13 +6997,15 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) if ( nDevices == 0 ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: no devices found!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } if ( device >= nDevices ) { close( mixerfd ); errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); + return info; } oss_audioinfo ainfo; @@ -6852,7 +7015,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) if ( result == -1 ) { errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -6881,7 +7044,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) if ( info.nativeFormats == 0 ) { errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return info; } @@ -6908,7 +7071,7 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device ) if ( info.sampleRates.size() == 0 ) { errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ")."; errorText_ = errorStream_.str(); - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } else { info.probed = true; @@ -7357,7 +7520,7 @@ void RtApiOss :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7406,7 +7569,7 @@ void RtApiOss :: startStream() verifyStream(); if ( stream_.state == STREAM_RUNNING ) { errorText_ = "RtApiOss::startStream(): the stream is already running!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7428,7 +7591,7 @@ void RtApiOss :: stopStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiOss::stopStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7465,7 +7628,7 @@ void RtApiOss :: stopStream() result = write( handle->id[0], buffer, samples * formatBytes(format) ); if ( result == -1 ) { errorText_ = "RtApiOss::stopStream: audio write error."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); } } @@ -7492,7 +7655,7 @@ void RtApiOss :: stopStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: abortStream() @@ -7500,7 +7663,7 @@ void RtApiOss :: abortStream() verifyStream(); if ( stream_.state == STREAM_STOPPED ) { errorText_ = "RtApiOss::abortStream(): the stream is already stopped!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7538,7 +7701,7 @@ void RtApiOss :: abortStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: callbackEvent() @@ -7556,7 +7719,7 @@ void RtApiOss :: callbackEvent() if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7626,7 +7789,7 @@ void RtApiOss :: callbackEvent() // specific means for determining that. handle->xrun[0] = true; errorText_ = "RtApiOss::callbackEvent: audio write error."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); // Continue on to input section. } } @@ -7653,7 +7816,7 @@ void RtApiOss :: callbackEvent() // specific means for determining that. handle->xrun[1] = true; errorText_ = "RtApiOss::callbackEvent: audio read error."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto unlock; } @@ -7673,7 +7836,7 @@ void RtApiOss :: callbackEvent() if ( doStopStream == 1 ) this->stopStream(); } -extern "C" void *ossCallbackHandler( void *ptr ) +static void *ossCallbackHandler( void *ptr ) { CallbackInfo *info = (CallbackInfo *) ptr; RtApiOss *object = (RtApiOss *) info->object; @@ -7699,20 +7862,41 @@ extern "C" void *ossCallbackHandler( void *ptr ) // This method can be modified to control the behavior of error // message printing. -void RtApi :: error( RtError::Type type ) +void RtApi :: error( RtAudioError::Type type ) { errorStream_.str(""); // clear the ostringstream - if ( type == RtError::WARNING && showWarnings_ == true ) + + RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback; + if ( errorCallback ) { + // abortStream() can generate new error messages. Ignore them. Just keep original one. + + if ( firstErrorOccurred_ ) + return; + + firstErrorOccurred_ = true; + const std::string errorMessage = errorText_; + + if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) { + stream_.callbackInfo.isRunning = false; // exit from the thread + abortStream(); + } + + errorCallback( type, errorMessage ); + firstErrorOccurred_ = false; + return; + } + + if ( type == RtAudioError::WARNING && showWarnings_ == true ) std::cerr << '\n' << errorText_ << "\n\n"; - else if ( type != RtError::WARNING ) - throw( RtError( errorText_, type ) ); + else if ( type != RtAudioError::WARNING ) + throw( RtAudioError( errorText_, type ) ); } void RtApi :: verifyStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApi:: a stream is not open!"; - error( RtError::INVALID_USE ); + error( RtAudioError::INVALID_USE ); } } @@ -7731,6 +7915,7 @@ void RtApi :: clearStreamInfo() stream_.callbackInfo.callback = 0; stream_.callbackInfo.userData = 0; stream_.callbackInfo.isRunning = false; + stream_.callbackInfo.errorCallback = 0; for ( int i=0; i<2; i++ ) { stream_.device[i] = 11111; stream_.doConvertBuffer[i] = false; @@ -7766,7 +7951,7 @@ unsigned int RtApi :: formatBytes( RtAudioFormat format ) return 1; errorText_ = "RtApi::formatBytes: undefined format."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; }