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