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
35static jack_client_t * (*JACK_jack_client_open) (const char *, jack_options_t, jack_status_t *, ...);
36static int (*JACK_jack_client_close) (jack_client_t *);
37static void (*JACK_jack_on_shutdown) (jack_client_t *, JackShutdownCallback, void *);
38static int (*JACK_jack_activate) (jack_client_t *);
39static int (*JACK_jack_deactivate) (jack_client_t *);
40static void * (*JACK_jack_port_get_buffer) (jack_port_t *, jack_nframes_t);
41static int (*JACK_jack_port_unregister) (jack_client_t *, jack_port_t *);
42static void (*JACK_jack_free) (void *);
43static const char ** (*JACK_jack_get_ports) (jack_client_t *, const char *, const char *, unsigned long);
44static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
45static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
46static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
47static jack_port_t * (*JACK_jack_port_by_name) (jack_client_t *, const char *);
48static const char * (*JACK_jack_port_name) (const jack_port_t *);
49static const char * (*JACK_jack_port_type) (const jack_port_t *);
50static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
51static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
52
53static int load_jack_syms(void);
54
55
56#ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
57
58static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
59static void *jack_handle = NULL;
60
61/* !!! FIXME: this is copy/pasted in several places now */
62static int
63load_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
78static void
79UnloadJackLibrary(void)
80{
81 if (jack_handle != NULL) {
82 SDL_UnloadObject(jack_handle);
83 jack_handle = NULL;
84 }
85}
86
87static int
88LoadJackLibrary(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
110static void
111UnloadJackLibrary(void)
112{
113}
114
115static int
116LoadJackLibrary(void)
117{
118 load_jack_syms();
119 return 0;
120}
121
122#endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
123
124
125static int
126load_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
149static void
150jackShutdownCallback(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
161static int
162jackProcessPlaybackCallback(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 */
193static void
194JACK_WaitDevice(_THIS)
195{
196 if (SDL_AtomicGet(&this->enabled)) {
197 if (SDL_SemWait(this->hidden->iosem) == -1) {
199 }
200 }
201}
202
203static Uint8 *
204JACK_GetDeviceBuf(_THIS)
205{
206 return (Uint8 *) this->hidden->iobuffer;
207}
208
209
210static int
211jackProcessCaptureCallback(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
237static int
238JACK_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
251static void
252JACK_FlushCapture(_THIS)
253{
254 SDL_SemWait(this->hidden->iosem);
255}
256
257
258static void
259JACK_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
283static int
284JACK_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
403static void
404JACK_Deinitialize(void)
405{
406 UnloadJackLibrary();
407}
408
409static int
410JACK_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;
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: */
#define _THIS
#define SDL_assert(condition)
Definition: SDL_assert.h:169
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1668
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:486
#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:52
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:164
uint8_t Uint8
Definition: SDL_stdinc.h:179
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:167
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
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
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:74
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