SDL  2.0
SDL_jackaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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 const char * (*JACK_jack_port_name) (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_name);
139  SDL_JACK_SYM(jack_connect);
140  SDL_JACK_SYM(jack_set_process_callback);
141  return 0;
142 }
143 
144 
145 static void
146 jackShutdownCallback(void *arg) /* JACK went away; device is lost. */
147 {
148  SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
150  SDL_SemPost(this->hidden->iosem); /* unblock the SDL thread. */
151 }
152 
153 // !!! FIXME: implement and register these!
154 //typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
155 //typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
156 
157 static int
158 jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
159 {
160  SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
161  jack_port_t **ports = this->hidden->sdlports;
162  const int total_channels = this->spec.channels;
163  const int total_frames = this->spec.samples;
164  int channelsi;
165 
166  if (!SDL_AtomicGet(&this->enabled)) {
167  /* silence the buffer to avoid repeats and corruption. */
168  SDL_memset(this->hidden->iobuffer, '\0', this->spec.size);
169  }
170 
171  for (channelsi = 0; channelsi < total_channels; channelsi++) {
172  float *dst = (float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
173  if (dst) {
174  const float *src = ((float *) this->hidden->iobuffer) + channelsi;
175  int framesi;
176  for (framesi = 0; framesi < total_frames; framesi++) {
177  *(dst++) = *src;
178  src += total_channels;
179  }
180  }
181  }
182 
183  SDL_SemPost(this->hidden->iosem); /* tell SDL thread we're done; refill the buffer. */
184  return 0; /* success */
185 }
186 
187 
188 /* This function waits until it is possible to write a full sound buffer */
189 static void
190 JACK_WaitDevice(_THIS)
191 {
192  if (SDL_AtomicGet(&this->enabled)) {
193  if (SDL_SemWait(this->hidden->iosem) == -1) {
195  }
196  }
197 }
198 
199 static Uint8 *
200 JACK_GetDeviceBuf(_THIS)
201 {
202  return (Uint8 *) this->hidden->iobuffer;
203 }
204 
205 
206 static int
207 jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
208 {
209  SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
210  if (SDL_AtomicGet(&this->enabled)) {
211  jack_port_t **ports = this->hidden->sdlports;
212  const int total_channels = this->spec.channels;
213  const int total_frames = this->spec.samples;
214  int channelsi;
215 
216  for (channelsi = 0; channelsi < total_channels; channelsi++) {
217  const float *src = (const float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
218  if (src) {
219  float *dst = ((float *) this->hidden->iobuffer) + channelsi;
220  int framesi;
221  for (framesi = 0; framesi < total_frames; framesi++) {
222  *dst = *(src++);
223  dst += total_channels;
224  }
225  }
226  }
227  }
228 
229  SDL_SemPost(this->hidden->iosem); /* tell SDL thread we're done; new buffer is ready! */
230  return 0; /* success */
231 }
232 
233 static int
234 JACK_CaptureFromDevice(_THIS, void *buffer, int buflen)
235 {
236  SDL_assert(buflen == this->spec.size); /* we always fill a full buffer. */
237 
238  /* Wait for JACK to fill the iobuffer */
239  if (SDL_SemWait(this->hidden->iosem) == -1) {
240  return -1;
241  }
242 
243  SDL_memcpy(buffer, this->hidden->iobuffer, buflen);
244  return buflen;
245 }
246 
247 static void
248 JACK_FlushCapture(_THIS)
249 {
250  SDL_SemWait(this->hidden->iosem);
251 }
252 
253 
254 static void
255 JACK_CloseDevice(_THIS)
256 {
257  if (this->hidden->client) {
258  JACK_jack_deactivate(this->hidden->client);
259 
260  if (this->hidden->sdlports) {
261  const int channels = this->spec.channels;
262  int i;
263  for (i = 0; i < channels; i++) {
264  JACK_jack_port_unregister(this->hidden->client, this->hidden->sdlports[i]);
265  }
266  SDL_free(this->hidden->sdlports);
267  }
268 
269  JACK_jack_client_close(this->hidden->client);
270  }
271 
272  if (this->hidden->iosem) {
273  SDL_DestroySemaphore(this->hidden->iosem);
274  }
275 
276  if (this->hidden->devports) {
277  JACK_jack_free(this->hidden->devports);
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  jack_client_t *client = NULL;
296  jack_status_t status;
297  int channels = 0;
298  int i;
299 
300  /* Initialize all variables that we clean on shutdown */
301  this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof (*this->hidden));
302  if (this->hidden == NULL) {
303  return SDL_OutOfMemory();
304  }
305 
306  /* !!! FIXME: we _still_ need an API to specify an app name */
307  client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
308  this->hidden->client = client;
309  if (client == NULL) {
310  return SDL_SetError("Can't open JACK client");
311  }
312 
313  devports = JACK_jack_get_ports(client, NULL, NULL, JackPortIsPhysical | sysportflags);
314  this->hidden->devports = devports;
315  if (!devports || !devports[0]) {
316  return SDL_SetError("No physical JACK ports available");
317  }
318 
319  while (devports[++channels]) {
320  /* spin to count devports */
321  }
322 
323  /* !!! 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." */
324 
325  /* Jack pretty much demands what it wants. */
326  this->spec.format = AUDIO_F32SYS;
327  this->spec.freq = JACK_jack_get_sample_rate(client);
328  this->spec.channels = channels;
329  this->spec.samples = JACK_jack_get_buffer_size(client);
330 
332 
333  this->hidden->iosem = SDL_CreateSemaphore(0);
334  if (!this->hidden->iosem) {
335  return -1; /* error was set by SDL_CreateSemaphore */
336  }
337 
338  this->hidden->iobuffer = (float *) SDL_calloc(1, this->spec.size);
339  if (!this->hidden->iobuffer) {
340  return SDL_OutOfMemory();
341  }
342 
343  /* Build SDL's ports, which we will connect to the device ports. */
344  this->hidden->sdlports = (jack_port_t **) SDL_calloc(channels, sizeof (jack_port_t *));
345  if (this->hidden->sdlports == NULL) {
346  return SDL_OutOfMemory();
347  }
348 
349  for (i = 0; i < channels; i++) {
350  char portname[32];
351  SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
352  this->hidden->sdlports[i] = JACK_jack_port_register(client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
353  if (this->hidden->sdlports[i] == NULL) {
354  return SDL_SetError("jack_port_register failed");
355  }
356  }
357 
358  if (JACK_jack_set_process_callback(client, callback, this) != 0) {
359  return SDL_SetError("JACK: Couldn't set process callback");
360  }
361 
362  JACK_jack_on_shutdown(client, jackShutdownCallback, this);
363 
364  if (JACK_jack_activate(client) != 0) {
365  return SDL_SetError("Failed to activate JACK client");
366  }
367 
368  /* once activated, we can connect all the ports. */
369  for (i = 0; i < channels; i++) {
370  const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
371  const char *srcport = iscapture ? devports[i] : sdlport;
372  const char *dstport = iscapture ? sdlport : devports[i];
373  if (JACK_jack_connect(client, srcport, dstport) != 0) {
374  return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
375  }
376  }
377 
378  /* don't need these anymore. */
379  this->hidden->devports = NULL;
380  JACK_jack_free(devports);
381 
382  /* We're ready to rock and roll. :-) */
383  return 0;
384 }
385 
386 static void
387 JACK_Deinitialize(void)
388 {
389  UnloadJackLibrary();
390 }
391 
392 static int
393 JACK_Init(SDL_AudioDriverImpl * impl)
394 {
395  if (LoadJackLibrary() < 0) {
396  return 0;
397  } else {
398  /* Make sure a JACK server is running and available. */
399  jack_status_t status;
400  jack_client_t *client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
401  if (client == NULL) {
402  UnloadJackLibrary();
403  return 0;
404  }
405  JACK_jack_client_close(client);
406  }
407 
408  /* Set the function pointers */
409  impl->OpenDevice = JACK_OpenDevice;
410  impl->WaitDevice = JACK_WaitDevice;
411  impl->GetDeviceBuf = JACK_GetDeviceBuf;
412  impl->CloseDevice = JACK_CloseDevice;
413  impl->Deinitialize = JACK_Deinitialize;
414  impl->CaptureFromDevice = JACK_CaptureFromDevice;
415  impl->FlushCapture = JACK_FlushCapture;
418  impl->HasCaptureSupport = SDL_TRUE;
419 
420  return 1; /* this audio target is available. */
421 }
422 
424  "jack", "JACK Audio Connection Kit", JACK_Init, 0
425 };
426 
427 #endif /* SDL_AUDIO_DRIVER_JACK */
428 
429 /* vi: set ts=4 sw=4 expandtab: */
const char ** devports
Definition: SDL_jackaudio.h:36
GLenum GLenum dst
#define SDL_CreateSemaphore
Uint16 samples
Definition: SDL_audio.h:183
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:449
jack_client_t * client
Definition: SDL_jackaudio.h:33
const EGLAttrib EGLOutputPortEXT * ports
Definition: eglext.h:748
GLenum src
#define SDL_LoadObject
#define SDL_UnloadObject
#define SDL_SemPost
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_bool retval
#define SDL_memcpy
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
#define AUDIO_F32SYS
Definition: SDL_audio.h:125
Uint8 channels
Definition: SDL_audio.h:181
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
GLenum const void * addr
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:83
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
GLenum GLenum GLsizei const GLuint GLboolean enabled
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
Uint32 size
Definition: SDL_audio.h:185
#define SDL_assert(condition)
Definition: SDL_assert.h:169
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
#define NULL
Definition: begin_code.h:164
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
GLuint buffer
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:76
#define SDL_SetError
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
#define SDL_calloc
SDL_AudioFormat format
Definition: SDL_audio.h:180
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:77
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_SemWait
#define SDL_DestroySemaphore
AudioBootStrap JACK_bootstrap
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_AtomicGet
#define SDL_snprintf
void * SDL_LoadFunction(void *handle, const char *name)
#define SDL_memset