weak/runtime jack linking: load libjack dynamically at runtime
[ardour.git] / libs / backends / jack / weak_libjack.c
1 /* runtime/weak dynamic JACK linking
2  *
3  * (C) 2014 Robin Gareus <robin@gareus.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "weak_libjack.h"
21
22 #ifndef USE_WEAK_JACK
23
24 int have_libjack (void) {
25         return 0;
26 }
27
28 #else
29
30 #include <stdio.h>
31 #include <string.h>
32 #include <assert.h>
33
34 #ifdef PLATFORM_WINDOWS
35 #include <windows.h>
36 #else
37 #include <dlfcn.h>
38 #endif
39
40 #include <jack/jack.h>
41 #include <jack/ringbuffer.h>
42 #include <jack/transport.h>
43 #include <jack/midiport.h>
44 #include <jack/session.h>
45 #include <jack/thread.h>
46
47 static void* lib_open(const char* const so) {
48 #ifdef PLATFORM_WINDOWS
49         return (void*) LoadLibraryA(so);
50 #else
51         return dlopen(so, RTLD_NOW|RTLD_LOCAL);
52 #endif
53 }
54
55 static void* lib_symbol(void* const lib, const char* const sym) {
56 #ifdef PLATFORM_WINDOWS
57         return (void*) GetProcAddress((HMODULE)lib, sym);
58 #else
59         return dlsym(lib, sym);
60 #endif
61 }
62
63 #ifdef COMPILER_MSVC
64 typedef void * pvoid_t;
65 #define MAPSYM(SYM, FAIL) _j._ ## SYM = lib_symbol(lib, "jack_" # SYM); \
66         if (!_j._ ## SYM) err |= FAIL;
67 #else
68 typedef void * __attribute__ ((__may_alias__)) pvoid_t;
69 #define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \
70         if (!_j._ ## SYM) err |= FAIL;
71 #endif
72 typedef void (* func_t) (void);
73
74 /* function pointers to the real jack API */
75 static struct WeakJack {
76         func_t _client_open;
77         func_t _client_close;
78         func_t _get_client_name;
79
80         func_t _get_buffer_size;
81         func_t _get_sample_rate;
82         func_t _frames_since_cycle_start;
83         func_t _frame_time;
84         func_t _last_frame_time;
85         func_t _cpu_load;
86         func_t _is_realtime;
87
88         func_t _set_freewheel;
89         func_t _set_buffer_size;
90
91         func_t _on_shutdown;
92         func_t _on_info_shutdown;
93         func_t _set_process_callback;
94         func_t _set_freewheel_callback;
95         func_t _set_buffer_size_callback;
96         func_t _set_sample_rate_callback;
97         func_t _set_port_registration_callback;
98         func_t _set_port_connect_callback;
99         func_t _set_graph_order_callback;
100         func_t _set_xrun_callback;
101         func_t _set_latency_callback;
102         func_t _set_error_function;
103
104         func_t _activate;
105         func_t _deactivate;
106
107         func_t _recompute_total_latencies;
108         func_t _port_get_total_latency;
109         func_t _port_get_latency_range;
110         func_t _port_set_latency_range;
111         func_t _port_get_buffer;
112         func_t _port_request_monitor;
113         func_t _port_ensure_monitor;
114         func_t _port_monitoring_input;
115
116         func_t _port_name;
117         func_t _port_flags;
118         func_t _get_ports;
119         func_t _port_name_size;
120         func_t _port_type_size;
121         func_t _port_type_get_buffer_size;
122         func_t _port_by_name;
123         func_t _port_by_id;
124         func_t _port_register;
125         func_t _port_unregister;
126         func_t _port_type;
127         func_t _port_get_connections;
128         func_t _port_get_all_connections;
129         func_t _port_set_name;
130         func_t _port_disconnect;
131         func_t _connect;
132         func_t _disconnect;
133         func_t _free;
134         func_t _cycle_wait;
135         func_t _cycle_signal;
136         func_t _set_process_thread;
137         func_t _set_thread_init_callback;
138
139         func_t _get_current_transport_frame;
140         func_t _transport_locate;
141         func_t _transport_start;
142         func_t _transport_stop;
143         func_t _transport_query;
144         func_t _set_sync_callback;
145         func_t _set_timebase_callback;
146         func_t _release_timebase;
147
148         func_t _midi_get_event_count;
149         func_t _midi_event_get;
150         func_t _midi_event_write;
151         func_t _midi_clear_buffer;
152
153         func_t _set_session_callback;
154         func_t _session_reply;
155         func_t _session_event_free;
156
157         func_t _ringbuffer_create;
158         func_t _ringbuffer_free;
159         func_t _ringbuffer_reset;
160         func_t _ringbuffer_read_advance;
161         func_t _ringbuffer_write_advance;
162         func_t _ringbuffer_read_space;
163         func_t _ringbuffer_write_space;
164         func_t _ringbuffer_read;
165         func_t _ringbuffer_write;
166         func_t _ringbuffer_mlock;
167
168         func_t _client_real_time_priority;
169         func_t _client_max_real_time_priority;
170         func_t _acquire_real_time_scheduling;
171         func_t _drop_real_time_scheduling;
172         func_t _client_stop_thread;
173         func_t _client_kill_thread;
174         func_t _client_create_thread;
175 } _j;
176
177 static int _status = -1;
178
179 int have_libjack (void) {
180         return _status;
181 }
182
183 __attribute__((constructor))
184 static void init_weak_jack(void)
185 {
186         void* lib;
187         int err = 0;
188
189         memset(&_j, 0, sizeof(_j));
190
191 #ifdef __APPLE__
192         lib = lib_open("libjack.dylib");
193         if (!lib) {
194                 lib = lib_open("/usr/local/lib/libjack.dylib");
195         }
196 #elif (defined PLATFORM_WINDOWS)
197         lib = lib_open("libjack.dll");
198 #else
199         lib = lib_open("libjack.so");
200 #endif
201         if (!lib) {
202                 _status = -2;
203                 return;
204         }
205
206         MAPSYM(client_open, 2)
207         MAPSYM(client_close, 1)
208         MAPSYM(get_client_name, 1)
209         MAPSYM(get_sample_rate, 1)
210         MAPSYM(get_buffer_size, 1)
211         MAPSYM(frames_since_cycle_start, 1)
212         MAPSYM(frame_time, 1)
213         MAPSYM(last_frame_time, 1)
214         MAPSYM(cpu_load, 1)
215         MAPSYM(is_realtime, 1)
216         MAPSYM(set_freewheel, 1)
217         MAPSYM(set_buffer_size, 1)
218         MAPSYM(on_shutdown, 0)
219         MAPSYM(on_info_shutdown, 0)
220         MAPSYM(set_process_callback, 1)
221         MAPSYM(set_freewheel_callback, 1)
222         MAPSYM(set_buffer_size_callback, 1)
223         MAPSYM(set_sample_rate_callback, 1)
224         MAPSYM(set_port_registration_callback, 1)
225         MAPSYM(set_port_connect_callback, 1)
226         MAPSYM(set_graph_order_callback, 1)
227         MAPSYM(set_xrun_callback, 1)
228         MAPSYM(set_latency_callback, 1)
229         MAPSYM(set_error_function, 1)
230         MAPSYM(activate, 1)
231         MAPSYM(deactivate, 1)
232         MAPSYM(recompute_total_latencies, 0)
233         MAPSYM(port_get_total_latency, 0)
234         MAPSYM(port_get_latency_range, 0)
235         MAPSYM(port_set_latency_range, 0)
236         MAPSYM(port_get_buffer, 1)
237         MAPSYM(port_request_monitor, 1)
238         MAPSYM(port_ensure_monitor, 1)
239         MAPSYM(port_monitoring_input, 1)
240         MAPSYM(port_name, 1)
241         MAPSYM(port_flags, 1)
242         MAPSYM(get_ports, 1)
243         MAPSYM(port_name_size, 1)
244         MAPSYM(port_type_size, 1)
245         MAPSYM(port_type_get_buffer_size, 1)
246         MAPSYM(port_by_name, 1)
247         MAPSYM(port_by_id, 1)
248         MAPSYM(port_register, 1)
249         MAPSYM(port_unregister, 1)
250         MAPSYM(port_type, 1)
251         MAPSYM(port_get_connections, 1)
252         MAPSYM(port_get_all_connections, 1)
253         MAPSYM(port_set_name, 1)
254         MAPSYM(port_disconnect, 1)
255         MAPSYM(connect, 1)
256         MAPSYM(disconnect, 1)
257         MAPSYM(free, 0)
258         MAPSYM(cycle_wait, 0)
259         MAPSYM(cycle_signal, 0)
260         MAPSYM(set_process_thread, 0)
261         MAPSYM(set_thread_init_callback, 0)
262         MAPSYM(get_current_transport_frame, 1)
263         MAPSYM(transport_locate, 1)
264         MAPSYM(transport_start, 1)
265         MAPSYM(transport_stop, 1)
266         MAPSYM(transport_query, 1)
267         MAPSYM(set_sync_callback, 1)
268         MAPSYM(set_timebase_callback, 1)
269         MAPSYM(release_timebase, 1)
270         MAPSYM(midi_get_event_count, 1)
271         MAPSYM(midi_event_get, 1)
272         MAPSYM(midi_event_write, 1)
273         MAPSYM(midi_clear_buffer, 1)
274         MAPSYM(set_session_callback, 0)
275         MAPSYM(session_reply, 0)
276         MAPSYM(session_event_free, 0)
277         MAPSYM(ringbuffer_create, 1)
278         MAPSYM(ringbuffer_free, 1)
279         MAPSYM(ringbuffer_reset, 1)
280         MAPSYM(ringbuffer_read_advance, 1)
281         MAPSYM(ringbuffer_write_advance, 1)
282         MAPSYM(ringbuffer_read_space, 1)
283         MAPSYM(ringbuffer_write_space, 1)
284         MAPSYM(ringbuffer_read, 1)
285         MAPSYM(ringbuffer_write, 1)
286         MAPSYM(ringbuffer_mlock, 0)
287         MAPSYM(client_real_time_priority, 0)
288         MAPSYM(client_max_real_time_priority, 0)
289         MAPSYM(acquire_real_time_scheduling, 0)
290         MAPSYM(client_create_thread, 0)
291         MAPSYM(drop_real_time_scheduling, 0)
292         MAPSYM(client_stop_thread, 0)
293         MAPSYM(client_kill_thread, 0)
294
295         /* if a required symbol is not found, disable JACK completly */
296         if (err) {
297                 _j._client_open = NULL;
298         }
299
300         _status = err;
301 }
302
303 /*******************************************************************************
304  * Macros to wrap jack API
305  */
306
307 #ifndef NDEBUG
308 # define WJACK_WARNING(NAME) \
309         fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME);
310 #else
311 # define WJACK_WARNING(NAME) ;
312 #endif
313
314 /* abstraction for jack_client functions */
315 #define JCFUN(RTYPE, NAME, RVAL) \
316         RTYPE WJACK_ ## NAME (jack_client_t *client) { \
317                 if (_j._ ## NAME) { \
318                         return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \
319                 } else { \
320                         WJACK_WARNING(NAME) \
321                         return RVAL; \
322                 } \
323         }
324
325 /* abstraction for NOOP functions */
326 #define JPFUN(RTYPE, NAME, DEF, ARGS, RVAL) \
327         RTYPE WJACK_ ## NAME DEF { \
328                 if (_j._ ## NAME) { \
329                         return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
330                 } else { \
331                         WJACK_WARNING(NAME) \
332                         return RVAL; \
333                 } \
334         }
335
336 /* abstraction for functions with return-value-pointer args */
337 #define JXFUN(RTYPE, NAME, DEF, ARGS, CODE) \
338         RTYPE WJACK_ ## NAME DEF { \
339                 if (_j._ ## NAME) { \
340                         return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
341                 } else { \
342                         WJACK_WARNING(NAME) \
343                         CODE \
344                 } \
345         }
346
347 /* abstraction for void functions with return-value-pointer args */
348 #define JVFUN(RTYPE, NAME, DEF, ARGS, CODE) \
349         RTYPE WJACK_ ## NAME DEF { \
350                 if (_j._ ## NAME) { \
351                         ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \
352                 } else { \
353                         WJACK_WARNING(NAME) \
354                         CODE \
355                 } \
356         }
357
358 /******************************************************************************
359  * wrapper functions.
360  *
361  * if a function pointer is set in the static struct WeakJack _j,
362  * call the function, if not a dummy NOOP implementation is provided.
363  *
364  * The latter is mainly for the benefit for compile-time (warnings),
365  * if libjack is not found, jack_client_open() will fail and none
366  * of the application will never call any of the other functions.
367  */
368
369 /* <jack/jack.h> */
370
371 /* expand ellipsis for jack-session */
372 jack_client_t * WJACK_client_open2 (const char *client_name, jack_options_t options, jack_status_t *status, const char *uuid) {
373         if (_j._client_open) {
374                 return ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))(_j._client_open))(client_name, options, status, uuid);
375         } else {
376                 WJACK_WARNING(client_open);
377                 if (status) *status = 0;
378                 return NULL;
379         }
380 }
381
382 jack_client_t * WJACK_client_open1 (const char *client_name, jack_options_t options, jack_status_t *status) {
383         if (_j._client_open) {
384                 return ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))_j._client_open)(client_name, options, status);
385         } else {
386                 WJACK_WARNING(client_open);
387                 if (status) *status = 0;
388                 return NULL;
389         }
390 }
391
392 JCFUN(int,   client_close, 0)
393 JCFUN(char*, get_client_name, NULL)
394 JVFUN(void,  on_shutdown, (jack_client_t *c, JackShutdownCallback s, void *a), (c,s,a),)
395 JVFUN(void,  on_info_shutdown, (jack_client_t *c, JackInfoShutdownCallback s, void *a), (c,s,a),)
396
397 JPFUN(int,   set_process_callback, (jack_client_t *c, JackProcessCallback p, void *a), (c,p,a), -1)
398 JPFUN(int,   set_freewheel_callback, (jack_client_t *c, JackFreewheelCallback p, void *a), (c,p,a), -1)
399 JPFUN(int,   set_buffer_size_callback, (jack_client_t *c, JackBufferSizeCallback p, void *a), (c,p,a), -1)
400 JPFUN(int,   set_sample_rate_callback, (jack_client_t *c, JackSampleRateCallback p, void *a), (c,p,a), -1)
401 JPFUN(int,   set_port_registration_callback, (jack_client_t *c, JackPortRegistrationCallback p, void *a), (c,p,a), -1)
402 JPFUN(int,   set_port_connect_callback, (jack_client_t *c, JackPortConnectCallback p, void *a), (c,p,a), -1)
403 JPFUN(int,   set_graph_order_callback, (jack_client_t *c, JackGraphOrderCallback g, void *a), (c,g,a), -1)
404 JPFUN(int,   set_xrun_callback, (jack_client_t *c, JackXRunCallback g, void *a), (c,g,a), -1)
405 JPFUN(int,   set_latency_callback, (jack_client_t *c, JackLatencyCallback g, void *a), (c,g,a), -1)
406 JVFUN(void,  set_error_function, (void (*f)(const char *)), (f),)
407
408 JCFUN(int,   activate, -1)
409 JCFUN(int,   deactivate, -1)
410
411 JCFUN(jack_nframes_t, get_sample_rate, 0)
412 JCFUN(jack_nframes_t, get_buffer_size, 0)
413 JPFUN(jack_nframes_t, frames_since_cycle_start, (const jack_client_t *c), (c), 0)
414 JPFUN(jack_nframes_t, frame_time, (const jack_client_t *c), (c), 0)
415 JPFUN(jack_nframes_t, last_frame_time, (const jack_client_t *c), (c), 0)
416 JCFUN(float,          cpu_load, 0)
417 JCFUN(int,            is_realtime, 0)
418
419 JPFUN(int, set_freewheel, (jack_client_t *c, int o), (c,o), 0)
420 JPFUN(int, set_buffer_size, (jack_client_t *c, jack_nframes_t b), (c,b), 0)
421
422 JCFUN(int,            recompute_total_latencies, 0)
423 JPFUN(jack_nframes_t, port_get_total_latency, (jack_client_t *c, jack_port_t *p), (c,p), 0)
424 JVFUN(void,           port_get_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r), if (r) {r->min = r->max = 0;})
425 JVFUN(void,           port_set_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r),)
426 JPFUN(void*,          port_get_buffer, (jack_port_t *p, jack_nframes_t n), (p,n), NULL)
427 JPFUN(int,            port_request_monitor, (jack_port_t *p, int o), (p,o), 0)
428 JPFUN(int,            port_ensure_monitor, (jack_port_t *p, int o), (p,o), 0)
429 JPFUN(int,            port_monitoring_input, (jack_port_t *p), (p), 0)
430
431 JPFUN(const char*,    port_name, (const jack_port_t *p), (p), NULL)
432 JPFUN(int,            port_flags, (const jack_port_t *p), (p), 0)
433 JPFUN(const char**,   get_ports,(jack_client_t *c, const char *p, const char *t, unsigned long f), (c,p,t,f), NULL)
434 JPFUN(int,            port_name_size, (void), (), 0)
435 JPFUN(int,            port_type_size, (void), (), 0)
436 JPFUN(size_t,         port_type_get_buffer_size, (jack_client_t *c, const char *t), (c,t), 0)
437 JPFUN(jack_port_t*,   port_by_name, (jack_client_t *c, const char *n), (c,n), NULL)
438 JPFUN(jack_port_t*,   port_by_id, (jack_client_t *c, jack_port_id_t i), (c,i), NULL)
439 JPFUN(jack_port_t*,   port_register, (jack_client_t *c, const char *n, const char *t, unsigned long f, unsigned long b), (c,n,t,f,b), NULL)
440 JPFUN(int,            port_unregister, (jack_client_t *c, jack_port_t *p), (c,p), 0)
441 JPFUN(const char *,   port_type, (const jack_port_t *p), (p), 0)
442 JPFUN(const char **,  port_get_connections, (const jack_port_t *p), (p), 0)
443 JPFUN(const char **,  port_get_all_connections, (const jack_client_t *c, const jack_port_t *p), (c,p), 0)
444 JPFUN(int,            port_set_name, (jack_port_t *p, const char *n), (p,n), 0)
445 JPFUN(int,            port_disconnect, (jack_client_t *c, jack_port_t *p), (c,p), 0)
446 JPFUN(int,            connect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1)
447 JPFUN(int,            disconnect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1)
448 JVFUN(void,           free, (void *p), (p), free(p);)
449 JCFUN(jack_nframes_t, cycle_wait, 0)
450 JVFUN(void,           cycle_signal, (jack_client_t *c, int s), (c,s),)
451 JPFUN(int,            set_process_thread, (jack_client_t *c, JackThreadCallback p, void *a), (c,p,a), -1)
452 JPFUN(int,            set_thread_init_callback, (jack_client_t *c, JackThreadInitCallback p, void *a), (c,p,a), -1)
453
454 JPFUN(int,  transport_locate, (jack_client_t *c, jack_nframes_t f), (c,f), 0)
455 JVFUN(void, transport_start, (jack_client_t *c), (c),)
456 JVFUN(void, transport_stop, (jack_client_t *c), (c),)
457 JPFUN(jack_nframes_t, get_current_transport_frame, (const jack_client_t *c), (c), 0)
458 JXFUN(jack_transport_state_t, transport_query, (const jack_client_t *c, jack_position_t *p), (c,p), memset(p, 0, sizeof(jack_position_t)); return 0;)
459 JPFUN(int,  set_sync_callback, (jack_client_t *c, JackSyncCallback p, void *a), (c,p,a), -1)
460 JPFUN(int,  set_timebase_callback, (jack_client_t *c, int l, JackTimebaseCallback p, void *a), (c,l,p,a), -1)
461 JCFUN(int,  release_timebase, 0)
462
463 /* <jack/midiport.h> */
464 JPFUN(uint32_t, midi_get_event_count, (void* p), (p), 0)
465 JPFUN(int,      midi_event_get, (jack_midi_event_t *e, void *p, uint32_t i), (e,p,i), -1)
466 JPFUN(int,      midi_event_write, (void *b, jack_nframes_t t, const jack_midi_data_t *d, size_t s), (b,t,d,s), -1)
467 JVFUN(void,     midi_clear_buffer, (void *b), (b),)
468
469 /* <jack/session.h> */
470 JPFUN(int, set_session_callback, (jack_client_t *c, JackSessionCallback s, void *a), (c,s,a), -1)
471 JPFUN(int, session_reply, (jack_client_t *c, jack_session_event_t *e), (c,e), -1)
472 JVFUN(void, session_event_free, (jack_session_event_t *e), (e), )
473
474 /* <jack/ringbuffer.h> */
475 JPFUN(jack_ringbuffer_t *, ringbuffer_create, (size_t s), (s), NULL)
476 JVFUN(void, ringbuffer_free, (jack_ringbuffer_t *rb), (rb), )
477 JVFUN(void, ringbuffer_reset, (jack_ringbuffer_t *rb), (rb), )
478 JVFUN(void, ringbuffer_read_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), )
479 JVFUN(void, ringbuffer_write_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), )
480 JPFUN(size_t, ringbuffer_read_space, (const jack_ringbuffer_t *rb), (rb), 0)
481 JPFUN(size_t, ringbuffer_write_space, (const jack_ringbuffer_t *rb), (rb), 0)
482 JPFUN(size_t, ringbuffer_read, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0)
483 JPFUN(size_t, ringbuffer_write, (jack_ringbuffer_t *rb, const char *s, size_t c), (rb,s,c), 0)
484 JPFUN(int, ringbuffer_mlock, (jack_ringbuffer_t *rb), (rb), 0)
485
486 /* <jack/thread.h> */
487 JCFUN(int, client_real_time_priority, 0)
488 JCFUN(int, client_max_real_time_priority, 0)
489 JPFUN(int, acquire_real_time_scheduling, (jack_native_thread_t t, int p), (t,p), 0)
490 JPFUN(int, drop_real_time_scheduling, (jack_native_thread_t t), (t), 0)
491 JPFUN(int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0)
492 JPFUN(int, client_kill_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0)
493 JPFUN(int, client_create_thread, \
494                 (jack_client_t* c, jack_native_thread_t *t, int p, int r, void *(*f)(void*), void *a), (c,t,p,r,f,a), 0)
495
496 #endif