Fixed bug 4184 - jack audio driver fails in presence of midi ports
authorSam Lantinga <slouken@libsdl.org>
Fri, 01 Jun 2018 19:43:53 -0700
changeset 1200594f3f018d3eb
parent 12004 757d81897470
child 12006 1b7ed3438a7f
Fixed bug 4184 - jack audio driver fails in presence of midi ports

Martin Širokov

Launching an SDL application with SDL_AUDIODRIVER=jack, and then calling SDL_OpenAudioDevice() with whatever parameters fails with an error like this one:

SDL_OpenAudioDevice: Couldn't connect JACK ports: SDL:sdl_jack_output_0 => system:midi_playback_1

This happens because JACK_OpenDevice in src/audio/jack/SDL_jackaudio.c blindly tries to connect to all input ports without checking whether they are for audio or midi.

The fix is to check port types and ignore all non audio ports. Also I removed devports field from struct SDL_PrivateAudioData, because it's never really used and removing unused ports from it would be PITA.
src/audio/jack/SDL_jackaudio.c
src/audio/jack/SDL_jackaudio.h
     1.1 --- a/src/audio/jack/SDL_jackaudio.c	Tue May 29 11:18:01 2018 -0700
     1.2 +++ b/src/audio/jack/SDL_jackaudio.c	Fri Jun 01 19:43:53 2018 -0700
     1.3 @@ -44,7 +44,9 @@
     1.4  static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
     1.5  static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
     1.6  static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
     1.7 +static jack_port_t * (*JACK_jack_port_by_name) (jack_client_t *, const char *);
     1.8  static const char * (*JACK_jack_port_name) (const jack_port_t *);
     1.9 +static const char * (*JACK_jack_port_type) (const jack_port_t *);
    1.10  static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
    1.11  static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
    1.12  
    1.13 @@ -135,7 +137,9 @@
    1.14      SDL_JACK_SYM(jack_get_sample_rate);
    1.15      SDL_JACK_SYM(jack_get_buffer_size);
    1.16      SDL_JACK_SYM(jack_port_register);
    1.17 +    SDL_JACK_SYM(jack_port_by_name);
    1.18      SDL_JACK_SYM(jack_port_name);
    1.19 +    SDL_JACK_SYM(jack_port_type);
    1.20      SDL_JACK_SYM(jack_connect);
    1.21      SDL_JACK_SYM(jack_set_process_callback);
    1.22      return 0;
    1.23 @@ -273,10 +277,6 @@
    1.24          SDL_DestroySemaphore(this->hidden->iosem);
    1.25      }
    1.26  
    1.27 -    if (this->hidden->devports) {
    1.28 -        JACK_jack_free(this->hidden->devports);
    1.29 -    }
    1.30 -
    1.31      SDL_free(this->hidden->iobuffer);
    1.32  }
    1.33  
    1.34 @@ -292,9 +292,11 @@
    1.35      const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
    1.36      const char *sdlportstr = iscapture ? "input" : "output";
    1.37      const char **devports = NULL;
    1.38 +    int *audio_ports;
    1.39      jack_client_t *client = NULL;
    1.40      jack_status_t status;
    1.41      int channels = 0;
    1.42 +    int ports = 0;
    1.43      int i;
    1.44  
    1.45      /* Initialize all variables that we clean on shutdown */
    1.46 @@ -311,15 +313,30 @@
    1.47      }
    1.48  
    1.49      devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
    1.50 -    this->hidden->devports = devports;
    1.51      if (!devports || !devports[0]) {
    1.52          return SDL_SetError("No physical JACK ports available");
    1.53      }
    1.54  
    1.55 -    while (devports[++channels]) {
    1.56 +    while (devports[++ports]) {
    1.57          /* spin to count devports */
    1.58      }
    1.59  
    1.60 +    /* Filter out non-audio ports */
    1.61 +    audio_ports = SDL_calloc(ports, sizeof *audio_ports);
    1.62 +    for (i = 0; i < ports; i++) {
    1.63 +        const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
    1.64 +        const char *type = JACK_jack_port_type(dport);
    1.65 +        const int len = SDL_strlen(type);
    1.66 +        /* See if type ends with "audio" */
    1.67 +        if (len >= 5 && !SDL_memcmp(type+len-5, "audio", 5)) {
    1.68 +            audio_ports[channels++] = i;
    1.69 +        }
    1.70 +    }
    1.71 +    if (channels == 0) {
    1.72 +        return SDL_SetError("No physical JACK ports available");
    1.73 +    }
    1.74 +
    1.75 +
    1.76      /* !!! FIXME: docs say about buffer size: "This size may change, clients that depend on it must register a bufsize_callback so they will be notified if it does." */
    1.77  
    1.78      /* Jack pretty much demands what it wants. */
    1.79 @@ -368,16 +385,16 @@
    1.80      /* once activated, we can connect all the ports. */
    1.81      for (i = 0; i < channels; i++) {
    1.82          const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
    1.83 -        const char *srcport = iscapture ? devports[i] : sdlport;
    1.84 -        const char *dstport = iscapture ? sdlport : devports[i];
    1.85 +        const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
    1.86 +        const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
    1.87          if (JACK_jack_connect(client, srcport, dstport) != 0) {
    1.88              return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
    1.89          }
    1.90      }
    1.91  
    1.92      /* don't need these anymore. */
    1.93 -    this->hidden->devports = NULL;
    1.94      JACK_jack_free(devports);
    1.95 +    SDL_free(audio_ports);
    1.96  
    1.97      /* We're ready to rock and roll. :-) */
    1.98      return 0;
     2.1 --- a/src/audio/jack/SDL_jackaudio.h	Tue May 29 11:18:01 2018 -0700
     2.2 +++ b/src/audio/jack/SDL_jackaudio.h	Fri Jun 01 19:43:53 2018 -0700
     2.3 @@ -33,7 +33,6 @@
     2.4      jack_client_t *client;
     2.5      SDL_sem *iosem;
     2.6      float *iobuffer;
     2.7 -    const char **devports;
     2.8      jack_port_t **sdlports;
     2.9  };
    2.10