X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=RtAudio.cpp;h=b7fa8e8367813dc71b0c5b05965e14746fe2b6cc;hb=c8872791415514880108c2e46f0c0bb2b7236acf;hp=111039404af9339ab508a4efcfda6458788c6d33;hpb=3b1331c16ed513ec92f41d13692bcbe1792c3d1a;p=rtaudio-cdist.git diff --git a/RtAudio.cpp b/RtAudio.cpp index 1110394..b7fa8e8 100644 --- a/RtAudio.cpp +++ b/RtAudio.cpp @@ -107,12 +107,12 @@ void RtAudio :: getCompiledApi( std::vector &apis ) #if defined(__UNIX_JACK__) apis.push_back( UNIX_JACK ); #endif -#if defined(__LINUX_ALSA__) - apis.push_back( LINUX_ALSA ); -#endif #if defined(__LINUX_PULSE__) apis.push_back( LINUX_PULSE ); #endif +#if defined(__LINUX_ALSA__) + apis.push_back( LINUX_ALSA ); +#endif #if defined(__LINUX_OSS__) apis.push_back( LINUX_OSS ); #endif @@ -1973,7 +1973,7 @@ unsigned int RtApiJack :: getDeviceCount( void ) const char **ports; std::string port, previousPort; unsigned int nChannels = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, NULL, 0 ); + ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; @@ -2012,7 +2012,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) const char **ports; std::string port, previousPort; unsigned int nPorts = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, NULL, 0 ); + ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; @@ -2047,7 +2047,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) // Count the available ports containing the client name as device // channels. Jack "input ports" equal RtAudio output channels. unsigned int nChannels = 0; - ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsInput ); + ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); @@ -2056,7 +2056,7 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device ) // Jack "output ports" equal RtAudio input channels. nChannels = 0; - ports = jack_get_ports( client, info.name.c_str(), NULL, JackPortIsOutput ); + ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput ); if ( ports ) { while ( ports[ nChannels ] ) nChannels++; free( ports ); @@ -2130,7 +2130,7 @@ static void jackShutdown( void *infoPointer ) static int jackXrun( void *infoPointer ) { - JackHandle *handle = (JackHandle *) infoPointer; + JackHandle *handle = *((JackHandle **) infoPointer); if ( handle->ports[0] ) handle->xrun[0] = true; if ( handle->ports[1] ) handle->xrun[1] = true; @@ -2168,7 +2168,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne const char **ports; std::string port, previousPort, deviceName; unsigned int nPorts = 0, nDevices = 0; - ports = jack_get_ports( client, NULL, NULL, 0 ); + ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 ); if ( ports ) { // Parse the port names up to the first colon (:). size_t iColon = 0; @@ -2192,22 +2192,24 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne return FAILURE; } - // Count the available ports containing the client name as device - // channels. Jack "input ports" equal RtAudio output channels. - unsigned int nChannels = 0; unsigned long flag = JackPortIsInput; if ( mode == INPUT ) flag = JackPortIsOutput; - ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); - if ( ports ) { - while ( ports[ nChannels ] ) nChannels++; - free( ports ); - } - // Compare the jack ports for specified client to the requested number of channels. - if ( nChannels < (channels + firstChannel) ) { - errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ")."; - errorText_ = errorStream_.str(); - return FAILURE; + if ( ! (options && (options->flags & RTAUDIO_JACK_DONT_CONNECT)) ) { + // Count the available ports containing the client name as device + // channels. Jack "input ports" equal RtAudio output channels. + unsigned int nChannels = 0; + ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); + if ( ports ) { + while ( ports[ nChannels ] ) nChannels++; + free( ports ); + } + // Compare the jack ports for specified client to the requested number of channels. + if ( nChannels < (channels + firstChannel) ) { + errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ")."; + errorText_ = errorStream_.str(); + return FAILURE; + } } // Check the jack server sample rate. @@ -2221,7 +2223,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne stream_.sampleRate = jackRate; // Get the latency of the JACK port. - ports = jack_get_ports( client, deviceName.c_str(), NULL, flag ); + ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag ); if ( ports[ firstChannel ] ) { // Added by Ge Wang jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency); @@ -2334,7 +2336,7 @@ bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne else { stream_.mode = mode; jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo ); - jack_set_xrun_callback( handle->client, jackXrun, (void *) &handle ); + jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle ); jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo ); } @@ -2453,7 +2455,7 @@ void RtApiJack :: startStream( void ) // Get the list of available ports. if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) { result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), NULL, JackPortIsInput); + ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!"; goto unlock; @@ -2477,7 +2479,7 @@ void RtApiJack :: startStream( void ) if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) { result = 1; - ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), NULL, JackPortIsOutput ); + ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput ); if ( ports == NULL) { errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!"; goto unlock; @@ -3163,8 +3165,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); if ( result != ASE_OK ) { // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges - // but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver - // in that case, let's be naïve and try that instead + // but only accept the preferred buffer size as parameter for ASIOCreateBuffers (e.g. Creative's ASIO driver). + // In that case, let's be naïve and try that instead. *bufferSize = preferSize; stream_.bufferSize = *bufferSize; result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); @@ -5121,6 +5123,8 @@ Exit: // Various revisions for RtAudio 4.0 by Gary Scavone, April 2007 // Changed device query structure for RtAudio 4.0.7, January 2010 +#include +#include #include #include #include @@ -7560,30 +7564,41 @@ 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) +#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { - // 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; + struct sched_param param; 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; - stream_.callbackInfo.priority = priority; + param.sched_priority = priority; + + // Set the policy BEFORE the priority. Otherwise it fails. + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); + // This is definitely required. Otherwise it fails. + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedparam(&attr, ¶m); } + else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); +#else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); #endif stream_.callbackInfo.isRunning = true; result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { - stream_.callbackInfo.isRunning = false; - errorText_ = "RtApiAlsa::error creating callback thread!"; - goto error; + // Failed. Try instead with default attributes. + result = pthread_create( &stream_.callbackInfo.thread, NULL, alsaCallbackHandler, &stream_.callbackInfo ); + if ( result ) { + stream_.callbackInfo.isRunning = false; + errorText_ = "RtApiAlsa::error creating callback thread!"; + goto error; + } } } @@ -7998,11 +8013,11 @@ static void *alsaCallbackHandler( void *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) +#ifdef SCHED_RR // Undefined with some OSes (e.g. 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 ); + std::cerr << "RtAudio alsa: " << + (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << + "running realtime scheduling" << std::endl; } #endif @@ -8085,7 +8100,15 @@ static void *pulseaudio_callback( void * user ) CallbackInfo *cbi = static_cast( user ); RtApiPulse *context = static_cast( cbi->object ); volatile bool *isRunning = &cbi->isRunning; - + +#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) + if (cbi->doRealtime) { + std::cerr << "RtAudio pulse: " << + (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << + "running realtime scheduling" << std::endl; + } +#endif + while ( *isRunning ) { pthread_testcancel(); context->callbackEvent(); @@ -8470,15 +8493,56 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, if ( !stream_.callbackInfo.isRunning ) { stream_.callbackInfo.object = this; + + stream_.state = STREAM_STOPPED; + // Set the thread attributes for joinable and realtime scheduling + // priority (optional). The higher priority will only take affect + // if the program is run as root or suid. Note, under Linux + // processes with CAP_SYS_NICE privilege, a user can change + // scheduling policy and priority (thus need not be root). See + // POSIX "capabilities". + pthread_attr_t attr; + pthread_attr_init( &attr ); + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); +#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) + if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + stream_.callbackInfo.doRealtime = true; + struct sched_param param; + 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; + + // Set the policy BEFORE the priority. Otherwise it fails. + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); + // This is definitely required. Otherwise it fails. + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedparam(&attr, ¶m); + } + else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); +#else + pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); +#endif + stream_.callbackInfo.isRunning = true; - if ( pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo) != 0 ) { - errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; - goto error; + int result = pthread_create( &pah->thread, &attr, pulseaudio_callback, (void *)&stream_.callbackInfo); + pthread_attr_destroy(&attr); + if(result != 0) { + // Failed. Try instead with default attributes. + result = pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo); + if(result != 0) { + stream_.callbackInfo.isRunning = false; + errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread."; + goto error; + } } } - stream_.state = STREAM_STOPPED; - return true; + return SUCCESS; error: if ( pah && stream_.callbackInfo.isRunning ) { @@ -8499,6 +8563,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode, stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -9060,8 +9125,9 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned 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) +#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) { + stream_.callbackInfo.doRealtime = true; struct sched_param param; int priority = options->priority; int min = sched_get_priority_min( SCHED_RR ); @@ -9069,8 +9135,13 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned 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 ); + + // Set the policy BEFORE the priority. Otherwise it fails. + pthread_attr_setschedpolicy(&attr, SCHED_RR); + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); + // This is definitely required. Otherwise it fails. + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedparam(&attr, ¶m); } else pthread_attr_setschedpolicy( &attr, SCHED_OTHER ); @@ -9082,9 +9153,13 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo ); pthread_attr_destroy( &attr ); if ( result ) { - stream_.callbackInfo.isRunning = false; - errorText_ = "RtApiOss::error creating callback thread!"; - goto error; + // Failed. Try instead with default attributes. + result = pthread_create( &stream_.callbackInfo.thread, NULL, ossCallbackHandler, &stream_.callbackInfo ); + if ( result ) { + stream_.callbackInfo.isRunning = false; + errorText_ = "RtApiOss::error creating callback thread!"; + goto error; + } } } @@ -9111,6 +9186,7 @@ bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned stream_.deviceBuffer = 0; } + stream_.state = STREAM_CLOSED; return FAILURE; } @@ -9440,6 +9516,14 @@ static void *ossCallbackHandler( void *ptr ) RtApiOss *object = (RtApiOss *) info->object; bool *isRunning = &info->isRunning; +#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread) + if (info->doRealtime) { + std::cerr << "RtAudio oss: " << + (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") << + "running realtime scheduling" << std::endl; + } +#endif + while ( *isRunning == true ) { pthread_testcancel(); object->callbackEvent();