diff options
Diffstat (limited to '3rdparty/portaudio/src/hostapi/jack/pa_jack.c')
-rw-r--r-- | 3rdparty/portaudio/src/hostapi/jack/pa_jack.c | 147 |
1 files changed, 100 insertions, 47 deletions
diff --git a/3rdparty/portaudio/src/hostapi/jack/pa_jack.c b/3rdparty/portaudio/src/hostapi/jack/pa_jack.c index a800f8e1dd9..5c20b4420d0 100644 --- a/3rdparty/portaudio/src/hostapi/jack/pa_jack.c +++ b/3rdparty/portaudio/src/hostapi/jack/pa_jack.c @@ -53,10 +53,10 @@ #include <stdio.h> #include <assert.h> #include <sys/types.h> -#include <unistd.h> #include <errno.h> /* EBUSY */ #include <signal.h> /* sig_atomic_t */ #include <math.h> +#include <pthread.h> #include <semaphore.h> #include <jack/types.h> @@ -71,12 +71,12 @@ #include "pa_ringbuffer.h" #include "pa_debugprint.h" +#include "pa_jack.h" + static pthread_t mainThread_; static char *jackErr_ = NULL; static const char* clientName_ = "PortAudio"; - -#define STRINGIZE_HELPER(expr) #expr -#define STRINGIZE(expr) STRINGIZE_HELPER(expr) +static const char* port_regex_suffix = ":.*"; /* Check PaError */ #define ENSURE_PA(expr) \ @@ -84,13 +84,13 @@ static const char* clientName_ = "PortAudio"; PaError paErr; \ if( (paErr = (expr)) < paNoError ) \ { \ - if( (paErr) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ + if( (paErr) == paUnanticipatedHostError && pthread_equal( pthread_self(), mainThread_ ) ) \ { \ const char *err = jackErr_; \ if (! err ) err = "unknown error"; \ PaUtil_SetLastHostErrorInfo( paJACK, -1, err ); \ } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( __LINE__ ) "\n" )); \ result = paErr; \ goto error; \ } \ @@ -100,13 +100,13 @@ static const char* clientName_ = "PortAudio"; do { \ if( (expr) == 0 ) \ { \ - if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \ + if( (code) == paUnanticipatedHostError && pthread_equal( pthread_self(), mainThread_ ) ) \ { \ const char *err = jackErr_; \ if (!err) err = "unknown error"; \ PaUtil_SetLastHostErrorInfo( paJACK, -1, err ); \ } \ - PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \ + PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " PA_STRINGIZE( __LINE__ ) "\n" )); \ result = (code); \ goto error; \ } \ @@ -275,10 +275,10 @@ static PaError BlockingTermFIFO( PaUtilRingBuffer *rbuf ) static int BlockingCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo* timeInfo, - PaStreamCallbackFlags statusFlags, - void *userData ) + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) { struct PaJackStream *stream = (PaJackStream *)userData; long numBytes = stream->bytesPerFrame * framesPerBuffer; @@ -449,6 +449,38 @@ BlockingWaitEmpty( PaStream *s ) /* ---- jack driver ---- */ +/* copy null terminated string source to destination, escaping regex characters with '\\' in the process */ +static void copy_string_and_escape_regex_chars( char *destination, const char *source, size_t destbuffersize ) +{ + assert( destination != source ); + assert( destbuffersize > 0 ); + + char *dest = destination; + /* dest_stop is the last location that we can null-terminate the string */ + char *dest_stop = destination + (destbuffersize - 1); + + const char *src = source; + + while ( *src != '\0' && dest != dest_stop ) + { + const char c = *src; + if ( strchr( "\\()[]{}*+?|$^.", c ) != NULL ) + { + if( (dest + 1) == dest_stop ) + break; /* only proceed if we can write both c and the escape */ + + *dest = '\\'; + dest++; + } + *dest = c; + dest++; + + src++; + } + + *dest = '\0'; +} + /* BuildDeviceList(): * * The process of determining a list of PortAudio "devices" from @@ -470,8 +502,12 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) const char **jack_ports = NULL; char **client_names = NULL; - char *regex_pattern = NULL; - int port_index, client_index, i; + char *port_regex_string = NULL; + // In the worst case scenario, every character would be escaped, doubling the string size. + // Add 1 for null terminator. + size_t device_name_regex_escaped_size = jack_client_name_size() * 2 + 1; + size_t port_regex_size = device_name_regex_escaped_size + strlen(port_regex_suffix); + unsigned long port_index, client_index, i; double globalSampleRate; regex_t port_regex; unsigned long numClients = 0, numPorts = 0; @@ -488,8 +524,8 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) * associated with the previous list */ PaUtil_FreeAllAllocations( jackApi->deviceInfoMemory ); - regex_pattern = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, jack_client_name_size() + 3 ); - tmp_client_name = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, jack_client_name_size() ); + port_regex_string = PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, port_regex_size ); + tmp_client_name = PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, jack_client_name_size() ); /* We can only retrieve the list of clients indirectly, by first * asking for a list of all ports, then parsing the port names @@ -501,7 +537,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) while( jack_ports[numPorts] ) ++numPorts; /* At least there will be one port per client :) */ - UNLESS( client_names = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, numPorts * + UNLESS( client_names = PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, numPorts * sizeof (char *) ), paInsufficientMemory ); /* Build a list of clients from the list of ports */ @@ -510,11 +546,12 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) int client_seen = FALSE; regmatch_t match_info; const char *port = jack_ports[port_index]; + PA_DEBUG(( "JACK port found: %s\n", port )); /* extract the client name from the port name, using a regex * that parses the clientname:portname syntax */ UNLESS( !regexec( &port_regex, port, 1, &match_info, 0 ), paInternalError ); - assert(match_info.rm_eo - match_info.rm_so < jack_client_name_size()); + assert(match_info.rm_eo - match_info.rm_so <= jack_client_name_size()); memcpy( tmp_client_name, port + match_info.rm_so, match_info.rm_eo - match_info.rm_so ); tmp_client_name[match_info.rm_eo - match_info.rm_so] = '\0'; @@ -529,7 +566,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) if (client_seen) continue; /* A: Nothing to see here, move along */ - UNLESS( client_names[numClients] = (char*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, + UNLESS( client_names[numClients] = (char*)PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, strlen(tmp_client_name) + 1), paInsufficientMemory ); /* The alsa_pcm client should go in spot 0. If this @@ -556,7 +593,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) /* there is one global sample rate all clients must conform to */ globalSampleRate = jack_get_sample_rate( jackApi->jack_client ); - UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, + UNLESS( commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, sizeof(PaDeviceInfo*) * numClients ), paInsufficientMemory ); assert( commonApi->info.deviceCount == 0 ); @@ -567,9 +604,9 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) PaDeviceInfo *curDevInfo; const char **clientPorts = NULL; - UNLESS( curDevInfo = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, + UNLESS( curDevInfo = (PaDeviceInfo*)PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, sizeof(PaDeviceInfo) ), paInsufficientMemory ); - UNLESS( curDevInfo->name = (char*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, + UNLESS( curDevInfo->name = (char*)PaUtil_GroupAllocateZeroInitializedMemory( jackApi->deviceInfoMemory, strlen(client_names[client_index]) + 1 ), paInsufficientMemory ); strcpy( (char *)curDevInfo->name, client_names[client_index] ); @@ -582,11 +619,13 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) /* To determine how many input and output channels are available, * we re-query jackd with more specific parameters. */ - - sprintf( regex_pattern, "%s:.*", client_names[client_index] ); + copy_string_and_escape_regex_chars( port_regex_string, + client_names[client_index], + device_name_regex_escaped_size ); + strncat( port_regex_string, port_regex_suffix, port_regex_size ); /* ... what are your output ports (that we could input from)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, + clientPorts = jack_get_ports( jackApi->jack_client, port_regex_string, JACK_PORT_TYPE_FILTER, JackPortIsOutput); curDevInfo->maxInputChannels = 0; curDevInfo->defaultLowInputLatency = 0.; @@ -607,7 +646,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) } /* ... what are your input ports (that we could output to)? */ - clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, + clientPorts = jack_get_ports( jackApi->jack_client, port_regex_string, JACK_PORT_TYPE_FILTER, JackPortIsInput); curDevInfo->maxOutputChannels = 0; curDevInfo->defaultLowOutputLatency = 0.; @@ -627,6 +666,11 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) free(clientPorts); } + PA_DEBUG(( "Adding JACK device %s with %d input channels and %d output channels\n", + client_names[client_index], + curDevInfo->maxInputChannels, + curDevInfo->maxOutputChannels )); + /* Add this client to the list of devices */ commonApi->deviceInfos[client_index] = curDevInfo; ++commonApi->info.deviceCount; @@ -651,7 +695,7 @@ static void UpdateSampleRate( PaJackStream *stream, double sampleRate ) static void JackErrorCallback( const char *msg ) { - if( pthread_self() == mainThread_ ) + if( pthread_equal( pthread_self(), mainThread_ ) ) { assert( msg ); jackErr_ = realloc( jackErr_, strlen( msg ) + 1 ); @@ -716,7 +760,7 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, *hostApi = NULL; /* Initialize to NULL */ UNLESS( jackHostApi = (PaJackHostApiRepresentation*) - PaUtil_AllocateMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory ); + PaUtil_AllocateZeroInitializedMemory( sizeof(PaJackHostApiRepresentation) ), paInsufficientMemory ); UNLESS( jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(), paInsufficientMemory ); mainThread_ = pthread_self(); @@ -916,7 +960,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, #define ABS(x) ( (x) > 0 ? (x) : -(x) ) if( ABS(sampleRate - jack_get_sample_rate(((PaJackHostApiRepresentation *) hostApi)->jack_client )) > 1 ) - return paInvalidSampleRate; + return paInvalidSampleRate; #undef ABS return paFormatIsSupported; @@ -937,24 +981,24 @@ static PaError InitializeStream( PaJackStream *stream, PaJackHostApiRepresentati if( numInputChannels > 0 ) { UNLESS( stream->local_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), + (jack_port_t**) PaUtil_GroupAllocateZeroInitializedMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), paInsufficientMemory ); - memset( stream->local_input_ports, 0, sizeof(jack_port_t*) * numInputChannels ); + /* NOTE: we depend on stream->local_input_ports being zero-initialized */ UNLESS( stream->remote_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), + (jack_port_t**) PaUtil_GroupAllocateZeroInitializedMemory( stream->stream_memory, sizeof(jack_port_t*) * numInputChannels ), paInsufficientMemory ); - memset( stream->remote_output_ports, 0, sizeof(jack_port_t*) * numInputChannels ); + /* NOTE: we depend on stream->remote_output_ports being zero-initialized */ } if( numOutputChannels > 0 ) { UNLESS( stream->local_output_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), + (jack_port_t**) PaUtil_GroupAllocateZeroInitializedMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), paInsufficientMemory ); - memset( stream->local_output_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); + /* NOTE: we depend on stream->local_output_ports being zero-initialized */ UNLESS( stream->remote_input_ports = - (jack_port_t**) PaUtil_GroupAllocateMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), + (jack_port_t**) PaUtil_GroupAllocateZeroInitializedMemory( stream->stream_memory, sizeof(jack_port_t*) * numOutputChannels ), paInsufficientMemory ); - memset( stream->remote_input_ports, 0, sizeof(jack_port_t*) * numOutputChannels ); + /* NOTE: we depend on stream->remote_input_ports being zero-initialized */ } stream->num_incoming_connections = numInputChannels; @@ -1077,9 +1121,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaError result = paNoError; PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; PaJackStream *stream = NULL; - char *port_string = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, jack_port_name_size() ); - unsigned long regexSz = jack_client_name_size() + 3; - char *regex_pattern = PaUtil_GroupAllocateMemory( jackHostApi->deviceInfoMemory, regexSz ); + char *port_string = PaUtil_GroupAllocateZeroInitializedMemory( jackHostApi->deviceInfoMemory, jack_port_name_size() ); + // In the worst case every character would be escaped which would double the string length. + // Add 1 for null terminator + size_t regex_escaped_client_name_size = jack_client_name_size() * 2 + 1; + unsigned long regex_size = regex_escaped_client_name_size + strlen(port_regex_suffix); + char *regex_pattern = PaUtil_GroupAllocateZeroInitializedMemory( jackHostApi->deviceInfoMemory, regex_size ); const char **jack_ports = NULL; /* int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); */ int i; @@ -1159,17 +1206,17 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, #define ABS(x) ( (x) > 0 ? (x) : -(x) ) if( ABS(sampleRate - jackSr) > 1 ) - return paInvalidSampleRate; + return paInvalidSampleRate; #undef ABS - UNLESS( stream = (PaJackStream*)PaUtil_AllocateMemory( sizeof(PaJackStream) ), paInsufficientMemory ); + UNLESS( stream = (PaJackStream*)PaUtil_AllocateZeroInitializedMemory( sizeof(PaJackStream) ), paInsufficientMemory ); ENSURE_PA( InitializeStream( stream, jackHostApi, inputChannelCount, outputChannelCount ) ); /* the blocking emulation, if necessary */ stream->isBlockingStream = !streamCallback; if( stream->isBlockingStream ) { - float latency = 0.001; /* 1ms is the absolute minimum we support */ + float latency = 0.001f; /* 1ms is the absolute minimum we support */ int minimum_buffer_frames = 0; if( inputParameters && inputParameters->suggestedLatency > latency ) @@ -1237,7 +1284,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, int err = 0; /* Get output ports of our capture device */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ inputParameters->device ]->name ); + copy_string_and_escape_regex_chars( regex_pattern, + hostApi->deviceInfos[ inputParameters->device ]->name, + regex_escaped_client_name_size ); + strncat( regex_pattern, port_regex_suffix, regex_size ); UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, JACK_PORT_TYPE_FILTER, JackPortIsOutput ), paUnanticipatedHostError ); for( i = 0; i < inputChannelCount && jack_ports[i]; i++ ) @@ -1261,7 +1311,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, int err = 0; /* Get input ports of our playback device */ - snprintf( regex_pattern, regexSz, "%s:.*", hostApi->deviceInfos[ outputParameters->device ]->name ); + copy_string_and_escape_regex_chars( regex_pattern, + hostApi->deviceInfos[ outputParameters->device ]->name, + regex_escaped_client_name_size ); + strncat( regex_pattern, port_regex_suffix, regex_size ); UNLESS( jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, JACK_PORT_TYPE_FILTER, JackPortIsInput ), paUnanticipatedHostError ); for( i = 0; i < outputChannelCount && jack_ports[i]; i++ ) @@ -1608,7 +1661,7 @@ static PaError StartStream( PaStream *s ) { int r = jack_connect( stream->jack_client, jack_port_name( stream->remote_output_ports[i] ), jack_port_name( stream->local_input_ports[i] ) ); - UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError ); + UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError ); } } @@ -1618,7 +1671,7 @@ static PaError StartStream( PaStream *s ) { int r = jack_connect( stream->jack_client, jack_port_name( stream->local_output_ports[i] ), jack_port_name( stream->remote_input_ports[i] ) ); - UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError ); + UNLESS( 0 == r || EEXIST == r, paUnanticipatedHostError ); } } |