view playogg.c @ 1:03892700ff0d

Enabled file output drivers for format conversion.
author Eris Caffee <discordia@eldalin.com>
date Tue, 22 Jul 2014 16:27:42 -0500
parents 4d04dc4239b4
children 3617357a1b90
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
5 #include <getopt.h>
6 #include <signal.h>
8 #include <ao/ao.h>
9 #include <vorbis/vorbisfile.h>
11 #include "playogg.h"
13 /******************************************************************************/
14 static struct opts {
15 int debug;
16 int verbose;
17 int list_devices;
18 char* device;
19 char **files;
20 int skip_next;
21 char *outfile;
22 int ao_endian;
23 int ao_device_id;
24 ao_device *ao_device;
25 ao_info *ao_driver_info;
26 ao_sample_format ao_format;
27 } options;
29 int
30 main (int argc, char **argv ) {
32 init();
33 parse_command_line( argc, argv );
35 if ( ! sound_init() ) {
36 exit( EXIT_FAILURE );
37 }
39 if ( options.list_devices ) {
40 exit( sound_list_devices() );
41 }
43 char **file = options.files;
44 int ok = 1;
45 while (ok && file && *file) {
46 if ( options.verbose ) {
47 printf( "Next file: %s\n", *file );
48 }
49 ok = sound_play_file( *file );
50 file++;
51 if ( options.outfile ) {
52 ok = 0; // Only convert one file
53 }
54 }
56 exit( EXIT_SUCCESS );
57 }
59 /******************************************************************************/
60 void
61 sig_handler ( int sig ) {
62 if ( SIGINT == sig ) {
63 options.skip_next = 1;
64 }
65 }
67 /******************************************************************************/
68 void
69 err_exit ( char * msg ) {
70 fprintf(stderr, "FATAL ERROR: %s\n", msg ? msg : "Unknown error" );
71 exit( EXIT_FAILURE );
72 }
74 /******************************************************************************/
75 void
76 init ( void ) {
77 atexit( shutdown );
79 options.debug = 0;
80 options.verbose = 0;
81 options.device = NULL;
82 options.list_devices = 0;
83 options.files = NULL;
84 options.skip_next = 0;
85 options.outfile = NULL;
86 options.ao_endian = 1;
87 options.ao_device_id = -1;
88 options.ao_device = NULL;
89 options.ao_driver_info = NULL;
90 memset( &options.ao_format, 0, sizeof(ao_sample_format) );
92 struct sigaction sigact;
93 memset( &sigact, 0, sizeof(sigact) );
94 sigact.sa_handler = sig_handler;
95 sigaction( SIGINT, &sigact, NULL );
97 }
99 /******************************************************************************/
100 int
101 min ( int a, int b ) {
102 if (a <= b) {
103 return a;
104 }
105 else {
106 return b;
107 }
108 }
111 /******************************************************************************/
112 void
113 parse_command_line (int argc, char **argv ) {
115 static const char *short_options = "hd:lf:";
116 static const struct option long_options[] = {
117 { "help", 0, 0, 'h' },
118 { "verbose", 0, 0, 0 },
119 { "debug", 0, 0, 0 },
120 { "device", 1, 0, 'd' },
121 { "list-devices", 0, 0, 'l' },
122 { "outfile" , 1, 0, 'f' },
123 { NULL, 0, NULL, 0}
124 };
126 int c;
127 int option_index = 0;
128 while (1) {
129 c = getopt_long( argc, argv, short_options, long_options, &option_index );
131 if ( -1 == c ) {
132 break;
133 }
135 switch (c) {
137 case 0:
138 1;
139 int len = strlen( long_options[option_index].name );
140 if ( 0 == strncmp( long_options[option_index].name, "verbose", min(len, 7) ) ) {
141 options.verbose = 1;
142 }
143 else if ( 0 == strncmp( long_options[option_index].name, "debug", min(len, 5) ) ) {
144 options.debug = 1;
145 }
146 else {
147 fprintf( stderr, "Unrecognized option on command line: --%s\n", optarg );
148 show_help();
149 exit( EXIT_FAILURE );
150 }
152 break;
154 case 'f':
155 options.outfile = strdup( optarg );
156 printf( "outfile is %s\n", options.outfile );
157 break;
159 case 'h':
160 show_help();
161 exit( EXIT_SUCCESS );
162 break;
164 case 'd':
165 options.device = strdup( optarg );
166 printf( "device is %s\n", options.device );
167 break;
169 case 'l':
170 options.list_devices = 1;
171 break;
173 case 'v':
174 options.verbose = 1;
175 break;
177 default:
178 fprintf( stderr, "Unrecognized option on command line: -%c\n", c );
179 show_help();
180 exit( EXIT_FAILURE );
181 break;
182 }
183 }
185 if (optind < argc) {
186 options.files = calloc( argc - optind + 1, sizeof(char *) );
187 int i = 0;
188 while ( optind < argc) {
189 options.files[i++] = argv[optind++];
190 }
191 }
193 if (options.debug) {
194 fprintf( stderr, "verbose: %d\n", options.verbose );
195 fprintf( stderr, "debug : %d\n", options.debug );
196 fprintf( stderr, "device : %s\n", options.device );
197 fprintf( stderr, "list_devices : %d\n", options.list_devices );
198 fprintf( stderr, "files : ");
199 char **ptr = options.files;
200 while (ptr && *ptr) {
201 fprintf( stderr, "%s ", *ptr );
202 ptr++;
203 }
204 fprintf( stderr, "\n" );
205 }
207 }
209 /******************************************************************************/
210 void
211 shutdown ( void ) {
212 if ( options.device ) {
213 free( options.device );
214 }
215 if ( options.outfile ) {
216 free( options.outfile );
217 }
218 }
220 /******************************************************************************/
221 void
222 show_help ( void ) {
223 printf(
224 "Usage: myplayogg [-hlv] filename\n"
225 "\n"
226 "Play the Ogg Vorbis encoded file.\n"
227 "\n"
228 "Options:\n"
229 "\n"
230 "-h --help Show these instructions.\n"
231 " --verbose Print extra information.\n"
232 " --debug Print debugging information.\n"
233 "-d --device= Specify the output device to use.\n"
234 "-l --list-devices Print a list fo available output devices.\n"
235 "-f --outfile= Specify the name of the output file when using a file output device for format conversion.\n"
236 "\n"
237 );
238 }
240 /******************************************************************************/
241 int
242 sound_list_devices ( void ) {
243 ao_info ** driver_list = NULL;
244 int driver_count = -1;
246 driver_list = ao_driver_info_list( &driver_count );
247 if ( driver_list == NULL ) {
248 fprintf( stderr, "Unable to get list of drivers" );
249 return EXIT_FAILURE;
250 }
252 int default_driver_id = ao_default_driver_id();
254 for ( int i = 0; i < driver_count; ++i )
255 {
256 printf( "%s\n", driver_list[i]->name );
257 int driver_id = ao_driver_id( driver_list[i]->short_name );
258 if ( default_driver_id == driver_id ) {
259 printf( "\tDEFAULT DRIVER\n" );
260 }
261 printf( "\tshort name :\t%s\n", driver_list[i]->short_name );
262 printf( "\ttype :\t%s\n",
263 driver_list[i]->type == AO_TYPE_LIVE ? "live" :
264 (driver_list[i]->type == AO_TYPE_FILE ? "file" :
265 "unknown")
266 );
267 printf( "\tcomment :\t%s\n", driver_list[i]->comment);
268 }
270 return EXIT_SUCCESS;
271 }
273 /******************************************************************************/
274 int
275 sound_init ( void ) {
277 ao_initialize();
278 atexit( sound_shutdown );
280 options.ao_endian = ao_is_big_endian();
281 options.ao_device_id = ao_default_driver_id();
282 if ( options.device ) {
283 options.ao_device_id = ao_driver_id( options.device );
284 }
285 if ( -1 == options.ao_device_id ) {
286 fprintf( stderr, "Unable to open output device.\n" );
287 return 0;
288 }
290 options.ao_driver_info = ao_driver_info( options.ao_device_id );
292 options.ao_format.bits = 16;
293 options.ao_format.channels = 2;
294 options.ao_format.rate = 44100;
295 options.ao_format.byte_format = options.ao_driver_info->preferred_byte_format;
297 if ( AO_TYPE_LIVE == options.ao_driver_info->type ) {
298 options.ao_device = ao_open_live( options.ao_device_id, &options.ao_format, NULL );
299 }
300 else if ( AO_TYPE_FILE == options.ao_driver_info->type ) {
301 if ( options.outfile ) {
302 options.ao_device = ao_open_file( options.ao_device_id, options.outfile, 1, &options.ao_format, NULL );
303 }
304 else {
305 fprintf( stderr, "error: No output file name specified.\n" );
306 return 0;
307 }
308 }
309 else {
310 fprintf( stderr, "error: Unable to open output device.\n" );
311 return 0;
312 }
314 return 1;
315 }
317 /******************************************************************************/
318 int
319 sound_play_file ( char *file ) {
320 OggVorbis_File vf;
321 int ok = 1;
323 int err = ov_fopen( file, &vf );
324 if ( err ) {
325 fprintf( stderr, "Unable to open file: %d\n", err );
326 ok = 0;
327 return ok;
328 }
330 sound_print_comments( &vf );
332 if ( options.debug ) {
333 vorbis_info *vi = ov_info( &vf, -1 );
334 printf( "Encoded by: %s\n\n", ov_comment( &vf, -1 )->vendor );
335 printf( "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
336 printf( "Decoded length: %ld samples\n", (long) ov_pcm_total( &vf, -1 ) );
337 }
339 char pcm[PCM_BUFFER_SIZE];
340 int current_section;
341 int done = 0;
343 while ( ! done && ! options.skip_next ) {
344 long bytes_read = ov_read( &vf, pcm, sizeof(pcm), options.ao_endian, 2, 1, &current_section );
346 if ( 0 == bytes_read ) {
347 done = 1;
348 }
349 else if ( OV_HOLE == bytes_read ) {
350 if (options.verbose ) {
351 fprintf( stderr, "error: hole in data\n" );
352 }
353 }
354 else if ( OV_EBADLINK == bytes_read ) {
355 if (options.verbose ) {
356 fprintf( stderr, "error: invalid stream section in data\n" );
357 }
358 }
359 else if ( OV_EINVAL == bytes_read ) {
360 fprintf( stderr, "error: Unable to read file headers.\n" );
361 done = 1;
362 }
363 else {
364 // Play it!
365 if ( ! ao_play( options.ao_device, pcm, bytes_read ) ) {
366 done = 1;
367 ok = 0;
368 }
369 }
371 }
373 ov_clear( &vf );
375 if ( options.skip_next ) {
376 options.skip_next = 0;
377 }
379 return ok;
380 }
382 /******************************************************************************/
383 void
384 sound_print_comments ( OggVorbis_File *vf) {
386 char **comments = ov_comment( vf, -1 )->user_comments;
388 char *track_title = NULL;
389 char **ptr = comments;
390 while ( ptr && *ptr ) {
391 char * pos = strstr( *ptr, "title" );
392 if ( NULL != pos ) {
393 pos = strchr( *ptr, '=' );
394 if ( pos ) {
395 track_title = pos + 1;
396 }
397 }
398 ++ptr;
399 }
401 printf( "Now playing: %s\n", track_title ? track_title : "[unknown]" );
403 if ( options.verbose ) {
404 ptr = comments;
405 while ( ptr && *ptr ) {
406 char * pos = strstr( *ptr, "title" );
407 if ( NULL == pos ) {
408 char *temp = strdup(*ptr);
409 pos = strchr( temp, '=' );
410 *pos = '\0';
411 printf( "\t%s: %s\n", temp, pos+1);
412 free(temp);
413 }
414 ++ptr;
415 }
416 }
417 }
419 /******************************************************************************/
420 void
421 sound_shutdown ( void ) {
422 ao_shutdown();
423 }