Mercurial > hg > vamp-live-host
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 |