/* Open an MP3 file and dump any ID tag it has */
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <ctype.h>
#include "lfn.h"

#pragma pack(1)
typedef struct mp3_info_tag
 { char T,A,G;        // Should be capital T,A,G
   char title[30];    // Note: These are padded with spaces, not terminated
   char artist[30];
   char album[30];
   char year[4];
   union 
   { struct 
      { char comment[30];
      } v1_0;
     struct 
      { char comment[28];
        char zer;
        unsigned char track; // 1->255 if zero is zero
      } v1_1;
   };
   unsigned char style; // 0x11 = Rock, 0xFF=Unknown
 } mp3_info_t;

typedef unsigned char BYTE;

typedef struct mp3_header_tag
{ unsigned long
	frameSync:11,		// Frame sync (all bits set) 
	mpegAudioID:2,		// MPEG Audio version ID
	layerDesc:2,		// Layer description
	noCRC:1,			// Protection bit
	bitRate:4,			// Bitrate index
	sampleRate:2,		// Sampling rate frequency index
	hasPadding:1,		// Padding bit
	isPrivate:1,		// application specific
	channelMode:2,		// Channel Mode
	modeExtension:2,	// Joint stereo mode bits (only used if in joint stereo mode)
	copyrighted:1,		// Audio is copyrighted
	original:1,			// If this is the Original media
	emphasis:2;			// Which pre-emphasis has been used

  unsigned int
	c_bitRate,
	c_sampleRate,
	c_frameSize;

} mp3_header_t;

char *mpegAudio[4]		={ "2.5",NULL,"2","1" };
char *layerDesc[4]		={ NULL,"III","II","I" };
char *channelMode[4]	={ "Stereo", "Joint stereo", "Dual channel", "Single channel" };
char *emphasisMode[4]	={ "none", "50/15 ms", NULL, "CCIT J.17" };

int bitrate_v2_l23[15]={ 0, 8,16,24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160 };
int bitrate_v2_l1[15]= { 0,32,48,56, 64, 80, 96,112,128,144,160,176,192,224,256 };
int bitrate_v1_l3[15]= { 0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320 };
int bitrate_v1_l2[15]= { 0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384 };
int bitrate_v1_l1[15]= { 0,32,64,96,128,160,192,224,256,288,320,352,384,416,448 };

int *bitrates[4][4]= // [mpegAudioID][layerDesc]
{ NULL,bitrate_v2_l23,bitrate_v2_l23,bitrate_v2_l1,		// mpeg 2.5, layers BAD,3,2,1
  NULL,NULL,NULL,NULL,									// mpeg BAD
  NULL,bitrate_v2_l23,bitrate_v2_l23,bitrate_v2_l1,		// mpeg 2, layers BAD,3,2,1
  NULL,bitrate_v1_l3, bitrate_v1_l2, bitrate_v1_l1		// mpeg 1, layers BAD,3,2,1
};

int sampleRates[4][4]= // [mpegAudioID][sampleRate]
{ 11025, 12000, 8000, 0,
  0,0,0,0,
  22050, 24000, 16000, 0,
  44100, 22050, 11025, 0
};

BYTE bitval(BYTE val,int siz,int pos)
{ return (val>>pos)&((1<<siz)-1);
}

static int dump_mp3_header(FILE *in)
{ BYTE header[4];
  mp3_header_t head;

  // Read header data
  if (fread(header,sizeof(header),1,in)!=1)
  { return 1;
  }

  // Decode AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM into structure
  head.frameSync=(header[0]<<3)|bitval(header[1],3,5);
  head.mpegAudioID=bitval(header[1],2,3);
  head.layerDesc=bitval(header[1],2,1);
  head.noCRC=bitval(header[1],1,0);
  head.bitRate=bitval(header[2],4,4);
  head.sampleRate=bitval(header[2],2,2);
  head.hasPadding=bitval(header[2],1,1);
  head.isPrivate=bitval(header[2],1,0);
  head.channelMode=bitval(header[3],2,6);
  head.modeExtension=bitval(header[3],2,4);
  head.copyrighted=bitval(header[3],1,3);
  head.original=bitval(header[3],1,2);
  head.emphasis=bitval(header[3],2,0);

#if 0
  printf("%02X %02X %02X %02X\n",header[0],header[1],header[2],header[3]);
  printf("frameSync %X\n",head.frameSync);
  printf("mpegAudioID %X\n",head.mpegAudioID);
  printf("layerDesc %X\n",head.layerDesc);
  printf("noCRC %X\n",head.noCRC);
  printf("bitRate %X\n",head.bitRate);
  printf("sampleRate %X\n",head.sampleRate);
  printf("hasPadding %X\n",head.hasPadding);
  printf("isPrivate %X\n",head.isPrivate);
  printf("channelMode %X\n",head.channelMode);
  printf("modeExtension %X\n",head.modeExtension);
  printf("copyrighted %X\n",head.copyrighted);
  printf("original %X\n",head.original);
  printf("emphasis %X\n",head.emphasis);
#endif

  // Check for illegal values
  if (head.frameSync!=0x7FF)
  { fprintf(stderr,"Sync of 0x%X illegal\n",head.frameSync);
	return 'A';
  }

  if (head.mpegAudioID==1)
  { fprintf(stderr,"Audio ID of 0x%X illegal\n",head.mpegAudioID);
	return 'B';
  }

  if (head.layerDesc==0)
  { fprintf(stderr,"Layer description of 0x%X illegal\n",head.layerDesc);
	return 'C';
  }

  if (head.bitRate==0xF)
  { fprintf(stderr,"Bit rate of 0x%X illegal\n",head.bitRate);
	return 'E';
  }

  if (head.sampleRate==3)
  { fprintf(stderr,"Sample Rate of 0x%X illegal\n",head.sampleRate);
	return 'F';
  }

  if (head.emphasis==2)
  { fprintf(stderr,"Emphasis mode 0x%X illegal\n",head.emphasis);
	return 'M';
  }

  if (bitrates[head.mpegAudioID][head.layerDesc]==NULL)
  { fprintf(stderr,"Mpeg %s with Layer %s, illegal\n",mpegAudio[head.mpegAudioID],layerDesc[head.layerDesc]);
	return 'Z';
  }
  head.c_bitRate=bitrates[head.mpegAudioID][head.layerDesc][head.bitRate];

  if (sampleRates[head.mpegAudioID][head.sampleRate]==0)
  { fprintf(stderr,"Mpeg %s with sample rate of 0x%X, illegal\n",mpegAudio[head.mpegAudioID],head.sampleRate);
	return 'Z';
  }
  head.c_sampleRate=sampleRates[head.mpegAudioID][head.sampleRate];

  head.c_frameSize=(((head.layerDesc==3) ? 12 : 144)*1000*head.c_bitRate)/head.c_sampleRate;
  if (head.hasPadding)
  { head.c_frameSize+=(head.layerDesc==3) ? 1 : 4;
  }

//  printf("MPEG%s layer %s has \n",mpegAudio[head.mpegAudioID],layerDesc[head.layerDesc],head.noCRC ? "No CRC" : "With CRC");
//  printf("Bitrate %dKbps, sampled at %uHz\n",head.c_bitRate,head.c_sampleRate);
//  printf("%d Pad values, Private bit = %d, frame size of %d bytes\n",head.hasPadding,head.isPrivate,head.c_frameSize);
//  printf("%s ext %X\n",channelMode[head.channelMode],head.modeExtension);
//  printf("Copyright flag %d, Original flag %d, Pre-empasis = %s\n",head.copyrighted,head.original,emphasisMode[head.emphasis]);

  printf("MPEG%s-%-3s %dK %-15s ",mpegAudio[head.mpegAudioID],layerDesc[head.layerDesc],head.c_bitRate,channelMode[head.channelMode]);
//  printf("Bitrate %dKbps, sampled at %uHz\n",head.c_bitRate,head.c_sampleRate);
//  printf("%d Pad values, Private bit = %d, frame size of %d bytes\n",head.hasPadding,head.isPrivate,head.c_frameSize);
//  printf("%s ext %X\n",channelMode[head.channelMode],head.modeExtension);
//  printf("Copyright flag %d, Original flag %d, Pre-empasis = %s\n",head.copyrighted,head.original,emphasisMode[head.emphasis]);

  return 0;
}

static int dump_id3_tag(FILE *in)
{ mp3_info_t tag;
  char buf[32],*cp;

  if (fread(&tag,sizeof(tag),1,in)!=1 ||
		tag.T!='T' || tag.A!='A' || tag.G!='G')
  { return 1;
  }

  strncpy(buf,tag.artist,sizeof(tag.artist));
  cp=buf+sizeof(tag.artist);
  do *(cp--)='\0'; while (cp>=buf && isspace(*cp));
  printf("%s - ",buf);
  strncpy(buf,tag.title,sizeof(tag.title));
  cp=buf+sizeof(tag.title);
  do *(cp--)='\0'; while (cp>=buf && isspace(*cp));
  printf("%s\n",buf);
  return 0;
}

int main(int argc, char *argv[])
{ int parloop,retv ; lfn_find_t nxt_file ;
  char fullname[256],*path,extended;

  if (argc<2) 
  { fputs("Syntax : MP3 [+] <file>...\n"
	  "Displays MP3 files found and any TAG fields within them\n"
          "If the first parameter is '+' then the file header is displayed\n",stderr) ;
    return 20;
  }

  extended=0;
  for( parloop=1,retv=0 ; parloop<argc ; parloop++ )
   if (strcmp(argv[parloop],"+")==0)
   { extended=1;
   } else
   if (lfn_findfirst(argv[parloop],0,_A_NORMAL|_A_ARCH|_A_RDONLY,&nxt_file)!=0)
   { printf("%-14s (Nothing found)\n",argv[parloop]);
   } else
   { strcpy(fullname,argv[parloop]);
     if ((path=strrchr(fullname,'\\'))==NULL)
      path=strrchr(fullname,':');
     path=(path==NULL) ? fullname : path+1;
     
     do { FILE *in;
#if _MSC_VER >= 1000
          if (strlen(nxt_file.name))
            strcpy(path,nxt_file.name);
#else
          if (strlen(nxt_file.old_name))
            strcpy(path,nxt_file.old_name);
#endif
          printf("%-14s ",nxt_file.name);
          if (nxt_file.size<=sizeof(mp3_info_t) || 
              (in=fopen(fullname,"rb"))==NULL)
          { printf("(Bad File)\n");
			continue;
          } else
          if (extended && dump_mp3_header(in))
		  { printf("(Bad MP3 header)\n");
		  } else
		  if (fseek(in,nxt_file.size-sizeof(mp3_info_t),SEEK_SET)!=0 || 
				dump_id3_tag(in)!=0)
		  { printf("(Bad ID3 tag)\n");
		  }
		  fclose(in);
        } while (lfn_findnext(&nxt_file)==0);
     lfn_findend(&nxt_file);
   }
  return retv;
}



/************************** MPEG HEADER INFO
Sign Length
(bits) Position
(bits) Description 
A 11 (31-21) Frame sync (all bits set) 

B 2 (20,19) MPEG Audio version ID
00 - MPEG Version 2.5
01 - reserved
10 - MPEG Version 2 (ISO/IEC 13818-3)
11 - MPEG Version 1 (ISO/IEC 11172-3) 
Note: MPEG Version 2.5 is not official standard. Bit No 20 in frame header is used to indicate version 2.5. Applications that do not support this MPEG version expect this bit always to be set, meaning that frame sync (A) is twelve bits long, not eleve n as stated here. Accordingly, B is one bit long (represents only bit No 19). I recommend using methodology presented here, since this allows you to distinguish all three versions and keep full compatibility. 
 
C 2 (18,17) Layer description
00 - reserved
01 - Layer III
10 - Layer II
11 - Layer I 
D 1 (16) Protection bit
0 - Protected by CRC (16bit crc follows header)
1 - Not protected 
E 4 (15,12) Bitrate index
bits V1,L1 V1,L2 V1,L3 V2,L1 V2, L2 & L3 
0000 free free free free free 
0001 32 32 32 32 8 
0010 64 48 40 48 16 
0011 96 56 48 56 24 
0100 128 64 56 64 32 
0101 160 80 64 80 40 
0110 192 96 80 96 48 
0111 224 112 96 112 56 
1000 256 128 112 128 64 
1001 288 160 128 144 80 
1010 320 192 160 160 96 
1011 352 224 192 176 112 
1100 384 256 224 192 128 
1101 416 320 256 224 144 
1110 448 384 320 256 160 
1111 bad bad bad bad bad 

NOTES: All values are in kbps
V1 - MPEG Version 1
V2 - MPEG Version 2 and Version 2.5
L1 - Layer I
L2 - Layer II
L3 - Layer III
"free" means free format. If the correct fixed bitrate (such files cannot use variable bitrate) is different than those presented in upper table it must be determined by the application. This may be implemented only for internal purposes since third party applications have no means to findout correct bitrate. Howewer, this is not impossible to do but demands lot's of efforts.
"bad" means that this is not an allowed value 

MPEG files may have variable bitrate (VBR). This means that bitrate in the file may change. I have learned about two used methods: 

bitrate switching. Each frame may be created with different bitrate. It may be used in all layers. Layer III decoders must support this method. Layer I & II decoders may support it. 
bit reservoir. Bitrate may be borrowed (within limits) from previous frames in order to provide more bits to demanding parts of the input signal. This causes, however, that the frames are no longer independent, which means you should not cut this fil es. This is supported only in Layer III. 
More about VBR you may find on Xing Tech site 

For Layer II there are some combinations of bitrate and mode which are not allowed. Here is a list of allowed combinations. bitrate allowed modes 
free all 
32 single channel 
48 single channel 
56 single channel 
64 all 
80 single channel 
96 all 
112 all 
128 all 
160 all 
192 all 
224 stereo, intensity stereo, dual channel 
256 stereo, intensity stereo, dual channel 
320 stereo, intensity stereo, dual channel 
384 stereo, intensity stereo, dual channel 

 
F 2 (11,10) Sampling rate frequency index (values are in Hz) bits MPEG1 MPEG2 MPEG2.5 
00 44100 22050 11025 
01 48000 24000 12000 
10 32000 16000 8000 
11 reserv. reserv. reserv. 
 
G 1 (9) Padding bit
0 - frame is not padded
1 - frame is padded with one extra slot
Padding is used to fit the bit rates exactly. For an example: 128k 44.1kHz layer II uses a lot of 418 bytes and some of 417 bytes long frames to get the exact 128k bitrate. For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long. 
How to calculate frame length 

First, let's distinguish two terms frame size and frame length. Frame size is the number of samples contained in a frame. It is constant and always 384 samples for Layer I and 1152 samples for Layer II and Layer III. Frame length is length of a frame when compressed. When you are reading MPEG file you must calculate this to be able to find each consecutive frame. Remember, frame length may change from frame to frame due to padding or bitrate switching. 

Read the BitRate, SampleRate and Padding (remember if padding is used in a frame it is four bytes long for Layer I or one byte long for Layer II and Layer III) of the frame header. 

For Layer I files us this formula: 

FrameSize = 12 * BitRate / SampleRate + Padding


For Layer II & III files use this formula: 

FrameSize = 144 * BitRate / SampleRate + Padding 

Example:
Layer III, BitRate=128000, SampleRate=441000, Padding=0  ==>  FrameSize=417 bytes 
 
H 1 (8) Private bit. It may be freely used for specific needs of an application, i.e. if it has to trigger some application specific events. 
I 2 (7,6) Channel Mode
00 - Stereo
01 - Joint stereo (Stereo)
10 - Dual channel (Stereo)
11 - Single channel (Mono) 
J 2 (5,4) Mode extension (Only if Joint stereo) 
Mode extension is used to join informations that are of no use for stereo effect, thus reducing needed resources. These bits are dynamically determined by an encoder in Joint stereo mode. 

Complete frequency range of MPEG file is divided in subbands There are 32 subbands. For Layer I & II these two bits determine frequency range (bands) where intensity stereo is applied. For Layer III these two bits determine which type of joint stereo is used (intensity stereo or m/s stereo). Frequency range is determined within decompression algorythm. Layer I and II Layer III 
value Layer I & II 
00 bands 4 to 31 
01 bands 8 to 31 
10 bands 12 to 31 
11 bands 16 to 31 
 Intensity stereo MS stereo 
off off 
on off 
off on 
on on 

K 1 (3) Copyright
0 - Audio is not copyrighted
1 - Audio is copyrighted 

L 1 (2) Original
0 - Copy of original media
1 - Original media 

M 2 (1,0) Emphasis
00 - none
01 - 50/15 ms
10 - reserved
11 - CCIT J.17 
*************************************************************/

