cannam@0
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
cannam@0
|
2
|
cannam@0
|
3 #ifdef HAVE_JACK
|
cannam@0
|
4
|
cannam@0
|
5 #include "AudioJACKSource.h"
|
cannam@0
|
6 #include "AudioCallbackRecordTarget.h"
|
cannam@0
|
7
|
cannam@0
|
8 #include <iostream>
|
cannam@0
|
9 #include <cmath>
|
cannam@0
|
10
|
Chris@15
|
11 #include <cstdio>
|
Chris@15
|
12
|
cannam@0
|
13 //#define DEBUG_AUDIO_JACK_SOURCE 1
|
cannam@0
|
14
|
cannam@0
|
15 #ifdef BUILD_STATIC
|
cannam@0
|
16 #ifdef Q_OS_LINUX
|
cannam@0
|
17
|
cannam@0
|
18 // Some lunacy to enable JACK support in static builds. JACK isn't
|
cannam@0
|
19 // supposed to be linked statically, because it depends on a
|
cannam@0
|
20 // consistent shared memory layout between client library and daemon,
|
cannam@0
|
21 // so it's very fragile in the face of version mismatches.
|
cannam@0
|
22 //
|
cannam@0
|
23 // Therefore for static builds on Linux we avoid linking against JACK
|
cannam@0
|
24 // at all during the build, instead using dlopen and runtime symbol
|
cannam@0
|
25 // lookup to switch on JACK support at runtime. The following big
|
cannam@0
|
26 // mess (down to the #endifs) is the code that implements this.
|
cannam@0
|
27
|
cannam@0
|
28 static void *symbol(const char *name)
|
cannam@0
|
29 {
|
cannam@0
|
30 static bool attempted = false;
|
cannam@0
|
31 static void *library = 0;
|
cannam@0
|
32 static std::map<const char *, void *> symbols;
|
cannam@0
|
33 if (symbols.find(name) != symbols.end()) return symbols[name];
|
cannam@0
|
34 if (!library) {
|
cannam@0
|
35 if (!attempted) {
|
cannam@0
|
36 library = ::dlopen("libjack.so.1", RTLD_NOW);
|
cannam@0
|
37 if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
|
cannam@0
|
38 if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
|
cannam@0
|
39 if (!library) {
|
cannam@0
|
40 std::cerr << "WARNING: AudioJACKSource: Failed to load JACK library: "
|
cannam@0
|
41 << ::dlerror() << " (tried .so, .so.0, .so.1)"
|
cannam@0
|
42 << std::endl;
|
cannam@0
|
43 }
|
cannam@0
|
44 attempted = true;
|
cannam@0
|
45 }
|
cannam@0
|
46 if (!library) return 0;
|
cannam@0
|
47 }
|
cannam@0
|
48 void *symbol = ::dlsym(library, name);
|
cannam@0
|
49 if (!symbol) {
|
cannam@0
|
50 std::cerr << "WARNING: AudioJACKSource: Failed to locate symbol "
|
cannam@0
|
51 << name << ": " << ::dlerror() << std::endl;
|
cannam@0
|
52 }
|
cannam@0
|
53 symbols[name] = symbol;
|
cannam@0
|
54 return symbol;
|
cannam@0
|
55 }
|
cannam@0
|
56
|
Chris@15
|
57 static jack_client_t *dynamic_jack_client_open(const char *client_name,
|
Chris@15
|
58 jack_options_t options,
|
Chris@15
|
59 jack_status_t *status, ...)
|
Chris@15
|
60 {
|
Chris@15
|
61 typedef jack_client_t *(*func)(const char *client_name,
|
Chris@15
|
62 jack_options_t options,
|
Chris@15
|
63 jack_status_t *status, ...);
|
Chris@15
|
64 void *s = symbol("jack_client_open");
|
Chris@15
|
65 if (!s) return 0;
|
Chris@15
|
66 func f = (func)s;
|
Chris@15
|
67 return f(client_name, options, status); // varargs not supported here
|
Chris@15
|
68 }
|
Chris@15
|
69
|
cannam@0
|
70 static int dynamic_jack_set_process_callback(jack_client_t *client,
|
cannam@0
|
71 JackProcessCallback process_callback,
|
cannam@0
|
72 void *arg)
|
cannam@0
|
73 {
|
cannam@0
|
74 typedef int (*func)(jack_client_t *client,
|
cannam@0
|
75 JackProcessCallback process_callback,
|
cannam@0
|
76 void *arg);
|
cannam@0
|
77 void *s = symbol("jack_set_process_callback");
|
cannam@0
|
78 if (!s) return 1;
|
cannam@0
|
79 func f = (func)s;
|
cannam@0
|
80 return f(client, process_callback, arg);
|
cannam@0
|
81 }
|
cannam@0
|
82
|
cannam@0
|
83 static int dynamic_jack_set_xrun_callback(jack_client_t *client,
|
cannam@0
|
84 JackXRunCallback xrun_callback,
|
cannam@0
|
85 void *arg)
|
cannam@0
|
86 {
|
cannam@0
|
87 typedef int (*func)(jack_client_t *client,
|
cannam@0
|
88 JackXRunCallback xrun_callback,
|
cannam@0
|
89 void *arg);
|
cannam@0
|
90 void *s = symbol("jack_set_xrun_callback");
|
cannam@0
|
91 if (!s) return 1;
|
cannam@0
|
92 func f = (func)s;
|
cannam@0
|
93 return f(client, xrun_callback, arg);
|
cannam@0
|
94 }
|
cannam@0
|
95
|
cannam@0
|
96 static const char **dynamic_jack_get_ports(jack_client_t *client,
|
cannam@0
|
97 const char *port_name_pattern,
|
cannam@0
|
98 const char *type_name_pattern,
|
cannam@0
|
99 unsigned long flags)
|
cannam@0
|
100 {
|
cannam@0
|
101 typedef const char **(*func)(jack_client_t *client,
|
cannam@0
|
102 const char *port_name_pattern,
|
cannam@0
|
103 const char *type_name_pattern,
|
cannam@0
|
104 unsigned long flags);
|
cannam@0
|
105 void *s = symbol("jack_get_ports");
|
cannam@0
|
106 if (!s) return 0;
|
cannam@0
|
107 func f = (func)s;
|
cannam@0
|
108 return f(client, port_name_pattern, type_name_pattern, flags);
|
cannam@0
|
109 }
|
cannam@0
|
110
|
cannam@0
|
111 static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
|
cannam@0
|
112 const char *port_name,
|
cannam@0
|
113 const char *port_type,
|
cannam@0
|
114 unsigned long flags,
|
cannam@0
|
115 unsigned long buffer_size)
|
cannam@0
|
116 {
|
cannam@0
|
117 typedef jack_port_t *(*func)(jack_client_t *client,
|
cannam@0
|
118 const char *port_name,
|
cannam@0
|
119 const char *port_type,
|
cannam@0
|
120 unsigned long flags,
|
cannam@0
|
121 unsigned long buffer_size);
|
cannam@0
|
122 void *s = symbol("jack_port_register");
|
cannam@0
|
123 if (!s) return 0;
|
cannam@0
|
124 func f = (func)s;
|
cannam@0
|
125 return f(client, port_name, port_type, flags, buffer_size);
|
cannam@0
|
126 }
|
cannam@0
|
127
|
cannam@0
|
128 static int dynamic_jack_connect(jack_client_t *client,
|
cannam@0
|
129 const char *source,
|
cannam@0
|
130 const char *dest)
|
cannam@0
|
131 {
|
cannam@0
|
132 typedef int (*func)(jack_client_t *client,
|
cannam@0
|
133 const char *source,
|
cannam@0
|
134 const char *dest);
|
cannam@0
|
135 void *s = symbol("jack_connect");
|
cannam@0
|
136 if (!s) return 1;
|
cannam@0
|
137 func f = (func)s;
|
cannam@0
|
138 return f(client, source, dest);
|
cannam@0
|
139 }
|
cannam@0
|
140
|
cannam@0
|
141 static void *dynamic_jack_port_get_buffer(jack_port_t *port,
|
cannam@0
|
142 jack_nframes_t sz)
|
cannam@0
|
143 {
|
cannam@0
|
144 typedef void *(*func)(jack_port_t *, jack_nframes_t);
|
cannam@0
|
145 void *s = symbol("jack_port_get_buffer");
|
cannam@0
|
146 if (!s) return 0;
|
cannam@0
|
147 func f = (func)s;
|
cannam@0
|
148 return f(port, sz);
|
cannam@0
|
149 }
|
cannam@0
|
150
|
cannam@0
|
151 static int dynamic_jack_port_unregister(jack_client_t *client,
|
cannam@0
|
152 jack_port_t *port)
|
cannam@0
|
153 {
|
cannam@0
|
154 typedef int(*func)(jack_client_t *, jack_port_t *);
|
cannam@0
|
155 void *s = symbol("jack_port_unregister");
|
cannam@0
|
156 if (!s) return 0;
|
cannam@0
|
157 func f = (func)s;
|
cannam@0
|
158 return f(client, port);
|
cannam@0
|
159 }
|
cannam@0
|
160
|
cannam@0
|
161 #define dynamic1(rv, name, argtype, failval) \
|
cannam@0
|
162 static rv dynamic_##name(argtype arg) { \
|
cannam@0
|
163 typedef rv (*func) (argtype); \
|
cannam@0
|
164 void *s = symbol(#name); \
|
cannam@0
|
165 if (!s) return failval; \
|
cannam@0
|
166 func f = (func) s; \
|
cannam@0
|
167 return f(arg); \
|
cannam@0
|
168 }
|
cannam@0
|
169
|
cannam@0
|
170 dynamic1(jack_client_t *, jack_client_new, const char *, 0);
|
cannam@0
|
171 dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
|
cannam@0
|
172 dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
|
cannam@0
|
173 dynamic1(int, jack_activate, jack_client_t *, 1);
|
cannam@0
|
174 dynamic1(int, jack_deactivate, jack_client_t *, 1);
|
cannam@0
|
175 dynamic1(int, jack_client_close, jack_client_t *, 1);
|
cannam@0
|
176 dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
|
cannam@0
|
177 dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
|
cannam@0
|
178
|
cannam@0
|
179 #define jack_client_new dynamic_jack_client_new
|
Chris@15
|
180 #define jack_client_open dynamic_jack_client_open
|
cannam@0
|
181 #define jack_get_buffer_size dynamic_jack_get_buffer_size
|
cannam@0
|
182 #define jack_get_sample_rate dynamic_jack_get_sample_rate
|
cannam@0
|
183 #define jack_set_process_callback dynamic_jack_set_process_callback
|
cannam@0
|
184 #define jack_set_xrun_callback dynamic_jack_set_xrun_callback
|
cannam@0
|
185 #define jack_activate dynamic_jack_activate
|
cannam@0
|
186 #define jack_deactivate dynamic_jack_deactivate
|
cannam@0
|
187 #define jack_client_close dynamic_jack_client_close
|
cannam@0
|
188 #define jack_get_ports dynamic_jack_get_ports
|
cannam@0
|
189 #define jack_port_register dynamic_jack_port_register
|
cannam@0
|
190 #define jack_port_unregister dynamic_jack_port_unregister
|
cannam@0
|
191 #define jack_port_get_latency dynamic_jack_port_get_latency
|
cannam@0
|
192 #define jack_port_name dynamic_jack_port_name
|
cannam@0
|
193 #define jack_connect dynamic_jack_connect
|
cannam@0
|
194 #define jack_port_get_buffer dynamic_jack_port_get_buffer
|
cannam@0
|
195
|
cannam@0
|
196 #endif
|
cannam@0
|
197 #endif
|
cannam@0
|
198
|
cannam@0
|
199 AudioJACKSource::AudioJACKSource(AudioCallbackRecordTarget *target) :
|
cannam@0
|
200 AudioCallbackRecordSource(target),
|
cannam@0
|
201 m_client(0),
|
cannam@0
|
202 m_bufferSize(0),
|
cannam@0
|
203 m_sampleRate(0)
|
cannam@0
|
204 {
|
Chris@15
|
205 JackOptions options = JackNullOption;
|
Chris@15
|
206 #ifdef HAVE_PORTAUDIO_2_0
|
Chris@15
|
207 options = JackNoStartServer;
|
Chris@15
|
208 #endif
|
Chris@15
|
209 #ifdef HAVE_LIBPULSE
|
Chris@15
|
210 options = JackNoStartServer;
|
Chris@15
|
211 #endif
|
Chris@15
|
212
|
Chris@15
|
213 JackStatus status = JackStatus(0);
|
Chris@15
|
214 m_client = jack_client_open("vamp-live-host", options, &status);
|
cannam@0
|
215
|
cannam@0
|
216 if (!m_client) {
|
Chris@15
|
217 std::cerr
|
Chris@15
|
218 << "ERROR: AudioJACKSource: Failed to connect to JACK server"
|
Chris@15
|
219 << std::endl;
|
Chris@15
|
220 return;
|
cannam@0
|
221 }
|
cannam@0
|
222
|
cannam@0
|
223 m_bufferSize = jack_get_buffer_size(m_client);
|
cannam@0
|
224 m_sampleRate = jack_get_sample_rate(m_client);
|
cannam@0
|
225
|
cannam@0
|
226 jack_set_xrun_callback(m_client, xrunStatic, this);
|
cannam@0
|
227 jack_set_process_callback(m_client, processStatic, this);
|
cannam@0
|
228
|
cannam@0
|
229 if (jack_activate(m_client)) {
|
cannam@0
|
230 std::cerr << "ERROR: AudioJACKSource: Failed to activate JACK client"
|
cannam@0
|
231 << std::endl;
|
cannam@0
|
232 }
|
cannam@0
|
233
|
cannam@0
|
234 m_target->setSourceBlockSize(m_bufferSize);
|
cannam@0
|
235 m_target->setSourceSampleRate(m_sampleRate);
|
cannam@0
|
236
|
cannam@0
|
237 size_t channels = m_target->getChannelCount();
|
cannam@0
|
238
|
cannam@0
|
239 while (m_inputs.size() < channels) {
|
cannam@0
|
240
|
cannam@0
|
241 char name[20];
|
cannam@0
|
242 jack_port_t *port;
|
cannam@0
|
243
|
Chris@15
|
244 sprintf(name, "in %d", (int)(m_inputs.size() + 1));
|
cannam@0
|
245
|
cannam@0
|
246 port = jack_port_register(m_client,
|
cannam@0
|
247 name,
|
cannam@0
|
248 JACK_DEFAULT_AUDIO_TYPE,
|
cannam@0
|
249 JackPortIsInput,
|
cannam@0
|
250 0);
|
cannam@0
|
251
|
cannam@0
|
252 if (!port) {
|
cannam@0
|
253 std::cerr
|
cannam@0
|
254 << "ERROR: AudioJACKSource: Failed to create JACK input port "
|
cannam@0
|
255 << m_inputs.size() << std::endl;
|
cannam@0
|
256 } else {
|
cannam@0
|
257 m_target->setSourceRecordLatency(jack_port_get_latency(port));
|
cannam@0
|
258 }
|
cannam@0
|
259
|
cannam@0
|
260 m_inputs.push_back(port);
|
cannam@0
|
261 }
|
cannam@0
|
262 }
|
cannam@0
|
263
|
cannam@0
|
264 AudioJACKSource::~AudioJACKSource()
|
cannam@0
|
265 {
|
cannam@0
|
266 if (m_client) {
|
cannam@0
|
267 jack_deactivate(m_client);
|
cannam@0
|
268 jack_client_close(m_client);
|
cannam@0
|
269 }
|
cannam@0
|
270 }
|
cannam@0
|
271
|
cannam@0
|
272 bool
|
cannam@0
|
273 AudioJACKSource::isOK() const
|
cannam@0
|
274 {
|
cannam@0
|
275 return (m_client != 0);
|
cannam@0
|
276 }
|
cannam@0
|
277
|
cannam@0
|
278 int
|
cannam@0
|
279 AudioJACKSource::processStatic(jack_nframes_t nframes, void *arg)
|
cannam@0
|
280 {
|
cannam@0
|
281 return ((AudioJACKSource *)arg)->process(nframes);
|
cannam@0
|
282 }
|
cannam@0
|
283
|
cannam@0
|
284 int
|
cannam@0
|
285 AudioJACKSource::xrunStatic(void *arg)
|
cannam@0
|
286 {
|
cannam@0
|
287 return ((AudioJACKSource *)arg)->xrun();
|
cannam@0
|
288 }
|
cannam@0
|
289
|
cannam@0
|
290 int
|
cannam@0
|
291 AudioJACKSource::process(jack_nframes_t nframes)
|
cannam@0
|
292 {
|
cannam@0
|
293 if (m_inputs.size() < m_target->getChannelCount()) {
|
cannam@0
|
294 return 0;
|
cannam@0
|
295 }
|
cannam@0
|
296
|
cannam@0
|
297 #ifdef DEBUG_AUDIO_JACK_SOURCE
|
cannam@0
|
298 std::cout << "AudioJACKSource::process(" << nframes << "), have " << m_inputs.size() << " inputs" << std::endl;
|
cannam@0
|
299 #endif
|
cannam@0
|
300
|
cannam@0
|
301 #ifdef DEBUG_AUDIO_JACK_SOURCE
|
cannam@0
|
302 if (m_bufferSize != nframes) {
|
cannam@0
|
303 std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
|
cannam@0
|
304 }
|
cannam@0
|
305 #endif
|
cannam@0
|
306
|
cannam@0
|
307 float **buffers = (float **)alloca(m_inputs.size() * sizeof(float *));
|
cannam@0
|
308
|
cannam@0
|
309 for (size_t ch = 0; ch < m_inputs.size(); ++ch) {
|
cannam@0
|
310 buffers[ch] = (float *)jack_port_get_buffer(m_inputs[ch], nframes);
|
cannam@0
|
311 }
|
cannam@0
|
312
|
cannam@0
|
313 if (m_target) {
|
cannam@0
|
314 m_target->putSamples(nframes, buffers);
|
cannam@0
|
315 }
|
cannam@0
|
316
|
cannam@0
|
317 float peakLeft = 0.0, peakRight = 0.0;
|
cannam@0
|
318
|
cannam@0
|
319 for (size_t ch = 0; ch < m_inputs.size(); ++ch) {
|
cannam@0
|
320
|
cannam@0
|
321 float peak = 0.0;
|
cannam@0
|
322
|
cannam@0
|
323 for (size_t i = 0; i < nframes; ++i) {
|
cannam@0
|
324 float sample = fabsf(buffers[ch][i]);
|
cannam@0
|
325 if (sample > peak) peak = sample;
|
cannam@0
|
326 }
|
cannam@0
|
327
|
cannam@0
|
328 if (ch == 0) peakLeft = peak;
|
cannam@0
|
329 if (ch > 0 || m_inputs.size() == 1) peakRight = peak;
|
cannam@0
|
330 }
|
cannam@0
|
331
|
cannam@0
|
332 if (m_target) {
|
cannam@0
|
333 m_target->setInputLevels(peakLeft, peakRight);
|
cannam@0
|
334 }
|
cannam@0
|
335
|
cannam@0
|
336 return 0;
|
cannam@0
|
337 }
|
cannam@0
|
338
|
cannam@0
|
339 int
|
cannam@0
|
340 AudioJACKSource::xrun()
|
cannam@0
|
341 {
|
cannam@0
|
342 std::cerr << "AudioJACKSource: xrun!" << std::endl;
|
cannam@0
|
343 if (m_target) m_target->audioProcessingOverload();
|
cannam@0
|
344 return 0;
|
cannam@0
|
345 }
|
cannam@0
|
346
|
cannam@0
|
347 #endif /* HAVE_JACK */
|
cannam@0
|
348
|