annotate audioio/AudioJACKTarget.cpp @ 256:f3f9e3d647c1

Give a dedicated key to toggling the centre line, and move it out of the overlay level setting -- reducing number of overlay levels to 3. Introduce two distinct vertical scale types (so that we can hide the spectrogram colour scale part easily)
author Chris Cannam
date Mon, 30 Jan 2012 16:02:14 +0000
parents 8aace2d9f1c2
children 7f08a7e35b3c 068235cf5bf7
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@43 53 std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
Chris@43 54 << ::dlerror() << " (tried .so, .so.0, .so.1)"
Chris@43 55 << std::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@43 63 std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
Chris@43 64 << name << ": " << ::dlerror() << std::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@43 174 #define dynamic1(rv, name, argtype, failval) \
Chris@43 175 static rv dynamic_##name(argtype arg) { \
Chris@43 176 typedef rv (*func) (argtype); \
Chris@43 177 void *s = symbol(#name); \
Chris@43 178 if (!s) return failval; \
Chris@43 179 func f = (func) s; \
Chris@43 180 return f(arg); \
Chris@43 181 }
Chris@43 182
Chris@43 183 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
Chris@43 184 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
Chris@43 185 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
Chris@43 186 dynamic1(int, jack_activate, jack_client_t *, 1);
Chris@43 187 dynamic1(int, jack_deactivate, jack_client_t *, 1);
Chris@43 188 dynamic1(int, jack_client_close, jack_client_t *, 1);
Chris@97 189 dynamic1(jack_nframes_t, jack_frame_time, jack_client_t *, 0);
Chris@43 190 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
Chris@43 191 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
Chris@43 192
Chris@43 193 #define jack_client_new dynamic_jack_client_new
Chris@56 194 #define jack_client_open dynamic_jack_client_open
Chris@43 195 #define jack_get_buffer_size dynamic_jack_get_buffer_size
Chris@43 196 #define jack_get_sample_rate dynamic_jack_get_sample_rate
Chris@43 197 #define jack_set_process_callback dynamic_jack_set_process_callback
Chris@43 198 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
Chris@43 199 #define jack_activate dynamic_jack_activate
Chris@43 200 #define jack_deactivate dynamic_jack_deactivate
Chris@43 201 #define jack_client_close dynamic_jack_client_close
Chris@97 202 #define jack_frame_time dynamic_jack_frame_time
Chris@43 203 #define jack_get_ports dynamic_jack_get_ports
Chris@43 204 #define jack_port_register dynamic_jack_port_register
Chris@43 205 #define jack_port_unregister dynamic_jack_port_unregister
Chris@43 206 #define jack_port_get_latency dynamic_jack_port_get_latency
Chris@43 207 #define jack_port_name dynamic_jack_port_name
Chris@43 208 #define jack_connect dynamic_jack_connect
Chris@43 209 #define jack_port_get_buffer dynamic_jack_port_get_buffer
Chris@43 210
Chris@43 211 #endif
Chris@43 212 #endif
Chris@43 213
Chris@43 214 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
Chris@43 215 AudioCallbackPlayTarget(source),
Chris@43 216 m_client(0),
Chris@43 217 m_bufferSize(0),
Chris@70 218 m_sampleRate(0),
Chris@70 219 m_done(false)
Chris@43 220 {
Chris@56 221 JackOptions options = JackNullOption;
Chris@117 222 #ifdef HAVE_PORTAUDIO_2_0
Chris@117 223 options = JackNoStartServer;
Chris@117 224 #endif
Chris@117 225 #ifdef HAVE_LIBPULSE
Chris@56 226 options = JackNoStartServer;
Chris@56 227 #endif
Chris@56 228
Chris@56 229 JackStatus status = JackStatus(0);
Chris@57 230 m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
Chris@57 231 options, &status);
Chris@57 232
Chris@43 233 if (!m_client) {
Chris@56 234 std::cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
Chris@56 235 << status << std::endl;
Chris@56 236 return;
Chris@43 237 }
Chris@43 238
Chris@43 239 m_bufferSize = jack_get_buffer_size(m_client);
Chris@43 240 m_sampleRate = jack_get_sample_rate(m_client);
Chris@43 241
Chris@43 242 jack_set_xrun_callback(m_client, xrunStatic, this);
Chris@43 243 jack_set_process_callback(m_client, processStatic, this);
Chris@43 244
Chris@43 245 if (jack_activate(m_client)) {
Chris@43 246 std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
Chris@43 247 << std::endl;
Chris@43 248 }
Chris@43 249
Chris@43 250 if (m_source) {
Chris@43 251 sourceModelReplaced();
Chris@43 252 }
Chris@84 253
Chris@84 254 // Mainstream JACK (though not jackdmp) calls mlockall() to lock
Chris@84 255 // down all memory for real-time operation. That isn't a terribly
Chris@84 256 // good idea in an application like this that may have very high
Chris@84 257 // dynamic memory usage in other threads, as mlockall() applies
Chris@84 258 // across all threads. We're far better off undoing it here and
Chris@84 259 // accepting the possible loss of true RT capability.
Chris@84 260 MUNLOCKALL();
Chris@43 261 }
Chris@43 262
Chris@43 263 AudioJACKTarget::~AudioJACKTarget()
Chris@43 264 {
Chris@233 265 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget()" << endl;
Chris@70 266
Chris@91 267 if (m_source) {
Chris@91 268 m_source->setTarget(0, m_bufferSize);
Chris@91 269 }
Chris@91 270
Chris@70 271 shutdown();
Chris@70 272
Chris@43 273 if (m_client) {
Chris@70 274
Chris@70 275 while (m_outputs.size() > 0) {
Chris@70 276 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@70 277 --itr;
Chris@70 278 jack_port_t *port = *itr;
Chris@70 279 std::cerr << "unregister " << m_outputs.size() << std::endl;
Chris@70 280 if (port) jack_port_unregister(m_client, port);
Chris@70 281 m_outputs.erase(itr);
Chris@70 282 }
Chris@70 283 std::cerr << "Deactivating... ";
Chris@43 284 jack_deactivate(m_client);
Chris@70 285 std::cerr << "done\nClosing... ";
Chris@43 286 jack_client_close(m_client);
Chris@70 287 std::cerr << "done" << std::endl;
Chris@43 288 }
Chris@70 289
Chris@70 290 m_client = 0;
Chris@70 291
Chris@233 292 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget() done" << endl;
Chris@43 293 }
Chris@43 294
Chris@70 295 void
Chris@70 296 AudioJACKTarget::shutdown()
Chris@70 297 {
Chris@70 298 m_done = true;
Chris@70 299 }
Chris@70 300
Chris@43 301 bool
Chris@43 302 AudioJACKTarget::isOK() const
Chris@43 303 {
Chris@43 304 return (m_client != 0);
Chris@43 305 }
Chris@43 306
Chris@91 307 double
Chris@91 308 AudioJACKTarget::getCurrentTime() const
Chris@91 309 {
Chris@91 310 if (m_client && m_sampleRate) {
Chris@91 311 return double(jack_frame_time(m_client)) / double(m_sampleRate);
Chris@91 312 } else {
Chris@91 313 return 0.0;
Chris@91 314 }
Chris@91 315 }
Chris@91 316
Chris@43 317 int
Chris@43 318 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
Chris@43 319 {
Chris@43 320 return ((AudioJACKTarget *)arg)->process(nframes);
Chris@43 321 }
Chris@43 322
Chris@43 323 int
Chris@43 324 AudioJACKTarget::xrunStatic(void *arg)
Chris@43 325 {
Chris@43 326 return ((AudioJACKTarget *)arg)->xrun();
Chris@43 327 }
Chris@43 328
Chris@43 329 void
Chris@43 330 AudioJACKTarget::sourceModelReplaced()
Chris@43 331 {
Chris@43 332 m_mutex.lock();
Chris@43 333
Chris@91 334 m_source->setTarget(this, m_bufferSize);
Chris@43 335 m_source->setTargetSampleRate(m_sampleRate);
Chris@43 336
Chris@43 337 size_t channels = m_source->getSourceChannelCount();
Chris@43 338
Chris@43 339 // Because we offer pan, we always want at least 2 channels
Chris@43 340 if (channels < 2) channels = 2;
Chris@43 341
Chris@43 342 if (channels == m_outputs.size() || !m_client) {
Chris@43 343 m_mutex.unlock();
Chris@43 344 return;
Chris@43 345 }
Chris@43 346
Chris@43 347 const char **ports =
Chris@43 348 jack_get_ports(m_client, NULL, NULL,
Chris@43 349 JackPortIsPhysical | JackPortIsInput);
Chris@43 350 size_t physicalPortCount = 0;
Chris@43 351 while (ports[physicalPortCount]) ++physicalPortCount;
Chris@43 352
Chris@43 353 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@233 354 SVDEBUG << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << endl;
Chris@43 355 #endif
Chris@43 356
Chris@43 357 while (m_outputs.size() < channels) {
Chris@43 358
Chris@43 359 char name[20];
Chris@43 360 jack_port_t *port;
Chris@43 361
Chris@110 362 sprintf(name, "out %d", int(m_outputs.size() + 1));
Chris@43 363
Chris@43 364 port = jack_port_register(m_client,
Chris@43 365 name,
Chris@43 366 JACK_DEFAULT_AUDIO_TYPE,
Chris@43 367 JackPortIsOutput,
Chris@43 368 0);
Chris@43 369
Chris@43 370 if (!port) {
Chris@43 371 std::cerr
Chris@43 372 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
Chris@43 373 << m_outputs.size() << std::endl;
Chris@43 374 } else {
Chris@43 375 m_source->setTargetPlayLatency(jack_port_get_latency(port));
Chris@43 376 }
Chris@43 377
Chris@43 378 if (m_outputs.size() < physicalPortCount) {
Chris@43 379 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
Chris@43 380 }
Chris@43 381
Chris@43 382 m_outputs.push_back(port);
Chris@43 383 }
Chris@43 384
Chris@43 385 while (m_outputs.size() > channels) {
Chris@43 386 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@43 387 --itr;
Chris@43 388 jack_port_t *port = *itr;
Chris@43 389 if (port) jack_port_unregister(m_client, port);
Chris@43 390 m_outputs.erase(itr);
Chris@43 391 }
Chris@43 392
Chris@43 393 m_mutex.unlock();
Chris@43 394 }
Chris@43 395
Chris@43 396 int
Chris@43 397 AudioJACKTarget::process(jack_nframes_t nframes)
Chris@43 398 {
Chris@70 399 if (m_done) return 0;
Chris@70 400
Chris@43 401 if (!m_mutex.tryLock()) {
Chris@43 402 return 0;
Chris@43 403 }
Chris@43 404
Chris@43 405 if (m_outputs.empty()) {
Chris@43 406 m_mutex.unlock();
Chris@43 407 return 0;
Chris@43 408 }
Chris@43 409
Chris@43 410 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 411 std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
Chris@43 412 #endif
Chris@43 413
Chris@43 414 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 415 if (m_bufferSize != nframes) {
Chris@43 416 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
Chris@43 417 }
Chris@43 418 #endif
Chris@43 419
Chris@43 420 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
Chris@43 421
Chris@43 422 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 423 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
Chris@43 424 }
Chris@43 425
Chris@43 426 size_t received = 0;
Chris@43 427
Chris@43 428 if (m_source) {
Chris@43 429 received = m_source->getSourceSamples(nframes, buffers);
Chris@43 430 }
Chris@43 431
Chris@43 432 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 433 for (size_t i = received; i < nframes; ++i) {
Chris@43 434 buffers[ch][i] = 0.0;
Chris@43 435 }
Chris@43 436 }
Chris@43 437
Chris@43 438 float peakLeft = 0.0, peakRight = 0.0;
Chris@43 439
Chris@43 440 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 441
Chris@43 442 float peak = 0.0;
Chris@43 443
Chris@43 444 for (size_t i = 0; i < nframes; ++i) {
Chris@43 445 buffers[ch][i] *= m_outputGain;
Chris@43 446 float sample = fabsf(buffers[ch][i]);
Chris@43 447 if (sample > peak) peak = sample;
Chris@43 448 }
Chris@43 449
Chris@43 450 if (ch == 0) peakLeft = peak;
Chris@43 451 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
Chris@43 452 }
Chris@43 453
Chris@43 454 if (m_source) {
Chris@43 455 m_source->setOutputLevels(peakLeft, peakRight);
Chris@43 456 }
Chris@43 457
Chris@43 458 m_mutex.unlock();
Chris@43 459 return 0;
Chris@43 460 }
Chris@43 461
Chris@43 462 int
Chris@43 463 AudioJACKTarget::xrun()
Chris@43 464 {
Chris@43 465 std::cerr << "AudioJACKTarget: xrun!" << std::endl;
Chris@43 466 if (m_source) m_source->audioProcessingOverload();
Chris@43 467 return 0;
Chris@43 468 }
Chris@43 469
Chris@43 470 #endif /* HAVE_JACK */
Chris@43 471