root/trunk/alsa-plugin/freebob/pcm_freebob.c

Revision 193, 22.0 kB (checked in by pieterpalmers, 18 years ago)

minor cleanups

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
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 
Note: See TracBrowser for help on using the browser.