annotate audioio/AudioJACKTarget.cpp @ 83:c60bf8995166

* Ensure plugin version is updated when a transform is re-run using a newer plugin -- so that the plugin version saved in the session corresponds with the one that was actually used, not the one previously specified * Fix uninitialised font size in preferences -- was causing all sorts of grief
author Chris Cannam
date Mon, 28 Jan 2008 17:43:44 +0000
parents 716e9d2f91c7
children 19142c58cc4c
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@43 24 //#define DEBUG_AUDIO_JACK_TARGET 1
Chris@43 25
Chris@43 26 #ifdef BUILD_STATIC
Chris@43 27 #ifdef Q_OS_LINUX
Chris@43 28
Chris@43 29 // Some lunacy to enable JACK support in static builds. JACK isn't
Chris@43 30 // supposed to be linked statically, because it depends on a
Chris@43 31 // consistent shared memory layout between client library and daemon,
Chris@43 32 // so it's very fragile in the face of version mismatches.
Chris@43 33 //
Chris@43 34 // Therefore for static builds on Linux we avoid linking against JACK
Chris@43 35 // at all during the build, instead using dlopen and runtime symbol
Chris@43 36 // lookup to switch on JACK support at runtime. The following big
Chris@43 37 // mess (down to the #endifs) is the code that implements this.
Chris@43 38
Chris@43 39 static void *symbol(const char *name)
Chris@43 40 {
Chris@43 41 static bool attempted = false;
Chris@43 42 static void *library = 0;
Chris@43 43 static std::map<const char *, void *> symbols;
Chris@43 44 if (symbols.find(name) != symbols.end()) return symbols[name];
Chris@43 45 if (!library) {
Chris@43 46 if (!attempted) {
Chris@43 47 library = ::dlopen("libjack.so.1", RTLD_NOW);
Chris@43 48 if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
Chris@43 49 if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
Chris@43 50 if (!library) {
Chris@43 51 std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
Chris@43 52 << ::dlerror() << " (tried .so, .so.0, .so.1)"
Chris@43 53 << std::endl;
Chris@43 54 }
Chris@43 55 attempted = true;
Chris@43 56 }
Chris@43 57 if (!library) return 0;
Chris@43 58 }
Chris@43 59 void *symbol = ::dlsym(library, name);
Chris@43 60 if (!symbol) {
Chris@43 61 std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
Chris@43 62 << name << ": " << ::dlerror() << std::endl;
Chris@43 63 }
Chris@43 64 symbols[name] = symbol;
Chris@43 65 return symbol;
Chris@43 66 }
Chris@43 67
Chris@56 68 static jack_client_t *dynamic_jack_client_open(const char *client_name,
Chris@56 69 jack_options_t options,
Chris@56 70 jack_status_t *status, ...)
Chris@56 71 {
Chris@70 72 typedef jack_client_t *(*func)(const char *client_name,
Chris@70 73 jack_options_t options,
Chris@70 74 jack_status_t *status, ...);
Chris@56 75 void *s = symbol("jack_client_open");
Chris@56 76 if (!s) return 0;
Chris@56 77 func f = (func)s;
Chris@56 78 return f(client_name, options, status); // varargs not supported here
Chris@56 79 }
Chris@56 80
Chris@43 81 static int dynamic_jack_set_process_callback(jack_client_t *client,
Chris@43 82 JackProcessCallback process_callback,
Chris@43 83 void *arg)
Chris@43 84 {
Chris@43 85 typedef int (*func)(jack_client_t *client,
Chris@43 86 JackProcessCallback process_callback,
Chris@43 87 void *arg);
Chris@43 88 void *s = symbol("jack_set_process_callback");
Chris@43 89 if (!s) return 1;
Chris@43 90 func f = (func)s;
Chris@43 91 return f(client, process_callback, arg);
Chris@43 92 }
Chris@43 93
Chris@43 94 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
Chris@43 95 JackXRunCallback xrun_callback,
Chris@43 96 void *arg)
Chris@43 97 {
Chris@43 98 typedef int (*func)(jack_client_t *client,
Chris@43 99 JackXRunCallback xrun_callback,
Chris@43 100 void *arg);
Chris@43 101 void *s = symbol("jack_set_xrun_callback");
Chris@43 102 if (!s) return 1;
Chris@43 103 func f = (func)s;
Chris@43 104 return f(client, xrun_callback, arg);
Chris@43 105 }
Chris@43 106
Chris@43 107 static const char **dynamic_jack_get_ports(jack_client_t *client,
Chris@43 108 const char *port_name_pattern,
Chris@43 109 const char *type_name_pattern,
Chris@43 110 unsigned long flags)
Chris@43 111 {
Chris@43 112 typedef const char **(*func)(jack_client_t *client,
Chris@43 113 const char *port_name_pattern,
Chris@43 114 const char *type_name_pattern,
Chris@43 115 unsigned long flags);
Chris@43 116 void *s = symbol("jack_get_ports");
Chris@43 117 if (!s) return 0;
Chris@43 118 func f = (func)s;
Chris@43 119 return f(client, port_name_pattern, type_name_pattern, flags);
Chris@43 120 }
Chris@43 121
Chris@43 122 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
Chris@43 123 const char *port_name,
Chris@43 124 const char *port_type,
Chris@43 125 unsigned long flags,
Chris@43 126 unsigned long buffer_size)
Chris@43 127 {
Chris@43 128 typedef jack_port_t *(*func)(jack_client_t *client,
Chris@43 129 const char *port_name,
Chris@43 130 const char *port_type,
Chris@43 131 unsigned long flags,
Chris@43 132 unsigned long buffer_size);
Chris@43 133 void *s = symbol("jack_port_register");
Chris@43 134 if (!s) return 0;
Chris@43 135 func f = (func)s;
Chris@43 136 return f(client, port_name, port_type, flags, buffer_size);
Chris@43 137 }
Chris@43 138
Chris@43 139 static int dynamic_jack_connect(jack_client_t *client,
Chris@43 140 const char *source,
Chris@43 141 const char *dest)
Chris@43 142 {
Chris@43 143 typedef int (*func)(jack_client_t *client,
Chris@43 144 const char *source,
Chris@43 145 const char *dest);
Chris@43 146 void *s = symbol("jack_connect");
Chris@43 147 if (!s) return 1;
Chris@43 148 func f = (func)s;
Chris@43 149 return f(client, source, dest);
Chris@43 150 }
Chris@43 151
Chris@43 152 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
Chris@43 153 jack_nframes_t sz)
Chris@43 154 {
Chris@43 155 typedef void *(*func)(jack_port_t *, jack_nframes_t);
Chris@43 156 void *s = symbol("jack_port_get_buffer");
Chris@43 157 if (!s) return 0;
Chris@43 158 func f = (func)s;
Chris@43 159 return f(port, sz);
Chris@43 160 }
Chris@43 161
Chris@43 162 static int dynamic_jack_port_unregister(jack_client_t *client,
Chris@43 163 jack_port_t *port)
Chris@43 164 {
Chris@43 165 typedef int(*func)(jack_client_t *, jack_port_t *);
Chris@43 166 void *s = symbol("jack_port_unregister");
Chris@43 167 if (!s) return 0;
Chris@43 168 func f = (func)s;
Chris@43 169 return f(client, port);
Chris@43 170 }
Chris@43 171
Chris@43 172 #define dynamic1(rv, name, argtype, failval) \
Chris@43 173 static rv dynamic_##name(argtype arg) { \
Chris@43 174 typedef rv (*func) (argtype); \
Chris@43 175 void *s = symbol(#name); \
Chris@43 176 if (!s) return failval; \
Chris@43 177 func f = (func) s; \
Chris@43 178 return f(arg); \
Chris@43 179 }
Chris@43 180
Chris@43 181 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
Chris@43 182 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
Chris@43 183 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
Chris@43 184 dynamic1(int, jack_activate, jack_client_t *, 1);
Chris@43 185 dynamic1(int, jack_deactivate, jack_client_t *, 1);
Chris@43 186 dynamic1(int, jack_client_close, jack_client_t *, 1);
Chris@43 187 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
Chris@43 188 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
Chris@43 189
Chris@43 190 #define jack_client_new dynamic_jack_client_new
Chris@56 191 #define jack_client_open dynamic_jack_client_open
Chris@43 192 #define jack_get_buffer_size dynamic_jack_get_buffer_size
Chris@43 193 #define jack_get_sample_rate dynamic_jack_get_sample_rate
Chris@43 194 #define jack_set_process_callback dynamic_jack_set_process_callback
Chris@43 195 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
Chris@43 196 #define jack_activate dynamic_jack_activate
Chris@43 197 #define jack_deactivate dynamic_jack_deactivate
Chris@43 198 #define jack_client_close dynamic_jack_client_close
Chris@43 199 #define jack_get_ports dynamic_jack_get_ports
Chris@43 200 #define jack_port_register dynamic_jack_port_register
Chris@43 201 #define jack_port_unregister dynamic_jack_port_unregister
Chris@43 202 #define jack_port_get_latency dynamic_jack_port_get_latency
Chris@43 203 #define jack_port_name dynamic_jack_port_name
Chris@43 204 #define jack_connect dynamic_jack_connect
Chris@43 205 #define jack_port_get_buffer dynamic_jack_port_get_buffer
Chris@43 206
Chris@43 207 #endif
Chris@43 208 #endif
Chris@43 209
Chris@43 210 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
Chris@43 211 AudioCallbackPlayTarget(source),
Chris@43 212 m_client(0),
Chris@43 213 m_bufferSize(0),
Chris@70 214 m_sampleRate(0),
Chris@70 215 m_done(false)
Chris@43 216 {
Chris@56 217 JackOptions options = JackNullOption;
Chris@56 218 #ifdef HAVE_PORTAUDIO
Chris@56 219 options = JackNoStartServer;
Chris@56 220 #endif
Chris@56 221
Chris@56 222 JackStatus status = JackStatus(0);
Chris@57 223 m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
Chris@57 224 options, &status);
Chris@57 225
Chris@43 226 if (!m_client) {
Chris@56 227 std::cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
Chris@56 228 << status << std::endl;
Chris@56 229 return;
Chris@43 230 }
Chris@43 231
Chris@43 232 m_bufferSize = jack_get_buffer_size(m_client);
Chris@43 233 m_sampleRate = jack_get_sample_rate(m_client);
Chris@43 234
Chris@43 235 jack_set_xrun_callback(m_client, xrunStatic, this);
Chris@43 236 jack_set_process_callback(m_client, processStatic, this);
Chris@43 237
Chris@43 238 if (jack_activate(m_client)) {
Chris@43 239 std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
Chris@43 240 << std::endl;
Chris@43 241 }
Chris@43 242
Chris@43 243 if (m_source) {
Chris@43 244 sourceModelReplaced();
Chris@43 245 }
Chris@43 246 }
Chris@43 247
Chris@43 248 AudioJACKTarget::~AudioJACKTarget()
Chris@43 249 {
Chris@43 250 std::cerr << "AudioJACKTarget::~AudioJACKTarget()" << std::endl;
Chris@70 251
Chris@70 252 shutdown();
Chris@70 253
Chris@43 254 if (m_client) {
Chris@70 255
Chris@70 256 while (m_outputs.size() > 0) {
Chris@70 257 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@70 258 --itr;
Chris@70 259 jack_port_t *port = *itr;
Chris@70 260 std::cerr << "unregister " << m_outputs.size() << std::endl;
Chris@70 261 if (port) jack_port_unregister(m_client, port);
Chris@70 262 m_outputs.erase(itr);
Chris@70 263 }
Chris@70 264 std::cerr << "Deactivating... ";
Chris@43 265 jack_deactivate(m_client);
Chris@70 266 std::cerr << "done\nClosing... ";
Chris@43 267 jack_client_close(m_client);
Chris@70 268 std::cerr << "done" << std::endl;
Chris@43 269 }
Chris@70 270
Chris@70 271 m_client = 0;
Chris@70 272
Chris@43 273 std::cerr << "AudioJACKTarget::~AudioJACKTarget() done" << std::endl;
Chris@43 274 }
Chris@43 275
Chris@70 276 void
Chris@70 277 AudioJACKTarget::shutdown()
Chris@70 278 {
Chris@70 279 m_done = true;
Chris@70 280 }
Chris@70 281
Chris@43 282 bool
Chris@43 283 AudioJACKTarget::isOK() const
Chris@43 284 {
Chris@43 285 return (m_client != 0);
Chris@43 286 }
Chris@43 287
Chris@43 288 int
Chris@43 289 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
Chris@43 290 {
Chris@43 291 return ((AudioJACKTarget *)arg)->process(nframes);
Chris@43 292 }
Chris@43 293
Chris@43 294 int
Chris@43 295 AudioJACKTarget::xrunStatic(void *arg)
Chris@43 296 {
Chris@43 297 return ((AudioJACKTarget *)arg)->xrun();
Chris@43 298 }
Chris@43 299
Chris@43 300 void
Chris@43 301 AudioJACKTarget::sourceModelReplaced()
Chris@43 302 {
Chris@43 303 m_mutex.lock();
Chris@43 304
Chris@43 305 m_source->setTargetBlockSize(m_bufferSize);
Chris@43 306 m_source->setTargetSampleRate(m_sampleRate);
Chris@43 307
Chris@43 308 size_t channels = m_source->getSourceChannelCount();
Chris@43 309
Chris@43 310 // Because we offer pan, we always want at least 2 channels
Chris@43 311 if (channels < 2) channels = 2;
Chris@43 312
Chris@43 313 if (channels == m_outputs.size() || !m_client) {
Chris@43 314 m_mutex.unlock();
Chris@43 315 return;
Chris@43 316 }
Chris@43 317
Chris@43 318 const char **ports =
Chris@43 319 jack_get_ports(m_client, NULL, NULL,
Chris@43 320 JackPortIsPhysical | JackPortIsInput);
Chris@43 321 size_t physicalPortCount = 0;
Chris@43 322 while (ports[physicalPortCount]) ++physicalPortCount;
Chris@43 323
Chris@43 324 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 325 std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
Chris@43 326 #endif
Chris@43 327
Chris@43 328 while (m_outputs.size() < channels) {
Chris@43 329
Chris@43 330 char name[20];
Chris@43 331 jack_port_t *port;
Chris@43 332
Chris@43 333 sprintf(name, "out %d", m_outputs.size() + 1);
Chris@43 334
Chris@43 335 port = jack_port_register(m_client,
Chris@43 336 name,
Chris@43 337 JACK_DEFAULT_AUDIO_TYPE,
Chris@43 338 JackPortIsOutput,
Chris@43 339 0);
Chris@43 340
Chris@43 341 if (!port) {
Chris@43 342 std::cerr
Chris@43 343 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
Chris@43 344 << m_outputs.size() << std::endl;
Chris@43 345 } else {
Chris@43 346 m_source->setTargetPlayLatency(jack_port_get_latency(port));
Chris@43 347 }
Chris@43 348
Chris@43 349 if (m_outputs.size() < physicalPortCount) {
Chris@43 350 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
Chris@43 351 }
Chris@43 352
Chris@43 353 m_outputs.push_back(port);
Chris@43 354 }
Chris@43 355
Chris@43 356 while (m_outputs.size() > channels) {
Chris@43 357 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@43 358 --itr;
Chris@43 359 jack_port_t *port = *itr;
Chris@43 360 if (port) jack_port_unregister(m_client, port);
Chris@43 361 m_outputs.erase(itr);
Chris@43 362 }
Chris@43 363
Chris@43 364 m_mutex.unlock();
Chris@43 365 }
Chris@43 366
Chris@43 367 int
Chris@43 368 AudioJACKTarget::process(jack_nframes_t nframes)
Chris@43 369 {
Chris@70 370 if (m_done) return 0;
Chris@70 371
Chris@43 372 if (!m_mutex.tryLock()) {
Chris@43 373 return 0;
Chris@43 374 }
Chris@43 375
Chris@43 376 if (m_outputs.empty()) {
Chris@43 377 m_mutex.unlock();
Chris@43 378 return 0;
Chris@43 379 }
Chris@43 380
Chris@43 381 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 382 std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
Chris@43 383 #endif
Chris@43 384
Chris@43 385 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@43 386 if (m_bufferSize != nframes) {
Chris@43 387 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
Chris@43 388 }
Chris@43 389 #endif
Chris@43 390
Chris@43 391 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
Chris@43 392
Chris@43 393 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 394 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
Chris@43 395 }
Chris@43 396
Chris@43 397 size_t received = 0;
Chris@43 398
Chris@43 399 if (m_source) {
Chris@43 400 received = m_source->getSourceSamples(nframes, buffers);
Chris@43 401 }
Chris@43 402
Chris@43 403 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 404 for (size_t i = received; i < nframes; ++i) {
Chris@43 405 buffers[ch][i] = 0.0;
Chris@43 406 }
Chris@43 407 }
Chris@43 408
Chris@43 409 float peakLeft = 0.0, peakRight = 0.0;
Chris@43 410
Chris@43 411 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@43 412
Chris@43 413 float peak = 0.0;
Chris@43 414
Chris@43 415 for (size_t i = 0; i < nframes; ++i) {
Chris@43 416 buffers[ch][i] *= m_outputGain;
Chris@43 417 float sample = fabsf(buffers[ch][i]);
Chris@43 418 if (sample > peak) peak = sample;
Chris@43 419 }
Chris@43 420
Chris@43 421 if (ch == 0) peakLeft = peak;
Chris@43 422 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
Chris@43 423 }
Chris@43 424
Chris@43 425 if (m_source) {
Chris@43 426 m_source->setOutputLevels(peakLeft, peakRight);
Chris@43 427 }
Chris@43 428
Chris@43 429 m_mutex.unlock();
Chris@43 430 return 0;
Chris@43 431 }
Chris@43 432
Chris@43 433 int
Chris@43 434 AudioJACKTarget::xrun()
Chris@43 435 {
Chris@43 436 std::cerr << "AudioJACKTarget: xrun!" << std::endl;
Chris@43 437 if (m_source) m_source->audioProcessingOverload();
Chris@43 438 return 0;
Chris@43 439 }
Chris@43 440
Chris@43 441 #endif /* HAVE_JACK */
Chris@43 442