Atlas - SDL_jackaudio.c

Home / ext / SDL2 / src / audio / jack Lines: 1 | Size: 14329 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 Sam Lantinga <[email protected]> 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; 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 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) { 198 SDL_OpenedAudioDeviceDisconnected(this); 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 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 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; 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 440AudioBootStrap 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: */ 447
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.