X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=RtAudio.cpp;h=efed868e185b26d8db4eb279102e5a5d4ec7e5c0;hb=12516be654cafb0f681174d1fd99b5f61e254307;hp=823faafc476436a9275206f2181469e3f5b9ad6a;hpb=5d8514d7eb3918a947ec97b45f4105630c64468d;p=rtaudio-cdist.git diff --git a/RtAudio.cpp b/RtAudio.cpp index 823faaf..efed868 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 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,14 +595,18 @@ 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; } //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); int length = CFStringGetLength(cfname); char *mname = (char *)malloc(length * 3 + 1); +#if defined( UNICODE ) || defined( _UNICODE ) + CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8); +#else CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding()); +#endif info.name.append( (const char *)mname, strlen(mname) ); info.name.append( ": " ); CFRelease( cfname ); @@ -592,14 +617,18 @@ 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; } //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() ); length = CFStringGetLength(cfname); char *name = (char *)malloc(length * 3 + 1); +#if defined( UNICODE ) || defined( _UNICODE ) + CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8); +#else CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding()); +#endif info.name.append( (const char *)name, strlen(name) ); CFRelease( cfname ); free(name); @@ -614,7 +643,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 +651,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 +660,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 +676,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 +684,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 +693,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 +718,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 +728,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 +784,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 +801,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 +1524,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 +1533,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 +1871,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 +1930,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 +1959,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 +1989,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 +2011,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 +2025,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 +2034,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 +2046,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 +2079,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; } } @@ -2094,8 +2145,17 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne // Get the latency of the JACK port. ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); - if ( ports[ firstChannel ] ) - stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) ); + if ( ports[ firstChannel ] ) { + // Added by Ge Wang + jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); + // the range (usually the min and max are equal) + jack_latency_range_t latrange; latrange.min = latrange.max = 0; + // get the latency range + jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange ); + // be optimistic, use the min! + stream_.latency[mode] = latrange.min; + //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) ); + } free( ports ); // The jack server always uses 32-bit floating-point data. @@ -2256,7 +2316,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; } @@ -2298,7 +2358,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; } @@ -2364,7 +2424,7 @@ void RtApiJack :: startStream( void ) unlock: if ( result == 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiJack :: stopStream( void ) @@ -2372,7 +2432,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; } @@ -2394,7 +2454,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; } @@ -2409,7 +2469,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; @@ -2423,12 +2483,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; } @@ -2437,6 +2497,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 ) @@ -2556,11 +2617,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 @@ -2574,8 +2635,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() { @@ -2586,7 +2647,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; @@ -2617,19 +2678,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 ]; @@ -2640,7 +2703,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; } @@ -2649,7 +2712,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; } @@ -2657,7 +2720,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; } @@ -2668,7 +2731,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; } @@ -2695,7 +2758,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; } @@ -2708,6 +2771,8 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device ) info.nativeFormats |= RTAUDIO_FLOAT32; else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) info.nativeFormats |= RTAUDIO_FLOAT64; + else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) + info.nativeFormats |= RTAUDIO_SINT24; if ( info.outputChannels > 0 ) if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true; @@ -2719,7 +2784,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 ); @@ -2860,6 +2925,10 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.deviceFormat[mode] = RTAUDIO_FLOAT64; if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true; } + else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) { + stream_.deviceFormat[mode] = RTAUDIO_SINT24; + if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true; + } if ( stream_.deviceFormat[mode] == 0 ) { drivers.removeCurrentDriver(); @@ -3049,7 +3118,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; @@ -3095,7 +3164,7 @@ void RtApiAsio :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAsio::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -3138,7 +3207,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; } @@ -3160,7 +3229,7 @@ void RtApiAsio :: startStream() stopThreadCalled = false; if ( result == ASE_OK ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: stopStream() @@ -3168,7 +3237,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; } @@ -3189,7 +3258,7 @@ void RtApiAsio :: stopStream() } if ( result == ASE_OK ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAsio :: abortStream() @@ -3197,7 +3266,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; } @@ -3215,7 +3284,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; @@ -3230,7 +3299,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; } @@ -3378,7 +3447,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, @@ -3390,7 +3459,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; } @@ -3398,7 +3467,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; @@ -3476,7 +3545,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." }, @@ -3557,7 +3626,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]; @@ -3569,7 +3638,10 @@ struct DsDevice { : found(false) { validId[0] = false; validId[1] = false; } }; -std::vector< DsDevice > dsDevices; +struct DsProbeData { + bool isInput; + std::vector* dsDevices; +}; RtApiDs :: RtApiDs() { @@ -3607,21 +3679,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. @@ -3632,7 +3706,7 @@ unsigned int RtApiDs :: getDeviceCount( void ) for ( unsigned int i=0; i(dsDevices.size()); } RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device ) @@ -3645,13 +3719,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; @@ -3663,7 +3739,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; } @@ -3673,7 +3749,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; } @@ -3710,7 +3786,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; } @@ -3721,7 +3797,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; } @@ -3818,7 +3894,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned return FAILURE; } - unsigned int nDevices = dsDevices.size(); + size_t nDevices = dsDevices.size(); if ( nDevices == 0 ) { // This should not happen because a check is made before this function is called. errorText_ = "RtApiDs::probeDeviceOpen: no devices found!"; @@ -3879,7 +3955,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned // Determine the device buffer size. By default, we'll use the value // defined above (32K), but we will grow it to make allowances for // very large software buffer sizes. - DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;; + DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE; DWORD dsPointerLeadTime = 0; void *ohandle = 0, *bhandle = 0; @@ -4306,6 +4382,7 @@ bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -4313,7 +4390,7 @@ void RtApiDs :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiDs::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -4368,7 +4445,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; } @@ -4416,7 +4493,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() @@ -4424,7 +4501,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; } @@ -4513,7 +4590,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() @@ -4521,7 +4598,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; } @@ -4540,7 +4617,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; } @@ -4626,26 +4703,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 ); @@ -4665,7 +4746,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]; @@ -4715,7 +4797,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 @@ -4755,7 +4838,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 @@ -4767,7 +4851,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; @@ -4801,7 +4886,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 @@ -4861,7 +4947,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 @@ -4874,7 +4961,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 ) { @@ -4894,7 +4982,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; @@ -4916,7 +5005,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; @@ -4932,12 +5021,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 @@ -4947,14 +5036,15 @@ std::string convertTChar( LPCTSTR name ) static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, LPCTSTR description, - LPCTSTR module, + 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; @@ -4986,13 +5076,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: @@ -5210,7 +5318,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 ]; @@ -5224,23 +5332,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; } @@ -5250,7 +5360,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; } @@ -5261,30 +5371,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; } @@ -5295,7 +5409,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; } @@ -5305,7 +5419,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; } @@ -5339,7 +5453,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; } @@ -5349,7 +5463,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; } @@ -5363,7 +5477,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; } @@ -5391,17 +5505,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 @@ -5468,6 +5585,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!"; @@ -5848,7 +5974,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 { @@ -5866,22 +5992,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_setschedparam( &attr, ¶m ); - pthread_attr_setschedpolicy( &attr, SCHED_RR ); + stream_.callbackInfo.priority = priority; } - else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); -#else - pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; @@ -5919,6 +6044,7 @@ bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -5926,7 +6052,7 @@ void RtApiAlsa :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiAlsa::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -5979,7 +6105,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; } @@ -6002,6 +6128,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] ); @@ -6016,12 +6143,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() @@ -6029,7 +6156,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; } @@ -6061,10 +6188,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() @@ -6072,7 +6200,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; } @@ -6104,7 +6232,7 @@ void RtApiAlsa :: abortStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result >= 0 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiAlsa :: callbackEvent() @@ -6124,7 +6252,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; } @@ -6207,7 +6335,7 @@ void RtApiAlsa :: callbackEvent() errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto tryOutput; } @@ -6277,7 +6405,7 @@ void RtApiAlsa :: callbackEvent() errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << "."; errorText_ = errorStream_.str(); } - error( RtError::WARNING ); + error( RtAudioError::WARNING ); goto unlock; } @@ -6293,12 +6421,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(); @@ -6319,9 +6455,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; @@ -6373,7 +6508,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 ); @@ -6445,14 +6580,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 ); @@ -6462,50 +6597,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: @@ -6522,12 +6659,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; } @@ -6546,12 +6683,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; } @@ -6565,7 +6702,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; } } @@ -6579,12 +6717,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; } @@ -6598,7 +6736,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; } } @@ -6654,20 +6793,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. @@ -6679,6 +6814,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 ) { @@ -6695,9 +6857,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; @@ -6721,8 +6889,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; @@ -6731,11 +6897,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__ *********************// @@ -6747,11 +6932,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. @@ -6780,7 +6965,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; } @@ -6788,7 +6973,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; } @@ -6804,7 +6989,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; } @@ -6813,7 +6998,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; } @@ -6821,13 +7006,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; @@ -6837,7 +7024,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; } @@ -6866,7 +7053,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; } @@ -6893,7 +7080,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; @@ -7342,7 +7529,7 @@ void RtApiOss :: closeStream() { if ( stream_.state == STREAM_CLOSED ) { errorText_ = "RtApiOss::closeStream(): no open stream to close!"; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return; } @@ -7391,7 +7578,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; } @@ -7413,7 +7600,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; } @@ -7450,7 +7637,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 ); } } @@ -7477,7 +7664,7 @@ void RtApiOss :: stopStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: abortStream() @@ -7485,7 +7672,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; } @@ -7523,7 +7710,7 @@ void RtApiOss :: abortStream() MUTEX_UNLOCK( &stream_.mutex ); if ( result != -1 ) return; - error( RtError::SYSTEM_ERROR ); + error( RtAudioError::SYSTEM_ERROR ); } void RtApiOss :: callbackEvent() @@ -7541,7 +7728,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; } @@ -7611,7 +7798,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. } } @@ -7638,7 +7825,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; } @@ -7658,7 +7845,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; @@ -7684,20 +7871,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 ); } } @@ -7716,6 +7924,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; @@ -7741,16 +7950,17 @@ unsigned int RtApi :: formatBytes( RtAudioFormat format ) { if ( format == RTAUDIO_SINT16 ) return 2; - else if ( format == RTAUDIO_SINT24 || format == RTAUDIO_SINT32 || - format == RTAUDIO_FLOAT32 ) + else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 ) return 4; else if ( format == RTAUDIO_FLOAT64 ) return 8; + else if ( format == RTAUDIO_SINT24 ) + return 3; else if ( format == RTAUDIO_SINT8 ) return 1; errorText_ = "RtApi::formatBytes: undefined format."; - error( RtError::WARNING ); + error( RtAudioError::WARNING ); return 0; } @@ -7878,11 +8088,11 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info } } else if (info.inFormat == RTAUDIO_SINT24) { - Int32 *in = (Int32 *)inBuffer; + Int24 *in = (Int24 *)inBuffer; scale = 1.0 / 8388607.5; for (unsigned int i=0; i>= 8; + out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8); + //out[info.outOffset[j]] >>= 8; } in += info.inJump; out += info.outJump; @@ -8162,10 +8372,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info } } else if (info.inFormat == RTAUDIO_SINT24) { - Int32 *in = (Int32 *)inBuffer; + Int24 *in = (Int24 *)inBuffer; for (unsigned int i=0; i> 8) & 0x0000ffff); + out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8); } in += info.inJump; out += info.outJump; @@ -8226,10 +8436,10 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info } } else if (info.inFormat == RTAUDIO_SINT24) { - Int32 *in = (Int32 *)inBuffer; + Int24 *in = (Int24 *)inBuffer; for (unsigned int i=0; i> 16) & 0x000000ff); + out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16); } in += info.inJump; out += info.outJump; @@ -8268,9 +8478,9 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info } } - //static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); } - //static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } - //static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } +//static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); } +//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); } +//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); } void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) { @@ -8289,8 +8499,7 @@ void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat ptr += 2; } } - else if ( format == RTAUDIO_SINT24 || - format == RTAUDIO_SINT32 || + else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 ) { for ( unsigned int i=0; i