SDL_jackaudio.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "../../SDL_internal.h"
  19. #if SDL_AUDIO_DRIVER_JACK
  20. #include "SDL_assert.h"
  21. #include "SDL_timer.h"
  22. #include "SDL_audio.h"
  23. #include "../SDL_audio_c.h"
  24. #include "SDL_jackaudio.h"
  25. #include "SDL_loadso.h"
  26. #include "../../thread/SDL_systhread.h"
  27. static jack_client_t * (*JACK_jack_client_open) (const char *, jack_options_t, jack_status_t *, ...);
  28. static int (*JACK_jack_client_close) (jack_client_t *);
  29. static void (*JACK_jack_on_shutdown) (jack_client_t *, JackShutdownCallback, void *);
  30. static int (*JACK_jack_activate) (jack_client_t *);
  31. static void * (*JACK_jack_port_get_buffer) (jack_port_t *, jack_nframes_t);
  32. static int (*JACK_jack_port_unregister) (jack_client_t *, jack_port_t *);
  33. static void (*JACK_jack_free) (void *);
  34. static const char ** (*JACK_jack_get_ports) (jack_client_t *, const char *, const char *, unsigned long);
  35. static jack_nframes_t (*JACK_jack_get_sample_rate) (jack_client_t *);
  36. static jack_nframes_t (*JACK_jack_get_buffer_size) (jack_client_t *);
  37. static jack_port_t * (*JACK_jack_port_register) (jack_client_t *, const char *, const char *, unsigned long, unsigned long);
  38. static const char * (*JACK_jack_port_name) (const jack_port_t *);
  39. static int (*JACK_jack_connect) (jack_client_t *, const char *, const char *);
  40. static int (*JACK_jack_set_process_callback) (jack_client_t *, JackProcessCallback, void *);
  41. static int load_jack_syms(void);
  42. #ifdef SDL_AUDIO_DRIVER_JACK_DYNAMIC
  43. static const char *jack_library = SDL_AUDIO_DRIVER_JACK_DYNAMIC;
  44. static void *jack_handle = NULL;
  45. /* !!! FIXME: this is copy/pasted in several places now */
  46. static int
  47. load_jack_sym(const char *fn, void **addr)
  48. {
  49. *addr = SDL_LoadFunction(jack_handle, fn);
  50. if (*addr == NULL) {
  51. /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
  52. return 0;
  53. }
  54. return 1;
  55. }
  56. /* cast funcs to char* first, to please GCC's strict aliasing rules. */
  57. #define SDL_JACK_SYM(x) \
  58. if (!load_jack_sym(#x, (void **) (char *) &JACK_##x)) return -1
  59. static void
  60. UnloadJackLibrary(void)
  61. {
  62. if (jack_handle != NULL) {
  63. SDL_UnloadObject(jack_handle);
  64. jack_handle = NULL;
  65. }
  66. }
  67. static int
  68. LoadJackLibrary(void)
  69. {
  70. int retval = 0;
  71. if (jack_handle == NULL) {
  72. jack_handle = SDL_LoadObject(jack_library);
  73. if (jack_handle == NULL) {
  74. retval = -1;
  75. /* Don't call SDL_SetError(): SDL_LoadObject already did. */
  76. } else {
  77. retval = load_jack_syms();
  78. if (retval < 0) {
  79. UnloadJackLibrary();
  80. }
  81. }
  82. }
  83. return retval;
  84. }
  85. #else
  86. #define SDL_JACK_SYM(x) JACK_##x = x
  87. static void
  88. UnloadJackLibrary(void)
  89. {
  90. }
  91. static int
  92. LoadJackLibrary(void)
  93. {
  94. load_jack_syms();
  95. return 0;
  96. }
  97. #endif /* SDL_AUDIO_DRIVER_JACK_DYNAMIC */
  98. static int
  99. load_jack_syms(void)
  100. {
  101. SDL_JACK_SYM(jack_client_open);
  102. SDL_JACK_SYM(jack_client_close);
  103. SDL_JACK_SYM(jack_on_shutdown);
  104. SDL_JACK_SYM(jack_activate);
  105. SDL_JACK_SYM(jack_port_get_buffer);
  106. SDL_JACK_SYM(jack_port_unregister);
  107. SDL_JACK_SYM(jack_free);
  108. SDL_JACK_SYM(jack_get_ports);
  109. SDL_JACK_SYM(jack_get_sample_rate);
  110. SDL_JACK_SYM(jack_get_buffer_size);
  111. SDL_JACK_SYM(jack_port_register);
  112. SDL_JACK_SYM(jack_port_name);
  113. SDL_JACK_SYM(jack_connect);
  114. SDL_JACK_SYM(jack_set_process_callback);
  115. return 0;
  116. }
  117. static jack_client_t *JACK_client = NULL;
  118. static void
  119. DisconnectFromJackServer(void)
  120. {
  121. if (JACK_client) {
  122. JACK_jack_client_close(JACK_client);
  123. JACK_client = NULL;
  124. }
  125. }
  126. static void
  127. jackShutdownCallback(void *arg)
  128. {
  129. /* !!! FIXME: alert SDL that _every_ open device is lost here */
  130. fprintf(stderr, "SDL JACK FIXME: shutdown callback fired! All audio devices are lost!\n");
  131. fflush(stderr);
  132. // !!! FIXME: need to put the client (and callback) in the SDL device SDL_SemPost(this->hidden->iosem); /* unblock the SDL thread. */
  133. }
  134. static int
  135. ConnectToJackServer(void)
  136. {
  137. /* !!! FIXME: we _still_ need an API to specify an app name */
  138. jack_status_t status;
  139. JACK_client = JACK_jack_client_open("SDL", JackNoStartServer, &status, NULL);
  140. if (JACK_client == NULL) {
  141. return -1;
  142. }
  143. JACK_jack_on_shutdown(JACK_client, jackShutdownCallback, NULL);
  144. #if 0 // !!! FIXME: we need to move JACK_client into the SDL audio device.
  145. if (JACK_jack_activate(JACK_client) != 0) {
  146. DisconnectFromJackServer();
  147. return -1;
  148. }
  149. #endif
  150. return 0;
  151. }
  152. // !!! FIXME: implement and register these!
  153. //typedef int(* JackSampleRateCallback)(jack_nframes_t nframes, void *arg)
  154. //typedef int(* JackBufferSizeCallback)(jack_nframes_t nframes, void *arg)
  155. static int
  156. jackProcessPlaybackCallback(jack_nframes_t nframes, void *arg)
  157. {
  158. SDL_AudioDevice *this = (SDL_AudioDevice *) arg;
  159. jack_port_t **ports = this->hidden->sdlports;
  160. const int total_channels = this->spec.channels;
  161. const int total_frames = this->spec.samples;
  162. int channelsi;
  163. if (!SDL_AtomicGet(&this->enabled)) {
  164. /* silence the buffer to avoid repeats and corruption. */
  165. SDL_memset(this->hidden->iobuffer, '\0', this->spec.size);
  166. }
  167. for (channelsi = 0; channelsi < total_channels; channelsi++) {
  168. float *dst = (float *) JACK_jack_port_get_buffer(ports[channelsi], nframes);
  169. if (dst) {
  170. const float *src = ((float *) this->hidden->iobuffer) + channelsi;
  171. int framesi;
  172. for (framesi = 0; framesi < total_frames; framesi++) {
  173. *(dst++) = *src;
  174. src += total_channels;
  175. }
  176. }
  177. }
  178. SDL_SemPost(this->hidden->iosem); /* tell SDL thread we're done; refill the buffer. */
  179. return 0; /* success */
  180. }
  181. /* This function waits until it is possible to write a full sound buffer */
  182. static void
  183. JACK_WaitDevice(_THIS)
  184. {
  185. if (SDL_AtomicGet(&this->enabled)) {
  186. if (SDL_SemWait(this->hidden->iosem) == -1) {
  187. SDL_OpenedAudioDeviceDisconnected(this);
  188. }
  189. }
  190. }
  191. static Uint8 *
  192. JACK_GetDeviceBuf(_THIS)
  193. {
  194. return (Uint8 *) this->hidden->iobuffer;
  195. }
  196. #if 0 // !!! FIXME
  197. /* JACK thread calls this. */
  198. static int
  199. jackProcessCaptureCallback(jack_nframes_t nframes, void *arg)
  200. {
  201. jack_port_get_buffer(
  202. asdasd
  203. }
  204. /* SDL thread calls this. */
  205. static int
  206. JACK_CaptureFromDevice(_THIS, void *buffer, int buflen)
  207. {
  208. return SDL_SemWait(this->hidden->iosem) == 0) ? buflen : -1;
  209. }
  210. #endif
  211. static void
  212. JACK_CloseDevice(_THIS)
  213. {
  214. if (this->hidden->sdlports) {
  215. const int channels = this->spec.channels;
  216. int i;
  217. for (i = 0; i < channels; i++) {
  218. JACK_jack_port_unregister(JACK_client, this->hidden->sdlports[i]);
  219. }
  220. SDL_free(this->hidden->sdlports);
  221. }
  222. if (this->hidden->iosem) {
  223. SDL_DestroySemaphore(this->hidden->iosem);
  224. }
  225. if (this->hidden->devports) {
  226. JACK_jack_free(this->hidden->devports);
  227. }
  228. SDL_free(this->hidden->iobuffer);
  229. }
  230. static int
  231. JACK_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
  232. {
  233. /* Note that JACK uses "output" for capture devices (they output audio
  234. data to us) and "input" for playback (we input audio data to them).
  235. Likewise, SDL's playback port will be "output" (we write data out)
  236. and capture will be "input" (we read data in). */
  237. const unsigned long sysportflags = iscapture ? JackPortIsOutput : JackPortIsInput;
  238. const unsigned long sdlportflags = iscapture ? JackPortIsInput : JackPortIsOutput;
  239. const char *sdlportstr = iscapture ? "input" : "output";
  240. const char **devports = NULL;
  241. int channels = 0;
  242. int i;
  243. /* Initialize all variables that we clean on shutdown */
  244. this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof (*this->hidden));
  245. if (this->hidden == NULL) {
  246. return SDL_OutOfMemory();
  247. }
  248. devports = JACK_jack_get_ports(JACK_client, NULL, NULL, JackPortIsPhysical | sysportflags);
  249. this->hidden->devports = devports;
  250. if (!devports || !devports[0]) {
  251. return SDL_SetError("No physical JACK ports available");
  252. }
  253. while (devports[++channels]) {
  254. /* spin to count devports */
  255. }
  256. /* !!! 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." */
  257. /* Jack pretty much demands what it wants. */
  258. this->spec.format = AUDIO_F32SYS;
  259. this->spec.freq = JACK_jack_get_sample_rate(JACK_client);
  260. this->spec.channels = channels;
  261. this->spec.samples = JACK_jack_get_buffer_size(JACK_client);
  262. SDL_CalculateAudioSpec(&this->spec);
  263. this->hidden->iosem = SDL_CreateSemaphore(0);
  264. if (!this->hidden->iosem) {
  265. return -1; /* error was set by SDL_CreateSemaphore */
  266. }
  267. this->hidden->iobuffer = (float *) SDL_calloc(1, this->spec.size);
  268. if (!this->hidden->iobuffer) {
  269. return SDL_OutOfMemory();
  270. }
  271. /* Build SDL's ports, which we will connect to the device ports. */
  272. this->hidden->sdlports = (jack_port_t **) SDL_calloc(channels, sizeof (jack_port_t *));
  273. if (this->hidden->sdlports == NULL) {
  274. return SDL_OutOfMemory();
  275. }
  276. if (JACK_jack_set_process_callback(JACK_client, jackProcessPlaybackCallback, this) != 0) {
  277. return SDL_SetError("JACK: Couldn't set process callback");
  278. }
  279. for (i = 0; i < channels; i++) {
  280. char portname[32];
  281. SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
  282. this->hidden->sdlports[i] = JACK_jack_port_register(JACK_client, portname, JACK_DEFAULT_AUDIO_TYPE, sdlportflags, 0);
  283. if (this->hidden->sdlports[i] == NULL) {
  284. return SDL_SetError("jack_port_register failed");
  285. }
  286. }
  287. if (JACK_jack_activate(JACK_client) != 0) {
  288. return SDL_SetError("jack_activate failed");
  289. }
  290. /* once activated, we can connect all the ports. */
  291. for (i = 0; i < channels; i++) {
  292. char portname[32];
  293. SDL_snprintf(portname, sizeof (portname), "sdl_jack_%s_%d", sdlportstr, i);
  294. const char *sdlport = JACK_jack_port_name(this->hidden->sdlports[i]);
  295. const char *srcport = iscapture ? devports[i] : sdlport;
  296. const char *dstport = iscapture ? sdlport : devports[i];
  297. if (JACK_jack_connect(JACK_client, srcport, dstport) != 0) {
  298. return SDL_SetError("Couldn't connect JACK ports: %s => %s", srcport, dstport);
  299. }
  300. }
  301. /* don't need these anymore. */
  302. this->hidden->devports = NULL;
  303. JACK_jack_free(devports);
  304. /* We're ready to rock and roll. :-) */
  305. return 0;
  306. }
  307. static void
  308. JACK_Deinitialize(void)
  309. {
  310. DisconnectFromJackServer();
  311. UnloadJackLibrary();
  312. }
  313. static int
  314. JACK_Init(SDL_AudioDriverImpl * impl)
  315. {
  316. if (LoadJackLibrary() < 0) {
  317. return 0;
  318. }
  319. if (ConnectToJackServer() < 0) {
  320. UnloadJackLibrary();
  321. return 0;
  322. }
  323. /* Set the function pointers */
  324. impl->OnlyHasDefaultOutputDevice = SDL_TRUE;
  325. // !!! FIXME impl->OnlyHasDefaultCaptureDevice = SDL_TRUE;
  326. impl->OpenDevice = JACK_OpenDevice;
  327. impl->WaitDevice = JACK_WaitDevice;
  328. impl->GetDeviceBuf = JACK_GetDeviceBuf;
  329. impl->CloseDevice = JACK_CloseDevice;
  330. impl->Deinitialize = JACK_Deinitialize;
  331. // !!! FIXME impl->CaptureFromDevice = JACK_CaptureFromDevice;
  332. // !!! FIXME impl->HasCaptureSupport = SDL_TRUE;
  333. return 1; /* this audio target is available. */
  334. }
  335. AudioBootStrap JACK_bootstrap = {
  336. "jack", "JACK Audio Connection Kit", JACK_Init, 0
  337. };
  338. #endif /* SDL_AUDIO_DRIVER_JACK */
  339. /* vi: set ts=4 sw=4 expandtab: */