annotate audioio/AudioJACKTarget.cpp @ 201:de783e8ee5f0

* Hoist alignment model set/query up to Model, so any models can be aligned * Add Model::aboutToDelete and aboutToBeDeleted for management of models that are contained by or referred to by other models instead of only the document
author Chris Cannam
date Wed, 24 Oct 2007 15:21:38 +0000
parents 75b0087233d8
children 738968d96e19
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #ifdef HAVE_JACK
Chris@0 17
Chris@0 18 #include "AudioJACKTarget.h"
Chris@0 19 #include "AudioCallbackPlaySource.h"
Chris@0 20
Chris@0 21 #include <iostream>
Chris@0 22 #include <cmath>
Chris@0 23
Chris@0 24 //#define DEBUG_AUDIO_JACK_TARGET 1
Chris@0 25
Chris@2 26 #ifdef BUILD_STATIC
Chris@2 27 #ifdef Q_OS_LINUX
Chris@2 28
Chris@2 29 // Some lunacy to enable JACK support in static builds. JACK isn't
Chris@2 30 // supposed to be linked statically, because it depends on a
Chris@2 31 // consistent shared memory layout between client library and daemon,
Chris@2 32 // so it's very fragile in the face of version mismatches.
Chris@2 33 //
Chris@2 34 // Therefore for static builds on Linux we avoid linking against JACK
Chris@2 35 // at all during the build, instead using dlopen and runtime symbol
Chris@2 36 // lookup to switch on JACK support at runtime. The following big
Chris@2 37 // mess (down to the #endifs) is the code that implements this.
Chris@2 38
Chris@2 39 static void *symbol(const char *name)
Chris@2 40 {
Chris@2 41 static bool attempted = false;
Chris@2 42 static void *library = 0;
Chris@2 43 static std::map<const char *, void *> symbols;
Chris@2 44 if (symbols.find(name) != symbols.end()) return symbols[name];
Chris@2 45 if (!library) {
Chris@2 46 if (!attempted) {
Chris@2 47 library = ::dlopen("libjack.so.1", RTLD_NOW);
Chris@2 48 if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
Chris@2 49 if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
Chris@2 50 if (!library) {
Chris@2 51 std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
Chris@2 52 << ::dlerror() << " (tried .so, .so.0, .so.1)"
Chris@2 53 << std::endl;
Chris@2 54 }
Chris@2 55 attempted = true;
Chris@2 56 }
Chris@2 57 if (!library) return 0;
Chris@2 58 }
Chris@2 59 void *symbol = ::dlsym(library, name);
Chris@2 60 if (!symbol) {
Chris@2 61 std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
Chris@2 62 << name << ": " << ::dlerror() << std::endl;
Chris@2 63 }
Chris@2 64 symbols[name] = symbol;
Chris@2 65 return symbol;
Chris@2 66 }
Chris@2 67
Chris@2 68 static int dynamic_jack_set_process_callback(jack_client_t *client,
Chris@2 69 JackProcessCallback process_callback,
Chris@2 70 void *arg)
Chris@2 71 {
Chris@2 72 typedef int (*func)(jack_client_t *client,
Chris@2 73 JackProcessCallback process_callback,
Chris@2 74 void *arg);
Chris@2 75 void *s = symbol("jack_set_process_callback");
Chris@2 76 if (!s) return 1;
Chris@2 77 func f = (func)s;
Chris@2 78 return f(client, process_callback, arg);
Chris@2 79 }
Chris@2 80
Chris@41 81 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
Chris@41 82 JackXRunCallback xrun_callback,
Chris@41 83 void *arg)
Chris@41 84 {
Chris@41 85 typedef int (*func)(jack_client_t *client,
Chris@41 86 JackXRunCallback xrun_callback,
Chris@41 87 void *arg);
Chris@41 88 void *s = symbol("jack_set_xrun_callback");
Chris@41 89 if (!s) return 1;
Chris@41 90 func f = (func)s;
Chris@41 91 return f(client, xrun_callback, arg);
Chris@41 92 }
Chris@41 93
Chris@2 94 static const char **dynamic_jack_get_ports(jack_client_t *client,
Chris@2 95 const char *port_name_pattern,
Chris@2 96 const char *type_name_pattern,
Chris@2 97 unsigned long flags)
Chris@2 98 {
Chris@2 99 typedef const char **(*func)(jack_client_t *client,
Chris@2 100 const char *port_name_pattern,
Chris@2 101 const char *type_name_pattern,
Chris@2 102 unsigned long flags);
Chris@2 103 void *s = symbol("jack_get_ports");
Chris@2 104 if (!s) return 0;
Chris@2 105 func f = (func)s;
Chris@2 106 return f(client, port_name_pattern, type_name_pattern, flags);
Chris@2 107 }
Chris@2 108
Chris@2 109 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
Chris@2 110 const char *port_name,
Chris@2 111 const char *port_type,
Chris@2 112 unsigned long flags,
Chris@2 113 unsigned long buffer_size)
Chris@2 114 {
Chris@2 115 typedef jack_port_t *(*func)(jack_client_t *client,
Chris@2 116 const char *port_name,
Chris@2 117 const char *port_type,
Chris@2 118 unsigned long flags,
Chris@2 119 unsigned long buffer_size);
Chris@2 120 void *s = symbol("jack_port_register");
Chris@2 121 if (!s) return 0;
Chris@2 122 func f = (func)s;
Chris@2 123 return f(client, port_name, port_type, flags, buffer_size);
Chris@2 124 }
Chris@2 125
Chris@2 126 static int dynamic_jack_connect(jack_client_t *client,
Chris@2 127 const char *source,
Chris@2 128 const char *dest)
Chris@2 129 {
Chris@2 130 typedef int (*func)(jack_client_t *client,
Chris@2 131 const char *source,
Chris@2 132 const char *dest);
Chris@2 133 void *s = symbol("jack_connect");
Chris@2 134 if (!s) return 1;
Chris@2 135 func f = (func)s;
Chris@2 136 return f(client, source, dest);
Chris@2 137 }
Chris@2 138
Chris@2 139 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
Chris@2 140 jack_nframes_t sz)
Chris@2 141 {
Chris@2 142 typedef void *(*func)(jack_port_t *, jack_nframes_t);
Chris@2 143 void *s = symbol("jack_port_get_buffer");
Chris@2 144 if (!s) return 0;
Chris@2 145 func f = (func)s;
Chris@2 146 return f(port, sz);
Chris@2 147 }
Chris@2 148
Chris@2 149 static int dynamic_jack_port_unregister(jack_client_t *client,
Chris@2 150 jack_port_t *port)
Chris@2 151 {
Chris@2 152 typedef int(*func)(jack_client_t *, jack_port_t *);
Chris@2 153 void *s = symbol("jack_port_unregister");
Chris@2 154 if (!s) return 0;
Chris@2 155 func f = (func)s;
Chris@2 156 return f(client, port);
Chris@2 157 }
Chris@2 158
Chris@2 159 #define dynamic1(rv, name, argtype, failval) \
Chris@2 160 static rv dynamic_##name(argtype arg) { \
Chris@2 161 typedef rv (*func) (argtype); \
Chris@2 162 void *s = symbol(#name); \
Chris@2 163 if (!s) return failval; \
Chris@2 164 func f = (func) s; \
Chris@2 165 return f(arg); \
Chris@2 166 }
Chris@2 167
Chris@2 168 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
Chris@2 169 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
Chris@2 170 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
Chris@2 171 dynamic1(int, jack_activate, jack_client_t *, 1);
Chris@2 172 dynamic1(int, jack_deactivate, jack_client_t *, 1);
Chris@2 173 dynamic1(int, jack_client_close, jack_client_t *, 1);
Chris@2 174 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
Chris@2 175 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
Chris@2 176
Chris@2 177 #define jack_client_new dynamic_jack_client_new
Chris@2 178 #define jack_get_buffer_size dynamic_jack_get_buffer_size
Chris@2 179 #define jack_get_sample_rate dynamic_jack_get_sample_rate
Chris@2 180 #define jack_set_process_callback dynamic_jack_set_process_callback
Chris@41 181 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
Chris@2 182 #define jack_activate dynamic_jack_activate
Chris@2 183 #define jack_deactivate dynamic_jack_deactivate
Chris@2 184 #define jack_client_close dynamic_jack_client_close
Chris@2 185 #define jack_get_ports dynamic_jack_get_ports
Chris@2 186 #define jack_port_register dynamic_jack_port_register
Chris@2 187 #define jack_port_unregister dynamic_jack_port_unregister
Chris@2 188 #define jack_port_get_latency dynamic_jack_port_get_latency
Chris@2 189 #define jack_port_name dynamic_jack_port_name
Chris@2 190 #define jack_connect dynamic_jack_connect
Chris@2 191 #define jack_port_get_buffer dynamic_jack_port_get_buffer
Chris@2 192
Chris@2 193 #endif
Chris@2 194 #endif
Chris@2 195
Chris@0 196 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
Chris@0 197 AudioCallbackPlayTarget(source),
Chris@0 198 m_client(0),
Chris@0 199 m_bufferSize(0),
Chris@0 200 m_sampleRate(0)
Chris@0 201 {
Chris@131 202 char name[100];
Chris@0 203 strcpy(name, "Sonic Visualiser");
Chris@0 204 m_client = jack_client_new(name);
Chris@0 205
Chris@0 206 if (!m_client) {
Chris@0 207 sprintf(name, "Sonic Visualiser (%d)", (int)getpid());
Chris@0 208 m_client = jack_client_new(name);
Chris@0 209 if (!m_client) {
Chris@0 210 std::cerr
Chris@0 211 << "ERROR: AudioJACKTarget: Failed to connect to JACK server"
Chris@0 212 << std::endl;
Chris@0 213 }
Chris@0 214 }
Chris@0 215
Chris@0 216 if (!m_client) return;
Chris@0 217
Chris@0 218 m_bufferSize = jack_get_buffer_size(m_client);
Chris@0 219 m_sampleRate = jack_get_sample_rate(m_client);
Chris@0 220
Chris@41 221 jack_set_xrun_callback(m_client, xrunStatic, this);
Chris@0 222 jack_set_process_callback(m_client, processStatic, this);
Chris@0 223
Chris@0 224 if (jack_activate(m_client)) {
Chris@0 225 std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
Chris@0 226 << std::endl;
Chris@0 227 }
Chris@0 228
Chris@0 229 if (m_source) {
Chris@0 230 sourceModelReplaced();
Chris@0 231 }
Chris@0 232 }
Chris@0 233
Chris@0 234 AudioJACKTarget::~AudioJACKTarget()
Chris@0 235 {
Chris@0 236 if (m_client) {
Chris@0 237 jack_deactivate(m_client);
Chris@0 238 jack_client_close(m_client);
Chris@0 239 }
Chris@0 240 }
Chris@0 241
Chris@0 242 bool
Chris@0 243 AudioJACKTarget::isOK() const
Chris@0 244 {
Chris@0 245 return (m_client != 0);
Chris@0 246 }
Chris@0 247
Chris@0 248 int
Chris@0 249 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
Chris@0 250 {
Chris@0 251 return ((AudioJACKTarget *)arg)->process(nframes);
Chris@0 252 }
Chris@0 253
Chris@41 254 int
Chris@41 255 AudioJACKTarget::xrunStatic(void *arg)
Chris@41 256 {
Chris@41 257 return ((AudioJACKTarget *)arg)->xrun();
Chris@41 258 }
Chris@41 259
Chris@0 260 void
Chris@0 261 AudioJACKTarget::sourceModelReplaced()
Chris@0 262 {
Chris@0 263 m_mutex.lock();
Chris@0 264
Chris@0 265 m_source->setTargetBlockSize(m_bufferSize);
Chris@0 266 m_source->setTargetSampleRate(m_sampleRate);
Chris@0 267
Chris@0 268 size_t channels = m_source->getSourceChannelCount();
Chris@0 269
Chris@0 270 // Because we offer pan, we always want at least 2 channels
Chris@0 271 if (channels < 2) channels = 2;
Chris@0 272
Chris@0 273 if (channels == m_outputs.size() || !m_client) {
Chris@0 274 m_mutex.unlock();
Chris@0 275 return;
Chris@0 276 }
Chris@0 277
Chris@0 278 const char **ports =
Chris@0 279 jack_get_ports(m_client, NULL, NULL,
Chris@0 280 JackPortIsPhysical | JackPortIsInput);
Chris@0 281 size_t physicalPortCount = 0;
Chris@0 282 while (ports[physicalPortCount]) ++physicalPortCount;
Chris@0 283
Chris@0 284 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 285 std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
Chris@0 286 #endif
Chris@0 287
Chris@0 288 while (m_outputs.size() < channels) {
Chris@0 289
Chris@0 290 char name[20];
Chris@0 291 jack_port_t *port;
Chris@0 292
Chris@0 293 sprintf(name, "out %d", m_outputs.size() + 1);
Chris@0 294
Chris@0 295 port = jack_port_register(m_client,
Chris@0 296 name,
Chris@0 297 JACK_DEFAULT_AUDIO_TYPE,
Chris@0 298 JackPortIsOutput,
Chris@0 299 0);
Chris@0 300
Chris@0 301 if (!port) {
Chris@0 302 std::cerr
Chris@0 303 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
Chris@0 304 << m_outputs.size() << std::endl;
Chris@0 305 } else {
Chris@0 306 m_source->setTargetPlayLatency(jack_port_get_latency(port));
Chris@0 307 }
Chris@0 308
Chris@0 309 if (m_outputs.size() < physicalPortCount) {
Chris@0 310 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
Chris@0 311 }
Chris@0 312
Chris@0 313 m_outputs.push_back(port);
Chris@0 314 }
Chris@0 315
Chris@0 316 while (m_outputs.size() > channels) {
Chris@0 317 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@0 318 --itr;
Chris@0 319 jack_port_t *port = *itr;
Chris@0 320 if (port) jack_port_unregister(m_client, port);
Chris@0 321 m_outputs.erase(itr);
Chris@0 322 }
Chris@0 323
Chris@0 324 m_mutex.unlock();
Chris@0 325 }
Chris@0 326
Chris@0 327 int
Chris@0 328 AudioJACKTarget::process(jack_nframes_t nframes)
Chris@0 329 {
Chris@0 330 if (!m_mutex.tryLock()) {
Chris@0 331 return 0;
Chris@0 332 }
Chris@0 333
Chris@0 334 if (m_outputs.empty()) {
Chris@0 335 m_mutex.unlock();
Chris@0 336 return 0;
Chris@0 337 }
Chris@0 338
Chris@0 339 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 340 std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
Chris@0 341 #endif
Chris@0 342
Chris@0 343 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 344 if (m_bufferSize != nframes) {
Chris@0 345 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
Chris@0 346 }
Chris@0 347 #endif
Chris@0 348
Chris@0 349 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
Chris@0 350
Chris@0 351 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@0 352 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
Chris@0 353 }
Chris@0 354
Chris@106 355 size_t received = 0;
Chris@106 356
Chris@0 357 if (m_source) {
Chris@106 358 received = m_source->getSourceSamples(nframes, buffers);
Chris@106 359 }
Chris@106 360
Chris@106 361 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@106 362 for (size_t i = received; i < nframes; ++i) {
Chris@106 363 buffers[ch][i] = 0.0;
Chris@106 364 }
Chris@0 365 }
Chris@0 366
Chris@0 367 float peakLeft = 0.0, peakRight = 0.0;
Chris@0 368
Chris@0 369 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@0 370
Chris@0 371 float peak = 0.0;
Chris@0 372
Chris@0 373 for (size_t i = 0; i < nframes; ++i) {
Chris@0 374 buffers[ch][i] *= m_outputGain;
Chris@0 375 float sample = fabsf(buffers[ch][i]);
Chris@0 376 if (sample > peak) peak = sample;
Chris@0 377 }
Chris@0 378
Chris@0 379 if (ch == 0) peakLeft = peak;
Chris@0 380 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
Chris@0 381 }
Chris@0 382
Chris@0 383 if (m_source) {
Chris@0 384 m_source->setOutputLevels(peakLeft, peakRight);
Chris@0 385 }
Chris@0 386
Chris@0 387 m_mutex.unlock();
Chris@0 388 return 0;
Chris@0 389 }
Chris@0 390
Chris@41 391 int
Chris@41 392 AudioJACKTarget::xrun()
Chris@41 393 {
Chris@41 394 std::cerr << "AudioJACKTarget: xrun!" << std::endl;
Chris@42 395 if (m_source) m_source->audioProcessingOverload();
Chris@57 396 return 0;
Chris@41 397 }
Chris@0 398
Chris@0 399 #endif /* HAVE_JACK */
Chris@0 400