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