lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: This file copyright 2006 Chris Cannam. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #ifdef HAVE_JACK lbajardsilogic@0: lbajardsilogic@0: #include "AudioJACKTarget.h" lbajardsilogic@0: #include "AudioCallbackPlaySource.h" lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: //#define DEBUG_AUDIO_JACK_TARGET 1 lbajardsilogic@0: lbajardsilogic@0: #ifdef BUILD_STATIC lbajardsilogic@0: #ifdef Q_OS_LINUX lbajardsilogic@0: lbajardsilogic@0: // Some lunacy to enable JACK support in static builds. JACK isn't lbajardsilogic@0: // supposed to be linked statically, because it depends on a lbajardsilogic@0: // consistent shared memory layout between client library and daemon, lbajardsilogic@0: // so it's very fragile in the face of version mismatches. lbajardsilogic@0: // lbajardsilogic@0: // Therefore for static builds on Linux we avoid linking against JACK lbajardsilogic@0: // at all during the build, instead using dlopen and runtime symbol lbajardsilogic@0: // lookup to switch on JACK support at runtime. The following big lbajardsilogic@0: // mess (down to the #endifs) is the code that implements this. lbajardsilogic@0: lbajardsilogic@0: static void *symbol(const char *name) lbajardsilogic@0: { lbajardsilogic@0: static bool attempted = false; lbajardsilogic@0: static void *library = 0; lbajardsilogic@0: static std::map symbols; lbajardsilogic@0: if (symbols.find(name) != symbols.end()) return symbols[name]; lbajardsilogic@0: if (!library) { lbajardsilogic@0: if (!attempted) { lbajardsilogic@0: library = ::dlopen("libjack.so.1", RTLD_NOW); lbajardsilogic@0: if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW); lbajardsilogic@0: if (!library) library = ::dlopen("libjack.so", RTLD_NOW); lbajardsilogic@0: if (!library) { lbajardsilogic@0: std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: " lbajardsilogic@0: << ::dlerror() << " (tried .so, .so.0, .so.1)" lbajardsilogic@0: << std::endl; lbajardsilogic@0: } lbajardsilogic@0: attempted = true; lbajardsilogic@0: } lbajardsilogic@0: if (!library) return 0; lbajardsilogic@0: } lbajardsilogic@0: void *symbol = ::dlsym(library, name); lbajardsilogic@0: if (!symbol) { lbajardsilogic@0: std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol " lbajardsilogic@0: << name << ": " << ::dlerror() << std::endl; lbajardsilogic@0: } lbajardsilogic@0: symbols[name] = symbol; lbajardsilogic@0: return symbol; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static int dynamic_jack_set_process_callback(jack_client_t *client, lbajardsilogic@0: JackProcessCallback process_callback, lbajardsilogic@0: void *arg) lbajardsilogic@0: { lbajardsilogic@0: typedef int (*func)(jack_client_t *client, lbajardsilogic@0: JackProcessCallback process_callback, lbajardsilogic@0: void *arg); lbajardsilogic@0: void *s = symbol("jack_set_process_callback"); lbajardsilogic@0: if (!s) return 1; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, process_callback, arg); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static int dynamic_jack_set_xrun_callback(jack_client_t *client, lbajardsilogic@0: JackXRunCallback xrun_callback, lbajardsilogic@0: void *arg) lbajardsilogic@0: { lbajardsilogic@0: typedef int (*func)(jack_client_t *client, lbajardsilogic@0: JackXRunCallback xrun_callback, lbajardsilogic@0: void *arg); lbajardsilogic@0: void *s = symbol("jack_set_xrun_callback"); lbajardsilogic@0: if (!s) return 1; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, xrun_callback, arg); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static const char **dynamic_jack_get_ports(jack_client_t *client, lbajardsilogic@0: const char *port_name_pattern, lbajardsilogic@0: const char *type_name_pattern, lbajardsilogic@0: unsigned long flags) lbajardsilogic@0: { lbajardsilogic@0: typedef const char **(*func)(jack_client_t *client, lbajardsilogic@0: const char *port_name_pattern, lbajardsilogic@0: const char *type_name_pattern, lbajardsilogic@0: unsigned long flags); lbajardsilogic@0: void *s = symbol("jack_get_ports"); lbajardsilogic@0: if (!s) return 0; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, port_name_pattern, type_name_pattern, flags); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static jack_port_t *dynamic_jack_port_register(jack_client_t *client, lbajardsilogic@0: const char *port_name, lbajardsilogic@0: const char *port_type, lbajardsilogic@0: unsigned long flags, lbajardsilogic@0: unsigned long buffer_size) lbajardsilogic@0: { lbajardsilogic@0: typedef jack_port_t *(*func)(jack_client_t *client, lbajardsilogic@0: const char *port_name, lbajardsilogic@0: const char *port_type, lbajardsilogic@0: unsigned long flags, lbajardsilogic@0: unsigned long buffer_size); lbajardsilogic@0: void *s = symbol("jack_port_register"); lbajardsilogic@0: if (!s) return 0; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, port_name, port_type, flags, buffer_size); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static int dynamic_jack_connect(jack_client_t *client, lbajardsilogic@0: const char *source, lbajardsilogic@0: const char *dest) lbajardsilogic@0: { lbajardsilogic@0: typedef int (*func)(jack_client_t *client, lbajardsilogic@0: const char *source, lbajardsilogic@0: const char *dest); lbajardsilogic@0: void *s = symbol("jack_connect"); lbajardsilogic@0: if (!s) return 1; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, source, dest); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static void *dynamic_jack_port_get_buffer(jack_port_t *port, lbajardsilogic@0: jack_nframes_t sz) lbajardsilogic@0: { lbajardsilogic@0: typedef void *(*func)(jack_port_t *, jack_nframes_t); lbajardsilogic@0: void *s = symbol("jack_port_get_buffer"); lbajardsilogic@0: if (!s) return 0; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(port, sz); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: static int dynamic_jack_port_unregister(jack_client_t *client, lbajardsilogic@0: jack_port_t *port) lbajardsilogic@0: { lbajardsilogic@0: typedef int(*func)(jack_client_t *, jack_port_t *); lbajardsilogic@0: void *s = symbol("jack_port_unregister"); lbajardsilogic@0: if (!s) return 0; lbajardsilogic@0: func f = (func)s; lbajardsilogic@0: return f(client, port); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #define dynamic1(rv, name, argtype, failval) \ lbajardsilogic@0: static rv dynamic_##name(argtype arg) { \ lbajardsilogic@0: typedef rv (*func) (argtype); \ lbajardsilogic@0: void *s = symbol(#name); \ lbajardsilogic@0: if (!s) return failval; \ lbajardsilogic@0: func f = (func) s; \ lbajardsilogic@0: return f(arg); \ lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: dynamic1(jack_client_t *, jack_client_new, const char *, 0); lbajardsilogic@0: dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0); lbajardsilogic@0: dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0); lbajardsilogic@0: dynamic1(int, jack_activate, jack_client_t *, 1); lbajardsilogic@0: dynamic1(int, jack_deactivate, jack_client_t *, 1); lbajardsilogic@0: dynamic1(int, jack_client_close, jack_client_t *, 1); lbajardsilogic@0: dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0); lbajardsilogic@0: dynamic1(const char *, jack_port_name, const jack_port_t *, 0); lbajardsilogic@0: lbajardsilogic@0: #define jack_client_new dynamic_jack_client_new lbajardsilogic@0: #define jack_get_buffer_size dynamic_jack_get_buffer_size lbajardsilogic@0: #define jack_get_sample_rate dynamic_jack_get_sample_rate lbajardsilogic@0: #define jack_set_process_callback dynamic_jack_set_process_callback lbajardsilogic@0: #define jack_set_xrun_callback dynamic_jack_set_xrun_callback lbajardsilogic@0: #define jack_activate dynamic_jack_activate lbajardsilogic@0: #define jack_deactivate dynamic_jack_deactivate lbajardsilogic@0: #define jack_client_close dynamic_jack_client_close lbajardsilogic@0: #define jack_get_ports dynamic_jack_get_ports lbajardsilogic@0: #define jack_port_register dynamic_jack_port_register lbajardsilogic@0: #define jack_port_unregister dynamic_jack_port_unregister lbajardsilogic@0: #define jack_port_get_latency dynamic_jack_port_get_latency lbajardsilogic@0: #define jack_port_name dynamic_jack_port_name lbajardsilogic@0: #define jack_connect dynamic_jack_connect lbajardsilogic@0: #define jack_port_get_buffer dynamic_jack_port_get_buffer lbajardsilogic@0: lbajardsilogic@0: #endif lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) : lbajardsilogic@0: AudioCallbackPlayTarget(source), lbajardsilogic@0: m_client(0), lbajardsilogic@0: m_bufferSize(0), lbajardsilogic@0: m_sampleRate(0) lbajardsilogic@0: { lbajardsilogic@0: char name[100]; lbajardsilogic@0: strcpy(name, "Sonic Visualiser"); lbajardsilogic@0: m_client = jack_client_new(name); lbajardsilogic@0: lbajardsilogic@0: if (!m_client) { lbajardsilogic@0: sprintf(name, "Sonic Visualiser (%d)", (int)getpid()); lbajardsilogic@0: m_client = jack_client_new(name); lbajardsilogic@0: if (!m_client) { lbajardsilogic@0: std::cerr lbajardsilogic@0: << "ERROR: AudioJACKTarget: Failed to connect to JACK server" lbajardsilogic@0: << std::endl; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (!m_client) return; lbajardsilogic@0: lbajardsilogic@0: m_bufferSize = jack_get_buffer_size(m_client); lbajardsilogic@0: m_sampleRate = jack_get_sample_rate(m_client); lbajardsilogic@0: lbajardsilogic@0: jack_set_xrun_callback(m_client, xrunStatic, this); lbajardsilogic@0: jack_set_process_callback(m_client, processStatic, this); lbajardsilogic@0: lbajardsilogic@0: if (jack_activate(m_client)) { lbajardsilogic@0: std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client" lbajardsilogic@0: << std::endl; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_source) { lbajardsilogic@0: sourceModelReplaced(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: AudioJACKTarget::~AudioJACKTarget() lbajardsilogic@0: { lbajardsilogic@0: if (m_client) { lbajardsilogic@0: jack_deactivate(m_client); lbajardsilogic@0: jack_client_close(m_client); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: AudioJACKTarget::isOK() const lbajardsilogic@0: { lbajardsilogic@0: return (m_client != 0); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg) lbajardsilogic@0: { lbajardsilogic@0: return ((AudioJACKTarget *)arg)->process(nframes); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioJACKTarget::xrunStatic(void *arg) lbajardsilogic@0: { lbajardsilogic@0: return ((AudioJACKTarget *)arg)->xrun(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: AudioJACKTarget::sourceModelReplaced() lbajardsilogic@0: { lbajardsilogic@0: m_mutex.lock(); lbajardsilogic@0: lbajardsilogic@0: m_source->setTargetBlockSize(m_bufferSize); lbajardsilogic@0: m_source->setTargetSampleRate(m_sampleRate); lbajardsilogic@0: lbajardsilogic@0: size_t channels = m_source->getSourceChannelCount(); lbajardsilogic@0: lbajardsilogic@0: // Because we offer pan, we always want at least 2 channels lbajardsilogic@0: if (channels < 2) channels = 2; lbajardsilogic@0: lbajardsilogic@0: if (channels == m_outputs.size() || !m_client) { lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: const char **ports = lbajardsilogic@0: jack_get_ports(m_client, NULL, NULL, lbajardsilogic@0: JackPortIsPhysical | JackPortIsInput); lbajardsilogic@0: size_t physicalPortCount = 0; lbajardsilogic@0: while (ports[physicalPortCount]) ++physicalPortCount; lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_AUDIO_JACK_TARGET lbajardsilogic@0: std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: while (m_outputs.size() < channels) { lbajardsilogic@0: lbajardsilogic@0: char name[20]; lbajardsilogic@0: jack_port_t *port; lbajardsilogic@0: lbajardsilogic@0: sprintf(name, "out %d", m_outputs.size() + 1); lbajardsilogic@0: lbajardsilogic@0: port = jack_port_register(m_client, lbajardsilogic@0: name, lbajardsilogic@0: JACK_DEFAULT_AUDIO_TYPE, lbajardsilogic@0: JackPortIsOutput, lbajardsilogic@0: 0); lbajardsilogic@0: lbajardsilogic@0: if (!port) { lbajardsilogic@0: std::cerr lbajardsilogic@0: << "ERROR: AudioJACKTarget: Failed to create JACK output port " lbajardsilogic@0: << m_outputs.size() << std::endl; lbajardsilogic@0: } else { lbajardsilogic@0: m_source->setTargetPlayLatency(jack_port_get_latency(port)); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_outputs.size() < physicalPortCount) { lbajardsilogic@0: jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_outputs.push_back(port); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: while (m_outputs.size() > channels) { lbajardsilogic@0: std::vector::iterator itr = m_outputs.end(); lbajardsilogic@0: --itr; lbajardsilogic@0: jack_port_t *port = *itr; lbajardsilogic@0: if (port) jack_port_unregister(m_client, port); lbajardsilogic@0: m_outputs.erase(itr); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioJACKTarget::process(jack_nframes_t nframes) lbajardsilogic@0: { lbajardsilogic@0: if (!m_mutex.tryLock()) { lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_outputs.empty()) { lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_AUDIO_JACK_TARGET lbajardsilogic@0: std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl; lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: #ifdef DEBUG_AUDIO_JACK_TARGET lbajardsilogic@0: if (m_bufferSize != nframes) { lbajardsilogic@0: std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl; lbajardsilogic@0: } lbajardsilogic@0: #endif lbajardsilogic@0: lbajardsilogic@0: float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *)); lbajardsilogic@0: lbajardsilogic@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { lbajardsilogic@0: buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: size_t received = 0; lbajardsilogic@0: lbajardsilogic@0: if (m_source) { lbajardsilogic@0: received = m_source->getSourceSamples(nframes, buffers); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { lbajardsilogic@0: for (size_t i = received; i < nframes; ++i) { lbajardsilogic@0: buffers[ch][i] = 0.0; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: float peakLeft = 0.0, peakRight = 0.0; lbajardsilogic@0: lbajardsilogic@0: for (size_t ch = 0; ch < m_outputs.size(); ++ch) { lbajardsilogic@0: lbajardsilogic@0: float peak = 0.0; lbajardsilogic@0: lbajardsilogic@0: for (size_t i = 0; i < nframes; ++i) { lbajardsilogic@0: buffers[ch][i] *= m_outputGain; lbajardsilogic@0: float sample = fabsf(buffers[ch][i]); lbajardsilogic@0: if (sample > peak) peak = sample; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (ch == 0) peakLeft = peak; lbajardsilogic@0: if (ch > 0 || m_outputs.size() == 1) peakRight = peak; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_source) { lbajardsilogic@0: m_source->setOutputLevels(peakLeft, peakRight); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_mutex.unlock(); lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: AudioJACKTarget::xrun() lbajardsilogic@0: { lbajardsilogic@0: std::cerr << "AudioJACKTarget: xrun!" << std::endl; lbajardsilogic@0: if (m_source) m_source->audioProcessingOverload(); lbajardsilogic@0: return 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: #endif /* HAVE_JACK */ lbajardsilogic@0: