annotate sv/audioio/AudioJACKTarget.cpp @ 58:b3c3a5fa185f

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