AudioJACKTarget.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #ifdef HAVE_JACK
00017 
00018 #include "AudioJACKTarget.h"
00019 #include "AudioCallbackPlaySource.h"
00020 
00021 #include <iostream>
00022 #include <cmath>
00023 
00024 //#define DEBUG_AUDIO_JACK_TARGET 1
00025 
00026 #ifdef BUILD_STATIC
00027 #ifdef Q_OS_LINUX
00028 
00029 // Some lunacy to enable JACK support in static builds.  JACK isn't
00030 // supposed to be linked statically, because it depends on a
00031 // consistent shared memory layout between client library and daemon,
00032 // so it's very fragile in the face of version mismatches.
00033 //
00034 // Therefore for static builds on Linux we avoid linking against JACK
00035 // at all during the build, instead using dlopen and runtime symbol
00036 // lookup to switch on JACK support at runtime.  The following big
00037 // mess (down to the #endifs) is the code that implements this.
00038 
00039 static void *symbol(const char *name)
00040 {
00041     static bool attempted = false;
00042     static void *library = 0;
00043     static std::map<const char *, void *> symbols;
00044     if (symbols.find(name) != symbols.end()) return symbols[name];
00045     if (!library) {
00046         if (!attempted) {
00047             library = ::dlopen("libjack.so.1", RTLD_NOW);
00048             if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
00049             if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
00050             if (!library) {
00051                 std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
00052                           << ::dlerror() << " (tried .so, .so.0, .so.1)"
00053                           << std::endl;
00054             }
00055             attempted = true;
00056         }
00057         if (!library) return 0;
00058     }
00059     void *symbol = ::dlsym(library, name);
00060     if (!symbol) {
00061         std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
00062                   << name << ": " << ::dlerror() << std::endl;
00063     }
00064     symbols[name] = symbol;
00065     return symbol;
00066 }
00067 
00068 static jack_client_t *dynamic_jack_client_open(const char *client_name,
00069                                                jack_options_t options,
00070                                                jack_status_t *status, ...)
00071 {
00072     typedef jack_client_t *(*func)(const char *client_name,
00073                                    jack_options_t options,
00074                                    jack_status_t *status, ...);
00075     void *s = symbol("jack_client_open");
00076     if (!s) return 0;
00077     func f = (func)s;
00078     return f(client_name, options, status); // varargs not supported here
00079 }
00080 
00081 static int dynamic_jack_set_process_callback(jack_client_t *client,
00082                                              JackProcessCallback process_callback,
00083                                              void *arg)
00084 {
00085     typedef int (*func)(jack_client_t *client,
00086                         JackProcessCallback process_callback,
00087                         void *arg);
00088     void *s = symbol("jack_set_process_callback");
00089     if (!s) return 1;
00090     func f = (func)s;
00091     return f(client, process_callback, arg);
00092 }
00093 
00094 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
00095                                           JackXRunCallback xrun_callback,
00096                                           void *arg)
00097 {
00098     typedef int (*func)(jack_client_t *client,
00099                         JackXRunCallback xrun_callback,
00100                         void *arg);
00101     void *s = symbol("jack_set_xrun_callback");
00102     if (!s) return 1;
00103     func f = (func)s;
00104     return f(client, xrun_callback, arg);
00105 }
00106 
00107 static const char **dynamic_jack_get_ports(jack_client_t *client, 
00108                                            const char *port_name_pattern, 
00109                                            const char *type_name_pattern, 
00110                                            unsigned long flags)
00111 {
00112     typedef const char **(*func)(jack_client_t *client, 
00113                                  const char *port_name_pattern, 
00114                                  const char *type_name_pattern, 
00115                                  unsigned long flags);
00116     void *s = symbol("jack_get_ports");
00117     if (!s) return 0;
00118     func f = (func)s;
00119     return f(client, port_name_pattern, type_name_pattern, flags);
00120 }
00121 
00122 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
00123                                                const char *port_name,
00124                                                const char *port_type,
00125                                                unsigned long flags,
00126                                                unsigned long buffer_size)
00127 {
00128     typedef jack_port_t *(*func)(jack_client_t *client,
00129                                  const char *port_name,
00130                                  const char *port_type,
00131                                  unsigned long flags,
00132                                  unsigned long buffer_size);
00133     void *s = symbol("jack_port_register");
00134     if (!s) return 0;
00135     func f = (func)s;
00136     return f(client, port_name, port_type, flags, buffer_size);
00137 }
00138 
00139 static int dynamic_jack_connect(jack_client_t *client,
00140                                 const char *source,
00141                                 const char *dest)
00142 {
00143     typedef int (*func)(jack_client_t *client,
00144                         const char *source,
00145                         const char *dest);
00146     void *s = symbol("jack_connect");
00147     if (!s) return 1;
00148     func f = (func)s;
00149     return f(client, source, dest);
00150 }
00151 
00152 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
00153                                           jack_nframes_t sz)
00154 {
00155     typedef void *(*func)(jack_port_t *, jack_nframes_t);
00156     void *s = symbol("jack_port_get_buffer");
00157     if (!s) return 0;
00158     func f = (func)s;
00159     return f(port, sz);
00160 }
00161 
00162 static int dynamic_jack_port_unregister(jack_client_t *client,
00163                                         jack_port_t *port)
00164 {
00165     typedef int(*func)(jack_client_t *, jack_port_t *);
00166     void *s = symbol("jack_port_unregister");
00167     if (!s) return 0;
00168     func f = (func)s;
00169     return f(client, port);
00170 }
00171 
00172 #define dynamic1(rv, name, argtype, failval) \
00173     static rv dynamic_##name(argtype arg) { \
00174         typedef rv (*func) (argtype); \
00175         void *s = symbol(#name); \
00176         if (!s) return failval; \
00177         func f = (func) s; \
00178         return f(arg); \
00179     }
00180 
00181 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
00182 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
00183 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
00184 dynamic1(int, jack_activate, jack_client_t *, 1);
00185 dynamic1(int, jack_deactivate, jack_client_t *, 1);
00186 dynamic1(int, jack_client_close, jack_client_t *, 1);
00187 dynamic1(jack_nframes_t, jack_frame_time, jack_client_t *, 0);
00188 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
00189 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
00190 
00191 #define jack_client_new dynamic_jack_client_new
00192 #define jack_client_open dynamic_jack_client_open
00193 #define jack_get_buffer_size dynamic_jack_get_buffer_size
00194 #define jack_get_sample_rate dynamic_jack_get_sample_rate
00195 #define jack_set_process_callback dynamic_jack_set_process_callback
00196 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
00197 #define jack_activate dynamic_jack_activate
00198 #define jack_deactivate dynamic_jack_deactivate
00199 #define jack_client_close dynamic_jack_client_close
00200 #define jack_frame_time dynamic_jack_frame_time
00201 #define jack_get_ports dynamic_jack_get_ports
00202 #define jack_port_register dynamic_jack_port_register
00203 #define jack_port_unregister dynamic_jack_port_unregister
00204 #define jack_port_get_latency dynamic_jack_port_get_latency
00205 #define jack_port_name dynamic_jack_port_name
00206 #define jack_connect dynamic_jack_connect
00207 #define jack_port_get_buffer dynamic_jack_port_get_buffer
00208 
00209 #endif
00210 #endif
00211 
00212 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
00213     AudioCallbackPlayTarget(source),
00214     m_client(0),
00215     m_bufferSize(0),
00216     m_sampleRate(0),
00217     m_done(false)
00218 {
00219     JackOptions options = JackNullOption;
00220 #ifdef HAVE_PORTAUDIO
00221     options = JackNoStartServer;
00222 #endif
00223 
00224     JackStatus status = JackStatus(0);
00225     m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
00226                                 options, &status);
00227     
00228     if (!m_client) {
00229         std::cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
00230                   << status << std::endl;
00231         return;
00232     }
00233 
00234     m_bufferSize = jack_get_buffer_size(m_client);
00235     m_sampleRate = jack_get_sample_rate(m_client);
00236 
00237     jack_set_xrun_callback(m_client, xrunStatic, this);
00238     jack_set_process_callback(m_client, processStatic, this);
00239 
00240     if (jack_activate(m_client)) {
00241         std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
00242                   << std::endl;
00243     }
00244 
00245     if (m_source) {
00246         sourceModelReplaced();
00247     }
00248     
00249     // Mainstream JACK (though not jackdmp) calls mlockall() to lock
00250     // down all memory for real-time operation.  That isn't a terribly
00251     // good idea in an application like this that may have very high
00252     // dynamic memory usage in other threads, as mlockall() applies
00253     // across all threads.  We're far better off undoing it here and
00254     // accepting the possible loss of true RT capability.
00255     MUNLOCKALL();
00256 }
00257 
00258 AudioJACKTarget::~AudioJACKTarget()
00259 {
00260     std::cerr << "AudioJACKTarget::~AudioJACKTarget()" << std::endl;
00261 
00262     if (m_source) {
00263         m_source->setTarget(0, m_bufferSize);
00264     }
00265 
00266     shutdown();
00267 
00268     if (m_client) {
00269 
00270         while (m_outputs.size() > 0) {
00271             std::vector<jack_port_t *>::iterator itr = m_outputs.end();
00272             --itr;
00273             jack_port_t *port = *itr;
00274             std::cerr << "unregister " << m_outputs.size() << std::endl;
00275             if (port) jack_port_unregister(m_client, port);
00276             m_outputs.erase(itr);
00277         }
00278         std::cerr << "Deactivating... ";
00279         jack_deactivate(m_client);
00280         std::cerr << "done\nClosing... ";
00281         jack_client_close(m_client);
00282         std::cerr << "done" << std::endl;
00283     }
00284 
00285     m_client = 0;
00286 
00287     std::cerr << "AudioJACKTarget::~AudioJACKTarget() done" << std::endl;
00288 }
00289 
00290 void
00291 AudioJACKTarget::shutdown()
00292 {
00293     m_done = true;
00294 }
00295 
00296 bool
00297 AudioJACKTarget::isOK() const
00298 {
00299     return (m_client != 0);
00300 }
00301 
00302 double
00303 AudioJACKTarget::getCurrentTime() const
00304 {
00305     if (m_client && m_sampleRate) {
00306         return double(jack_frame_time(m_client)) / double(m_sampleRate);
00307     } else {
00308         return 0.0;
00309     }
00310 }
00311 
00312 int
00313 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
00314 {
00315     return ((AudioJACKTarget *)arg)->process(nframes);
00316 }
00317 
00318 int
00319 AudioJACKTarget::xrunStatic(void *arg)
00320 {
00321     return ((AudioJACKTarget *)arg)->xrun();
00322 }
00323 
00324 void
00325 AudioJACKTarget::sourceModelReplaced()
00326 {
00327     m_mutex.lock();
00328 
00329     m_source->setTarget(this, m_bufferSize);
00330     m_source->setTargetSampleRate(m_sampleRate);
00331 
00332     size_t channels = m_source->getSourceChannelCount();
00333 
00334     // Because we offer pan, we always want at least 2 channels
00335     if (channels < 2) channels = 2;
00336 
00337     if (channels == m_outputs.size() || !m_client) {
00338         m_mutex.unlock();
00339         return;
00340     }
00341 
00342     const char **ports =
00343         jack_get_ports(m_client, NULL, NULL,
00344                        JackPortIsPhysical | JackPortIsInput);
00345     size_t physicalPortCount = 0;
00346     while (ports[physicalPortCount]) ++physicalPortCount;
00347 
00348 #ifdef DEBUG_AUDIO_JACK_TARGET    
00349     std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
00350 #endif
00351 
00352     while (m_outputs.size() < channels) {
00353         
00354         char name[20];
00355         jack_port_t *port;
00356 
00357         sprintf(name, "out %d", m_outputs.size() + 1);
00358 
00359         port = jack_port_register(m_client,
00360                                   name,
00361                                   JACK_DEFAULT_AUDIO_TYPE,
00362                                   JackPortIsOutput,
00363                                   0);
00364 
00365         if (!port) {
00366             std::cerr
00367                 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
00368                 << m_outputs.size() << std::endl;
00369         } else {
00370             m_source->setTargetPlayLatency(jack_port_get_latency(port));
00371         }
00372 
00373         if (m_outputs.size() < physicalPortCount) {
00374             jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
00375         }
00376 
00377         m_outputs.push_back(port);
00378     }
00379 
00380     while (m_outputs.size() > channels) {
00381         std::vector<jack_port_t *>::iterator itr = m_outputs.end();
00382         --itr;
00383         jack_port_t *port = *itr;
00384         if (port) jack_port_unregister(m_client, port);
00385         m_outputs.erase(itr);
00386     }
00387 
00388     m_mutex.unlock();
00389 }
00390 
00391 int
00392 AudioJACKTarget::process(jack_nframes_t nframes)
00393 {
00394     if (m_done) return 0;
00395 
00396     if (!m_mutex.tryLock()) {
00397         return 0;
00398     }
00399 
00400     if (m_outputs.empty()) {
00401         m_mutex.unlock();
00402         return 0;
00403     }
00404 
00405 #ifdef DEBUG_AUDIO_JACK_TARGET    
00406     std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
00407 #endif
00408 
00409 #ifdef DEBUG_AUDIO_JACK_TARGET    
00410     if (m_bufferSize != nframes) {
00411         std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
00412     }
00413 #endif
00414 
00415     float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
00416 
00417     for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
00418         buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
00419     }
00420 
00421     size_t received = 0;
00422 
00423     if (m_source) {
00424         received = m_source->getSourceSamples(nframes, buffers);
00425     }
00426 
00427     for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
00428         for (size_t i = received; i < nframes; ++i) {
00429             buffers[ch][i] = 0.0;
00430         }
00431     }
00432 
00433     float peakLeft = 0.0, peakRight = 0.0;
00434 
00435     for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
00436 
00437         float peak = 0.0;
00438 
00439         for (size_t i = 0; i < nframes; ++i) {
00440             buffers[ch][i] *= m_outputGain;
00441             float sample = fabsf(buffers[ch][i]);
00442             if (sample > peak) peak = sample;
00443         }
00444 
00445         if (ch == 0) peakLeft = peak;
00446         if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
00447     }
00448             
00449     if (m_source) {
00450         m_source->setOutputLevels(peakLeft, peakRight);
00451     }
00452 
00453     m_mutex.unlock();
00454     return 0;
00455 }
00456 
00457 int
00458 AudioJACKTarget::xrun()
00459 {
00460     std::cerr << "AudioJACKTarget: xrun!" << std::endl;
00461     if (m_source) m_source->audioProcessingOverload();
00462     return 0;
00463 }
00464 
00465 #endif /* HAVE_JACK */
00466 

Generated on Wed Feb 20 15:45:24 2008 for SonicVisualiser by  doxygen 1.5.1