Skip to content
This repository has been archived by the owner on Mar 27, 2020. It is now read-only.

Commit

Permalink
perform all jack calls via dynamically loaded shims
Browse files Browse the repository at this point in the history
  • Loading branch information
Skrylar committed Jan 11, 2019
1 parent 1f79a8a commit 1b2722e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 43 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down
82 changes: 72 additions & 10 deletions src/jaq.cc
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
#include "jaq.hh"
#include "skjack.hh"

#include <dlfcn.h>

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);
}

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,
Expand All @@ -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) {
Expand All @@ -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<z>(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;
}
Expand All @@ -74,3 +134,5 @@ bool client::close() {
}

} /* namespace jaq */

#undef PARTY_HAT_SUFFIX
74 changes: 42 additions & 32 deletions src/jaq.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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_default_audio_sample_t*>(
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
Expand All @@ -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) {}
Expand All @@ -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<jack_default_audio_sample_t*>(
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*/}
};
}

0 comments on commit 1b2722e

Please sign in to comment.