#!/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 , $size , $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!\nCorrect syntax perl name of the program (e.g. Exercise3.pl) and name of 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 ".$source." 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 file (Define possition with SEEK_SET).
read( FH , $lines , 3 ); # Read 24 bits (3 Bytes) ID3 and store the data in $lines. Header_ID
( $type ) = unpack ( "A3" , $lines ); # (A) text (ASCII) string, will 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 (low 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 (low 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_number."\n";
if($flags == 0) {
print("\nThe extended flags has no corresponding data: \$00 was detected. 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 data 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 header (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 proceed 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 Bytes 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 data 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 character (usually 8 bits).
print("This is the extended header flags: $extended_flags\n");
print("This is the extended header size, after sync_safe: $extended_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_SET 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 measured.
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 integer.
$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 Frame_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
####
perl mp3.pl song.mp3
####
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.
####
exit(0);
####
print("This is the frame_id: $frame_id\n");
####
This is the frame_id: TPE1
####
I have found one matching pattern: TPE1