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

Revision 1172, 20.9 kB (checked in by ppalmers, 15 years ago)

lay down the foundations for easy ALSA/Pulse support

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
313     signal (SIGINT, sighandler);
314     signal (SIGPIPE, sighandler);
315
316     ffado_device_info_t device_info;
317     memset(&device_info,0,sizeof(ffado_device_info_t));
318
319     ffado_options_t dev_options;
320     memset(&dev_options,0,sizeof(ffado_options_t));
321
322     dev_options.sample_rate = arguments.sample_rate;
323     dev_options.period_size = arguments.period;
324
325     dev_options.nb_buffers = arguments.nb_buffers;
326
327     dev_options.realtime = (arguments.rtprio != 0);
328     dev_options.packetizer_priority = arguments.rtprio;
329    
330     dev_options.verbose = arguments.verbose;
331        
332     dev_options.slave_mode = arguments.slave_mode;
333     dev_options.snoop_mode = arguments.snoop_mode;
334
335     ffado_device_t *dev=ffado_streaming_init(device_info, dev_options);
336
337     if (!dev) {
338         debugError("Could not init Ffado Streaming layer\n");
339         exit(-1);
340     }
341     if (arguments.audio_buffer_type == 0) {
342         ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_float);
343     } else {
344         ffado_streaming_set_audio_datatype(dev, ffado_audio_datatype_int24);
345     }
346
347     int nb_in_channels_all = ffado_streaming_get_nb_capture_streams(dev);
348     int nb_out_channels_all = ffado_streaming_get_nb_playback_streams(dev);
349
350     // only do audio for now
351     for (i=0; i < nb_in_channels_all; i++) {
352         switch (ffado_streaming_get_capture_stream_type(dev,i)) {
353             case ffado_stream_type_audio:
354                 nb_in_channels++;
355                 break;
356             case ffado_stream_type_midi:
357             default:
358                 break;
359         }
360     }
361     for (i=0; i < nb_out_channels_all; i++) {
362         switch (ffado_streaming_get_playback_stream_type(dev,i)) {
363             case ffado_stream_type_audio:
364                 nb_out_channels++;
365                 break;
366             case ffado_stream_type_midi:
367             default:
368                 break;
369         }
370     }
371
372     printMessage("Channel count: %d capture, %d playback\n",
373                  nb_in_channels, nb_out_channels);
374
375     if(arguments.playback > nb_out_channels) {
376         debugError("Too many playback channels requested (want: %d, have:%d)\n",
377                    arguments.playback, nb_out_channels);
378         return -1;
379     }
380     if(arguments.capture > nb_in_channels) {
381         debugError("Too many capture channels requested (want: %d, have:%d)\n",
382                    arguments.capture, nb_in_channels);
383         return -1;
384     }
385
386     printMessage("Buffer size: %d capture, %d playback\n",
387                   nb_in_channels*dev_options.period_size * 4,
388                   nb_out_channels*dev_options.period_size * 4);
389
390     // allocate the IPC structures
391     #define NB_BUFFERS 4
392     IpcRingBuffer* capturebuffer = NULL;
393     IpcRingBuffer* playbackbuffer = NULL;
394     if(arguments.capture) {
395         // 4 bytes per channel per sample
396         capturebuffer = new IpcRingBuffer("capturebuffer",
397                                 IpcRingBuffer::eBT_Master,
398                                 IpcRingBuffer::eD_Outward,
399                                 IpcRingBuffer::eB_NonBlocking,
400                                 NB_BUFFERS, dev_options.period_size * arguments.capture * 4);
401         if(capturebuffer == NULL) {
402             debugError("Could not create capture IPC buffer\n");
403             exit(-1);
404         }
405    
406         capturebuffer->setVerboseLevel(arguments.verbose);
407    
408         if(!capturebuffer->init()) {
409             debugError("Could not init capture buffer\n");
410             delete capturebuffer;
411             exit(-1);
412         }
413         // indexes to the memory locations where the frames should be put
414         audiobuffers_in = (uint32_t **)calloc(arguments.capture, sizeof(uint32_t *));
415     }
416
417     if(arguments.playback) {
418         // 4 bytes per channel per sample
419         playbackbuffer = new IpcRingBuffer("playbackbuffer",
420                                 IpcRingBuffer::eBT_Master,
421                                 IpcRingBuffer::eD_Inward,
422                                 IpcRingBuffer::eB_NonBlocking,
423                                 NB_BUFFERS, dev_options.period_size * arguments.playback * 4);
424         if(playbackbuffer == NULL) {
425             debugError("Could not create playback IPC buffer\n");
426             exit(-1);
427         }
428    
429         playbackbuffer->setVerboseLevel(arguments.verbose);
430    
431         if(!playbackbuffer->init()) {
432             debugError("Could not init playback buffer\n");
433             delete capturebuffer;
434             delete playbackbuffer;
435             exit(-1);
436         }
437         // indexes to the memory locations where the frames should be get
438         audiobuffers_out = (uint32_t **)calloc(arguments.playback, sizeof(uint32_t *));
439     }
440
441     // this serves in case we miss a cycle, or a channel is disabled
442     nullbuffer = (float *)calloc(arguments.period, sizeof(float));
443
444     // give us RT prio
445     set_realtime_priority(arguments.rtprio);
446
447     // start the streaming layer
448     if (ffado_streaming_prepare(dev)) {
449         debugFatal("Could not prepare streaming system\n");
450         ffado_streaming_finish(dev);
451         return -1;
452     }
453     start_flag = ffado_streaming_start(dev);
454
455     // enter the loop
456     debugOutput(DEBUG_LEVEL_NORMAL,
457                 "Entering receive loop (IN: %d, OUT: %d)\n",
458                 arguments.capture, arguments.playback);
459
460     while(run && start_flag==0) {
461         bool need_silent;
462         enum IpcRingBuffer::eResult msg_res;
463
464         ffado_wait_response response;
465         response = ffado_streaming_wait(dev);
466         if (response == ffado_wait_xrun) {
467             debugOutput(DEBUG_LEVEL_NORMAL, "Xrun\n");
468             ffado_streaming_reset(dev);
469             continue;
470         } else if (response == ffado_wait_error) {
471             debugError("fatal xrun\n");
472             break;
473         }
474
475         // get a block pointer from the IPC buffer to write
476         if(arguments.capture) {
477             uint32_t *audiobuffers_raw;
478             msg_res = capturebuffer->requestBlockForWrite((void**) &audiobuffers_raw); // pointer voodoo
479             if(msg_res == IpcRingBuffer::eR_OK) {
480                 // if we got a valid pointer, setup the stream pointers
481                 for (i=0; i < nb_in_channels; i++) {
482                     if(i < arguments.capture) {
483                         audiobuffers_in[i] = audiobuffers_raw + i*dev_options.period_size;
484                         switch (ffado_streaming_get_capture_stream_type(dev,i)) {
485                             case ffado_stream_type_audio:
486                                 /* assign the audiobuffer to the stream */
487                                 ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i]));
488                                 ffado_streaming_capture_stream_onoff(dev, i, 1);
489                                 break;
490                                 // this is done with read/write routines because the nb of bytes can differ.
491                             case ffado_stream_type_midi:
492                                 ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(audiobuffers_in[i]));
493                                 ffado_streaming_capture_stream_onoff(dev, i, 1);
494                             default:
495                                 break;
496                         }
497                     } else {
498                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
499                         ffado_streaming_capture_stream_onoff(dev, i, 0);
500                     }
501                 }
502                 need_silent = false;
503             } else  {
504                 need_silent = true;
505                 debugOutput(DEBUG_LEVEL_NORMAL, "CAP: missed period %d\n", nb_periods);
506             }
507         }
508         if(need_silent) {
509             // if not, use the null buffer
510             for (i=0; i < nb_in_channels; i++) {
511                 switch (ffado_streaming_get_capture_stream_type(dev,i)) {
512                     case ffado_stream_type_audio:
513                         /* assign the audiobuffer to the stream */
514                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
515                         ffado_streaming_capture_stream_onoff(dev, i, 0);
516                         break;
517                         // this is done with read/write routines because the nb of bytes can differ.
518                     case ffado_stream_type_midi:
519                         ffado_streaming_set_capture_stream_buffer(dev, i, (char *)(nullbuffer));
520                         ffado_streaming_capture_stream_onoff(dev, i, 0);
521                     default:
522                         break;
523                 }
524             }
525         }
526
527         // transfer
528         ffado_streaming_transfer_capture_buffers(dev);
529
530         if(!need_silent && msg_res == IpcRingBuffer::eR_OK) {
531             // if we had a good block, release it
532             // FIXME: we should check for errors here
533             capturebuffer->releaseBlockForWrite();
534         }
535
536         if(arguments.playback) {
537             uint32_t *audiobuffers_raw;
538             // get a block pointer from the IPC buffer to read
539             msg_res = playbackbuffer->requestBlockForRead((void**) &audiobuffers_raw); // pointer voodoo
540             if(msg_res == IpcRingBuffer::eR_OK) {
541                 // if we got a valid pointer, setup the stream pointers
542                 for (i=0; i < nb_out_channels; i++) {
543                     if(i < arguments.playback) {
544                         audiobuffers_out[i] = audiobuffers_raw + i*dev_options.period_size;
545                         switch (ffado_streaming_get_playback_stream_type(dev,i)) {
546                             case ffado_stream_type_audio:
547                                 /* assign the audiobuffer to the stream */
548                                 ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i]));
549                                 ffado_streaming_playback_stream_onoff(dev, i, 1);
550                                 break;
551                                 // this is done with read/write routines because the nb of bytes can differ.
552                             case ffado_stream_type_midi:
553                                 ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(audiobuffers_out[i]));
554                                 ffado_streaming_playback_stream_onoff(dev, i, 1);
555                             default:
556                                 break;
557                         }
558                     } else {
559                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
560                         ffado_streaming_playback_stream_onoff(dev, i, 0);
561                     }
562                 }
563                 need_silent=false;
564             } else {
565                 debugOutput(DEBUG_LEVEL_NORMAL, "PBK: missed period %d\n", nb_periods);
566                 need_silent=true;
567             }
568         } else {
569             need_silent=true;
570         }
571         if(need_silent) {
572             // if not, use the null buffer
573             memset(nullbuffer, 0, arguments.period * sizeof(float)); // clean it first
574             for (i=0; i < nb_out_channels; i++) {
575                 switch (ffado_streaming_get_playback_stream_type(dev,i)) {
576                     case ffado_stream_type_audio:
577                         /* assign the audiobuffer to the stream */
578                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
579                         ffado_streaming_playback_stream_onoff(dev, i, 0);
580                         break;
581                         // this is done with read/write routines because the nb of bytes can differ.
582                     case ffado_stream_type_midi:
583                         ffado_streaming_set_playback_stream_buffer(dev, i, (char *)(nullbuffer));
584                         ffado_streaming_playback_stream_onoff(dev, i, 0);
585                     default:
586                         break;
587                 }
588             }
589         }
590
591         // transfer playback buffers
592         ffado_streaming_transfer_playback_buffers(dev);
593
594         if(!need_silent && msg_res == IpcRingBuffer::eR_OK) {
595             // if we had a good block, release it
596             // FIXME: we should check for errors here
597             capturebuffer->releaseBlockForRead();
598         }
599
600         nb_periods++;
601     }
602
603     debugOutput(DEBUG_LEVEL_NORMAL, "Exiting receive loop\n");
604
605     ffado_streaming_stop(dev);
606     ffado_streaming_finish(dev);
607
608     delete capturebuffer;
609     delete playbackbuffer;
610
611     free(nullbuffer);
612     free(audiobuffers_in);
613     free(audiobuffers_out);
614
615   return EXIT_SUCCESS;
616 }
Note: See TracBrowser for help on using the browser.