annotate audioio/AudioJACKTarget.cpp @ 451:dc1a360f2b69

Allow layers to be loaded without models if their layer class explicitly says it's OK (otherwise default template won't load, as it has an empty waveform layer)
author Chris Cannam
date Mon, 20 Apr 2015 10:10:26 +0100
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