annotate audioio/AudioJACKTarget.cpp @ 35:06787742542a

* Add a bit of resistance to pane dragging so as to make it harder to inadvertently drag in the other axis from the one you intended
author Chris Cannam
date Fri, 22 Sep 2006 16:46:10 +0000
parents 42a78f0e8fe2
children fbd7a497fd89
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@2 81 static const char **dynamic_jack_get_ports(jack_client_t *client,
Chris@2 82 const char *port_name_pattern,
Chris@2 83 const char *type_name_pattern,
Chris@2 84 unsigned long flags)
Chris@2 85 {
Chris@2 86 typedef const char **(*func)(jack_client_t *client,
Chris@2 87 const char *port_name_pattern,
Chris@2 88 const char *type_name_pattern,
Chris@2 89 unsigned long flags);
Chris@2 90 void *s = symbol("jack_get_ports");
Chris@2 91 if (!s) return 0;
Chris@2 92 func f = (func)s;
Chris@2 93 return f(client, port_name_pattern, type_name_pattern, flags);
Chris@2 94 }
Chris@2 95
Chris@2 96 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
Chris@2 97 const char *port_name,
Chris@2 98 const char *port_type,
Chris@2 99 unsigned long flags,
Chris@2 100 unsigned long buffer_size)
Chris@2 101 {
Chris@2 102 typedef jack_port_t *(*func)(jack_client_t *client,
Chris@2 103 const char *port_name,
Chris@2 104 const char *port_type,
Chris@2 105 unsigned long flags,
Chris@2 106 unsigned long buffer_size);
Chris@2 107 void *s = symbol("jack_port_register");
Chris@2 108 if (!s) return 0;
Chris@2 109 func f = (func)s;
Chris@2 110 return f(client, port_name, port_type, flags, buffer_size);
Chris@2 111 }
Chris@2 112
Chris@2 113 static int dynamic_jack_connect(jack_client_t *client,
Chris@2 114 const char *source,
Chris@2 115 const char *dest)
Chris@2 116 {
Chris@2 117 typedef int (*func)(jack_client_t *client,
Chris@2 118 const char *source,
Chris@2 119 const char *dest);
Chris@2 120 void *s = symbol("jack_connect");
Chris@2 121 if (!s) return 1;
Chris@2 122 func f = (func)s;
Chris@2 123 return f(client, source, dest);
Chris@2 124 }
Chris@2 125
Chris@2 126 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
Chris@2 127 jack_nframes_t sz)
Chris@2 128 {
Chris@2 129 typedef void *(*func)(jack_port_t *, jack_nframes_t);
Chris@2 130 void *s = symbol("jack_port_get_buffer");
Chris@2 131 if (!s) return 0;
Chris@2 132 func f = (func)s;
Chris@2 133 return f(port, sz);
Chris@2 134 }
Chris@2 135
Chris@2 136 static int dynamic_jack_port_unregister(jack_client_t *client,
Chris@2 137 jack_port_t *port)
Chris@2 138 {
Chris@2 139 typedef int(*func)(jack_client_t *, jack_port_t *);
Chris@2 140 void *s = symbol("jack_port_unregister");
Chris@2 141 if (!s) return 0;
Chris@2 142 func f = (func)s;
Chris@2 143 return f(client, port);
Chris@2 144 }
Chris@2 145
Chris@2 146 #define dynamic1(rv, name, argtype, failval) \
Chris@2 147 static rv dynamic_##name(argtype arg) { \
Chris@2 148 typedef rv (*func) (argtype); \
Chris@2 149 void *s = symbol(#name); \
Chris@2 150 if (!s) return failval; \
Chris@2 151 func f = (func) s; \
Chris@2 152 return f(arg); \
Chris@2 153 }
Chris@2 154
Chris@2 155 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
Chris@2 156 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
Chris@2 157 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
Chris@2 158 dynamic1(int, jack_activate, jack_client_t *, 1);
Chris@2 159 dynamic1(int, jack_deactivate, jack_client_t *, 1);
Chris@2 160 dynamic1(int, jack_client_close, jack_client_t *, 1);
Chris@2 161 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
Chris@2 162 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
Chris@2 163
Chris@2 164 #define jack_client_new dynamic_jack_client_new
Chris@2 165 #define jack_get_buffer_size dynamic_jack_get_buffer_size
Chris@2 166 #define jack_get_sample_rate dynamic_jack_get_sample_rate
Chris@2 167 #define jack_set_process_callback dynamic_jack_set_process_callback
Chris@2 168 #define jack_activate dynamic_jack_activate
Chris@2 169 #define jack_deactivate dynamic_jack_deactivate
Chris@2 170 #define jack_client_close dynamic_jack_client_close
Chris@2 171 #define jack_get_ports dynamic_jack_get_ports
Chris@2 172 #define jack_port_register dynamic_jack_port_register
Chris@2 173 #define jack_port_unregister dynamic_jack_port_unregister
Chris@2 174 #define jack_port_get_latency dynamic_jack_port_get_latency
Chris@2 175 #define jack_port_name dynamic_jack_port_name
Chris@2 176 #define jack_connect dynamic_jack_connect
Chris@2 177 #define jack_port_get_buffer dynamic_jack_port_get_buffer
Chris@2 178
Chris@2 179 #endif
Chris@2 180 #endif
Chris@2 181
Chris@0 182 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
Chris@0 183 AudioCallbackPlayTarget(source),
Chris@0 184 m_client(0),
Chris@0 185 m_bufferSize(0),
Chris@0 186 m_sampleRate(0)
Chris@0 187 {
Chris@0 188 char name[20];
Chris@0 189 strcpy(name, "Sonic Visualiser");
Chris@0 190 m_client = jack_client_new(name);
Chris@0 191
Chris@0 192 if (!m_client) {
Chris@0 193 sprintf(name, "Sonic Visualiser (%d)", (int)getpid());
Chris@0 194 m_client = jack_client_new(name);
Chris@0 195 if (!m_client) {
Chris@0 196 std::cerr
Chris@0 197 << "ERROR: AudioJACKTarget: Failed to connect to JACK server"
Chris@0 198 << std::endl;
Chris@0 199 }
Chris@0 200 }
Chris@0 201
Chris@0 202 if (!m_client) return;
Chris@0 203
Chris@0 204 m_bufferSize = jack_get_buffer_size(m_client);
Chris@0 205 m_sampleRate = jack_get_sample_rate(m_client);
Chris@0 206
Chris@0 207 jack_set_process_callback(m_client, processStatic, this);
Chris@0 208
Chris@0 209 if (jack_activate(m_client)) {
Chris@0 210 std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
Chris@0 211 << std::endl;
Chris@0 212 }
Chris@0 213
Chris@0 214 if (m_source) {
Chris@0 215 sourceModelReplaced();
Chris@0 216 }
Chris@0 217 }
Chris@0 218
Chris@0 219 AudioJACKTarget::~AudioJACKTarget()
Chris@0 220 {
Chris@0 221 if (m_client) {
Chris@0 222 jack_deactivate(m_client);
Chris@0 223 jack_client_close(m_client);
Chris@0 224 }
Chris@0 225 }
Chris@0 226
Chris@0 227 bool
Chris@0 228 AudioJACKTarget::isOK() const
Chris@0 229 {
Chris@0 230 return (m_client != 0);
Chris@0 231 }
Chris@0 232
Chris@0 233 int
Chris@0 234 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
Chris@0 235 {
Chris@0 236 return ((AudioJACKTarget *)arg)->process(nframes);
Chris@0 237 }
Chris@0 238
Chris@0 239 void
Chris@0 240 AudioJACKTarget::sourceModelReplaced()
Chris@0 241 {
Chris@0 242 m_mutex.lock();
Chris@0 243
Chris@0 244 m_source->setTargetBlockSize(m_bufferSize);
Chris@0 245 m_source->setTargetSampleRate(m_sampleRate);
Chris@0 246
Chris@0 247 size_t channels = m_source->getSourceChannelCount();
Chris@0 248
Chris@0 249 // Because we offer pan, we always want at least 2 channels
Chris@0 250 if (channels < 2) channels = 2;
Chris@0 251
Chris@0 252 if (channels == m_outputs.size() || !m_client) {
Chris@0 253 m_mutex.unlock();
Chris@0 254 return;
Chris@0 255 }
Chris@0 256
Chris@0 257 const char **ports =
Chris@0 258 jack_get_ports(m_client, NULL, NULL,
Chris@0 259 JackPortIsPhysical | JackPortIsInput);
Chris@0 260 size_t physicalPortCount = 0;
Chris@0 261 while (ports[physicalPortCount]) ++physicalPortCount;
Chris@0 262
Chris@0 263 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 264 std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
Chris@0 265 #endif
Chris@0 266
Chris@0 267 while (m_outputs.size() < channels) {
Chris@0 268
Chris@0 269 char name[20];
Chris@0 270 jack_port_t *port;
Chris@0 271
Chris@0 272 sprintf(name, "out %d", m_outputs.size() + 1);
Chris@0 273
Chris@0 274 port = jack_port_register(m_client,
Chris@0 275 name,
Chris@0 276 JACK_DEFAULT_AUDIO_TYPE,
Chris@0 277 JackPortIsOutput,
Chris@0 278 0);
Chris@0 279
Chris@0 280 if (!port) {
Chris@0 281 std::cerr
Chris@0 282 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
Chris@0 283 << m_outputs.size() << std::endl;
Chris@0 284 } else {
Chris@0 285 m_source->setTargetPlayLatency(jack_port_get_latency(port));
Chris@0 286 }
Chris@0 287
Chris@0 288 if (m_outputs.size() < physicalPortCount) {
Chris@0 289 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
Chris@0 290 }
Chris@0 291
Chris@0 292 m_outputs.push_back(port);
Chris@0 293 }
Chris@0 294
Chris@0 295 while (m_outputs.size() > channels) {
Chris@0 296 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
Chris@0 297 --itr;
Chris@0 298 jack_port_t *port = *itr;
Chris@0 299 if (port) jack_port_unregister(m_client, port);
Chris@0 300 m_outputs.erase(itr);
Chris@0 301 }
Chris@0 302
Chris@0 303 m_mutex.unlock();
Chris@0 304 }
Chris@0 305
Chris@0 306 int
Chris@0 307 AudioJACKTarget::process(jack_nframes_t nframes)
Chris@0 308 {
Chris@0 309 if (!m_mutex.tryLock()) {
Chris@0 310 return 0;
Chris@0 311 }
Chris@0 312
Chris@0 313 if (m_outputs.empty()) {
Chris@0 314 m_mutex.unlock();
Chris@0 315 return 0;
Chris@0 316 }
Chris@0 317
Chris@0 318 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 319 std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
Chris@0 320 #endif
Chris@0 321
Chris@0 322 #ifdef DEBUG_AUDIO_JACK_TARGET
Chris@0 323 if (m_bufferSize != nframes) {
Chris@0 324 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
Chris@0 325 }
Chris@0 326 #endif
Chris@0 327
Chris@0 328 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
Chris@0 329
Chris@0 330 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@0 331 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
Chris@0 332 }
Chris@0 333
Chris@0 334 if (m_source) {
Chris@0 335 m_source->getSourceSamples(nframes, buffers);
Chris@0 336 } else {
Chris@0 337 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@0 338 for (size_t i = 0; i < nframes; ++i) {
Chris@0 339 buffers[ch][i] = 0.0;
Chris@0 340 }
Chris@0 341 }
Chris@0 342 }
Chris@0 343
Chris@0 344 float peakLeft = 0.0, peakRight = 0.0;
Chris@0 345
Chris@0 346 for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
Chris@0 347
Chris@0 348 float peak = 0.0;
Chris@0 349
Chris@0 350 for (size_t i = 0; i < nframes; ++i) {
Chris@0 351 buffers[ch][i] *= m_outputGain;
Chris@0 352 float sample = fabsf(buffers[ch][i]);
Chris@0 353 if (sample > peak) peak = sample;
Chris@0 354 }
Chris@0 355
Chris@0 356 if (ch == 0) peakLeft = peak;
Chris@0 357 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
Chris@0 358 }
Chris@0 359
Chris@0 360 if (m_source) {
Chris@0 361 m_source->setOutputLevels(peakLeft, peakRight);
Chris@0 362 }
Chris@0 363
Chris@0 364 m_mutex.unlock();
Chris@0 365 return 0;
Chris@0 366 }
Chris@0 367
Chris@0 368
Chris@0 369 #ifdef INCLUDE_MOCFILES
Chris@0 370 #include "AudioJACKTarget.moc.cpp"
Chris@0 371 #endif
Chris@0 372
Chris@0 373 #endif /* HAVE_JACK */
Chris@0 374