1 |
/* |
---|
2 |
* FreeBob Backend for Jack |
---|
3 |
* FreeBob = Firewire (pro-)audio for linux |
---|
4 |
* |
---|
5 |
* http://freebob.sf.net |
---|
6 |
* http://jackit.sf.net |
---|
7 |
* |
---|
8 |
* Copyright (C) 2005 Pieter Palmers <pieterpalmers@users.sourceforge.net> |
---|
9 |
* |
---|
10 |
* This program is free software; you can redistribute it and/or modify |
---|
11 |
* it under the terms of the GNU General Public License as published by |
---|
12 |
* the Free Software Foundation; either version 2 of the License, or |
---|
13 |
* (at your option) any later version. |
---|
14 |
* |
---|
15 |
* This program is distributed in the hope that it will be useful, |
---|
16 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 |
* GNU General Public License for more details. |
---|
19 |
* |
---|
20 |
* You should have received a copy of the GNU General Public License |
---|
21 |
* along with this program; if not, write to the Free Software |
---|
22 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
23 |
*/ |
---|
24 |
|
---|
25 |
/* |
---|
26 |
* Main Jack driver entry routines |
---|
27 |
* |
---|
28 |
*/ |
---|
29 |
|
---|
30 |
#include <math.h> |
---|
31 |
#include <stdio.h> |
---|
32 |
#include <memory.h> |
---|
33 |
#include <unistd.h> |
---|
34 |
#include <stdlib.h> |
---|
35 |
#include <errno.h> |
---|
36 |
#include <stdarg.h> |
---|
37 |
#include <sys/mman.h> |
---|
38 |
|
---|
39 |
#include <jack/types.h> |
---|
40 |
#include <jack/internal.h> |
---|
41 |
#include <jack/engine.h> |
---|
42 |
#include <sysdeps/time.h> |
---|
43 |
|
---|
44 |
#include "freebob_driver.h" |
---|
45 |
|
---|
46 |
#define SAMPLE_MAX_24BIT 8388608.0f |
---|
47 |
#define SAMPLE_MAX_16BIT 32768.0f |
---|
48 |
|
---|
49 |
static int freebob_driver_stop (freebob_driver_t *driver); |
---|
50 |
|
---|
51 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
52 |
static freebob_driver_midi_handle_t *freebob_driver_midi_init(freebob_driver_t *driver); |
---|
53 |
static void freebob_driver_midi_finish (freebob_driver_midi_handle_t *m); |
---|
54 |
static int freebob_driver_midi_start (freebob_driver_midi_handle_t *m); |
---|
55 |
static int freebob_driver_midi_stop (freebob_driver_midi_handle_t *m); |
---|
56 |
#endif |
---|
57 |
|
---|
58 |
// enable verbose messages |
---|
59 |
static int g_verbose=0; |
---|
60 |
|
---|
61 |
static int |
---|
62 |
freebob_driver_attach (freebob_driver_t *driver) |
---|
63 |
{ |
---|
64 |
char buf[64]; |
---|
65 |
channel_t chn; |
---|
66 |
jack_port_t *port; |
---|
67 |
int port_flags; |
---|
68 |
|
---|
69 |
g_verbose=driver->engine->verbose; |
---|
70 |
driver->device_options.verbose=g_verbose; |
---|
71 |
|
---|
72 |
driver->engine->set_buffer_size (driver->engine, driver->period_size); |
---|
73 |
driver->engine->set_sample_rate (driver->engine, driver->sample_rate); |
---|
74 |
|
---|
75 |
/* packetizer thread options */ |
---|
76 |
driver->device_options.realtime=(driver->engine->control->real_time? 1 : 0); |
---|
77 |
|
---|
78 |
driver->device_options.packetizer_priority=driver->engine->control->client_priority + |
---|
79 |
FREEBOB_RT_PRIORITY_PACKETIZER_RELATIVE; |
---|
80 |
if (driver->device_options.packetizer_priority>98) { |
---|
81 |
driver->device_options.packetizer_priority=98; |
---|
82 |
} |
---|
83 |
|
---|
84 |
driver->dev=freebob_streaming_init(&driver->device_info,driver->device_options); |
---|
85 |
|
---|
86 |
if(!driver->dev) { |
---|
87 |
printError("Error creating freebob streaming device"); |
---|
88 |
return -1; |
---|
89 |
} |
---|
90 |
|
---|
91 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
92 |
driver->midi_handle=freebob_driver_midi_init(driver); |
---|
93 |
if(!driver->midi_handle) { |
---|
94 |
printError("-----------------------------------------------------------"); |
---|
95 |
printError("Error creating midi device!"); |
---|
96 |
printError("FreeBob will run without MIDI support."); |
---|
97 |
printError("Consult the above error messages to solve the problem. "); |
---|
98 |
printError("-----------------------------------------------------------\n\n"); |
---|
99 |
} |
---|
100 |
#endif |
---|
101 |
|
---|
102 |
if (driver->device_options.realtime) { |
---|
103 |
printMessage("Streaming thread running with Realtime scheduling, priority %d", |
---|
104 |
driver->device_options.packetizer_priority); |
---|
105 |
} else { |
---|
106 |
printMessage("Streaming thread running without Realtime scheduling"); |
---|
107 |
} |
---|
108 |
|
---|
109 |
/* ports */ |
---|
110 |
port_flags = JackPortIsOutput|JackPortIsPhysical|JackPortIsTerminal; |
---|
111 |
|
---|
112 |
driver->capture_nchannels=freebob_streaming_get_nb_capture_streams(driver->dev); |
---|
113 |
|
---|
114 |
for (chn = 0; chn < driver->capture_nchannels; chn++) { |
---|
115 |
|
---|
116 |
freebob_streaming_get_capture_stream_name(driver->dev, chn, buf, sizeof(buf) - 1); |
---|
117 |
|
---|
118 |
if(freebob_streaming_get_capture_stream_type(driver->dev, chn) != freebob_stream_type_audio) { |
---|
119 |
printMessage ("Don't register capture port %s", buf); |
---|
120 |
|
---|
121 |
// we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines |
---|
122 |
driver->capture_ports = |
---|
123 |
jack_slist_append (driver->capture_ports, NULL); |
---|
124 |
} else { |
---|
125 |
printMessage ("Registering capture port %s", buf); |
---|
126 |
if ((port = jack_port_register (driver->client, buf, |
---|
127 |
JACK_DEFAULT_AUDIO_TYPE, |
---|
128 |
port_flags, 0)) == NULL) { |
---|
129 |
printError (" cannot register port for %s", buf); |
---|
130 |
break; |
---|
131 |
} |
---|
132 |
driver->capture_ports = |
---|
133 |
jack_slist_append (driver->capture_ports, port); |
---|
134 |
// setup port parameters |
---|
135 |
if(freebob_streaming_set_capture_buffer_type(driver->dev, chn, freebob_buffer_type_float)) { |
---|
136 |
printError(" cannot set port buffer type for %s", buf); |
---|
137 |
} |
---|
138 |
if (freebob_streaming_set_capture_stream_buffer(driver->dev, chn, NULL)) { |
---|
139 |
printError(" cannot configure initial port buffer for %s", buf); |
---|
140 |
} |
---|
141 |
if(freebob_streaming_capture_stream_onoff(driver->dev, chn, 1)) { |
---|
142 |
printError(" cannot enable port %s", buf); |
---|
143 |
} |
---|
144 |
} |
---|
145 |
// jack_port_set_latency (port, driver->period_size); |
---|
146 |
|
---|
147 |
} |
---|
148 |
|
---|
149 |
port_flags = JackPortIsInput|JackPortIsPhysical|JackPortIsTerminal; |
---|
150 |
|
---|
151 |
driver->playback_nchannels=freebob_streaming_get_nb_playback_streams(driver->dev); |
---|
152 |
|
---|
153 |
for (chn = 0; chn < driver->playback_nchannels; chn++) { |
---|
154 |
|
---|
155 |
freebob_streaming_get_playback_stream_name(driver->dev, chn, buf, sizeof(buf) - 1); |
---|
156 |
|
---|
157 |
if(freebob_streaming_get_playback_stream_type(driver->dev, chn) != freebob_stream_type_audio) { |
---|
158 |
printMessage ("Don't register playback port %s", buf); |
---|
159 |
|
---|
160 |
// we have to add a NULL entry in the list to be able to loop over the channels in the read/write routines |
---|
161 |
driver->playback_ports = |
---|
162 |
jack_slist_append (driver->playback_ports, NULL); |
---|
163 |
} else { |
---|
164 |
printMessage ("Registering playback port %s", buf); |
---|
165 |
if ((port = jack_port_register (driver->client, buf, |
---|
166 |
JACK_DEFAULT_AUDIO_TYPE, |
---|
167 |
port_flags, 0)) == NULL) { |
---|
168 |
printError(" cannot register port for %s", buf); |
---|
169 |
break; |
---|
170 |
} |
---|
171 |
driver->playback_ports = |
---|
172 |
jack_slist_append (driver->playback_ports, port); |
---|
173 |
|
---|
174 |
// setup port parameters |
---|
175 |
if(freebob_streaming_set_playback_buffer_type(driver->dev, chn, freebob_buffer_type_float)) { |
---|
176 |
printError(" cannot set port buffer type for %s", buf); |
---|
177 |
} |
---|
178 |
if (freebob_streaming_set_playback_stream_buffer(driver->dev, chn, NULL)) { |
---|
179 |
printError(" cannot configure initial port buffer for %s", buf); |
---|
180 |
} |
---|
181 |
if(freebob_streaming_playback_stream_onoff(driver->dev, chn, 1)) { |
---|
182 |
printError(" cannot enable port %s", buf); |
---|
183 |
} |
---|
184 |
} |
---|
185 |
// jack_port_set_latency (port, (driver->period_size * (driver->user_nperiods - 1)) + driver->playback_frame_latency); |
---|
186 |
|
---|
187 |
} |
---|
188 |
|
---|
189 |
if(!freebob_streaming_prepare(driver->dev)) { |
---|
190 |
printError("Could not prepare streaming device!"); |
---|
191 |
return -1; |
---|
192 |
} |
---|
193 |
|
---|
194 |
|
---|
195 |
return jack_activate (driver->client); |
---|
196 |
} |
---|
197 |
|
---|
198 |
static int |
---|
199 |
freebob_driver_detach (freebob_driver_t *driver) |
---|
200 |
{ |
---|
201 |
JSList *node; |
---|
202 |
|
---|
203 |
if (driver->engine == NULL) { |
---|
204 |
return 0; |
---|
205 |
} |
---|
206 |
|
---|
207 |
for (node = driver->capture_ports; node; |
---|
208 |
node = jack_slist_next (node)) { |
---|
209 |
jack_port_unregister (driver->client, |
---|
210 |
((jack_port_t *) node->data)); |
---|
211 |
} |
---|
212 |
|
---|
213 |
jack_slist_free (driver->capture_ports); |
---|
214 |
driver->capture_ports = 0; |
---|
215 |
|
---|
216 |
for (node = driver->playback_ports; node; |
---|
217 |
node = jack_slist_next (node)) { |
---|
218 |
jack_port_unregister (driver->client, |
---|
219 |
((jack_port_t *) node->data)); |
---|
220 |
} |
---|
221 |
|
---|
222 |
jack_slist_free (driver->playback_ports); |
---|
223 |
driver->playback_ports = 0; |
---|
224 |
|
---|
225 |
freebob_streaming_finish(driver->dev); |
---|
226 |
driver->dev=NULL; |
---|
227 |
|
---|
228 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
229 |
if(driver->midi_handle) { |
---|
230 |
freebob_driver_midi_finish(driver->midi_handle); |
---|
231 |
} |
---|
232 |
#endif |
---|
233 |
driver->midi_handle=NULL; |
---|
234 |
|
---|
235 |
return 0; |
---|
236 |
|
---|
237 |
} |
---|
238 |
|
---|
239 |
static inline void |
---|
240 |
freebob_driver_read_from_channel (freebob_driver_t *driver, |
---|
241 |
channel_t channel, |
---|
242 |
jack_default_audio_sample_t *dst, |
---|
243 |
jack_nframes_t nsamples) |
---|
244 |
{ |
---|
245 |
|
---|
246 |
freebob_sample_t buffer[nsamples]; |
---|
247 |
char *src=(char *)buffer; |
---|
248 |
|
---|
249 |
freebob_streaming_read(driver->dev, channel, buffer, nsamples); |
---|
250 |
|
---|
251 |
/* ALERT: signed sign-extension portability !!! */ |
---|
252 |
|
---|
253 |
while (nsamples--) { |
---|
254 |
int x; |
---|
255 |
#if __BYTE_ORDER == __LITTLE_ENDIAN |
---|
256 |
memcpy((char*)&x + 1, src, 3); |
---|
257 |
#elif __BYTE_ORDER == __BIG_ENDIAN |
---|
258 |
memcpy(&x, src, 3); |
---|
259 |
#endif |
---|
260 |
x >>= 8; |
---|
261 |
*dst = x / SAMPLE_MAX_24BIT; |
---|
262 |
dst++; |
---|
263 |
src += sizeof(freebob_sample_t); |
---|
264 |
} |
---|
265 |
|
---|
266 |
} |
---|
267 |
|
---|
268 |
static int |
---|
269 |
freebob_driver_read (freebob_driver_t * driver, jack_nframes_t nframes) |
---|
270 |
{ |
---|
271 |
jack_default_audio_sample_t* buf; |
---|
272 |
channel_t chn; |
---|
273 |
JSList *node; |
---|
274 |
jack_port_t* port; |
---|
275 |
|
---|
276 |
freebob_sample_t nullbuffer[nframes]; |
---|
277 |
|
---|
278 |
freebob_streaming_stream_type stream_type; |
---|
279 |
|
---|
280 |
printEnter(); |
---|
281 |
|
---|
282 |
for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { |
---|
283 |
stream_type = freebob_streaming_get_capture_stream_type(driver->dev, chn); |
---|
284 |
if(stream_type == freebob_stream_type_audio) { |
---|
285 |
port = (jack_port_t *) node->data; |
---|
286 |
|
---|
287 |
buf = jack_port_get_buffer (port, nframes); |
---|
288 |
if(!buf) buf=(jack_default_audio_sample_t*)nullbuffer; |
---|
289 |
|
---|
290 |
freebob_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(buf)); |
---|
291 |
} |
---|
292 |
} |
---|
293 |
|
---|
294 |
// now transfer the buffers |
---|
295 |
freebob_streaming_transfer_capture_buffers(driver->dev); |
---|
296 |
|
---|
297 |
printExit(); |
---|
298 |
|
---|
299 |
return 0; |
---|
300 |
|
---|
301 |
} |
---|
302 |
|
---|
303 |
static inline void |
---|
304 |
freebob_driver_write_to_channel (freebob_driver_t *driver, |
---|
305 |
channel_t channel, |
---|
306 |
jack_default_audio_sample_t *buf, |
---|
307 |
jack_nframes_t nsamples) |
---|
308 |
{ |
---|
309 |
long long y; |
---|
310 |
freebob_sample_t buffer[nsamples]; |
---|
311 |
unsigned int i=0; |
---|
312 |
char *dst=(char *)buffer; |
---|
313 |
|
---|
314 |
// convert from float to integer |
---|
315 |
for(;i<nsamples;i++) { |
---|
316 |
y = (long long)(*buf * SAMPLE_MAX_24BIT); |
---|
317 |
|
---|
318 |
if (y > (INT_MAX >> 8 )) { |
---|
319 |
y = (INT_MAX >> 8); |
---|
320 |
} else if (y < (INT_MIN >> 8 )) { |
---|
321 |
y = (INT_MIN >> 8 ); |
---|
322 |
} |
---|
323 |
#if __BYTE_ORDER == __LITTLE_ENDIAN |
---|
324 |
memcpy (dst, &y, 3); |
---|
325 |
#elif __BYTE_ORDER == __BIG_ENDIAN |
---|
326 |
memcpy (dst, (char *)&y + 5, 3); |
---|
327 |
#endif |
---|
328 |
dst += sizeof(freebob_sample_t); |
---|
329 |
buf++; |
---|
330 |
} |
---|
331 |
|
---|
332 |
// write to the freebob streaming device |
---|
333 |
freebob_streaming_write(driver->dev, channel, buffer, nsamples); |
---|
334 |
|
---|
335 |
} |
---|
336 |
|
---|
337 |
static int |
---|
338 |
freebob_driver_write (freebob_driver_t * driver, jack_nframes_t nframes) |
---|
339 |
{ |
---|
340 |
channel_t chn; |
---|
341 |
JSList *node; |
---|
342 |
jack_default_audio_sample_t* buf; |
---|
343 |
|
---|
344 |
jack_port_t *port; |
---|
345 |
|
---|
346 |
freebob_streaming_stream_type stream_type; |
---|
347 |
|
---|
348 |
freebob_sample_t nullbuffer[nframes]; |
---|
349 |
|
---|
350 |
memset(&nullbuffer,0,nframes*sizeof(freebob_sample_t)); |
---|
351 |
|
---|
352 |
printEnter(); |
---|
353 |
|
---|
354 |
driver->process_count++; |
---|
355 |
|
---|
356 |
assert(driver->dev); |
---|
357 |
|
---|
358 |
if (driver->engine->freewheeling) { |
---|
359 |
return 0; |
---|
360 |
} |
---|
361 |
|
---|
362 |
for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { |
---|
363 |
stream_type=freebob_streaming_get_playback_stream_type(driver->dev, chn); |
---|
364 |
if(stream_type == freebob_stream_type_audio) { |
---|
365 |
port = (jack_port_t *) node->data; |
---|
366 |
|
---|
367 |
buf = jack_port_get_buffer (port, nframes); |
---|
368 |
if(!buf) buf=(jack_default_audio_sample_t*)nullbuffer; |
---|
369 |
|
---|
370 |
freebob_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf)); |
---|
371 |
} |
---|
372 |
} |
---|
373 |
|
---|
374 |
freebob_streaming_transfer_playback_buffers(driver->dev); |
---|
375 |
|
---|
376 |
printExit(); |
---|
377 |
|
---|
378 |
return 0; |
---|
379 |
} |
---|
380 |
|
---|
381 |
//static inline jack_nframes_t |
---|
382 |
static jack_nframes_t |
---|
383 |
freebob_driver_wait (freebob_driver_t *driver, int extra_fd, int *status, |
---|
384 |
float *delayed_usecs) |
---|
385 |
{ |
---|
386 |
int nframes; |
---|
387 |
jack_time_t wait_enter; |
---|
388 |
jack_time_t wait_ret; |
---|
389 |
|
---|
390 |
printEnter(); |
---|
391 |
|
---|
392 |
wait_enter = jack_get_microseconds (); |
---|
393 |
if (wait_enter > driver->wait_next) { |
---|
394 |
/* |
---|
395 |
* This processing cycle was delayed past the |
---|
396 |
* next due interrupt! Do not account this as |
---|
397 |
* a wakeup delay: |
---|
398 |
*/ |
---|
399 |
driver->wait_next = 0; |
---|
400 |
driver->wait_late++; |
---|
401 |
} |
---|
402 |
// *status = -2; interrupt |
---|
403 |
// *status = -3; timeout |
---|
404 |
// *status = -4; extra FD |
---|
405 |
|
---|
406 |
nframes=freebob_streaming_wait(driver->dev); |
---|
407 |
|
---|
408 |
wait_ret = jack_get_microseconds (); |
---|
409 |
|
---|
410 |
if (driver->wait_next && wait_ret > driver->wait_next) { |
---|
411 |
*delayed_usecs = wait_ret - driver->wait_next; |
---|
412 |
} |
---|
413 |
driver->wait_last = wait_ret; |
---|
414 |
driver->wait_next = wait_ret + driver->period_usecs; |
---|
415 |
driver->engine->transport_cycle_start (driver->engine, wait_ret); |
---|
416 |
|
---|
417 |
// transfer the streaming buffers |
---|
418 |
// we now do this in the read/write functions |
---|
419 |
// freebob_streaming_transfer_buffers(driver->dev); |
---|
420 |
|
---|
421 |
if (nframes < 0) { |
---|
422 |
*status=0; |
---|
423 |
|
---|
424 |
return 0; |
---|
425 |
//nframes=driver->period_size; //debug |
---|
426 |
} |
---|
427 |
|
---|
428 |
*status = 0; |
---|
429 |
driver->last_wait_ust = wait_ret; |
---|
430 |
|
---|
431 |
// FIXME: this should do something more usefull |
---|
432 |
*delayed_usecs = 0; |
---|
433 |
|
---|
434 |
printExit(); |
---|
435 |
|
---|
436 |
return nframes - nframes % driver->period_size; |
---|
437 |
|
---|
438 |
} |
---|
439 |
|
---|
440 |
static int |
---|
441 |
freebob_driver_run_cycle (freebob_driver_t *driver) |
---|
442 |
{ |
---|
443 |
jack_engine_t *engine = driver->engine; |
---|
444 |
int wait_status=0; |
---|
445 |
float delayed_usecs=0.0; |
---|
446 |
|
---|
447 |
jack_nframes_t nframes = freebob_driver_wait (driver, -1, &wait_status, |
---|
448 |
&delayed_usecs); |
---|
449 |
|
---|
450 |
if ((wait_status < 0)) { |
---|
451 |
printError( "wait status < 0! (= %d)",wait_status); |
---|
452 |
return -1; |
---|
453 |
} |
---|
454 |
|
---|
455 |
if ((nframes == 0)) { |
---|
456 |
/* we detected an xrun and restarted: notify |
---|
457 |
* clients about the delay. */ |
---|
458 |
printMessage("xrun detected"); |
---|
459 |
engine->delay (engine, delayed_usecs); |
---|
460 |
return 0; |
---|
461 |
} |
---|
462 |
|
---|
463 |
return engine->run_cycle (engine, nframes, delayed_usecs); |
---|
464 |
|
---|
465 |
} |
---|
466 |
/* |
---|
467 |
* in a null cycle we should discard the input and write silence to the outputs |
---|
468 |
*/ |
---|
469 |
static int |
---|
470 |
freebob_driver_null_cycle (freebob_driver_t* driver, jack_nframes_t nframes) |
---|
471 |
{ |
---|
472 |
channel_t chn; |
---|
473 |
JSList *node; |
---|
474 |
snd_pcm_sframes_t nwritten; |
---|
475 |
|
---|
476 |
freebob_streaming_stream_type stream_type; |
---|
477 |
|
---|
478 |
jack_default_audio_sample_t buff[nframes]; |
---|
479 |
jack_default_audio_sample_t* buffer=(jack_default_audio_sample_t*)buff; |
---|
480 |
|
---|
481 |
printEnter(); |
---|
482 |
|
---|
483 |
memset(buffer,0,nframes*sizeof(jack_default_audio_sample_t)); |
---|
484 |
|
---|
485 |
assert(driver->dev); |
---|
486 |
|
---|
487 |
if (driver->engine->freewheeling) { |
---|
488 |
return 0; |
---|
489 |
} |
---|
490 |
|
---|
491 |
// write silence to buffer |
---|
492 |
nwritten = 0; |
---|
493 |
|
---|
494 |
for (chn = 0, node = driver->playback_ports; node; node = jack_slist_next (node), chn++) { |
---|
495 |
stream_type=freebob_streaming_get_playback_stream_type(driver->dev, chn); |
---|
496 |
|
---|
497 |
if(stream_type == freebob_stream_type_audio) { |
---|
498 |
freebob_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buffer)); |
---|
499 |
} |
---|
500 |
} |
---|
501 |
|
---|
502 |
freebob_streaming_transfer_playback_buffers(driver->dev); |
---|
503 |
|
---|
504 |
// read & discard from input ports |
---|
505 |
for (chn = 0, node = driver->capture_ports; node; node = jack_slist_next (node), chn++) { |
---|
506 |
stream_type=freebob_streaming_get_capture_stream_type(driver->dev, chn); |
---|
507 |
if(stream_type == freebob_stream_type_audio) { |
---|
508 |
freebob_streaming_set_capture_stream_buffer(driver->dev, chn, (char *)(buffer)); |
---|
509 |
} |
---|
510 |
} |
---|
511 |
|
---|
512 |
// now transfer the buffers |
---|
513 |
freebob_streaming_transfer_capture_buffers(driver->dev); |
---|
514 |
|
---|
515 |
printExit(); |
---|
516 |
return 0; |
---|
517 |
} |
---|
518 |
|
---|
519 |
static int |
---|
520 |
freebob_driver_start (freebob_driver_t *driver) |
---|
521 |
{ |
---|
522 |
int retval=0; |
---|
523 |
|
---|
524 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
525 |
if(driver->midi_handle) { |
---|
526 |
if((retval=freebob_driver_midi_start(driver->midi_handle))) { |
---|
527 |
printError("Could not start MIDI threads"); |
---|
528 |
return retval; |
---|
529 |
} |
---|
530 |
} |
---|
531 |
#endif |
---|
532 |
|
---|
533 |
if((retval=freebob_streaming_start(driver->dev))) { |
---|
534 |
printError("Could not start streaming threads"); |
---|
535 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
536 |
if(driver->midi_handle) { |
---|
537 |
freebob_driver_midi_stop(driver->midi_handle); |
---|
538 |
} |
---|
539 |
#endif |
---|
540 |
return retval; |
---|
541 |
} |
---|
542 |
|
---|
543 |
return 0; |
---|
544 |
|
---|
545 |
} |
---|
546 |
|
---|
547 |
static int |
---|
548 |
freebob_driver_stop (freebob_driver_t *driver) |
---|
549 |
{ |
---|
550 |
int retval=0; |
---|
551 |
|
---|
552 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
553 |
if(driver->midi_handle) { |
---|
554 |
if((retval=freebob_driver_midi_stop(driver->midi_handle))) { |
---|
555 |
printError("Could not stop MIDI threads"); |
---|
556 |
return retval; |
---|
557 |
} |
---|
558 |
} |
---|
559 |
#endif |
---|
560 |
if((retval=freebob_streaming_stop(driver->dev))) { |
---|
561 |
printError("Could not stop streaming threads"); |
---|
562 |
return retval; |
---|
563 |
} |
---|
564 |
|
---|
565 |
return 0; |
---|
566 |
} |
---|
567 |
|
---|
568 |
|
---|
569 |
static int |
---|
570 |
freebob_driver_bufsize (freebob_driver_t* driver, jack_nframes_t nframes) |
---|
571 |
{ |
---|
572 |
printError("Buffer size change requested but not supported!!!"); |
---|
573 |
|
---|
574 |
/* |
---|
575 |
driver->period_size = nframes; |
---|
576 |
driver->period_usecs = |
---|
577 |
(jack_time_t) floor ((((float) nframes) / driver->sample_rate) |
---|
578 |
* 1000000.0f); |
---|
579 |
*/ |
---|
580 |
|
---|
581 |
/* tell the engine to change its buffer size */ |
---|
582 |
//driver->engine->set_buffer_size (driver->engine, nframes); |
---|
583 |
|
---|
584 |
return -1; // unsupported |
---|
585 |
} |
---|
586 |
|
---|
587 |
typedef void (*JackDriverFinishFunction) (jack_driver_t *); |
---|
588 |
|
---|
589 |
freebob_driver_t * |
---|
590 |
freebob_driver_new (jack_client_t * client, |
---|
591 |
char *name, |
---|
592 |
freebob_jack_settings_t *params) |
---|
593 |
{ |
---|
594 |
freebob_driver_t *driver; |
---|
595 |
|
---|
596 |
assert(params); |
---|
597 |
|
---|
598 |
if(freebob_get_api_version() != 2) { |
---|
599 |
printError("Incompatible libfreebob version! (%s)", freebob_get_version()); |
---|
600 |
return NULL; |
---|
601 |
} |
---|
602 |
|
---|
603 |
printMessage("Starting Freebob backend (%s)", freebob_get_version()); |
---|
604 |
|
---|
605 |
driver = calloc (1, sizeof (freebob_driver_t)); |
---|
606 |
|
---|
607 |
/* Setup the jack interfaces */ |
---|
608 |
jack_driver_nt_init ((jack_driver_nt_t *) driver); |
---|
609 |
|
---|
610 |
driver->nt_attach = (JackDriverNTAttachFunction) freebob_driver_attach; |
---|
611 |
driver->nt_detach = (JackDriverNTDetachFunction) freebob_driver_detach; |
---|
612 |
driver->nt_start = (JackDriverNTStartFunction) freebob_driver_start; |
---|
613 |
driver->nt_stop = (JackDriverNTStopFunction) freebob_driver_stop; |
---|
614 |
driver->nt_run_cycle = (JackDriverNTRunCycleFunction) freebob_driver_run_cycle; |
---|
615 |
driver->null_cycle = (JackDriverNullCycleFunction) freebob_driver_null_cycle; |
---|
616 |
driver->write = (JackDriverReadFunction) freebob_driver_write; |
---|
617 |
driver->read = (JackDriverReadFunction) freebob_driver_read; |
---|
618 |
driver->nt_bufsize = (JackDriverNTBufSizeFunction) freebob_driver_bufsize; |
---|
619 |
|
---|
620 |
/* copy command line parameter contents to the driver structure */ |
---|
621 |
memcpy(&driver->settings,params,sizeof(freebob_jack_settings_t)); |
---|
622 |
|
---|
623 |
/* prepare all parameters */ |
---|
624 |
driver->sample_rate = params->sample_rate; |
---|
625 |
driver->period_size = params->period_size; |
---|
626 |
driver->last_wait_ust = 0; |
---|
627 |
|
---|
628 |
driver->period_usecs = |
---|
629 |
(jack_time_t) floor ((((float) driver->period_size) * 1000000.0f) / driver->sample_rate); |
---|
630 |
|
---|
631 |
driver->client = client; |
---|
632 |
driver->engine = NULL; |
---|
633 |
|
---|
634 |
memset(&driver->device_options,0,sizeof(driver->device_options)); |
---|
635 |
driver->device_options.sample_rate=params->sample_rate; |
---|
636 |
driver->device_options.period_size=params->period_size; |
---|
637 |
driver->device_options.nb_buffers=params->buffer_size; |
---|
638 |
driver->device_options.node_id=params->node_id; |
---|
639 |
driver->device_options.port=params->port; |
---|
640 |
|
---|
641 |
if(!params->capture_ports) { |
---|
642 |
driver->device_options.directions |= FREEBOB_IGNORE_CAPTURE; |
---|
643 |
} |
---|
644 |
|
---|
645 |
if(!params->playback_ports) { |
---|
646 |
driver->device_options.directions |= FREEBOB_IGNORE_PLAYBACK; |
---|
647 |
} |
---|
648 |
|
---|
649 |
debugPrint(DEBUG_LEVEL_STARTUP, " Driver compiled on %s %s", __DATE__, __TIME__); |
---|
650 |
debugPrint(DEBUG_LEVEL_STARTUP, " Created driver %s", name); |
---|
651 |
debugPrint(DEBUG_LEVEL_STARTUP, " period_size: %d", driver->period_size); |
---|
652 |
debugPrint(DEBUG_LEVEL_STARTUP, " period_usecs: %d", driver->period_usecs); |
---|
653 |
debugPrint(DEBUG_LEVEL_STARTUP, " sample rate: %d", driver->sample_rate); |
---|
654 |
|
---|
655 |
return (freebob_driver_t *) driver; |
---|
656 |
|
---|
657 |
} |
---|
658 |
|
---|
659 |
static void |
---|
660 |
freebob_driver_delete (freebob_driver_t *driver) |
---|
661 |
{ |
---|
662 |
jack_driver_nt_finish ((jack_driver_nt_t *) driver); |
---|
663 |
free (driver); |
---|
664 |
} |
---|
665 |
|
---|
666 |
#ifdef FREEBOB_DRIVER_WITH_MIDI |
---|
667 |
/* |
---|
668 |
* MIDI support |
---|
669 |
*/ |
---|
670 |
|
---|
671 |
// the thread that will queue the midi events from the seq to the stream buffers |
---|
672 |
|
---|
673 |
void * freebob_driver_midi_queue_thread(void *arg) |
---|
674 |
{ |
---|
675 |
freebob_driver_midi_handle_t *m=(freebob_driver_midi_handle_t *)arg; |
---|
676 |
assert(m); |
---|
677 |
snd_seq_event_t *ev; |
---|
678 |
unsigned char work_buffer[MIDI_TRANSMIT_BUFFER_SIZE]; |
---|
679 |
int bytes_to_send; |
---|
680 |
int b; |
---|
681 |
int i; |
---|
682 |
|
---|
683 |
printMessage("MIDI queue thread started"); |
---|
684 |
|
---|
685 |
while(1) { |
---|
686 |
// get next event, if one is present |
---|
687 |
while ((snd_seq_event_input(m->seq_handle, &ev) > 0)) { |
---|
688 |
// get the port this event is originated from |
---|
689 |
freebob_midi_port_t *port=NULL; |
---|
690 |
for (i=0;i<m->nb_output_ports;i++) { |
---|
691 |
if(m->output_ports[i]->seq_port_nr == ev->dest.port) { |
---|
692 |
port=m->output_ports[i]; |
---|
693 |
break; |
---|
694 |
} |
---|
695 |
} |
---|
696 |
|
---|
697 |
if(!port) { |
---|
698 |
printError(" Could not find target port for event: dst=%d src=%d", ev->dest.port, ev->source.port); |
---|
699 |
|
---|
700 |
break; |
---|
701 |
} |
---|
702 |
|
---|
703 |
// decode it to the work buffer |
---|
704 |
if((bytes_to_send = snd_midi_event_decode ( port->parser, |
---|
705 |
work_buffer, |
---|
706 |
MIDI_TRANSMIT_BUFFER_SIZE, |
---|
707 |
ev))<0) |
---|
708 |
{ // failed |
---|
709 |
printError(" Error decoding event for port %d (errcode=%d)", port->seq_port_nr,bytes_to_send); |
---|
710 |
bytes_to_send=0; |
---|
711 |
//return -1; |
---|
712 |
} |
---|
713 |
|
---|
714 |
for(b=0;b<bytes_to_send;b++) { |
---|
715 |
freebob_sample_t tmp_event=work_buffer[b]; |
---|
716 |
if(freebob_streaming_write(m->dev, port->stream_nr, &tmp_event, 1)<1) { |
---|
717 |
printError(" Midi send buffer overrun"); |
---|
718 |
} |
---|
719 |
} |
---|
720 |
|
---|
721 |
} |
---|
722 |
|
---|
723 |
// sleep for some time |
---|
724 |
usleep(MIDI_THREAD_SLEEP_TIME_USECS); |
---|
725 |
} |
---|
726 |
return NULL; |
---|
727 |
} |
---|
728 |
|
---|
729 |
// the dequeue thread (maybe we need one thread per stream) |
---|
730 |
void *freebob_driver_midi_dequeue_thread (void *arg) { |
---|
731 |
freebob_driver_midi_handle_t *m=(freebob_driver_midi_handle_t *)arg; |
---|
732 |
|
---|
733 |
int i; |
---|
734 |
int s; |
---|
735 |
|
---|
736 |
int samples_read; |
---|
737 |
|
---|
738 |
assert(m); |
---|
739 |
|
---|
740 |
while(1) { |
---|
741 |
// read incoming events |
---|
742 |
|
---|
743 |
for (i=0;i<m->nb_input_ports;i++) { |
---|
744 |
unsigned int buff[64]; |
---|
745 |
|
---|
746 |
freebob_midi_port_t *port=m->input_ports[i]; |
---|
747 |
|
---|
748 |
if(!port) { |
---|
749 |
printError(" something went wrong when setting up the midi input port map (%d)",i); |
---|
750 |
} |
---|
751 |
|
---|
752 |
do { |
---|
753 |
samples_read=freebob_streaming_read(m->dev, port->stream_nr, buff, 64); |
---|
754 |
|
---|
755 |
for (s=0;s<samples_read;s++) { |
---|
756 |
unsigned int *byte=(buff+s) ; |
---|
757 |
snd_seq_event_t ev; |
---|
758 |
if ((snd_midi_event_encode_byte(port->parser,(*byte) & 0xFF, &ev)) > 0) { |
---|
759 |
// a midi message is complete, send it out to ALSA |
---|
760 |
snd_seq_ev_set_subs(&ev); |
---|
761 |
snd_seq_ev_set_direct(&ev); |
---|
762 |
snd_seq_ev_set_source(&ev, port->seq_port_nr); |
---|
763 |
snd_seq_event_output_direct(port->seq_handle, &ev); |
---|
764 |
} |
---|
765 |
} |
---|
766 |
} while (samples_read>0); |
---|
767 |
} |
---|
768 |
|
---|
769 |
// sleep for some time |
---|
770 |
usleep(MIDI_THREAD_SLEEP_TIME_USECS); |
---|
771 |
} |
---|
772 |
return NULL; |
---|
773 |
} |
---|
774 |
|
---|
775 |
static freebob_driver_midi_handle_t *freebob_driver_midi_init(freebob_driver_t *driver) { |
---|
776 |
// int err; |
---|
777 |
|
---|
778 |
char buf[256]; |
---|
779 |
channel_t chn; |
---|
780 |
int nchannels; |
---|
781 |
int i=0; |
---|
782 |
|
---|
783 |
freebob_device_t *dev=driver->dev; |
---|
784 |
|
---|
785 |
assert(dev); |
---|
786 |
|
---|
787 |
freebob_driver_midi_handle_t *m=calloc(1,sizeof(freebob_driver_midi_handle_t)); |
---|
788 |
if (!m) { |
---|
789 |
printError("not enough memory to create midi structure"); |
---|
790 |
return NULL; |
---|
791 |
} |
---|
792 |
|
---|
793 |
if (snd_seq_open(&m->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) { |
---|
794 |
printError("Error opening ALSA sequencer."); |
---|
795 |
free(m); |
---|
796 |
return NULL; |
---|
797 |
} |
---|
798 |
|
---|
799 |
snd_seq_set_client_name(m->seq_handle, "FreeBoB Jack MIDI"); |
---|
800 |
|
---|
801 |
// find out the number of midi in/out ports we need to setup |
---|
802 |
nchannels=freebob_streaming_get_nb_capture_streams(dev); |
---|
803 |
|
---|
804 |
m->nb_input_ports=0; |
---|
805 |
|
---|
806 |
for (chn = 0; chn < nchannels; chn++) { |
---|
807 |
if(freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
808 |
m->nb_input_ports++; |
---|
809 |
} |
---|
810 |
} |
---|
811 |
|
---|
812 |
m->input_ports=calloc(m->nb_input_ports,sizeof(freebob_midi_port_t *)); |
---|
813 |
if(!m->input_ports) { |
---|
814 |
printError("not enough memory to create midi structure"); |
---|
815 |
free(m); |
---|
816 |
return NULL; |
---|
817 |
} |
---|
818 |
|
---|
819 |
i=0; |
---|
820 |
for (chn = 0; chn < nchannels; chn++) { |
---|
821 |
if(freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
822 |
m->input_ports[i]=calloc(1,sizeof(freebob_midi_port_t)); |
---|
823 |
if(!m->input_ports[i]) { |
---|
824 |
// fixme |
---|
825 |
printError("Could not allocate memory for seq port"); |
---|
826 |
continue; |
---|
827 |
} |
---|
828 |
|
---|
829 |
freebob_streaming_get_capture_stream_name(dev, chn, buf, sizeof(buf) - 1); |
---|
830 |
printMessage("Register MIDI IN port %s", buf); |
---|
831 |
|
---|
832 |
m->input_ports[i]->seq_port_nr=snd_seq_create_simple_port(m->seq_handle, buf, |
---|
833 |
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, |
---|
834 |
SND_SEQ_PORT_TYPE_MIDI_GENERIC); |
---|
835 |
|
---|
836 |
if(m->input_ports[i]->seq_port_nr<0) { |
---|
837 |
printError("Could not create seq port"); |
---|
838 |
m->input_ports[i]->stream_nr=-1; |
---|
839 |
m->input_ports[i]->seq_port_nr=-1; |
---|
840 |
} else { |
---|
841 |
m->input_ports[i]->stream_nr=chn; |
---|
842 |
m->input_ports[i]->seq_handle=m->seq_handle; |
---|
843 |
if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->input_ports[i]->parser)) < 0) { |
---|
844 |
printError("could not init parser for MIDI IN port %d",i); |
---|
845 |
m->input_ports[i]->stream_nr=-1; |
---|
846 |
m->input_ports[i]->seq_port_nr=-1; |
---|
847 |
} else { |
---|
848 |
if(freebob_streaming_set_capture_buffer_type(dev, chn, freebob_buffer_type_midi)) { |
---|
849 |
printError(" cannot set port buffer type for %s", buf); |
---|
850 |
m->input_ports[i]->stream_nr=-1; |
---|
851 |
m->input_ports[i]->seq_port_nr=-1; |
---|
852 |
} |
---|
853 |
if(freebob_streaming_capture_stream_onoff(dev, chn, 1)) { |
---|
854 |
printError(" cannot enable port %s", buf); |
---|
855 |
m->input_ports[i]->stream_nr=-1; |
---|
856 |
m->input_ports[i]->seq_port_nr=-1; |
---|
857 |
} |
---|
858 |
|
---|
859 |
} |
---|
860 |
} |
---|
861 |
|
---|
862 |
i++; |
---|
863 |
} |
---|
864 |
} |
---|
865 |
|
---|
866 |
// playback |
---|
867 |
nchannels=freebob_streaming_get_nb_playback_streams(dev); |
---|
868 |
|
---|
869 |
m->nb_output_ports=0; |
---|
870 |
|
---|
871 |
for (chn = 0; chn < nchannels; chn++) { |
---|
872 |
if(freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
873 |
m->nb_output_ports++; |
---|
874 |
} |
---|
875 |
} |
---|
876 |
|
---|
877 |
m->output_ports=calloc(m->nb_output_ports,sizeof(freebob_midi_port_t *)); |
---|
878 |
if(!m->output_ports) { |
---|
879 |
printError("not enough memory to create midi structure"); |
---|
880 |
for (i = 0; i < m->nb_input_ports; i++) { |
---|
881 |
free(m->input_ports[i]); |
---|
882 |
} |
---|
883 |
free(m->input_ports); |
---|
884 |
free(m); |
---|
885 |
return NULL; |
---|
886 |
} |
---|
887 |
|
---|
888 |
i=0; |
---|
889 |
for (chn = 0; chn < nchannels; chn++) { |
---|
890 |
if(freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
891 |
m->output_ports[i]=calloc(1,sizeof(freebob_midi_port_t)); |
---|
892 |
if(!m->output_ports[i]) { |
---|
893 |
// fixme |
---|
894 |
printError("Could not allocate memory for seq port"); |
---|
895 |
continue; |
---|
896 |
} |
---|
897 |
|
---|
898 |
freebob_streaming_get_playback_stream_name(dev, chn, buf, sizeof(buf) - 1); |
---|
899 |
printMessage("Register MIDI OUT port %s", buf); |
---|
900 |
|
---|
901 |
m->output_ports[i]->seq_port_nr=snd_seq_create_simple_port(m->seq_handle, buf, |
---|
902 |
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, |
---|
903 |
SND_SEQ_PORT_TYPE_MIDI_GENERIC); |
---|
904 |
|
---|
905 |
|
---|
906 |
if(m->output_ports[i]->seq_port_nr<0) { |
---|
907 |
printError("Could not create seq port"); |
---|
908 |
m->output_ports[i]->stream_nr=-1; |
---|
909 |
m->output_ports[i]->seq_port_nr=-1; |
---|
910 |
} else { |
---|
911 |
m->output_ports[i]->stream_nr=chn; |
---|
912 |
m->output_ports[i]->seq_handle=m->seq_handle; |
---|
913 |
if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->output_ports[i]->parser)) < 0) { |
---|
914 |
printError("could not init parser for MIDI OUT port %d",i); |
---|
915 |
m->output_ports[i]->stream_nr=-1; |
---|
916 |
m->output_ports[i]->seq_port_nr=-1; |
---|
917 |
} else { |
---|
918 |
if(freebob_streaming_set_playback_buffer_type(dev, chn, freebob_buffer_type_midi)) { |
---|
919 |
printError(" cannot set port buffer type for %s", buf); |
---|
920 |
m->input_ports[i]->stream_nr=-1; |
---|
921 |
m->input_ports[i]->seq_port_nr=-1; |
---|
922 |
} |
---|
923 |
if(freebob_streaming_playback_stream_onoff(dev, chn, 1)) { |
---|
924 |
printError(" cannot enable port %s", buf); |
---|
925 |
m->input_ports[i]->stream_nr=-1; |
---|
926 |
m->input_ports[i]->seq_port_nr=-1; |
---|
927 |
} |
---|
928 |
} |
---|
929 |
} |
---|
930 |
|
---|
931 |
i++; |
---|
932 |
} |
---|
933 |
} |
---|
934 |
|
---|
935 |
m->dev=dev; |
---|
936 |
m->driver=driver; |
---|
937 |
|
---|
938 |
return m; |
---|
939 |
} |
---|
940 |
|
---|
941 |
static int |
---|
942 |
freebob_driver_midi_start (freebob_driver_midi_handle_t *m) |
---|
943 |
{ |
---|
944 |
assert(m); |
---|
945 |
// start threads |
---|
946 |
|
---|
947 |
m->queue_thread_realtime=(m->driver->engine->control->real_time? 1 : 0); |
---|
948 |
m->queue_thread_priority= |
---|
949 |
m->driver->engine->control->client_priority + |
---|
950 |
FREEBOB_RT_PRIORITY_MIDI_RELATIVE; |
---|
951 |
|
---|
952 |
if (m->queue_thread_priority>98) { |
---|
953 |
m->queue_thread_priority=98; |
---|
954 |
} |
---|
955 |
if (m->queue_thread_realtime) { |
---|
956 |
printMessage("MIDI threads running with Realtime scheduling, priority %d", |
---|
957 |
m->queue_thread_priority); |
---|
958 |
} else { |
---|
959 |
printMessage("MIDI threads running without Realtime scheduling"); |
---|
960 |
} |
---|
961 |
|
---|
962 |
if (jack_client_create_thread(NULL, &m->queue_thread, m->queue_thread_priority, m->queue_thread_realtime, freebob_driver_midi_queue_thread, (void *)m)) { |
---|
963 |
printError(" cannot create midi queueing thread"); |
---|
964 |
return -1; |
---|
965 |
} |
---|
966 |
|
---|
967 |
if (jack_client_create_thread(NULL, &m->dequeue_thread, m->queue_thread_priority, m->queue_thread_realtime, freebob_driver_midi_dequeue_thread, (void *)m)) { |
---|
968 |
printError(" cannot create midi dequeueing thread"); |
---|
969 |
return -1; |
---|
970 |
} |
---|
971 |
return 0; |
---|
972 |
} |
---|
973 |
|
---|
974 |
static int |
---|
975 |
freebob_driver_midi_stop (freebob_driver_midi_handle_t *m) |
---|
976 |
{ |
---|
977 |
assert(m); |
---|
978 |
|
---|
979 |
pthread_cancel (m->queue_thread); |
---|
980 |
pthread_join (m->queue_thread, NULL); |
---|
981 |
|
---|
982 |
pthread_cancel (m->dequeue_thread); |
---|
983 |
pthread_join (m->dequeue_thread, NULL); |
---|
984 |
return 0; |
---|
985 |
|
---|
986 |
} |
---|
987 |
|
---|
988 |
static void |
---|
989 |
freebob_driver_midi_finish (freebob_driver_midi_handle_t *m) |
---|
990 |
{ |
---|
991 |
assert(m); |
---|
992 |
|
---|
993 |
int i; |
---|
994 |
// TODO: add state info here, if not stopped then stop |
---|
995 |
|
---|
996 |
for (i=0;i<m->nb_input_ports;i++) { |
---|
997 |
free(m->input_ports[i]); |
---|
998 |
|
---|
999 |
} |
---|
1000 |
free(m->input_ports); |
---|
1001 |
|
---|
1002 |
for (i=0;i<m->nb_output_ports;i++) { |
---|
1003 |
free(m->output_ports[i]); |
---|
1004 |
} |
---|
1005 |
free(m->output_ports); |
---|
1006 |
|
---|
1007 |
free(m); |
---|
1008 |
} |
---|
1009 |
#endif |
---|
1010 |
/* |
---|
1011 |
* dlopen plugin stuff |
---|
1012 |
*/ |
---|
1013 |
|
---|
1014 |
const char driver_client_name[] = "freebob_pcm"; |
---|
1015 |
|
---|
1016 |
const jack_driver_desc_t * |
---|
1017 |
driver_get_descriptor () |
---|
1018 |
{ |
---|
1019 |
jack_driver_desc_t * desc; |
---|
1020 |
jack_driver_param_desc_t * params; |
---|
1021 |
unsigned int i; |
---|
1022 |
|
---|
1023 |
desc = calloc (1, sizeof (jack_driver_desc_t)); |
---|
1024 |
|
---|
1025 |
strcpy (desc->name, "freebob"); |
---|
1026 |
desc->nparams = 6; |
---|
1027 |
|
---|
1028 |
params = calloc (desc->nparams, sizeof (jack_driver_param_desc_t)); |
---|
1029 |
desc->params = params; |
---|
1030 |
|
---|
1031 |
i = 0; |
---|
1032 |
strcpy (params[i].name, "device"); |
---|
1033 |
params[i].character = 'd'; |
---|
1034 |
params[i].type = JackDriverParamString; |
---|
1035 |
strcpy (params[i].value.str, "hw:0"); |
---|
1036 |
strcpy (params[i].short_desc, "The FireWire device to use. Format is: 'hw:port[,node]'."); |
---|
1037 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1038 |
|
---|
1039 |
i++; |
---|
1040 |
strcpy (params[i].name, "period"); |
---|
1041 |
params[i].character = 'p'; |
---|
1042 |
params[i].type = JackDriverParamUInt; |
---|
1043 |
params[i].value.ui = 1024; |
---|
1044 |
strcpy (params[i].short_desc, "Frames per period"); |
---|
1045 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1046 |
|
---|
1047 |
i++; |
---|
1048 |
strcpy (params[i].name, "nperiods"); |
---|
1049 |
params[i].character = 'n'; |
---|
1050 |
params[i].type = JackDriverParamUInt; |
---|
1051 |
params[i].value.ui = 3; |
---|
1052 |
strcpy (params[i].short_desc, "Number of periods of playback latency"); |
---|
1053 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1054 |
|
---|
1055 |
i++; |
---|
1056 |
strcpy (params[i].name, "rate"); |
---|
1057 |
params[i].character = 'r'; |
---|
1058 |
params[i].type = JackDriverParamUInt; |
---|
1059 |
params[i].value.ui = 48000U; |
---|
1060 |
strcpy (params[i].short_desc, "Sample rate"); |
---|
1061 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1062 |
|
---|
1063 |
i++; |
---|
1064 |
strcpy (params[i].name, "capture"); |
---|
1065 |
params[i].character = 'i'; |
---|
1066 |
params[i].type = JackDriverParamUInt; |
---|
1067 |
params[i].value.ui = 1U; |
---|
1068 |
strcpy (params[i].short_desc, "Provide capture ports."); |
---|
1069 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1070 |
|
---|
1071 |
i++; |
---|
1072 |
strcpy (params[i].name, "playback"); |
---|
1073 |
params[i].character = 'o'; |
---|
1074 |
params[i].type = JackDriverParamUInt; |
---|
1075 |
params[i].value.ui = 1U; |
---|
1076 |
strcpy (params[i].short_desc, "Provide playback ports."); |
---|
1077 |
strcpy (params[i].long_desc, params[i].short_desc); |
---|
1078 |
|
---|
1079 |
return desc; |
---|
1080 |
} |
---|
1081 |
|
---|
1082 |
|
---|
1083 |
jack_driver_t * |
---|
1084 |
driver_initialize (jack_client_t *client, JSList * params) |
---|
1085 |
{ |
---|
1086 |
jack_driver_t *driver; |
---|
1087 |
|
---|
1088 |
unsigned int port=0; |
---|
1089 |
unsigned int node_id=-1; |
---|
1090 |
int nbitems; |
---|
1091 |
|
---|
1092 |
const JSList * node; |
---|
1093 |
const jack_driver_param_t * param; |
---|
1094 |
|
---|
1095 |
freebob_jack_settings_t cmlparams; |
---|
1096 |
|
---|
1097 |
char *device_name="hw:0"; |
---|
1098 |
|
---|
1099 |
cmlparams.period_size_set=0; |
---|
1100 |
cmlparams.sample_rate_set=0; |
---|
1101 |
cmlparams.buffer_size_set=0; |
---|
1102 |
cmlparams.port_set=0; |
---|
1103 |
cmlparams.node_id_set=0; |
---|
1104 |
|
---|
1105 |
/* default values */ |
---|
1106 |
cmlparams.period_size=1024; |
---|
1107 |
cmlparams.sample_rate=48000; |
---|
1108 |
cmlparams.buffer_size=3; |
---|
1109 |
cmlparams.port=0; |
---|
1110 |
cmlparams.node_id=-1; |
---|
1111 |
cmlparams.playback_ports=1; |
---|
1112 |
cmlparams.capture_ports=1; |
---|
1113 |
|
---|
1114 |
for (node = params; node; node = jack_slist_next (node)) |
---|
1115 |
{ |
---|
1116 |
param = (jack_driver_param_t *) node->data; |
---|
1117 |
|
---|
1118 |
switch (param->character) |
---|
1119 |
{ |
---|
1120 |
case 'd': |
---|
1121 |
device_name = strdup (param->value.str); |
---|
1122 |
break; |
---|
1123 |
case 'p': |
---|
1124 |
cmlparams.period_size = param->value.ui; |
---|
1125 |
cmlparams.period_size_set = 1; |
---|
1126 |
break; |
---|
1127 |
case 'n': |
---|
1128 |
cmlparams.buffer_size = param->value.ui; |
---|
1129 |
cmlparams.buffer_size_set = 1; |
---|
1130 |
break; |
---|
1131 |
case 'r': |
---|
1132 |
cmlparams.sample_rate = param->value.ui; |
---|
1133 |
cmlparams.sample_rate_set = 1; |
---|
1134 |
break; |
---|
1135 |
case 'i': |
---|
1136 |
cmlparams.capture_ports = param->value.ui; |
---|
1137 |
break; |
---|
1138 |
case 'o': |
---|
1139 |
cmlparams.playback_ports = param->value.ui; |
---|
1140 |
break; |
---|
1141 |
} |
---|
1142 |
} |
---|
1143 |
|
---|
1144 |
nbitems=sscanf(device_name,"hw:%u,%u",&port,&node_id); |
---|
1145 |
if (nbitems<2) { |
---|
1146 |
nbitems=sscanf(device_name,"hw:%u",&port); |
---|
1147 |
|
---|
1148 |
if(nbitems < 1) { |
---|
1149 |
free(device_name); |
---|
1150 |
printError("device (-d) argument not valid\n"); |
---|
1151 |
return NULL; |
---|
1152 |
} else { |
---|
1153 |
cmlparams.port = port; |
---|
1154 |
cmlparams.port_set=1; |
---|
1155 |
|
---|
1156 |
cmlparams.node_id = -1; |
---|
1157 |
cmlparams.node_id_set=0; |
---|
1158 |
} |
---|
1159 |
} else { |
---|
1160 |
cmlparams.port = port; |
---|
1161 |
cmlparams.port_set=1; |
---|
1162 |
|
---|
1163 |
cmlparams.node_id = node_id; |
---|
1164 |
cmlparams.node_id_set=1; |
---|
1165 |
} |
---|
1166 |
|
---|
1167 |
jack_error("Freebob using Firewire port %d, node %d",cmlparams.port,cmlparams.node_id); |
---|
1168 |
|
---|
1169 |
driver=(jack_driver_t *)freebob_driver_new (client, "freebob_pcm", &cmlparams); |
---|
1170 |
|
---|
1171 |
return driver; |
---|
1172 |
} |
---|
1173 |
|
---|
1174 |
void |
---|
1175 |
driver_finish (jack_driver_t *driver) |
---|
1176 |
{ |
---|
1177 |
freebob_driver_t *drv=(freebob_driver_t *) driver; |
---|
1178 |
// If jack hasn't called the detach method, do it now. As of jack 0.101.1 |
---|
1179 |
// the detach method was not being called explicitly on closedown, and |
---|
1180 |
// we need it to at least deallocate the iso resources. |
---|
1181 |
if (drv->dev != NULL) |
---|
1182 |
freebob_driver_detach(drv); |
---|
1183 |
freebob_driver_delete (drv); |
---|
1184 |
} |
---|