cannam@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ cannam@0: cannam@0: #ifdef HAVE_JACK cannam@0: cannam@0: #include "AudioJACKSource.h" cannam@0: #include "AudioCallbackRecordTarget.h" cannam@0: cannam@0: #include cannam@0: #include cannam@0: Chris@15: #include Chris@15: cannam@0: //#define DEBUG_AUDIO_JACK_SOURCE 1 cannam@0: cannam@0: #ifdef BUILD_STATIC cannam@0: #ifdef Q_OS_LINUX cannam@0: cannam@0: // Some lunacy to enable JACK support in static builds. JACK isn't cannam@0: // supposed to be linked statically, because it depends on a cannam@0: // consistent shared memory layout between client library and daemon, cannam@0: // so it's very fragile in the face of version mismatches. cannam@0: // cannam@0: // Therefore for static builds on Linux we avoid linking against JACK cannam@0: // at all during the build, instead using dlopen and runtime symbol cannam@0: // lookup to switch on JACK support at runtime. The following big cannam@0: // mess (down to the #endifs) is the code that implements this. cannam@0: cannam@0: static void *symbol(const char *name) cannam@0: { cannam@0: static bool attempted = false; cannam@0: static void *library = 0; cannam@0: static std::map symbols; cannam@0: if (symbols.find(name) != symbols.end()) return symbols[name]; cannam@0: if (!library) { cannam@0: if (!attempted) { cannam@0: library = ::dlopen("libjack.so.1", RTLD_NOW); cannam@0: if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW); cannam@0: if (!library) library = ::dlopen("libjack.so", RTLD_NOW); cannam@0: if (!library) { cannam@0: std::cerr << "WARNING: AudioJACKSource: Failed to load JACK library: " cannam@0: << ::dlerror() << " (tried .so, .so.0, .so.1)" cannam@0: << std::endl; cannam@0: } cannam@0: attempted = true; cannam@0: } cannam@0: if (!library) return 0; cannam@0: } cannam@0: void *symbol = ::dlsym(library, name); cannam@0: if (!symbol) { cannam@0: std::cerr << "WARNING: AudioJACKSource: Failed to locate symbol " cannam@0: << name << ": " << ::dlerror() << std::endl; cannam@0: } cannam@0: symbols[name] = symbol; cannam@0: return symbol; cannam@0: } cannam@0: Chris@15: static jack_client_t *dynamic_jack_client_open(const char *client_name, Chris@15: jack_options_t options, Chris@15: jack_status_t *status, ...) Chris@15: { Chris@15: typedef jack_client_t *(*func)(const char *client_name, Chris@15: jack_options_t options, Chris@15: jack_status_t *status, ...); Chris@15: void *s = symbol("jack_client_open"); Chris@15: if (!s) return 0; Chris@15: func f = (func)s; Chris@15: return f(client_name, options, status); // varargs not supported here Chris@15: } Chris@15: cannam@0: static int dynamic_jack_set_process_callback(jack_client_t *client, cannam@0: JackProcessCallback process_callback, cannam@0: void *arg) cannam@0: { cannam@0: typedef int (*func)(jack_client_t *client, cannam@0: JackProcessCallback process_callback, cannam@0: void *arg); cannam@0: void *s = symbol("jack_set_process_callback"); cannam@0: if (!s) return 1; cannam@0: func f = (func)s; cannam@0: return f(client, process_callback, arg); cannam@0: } cannam@0: cannam@0: static int dynamic_jack_set_xrun_callback(jack_client_t *client, cannam@0: JackXRunCallback xrun_callback, cannam@0: void *arg) cannam@0: { cannam@0: typedef int (*func)(jack_client_t *client, cannam@0: JackXRunCallback xrun_callback, cannam@0: void *arg); cannam@0: void *s = symbol("jack_set_xrun_callback"); cannam@0: if (!s) return 1; cannam@0: func f = (func)s; cannam@0: return f(client, xrun_callback, arg); cannam@0: } cannam@0: cannam@0: static const char **dynamic_jack_get_ports(jack_client_t *client, cannam@0: const char *port_name_pattern, cannam@0: const char *type_name_pattern, cannam@0: unsigned long flags) cannam@0: { cannam@0: typedef const char **(*func)(jack_client_t *client, cannam@0: const char *port_name_pattern, cannam@0: const char *type_name_pattern, cannam@0: unsigned long flags); cannam@0: void *s = symbol("jack_get_ports"); cannam@0: if (!s) return 0; cannam@0: func f = (func)s; cannam@0: return f(client, port_name_pattern, type_name_pattern, flags); cannam@0: } cannam@0: cannam@0: static jack_port_t *dynamic_jack_port_register(jack_client_t *client, cannam@0: const char *port_name, cannam@0: const char *port_type, cannam@0: unsigned long flags, cannam@0: unsigned long buffer_size) cannam@0: { cannam@0: typedef jack_port_t *(*func)(jack_client_t *client, cannam@0: const char *port_name, cannam@0: const char *port_type, cannam@0: unsigned long flags, cannam@0: unsigned long buffer_size); cannam@0: void *s = symbol("jack_port_register"); cannam@0: if (!s) return 0; cannam@0: func f = (func)s; cannam@0: return f(client, port_name, port_type, flags, buffer_size); cannam@0: } cannam@0: cannam@0: static int dynamic_jack_connect(jack_client_t *client, cannam@0: const char *source, cannam@0: const char *dest) cannam@0: { cannam@0: typedef int (*func)(jack_client_t *client, cannam@0: const char *source, cannam@0: const char *dest); cannam@0: void *s = symbol("jack_connect"); cannam@0: if (!s) return 1; cannam@0: func f = (func)s; cannam@0: return f(client, source, dest); cannam@0: } cannam@0: cannam@0: static void *dynamic_jack_port_get_buffer(jack_port_t *port, cannam@0: jack_nframes_t sz) cannam@0: { cannam@0: typedef void *(*func)(jack_port_t *, jack_nframes_t); cannam@0: void *s = symbol("jack_port_get_buffer"); cannam@0: if (!s) return 0; cannam@0: func f = (func)s; cannam@0: return f(port, sz); cannam@0: } cannam@0: cannam@0: static int dynamic_jack_port_unregister(jack_client_t *client, cannam@0: jack_port_t *port) cannam@0: { cannam@0: typedef int(*func)(jack_client_t *, jack_port_t *); cannam@0: void *s = symbol("jack_port_unregister"); cannam@0: if (!s) return 0; cannam@0: func f = (func)s; cannam@0: return f(client, port); cannam@0: } cannam@0: cannam@0: #define dynamic1(rv, name, argtype, failval) \ cannam@0: static rv dynamic_##name(argtype arg) { \ cannam@0: typedef rv (*func) (argtype); \ cannam@0: void *s = symbol(#name); \ cannam@0: if (!s) return failval; \ cannam@0: func f = (func) s; \ cannam@0: return f(arg); \ cannam@0: } cannam@0: cannam@0: dynamic1(jack_client_t *, jack_client_new, const char *, 0); cannam@0: dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0); cannam@0: dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0); cannam@0: dynamic1(int, jack_activate, jack_client_t *, 1); cannam@0: dynamic1(int, jack_deactivate, jack_client_t *, 1); cannam@0: dynamic1(int, jack_client_close, jack_client_t *, 1); cannam@0: dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0); cannam@0: dynamic1(const char *, jack_port_name, const jack_port_t *, 0); cannam@0: cannam@0: #define jack_client_new dynamic_jack_client_new Chris@15: #define jack_client_open dynamic_jack_client_open cannam@0: #define jack_get_buffer_size dynamic_jack_get_buffer_size cannam@0: #define jack_get_sample_rate dynamic_jack_get_sample_rate cannam@0: #define jack_set_process_callback dynamic_jack_set_process_callback cannam@0: #define jack_set_xrun_callback dynamic_jack_set_xrun_callback cannam@0: #define jack_activate dynamic_jack_activate cannam@0: #define jack_deactivate dynamic_jack_deactivate cannam@0: #define jack_client_close dynamic_jack_client_close cannam@0: #define jack_get_ports dynamic_jack_get_ports cannam@0: #define jack_port_register dynamic_jack_port_register cannam@0: #define jack_port_unregister dynamic_jack_port_unregister cannam@0: #define jack_port_get_latency dynamic_jack_port_get_latency cannam@0: #define jack_port_name dynamic_jack_port_name cannam@0: #define jack_connect dynamic_jack_connect cannam@0: #define jack_port_get_buffer dynamic_jack_port_get_buffer cannam@0: cannam@0: #endif cannam@0: #endif cannam@0: cannam@0: AudioJACKSource::AudioJACKSource(AudioCallbackRecordTarget *target) : cannam@0: AudioCallbackRecordSource(target), cannam@0: m_client(0), cannam@0: m_bufferSize(0), cannam@0: m_sampleRate(0) cannam@0: { Chris@15: JackOptions options = JackNullOption; Chris@15: #ifdef HAVE_PORTAUDIO_2_0 Chris@15: options = JackNoStartServer; Chris@15: #endif Chris@15: #ifdef HAVE_LIBPULSE Chris@15: options = JackNoStartServer; Chris@15: #endif Chris@15: Chris@15: JackStatus status = JackStatus(0); Chris@15: m_client = jack_client_open("vamp-live-host", options, &status); cannam@0: cannam@0: if (!m_client) { Chris@15: std::cerr Chris@15: << "ERROR: AudioJACKSource: Failed to connect to JACK server" Chris@15: << std::endl; Chris@15: return; cannam@0: } cannam@0: cannam@0: m_bufferSize = jack_get_buffer_size(m_client); cannam@0: m_sampleRate = jack_get_sample_rate(m_client); cannam@0: cannam@0: jack_set_xrun_callback(m_client, xrunStatic, this); cannam@0: jack_set_process_callback(m_client, processStatic, this); cannam@0: cannam@0: if (jack_activate(m_client)) { cannam@0: std::cerr << "ERROR: AudioJACKSource: Failed to activate JACK client" cannam@0: << std::endl; cannam@0: } cannam@0: cannam@0: m_target->setSourceBlockSize(m_bufferSize); cannam@0: m_target->setSourceSampleRate(m_sampleRate); cannam@0: cannam@0: size_t channels = m_target->getChannelCount(); cannam@0: cannam@0: while (m_inputs.size() < channels) { cannam@0: cannam@0: char name[20]; cannam@0: jack_port_t *port; cannam@0: Chris@15: sprintf(name, "in %d", (int)(m_inputs.size() + 1)); cannam@0: cannam@0: port = jack_port_register(m_client, cannam@0: name, cannam@0: JACK_DEFAULT_AUDIO_TYPE, cannam@0: JackPortIsInput, cannam@0: 0); cannam@0: cannam@0: if (!port) { cannam@0: std::cerr cannam@0: << "ERROR: AudioJACKSource: Failed to create JACK input port " cannam@0: << m_inputs.size() << std::endl; cannam@0: } else { cannam@0: m_target->setSourceRecordLatency(jack_port_get_latency(port)); cannam@0: } cannam@0: cannam@0: m_inputs.push_back(port); cannam@0: } cannam@0: } cannam@0: cannam@0: AudioJACKSource::~AudioJACKSource() cannam@0: { cannam@0: if (m_client) { cannam@0: jack_deactivate(m_client); cannam@0: jack_client_close(m_client); cannam@0: } cannam@0: } cannam@0: cannam@0: bool cannam@0: AudioJACKSource::isOK() const cannam@0: { cannam@0: return (m_client != 0); cannam@0: } cannam@0: cannam@0: int cannam@0: AudioJACKSource::processStatic(jack_nframes_t nframes, void *arg) cannam@0: { cannam@0: return ((AudioJACKSource *)arg)->process(nframes); cannam@0: } cannam@0: cannam@0: int cannam@0: AudioJACKSource::xrunStatic(void *arg) cannam@0: { cannam@0: return ((AudioJACKSource *)arg)->xrun(); cannam@0: } cannam@0: cannam@0: int cannam@0: AudioJACKSource::process(jack_nframes_t nframes) cannam@0: { cannam@0: if (m_inputs.size() < m_target->getChannelCount()) { cannam@0: return 0; cannam@0: } cannam@0: cannam@0: #ifdef DEBUG_AUDIO_JACK_SOURCE cannam@0: std::cout << "AudioJACKSource::process(" << nframes << "), have " << m_inputs.size() << " inputs" << std::endl; cannam@0: #endif cannam@0: cannam@0: #ifdef DEBUG_AUDIO_JACK_SOURCE cannam@0: if (m_bufferSize != nframes) { cannam@0: std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; cannam@0: } cannam@0: #endif cannam@0: cannam@0: float **buffers = (float **)alloca(m_inputs.size() * sizeof(float *)); cannam@0: cannam@0: for (size_t ch = 0; ch < m_inputs.size(); ++ch) { cannam@0: buffers[ch] = (float *)jack_port_get_buffer(m_inputs[ch], nframes); cannam@0: } cannam@0: cannam@0: if (m_target) { cannam@0: m_target->putSamples(nframes, buffers); cannam@0: } cannam@0: cannam@0: float peakLeft = 0.0, peakRight = 0.0; cannam@0: cannam@0: for (size_t ch = 0; ch < m_inputs.size(); ++ch) { cannam@0: cannam@0: float peak = 0.0; cannam@0: cannam@0: for (size_t i = 0; i < nframes; ++i) { cannam@0: float sample = fabsf(buffers[ch][i]); cannam@0: if (sample > peak) peak = sample; cannam@0: } cannam@0: cannam@0: if (ch == 0) peakLeft = peak; cannam@0: if (ch > 0 || m_inputs.size() == 1) peakRight = peak; cannam@0: } cannam@0: cannam@0: if (m_target) { cannam@0: m_target->setInputLevels(peakLeft, peakRight); cannam@0: } cannam@0: cannam@0: return 0; cannam@0: } cannam@0: cannam@0: int cannam@0: AudioJACKSource::xrun() cannam@0: { cannam@0: std::cerr << "AudioJACKSource: xrun!" << std::endl; cannam@0: if (m_target) m_target->audioProcessingOverload(); cannam@0: return 0; cannam@0: } cannam@0: cannam@0: #endif /* HAVE_JACK */ cannam@0: