Chris@43
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@43
|
2
|
Chris@43
|
3 /*
|
Chris@43
|
4 Sonic Visualiser
|
Chris@43
|
5 An audio file viewer and annotation editor.
|
Chris@43
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@43
|
7 This file copyright 2006 Chris Cannam.
|
Chris@43
|
8
|
Chris@43
|
9 This program is free software; you can redistribute it and/or
|
Chris@43
|
10 modify it under the terms of the GNU General Public License as
|
Chris@43
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@43
|
12 License, or (at your option) any later version. See the file
|
Chris@43
|
13 COPYING included with this distribution for more information.
|
Chris@43
|
14 */
|
Chris@43
|
15
|
Chris@43
|
16 #ifdef HAVE_JACK
|
Chris@43
|
17
|
Chris@43
|
18 #include "AudioJACKTarget.h"
|
Chris@43
|
19 #include "AudioCallbackPlaySource.h"
|
Chris@43
|
20
|
Chris@43
|
21 #include <iostream>
|
Chris@43
|
22 #include <cmath>
|
Chris@43
|
23
|
Chris@110
|
24 #include <alloca.h>
|
Chris@110
|
25
|
Chris@43
|
26 //#define DEBUG_AUDIO_JACK_TARGET 1
|
Chris@43
|
27
|
Chris@43
|
28 #ifdef BUILD_STATIC
|
Chris@43
|
29 #ifdef Q_OS_LINUX
|
Chris@43
|
30
|
Chris@43
|
31 // Some lunacy to enable JACK support in static builds. JACK isn't
|
Chris@43
|
32 // supposed to be linked statically, because it depends on a
|
Chris@43
|
33 // consistent shared memory layout between client library and daemon,
|
Chris@43
|
34 // so it's very fragile in the face of version mismatches.
|
Chris@43
|
35 //
|
Chris@43
|
36 // Therefore for static builds on Linux we avoid linking against JACK
|
Chris@43
|
37 // at all during the build, instead using dlopen and runtime symbol
|
Chris@43
|
38 // lookup to switch on JACK support at runtime. The following big
|
Chris@43
|
39 // mess (down to the #endifs) is the code that implements this.
|
Chris@43
|
40
|
Chris@43
|
41 static void *symbol(const char *name)
|
Chris@43
|
42 {
|
Chris@43
|
43 static bool attempted = false;
|
Chris@43
|
44 static void *library = 0;
|
Chris@43
|
45 static std::map<const char *, void *> symbols;
|
Chris@43
|
46 if (symbols.find(name) != symbols.end()) return symbols[name];
|
Chris@43
|
47 if (!library) {
|
Chris@43
|
48 if (!attempted) {
|
Chris@43
|
49 library = ::dlopen("libjack.so.1", RTLD_NOW);
|
Chris@43
|
50 if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
|
Chris@43
|
51 if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
|
Chris@43
|
52 if (!library) {
|
Chris@293
|
53 cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
|
Chris@43
|
54 << ::dlerror() << " (tried .so, .so.0, .so.1)"
|
Chris@293
|
55 << endl;
|
Chris@43
|
56 }
|
Chris@43
|
57 attempted = true;
|
Chris@43
|
58 }
|
Chris@43
|
59 if (!library) return 0;
|
Chris@43
|
60 }
|
Chris@43
|
61 void *symbol = ::dlsym(library, name);
|
Chris@43
|
62 if (!symbol) {
|
Chris@293
|
63 cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
|
Chris@293
|
64 << name << ": " << ::dlerror() << endl;
|
Chris@43
|
65 }
|
Chris@43
|
66 symbols[name] = symbol;
|
Chris@43
|
67 return symbol;
|
Chris@43
|
68 }
|
Chris@43
|
69
|
Chris@56
|
70 static jack_client_t *dynamic_jack_client_open(const char *client_name,
|
Chris@56
|
71 jack_options_t options,
|
Chris@56
|
72 jack_status_t *status, ...)
|
Chris@56
|
73 {
|
Chris@70
|
74 typedef jack_client_t *(*func)(const char *client_name,
|
Chris@70
|
75 jack_options_t options,
|
Chris@70
|
76 jack_status_t *status, ...);
|
Chris@56
|
77 void *s = symbol("jack_client_open");
|
Chris@56
|
78 if (!s) return 0;
|
Chris@56
|
79 func f = (func)s;
|
Chris@56
|
80 return f(client_name, options, status); // varargs not supported here
|
Chris@56
|
81 }
|
Chris@56
|
82
|
Chris@43
|
83 static int dynamic_jack_set_process_callback(jack_client_t *client,
|
Chris@43
|
84 JackProcessCallback process_callback,
|
Chris@43
|
85 void *arg)
|
Chris@43
|
86 {
|
Chris@43
|
87 typedef int (*func)(jack_client_t *client,
|
Chris@43
|
88 JackProcessCallback process_callback,
|
Chris@43
|
89 void *arg);
|
Chris@43
|
90 void *s = symbol("jack_set_process_callback");
|
Chris@43
|
91 if (!s) return 1;
|
Chris@43
|
92 func f = (func)s;
|
Chris@43
|
93 return f(client, process_callback, arg);
|
Chris@43
|
94 }
|
Chris@43
|
95
|
Chris@43
|
96 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
|
Chris@43
|
97 JackXRunCallback xrun_callback,
|
Chris@43
|
98 void *arg)
|
Chris@43
|
99 {
|
Chris@43
|
100 typedef int (*func)(jack_client_t *client,
|
Chris@43
|
101 JackXRunCallback xrun_callback,
|
Chris@43
|
102 void *arg);
|
Chris@43
|
103 void *s = symbol("jack_set_xrun_callback");
|
Chris@43
|
104 if (!s) return 1;
|
Chris@43
|
105 func f = (func)s;
|
Chris@43
|
106 return f(client, xrun_callback, arg);
|
Chris@43
|
107 }
|
Chris@43
|
108
|
Chris@43
|
109 static const char **dynamic_jack_get_ports(jack_client_t *client,
|
Chris@43
|
110 const char *port_name_pattern,
|
Chris@43
|
111 const char *type_name_pattern,
|
Chris@43
|
112 unsigned long flags)
|
Chris@43
|
113 {
|
Chris@43
|
114 typedef const char **(*func)(jack_client_t *client,
|
Chris@43
|
115 const char *port_name_pattern,
|
Chris@43
|
116 const char *type_name_pattern,
|
Chris@43
|
117 unsigned long flags);
|
Chris@43
|
118 void *s = symbol("jack_get_ports");
|
Chris@43
|
119 if (!s) return 0;
|
Chris@43
|
120 func f = (func)s;
|
Chris@43
|
121 return f(client, port_name_pattern, type_name_pattern, flags);
|
Chris@43
|
122 }
|
Chris@43
|
123
|
Chris@43
|
124 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
|
Chris@43
|
125 const char *port_name,
|
Chris@43
|
126 const char *port_type,
|
Chris@43
|
127 unsigned long flags,
|
Chris@43
|
128 unsigned long buffer_size)
|
Chris@43
|
129 {
|
Chris@43
|
130 typedef jack_port_t *(*func)(jack_client_t *client,
|
Chris@43
|
131 const char *port_name,
|
Chris@43
|
132 const char *port_type,
|
Chris@43
|
133 unsigned long flags,
|
Chris@43
|
134 unsigned long buffer_size);
|
Chris@43
|
135 void *s = symbol("jack_port_register");
|
Chris@43
|
136 if (!s) return 0;
|
Chris@43
|
137 func f = (func)s;
|
Chris@43
|
138 return f(client, port_name, port_type, flags, buffer_size);
|
Chris@43
|
139 }
|
Chris@43
|
140
|
Chris@43
|
141 static int dynamic_jack_connect(jack_client_t *client,
|
Chris@43
|
142 const char *source,
|
Chris@43
|
143 const char *dest)
|
Chris@43
|
144 {
|
Chris@43
|
145 typedef int (*func)(jack_client_t *client,
|
Chris@43
|
146 const char *source,
|
Chris@43
|
147 const char *dest);
|
Chris@43
|
148 void *s = symbol("jack_connect");
|
Chris@43
|
149 if (!s) return 1;
|
Chris@43
|
150 func f = (func)s;
|
Chris@43
|
151 return f(client, source, dest);
|
Chris@43
|
152 }
|
Chris@43
|
153
|
Chris@43
|
154 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
|
Chris@43
|
155 jack_nframes_t sz)
|
Chris@43
|
156 {
|
Chris@43
|
157 typedef void *(*func)(jack_port_t *, jack_nframes_t);
|
Chris@43
|
158 void *s = symbol("jack_port_get_buffer");
|
Chris@43
|
159 if (!s) return 0;
|
Chris@43
|
160 func f = (func)s;
|
Chris@43
|
161 return f(port, sz);
|
Chris@43
|
162 }
|
Chris@43
|
163
|
Chris@43
|
164 static int dynamic_jack_port_unregister(jack_client_t *client,
|
Chris@43
|
165 jack_port_t *port)
|
Chris@43
|
166 {
|
Chris@43
|
167 typedef int(*func)(jack_client_t *, jack_port_t *);
|
Chris@43
|
168 void *s = symbol("jack_port_unregister");
|
Chris@43
|
169 if (!s) return 0;
|
Chris@43
|
170 func f = (func)s;
|
Chris@43
|
171 return f(client, port);
|
Chris@43
|
172 }
|
Chris@43
|
173
|
Chris@366
|
174 static void dynamic_jack_port_get_latency_range(jack_port_t *port,
|
Chris@366
|
175 jack_latency_callback_mode_t mode,
|
Chris@366
|
176 jack_latency_range_t *range)
|
Chris@366
|
177 {
|
Chris@366
|
178 typedef void (*func)(jack_port_t *, jack_latency_callback_mode_t, jack_latency_range_t *);
|
Chris@366
|
179 void *s = symbol("jack_port_get_latency_range");
|
Chris@366
|
180 if (!s) {
|
Chris@366
|
181 range.min = range.max = 0;
|
Chris@366
|
182 return;
|
Chris@366
|
183 }
|
Chris@366
|
184 func f = (func)s;
|
Chris@366
|
185 f(port, mode, range);
|
Chris@366
|
186 }
|
Chris@366
|
187
|
Chris@43
|
188 #define dynamic1(rv, name, argtype, failval) \
|
Chris@43
|
189 static rv dynamic_##name(argtype arg) { \
|
Chris@43
|
190 typedef rv (*func) (argtype); \
|
Chris@43
|
191 void *s = symbol(#name); \
|
Chris@43
|
192 if (!s) return failval; \
|
Chris@43
|
193 func f = (func) s; \
|
Chris@43
|
194 return f(arg); \
|
Chris@43
|
195 }
|
Chris@43
|
196
|
Chris@43
|
197 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
|
Chris@43
|
198 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
|
Chris@43
|
199 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
|
Chris@43
|
200 dynamic1(int, jack_activate, jack_client_t *, 1);
|
Chris@43
|
201 dynamic1(int, jack_deactivate, jack_client_t *, 1);
|
Chris@43
|
202 dynamic1(int, jack_client_close, jack_client_t *, 1);
|
Chris@97
|
203 dynamic1(jack_nframes_t, jack_frame_time, jack_client_t *, 0);
|
Chris@43
|
204 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
|
Chris@43
|
205
|
Chris@43
|
206 #define jack_client_new dynamic_jack_client_new
|
Chris@56
|
207 #define jack_client_open dynamic_jack_client_open
|
Chris@43
|
208 #define jack_get_buffer_size dynamic_jack_get_buffer_size
|
Chris@43
|
209 #define jack_get_sample_rate dynamic_jack_get_sample_rate
|
Chris@43
|
210 #define jack_set_process_callback dynamic_jack_set_process_callback
|
Chris@43
|
211 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
|
Chris@43
|
212 #define jack_activate dynamic_jack_activate
|
Chris@43
|
213 #define jack_deactivate dynamic_jack_deactivate
|
Chris@43
|
214 #define jack_client_close dynamic_jack_client_close
|
Chris@97
|
215 #define jack_frame_time dynamic_jack_frame_time
|
Chris@43
|
216 #define jack_get_ports dynamic_jack_get_ports
|
Chris@43
|
217 #define jack_port_register dynamic_jack_port_register
|
Chris@43
|
218 #define jack_port_unregister dynamic_jack_port_unregister
|
Chris@43
|
219 #define jack_port_name dynamic_jack_port_name
|
Chris@43
|
220 #define jack_connect dynamic_jack_connect
|
Chris@43
|
221 #define jack_port_get_buffer dynamic_jack_port_get_buffer
|
Chris@43
|
222
|
Chris@43
|
223 #endif
|
Chris@43
|
224 #endif
|
Chris@43
|
225
|
Chris@43
|
226 AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
|
Chris@43
|
227 AudioCallbackPlayTarget(source),
|
Chris@43
|
228 m_client(0),
|
Chris@43
|
229 m_bufferSize(0),
|
Chris@70
|
230 m_sampleRate(0),
|
Chris@70
|
231 m_done(false)
|
Chris@43
|
232 {
|
Chris@56
|
233 JackOptions options = JackNullOption;
|
Chris@117
|
234 #ifdef HAVE_PORTAUDIO_2_0
|
Chris@117
|
235 options = JackNoStartServer;
|
Chris@117
|
236 #endif
|
Chris@117
|
237 #ifdef HAVE_LIBPULSE
|
Chris@56
|
238 options = JackNoStartServer;
|
Chris@56
|
239 #endif
|
Chris@56
|
240
|
Chris@56
|
241 JackStatus status = JackStatus(0);
|
Chris@57
|
242 m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
|
Chris@57
|
243 options, &status);
|
Chris@57
|
244
|
Chris@43
|
245 if (!m_client) {
|
Chris@293
|
246 cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
|
Chris@293
|
247 << status << endl;
|
Chris@56
|
248 return;
|
Chris@43
|
249 }
|
Chris@43
|
250
|
Chris@43
|
251 m_bufferSize = jack_get_buffer_size(m_client);
|
Chris@43
|
252 m_sampleRate = jack_get_sample_rate(m_client);
|
Chris@43
|
253
|
Chris@43
|
254 jack_set_xrun_callback(m_client, xrunStatic, this);
|
Chris@43
|
255 jack_set_process_callback(m_client, processStatic, this);
|
Chris@43
|
256
|
Chris@43
|
257 if (jack_activate(m_client)) {
|
Chris@293
|
258 cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
|
Chris@293
|
259 << endl;
|
Chris@43
|
260 }
|
Chris@43
|
261
|
Chris@43
|
262 if (m_source) {
|
Chris@43
|
263 sourceModelReplaced();
|
Chris@43
|
264 }
|
Chris@84
|
265
|
Chris@84
|
266 // Mainstream JACK (though not jackdmp) calls mlockall() to lock
|
Chris@84
|
267 // down all memory for real-time operation. That isn't a terribly
|
Chris@84
|
268 // good idea in an application like this that may have very high
|
Chris@84
|
269 // dynamic memory usage in other threads, as mlockall() applies
|
Chris@84
|
270 // across all threads. We're far better off undoing it here and
|
Chris@84
|
271 // accepting the possible loss of true RT capability.
|
Chris@84
|
272 MUNLOCKALL();
|
Chris@43
|
273 }
|
Chris@43
|
274
|
Chris@43
|
275 AudioJACKTarget::~AudioJACKTarget()
|
Chris@43
|
276 {
|
Chris@233
|
277 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget()" << endl;
|
Chris@70
|
278
|
Chris@91
|
279 if (m_source) {
|
Chris@91
|
280 m_source->setTarget(0, m_bufferSize);
|
Chris@91
|
281 }
|
Chris@91
|
282
|
Chris@70
|
283 shutdown();
|
Chris@70
|
284
|
Chris@43
|
285 if (m_client) {
|
Chris@70
|
286
|
Chris@70
|
287 while (m_outputs.size() > 0) {
|
Chris@70
|
288 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
|
Chris@70
|
289 --itr;
|
Chris@70
|
290 jack_port_t *port = *itr;
|
Chris@293
|
291 cerr << "unregister " << m_outputs.size() << endl;
|
Chris@70
|
292 if (port) jack_port_unregister(m_client, port);
|
Chris@70
|
293 m_outputs.erase(itr);
|
Chris@70
|
294 }
|
Chris@293
|
295 cerr << "Deactivating... ";
|
Chris@43
|
296 jack_deactivate(m_client);
|
Chris@293
|
297 cerr << "done\nClosing... ";
|
Chris@43
|
298 jack_client_close(m_client);
|
Chris@293
|
299 cerr << "done" << endl;
|
Chris@43
|
300 }
|
Chris@70
|
301
|
Chris@70
|
302 m_client = 0;
|
Chris@70
|
303
|
Chris@233
|
304 SVDEBUG << "AudioJACKTarget::~AudioJACKTarget() done" << endl;
|
Chris@43
|
305 }
|
Chris@43
|
306
|
Chris@70
|
307 void
|
Chris@70
|
308 AudioJACKTarget::shutdown()
|
Chris@70
|
309 {
|
Chris@70
|
310 m_done = true;
|
Chris@70
|
311 }
|
Chris@70
|
312
|
Chris@43
|
313 bool
|
Chris@43
|
314 AudioJACKTarget::isOK() const
|
Chris@43
|
315 {
|
Chris@43
|
316 return (m_client != 0);
|
Chris@43
|
317 }
|
Chris@43
|
318
|
Chris@91
|
319 double
|
Chris@91
|
320 AudioJACKTarget::getCurrentTime() const
|
Chris@91
|
321 {
|
Chris@91
|
322 if (m_client && m_sampleRate) {
|
Chris@91
|
323 return double(jack_frame_time(m_client)) / double(m_sampleRate);
|
Chris@91
|
324 } else {
|
Chris@91
|
325 return 0.0;
|
Chris@91
|
326 }
|
Chris@91
|
327 }
|
Chris@91
|
328
|
Chris@43
|
329 int
|
Chris@43
|
330 AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
|
Chris@43
|
331 {
|
Chris@43
|
332 return ((AudioJACKTarget *)arg)->process(nframes);
|
Chris@43
|
333 }
|
Chris@43
|
334
|
Chris@43
|
335 int
|
Chris@43
|
336 AudioJACKTarget::xrunStatic(void *arg)
|
Chris@43
|
337 {
|
Chris@43
|
338 return ((AudioJACKTarget *)arg)->xrun();
|
Chris@43
|
339 }
|
Chris@43
|
340
|
Chris@43
|
341 void
|
Chris@43
|
342 AudioJACKTarget::sourceModelReplaced()
|
Chris@43
|
343 {
|
Chris@43
|
344 m_mutex.lock();
|
Chris@43
|
345
|
Chris@91
|
346 m_source->setTarget(this, m_bufferSize);
|
Chris@43
|
347 m_source->setTargetSampleRate(m_sampleRate);
|
Chris@43
|
348
|
Chris@366
|
349 int channels = m_source->getSourceChannelCount();
|
Chris@43
|
350
|
Chris@43
|
351 // Because we offer pan, we always want at least 2 channels
|
Chris@43
|
352 if (channels < 2) channels = 2;
|
Chris@43
|
353
|
Chris@366
|
354 if (channels == (int)m_outputs.size() || !m_client) {
|
Chris@43
|
355 m_mutex.unlock();
|
Chris@43
|
356 return;
|
Chris@43
|
357 }
|
Chris@43
|
358
|
Chris@43
|
359 const char **ports =
|
Chris@43
|
360 jack_get_ports(m_client, NULL, NULL,
|
Chris@43
|
361 JackPortIsPhysical | JackPortIsInput);
|
Chris@366
|
362 int physicalPortCount = 0;
|
Chris@43
|
363 while (ports[physicalPortCount]) ++physicalPortCount;
|
Chris@43
|
364
|
Chris@43
|
365 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@233
|
366 SVDEBUG << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << endl;
|
Chris@43
|
367 #endif
|
Chris@43
|
368
|
Chris@366
|
369 while ((int)m_outputs.size() < channels) {
|
Chris@43
|
370
|
Chris@43
|
371 char name[20];
|
Chris@43
|
372 jack_port_t *port;
|
Chris@43
|
373
|
Chris@110
|
374 sprintf(name, "out %d", int(m_outputs.size() + 1));
|
Chris@43
|
375
|
Chris@43
|
376 port = jack_port_register(m_client,
|
Chris@43
|
377 name,
|
Chris@43
|
378 JACK_DEFAULT_AUDIO_TYPE,
|
Chris@43
|
379 JackPortIsOutput,
|
Chris@43
|
380 0);
|
Chris@43
|
381
|
Chris@43
|
382 if (!port) {
|
Chris@293
|
383 cerr
|
Chris@43
|
384 << "ERROR: AudioJACKTarget: Failed to create JACK output port "
|
Chris@293
|
385 << m_outputs.size() << endl;
|
Chris@43
|
386 } else {
|
Chris@366
|
387 jack_latency_range_t range;
|
Chris@366
|
388 jack_port_get_latency_range(port, JackPlaybackLatency, &range);
|
Chris@366
|
389 m_source->setTargetPlayLatency(range.max);
|
Chris@366
|
390 cerr << "AudioJACKTarget: output latency is " << range.max << endl;
|
Chris@43
|
391 }
|
Chris@43
|
392
|
Chris@366
|
393 if ((int)m_outputs.size() < physicalPortCount) {
|
Chris@43
|
394 jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
|
Chris@43
|
395 }
|
Chris@43
|
396
|
Chris@43
|
397 m_outputs.push_back(port);
|
Chris@43
|
398 }
|
Chris@43
|
399
|
Chris@366
|
400 while ((int)m_outputs.size() > channels) {
|
Chris@43
|
401 std::vector<jack_port_t *>::iterator itr = m_outputs.end();
|
Chris@43
|
402 --itr;
|
Chris@43
|
403 jack_port_t *port = *itr;
|
Chris@43
|
404 if (port) jack_port_unregister(m_client, port);
|
Chris@43
|
405 m_outputs.erase(itr);
|
Chris@43
|
406 }
|
Chris@43
|
407
|
Chris@43
|
408 m_mutex.unlock();
|
Chris@43
|
409 }
|
Chris@43
|
410
|
Chris@43
|
411 int
|
Chris@43
|
412 AudioJACKTarget::process(jack_nframes_t nframes)
|
Chris@43
|
413 {
|
Chris@70
|
414 if (m_done) return 0;
|
Chris@70
|
415
|
Chris@43
|
416 if (!m_mutex.tryLock()) {
|
Chris@43
|
417 return 0;
|
Chris@43
|
418 }
|
Chris@43
|
419
|
Chris@43
|
420 if (m_outputs.empty()) {
|
Chris@43
|
421 m_mutex.unlock();
|
Chris@43
|
422 return 0;
|
Chris@43
|
423 }
|
Chris@43
|
424
|
Chris@43
|
425 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@293
|
426 cout << "AudioJACKTarget::process(" << nframes << "): have a source" << endl;
|
Chris@43
|
427 #endif
|
Chris@43
|
428
|
Chris@43
|
429 #ifdef DEBUG_AUDIO_JACK_TARGET
|
Chris@43
|
430 if (m_bufferSize != nframes) {
|
Chris@293
|
431 cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << endl;
|
Chris@43
|
432 }
|
Chris@43
|
433 #endif
|
Chris@43
|
434
|
Chris@43
|
435 float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
|
Chris@43
|
436
|
Chris@366
|
437 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
|
Chris@43
|
438 buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
|
Chris@43
|
439 }
|
Chris@43
|
440
|
Chris@366
|
441 int received = 0;
|
Chris@43
|
442
|
Chris@43
|
443 if (m_source) {
|
Chris@43
|
444 received = m_source->getSourceSamples(nframes, buffers);
|
Chris@43
|
445 }
|
Chris@43
|
446
|
Chris@366
|
447 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
|
Chris@366
|
448 for (int i = received; i < (int)nframes; ++i) {
|
Chris@43
|
449 buffers[ch][i] = 0.0;
|
Chris@43
|
450 }
|
Chris@43
|
451 }
|
Chris@43
|
452
|
Chris@43
|
453 float peakLeft = 0.0, peakRight = 0.0;
|
Chris@43
|
454
|
Chris@366
|
455 for (int ch = 0; ch < (int)m_outputs.size(); ++ch) {
|
Chris@43
|
456
|
Chris@43
|
457 float peak = 0.0;
|
Chris@43
|
458
|
Chris@366
|
459 for (int i = 0; i < (int)nframes; ++i) {
|
Chris@43
|
460 buffers[ch][i] *= m_outputGain;
|
Chris@43
|
461 float sample = fabsf(buffers[ch][i]);
|
Chris@43
|
462 if (sample > peak) peak = sample;
|
Chris@43
|
463 }
|
Chris@43
|
464
|
Chris@43
|
465 if (ch == 0) peakLeft = peak;
|
Chris@43
|
466 if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
|
Chris@43
|
467 }
|
Chris@43
|
468
|
Chris@43
|
469 if (m_source) {
|
Chris@43
|
470 m_source->setOutputLevels(peakLeft, peakRight);
|
Chris@43
|
471 }
|
Chris@43
|
472
|
Chris@43
|
473 m_mutex.unlock();
|
Chris@43
|
474 return 0;
|
Chris@43
|
475 }
|
Chris@43
|
476
|
Chris@43
|
477 int
|
Chris@43
|
478 AudioJACKTarget::xrun()
|
Chris@43
|
479 {
|
Chris@293
|
480 cerr << "AudioJACKTarget: xrun!" << endl;
|
Chris@43
|
481 if (m_source) m_source->audioProcessingOverload();
|
Chris@43
|
482 return 0;
|
Chris@43
|
483 }
|
Chris@43
|
484
|
Chris@43
|
485 #endif /* HAVE_JACK */
|
Chris@43
|
486
|