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