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