SDL  2.0
SDL_jackaudio.c
Go to the documentation of this file.
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;
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) {
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 
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;
435  impl->HasCaptureSupport = SDL_TRUE;
436 
437  return 1; /* this audio target is available. */
438 }
439 
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: */
SDL_memset
#define SDL_memset
Definition: SDL_dynapi_overrides.h:386
SDL_AudioSpec::channels
Uint8 channels
Definition: SDL_audio.h:182
SDL_AudioDriverImpl::HasCaptureSupport
int HasCaptureSupport
Definition: SDL_sysaudio.h:90
SDL_CreateSemaphore
#define SDL_CreateSemaphore
Definition: SDL_dynapi_overrides.h:264
SDL_AUDIO_DRIVER_JACK_DYNAMIC
#define SDL_AUDIO_DRIVER_JACK_DYNAMIC
Definition: SDL_config.h:257
SDL_AudioDriverImpl::FlushCapture
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:76
NULL
#define NULL
Definition: begin_code.h:167
handle
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
SDL_timer.h
SDL_AudioSpec::samples
Uint16 samples
Definition: SDL_audio.h:184
SDL_AudioSpec::format
SDL_AudioFormat format
Definition: SDL_audio.h:181
SDL_AudioDriverImpl::OpenDevice
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:68
SDL_UnloadObject
#define SDL_UnloadObject
Definition: SDL_dynapi_overrides.h:234
callback
static Uint32 callback(Uint32 interval, void *param)
Definition: testtimer.c:34
ports
const EGLAttrib EGLOutputPortEXT * ports
Definition: eglext.h:748
SDL_OpenedAudioDeviceDisconnected
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
SDL_PrivateAudioData::iscapture
SDL_bool iscapture
Definition: SDL_qsa_audio.h:37
SDL_SemPost
#define SDL_SemPost
Definition: SDL_dynapi_overrides.h:269
SDL_LoadObject
#define SDL_LoadObject
Definition: SDL_dynapi_overrides.h:232
AudioBootStrap
Definition: SDL_sysaudio.h:176
SDL_PrivateAudioData
Definition: SDL_alsa_audio.h:33
dst
GLenum GLenum dst
Definition: SDL_opengl_glext.h:1737
SDL_AudioDriverImpl::WaitDevice
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
len
GLenum GLsizei len
Definition: SDL_opengl_glext.h:2926
SDL_memcpy
#define SDL_memcpy
Definition: SDL_dynapi_overrides.h:387
buffer
GLuint buffer
Definition: SDL_opengl_glext.h:533
SDL_audio.h
retval
SDL_bool retval
Definition: testgamecontroller.c:65
SDL_AudioDriverImpl
Definition: SDL_sysaudio.h:65
SDL_memcmp
#define SDL_memcmp
Definition: SDL_dynapi_overrides.h:389
SDL_free
#define SDL_free
Definition: SDL_dynapi_overrides.h:377
SDL_AudioDriverImpl::OnlyHasDefaultOutputDevice
int OnlyHasDefaultOutputDevice
Definition: SDL_sysaudio.h:91
AUDIO_F32SYS
#define AUDIO_F32SYS
Definition: SDL_audio.h:130
SDL_assert.h
addr
GLenum const void * addr
Definition: SDL_opengl_glext.h:7945
SDL_CalculateAudioSpec
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
_THIS
#define _THIS
Definition: SDL_alsa_audio.h:31
SDL_AudioSpec::freq
int freq
Definition: SDL_audio.h:180
SDL_AudioDriverImpl::OnlyHasDefaultCaptureDevice
int OnlyHasDefaultCaptureDevice
Definition: SDL_sysaudio.h:92
SDL_assert
#define SDL_assert(condition)
Definition: SDL_assert.h:169
SDL_jackaudio.h
SDL_OutOfMemory
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
spec
SDL_AudioSpec spec
Definition: loopwave.c:31
SDL_AudioDriverImpl::CaptureFromDevice
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:75
SDL_calloc
#define SDL_calloc
Definition: SDL_dynapi_overrides.h:375
SDL_AudioSpec::size
Uint32 size
Definition: SDL_audio.h:186
src
GLenum src
Definition: SDL_opengl_glext.h:1737
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_SemWait
#define SDL_SemWait
Definition: SDL_dynapi_overrides.h:266
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
SDL_snprintf
#define SDL_snprintf
Definition: SDL_dynapi_overrides.h:40
SDL_LoadFunction
void * SDL_LoadFunction(void *handle, const char *name)
SDL_AudioDriverImpl::GetDeviceBuf
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
SDL_DestroySemaphore
#define SDL_DestroySemaphore
Definition: SDL_dynapi_overrides.h:265
SDL_strlen
#define SDL_strlen
Definition: SDL_dynapi_overrides.h:393
enabled
GLenum GLenum GLsizei const GLuint GLboolean enabled
Definition: SDL_opengl_glext.h:2479
SDL_AtomicGet
#define SDL_AtomicGet
Definition: SDL_dynapi_overrides.h:68
void
const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char int const SDL_PRINTF_FORMAT_STRING char const char const SDL_SCANF_FORMAT_STRING char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
JACK_bootstrap
AudioBootStrap JACK_bootstrap
SDL_loadso.h
type
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
SDL_AudioDriverImpl::CloseDevice
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:78
i
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
SDL_PrivateAudioData::client
jack_client_t * client
Definition: SDL_jackaudio.h:33
SDL_AudioDevice
Definition: SDL_sysaudio.h:131
SDL_AudioDriverImpl::Deinitialize
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:82
Uint8
uint8_t Uint8
Definition: SDL_stdinc.h:179