1 /* lsmp3.c *************************************** UPDATED: 1999-04-08 23:38 TT
2 *
3 * Copyright : (C) 1999 Tormod Tjaberg
4 * Author : Tormod Tjaberg (tjaberg@online.no)
5 * Ripper : Zeograd (zeograd@caramail.com)
6 * Created : 1999-02-14
7 * Ripped : around 1999-11-21
8 * Description : Display the ID3 tag v1.0 & v1.1 of an MP3 file
9 * Author : Tormod Tjaberg
10 *
11 * Note:
12 * . Were shamelessly ripped from zeograd to know MP3 length for Hu-Go!
13 *
14 * . Genres were taken from Winamp 2.091 using doctored files :)
15 *
16 * . TAG code info is based on ID3-Tag Specification V1.1 by Michael Mutschler
17 * http://tick.informatik.uni-stuttgart.de/~mutschml/MP3ext/ID3-Tag.html
18 *
19 * . The MP3 header documentation is gleaned from several sources.
20 * I could not find a definitive source which explained all versions.
21 *
22 * . The "Private Life Of MP3 frames"
23 * <http://www.id3.org>
24 *
25 * . The Perl script "mp3tag.pm" by Peter D. Kovacs sent to me by Ole
26 * <http://www.egr.uri.edu/~kovacsp>
27 *
28 * . A lot of the header decoder info is from Oliver Fromme's "mp3asm"
29 * <http://mpg.123.org>
30 *
31 * Changes:
32 * 1999-04-08: 1.17: Exit properly after displaying "usage" text
33 * 1999-03-07: 1.16: Cosmetic change
34 * 1999-02-26: 1.15: Even more info, restructured layout
35 * 1999-02-26: 1.10: Support for MP3 frame info
36 * 1999-02-14: 1.00: Initial release, TAG's only
37 */
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <stdarg.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <limits.h>
44 #include <assert.h>
45
46 #define PROGRAM_NAME "lsmp3"
47 #define PROGRAM_VER "1.17"
48
49 #define ID3_TAG_LEN 128
50 #define TAG_LEN 3
51 #define SONGNAME_LEN 30
52 #define ARTIST_LEN 30
53 #define ALBUM_LEN 30
54 #define YEAR_LEN 4
55 #define COMMENT_LEN 30
56 #define NUM_GENRE 148
57 #define FRAME_SIZ 2048
58
59 /* Clever little option mimic wildcard expansion from
60 * shell ONLY available with the Zortech compiler ver 3.0x and upwards
61 */
62 #if defined(__ZTC__)
63 extern int _cdecl __wildcard;
64 int *__wild = &__wildcard;
65 #endif
66
67 /* Needs to be at least 4 bytes */
68 typedef unsigned long uint4;
69
70 /*
71 * 1st index: 0 = "MPEG 1.0", 1 = "MPEG 2.0"
72 * 2nd index: 0 unused, 1 = "Layer 3", 2 = "Layer 2", 3 = "Layer 1"
73 * 3rd index: BitRateIdx index from frame header
74 */
75 int KbPerSecTab[2][4][16] = {
76 {
77 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
78 {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320},
79 {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384},
80 {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}
81 },
82 {
83 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
84 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
85 {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160},
86 {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}
87 }
88 };
89
90 unsigned int FrqTab[] = { 44100, 48000, 32000, 22050, 24000, 16000, 11025 };
91 char *EmphasisTbl[] = { "none", "50/15ms", "unknown", "CCITTj.17" };
92
93 char
fBufferEmpty(char * pBuffer)94 fBufferEmpty (char *pBuffer)
95 {
96 if (pBuffer == NULL)
97 return 1;
98
99 for (; isspace (*pBuffer) && *pBuffer != '\0'; pBuffer++)
100 ;
101
102 if (*pBuffer == '\0')
103 return 1;
104 else
105 return 0;
106 }
107
108
109 float
MP3_length(char * argv)110 MP3_length (char *argv)
111 {
112 FILE *fp;
113 char *pFileSpec;
114 long FilePos;
115 char TagStr[TAG_LEN + 1];
116 char SongNameStr[SONGNAME_LEN + 1];
117 char ArtistStr[ARTIST_LEN + 1];
118 char AlbumStr[ALBUM_LEN + 1];
119 char YearStr[YEAR_LEN + 1];
120 char CommentStr[COMMENT_LEN + 1];
121 unsigned char *pBuf;
122 unsigned char *pBufTrav;
123 unsigned char *pBufEnd;
124 int Layer;
125 int BitRateIdx;
126 int Frq;
127 int MajorVer;
128 int MinorVer = 0;
129 int KbPerSec = 0;
130 int Mode;
131 int Emphasis;
132 int fCRC = 0;
133 int fPadding = 0;
134 int fPrivate = 0;
135 int fCopyright = 0;
136 int fOriginal = 0;
137 uint4 Head;
138 long n;
139 /* long Length; */
140 long TotTime = 0;
141 long TotLength = 0;
142 long TotNum = 0;
143 int i;
144
145 /* Add a few extra bytes so we may overstep our buffer */
146 if ((pBuf = (unsigned char *) malloc (FRAME_SIZ + 8)) == NULL)
147 return 0;
148
149 {
150 pFileSpec = argv;
151
152 if ((fp = fopen (pFileSpec, "rb")) == NULL)
153 return 0;
154
155 /* Determine if this is an MP3 */
156 n = fread (pBuf, sizeof (char), FRAME_SIZ, fp);
157
158 if (n <= 128)
159 {
160 /* Too short */
161 fclose (fp);
162 return 0;
163 }
164
165 /* Use KbPerSec as a sentinel */
166 for (KbPerSec = -1, pBufTrav = pBuf, pBufEnd = pBuf + n;
167 pBufTrav != pBufEnd; pBufTrav++)
168 {
169 /* Search for the sync 12 or 11 bits set !, I'm assuming that
170 * the alignment could be anything.
171 */
172 if (pBufTrav[0] == 0xff && (pBufTrav[1] & 0xe0) == 0xe0)
173 {
174 Head = (uint4) pBufTrav[0] << 24 |
175 (uint4) pBufTrav[1] << 16 |
176 (uint4) pBufTrav[2] << 8 | (uint4) pBufTrav[3];
177
178 /* Test code from mp3asm */
179 if ((Head & 0x0c00) == 0x0c00 || !(Head & 0x060000))
180 return 0;
181
182 if ((BitRateIdx = (0x0f & Head >> 12)) == 0)
183 return 0;
184
185 if ((Layer = 4 - (0x03 & Head >> 17)) > 3)
186 return 0;
187
188 if (Head & 0x00100000)
189 {
190 if (Head & 0x00080000)
191 { /* MPEG 1.0 */
192 MajorVer = 1;
193 Frq = FrqTab[0x03 & (Head >> 10)];
194 }
195 else
196 { /* MPEG 2.0 */
197 MajorVer = 2;
198 Frq = FrqTab[3 + (0x03 & (Head >> 10))];
199 }
200 MinorVer = 0;
201 }
202 else
203 { /* "MPEG 2.5" */
204 Frq = FrqTab[6];
205 MajorVer = 2;
206 MinorVer = 5;
207 }
208
209 KbPerSec = KbPerSecTab[MajorVer - 1][4 - Layer][BitRateIdx];
210
211 Mode = 0x03 & (Head >> 6);
212
213 /* If the protecion bit is NOT set then a 16bit CRC follows
214 * the frame header
215 */
216 if (!(Head & 0x10000))
217 fCRC = 1;
218
219 if (Head & 0x0200)
220 fPadding = 1;
221
222 if (Head & 0x100)
223 fPrivate = 1;
224
225 if (Head & 0x08)
226 fCopyright = 1;
227
228 if (Head & 0x04)
229 fOriginal = 1;
230
231 Emphasis = Head & 0x3;
232
233 break;
234 }
235 }
236
237 /* Is this a valid MP3 file ? */
238 if (KbPerSec == -1)
239 {
240 fclose (fp);
241 return 0;
242 }
243
244 /* Seek to the end of the file */
245 fseek (fp, 0, SEEK_END);
246 FilePos = ftell (fp);
247
248 return (float) FilePos / ((long) KbPerSec * 125);
249
250 /*
251 TotTime += Length;
252 TotLength += FilePos;
253 TotNum++;
254
255
256 printf("File : %s (%ldK, %d:%02ds)\n",
257 pFileSpec, FilePos / 1024, (int) (Length / 60), (int) (Length - ((Length / 60) * 60)));
258 printf("Info : MPEG ver %d.%d layer %d, freq %uHz, KBit/s %d, %s\n",
259 MajorVer, MinorVer, Layer, Frq, KbPerSec, Mode == 3 ? "Mono" : "Stereo");
260 */
261 #if 0
262 /* Just in case you need it :) */
263 printf
264 ("Details: Private=%s CRC's=%s Copyright=%s Original=%s Emphasis=%s\n",
265 fPrivate ? "Yes" : "No", fCRC ? "Yes" : "No",
266 fCopyright ? "Yes" : "No", fOriginal ? "Yes" : "No",
267 EmphasisTbl[Emphasis]);
268 #endif
269
270 if (pBuf[0] == 'I' && pBuf[1] == 'D' && pBuf[2] == '3')
271 {
272 fclose (fp);
273 return 0;
274 }
275
276 /* Look for the TAG ID go backwards */
277 if (FilePos < ID3_TAG_LEN)
278 {
279 fclose (fp);
280 return 0;
281 }
282
283 /* Position ourselves in front of the TAG */
284 fseek (fp, -ID3_TAG_LEN, SEEK_END);
285
286 /* Start with a clean slate */
287 TagStr[0] = 0;
288 if (fread (TagStr, sizeof (char), TAG_LEN, fp) != TAG_LEN)
289 {
290 fclose (fp);
291 return 0;
292 }
293
294 if (!(TagStr[0] == 'T' && TagStr[1] == 'A' && TagStr[2] == 'G'))
295 {
296 fclose (fp);
297 return 0;
298 }
299
300 /* Read in all the fields and null terminate them */
301 fread (SongNameStr, sizeof (char), SONGNAME_LEN, fp);
302 SongNameStr[SONGNAME_LEN] = 0;
303 fread (ArtistStr, sizeof (char), ARTIST_LEN, fp);
304 ArtistStr[ARTIST_LEN] = 0;
305 fread (AlbumStr, sizeof (char), ALBUM_LEN, fp);
306 AlbumStr[ALBUM_LEN] = 0;
307 fread (YearStr, sizeof (char), YEAR_LEN, fp);
308 YearStr[YEAR_LEN] = 0;
309 fread (CommentStr, sizeof (char), COMMENT_LEN, fp);
310 CommentStr[COMMENT_LEN] = 0;
311
312 /* Finally get the Genre */
313 i = getc (fp);
314
315 fclose (fp);
316
317 if (i == EOF)
318 return 0;
319
320 /* All stuff is read in, display it, now instead of storing
321 * 0's a lot of people hve stored space, handle it
322 */
323 /*
324 if (!fBufferEmpty(ArtistStr))
325 printf("Artist : %s\n", ArtistStr);
326
327 if (!fBufferEmpty(SongNameStr))
328 printf("Title : %s\n", SongNameStr);
329
330 if (!fBufferEmpty(AlbumStr))
331 printf("Album : %s\n", AlbumStr);
332
333 if (!fBufferEmpty(YearStr))
334 printf("Year : %s\n", YearStr);
335
336 if (!fBufferEmpty(CommentStr))
337 printf("Comment: %s\n", CommentStr);
338 */
339
340 /* Special ID3 ver 1.1 addition */
341
342 /*
343 if (CommentStr[COMMENT_LEN - 2] == 0 &&
344 CommentStr[COMMENT_LEN - 1] != 0)
345 printf("Track : %d\n", CommentStr[COMMENT_LEN - 1]);
346 */
347
348 /* 255 means unused */
349
350 /*
351 if (i != 255)
352 {
353 if (i >= 0 && i < NUM_GENRE)
354 printf("Genre : %s\n", pGenreTbl[i]);
355 else
356 printf("Genre : unknown genre\n");
357 }
358 */
359 putchar ('\n');
360 }
361
362 if (pBuf != NULL)
363 free (pBuf);
364
365 if (TotNum > 1)
366 printf ("Total : %ld files %ldK %d:%02ds\n", TotNum, TotLength / 1024,
367 (int) (TotTime / 60), (int) (TotTime - ((TotTime / 60) * 60)));
368
369 return 0;
370 }
371