root/trunk/libffado/tests/streaming/teststreaming-ipc.cpp

Revision 1240, 21.3 kB (checked in by ppalmers, 16 years ago)

fix bugs in IPC comms. Add preliminary FFADO-IPC ALSA plugin

Line 
1 /*
2  * Copyright (C) 2005-2008 by Pieter Palmers
3  *
4  * This file is part of FFADO
5  * FFADO = Free Firewire (pro-)audio drivers for linux
6  *
7  * FFADO is based upon FreeBoB.
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 2 of the License, or
12  * (at your option) version 3 of the License.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  */
23
24
25 /**
26  * Test application for the IPC audio server
27  */
28
29 #include "config.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <signal.h>
36 #include <sched.h>
37
38 #include "libffado/ffado.h"
39
40 #include "debugmodule/debugmodule.h"
41
42 #include "libutil/IpcRingBuffer.h"
43 #include "libutil/SystemTimeSource.h"
44
45 #include <math.h>
46 #include <argp.h>
47
48 int run;
49
50 DECLARE_GLOBAL_DEBUG_MODULE;
51
52 using namespace Util;
53
54 // Program documentation.
55 // Program documentation.
56 static char doc[] = "FFADO -- a driver for Firewire Audio devices (IPC streaming test application)\n\n"
57                     ;
58
59 // A description of the arguments we accept.
60 static char args_doc[] = "";
61
62 struct arguments
63 {
64     long int verbose;
65     long int test_tone;
66     long int test_tone_freq;
67     long int period;
68     long int slave_mode;
69     long int snoop_mode;
70     long int nb_buffers;
71     long int sample_rate;
72     long int rtprio;
73     long int audio_buffer_type;
74     long int playback;
75     long int capture;
76     char* args[2];
77    
78 };
79
80 // The options we understand.
81 static struct argp_option options[] = {
82     {"verbose",  'v', "level",    0,  "Verbose level" },
83     {"rtprio",  'P', "prio",  0,  "Realtime priority (0 = no RT scheduling)" },
84     {"test-tone",  't', "bool",  0,  "Output test sine" },
85     {"test-tone-freq",  'f', "hz",  0,  "Test sine frequency" },
86     {"samplerate",  'r', "hz",  0,  "Sample rate" },
87     {"period",  'p', "frames",  0,  "Period (buffer) size" },
88     {"nb_buffers",  'n', "nb",  0,  "Nb buffers (periods)" },
89     {"slave_mode",  's', "bool",  0,  "Run in slave mode" },
90     {"snoop_mode",  'S', "bool",  0,  "Run in snoop mode" },
91     {"audio_buffer_type",  'b', "",  0,  "Datatype of audio buffers (0=float, 1=int24)" },
92     {"playback",  'o', "",  0,  "Number of playback channels" },
93     {"capture",  'i', "",  0,  "Number of capture channels" },
94     { 0 }
95 };
96
97 //-------------------------------------------------------------
98
99 // Parse a single option.
100 static error_t
101 parse_opt( int key, char* arg, struct argp_state* state )
102 {
103     // Get the input argument from `argp_parse', which we
104     // know is a pointer to our arguments structure.
105     struct arguments* arguments = ( struct arguments* ) state->input;
106     char* tail;
107
108     errno = 0;
109     switch (key) {
110     case 'v':
111         if (arg) {
112             arguments->verbose = strtol( arg, &tail, 0 );
113             if ( errno ) {
114                 fprintf( stderr,  "Could not parse 'verbose' argument\n" );
115                 return ARGP_ERR_UNKNOWN;
116             }
117         }
118         break;
119     case 'P':
120         if (arg) {
121             arguments->rtprio = strtol( arg, &tail, 0 );
122             if ( errno ) {
123                 fprintf( stderr,  "Could not parse 'rtprio' argument\n" );
124                 return ARGP_ERR_UNKNOWN;
125             }
126         }
127         break;
128     case 'p':
129         if (arg) {
130             arguments->period = strtol( arg, &tail, 0 );
131             if ( errno ) {
132                 fprintf( stderr,  "Could not parse 'period' argument\n" );
133                 return ARGP_ERR_UNKNOWN;
134             }
135         }
136         break;
137     case 'n':
138         if (arg) {
139             arguments->nb_buffers = strtol( arg, &tail, 0 );
140             if ( errno ) {
141                 fprintf( stderr,  "Could not parse 'nb_buffers' argument\n" );
142                 return ARGP_ERR_UNKNOWN;
143             }
144         }
145         break;
146     case 'r':
147         if (arg) {
148             arguments->sample_rate = strtol( arg, &tail, 0 );
149             if ( errno ) {
150                 fprintf( stderr,  "Could not parse 'samplerate' argument\n" );
151                 return ARGP_ERR_UNKNOWN;
152             }
153         }
154         break;
155     case 't':
156         if (arg) {
157             arguments->test_tone = strtol( arg, &tail, 0 );
158             if ( errno ) {
159                 fprintf( stderr,  "Could not parse 'test-tone' argument\n" );
160                 return ARGP_ERR_UNKNOWN;
161             }
162         }
163         break;
164     case 'f':
165         if (arg) {
166             arguments->test_tone_freq = strtol( arg, &tail, 0 );
167             if ( errno ) {
168                 fprintf( stderr,  "Could not parse 'test-tone-freq' argument\n" );
169                 return ARGP_ERR_UNKNOWN;
170             }
171         }
172         break;
173     case 'b':
174         if (arg) {
175             arguments->audio_buffer_type = strtol( arg, &tail, 0 );
176             if ( errno ) {
177                 fprintf( stderr,  "Could not parse 'audio-buffer-type' argument\n" );
178                 return ARGP_ERR_UNKNOWN;
179             }
180         }
181         break;
182     case 'i':
183         if (arg) {
184             arguments->capture = strtol( arg, &tail, 0 );
185             if ( errno ) {
186                 fprintf( stderr,  "Could not parse 'capture' argument\n" );
187                 return ARGP_ERR_UNKNOWN;
188             }
189         }
190         break;
191     case 'o':
192         if (arg) {
193             arguments->playback = strtol( arg, &tail, 0 );
194             if ( errno ) {
195                 fprintf( stderr,  "Could not parse 'playback' argument\n" );
196                 return ARGP_ERR_UNKNOWN;
197             }
198         }
199         break;
200     case 's':
201         if (arg) {
202             arguments->slave_mode = strtol( arg, &tail, 0 );
203             if ( errno ) {
204                 fprintf( stderr,  "Could not parse 'slave_mode' argument\n" );
205                 return ARGP_ERR_UNKNOWN;
206             }
207         }
208         break;
209     case 'S':
210         if (arg) {
211             arguments->snoop_mode = strtol( arg, &tail, 0 );
212             if ( errno ) {
213                 fprintf( stderr,  "Could not parse 'snoop_mode' argument\n" );
214                 return ARGP_ERR_UNKNOWN;
215             }
216         }
217         break;
218     case ARGP_KEY_ARG:
219         break;
220     case ARGP_KEY_END:
221         break;
222     default:
223         return ARGP_ERR_UNKNOWN;
224     }
225     return 0;
226 }
227
228 // Our argp parser.
229 static struct argp argp = { options, parse_opt, args_doc, doc };
230
231 int set_realtime_priority(unsigned int prio)
232 {
233     debugOutput(DEBUG_LEVEL_NORMAL, "Setting thread prio to %u\n", prio);
234     if (prio > 0) {
235         struct sched_param schp;
236         /*
237         * set the process to realtime privs
238         */
239         memset(&schp, 0, sizeof(schp));
240         schp.sched_priority = prio;
241        
242         if (sched_setscheduler(0, SCHED_FIFO, &schp) != 0) {
243             perror("sched_setscheduler");
244             return -1;
245         }
246   } else {
247         struct sched_param schp;
248         /*
249         * set the process to realtime privs
250         */
251         memset(&schp, 0, sizeof(schp));
252         schp.sched_priority = 0;
253        
254         if (sched_setscheduler(0, SCHED_OTHER, &schp) != 0) {
255             perror("sched_setscheduler");
256             return -1;
257         }
258   }
259   return 0;
260 }
261
262 static void sighandler (int sig)
263 {
264     run = 0;
265 }
266
267 int main(int argc, char *argv[])
268 {
269
270     struct arguments arguments;
271
272     // Default values.
273     arguments.verbose           = 6;
274     arguments.period            = 1024;
275     arguments.slave_mode        = 0;
276     arguments.snoop_mode        = 0;
277     arguments.nb_buffers        = 3;
278     arguments.sample_rate       = 44100;
279     arguments.rtprio            = 0;
280     arguments.audio_buffer_type = 1;
281     arguments.playback          = 0;
282     arguments.capture           = 0;
283
284     // Parse our arguments; every option seen by `parse_opt' will
285     // be reflected in `arguments'.
286     if ( argp_parse ( &argp, argc, argv, 0, 0, &arguments ) ) {
287         debugError("Could not parse command line\n" );
288         return -1;
289     }
290
291     debugOutput(DEBUG_LEVEL_NORMAL, "verbose level = %d\n", arguments.verbose);
292     setDebugLevel(arguments.verbose);
293
294     if(arguments.playback == 0 && arguments.capture == 0) {
295         debugError("No playback nor capture channels requested\n");
296         return -1;
297     }
298
299     int nb_in_channels=0, nb_out_channels=0;
300     int i=0;
301     int start_flag = 0;
302
303     int nb_periods=0;
304
305     uint32_t **audiobuffers_in = NULL;
306     uint32_t **audiobuffers_out = NULL;
307     float *nullbuffer = NULL;
308
309     run=1;
310
311     debugOutput(DEBUG_LEVEL_NORMAL, "FFADO streaming test application (3)\n");
312     printMessage(" period %d, nb_buffers %d, playback %d, capture %d\n",
313                  arguments.period, arguments.nb_buffers,
314                  arguments.playback,
315                  arguments.capture );
316
317     signal (SIGINT, sighandler);
318     signal (SIGPIPE, sighandler);
319
320     ffado_device_info_t device_info;
321     memset(&device_info,0,sizeof(ffado_device_info_t));
322
323     ffado_options_t dev_options;
324     memset(&dev_options,0,sizeof(ffado_options_t));
325
326     dev_options.sample_rate = arguments.sample_rate;
327     dev_options.period_size = arguments.period;
328
329     dev_options.nb_buffers = arguments.nb_buffers;
330
331     dev_options.realtime = (arguments.rtprio != 0);
332     dev_options.packetizer_priority = arguments.rtprio;
333    
334     dev_options.verbose = arguments.verbose;
335        
336     dev_options.slave_mode = arguments.slave_mode;
337     dev_options.snoop_mode = arguments.snoop_mode;
338
339     ffado_device_t *dev=ffado_streaming_init(device_info, dev_options);
340
341     if (!dev) {
342         debugError("Could not init Ffado Streaming layer\n");
343         exit(-1);
344     }
345     if (arguments.audio_buffer_type == 0) {
346         ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_float);
347     } else {
348         ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_int24);
349     }
350
351     int nb_in_channels_all = ffado_streaming_get_nb_capture_streams(dev);
352     int nb_out_channels_all = ffado_streaming_get_nb_playback_streams(dev);
353
354     // only do audio for now
355     for (i=0; i < nb_in_channels_all; i++) {
356         switch (ffado_streaming_get_capture_stream_type(dev,i)) {
357             case ffado_stream_type_audio:
358                 nb_in_channels++;
359                 break;
360             case ffado_stream_type_midi:
361             default:
362                 break;
363         }
364     }
365     for (i=0; i < nb_out_channels_all; i++) {
366         switch (ffado_streaming_get_playback_stream_type(dev,i)) {
367             case ffado_stream_type_audio:
368                 nb_out_channels++;
369                 break;
370             case ffado_stream_type_midi:
371             default:
372                 break;
373         }
374     }
375
376     printMessage("Device channel count: %d capture, %d playback\n",
377                  nb_in_channels, nb_out_channels);
378     printMessage("Requested channel count: %d capture, %d playback\n",
379                  arguments.capture, arguments.playback);
380
381     if(arguments.playback > nb_out_channels) {
382         debugError("Too many playback channels requested (want: %d, have:%d)\n",
383                    arguments.playback, nb_out_channels);
384         return -1;
385     }
386     if(arguments.capture > nb_in_channels) {
387         debugError("Too many capture channels requested (want: %d, have:%d)\n",
388                    arguments.capture, nb_in_channels);
389         return -1;
390     }
391
392     printMessage("Buffer size: %d capture, %d playback\n",
393                   nb_in_channels*dev_options.period_size * 4,
394                   nb_out_channels*dev_options.period_size * 4);
395
396     // allocate the IPC structures
397     IpcRingBuffer* capturebuffer = NULL;
398     IpcRingBuffer* playbackbuffer = NULL;
399     if(arguments.capture) {
400         // 4 bytes per channel per sample
401         capturebuffer = new IpcRingBuffer("capturebuffer",
402                                 IpcRingBuffer::eBT_Master,
403                                 IpcRingBuffer::eD_Outward,
404                                 IpcRingBuffer::eB_NonBlocking,
405                                 arguments.nb_buffers,
406                                 dev_options.period_size * arguments.capture * 4);
407         if(capturebuffer == NULL) {
408             debugError("Could not create capture IPC buffer\n");
409             exit(-1);
410         }
411    
412         capturebuffer->setVerboseLevel(arguments.verbose);
413    
414         if(!capturebuffer->init()) {
415             debugError("Could not init capture buffer\n");
416             delete capturebuffer;
417             exit(-1);
418         }
419         // indexes to the memory locations where the frames should be put
420         audiobuffers_in = (uint32_t **)calloc(arguments.capture, sizeof(uint32_t *));
421     }
422
423     if(arguments.playback) {
424         // 4 bytes per channel per sample
425         playbackbuffer = new IpcRingBuffer("playbackbuffer",
426                                 IpcRingBuffer::eBT_Master,
427                                 IpcRingBuffer::eD_Inward,
428                                 IpcRingBuffer::eB_NonBlocking,
429                                 arguments.nb_buffers,
430                                 dev_options.period_size * arguments.playback * 4);
431         if(playbackbuffer == NULL) {
432             debugError("Could not create playback IPC buffer\n");
433             exit(-1);
434         }
435    
436         playbackbuffer->setVerboseLevel(arguments.verbose);
437    
438         if(!playbackbuffer->init()) {
439             debugError("Could not init playback buffer\n");
440             delete capturebuffer;
441             delete playbackbuffer;
442             exit(-1);
443         }
444         // indexes to the memory locations where the frames should be get
445         audiobuffers_out = (uint32_t **)calloc(arguments.playback, sizeof(uint32_t *));
446     }
447
448     // this serves in case we miss a cycle, or a channel is disabled
449     nullbuffer = (float *)calloc(arguments.period, sizeof(float));
450
451     // give us RT prio
452     set_realtime_priority(arguments.rtprio);
453
454     // start the streaming layer
455     if (ffado_streaming_prepare(dev)) {
456         debugFatal("Could not prepare streaming system\n");
457         ffado_streaming_finish(dev);
458         return -1;
459     }
460     start_flag = ffado_streaming_start(dev);
461
462     // enter the loop
463     debugOutput(DEBUG_LEVEL_NORMAL,
464                 "Entering receive loop (IN: %d, OUT: %d)\n",
465                 arguments.capture, arguments.playback);
466
467     while(run && start_flag==0) {
468         bool need_silent;
469         enum IpcRingBuffer::eResult msg_res;
470
471         ffado_wait_response response;
472         response = ffado_streaming_wait(dev);
473         if (response == ffado_wait_xrun) {
474             debugOutput(DEBUG_LEVEL_NORMAL, "Xrun\n");
475             ffado_streaming_reset(dev);
476             continue;
477         } else if (response == ffado_wait_error) {
478             debugError("fatal xrun\n");
479             break;
480         }
481
482         // get a block pointer from the IPC buffer to write
483         if(arguments.capture) {
484             uint32_t *audiobuffers_raw;
485             msg_res = capturebuffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo
486             if(msg_res == IpcRingBuffer::eR_OK) {
487                 // if we got a valid pointer, setup the stream pointers
488                 for (i=0; i < nb_in_channels; i++) {
489                     if(i < arguments.capture) {
490                         audiobuffers_in[i] = audiobuffers_raw + i*dev_options.period_size;
491                         switch (ffado_streaming_get_capture_stream_type(dev,i)) {
492                             case ffado_stream_type_audio:
493                                 /* assign the audiobuffer to the stream */
494                                 ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i]));
495                                 ffado_streaming_capture_stream_onoff(dev, i, 1);
496                                 break;
497                                 // this is done with read/write routines because the nb of bytes can differ.
498                             case ffado_stream_type_midi:
499                                 ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i]));
500                                 ffado_streaming_capture_stream_onoff(dev, i, 1);
501                             default:
502                                 break;
503                         }
504                     } else {
505                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
506                         ffado_streaming_capture_stream_onoff(dev, i, 0);
507                     }
508                 }
509                 need_silent = false;
510             } else  {
511                 need_silent = true;
512                 debugOutput(DEBUG_LEVEL_NORMAL, "CAP: missed period %d\n", nb_periods);
513             }
514         } else {
515             need_silent=true;
516         }
517         if(need_silent) {
518             // if not, use the null buffer
519             for (i=0; i < nb_in_channels; i++) {
520                 switch (ffado_streaming_get_capture_stream_type(dev,i)) {
521                     case ffado_stream_type_audio:
522                         /* assign the audiobuffer to the stream */
523                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
524                         ffado_streaming_capture_stream_onoff(dev, i, 0);
525                         break;
526                         // this is done with read/write routines because the nb of bytes can differ.
527                     case ffado_stream_type_midi:
528                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
529                         ffado_streaming_capture_stream_onoff(dev, i, 0);
530                     default:
531                         break;
532                 }
533             }
534         }
535
536         // transfer
537         ffado_streaming_transfer_capture_buffers(dev);
538
539         if(capturebuffer && !need_silent && msg_res == IpcRingBuffer::eR_OK) {
540             // if we had a good block, release it
541             // FIXME: we should check for errors here
542             capturebuffer->releaseBlockForWrite();
543         }
544
545         if(arguments.playback) {
546             uint32_t *audiobuffers_raw;
547             // get a block pointer from the IPC buffer to read
548             msg_res = playbackbuffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo
549             if(msg_res == IpcRingBuffer::eR_OK) {
550                 // if we got a valid pointer, setup the stream pointers
551                 for (i=0; i < nb_out_channels; i++) {
552                     if(i < arguments.playback) {
553                         audiobuffers_out[i] = audiobuffers_raw + i*dev_options.period_size;
554                         switch (ffado_streaming_get_playback_stream_type(dev,i)) {
555                             case ffado_stream_type_audio:
556                                 /* assign the audiobuffer to the stream */
557                                 ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i]));
558                                 ffado_streaming_playback_stream_onoff(dev, i, 1);
559                                 break;
560                                 // this is done with read/write routines because the nb of bytes can differ.
561                             case ffado_stream_type_midi:
562                                 ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i]));
563                                 ffado_streaming_playback_stream_onoff(dev, i, 1);
564                             default:
565                                 break;
566                         }
567                     } else {
568                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
569                         ffado_streaming_playback_stream_onoff(dev, i, 0);
570                     }
571                 }
572                 need_silent=false;
573             } else {
574                 debugOutput(DEBUG_LEVEL_NORMAL, "PBK: missed period %d\n", nb_periods);
575                 need_silent=true;
576             }
577         } else {
578             need_silent=true;
579         }
580         if(need_silent) {
581             // if not, use the null buffer
582             memset(nullbuffer, 0, arguments.period * sizeof(float)); // clean it first
583             for (i=0; i < nb_out_channels; i++) {
584                 switch (ffado_streaming_get_playback_stream_type(dev,i)) {
585                     case ffado_stream_type_audio:
586                         /* assign the audiobuffer to the stream */
587                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
588                         ffado_streaming_playback_stream_onoff(dev, i, 0);
589                         break;
590                         // this is done with read/write routines because the nb of bytes can differ.
591                     case ffado_stream_type_midi:
592                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
593                         ffado_streaming_playback_stream_onoff(dev, i, 0);
594                     default:
595                         break;
596                 }
597             }
598         }
599
600         // transfer playback buffers
601         ffado_streaming_transfer_playback_buffers(dev);
602
603         if(playbackbuffer && !need_silent && msg_res == IpcRingBuffer::eR_OK) {
604             // if we had a good block, release it
605             // FIXME: we should check for errors here
606             playbackbuffer->releaseBlockForRead();
607         }
608
609         nb_periods++;
610     }
611
612     debugOutput(DEBUG_LEVEL_NORMAL, "Exiting receive loop\n");
613
614     ffado_streaming_stop(dev);
615     ffado_streaming_finish(dev);
616
617     delete capturebuffer;
618     delete playbackbuffer;
619
620     free(nullbuffer);
621     free(audiobuffers_in);
622     free(audiobuffers_out);
623
624   return EXIT_SUCCESS;
625 }
Note: See TracBrowser for help on using the browser.