annotate audioio/AudioJACKTarget.cpp @ 439:9429622647e6 cxx11

c++11 flag in qmake project
author Chris Cannam
date Wed, 11 Mar 2015 12:20:06 +0000
parents 72c662fe7ea3
children
rev   line source
Chris@43 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@43 2
Chris@43 3 /*
Chris@43 4 Sonic Visualiser
Chris@43 5 An audio file viewer and annotation editor.
Chris@43 6 Centre for Digital Music, Queen Mary, University of London.
Chris@43 7 This file copyright 2006 Chris Cannam.
Chris@43 8
Chris@43 9 This program is free software; you can redistribute it and/or
Chris@43 10 modify it under the terms of the GNU General Public License as
Chris@43 11 published by the Free Software Foundation; either version 2 of the
Chris@43 12 License, or (at your option) any later version. See the file
Chris@43 13 COPYING included with this distribution for more information.
Chris@43 14 */
Chris@43 15
Chris@43 16 #ifdef HAVE_JACK
Chris@43 17
Chris@43 18 #include "AudioJACKTarget.h"
Chris@43 19 #include "AudioCallbackPlaySource.h"
Chris@43 20
Chris@43 21 #include <iostream>
Chris@43 22 #include <cmath>
Chris@43 23
Chris@110 24 #include <alloca.h>
Chris@110 25
Chris@43 26 //#define DEBUG_AUDIO_JACK_TARGET 1
Chris@43 27
Chris@43 28 #ifdef BUILD_STATIC
Chris@43 29 #ifdef Q_OS_LINUX
Chris@43 30
Chris@43 31 // Some lunacy to enable JACK support in static builds. JACK isn't
Chris@43 32 // supposed to be linked statically, because it depends on a
Chris@43 33 // consistent shared memory layout between client library and daemon,
Chris@43 34 // so it's very fragile in the face of version mismatches.
Chris@43 35 //
Chris@43 36 // Therefore for static builds on Linux we avoid linking against JACK
Chris@43 37 // at all during the build, instead using dlopen and runtime symbol
Chris@43 38 // lookup to switch on JACK support at runtime. The following big
Chris@43 39 // mess (down to the #endifs) is the code that implements this.
Chris@43 40
Chris@43 41 static void *symbol(const char *name)
Chris@43 42 {
Chris@43 43 static bool attempted = false;
Chris@43 44 static void *library = 0;
Chris@43 45 static std::map<const char *, void *> symbols;
Chris@43 46 if (symbols.find(name) != symbols.end()) return symbols[name];
Chris@43 47 if (!library) {
Chris@43 48 if (!attempted) {
Chris@43 49 library = ::dlopen("libjack.so.1", RTLD_NOW);
Chris@43 50 if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
Chris@43 51 if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
Chris@43 52 if (!library) {
Chris@293 53 cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
Chris@43 54 << ::dlerror() << " (tried .so, .so.0, .so.1)"
Chris@293 55 << endl;
Chris@43 56 }
Chris@43 57 attempted = true;
Chris@43 58 }
Chris@43 59 if (!library) return 0;
Chris@43 60 }
Chris@43 61 void *symbol = ::dlsym(library, name);
Chris@43 62 if (!symbol) {
Chris@293 63 cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
Chris@293 64 << name << ": " << ::dlerror() << endl;
Chris@43 65 }
Chris@43 66 symbols[name] = symbol;
Chris@43 67 return symbol;
Chris@43 68 }
Chris@43 69
Chris@56 70 static jack_client_t *dynamic_jack_client_open(const char *client_name,
Chris@56 71 jack_options_t options,
Chris@56 72 jack_status_t *status, ...)
Chris@56 73 {
Chris@70 74 typedef jack_client_t *(*func)(const char *client_name,
Chris@70 75 jack_options_t options,
Chris@70 76 jack_status_t *status, ...);
Chris@56 77 void *s = symbol("jack_client_open");
Chris@56 78 if (!s) return 0;
Chris@56 79 func f = (func)s;
Chris@56 80 return f(client_name, options, status); // varargs not supported here
Chris@56 81 }
Chris@56 82
Chris@43 83 static int dynamic_jack_set_process_callback(jack_client_t *client,
Chris@43 84 JackProcessCallback process_callback,
Chris@43 85 void *arg)
Chris@43 86 {
Chris@43 87 typedef int (*func)(jack_client_t *client,
Chris@43 88 JackProcessCallback process_callback,
Chris@43 89 void *arg);
Chris@43 90 void *s = symbol("jack_set_process_callback");
Chris@43 91 if (!s) return 1;
Chris@43 92 func f = (func)s;
Chris@43 93 return f(client, process_callback, arg);
Chris@43 94 }
Chris@43 95
Chris@43 96 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
Chris@43 97 JackXRunCallback xrun_callback,
Chris@43 98 void *arg)
Chris@43 99 {
Chris@43 100 typedef int (*func)(jack_client_t *client,
Chris@43 101 JackXRunCallback xrun_callback,
Chris@43 102 void *arg);
Chris@43 103 void *s = symbol("jack_set_xrun_callback");
Chris@43 104 if (!s) return 1;
Chris@43 105 func f = (func)s;
Chris@43 106 return f(client, xrun_callback, arg);
Chris@43 107 }
Chris@43 108
Chris@43 109 static const char **dynamic_jack_get_ports(jack_client_t *client,
Chris@43 110 const char *port_name_pattern,
Chris@43 111 const char *type_name_pattern,
Chris@43 112 unsigned long flags)
Chris@43 113 {
Chris@43 114 typedef const char **(*func)(jack_client_t *client,
Chris@43 115 const char *port_name_pattern,
Chris@43 116 const char *type_name_pattern,
Chris@43 117 unsigned long flags);
Chris@43 118 void *s = symbol("jack_get_ports");
Chris@43 119 if (!s) return 0;
Chris@43 120 func f = (func)s;
Chris@43 121 return f(client, port_name_pattern, type_name_pattern, flags);
Chris@43 122 }
Chris@43 123
Chris@43 124 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
Chris@43 125 const char *port_name,
Chris@43 126 const char *port_type,
Chris@43 127 unsigned long flags,
Chris@43 128 unsigned long buffer_size)
Chris@43 129 {
Chris@43 130 typedef jack_port_t *(*func)(jack_client_t *client,
Chris@43 131 const char *port_name,
Chris@43 132 const char *port_type,
Chris@43 133 unsigned long flags,
Chris@43 134 unsigned long buffer_size);
Chris@43 135 void *s = symbol("jack_port_register");
Chris@43 136 if (!s) return 0;
Chris@43 137 func f = (func)s;
Chris@43 138 return f(client, port_name, port_type, flags, buffer_size);
Chris@43 139 }
Chris@43 140
Chris@43 141 static int dynamic_jack_connect(jack_client_t *client,
Chris@43 142 const char *source,
Chris@43 143 const char *dest)
Chris@43 144 {
Chris@43 145 typedef int (*func)(jack_client_t *client,
Chris@43 146 const char *source,
Chris@43 147 const char *dest);
Chris@43 148 void *s = symbol("jack_connect");
Chris@43 149 if (!s) return 1;
Chris@43 150 func f = (func)s;
Chris@43 151 return f(client, source, dest);
Chris@43 152 }
Chris@43 153
Chris@43 154 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
Chris@43 155 jack_nframes_t sz)
Chris@43 156 {
Chris@43 157 typedef void *(*func)(jack_port_t *, jack_nframes_t);
Chris@43 158 void *s = symbol("jack_port_get_buffer");
Chris@43 159 if (!s) return 0;
Chris@43 160 func f = (func)s;
Chris@43 161 return f(port, sz);
Chris@43 162 }
Chris@43 163
Chris@43 164 static int dynamic_jack_port_unregister(jack_client_t *client,
Chris@43 165 jack_port_t *port)
Chris@43 166 {
Chris@43 167 typedef int(*func)(jack_client_t *, jack_port_t *);
Chris@43 168 void *s = symbol("jack_port_unregister");
Chris@43 169 if (!s) return 0;
Chris@43 170 func f = (func)s;
Chris@43 171 return f(client, port);
Chris@43 172 }
Chris@43 173
Chris@366 174 static void dynamic_jack_port_get_latency_range(jack_port_t *port,
Chris@366 175 jack_latency_callback_mode_t mode,
Chris@366 176 jack_latency_range_t *range)
Chris@366 177 {
Chris@366 178 typedef void (*func)(jack_port_t *, jack_latency_callback_mode_t, jack_latency_range_t *);
Chris@366 179 void *s = symbol("jack_port_get_latency_range");
Chris@366 180 if (!s) {
Chris@415 181 range->min = range->max = 0;
Chris@366 182 return;
Chris@366 183 }
Chris@366 184 func f = (func)s;
Chris@366 185 f(port, mode, range);
Chris@366 186 }
Chris@366 187
Chris@43 188 #define dynamic1(rv, name, argtype, failval) \
Chris@43 189 static rv dynamic_##name(argtype arg) { \
Chris@43 190 typedef rv (*func) (argtype); \
Chris@43 191 void *s = symbol(#name); \
Chris@43 192 if (!s) return failval; \
Chris@43 193 func f = (func) s; \
Chris@43 194 return f(arg); \
Chris@43 195 }
Chris@43 196
Chris@43 197 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
Chris@43 198 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
Chris@43 199 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
Chris@43 200 dynamic1(int, jack_activate, jack_client_t *, 1);
Chris@43 201 dynamic1(int, jack_deactivate, jack_client_t *, 1);
Chris@43 202 dynamic1(int, jack_client_close, jack_client_t *, 1);
Chris@97 203 dynamic1(jack_nframes_t, jack_frame_time, jack_client_t *, 0);
Chris@43 204 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
Chris@43 205
Chris@43 206 #define jack_client_new dynamic_jack_client_new
Chris@56 207 #define jack_client_open dynamic_jack_client_open
Chris@43 208 #define jack_get_buffer_size dynamic_jack_get_buffer_size
Chris@43 209 #define jack_get_sample_rate dynamic_jack_get_sample_rate
Chris@43 210 #define jack_set_process_callback dynamic_jack_set_process_callback
Chris@43 211 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
Chris@43 212 #define jack_activate dynamic_jack_activate
Chris@43 213 #define jack_deactivate dynamic_jack_deactivate
Chris@43 214 #define jack_client_close dynamic_jack_client_close
Chris@97 215 #define jack_frame_time dynamic_jack_frame_time
Chris@43 216 #define jack_get_ports dynamic_jack_get_ports
Chris@43 217 #define jack_port_register dynamic_jack_port_register
Chris@43 218 #define jack_port_unregister dynamic_jack_port_unregister
Chris@43 219 #define jack_port_name dynamic_jack_port_name
Chris@43 220 #define jack_connect dynamic_jack_connect
Chris@43 221 #define jack_port_get_buffer dynamic_jack_port_get_buffer
Chris@43 222
Chris@43 223 #endif
Chris@43 224 #endif
Chris@43 225
Chris@43 226 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
Chris@43 227 AudioCallbackPlayTarget(source),
Chris@43 228 m_client(0),
Chris@43 229 m_bufferSize(0),
Chris@70 230 m_sampleRate(0),
Chris@70 231 m_done(false)
Chris@43 232 {
Chris@56 233 JackOptions options = JackNullOption;
Chris@117 234 #ifdef HAVE_PORTAUDIO_2_0
Chris@117 235 options = JackNoStartServer;
Chris@117 236 #endif
Chris@117 237 #ifdef HAVE_LIBPULSE
Chris@56 238 options = JackNoStartServer;
Chris@56 239 #endif
Chris@56 240
Chris@56 241 JackStatus status = JackStatus(0);
Chris@57 242 m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
Chris@57 243 options, &status);
Chris@57 244
Chris@43 245 if (!m_client) {
Chris@293 246 cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
Chris@293 247 << status << endl;
Chris@56 248 return;
Chris@43 249 }
Chris@43 250
Chris@43 251 m_bufferSize = jack_get_buffer_size(m_client);
Chris@43 252 m_sampleRate = jack_get_sample_rate(m_client);
Chris@43 253
Chris@43 254 jack_set_xrun_callback(m_client, xrunStatic, this);
Chris@43 255 jack_set_process_callback(m_client, processStatic, this);
Chris@43 256
Chris@43 257 if (jack_activate(m_client)) {
Chris@293 258 cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
Chris@293 259 << endl;
Chris@43 260 }
Chris@43 261
Chris@43 262 if (m_source) {
Chris@43 263 sourceModelReplaced();
Chris@43 264 }
Chris@84 265
Chris@84 266 // Mainstream JACK (though not jackdmp) calls mlockall() to lock
Chris@84 267 // down all memory for real-time operation. That isn't a terribly
Chris@84 268 // good idea in an application like this that may have very high
Chris@84 269 // dynamic memory usage in other threads, as mlockall() applies
Chris@84 270 // across all threads. We're far better off undoing it here and
Chris@84 271 // accepting the possible loss of true RT capability.
Chris@84 272 MUNLOCKALL();
Chris@43 273 }
Chris@43 274
Chris@43 275 AudioJACKTarget::~AudioJACKTarget()
Chris@43 276 {
Chris@233 277 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget()" << endl;
Chris@70 278
Chris@91 279 if (m_source) {
Chris@91 280 m_source->setTarget(0, m_bufferSize);
Chris@91 281 }
Chris@91 282
Chris@70 283 shutdown();
Chris@70 284
Chris@43 285 if (m_client) {
Chris@70 286
Chris@70 287 while (m_outputs.size() > 0) {
Chris@70 288 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@70 289 --itr;
Chris@70 290 jack_port_t *port = *itr;
Chris@293 291 cerr << "unregister " << m_outputs.size() << endl;
Chris@70 292 if (port) jack_port_unregister(m_client, port);
Chris@70 293 m_outputs.erase(itr);
Chris@70 294 }
Chris@293 295 cerr << "Deactivating... ";
Chris@43 296 jack_deactivate(m_client);
Chris@293 297 cerr << "done\nClosing... ";
Chris@43 298 jack_client_close(m_client);
Chris@293 299 cerr << "done" << endl;
Chris@43 300 }
Chris@70 301
Chris@70 302 m_client = 0;
Chris@70 303
Chris@233 304 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget() done" << endl;
Chris@43 305 }
Chris@43 306
Chris@70 307 void
Chris@70 308 AudioJACKTarget::shutdown()
Chris@70 309 {
Chris@70 310 m_done = true;
Chris@70 311 }
Chris@70 312
Chris@43 313 bool
Chris@43 314 AudioJACKTarget::isOK() const
Chris@43 315 {
Chris@43 316 return (m_client != 0);
Chris@43 317 }
Chris@43 318
Chris@91 319 double
Chris@91 320 AudioJACKTarget::getCurrentTime() const
Chris@91 321 {
Chris@91 322 if (m_client && m_sampleRate) {
Chris@91 323 return double(jack_frame_time(m_client)) / double(m_sampleRate);
Chris@91 324 } else {
Chris@91 325 return 0.0;
Chris@91 326 }
Chris@91 327 }
Chris@91 328
Chris@43 329 int
Chris@43 330 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
Chris@43 331 {
Chris@43 332 return ((AudioJACKTarget *)arg)->process(nframes);
Chris@43 333 }
Chris@43 334
Chris@43 335 int
Chris@43 336 AudioJACKTarget::xrunStatic(void *arg)
Chris@43 337 {
Chris@43 338 return ((AudioJACKTarget *)arg)->xrun();
Chris@43 339 }
Chris@43 340
Chris@43 341 void
Chris@43 342 AudioJACKTarget::sourceModelReplaced()
Chris@43 343 {
Chris@43 344 m_mutex.lock();
Chris@43 345
Chris@91 346 m_source->setTarget(this, m_bufferSize);
Chris@43 347 m_source->setTargetSampleRate(m_sampleRate);
Chris@43 348
Chris@366 349 int channels = m_source->getSourceChannelCount();
Chris@43 350
Chris@43 351 // Because we offer pan, we always want at least 2 channels
Chris@43 352 if (channels < 2) channels = 2;
Chris@43 353
Chris@366 354 if (channels == (int)m_outputs.size() || !m_client) {
Chris@43 355 m_mutex.unlock();
Chris@43 356 return;
Chris@43 357 }
Chris@43 358
Chris@43 359 const char **ports =
Chris@43 360 jack_get_ports(m_client, NULL, NULL,
Chris@43 361 JackPortIsPhysical | JackPortIsInput);
Chris@366 362 int physicalPortCount = 0;
Chris@43 363 while (ports[physicalPortCount]) ++physicalPortCount;
Chris@43 364
Chris@43 365 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@233 366 SVDEBUG << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << endl;
Chris@43 367 #endif
Chris@43 368
Chris@366 369 while ((int)m_outputs.size() < channels) {
Chris@416 370
Chris@416 371 const int namelen = 30;
Chris@416 372 char name[namelen];
Chris@43 373 jack_port_t *port;
Chris@43 374
Chris@416 375 snprintf(name, namelen, "out %d", int(m_outputs.size() + 1));
Chris@43 376
Chris@43 377 port = jack_port_register(m_client,
Chris@43 378 name,
Chris@43 379 JACK_DEFAULT_AUDIO_TYPE,
Chris@43 380 JackPortIsOutput,
Chris@43 381 0);
Chris@43 382
Chris@43 383 if (!port) {
Chris@293 384 cerr
Chris@43 385 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
Chris@293 386 << m_outputs.size() << endl;
Chris@43 387 } else {
Chris@366 388 jack_latency_range_t range;
Chris@366 389 jack_port_get_latency_range(port, JackPlaybackLatency, &range);
Chris@366 390 m_source->setTargetPlayLatency(range.max);
Chris@366 391 cerr << "AudioJACKTarget: output latency is " << range.max << endl;
Chris@43 392 }
Chris@43 393
Chris@366 394 if ((int)m_outputs.size() < physicalPortCount) {
Chris@43 395 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
Chris@43 396 }
Chris@43 397
Chris@43 398 m_outputs.push_back(port);
Chris@43 399 }
Chris@43 400
Chris@366 401 while ((int)m_outputs.size() > channels) {
Chris@43 402 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@43 403 --itr;
Chris@43 404 jack_port_t *port = *itr;
Chris@43 405 if (port) jack_port_unregister(m_client, port);
Chris@43 406 m_outputs.erase(itr);
Chris@43 407 }
Chris@43 408
Chris@43 409 m_mutex.unlock();
Chris@43 410 }
Chris@43 411
Chris@43 412 int
Chris@43 413 AudioJACKTarget::process(jack_nframes_t nframes)
Chris@43 414 {
Chris@70 415 if (m_done) return 0;
Chris@70 416
Chris@43 417 if (!m_mutex.tryLock()) {
Chris@43 418 return 0;
Chris@43 419 }
Chris@43 420
Chris@43 421 if (m_outputs.empty()) {
Chris@43 422 m_mutex.unlock();
Chris@43 423 return 0;
Chris@43 424 }
Chris@43 425
Chris@43 426 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@293 427 cout << "AudioJACKTarget::process(" << nframes << "): have a source" << endl;
Chris@43 428 #endif
Chris@43 429
Chris@43 430 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 431 if (m_bufferSize != nframes) {
Chris@293 432 cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << endl;
Chris@43 433 }
Chris@43 434 #endif
Chris@43 435
Chris@43 436 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
Chris@43 437
Chris@366 438 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
Chris@43 439 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
Chris@43 440 }
Chris@43 441
Chris@436 442 sv_frame_t received = 0;
Chris@43 443
Chris@43 444 if (m_source) {
Chris@43 445 received = m_source->getSourceSamples(nframes, buffers);
Chris@43 446 }
Chris@43 447
Chris@366 448 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
Chris@436 449 for (sv_frame_t i = received; i < nframes; ++i) {
Chris@43 450 buffers[ch][i] = 0.0;
Chris@43 451 }
Chris@43 452 }
Chris@43 453
Chris@43 454 float peakLeft = 0.0, peakRight = 0.0;
Chris@43 455
Chris@366 456 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
Chris@43 457
Chris@43 458 float peak = 0.0;
Chris@43 459
Chris@366 460 for (int i = 0; i < (int)nframes; ++i) {
Chris@43 461 buffers[ch][i] *= m_outputGain;
Chris@43 462 float sample = fabsf(buffers[ch][i]);
Chris@43 463 if (sample > peak) peak = sample;
Chris@43 464 }
Chris@43 465
Chris@43 466 if (ch == 0) peakLeft = peak;
Chris@43 467 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
Chris@43 468 }
Chris@43 469
Chris@43 470 if (m_source) {
Chris@43 471 m_source->setOutputLevels(peakLeft, peakRight);
Chris@43 472 }
Chris@43 473
Chris@43 474 m_mutex.unlock();
Chris@43 475 return 0;
Chris@43 476 }
Chris@43 477
Chris@43 478 int
Chris@43 479 AudioJACKTarget::xrun()
Chris@43 480 {
Chris@293 481 cerr << "AudioJACKTarget: xrun!" << endl;
Chris@43 482 if (m_source) m_source->audioProcessingOverload();
Chris@43 483 return 0;
Chris@43 484 }
Chris@43 485
Chris@43 486 #endif /* HAVE_JACK */
Chris@43 487