ogg123.c 17.8 KB
Newer Older
1
/* ogg123.c by Kenneth Arnold <kcarnold@arnoldnet.net> */
Jack Moffitt's avatar
Jack Moffitt committed
2 3 4 5
/* Modified to use libao by Stan Seibert <volsung@asu.edu> */

/********************************************************************
 *                                                                  *
Monty's avatar
 
Monty committed
6
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
Jack Moffitt's avatar
Jack Moffitt committed
7 8
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
Monty's avatar
 
Monty committed
9
 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
Jack Moffitt's avatar
Jack Moffitt committed
10
 *                                                                  *
Monty's avatar
 
Monty committed
11 12
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2000             *
 * by Monty <monty@xiph.org> and the XIPHOPHORUS Company            *
Jack Moffitt's avatar
Jack Moffitt committed
13 14 15 16
 * http://www.xiph.org/                                             *
 *                                                                  *
 ********************************************************************

17
 last mod: $Id: ogg123.c,v 1.44 2001/08/07 21:37:50 volsung Exp $
Jack Moffitt's avatar
Jack Moffitt committed
18 19 20

 ********************************************************************/

Kenneth Arnold's avatar
Kenneth Arnold committed
21
/* FIXME : That was a messy message. Fix it. */
Jack Moffitt's avatar
Jack Moffitt committed
22

23
#include <sys/types.h>
Jack Moffitt's avatar
Jack Moffitt committed
24
#include <string.h>
25 26
#include <stdlib.h>
#include <stdio.h>
Jack Moffitt's avatar
Jack Moffitt committed
27 28
#include <netdb.h>
#include <netinet/in.h>
Michael Smith's avatar
 
Michael Smith committed
29
#include <sys/socket.h>
Jack Moffitt's avatar
Jack Moffitt committed
30
#include <errno.h>
Kenneth Arnold's avatar
Kenneth Arnold committed
31
#include <time.h>
32
#include <getopt.h>
Jack Moffitt's avatar
Jack Moffitt committed
33

34 35
#include <signal.h>

36 37
#include "ogg123.h"

38 39 40
/* take buffer out of the data segment, not the stack */
char convbuffer[BUFFER_CHUNK_SIZE];
int convsize = BUFFER_CHUNK_SIZE;
41
buf_t * buffer = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
42

43 44 45
static char skipfile_requested;
static void (*old_sig)(int);

Kenneth Arnold's avatar
Kenneth Arnold committed
46 47 48 49 50 51 52
struct {
    char *key;			/* includes the '=' for programming convenience */
    char *formatstr;		/* formatted output */
} ogg_comment_keys[] = {
  {"ARTIST=", "Artist: %s\n"},
  {"ALBUM=", "Album: %s\n"},
  {"TITLE=", "Title: %s\n"},
Ralph Giles's avatar
 
Ralph Giles committed
53 54
  {"VERSION=", "Version: %s\n"},
  {"TRACKNUMBER=", "Track number: %s\n"},
Kenneth Arnold's avatar
Kenneth Arnold committed
55 56 57 58 59 60 61 62
  {"ORGANIZATION=", "Organization: %s\n"},
  {"GENRE=", "Genre: %s\n"},
  {"DESCRIPTION=", "Description: %s\n"},
  {"DATE=", "Date: %s\n"},
  {"LOCATION=", "Location: %s\n"},
  {"COPYRIGHT=", "Copyright %s\n"},
  {NULL, NULL}
};
Jack Moffitt's avatar
Jack Moffitt committed
63 64

struct option long_options[] = {
Kenneth Arnold's avatar
Kenneth Arnold committed
65 66 67
    {"help", no_argument, 0, 'h'},
    {"version", no_argument, 0, 'V'},
    {"device", required_argument, 0, 'd'},
68
    {"file", required_argument, 0, 'f'},
Kenneth Arnold's avatar
Kenneth Arnold committed
69 70 71 72 73
    {"skip", required_argument, 0, 'k'},
    {"device-option", required_argument, 0, 'o'},
    {"verbose", no_argument, 0, 'v'},
    {"quiet", no_argument, 0, 'q'},
    {"shuffle", no_argument, 0, 'z'},
74
    {"buffer", required_argument, 0, 'b'},
Kenneth Arnold's avatar
Kenneth Arnold committed
75
    {"delay", required_argument, 0, 'l'},
Kenneth Arnold's avatar
Kenneth Arnold committed
76
    {0, 0, 0, 0}
Jack Moffitt's avatar
Jack Moffitt committed
77 78
};

79
void usage(void)
Jack Moffitt's avatar
Jack Moffitt committed
80
{
81 82 83
    FILE *o = stderr;
    int i, driver_count;
    ao_info **devices = ao_driver_info_list(&driver_count);
Kenneth Arnold's avatar
Kenneth Arnold committed
84 85

    fprintf(o,
86
	    "Ogg123 from " PACKAGE " " VERSION "\n"
Kenneth Arnold's avatar
Kenneth Arnold committed
87 88 89 90
	    " by Kenneth Arnold <kcarnold@arnoldnet.net> and others\n\n"
	    "Usage: ogg123 [<options>] <input file> ...\n\n"
	    "  -h, --help     this help\n"
	    "  -V, --version  display Ogg123 version\n"
91
	    "  -d, --device=d uses 'd' as an output device\n"
92 93 94 95 96 97 98 99 100 101 102
	    "      Possible devices are:\n"
	    "        ");

    for(i = 0; i < driver_count; i++)
      fprintf(o,"%s ",devices[i]->short_name);

    fprintf(o,"\n");

    fprintf(o,
	    "  -f, --file=filename  Set the output filename for a previously\n"
	    "      specified file device (with -d).\n"
Kenneth Arnold's avatar
Kenneth Arnold committed
103 104 105 106
	    "  -k n, --skip n  Skip the first 'n' seconds\n"
	    "  -o, --device-option=k:v passes special option k with value\n"
	    "      v to previously specified device (with -d).  See\n"
	    "      man page for more info.\n"
107
	    "  -b n, --buffer n  use a buffer of approximately 'n' kilobytes\n"
Kenneth Arnold's avatar
Kenneth Arnold committed
108 109
	    "  -v, --verbose  display progress and other useful stuff\n"
	    "  -q, --quiet    don't display anything (no title)\n"
Kenneth Arnold's avatar
Kenneth Arnold committed
110 111 112 113 114
	    "  -z, --shuffle  shuffle play\n"
	    "\n"
	    "ogg123 will skip to the next song on SIGINT (Ctrl-C) after s seconds after\n"
	    "song start."
	    "  -l, --delay=s  set s (default 1). If s=-1, disable song skip.\n");
Jack Moffitt's avatar
Jack Moffitt committed
115 116
}

Kenneth Arnold's avatar
Kenneth Arnold committed
117 118 119 120 121
int main(int argc, char **argv)
{
    ogg123_options_t opt;
    int ret;
    int option_index = 1;
122 123 124
    ao_option *temp_options = NULL;
    ao_option ** current_options = &temp_options;
    ao_info *info;
Kenneth Arnold's avatar
Kenneth Arnold committed
125
    int temp_driver_id = -1;
126 127
    devices_t *current;
    
Kenneth Arnold's avatar
Kenneth Arnold committed
128 129 130 131 132 133 134
    opt.read_file = NULL;
    opt.shuffle = 0;
    opt.verbose = 0;
    opt.quiet = 0;
    opt.seekpos = 0;
    opt.instream = NULL;
    opt.outdevices = NULL;
135
    opt.buffer_size = 0;
Kenneth Arnold's avatar
Kenneth Arnold committed
136
    opt.delay = 1;
Kenneth Arnold's avatar
Kenneth Arnold committed
137 138 139

    ao_initialize();

140
    while (-1 != (ret = getopt_long(argc, argv, "b:d:f:hl:k:o:qvVz",
Kenneth Arnold's avatar
Kenneth Arnold committed
141 142
				    long_options, &option_index))) {
	switch (ret) {
Jack Moffitt's avatar
Jack Moffitt committed
143
	case 0:
Kenneth Arnold's avatar
Kenneth Arnold committed
144 145 146
	    fprintf(stderr,
		    "Internal error: long option given when none expected.\n");
	    exit(1);
147
	case 'b':
148
	  opt.buffer_size = atoi(optarg) / (BUFFER_CHUNK_SIZE / 1024);
149 150 151
	  if (opt.buffer_size == 1)
	    opt.buffer_size = 0; /* Hack to work around boundary case in
				    buffering code.  FIXME! */
152
	  break;
Jack Moffitt's avatar
Jack Moffitt committed
153
	case 'd':
154
	    temp_driver_id = ao_driver_id(optarg);
Kenneth Arnold's avatar
Kenneth Arnold committed
155 156 157
	    if (temp_driver_id < 0) {
		fprintf(stderr, "No such device %s.\n", optarg);
		exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
158
	    }
159 160
	    current = append_device(opt.outdevices, temp_driver_id, 
				    NULL, NULL);
161 162 163
	    if(opt.outdevices == NULL)
		    opt.outdevices = current;
	    current_options = &current->options;
Kenneth Arnold's avatar
Kenneth Arnold committed
164
	    break;
165 166 167 168 169 170 171 172 173 174 175
	case 'f':
	    info = ao_driver_info(temp_driver_id);
	    if (info->type == AO_TYPE_FILE) {
	        free(current->filename);
		current->filename = strdup(optarg);
	    } else {
	        fprintf(stderr, "Driver %s is not a file output driver.\n",
			info->short_name);
	        exit(1);
	    }
	    break;
176
	case 'k':
Kenneth Arnold's avatar
Kenneth Arnold committed
177 178
	    opt.seekpos = atof(optarg);
	    break;
Kenneth Arnold's avatar
Kenneth Arnold committed
179 180 181
	case 'l':
	    opt.delay = atoi(optarg);
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
182
	case 'o':
183
	    if (optarg && !add_option(current_options, optarg)) {
Kenneth Arnold's avatar
Kenneth Arnold committed
184 185
		fprintf(stderr, "Incorrect option format: %s.\n", optarg);
		exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
186
	    }
Kenneth Arnold's avatar
Kenneth Arnold committed
187
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
188
	case 'h':
Kenneth Arnold's avatar
Kenneth Arnold committed
189 190
	    usage();
	    exit(0);
Jack Moffitt's avatar
Jack Moffitt committed
191
	case 'q':
Kenneth Arnold's avatar
Kenneth Arnold committed
192 193
	    opt.quiet++;
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
194
	case 'v':
Kenneth Arnold's avatar
Kenneth Arnold committed
195 196
	    opt.verbose++;
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
197
	case 'V':
198
	    fprintf(stderr, "Ogg123 from " PACKAGE " " VERSION "\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
199
	    exit(0);
Jack Moffitt's avatar
Jack Moffitt committed
200
	case 'z':
Kenneth Arnold's avatar
Kenneth Arnold committed
201 202
	    opt.shuffle = 1;
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
203
	case '?':
Kenneth Arnold's avatar
Kenneth Arnold committed
204
	    break;
Jack Moffitt's avatar
Jack Moffitt committed
205
	default:
Kenneth Arnold's avatar
Kenneth Arnold committed
206 207
	    usage();
	    exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
208 209
	}
    }
Kenneth Arnold's avatar
Kenneth Arnold committed
210 211 212

    /* Add last device to device list or use the default device */
    if (temp_driver_id < 0) {
213 214
	temp_driver_id = get_default_device();
	if(temp_driver_id < 0) {
215
		temp_driver_id = ao_default_driver_id();
216
	}
Kenneth Arnold's avatar
Kenneth Arnold committed
217 218
	if (temp_driver_id < 0) {
	    fprintf(stderr,
219
		    "Could not load default driver and no ~/.ogg123rc found. Exiting.\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
220
	    exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
221
	}
222 223
	opt.outdevices = append_device(opt.outdevices, temp_driver_id, 
				       temp_options, NULL);
Jack Moffitt's avatar
Jack Moffitt committed
224 225
    }

Kenneth Arnold's avatar
Kenneth Arnold committed
226 227 228
    if (optind == argc) {
	usage();
	exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
229 230
    }

Kenneth Arnold's avatar
Kenneth Arnold committed
231 232
    if (opt.shuffle) {
	int i;
233
	
Kenneth Arnold's avatar
Kenneth Arnold committed
234
	srand(time(NULL));
235 236 237 238 239 240

	for (i = optind; i < argc; i++) {
		int j = optind + rand() % (argc - i);
		char *temp = argv[i];
		argv[i] = argv[j];
		argv[j] = temp;
Jack Moffitt's avatar
Jack Moffitt committed
241 242 243
	}
    }

244 245 246 247 248 249
    while (optind < argc) {
	opt.read_file = argv[optind];
	play_file(opt);
	optind++;
    }

Kenneth Arnold's avatar
Kenneth Arnold committed
250
    while (opt.outdevices != NULL) {
Jack Moffitt's avatar
Jack Moffitt committed
251 252
      if (opt.outdevices->device)
        ao_close(opt.outdevices->device);
253 254 255
      current = opt.outdevices->next_device;
      free(opt.outdevices);
      opt.outdevices = current;
Kenneth Arnold's avatar
Kenneth Arnold committed
256
    }
Jack Moffitt's avatar
Jack Moffitt committed
257

258 259 260
    if (buffer != NULL)
	    buffer_shutdown(buffer);
    
Kenneth Arnold's avatar
Kenneth Arnold committed
261
    ao_shutdown();
262

Kenneth Arnold's avatar
Kenneth Arnold committed
263
    return (0);
Jack Moffitt's avatar
Jack Moffitt committed
264 265
}

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
/* Two signal handlers, one for SIGINT, and the second for
 * SIGALRM.  They are de/activated on an as-needed basis by the
 * player to allow the user to stop ogg123 or skip songs.
 */

void signal_skipfile(int which_signal)
{
  skipfile_requested = 1;

  /* libao, when writing wav's, traps SIGINT so it correctly
   * closes things down in the event of an interrupt.  We
   * honour this.   libao will re-raise SIGINT once it cleans
   * up properly, causing the application to exit.  This is 
   * desired since we would otherwise re-open output.wav 
   * and blow away existing "output.wav" file.
   */

  if (old_sig != NULL) {
    signal(which_signal,old_sig);
    raise(which_signal);
  }

}

void signal_activate_skipfile(int ignored)
{
  old_sig = signal(SIGINT,signal_skipfile);
}


296
void play_file(ogg123_options_t opt)
Jack Moffitt's avatar
Jack Moffitt committed
297
{
Kenneth Arnold's avatar
Kenneth Arnold committed
298
    /* Oh my gosh this is disgusting. Big cleanups here will include an
299 300 301
       almost complete rewrite of the hacked-out HTTP streaming and a shift
       to using callbacks for the vorbisfile input.
    */
Kenneth Arnold's avatar
Kenneth Arnold committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338

    OggVorbis_File vf;
    int current_section = -1, eof = 0, eos = 0, ret;
    int old_section = -1;
    long t_min = 0, c_min = 0, r_min = 0;
    double t_sec = 0, c_sec = 0, r_sec = 0;
    int is_big_endian = ao_is_big_endian();
    double realseekpos = opt.seekpos;

    /* Junk left over from the failed info struct */
    double u_time, u_pos;

    if (strcmp(opt.read_file, "-")) {	/* input file not stdin */
	if (!strncmp(opt.read_file, "http://", 7)) {
	    /* Stream down over http */
	    char *temp = NULL, *server = NULL, *port = NULL, *path = NULL;
	    int index;
	    long iport;

	    temp = opt.read_file + 7;
	    for (index = 0; temp[index] != '/' && temp[index] != ':';
		 index++);
	    server = (char *) malloc(index + 1);
	    strncpy(server, temp, index);
	    server[index] = '\0';

	    /* Was a port specified? */
	    if (temp[index] == ':') {
		/* Grab the port. */
		temp += index + 1;
		for (index = 0; temp[index] != '/'; index++);
		port = (char *) malloc(index + 1);
		strncpy(port, temp, index);
		port[index] = '\0';
		if ((iport = atoi(port)) <= 0 || iport > 65535) {
		    fprintf(stderr, "%s is not a valid port.\n", port);
		    exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
339
		}
Kenneth Arnold's avatar
Kenneth Arnold committed
340 341
	    } else
		iport = 80;
Jack Moffitt's avatar
Jack Moffitt committed
342

Kenneth Arnold's avatar
Kenneth Arnold committed
343 344 345 346 347
	    path = strdup(temp + index);

	    if ((opt.instream = http_open(server, iport, path)) == NULL) {
		fprintf(stderr, "Error while connecting to server!\n");
		exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
348
	    }
Kenneth Arnold's avatar
Kenneth Arnold committed
349 350 351 352 353 354 355
	    /* Send HTTP header */
	    fprintf(opt.instream,
		    "GET %s HTTP/1.0\r\n"
		    "Accept: */*\r\n"
		    "User-Agent: ogg123\r\n"
		    "Host: %s\r\n\r\n\r\n", path, server);

Michael Smith's avatar
 
Michael Smith committed
356 357
		fflush(opt.instream); /* Make sure these are all actually sent */

Kenneth Arnold's avatar
Kenneth Arnold committed
358
	    /* Dump headers */
Jack Moffitt's avatar
Jack Moffitt committed
359
	    {
Kenneth Arnold's avatar
Kenneth Arnold committed
360 361 362
		char last = 0, in = 0;
		int eol = 0;

363 364
		if (opt.verbose > 0)
		  fprintf(stderr, "HTTP Headers:\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
365 366 367
		for (;;) {
		    last = in;
		    in = getc(opt.instream);
368 369
		    if (opt.verbose > 0)
		      putc(in, stderr);
Kenneth Arnold's avatar
Kenneth Arnold committed
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
		    if (last == 13 && in == 10) {
			if (eol)
			    break;
			eol = 1;
		    } else if (in != 10 && in != 13)
			eol = 0;
		}
	    }
	    free(server);
	    free(path);
	} else {
	    if (opt.quiet < 1)
		fprintf(stderr, "Playing from file %s.\n", opt.read_file);
	    /* Open the file. */
	    if ((opt.instream = fopen(opt.read_file, "rb")) == NULL) {
		fprintf(stderr, "Error opening input file.\n");
		exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
387 388
	    }
	}
Kenneth Arnold's avatar
Kenneth Arnold committed
389 390 391 392
    } else {
	if (opt.quiet < 1)
	    fprintf(stderr, "Playing from standard input.\n");
	opt.instream = stdin;
Jack Moffitt's avatar
Jack Moffitt committed
393
    }
Kenneth Arnold's avatar
Kenneth Arnold committed
394 395 396 397

    if ((ov_open(opt.instream, &vf, NULL, 0)) < 0) {
	fprintf(stderr, "E: input not an Ogg Vorbis audio stream.\n");
	return;
Jack Moffitt's avatar
Jack Moffitt committed
398 399
    }

400 401 402 403
    /* Setup so that pressing ^C in the first second of playback
     * interrupts the program, but after the first second, skips
     * the song.  This functionality is similar to mpg123's abilities. */

Kenneth Arnold's avatar
Kenneth Arnold committed
404 405 406 407 408
    if (opt.delay > 0) {
        skipfile_requested = 0;
	signal(SIGALRM,signal_activate_skipfile);
	alarm(opt.delay);
    }
409

Kenneth Arnold's avatar
Kenneth Arnold committed
410 411 412 413 414
    while (!eof) {
	int i;
	vorbis_comment *vc = ov_comment(&vf, -1);
	vorbis_info *vi = ov_info(&vf, -1);

415
	if(open_audio_devices(&opt, vi->rate, vi->channels, &buffer) < 0)
Michael Smith's avatar
 
Michael Smith committed
416 417
		exit(1);

Kenneth Arnold's avatar
Kenneth Arnold committed
418
	if (opt.quiet < 1) {
Kenneth Arnold's avatar
Kenneth Arnold committed
419
	    if (eos && opt.verbose) fprintf (stderr, "\r                                                      \r\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
420 421
	    for (i = 0; i < vc->comments; i++) {
		char *cc = vc->user_comments[i];	/* current comment */
Jack Moffitt's avatar
Jack Moffitt committed
422 423
		int i;

Kenneth Arnold's avatar
Kenneth Arnold committed
424
		for (i = 0; ogg_comment_keys[i].key != NULL; i++)
425
		    if (!strncasecmp
Kenneth Arnold's avatar
Kenneth Arnold committed
426 427 428 429 430 431 432
			(ogg_comment_keys[i].key, cc,
			 strlen(ogg_comment_keys[i].key))) {
			fprintf(stderr, ogg_comment_keys[i].formatstr,
				cc + strlen(ogg_comment_keys[i].key));
			break;
		    }
		if (ogg_comment_keys[i].key == NULL)
433
		    fprintf(stderr, "Unrecognized comment: '%s'\n", cc);
Kenneth Arnold's avatar
Kenneth Arnold committed
434 435 436 437
	    }

	    fprintf(stderr, "\nBitstream is %d channel, %ldHz\n",
		    vi->channels, vi->rate);
438 439
	    if (opt.verbose > 1)
	      fprintf(stderr, "Encoded by: %s\n\n", vc->vendor);
Kenneth Arnold's avatar
Kenneth Arnold committed
440 441
	}

442
	if (opt.verbose > 0 && ov_seekable(&vf)) {
Kenneth Arnold's avatar
Kenneth Arnold committed
443 444 445 446 447
	    /* Seconds with double precision */
	    u_time = ov_time_total(&vf, -1);
	    t_min = (long) u_time / (long) 60;
	    t_sec = u_time - 60 * t_min;
	}
Jack Moffitt's avatar
Jack Moffitt committed
448

Kenneth Arnold's avatar
Kenneth Arnold committed
449 450
	if ((realseekpos > ov_time_total(&vf, -1)) || (realseekpos < 0))
	    /* If we're out of range set it to right before the end. If we set it
451
	     * right to the end when we seek it will go to the beginning of the song */
Kenneth Arnold's avatar
Kenneth Arnold committed
452 453 454 455 456 457
	    realseekpos = ov_time_total(&vf, -1) - 0.01;

	if (realseekpos > 0)
	    ov_time_seek(&vf, realseekpos);

	eos = 0;
458

Kenneth Arnold's avatar
Kenneth Arnold committed
459
	while (!eos) {
460 461 462

	    if (skipfile_requested) {
	      eof = eos = 1;
463 464
	      skipfile_requested = 0;
	      signal(SIGALRM,signal_activate_skipfile);
Kenneth Arnold's avatar
Kenneth Arnold committed
465
	      alarm(opt.delay);
466 467 468
	      if (buffer) {
		buffer_flush (buffer);
	      }
469 470 471
	      break;
  	    }

Kenneth Arnold's avatar
Kenneth Arnold committed
472 473 474 475 476 477 478
	    old_section = current_section;
	    ret =
		ov_read(&vf, convbuffer, sizeof(convbuffer), is_big_endian,
			2, 1, &current_section);
	    if (ret == 0) {
		/* End of file */
		eof = eos = 1;
479 480 481 482 483
	    } else if (ret == OV_HOLE) {
	      if (opt.verbose > 1) 
		/* we should be able to resync silently; if not there are 
		   bigger problems. */
		fprintf (stderr, "Warning: hole in the stream; probably harmless\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
484
	    } else if (ret < 0) {
485 486
	      /* Stream error */
	      fprintf(stderr, "Error: libvorbis reported a stream error.\n");
Kenneth Arnold's avatar
Kenneth Arnold committed
487 488 489 490 491
	    } else {
		/* did we enter a new logical bitstream */
		if (old_section != current_section && old_section != -1)
		    eos = 1;

492 493 494 495 496 497 498 499 500 501 502
		if (buffer)
		  {
		    chunk_t chunk;
		    chunk.len = ret;
		    memcpy (chunk.data, convbuffer, ret);
		    
		    submit_chunk (buffer, chunk);
		  }
		else
		  devices_write(convbuffer, ret, opt.outdevices);
		
Kenneth Arnold's avatar
Kenneth Arnold committed
503
		if (opt.verbose > 0) {
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
		    if (ov_seekable (&vf)) {
		      u_pos = ov_time_tell(&vf);
		      c_min = (long) u_pos / (long) 60;
		      c_sec = u_pos - 60 * c_min;
		      r_min = (long) (u_time - u_pos) / (long) 60;
		      r_sec = (u_time - u_pos) - 60 * r_min;
		      fprintf(stderr,
			      "\rTime: %02li:%05.2f [%02li:%05.2f] of %02li:%05.2f, Bitrate: %.1f   \r",
			      c_min, c_sec, r_min, r_sec, t_min, t_sec,
			      (float) ov_bitrate_instant(&vf) / 1000.0F);
		    } else {
		      /* working around a bug in vorbisfile */
		      u_pos = (double) ov_pcm_tell(&vf) / (double) vi->rate;
		      c_min = (long) u_pos / (long) 60;
		      c_sec = u_pos - 60 * c_min;
		      fprintf(stderr,
			      "\rTime: %02li:%05.2f, Bitrate: %.1f   \r",
			      c_min, c_sec,
			      (float) ov_bitrate_instant (&vf) / 1000.0F);
		    }
Jack Moffitt's avatar
Jack Moffitt committed
524
		}
Kenneth Arnold's avatar
Kenneth Arnold committed
525
	    }
Jack Moffitt's avatar
Jack Moffitt committed
526
	}
Kenneth Arnold's avatar
Kenneth Arnold committed
527
    }
Jack Moffitt's avatar
Jack Moffitt committed
528

529 530 531 532
    alarm(0);
    signal(SIGALRM,SIG_DFL);
    signal(SIGINT,old_sig);

Kenneth Arnold's avatar
Kenneth Arnold committed
533 534 535 536
    ov_clear(&vf);

    if (opt.quiet < 1)
	fprintf(stderr, "\nDone.\n");
Jack Moffitt's avatar
Jack Moffitt committed
537 538
}

Jack Moffitt's avatar
Jack Moffitt committed
539
int get_tcp_socket(void)
Jack Moffitt's avatar
Jack Moffitt committed
540
{
Kenneth Arnold's avatar
Kenneth Arnold committed
541
    return socket(AF_INET, SOCK_STREAM, 0);
Jack Moffitt's avatar
Jack Moffitt committed
542 543
}

Kenneth Arnold's avatar
Kenneth Arnold committed
544
FILE *http_open(char *server, int port, char *path)
Jack Moffitt's avatar
Jack Moffitt committed
545
{
Kenneth Arnold's avatar
Kenneth Arnold committed
546 547 548
    int sockfd = get_tcp_socket();
    struct hostent *host;
    struct sockaddr_in sock_name;
Jack Moffitt's avatar
Jack Moffitt committed
549

Kenneth Arnold's avatar
Kenneth Arnold committed
550 551
    if (sockfd == -1)
	return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
552

Kenneth Arnold's avatar
Kenneth Arnold committed
553 554 555
    if (!(host = gethostbyname(server))) {
	fprintf(stderr, "Unknown host: %s\n", server);
	return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
556 557
    }

Kenneth Arnold's avatar
Kenneth Arnold committed
558 559 560
    memcpy(&sock_name.sin_addr, host->h_addr, host->h_length);
    sock_name.sin_family = AF_INET;
    sock_name.sin_port = htons(port);
Jack Moffitt's avatar
Jack Moffitt committed
561

Kenneth Arnold's avatar
Kenneth Arnold committed
562 563 564 565
    if (connect(sockfd, (struct sockaddr *) &sock_name, sizeof(sock_name))) {
	if (errno == ECONNREFUSED)
	    fprintf(stderr, "Connection refused\n");
	return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
566
    }
Kenneth Arnold's avatar
Kenneth Arnold committed
567 568
    return fdopen(sockfd, "r+b");
}
Michael Smith's avatar
 
Michael Smith committed
569

570
int open_audio_devices(ogg123_options_t *opt, int rate, int channels, buf_t **buffer)
Michael Smith's avatar
 
Michael Smith committed
571
{
572 573
  static int prevrate=0, prevchan=0;
  devices_t *current;
574 575
  ao_sample_format format;

576 577 578 579
  if(prevrate == rate && prevchan == channels)
    return 0;
  
  if(prevrate !=0 && prevchan!=0)
Michael Smith's avatar
 
Michael Smith committed
580
	{
581
	  if (buffer != NULL && *buffer != NULL) {
582 583 584 585 586 587 588 589 590
	    buffer_shutdown (*buffer);
	    *buffer = NULL;
	  }

	  current = opt->outdevices;
	  while (current != NULL) {
	    ao_close(current->device);
	    current = current->next_device;
	  }
Michael Smith's avatar
 
Michael Smith committed
591
	}
592
  
593 594 595 596 597
  format.rate = prevrate = rate;
  format.channels = prevchan = channels;
  format.bits = 16;
  format.byte_format = AO_FMT_NATIVE;

598 599
  current = opt->outdevices;
  while (current != NULL) {
600
    ao_info *info = ao_driver_info(current->driver_id);
601 602 603 604 605 606
    
    if (opt->verbose > 0) {
      fprintf(stderr, "Device:   %s\n", info->name);
      fprintf(stderr, "Author:   %s\n", info->author);
      fprintf(stderr, "Comments: %s\n", info->comment);
      fprintf(stderr, "\n");	
Michael Smith's avatar
 
Michael Smith committed
607
    }
608
    
609 610 611 612 613 614 615
    if (current->filename == NULL)
      current->device = ao_open_live(current->driver_id, &format,
				     current->options);
    else
      current->device = ao_open_file(current->driver_id, current->filename,
				     0, &format, current->options);

616
    if (current->device == NULL) {
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
      switch (errno) {
      case AO_ENODRIVER:
	fprintf(stderr, "Error: No device not available.\n");
	break;
      case AO_ENOTLIVE:
	fprintf(stderr, "Error: %s requires an output filename to be specified with -f.\n", info->short_name);
	break;
      case AO_EBADOPTION:
	fprintf(stderr, "Error: Unsupported option value to %s device.\n",
		info->short_name);
	break;
      case AO_EOPENDEVICE:
	fprintf(stderr, "Error: Cannot open device %s.\n",
		info->short_name);
	break;
      case AO_EFAIL:
	fprintf(stderr, "Error: Device failure.\n");
	break;
      case AO_ENOTFILE:
	fprintf(stderr, "Error: An output file cannot be given for %s device.\n", info->short_name);
	break;
      case AO_EOPENFILE:
	fprintf(stderr, "Error: Cannot open file %s for writing.\n",
		current->filename);
	break;
      case AO_EFILEEXISTS:
	fprintf(stderr, "Error: File %s already exists.\n", current->filename);
	break;
      default:
	fprintf(stderr, "Error: This error should never happen.  Panic!\n");
	break;
      }
	
650 651 652 653 654 655 656 657
      return -1;
    }
    current = current->next_device;
  }
  
  if (opt->buffer_size)
    *buffer = fork_writer (opt->buffer_size, opt->outdevices);
  
Michael Smith's avatar
 
Michael Smith committed
658 659
    return 0;
}