/* 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" #define NDEBUG #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