From db5428474a892e76ee14cc7e95919261078ed50b Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 12 Sep 2023 18:29:28 -0500 Subject: [PATCH] use x42/weakjack as a possible solution to #1480 --- CMakeLists.txt | 12 +- extern/weakjack/.gitignore | 1 + extern/weakjack/README.md | 79 +++++++++ extern/weakjack/weak_libjack.c | 289 +++++++++++++++++++++++++++++++ extern/weakjack/weak_libjack.def | 179 +++++++++++++++++++ extern/weakjack/weak_libjack.h | 230 ++++++++++++++++++++++++ src/audio/jack.cpp | 11 +- src/audio/jack.h | 3 +- 8 files changed, 796 insertions(+), 8 deletions(-) create mode 100644 extern/weakjack/.gitignore create mode 100644 extern/weakjack/README.md create mode 100644 extern/weakjack/weak_libjack.c create mode 100644 extern/weakjack/weak_libjack.def create mode 100644 extern/weakjack/weak_libjack.h diff --git a/CMakeLists.txt b/CMakeLists.txt index eaad23559..593dfb265 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,14 +357,16 @@ endif() if (WITH_JACK) find_package(PkgConfig REQUIRED) pkg_check_modules(JACK REQUIRED jack) + list(APPEND AUDIO_SOURCES extern/weakjack/weak_libjack.c) list(APPEND AUDIO_SOURCES src/audio/jack.cpp) list(APPEND DEPENDENCIES_INCLUDE_DIRS ${JACK_INCLUDE_DIRS}) list(APPEND DEPENDENCIES_DEFINES HAVE_JACK) - list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${JACK_CFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LIBRARIES ${JACK_LIBRARIES}) - list(APPEND DEPENDENCIES_LIBRARY_DIRS ${JACK_LIBRARY_DIRS}) - list(APPEND DEPENDENCIES_LINK_OPTIONS ${JACK_LDFLAGS_OTHER}) - list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${JACK_LDFLAGS}) + list(APPEND DEPENDENCIES_DEFINES USE_WEAK_JACK) + #list(APPEND DEPENDENCIES_COMPILE_OPTIONS ${JACK_CFLAGS_OTHER}) + #list(APPEND DEPENDENCIES_LIBRARIES ${JACK_LIBRARIES}) + #list(APPEND DEPENDENCIES_LIBRARY_DIRS ${JACK_LIBRARY_DIRS}) + #list(APPEND DEPENDENCIES_LINK_OPTIONS ${JACK_LDFLAGS_OTHER}) + #list(APPEND DEPENDENCIES_LEGACY_LDFLAGS ${JACK_LDFLAGS}) message(STATUS "Building with JACK support") else() message(STATUS "Building without JACK support") diff --git a/extern/weakjack/.gitignore b/extern/weakjack/.gitignore new file mode 100644 index 000000000..1377554eb --- /dev/null +++ b/extern/weakjack/.gitignore @@ -0,0 +1 @@ +*.swp diff --git a/extern/weakjack/README.md b/extern/weakjack/README.md new file mode 100644 index 000000000..7b957d0f4 --- /dev/null +++ b/extern/weakjack/README.md @@ -0,0 +1,79 @@ +Weak-JACK +========= + +This small library abstracts the [JACK](http://jackaudio.org) Application Binary Interface. + +Background and Motivation +------------------------- + +The jack shared library needs to be installed system-wide (for all jack applications +to share), it can not be part of an application itself. + +JACK developers take great care to not break binary compatibility of libjack. An +application compiled with one version of jack will work with all future versions +of jack. However, this only works well on GNU/Linux, BSD and to some extend on OSX. + +weak-jack linking is useful (at least) in the following cases: + +* the resulting application should not be directly linked to libjack.[so|dll|dylib] + IOW: the application should start even if libjack is not installed. +* the ABI of libjack is not stable. Note, this is only relevant for the Windows .dll + (e.g. applications compiled and linked with jack-1.9.9 will crash with jack-1.9.10 + on windows. -- MSVC has a workaround: link by function-name, not ordinal, mingw + does not offer this) +* Reference new API functions (e.g. meta-data API or new latency compensation) + in the application, which is not available on older versions of jack. + +Usage +----- + +1. Copy the source files into your application's source (or use a git submodule) +2. replace all `#include` in your sources with `#include "weak-jack.h"` +3. add `weak_libjack.c` to the build-source of your project + (in case your build-system does not detect `#include` dependencies automatically, + also reference the header and .def file). +4. Define `USE_WEAK_JACK` for all platforms where you want to use weak-linking. Usually + `CFLAGS+=-DUSE_WEAK_JACK CXXFLAGS+=-DUSE_WEAK_JACK` +5. Do not link your application to libjack (`-ljack`) when `USE_WEAK_JACK` is defined. + +Note the jack-headers still need to be present when compiling the application. + +The application code itself does not need to be changed. + +The first call to `jack_client_open()` will try to find and load libjack, if it cannot be +found, it will fail (return `NULL` and set `jack_status_t` if provided to `JackFailure`.) + +It is possible to explicitly initialize and query availability of libjack using +`have_libjack();` it returns 0 if libjack is available and can be used. (see the header +file for non-zero error codes). + +Caveats +------- + +If libjack is not available, all `jack_*` API calls are turned into no-operation functions. +This is not a problem in general, as jack-applications will not use any part of the jack API if +jack_client_open fails. The only exception here may be `jack_ringbuffer`. Note that the ringbuffer +implementation is also part of libjack and will not be available. + +The dummy implementation for the ringbuffer API is safe (read, writes are ignored and return failure +or zero-bytes length), but if your application depends on it to work, you're out of luck :) + +The function wrappers in `weak_libjack.def` were collected pragmatically it's quite possible that +some JACK API calls have been missed. If you application fails to link (without -ljack), please report +at https://github.com/x42/weakjack/issues + +License +------- + +GNU General Public License version 2 (or later). + +Alternatives +------------ + +An alternative, more liberally licensed, implementation that abstracts and wraps jack completely +(incl headers) can be found at +https://github.com/falkTX/Carla/tree/master/source/jackbridge (C++ only), +and a jack2 specific version at https://github.com/sletz/jack2/blob/master/common/JackWeakAPI.c + +A variant for python bindings is also provided by falkTX: +https://github.com/falkTX/Cadence/blob/master/src/jacklib.py diff --git a/extern/weakjack/weak_libjack.c b/extern/weakjack/weak_libjack.c new file mode 100644 index 000000000..23886de16 --- /dev/null +++ b/extern/weakjack/weak_libjack.c @@ -0,0 +1,289 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "weak_libjack.h" + +#ifndef USE_WEAK_JACK + +int have_libjack (void) { + return 0; +} + +#else + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +static void* lib_open(const char* const so) { +#ifdef _WIN32 + return (void*) LoadLibraryA(so); +#else + return dlopen(so, RTLD_NOW|RTLD_LOCAL); +#endif +} + +static void* lib_symbol(void* const lib, const char* const sym) { +#ifdef _WIN32 + return (void*) GetProcAddress((HMODULE)lib, sym); +#else + return dlsym(lib, sym); +#endif +} + +#if defined _MSC_VER && ! defined __INTEL_COMPILER +typedef void * pvoid_t; +#define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#elif defined NDEBUG +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#else +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) { \ + if (FAIL) { \ + fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \ + } \ + err |= FAIL; \ + } +#endif + +typedef void (* func_t) (void); + +/* function pointers to the real jack API */ +static struct WeakJack { + func_t _client_open; // special case due to varargs + +#define JCFUN(ERR, RTYPE, NAME, RVAL) func_t _ ## NAME ; +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) func_t _ ## NAME ; +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; + +#include "weak_libjack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN +} _j; + +static int _status = -1; +#if !defined _MSC_VER || defined __INTEL_COMPILER +__attribute__((constructor)) +#endif +static void init_weak_jack(void) +{ + void* lib; + int err = 0; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: initializing\n"); +#endif + + memset(&_j, 0, sizeof(_j)); + +#ifdef __APPLE__ + lib = lib_open("libjack.dylib"); + if (!lib) { + lib = lib_open("/usr/local/lib/libjack.dylib"); + } + if (!lib) { + /* New Homebrew location */ + lib = lib_open("/opt/homebrew/lib/libjack.dylib"); + if (lib) { + fprintf(stderr, "*** WEAK-JACK: using Homebrew\n"); + } + } + if (!lib) { + /* MacPorts location */ + lib = lib_open("/opt/local/lib/libjack.dylib"); + if (lib) { + fprintf(stderr, "*** WEAK-JACK: using MacPorts\n"); + } + } + +#elif (defined _WIN32) +# if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__) + lib = lib_open("libjack64.dll"); +# else + lib = lib_open("libjack.dll"); +# endif +#else + lib = lib_open("libjack.so.0"); +#endif + if (!lib) { +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: libjack was not found\n"); +#endif + _status = -2; + return; + } + + /* found library, now lookup functions */ + MAPSYM(client_open, 2) + +#define JCFUN(ERR, RTYPE, NAME, RVAL) MAPSYM(NAME, ERR) +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) MAPSYM(NAME, ERR) +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) + +#include "weak_libjack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + + /* if a required symbol is not found, disable JACK completly */ + if (err) { + _j._client_open = NULL; + } + _status = err; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status); +#endif +} + +int have_libjack (void) { + if (_status == -1) { + init_weak_jack(); + } + return _status; +} + +/******************************************************************************* + * helper macros + */ + +#if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#else +#define likely(expr) (expr) +#endif + +#ifndef NDEBUG +# define WJACK_WARNING(NAME) \ + fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME); +#else +# define WJACK_WARNING(NAME) ; +#endif + +/****************************************************************************** + * JACK API wrapper functions. + * + * if a function pointer is set in the static struct WeakJack _j, + * the function is called directly. + * Otherwise a dummy NOOP implementation is provided. + * The latter is mainly for compile-time warnings. + * + * If libjack is not found, jack_client_open() will fail. + * In that case the application should not call any other libjack + * functions. Hence a real implementation is not needed. + * (jack ringbuffer may be an exception for some apps) + */ + +/* dedicated support for jack_client_open(,..) variable arg function macro */ +func_t WJACK_get_client_open(void) { + if (_status == -1) { + init_weak_jack(); + } + return _j._client_open; +} + +/* callback to set status */ +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) { + WJACK_WARNING(client_open); + if (status) { *status = JackFailure; } + return NULL; +} + +/******************************************************************************* + * Macros to wrap jack API + */ + +/* abstraction for jack_client functions + * rtype jack_function_name (jack_client_t *client) { return rval; } + */ +#define JCFUN(ERR, RTYPE, NAME, RVAL) \ + RTYPE WJACK_ ## NAME (jack_client_t *client) { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for NOOP functions with return value + * rtype jack_function_name (ARGS) { return rval; } + */ +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for functions that need custom code. + * e.g. functions with return-value-pointer args, + * use CODE to initialize value + * + * rtype jack_function_name (ARGS) { CODE } + */ +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +/* abstraction for void functions with return-value-pointer args + * void jack_function_name (ARGS) { CODE } + */ +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) \ + void WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + ((void (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +#include "weak_libjack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + +#endif // end USE_WEAK_JACK diff --git a/extern/weakjack/weak_libjack.def b/extern/weakjack/weak_libjack.def new file mode 100644 index 000000000..fda83a4fc --- /dev/null +++ b/extern/weakjack/weak_libjack.def @@ -0,0 +1,179 @@ +/* macro-absraction of the JACK API + * + * see weak_libjack.c for details, in general arguments are: + * + * [required], [return type], [name], [arguments], [code or return value] + * + * This file is included multiple times with different macro definitions + * do not add header guards. + * see https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros + */ + +#ifdef USE_WEAK_JACK + +/* */ +JCFUN(1, int, client_close, 0) +JCFUN(1, char*, get_client_name, NULL) +JVFUN(0, on_shutdown, (jack_client_t *c, JackShutdownCallback s, void *a), (c,s,a),) +JVFUN(0, on_info_shutdown, (jack_client_t *c, JackInfoShutdownCallback s, void *a), (c,s,a),) + +JPFUN(1, int, set_process_callback, (jack_client_t *c, JackProcessCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_freewheel_callback, (jack_client_t *c, JackFreewheelCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_buffer_size_callback, (jack_client_t *c, JackBufferSizeCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_sample_rate_callback, (jack_client_t *c, JackSampleRateCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_registration_callback, (jack_client_t *c, JackPortRegistrationCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_connect_callback, (jack_client_t *c, JackPortConnectCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_graph_order_callback, (jack_client_t *c, JackGraphOrderCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_xrun_callback, (jack_client_t *c, JackXRunCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_latency_callback, (jack_client_t *c, JackLatencyCallback g, void *a), (c,g,a), -1) +JVFUN(1, set_error_function, (void (*f)(const char *)), (f),) +JVFUN(1, set_info_function, (void (*f)(const char *)), (f),) + +JCFUN(1, int, activate, -1) +JCFUN(1, int, deactivate, -1) + +JPFUN(1, int, client_name_size, (), (), 32) + +JCFUN(1, jack_nframes_t, get_sample_rate, 0) +JCFUN(1, jack_nframes_t, get_buffer_size, 0) +JPFUN(1, jack_nframes_t, frames_since_cycle_start, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, last_frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_time_t, get_time, (void), (), 0) +JCFUN(1, float, cpu_load, 0) +JCFUN(1, int, is_realtime, 0) + +JPFUN(1, int, set_freewheel, (jack_client_t *c, int o), (c,o), 0) +JPFUN(1, int, set_buffer_size, (jack_client_t *c, jack_nframes_t b), (c,b), 0) + +JCFUN(0, int, recompute_total_latencies, 0) +JPFUN(0, jack_nframes_t, port_get_total_latency, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JVFUN(0, 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;}) +JVFUN(0, port_set_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r),) + +JPFUN(1, void*, port_get_buffer, (jack_port_t *p, jack_nframes_t n), (p,n), NULL) +JPFUN(1, int, port_request_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_ensure_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_monitoring_input, (jack_port_t *p), (p), 0) + +JPFUN(1, const char*, port_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, const char*, port_short_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, int, port_flags, (const jack_port_t *p), (p), 0) +JPFUN(1, int, port_is_mine, (const jack_client_t *c, const jack_port_t *p), (c,p), 0) +JPFUN(1, int, port_connected, (const jack_port_t *p), (p), 0) +JPFUN(1, const char**, get_ports,(jack_client_t *c, const char *p, const char *t, unsigned long f), (c,p,t,f), NULL) +JPFUN(1, int, port_name_size, (void), (), 0) +JPFUN(1, int, port_type_size, (void), (), 0) +JPFUN(1, size_t, port_type_get_buffer_size, (jack_client_t *c, const char *t), (c,t), 0) +JPFUN(1, jack_port_t*, port_by_name, (jack_client_t *c, const char *n), (c,n), NULL) +JPFUN(1, jack_port_t*, port_by_id, (jack_client_t *c, jack_port_id_t i), (c,i), NULL) +JPFUN(1, 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) +JPFUN(1, int, port_unregister, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, const char *, port_type, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_connections, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_all_connections, (const jack_client_t *c, const jack_port_t *p), (c,p), 0) +JPFUN(1, int, port_set_name, (jack_port_t *p, const char *n), (p,n), -1) +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif +JXFUN(0, int, port_rename, (jack_client_t *c, jack_port_t *p, const char *n), (c,p,n), return jack_port_set_name (p,n);) +#if defined(__clang__) +#pragma clang diagnostic pop +#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif +JPFUN(1, int, port_get_aliases, (const jack_port_t *port, char* const aliases[2]), (port,aliases), 0) +JPFUN(1, int, port_disconnect, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, int, connect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JPFUN(1, int, disconnect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JVFUN(0, free, (void *p), (p), free(p);) + +JCFUN(1, jack_nframes_t, cycle_wait, 0) +JVFUN(1, cycle_signal, (jack_client_t *c, int s), (c,s),) +JPFUN(1, int, set_process_thread, (jack_client_t *c, JackThreadCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_thread_init_callback, (jack_client_t *c, JackThreadInitCallback p, void *a), (c,p,a), -1) + +JPFUN(1, int, transport_locate, (jack_client_t *c, jack_nframes_t f), (c,f), 0) +JVFUN(1, transport_start, (jack_client_t *c), (c),) +JVFUN(1, transport_stop, (jack_client_t *c), (c),) +JPFUN(1, jack_nframes_t, get_current_transport_frame, (const jack_client_t *c), (c), 0) +JXFUN(1, 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 JackTransportStopped;) +JPFUN(1, int, set_sync_callback, (jack_client_t *c, JackSyncCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_timebase_callback, (jack_client_t *c, int l, JackTimebaseCallback p, void *a), (c,l,p,a), -1) +JPFUN(1, int, set_sync_timeout, (jack_client_t *c, jack_time_t t), (c,t), -1) +JCFUN(1, int, release_timebase, 0) + +/* */ +JPFUN(1, uint32_t, midi_get_event_count, (void* p), (p), 0) +JPFUN(1, int, midi_event_get, (jack_midi_event_t *e, void *p, uint32_t i), (e,p,i), -1) +JPFUN(1, int, midi_event_write, (void *b, jack_nframes_t t, const jack_midi_data_t *d, size_t s), (b,t,d,s), -1) +JVFUN(1, midi_clear_buffer, (void *b), (b),) + +/* */ +JPFUN(0, int, set_session_callback, (jack_client_t *c, JackSessionCallback s, void *a), (c,s,a), -1) +JPFUN(0, int, session_reply, (jack_client_t *c, jack_session_event_t *e), (c,e), -1) +JVFUN(0, session_event_free, (jack_session_event_t *e), (e), ) + +/* */ +JPFUN(1, jack_ringbuffer_t *, ringbuffer_create, (size_t s), (s), NULL) +JVFUN(1, ringbuffer_free, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_reset, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_read_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JVFUN(1, ringbuffer_write_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JPFUN(1, size_t, ringbuffer_read_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_write_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_read, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) +JPFUN(1, size_t, ringbuffer_write, (jack_ringbuffer_t *rb, const char *s, size_t c), (rb,s,c), 0) +JPFUN(0, int, ringbuffer_mlock, (jack_ringbuffer_t *rb), (rb), 0) +JVFUN(0, ringbuffer_get_read_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JVFUN(0, ringbuffer_get_write_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JPFUN(0, size_t, ringbuffer_peek, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) + +/* */ +JCFUN(0, int, client_real_time_priority, 0) +JCFUN(0, int, client_max_real_time_priority, 0) +JPFUN(0, int, acquire_real_time_scheduling, (jack_native_thread_t t, int p), (t,p), 0) +JPFUN(0, int, drop_real_time_scheduling, (jack_native_thread_t t), (t), 0) +#if (!defined _WIN32 && (defined _REENTRANT || defined __linux__)) + /* on POSIX systems, call pthread_join() when libjack does not provide jack_client_stop_thread */ +JXFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), if (t) { pthread_join(t, NULL); return 0; } else { return -1;}) +#else +JPFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +#endif +JPFUN(0, int, client_kill_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +#ifndef _WIN32 +JVFUN(0, set_thread_creator, (jack_thread_creator_t c), (c),) +#endif +JPFUN(1, int, client_create_thread, \ + (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) + +#ifndef NO_JACK_METADATA +/* - TODO*/ + +/* */ +JPFUN(0, char *, get_uuid_for_client_name, (jack_client_t* c, const char* n), (c,n), NULL) +JPFUN(0, char *, get_client_name_by_uuid, (jack_client_t* c, const char* u), (c,u), NULL) +JPFUN(0, jack_uuid_t, port_uuid, (const jack_port_t *p), (p), 0) + +/* */ +JPFUN(0, int, set_property, (jack_client_t* c, jack_uuid_t s, const char* k, const char* v, const char* t), (c,s,k,v,t), -1) +JXFUN(0, int, get_property, (jack_uuid_t s, const char* k, char** v, char** t), (s,k,v,t), if (v) *v=NULL; if (t) *t=NULL; return -1;) +JVFUN(0, free_description, (jack_description_t* d, int f), (d,f),) +JXFUN(0, int, get_properties, (jack_uuid_t s, jack_description_t* d), (s,d), if (d) {d->properties = NULL; d->property_cnt = 0;} return -1;) +JXFUN(0, int, get_all_properties, (jack_description_t** d), (d), if (d) *d=NULL; return -1;) +JPFUN(0, int, remove_property, (jack_client_t* c, jack_uuid_t s, const char* k), (c,s,k), -1) +JPFUN(0, int, remove_properties, (jack_client_t* c, jack_uuid_t s), (c,s), -1) +JPFUN(0, int, remove_all_properties, (jack_client_t* c), (c), -1) +JPFUN(0, int, set_property_change_callback, (jack_client_t *c, JackPropertyChangeCallback s, void *a), (c,s,a), -1) +#endif + +/* */ +JCFUN(1, float, get_max_delayed_usecs, 0.0) +JCFUN(1, float, get_xrun_delayed_usecs, 0.0) +JVFUN(0, reset_max_delayed_usecs, (jack_client_t *c), (c),) + +#endif // end USE_WEAK_JACK diff --git a/extern/weakjack/weak_libjack.h b/extern/weakjack/weak_libjack.h new file mode 100644 index 000000000..e23f92389 --- /dev/null +++ b/extern/weakjack/weak_libjack.h @@ -0,0 +1,230 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * The wrapped jack API itself is + * (C) 2001 Paul Davis + * (C) 2004 Jack O'Quin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _WEAK_JACK_H +#define _WEAK_JACK_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** check if libjack is available + * + * return 0 if libjack is dynamically linked of was + * successfully dl-opened. Otherwise: + * + * -1: library was not initialized + * -2: libjack was not found + * > 0 bitwise flags: + * 1: a required function was not found in libjack + * 2: jack_client_open was not found in libjack + */ +int have_libjack(void); + +#ifdef __cplusplus +} +#endif + +#ifdef USE_WEAK_JACK + +/* */ +#define jack_client_close WJACK_client_close +#define jack_get_client_name WJACK_get_client_name +#define jack_get_sample_rate WJACK_get_sample_rate +#define jack_get_buffer_size WJACK_get_buffer_size +#define jack_frames_since_cycle_start WJACK_frames_since_cycle_start +#define jack_frame_time WJACK_frame_time +#define jack_last_frame_time WJACK_last_frame_time +#define jack_get_time WJACK_get_time +#define jack_cpu_load WJACK_cpu_load +#define jack_is_realtime WJACK_is_realtime + +#define jack_client_name_size WJACK_client_name_size + +#define jack_set_freewheel WJACK_set_freewheel +#define jack_set_buffer_size WJACK_set_buffer_size + +#define jack_on_shutdown WJACK_on_shutdown +#define jack_on_info_shutdown WJACK_on_info_shutdown +#define jack_set_process_callback WJACK_set_process_callback +#define jack_set_freewheel_callback WJACK_set_freewheel_callback +#define jack_set_buffer_size_callback WJACK_set_buffer_size_callback +#define jack_set_sample_rate_callback WJACK_set_sample_rate_callback +#define jack_set_port_registration_callback WJACK_set_port_registration_callback +#define jack_set_port_connect_callback WJACK_set_port_connect_callback +#define jack_set_graph_order_callback WJACK_set_graph_order_callback +#define jack_set_xrun_callback WJACK_set_xrun_callback +#define jack_set_latency_callback WJACK_set_latency_callback +#define jack_set_error_function WJACK_set_error_function +#define jack_set_info_function WJACK_set_info_function + +#define jack_activate WJACK_activate +#define jack_deactivate WJACK_deactivate + +#define jack_recompute_total_latencies WJACK_recompute_total_latencies +#define jack_port_get_total_latency WJACK_port_get_total_latency +#define jack_port_get_latency_range WJACK_port_get_latency_range +#define jack_port_set_latency_range WJACK_port_set_latency_range +#define jack_port_get_buffer WJACK_port_get_buffer +#define jack_port_request_monitor WJACK_port_request_monitor +#define jack_port_ensure_monitor WJACK_port_ensure_monitor +#define jack_port_monitoring_input WJACK_port_monitoring_input + +#define jack_port_name WJACK_port_name +#define jack_port_short_name WJACK_port_short_name +#define jack_port_flags WJACK_port_flags +#define jack_port_is_mine WJACK_port_is_mine +#define jack_port_connected WJACK_port_connected +#define jack_get_ports WJACK_get_ports +#define jack_port_name_size WJACK_port_name_size +#define jack_port_type_size WJACK_port_type_size +#define jack_port_type_get_buffer_size WJACK_port_type_get_buffer_size +#define jack_port_by_name WJACK_port_by_name +#define jack_port_by_id WJACK_port_by_id +#define jack_port_set_name WJACK_port_set_name +#define jack_port_get_aliases WJACK_port_get_aliases +#define jack_port_rename WJACK_port_rename +#define jack_port_disconnect WJACK_port_disconnect +#define jack_port_register WJACK_port_register +#define jack_port_unregister WJACK_port_unregister +#define jack_port_type WJACK_port_type +#define jack_port_get_connections WJACK_port_get_connections +#define jack_port_get_all_connections WJACK_port_get_all_connections +#define jack_connect WJACK_connect +#define jack_disconnect WJACK_disconnect +#define jack_free WJACK_free + +#define jack_cycle_wait WJACK_cycle_wait +#define jack_cycle_signal WJACK_cycle_signal +#define jack_set_process_thread WJACK_set_process_thread +#define jack_set_thread_init_callback WJACK_set_thread_init_callback + +/* */ +#define jack_get_current_transport_frame WJACK_get_current_transport_frame +#define jack_transport_locate WJACK_transport_locate +#define jack_transport_start WJACK_transport_start +#define jack_transport_stop WJACK_transport_stop +#define jack_transport_query WJACK_transport_query +#define jack_set_sync_callback WJACK_set_sync_callback +#define jack_set_timebase_callback WJACK_set_timebase_callback +#define jack_release_timebase WJACK_release_timebase + +/* */ +#define jack_midi_get_event_count WJACK_midi_get_event_count +#define jack_midi_event_get WJACK_midi_event_get +#define jack_midi_event_write WJACK_midi_event_write +#define jack_midi_clear_buffer WJACK_midi_clear_buffer + +/* */ +#define jack_set_session_callback WJACK_set_session_callback +#define jack_session_reply WJACK_session_reply +#define jack_session_event_free WJACK_session_event_free + +/* */ +#define jack_ringbuffer_create WJACK_ringbuffer_create +#define jack_ringbuffer_free WJACK_ringbuffer_free +#define jack_ringbuffer_reset WJACK_ringbuffer_reset +#define jack_ringbuffer_read_advance WJACK_ringbuffer_read_advance +#define jack_ringbuffer_write_advance WJACK_ringbuffer_write_advance +#define jack_ringbuffer_read_space WJACK_ringbuffer_read_space +#define jack_ringbuffer_write_space WJACK_ringbuffer_write_space +#define jack_ringbuffer_read WJACK_ringbuffer_read +#define jack_ringbuffer_write WJACK_ringbuffer_write +#define jack_ringbuffer_mlock WJACK_ringbuffer_mlock +#define jack_ringbuffer_get_read_vector WJACK_ringbuffer_get_read_vector +#define jack_ringbuffer_get_write_vector WJACK_ringbuffer_get_write_vector +#define jack_ringbuffer_peek WJACK_ringbuffer_peek + +/* */ +#define jack_client_real_time_priority WJACK_client_real_time_priority +#define jack_client_max_real_time_priority WJACK_client_max_real_time_priority +#define jack_acquire_real_time_scheduling WJACK_acquire_real_time_scheduling +#define jack_client_create_thread WJACK_client_create_thread +#define jack_drop_real_time_scheduling WJACK_drop_real_time_scheduling +#define jack_client_stop_thread WJACK_client_stop_thread +#define jack_client_kill_thread WJACK_client_kill_thread +#define jack_set_thread_creator WJACK_set_thread_creator + +#define jack_client_open WJACK_client_client_openXXX + +#ifndef NO_JACK_METADATA +/* */ +#define jack_get_uuid_for_client_name WJACK_get_uuid_for_client_name +#define jack_get_client_name_by_uuid WJACK_get_client_name_by_uuid +#define jack_port_uuid WJACK_port_uuid + +#define jack_set_property WJACK_set_property +#define jack_get_property WJACK_get_property +#define jack_free_description WJACK_free_description +#define jack_get_properties WJACK_get_properties +#define jack_get_all_properties WJACK_get_all_properties +#define jack_remove_property WJACK_remove_property +#define jack_remove_properties WJACK_remove_properties +#define jack_remove_all_properties WJACK_remove_all_properties +#define jack_set_property_change_callback WJACK_set_property_change_callback +#endif + +/* */ +#define jack_get_max_delayed_usecs WJACK_get_max_delayed_usecs +#define jack_get_xrun_delayed_usecs WJACK_get_xrun_delayed_usecs +#define jack_reset_max_delayed_usecs WJACK_reset_max_delayed_usecs + +#endif // end USE_WEAK_JACK + +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_JACK_METADATA +#include +#endif + +#ifdef USE_WEAK_JACK + +#undef jack_client_open + +/* var-args hack */ + +#ifdef __cplusplus +extern "C" { +#endif +void (* WJACK_get_client_open (void)) (void); +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); +#ifdef __cplusplus +} +#endif + +#define jack_client_open(...) \ +( \ + (WJACK_get_client_open() != NULL) \ + ? ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))(WJACK_get_client_open()))(__VA_ARGS__) \ + : WJACK_no_client_open(__VA_ARGS__) \ +) + +#endif // end USE_WEAK_JACK + +#endif // _WEAK_JACK_H diff --git a/src/audio/jack.cpp b/src/audio/jack.cpp index 2ffe19b63..64c106c54 100644 --- a/src/audio/jack.cpp +++ b/src/audio/jack.cpp @@ -198,10 +198,19 @@ String TAAudioJACK::printStatus(jack_status_t status) { bool TAAudioJACK::init(TAAudioDesc& request, TAAudioDesc& response) { if (initialized) return false; - if (jack_client_open==NULL) { + int haveJACK=have_libjack(); + if (haveJACK==-1) { + logE("JACK library not initialized!"); + return false; + } + if (haveJACK==-2) { logE("JACK not installed!"); return false; } + if (haveJACK!=0) { + logE("JACK symbol error!"); + return false; + } desc=request; desc.outFormat=TA_AUDIO_FORMAT_F32; diff --git a/src/audio/jack.h b/src/audio/jack.h index 8018d6c20..588814479 100644 --- a/src/audio/jack.h +++ b/src/audio/jack.h @@ -18,8 +18,7 @@ */ #include "taAudio.h" -#include -#include +#include "../extern/weakjack/weak_libjack.h" class TAAudioJACK: public TAAudio { jack_client_t* ac;