As a beginner programmer I was trying step by step to create an ID3v2 TAG reader. As simple as possible, I have managed to make it work up to a point but it seems the footer goes really bad. In theory looks correct but in practice it does not. Any guidance of suggestion would be much appreciated.

I am reading the Header correctly in 10 Bytes and I print the header.

As a second step I am checking if the extended header exist, if it exist read and print the outputs if not proceed with SEEK_SET.

As a third and final step, I am trying to read the Footer, which contains all information (Artist, Album, etc.). In theory Footer is localized after the Header,if no Extended Header exists, so I am using SEEK_SET at 10 Bytes, else I am calculating the SEEK_SET based on the Header size and the Extended Header size.

In the Footer process I have created an until condition, ($length_of_data == 0) trying to read all predefined Header_Tags and on the last one I have set ($length_of_data = 0) which in theory it will terminate the process.

#!/usr/bin/perl use warnings; # it warns about undefined values use strict; # it's a lexically scoped declaration use Data::Dumper; use Fcntl qw( SEEK_SET ); $| = 1; #flushing output my ( $lines , $type , $major_version , $revision_number , $flags , $si +ze , $extended_size , $number_flags , $extended_flags ) = "\0"; my ( $frame_id , $frame_size , $frame_flags , $extended_header , $mp3_ +size , $length_of_data , $lines_0 , $lines_1 , $lines_2) = "\0"; my ( $lines_3 , $length , $characters , $i ); my @word = "\0" x 5; my @memory = (0) x 5; my $source = $ARGV[0] or die "Please provide one .mp3 file to open!\nC +orrect syntax perl name of the program (e.g. Exercise3.pl) and name o +f the mp3 file (e.g. silence.mp3) $!\n"; open(FH, ,"<", $source) or die "Can not open file: $source $!\n"; binmode(FH); # Open in binary mode. if (@ARGV > 1) { print "Please no more than one argument!\nCorrect syntax perl ".$s +ource." and name of the mp3 file (e.g. silence.mp3)!\n"; exit (); } else { print ("\nUser has chosen file: $source to open for reading!\n"); # Header 10 Bytes in total 3 Bytes + 1 Byte + 1 Byte + 1 Byte + 4 +Bytes = 10 Bytes seek( FH , 0 , SEEK_SET ) or die "Could not seek: $!"; # Set pointer at the beggining of fil +e (Define possition with SEEK_SET). read( FH , $lines , 3 ); # Read 24 bits (3 Bytes) ID3 and store th +e data in $lines. Header_ID ( $type ) = unpack ( "A3" , $lines ); # (A) text (ASCII) string, w +ill be space padded. # print("This is Header_ID: $type\n"); seek( FH , 3 , SEEK_SET ) or die "Could not seek: $!"; # Based on possition 0 with SEEK_SET +we move 3 byte. read( FH , $lines , 1 ); # Read 8 bits (1 Byte) and store data in +$lines Version (1 Byte Major_Version). ( $major_version ) = unpack ( "h", $lines ); # (h) A hex string (l +ow nybble first). # print("This is Major_Version: $major_version\n"); seek( FH , 4 , SEEK_SET ) or die "Could not seek: $!"; # Based on possition 0 with SEEK_SET +we move 4 byte. read( FH , $lines , 1 ); # Read 8 bits (1 Byte) and store the data + in $lines Version (1 Byte Revision_Number). ( $revision_number ) = unpack ( "h", $lines ); # (h) hex string (l +ow nybble first). # print("This is Revision_Number: $revision_number\n"); seek( FH , 5 , SEEK_SET ) or die "Could not seek: $!"; # Based on possition 0 with SEEK_SET +we move 5 byte. read( FH , $lines, 1 ); # Read 8 bits (1 Byte) and store the data +in $lines Flags (1 Byte Flags). ( $flags ) = unpack ( "h" , $lines ); # (h) hex string (low nybble + first). # print("This is Byte_Flags: $flags\n"); print "TAG Detected: ".$type."v2.".$major_version.".".$revision_nu +mber."\n"; if($flags == 0) { print("\nThe extended flags has no corresponding data: \$00 was de +tected. Proceeding!\n\n"); } else { print("Flags are not empty, we have found these characters: $flags +\n"); } seek( FH , 6 , SEEK_SET ) or die "Could not seek: $!"; # Based on possition 0 with SEEK_SET +we move 10 byte. read( FH , $lines, 4 ); # Read 32 bits (4 Bytes) and store the dat +a in $lines Size (4 Bytes Size). ( @memory ) = unpack ( "C4" , $lines ); # (I) An unsigned integer +. #print Dumper(@memory); # print ("This is the content of lines_0: ".$memory[0]."\n"); # print ("This is the content of lines_1: ".$memory[1]."\n"); # print ("This is the content of lines_2: ".$memory[2]."\n"); # print ("This is the content of lines_3: ".$memory[3]."\n"); $mp3_size = ($memory[0] & 0xFF) | (( $memory[1] & 0xFF ) << 7) | (( $memory[2] & 0xFF ) << 14) | (( $memory[3] & 0xFF ) << 21); print Dumper($mp3_size); $length_of_data = $mp3_size; # End of Header 10 complete Bytes # At this point we want to make sure that we have an extended head +er (ID3v2 flags %abcd0000) # Bit 7 of (ID3v2 flags %abcd0000) if is 1 (active indicates that +there is extended header if is 0 # it means there is no extended header. If extended header exist p +roceed else skip. if (( $flags & (0b01000000) ) == 0b01000000 ) { # Begging Extended header (Optional not vital for correct parsing) +. # Extended Header in tppotal 6 Bytes, size 4 bytes memory size 4 B +ytes is enough to read binary no characters # no need for binary to string conversion no need for terminating +string character ('\0'). # Emptying memory for future use. @memory = (0) x 5; read( FH , $lines, 4 ); # Read 32 bits (4 Bytes) and store the dat +a in $lines Extended size. ( @memory ) = unpack ( "C4" , $lines ); # (I) An unsigned integer. print ("This is the extended size of lines_0: ".$memory[0]."\n"); print ("This is the extended size of lines_1: ".$memory[1]."\n"); print ("This is the extended size of lines_2: ".$memory[2]."\n"); print ("This is the extended size of lines_3: ".$memory[3]."\n"); # Due to Sync_safe remove the 0 from the beggining of each stored +element and Bitwise, # although we are working with unsigned characters and integers it + is a good practice. # Synchsafe integers are integers that keep its highest bit (bit 7 +) zeroed, making # seven bits out of eight available. $extended_size = ($memory[0] & 0xFF) | (($memory[1] & 0xFF) << 7 ) | (($memory[2] & 0xFF) << 14 ) | (($memory[3] & 0xFF) << 21 ); read( FH , $lines, 1 ); # Read 8 bits (1 Byte) and store the data +in $lines Flags (1 Byte Flags). ( $number_flags ) = unpack ( "c" , $lines ); # (h) hex string (low + nybble first). print("This is the number of flags: $number_flags\n"); read( FH , $lines, 1 ); # Read 8 bits (1 Byte) and store the data +in $lines Flags (1 Byte Flags). ( $extended_flags ) = unpack ( "C" , $lines ); # An unsigned chara +cter (usually 8 bits). print("This is the extended header flags: $extended_flags\n"); print("This is the extended header size, after sync_safe: $extende +d_size\n"); # From the stored value we subtract the Extended Header to get the + total size so far. $length_of_data = $length_of_data - $extended_size; # Reposition the seek pointer after the Extended Header. seek( FH , $extended_size + $mp3_size , SEEK_SET ) or die "Could not seek: $!"; # Based on possition 0 with SEEK_ +SET we move $extended_size + $mp3_size. # End of Extended Header (6 Bytes in total) } else { # Set the pointer after 10 Bytes. seek( FH , 10 , SEEK_SET ) or die "Could not seek: $!"; # Based on position 0 with SEEK_S +ET we move 10 byte. # print("This is the length of data: ".$length_of_data."\n"); until($length_of_data == 0) { # Begging of Mp3 Frame (10 Bytes in total), 4 Bytes Frame_ID + + 4 Bytes Frame_Size + 2 Bytes Frame_Flags = 10 Bytes. # Loop through until the end of length of data previously meas +ured. read( FH , $lines, 4 ); # Read 32 bits (4 Bytes) and store the + data in $lines Extended Header. ( $frame_id ) = unpack ( "A4" , $lines ); # (c) signed char (8 +-bit) value. print("This is the frame_id: $frame_id\n"); read( FH , $lines, 4 ); # Read 32 bits (4 Bytes) and store the + data in $lines Extended Header. ( @memory ) = unpack ( "C4" , $lines ); # (I) An unsigned inte +ger. $frame_size = ($memory[0] & 0xFF) | (($memory[1] & 0xFF) << 7 ) | (($memory[2] & 0xFF) << 14 ) | (($memory[3] & 0xFF) << 21 ); read( FH , $lines, 2 ); # Read 16 bits (2 Bytes) and store the + data in $lines Frame Flags. ( $frame_flags ) = unpack ( "A2" , $lines ); # (c) signed char + (8-bit) value. $length = length($frame_id); # print("This is the length of frame_id: ".$length."\n"); foreach($frame_id) { if ( $frame_id eq "TALB") { print "I have found one matching pattern: TALB\n"; } elsif ( $frame_id eq "TCON") { print "I have found one matching pattern: TCON\n"; } elsif ( $frame_id eq "TIT2") { print "I have found one matching pattern: TIT2\n"; } elsif ( $frame_id eq "TPE1") { print "I have found one matching pattern: TPE1\n"; } elsif ( $frame_id eq "TRCK") { print "I have found one matching pattern: TRCK\n"; } elsif ( $frame_id eq "TYER") { print "I have found one matching pattern: TYER\n"; $length_of_data = 0; } # End of Mp3 Frame (10 Bytes in total), 4 Bytes Frame_ID + 4 Bytes Fra +me_Size + 2 Bytes Frame_Flags = 10 Bytes. } # End of until } # End foreach } # End of else condition }# End of Big else after argument condition close (FH) or die "Can not close file: $source: $!\n"; $| = 1; #flushing output

When I am executing the code:

perl mp3.pl song.mp3

I get the following output in the terminal:

Use of uninitialized value $memory[0] in bitwise and (&) at Exercise_3 +.pl line 143. Use of uninitialized value $memory[1] in bitwise and (&) at Exercise_3 +.pl line 143. Use of uninitialized value $memory[2] in bitwise and (&) at Exercise_3 +.pl line 143. Use of uninitialized value $memory[3] in bitwise and (&) at Exercise_3 +.pl line 143.

Repeating over and over, It is kind of strange because if I use the:

exit(0);

After the:

print("This is the frame_id: $frame_id\n");

The output of the terminal will be:

This is the frame_id: TPE1

Also if I move the exit(0); command to stop the program a bit further at the of until condition I get the following output:

I have found one matching pattern: TPE1

Which makes me believe that the program operates correctly before it starts looping. When it starts looping it will loose the sequence of SEEK_SET in result it will not read the file in sequence and it will read the file over and over.

I tried to use the until condition with ($lines = <FH>), inorder to take of advantage the SEEK_SET point 10 Bytes and maje the program loop over and over until the end of file. But maybe I assumed wrong because I had the same result as previously.

So at this point I have run out of ideas and debugging processes, the code should be running at any operating system, if someone wants to try it only need to provide an mp3 file as ARGV1 and in theory it should get the same output as me.

Any ideas, why is looping without printing all the $header_id since it prints one of them.

I hope providing my full code it will someone understand the structure and maybe possible small mistakes.

Thank you in advance for your time and effort, it means a lot to people who are beginners to ask someone who can provide assistance.


In reply to ID3v2 TAG Footer Reading goes wrong by thanos1983

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.