src/audio/jack/SDL_jackaudio.c
author Alex Szpakowski <slime73@gmail.com>
Sun, 26 May 2019 18:53:36 -0300
changeset 12757 96bca5da4965
parent 12503 806492103856
permissions -rw-r--r--
iOS: return SDL_GetWindowSize from SDL_GL_GetDrawableSize if there's no GLES view in the window (matches the behaviour of SDL_GL_GetDrawableSize on other platforms). Addresses bug #4629.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 #include "../../SDL_internal.h"
    23 
    24 #if SDL_AUDIO_DRIVER_JACK
    25 
    26 #include "SDL_assert.h"
    27 #include "SDL_timer.h"
    28 #include "SDL_audio.h"
    29 #include "../SDL_audio_c.h"
    30 #include "SDL_jackaudio.h"
    31 #include "SDL_loadso.h"
    32 #include "../../thread/SDL_systhread.h"
    33 
    34 
    35 static jack_client_t * (*JACK_jack_client_open) (const char *, jack_options_t, jack_status_t *, ...);
    36 static int (*JACK_jack_client_close) (jack_client_t *);
    37 static void (*JACK_jack_on_shutdown) (jack_client_t *, JackShutdownCallback, void *);
    38 static int (*JACK_jack_activate) (jack_client_t *);
    39 static int (*JACK_jack_deactivate) (jack_client_t *);
    40 static void * (*JACK_jack_port_get_buffer) (jack_port_t *, jack_nframes_t);
    41 static int (*JACK_jack_port_unregister) (jack_client_t *, jack_port_t *);
    42 static void (*JACK_jack_free) (void *);
    43 static const char ** (*JACK_jack_get_ports) (jack_client_t *, const char *, const char *, unsigned long);
    44 static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
    45 static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
    46 static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
    47 static jack_port_t * (*JACK_jack_port_by_name) (jack_client_t *, const char *);
    48 static const char * (*JACK_jack_port_name) (const jack_port_t *);
    49 static const char * (*JACK_jack_port_type) (const jack_port_t *);
    50 static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
    51 static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
    52 
    53 static int load_jack_syms(void);
    54 
    55 
    56 #ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
    57 
    58 static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
    59 static void *jack_handle = NULL;
    60 
    61 /* !!! FIXME: this is copy/pasted in several places now */
    62 static int
    63 load_jack_sym(const char *fn, void **addr)
    64 {
    65     *addr = SDL_LoadFunction(jack_handle, fn);
    66     if (*addr == NULL) {
    67         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    68         return 0;
    69     }
    70 
    71     return 1;
    72 }
    73 
    74 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    75 #define SDL_JACK_SYM(x) \
    76     if (!load_jack_sym(#x, (void **) (char *) &JACK_##x)) return -1
    77 
    78 static void
    79 UnloadJackLibrary(void)
    80 {
    81     if (jack_handle != NULL) {
    82         SDL_UnloadObject(jack_handle);
    83         jack_handle = NULL;
    84     }
    85 }
    86 
    87 static int
    88 LoadJackLibrary(void)
    89 {
    90     int retval = 0;
    91     if (jack_handle == NULL) {
    92         jack_handle = SDL_LoadObject(jack_library);
    93         if (jack_handle == NULL) {
    94             retval = -1;
    95             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
    96         } else {
    97             retval = load_jack_syms();
    98             if (retval < 0) {
    99                 UnloadJackLibrary();
   100             }
   101         }
   102     }
   103     return retval;
   104 }
   105 
   106 #else
   107 
   108 #define SDL_JACK_SYM(x) JACK_##x = x
   109 
   110 static void
   111 UnloadJackLibrary(void)
   112 {
   113 }
   114 
   115 static int
   116 LoadJackLibrary(void)
   117 {
   118     load_jack_syms();
   119     return 0;
   120 }
   121 
   122 #endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
   123 
   124 
   125 static int
   126 load_jack_syms(void)
   127 {
   128     SDL_JACK_SYM(jack_client_open);
   129     SDL_JACK_SYM(jack_client_close);
   130     SDL_JACK_SYM(jack_on_shutdown);
   131     SDL_JACK_SYM(jack_activate);
   132     SDL_JACK_SYM(jack_deactivate);
   133     SDL_JACK_SYM(jack_port_get_buffer);
   134     SDL_JACK_SYM(jack_port_unregister);
   135     SDL_JACK_SYM(jack_free);
   136     SDL_JACK_SYM(jack_get_ports);
   137     SDL_JACK_SYM(jack_get_sample_rate);
   138     SDL_JACK_SYM(jack_get_buffer_size);
   139     SDL_JACK_SYM(jack_port_register);
   140     SDL_JACK_SYM(jack_port_by_name);
   141     SDL_JACK_SYM(jack_port_name);
   142     SDL_JACK_SYM(jack_port_type);
   143     SDL_JACK_SYM(jack_connect);
   144     SDL_JACK_SYM(jack_set_process_callback);
   145     return 0;
   146 }
   147 
   148 
   149 static void
   150 jackShutdownCallback(void *arg)  /* JACK went away; device is lost. */
   151 {
   152     SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
   153     SDL_OpenedAudioDeviceDisconnected(this);
   154     SDL_SemPost(this->hidden->iosem);  /* unblock the SDL thread. */
   155 }
   156 
   157 // !!! FIXME: implement and register these!
   158 //typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
   159 //typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
   160 
   161 static int
   162 jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
   163 {
   164     SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
   165     jack_port_t **ports = this->hidden->sdlports;
   166     const int total_channels = this->spec.channels;
   167     const int total_frames = this->spec.samples;
   168     int channelsi;
   169 
   170     if (!SDL_AtomicGet(&this->enabled)) {
   171         /* silence the buffer to avoid repeats and corruption. */
   172         SDL_memset(this->hidden->iobuffer, '\0', this->spec.size);
   173     }
   174 
   175     for (channelsi = 0; channelsi < total_channels; channelsi++) {
   176         float *dst = (float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
   177         if (dst) {
   178             const float *src = ((float *) this->hidden->iobuffer) + channelsi;
   179             int framesi;
   180             for (framesi = 0; framesi < total_frames; framesi++) {
   181                 *(dst++) = *src;
   182                 src += total_channels;
   183             }
   184         }
   185     }
   186 
   187     SDL_SemPost(this->hidden->iosem);  /* tell SDL thread we're done; refill the buffer. */
   188     return 0;  /* success */
   189 }
   190 
   191 
   192 /* This function waits until it is possible to write a full sound buffer */
   193 static void
   194 JACK_WaitDevice(_THIS)
   195 {
   196     if (SDL_AtomicGet(&this->enabled)) {
   197         if (SDL_SemWait(this->hidden->iosem) == -1) {
   198             SDL_OpenedAudioDeviceDisconnected(this);
   199         }
   200     }
   201 }
   202 
   203 static Uint8 *
   204 JACK_GetDeviceBuf(_THIS)
   205 {
   206     return (Uint8 *) this->hidden->iobuffer;
   207 }
   208 
   209 
   210 static int
   211 jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
   212 {
   213     SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
   214     if (SDL_AtomicGet(&this->enabled)) {
   215         jack_port_t **ports = this->hidden->sdlports;
   216         const int total_channels = this->spec.channels;
   217         const int total_frames = this->spec.samples;
   218         int channelsi;
   219     
   220         for (channelsi = 0; channelsi < total_channels; channelsi++) {
   221             const float *src = (const float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
   222             if (src) {
   223                 float *dst = ((float *) this->hidden->iobuffer) + channelsi;
   224                 int framesi;
   225                 for (framesi = 0; framesi < total_frames; framesi++) {
   226                     *dst = *(src++);
   227                     dst += total_channels;
   228                 }
   229             }
   230         }
   231     }
   232 
   233     SDL_SemPost(this->hidden->iosem);  /* tell SDL thread we're done; new buffer is ready! */
   234     return 0;  /* success */
   235 }
   236 
   237 static int
   238 JACK_CaptureFromDevice(_THIS, void *buffer, int buflen)
   239 {
   240     SDL_assert(buflen == this->spec.size);  /* we always fill a full buffer. */
   241 
   242     /* Wait for JACK to fill the iobuffer */
   243     if (SDL_SemWait(this->hidden->iosem) == -1) {
   244         return -1;
   245     }
   246 
   247     SDL_memcpy(buffer, this->hidden->iobuffer, buflen);
   248     return buflen;
   249 }
   250 
   251 static void
   252 JACK_FlushCapture(_THIS)
   253 {
   254     SDL_SemWait(this->hidden->iosem);
   255 }
   256 
   257 
   258 static void
   259 JACK_CloseDevice(_THIS)
   260 {
   261     if (this->hidden->client) {
   262         JACK_jack_deactivate(this->hidden->client);
   263 
   264         if (this->hidden->sdlports) {
   265             const int channels = this->spec.channels;
   266             int i;
   267             for (i = 0; i < channels; i++) {
   268                 JACK_jack_port_unregister(this->hidden->client, this->hidden->sdlports[i]);
   269             }
   270             SDL_free(this->hidden->sdlports);
   271         }
   272 
   273         JACK_jack_client_close(this->hidden->client);
   274     }
   275 
   276     if (this->hidden->iosem) {
   277         SDL_DestroySemaphore(this->hidden->iosem);
   278     }
   279 
   280     SDL_free(this->hidden->iobuffer);
   281 }
   282 
   283 static int
   284 JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
   285 {
   286     /* Note that JACK uses "output" for capture devices (they output audio
   287         data to us) and "input" for playback (we input audio data to them).
   288         Likewise, SDL's playback port will be "output" (we write data out)
   289         and capture will be "input" (we read data in). */
   290     const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
   291     const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
   292     const JackProcessCallback callback = iscapture ? jackProcessCaptureCallback : jackProcessPlaybackCallback;
   293     const char *sdlportstr = iscapture ? "input" : "output";
   294     const char **devports = NULL;
   295     int *audio_ports;
   296     jack_client_t *client = NULL;
   297     jack_status_t status;
   298     int channels = 0;
   299     int ports = 0;
   300     int i;
   301 
   302     /* Initialize all variables that we clean on shutdown */
   303     this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof (*this->hidden));
   304     if (this->hidden == NULL) {
   305         return SDL_OutOfMemory();
   306     }
   307 
   308     /* !!! FIXME: we _still_ need an API to specify an app name */
   309     client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
   310     this->hidden->client = client;
   311     if (client == NULL) {
   312         return SDL_SetError("Can't open JACK client");
   313     }
   314 
   315     devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
   316     if (!devports || !devports[0]) {
   317         return SDL_SetError("No physical JACK ports available");
   318     }
   319 
   320     while (devports[++ports]) {
   321         /* spin to count devports */
   322     }
   323 
   324     /* Filter out non-audio ports */
   325     audio_ports = SDL_calloc(ports, sizeof *audio_ports);
   326     for (i = 0; i < ports; i++) {
   327         const jack_port_t *dport = JACK_jack_port_by_name(client, devports[i]);
   328         const char *type = JACK_jack_port_type(dport);
   329         const int len = SDL_strlen(type);
   330         /* See if type ends with "audio" */
   331         if (len >= 5 && !SDL_memcmp(type+len-5, "audio", 5)) {
   332             audio_ports[channels++] = i;
   333         }
   334     }
   335     if (channels == 0) {
   336         return SDL_SetError("No physical JACK ports available");
   337     }
   338 
   339 
   340     /* !!! 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." */
   341 
   342     /* Jack pretty much demands what it wants. */
   343     this->spec.format = AUDIO_F32SYS;
   344     this->spec.freq = JACK_jack_get_sample_rate(client);
   345     this->spec.channels = channels;
   346     this->spec.samples = JACK_jack_get_buffer_size(client);
   347 
   348     SDL_CalculateAudioSpec(&this->spec);
   349 
   350     this->hidden->iosem = SDL_CreateSemaphore(0);
   351     if (!this->hidden->iosem) {
   352         return -1;  /* error was set by SDL_CreateSemaphore */
   353     }
   354 
   355     this->hidden->iobuffer = (float *) SDL_calloc(1, this->spec.size);
   356     if (!this->hidden->iobuffer) {
   357         return SDL_OutOfMemory();
   358     }
   359 
   360     /* Build SDL's ports, which we will connect to the device ports. */
   361     this->hidden->sdlports = (jack_port_t **) SDL_calloc(channels, sizeof (jack_port_t *));
   362     if (this->hidden->sdlports == NULL) {
   363         return SDL_OutOfMemory();
   364     }
   365 
   366     for (i = 0; i < channels; i++) {
   367         char portname[32];
   368         SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
   369         this->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
   370         if (this->hidden->sdlports[i] == NULL) {
   371             return SDL_SetError("jack_port_register failed");
   372         }
   373     }
   374 
   375     if (JACK_jack_set_process_callback(client, callback, this) != 0) {
   376         return SDL_SetError("JACK: Couldn't set process callback");
   377     }
   378 
   379     JACK_jack_on_shutdown(client, jackShutdownCallback, this);
   380 
   381     if (JACK_jack_activate(client) != 0) {
   382         return SDL_SetError("Failed to activate JACK client");
   383     }
   384 
   385     /* once activated, we can connect all the ports. */
   386     for (i = 0; i < channels; i++) {
   387         const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
   388         const char *srcport = iscapture ? devports[audio_ports[i]] : sdlport;
   389         const char *dstport = iscapture ? sdlport : devports[audio_ports[i]];
   390         if (JACK_jack_connect(client, srcport, dstport) != 0) {
   391             return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
   392         }
   393     }
   394 
   395     /* don't need these anymore. */
   396     JACK_jack_free(devports);
   397     SDL_free(audio_ports);
   398 
   399     /* We're ready to rock and roll. :-) */
   400     return 0;
   401 }
   402 
   403 static void
   404 JACK_Deinitialize(void)
   405 {
   406     UnloadJackLibrary();
   407 }
   408 
   409 static int
   410 JACK_Init(SDL_AudioDriverImpl * impl)
   411 {
   412     if (LoadJackLibrary() < 0) {
   413         return 0;
   414     } else {
   415         /* Make sure a JACK server is running and available. */
   416         jack_status_t status;
   417         jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
   418         if (client == NULL) {
   419             UnloadJackLibrary();
   420             return 0;
   421         }
   422         JACK_jack_client_close(client);
   423     }
   424 
   425     /* Set the function pointers */
   426     impl->OpenDevice = JACK_OpenDevice;
   427     impl->WaitDevice = JACK_WaitDevice;
   428     impl->GetDeviceBuf = JACK_GetDeviceBuf;
   429     impl->CloseDevice = JACK_CloseDevice;
   430     impl->Deinitialize = JACK_Deinitialize;
   431     impl->CaptureFromDevice = JACK_CaptureFromDevice;
   432     impl->FlushCapture = JACK_FlushCapture;
   433     impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
   434     impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
   435     impl->HasCaptureSupport = SDL_TRUE;
   436 
   437     return 1;   /* this audio target is available. */
   438 }
   439 
   440 AudioBootStrap JACK_bootstrap = {
   441     "jack", "JACK Audio Connection Kit", JACK_Init, 0
   442 };
   443 
   444 #endif /* SDL_AUDIO_DRIVER_JACK */
   445 
   446 /* vi: set ts=4 sw=4 expandtab: */