comparison audioio/AudioJACKSource.cpp @ 0:a6020bf991cd

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