/*
* A simple id3 tag reader
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#define MP3FILE "song2.mp3"
struct HEADERV2
{
uint8_t id[3];
uint8_t ver[2];
uint8_t flags;
uint8_t size[4];
uint32_t tagsize; /* does NOT include 10 byte header */
} __attribute__((__packed__));
struct HEADERFRAME
{
uint8_t id[4];
uint8_t size[4];
uint8_t flags[2];
uint32_t framesize;
} __attribute__((__packed__));
struct HEADERV1
{
uint8_t id[3];
uint8_t title[30];
uint8_t artist[30];
uint8_t album[30];
uint8_t year[4];
uint8_t comment[30];
uint8_t genre;
} __attribute__((__packed__));
int synchsafe(int in);
int unsynchsafe(int in);
void readid3v2header(FILE *f, struct HEADERV2 *header);
void printid3v2headerinfo(struct HEADERV2 *header);
void readid3v2frames(FILE *f, struct HEADERV2 *header);
void printid3v2frameinfo(struct HEADERFRAME *frame);
void readid3v1header(FILE *f, struct HEADERV1 *header);
void printid3v1headerinfo(struct HEADERV1 *header);
int
main(void)
{
printf("parsing mp3 file %s\n", MP3FILE);
FILE *f = fopen(MP3FILE, "rb");
if (!f)
{
fprintf(stderr, "fopen failed on %s\n", MP3FILE);
exit(1);
}
struct HEADERV2 *headerv2 = malloc(sizeof(struct HEADERV2));
if (!headerv2)
{
fprintf(stderr, "malloc failed\n");
exit(1);
}
struct HEADERV1 *headerv1 = malloc(sizeof(struct HEADERV1));
if (!headerv1)
{
fprintf(stderr, "malloc failed\n");
free(headerv2);
exit(1);
}
readid3v1header(f, headerv1);
printid3v1headerinfo(headerv1);
puts("");
readid3v2header(f, headerv2);
printid3v2headerinfo(headerv2);
puts("");
readid3v2frames(f, headerv2);
fclose(f);
free(headerv2);
free(headerv1);
return 0;
}
int
synchsafe(int in) // not used yet
{
int out, mask = 0x7F;
while (mask ^ 0x7FFFFFFF)
{
out = in & ~mask;
out <<= 1;
out |= in & mask;
mask = ((mask + 1) << 8) - 1;
in = out;
}
return out;
}
int
unsynchsafe(int in) // not used yet
{
int out = 0, mask = 0x7F000000;
while (mask)
{
out >>= 1;
out |= in & mask;
mask >>= 8;
}
return out;
}
void
readid3v1header(FILE *f, struct HEADERV1 *header)
{
/* read 128 byte id3v1 header */
fpos_t oldpos;
/* save our file position */
fgetpos(f, &oldpos);
fseek(f, -128L, SEEK_END);
fread(header, 1, 128, f);
/* restore our file position */
fsetpos(f, &oldpos);
}
void
printid3v1headerinfo(struct HEADERV1 *header)
{
printf("TAG v1 header data\n");
char title[31];
char artist[31];
char album[31];
char year[5];
char comment[31];
strncpy(title, (char *)header->title, 30);
strncpy(artist, (char *)header->artist, 30);
strncpy(album, (char *)header->album, 30);
strncpy(year, (char *)header->year, 4);
strncpy(comment, (char *)header->comment, 30);
printf("title:\t%s\n", title);
printf("artist:\t%s\n", artist);
printf("album:\t%s\n", album);
printf("year:\t%c%c%c%c\n", year[0], year[1], year[2], year[3]);
printf("comment:\t%s\n", comment);
}
void
readid3v2header(FILE *f, struct HEADERV2 *header)
{
/*
* read 10 byte id3v2 header
* 3 bytes->id
* 2 bytes->version
* 1 bytes->flags
* 4 bytes->size
* the size is encoded into 4 bytes as a synchsafe integer, the most significant bit is ignored
* so we need to convert it
*/
fread(header, 1, 10, f);
/*
* the tag size does NOT include the 10 byte header
* so it is safe to read tagsize bytes from the file
* to obtain the entire header
*/
header->tagsize = header->size[0] << 21 |
header->size[1] << 14 |
header->size[2] << 7 |
header->size[3];
}
void
printid3v2headerinfo(struct HEADERV2 *header)
{
puts("TAGv2 header raw bytes");
int i;
printf("id:\t");
for (i = 0; i < 3; i++)
printf("%02X ", header->id[i]);
printf("(%c%c%c)\n", header->id[0], header->id[1], header->id[2]);
printf("ver:\t");
for (i = 0; i < 2; i++)
printf("%02X ", header->ver[i]);
puts("");
printf("flags:\t");
printf("%02X\n", header->flags);
printf("size:\t");
for (i = 0; i < 4; i++)
printf("%02X ", header->size[i]);
printf("(%u bytes)\n", header->tagsize);
}
void
readid3v2frames(FILE *f, struct HEADERV2 *header)
{
/* we read at absolute most header->tagsize bytes */
size_t bytesread = 0;
struct HEADERFRAME *frame = malloc(sizeof(struct HEADERFRAME));
bytesread += fread(frame, 1, 10, f);
frame->framesize = frame->size[0] << 21 |
frame->size[1] << 14 |
frame->size[2] << 7 |
frame->size[3];
printid3v2frameinfo(frame);
free(frame);
}
void
printid3v2frameinfo(struct HEADERFRAME *frame)
{
puts("TAGv2 frame raw bytes");
int i;
printf("id:\t");
for (i = 0; i < 4; i++)
printf("%02X ", frame->id[i]);
printf("(%c%c%c%c)\n", frame->id[0], frame->id[1], frame->id[2], frame->id[3]);
printf("size:\t");
for (i = 0; i < 4; i++)
printf("%02X ", frame->size[i]);
printf("(%u bytes)\n", frame->framesize);
}