annotate audioio/AudioJACKTarget.cpp @ 187:77cd75905998

* Ensure locale from environment is retained after plugin load, not just C locale
author Chris Cannam
date Fri, 05 Oct 2007 13:26:47 +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