From 1b2722e37d897fed2a177d9ad8d21c2ebb48b33b Mon Sep 17 00:00:00 2001 From: Joshua Cearley Date: Thu, 10 Jan 2019 21:28:28 -0600 Subject: [PATCH] perform all jack calls via dynamically loaded shims --- Makefile | 4 ++- meson.build | 2 ++ src/jaq.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++++------- src/jaq.hh | 74 ++++++++++++++++++++++++++--------------------- 4 files changed, 119 insertions(+), 43 deletions(-) diff --git a/Makefile b/Makefile index 6f70473..aa84a9e 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,9 @@ CXXFLAGS += # Careful about linking to shared libraries, since you can't assume much about the user's environment and library search path. # Static libraries are fine. -LDFLAGS += $(pkg-config --libs jack) + +# NOTE: we explicitly don't link to jack anymore; we're using dlopen/dlsym to grab it at runtime +LDFLAGS += # Add .cpp and .c files to the build SOURCES += $(wildcard src/*.cc) diff --git a/meson.build b/meson.build index b6703df..630454d 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,8 @@ add_project_arguments(['-DSLUG=' + slug, '-DVERSION=' + meson.project_version(), add_project_arguments(['-Wall', '-Wextra', '-std=c++11', '-pedantic', '-g'], language: 'c') add_project_arguments(['-Wall', '-Wextra', '-std=c++11', '-pedantic', '-g'], language: 'cpp') +add_project_link_arguments(['-ldl'], language: 'cpp') + # TODO: swap this with a subproject reference once we subordinate rack to a workspace rack_include = include_directories('/opt/rack/include') diff --git a/src/jaq.cc b/src/jaq.cc index 63e70d2..f7341e3 100644 --- a/src/jaq.cc +++ b/src/jaq.cc @@ -1,9 +1,23 @@ -#include "jaq.hh" +#include "skjack.hh" #include namespace jaq { +void* client::lib = 0; + +jack_client_t* (*client::x_jack_client_open)(const char*, unsigned long, jack_status_t*); +jack_nframes_t (*client::x_jack_get_buffer_size)(jack_client_t*); +jack_nframes_t (*client::x_jack_get_sample_rate)(jack_client_t*); +int (*client::x_jack_set_buffer_size_callback)(jack_client_t*, JackBufferSizeCallback, void*); +int (*client::x_jack_set_sample_rate_callback)(jack_client_t*, JackSampleRateCallback, void*); +int (*client::x_jack_set_process_callback)(jack_client_t*, JackProcessCallback, void*); +int (*client::x_jack_port_rename)(jack_client_t*, jack_port_t*, const char*); +int (*client::x_jack_port_unregister)(jack_client_t*, jack_port_t*); +jack_port_t* (*client::x_jack_port_register)(jack_client_t*, const char*, const char*, unsigned long, unsigned long); +void* (*client::x_jack_port_get_buffer)(jack_port_t*, jack_nframes_t); +int (*client::x_jack_activate)(jack_client_t*); + bool port::alive() const { return (mom && mom->alive() && handle); } @@ -11,7 +25,7 @@ bool port::alive() const { bool port::register_audio(client& mom, const char* name, unsigned long flags) { if (!mom.alive()) return false; this->mom = &mom; - handle = jack_port_register( + handle = client::x_jack_port_register( mom.handle, name, JACK_DEFAULT_AUDIO_TYPE, @@ -27,13 +41,13 @@ bool port::register_audio(client& mom, const char* name, unsigned long flags) { bool port::rename(const std::string& new_name) { if (!alive()) return false; - jack_port_rename(mom->handle, handle, new_name.c_str()); + client::x_jack_port_rename(mom->handle, handle, new_name.c_str()); // FIXME renames can actually fail, but detecting that is spotty return true; } void port::unregister() { - if (alive()) jack_port_unregister(mom->handle, handle); + if (alive()) client::x_jack_port_unregister(mom->handle, handle); } int client::on_jack_buffer_size(jack_nframes_t nframes, void* dptr) { @@ -48,23 +62,69 @@ int client::on_jack_sample_rate(jack_nframes_t nframes, void* dptr) { return 0; } +#if ARCH_LIN +#define PARTY_HAT_SUFFIX ".so.0" +#elif ARCH_MAC +#define PARTY_HAT_SUFFIX ".dylib" +#else +#error "Your platform is not yet supported :(" +#endif + bool client::link() { + lib = dlopen("libjack" PARTY_HAT_SUFFIX, RTLD_LAZY); + if (!lib) { + warn("libjack" PARTY_HAT_SUFFIX " is not in linker path!"); + lib = dlopen("/usr/lib/libjack" PARTY_HAT_SUFFIX, RTLD_LAZY); + if (!lib) { + warn("/usr/lib/libjack" PARTY_HAT_SUFFIX " was not found either"); + lib = dlopen("/usr/lib/libjack" PARTY_HAT_SUFFIX, RTLD_LAZY); + if (!lib) { + warn("/usr/local/lib/libjack" PARTY_HAT_SUFFIX " was not found either"); + warn("I can't find any JACKs."); + return false; + } + } + } + + info("We linked to JACK :^)"); + +#define knab(y, z) x_##y = reinterpret_cast(dlsym(lib, #y)); if (!x_##y) { warn("Could not find " #y " in your JACK."); goto goddamnit; } + + knab(jack_client_open, jack_client_t* (*)(const char*, unsigned long, jack_status_t*)); + knab(jack_get_buffer_size, jack_nframes_t (*)(jack_client_t*)); + knab(jack_get_sample_rate, jack_nframes_t (*)(jack_client_t*)); + knab(jack_set_buffer_size_callback, int (*)(jack_client_t*, JackBufferSizeCallback, void*)); + knab(jack_set_sample_rate_callback, int (*)(jack_client_t*, JackSampleRateCallback, void*)); + knab(jack_set_process_callback, int (*)(jack_client_t*, JackProcessCallback, void*)); + knab(jack_port_rename, int (*)(jack_client_t*, jack_port_t*, const char*)); + knab(jack_port_unregister, int (*)(jack_client_t*, jack_port_t*)); + knab(jack_port_register, jack_port_t* (*)(jack_client_t*, const char*, const char*, unsigned long, unsigned long)); + knab(jack_port_get_buffer, void* (*)(jack_port_t*, jack_nframes_t)); + knab(jack_activate, int (*)(jack_client_t*)); + +#undef knab + return true; + +goddamnit: + dlclose(lib); + lib = 0; + return false; } bool client::open() { if (handle) return true; jack_status_t jstatus; - handle = jack_client_open("VCV Rack", JackNoStartServer, &jstatus); + handle = x_jack_client_open("VCV Rack", JackNoStartServer, &jstatus); if (!handle) return false; - buffersize_max = jack_get_buffer_size(handle); - buffersize = jack_get_buffer_size(handle); - samplerate = jack_get_sample_rate(handle); + buffersize_max = x_jack_get_buffer_size(handle); + buffersize = x_jack_get_buffer_size(handle); + samplerate = x_jack_get_sample_rate(handle); - jack_set_buffer_size_callback(handle, &on_jack_buffer_size, this); - jack_set_sample_rate_callback(handle, &on_jack_sample_rate, this); + x_jack_set_buffer_size_callback(handle, &on_jack_buffer_size, this); + x_jack_set_sample_rate_callback(handle, &on_jack_sample_rate, this); return true; } @@ -74,3 +134,5 @@ bool client::close() { } } /* namespace jaq */ + +#undef PARTY_HAT_SUFFIX diff --git a/src/jaq.hh b/src/jaq.hh index 0796fcb..f90d690 100644 --- a/src/jaq.hh +++ b/src/jaq.hh @@ -14,40 +14,22 @@ #pragma clang diagnostic pop namespace jaq { - struct client; - - struct port { - client* mom; - jack_port_t* handle; - - port() : mom(0), handle(0) {} - - bool alive() const; - - bool register_audio(client& mom, const char* name, unsigned long flags); - - void unregister(); - - // returns port memory as an array of samples; `samples` should match - // whatever you recieved via the `nsamples` parameter - inline jack_default_audio_sample_t* get_audio_buffer(jack_nframes_t nframes) { - if (alive()) - return reinterpret_cast( - jack_port_get_buffer(handle, nframes)); - - return NULL; - } - - bool rename(const std::string& new_name); - - private: - port(const port&) {/*don't ocopy that floppy*/} - }; - struct client { jack_client_t* handle; // handle to our jack client static void* lib; // handle to the jack library + static jack_nframes_t (*x_jack_get_buffer_size)(jack_client_t*); + static jack_nframes_t (*x_jack_get_sample_rate)(jack_client_t*); + static int (*x_jack_set_buffer_size_callback)(jack_client_t*, JackBufferSizeCallback, void*); + static int (*x_jack_set_sample_rate_callback)(jack_client_t*, JackSampleRateCallback, void*); + static int (*x_jack_set_process_callback)(jack_client_t*, JackProcessCallback, void*); + static jack_client_t* (*x_jack_client_open)(const char*, unsigned long, jack_status_t*); + static int (*x_jack_port_rename)(jack_client_t*, jack_port_t*, const char*); + static int (*x_jack_port_unregister)(jack_client_t*, jack_port_t*); + static jack_port_t* (*x_jack_port_register)(jack_client_t*, const char*, const char*, unsigned long, unsigned long); + static void* (*x_jack_port_get_buffer)(jack_port_t*, jack_nframes_t); + static int (*x_jack_activate)(jack_client_t*); + static bool link(); // try to dynamically link to jack // these are publicly read-only; we do keep them updated via callbacks though @@ -61,11 +43,11 @@ namespace jaq { inline bool alive() const { return handle != 0; } inline void activate() { - if (handle) jack_activate(handle); + if (handle) x_jack_activate(handle); } inline void set_process_callback(JackProcessCallback cb, void* user) { - if (handle) jack_set_process_callback(handle, cb, user); + if (handle) x_jack_set_process_callback(handle, cb, user); } client() : handle(0) {} @@ -76,4 +58,32 @@ namespace jaq { static int on_jack_buffer_size(jack_nframes_t nframes, void* arg); static int on_jack_sample_rate(jack_nframes_t nframes, void* arg); }; + + struct port { + client* mom; + jack_port_t* handle; + + port() : mom(0), handle(0) {} + + bool alive() const; + + bool register_audio(client& mom, const char* name, unsigned long flags); + + void unregister(); + + // returns port memory as an array of samples; `samples` should match + // whatever you recieved via the `nsamples` parameter + inline jack_default_audio_sample_t* get_audio_buffer(jack_nframes_t nframes) { + if (alive()) + return reinterpret_cast( + client::x_jack_port_get_buffer(handle, nframes)); + + return NULL; + } + + bool rename(const std::string& new_name); + + private: + port(const port&) {/*don't ocopy that floppy*/} + }; }