view playogg.c @ 3:4d73408a0b71

Updated the help display.
author Eris Caffee <discordia@eldalin.com>
date Tue, 05 Aug 2014 12:41:54 -0500
parents 3617357a1b90
children
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include <getopt.h>
6 #include <signal.h>
7 #include <termios.h>
8 #include <unistd.h>
10 #include <ao/ao.h>
11 #include <vorbis/vorbisfile.h>
13 #include "playogg.h"
15 /******************************************************************************/
16 static struct opts {
17 int debug;
18 int verbose;
19 int list_devices;
20 char* device;
21 char **files;
22 char *outfile;
23 int ao_endian;
24 int ao_device_id;
25 ao_device *ao_device;
26 ao_info *ao_driver_info;
27 ao_sample_format ao_format;
28 } options;
32 struct termios termios_orig;
34 /******************************************************************************/
35 int
36 main (int argc, char **argv ) {
38 init();
39 parse_command_line( argc, argv );
41 term_init();
43 if ( ! sound_init() ) {
44 exit( EXIT_FAILURE );
45 }
47 if ( options.list_devices ) {
48 exit( sound_list_devices() );
49 }
51 char **file = options.files;
52 int next = 1; /* -1 play previous file, 0 stop playing, 1 play next file */
53 while (next && file && *file) {
54 if ( options.verbose ) {
55 printf( "Next file: %s\n", *file );
56 }
57 next = sound_play_file( *file );
58 if ( next < 0 ) {
59 if ( file > options.files ) {
60 file--;
61 }
62 }
63 else {
64 file++;
65 }
66 if ( options.outfile ) {
67 next = 0; // Only convert one file
68 }
69 }
71 exit( EXIT_SUCCESS );
72 }
74 /******************************************************************************/
75 void
76 err_exit ( char * msg ) {
77 fprintf(stderr, "FATAL ERROR: %s\n", msg ? msg : "Unknown error" );
78 exit( EXIT_FAILURE );
79 }
81 /******************************************************************************/
82 void
83 init ( void ) {
84 atexit( shutdown );
86 options.debug = 0;
87 options.verbose = 0;
88 options.device = NULL;
89 options.list_devices = 0;
90 options.files = NULL;
91 options.outfile = NULL;
92 options.ao_endian = 1;
93 options.ao_device_id = -1;
94 options.ao_device = NULL;
95 options.ao_driver_info = NULL;
96 memset( &options.ao_format, 0, sizeof(ao_sample_format) );
98 }
100 /******************************************************************************/
101 int
102 min ( int a, int b ) {
103 if (a <= b) {
104 return a;
105 }
106 else {
107 return b;
108 }
109 }
112 /******************************************************************************/
113 void
114 parse_command_line (int argc, char **argv ) {
116 static const char *short_options = "hd:lf:";
117 static const struct option long_options[] = {
118 { "help", 0, 0, 'h' },
119 { "verbose", 0, 0, 0 },
120 { "debug", 0, 0, 0 },
121 { "device", 1, 0, 'd' },
122 { "list-devices", 0, 0, 'l' },
123 { "outfile" , 1, 0, 'f' },
124 { NULL, 0, NULL, 0}
125 };
127 int c;
128 int option_index = 0;
129 while (1) {
130 c = getopt_long( argc, argv, short_options, long_options, &option_index );
132 if ( -1 == c ) {
133 break;
134 }
136 switch (c) {
138 case 0:
139 1;
140 int len = strlen( long_options[option_index].name );
141 if ( 0 == strncmp( long_options[option_index].name, "verbose", min(len, 7) ) ) {
142 options.verbose = 1;
143 }
144 else if ( 0 == strncmp( long_options[option_index].name, "debug", min(len, 5) ) ) {
145 options.debug = 1;
146 }
147 else {
148 fprintf( stderr, "Unrecognized option on command line: --%s\n", optarg );
149 show_help();
150 exit( EXIT_FAILURE );
151 }
153 break;
155 case 'f':
156 options.outfile = strdup( optarg );
157 printf( "outfile is %s\n", options.outfile );
158 break;
160 case 'h':
161 show_help();
162 exit( EXIT_SUCCESS );
163 break;
165 case 'd':
166 options.device = strdup( optarg );
167 printf( "device is %s\n", options.device );
168 break;
170 case 'l':
171 options.list_devices = 1;
172 break;
174 case 'v':
175 options.verbose = 1;
176 break;
178 default:
179 fprintf( stderr, "Unrecognized option on command line: -%c\n", c );
180 show_help();
181 exit( EXIT_FAILURE );
182 break;
183 }
184 }
186 if (optind < argc) {
187 options.files = calloc( argc - optind + 1, sizeof(char *) );
188 int i = 0;
189 while ( optind < argc) {
190 options.files[i++] = argv[optind++];
191 }
192 }
194 if (options.debug) {
195 fprintf( stderr, "verbose: %d\n", options.verbose );
196 fprintf( stderr, "debug : %d\n", options.debug );
197 fprintf( stderr, "device : %s\n", options.device );
198 fprintf( stderr, "list_devices : %d\n", options.list_devices );
199 fprintf( stderr, "files : ");
200 char **ptr = options.files;
201 while (ptr && *ptr) {
202 fprintf( stderr, "%s ", *ptr );
203 ptr++;
204 }
205 fprintf( stderr, "\n" );
206 }
208 }
210 /******************************************************************************/
211 void
212 shutdown ( void ) {
213 if ( options.device ) {
214 free( options.device );
215 }
216 if ( options.outfile ) {
217 free( options.outfile );
218 }
219 }
221 /******************************************************************************/
222 void
223 show_help ( void ) {
224 printf(
225 "Usage: myplayogg [-hlv] filename\n"
226 "\n"
227 "Play the Ogg Vorbis encoded file.\n"
228 "\n"
229 "Options:\n"
230 "\n"
231 "-h --help Show these instructions.\n"
232 " --verbose Print extra information.\n"
233 " --debug Print debugging information.\n"
234 "-d --device= Specify the output device to use.\n"
235 "-l --list-devices Print a list fo available output devices.\n"
236 "-f --outfile= Specify the name of the output file when using a file output device for format conversion.\n"
237 "\n"
238 "While playing you can use the following keys to move around:\n"
239 "\tp\tPause / Play\n"
240 "\tq\tQuit\n"
241 "\tn\tNext song\n"
242 "\tv\tpreVious song\n"
243 "\tf\tfast Forward\n"
244 "\tb\tfast Backward\n"
245 "\n"
246 );
247 }
249 /******************************************************************************/
250 int
251 sound_list_devices ( void ) {
252 ao_info ** driver_list = NULL;
253 int driver_count = -1;
255 driver_list = ao_driver_info_list( &driver_count );
256 if ( driver_list == NULL ) {
257 fprintf( stderr, "Unable to get list of drivers" );
258 return EXIT_FAILURE;
259 }
261 int default_driver_id = ao_default_driver_id();
263 for ( int i = 0; i < driver_count; ++i )
264 {
265 printf( "%s\n", driver_list[i]->name );
266 int driver_id = ao_driver_id( driver_list[i]->short_name );
267 if ( default_driver_id == driver_id ) {
268 printf( "\tDEFAULT DRIVER\n" );
269 }
270 printf( "\tshort name :\t%s\n", driver_list[i]->short_name );
271 printf( "\ttype :\t%s\n",
272 driver_list[i]->type == AO_TYPE_LIVE ? "live" :
273 (driver_list[i]->type == AO_TYPE_FILE ? "file" :
274 "unknown")
275 );
276 printf( "\tcomment :\t%s\n", driver_list[i]->comment);
277 }
279 return EXIT_SUCCESS;
280 }
282 /******************************************************************************/
283 int
284 sound_init ( void ) {
286 ao_initialize();
287 atexit( sound_shutdown );
289 options.ao_endian = ao_is_big_endian();
290 options.ao_device_id = ao_default_driver_id();
291 if ( options.device ) {
292 options.ao_device_id = ao_driver_id( options.device );
293 }
294 if ( -1 == options.ao_device_id ) {
295 fprintf( stderr, "Unable to open output device.\n" );
296 return 0;
297 }
299 options.ao_driver_info = ao_driver_info( options.ao_device_id );
301 options.ao_format.bits = 16;
302 options.ao_format.channels = 2;
303 options.ao_format.rate = 44100;
304 options.ao_format.byte_format = options.ao_driver_info->preferred_byte_format;
306 if ( AO_TYPE_LIVE == options.ao_driver_info->type ) {
307 options.ao_device = ao_open_live( options.ao_device_id, &options.ao_format, NULL );
308 }
309 else if ( AO_TYPE_FILE == options.ao_driver_info->type ) {
310 if ( options.outfile ) {
311 options.ao_device = ao_open_file( options.ao_device_id, options.outfile, 1, &options.ao_format, NULL );
312 }
313 else {
314 fprintf( stderr, "error: No output file name specified.\n" );
315 return 0;
316 }
317 }
318 else {
319 fprintf( stderr, "error: Unable to open output device.\n" );
320 return 0;
321 }
323 return 1;
324 }
326 /******************************************************************************/
327 int
328 sound_play_file ( char *file ) {
329 OggVorbis_File vf;
330 int next_file = 1;
332 int err = ov_fopen( file, &vf );
333 if ( err ) {
334 if ( OV_ENOTVORBIS == err ) {
335 fprintf( stderr, "Not a Vorbis file\n" );
336 }
337 else if ( OV_EVERSION == err ) {
338 fprintf( stderr, "Incompatible Vorbis version\n" );
339 }
340 else {
341 fprintf( stderr, "Unable to open file: %d\n", err );
342 }
343 return next_file;
344 }
346 sound_print_comments( &vf );
348 if ( options.debug ) {
349 vorbis_info *vi = ov_info( &vf, -1 );
350 printf( "Encoded by: %s\n\n", ov_comment( &vf, -1 )->vendor );
351 printf( "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
352 printf( "Decoded length: %ld samples\n", (long) ov_pcm_total( &vf, -1 ) );
353 }
355 int done = 0;
356 int paused = 0;
358 while ( ! done ) {
360 if ( ! paused ) {
361 if ( ! sound_play_section( &vf ) ) {
362 done = 1;
363 }
364 }
366 char c;
367 int num_read = read( fileno( stdin ), &c, sizeof( char ) );
368 if ( 1 == num_read ) {
369 switch (c) {
371 case 'p' :
372 case 'P' :
373 paused = ~paused;
374 break;
376 case 'q' :
377 case 'Q' :
378 done = 1;
379 next_file = 0;
380 break;
382 case 'n' :
383 case 'N' :
384 done = 1;
385 next_file = 1;
386 break;
388 case 'v' :
389 case 'V' :
390 done = 1;
391 next_file = -1;
392 break;
394 case 'f' :
395 case 'F' :
396 done = sound_skip_ahead( &vf );
397 break;
399 case 'b' :
400 case 'B' :
401 done = sound_skip_back( &vf );
402 break;
404 default:
405 break;
406 }
409 }
411 }
413 ov_clear( &vf );
415 return next_file;
416 }
418 /******************************************************************************/
419 int
420 sound_play_section ( OggVorbis_File *vf ) {
421 static char pcm[PCM_BUFFER_SIZE];
422 static int current_section;
424 int ok = 1;
426 long bytes_read = ov_read( vf, pcm, sizeof(pcm), options.ao_endian, 2, 1, &current_section );
428 if ( 0 == bytes_read ) {
429 ok = 0;
430 }
431 else if ( OV_HOLE == bytes_read ) {
432 if (options.verbose ) {
433 fprintf( stderr, "error: hole in data\n" );
434 }
435 }
436 else if ( OV_EBADLINK == bytes_read ) {
437 if (options.verbose ) {
438 fprintf( stderr, "error: invalid stream section in data\n" );
439 }
440 }
441 else if ( OV_EINVAL == bytes_read ) {
442 fprintf( stderr, "error: Unable to read file headers.\n" );
443 ok = 0;
444 }
445 else {
446 // Play it!
447 if ( ! ao_play( options.ao_device, pcm, bytes_read ) ) {
448 ok = 0;
449 }
450 }
452 return ok;
453 }
455 /******************************************************************************/
456 void
457 sound_print_comments ( OggVorbis_File *vf ) {
459 char **comments = ov_comment( vf, -1 )->user_comments;
461 char *track_title = NULL;
462 char **ptr = comments;
463 while ( ptr && *ptr ) {
464 char * pos = strstr( *ptr, "title" );
465 if ( NULL != pos ) {
466 pos = strchr( *ptr, '=' );
467 if ( pos ) {
468 track_title = pos + 1;
469 }
470 }
471 ++ptr;
472 }
474 printf( "Now playing: %s\n", track_title ? track_title : "[unknown]" );
476 if ( options.verbose ) {
477 ptr = comments;
478 while ( ptr && *ptr ) {
479 char * pos = strstr( *ptr, "title" );
480 if ( NULL == pos ) {
481 char *temp = strdup(*ptr);
482 pos = strchr( temp, '=' );
483 *pos = '\0';
484 printf( "\t%s: %s\n", temp, pos+1);
485 free(temp);
486 }
487 ++ptr;
488 }
489 }
490 }
492 /******************************************************************************/
493 void
494 sound_shutdown ( void ) {
495 ao_shutdown();
496 }
498 /******************************************************************************/
499 #define SKIP_TIME 1.0
500 int
501 sound_skip_ahead ( OggVorbis_File *vf ) {
502 double total_time = ov_time_total( vf, -1 );
503 double current_time = ov_time_tell( vf );
504 double new_position = current_time + SKIP_TIME;
505 if ( total_time < new_position ) {
506 return 1;
507 }
508 return ov_time_seek( vf, new_position );
509 }
511 /******************************************************************************/
512 int
513 sound_skip_back ( OggVorbis_File *vf ) {
514 double current_time = ov_time_tell( vf );
515 double new_position = current_time - SKIP_TIME;
516 if ( new_position > 0.0 ) {
517 return ov_time_seek( vf, new_position );
518 }
519 return ov_time_seek( vf, 0.0 );
520 }
522 /******************************************************************************/
523 void
524 term_init ( void ) {
525 tcgetattr( fileno( stdin ), &termios_orig );
527 struct termios termios_new;
528 tcgetattr( fileno( stdin ), &termios_new );
529 termios_new.c_lflag |= ICANON;
530 termios_new.c_lflag ^= ICANON;
531 termios_new.c_lflag |= ECHO;
532 termios_new.c_lflag ^= ECHO;
533 termios_new.c_cc[VMIN] = 0;
534 termios_new.c_cc[VTIME] = 0;
535 tcsetattr( fileno( stdin ), TCSANOW, &termios_new );
537 atexit( term_reset );
539 }
541 /******************************************************************************/
542 void
543 term_reset ( void ) {
544 tcsetattr( fileno( stdin ), TCSANOW, &termios_orig );
545 }