Thread: Reading a MP3 header

  1. #1
    Registered User
    Join Date
    Jun 2004
    Posts
    722

    Reading a MP3 header

    First, if you don't know how a mp3 file header is you should google a bit before trying to understand this. Here's a good link
    Any way I'm trying to read a frame header from any mp3 file.
    Code:
    #include<conio.h>
    #include<stdio.h>
    
    #define	MPEGVersion2_5 0
    #define	MPEGreserved   1
    #define	MPEGVersion2   2
    #define	MPEGVersion1   3
    
    #define LAYER1	3
    #define LAYER2	2
    #define LAYER3	1
    #define LAYERRESERVED	0
    //some macros defined by me... you can do what ever you want with these
    #define BITRATEFREE 0xfffe
    #define BITRATEBAD	0xffff
    
    int bitrates[] = {
    	BITRATEFREE, BITRATEFREE, BITRATEFREE, BITRATEFREE, BITRATEFREE, 
    	32,  32,  32,  32,	  8,
    	64,  48,  40,  48,	 16,
    	96,  56,  48,  56,  24,
    	128,  64,  56,  64,  32,
    	160,  80,  64,  80,  40,
    	192,  96,  80,  96,  48,
    	224, 112,  96, 112,  56,
    	256, 128, 112, 128,  64,
    	288, 160, 128, 144,  80,
    	320, 192, 160, 160,  96,
    	352, 224, 192, 176, 112,
    	384, 256, 224, 192, 128,
    	416, 320, 256, 224, 144,
    	448, 384, 320, 256, 160,
    	BITRATEBAD, BITRATEBAD, BITRATEBAD, BITRATEBAD, BITRATEBAD
    };
    
    typedef struct{
    	unsigned	framesync	:11;	//Frame synchronizer
    	unsigned	MPEGID		: 2;	//MPEG version ID
    	unsigned	layer		: 2;	//Layer
    	unsigned	protectbit	: 1;	//CRC Protection
    	unsigned	bitrateindx	: 4;	//Bitrate index
    	unsigned	samplefreq	: 2;	//Samplig rate frequency index
    	unsigned	paddingbit	: 1;	//Padding
    	unsigned	privatebit	: 1;	//Private bit
    	unsigned	channel		: 2;	//Channel
    	unsigned	modeext		: 2;	//Mode extension (only if Joint Stereo is set)
    	unsigned	copyright	: 1;	//Copyright
    	unsigned	original	: 1;	//Original
    	unsigned	emphasis	: 2;	//Emphasis
    } MP3HEADER;
    
    typedef struct{
    	char	tagid  [ 3];	//0-2     3  Tag identifier. Must contain "TAG" string if Tag is valid. 
    	char	name   [30];	//3-32   30  Song Name 
    	char	artist [30];	//33-62  30  Artist 
    	char	album  [30];	//63-92  30  Album
    	int		year   :32 ;	//93-96   4  Year 
    	char	comment[30];	//97-126 30  Comment 
    	char	genre  : 8 ;	//127  1  Genre 
    } MP3ID3TAG1;
    
    #pragma pack(1)
    typedef struct{
    	char	tagid[3];//0-2  TAG identifier. It contains of string "ID3" 
    	short	tagverm	;//3-4  TAG version
    	char	flags	;//5	Flags 
    	long	size	;//6-9  Size of TAG MSB
    } MP3ID3TAG2;
    
    int unpacktagsize(MP3ID3TAG2 id){
    	int a, b, c, d;
    	a = id.size & 0x7f000000,
    	b = id.size & 0x007f0000,
    	c = id.size & 0x00007f00,
    	d = id.size & 0x0000007f;
    	return	a>>3 | b>>2 | c>>1 | d>>0;
    }
    
    int readMP3header(FILE *f, MP3HEADER *h){
    	MP3ID3TAG2 tag;
    
    	//push file point to the beginning
    	rewind(f);
    	fread(&tag, 1, sizeof(MP3ID3TAG2), f);
    
    	//tag id3v2 are located at the beginning of file, id3v1 at the end
    	if(tag.tagid[0]=='I' && tag.tagid[1]=='D' && tag.tagid[2]=='3'){//is tag id3v2 - jump to end of tag
    		fseek(f, unpacktagsize(tag), SEEK_CUR);
    	}else{//isn't tag id3v2 - go back
    		rewind(f);
    	}
    	//I'm currently not interested in the final state of the file pointer
    	do{//read a frame header
    		if(fread(h, 1, sizeof(MP3HEADER), f)<1)//returns -1 if eof
    			return 0;
    		fseek(f, 1-sizeof(MP3HEADER), SEEK_CUR);
    	//verifies if the framesync bytes are wellset - if not no frame header was read: go to next file byte
    	}while(~h->framesync);
    	
    	return 1;
    }
    
    int MP3bitrate(MP3HEADER h){
    	int index=0;
    	if( (0x03&h.MPEGID)==MPEGVersion1 ){
    		index = 3-h.layer;
    	}else if( (0x03&h.MPEGID)==MPEGVersion2 || (0x03&h.MPEGID)==MPEGVersion2_5 ){
    		index = (0x03&h.layer) == LAYER1 ? 3 :4 ;
    	}	
    	return bitrates[ index + (0x0f&h.bitrateindx) * 5];
    }
    
    main(){
    	FILE *f;
    	MP3HEADER h;
    	int i;
    	
    	f = fopen(YOUR_FILE_HERE, "rb");
    
    	if(readMP3header(f, &h))
    		printf("MPEGID: %d\nLAYER: %d\nBITIND: %d\nBITRATE: %d\n",
    			0x03&h.MPEGID, 0x03&h.layer, 0x0f&h.bitrateindx , MP3bitrate(h));
    	else
    		printf("nop, header not found.");
    	getch();
    }
    Well, the problem... The first frame header isn't found first. Only after the first iteration, the second time, or not. I think it has something to do with the tag skipping. I get the wrong bitrate, and If I try to read several frames from the same file I get diferent bitrates every time.

    [edit]
    Opps, forgot to put link about ID tags sorry.
    Here's one.
    [/edit]
    Last edited by xErath; 06-22-2004 at 11:06 AM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    I bet you will find that sizeof(MP3ID3TAG2) is 10, and not the 9 you expect.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Jun 2004
    Posts
    722
    Opps... another mistake... It's corrected now, but still doesn't work... now the problem is to make the MP3HEADER structure fit in 4 bytes... #pragma pack directive doesn't work. The compiler gives the framesync field 2 bytes and 3 to the rest of the fields.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Your only portable solution is to read the information into an array of bytes, then extract the information using normal bitwise masking and shifting operations, much like you do in unpacktagsize().

    Bitfields are useless for external data types.
    For example, given
    struct { int layer : 2 ; }
    You don't know whether that maps to bits 0,1; 7,8; 14,15; or 30,31. The compiler is free to choose any of those.
    Add to that all the different ways of saying "#pragma pack(1)", and likely endian issues across different machines.
    All in all, it's just much easier to just do the work yourself.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Jun 2004
    Posts
    722
    Quote Originally Posted by Salem
    Your only portable solution is to read the information into an array of bytes, then extract the information using normal bitwise masking and shifting operations, much like you do in unpacktagsize().
    Bah.. doubled work... Lets see what I can do....

  6. #6
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    I started this last night when I was st..under the weather. Finished this morning, when I woke up. Here ya go, enjoy the free ride.
    Code:
    #include <stdio.h>
    
    typedef enum mp3h_tags { MP3H_FRAMESYNC, MP3HM_PEGID, MP3H_LAYER,
            MP3H_PROTECTBIT, MP3H_BITRATEINDX, MP3H_SAMPLEFREQ, MP3H_PADDINGBIT,
            MP3H_PRIVATEBIT, MP3H_CHANNEL, MP3H_MODEEXT, MP3H_COPYRIGHT,
            MP3H_ORIGINAL, MP3H_EMPHASIS } mp3t_t;
    
    unsigned int shifts[] = { 0, 11, 13, 15, 16, 20, 22, 23, 24, 26, 28, 29, 30 };
    unsigned int masks[] = { 0x7FF, 0x1800, 0x6000, 0x8000, 0xF0000, 0x300000,
            0x400000, 0x800000, 0x3000000, 0xC000000, 0x10000000, 0x20000000,
            0xC0000000 };
    
    void setvalue( unsigned int *mp3header, mp3t_t tag, unsigned int value )
    {
            *mp3header &= ~masks[tag];
            *mp3header |= (( masks[tag]) & (value<<shifts[tag]));
    }
    
    void showbits( unsigned int bits )
    {
            int x;
            for( x =0; x < sizeof bits << 3; x++ )
                    printf("%d", !!(bits  & (1<<x)) );
            printf("\n");
    }
    
    int main ( void )
    {
            unsigned int x = 0;
    
            printf("bits  0-10 (11 total): "); showbits( masks[0] );
            printf("bits 11-12 ( 2 total): "); showbits( masks[1] );
            printf("bits 13-14 ( 2 total): "); showbits( masks[2] );
            printf("bits 15    ( 1 total): "); showbits( masks[3] );
            printf("bits 16-19 ( 4 total): "); showbits( masks[4] );
            printf("bits 20-21 ( 2 total): "); showbits( masks[5] );
            printf("bits 22    ( 1 total): "); showbits( masks[6] );
            printf("bits 23    ( 1 total): "); showbits( masks[7] );
            printf("bits 24-25 ( 2 total): "); showbits( masks[8] );
            printf("bits 26-27 ( 2 total): "); showbits( masks[9] );
            printf("bits 28    ( 1 total): "); showbits( masks[10] );
            printf("bits 29    ( 1 total): "); showbits( masks[11] );
            printf("bits 30-31 ( 2 total): "); showbits( masks[12] );
            printf("\n");
    
            printf("An example:\nSet tag1, value of 2\n");
            setvalue( &x, 1, 2 ); showbits( x );
            printf("Set tag0, value of 300\n"); 
            setvalue( &x, 0, 300 ); showbits( x );
            printf("Set tag0, value of 1\n"); 
            setvalue( &x, 0, 1 ); showbits( x );
    
            return 0;
    }
    
    /*
    bits  0-10 (11 total): 11111111111000000000000000000000
    bits 11-12 ( 2 total): 00000000000110000000000000000000
    bits 13-14 ( 2 total): 00000000000001100000000000000000
    bits 15    ( 1 total): 00000000000000010000000000000000
    bits 16-19 ( 4 total): 00000000000000001111000000000000
    bits 20-21 ( 2 total): 00000000000000000000110000000000
    bits 22    ( 1 total): 00000000000000000000001000000000
    bits 23    ( 1 total): 00000000000000000000000100000000
    bits 24-25 ( 2 total): 00000000000000000000000011000000
    bits 26-27 ( 2 total): 00000000000000000000000000110000
    bits 28    ( 1 total): 00000000000000000000000000001000
    bits 29    ( 1 total): 00000000000000000000000000000100
    bits 30-31 ( 2 total): 00000000000000000000000000000011
    
    An example:
    Set tag1, value of 2
    00000000000010000000000000000000
    Set tag0, value of 300
    00110100100010000000000000000000
    Set tag0, value of 1
    10000000000010000000000000000000
    */
    Sans error-checking and tidyness.

    Quzah.
    Hope is the first step on the road to disappointment.

  7. #7
    Registered User
    Join Date
    Jun 2004
    Posts
    722
    Nice Quzah! hum, I wasn't considering using enum, but I think it would in fact be better. Now I have to do the reverse stuff.
    Thanx

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Class methods in header or source file?
    By TriKri in forum C++ Programming
    Replies: 13
    Last Post: 09-17-2007, 05:23 AM
  2. header file/class problems
    By Calef13 in forum C++ Programming
    Replies: 6
    Last Post: 07-20-2007, 08:33 AM
  3. Obtaining source & destination IP,details of ICMP Header & each of field of it ???
    By cromologic in forum Networking/Device Communication
    Replies: 1
    Last Post: 04-29-2006, 02:49 PM
  4. Problems in reading binary file
    By serena in forum C Programming
    Replies: 3
    Last Post: 04-14-2005, 03:54 AM
  5. Reading Bitmap File Header Problem.
    By StanleyC in forum C Programming
    Replies: 7
    Last Post: 08-26-2004, 05:54 PM