annotate audioio/AudioJACKTarget.cpp @ 15:cc566264c935

* timestretcher improvements -- simplify API (it can calculate its own processing block sizes etc)
author Chris Cannam
date Wed, 13 Sep 2006 11:56:44 +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