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 |