<?php
/**
* Retrieves ID3v2 tag information from MP3 files.
*
* Changelog:
* 1.0 base done by Tadeu.
* 1.1 added ability to retrieve more information i.e attached picture,
* mood, comments and so on.
* 2.0 Rebuilt, cleaned up the class so it fits PHP5 standards and
* fine OOP. ;)
*
* @author Tadeu Oliveira <tadeu.fo ( at ) gmail.com>, Ronald Pompa <pompa ( at ) kth.se>
* @version 2.0
**/
class ID3 {
// instance vars
private $file_name;
private $tags;
private $tags_count;
// constants
const DEBUG = false;
/**
* Constructor reads in a file location of an mp3 file.
*
* @param $file_name An mp3 file.
**/
public function __construct($file_name)
{
$this->file_name = $file_name;
$this->tags = array();
$this->tags_count = 0;
$this->getInfo();
}
/**
* Destructor removes all pointers to data that were used in
* the instance variables.
*
*/
public function __destruct()
{
$this->tags = NULL;
$this->tags_count = 0;
$this->file_name = "";
}
/**
* Converts hex data to binary.
*
* @return $data Returns data as binary.
**/
private function hex2bin($data)
{
$len = strlen($data);
for($i=0;$i<$len;$i+=2) {
$data .= pack("C",hexdec(substr($data,$i,2)));
}
return $data;
}
/**
* Gets the frame size.
*
* @return $total the total of the frame size.
**/
private function get_frame_size($fourBytes)
{
$tamanho[0] = str_pad(base_convert(substr($fourBytes,0,2),16,2),7,0,STR_PAD_LEFT);
$tamanho[1] = str_pad(base_convert(substr($fourBytes,2,2),16,2),7,0,STR_PAD_LEFT);
$tamanho[2] = str_pad(base_convert(substr($fourBytes,4,2),16,2),7,0,STR_PAD_LEFT);
$tamanho[3] = str_pad(base_convert(substr($fourBytes,6,2),16,2),7,0,STR_PAD_LEFT);
$total = $tamanho[0].$tamanho[1].$tamanho[2].$tamanho[3];
$tamanho[0] = substr($total,0,8);
$tamanho[1] = substr($total,8,8);
$tamanho[2] = substr($total,16,8);
$tamanho[3] = substr($total,24,8);
$total = $tamanho[0].$tamanho[1].$tamanho[2].$tamanho[3];
$total = base_convert($total,2,10);
return $total;
}
/**
* Retrieves tags from the ID3 header.
*
* @param $text Binary safe data from an mp3 file.
* @param $tags An array to put your retrieved data in.
**/
private function extractTags($text, &$tags)
{
$size = -1;
while ((strlen($text) != 0) and ($size != 0)) {
$ID = substr($text,0,4);
$aux = substr($text,4,4);
$aux = bin2hex($aux);
$size = $this->get_frame_size($aux);
$flags = substr($text,8,2);
$info = substr($text,11,$size-1);
if ($size != 0){
$tags[$ID] = $info;
$this->tags_count++;
}
$text = substr($text,10+$size,strlen($text));
}
}
/**
* Gets the necessary data from an mp3 file.
*
* @return true if no errors occurs.
**/
private function getInfo()
{
//TODO check for mp3 ext.
if ($this->file_name != "") {
$mp3 = @fopen($this->file_name,"rb");
$header = @fread($mp3,10);
if (!$header) {
if (self::DEBUG)
printf("Unable to open MP3 file.\n");
return false;
}
if (substr($header,0,3) != "ID3") {
if (self::DEBUG)
printf("ID3v2 Tag not found on this file.\n");
return false;
}
$header = bin2hex($header);
$version = base_convert(substr($header,6,2),16,10) . "." .
base_convert(substr($header,8,2),16,10);
$flags = base_convert(substr($header,10,2),16,2);
$flags = str_pad($flags,8,0,STR_PAD_LEFT);
// no func really. Just interesting to see.
if (self::DEBUG) {
if ($flags[7] == 1){
printf("with Unsynchronisation\n");
}
else if ($flags[6] == 1){
printf("with Extended header\n");
}
}
if ($flags[5] == 1){
if (self::DEBUG)
printf("TAG not Supported.");
return false;
}
$total = $this->get_frame_size(substr($header,12,8));
$text = @fread($mp3,$total);
fclose($mp3);
$this->extractTags($text, $this->tags);
} else {
if (self::DEBUG)
printf("Parameter missing in constructor, please put in a filename!\n");
return false;
}
return true;
}
/**
* Retrieves information based on what frame you assign it to.
* More info found on http://www.id3.org/id3v2.4.0-frames-
*
* The most common are: TIT2 (title), TPE1 (artist), TRCK (track),
* TALB (album), APIC (attached picture).
*
* @param $frame An ID3 frame tag.
* @return Returns the information if tag found, else false.
**/
public function getFrame($frame)
{
if (array_key_exists($frame, $this->tags)){
return $this->tags[$frame];
} else {
if (self::DEBUG)
printf($frame . " was not found in the file.");
return false;
}
}
}
?>