1 |
/* |
---|
2 |
* ALSA Freebob Plugin |
---|
3 |
* |
---|
4 |
* Copyright (c) 2006 Pieter Palmers <pieterpalmers@users.sourceforge.net> |
---|
5 |
* |
---|
6 |
* Based upon the JACK <-> ALSA plugin |
---|
7 |
* Copyright (c) 2003 by Maarten de Boer <mdeboer@iua.upf.es> |
---|
8 |
* 2005 Takashi Iwai <tiwai@suse.de> |
---|
9 |
* |
---|
10 |
* This library is free software; you can redistribute it and/or modify |
---|
11 |
* it under the terms of the GNU Lesser General Public License as |
---|
12 |
* published by the Free Software Foundation; either version 2.1 of |
---|
13 |
* the License, or (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 Lesser General Public License for more details. |
---|
19 |
* |
---|
20 |
* You should have received a copy of the GNU Lesser General Public |
---|
21 |
* License along with this library; if not, write to the Free Software |
---|
22 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
23 |
* |
---|
24 |
*/ |
---|
25 |
|
---|
26 |
#include <byteswap.h> |
---|
27 |
#include <sys/shm.h> |
---|
28 |
#include <sys/types.h> |
---|
29 |
#include <sys/socket.h> |
---|
30 |
#include <alsa/asoundlib.h> |
---|
31 |
#include <alsa/pcm_external.h> |
---|
32 |
|
---|
33 |
#include <libfreebob/freebob.h> |
---|
34 |
#include <libfreebob/freebob_streaming.h> |
---|
35 |
|
---|
36 |
#include <pthread.h> |
---|
37 |
|
---|
38 |
#define FREEBOB_PLUGIN_VERSION "0.0.8" |
---|
39 |
|
---|
40 |
#define FREEBOB_USE_RT 1 |
---|
41 |
#define FREEBOB_RT_PRIORITY_PACKETIZER 60 |
---|
42 |
// midi priority should be higher than the audio priority in order to |
---|
43 |
// make sure events are not only delivered on period boundarys |
---|
44 |
// but I think it should be smaller than the packetizer thread in order not |
---|
45 |
// to lose any packets |
---|
46 |
#define FREEBOB_RT_PRIORITY_MIDI 59 |
---|
47 |
|
---|
48 |
// undef this to disable midi support |
---|
49 |
#define FREEBOB_WITH_MIDI |
---|
50 |
|
---|
51 |
#ifdef FREEBOB_WITH_MIDI |
---|
52 |
|
---|
53 |
#define ALSA_SEQ_BUFF_SIZE 1024 |
---|
54 |
#define MIDI_TRANSMIT_BUFFER_SIZE 1024 |
---|
55 |
#define MIDI_THREAD_SLEEP_TIME_USECS 1000 |
---|
56 |
|
---|
57 |
typedef struct { |
---|
58 |
int stream_nr; |
---|
59 |
int seq_port_nr; |
---|
60 |
snd_midi_event_t *parser; |
---|
61 |
snd_seq_t *seq_handle; |
---|
62 |
} freebob_midi_port_t; |
---|
63 |
|
---|
64 |
typedef struct _snd_pcm_freebob_midi_handle { |
---|
65 |
freebob_device_t *dev; |
---|
66 |
|
---|
67 |
snd_seq_t *seq_handle; |
---|
68 |
|
---|
69 |
pthread_t queue_thread; |
---|
70 |
pthread_t dequeue_thread; |
---|
71 |
int queue_thread_realtime; |
---|
72 |
int queue_thread_priority; |
---|
73 |
|
---|
74 |
int nb_input_ports; |
---|
75 |
int nb_output_ports; |
---|
76 |
|
---|
77 |
freebob_midi_port_t **input_ports; |
---|
78 |
freebob_midi_port_t **output_ports; |
---|
79 |
|
---|
80 |
freebob_midi_port_t **input_stream_port_map; |
---|
81 |
int *output_port_stream_map; |
---|
82 |
|
---|
83 |
|
---|
84 |
} snd_pcm_freebob_midi_handle_t; |
---|
85 |
|
---|
86 |
|
---|
87 |
#endif |
---|
88 |
|
---|
89 |
// #define PRINT_FUNCTION_ENTRY (printf("entering %s\n",__FUNCTION__)) |
---|
90 |
#define PRINT_FUNCTION_ENTRY |
---|
91 |
typedef struct { |
---|
92 |
snd_pcm_ioplug_t io; |
---|
93 |
|
---|
94 |
int fd; |
---|
95 |
int activated; |
---|
96 |
|
---|
97 |
unsigned int hw_ptr; |
---|
98 |
unsigned int sample_bits; |
---|
99 |
|
---|
100 |
unsigned int channels; |
---|
101 |
snd_pcm_channel_area_t *areas; |
---|
102 |
|
---|
103 |
freebob_device_t *streaming_device; |
---|
104 |
freebob_handle_t fb_handle; |
---|
105 |
|
---|
106 |
// options |
---|
107 |
freebob_options_t dev_options; |
---|
108 |
|
---|
109 |
snd_pcm_stream_t stream; |
---|
110 |
|
---|
111 |
// thread for polling |
---|
112 |
pthread_t thread; |
---|
113 |
|
---|
114 |
#ifdef FREEBOB_WITH_MIDI |
---|
115 |
snd_pcm_freebob_midi_handle_t *midi_handle; |
---|
116 |
#endif |
---|
117 |
|
---|
118 |
} snd_pcm_freebob_t; |
---|
119 |
|
---|
120 |
#ifdef FREEBOB_WITH_MIDI |
---|
121 |
static snd_pcm_freebob_midi_handle_t *snd_pcm_freebob_midi_init(snd_pcm_freebob_t *driver); |
---|
122 |
static void snd_pcm_freebob_midi_finish (snd_pcm_freebob_midi_handle_t *m); |
---|
123 |
#endif |
---|
124 |
|
---|
125 |
static int snd_pcm_freebob_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) { |
---|
126 |
|
---|
127 |
PRINT_FUNCTION_ENTRY; |
---|
128 |
|
---|
129 |
snd_pcm_freebob_t *freebob = io->private_data; |
---|
130 |
unsigned int i=0; |
---|
131 |
|
---|
132 |
freebob->channels=0; |
---|
133 |
if (freebob->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
134 |
//fixme: assumes that the audio streams are always the first streams |
---|
135 |
for (i=0;i < (unsigned int)freebob_streaming_get_nb_playback_streams(freebob->streaming_device);i++) { |
---|
136 |
if(freebob_streaming_get_playback_stream_type(freebob->streaming_device,i)==freebob_stream_type_audio) { |
---|
137 |
freebob->channels++; |
---|
138 |
} |
---|
139 |
} |
---|
140 |
for (i=0;i<freebob->channels;i++) { |
---|
141 |
// setup buffers, currently left at default |
---|
142 |
|
---|
143 |
} |
---|
144 |
} else { |
---|
145 |
//fixme: assumes that the audio streams are always the first streams |
---|
146 |
for (i=0;i < (unsigned int)freebob_streaming_get_nb_capture_streams(freebob->streaming_device);i++) { |
---|
147 |
if(freebob_streaming_get_capture_stream_type(freebob->streaming_device,i)==freebob_stream_type_audio) { |
---|
148 |
freebob->channels++; |
---|
149 |
} |
---|
150 |
} |
---|
151 |
for (i=0;i<freebob->channels;i++) { |
---|
152 |
// setup buffers, currently left at default |
---|
153 |
|
---|
154 |
} |
---|
155 |
} |
---|
156 |
|
---|
157 |
return 0; |
---|
158 |
} |
---|
159 |
|
---|
160 |
static int |
---|
161 |
snd_pcm_freebob_pollfunction(snd_pcm_freebob_t *freebob) |
---|
162 |
{ |
---|
163 |
|
---|
164 |
// PRINT_FUNCTION_ENTRY; |
---|
165 |
int retval; |
---|
166 |
static char buf[1]; |
---|
167 |
snd_pcm_ioplug_t *io=&freebob->io; |
---|
168 |
const snd_pcm_channel_area_t *areas; |
---|
169 |
unsigned int i; |
---|
170 |
|
---|
171 |
assert(freebob); |
---|
172 |
assert(freebob->streaming_device); |
---|
173 |
|
---|
174 |
retval = freebob_streaming_wait(freebob->streaming_device); |
---|
175 |
if (retval < 0) { |
---|
176 |
printf("Xrun\n"); |
---|
177 |
freebob_streaming_reset(freebob->streaming_device); |
---|
178 |
return 0; |
---|
179 |
} |
---|
180 |
|
---|
181 |
areas = snd_pcm_ioplug_mmap_areas(io); |
---|
182 |
|
---|
183 |
snd_pcm_uframes_t offset = freebob->hw_ptr; |
---|
184 |
|
---|
185 |
for (i=0;i<freebob->channels;i++) { |
---|
186 |
freebob_sample_t *buff=(freebob_sample_t *)((char *)areas[i].addr + (areas[i].step * offset / 8)); |
---|
187 |
// printf("%d: %p %d %d %p\n",i, areas[i].addr, offset, areas[i].step, buff); |
---|
188 |
|
---|
189 |
if (freebob->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
190 |
freebob_streaming_set_playback_stream_buffer(freebob->streaming_device, i, (char *)buff, freebob_buffer_type_uint24); |
---|
191 |
} else { |
---|
192 |
freebob_streaming_set_capture_stream_buffer(freebob->streaming_device, i, (char *)buff, freebob_buffer_type_uint24); |
---|
193 |
} |
---|
194 |
} |
---|
195 |
|
---|
196 |
if (freebob->stream == SND_PCM_STREAM_PLAYBACK) { |
---|
197 |
freebob_streaming_transfer_playback_buffers(freebob->streaming_device); |
---|
198 |
} else { |
---|
199 |
freebob_streaming_transfer_capture_buffers(freebob->streaming_device); |
---|
200 |
} |
---|
201 |
|
---|
202 |
freebob->hw_ptr += freebob->dev_options.period_size; |
---|
203 |
freebob->hw_ptr %= io->buffer_size; |
---|
204 |
|
---|
205 |
write(freebob->fd, buf, 1); /* for polling */ |
---|
206 |
|
---|
207 |
return 0; |
---|
208 |
} |
---|
209 |
|
---|
210 |
static void * freebob_workthread(void *arg) |
---|
211 |
{ |
---|
212 |
PRINT_FUNCTION_ENTRY; |
---|
213 |
snd_pcm_freebob_t *freebob = (snd_pcm_freebob_t *)arg; |
---|
214 |
|
---|
215 |
int oldstate; |
---|
216 |
|
---|
217 |
pthread_setcancelstate (PTHREAD_CANCEL_DEFERRED, &oldstate); |
---|
218 |
|
---|
219 |
while (1) { |
---|
220 |
snd_pcm_freebob_pollfunction(freebob); |
---|
221 |
pthread_testcancel(); |
---|
222 |
} |
---|
223 |
|
---|
224 |
} |
---|
225 |
|
---|
226 |
static int snd_pcm_freebob_poll_revents(snd_pcm_ioplug_t *io, |
---|
227 |
struct pollfd *pfds, unsigned int nfds, |
---|
228 |
unsigned short *revents) |
---|
229 |
{ |
---|
230 |
static char buf[1]; |
---|
231 |
|
---|
232 |
PRINT_FUNCTION_ENTRY; |
---|
233 |
|
---|
234 |
assert(pfds && nfds == 1 && revents); |
---|
235 |
|
---|
236 |
read(pfds[0].fd, buf, 1); |
---|
237 |
|
---|
238 |
*revents = pfds[0].revents; |
---|
239 |
return 0; |
---|
240 |
} |
---|
241 |
|
---|
242 |
static void snd_pcm_freebob_free(snd_pcm_freebob_t *freebob) |
---|
243 |
{ |
---|
244 |
PRINT_FUNCTION_ENTRY; |
---|
245 |
if (freebob) { |
---|
246 |
if(freebob->fb_handle) |
---|
247 |
freebob_destroy_handle(freebob->fb_handle ); |
---|
248 |
|
---|
249 |
close(freebob->fd); |
---|
250 |
close(freebob->io.poll_fd); |
---|
251 |
free(freebob); |
---|
252 |
} |
---|
253 |
} |
---|
254 |
|
---|
255 |
static int snd_pcm_freebob_close(snd_pcm_ioplug_t *io) |
---|
256 |
{ |
---|
257 |
PRINT_FUNCTION_ENTRY; |
---|
258 |
snd_pcm_freebob_t *freebob = io->private_data; |
---|
259 |
|
---|
260 |
freebob_streaming_finish(freebob->streaming_device); |
---|
261 |
|
---|
262 |
#ifdef FREEBOB_WITH_MIDI |
---|
263 |
snd_pcm_freebob_midi_finish(freebob->midi_handle); |
---|
264 |
#endif |
---|
265 |
|
---|
266 |
snd_pcm_freebob_free(freebob); |
---|
267 |
return 0; |
---|
268 |
} |
---|
269 |
|
---|
270 |
static snd_pcm_sframes_t snd_pcm_freebob_pointer(snd_pcm_ioplug_t *io) |
---|
271 |
{ |
---|
272 |
PRINT_FUNCTION_ENTRY; |
---|
273 |
|
---|
274 |
snd_pcm_freebob_t *freebob = io->private_data; |
---|
275 |
|
---|
276 |
return freebob->hw_ptr; |
---|
277 |
} |
---|
278 |
|
---|
279 |
static int snd_pcm_freebob_start(snd_pcm_ioplug_t *io) |
---|
280 |
{ |
---|
281 |
int result = 0; |
---|
282 |
snd_pcm_freebob_t *freebob = io->private_data; |
---|
283 |
|
---|
284 |
PRINT_FUNCTION_ENTRY; |
---|
285 |
|
---|
286 |
result = pthread_create (&freebob->thread, 0, freebob_workthread, freebob); |
---|
287 |
if(result) return result; |
---|
288 |
|
---|
289 |
return freebob_streaming_start(freebob->streaming_device); |
---|
290 |
} |
---|
291 |
|
---|
292 |
static int snd_pcm_freebob_stop(snd_pcm_ioplug_t *io) |
---|
293 |
{ |
---|
294 |
snd_pcm_freebob_t *freebob = io->private_data; |
---|
295 |
|
---|
296 |
PRINT_FUNCTION_ENTRY; |
---|
297 |
|
---|
298 |
if(pthread_cancel(freebob->thread)) { |
---|
299 |
fprintf(stderr,"could not cancel thread!\n"); |
---|
300 |
} |
---|
301 |
|
---|
302 |
if(pthread_join(freebob->thread,NULL)) { |
---|
303 |
fprintf(stderr,"could not join thread!\n"); |
---|
304 |
} |
---|
305 |
|
---|
306 |
return freebob_streaming_stop(freebob->streaming_device); |
---|
307 |
} |
---|
308 |
|
---|
309 |
static int snd_pcm_freebob_prepare(snd_pcm_ioplug_t *io) |
---|
310 |
{ |
---|
311 |
// snd_pcm_freebob_t *freebob = io->private_data; |
---|
312 |
PRINT_FUNCTION_ENTRY; |
---|
313 |
|
---|
314 |
return 0; |
---|
315 |
} |
---|
316 |
|
---|
317 |
static snd_pcm_ioplug_callback_t freebob_pcm_callback = { |
---|
318 |
.close = snd_pcm_freebob_close, |
---|
319 |
.start = snd_pcm_freebob_start, |
---|
320 |
.stop = snd_pcm_freebob_stop, |
---|
321 |
.pointer = snd_pcm_freebob_pointer, |
---|
322 |
.hw_params = snd_pcm_freebob_hw_params, |
---|
323 |
.prepare = snd_pcm_freebob_prepare, |
---|
324 |
.poll_revents = snd_pcm_freebob_poll_revents, |
---|
325 |
}; |
---|
326 |
|
---|
327 |
#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof(ary[0])) |
---|
328 |
|
---|
329 |
static int freebob_set_hw_constraint(snd_pcm_freebob_t *freebob) |
---|
330 |
{ |
---|
331 |
PRINT_FUNCTION_ENTRY; |
---|
332 |
unsigned int access_list[] = { |
---|
333 |
SND_PCM_ACCESS_MMAP_NONINTERLEAVED, |
---|
334 |
SND_PCM_ACCESS_RW_NONINTERLEAVED, |
---|
335 |
}; |
---|
336 |
|
---|
337 |
unsigned int *rate_list=NULL; |
---|
338 |
|
---|
339 |
unsigned int format = SND_PCM_FORMAT_S24; |
---|
340 |
int err; |
---|
341 |
int i; |
---|
342 |
|
---|
343 |
// FIXME: this restricts the plugin to the first discovered device! |
---|
344 |
freebob->dev_options.node_id=freebob_get_device_node_id(freebob->fb_handle, 0); |
---|
345 |
|
---|
346 |
freebob_supported_stream_format_info_t* stream_info; |
---|
347 |
|
---|
348 |
// printf(" port = %d, node_id = %d\n", freebob->dev_options.port, freebob->dev_options.node_id); |
---|
349 |
|
---|
350 |
// find the supported samplerate nb channels combinations |
---|
351 |
stream_info = freebob_get_supported_stream_format_info( freebob->fb_handle, |
---|
352 |
freebob->dev_options.node_id, |
---|
353 |
freebob->stream == SND_PCM_STREAM_PLAYBACK ? 1 : 0 ); |
---|
354 |
|
---|
355 |
// debug statement |
---|
356 |
// freebob_print_supported_stream_format_info( stream_info ); |
---|
357 |
|
---|
358 |
if(!(rate_list=calloc(stream_info->nb_formats, sizeof(unsigned int)))) { |
---|
359 |
SNDERR("Could not allocate sample rate list!\n"); |
---|
360 |
return -ENOMEM; |
---|
361 |
} |
---|
362 |
|
---|
363 |
int min_channels=99999; |
---|
364 |
int max_channels=0; |
---|
365 |
|
---|
366 |
for (i=0;i<stream_info->nb_formats;i++) { |
---|
367 |
freebob_supported_stream_format_spec_t* format_spec = stream_info->formats[i]; |
---|
368 |
|
---|
369 |
if ( format_spec ) { |
---|
370 |
if (format_spec->nb_audio_channels < min_channels) |
---|
371 |
min_channels=format_spec->nb_audio_channels; |
---|
372 |
|
---|
373 |
if (format_spec->nb_audio_channels > max_channels) |
---|
374 |
max_channels=format_spec->nb_audio_channels; |
---|
375 |
|
---|
376 |
rate_list[i]=format_spec->samplerate; |
---|
377 |
} |
---|
378 |
} |
---|
379 |
|
---|
380 |
// SNDERR("minchannels: %d \n",min_channels); |
---|
381 |
// SNDERR("maxchannels: %d \n",max_channels); |
---|
382 |
|
---|
383 |
freebob_free_supported_stream_format_info( stream_info ); |
---|
384 |
|
---|
385 |
freebob->sample_bits = snd_pcm_format_physical_width(format); |
---|
386 |
|
---|
387 |
// setup the plugin capabilities |
---|
388 |
if ((err = snd_pcm_ioplug_set_param_list(&freebob->io, SND_PCM_IOPLUG_HW_ACCESS, |
---|
389 |
ARRAY_SIZE(access_list), access_list)) < 0 || |
---|
390 |
(err = snd_pcm_ioplug_set_param_list(&freebob->io, SND_PCM_IOPLUG_HW_RATE, |
---|
391 |
ARRAY_SIZE(rate_list), rate_list)) < 0 || |
---|
392 |
(err = snd_pcm_ioplug_set_param_list(&freebob->io, SND_PCM_IOPLUG_HW_FORMAT, |
---|
393 |
1, &format)) < 0 || |
---|
394 |
(err = snd_pcm_ioplug_set_param_minmax(&freebob->io, SND_PCM_IOPLUG_HW_CHANNELS, |
---|
395 |
max_channels, max_channels)) < 0 || |
---|
396 |
(err = snd_pcm_ioplug_set_param_minmax(&freebob->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, |
---|
397 |
512*8, 512*8)) < 0 || |
---|
398 |
(err = snd_pcm_ioplug_set_param_minmax(&freebob->io, SND_PCM_IOPLUG_HW_PERIODS, |
---|
399 |
3, 64)) < 0) |
---|
400 |
return err; |
---|
401 |
|
---|
402 |
free(rate_list); |
---|
403 |
|
---|
404 |
return 0; |
---|
405 |
} |
---|
406 |
|
---|
407 |
static int snd_pcm_freebob_open(snd_pcm_t **pcmp, const char *name, |
---|
408 |
freebob_options_t *dev_options, |
---|
409 |
snd_pcm_stream_t stream, int mode) |
---|
410 |
{ |
---|
411 |
|
---|
412 |
PRINT_FUNCTION_ENTRY; |
---|
413 |
snd_pcm_freebob_t *freebob; |
---|
414 |
int err; |
---|
415 |
int fd[2]; |
---|
416 |
|
---|
417 |
printf("FreeBob plugin for ALSA\n version %s compiled %s %s\n using %s\n", |
---|
418 |
FREEBOB_PLUGIN_VERSION, __DATE__, __TIME__, freebob_get_version()); |
---|
419 |
|
---|
420 |
assert(pcmp); |
---|
421 |
assert(dev_options); |
---|
422 |
|
---|
423 |
freebob = calloc(1, sizeof(*freebob)); |
---|
424 |
if (!freebob) |
---|
425 |
return -ENOMEM; |
---|
426 |
|
---|
427 |
memcpy(&freebob->dev_options,dev_options,sizeof(freebob->dev_options)); |
---|
428 |
|
---|
429 |
freebob->stream=stream; |
---|
430 |
|
---|
431 |
// discover the devices to discover the capabilities |
---|
432 |
freebob->fb_handle = freebob_new_handle( freebob->dev_options.port ); |
---|
433 |
if ( !freebob->fb_handle ) { |
---|
434 |
SNDERR("Could not create freebob handle\n" ); |
---|
435 |
return -ENOMEM; |
---|
436 |
} |
---|
437 |
|
---|
438 |
if ( freebob_discover_devices( freebob->fb_handle, 0 ) != 0 ) { |
---|
439 |
SNDERR("Could not discover devices\n" ); |
---|
440 |
freebob_destroy_handle( freebob->fb_handle ); |
---|
441 |
return -EINVAL; |
---|
442 |
} |
---|
443 |
|
---|
444 |
freebob_device_info_t device_info; |
---|
445 |
|
---|
446 |
freebob->dev_options.directions=0; |
---|
447 |
|
---|
448 |
if(stream == SND_PCM_STREAM_PLAYBACK) { |
---|
449 |
freebob->dev_options.directions |= FREEBOB_IGNORE_CAPTURE; |
---|
450 |
} else { |
---|
451 |
freebob->dev_options.directions |= FREEBOB_IGNORE_PLAYBACK; |
---|
452 |
} |
---|
453 |
|
---|
454 |
freebob->streaming_device=freebob_streaming_init(&device_info,freebob->dev_options); |
---|
455 |
|
---|
456 |
if(!freebob->streaming_device) { |
---|
457 |
SNDERR("FREEBOB: Error creating virtual device\n"); |
---|
458 |
freebob_destroy_handle( freebob->fb_handle ); |
---|
459 |
return -EINVAL; |
---|
460 |
} |
---|
461 |
|
---|
462 |
#ifdef FREEBOB_WITH_MIDI |
---|
463 |
freebob->midi_handle=snd_pcm_freebob_midi_init(freebob); |
---|
464 |
if(!freebob->midi_handle) { |
---|
465 |
SNDERR("FREEBOB: Error creating midi device"); |
---|
466 |
freebob_destroy_handle( freebob->fb_handle ); |
---|
467 |
return -EINVAL; |
---|
468 |
} |
---|
469 |
#endif |
---|
470 |
|
---|
471 |
socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); |
---|
472 |
|
---|
473 |
freebob->fd = fd[0]; |
---|
474 |
|
---|
475 |
freebob->io.version = SND_PCM_IOPLUG_VERSION; |
---|
476 |
freebob->io.name = "FreeBob PCM Plugin"; |
---|
477 |
freebob->io.callback = &freebob_pcm_callback; |
---|
478 |
freebob->io.private_data = freebob; |
---|
479 |
freebob->io.mmap_rw = 1; |
---|
480 |
freebob->io.poll_fd = fd[1]; |
---|
481 |
freebob->io.poll_events = stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN; |
---|
482 |
|
---|
483 |
err = snd_pcm_ioplug_create(&freebob->io, name, stream, mode); |
---|
484 |
if (err < 0) { |
---|
485 |
snd_pcm_freebob_free(freebob); |
---|
486 |
return err; |
---|
487 |
} |
---|
488 |
|
---|
489 |
err = freebob_set_hw_constraint(freebob); |
---|
490 |
if (err < 0) { |
---|
491 |
snd_pcm_ioplug_delete(&freebob->io); |
---|
492 |
return err; |
---|
493 |
} |
---|
494 |
|
---|
495 |
*pcmp = freebob->io.pcm; |
---|
496 |
|
---|
497 |
return 0; |
---|
498 |
} |
---|
499 |
|
---|
500 |
SND_PCM_PLUGIN_DEFINE_FUNC(freebob) |
---|
501 |
{ |
---|
502 |
snd_config_iterator_t i, next; |
---|
503 |
int err; |
---|
504 |
long tmp_int; |
---|
505 |
|
---|
506 |
freebob_options_t dev_options; |
---|
507 |
|
---|
508 |
dev_options.node_id=0; |
---|
509 |
dev_options.port=0; |
---|
510 |
|
---|
511 |
dev_options.sample_rate=44100; |
---|
512 |
dev_options.period_size=512; |
---|
513 |
dev_options.nb_buffers=3; |
---|
514 |
|
---|
515 |
/* packetizer thread options */ |
---|
516 |
|
---|
517 |
dev_options.realtime=FREEBOB_USE_RT; |
---|
518 |
dev_options.packetizer_priority=FREEBOB_RT_PRIORITY_PACKETIZER; |
---|
519 |
|
---|
520 |
snd_config_for_each(i, next, conf) { |
---|
521 |
snd_config_t *n = snd_config_iterator_entry(i); |
---|
522 |
const char *id; |
---|
523 |
if (snd_config_get_id(n, &id) < 0) |
---|
524 |
continue; |
---|
525 |
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0) |
---|
526 |
continue; |
---|
527 |
if (strcmp(id, "port") == 0) { |
---|
528 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { |
---|
529 |
SNDERR("Invalid type for %s", id); |
---|
530 |
return -EINVAL; |
---|
531 |
} |
---|
532 |
if (snd_config_get_integer(n,&tmp_int)) { |
---|
533 |
SNDERR("Could not get value for %s", id); |
---|
534 |
return -EINVAL; |
---|
535 |
} |
---|
536 |
dev_options.port=tmp_int; |
---|
537 |
continue; |
---|
538 |
} |
---|
539 |
if (strcmp(id, "node") == 0) { |
---|
540 |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_INTEGER) { |
---|
541 |
SNDERR("Invalid type for %s", id); |
---|
542 |
return -EINVAL; |
---|
543 |
} |
---|
544 |
if (snd_config_get_integer(n,&tmp_int)) { |
---|
545 |
SNDERR("Could not get value for %s", id); |
---|
546 |
return -EINVAL; |
---|
547 |
} |
---|
548 |
dev_options.node_id=tmp_int; |
---|
549 |
continue; |
---|
550 |
} |
---|
551 |
|
---|
552 |
SNDERR("Unknown field %s", id); |
---|
553 |
return -EINVAL; |
---|
554 |
} |
---|
555 |
|
---|
556 |
err = snd_pcm_freebob_open(pcmp, name, &dev_options, stream, mode); |
---|
557 |
|
---|
558 |
return err; |
---|
559 |
|
---|
560 |
} |
---|
561 |
|
---|
562 |
SND_PCM_PLUGIN_SYMBOL(freebob); |
---|
563 |
|
---|
564 |
// midi stuff |
---|
565 |
#ifdef FREEBOB_WITH_MIDI |
---|
566 |
/* |
---|
567 |
* MIDI support |
---|
568 |
*/ |
---|
569 |
|
---|
570 |
// the thread that will queue the midi events from the seq to the stream buffers |
---|
571 |
|
---|
572 |
void * snd_pcm_freebob_midi_queue_thread(void *arg) |
---|
573 |
{ |
---|
574 |
snd_pcm_freebob_midi_handle_t *m=(snd_pcm_freebob_midi_handle_t *)arg; |
---|
575 |
assert(m); |
---|
576 |
snd_seq_event_t *ev; |
---|
577 |
unsigned char work_buffer[MIDI_TRANSMIT_BUFFER_SIZE]; |
---|
578 |
int bytes_to_send; |
---|
579 |
int b; |
---|
580 |
int i; |
---|
581 |
|
---|
582 |
SNDERR ("FREEBOB: MIDI queue thread started"); |
---|
583 |
|
---|
584 |
while(1) { |
---|
585 |
// get next event, if one is present |
---|
586 |
while ((snd_seq_event_input(m->seq_handle, &ev) > 0)) { |
---|
587 |
// get the port this event is originated from |
---|
588 |
freebob_midi_port_t *port=NULL; |
---|
589 |
for (i=0;i<m->nb_output_ports;i++) { |
---|
590 |
if(m->output_ports[i]->seq_port_nr == ev->dest.port) { |
---|
591 |
port=m->output_ports[i]; |
---|
592 |
break; |
---|
593 |
} |
---|
594 |
} |
---|
595 |
|
---|
596 |
if(!port) { |
---|
597 |
SNDERR ("FREEBOB: Could not find target port for event: dst=%d src=%d\n", ev->dest.port, ev->source.port); |
---|
598 |
|
---|
599 |
break; |
---|
600 |
} |
---|
601 |
|
---|
602 |
// decode it to the work buffer |
---|
603 |
if((bytes_to_send = snd_midi_event_decode ( port->parser, |
---|
604 |
work_buffer, |
---|
605 |
MIDI_TRANSMIT_BUFFER_SIZE, |
---|
606 |
ev))<0) |
---|
607 |
{ // failed |
---|
608 |
SNDERR ("FREEBOB: Error decoding event for port %d (errcode=%d)\n", port->seq_port_nr,bytes_to_send); |
---|
609 |
bytes_to_send=0; |
---|
610 |
//return -1; |
---|
611 |
} |
---|
612 |
|
---|
613 |
for(b=0;b<bytes_to_send;b++) { |
---|
614 |
freebob_sample_t tmp_event=work_buffer[b]; |
---|
615 |
if(freebob_streaming_write(m->dev, port->stream_nr, &tmp_event, 1)<1) { |
---|
616 |
SNDERR ("FREEBOB: Midi send buffer overrun\n"); |
---|
617 |
} |
---|
618 |
} |
---|
619 |
|
---|
620 |
} |
---|
621 |
|
---|
622 |
// sleep for some time |
---|
623 |
usleep(MIDI_THREAD_SLEEP_TIME_USECS); |
---|
624 |
} |
---|
625 |
return NULL; |
---|
626 |
} |
---|
627 |
|
---|
628 |
// the dequeue thread (maybe we need one thread per stream) |
---|
629 |
void *snd_pcm_freebob_midi_dequeue_thread (void *arg) { |
---|
630 |
snd_pcm_freebob_midi_handle_t *m=(snd_pcm_freebob_midi_handle_t *)arg; |
---|
631 |
|
---|
632 |
int i; |
---|
633 |
int s; |
---|
634 |
|
---|
635 |
int samples_read; |
---|
636 |
|
---|
637 |
assert(m); |
---|
638 |
|
---|
639 |
while(1) { |
---|
640 |
// read incoming events |
---|
641 |
|
---|
642 |
for (i=0;i<m->nb_input_ports;i++) { |
---|
643 |
unsigned int buff[64]; |
---|
644 |
|
---|
645 |
freebob_midi_port_t *port=m->input_ports[i]; |
---|
646 |
|
---|
647 |
if(!port) { |
---|
648 |
SNDERR("FREEBOB: something went wrong when setting up the midi input port map (%d)",i); |
---|
649 |
} |
---|
650 |
|
---|
651 |
do { |
---|
652 |
samples_read=freebob_streaming_read(m->dev, port->stream_nr, buff, 64); |
---|
653 |
|
---|
654 |
for (s=0;s<samples_read;s++) { |
---|
655 |
unsigned int *byte=(buff+s) ; |
---|
656 |
snd_seq_event_t ev; |
---|
657 |
if ((snd_midi_event_encode_byte(port->parser,(*byte) & 0xFF, &ev)) > 0) { |
---|
658 |
// a midi message is complete, send it out to ALSA |
---|
659 |
snd_seq_ev_set_subs(&ev); |
---|
660 |
snd_seq_ev_set_direct(&ev); |
---|
661 |
snd_seq_ev_set_source(&ev, port->seq_port_nr); |
---|
662 |
snd_seq_event_output_direct(port->seq_handle, &ev); |
---|
663 |
} |
---|
664 |
} |
---|
665 |
} while (samples_read>0); |
---|
666 |
} |
---|
667 |
|
---|
668 |
// sleep for some time |
---|
669 |
usleep(MIDI_THREAD_SLEEP_TIME_USECS); |
---|
670 |
} |
---|
671 |
return NULL; |
---|
672 |
} |
---|
673 |
|
---|
674 |
static snd_pcm_freebob_midi_handle_t *snd_pcm_freebob_midi_init(snd_pcm_freebob_t *freebob) { |
---|
675 |
// int err; |
---|
676 |
|
---|
677 |
char buf[256]; |
---|
678 |
int chn; |
---|
679 |
int nchannels; |
---|
680 |
int i=0; |
---|
681 |
|
---|
682 |
freebob_device_t *dev=freebob->streaming_device; |
---|
683 |
|
---|
684 |
assert(dev); |
---|
685 |
|
---|
686 |
snd_pcm_freebob_midi_handle_t *m=calloc(1,sizeof(snd_pcm_freebob_midi_handle_t)); |
---|
687 |
if (!m) { |
---|
688 |
SNDERR("FREEBOB: not enough memory to create midi structure"); |
---|
689 |
return NULL; |
---|
690 |
} |
---|
691 |
|
---|
692 |
if (snd_seq_open(&m->seq_handle, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK) < 0) { |
---|
693 |
SNDERR("FREEBOB: Error opening ALSA sequencer."); |
---|
694 |
free(m); |
---|
695 |
return NULL; |
---|
696 |
} |
---|
697 |
|
---|
698 |
snd_seq_set_client_name(m->seq_handle, "FreeBob MIDI"); |
---|
699 |
|
---|
700 |
// find out the number of midi in/out ports we need to setup |
---|
701 |
nchannels=freebob_streaming_get_nb_capture_streams(dev); |
---|
702 |
|
---|
703 |
m->nb_input_ports=0; |
---|
704 |
|
---|
705 |
for (chn = 0; chn < nchannels; chn++) { |
---|
706 |
if(freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
707 |
m->nb_input_ports++; |
---|
708 |
} |
---|
709 |
} |
---|
710 |
|
---|
711 |
m->input_ports=calloc(m->nb_input_ports,sizeof(freebob_midi_port_t *)); |
---|
712 |
if(!m->input_ports) { |
---|
713 |
SNDERR("FREEBOB: not enough memory to create midi structure"); |
---|
714 |
free(m); |
---|
715 |
return NULL; |
---|
716 |
} |
---|
717 |
|
---|
718 |
i=0; |
---|
719 |
for (chn = 0; chn < nchannels; chn++) { |
---|
720 |
if(freebob_streaming_get_capture_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
721 |
m->input_ports[i]=calloc(1,sizeof(freebob_midi_port_t)); |
---|
722 |
if(!m->input_ports[i]) { |
---|
723 |
// fixme |
---|
724 |
SNDERR("FREEBOB: Could not allocate memory for seq port"); |
---|
725 |
continue; |
---|
726 |
} |
---|
727 |
|
---|
728 |
freebob_streaming_get_capture_stream_name(dev, chn, buf, sizeof(buf) - 1); |
---|
729 |
SNDERR("FREEBOB: Register MIDI IN port %s\n", buf); |
---|
730 |
|
---|
731 |
m->input_ports[i]->seq_port_nr=snd_seq_create_simple_port(m->seq_handle, buf, |
---|
732 |
SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, |
---|
733 |
SND_SEQ_PORT_TYPE_MIDI_GENERIC); |
---|
734 |
|
---|
735 |
if(m->input_ports[i]->seq_port_nr<0) { |
---|
736 |
SNDERR("FREEBOB: Could not create seq port"); |
---|
737 |
m->input_ports[i]->stream_nr=-1; |
---|
738 |
m->input_ports[i]->seq_port_nr=-1; |
---|
739 |
} else { |
---|
740 |
m->input_ports[i]->stream_nr=chn; |
---|
741 |
m->input_ports[i]->seq_handle=m->seq_handle; |
---|
742 |
if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->input_ports[i]->parser)) < 0) { |
---|
743 |
fprintf(stderr, "FREEBOB: could not init parser for MIDI IN port %d\n",i); |
---|
744 |
m->input_ports[i]->stream_nr=-1; |
---|
745 |
m->input_ports[i]->seq_port_nr=-1; |
---|
746 |
} |
---|
747 |
} |
---|
748 |
|
---|
749 |
i++; |
---|
750 |
} |
---|
751 |
} |
---|
752 |
|
---|
753 |
// playback |
---|
754 |
nchannels=freebob_streaming_get_nb_playback_streams(dev); |
---|
755 |
|
---|
756 |
m->nb_output_ports=0; |
---|
757 |
|
---|
758 |
for (chn = 0; chn < nchannels; chn++) { |
---|
759 |
if(freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
760 |
m->nb_output_ports++; |
---|
761 |
} |
---|
762 |
} |
---|
763 |
|
---|
764 |
m->output_ports=calloc(m->nb_output_ports,sizeof(freebob_midi_port_t *)); |
---|
765 |
if(!m->output_ports) { |
---|
766 |
SNDERR("FREEBOB: not enough memory to create midi structure"); |
---|
767 |
for (i = 0; i < m->nb_input_ports; i++) { |
---|
768 |
free(m->input_ports[i]); |
---|
769 |
} |
---|
770 |
free(m->input_ports); |
---|
771 |
free(m); |
---|
772 |
return NULL; |
---|
773 |
} |
---|
774 |
|
---|
775 |
i=0; |
---|
776 |
for (chn = 0; chn < nchannels; chn++) { |
---|
777 |
if(freebob_streaming_get_playback_stream_type(dev, chn) == freebob_stream_type_midi) { |
---|
778 |
m->output_ports[i]=calloc(1,sizeof(freebob_midi_port_t)); |
---|
779 |
if(!m->output_ports[i]) { |
---|
780 |
// fixme |
---|
781 |
SNDERR("FREEBOB: Could not allocate memory for seq port"); |
---|
782 |
continue; |
---|
783 |
} |
---|
784 |
|
---|
785 |
freebob_streaming_get_playback_stream_name(dev, chn, buf, sizeof(buf) - 1); |
---|
786 |
SNDERR("FREEBOB: Register MIDI OUT port %s\n", buf); |
---|
787 |
|
---|
788 |
m->output_ports[i]->seq_port_nr=snd_seq_create_simple_port(m->seq_handle, buf, |
---|
789 |
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, |
---|
790 |
SND_SEQ_PORT_TYPE_MIDI_GENERIC); |
---|
791 |
|
---|
792 |
|
---|
793 |
if(m->output_ports[i]->seq_port_nr<0) { |
---|
794 |
SNDERR("FREEBOB: Could not create seq port"); |
---|
795 |
m->output_ports[i]->stream_nr=-1; |
---|
796 |
m->output_ports[i]->seq_port_nr=-1; |
---|
797 |
} else { |
---|
798 |
m->output_ports[i]->stream_nr=chn; |
---|
799 |
m->output_ports[i]->seq_handle=m->seq_handle; |
---|
800 |
if (snd_midi_event_new ( ALSA_SEQ_BUFF_SIZE, &(m->output_ports[i]->parser)) < 0) { |
---|
801 |
fprintf(stderr, "FREEBOB: could not init parser for MIDI OUT port %d\n",i); |
---|
802 |
m->output_ports[i]->stream_nr=-1; |
---|
803 |
m->output_ports[i]->seq_port_nr=-1; |
---|
804 |
} |
---|
805 |
} |
---|
806 |
|
---|
807 |
i++; |
---|
808 |
} |
---|
809 |
} |
---|
810 |
|
---|
811 |
|
---|
812 |
// start threads |
---|
813 |
m->dev=dev; |
---|
814 |
|
---|
815 |
m->queue_thread_priority=FREEBOB_RT_PRIORITY_MIDI; |
---|
816 |
m->queue_thread_realtime=FREEBOB_USE_RT; |
---|
817 |
|
---|
818 |
if (pthread_create (&m->queue_thread, 0, snd_pcm_freebob_midi_queue_thread, (void *)m)) { |
---|
819 |
SNDERR("FREEBOB: cannot create midi queueing thread"); |
---|
820 |
free(m); |
---|
821 |
return NULL; |
---|
822 |
} |
---|
823 |
|
---|
824 |
if (pthread_create (&m->dequeue_thread, 0, snd_pcm_freebob_midi_dequeue_thread, (void *)m)) { |
---|
825 |
SNDERR("FREEBOB: cannot create midi dequeueing thread"); |
---|
826 |
free(m); |
---|
827 |
return NULL; |
---|
828 |
} |
---|
829 |
return m; |
---|
830 |
} |
---|
831 |
|
---|
832 |
static void |
---|
833 |
snd_pcm_freebob_midi_finish (snd_pcm_freebob_midi_handle_t *m) |
---|
834 |
{ |
---|
835 |
assert(m); |
---|
836 |
|
---|
837 |
int i; |
---|
838 |
|
---|
839 |
pthread_cancel (m->queue_thread); |
---|
840 |
pthread_join (m->queue_thread, NULL); |
---|
841 |
|
---|
842 |
pthread_cancel (m->dequeue_thread); |
---|
843 |
pthread_join (m->dequeue_thread, NULL); |
---|
844 |
|
---|
845 |
|
---|
846 |
for (i=0;i<m->nb_input_ports;i++) { |
---|
847 |
free(m->input_ports[i]); |
---|
848 |
|
---|
849 |
} |
---|
850 |
free(m->input_ports); |
---|
851 |
|
---|
852 |
for (i=0;i<m->nb_output_ports;i++) { |
---|
853 |
free(m->output_ports[i]); |
---|
854 |
} |
---|
855 |
free(m->output_ports); |
---|
856 |
|
---|
857 |
free(m); |
---|
858 |
} |
---|
859 |
#endif |
---|