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