📄 pa_jack.c
字号:
PaJackStream *stream = (PaJackStream *)s; long bytesRead; char *p = (char *) data; long numBytes = stream->bytesPerFrame * numFrames; while( numBytes > 0 ) { bytesRead = PaUtil_ReadRingBuffer( &stream->inFIFO, p, numBytes ); numBytes -= bytesRead; p += bytesRead; if( numBytes > 0 ) { /* see write for an explanation */ if( stream->data_available ) stream->data_available = 0; else sem_wait( &stream->data_semaphore ); } } return result;}static PaError BlockingWriteStream( PaStream* s, const void *data, unsigned long numFrames ){ PaError result = paNoError; PaJackStream *stream = (PaJackStream *)s; long bytesWritten; char *p = (char *) data; long numBytes = stream->bytesPerFrame * numFrames; while( numBytes > 0 ) { bytesWritten = PaUtil_WriteRingBuffer( &stream->outFIFO, p, numBytes ); numBytes -= bytesWritten; p += bytesWritten; if( numBytes > 0 ) { /* we use the following algorithm: * (1) write data * (2) if some data didn't fit into the ringbuffer, set data_available to 0 * to indicate to the audio that if space becomes available, we want to know * (3) retry to write data (because it might be that between (1) and (2) * new space in the buffer became available) * (4) if this failed, we are sure that the buffer is really empty and * we will definitely receive a notification when it becomes available * thus we can safely sleep * * if the algorithm bailed out in step (3) before, it leaks a count of 1 * on the semaphore; however, it doesn't matter, because if we block in (4), * we also do it in a loop */ if( stream->data_available ) stream->data_available = 0; else sem_wait( &stream->data_semaphore ); } } return result;}static signed longBlockingGetStreamReadAvailable( PaStream* s ){ PaJackStream *stream = (PaJackStream *)s; int bytesFull = PaUtil_GetRingBufferReadAvailable( &stream->inFIFO ); return bytesFull / stream->bytesPerFrame;}static signed longBlockingGetStreamWriteAvailable( PaStream* s ){ PaJackStream *stream = (PaJackStream *)s; int bytesEmpty = PaUtil_GetRingBufferWriteAvailable( &stream->outFIFO ); return bytesEmpty / stream->bytesPerFrame;}static PaErrorBlockingWaitEmpty( PaStream *s ){ PaJackStream *stream = (PaJackStream *)s; while( PaUtil_GetRingBufferReadAvailable( &stream->outFIFO ) > 0 ) { stream->data_available = 0; sem_wait( &stream->data_semaphore ); } return 0;}/* ---- jack driver ---- *//* BuildDeviceList(): * * The process of determining a list of PortAudio "devices" from * JACK's client/port system is fairly involved, so it is separated * into its own routine. */static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ){ /* Utility macros for the repetitive process of allocating memory */ /* JACK has no concept of a device. To JACK, there are clients * which have an arbitrary number of ports. To make this * intelligible to PortAudio clients, we will group each JACK client * into a device, and make each port of that client a channel */ PaError result = paNoError; PaUtilHostApiRepresentation *commonApi = &jackApi->commonHostApiRep; const char **jack_ports = NULL; char **client_names = NULL; char *regex_pattern = NULL; int port_index, client_index, i; double globalSampleRate; regex_t port_regex; unsigned long numClients = 0, numPorts = 0; char *tmp_client_name = NULL; commonApi->info.defaultInputDevice = paNoDevice; commonApi->info.defaultOutputDevice = paNoDevice; commonApi->info.deviceCount = 0; /* Parse the list of ports, using a regex to grab the client names */ ASSERT_CALL( regcomp( &port_regex, "^[^:]*", REG_EXTENDED ), 0 ); /* since we are rebuilding the list of devices, free all memory * 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() ); /* We can only retrieve the list of clients indirectly, by first * asking for a list of all ports, then parsing the port names * according to the client_name:port_name convention (which is * enforced by jackd) * A: If jack_get_ports returns NULL, there's nothing for us to do */ UNLESS( (jack_ports = jack_get_ports( jackApi->jack_client, "", "", 0 )) && jack_ports[0], paNoError ); /* Find number of ports */ while( jack_ports[numPorts] ) ++numPorts; /* At least there will be one port per client :) */ UNLESS( client_names = PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, numPorts * sizeof (char *) ), paInsufficientMemory ); /* Build a list of clients from the list of ports */ for( numClients = 0, port_index = 0; jack_ports[port_index] != NULL; port_index++ ) { int client_seen = FALSE; regmatch_t match_info; const char *port = jack_ports[port_index]; /* 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()); 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'; /* do we know about this port's client yet? */ for( i = 0; i < numClients; i++ ) { if( strcmp( tmp_client_name, client_names[i] ) == 0 ) client_seen = TRUE; } if (client_seen) continue; /* A: Nothing to see here, move along */ UNLESS( client_names[numClients] = (char*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, strlen(tmp_client_name) + 1), paInsufficientMemory ); /* The alsa_pcm client should go in spot 0. If this * is the alsa_pcm client AND we are NOT about to put * it in spot 0 put it in spot 0 and move whatever * was already in spot 0 to the end. */ if( strcmp( "alsa_pcm", tmp_client_name ) == 0 && numClients > 0 ) { /* alsa_pcm goes in spot 0 */ strcpy( client_names[ numClients ], client_names[0] ); strcpy( client_names[0], tmp_client_name ); } else { /* put the new client at the end of the client list */ strcpy( client_names[ numClients ], tmp_client_name ); } ++numClients; } /* Now we have a list of clients, which will become the list of * PortAudio devices. */ /* 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, sizeof(PaDeviceInfo*) * numClients ), paInsufficientMemory ); assert( commonApi->info.deviceCount == 0 ); /* Create a PaDeviceInfo structure for every client */ for( client_index = 0; client_index < numClients; client_index++ ) { PaDeviceInfo *curDevInfo; const char **clientPorts = NULL; UNLESS( curDevInfo = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, sizeof(PaDeviceInfo) ), paInsufficientMemory ); UNLESS( curDevInfo->name = (char*)PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, strlen(client_names[client_index]) + 1 ), paInsufficientMemory ); strcpy( (char *)curDevInfo->name, client_names[client_index] ); curDevInfo->structVersion = 2; curDevInfo->hostApi = jackApi->hostApiIndex; /* JACK is very inflexible: there is one sample rate the whole * system must run at, and all clients must speak IEEE float. */ curDevInfo->defaultSampleRate = globalSampleRate; /* 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] ); /* ... what are your output ports (that we could input from)? */ clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, NULL, JackPortIsOutput); curDevInfo->maxInputChannels = 0; curDevInfo->defaultLowInputLatency = 0.; curDevInfo->defaultHighInputLatency = 0.; if( clientPorts ) { jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); curDevInfo->defaultLowInputLatency = curDevInfo->defaultHighInputLatency = jack_port_get_latency( p ) / globalSampleRate; for( i = 0; clientPorts[i] != NULL; i++) { /* The number of ports returned is the number of output channels. * We don't care what they are, we just care how many */ curDevInfo->maxInputChannels++; } free(clientPorts); } /* ... what are your input ports (that we could output to)? */ clientPorts = jack_get_ports( jackApi->jack_client, regex_pattern, NULL, JackPortIsInput); curDevInfo->maxOutputChannels = 0; curDevInfo->defaultLowOutputLatency = 0.; curDevInfo->defaultHighOutputLatency = 0.; if( clientPorts ) { jack_port_t *p = jack_port_by_name( jackApi->jack_client, clientPorts[0] ); curDevInfo->defaultLowOutputLatency = curDevInfo->defaultHighOutputLatency = jack_port_get_latency( p ) / globalSampleRate; for( i = 0; clientPorts[i] != NULL; i++) { /* The number of ports returned is the number of input channels. * We don't care what they are, we just care how many */ curDevInfo->maxOutputChannels++; } free(clientPorts); } /* Add this client to the list of devices */ commonApi->deviceInfos[client_index] = curDevInfo; ++commonApi->info.deviceCount; if( commonApi->info.defaultInputDevice == paNoDevice && curDevInfo->maxInputChannels > 0 ) commonApi->info.defaultInputDevice = client_index; if( commonApi->info.defaultOutputDevice == paNoDevice && curDevInfo->maxOutputChannels > 0 ) commonApi->info.defaultOutputDevice = client_index; }error: regfree( &port_regex ); free( jack_ports ); return result;}static void UpdateSampleRate( PaJackStream *stream, double sampleRate ){ /* XXX: Maybe not the cleanest way of going about this? */ stream->cpuLoadMeasurer.samplingPeriod = stream->bufferProcessor.samplePeriod = 1. / sampleRate; stream->streamRepresentation.streamInfo.sampleRate = sampleRate;}static void JackErrorCallback( const char *msg ){ if( pthread_self() == mainThread_ ) { assert( msg ); jackErr_ = realloc( jackErr_, strlen( msg ) + 1 ); strcpy( jackErr_, msg ); }}static void JackOnShutdown( void *arg ){ PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; PaJackStream *stream = jackApi->processQueue; PA_DEBUG(( "%s: JACK server is shutting down\n", __FUNCTION__ )); for( ; stream; stream = stream->next ) { stream->is_active = 0; } /* Make sure that the main thread doesn't get stuck waiting on the condition */ ASSERT_CALL( pthread_mutex_lock( &jackApi->mtx ), 0 ); jackApi->jackIsDown = 1; ASSERT_CALL( pthread_cond_signal( &jackApi->cond ), 0 ); ASSERT_CALL( pthread_mutex_unlock( &jackApi->mtx ), 0 );}static int JackSrCb( jack_nframes_t nframes, void *arg ){ PaJackHostApiRepresentation *jackApi = (PaJackHostApiRepresentation *)arg; double sampleRate = (double)nframes; PaJackStream *stream = jackApi->processQueue; /* Update all streams in process queue */ PA_DEBUG(( "%s: Acting on change in JACK samplerate: %f\n", __FUNCTION__, sampleRate )); for( ; stream; stream = stream->next ) { if( stream->streamRepresentation.streamInfo.sampleRate != sampleRate ) { PA_DEBUG(( "%s: Updating samplerate\n", __FUNCTION__ )); UpdateSampleRate( stream, sampleRate ); } } return 0;}static int JackXRunCb(void *arg) { PaJackHostApiRepresentation *hostApi = (PaJackHostApiRepresentation *)arg; assert( hostApi ); hostApi->xrun = TRUE; PA_DEBUG(( "%s: JACK signalled xrun\n", __FUNCTION__ ));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -