1 /*-
2  * Copyright (c) 2001 Yar Tikhiy <yar@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #ifndef lint
37 static const char rcsid[] =
38     "$Id: mp3ck.c,v 1.4 2005/04/03 04:49:22 yarq Exp $";
39 #endif
40 
41 #define MPEG_HDR_SIZE	4
42 #define MPEG_SPF	1152L	/* samples per frame XXX: not true for L1 */
43 
44 #define MPEG_VERID(c)	(((c) >> 3) & 3)
45 #define MPEG_LAYERD(c)	(((c) >> 1) & 3)
46 #define MPEG_PROT(c)	((c) & 1)
47 
48 #define MPEG_BRIDX(c)	(((c) >> 4) & 0xf)
49 #define MPEG_FRIDX(c)	(((c) >> 2) & 3)
50 #define MPEG_PAD(c)	(((c) >> 1) & 1)
51 #define MPEG_PRIV(c)	((c) & 1)
52 
53 #define	MPEG_CHAN(c)	(((c) >> 6) & 3)
54 #define	MPEG_MODEX(c)	(((c) >> 4) & 3)
55 #define	MPEG_CPRT(c)	(((c) >> 3) & 1)
56 #define	MPEG_ORIG(c)	(((c) >> 2) & 1)
57 #define	MPEG_EMPH(c)	((c) & 3)
58 
59 #define MPEG_LAYER(c)	(4 - MPEG_LAYERD(c))
60 /*
61  * NB: ver == 4 for MPEG2.5
62  */
63 #define MPEG_VER(c)	(4 - MPEG_VERID(c))
64 #define MPEG_BRSELECTOR(ver, layer) \
65 	((ver) == 1 ? (layer) - 1 : ((layer) == 1 ? 3 : 4))
66 #define MPEG_BITRATE(c, ver, layer) \
67 	(bitratetab[MPEG_BRSELECTOR((ver), (layer))][MPEG_BRIDX(c)])
68 #define	MPEG_FREQ(c, ver) \
69 	(freqtab[(ver) == 4 ? 2 : (ver) - 1][MPEG_FRIDX(c)])
70 
71 #define ID3V1_SIZE	128
72 #define ID3V1_LABEL_LEN	3
73 
74 #define ID3V2_LABEL_LEN	3
75 /*
76  * This struct doesn't include the "ID3" label on purpose.
77  * See the function id3v2().
78  */
79 struct id3v2 {
80 	/* unsigned char id3[3]; */
81 	unsigned char major;
82 	unsigned char revision;
83 	unsigned char flags;
84 	unsigned char size[4];
85 };
86 
87 #define	ID3V2_FLAG_UNSYNC	0x80
88 #define ID3V2_FLAG_EXTHDR	0x40
89 #define ID3V2_FLAG_EXP		0x20
90 #define ID3V2_FLAG_FOOTER	0x10
91 
92 #define SYNCSAFE32(n) ((n)[3] + 128 * ((n)[2] + 128 * ((n)[1] + 128 * (n)[0])))
93 
94 #define	MSG_NONE	0	/* don't forget to update this! */
95 #define	MSG_ERR		0
96 #define	MSG_WRN		1
97 #define	MSG_NTC		2
98 #define	MSG_NFO		3
99 #define	MSG_DBG		4
100 #define	MSG_ALL		4	/* don't forget to update this! */
101 
102 int pedantic = 0;		/* treat some warnings as errors */
103 int time_offset = 0;		/* show offsets as mm:ss if possible */
104 int verbosity = MSG_NTC;	/* how much output to produce */
105 
106 static int  check(const char *);
107 static void frametotime(long, long *, long *);
108 static void usage(void);
109 
110 int
main(int argc,char ** argv)111 main(int argc, char **argv)
112 {
113 	int opt;
114 	int rc, maxrc;
115 
116 	while ((opt = getopt(argc, argv, "dhpqstv")) != -1)
117 		switch (opt) {
118 		case 'd':
119 			verbosity = MSG_ALL;
120 			break;
121 		case 'p':
122 			pedantic = 1;
123 			break;
124 		case 'q':
125 			verbosity--;
126 			break;
127 		case 's':
128 			verbosity = MSG_NONE;
129 			break;
130 		case 't':
131 			time_offset = 1;
132 			break;
133 		case 'v':
134 			verbosity++;
135 			break;
136 		case 'h':
137 		case '?':
138 			usage();
139 			/* NOTREACHED */
140 		}
141 
142 	argc -= optind;
143 	argv += optind;
144 	if (argc < 1)
145 		usage();
146 
147 	maxrc = 0;
148 	for (; *argv; argv++) {
149 		rc = check(*argv);
150 		if (rc > maxrc)
151 			maxrc = rc;
152 	}
153 
154 	/*
155 	 * 0 - OK
156 	 * 1 - MPEG format error
157 	 * 2 - file or system error
158 	 */
159 	if (maxrc > 2)
160 		maxrc = 2;
161 	exit(maxrc);
162 	return (0);	/* make cc happy */
163 }
164 
165 long bitratetab[][16] = {
166 	/* 0: V1, L1 */
167 	{ -1, 32000L, 64000L, 96000L, 128000L, 160000L, 192000L, 224000L,
168 	    256000L, 288000L, 320000L, 352000L, 384000L, 416000L, 448000L, -1 },
169 	/* 1: V1, L2 */
170 	{ -1, 32000L, 48000L, 56000L, 64000L, 80000L, 96000L, 112000L,
171 	    128000L, 160000L, 192000L, 224000L, 256000L, 320000L, 384000L, -1 },
172 	/* 2: V1, L3 */
173 	{ -1, 32000L, 40000L, 48000L, 56000L, 64000L, 80000L, 96000L,
174 	    112000L, 128000L, 160000L, 192000L, 224000L, 256000L, 320000L, -1 },
175 	/* 3: V2, L1 */
176 	{ -1, 32000L, 48000L, 56000L, 64000L, 80000L, 96000L, 112000L,
177 	    128000L, 144000L, 160000L, 176000L, 192000L, 224000L, 256000L, -1 },
178 	/* 4: V2, L2 & L3 */
179 	{ -1, 8000L, 16000L, 24000L, 32000L, 40000L, 48000L, 56000L,
180 	    64000L, 80000L, 96000L, 112000L, 128000L, 144000L, 160000L, -1 }
181 };
182 
183 long freqtab[][4] = {
184 	{ 44100L, 48000L, 32000L, -1 },
185 	{ 22050L, 24000L, 16000L, -1 },
186 	{ 11025L, 12000L, 8000L, -1 }
187 };
188 
189 enum state {START, SYNC, FRAME, ID3V1, ID3V2, FINISH, QUIT, NOSTATE};
190 
191 const char *file;	/* current file name */
192 FILE *fp;		/* current file handle */
193 int  c;			/* current byte */
194 int  insync;		/* if we're seeing what we expected */
195 long goodend;		/* offset right beyond last good frame or tag */
196 long nframes;		/* total audio frames seen */
197 long where;		/* starting offset of current frame/tag (for wrn()) */
198 int  rc;		/* what to return to upper layer */
199 int  vbr;		/* current file has VBR */
200 
201 /* for final info/statistics */
202 long kbps;		/* sum of kbps of seen frames if VBR */
203 long hz;
204 int  chanmode;
205 int  hascrc;
206 int  hasid3v1;
207 int  hasid3v2;
208 
209 static enum state start(void);
210 static enum state sync1(void);
211 static enum state frame(void);
212 static enum state id3v1(void);
213 static enum state id3v2(void);
214 static enum state finish(void);
215 
216 static void wrn(int, long, const char *, ...)
217 #ifdef __GNUC__
218 	__attribute__ ((format (printf, 3, 4)))
219 #endif
220 ;
221 static void wrnsync(void);
222 static void diffhdr(const char *, const char *);
223 
224 static int
check(const char * fn)225 check(const char *fn)
226 {
227 	enum state state;
228 
229 	file = fn;
230 
231 	for (state = START; state != QUIT;)
232 		switch (state) {
233 		case START:
234 			state = start();
235 			break;
236 		case SYNC:
237 			state = sync1();
238 			break;
239 		case FRAME:
240 			state = frame();
241 			break;
242 		case ID3V1:
243 			state = id3v1();
244 			break;
245 		case ID3V2:
246 			state = id3v2();
247 			break;
248 		case FINISH:
249 			state = finish();
250 			break;
251 		default:
252 			printf("Can't happen: unknown state\n");
253 			exit(2);
254 		}
255 	return (rc);
256 }
257 
258 static enum state
start()259 start()
260 {
261 
262 	if ((fp = fopen(file, "rb")) == NULL) {
263 		rc = 2;
264 		perror(file);
265 		return (QUIT);
266 	}
267 
268 	c = EOF;
269 	insync = 1;
270 	goodend = 0;
271 	nframes = 0;
272 	hasid3v1 = hasid3v2 = 0;
273 	vbr = 0;
274 	rc = 0;
275 
276 	return (SYNC);
277 }
278 
279 /*
280  * When transitioning to this state:
281  * Set `c' to EOF to proceed to the next byte.
282  * Leave `c' untouched to restart sync'ing from the current position.
283  */
284 static enum state
sync1()285 sync1()
286 {
287 
288 	if (c == EOF) /* allow restarting from the same stream position */
289 		c = getc(fp);
290 
291 	where = ftell(fp) - 1;
292 
293 	switch (c) {
294 	case EOF:
295 		if (ftell(fp) == 0) {
296 			rc = 1;
297 			wrn(MSG_WRN, -1, "empty file");
298 		} else if (nframes == 0) {
299 			rc = 1;
300 			wrn(MSG_WRN, -1, "no audio frames");
301 		} else if (!insync) {
302 			if (pedantic)
303 				rc = 1;
304 			wrn(MSG_NTC, goodend, "trailing junk");
305 		}
306 		return (FINISH);
307 		/* NOTREACHED */
308 
309 	case 'I':
310 		if (insync)
311 			return (ID3V2);
312 		else {
313 			c = EOF;
314 			return (SYNC);
315 		}
316 		/* NOTREACHED */
317 
318 	case 'T':
319 		if (insync)
320 			return (ID3V1);
321 		else {
322 			c = EOF;
323 			return (SYNC);
324 		}
325 		/* NOTREACHED */
326 
327 	case 0xff:
328 		return (FRAME);
329 		/* NOTREACHED */
330 
331 	default:
332 		wrnsync();
333 		insync = 0;
334 		c = EOF;
335 		return (SYNC);
336 		/* NOTREACHED */
337 	}
338 
339 	return (NOSTATE);
340 }
341 
342 static enum state
frame()343 frame()
344 {
345 	int  i;
346 	int  ver, layer, prot, pad;
347 	long bitrate, freq;
348 	long framelen, n;
349 	char hdr[MPEG_HDR_SIZE - 1];
350 	static char phdr[MPEG_HDR_SIZE - 1];
351 
352 	i = 0;
353 
354 	if ((c = getc(fp)) == EOF) {
355 		rc = 1;
356 		wrn(MSG_WRN, 0, "unexpected EOF at frame header+1");
357 		return (FINISH);
358 	}
359 	if ((c & 0xe0) != 0xe0) {
360 		wrnsync();
361 		insync = 0;
362 		return (SYNC);
363 	}
364 	hdr[i++] = c;
365 
366 	ver = MPEG_VER(c);
367 	switch (ver) {
368 	case 1:
369 	case 2:
370 	case 4:	/* 2.5 */
371 		break;
372 	case 3:
373 		rc = 1;
374 		wrn(MSG_WRN, 0, "invalid MPEG version ID");
375 		insync = 0;
376 		return (SYNC);
377 		/* NOTREACHED */
378 	default:
379 		abort();
380 		/* NOTREACHED */
381 	}
382 
383 	layer = MPEG_LAYER(c);
384 	switch (layer) {
385 	case 1:
386 		rc = 1;
387 		wrn(MSG_WRN, 0, "layer I unsupported");
388 		insync = 0;
389 		return (SYNC);
390 		/* NOTREACHED */
391 	case 2:
392 	case 3:
393 		break;
394 	case 4:
395 		rc = 1;
396 		wrn(MSG_WRN, 0, "invalid layer description");
397 		insync = 0;
398 		return (SYNC);
399 		/* NOTREACHED */
400 	default:
401 		abort();
402 		/* NOTREACHED */
403 	}
404 
405 	prot = MPEG_PROT(c);
406 #if notyet
407 	crc = 0xffff;
408 #endif
409 
410 	if ((c = getc(fp)) == EOF) {
411 		rc = 1;
412 		wrn(MSG_WRN, 0, "unexpected EOF at frame header+2");
413 		return (FINISH);
414 	}
415 #if notyet
416 	if (prot)
417 		crcupdate(c, &crc);
418 #endif
419 	hdr[i++] = c;
420 
421 	bitrate = MPEG_BITRATE(c, ver, layer);
422 	if (bitrate < 0) {
423 		rc = 1;
424 		wrn(MSG_WRN, 0, "invalid bitrate index");
425 		insync = 0;
426 		return (SYNC);
427 	}
428 
429 	freq = MPEG_FREQ(c, ver);
430 	if (freq < 0) {
431 		rc = 1;
432 		wrn(MSG_WRN, 0, "invalid sampling frequency index");
433 		insync = 0;
434 		return (SYNC);
435 	}
436 
437 	pad = MPEG_PAD(c);
438 
439 	if ((c = getc(fp)) == EOF) {
440 		rc = 1;
441 		wrn(MSG_WRN, 0, "unexpected EOF at frame header+3");
442 		return (FINISH);
443 	}
444 #if notyet
445 	if (prot)
446 		crcupdate(c, &crc);
447 #endif
448 	hdr[i++] = c;
449 
450 	framelen = MPEG_SPF / 8 * bitrate / freq + pad;
451 
452 	if (!insync) {
453 		rc = 1;
454 		wrn(MSG_WRN, 0,
455 		    "sync after %ld bytes of junk,", where - goodend);
456 	}
457 	wrn(MSG_DBG, -1, "frame br %ld hz %ld pad %d%s len %ld "
458 	       "at %#lx next %#lx",
459 	       bitrate, freq, pad, prot ? " CRC" : "", framelen,
460 	       ftell(fp) - MPEG_HDR_SIZE,
461 	       ftell(fp) - MPEG_HDR_SIZE + framelen);
462 	if (nframes) {
463 		diffhdr(phdr, hdr);
464 		if (vbr)
465 			kbps += bitrate / 1000;
466 	} else {
467 		kbps = bitrate / 1000;
468 		hz = freq;
469 		hascrc = prot;
470 		chanmode = MPEG_CHAN(c);
471 	}
472 
473 	memcpy(phdr, hdr, sizeof(hdr) / sizeof(hdr[0]));
474 
475 	/* skip the rest of the frame */
476 	for (n = framelen - MPEG_HDR_SIZE; n; n--)
477 		if ((c = getc(fp)) == EOF) {
478 			rc = 1;
479 			wrn(MSG_WRN, 0, "last frame incomplete "
480 			       "(%ld of %ld bytes missing)", n, framelen);
481 			return (FINISH);
482 		}
483 
484 	c = EOF;
485 	insync = 1;
486 	goodend = ftell(fp);
487 	nframes++;
488 	return (SYNC);
489 }
490 
491 static enum state
id3v1()492 id3v1()
493 {
494 	int n;
495 
496 	if ((c = getc(fp)) == EOF) {
497 		if (pedantic)
498 			rc = 1;
499 		wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v1 tag?)");
500 		return (FINISH);
501 	}
502 	if (c != 'A') {
503 		insync = 0;
504 		return (SYNC);
505 	}
506 	if ((c = getc(fp)) == EOF) {
507 		if (pedantic)
508 			rc = 1;
509 		wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v1 tag?)");
510 		return (FINISH);
511 	}
512 	if (c != 'G') {
513 		insync = 0;
514 		return (SYNC);
515 	}
516 
517 	hasid3v1 = 1;
518 	wrn(MSG_DBG, -1, "ID3v1 tag at %#lx", ftell(fp) - 3);
519 
520 	for (n = ID3V1_SIZE - ID3V1_LABEL_LEN; n; n--)
521 		if ((c = getc(fp)) == EOF) {
522 			rc = 1;
523 			wrn(MSG_WRN, 0, "ID3v1 tag incomplete "
524 			       "(%d of %d bytes missing)",
525 			       n, ID3V1_SIZE);
526 			return (FINISH);
527 		}
528 	if ((c = getc(fp)) == EOF)
529 		return (FINISH);
530 
531 	rc = 1;
532 	wrn(MSG_WRN, 0, "ID3v1 in the middle of the file");
533 	insync = 1;
534 	return (SYNC);
535 }
536 
537 static enum state
id3v2()538 id3v2()
539 {
540 	long idlen, n;
541 	struct id3v2 id;
542 
543 	if ((c = getc(fp)) == EOF) {
544 		if (pedantic)
545 			rc = 1;
546 		wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v2 tag?)");
547 		return (FINISH);
548 	}
549 	if (c != 'D') {
550 		wrnsync();
551 		insync = 0;
552 		return (SYNC);
553 	}
554 	if ((c = getc(fp)) == EOF) {
555 		if (pedantic)
556 			rc = 1;
557 		wrn(MSG_NTC, goodend, "trailing junk (truncated ID3v2 tag?)");
558 		return (FINISH);
559 	}
560 	if (c != '3') {
561 		wrnsync();
562 		insync = 0;
563 		return (SYNC);
564 	}
565 
566 	hasid3v2 = 1;
567 
568 	if (fread(&id, sizeof(id), 1, fp) != 1) {
569 		rc = 1;
570 		wrn(MSG_WRN, 0, "ID3v2 tag incomplete");
571 		return (FINISH);
572 	}
573 	if (id.major < 2 || id.major > 4) {
574 		rc = 1;
575 		wrn(MSG_WRN, 0, "ID3v2.%d.%d unsupported", id.major, id.revision);
576 		insync = 0;
577 		return (SYNC);
578 	}
579 
580 	idlen = ID3V2_LABEL_LEN + sizeof(id) + SYNCSAFE32(id.size);
581 	if (id.flags & ID3V2_FLAG_FOOTER)
582 		idlen += ID3V2_LABEL_LEN + sizeof(id);
583 
584 	wrn(MSG_DBG, -1, "ID3v2.%d.%d tag len %ld at %#lx next %#lx",
585 	      id.major, id.revision, idlen,
586 	      ftell(fp) - ID3V2_LABEL_LEN - sizeof(id),
587 	      ftell(fp) - ID3V2_LABEL_LEN - sizeof(id) + idlen);
588 
589 	for (n = idlen - ID3V2_LABEL_LEN - sizeof(id); n; n--)
590 		if ((c = getc(fp)) == EOF) {
591 			rc = 1;
592 			wrn(MSG_WRN, 0, "ID3v2 tag incomplete "
593 			       "(%ld of %ld bytes missing)", n, idlen);
594 			return (FINISH);
595 		}
596 	c = EOF;
597 	insync = 1;
598 	goodend = ftell(fp);
599 	return (SYNC);
600 }
601 
602 static enum state
finish()603 finish()
604 {
605 	long mm, ss;
606 	const char *st;
607 
608 	if (ferror(fp)) {
609 		rc = 2;
610 		perror(file);
611 	}
612 	fclose(fp);
613 
614 	if (nframes) {
615 		frametotime(nframes, &mm, &ss);
616 		switch (chanmode) {
617 		case 0:
618 			st = "stereo";
619 			break;
620 		case 1:
621 			st = "joint stereo";
622 			break;
623 		case 2:
624 			st = "dual stereo";
625 			break;
626 		case 3:
627 			st = "mono";
628 			break;
629 		default:
630 			abort();
631 		}
632 		wrn(MSG_NFO, -1, "%s%ld kbps %ld hz %s%s%s%s (%ld:%02ld)",
633 		        vbr ? "VBR/" : "",
634 			vbr ? kbps / nframes : kbps,
635 			hz, st,
636 			hascrc ? " CRC" : "",
637 			hasid3v1 ? " ID3v1" : "",
638 			hasid3v2 ? " ID3v2" : "",
639 			mm, ss);
640 	}
641 
642 	return (QUIT);
643 }
644 
645 static void
wrn(int level,long offset,const char * fmt,...)646 wrn(int level, long offset, const char *fmt, ...)
647 {
648 	long mm, ss;
649 	va_list ap;
650 
651 	if (level > verbosity)
652 		return;
653 	va_start(ap, fmt);
654 	printf("%s: ", file);
655 	vprintf(fmt, ap);
656 	if (offset >= 0) {
657 		if (time_offset && offset == 0) {
658 			frametotime(nframes, &mm, &ss);
659 			printf(" at %ld:%02ld", mm, ss);
660 		} else
661 			printf(" at byte offset %#lx", offset ? offset : where);
662 	}
663 	printf("\n");
664 	va_end(ap);
665 }
666 
667 /*
668  * Don't print the same "lost sync" twice.
669  */
670 static void
wrnsync()671 wrnsync()
672 {
673 	if (insync) {
674 		if (goodend == 0) {
675 			rc = 1;
676 			wrn(MSG_WRN, -1, "leading junk");
677 		} else
678 			/*
679 			 * warning on lost sync is issued after
680 			 * its nature gets clear: leading or trailing junk,
681 			 * broken middle of file etc.
682 			 */
683 			wrn(MSG_DBG, 0, "lost sync");
684 	}
685 }
686 
687 /*
688  * Check MPEG header fields that must be consistent
689  * through a whole file.
690  */
691 static void
diffhdr(const char * h1,const char * h2)692 diffhdr(const char *h1, const char *h2)
693 {
694 
695 #define DIFFTEST(byte, macro, msg)					\
696 	if (MPEG_##macro(h1[byte]) != MPEG_##macro(h2[byte])) {		\
697 		rc = 1;							\
698 		wrn(MSG_WRN, 0, "MPEG " msg " inconsistency (%d -> %d)",\
699 		    MPEG_##macro(h1[byte]), MPEG_##macro(h2[byte]));	\
700 	}
701 
702 #define DIFFTEST2(byte, macro, msg)					\
703 	if (MPEG_##macro(h1[byte], MPEG_VER(h1[0])) !=			\
704 	    MPEG_##macro(h2[byte], MPEG_VER(h2[0]))) {			\
705 		rc = 1;							\
706 		wrn(MSG_WRN, 0, "MPEG " msg " inconsistency (%ld -> %ld)",\
707 		    MPEG_##macro(h1[byte], MPEG_VER(h1[0])),		\
708 		    MPEG_##macro(h2[byte], MPEG_VER(h2[0])));		\
709 	}
710 
711 	DIFFTEST(0, VERID, "version ID");
712 	DIFFTEST(0, LAYERD, "layer descriptor");
713 	DIFFTEST(0, PROT, "protection bit");
714 	/*DIFFTEST2(1, BITRATE, "bitrate");*/
715 	if (!vbr &&
716 	    MPEG_BITRATE(h1[1], MPEG_VER(h1[0]), MPEG_LAYER(h1[0])) !=
717 	    MPEG_BITRATE(h2[1], MPEG_VER(h2[0]), MPEG_LAYER(h2[0]))) {
718 		vbr = 1;
719 		kbps *= nframes; /* if VBR, kbps holds sum of bitrates */
720 	}
721 	DIFFTEST2(1, FREQ, "sampling rate frequency");
722 	DIFFTEST(1, PRIV, "private bit");
723 	DIFFTEST(2, CHAN, "channel mode");
724 	DIFFTEST(2, CPRT, "copyright bit");
725 	DIFFTEST(2, ORIG, "original bit");
726 	DIFFTEST(2, EMPH, "emphasis");
727 }
728 
729 static void
frametotime(long frame,long * min,long * sec)730 frametotime(long frame, long *min, long *sec)
731 {
732 	long t;
733 
734 	if (frame)
735 		t = frame * MPEG_SPF / hz; /* XXX uses global var */
736 	else
737 		t = 0;	/* Don't use hz in this case since it is 0 */
738 	*min = t / 60;
739 	*sec = t % 60;
740 }
741 
742 static void
usage()743 usage()
744 {
745 	printf("usage: mp3ck [-dhqstv] file ...\n");
746 	exit(2);
747 }
748