view playogg.c @ 0:4d04dc4239b4

Simple demo programs for libao and libvorbisfile.
author Eris Caffee <discordia@eldalin.com>
date Tue, 22 Jul 2014 14:42:26 -0500
parents
children 03892700ff0d
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 int ao_endian;
22 int ao_device_id;
23 ao_device *ao_device;
24 ao_info *ao_driver_info;
25 ao_sample_format ao_format;
26 } options;
28 int
29 main (int argc, char **argv ) {
31 init();
32 parse_command_line( argc, argv );
34 if ( ! sound_init() ) {
35 exit( EXIT_FAILURE );
36 }
38 if ( options.list_devices ) {
39 exit( sound_list_devices() );
40 }
42 char **file = options.files;
43 int ok = 1;
44 while (ok && file && *file) {
45 if ( options.verbose ) {
46 printf( "Next file: %s\n", *file );
47 }
48 ok = sound_play_file( *file );
49 file++;
50 }
52 exit( EXIT_SUCCESS );
53 }
55 /******************************************************************************/
56 void
57 sig_handler ( int sig ) {
58 if ( SIGINT == sig ) {
59 options.skip_next = 1;
60 }
61 }
63 /******************************************************************************/
64 void
65 err_exit ( char * msg ) {
66 fprintf(stderr, "FATAL ERROR: %s\n", msg ? msg : "Unknown error" );
67 exit( EXIT_FAILURE );
68 }
70 /******************************************************************************/
71 void
72 init ( void ) {
73 atexit( shutdown );
75 options.debug = 0;
76 options.verbose = 0;
77 options.device = NULL;
78 options.list_devices = 0;
79 options.files = NULL;
80 options.skip_next = 0;
81 options.ao_endian = 1;
82 options.ao_device_id = -1;
83 options.ao_device = NULL;
84 options.ao_driver_info = NULL;
85 memset( &options.ao_format, 0, sizeof(ao_sample_format) );
87 struct sigaction sigact;
88 memset( &sigact, 0, sizeof(sigact) );
89 sigact.sa_handler = sig_handler;
90 sigaction( SIGINT, &sigact, NULL );
92 }
94 /******************************************************************************/
95 int
96 min ( int a, int b ) {
97 if (a <= b) {
98 return a;
99 }
100 else {
101 return b;
102 }
103 }
106 /******************************************************************************/
107 void
108 parse_command_line (int argc, char **argv ) {
110 static const char *short_options = "hd:l";
111 static const struct option long_options[] = {
112 { "help", 0, 0, 'h' },
113 { "verbose", 0, 0, 0 },
114 { "debug", 0, 0, 0 },
115 { "device", 1, 0, 'd' },
116 { "list-devices", 0, 0, 'l' },
117 { NULL, 0, NULL, 0}
118 };
120 int c;
121 int option_index = 0;
122 while (1) {
123 c = getopt_long( argc, argv, short_options, long_options, &option_index );
125 if ( -1 == c ) {
126 break;
127 }
129 switch (c) {
131 case 0:
132 1;
133 int len = strlen( long_options[option_index].name );
134 if ( 0 == strncmp( long_options[option_index].name, "verbose", min(len, 7) ) ) {
135 options.verbose = 1;
136 }
137 else if ( 0 == strncmp( long_options[option_index].name, "debug", min(len, 5) ) ) {
138 options.debug = 1;
139 }
140 else {
141 fprintf( stderr, "Unrecognized option on command line: --%s\n", optarg );
142 show_help();
143 exit( EXIT_FAILURE );
144 }
146 break;
148 case 'h':
149 show_help();
150 exit( EXIT_SUCCESS );
151 break;
153 case 'd':
154 options.device = strdup(optarg);
155 break;
157 case 'l':
158 options.list_devices = 1;
159 break;
161 case 'v':
162 options.verbose = 1;
163 break;
165 default:
166 fprintf( stderr, "Unrecognized option on command line: -%c\n", c );
167 show_help();
168 exit( EXIT_FAILURE );
169 break;
170 }
171 }
173 if (optind < argc) {
174 options.files = calloc( argc - optind + 1, sizeof(char *) );
175 int i = 0;
176 while ( optind < argc) {
177 options.files[i++] = argv[optind++];
178 }
179 }
181 if (options.debug) {
182 fprintf( stderr, "verbose: %d\n", options.verbose );
183 fprintf( stderr, "debug : %d\n", options.debug );
184 fprintf( stderr, "device : %s\n", options.device );
185 fprintf( stderr, "list_devices : %d\n", options.list_devices );
186 fprintf( stderr, "files : ");
187 char **ptr = options.files;
188 while (ptr && *ptr) {
189 fprintf( stderr, "%s ", *ptr );
190 ptr++;
191 }
192 fprintf( stderr, "\n" );
193 }
195 }
197 /******************************************************************************/
198 void
199 shutdown ( void ) {
200 if ( options.device ) {
201 free( options.device );
202 }
203 }
205 /******************************************************************************/
206 void
207 show_help ( void ) {
208 printf(
209 "Usage: myplayogg [-hlv] filename\n"
210 "\n"
211 "Play the Ogg Vorbis encoded file.\n"
212 "\n"
213 "Options:\n"
214 "\n"
215 "-h --help Show these instructions.\n"
216 " --verbose Print extra information.\n"
217 " --debug Print debugging information.\n"
218 "-d --device Specify the output device to use.\n"
219 "-l --list-devices Print a list fo available output devices.\n"
220 "\n"
221 );
222 }
224 /******************************************************************************/
225 int
226 sound_list_devices ( void ) {
227 ao_info ** driver_list = NULL;
228 int driver_count = -1;
230 driver_list = ao_driver_info_list( &driver_count );
231 if ( driver_list == NULL ) {
232 fprintf( stderr, "Unable to get list of drivers" );
233 return EXIT_FAILURE;
234 }
236 int default_driver_id = ao_default_driver_id();
238 for ( int i = 0; i < driver_count; ++i )
239 {
240 printf( "%s\n", driver_list[i]->name );
241 int driver_id = ao_driver_id( driver_list[i]->short_name );
242 if ( default_driver_id == driver_id ) {
243 printf( "\tDEFAULT DRIVER\n" );
244 }
245 printf( "\tshort name :\t%s\n", driver_list[i]->short_name );
246 printf( "\ttype :\t%s\n",
247 driver_list[i]->type == AO_TYPE_LIVE ? "live" :
248 (driver_list[i]->type == AO_TYPE_FILE ? "file" :
249 "unknown")
250 );
251 printf( "\tcomment :\t%s\n", driver_list[i]->comment);
252 }
254 return EXIT_SUCCESS;
255 }
257 /******************************************************************************/
258 int
259 sound_init ( void ) {
261 ao_initialize();
262 atexit( sound_shutdown );
264 options.ao_endian = ao_is_big_endian();
265 options.ao_device_id = ao_default_driver_id();
266 if ( options.device ) {
267 options.ao_device_id = ao_driver_id( options.device );
268 }
269 if ( -1 == options.ao_device_id ) {
270 fprintf( stderr, "Unable to open output device.\n" );
271 return 0;
272 }
274 options.ao_driver_info = ao_driver_info( options.ao_device_id );
276 options.ao_format.bits = 16;
277 options.ao_format.channels = 2;
278 options.ao_format.rate = 44100;
279 options.ao_format.byte_format = options.ao_driver_info->preferred_byte_format;
281 if ( AO_TYPE_LIVE == options.ao_driver_info->type ) {
282 options.ao_device = ao_open_live( options.ao_device_id, &options.ao_format, NULL );
283 }
284 else {
285 fprintf( stderr, "error: Only live output devices are supported.\n" );
286 return 0;
287 }
289 return 1;
290 }
292 /******************************************************************************/
293 int
294 sound_play_file ( char *file ) {
295 OggVorbis_File vf;
296 int ok = 1;
298 int err = ov_fopen( file, &vf );
299 if ( err ) {
300 fprintf( stderr, "Unable to open file: %d\n", err );
301 ok = 0;
302 return ok;
303 }
305 sound_print_comments( &vf );
307 if ( options.debug ) {
308 vorbis_info *vi = ov_info( &vf, -1 );
309 printf( "Encoded by: %s\n\n", ov_comment( &vf, -1 )->vendor );
310 printf( "Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
311 printf( "Decoded length: %ld samples\n", (long) ov_pcm_total( &vf, -1 ) );
312 }
314 char pcm[PCM_BUFFER_SIZE];
315 int current_section;
316 int done = 0;
318 while ( ! done && ! options.skip_next ) {
319 long bytes_read = ov_read( &vf, pcm, sizeof(pcm), options.ao_endian, 2, 1, &current_section );
321 if ( 0 == bytes_read ) {
322 done = 1;
323 }
324 else if ( OV_HOLE == bytes_read ) {
325 if (options.verbose ) {
326 fprintf( stderr, "error: hole in data\n" );
327 }
328 }
329 else if ( OV_EBADLINK == bytes_read ) {
330 if (options.verbose ) {
331 fprintf( stderr, "error: invalid stream section in data\n" );
332 }
333 }
334 else if ( OV_EINVAL == bytes_read ) {
335 fprintf( stderr, "error: Unable to read file headers.\n" );
336 done = 1;
337 }
338 else {
339 // Play it!
340 if ( ! ao_play( options.ao_device, pcm, bytes_read ) ) {
341 done = 1;
342 ok = 0;
343 }
344 }
346 }
348 ov_clear( &vf );
350 if ( options.skip_next ) {
351 options.skip_next = 0;
352 }
354 return ok;
355 }
357 /******************************************************************************/
358 void
359 sound_print_comments ( OggVorbis_File *vf) {
361 char **comments = ov_comment( vf, -1 )->user_comments;
363 char *track_title = NULL;
364 char **ptr = comments;
365 while ( ptr && *ptr ) {
366 char * pos = strstr( *ptr, "title" );
367 if ( NULL != pos ) {
368 pos = strchr( *ptr, '=' );
369 if ( pos ) {
370 track_title = pos + 1;
371 }
372 }
373 ++ptr;
374 }
376 printf( "Now playing: %s\n", track_title ? track_title : "[unknown]" );
378 if ( options.verbose ) {
379 ptr = comments;
380 while ( ptr && *ptr ) {
381 char * pos = strstr( *ptr, "title" );
382 if ( NULL == pos ) {
383 char *temp = strdup(*ptr);
384 pos = strchr( temp, '=' );
385 *pos = '\0';
386 printf( "\t%s: %s\n", temp, pos+1);
387 free(temp);
388 }
389 ++ptr;
390 }
391 }
392 }
394 /******************************************************************************/
395 void
396 sound_shutdown ( void ) {
397 ao_shutdown();
398 }